diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index d75eae537f..a401a04807 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -476,7 +476,7 @@ fn canvas_cursor(app: &mut VectorcraftApp, ui: &Ui, view_info: vectorcraft_engin return; } let m = ui.input(|i| i.modifiers); - let space = ui.input(|i| i.key_down(egui::Key::Space)); + let space = drag::space_lends_view_tools(app, m, ui.input(|i| i.key_down(egui::Key::Space))); let drag = ui.data(|d| d.get_temp::(drag_id())); let panning = matches!(drag, Some(Drag::Pan { .. })); let tool_holds = drag.is_some_and(drag::tool_holds); diff --git a/crates/ui-egui/src/canvas/drag.rs b/crates/ui-egui/src/canvas/drag.rs index 1a385d5642..6f45ee5d01 100644 --- a/crates/ui-egui/src/canvas/drag.rs +++ b/crates/ui-egui/src/canvas/drag.rs @@ -265,6 +265,12 @@ pub fn mods(m: egui::Modifiers, space: bool) -> Mods { Mods { shift: m.shift, alt: m.alt, cmd: m.command, ctrl: m.ctrl, space } } +/// Whether Space, held with the modifiers `m`, lends the Hand, Rotate View or Zoom tool. While the +/// Type tool edits text, Space types a space, and only Cmd+Space still lends the Zoom tool. +pub(super) fn space_lends_view_tools(app: &VectorcraftApp, m: egui::Modifiers, space: bool) -> bool { + space && (m.command || !app.session.tool_wants_text()) +} + /// Drop the tool press in progress, if any: nothing it began is kept, and the rest of the press /// does nothing ([`Drag::Gesture`]). pub(super) fn drop_press(app: &mut VectorcraftApp, ui: &Ui, view: vectorcraft_engine::ViewInfo) { diff --git a/crates/ui-egui/src/canvas/input.rs b/crates/ui-egui/src/canvas/input.rs index 5f06eb980d..28e74ffb4f 100644 --- a/crates/ui-egui/src/canvas/input.rs +++ b/crates/ui-egui/src/canvas/input.rs @@ -1,8 +1,8 @@ use super::artboards::{artboard_name_at, begin_artboard_name_edit, name_press_id, select_named_artboard}; use super::drag::{ FrameMove, HoverContext, apply_requests, backquote_at_release, backquote_seen, dispatch, drag_art_out, drag_id, drag_layer_id, drag_pos_id, - drop_press, mods, move_pose, on_canvas, pen_pose, pen_pressure, tool_drag_doc_id, tool_drag_mods_changed, tool_drag_moved, tool_frame_moves, - tool_holds, tool_press_doc_id, + drop_press, mods, move_pose, on_canvas, pen_pose, pen_pressure, space_lends_view_tools, tool_drag_doc_id, tool_drag_mods_changed, + tool_drag_moved, tool_frame_moves, tool_holds, tool_press_doc_id, }; use super::view::{ HOLD_DELAY, HOLD_ZOOM, SCRUB_SLOP, SCRUB_ZOOM, WheelTurn, animated_zoom, pan_pixels, rotate_about, scroll_view, touch_gestures, trackpad_turn, @@ -216,6 +216,7 @@ fn wheel_view( wheel(i, &app.session.prefs, wheel_zooms, animate, line, rect.height(), &mut turn), ) }); + let space = space_lends_view_tools(app, m, space); if turn != turn_before { ui.data_mut(|d| d.insert_temp(turn_id, turn)); } @@ -271,7 +272,8 @@ fn begin_drag(app: &mut VectorcraftApp, ui: &Ui, f: &Pointing, p: Pos2) { let Pointing { resp, rect, m, space, xf, v, view, .. } = *f; let tool = app.session.tool_id(); // Held, Space is the Hand tool for the moment, Space+Shift is the Rotate View tool and - // Cmd+Space is the Zoom tool (with Alt, zooming out), whatever the tool. + // Cmd+Space is the Zoom tool (with Alt, zooming out), whatever the tool. While the Type tool + // edits text, only Cmd+Space does ([`space_lends_view_tools`]). let zoom_mode = if space { m.command } else { tool == "zoom" }; let rotate_mode = if space { m.shift && !m.command } else { tool == "rotateView" }; let pan_mode = if space { !m.command && !m.shift } else { tool == "hand" }; diff --git a/crates/ui-egui/src/canvas/tests.rs b/crates/ui-egui/src/canvas/tests.rs index 6bf8a6fa97..49b6f883a3 100644 --- a/crates/ui-egui/src/canvas/tests.rs +++ b/crates/ui-egui/src/canvas/tests.rs @@ -7047,6 +7047,48 @@ fn cmd_space_zooms_and_space_pans_whatever_the_tool() { assert!((after.center.x - (before.center.x - 30.0 / before.zoom)).abs() < 1e-6, "{before:?} → {after:?}"); } +/// While the Type tool edits text, Space types a space: held, it shows no hand and a drag with it +/// does not pan the view. Cmd+Space still zooms. +#[test] +fn space_types_a_space_while_editing_text_and_does_not_pan() { + use egui::{CursorIcon, Event, Modifiers}; + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.session.prefs.placeholder_text = false; + let ctx = egui::Context::default(); + let id = start_typing(&mut app, &ctx); + let at = Xf::new(app.canvas_rect.unwrap(), app.view().unwrap()).to_screen(Point::new(100.0, 100.0)); + 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 }; + // The cursor the canvas sets over a window without the focus, as in + // `the_rotate_cursor_shows_for_space_shift_and_the_rotate_view_tool`. + let shown = |app: &mut VectorcraftApp| { + let screen_rect = Some(egui::Rect::from_min_size(Pos2::ZERO, vec2(800.0, 600.0))); + let mut out = ctx.run_ui(egui::RawInput { screen_rect, focused: false, ..Default::default() }, |ui| show(app, ui)); + out.textures_delta.clear(); + out.platform_output.cursor_icon + }; + typing_frame(&mut app, &ctx, vec![Event::Text("a".into())]); + typing_frame(&mut app, &ctx, vec![Event::PointerMoved(at), space(true, Modifiers::NONE), Event::Text(" ".into())]); + assert_eq!(plain(&app, id), "a ", "Space types a space"); + assert_ne!(shown(&mut app), CursorIcon::Grab, "held while editing, Space shows no hand"); + let before = *app.view().unwrap(); + typing_frame(&mut app, &ctx, vec![button(at, true, Modifiers::NONE)]); + typing_frame(&mut app, &ctx, vec![Event::PointerMoved(at + vec2(30.0, 0.0))]); + typing_frame(&mut app, &ctx, vec![button(at + vec2(30.0, 0.0), false, Modifiers::NONE)]); + let after = *app.view().unwrap(); + assert_eq!((after.center, after.zoom, after.rotation), (before.center, before.zoom, before.rotation), "a drag with Space held does not pan"); + assert!(app.session.tool_wants_text(), "editing goes on"); + typing_frame(&mut app, &ctx, vec![space(false, Modifiers::NONE)]); + // Cmd+Space still lends the Zoom tool. + typing_frame(&mut app, &ctx, vec![Event::ModifiersChanged(Modifiers::COMMAND), space(true, Modifiers::COMMAND), Event::PointerMoved(at)]); + assert_eq!(shown(&mut app), CursorIcon::ZoomIn, "Cmd+Space shows the magnifier"); + typing_frame(&mut app, &ctx, vec![button(at, true, Modifiers::COMMAND)]); + typing_frame(&mut app, &ctx, vec![button(at, false, Modifiers::COMMAND)]); + assert_eq!(app.view().unwrap().zoom, crate::state::next_zoom(before.zoom, true), "Cmd+Space click zooms in"); + typing_frame(&mut app, &ctx, vec![space(false, Modifiers::COMMAND), Event::ModifiersChanged(Modifiers::NONE)]); + assert_eq!(plain(&app, id), "a ", "the text is as typed"); +} + #[test] fn space_shift_drag_rotates_the_view_whatever_the_tool() { use egui::{Event, Modifiers}; diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 93b133902d..7b0b46c189 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -1092,6 +1092,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - **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. A single-key tool shortcut held for 0.25 s or more, or held while its tool is used, chooses that tool only until the key is let go, and the tool active before comes back (after a drag in progress ends); a quicker press chooses the tool for good. This is VectorCraft's own rule and its own threshold, and General › Hold a Tool Shortcut to Use the Tool Temporarily (`holdToolKeys`, on by default) turns it off. Space held while the Zoom tool drags a marquee moves the marquee and keeps its size ([Default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html)). When Space is let go, the drag sizes the marquee again from where it moved to. A Space+Shift drag rotates the view whatever the tool, as a Rotate View tool drag does, and Shift does not round the angle to 15°. + While the Type tool edits text, Space types a space: held, it does not show the hand, pan or rotate the view, and Cmd+Space still zooms ([the help page](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) lists the Spacebar as the Hand tool when not in text-edit mode). Test: `canvas::tests::space_types_a_space_while_editing_text_and_does_not_pan`. - **Swatches panel clicks:** Ctrl/Cmd-clicking New Swatch makes a spot color, and Ctrl/Cmd+Shift-clicking it makes a global process color. - **Panel drag and drop:** art dragged off the canvas onto the Symbols panel becomes a symbol, art dragged onto the Swatches panel becomes a pattern swatch, and art dropped on the Brushes panel opens New Brush for that art; a symbol dragged onto the canvas places an instance where it is dropped, and a brush dragged onto a path is applied to it. New Brush asks for the type of brush first, as the [Brushes](https://web.archive.org/web/20241230205225/https://helpx.adobe.com/illustrator/using/brushes.html) page (archived copy) describes. The Brushes panel's New Brush button, its menu's New Brush… and art dropped on the panel open the New Brush dialog, where Scatter Brush, Art Brush and Pattern Brush need selected or dropped art. OK opens that type's Brush Options for the new brush, and their OK makes the brush with the name and options set; Cancel in either dialog adds no brush (`ui.newBrush`). The menu's New Calligraphic Brush, New Bristle Brush and New Art, Scatter and Pattern Brush from Selection items still make a brush at once.