diff --git a/crates/ui-egui/src/panel_docking.rs b/crates/ui-egui/src/panel_docking.rs index 83cad5282..a35166d97 100644 --- a/crates/ui-egui/src/panel_docking.rs +++ b/crates/ui-egui/src/panel_docking.rs @@ -1237,7 +1237,7 @@ const DOCK_LABELS: &[(&str, &str)] = &[ ]; /// The size a panel floats at (tab strip included), whatever the size of the dock it left. -fn floating_size(panel: &str) -> egui::Vec2 { +pub(crate) fn floating_size(panel: &str) -> egui::Vec2 { match panel { "properties" | "layers" | "libraries" => egui::vec2(300.0, 420.0), // Tall enough to show its Corners and Shared Edges sections, last in it, which the Corner diff --git a/crates/ui-egui/src/panels/tabs.rs b/crates/ui-egui/src/panels/tabs.rs index ae49d2048..c8ef4331c 100644 --- a/crates/ui-egui/src/panels/tabs.rs +++ b/crates/ui-egui/src/panels/tabs.rs @@ -264,15 +264,19 @@ fn above_text(app: &mut VectorcraftApp, ctx: &egui::Context, n: &Node, span: f64 return None; } let width = span as f32 * per + RULER_LEFT * 2.0; - let height = crate::panel_docking::layout_of(&app.ui).floating.iter().find(|w| w.contains(&"tabs".to_string())).map_or(240.0, |w| w.rect[3]); - // Text near the top of the window: the panel goes just below the text when it fits there in - // the canvas, else at the window's top, so its bar stays where it can be grabbed rather than - // above the screen. + let height = crate::panel_docking::layout_of(&app.ui) + .floating + .iter() + .find(|w| w.contains(&"tabs".to_string())) + .map_or(crate::panel_docking::floating_size("tabs").y, |w| w.rect[3]); + // Text near the top of the window: the panel goes just below the text when that place is in + // the window and the panel fits there in the canvas, else at the window's top, so its bar + // stays where it can be grabbed rather than above the screen. let above = on_screen.top() - height - 6.0; let below = on_screen.bottom() + 6.0; let top = if above >= 0.0 { above - } else if below + height <= app.canvas_rect.map_or(f32::NEG_INFINITY, |c| c.bottom()) { + } else if below >= 0.0 && below + height <= app.canvas_rect.map_or(f32::NEG_INFINITY, |c| c.bottom()) { below } else { 0.0 diff --git a/crates/ui-egui/src/panels/tabs/tests.rs b/crates/ui-egui/src/panels/tabs/tests.rs index 8a6d2394e..7b02b72ff 100644 --- a/crates/ui-egui/src/panels/tabs/tests.rs +++ b/crates/ui-egui/src/panels/tabs/tests.rs @@ -1081,3 +1081,28 @@ fn above_text_keeps_the_panel_bar_in_the_window() { assert_eq!(g.native.unwrap()[1], 100.0 + top, "text {text_height} high: the OS window"); } } + +/// Position Panel Above Text puts the panel at the window's top when the place below the text is +/// above the window (text panned above the canvas), or when the panel at the height it floats at +/// does not fit below the text in the canvas. +#[test] +fn above_text_puts_the_panel_at_the_top_when_below_the_text_does_not_fit() { + // The view's centre and the text's height: panned so the text is above the canvas, and a text + // whose bottom leaves room for 240 points of panel but not for the 360 it floats at. + for (center_y, text_height) in [(600.0, 100.0), (350.0, 340.0)] { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 600, "height": 800})).unwrap(); + app.session.execute("text.create", &json!({"x": 40, "y": 20, "text": "a b", "area": {"width": 300, "height": text_height}})).unwrap(); + app.canvas_rect = Some(egui::Rect::from_min_size(egui::pos2(50.0, 40.0), vec2(1000.0, 700.0))); + if let Some(v) = app.view_mut() { + v.zoom = 1.0; + v.center = vectorcraft_geom::Point::new(300.0, center_y); + } + let (node, _, span) = current(&app).unwrap(); + let ctx = egui::Context::default(); + above_text(&mut app, &ctx, &node, span).unwrap(); + let layout = app.ui.docking.clone().unwrap(); + let g = layout.floating.iter().find(|w| w.contains(&"tabs".to_string())).unwrap(); + assert_eq!(g.rect[1], 0.0, "view centered at y {center_y}"); + } +}