Browse / github.com/storytold/vectorcraft / patch
G1: Gradient tool stop nudges use Keyboard Increment along the gradient axis
G1: Gradient tool stop nudges use Keyboard Increment along the gradient axis ## Summary Arrow keys on the Gradient tool move the selected color stop along the gradient bar. Those steps now follow Preferences › General › Keyboard Increment (Shift ×10), measured along the gradient vector in document units, instead of a fixed 1% / 10% of the bar. ## Plan - Add a helper that converts keyboard increment + gradient length into a normalized offset step, with a safe fallback when the vector is degenerate. - Use it in `GradientTool::key` for Left/Right stop nudges. - Extend the existing arrow-key regression test and add a case with a non-default increment. ## Tests - `cargo t
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- nrq6993-a11y (imported from pull request #3202, open)
- Base
8bf090b25389b48aa6f9d7415de57c9ce7ccf8cb- Size
- +26 −3 in 1 file
- Review
- pending
- Shared
- 2026-10-28
- Id
7207b2db7bcf92bbba9af28e
How it went
No other operator has reported using it yet.
Diff
raw · crates/tools/src/xform/gradient.rs
@@ -12,7 +12,8 @@ //! one, drag one to move it (Alt drags a copy), drag it off the bar to delete it, and drag a //! diamond above the bar to move a midpoint. Double-clicking a stop opens its popover. The //! selected stop (`gradient.selectStop`) is shared with the Gradient and Color panels: Delete or-//! Backspace removes it (never below two stops) and the arrow keys nudge it.+//! Backspace removes it (never below two stops) and the arrow keys nudge it by the keyboard+//! increment along the gradient vector (Shift ×10). //! //! A radial gradient also shows its extent: a dashed ellipse around the centre (the start, a //! ring). Drag the dot on the ellipse across the bar to change the aspect ratio and the ellipse@@ -58,10 +59,20 @@ const CENTRE_HIT: f64 = 8.0; const OFF_BAR: f64 = 24.0; /// A press becomes a drag after this much movement. const DRAG_START: f64 = 3.0;-/// Arrow-key nudges of the selected stop (Shift: the big step).+/// Fallback offset step when the gradient vector has no length (1% / 10%). const NUDGE: f32 = 0.01; const NUDGE_BIG: f32 = 0.1; +/// How far a stop moves along the bar for one arrow-key press, as a fraction of the gradient length.+fn stop_nudge_step(cx: &ToolContext, geom: &GradientGeom, big: bool) -> f32 {+ let len = geom.end.distance(geom.start);+ if len < 1e-9 {+ return if big { NUDGE_BIG } else { NUDGE };+ }+ let k = cx.keyboard_increment * if big { 10.0 } else { 1.0 };+ (k / len) as f32+}+ /// The gradient annotator: the linear or radial gradient behind the active proxy of the first /// selected object that has one, placed in document coordinates. #[derive(Clone, Debug, PartialEq)]@@ -535,7 +546,7 @@ impl Tool for GradientTool { None => return vec![], }, _ => {- let step = if mods.shift { NUDGE_BIG } else { NUDGE };+ let step = stop_nudge_step(cx, &a.geom, mods.shift); move_stop(stops, i, stops[i].offset + if key == ToolKey::Left { -step } else { step }) } };@@ -836,6 +847,18 @@ mod tests { assert_eq!(a[1], GradientTool::select_stop(1)); } + #[test]+ fn gradient_stop_nudge_uses_keyboard_increment_along_the_bar() {+ let (d, s) = graded(two());+ let p = paint();+ let mut cx = cx(&d, &s, &p);+ cx.gradient_stop = Some(1);+ cx.keyboard_increment = 5.0;+ let mut t = GradientTool::default();+ let a = t.key(&cx, ToolKey::Left, Mods::default());+ assert_eq!(offsets(exec(&a[0]).1), vec![0.0, 95.0]);+ }+ #[test] fn a_click_inside_the_art_applies_the_gradient_from_there() { let (d, id) = doc_with_rect();