mirror of
https://github.com/storytold/figcraft.git
synced 2026-11-08 19:42:05 +00:00
Merge pull request #2053 from TrevorBrown-dev/fix/minimized-click-shifts-frame-2051
Minimize: a click that shows the right sidebar no longer moves the frame it selects (#2051)
This commit is contained in:
2 files changed
+152
-9
No files matched your search
@@ -195,21 +195,37 @@ fn pointer_event(app: &FigApp, kind: PointerKind, pos: Pos2, m: &egui::Modifiers
|
||||
/// labels, handles, comments, versions/branch/dev/live UI and the cursor.
|
||||
pub fn show(app: &mut FigApp, ui: &mut Ui, worker: &mut Worker) {
|
||||
let rect = ui.max_rect();
|
||||
app.canvas.rect = rect;
|
||||
let old_rect = std::mem::replace(&mut app.canvas.rect, rect);
|
||||
let ctx = ui.ctx().clone();
|
||||
let ppp = ctx.pixels_per_point();
|
||||
let resp = ui.interact(rect, ui.scope_id().with("canvas"), Sense::click_and_drag());
|
||||
let key_box = app.ui.key_box;
|
||||
crate::a11y::canvas(&mut app.ui.nav, &resp, || key_box.map_or_else(|| describe(&app.session), describe_box));
|
||||
if let Ok(ds) = app.session.ds_mut()
|
||||
&& ((ds.view.vw - f64::from(rect.width())).abs() > 0.5 || (ds.view.vh - f64::from(rect.height())).abs() > 0.5)
|
||||
{
|
||||
// Keep the centre; the first sizing after opening re-fits.
|
||||
if let Ok(ds) = app.session.ds_mut() {
|
||||
let resized = (ds.view.vw - f64::from(rect.width())).abs() > 0.5 || (ds.view.vh - f64::from(rect.height())).abs() > 0.5;
|
||||
// The first sizing after opening re-fits.
|
||||
let first = ds.view.vw == 1200.0 && ds.view.vh == 800.0;
|
||||
ds.view.vw = f64::from(rect.width());
|
||||
ds.view.vh = f64::from(rect.height());
|
||||
if first {
|
||||
ds.zoom_to_fit();
|
||||
// While a button is held that went down on the canvas (a click that selects shows the
|
||||
// right panel, minimized, and a click that deselects hides it; the left panel and rail
|
||||
// can appear too) the page point under a still pointer stays where it is: the gesture's
|
||||
// own pointer events would otherwise map to another page point and the move handling
|
||||
// would take that for a drag. `to_page` is `origin + (p - rect.min) / z` with
|
||||
// `origin = c - vw / 2 / z`, so the centre moves by half the size change plus the
|
||||
// rect's own move, over the zoom.
|
||||
let held = resp.is_pointer_button_down_on() && !first && old_rect.is_finite();
|
||||
let z = ds.view.zoom.max(1e-9);
|
||||
let (dw, dh) = if resized { (f64::from(rect.width()) - ds.view.vw, f64::from(rect.height()) - ds.view.vh) } else { (0.0, 0.0) };
|
||||
if resized {
|
||||
// Otherwise keep the centre.
|
||||
ds.view.vw = f64::from(rect.width());
|
||||
ds.view.vh = f64::from(rect.height());
|
||||
if first {
|
||||
ds.zoom_to_fit();
|
||||
}
|
||||
}
|
||||
if held {
|
||||
ds.view.cx += (dw / 2.0 + f64::from(rect.min.x - old_rect.min.x)) / z;
|
||||
ds.view.cy += (dh / 2.0 + f64::from(rect.min.y - old_rect.min.y)) / z;
|
||||
}
|
||||
}
|
||||
let own_cursor = input(app, ui, &resp);
|
||||
|
||||
@@ -346,3 +346,130 @@ fn a_narrow_window_shows_one_side_panel_at_a_time() {
|
||||
let wide = canvas_width(&mut app, vec2(1440.0, 900.0));
|
||||
assert!(wide > 600.0 && wide < 1100.0, "a wide window shows both panels beside the canvas ({wide})");
|
||||
}
|
||||
|
||||
/// Five frames side by side at 46% zoom with the interface minimized and nothing selected.
|
||||
fn minimized_row() -> (Ui, Vec<figcraft_doc::NodeId>) {
|
||||
let mut s = Session::new();
|
||||
let mut ids = Vec::new();
|
||||
for k in 0..5 {
|
||||
let r = s.execute("frame.create", &json!({"x": k * 470, "y": 0, "width": 400, "height": 600})).unwrap();
|
||||
ids.push(figcraft_engine::params::id_of(&r["id"]).unwrap());
|
||||
}
|
||||
s.execute("select.set", &json!({"ids": []})).unwrap();
|
||||
let mut u = Ui::new(s);
|
||||
u.app.run("view.minimize", json!({"on": true}));
|
||||
u.app.run("view.set", json!({"zoom": 0.46, "cx": 1500, "cy": 300}));
|
||||
for _ in 0..4 {
|
||||
u.frame(vec![]);
|
||||
}
|
||||
(u, ids)
|
||||
}
|
||||
|
||||
fn frame_x(u: &Ui, id: figcraft_doc::NodeId) -> f64 {
|
||||
u.app.session.doc().unwrap().world_bounds(id).unwrap().x0
|
||||
}
|
||||
|
||||
/// A click on a frame while minimized selects it and shows the right panel (the canvas gets
|
||||
/// narrower): the frame stays where it was, and no step is added to the history.
|
||||
#[test]
|
||||
fn a_click_that_shows_the_right_panel_does_not_move_the_frame() {
|
||||
let (mut u, ids) = minimized_row();
|
||||
assert!(u.app.ui.right_panel_rect.is_none(), "nothing selected: no right panel");
|
||||
let wide = u.app.canvas.rect.width();
|
||||
let steps = u.app.session.ds().unwrap().history.undo.len();
|
||||
let p = u.app.to_screen(1880.0 + 200.0, 300.0);
|
||||
u.frame(vec![Event::PointerMoved(p)]);
|
||||
u.frame(vec![Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed: true, modifiers: Default::default() }]);
|
||||
u.frame(vec![]);
|
||||
u.frame(vec![]);
|
||||
u.frame(vec![Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed: false, modifiers: Default::default() }]);
|
||||
for _ in 0..4 {
|
||||
u.frame(vec![]);
|
||||
}
|
||||
assert_eq!(u.app.session.selection(), vec![ids[4]], "the click selects the frame");
|
||||
assert!(u.app.ui.right_panel_rect.is_some(), "the right panel shows");
|
||||
assert!(u.app.canvas.rect.width() < wide - 100.0, "the canvas is narrower");
|
||||
assert_eq!(frame_x(&u, ids[4]), 1880.0, "a click did not move the frame");
|
||||
assert_eq!(u.app.session.ds().unwrap().history.undo.len(), steps, "no history step");
|
||||
}
|
||||
|
||||
/// A real drag still moves the frame, by the pointer's screen movement over the zoom, in one step.
|
||||
#[test]
|
||||
fn a_drag_after_the_right_panel_shows_moves_the_frame_by_the_pointer() {
|
||||
let (mut u, ids) = minimized_row();
|
||||
let steps = u.app.session.ds().unwrap().history.undo.len();
|
||||
let p = u.app.to_screen(1880.0 + 200.0, 300.0);
|
||||
u.frame(vec![Event::PointerMoved(p)]);
|
||||
u.frame(vec![Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed: true, modifiers: Default::default() }]);
|
||||
u.frame(vec![]);
|
||||
u.frame(vec![Event::PointerMoved(p + vec2(50.0, 0.0))]);
|
||||
u.frame(vec![Event::PointerMoved(p + vec2(100.0, 0.0))]);
|
||||
u.frame(vec![Event::PointerButton {
|
||||
pos: p + vec2(100.0, 0.0),
|
||||
button: egui::PointerButton::Primary,
|
||||
pressed: false,
|
||||
modifiers: Default::default(),
|
||||
}]);
|
||||
for _ in 0..3 {
|
||||
u.frame(vec![]);
|
||||
}
|
||||
assert!(u.app.ui.right_panel_rect.is_some(), "the right panel shows");
|
||||
let moved = frame_x(&u, ids[4]) - 1880.0;
|
||||
assert!((moved - 100.0 / 0.46).abs() < 6.0, "moved by 100 points over the zoom, got {moved}");
|
||||
assert_eq!(u.app.session.ds().unwrap().history.undo.len(), steps + 1, "one step");
|
||||
}
|
||||
|
||||
/// Clicking empty canvas deselects and hides the right panel again; the marquee that starts there
|
||||
/// has no offset: nothing moves and nothing is selected.
|
||||
#[test]
|
||||
fn a_click_that_hides_the_right_panel_does_not_move_the_frame() {
|
||||
let (mut u, ids) = minimized_row();
|
||||
u.app.run("select.set", json!({"ids": [ids[4]]}));
|
||||
for _ in 0..3 {
|
||||
u.frame(vec![]);
|
||||
}
|
||||
assert!(u.app.ui.right_panel_rect.is_some());
|
||||
let p = u.app.to_screen(1880.0 + 200.0, 1000.0);
|
||||
let steps = u.app.session.ds().unwrap().history.undo.len();
|
||||
u.frame(vec![Event::PointerMoved(p)]);
|
||||
u.frame(vec![Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed: true, modifiers: Default::default() }]);
|
||||
u.frame(vec![]);
|
||||
u.frame(vec![Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed: false, modifiers: Default::default() }]);
|
||||
for _ in 0..3 {
|
||||
u.frame(vec![]);
|
||||
}
|
||||
assert!(u.app.ui.right_panel_rect.is_none(), "deselected: no right panel");
|
||||
assert_eq!(frame_x(&u, ids[4]), 1880.0);
|
||||
assert_eq!(u.app.session.ds().unwrap().history.undo.len(), steps);
|
||||
}
|
||||
|
||||
/// The left sidebar and rail appearing while a press is held (Minimize toggled off mid-press)
|
||||
/// moves the canvas's left edge: a still pointer keeps its page point, so the release moves nothing.
|
||||
#[test]
|
||||
fn a_held_press_keeps_its_page_point_when_the_canvas_moves() {
|
||||
let (mut u, ids) = minimized_row();
|
||||
u.app.run("select.set", json!({"ids": [ids[3]]}));
|
||||
for _ in 0..3 {
|
||||
u.frame(vec![]);
|
||||
}
|
||||
let steps = u.app.session.ds().unwrap().history.undo.len();
|
||||
let left = u.app.canvas.rect.min.x;
|
||||
let p = u.app.to_screen(1880.0 + 200.0, 300.0);
|
||||
u.frame(vec![Event::PointerMoved(p)]);
|
||||
u.frame(vec![Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed: true, modifiers: Default::default() }]);
|
||||
u.frame(vec![]);
|
||||
let before = u.app.to_page(p);
|
||||
u.app.run("view.minimize", json!({"on": false}));
|
||||
u.frame(vec![]);
|
||||
u.frame(vec![]);
|
||||
assert!(u.app.canvas.rect.min.x > left + 100.0, "the left sidebar and rail moved the canvas's edge");
|
||||
let after = u.app.to_page(p);
|
||||
assert!((after.x - before.x).abs() < 0.5 && (after.y - before.y).abs() < 0.5, "the pointer's page point moved: {before:?} to {after:?}");
|
||||
u.frame(vec![Event::PointerButton { pos: p, button: egui::PointerButton::Primary, pressed: false, modifiers: Default::default() }]);
|
||||
for _ in 0..3 {
|
||||
u.frame(vec![]);
|
||||
}
|
||||
assert_eq!(frame_x(&u, ids[4]), 1880.0, "the frame did not move");
|
||||
assert_eq!(frame_x(&u, ids[3]), 1410.0);
|
||||
assert_eq!(u.app.session.ds().unwrap().history.undo.len(), steps, "no history step");
|
||||
}
|
||||
Reference in new issue
Block a user