From 91509f1776fdc36286f271af6a44ef5070c630e6 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Tue, 10 Nov 2026 16:26:14 -0800 Subject: [PATCH] Guides: an option makes Cmd/Ctrl ruler drags artboard guides Preferences > Guides & Grid gets Artboard Guides with Cmd/Ctrl + Ruler Drag (cmdRulerDragArtboardGuides), off by default. With it on, Cmd/Ctrl held when a drag leaves a ruler makes a guide of the active artboard, with any tool but the Artboard tool; with the Artboard tool it makes a canvas guide instead. The key is read when the drag starts, so letting it go during the drag keeps the kind of guide. Such a guide shows and is made only where it lies within the active artboard; released elsewhere, it makes no guide and no undo step. A ruler click still makes no guide. This is VectorCraft's own option. The documented way to limit a guide to one artboard, dragging it out with the Artboard tool, is unchanged. Docs: docs/mcp.md, docs/target-app-parity.md. --- crates/engine/src/cmd/prefscmds.rs | 1 + crates/engine/src/prefs.rs | 4 ++ crates/engine/src/tests/menucmds.rs | 71 ++++++++++++++++++++++++++++ crates/engine/src/tooling.rs | 19 ++++++-- crates/tools/src/rulerguide.rs | 31 ++++++++++-- crates/tools/src/rulerguide/tests.rs | 30 ++++++++++++ crates/ui-egui/src/canvas/tests.rs | 46 ++++++++++++++++++ crates/ui-egui/src/i18n/bg.tsv | 1 + crates/ui-egui/src/i18n/bs.tsv | 1 + crates/ui-egui/src/i18n/ca.tsv | 1 + crates/ui-egui/src/i18n/cs.tsv | 1 + crates/ui-egui/src/i18n/da.tsv | 1 + crates/ui-egui/src/i18n/de.tsv | 1 + crates/ui-egui/src/i18n/el.tsv | 1 + crates/ui-egui/src/i18n/es.tsv | 1 + crates/ui-egui/src/i18n/fi.tsv | 1 + crates/ui-egui/src/i18n/fr.tsv | 1 + crates/ui-egui/src/i18n/hi.tsv | 1 + crates/ui-egui/src/i18n/hu.tsv | 1 + crates/ui-egui/src/i18n/id.tsv | 1 + crates/ui-egui/src/i18n/it.tsv | 1 + crates/ui-egui/src/i18n/ja.tsv | 1 + crates/ui-egui/src/i18n/ka.tsv | 1 + crates/ui-egui/src/i18n/ko.tsv | 1 + crates/ui-egui/src/i18n/nl.tsv | 1 + crates/ui-egui/src/i18n/pl.tsv | 1 + crates/ui-egui/src/i18n/pt-br.tsv | 1 + crates/ui-egui/src/i18n/ru.tsv | 1 + crates/ui-egui/src/i18n/sv.tsv | 1 + crates/ui-egui/src/i18n/tr.tsv | 1 + crates/ui-egui/src/i18n/uk.tsv | 1 + crates/ui-egui/src/i18n/vi.tsv | 1 + crates/ui-egui/src/i18n/zh-hans.tsv | 1 + crates/ui-egui/src/i18n/zh-hant.tsv | 1 + docs/mcp.md | 6 +++ docs/target-app-parity.md | 1 + 36 files changed, 229 insertions(+), 7 deletions(-) diff --git a/crates/engine/src/cmd/prefscmds.rs b/crates/engine/src/cmd/prefscmds.rs index ef8b156b6..39ce60426 100644 --- a/crates/engine/src/cmd/prefscmds.rs +++ b/crates/engine/src/cmd/prefscmds.rs @@ -229,6 +229,7 @@ pub const PREF_SPECS: &[PrefSpec] = &[ p!("guideColor", "Guides & Grid", "Guides", "Color", color), p!("guideStyle", "Guides & Grid", "Guides", "Style", choice(LINE_STYLE)), p!("lockGuidesByDefault", "Guides & Grid", "Guides", "Lock Guides by Default", bool), + p!("cmdRulerDragArtboardGuides", "Guides & Grid", "Guides", "Artboard Guides with Cmd/Ctrl + Ruler Drag", bool), p!("gridColor", "Guides & Grid", "Grid", "Color", color), p!("gridStyle", "Guides & Grid", "Grid", "Style", choice(LINE_STYLE)), p!("gridlineEvery", "Guides & Grid", "Grid", "Gridline every", len(1.0, 16383.0, General)), diff --git a/crates/engine/src/prefs.rs b/crates/engine/src/prefs.rs index 9ea86a21e..aa8b5c7f4 100644 --- a/crates/engine/src/prefs.rs +++ b/crates/engine/src/prefs.rs @@ -124,6 +124,9 @@ pub struct Prefs { pub guide_style: String, /// Lock Guides by Default: the app starts with View › Guides › Lock Guides on. pub lock_guides_by_default: bool, + /// Artboard Guides with Cmd/Ctrl + Ruler Drag: Cmd/Ctrl held when a guide is dragged out of a + /// ruler makes it a guide of the active artboard, or with the Artboard tool a canvas guide. + pub cmd_ruler_drag_artboard_guides: bool, pub grid_color: String, pub grid_style: String, pub gridline_every: f64, @@ -434,6 +437,7 @@ impl Default for Prefs { guide_color: s("#4affff"), guide_style: s("lines"), lock_guides_by_default: false, + cmd_ruler_drag_artboard_guides: false, grid_color: s("#c8c8c8"), grid_style: s("lines"), gridline_every: 72.0, diff --git a/crates/engine/src/tests/menucmds.rs b/crates/engine/src/tests/menucmds.rs index 763cf22ed..cf19f3cb1 100644 --- a/crates/engine/src/tests/menucmds.rs +++ b/crates/engine/src/tests/menucmds.rs @@ -4294,6 +4294,77 @@ fn guides_dragged_out_of_a_ruler_snap_and_take_the_artboard_tool_s_artboard() { ); } +/// With Preferences › Guides & Grid › Artboard Guides with Cmd/Ctrl + Ruler Drag on, Cmd/Ctrl held +/// when a guide is dragged out of a ruler makes a guide of the view's active artboard (one undo +/// step), shown and made only where it lies within that artboard; with the Artboard tool it makes +/// a canvas guide instead. The key counts on the drag's first event only. With the preference off, +/// Cmd/Ctrl changes nothing. +#[test] +fn cmd_ruler_drags_make_guides_of_the_active_artboard_when_the_preference_is_on() { + use vectorcraft_tools::{Mods, PointerEvent, PointerKind}; + let mut s = session(); + s.execute("artboard.new", &json!({"x": 900, "y": 0, "width": 400, "height": 300})).unwrap(); + let v = ViewInfo { artboard: 1, ..ViewInfo::default() }; + let cmd = Mods { cmd: true, ..Mods::default() }; + let ev = |kind, x, y, mods| PointerEvent::new(kind, x, y).with_mods(mods); + let owners = |s: &mut Session| -> Vec { + let list = s.execute("guide.list", &json!({})).unwrap(); + list.as_array().unwrap().iter().map(|g| g.get("artboard").cloned().unwrap_or(Value::Null)).collect() + }; + let drag = |s: &mut Session, view: ViewInfo, vertical: bool, at: f64, first: Mods| { + let (x, y) = if vertical { (at, 30.0) } else { (1000.0, at) }; + s.ruler_guide(vertical, &ev(PointerKind::Drag, x, y, first), true, view).unwrap(); + // Letting go of the key during the drag keeps the kind of guide. + s.ruler_guide(vertical, &ev(PointerKind::Up, x, y, Mods::default()), true, view).unwrap(); + }; + + // Off by default: a Cmd/Ctrl drag makes a canvas guide. + assert!(!s.prefs.cmd_ruler_drag_artboard_guides); + drag(&mut s, v, true, 1010.0, cmd); + assert_eq!(owners(&mut s), [Value::Null]); + + s.execute("prefs.set", &json!({"key": "cmdRulerDragArtboardGuides", "value": true})).unwrap(); + let n = undo_len(&s); + drag(&mut s, v, true, 1020.0, cmd); + drag(&mut s, v, false, 120.0, cmd); + assert_eq!(guides(&s)[1..], [(true, 1020.0), (false, 120.0)]); + assert_eq!(owners(&mut s)[1..], [json!(1), json!(1)]); + assert_eq!(undo_len(&s), n + 2); + s.execute("edit.undo", &json!({})).unwrap(); + s.execute("edit.redo", &json!({})).unwrap(); + assert_eq!(owners(&mut s)[1..], [json!(1), json!(1)], "redo keeps the artboard"); + + // Outside the active artboard (left of it, and below it): no preview, no guide, no undo step. + let n = undo_len(&s); + s.ruler_guide(true, &ev(PointerKind::Drag, 500.0, 30.0, cmd), true, v).unwrap(); + assert_eq!(guides(&s).len(), 3, "no preview"); + s.ruler_guide(true, &ev(PointerKind::Up, 500.0, 30.0, cmd), true, v).unwrap(); + drag(&mut s, v, false, 450.0, cmd); + assert_eq!((guides(&s).len(), undo_len(&s)), (3, n)); + // Dragged in and out again: the preview goes and nothing is made. + s.ruler_guide(true, &ev(PointerKind::Drag, 1100.0, 30.0, cmd), true, v).unwrap(); + assert_eq!(guides(&s).len(), 4, "the preview within the artboard"); + s.ruler_guide(true, &ev(PointerKind::Drag, 700.0, 30.0, Mods::default()), true, v).unwrap(); + s.ruler_guide(true, &ev(PointerKind::Up, 700.0, 30.0, Mods::default()), true, v).unwrap(); + assert_eq!((guides(&s).len(), undo_len(&s)), (3, n)); + + // An active artboard the document doesn't have: nothing is made. + drag(&mut s, ViewInfo { artboard: 7, ..v }, true, 1030.0, cmd); + assert_eq!((guides(&s).len(), undo_len(&s)), (3, n)); + + // Without the key: a canvas guide, as before. + drag(&mut s, v, true, 1040.0, Mods::default()); + assert_eq!(owners(&mut s)[3], Value::Null); + + // The Artboard tool: Cmd/Ctrl makes a canvas guide, and a plain drag an artboard guide. + s.select_tool("artboard", v).unwrap(); + s.set_tool_option("active", &json!(1)); + drag(&mut s, v, true, 400.0, cmd); + drag(&mut s, v, true, 1050.0, Mods::default()); + assert_eq!(guides(&s)[4..], [(true, 400.0), (true, 1050.0)]); + assert_eq!(owners(&mut s)[4..], [Value::Null, json!(1)]); +} + /// Guides saved before guides had layers are put on a layer when the document opens. #[test] fn guides_from_older_files_are_put_on_a_layer() { diff --git a/crates/engine/src/tooling.rs b/crates/engine/src/tooling.rs index 3f364c2ce..d65636b8e 100644 --- a/crates/engine/src/tooling.rs +++ b/crates/engine/src/tooling.rs @@ -709,15 +709,28 @@ impl Session { /// release), over the canvas or not (`on_canvas`). The guide shows where the pointer is over /// the canvas, snapped as a moved guide is, and is made where the button is released over it /// (one undo step); released anywhere else, none is. With the Artboard tool it is an artboard - /// guide of the active artboard. + /// guide of the active artboard. With Preferences › Guides & Grid › Artboard Guides with + /// Cmd/Ctrl + Ruler Drag on, `mods.cmd` on the drag's first event swaps the two: with another + /// tool the guide is one of the view's active artboard (`view.artboard`), shown and made only + /// where it lies within that artboard, and with the Artboard tool it is a canvas guide. pub fn ruler_guide(&mut self, vertical: bool, ev: &PointerEvent, on_canvas: bool, view: ViewInfo) -> Result<()> { + use vectorcraft_tools::rulerguide::NewGuide; let g = match self.ruler_guide.take() { Some(g) => Some(g), None => { let boards = self.active().map_or(0, |d| d.doc.artboards.len()); - let active = (self.tool.id() == "artboard").then(|| self.tool.options()["active"].as_u64()).flatten(); + let artboard_tool = self.tool.id() == "artboard"; + let swap = self.prefs.cmd_ruler_drag_artboard_guides && ev.mods.cmd; + let active = artboard_tool.then(|| self.tool.options()["active"].as_u64()).flatten(); let artboard = active.and_then(|i| usize::try_from(i).ok()).filter(|i| *i < boards); - self.with_tool_cx(view, |_, cx| Some(vectorcraft_tools::rulerguide::NewGuide::new(cx, vertical, artboard))) + let view_board = view.artboard; + self.with_tool_cx(view, |_, cx| { + Some(match (swap, artboard_tool) { + (false, _) => NewGuide::new(cx, vertical, artboard), + (true, true) => NewGuide::new(cx, vertical, None), + (true, false) => NewGuide::within(cx, vertical, view_board), + }) + }) } }; let Some(mut g) = g else { return Ok(()) }; diff --git a/crates/tools/src/rulerguide.rs b/crates/tools/src/rulerguide.rs index 87822f070..329776938 100644 --- a/crates/tools/src/rulerguide.rs +++ b/crates/tools/src/rulerguide.rs @@ -167,6 +167,8 @@ pub struct NewGuide { vertical: bool, /// The artboard (index) it belongs to: an artboard guide. artboard: Option, + /// Made only within its artboard ([`Self::within`]). + within: bool, snap: GuideSnap, /// The preview's orientation and position (none off the canvas), and the pointer. at: Option<(bool, f64)>, @@ -177,7 +179,24 @@ impl NewGuide { /// A vertical guide out of the left ruler, else a horizontal one out of the top ruler; an /// artboard guide of `artboard`, if any. pub fn new(cx: &ToolContext, vertical: bool, artboard: Option) -> Self { - Self { vertical, artboard, snap: GuideSnap::new(cx), at: None, pointer: Point::ZERO } + Self { vertical, artboard, within: false, snap: GuideSnap::new(cx), at: None, pointer: Point::ZERO } + } + + /// An artboard guide of `artboard` that shows, and is made, only where it lies within that + /// artboard: a vertical guide between its left and right edges, a horizontal one between its + /// top and bottom. With no such artboard, it never shows and nothing is made. + pub fn within(cx: &ToolContext, vertical: bool, artboard: usize) -> Self { + Self { within: true, ..Self::new(cx, vertical, Some(artboard)) } + } + + /// May the guide go at `at` (the x of a vertical guide, the y of a horizontal one)? + fn fits(&self, cx: &ToolContext, vertical: bool, at: f64) -> bool { + if !self.within { + return true; + } + let Some(r) = self.artboard.and_then(|i| cx.doc.artboards.get(i)).map(|a| a.rect) else { return false }; + let (lo, hi) = if vertical { (r.min_x(), r.max_x()) } else { (r.min_y(), r.max_y()) }; + lo <= at && at <= hi } /// The pointer at `ev`, over the canvas or not (`on_canvas`): the actions that show the guide @@ -185,9 +204,13 @@ impl NewGuide { pub fn pointer(&mut self, cx: &ToolContext, ev: &PointerEvent, on_canvas: bool) -> Vec { let mut out = vec![]; self.pointer = ev.pos; - if on_canvas { - let vertical = self.vertical != ev.mods.alt; - let at = self.snap.snap(cx, vertical, if vertical { ev.pos.x } else { ev.pos.y }, ev.mods.shift); + let place = on_canvas + .then(|| { + let vertical = self.vertical != ev.mods.alt; + (vertical, self.snap.snap(cx, vertical, if vertical { ev.pos.x } else { ev.pos.y }, ev.mods.shift)) + }) + .filter(|(vertical, at)| self.fits(cx, *vertical, *at)); + if let Some((vertical, at)) = place { if self.at.is_none() { out.push(Action::Begin("New Guide".into())); } diff --git a/crates/tools/src/rulerguide/tests.rs b/crates/tools/src/rulerguide/tests.rs index 7fa192d9e..a4a91d24c 100644 --- a/crates/tools/src/rulerguide/tests.rs +++ b/crates/tools/src/rulerguide/tests.rs @@ -148,6 +148,36 @@ fn a_new_guide_is_made_on_release_over_the_canvas_only() { assert_eq!(g.pointer(&c, &ev(PointerKind::Up, -5.0, 40.0, m), false), vec![Action::Cancel]); } +/// A guide made only within its artboard ([`NewGuide::within`]) shows and is made where its +/// position, on the axis Alt gives it, lies within the artboard (edges included). Outside it, or +/// with an artboard the document doesn't have, it goes as it does off the canvas. +#[test] +fn a_guide_within_its_artboard_is_made_only_inside_it() { + let (d, _) = doc_with_rect(); + let (s, p) = (Selection::default(), paint()); + let c = ToolContext { smart_guides: false, snap_to_point: false, ..cx(&d, &s, &p) }; + let m = Mods::default(); + let alt = Mods { alt: true, ..m }; + let preview = |vertical: bool, pos: f64| Action::Preview("guide.add".into(), json!({"vertical": vertical, "pos": pos, "artboard": 0})); + let mut g = NewGuide::within(&c, true, 0); + // Right of the 500 pt artboard: nothing, then its right edge. + assert_eq!(g.pointer(&c, &ev(PointerKind::Drag, 520.0, 40.0, m), true), vec![]); + assert_eq!(g.pointer(&c, &ev(PointerKind::Drag, 500.0, 40.0, m), true), vec![Action::Begin("New Guide".into()), preview(true, 500.0)]); + // Alt makes it horizontal: y 40 is inside, y 600 is not. + assert_eq!(g.pointer(&c, &ev(PointerKind::Drag, 700.0, 40.0, alt), true), vec![preview(false, 40.0)]); + assert_eq!(g.pointer(&c, &ev(PointerKind::Drag, 300.0, 600.0, alt), true), vec![Action::Cancel]); + assert!(g.overlays(&c).is_empty()); + assert_eq!(g.pointer(&c, &ev(PointerKind::Up, 300.0, 600.0, alt), true), vec![]); + // Released inside: made. + let mut g = NewGuide::within(&c, false, 0); + assert_eq!(g.pointer(&c, &ev(PointerKind::Drag, 900.0, 0.0, m), true), vec![Action::Begin("New Guide".into()), preview(false, 0.0)]); + assert_eq!(g.pointer(&c, &ev(PointerKind::Up, 900.0, 0.0, m), true), vec![Action::Commit]); + // No artboard 3: never shown. + let mut g = NewGuide::within(&c, true, 3); + assert_eq!(g.pointer(&c, &ev(PointerKind::Drag, 100.0, 40.0, m), true), vec![]); + assert_eq!(g.pointer(&c, &ev(PointerKind::Up, 100.0, 40.0, m), true), vec![]); +} + #[test] fn new_ruler_guide_alt_switches_axis_and_uses_the_release_position() { let (d, _) = doc_with_rect(); diff --git a/crates/ui-egui/src/canvas/tests.rs b/crates/ui-egui/src/canvas/tests.rs index a891179a6..52cd3904d 100644 --- a/crates/ui-egui/src/canvas/tests.rs +++ b/crates/ui-egui/src/canvas/tests.rs @@ -1412,6 +1412,52 @@ fn new_ruler_guide_alt_cancel_keeps_document_and_redo() { } } +/// With Preferences › Guides & Grid › Artboard Guides with Cmd/Ctrl + Ruler Drag on, Cmd/Ctrl held +/// when a drag leaves either ruler makes a guide of the active artboard, even when the key is let +/// go before the release; a guide outside the artboard is not made. With the preference off, the +/// same drag makes a canvas guide. +#[test] +fn cmd_ruler_drags_make_artboard_guides_with_the_preference_on() { + use egui::{Event, Modifiers}; + for source_vertical in [false, true] { + for on in [false, true] { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap(); + app.session.execute("prefs.set", &json!({"key": "cmdRulerDragArtboardGuides", "value": on})).unwrap(); + app.ui.view.rulers = true; + app.ui.view.smart_guides = false; + app.ui.view.snap_to_point = false; + app.select_tool("rectangle"); + let ctx = egui::Context::default(); + frame(&mut app, &ctx, vec![]); + let rect = app.canvas_rect.unwrap(); + let xf = Xf::new(rect, app.view().unwrap()); + let from = + if source_vertical { pos2(rect.left() - RULER / 2.0, rect.center().y) } else { pos2(rect.center().x, rect.top() - RULER / 2.0) }; + let drag = |app: &mut VectorcraftApp, to: Point| { + let at = xf.to_screen(to); + assert!(rect.contains(at)); + let button = |pos, pressed, modifiers| Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers }; + frame(app, &ctx, vec![Event::ModifiersChanged(Modifiers::COMMAND), Event::PointerMoved(from)]); + frame(app, &ctx, vec![button(from, true, Modifiers::COMMAND)]); + frame(app, &ctx, vec![Event::PointerMoved(at)]); + frame(app, &ctx, vec![Event::ModifiersChanged(Modifiers::NONE), Event::PointerMoved(at), button(at, false, Modifiers::NONE)]); + }; + let board = app.session.active().unwrap().doc.artboards[0].id; + drag(&mut app, Point::new(80.0, 120.0)); + let st = app.session.active().unwrap(); + assert_eq!(st.doc.guides.len(), 1, "vertical {source_vertical}, on {on}"); + assert_eq!(st.doc.guides[0].vertical, source_vertical); + assert_eq!(st.doc.guides[0].artboard, on.then_some(board), "vertical {source_vertical}, on {on}"); + // Beyond the artboard's right or bottom edge. + let beyond = xf.to_doc(rect.right_bottom() - egui::vec2(2.0, 2.0)); + assert!(beyond.x > 400.0 && beyond.y > 300.0, "{beyond:?}"); + drag(&mut app, beyond); + assert_eq!(app.session.active().unwrap().doc.guides.len(), if on { 1 } else { 2 }, "vertical {source_vertical}, on {on}"); + } + } +} + /// #451: a guide dragged out of a ruler lands on the art's side midpoints (halving an /// artboard-sized rectangle) and on the artboard's edges; an artboard guide is drawn across /// its artboard only. diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 354d811a3..cf7086c77 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -1826,6 +1826,7 @@ font-technology Variable Променливи Lock Grid Заключване на мрежата Lock Guides Заключване на водачите Lock Guides by Default Заключване на водачите по подразбиране + Artboard Guides with Cmd/Ctrl + Ruler Drag Водачи за работната област с Cmd/Ctrl + плъзгане от линийка Lock Others Заключване на другите Lock Slices Заключване на фрагментите Lock Station Point Заключване на базовата точка diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index d1fc0ea54..0f1509573 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -601,6 +601,7 @@ Show Guides Prikaži vodilice Lock Guides Zaključaj vodilice Lock Guides by Default Zaključaj vodilice po zadanom + Artboard Guides with Cmd/Ctrl + Ruler Drag Vodilice radne ploče uz Cmd/Ctrl + povlačenje s ravnala Unlock Guides Otključaj vodilice Make Guides Napravi vodilice Release Guides Oslobodi vodilice diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 9e77ecb32..4b3fb2ebe 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -2349,6 +2349,7 @@ digits Malayalam Malaiàlam Lock Grid Bloca la quadrícula Lock Guides Bloca les guies Lock Guides by Default Bloca les guies per defecte + Artboard Guides with Cmd/Ctrl + Ruler Drag Guies del tauler amb Cmd/Ctrl + arrossegament des de la regla Lock Others Bloca la resta Lock Selected Bloca la selecció Lock Slices Bloca els talls diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index de1723b90..9e397b224 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -610,6 +610,7 @@ Show Guides Zobrazit vodítka Lock Guides Zamknout vodítka Lock Guides by Default Ve výchozím stavu zamknout vodítka + Artboard Guides with Cmd/Ctrl + Ruler Drag Vodítka pracovní plochy tažením z pravítka s Cmd/Ctrl Unlock Guides Odemknout vodítka Make Guides Vytvořit vodítka Release Guides Uvolnit vodítka diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 77687493e..5ee90e6de 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -560,6 +560,7 @@ Show Guides Vis hjælpelinjer Lock Guides Lås hjælpelinjer Lock Guides by Default Lås hjælpelinjer som standard + Artboard Guides with Cmd/Ctrl + Ruler Drag Hjælpelinjer til kunstplade med Cmd/Ctrl + træk fra lineal Unlock Guides Lås hjælpelinjer op Make Guides Opret hjælpelinjer Release Guides Frigør hjælpelinjer diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 7e2619386..789de3bd0 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -1893,6 +1893,7 @@ file-filter Templates Vorlagen Lock Grid Raster sperren Lock Guides Hilfslinien sperren Lock Guides by Default Hilfslinien standardmäßig sperren + Artboard Guides with Cmd/Ctrl + Ruler Drag Zeichenflächen-Hilfslinien mit Cmd/Ctrl + Ziehen aus dem Lineal Lock Others Andere sperren Lock Slices Slices sperren Lock Station Point Standpunkt sperren diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index a95d84be3..0bc668831 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -1725,6 +1725,7 @@ font-technology Variable Μεταβλητές Lock Κλείδωμα Lock Guides Κλείδωμα οδηγών Lock Guides by Default Κλείδωμα οδηγών από προεπιλογή + Artboard Guides with Cmd/Ctrl + Ruler Drag Οδηγοί πίνακα σχεδίασης με Cmd/Ctrl + σύρσιμο από χάρακα Lock Slices Κλείδωμα τομών Low Χαμηλή Luminosity Φωτεινότητα diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index e0903d19d..c398fc1f1 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -1896,6 +1896,7 @@ font-technology Variable Variables Lock Grid Bloquear cuadrícula Lock Guides Bloquear guías Lock Guides by Default Bloquear guías de forma predeterminada + Artboard Guides with Cmd/Ctrl + Ruler Drag Guías de mesa de trabajo con Cmd/Ctrl + arrastre desde la regla Lock Others Bloquear otros Lock Slices Bloquear sectores Lock Station Point Bloquear punto de estación diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index b593200aa..1ca2c9a6d 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -601,6 +601,7 @@ Show Guides Näytä apuviivat Lock Guides Lukitse apuviivat Lock Guides by Default Lukitse apuviivat oletuksena + Artboard Guides with Cmd/Ctrl + Ruler Drag Piirtoalustan apuviivat Cmd/Ctrl + vetämällä viivaimesta Unlock Guides Avaa apuviivojen lukitus Make Guides Luo apuviivat Release Guides Vapauta apuviivat diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 6dd4e37a4..a31486e2d 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -1890,6 +1890,7 @@ font-technology Variable Variables Lock Grid Verrouiller la grille Lock Guides Verrouiller les repères Lock Guides by Default Verrouiller les repères par défaut + Artboard Guides with Cmd/Ctrl + Ruler Drag Repères de plan de travail avec Cmd/Ctrl + glissement depuis la règle Lock Others Verrouiller les autres Lock Slices Verrouiller les tranches Lock Station Point Verrouiller le point d’observation diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index 3397e7f34..fdafd7709 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -2566,6 +2566,7 @@ antiAlias Sharp शार्प Lock Grid लॉक ग्रिड Lock Guides गाइड्स लॉक करें Lock Guides by Default गाइड्स डिफ़ॉल्ट रूप से लॉक करें + Artboard Guides with Cmd/Ctrl + Ruler Drag Cmd/Ctrl + रूलर से ड्रैग करके आर्टबोर्ड गाइड्स Lock Others लॉक अन्य Lock Selected लॉक सिलेक्टेड Lock Slices स्लाइस लॉक करें diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 4b813215e..bf0e7cab9 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -2377,6 +2377,7 @@ file-filter Templates Sablonok Lock Grid Rács zárolása Lock Guides Segédvonalak zárolása Lock Guides by Default Segédvonalak zárolása alapértelmezetten + Artboard Guides with Cmd/Ctrl + Ruler Drag Rajztábla-segédvonalak Cmd/Ctrl + húzással a vonalzóból Lock Others Többi zárolása Lock Slices Szeletek zárolása Lock Station Point Nézőpont zárolása diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 33d229c85..037872963 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -560,6 +560,7 @@ Show Guides Tampilkan panduan Lock Guides Kunci panduan Lock Guides by Default Kunci panduan secara default + Artboard Guides with Cmd/Ctrl + Ruler Drag Panduan artboard dengan Cmd/Ctrl + seret dari penggaris Unlock Guides Buka kunci panduan Make Guides Buat panduan Release Guides Lepaskan panduan diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index b5fd752e9..b23b10bca 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -1925,6 +1925,7 @@ file-filter Templates Modelli Lock Grid Blocca griglia Lock Guides Blocca guide Lock Guides by Default Blocca guide per impostazione predefinita + Artboard Guides with Cmd/Ctrl + Ruler Drag Guide della tavola da disegno con Cmd/Ctrl + trascinamento dal righello Lock Others Blocca altri Lock Slices Blocca sezioni Lock Station Point Blocca punto di osservazione diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 56fc8a590..1d9e64e4d 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -628,6 +628,7 @@ digits Malayalam マラヤーラム数字 Hide Guides ガイドを隠す Lock Guides ガイドをロック Lock Guides by Default ガイドを初期設定でロック + Artboard Guides with Cmd/Ctrl + Ruler Drag Cmd/Ctrl + 定規からのドラッグでアートボードガイド Make Guides ガイドを作成 Release Guides ガイドを解除 Clear Guides ガイドを消去 diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 388dd4a01..e61045979 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -522,6 +522,7 @@ Show Guides დამხმარე ხაზების ჩვენება Lock Guides დამხმარე ხაზების ჩაკეტვა Lock Guides by Default დამხმარე ხაზების ნაგულისხმევად ჩაკეტვა + Artboard Guides with Cmd/Ctrl + Ruler Drag სამუშაო დაფის დამხმარე ხაზები Cmd/Ctrl + სახაზავიდან გადათრევით Unlock Guides დამხმარე ხაზების განბლოკვა Make Guides დამხმარე ხაზების შექმნა Release Guides დამხმარე ხაზების მოხსნა diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 03930932a..dfa7d1f78 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -623,6 +623,7 @@ digits Malayalam 말라얄람어 Hide Guides 안내선 숨기기 Lock Guides 안내선 잠그기 Lock Guides by Default 기본적으로 안내선 잠그기 + Artboard Guides with Cmd/Ctrl + Ruler Drag Cmd/Ctrl + 눈금자 드래그로 아트보드 안내선 Make Guides 안내선 만들기 Release Guides 안내선 해제 Clear Guides 안내선 지우기 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index c5151622d..dc8ea8380 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -574,6 +574,7 @@ Show Guides Hulplijnen tonen Lock Guides Hulplijnen vergrendelen Lock Guides by Default Hulplijnen standaard vergrendelen + Artboard Guides with Cmd/Ctrl + Ruler Drag Tekenbordhulplijnen met Cmd/Ctrl + slepen vanuit liniaal Unlock Guides Hulplijnen ontgrendelen Make Guides Hulplijnen maken Release Guides Hulplijnen vrijgeven diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 58016a4bd..f5847eed1 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -567,6 +567,7 @@ Show Guides Pokaż linie pomocnicze Lock Guides Zablokuj linie pomocnicze Lock Guides by Default Domyślnie blokuj linie pomocnicze + Artboard Guides with Cmd/Ctrl + Ruler Drag Linie pomocnicze obszaru kompozycji z Cmd/Ctrl + przeciąganiem z linijki Unlock Guides Odblokuj linie pomocnicze Make Guides Utwórz linie pomocnicze Release Guides Uwolnij linie pomocnicze diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index b0d940693..5a0d505a8 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -1617,6 +1617,7 @@ antiAlias Sharp Nítido Lock Grid Bloquear Grade Lock Guides Bloquear Guias Lock Guides by Default Bloquear Guias por Padrão + Artboard Guides with Cmd/Ctrl + Ruler Drag Guias de Prancheta com Cmd/Ctrl + Arrastar da Régua Lock Slices Bloquear Fatias Lock Station Point Bloquear Ponto de Estação Lossless (lossy WebP isn't available yet) Sem Perdas (WebP com perdas ainda não está disponível) diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index a00325e8b..c50bd209b 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -1922,6 +1922,7 @@ file-filter Templates Шаблоны Lock Grid Заблокировать сетку Lock Guides Заблокировать направляющие Lock Guides by Default Блокировать направляющие по умолчанию + Artboard Guides with Cmd/Ctrl + Ruler Drag Направляющие монтажной области с Cmd/Ctrl + перетаскиванием с линейки Lock Others Заблокировать остальные Lock Slices Заблокировать фрагменты Lock Station Point Заблокировать точку стояния diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index f2b24e7d0..441ce6e8f 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -1837,6 +1837,7 @@ font-technology Variable Variabla Lock Grid Lås rutnät Lock Guides Lås stödlinjer Lock Guides by Default Lås stödlinjer som standard + Artboard Guides with Cmd/Ctrl + Ruler Drag Stödlinjer för rityta med Cmd/Ctrl + dra från linjal Lock Others Lås övriga Lock Slices Lås skivor Lock Station Point Lås betraktarpunkt diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 76d73994b..2a9586531 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -1730,6 +1730,7 @@ font-technology Variable Değişken Lock Grid Izgarayı Kilitle Lock Guides Kilit Kılavuzları Lock Guides by Default Kılavuzları Varsayılan Olarak Kilitle + Artboard Guides with Cmd/Ctrl + Ruler Drag Cmd/Ctrl + Cetvelden Sürükleme ile Çalışma Yüzeyi Kılavuzları Lock Others Diğerlerini Kilitle Lock Slices Dilimleri Kilitle Lock Station Point Kilit İstasyon Noktası diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 4652ce4ca..8b0a1f98f 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -1919,6 +1919,7 @@ file-filter Templates Шаблони Lock Grid Заблокувати сітку Lock Guides Заблокувати напрямні Lock Guides by Default Блокувати напрямні за замовчуванням + Artboard Guides with Cmd/Ctrl + Ruler Drag Напрямні монтажної області з Cmd/Ctrl + перетягуванням з лінійки Lock Others Заблокувати решту Lock Slices Заблокувати фрагменти Lock Station Point Заблокувати точку стояння diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 4c9dac833..991339f31 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -2203,6 +2203,7 @@ digits Hindi Hindi Lock Grid Khóa lưới Lock Guides Khóa đường căn Lock Guides by Default Khóa đường căn theo mặc định + Artboard Guides with Cmd/Ctrl + Ruler Drag Đường căn bảng vẽ với Cmd/Ctrl + kéo từ thước Lock Others Khóa các mục khác Lock Slices Khóa lát cắt Lock Station Point Khóa điểm trạm diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index c62b17668..f7fbe5ce8 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -1806,6 +1806,7 @@ file-filter Templates 模板 Lock Grid 锁定网格 Lock Guides 锁定参考线 Lock Guides by Default 默认锁定参考线 + Artboard Guides with Cmd/Ctrl + Ruler Drag 按住 Cmd/Ctrl 从标尺拖动创建画板参考线 Lock Slices 锁定切片 Lock Station Point 锁定基准点 Lossless (lossy WebP isn't available yet) 无失真 (尚未提供有失真的 WebP) diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 0b3a21250..6bf1f24d0 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -2828,6 +2828,7 @@ file-filter Templates 範本 Lock Grid 鎖定格點 Lock Guides 鎖定參考線 Lock Guides by Default 預設鎖定參考線 + Artboard Guides with Cmd/Ctrl + Ruler Drag 按住 Cmd/Ctrl 從尺標拖曳建立工作區域參考線 Lock Others 鎖定其他 Lock Slices 鎖定切片 Lock Station Point 鎖定基準點 diff --git a/docs/mcp.md b/docs/mcp.md index bc2048393..2145eaff5 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -1977,6 +1977,12 @@ Guides off, View › Snap to Point pulls it onto anchors. In the desktop app a g same way and is made where it is released over the canvas (with the Artboard tool, as an artboard guide of the active artboard), a guide dropped off the canvas onto its ruler is deleted, and hidden guides (View › Guides › Hide Guides) can't be picked. An artboard guide is picked, drawn and snapped to across its artboard only. +Preferences › Guides & Grid › Artboard Guides with Cmd/Ctrl + Ruler Drag (`cmdRulerDragArtboardGuides`, off by +default) is VectorCraft's own option. With it on, Cmd/Ctrl held when a drag leaves a ruler makes the guide an artboard +guide of the active artboard, with any tool but the Artboard tool, and with the Artboard tool a canvas guide. The key +is read when the drag starts, so letting it go during the drag keeps the kind of guide. Such an artboard guide shows, +and is made, only where it lies within the active artboard: a vertical guide between its left and right edges, a +horizontal guide between its top and bottom. Released anywhere else, it makes no guide and no undo step. ```json {"name":"run_command","arguments":{"command":"guide.add","params":{"vertical":true,"pos":100}}} diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 6bf45e9c8..2e4256cb7 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -639,6 +639,7 @@ since 2026-10-01. Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a feature lands. - **New ruler guides (G1):** Alt swaps orientation while dragging from either ruler, following the [public shortcut reference](https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html). Preview, snapping and final placement use the current axis. Tool and egui event tests exercise both rulers, modifier changes, cancellation and one-step Undo/Redo; existing-guide Alt-copy and ruler-origin gestures are unchanged. +- **Artboard guides with Cmd/Ctrl + ruler drag:** VectorCraft's own option, Preferences › Guides & Grid › Artboard Guides with Cmd/Ctrl + Ruler Drag (`cmdRulerDragArtboardGuides`), off by default. With it on, Cmd/Ctrl held when a drag leaves a ruler makes a guide of the active artboard, and with the Artboard tool a canvas guide; the key is read when the drag starts. The guide is made only where it lies within the active artboard. The documented way to limit a guide to one artboard, dragging it out with the Artboard tool ([Align objects with guides](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/align-graphic-objects-with-guides.html)), is unchanged when the option is off. Tests `rulerguide::tests::a_guide_within_its_artboard_is_made_only_inside_it`, `tests::menucmds::cmd_ruler_drags_make_guides_of_the_active_artboard_when_the_preference_is_on` and `canvas::tests::cmd_ruler_drags_make_artboard_guides_with_the_preference_on`. - **Width-point finite-arithmetic follow-up (G1/M3):** Adjoining adjustments avoid intermediate overflow; non-finite normalized or resulting factors are rejected before committing. Engine regressions in `tests/widthpoints.rs` cover finite extremes, small nonzero results, atomic rejection, Undo/Redo and native save/reopen. Historical focused validation on the `a3357121` integration observed 2 expected RED failures with 11 passing controls, then all 13 tests passing with the repair; none were ignored. All three added regressions passed in current full CI. A full CI retry on the five-unit combined source at `a0671777` passed all seven stages with 6,613 test outcomes on 2026-10-14. An earlier full-CI run on the identical source panicked in the randomized `ai_mutated_save_never_panics` test; the cause remains unresolved. This five-fix batch does not claim to repair that failure. Scores and hours unchanged. - **Width-point creation (G1):** double-click a stroked segment with the Width tool to open Width Point Edit using sampled side widths. OK creates the point and optionally adjusts adjoining widths in one undo step. Cancel or Delete discards a prospective point without changing the document or redo history. Command and pointer tests cover profiles, compound paths, adjoining adjustment, existing-point editing and Undo/Redo. Message-catalog checks and full CI passed; the original capture verifies opening and Cancel, and the dialog layout was reviewed (2026-10-13: 28 focused Width tests, all seven cargo xtask ci stages, and a headless opening/Cancel capture with original PNG review). [Variable-width strokes](https://helpx.adobe.com/illustrator/using/stroke-object.html).