mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
G1: switch new ruler-guide orientation with Alt
Co-authored-by: Codex <[email protected]>
This commit is contained in:
1 parent
0e6b2e331d
commit
ff6aaaaeea
6 files changed
+213
-11
No files matched your search
@@ -110,13 +110,15 @@ impl GuideSnap {
|
||||
/// A guide dragged out of a ruler (a vertical one out of the left ruler), with any tool: it shows
|
||||
/// where the pointer is, snapped as a moved guide is, and is made where the button is released
|
||||
/// over the canvas (`guide.add`, previewed as it goes); taken off the canvas, it goes again.
|
||||
/// Alt swaps its orientation while the guide is dragged out of the ruler.
|
||||
pub struct NewGuide {
|
||||
/// The source ruler's orientation, before Alt swaps it.
|
||||
vertical: bool,
|
||||
/// The artboard (index) it belongs to: an artboard guide.
|
||||
artboard: Option<usize>,
|
||||
snap: GuideSnap,
|
||||
/// Where it shows (none off the canvas), and the pointer.
|
||||
at: Option<f64>,
|
||||
/// The preview's orientation and position (none off the canvas), and the pointer.
|
||||
at: Option<(bool, f64)>,
|
||||
pointer: Point,
|
||||
}
|
||||
|
||||
@@ -133,18 +135,19 @@ impl NewGuide {
|
||||
let mut out = vec![];
|
||||
self.pointer = ev.pos;
|
||||
if on_canvas {
|
||||
let at = self.snap.snap(cx, self.vertical, if self.vertical { ev.pos.x } else { ev.pos.y }, ev.mods.shift);
|
||||
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);
|
||||
if self.at.is_none() {
|
||||
out.push(Action::Begin("New Guide".into()));
|
||||
}
|
||||
if self.at != Some(at) {
|
||||
let mut p = json!({ "vertical": self.vertical, "pos": at });
|
||||
if self.at != Some((vertical, at)) {
|
||||
let mut p = json!({ "vertical": vertical, "pos": at });
|
||||
if let Some(ab) = self.artboard {
|
||||
p["artboard"] = json!(ab);
|
||||
}
|
||||
out.push(Action::Preview("guide.add".into(), p));
|
||||
}
|
||||
self.at = Some(at);
|
||||
self.at = Some((vertical, at));
|
||||
} else if self.at.take().is_some() {
|
||||
self.snap.snapped.clear();
|
||||
out.push(Action::Cancel);
|
||||
@@ -157,7 +160,7 @@ impl NewGuide {
|
||||
|
||||
/// While the guide shows: the target it snapped to and its position by the pointer.
|
||||
pub fn overlays(&self, cx: &ToolContext) -> Vec<Overlay> {
|
||||
self.at.map(|at| self.snap.overlays(cx, self.vertical, at, self.pointer)).unwrap_or_default()
|
||||
self.at.map(|(vertical, at)| self.snap.overlays(cx, vertical, at, self.pointer)).unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,6 +430,75 @@ mod tests {
|
||||
assert_eq!(g.pointer(&c, &ev(PointerKind::Up, -5.0, 40.0, m), false), vec![Action::Cancel]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_ruler_guide_alt_switches_axis_and_uses_the_release_position() {
|
||||
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) };
|
||||
for source_vertical in [false, true] {
|
||||
let mut g = NewGuide::new(&c, source_vertical, Some(0));
|
||||
for (i, alt) in [false, true, false, true].into_iter().enumerate() {
|
||||
let vertical = source_vertical != alt;
|
||||
let at = if vertical { 80.0 } else { 120.0 };
|
||||
let mut expected = vec![];
|
||||
if i == 0 {
|
||||
expected.push(Action::Begin("New Guide".into()));
|
||||
}
|
||||
expected.push(Action::Preview("guide.add".into(), json!({"vertical": vertical, "pos": at, "artboard": 0})));
|
||||
assert_eq!(g.pointer(&c, &ev(PointerKind::Drag, 80.0, 120.0, Mods { alt, ..Default::default() }), true), expected);
|
||||
let label = format!("{}: {at:.2} pt", if vertical { "X" } else { "Y" });
|
||||
assert!(matches!(g.overlays(&c).last(), Some(Overlay::Measure { text, .. }) if text == &label));
|
||||
}
|
||||
// Releasing at a new position with Alt released uses both final coordinates and modifiers.
|
||||
let at = if source_vertical { 190.0 } else { 240.0 };
|
||||
assert_eq!(
|
||||
g.pointer(&c, &ev(PointerKind::Up, 190.0, 240.0, Mods::default()), true),
|
||||
vec![Action::Preview("guide.add".into(), json!({"vertical": source_vertical, "pos": at, "artboard": 0})), Action::Commit]
|
||||
);
|
||||
assert!(g.overlays(&c).is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_ruler_guide_alt_switches_even_when_coordinates_are_equal() {
|
||||
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) };
|
||||
for source_vertical in [false, true] {
|
||||
let mut g = NewGuide::new(&c, source_vertical, None);
|
||||
g.pointer(&c, &ev(PointerKind::Drag, 80.0, 80.0, Mods::default()), true);
|
||||
for alt in [true, false] {
|
||||
assert_eq!(
|
||||
g.pointer(&c, &ev(PointerKind::Drag, 80.0, 80.0, Mods { alt, ..Default::default() }), true),
|
||||
vec![Action::Preview("guide.add".into(), json!({"vertical": source_vertical != alt, "pos": 80.0}))]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_ruler_guide_alt_snaps_on_the_effective_axis() {
|
||||
let (d, _) = doc_with_rect();
|
||||
let (s, p) = (Selection::default(), paint());
|
||||
let off = ToolContext { smart_guides: false, snap_to_point: false, ..cx(&d, &s, &p) };
|
||||
let grid = ToolContext { snap_to_grid: true, ..off };
|
||||
let pixel = ToolContext { snap_to_pixel: true, ..off };
|
||||
let smart = cx(&d, &s, &p);
|
||||
for (c, shift, x, y, expected_x, expected_y) in [
|
||||
(&off, true, 223.0, 237.0, 225.0, 235.0),
|
||||
(&grid, false, 226.0, 237.0, 225.0, 234.0),
|
||||
(&pixel, false, 226.4, 237.6, 226.0, 238.0),
|
||||
(&smart, false, 203.0, 102.0, 200.0, 100.0),
|
||||
] {
|
||||
for source_vertical in [false, true] {
|
||||
let mut g = NewGuide::new(c, source_vertical, None);
|
||||
let actions = g.pointer(c, &ev(PointerKind::Drag, x, y, Mods { alt: true, shift, ..Default::default() }), true);
|
||||
let expected = if source_vertical { expected_y } else { expected_x };
|
||||
assert_eq!(actions.last(), Some(&Action::Preview("guide.add".into(), json!({"vertical": !source_vertical, "pos": expected}))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// With Smart Guides off, Snap to Point pulls a dragged guide onto the anchors (within its
|
||||
/// distance), not the edges' midpoints.
|
||||
#[test]
|
||||
|
||||
@@ -2558,6 +2558,130 @@ mod tests {
|
||||
assert_eq!(app.session.active().unwrap().doc.art_bounds(), None, "the tool drew nothing");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_ruler_guides_follow_alt_through_preview_release_and_undo() {
|
||||
use egui::{Event, Modifiers};
|
||||
for source_vertical in [false, true] {
|
||||
for initially_alt 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("document.setRulerOrigin", &json!({"x": 17, "y": 23})).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 at = xf.to_screen(Point::new(80.0, 120.0));
|
||||
assert!(rect.contains(at));
|
||||
let held = |alt| if alt { Modifiers::ALT } else { Modifiers::NONE };
|
||||
let button = |pos, pressed, alt| Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: held(alt) };
|
||||
let before = app.session.active().unwrap().doc.clone();
|
||||
let undo = app.session.active().unwrap().history.undo.len();
|
||||
frame(&mut app, &ctx, vec![Event::ModifiersChanged(held(initially_alt)), Event::PointerMoved(from)]);
|
||||
frame(&mut app, &ctx, vec![button(from, true, initially_alt)]);
|
||||
frame(&mut app, &ctx, vec![Event::PointerMoved(at)]);
|
||||
let assert_guide = |app: &VectorcraftApp, alt: bool, pos: Point| {
|
||||
let guides = &app.session.active().unwrap().doc.guides;
|
||||
assert_eq!(guides.len(), 1);
|
||||
assert_eq!(guides[0].vertical, source_vertical != alt);
|
||||
let expected = if source_vertical != alt { pos.x } else { pos.y };
|
||||
assert!((guides[0].pos - expected).abs() < 1e-4, "{:?}, expected {expected}", guides[0]);
|
||||
assert_eq!(guides[0].artboard, None);
|
||||
};
|
||||
assert_guide(&app, initially_alt, Point::new(80.0, 120.0));
|
||||
// Modifier changes with no pointer movement refresh the existing preview.
|
||||
for alt in [!initially_alt, initially_alt] {
|
||||
frame(&mut app, &ctx, vec![Event::ModifiersChanged(held(alt))]);
|
||||
assert_guide(&app, alt, Point::new(80.0, 120.0));
|
||||
assert_eq!(app.session.active().unwrap().history.undo.len(), undo);
|
||||
}
|
||||
// Equal coordinates must not hide an orientation change in the preview.
|
||||
let equal = xf.to_screen(Point::new(120.0, 120.0));
|
||||
frame(&mut app, &ctx, vec![Event::PointerMoved(equal)]);
|
||||
frame(&mut app, &ctx, vec![Event::ModifiersChanged(held(!initially_alt))]);
|
||||
assert_guide(&app, !initially_alt, Point::new(120.0, 120.0));
|
||||
let release = Point::new(147.0, 183.0);
|
||||
let to = xf.to_screen(release);
|
||||
frame(&mut app, &ctx, vec![Event::ModifiersChanged(Modifiers::ALT), Event::PointerMoved(to), button(to, false, true)]);
|
||||
assert_guide(&app, true, release);
|
||||
let st = app.session.active().unwrap();
|
||||
assert_eq!(st.history.undo.len(), undo + 1);
|
||||
assert_eq!(st.history.undo.last().unwrap().label, "New Guide");
|
||||
assert_eq!(st.doc.ruler_origin, before.ruler_origin);
|
||||
assert_eq!(st.doc.art_bounds(), before.art_bounds(), "the rectangle tool never drew");
|
||||
let committed = st.doc.clone();
|
||||
// Canvas guides still span the viewport after changing orientation.
|
||||
let painted = shapes(&mut app, &ctx);
|
||||
let window = app.canvas_rect.unwrap();
|
||||
let position = Xf::new(window, app.view().unwrap()).to_screen(release);
|
||||
let ends = if source_vertical {
|
||||
[pos2(window.left(), position.y), pos2(window.right(), position.y)]
|
||||
} else {
|
||||
[pos2(position.x, window.top()), pos2(position.x, window.bottom())]
|
||||
};
|
||||
assert!(painted.iter().any(|s| matches!(s, Shape::LineSegment { points, .. }
|
||||
if (points[0] - ends[0]).length() < 0.01 && (points[1] - ends[1]).length() < 0.01)));
|
||||
app.session.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(&*app.session.active().unwrap().doc, &*before);
|
||||
app.session.execute("edit.redo", &json!({})).unwrap();
|
||||
assert_eq!(&*app.session.active().unwrap().doc, &*committed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_ruler_guide_alt_cancel_keeps_document_and_redo() {
|
||||
use egui::{Event, Modifiers};
|
||||
for source_vertical in [false, true] {
|
||||
for tool in ["rectangle", "artboard"] {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
|
||||
app.session.execute("document.setRulerOrigin", &json!({"x": 17, "y": 23})).unwrap();
|
||||
app.session.execute("shape.rectangle", &json!({"x": 40, "y": 50, "width": 20, "height": 30})).unwrap();
|
||||
app.session.execute("edit.undo", &json!({})).unwrap();
|
||||
app.ui.view.rulers = true;
|
||||
app.ui.view.smart_guides = false;
|
||||
app.ui.view.snap_to_point = false;
|
||||
app.select_tool(tool);
|
||||
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 at = xf.to_screen(Point::new(80.0, 120.0));
|
||||
let button = |pos, pressed| Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: Modifiers::ALT };
|
||||
let before = app.session.active().unwrap().doc.clone();
|
||||
let history = |app: &VectorcraftApp| {
|
||||
let st = app.session.active().unwrap();
|
||||
(st.history.undo.len(), st.history.redo.len(), st.is_dirty())
|
||||
};
|
||||
let before_history = history(&app);
|
||||
assert_eq!(before_history.1, 1);
|
||||
frame(&mut app, &ctx, vec![Event::ModifiersChanged(Modifiers::ALT), Event::PointerMoved(from)]);
|
||||
frame(&mut app, &ctx, vec![button(from, true)]);
|
||||
frame(&mut app, &ctx, vec![Event::PointerMoved(at)]);
|
||||
let guides = &app.session.active().unwrap().doc.guides;
|
||||
assert_eq!(guides.len(), 1);
|
||||
assert_eq!(guides[0].vertical, !source_vertical);
|
||||
let artboard = (tool == "artboard").then_some(before.artboards[0].id);
|
||||
assert_eq!(guides[0].artboard, artboard);
|
||||
// Returning to the source ruler cancels the preview and keeps the existing Redo.
|
||||
frame(&mut app, &ctx, vec![Event::PointerMoved(from), button(from, false)]);
|
||||
assert_eq!(&*app.session.active().unwrap().doc, &*before);
|
||||
assert_eq!(history(&app), before_history);
|
||||
app.session.execute("edit.redo", &json!({})).unwrap();
|
||||
assert!(app.session.active().unwrap().doc.art_bounds().is_some());
|
||||
assert!(app.session.active().unwrap().doc.guides.is_empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// #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.
|
||||
|
||||
+2
-1
@@ -43,8 +43,9 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
|
||||
|
||||
## G1 Interaction fidelity
|
||||
|
||||
- **Done:** double-click a stroked segment to create a width point through Width Point Edit. Opening, Cancel and deleting the prospective point preserve the document and history; OK creates the point and optionally adjusts adjoining widths in one undo step. Regression and visual evidence: [Width-point creation](target-app-parity.md#evidence-what-ships-today-by-area).
|
||||
- **Added:** Alt swaps the orientation of a new guide dragged from either ruler. Preview and final placement use the current axis; cancelling preserves the document and Redo, and a completed drag is one undo step. Tool and egui event regressions cover these interactions; the broader G1 pass and estimate remain unchanged.
|
||||
|
||||
- **Done:** double-click a stroked segment to create a width point through Width Point Edit. Opening, Cancel and deleting the prospective point preserve the document and history; OK creates the point and optionally adjusts adjoining widths in one undo step. Regression and visual evidence: [Width-point creation](target-app-parity.md#evidence-what-ships-today-by-area).
|
||||
- **Done:** Shift-double-click opens Width Point Edit with adjoining adjustment enabled. Plain double-click retains the default; dispatch, neighboring widths, Undo/Redo and Cancel have regression coverage.
|
||||
- **Done (#1227):** object clicks with the Artboard tool create a fitted artboard, preserving art and selection; handles, modified clicks and drags retain their meaning. Covered by tool-event regression tests and undo/cancellation checks.
|
||||
- **Done:** Document Setup → Edit Artboards applies valid settings, closes the dialog and activates the Artboard tool. Pointer-click tests cover successful handoff, invalid settings, OK, Cancel and Undo in `dialogs/document_setup.rs`.
|
||||
|
||||
@@ -63,6 +63,7 @@ Checked 2026-10-10 from the code, tests and open issues; no workflow fails, so V
|
||||
| M12 | Advanced color & art (CMYK/ICC, separations, Gradient Mesh, Live Paint, Image Trace, Graphs) | 🟡 Gradient Mesh, Live Paint, Image Trace (15 presets; Ignore Color, Shapes, Auto Grouping and Gradients options; measured in `image-trace-quality.md`; the Flat Logo mode places edges to a fraction of a pixel, restores sharp corners, snaps lines and circles, and refuses gradients), Recolor Artwork, colour management (ICC, soft proofing, separations preview), Graphs (all 9 tools, Graph Data/Type, group-selected series colours persist through regenerate/save/open) done; Graph Design and Marker designs, Transpose and Switch x/y, `|` label line breaks, the pie options, drop shadows, the legend across the top, overlapping widths, First Row/Column in Front, filled lines, kept label formatting and the Graph Data worksheet done; graph Column designs pending | 6–10 |
|
||||
| M13 | Automation (Actions ✅ record/playback, persisted; variables, scripting, batch) | 🟡 | 18–27 |
|
||||
| M14 | 1.0 polish (preferences, shortcut editor, workspaces, accessibility, packaging for all OSes) | 🟡 Numeric-field pointer selection and focus regression-tested (#1326, in part; short movements still select all); RPM runtime capabilities and glibc floor checked (#1275; real-distro install/start-up pending), Eyedropper cursor with precise-setting checks (#1269), Preferences, shortcut editor (chords with Shift and a punctuation key fire as typed), workspaces, a custom title bar on Windows/Linux with a Home button, content-sized dialogs, menus that scroll when longer than the window and macOS native menu dismissal done (M14.8), and native macOS save sheets with a Format menu (#866); an experimental, unsupported Windows 7 x64 portable OpenGL build (not verified by maintainers); accessibility and broader Windows/Linux packaging/QA pending | 18–28 |
|
||||
| — | Interaction fidelity pass (every tool's modifiers, Properties panel per context, isolation, nuance) | ⬜ (new ruler-guide Alt orientation implemented; broader pass remains) | 60–90 |
|
||||
| — | Interaction fidelity pass (every tool's modifiers, Properties panel per context, isolation, nuance; width-point creation and adjoining adjustment regression-tested; dialog layout reviewed) | ⬜ | 60–90 |
|
||||
| — | Hardening at scale (big-file corpus, fuzzing, cross-platform + browser QA) | 🟡 | 50–80 |
|
||||
| | **Total to full parity** (one agent; from [target-app-parity.md](target-app-parity.md)) | | **~360–590** |
|
||||
@@ -80,6 +81,7 @@ After placed documents (File › Place of a `.vectorcraft` file, linked), M4.14
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-13 | minor | Interaction fidelity: Alt swaps a new ruler guide's orientation during creation; preview, snapping, release, cancellation and Undo/Redo have event regressions. Scores and hours unchanged (estimated). |
|
||||
| 2026-10-13 | minor | G1: create width points through Width Point Edit with optional adjoining adjustment; command/pointer/history regressions and full CI passed; dialog layout reviewed; scores and hours unchanged |
|
||||
| 2026-10-13 | minor | Corner Radius: a link toggle behind the field in the Properties and Transform panels; off, a live rectangle shows a radius field for each corner (Top Left, Top Right, Bottom Right, Bottom Left), each setting its own corner (`singleCornerRadius` preference); split from #1594; score unchanged (estimated) |
|
||||
| 2026-10-13 | minor | Control transport deadlines (#1726): idle and partial clients and blocked replies release connection slots; parity scores and estimates unchanged. |
|
||||
|
||||
@@ -82,7 +82,7 @@ dominate; 3D, graphs and automation are specialist). They sum to 105. Feature de
|
||||
| Image Trace, graphs, image tools | 3 | 75% | graph Column designs, Image Trace Create Strokes depth (Create Object Mosaic is done) | 5–8 | estimated (raised from 72%) |
|
||||
| Layers, artboards, document setup | 5 | 78% | 30.0's artboard locking and colours and managing artboards from the canvas, Layers panel row context menus and Collect for Export (click-to-fit objects is done, #1227) | 5–8 | estimated (lowered from 82%: target moved) |
|
||||
| View & navigation | 3 | 70% | New View/Edit Views, multiple windows and Arrange, Snap to Glyph | 9–14 | estimated |
|
||||
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | 30.0's tangent and perpendicular snapping and its snapping panel, "intersect" smart guides, global/artboard ruler mode switch and video rulers, distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved) |
|
||||
| Guides, grids, smart guides, snapping, rulers | 3 | 80% | 30.0's tangent and perpendicular snapping and its snapping panel, "intersect" smart guides, global/artboard ruler mode switch and video rulers, distance readouts while drawing | 4–7 | estimated (lowered from 85%: target moved); new ruler-guide Alt orientation implemented |
|
||||
| File formats | 6 | 75% | `.ai` files open with a flattened or wrong layer structure in real cases (#951), guides lost (#779), no writing of Illustrator's editing data (#868), FreeHand/CorelDRAW/DWG/HEIC/JPEG 2000 import, 3D export; see [file-format-parity.md](file-format-parity.md) | 10–20 | estimated (lowered from 85%: open issues); DXF control-point SPLINE import closure regression-tested (#1299 follow-up after #1320) |
|
||||
| Export for Screens, Asset Export, slices, Save for Web | 3 | 85% | anti-aliasing ghost borders (#983), TIFF in Export for Screens (30.2) | 2–4 | estimated |
|
||||
| Print, colour management, separations, flattener | 3 | 80% | print preview fidelity | 2–4 | estimated |
|
||||
@@ -373,8 +373,9 @@ since 2026-10-01.
|
||||
|
||||
Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a feature lands.
|
||||
|
||||
- **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).
|
||||
- **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.
|
||||
|
||||
- **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).
|
||||
- **Rulers (#1212):** drag the intersection to preview and set a document zero point, or double-click it to reset to the active artboard's top-left. Transform, Properties and Control X/Y fields measure from it and convert entered positions back to absolute command coordinates. The origin survives every native format version and compression; geometry, guides and patterns stay in place. Global/artboard mode switching and video rulers remain separate. Public reference: [Use rulers](https://helpx.adobe.com/ca/illustrator/desktop/measure-and-align/grids-and-guides/use-rulers.html).
|
||||
|
||||
- **Document Setup modal handoff:** Document Setup → Edit Artboards applies valid settings, closes the dialog and activates the Artboard tool. Pointer-click tests cover successful handoff, invalid settings, OK, Cancel and Undo in `dialogs/document_setup.rs`.
|
||||
@@ -548,6 +549,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-13 | minor | G1: Alt switches the orientation of new ruler guides, including preview, snapping and release; scores and hours unchanged (estimated). |
|
||||
| 2026-10-13 | minor | G1: create width points through Width Point Edit with optional adjoining adjustment; command/pointer/history regressions and full CI passed; dialog layout reviewed; scores and hours unchanged |
|
||||
| 2026-10-13 | minor | Corner Radius: a link toggle behind the field in the Properties and Transform panels; off, a live rectangle shows a radius field for each corner (Top Left, Top Right, Bottom Right, Bottom Left), each setting its own corner (`singleCornerRadius` preference); split from #1594; score unchanged (estimated) |
|
||||
| 2026-10-13 | minor | 3D and Materials: the documented light settings, Intensity, Rotation, Height, Softness and Ambient Light ([three-d.md](three-d.md#lighting)); scores unchanged (3D ~25%) |
|
||||
|
||||
+2
-1
@@ -100,7 +100,7 @@ tools, never checked side by side) isn't captured row by row.
|
||||
| Ruler origin: corner drag with preview, double-click reset to the active artboard, relative X/Y fields, native save/reopen | done | #1212; `document.setRulerOrigin`, `document.resetRulerOrigin`; geometry and command coordinates stay absolute |
|
||||
| Ruler guides as Layers rows: shown, hidden, locked and deleted with their layer; rows dragged to another layer | done | `guide.setLayer`; guides from older files go on a layer |
|
||||
| Objects made into guides: drawn in the guide colour and style of Preferences; Lock Guides locks them too (no select, move or delete); a dragged selection snaps to ruler guides with Smart Guides on | done | #1534 (#1543, #1583) |
|
||||
| Alt swaps a ruler guide's orientation while dragging | missing | |
|
||||
| Alt swaps a new ruler guide's orientation while dragging | done | Both rulers; preview, snapping and final placement follow the current modifier; existing-guide Alt-copy is unchanged. |
|
||||
| Global/artboard ruler mode switch and video rulers | missing | menu stubs; custom document origin is done (#1212) |
|
||||
|
||||
## Transform handles, bounding box, Free Transform
|
||||
@@ -203,6 +203,7 @@ re-measure of the panels added since.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-13 | minor | Alt during a drag from either ruler swaps the new guide's orientation ([public shortcut reference](https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html)); tool and egui event tests cover preview, snapping, release coordinates, cancellation and one-step Undo/Redo; scores unchanged (estimated). |
|
||||
| 2026-10-13 | minor | G1: create width points through Width Point Edit with optional adjoining adjustment; command/pointer/history regressions and full CI passed; dialog layout reviewed; scores and hours unchanged |
|
||||
| 2026-10-13 | minor | Corner Radius: a link toggle behind the field in the Properties and Transform panels; off, a live rectangle shows a radius field for each corner (Top Left, Top Right, Bottom Right, Bottom Left), each setting its own corner (`singleCornerRadius` preference); split from #1594; score unchanged (estimated) |
|
||||
| 2026-10-13 | minor | Join merges the selected anchor points that lie on one another, as after Average, into one anchor (#1582, fixes #1533); correction: "Join with corner/smooth choice" was listed as done, but `path.join` and `path.average` are separate commands and Join has no such choice, so Average and Join in one step with that choice is its own row, missing; the Smooth tool smooths only the stretch of path under the drag and a round corner next to a curved side curves smoothly instead of sticking out of the shape (#1547, fixes #1512); Lock Guides also locks the objects made into guides: they can't be selected, moved or deleted (#1543); they are drawn in the guide colour and style of Preferences, and a dragged selection snaps to ruler guides with Smart Guides on (#1583, fixes #1534); Edit › Keyboard Shortcuts lists Edit › Duplicate and the other commands that are in no menu but run without parameters (panel-menu commands such as Break Link to Symbol), so they can be given shortcuts (#1549, #1580, fixes #1539); Make Compound Path on a group (such as outlined type) keeps the paint of its backmost path instead of drawing nothing, and compound paths joined into one keep their holes (#1537); scores unchanged (estimated) |
|
||||
|
||||
Reference in new issue
Block a user