From f3205e73441ab5e1f39d7a6a7c9b975642ab054c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 23 Oct 2026 08:23:41 +0000 Subject: [PATCH 1/2] G1: Star tool shows inner and outer radii in measurement labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary While dragging with the Star tool, the measurement label lists the outer radius (R1) and inner radius (R2) from the centre to the pointer, matching the radii used for the live preview (including Ctrl/Cmd holding the inner radius and Alt straightening the points). ## Tests - `star_drag_measure_label_shows_outer_and_inner_radii` in `crates/tools/src/shape.rs`. ## Sources - https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/modify-live-shapes/change-inner-and-outer-radius-of-stars.html — R1 is the outer radius and R2 the inner radius of a star. - https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html — while dragging a star, Command/Ctrl keeps the inner radius constant as the outer radius follows the pointer. Part of gap G1 Co-authored-by: nrq6993-a11y --- crates/tools/src/lib.rs | 4 ++++ crates/tools/src/shape.rs | 47 ++++++++++++++++++++++++++++++++------- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs index 29b77f38e..a87eb2cb7 100644 --- a/crates/tools/src/lib.rs +++ b/crates/tools/src/lib.rs @@ -392,6 +392,10 @@ impl ToolContext<'_> { pub fn size_label(&self, w: f64, h: f64) -> String { format!("W: {}\nH: {}", self.len(w), self.len(h)) } + /// Star tool drag: outer (R1) and inner (R2) radii from the centre to the pointer. + pub fn star_radii_label(&self, outer: f64, inner: f64) -> String { + format!("R1: {}\nR2: {}", self.len(outer), self.len(inner)) + } /// A move label: `dX: …` over `dY: …`. pub fn offset_label(&self, dx: f64, dy: f64) -> String { format!("dX: {}\ndY: {}", self.len(dx), self.len(dy)) diff --git a/crates/tools/src/shape.rs b/crates/tools/src/shape.rs index 47ddf7a84..d0e7ad1a9 100644 --- a/crates/tools/src/shape.rs +++ b/crates/tools/src/shape.rs @@ -308,15 +308,24 @@ impl Tool for ShapeTool { cx.segment_measure_label(s, self.last) } else { // A box's size as drawn (from its centre with Alt, square with Shift). - let (w, h) = match self.id { - "polygon" | "star" => ((self.last - s).x.abs(), (self.last - s).y.abs()), - _ => { - // Along the Constrain Angle's axes, as the box is drawn (`command_at`). - let (r, _) = turned_box(s, self.last, self.mods, cx.constrain_angle); - (r.width(), r.height()) + match self.id { + "star" => { + let outer = (self.last - s).hypot().max(0.01); + let ratio = if self.mods.alt { straight_ratio(self.points).unwrap_or(self.star_ratio) } else { self.star_ratio }; + let inner = self.inner.unwrap_or(outer * ratio); + cx.star_radii_label(outer, inner) } - }; - cx.size_label(w, h) + _ => { + let (w, h) = match self.id { + "polygon" => ((self.last - s).x.abs(), (self.last - s).y.abs()), + _ => { + let (r, _) = turned_box(s, self.last, self.mods, cx.constrain_angle); + (r.width(), r.height()) + } + }; + cx.size_label(w, h) + } + } }; o.push(Overlay::Measure { p, text }); } @@ -396,6 +405,28 @@ mod tests { assert!(matches!(a.last(), Some(Action::Preview(_, v)) if v["cx"] == 60.0 && v["cy"] == 60.0), "{a:?}"); } + #[test] + fn star_drag_measure_label_shows_outer_and_inner_radii() { + let (d, _) = doc_with_rect(); + let s = Selection::default(); + let p = paint(); + let cx = ToolContext { measurement_labels: true, smart_guides: false, ..cx(&d, &s, &p) }; + let mut t = ShapeTool::new("star"); + t.star_ratio = 0.5; + t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 10.0, 10.0)); + t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 50.0, 10.0)); + let want = cx.star_radii_label(40.0, 20.0); + let label = t + .overlays(&cx) + .into_iter() + .find_map(|o| match o { + Overlay::Measure { text, .. } => Some(text), + _ => None, + }) + .unwrap_or_default(); + assert_eq!(label, want, "{label}"); + } + /// During a star drag, Ctrl/Cmd keeps the inner radius while the outer one follows the pointer /// (the new ratio stays once the key is let go), and Alt keeps the shoulders straight (#812). #[test] From 94832657d19ee9c6b3e156892cf18405a5f27ea3 Mon Sep 17 00:00:00 2001 From: nrq6993-a11y Date: Sun, 25 Oct 2026 22:54:52 +0300 Subject: [PATCH 2/2] Star label: R2 from the preview's ratio, not the held radius --- crates/tools/src/shape.rs | 48 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/crates/tools/src/shape.rs b/crates/tools/src/shape.rs index d0e7ad1a9..34553a034 100644 --- a/crates/tools/src/shape.rs +++ b/crates/tools/src/shape.rs @@ -311,9 +311,10 @@ impl Tool for ShapeTool { match self.id { "star" => { let outer = (self.last - s).hypot().max(0.01); + // The same ratio `command_at` draws with, so the label matches the preview + // (Ctrl/Cmd's held radius is already in `star_ratio`, clamped; Alt overrides it). let ratio = if self.mods.alt { straight_ratio(self.points).unwrap_or(self.star_ratio) } else { self.star_ratio }; - let inner = self.inner.unwrap_or(outer * ratio); - cx.star_radii_label(outer, inner) + cx.star_radii_label(outer, outer * ratio) } _ => { let (w, h) = match self.id { @@ -427,6 +428,49 @@ mod tests { assert_eq!(label, want, "{label}"); } + /// The star label shows the radii the preview draws, also with Ctrl/Cmd + Alt and when a + /// Ctrl/Cmd drag shrinks the outer radius below the held inner one (the ratio is clamped). + #[test] + fn star_measure_label_matches_the_preview_with_modifiers_and_shrink() { + let (d, _) = doc_with_rect(); + let s = Selection::default(); + let p = paint(); + let cx = ToolContext { measurement_labels: true, smart_guides: false, ..cx(&d, &s, &p) }; + let label = |t: &ShapeTool| { + t.overlays(&cx) + .into_iter() + .find_map(|o| match o { + Overlay::Measure { text, .. } => Some(text), + _ => None, + }) + .unwrap_or_default() + }; + let radii = |a: &[Action]| match a.last() { + Some(Action::Preview(_, v)) => (v["radius1"].as_f64().unwrap(), v["radius2"].as_f64().unwrap()), + other => panic!("{other:?}"), + }; + let cmd = Mods { cmd: true, ..Default::default() }; + let cmd_alt = Mods { cmd: true, alt: true, ..Default::default() }; + let mut t = ShapeTool::new("star"); + t.star_ratio = 0.5; + t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 10.0, 10.0)); + t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 50.0, 10.0)); + // Ctrl/Cmd holds the 20 inner radius while the outer one grows. + let a = t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 70.0, 10.0).with_mods(cmd)); + let (r1, r2) = radii(&a); + assert_eq!(label(&t), cx.star_radii_label(r1, r2)); + // Ctrl/Cmd + Alt: the preview straightens the shoulders, and so does the label. + let a = t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 70.0, 10.0).with_mods(cmd_alt)); + let (r1, r2) = radii(&a); + assert!((r2 - 20.0).abs() > 1e-6, "Alt should override the held radius: {r2}"); + assert_eq!(label(&t), cx.star_radii_label(r1, r2)); + // Shrinking below the held inner radius clamps the ratio; the label follows the preview. + let a = t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 20.0, 10.0).with_mods(cmd)); + let (r1, r2) = radii(&a); + assert!(r2 <= r1 + 1e-9, "{r1} {r2}"); + assert_eq!(label(&t), cx.star_radii_label(r1, r2)); + } + /// During a star drag, Ctrl/Cmd keeps the inner radius while the outer one follows the pointer /// (the new ratio stays once the key is let go), and Alt keeps the shoulders straight (#812). #[test]