diff --git a/crates/ui-egui/src/panels/tabs.rs b/crates/ui-egui/src/panels/tabs.rs index f8a2d758e..e68ae9b54 100644 --- a/crates/ui-egui/src/panels/tabs.rs +++ b/crates/ui-egui/src/panels/tabs.rs @@ -265,7 +265,9 @@ fn above_text(app: &mut VectorcraftApp, ctx: &egui::Context, n: &Node, span: f64 } 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]); - let pos = egui::pos2(on_screen.left() - RULER_LEFT, on_screen.top() - height - 6.0); + // Text near the top of the window: the panel's bar stays inside the window, where it can be + // grabbed, not above the screen. + let pos = egui::pos2(on_screen.left() - RULER_LEFT, (on_screen.top() - height - 6.0).max(0.0)); let origin = ctx.input(|i| i.raw.viewports.get(&egui::ViewportId::ROOT).and_then(|v| v.inner_rect)).map(|r| r.min); crate::panel_docking::place_floating(app, "tabs", pos, width, origin).ok()?; Some(()) diff --git a/crates/ui-egui/src/panels/tabs/tests.rs b/crates/ui-egui/src/panels/tabs/tests.rs index 5fd7b17c4..8ab728afe 100644 --- a/crates/ui-egui/src/panels/tabs/tests.rs +++ b/crates/ui-egui/src/panels/tabs/tests.rs @@ -991,7 +991,8 @@ fn above_text_puts_the_ruler_over_the_text_at_the_zoom() { let mut app = VectorcraftApp::new(Session::new(), Default::default()); app.session.execute("file.new", &json!({"width": 600, "height": 400})).unwrap(); app.session.execute("text.create", &json!({"x": 40, "y": 200, "text": "a b", "area": {"width": 300, "height": 100}})).unwrap(); - app.canvas_rect = Some(egui::Rect::from_min_size(egui::pos2(50.0, 40.0), vec2(1000.0, 700.0))); + // The canvas starts low enough in the window for the panel to fit above the text at 200%. + app.canvas_rect = Some(egui::Rect::from_min_size(egui::pos2(50.0, 440.0), vec2(1000.0, 700.0))); for zoom in [1.0, 2.0] { if let Some(v) = app.view_mut() { v.zoom = zoom; @@ -1044,3 +1045,34 @@ fn above_text_moves_a_panel_floating_in_its_own_window() { assert_eq!([native[0], native[1]], [200.0 + g.rect[0], 100.0 + g.rect[1]], "the OS window goes above the text"); assert_eq!([native[2], native[3]], [g.rect[2], g.rect[3]]); } + +/// Text near the top of the window: Position Panel Above Text keeps the panel's bar inside the +/// window, so neither the panel nor its OS window goes above the screen. +#[test] +fn above_text_keeps_the_panel_bar_in_the_window() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 600, "height": 400})).unwrap(); + app.session.execute("text.create", &json!({"x": 40, "y": 10, "text": "a b", "area": {"width": 300, "height": 100}})).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; + } + let (node, _, span) = current(&app).unwrap(); + let xf = crate::canvas::Xf::new(app.canvas_rect.unwrap(), app.view().unwrap()); + assert!(xf.rect_to_screen(node.geometric_bounds().unwrap()).top() < 240.0, "no room above the text"); + let ctx = egui::Context::default(); + above_text(&mut app, &ctx, &node, span).unwrap(); + let mut layout = app.ui.docking.clone().unwrap(); + let window = layout.floating.iter_mut().find(|w| w.contains(&"tabs".to_string())).unwrap(); + assert_eq!(window.rect[1], 0.0, "the panel's bar at the window's top"); + window.native = Some([1500.0, 900.0, window.rect[2], window.rect[3]]); + app.ui.docking = Some(layout); + let mut raw = egui::RawInput::default(); + raw.viewports.entry(egui::ViewportId::ROOT).or_default().inner_rect = + Some(egui::Rect::from_min_size(egui::pos2(200.0, 100.0), vec2(1200.0, 800.0))); + ctx.run_ui(raw, |_| {}).textures_delta.clear(); + 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.native.unwrap()[1], 100.0, "the OS window's bar at the main window's content top"); +}