Tabs: Above Text puts the panel below text near the window's top

With no room above the text, the panel went to the top of the main
window's content and could cover the text. It now goes just below the
text when it fits there in the canvas, and at the window's top only when
it does not.
This commit is contained in:
dotCooCoo
2026-11-02 14:32:49 +07:00
committed by bflatastic
parent 48e2bc6508
commit 442430ff12
2 changed files with 44 additions and 29 deletions
+13 -3
View File
@@ -265,9 +265,19 @@ 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]);
// 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));
// 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 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()) {
below
} else {
0.0
};
let pos = egui::pos2(on_screen.left() - RULER_LEFT, top);
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(())
+31 -26
View File
@@ -1046,33 +1046,38 @@ fn above_text_moves_a_panel_floating_in_its_own_window() {
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.
/// Text near the top of the window: Position Panel Above Text puts the panel just below the text,
/// so neither the panel nor its OS window goes above the screen and the text stays in view. Text
/// with no room below it either gets the panel at the window's top.
#[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;
for (text_height, below) in [(100.0, true), (760.0, false)] {
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": 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;
}
let (node, _, span) = current(&app).unwrap();
let xf = crate::canvas::Xf::new(app.canvas_rect.unwrap(), app.view().unwrap());
let frame = xf.rect_to_screen(node.geometric_bounds().unwrap());
assert!(frame.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();
let top = if below { frame.bottom() + 6.0 } else { 0.0 };
assert_eq!(window.rect[1], top, "text {text_height} high");
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 + top, "text {text_height} high: the OS window");
}
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");
}