mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 18:10:01 -06:00
Merge pull request #6113 from ruchirguitar/fix/5949-hover-numeric-wheel
Numeric fields: step on wheel hover without taking focus
This commit is contained in:
@@ -202,7 +202,9 @@ fn wheel_view(
|
||||
) -> (egui::PointerState, egui::Modifiers, bool, Option<Pos2>) {
|
||||
let line = ui.ctx().options(|o| o.input_options.line_scroll_speed);
|
||||
let wheel_zooms = app.session.prefs.zoom_with_mouse_wheel;
|
||||
let animate = animated_zoom(&app.session.prefs);
|
||||
// A docked field may be drawn later and has not consumed its wheel event yet. Only canvas
|
||||
// hover may start a zoom glide; existing glides still decay outside the canvas in `wheel`.
|
||||
let animate = animated_zoom(&app.session.prefs) && resp.hovered();
|
||||
let turn_id = egui::Id::unique("canvas-wheel-turn");
|
||||
let turn_before: WheelTurn = ui.data(|d| d.get_temp(turn_id)).unwrap_or_default();
|
||||
let mut turn = turn_before;
|
||||
|
||||
@@ -4477,6 +4477,80 @@ fn zoom_with_mouse_wheel_preference() {
|
||||
assert!((zoom - 1.0).abs() < 1e-9 && scroll.x < -1.0 && scroll.y == 0.0, "on: Cmd-wheel scrolls sideways ({zoom}, {scroll:?})");
|
||||
}
|
||||
|
||||
/// A numeric panel drawn after the canvas owns its wheel turn even when the pointer enters the
|
||||
/// canvas on the next frame (#5949). Only a subsequent turn over the canvas starts a zoom glide.
|
||||
#[test]
|
||||
fn wheel_over_a_numeric_panel_field_does_not_start_a_canvas_zoom_glide() {
|
||||
use std::cell::Cell;
|
||||
for focused in [false, true] {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
app.run("file.new", json!({"width": 400, "height": 300})).unwrap();
|
||||
for key in ["zoomWithMouseWheel", "animatedZoom", "gpuPerformance"] {
|
||||
app.run("prefs.set", json!({"key": key, "value": true})).unwrap();
|
||||
}
|
||||
let ctx = egui::Context::default();
|
||||
crate::theme::install_fonts(&ctx);
|
||||
let value = Cell::new(10.0);
|
||||
let field_rect = Cell::new(egui::Rect::NOTHING);
|
||||
let field_id = Cell::new(egui::Id::NULL);
|
||||
let mut time = 0.0;
|
||||
let mut run = |app: &mut VectorcraftApp, events| {
|
||||
time += 0.016;
|
||||
let raw = egui::RawInput {
|
||||
time: Some(time),
|
||||
screen_rect: Some(egui::Rect::from_min_size(Pos2::ZERO, vec2(800.0, 600.0))),
|
||||
events,
|
||||
..Default::default()
|
||||
};
|
||||
let mut out = ctx.run_ui(raw, |ui| {
|
||||
// Match dock rendering order: canvas first, the numeric panel afterward.
|
||||
let mut canvas_ui = ui.new_child(egui::UiBuilder::new().max_rect(egui::Rect::from_min_size(Pos2::ZERO, vec2(550.0, 500.0))));
|
||||
show(app, &mut canvas_ui);
|
||||
let mut panel_ui = ui.new_child(egui::UiBuilder::new().max_rect(egui::Rect::from_min_size(pos2(600.0, 20.0), vec2(150.0, 100.0))));
|
||||
field_id.set(panel_ui.scope_id().with("number"));
|
||||
if let Some(v) = crate::widgets::plain_field(&mut panel_ui, "number", value.get(), "", 0, 80.0) {
|
||||
value.set(v);
|
||||
}
|
||||
field_rect.set(panel_ui.min_rect());
|
||||
});
|
||||
out.textures_delta.clear();
|
||||
};
|
||||
run(&mut app, vec![]);
|
||||
if focused {
|
||||
ctx.memory_mut(|m| m.request_focus(field_id.get()));
|
||||
}
|
||||
let over_field = field_rect.get().center();
|
||||
run(&mut app, vec![egui::Event::PointerMoved(over_field)]);
|
||||
let before = *app.view().unwrap();
|
||||
let turn = || egui::Event::MouseWheel {
|
||||
unit: egui::MouseWheelUnit::Line,
|
||||
delta: vec2(0.0, 1.0),
|
||||
phase: egui::TouchPhase::Move,
|
||||
modifiers: egui::Modifiers::NONE,
|
||||
source: egui::MouseWheelSource::Unknown,
|
||||
};
|
||||
run(&mut app, vec![turn()]);
|
||||
assert_eq!(value.get(), 11.0, "the hovered field steps, focused: {focused}");
|
||||
assert_eq!(ctx.memory(|m| m.has_focus(field_id.get())), focused, "hover does not take focus");
|
||||
assert_eq!(app.view().unwrap().zoom, before.zoom);
|
||||
let over_canvas = app.canvas_rect.unwrap().center();
|
||||
run(&mut app, vec![egui::Event::PointerMoved(over_canvas)]);
|
||||
for _ in 0..20 {
|
||||
run(&mut app, vec![]);
|
||||
assert_eq!(app.view().unwrap().zoom, before.zoom, "a panel turn cannot leave a canvas glide, focused: {focused}");
|
||||
assert_eq!(app.view().unwrap().center, before.center);
|
||||
}
|
||||
run(&mut app, vec![turn()]);
|
||||
let first = app.view().unwrap().zoom;
|
||||
assert!(first > before.zoom, "a turn over the canvas still zooms");
|
||||
for _ in 0..20 {
|
||||
run(&mut app, vec![]);
|
||||
}
|
||||
assert!(app.view().unwrap().zoom > first, "the canvas turn still glides over subsequent frames");
|
||||
assert_eq!(value.get(), 11.0, "the canvas turn does not step the field");
|
||||
}
|
||||
}
|
||||
|
||||
/// General › Scroll Sideways with Cmd/Ctrl + Wheel (on by default), through the control channel's
|
||||
/// `ui.wheel`: with Zoom with Mouse Wheel off, Cmd/Ctrl-wheel zooms about the pointer while the
|
||||
/// option is off and scrolls sideways while it is on, and a trackpad pinch, two fingers spread on a touch
|
||||
|
||||
@@ -920,11 +920,10 @@ fn wheel(at: Pos2, unit: egui::MouseWheelUnit, dy: f32, modifiers: Modifiers) ->
|
||||
]
|
||||
}
|
||||
|
||||
/// The mouse wheel over a focused numeric field steps it as ↑/↓ do (#485): a notch a step, Shift
|
||||
/// ten, Ctrl/Cmd a tenth; a trackpad's points add up to notches. Unfocused, or with the pointer
|
||||
/// elsewhere, the wheel isn't the field's.
|
||||
/// Hover stepping (#5949) keeps the existing units, modifiers and trackpad accumulation,
|
||||
/// without taking keyboard focus. Outside the field, the wheel is left alone.
|
||||
#[test]
|
||||
fn the_wheel_steps_a_focused_field_under_the_pointer() {
|
||||
fn the_wheel_steps_a_hovered_field_without_taking_focus() {
|
||||
use egui::MouseWheelUnit::{Line, Point};
|
||||
let mut f = Field::new();
|
||||
let u = Unit::Millimeters;
|
||||
@@ -932,16 +931,13 @@ fn the_wheel_steps_a_focused_field_under_the_pointer() {
|
||||
let draw = |ui: &mut egui::Ui| widgets::num_field(ui, "f", Some(u.to_pt(mm.get())), u, 80.0).inspect(|&v| mm.set(u.from_pt(v)));
|
||||
f.frame(vec![], &draw);
|
||||
let at = f.rect.get().center();
|
||||
assert_eq!(f.frame(wheel(at, Line, 1.0, Modifiers::NONE), &draw), None, "unfocused");
|
||||
f.frame(f.click(1), &draw);
|
||||
f.frame(vec![], &draw);
|
||||
for (dy, mods, want) in
|
||||
[(1.0, Modifiers::NONE, 5.0), (2.0, Modifiers::SHIFT, 25.0), (-1.0, Modifiers::COMMAND, 24.9), (-1.0, Modifiers::NONE, 24.0)]
|
||||
{
|
||||
assert!(f.frame(wheel(at, Line, dy, mods), &draw).is_some(), "{dy} {mods:?}");
|
||||
assert!((mm.get() - want).abs() < 1e-6, "{dy} {mods:?}: {}", mm.get());
|
||||
}
|
||||
assert_eq!(f.selection(), Some((0, "24 mm".len())), "the new value is selected");
|
||||
assert!(!f.ctx.memory(|m| m.has_focus(f.id.get())), "hover never takes keyboard focus");
|
||||
// Points: a step for each line's worth, what is left carried to the next turn.
|
||||
let line = f.ctx.options(|o| o.input_options.line_scroll_speed);
|
||||
assert_eq!(f.frame(wheel(at, Point, line * 0.6, Modifiers::NONE), &draw), None);
|
||||
@@ -957,16 +953,16 @@ fn the_wheel_steps_a_focused_field_under_the_pointer() {
|
||||
assert_eq!(f.frame(wheel(f.rect.get().center(), Line, -1.0, Modifiers::NONE), &count), Some(2.0));
|
||||
}
|
||||
|
||||
/// The wheel over a focused field steps it and leaves the panel where it is; over an unfocused one
|
||||
/// it scrolls the panel as before.
|
||||
/// Both focused and unfocused hovered fields own the wheel, including its smoothing tail.
|
||||
#[test]
|
||||
fn the_wheel_scrolls_the_panel_unless_over_the_focused_field() {
|
||||
fn the_wheel_scrolls_the_panel_unless_over_a_numeric_field() {
|
||||
for focus in [false, true] {
|
||||
let mut f = Field::new();
|
||||
let offset = Cell::new(0.0);
|
||||
let value = Cell::new(10.0);
|
||||
let draw = |ui: &mut egui::Ui| {
|
||||
let out = egui::ScrollArea::vertical().max_height(60.0).show(ui, |ui| {
|
||||
let v = widgets::plain_field(ui, "f", 10.0, "", 0, 80.0);
|
||||
let v = widgets::plain_field(ui, "f", value.get(), "", 0, 80.0).inspect(|&v| value.set(v));
|
||||
ui.add_space(600.0);
|
||||
v
|
||||
});
|
||||
@@ -994,11 +990,59 @@ fn the_wheel_scrolls_the_panel_unless_over_the_focused_field() {
|
||||
for _ in 0..20 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
if focus {
|
||||
assert_eq!((stepped, offset.get()), (Some(8.0), 0.0));
|
||||
} else {
|
||||
assert!(stepped.is_none() && offset.get() > 0.0, "{stepped:?} {}", offset.get());
|
||||
assert_eq!((stepped, offset.get()), (Some(8.0), 0.0), "focus {focus}");
|
||||
let line = f.ctx.options(|o| o.input_options.line_scroll_speed);
|
||||
assert_eq!(f.frame(wheel(at, egui::MouseWheelUnit::Point, -line * 0.6, Modifiers::NONE), &draw), None);
|
||||
assert_eq!(offset.get(), 0.0);
|
||||
assert_eq!(f.frame(wheel(at, egui::MouseWheelUnit::Point, -line * 0.6, Modifiers::NONE), &draw), Some(7.0));
|
||||
for _ in 0..20 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
assert_eq!(offset.get(), 0.0);
|
||||
assert_eq!(f.frame(wheel(Pos2::new(20.0, 45.0), egui::MouseWheelUnit::Line, -2.0, Modifiers::NONE), &draw), None);
|
||||
for _ in 0..20 {
|
||||
f.frame(vec![], &draw);
|
||||
}
|
||||
assert!(offset.get() > 0.0, "outside the field scrolls the panel");
|
||||
}
|
||||
}
|
||||
|
||||
/// A wheel turn over an unfocused field steps it without consuming an arrow key in the same frame.
|
||||
#[test]
|
||||
fn hover_wheel_keeps_arrow_keys_for_the_keyboard_focus() {
|
||||
let mut f = Field::new();
|
||||
let arrows = Cell::new(false);
|
||||
let draw = |ui: &mut egui::Ui| {
|
||||
let v = widgets::plain_field(ui, "f", 1.123, "", 3, 80.0);
|
||||
arrows.set(ui.input(|i| i.events.iter().any(|e| matches!(e, Event::Key { key: Key::ArrowUp, .. }))));
|
||||
v
|
||||
};
|
||||
f.frame(vec![], &draw);
|
||||
let at = f.rect.get().center();
|
||||
assert_eq!(f.frame(vec![Event::PointerMoved(at), key(Key::ArrowUp, Modifiers::NONE)], &draw), None);
|
||||
assert!(arrows.get());
|
||||
let mut events = wheel(at, egui::MouseWheelUnit::Line, 1.0, Modifiers::NONE);
|
||||
events.push(key(Key::ArrowUp, Modifiers::NONE));
|
||||
assert_eq!(f.frame(events, &draw), Some(2.0), "existing next-multiple rounding, wheel only");
|
||||
assert!(arrows.get(), "hover does not consume arrows");
|
||||
assert!(!f.ctx.memory(|m| m.has_focus(f.id.get())));
|
||||
}
|
||||
|
||||
/// Hover steps stop at both bounds, leave mixed values blank, and never edit disabled fields.
|
||||
#[test]
|
||||
fn hover_wheel_preserves_limits_mixed_values_and_disabled_fields() {
|
||||
for (value, enabled, dy, want) in [
|
||||
(Some(0.5), true, -1.0, Some(0.0)),
|
||||
(Some(0.0), true, -1.0, None),
|
||||
(Some(9.5), true, 1.0, Some(10.0)),
|
||||
(Some(10.0), true, -1.0, Some(9.0)),
|
||||
(None, true, -1.0, None),
|
||||
(Some(10.0), false, -1.0, None),
|
||||
] {
|
||||
let mut f = Field::new();
|
||||
let draw = |ui: &mut egui::Ui| ui.add_enabled_ui(enabled, |ui| widgets::range_field(ui, "f", value, 0.0..=10.0, "", 3, 80.0)).inner;
|
||||
f.frame(vec![], &draw);
|
||||
assert_eq!(f.frame(wheel(f.rect.get().center(), egui::MouseWheelUnit::Line, dy, Modifiers::NONE), &draw), want);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -247,9 +247,9 @@ pub(super) fn shown_alike(show: impl Fn(f64) -> String) -> impl Fn(f64, f64) ->
|
||||
/// a weight stops at 0, a miter limit at 1), and a plain step also stops at each of `stops`
|
||||
/// ([`step_through`]). The new value is applied at once, as in
|
||||
/// Illustrator's panels and dialogs, and the new text replaces `buf`, all selected so typing
|
||||
/// replaces it. Returns the new number when a step was taken. While the pointer is over the focused
|
||||
/// field the wheel is the field's (the panel under it doesn't scroll); an unfocused field leaves the
|
||||
/// wheel to the panel and the canvas.
|
||||
/// replaces it when focused. Returns the new number when a step was taken. While the pointer is
|
||||
/// over an enabled field the wheel is the field's (the panel under it doesn't scroll), without
|
||||
/// taking keyboard focus. Arrow keys still require keyboard focus.
|
||||
fn step_value(
|
||||
ui: &Ui,
|
||||
resp: &Response,
|
||||
@@ -261,11 +261,15 @@ fn step_value(
|
||||
) -> Option<f64> {
|
||||
// Memory focus, as the field's highlight and its typing use: `Response::has_focus` also needs the
|
||||
// window to report keyboard focus.
|
||||
if !ui.memory(|m| m.has_focus(resp.id)) {
|
||||
if !resp.enabled() {
|
||||
return None;
|
||||
}
|
||||
let focused = ui.memory(|m| m.has_focus(resp.id));
|
||||
use egui::{Event, Key, MouseWheelUnit};
|
||||
let wheel = resp.contains_pointer();
|
||||
if !focused && !wheel {
|
||||
return None;
|
||||
}
|
||||
// Wheel turns in notches: a line each, or `line` points of a trackpad or a fine wheel; what is
|
||||
// left of a notch carries over to the next turn.
|
||||
let (line, notch_id) = (ui.ctx().options(|o| o.input_options.line_scroll_speed), resp.id.with("wheel"));
|
||||
@@ -278,8 +282,8 @@ fn step_value(
|
||||
let (Event::Key { modifiers, .. } | Event::MouseWheel { modifiers, .. }) = e else { return true };
|
||||
let Some(&(mods, size)) = STEPS.iter().find(|(m, _)| modifiers.matches_logically(*m)) else { return true };
|
||||
let n = match e {
|
||||
Event::Key { key: Key::ArrowUp, pressed: true, .. } => 1.0,
|
||||
Event::Key { key: Key::ArrowDown, pressed: true, .. } => -1.0,
|
||||
Event::Key { key: Key::ArrowUp, pressed: true, .. } if focused => 1.0,
|
||||
Event::Key { key: Key::ArrowDown, pressed: true, .. } if focused => -1.0,
|
||||
Event::MouseWheel { unit, delta, .. } if wheel => {
|
||||
// Shift may turn the wheel sideways (the Mac does).
|
||||
let d = if modifiers.shift { delta.x + delta.y } else { delta.y };
|
||||
@@ -325,7 +329,9 @@ fn step_value(
|
||||
let v = within(if stops.contains(&v) { v } else { round_to(v, STEP_DECIMALS) }, bounds);
|
||||
*buf = show(v);
|
||||
ui.data_mut(|d| d.insert_temp(resp.id, buf.clone()));
|
||||
select_all(ui, resp.id, buf);
|
||||
if focused {
|
||||
select_all(ui, resp.id, buf);
|
||||
}
|
||||
Some(v)
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -1,9 +1,11 @@
|
||||
# Roadmap: milestones and what's next
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-08 · **Change:** minor (Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (numeric fields step on wheel hover without taking keyboard focus, #5949) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
Numeric fields step on wheel hover without taking keyboard focus (#5949). Arrow keys still require focus; modifiers, units, limits, mixed values and existing rounding stay in the shared stepping path. Headless regressions in `tests/numfields.rs` cover hovered unfocused fields, focused fields, trackpad accumulation and panel scroll exclusion. Scores and estimates unchanged.
|
||||
|
||||
Raster rendering fits and preflattens curved stroke outlines more tightly only below four times the raster tolerance, retaining thin-band coverage without changing stroke weight or vector export geometry. Current cached character/object outlines, cropped long strokes, gradient clips and effect rasters share the helper; cropped-region reuse and guarded flattening remain. The unchanged `fractional_curved_strokes_retain_coverage_between_notches` regression covers fractional weights, rotation, phase, opacity and f32-rounded endpoints. This is a quality/cost tradeoff, not a speed claim. Scores and estimates unchanged.
|
||||
|
||||
**PR #3273 revision — 2026-11-04:** Thin curved stroke coverage ported to the current raster-only cache, cropped-long-stroke and effect paths with guarded flattening; vector export geometry unchanged. Scores and estimates unchanged.
|
||||
@@ -183,6 +185,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-09 | minor | Numeric fields step on wheel hover without taking focus; keyboard focus and existing rounding rules remain (#5949). Scores and estimates unchanged. |
|
||||
| 2026-11-07 | minor | M2: with the Pen or Curvature tool, Edit › Copy and Cut take the anchors and segments a Cmd/Ctrl-lent Direct Selection picked, not the whole path (#6123); estimates unchanged |
|
||||
| 2026-11-06 | minor | M2: a shape just drawn moves by its centre point (Show Center) and turns from the rotate zone just outside a bounding-box corner with its tool still active, as the Selection tool's gestures do (#5477); estimates unchanged. |
|
||||
| 2026-11-06 | minor | Hindi (`hi`): 7,877-row machine-assisted catalog, zero/one singular plurals, desktop font fallback and optional Devanagari craft-fonts fallback; all 55 translation tests pass; native review and a web font remain; scores and estimates unchanged |
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# Parity with Adobe Illustrator
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-08 · **Change:** minor (Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (numeric fields step on wheel hover without taking keyboard focus, and a panel wheel turn cannot seed a canvas zoom glide, #5949) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
Numeric fields step on wheel hover without taking keyboard focus (#5949). Arrow keys still require focus; modifiers, units, limits, mixed values and existing rounding stay in the shared stepping path. Headless regressions in `tests/numfields.rs` cover hovered unfocused fields, focused fields, trackpad accumulation and panel scroll exclusion. A canvas-first/panel-second regression also checks that a field turn cannot start a canvas zoom glide when the pointer enters the canvas on the next frame. Scores and estimates unchanged.
|
||||
|
||||
Raster rendering fits and preflattens curved stroke outlines more tightly only below four times the raster tolerance, retaining thin-band coverage without changing stroke weight or vector export geometry. Current cached character/object outlines, cropped long strokes, gradient clips and effect rasters share the helper; cropped-region reuse and guarded flattening remain. The unchanged `fractional_curved_strokes_retain_coverage_between_notches` regression covers fractional weights, rotation, phase, opacity and f32-rounded endpoints. This is a quality/cost tradeoff, not a speed claim. Scores and estimates unchanged.
|
||||
|
||||
**PR #3273 revision — 2026-11-04:** Thin curved stroke coverage ported to the current raster-only cache, cropped-long-stroke and effect paths with guarded flattening; vector export geometry unchanged. Scores and estimates unchanged.
|
||||
@@ -774,7 +776,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- With Real-time Drawing and Editing off, a dragged selection whose outline points and evaluated objects come to more than 100,000 shows its moved bounds instead, as the selection at rest does (#525, #2460; `real_time_drawing_off_draws_the_bounds_of_a_selection_too_big_to_outline`, `real_time_drawing_off_draws_the_bounds_of_a_group_too_big_to_outline`, `real_time_drawing_off_draws_the_bounds_of_a_repeat_with_too_many_objects`).
|
||||
- With Real-time Drawing and Editing off, a move or handle drag of a group, blend, envelope, repeat or intertwine draws the outline of each of its objects instead of one union of them, as VectorCraft's own rule (`real_time_drawing_off_outlines_a_dragged_group_member_by_member`, `real_time_drawing_off_outlines_a_resized_group_member_by_member`, `real_time_drawing_off_outlines_a_dragged_blend_step_by_step`, `real_time_drawing_off_outlines_a_dragged_envelope_object_by_object`). A group with a Pathfinder effect draws the area the effect makes, a gradient mesh draws its mesh lines, and a symbol instance draws its bounds (`real_time_drawing_off_outlines_a_dragged_pathfinder_group_by_its_area`, `real_time_drawing_off_outlines_each_mesh_of_a_dragged_repeat_once`, `real_time_drawing_off_outlines_a_dragged_symbol_instance_by_its_bounds`).
|
||||
- Dialogs move by dragging their heading (the band down from the top edge, as a title bar), stay on screen and keep where they were moved for the session; the form dialogs' choices are dropdowns (Offset Path's Joins, Average's and Shear's and Reflect's axis), and Offset Path lists Offset, Joins, Miter Limit and previews (#423).
|
||||
- Dialogs open with their first field focused and its value selected: type a value and press Enter (Move, Rotate, Scale…). A dialog opened from a menu takes the keyboard focus every time it opens, not only the first (#1367), and one whose first field starts greyed out (Blend Options on Smooth Color, Layers Panel Options) gives the focus to that field once it is enabled (#1395). Number fields in panels, the Control bar and dialogs (preferences too) step with Up/Down, or with the mouse wheel over the focused field (#485: a notch a step; the panel under it then stays put, and an unfocused field leaves the wheel to scrolling) (Shift: ten, Ctrl/Cmd: a tenth of their unit), applied at once.
|
||||
- Dialogs open with their first field focused and its value selected: type a value and press Enter (Move, Rotate, Scale…). A dialog opened from a menu takes the keyboard focus every time it opens, not only the first (#1367), and one whose first field starts greyed out (Blend Options on Smooth Color, Layers Panel Options) gives the focus to that field once it is enabled (#1395). Number fields in panels, the Control bar and dialogs (preferences too) step with Up/Down, or with the mouse wheel over the hovered field without requiring focus (#485, #5949: a notch a step; the panel under it then stays put, and a disabled field leaves the wheel to scrolling) (Shift: ten, Ctrl/Cmd: a tenth of their unit), applied at once.
|
||||
- While Alt (Option on macOS) is held, the Flare Tool Options' Cancel button reads Reset, which puts every option back to the Flare tool's default and leaves the dialog open (`ui.dialogReset`).
|
||||
- A plain step of a number field (Up/Down or a wheel notch) goes to the next whole unit in its direction and a Ctrl/Cmd step goes to the next tenth, so 5.1 mm steps up to 6 mm and down to 5 mm (#2881). A Shift step adds or takes ten and keeps the fraction.
|
||||
- In the number, text and color fields of the panels and the Control bar, Shift+Enter (Shift+Return) applies the value and keeps the field active with its value selected; a text box of several lines takes it as a line break. In a dialog, Shift+Enter applies the value and confirms the dialog, as Enter does.
|
||||
@@ -1859,6 +1861,8 @@ Edit › Copy and Cut put the selected ruler guides on the clipboard, with the s
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-08 | minor | Panel numeric wheel turns do not seed canvas zoom glides; corrected the disabled-field scroll description (#5949). Scores and estimates unchanged. |
|
||||
| 2026-11-07 | minor | Numeric fields step on wheel hover without taking focus; keyboard focus and existing rounding rules remain (#5949). Scores and estimates unchanged. |
|
||||
| 2026-11-06 | minor | Direct Selection: with Snap to Grid, a dragged straight segment lands its anchors on the grid (#3835); evidence added; scores unchanged. |
|
||||
| 2026-11-06 | minor | Snap to Glyph › Glyph Bounds: the corners of each glyph's box are points a drawn point lands on, also on a curved side (`B`), and the box turns with rotated type, its edges then Angular Guides (#5112; [Snap to Glyph options](https://helpx.adobe.com/illustrator/desktop/design-with-text/special-characters-glyphs/snap-to-glyph-options.html)); scores and estimates unchanged. |
|
||||
| 2026-11-06 | minor | Drop Shadow: a swatch beside Color opens the Color Picker over the dialog, as the other effect colour fields do (#5364); correction: the #5365 entry listed Drop Shadow, whose Color is drawn outside the shared grid and had no swatch; scores unchanged. |
|
||||
|
||||
+6
-3
@@ -1,9 +1,11 @@
|
||||
# UI and interaction parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-08 · **Change:** minor (Character Styles panel: a double-click on a style's row opens its Character Style Options; the dialog keeps a fixed width and scrolls its sections, so its buttons stay on screen) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (numeric fields step on wheel hover without taking keyboard focus, #5949) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
Numeric fields step on wheel hover without taking keyboard focus (#5949). Arrow keys still require focus; modifiers, units, limits, mixed values and existing rounding stay in the shared stepping path. Headless regressions in `tests/numfields.rs` cover hovered unfocused fields, focused fields, trackpad accumulation and panel scroll exclusion. Scores and estimates unchanged.
|
||||
|
||||
| 2026-11-06 | minor | Opening a file that takes longer than 0.4 s shows a progress window: the file name, a moving bar (reading reports no steps), how many more files wait, Cancel (Esc; the file doesn't open) and Continue in Background (`file.cancelOpen`, `file.openInBackground`); it never opens over another dialog or progress window (part of #5930); scores and estimates unchanged |
|
||||
Raster rendering fits and preflattens curved stroke outlines more tightly only below four times the raster tolerance, retaining thin-band coverage without changing stroke weight or vector export geometry. Current cached character/object outlines, cropped long strokes, gradient clips and effect rasters share the helper; cropped-region reuse and guarded flattening remain. The unchanged `fractional_curved_strokes_retain_coverage_between_notches` regression covers fractional weights, rotation, phase, opacity and f32-rounded endpoints. This is a quality/cost tradeoff, not a speed claim. Scores and estimates unchanged.
|
||||
|
||||
@@ -259,7 +261,7 @@ tools, never checked side by side) isn't captured row by row.
|
||||
| Wheel over a closed font-family field in Character, Properties or Control | done | #5253: applies the next or previous family to selected text, one undo step per notch; uses saved font filters, ignores search text and stops at the list's ends. Escape and pointer departure keep the change. Other font-picking menus and modifier-wheel input keep their existing behavior. |
|
||||
| Arrow keys nudge by Keyboard Increment (0.001–1296 pt); Shift ×10; Alt copies | done | |
|
||||
| Fields commit what was typed on a click away or a move to another field, as Enter does; Escape discards it | done | #1787 |
|
||||
| Number fields: Up/Down step, Shift ×10, Cmd/Ctrl ÷10, wheel over a focused field; steps, the wheel and label scrubs stop at the field's bounds (a stroke weight at 0, a miter limit at 1) | done | Shift+arrow in the Stroke weight field doesn't step by 10 (#991); bounds #3370 |
|
||||
| Number fields: Up/Down step, Shift ×10, Cmd/Ctrl ÷10, wheel over a hovered field without a click; steps, the wheel and label scrubs stop at the field's bounds (a stroke weight at 0, a miter limit at 1) | done | Shift+arrow in the Stroke weight field doesn't step by 10 (#991); bounds #3370 |
|
||||
| Arithmetic in fields (`+ - * /`, and `%` of the value before it: `3cm*50%`, `50pt+25%`) and unit suffixes (`3 mm`, `1in`, `2p6`) | done | `Unit::parse`; mixed units in one expression not verified |
|
||||
| Numeric fields: focus click selects the whole value; dragging selects from the pressed character | partial | #1326; select-all waits for click release; px/pt/plain/optional fields, both directions and replacement text tested; native API input, 17 px partial replacement, Undo/Redo, saved-file round trip and screenshots verified; Tab, double-click, arrows and wheel preserved; short movements still classified as clicks can select all and remain open |
|
||||
| Units: pt, pc, in, mm, cm, px, ft-in, m, yd, ft; General/Stroke/Type units | done | units preference reverting to points on SVG output reported (#864) |
|
||||
@@ -268,7 +270,7 @@ tools, never checked side by side) isn't captured row by row.
|
||||
| Scrubby labels (beyond Illustrator) | done | #400; the labels that are links (the Control bar's Stroke: and Opacity:, the Properties panel's Stroke) scrub too, and a click on them still opens their panel (#4088) |
|
||||
| 3D Lighting Color picker, preview, reset dot and Reset all | initial | One to ten opaque RGB lights for Revolve, Extrude & Bevel, Inflate and Rotate; select, add or delete a light and edit its settings or gizmo; Standard, Diffuse, Top Left and Right select original rigs, with Custom for manual changes; Cancel restores all lights |
|
||||
| Dialogs open with the first field focused and selected; Enter confirms | done | on every open, not only the first (#1367); a first field that starts greyed out takes the focus once enabled (#1395) |
|
||||
| Value fields: selected text readable in every brightness; a focused field follows its value when a slider or preview changes it | partial | #1490 (part of #1465: the wheel over a hovered, unfocused field still scrolls the panel), #1457 |
|
||||
| Value fields: selected text readable in every brightness; a focused field follows its value when a slider or preview changes it | partial | #1490, #1457; hover wheel stepping #5949 |
|
||||
| Sliders in dialogs and popups (Save for Web, raster export options, Preferences, the opacity popup) show their rail, filled up to the handle | done | #1483 |
|
||||
| Rectangular Grid and Polar Grid click dialogs: size, divider counts with a skew slider each, Use Outside Rectangle As Frame / Create Compound Path From Ellipses, Fill Grid; kept as the tool's options, used by drags too | done | #1377. The options are the documented ones; how far a skew percentage moves the dividers and which way the radial skew turns are our own choice (the public documentation gives neither). The reference point locator is in the dialog, and a double-click on the tool opens it as the tool's Tool Options (next row) |
|
||||
| Double-click the Line Segment, Arc, Rectangular Grid or Polar Grid tool: its Tool Options ([Rectangular Grid tool](https://helpx.adobe.com/illustrator/using/tool-techniques/rectangular-grid-tool.html) and [Arc tool](https://helpx.adobe.com/illustrator/using/tool-techniques/arc-tool.html) step 3, [Tools](https://helpx.adobe.com/illustrator/using/tools.html)) | done | #2691. The tool's click dialog without a place to draw: Line Segment's Length, Angle and Fill Line; Arc's lengths, Reference Point, Type, Base Along, Slope and Fill Arc; the grids' Reference Point, Default Size, dividers, skews and switches. OK draws nothing and sets what the next drag draws with (Fill Line, the arc's type, base, slope and fill, the grids' dividers, skews and switches) and what a click's dialog opens at (with the Length, Angle and Default Size); Cancel changes nothing (`double_clicking_the_line_arc_and_grid_tools_opens_their_tool_options`) |
|
||||
@@ -482,6 +484,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-09 | minor | Numeric fields step on wheel hover without taking focus; keyboard focus and existing rounding rules remain (#5949). Scores and estimates unchanged. |
|
||||
| 2026-11-07 | minor | Pen and Curvature tool with Cmd/Ctrl held: Edit › Copy and Cut take the anchors and segments the lent Direct Selection picked, as with Direct Selection chosen, not the whole path; with the Pen's own last anchor selected Copy still takes the whole path (#6123); scores and estimates unchanged |
|
||||
| 2026-11-07 | minor | Live Corners: with Direct Selection, while every corner of a path is selected, a Shift-click on a corner widget picks that corner alone instead of deselecting it (#5188); adding a hidden corner's widget by Shift-click stays open (#1232); scores and estimates unchanged |
|
||||
| 2026-11-06 | minor | Keyboard shortcuts: Cmd/Ctrl+Alt+. and Cmd/Ctrl+Alt+, (Stroke Weight Up and Down) step each selected stroke's weight, or the next object drawn's (#5797); scores and estimates unchanged |
|
||||
|
||||
Reference in New Issue
Block a user