mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 09:29:44 -06:00
Fix #4995: Above Text moves a Tabs panel that already floats
Position Panel Above Text moved a floating Tabs panel only in the layout's in-window rect. On desktop builds a floating panel is an OS window, which moves only when its desktop rect changes, so the panel stayed where it was and the layout followed it back on the next frame. A docked panel floated above the text as expected. place_floating now also sets the window's desktop rect, from the main window's content origin, when the panel's window already has one.
This commit is contained in:
@@ -787,8 +787,9 @@ fn float_space(app: &VectorcraftApp) -> FloatSpace<String> {
|
||||
}
|
||||
|
||||
/// Float `panel` (open or not) as a window of its own with its top-left corner at `at`, `width`
|
||||
/// wide: the Tabs panel over the text it sets.
|
||||
pub(crate) fn place_floating(app: &mut VectorcraftApp, panel: &str, at: egui::Pos2, width: f32) -> Result<(), String> {
|
||||
/// wide: the Tabs panel over the text it sets. `origin`: the main window's content top-left on the
|
||||
/// desktop. A panel already floating in an OS window of its own moves to `at` from there (#4995).
|
||||
pub(crate) fn place_floating(app: &mut VectorcraftApp, panel: &str, at: egui::Pos2, width: f32, origin: Option<egui::Pos2>) -> Result<(), String> {
|
||||
let panel = normalize(panel).ok_or("unknown panel")?.to_string();
|
||||
if !(at.x.is_finite() && at.y.is_finite() && width.is_finite()) {
|
||||
return Err("panel position must be finite".into());
|
||||
@@ -800,8 +801,13 @@ pub(crate) fn place_floating(app: &mut VectorcraftApp, panel: &str, at: egui::Po
|
||||
let height = floating_window(&txn.workspace.layout, &panel).map_or(floating_size(&panel).y, |w| w.rect[3]);
|
||||
let rect = [at.x, at.y, width.max(1.0), height];
|
||||
let whole = floating_window(&txn.workspace.layout, &panel).is_some_and(|w| w.panels().count() == 1);
|
||||
let action = if whole { Action::MoveFloating { panel, rect } } else { Action::Float { panel, rect } };
|
||||
let native = whole && floating_window(&txn.workspace.layout, &panel).is_some_and(|w| w.native.is_some());
|
||||
let action = if whole { Action::MoveFloating { panel: panel.clone(), rect } } else { Action::Float { panel: panel.clone(), rect } };
|
||||
txn.apply(action)?;
|
||||
// The OS window follows its desktop rect, not the in-window one.
|
||||
if let Some(origin) = origin.filter(|o| native && o.is_finite()) {
|
||||
txn.apply(Action::SetNative { panel, rect: Some([origin.x + at.x, origin.y + at.y, rect[2], rect[3]]) })?;
|
||||
}
|
||||
txn.commit(app, Vec::new(), None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -266,8 +266,8 @@ 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);
|
||||
let _ = ctx;
|
||||
crate::panel_docking::place_floating(app, "tabs", pos, width).ok()?;
|
||||
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(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1012,3 +1012,35 @@ fn above_text_puts_the_ruler_over_the_text_at_the_zoom() {
|
||||
}
|
||||
assert_eq!(app.ui.docking.unwrap().floating.len(), 1, "placed again, not floated twice");
|
||||
}
|
||||
|
||||
/// Position Panel Above Text moves the panel when it already floats in an OS window of its own:
|
||||
/// the window's desktop rect goes above the text too, from the main window's top-left (#4995).
|
||||
#[test]
|
||||
fn above_text_moves_a_panel_floating_in_its_own_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": 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)));
|
||||
if let Some(v) = app.view_mut() {
|
||||
v.zoom = 1.0;
|
||||
}
|
||||
let (node, _, span) = current(&app).unwrap();
|
||||
let ctx = egui::Context::default();
|
||||
above_text(&mut app, &ctx, &node, span).unwrap();
|
||||
// The panel's OS window sits elsewhere on the desktop, as after the user dragged it there.
|
||||
let mut layout = app.ui.docking.clone().unwrap();
|
||||
let window = layout.floating.iter_mut().find(|w| w.contains(&"tabs".to_string())).unwrap();
|
||||
window.native = Some([1500.0, 900.0, window.rect[2], window.rect[3]]);
|
||||
app.ui.docking = Some(layout);
|
||||
// The main window's content starts at (200, 100) on the desktop.
|
||||
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();
|
||||
let native = g.native.unwrap();
|
||||
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]]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user