Fix #277: Ctrl+Space zooms (Ctrl+Alt+Space out), Space alone pans
Held Space always panned, whatever else was held, so the temporary Zoom tool keys did nothing. Held Space is now the Hand tool and Cmd+Space the Zoom tool (Alt: out), whatever the active tool, for the press and the cursor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
47104f5f4c
commit
3c096da84d
|
|
@ -110,7 +110,7 @@ grade by behaviour against `plan/illustrator/`, not by whether a menu item exist
|
||||||
- **Live Corners:** drag a live rectangle's corner widgets (Selection or Direct Selection) to round all corners, with a radius readout.
|
- **Live Corners:** drag a live rectangle's corner widgets (Selection or Direct Selection) to round all corners, with a radius readout.
|
||||||
- **Graphs:** Column, Stacked Column, Bar, Stacked Bar, Line, Area, Scatter, Pie and Radar graph tools with Graph Data and Graph Type.
|
- **Graphs:** Column, Stacked Column, Bar, Stacked Bar, Line, Area, Scatter, Pie and Radar graph tools with Graph Data and Graph Type.
|
||||||
- **Other:** Eyedropper, Gradient annotator, Artboard, Measure, Type, Hand, Zoom, Rotate View.
|
- **Other:** Eyedropper, Gradient annotator, Artboard, Measure, Type, Hand, Zoom, Rotate View.
|
||||||
- **Temporary tools:** Cmd with any other tool drags with the selection tool used last (Selection, Direct Selection or Group Selection).
|
- **Temporary tools:** Cmd with any other tool drags with the selection tool used last (Selection, Direct Selection or Group Selection). Held Space pans and Cmd+Space zooms (Cmd+Alt+Space out) whatever the tool.
|
||||||
- **Tool double-clicks:** double-clicking a tool's button opens its options: Hand fits the artboard in the window, Zoom shows 100 %, Rotate, Scale, Reflect and Shear open their Object › Transform dialogs, Gradient, Eyedropper, Blend and the Liquify tools their panels or options.
|
- **Tool double-clicks:** double-clicking a tool's button opens its options: Hand fits the artboard in the window, Zoom shows 100 %, Rotate, Scale, Reflect and Shear open their Object › Transform dialogs, Gradient, Eyedropper, Blend and the Liquify tools their panels or options.
|
||||||
- **Drawing aids:** Smart Guides and snapping, guides dragged out of the rulers, and Draw Normal / Behind / Inside modes.
|
- **Drawing aids:** Smart Guides and snapping, guides dragged out of the rulers, and Draw Normal / Behind / Inside modes.
|
||||||
- **Views:** the status bar's artboard navigator (first, previous, next, last; `view.goToArtboard`) goes to an artboard and fits it, and Fit Artboard in Window and Actual Size show the navigator's artboard.
|
- **Views:** the status bar's artboard navigator (first, previous, next, last; `view.goToArtboard`) goes to an artboard and fits it, and Fit Artboard in Window and Actual Size show the navigator's artboard.
|
||||||
|
|
|
||||||
|
|
@ -359,12 +359,13 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||||
let m = ui.input(|i| i.modifiers);
|
let m = ui.input(|i| i.modifiers);
|
||||||
let space = ui.input(|i| i.key_down(egui::Key::Space));
|
let space = ui.input(|i| i.key_down(egui::Key::Space));
|
||||||
let panning = matches!(ui.data(|d| d.get_temp::<Drag>(drag_id())), Some(Drag::Pan { .. }));
|
let panning = matches!(ui.data(|d| d.get_temp::<Drag>(drag_id())), Some(Drag::Pan { .. }));
|
||||||
|
let zooming = if space { m.command } else { app.session.tool_id() == "zoom" };
|
||||||
let cur = if panning {
|
let cur = if panning {
|
||||||
egui::CursorIcon::Grabbing
|
egui::CursorIcon::Grabbing
|
||||||
|
} else if zooming {
|
||||||
|
if m.alt { egui::CursorIcon::ZoomOut } else { egui::CursorIcon::ZoomIn }
|
||||||
} else if space || app.session.tool_id() == "hand" {
|
} else if space || app.session.tool_id() == "hand" {
|
||||||
egui::CursorIcon::Grab
|
egui::CursorIcon::Grab
|
||||||
} else if app.session.tool_id() == "zoom" {
|
|
||||||
if m.alt { egui::CursorIcon::ZoomOut } else { egui::CursorIcon::ZoomIn }
|
|
||||||
} else if let Some(p) = app.hover_doc {
|
} else if let Some(p) = app.hover_doc {
|
||||||
let c = app.session.cursor(p, mods(m, space), view_info);
|
let c = app.session.cursor(p, mods(m, space), view_info);
|
||||||
let painter = ui.ctx().layer_painter(egui::LayerId::new(egui::Order::Tooltip, egui::Id::new("tool-cursor")));
|
let painter = ui.ctx().layer_painter(egui::LayerId::new(egui::Order::Tooltip, egui::Id::new("tool-cursor")));
|
||||||
|
|
@ -450,7 +451,10 @@ fn handle_input(app: &mut VectorcraftApp, ui: &Ui, resp: &egui::Response, rect:
|
||||||
}
|
}
|
||||||
|
|
||||||
let tool = app.session.tool_id();
|
let tool = app.session.tool_id();
|
||||||
let pan_mode = space || tool == "hand";
|
// Held, Space is the Hand tool for the moment, and Cmd+Space the Zoom tool (with Alt, zooming
|
||||||
|
// out), whatever the tool.
|
||||||
|
let zoom_mode = if space { m.command } else { tool == "zoom" };
|
||||||
|
let pan_mode = if space { !m.command } else { tool == "hand" };
|
||||||
let middle_pan = matches!(drag, Some(Drag::Pan { middle: true, .. }));
|
let middle_pan = matches!(drag, Some(Drag::Pan { middle: true, .. }));
|
||||||
// egui counts a press a few pixels outside the canvas as on it (its interaction radius): only a
|
// egui counts a press a few pixels outside the canvas as on it (its interaction radius): only a
|
||||||
// press on the canvas itself reaches the tools, else it would land at the canvas's centre.
|
// press on the canvas itself reaches the tools, else it would land at the canvas's centre.
|
||||||
|
|
@ -461,10 +465,10 @@ fn handle_input(app: &mut VectorcraftApp, ui: &Ui, resp: &egui::Response, rect:
|
||||||
{
|
{
|
||||||
ui.ctx().memory_mut(|mem| mem.stop_text_input());
|
ui.ctx().memory_mut(|mem| mem.stop_text_input());
|
||||||
app.ui.flyout = None;
|
app.ui.flyout = None;
|
||||||
let d = if pan_mode {
|
let d = if zoom_mode {
|
||||||
Drag::Pan { start: p, center: v.center, middle: false }
|
|
||||||
} else if tool == "zoom" {
|
|
||||||
Drag::ZoomBox { start: p }
|
Drag::ZoomBox { start: p }
|
||||||
|
} else if pan_mode {
|
||||||
|
Drag::Pan { start: p, center: v.center, middle: false }
|
||||||
} else if tool == "rotateView" {
|
} else if tool == "rotateView" {
|
||||||
let c = rect.center();
|
let c = rect.center();
|
||||||
Drag::RotateView { start_angle: (p.y - c.y).atan2(p.x - c.x) as f64, start_rot: v.rotation }
|
Drag::RotateView { start_angle: (p.y - c.y).atan2(p.x - c.x) as f64, start_rot: v.rotation }
|
||||||
|
|
@ -1599,6 +1603,41 @@ mod tests {
|
||||||
assert_eq!(cmd_drag(&mut app, inside, inside), "selection");
|
assert_eq!(cmd_drag(&mut app, inside, inside), "selection");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cmd_space_zooms_and_space_pans_whatever_the_tool() {
|
||||||
|
use egui::{Event, Modifiers};
|
||||||
|
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||||
|
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
|
||||||
|
app.select_tool("rectangle");
|
||||||
|
let ctx = egui::Context::default();
|
||||||
|
frame(&mut app, &ctx, vec![]);
|
||||||
|
let at = app.canvas_rect.unwrap().center();
|
||||||
|
let space = |pressed, modifiers| Event::Key { key: egui::Key::Space, physical_key: None, pressed, repeat: false, modifiers };
|
||||||
|
let button = |pos, pressed, modifiers| Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers };
|
||||||
|
// A drag from `at` to `to` with Space and `held` down.
|
||||||
|
let drag = |app: &mut VectorcraftApp, held: Modifiers, to: Pos2| {
|
||||||
|
frame(app, &ctx, vec![Event::ModifiersChanged(held), space(true, held), Event::PointerMoved(at)]);
|
||||||
|
frame(app, &ctx, vec![button(at, true, held)]);
|
||||||
|
frame(app, &ctx, vec![Event::PointerMoved(to)]);
|
||||||
|
frame(app, &ctx, vec![button(to, false, held)]);
|
||||||
|
frame(app, &ctx, vec![space(false, held), Event::ModifiersChanged(Modifiers::NONE)]);
|
||||||
|
};
|
||||||
|
let z0 = app.view().unwrap().zoom;
|
||||||
|
drag(&mut app, Modifiers::COMMAND, at);
|
||||||
|
let z1 = app.view().unwrap().zoom;
|
||||||
|
assert_eq!(z1, crate::state::next_zoom(z0, true), "Cmd+Space click zooms in");
|
||||||
|
drag(&mut app, Modifiers::COMMAND | Modifiers::ALT, at);
|
||||||
|
assert_eq!(app.view().unwrap().zoom, crate::state::next_zoom(z1, false), "Cmd+Alt+Space click zooms out");
|
||||||
|
assert_eq!(app.session.active().unwrap().doc.art_bounds(), None, "the Rectangle tool drew nothing");
|
||||||
|
// Space alone pans, with the Zoom tool too.
|
||||||
|
app.select_tool("zoom");
|
||||||
|
let before = *app.view().unwrap();
|
||||||
|
drag(&mut app, Modifiers::NONE, at + vec2(30.0, 0.0));
|
||||||
|
let after = *app.view().unwrap();
|
||||||
|
assert_eq!(after.zoom, before.zoom);
|
||||||
|
assert!((after.center.x - (before.center.x - 30.0 / before.zoom)).abs() < 1e-6, "{before:?} → {after:?}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn middle_drag_pans_the_view_with_any_tool() {
|
fn middle_drag_pans_the_view_with_any_tool() {
|
||||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue