mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-28 03:37:00 -06:00
Merge pull request #3550 from Taha-Mahmoodi/fix/3093-panel-scrollbars
Fix #3093: panel scroll bars take a column of their own instead of covering controls
This commit is contained in:
+51
-10
@@ -166,10 +166,9 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
}
|
||||
egui::Frame::NONE.inner_margin(egui::Margin { left: 12, right: 10, top: 10, bottom: 8 }).show(ui, |ui| match active {
|
||||
DockTab::Properties => {
|
||||
egui::ScrollArea::vertical()
|
||||
.id_salt("props")
|
||||
.auto_shrink([false, false])
|
||||
.show(ui, |ui| panels::properties::show(app, ui));
|
||||
widgets::panel_scroll(ui, egui::ScrollArea::vertical().id_salt("props").auto_shrink([false, false]), |ui| {
|
||||
panels::properties::show(app, ui)
|
||||
});
|
||||
}
|
||||
DockTab::Layers => panels::layers::show(app, ui),
|
||||
DockTab::Libraries => panels::libraries(app, ui),
|
||||
@@ -512,12 +511,11 @@ pub(crate) fn panel_body(app: &mut VectorcraftApp, ui: &mut Ui, id: &str, width:
|
||||
// At least `tall` when the sections are taller: the flyout's area keeps the size its
|
||||
// content needs, and egui sizes a new area at most 400 points high, so a scroll area
|
||||
// that only took the room it was given never grew past it.
|
||||
egui::ScrollArea::vertical()
|
||||
.id_salt("props-flyout")
|
||||
.max_height(tall)
|
||||
.min_scrolled_height(tall)
|
||||
.auto_shrink([false, true])
|
||||
.show(ui, |ui| panels::properties::show(app, ui));
|
||||
widgets::panel_scroll(
|
||||
ui,
|
||||
egui::ScrollArea::vertical().id_salt("props-flyout").max_height(tall).min_scrolled_height(tall).auto_shrink([false, true]),
|
||||
|ui| panels::properties::show(app, ui),
|
||||
);
|
||||
}
|
||||
Some(DockTab::Layers) => {
|
||||
ui.set_max_height(tall);
|
||||
@@ -682,6 +680,49 @@ mod tests {
|
||||
assert!(icons.last().unwrap().bottom() <= 400.0, "scrolled into view: {:?}", icons.last());
|
||||
}
|
||||
|
||||
/// #3093: in a window too short for the Properties panel, its scroll bar takes a column of its
|
||||
/// own instead of floating over the panel's right edge, so it covers none of the controls
|
||||
/// there, such as the arrow of an image's Image Trace button.
|
||||
#[test]
|
||||
fn the_properties_scroll_bar_covers_no_control() {
|
||||
use egui::accesskit::{Action, Role};
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 300, "height": 300})).unwrap();
|
||||
app.run("shape.rectangle", json!({"x": 10, "y": 10, "width": 50, "height": 40})).unwrap();
|
||||
app.run("object.rasterize", json!({})).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
ctx.enable_accesskit();
|
||||
theme::install_fonts(&ctx);
|
||||
let screen = egui::Rect::from_min_size(egui::Pos2::ZERO, vec2(1440.0, 600.0));
|
||||
let mut nodes = vec![];
|
||||
for k in 0..4 {
|
||||
let input = egui::RawInput { time: Some(f64::from(k) * 0.1), screen_rect: Some(screen), ..Default::default() };
|
||||
let mut out = ctx.run_ui(input, |ui| show(&mut app, ui));
|
||||
out.textures_delta.clear();
|
||||
nodes = out.platform_output.accesskit_update.unwrap().nodes;
|
||||
}
|
||||
let rect = |n: &egui::accesskit::Node| {
|
||||
n.bounds().map(|b| egui::Rect::from_min_max(egui::pos2(b.x0 as f32, b.y0 as f32), egui::pos2(b.x1 as f32, b.y1 as f32)))
|
||||
};
|
||||
// The Properties panel's is the right-most (the icon column's slim strip bar is left of it).
|
||||
let bar = nodes
|
||||
.iter()
|
||||
.filter(|(_, n)| n.role() == Role::ScrollBar)
|
||||
.filter_map(|(_, n)| rect(n))
|
||||
.max_by(|a, b| a.left().total_cmp(&b.left()))
|
||||
.filter(|bar| bar.left() > screen.right() - DOCK_WIDTH);
|
||||
let bar = bar.expect("the Properties panel scrolls in a 600 pt window");
|
||||
let mut covered = vec![];
|
||||
for (_, n) in &nodes {
|
||||
let Some(r) = rect(n) else { continue };
|
||||
let control = n.role() != Role::ScrollBar && (n.supports_action(Action::Click) || n.supports_action(Action::Focus));
|
||||
if control && r.intersect(bar).is_positive() {
|
||||
covered.push(format!("{:?} {:?} at {r:?}", n.role(), n.label()));
|
||||
}
|
||||
}
|
||||
assert!(covered.is_empty(), "under the scroll bar at {bar:?}: {covered:#?}");
|
||||
}
|
||||
|
||||
const SCREEN: egui::Vec2 = vec2(1600.0, 1000.0);
|
||||
|
||||
/// A frame of the dock and its flyout on a 1600 × 1000 window.
|
||||
|
||||
@@ -29,7 +29,7 @@ use serde_json::{Value, json};
|
||||
|
||||
use crate::state::{DockTab, FloatingPanels, UiState, all_panels};
|
||||
use crate::theme::{self, Tokens};
|
||||
use crate::{VectorcraftApp, dock, icons, panels};
|
||||
use crate::{VectorcraftApp, dock, icons, panels, widgets};
|
||||
|
||||
/// Height of a floating group's title bar (its grip and ×).
|
||||
pub const TITLE: f32 = 14.0;
|
||||
@@ -911,7 +911,7 @@ pub(crate) fn docked_columns(app: &mut VectorcraftApp, ui: &mut egui::Ui) -> Vec
|
||||
// The open panels share the column's height, each at least `MIN_TALL`: more than fit
|
||||
// scroll, with the column.
|
||||
let tall = ((ui.available_height() - n * STRIP - (n - 1.0) * GAP - 24.0 * open) / open).max(MIN_TALL);
|
||||
egui::ScrollArea::vertical().id_salt(("dock-column-scroll", first)).auto_shrink([false, false]).show(ui, |ui| {
|
||||
widgets::panel_scroll(ui, egui::ScrollArea::vertical().id_salt(("dock-column-scroll", first)).auto_shrink([false, false]), |ui| {
|
||||
ui.spacing_mut().item_spacing = Vec2::ZERO;
|
||||
let width = ui.available_width();
|
||||
let top = ui.cursor().top();
|
||||
|
||||
@@ -771,10 +771,11 @@ impl craft_ui::docking::DockContent<String> for PanelContent<'_> {
|
||||
if crate::state::DockTab::from_id(panel).is_some() {
|
||||
crate::dock::panel_body(self.app, ui, panel, width, height);
|
||||
} else {
|
||||
egui::ScrollArea::vertical()
|
||||
.id_salt(("shared-panel-body", panel))
|
||||
.auto_shrink([false, false])
|
||||
.show(ui, |ui| crate::dock::panel_body(self.app, ui, panel, width, height));
|
||||
// The body is as wide as the scroll area leaves it beside its scroll bar.
|
||||
crate::widgets::panel_scroll(ui, egui::ScrollArea::vertical().id_salt(("shared-panel-body", panel)).auto_shrink([false, false]), |ui| {
|
||||
let width = ui.available_width();
|
||||
crate::dock::panel_body(self.app, ui, panel, width, height)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1205,7 +1206,7 @@ fn icon_rail(app: &mut VectorcraftApp, ui: &mut egui::Ui) {
|
||||
.frame(egui::Frame::NONE)
|
||||
.show(ui, |ui| {
|
||||
crate::theme::card(ui, &t, egui::Margin { left: 3, right: 3, top: 6, bottom: 6 }, |ui| {
|
||||
egui::ScrollArea::vertical().id_salt("shared-panel-icons-scroll").show(ui, |ui| {
|
||||
crate::widgets::strip_scroll(ui, "shared-panel-icons-scroll", |ui| {
|
||||
ui.spacing_mut().item_spacing.y = 2.0;
|
||||
// The tabbed group's panels dropped on the column head it, then the icon panels,
|
||||
// then the other panels dropped on it (those the column has no place for).
|
||||
|
||||
@@ -1177,6 +1177,22 @@ pub fn strip_scroll<R>(ui: &mut Ui, id: &str, add_contents: impl FnOnce(&mut Ui)
|
||||
.inner
|
||||
}
|
||||
|
||||
/// A panel's scrolling body (Properties, a dock column, a panel in the shared dock): `area` with
|
||||
/// the scroll bar of a menu's list ([`list_scroll_style`]), in a column of its own at the right
|
||||
/// edge, so it never covers the controls there, such as a dropdown's arrow (#3093). The scroll
|
||||
/// areas inside the body keep their own style.
|
||||
pub fn panel_scroll<R>(ui: &mut Ui, area: egui::ScrollArea, add_contents: impl FnOnce(&mut Ui) -> R) -> R {
|
||||
ui.scope(|ui| {
|
||||
let inner = std::mem::replace(&mut ui.spacing_mut().scroll, list_scroll_style());
|
||||
area.show(ui, |ui| {
|
||||
ui.spacing_mut().scroll = inner;
|
||||
add_contents(ui)
|
||||
})
|
||||
.inner
|
||||
})
|
||||
.inner
|
||||
}
|
||||
|
||||
/// Whether the blend-mode list draws a separator above `BlendMode::ALL[i]`: where a
|
||||
/// [`BlendMode::group`] starts.
|
||||
pub fn blend_separator_before(i: usize) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user