mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 09:33:54 +00:00
Merge pull request #6215 from ruchirguitar/SidePanelCollapseArrow
Add collapse-to-icons arrows to floating panels and rail flyouts
This commit is contained in:
@@ -1418,13 +1418,53 @@ impl craft_ui::docking::DockContent<String> for PanelContent<'_> {
|
||||
crate::panels::panel_menu_items(self.app, ui, id);
|
||||
}
|
||||
|
||||
fn group_header_width(&self, _panels: &[String], _active: &String, _floating: bool, _strip_width: f32) -> f32 {
|
||||
28.0
|
||||
fn group_header_width(&self, panels: &[String], _active: &String, floating: bool, _strip_width: f32) -> f32 {
|
||||
let flyout = self.app.ui.open_panel.as_ref().is_some_and(|panel| panels.contains(panel));
|
||||
if floating || flyout { 52.0 } else { 28.0 }
|
||||
}
|
||||
|
||||
fn group_header(&mut self, ui: &mut egui::Ui, _panels: &[String], active: &String, _floating: bool) {
|
||||
let rect = egui::Rect::from_center_size(ui.max_rect().center(), egui::vec2(16.0, 16.0));
|
||||
crate::panels::panel_menu(self.app, ui, active, rect);
|
||||
fn group_header(&mut self, ui: &mut egui::Ui, panels: &[String], active: &String, floating: bool) {
|
||||
let header = ui.max_rect();
|
||||
let flyout = self.app.ui.open_panel.clone().filter(|panel| panels.contains(panel));
|
||||
let menu_cell = if floating || flyout.is_some() {
|
||||
let split = (header.right() - 28.0).max(header.left());
|
||||
let cell = egui::Rect::from_min_max(header.min, egui::pos2(split, header.bottom()));
|
||||
if cell.is_positive() {
|
||||
let rect = egui::Rect::from_center_size(cell.center(), egui::vec2(24.0_f32.min(cell.width()), 24.0_f32.min(cell.height())));
|
||||
let tokens = crate::theme::Tokens::get(ui.ctx());
|
||||
let response = ui.interact(rect, ui.scope_id().with(("collapse-to-icons", active)), egui::Sense::click());
|
||||
response.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, ui.is_enabled(), tl!("Collapse to Icons")));
|
||||
let color = if response.hovered() || response.has_focus() { tokens.text_strong } else { tokens.text_dim };
|
||||
ui.painter().with_clip_rect(rect.intersect(ui.clip_rect())).text(
|
||||
rect.center(),
|
||||
egui::Align2::CENTER_CENTER,
|
||||
"»",
|
||||
egui::TextStyle::Button.resolve(ui.style()),
|
||||
color,
|
||||
);
|
||||
if response.on_hover_text(tl!("Collapse to Icons")).clicked() {
|
||||
let result = if let Some(panel) = flyout {
|
||||
// The opening icon can differ from the currently active tab.
|
||||
self.app.run("window.panel", json!({"panel": panel}))
|
||||
} else {
|
||||
self.app.run("window.panel.dock", json!({"panel": active, "icon": true, "group": true}))
|
||||
};
|
||||
match result {
|
||||
// Dock commands reveal their destination. This control leaves the group collapsed.
|
||||
Ok(_) => self.app.ui.open_panel = None,
|
||||
Err(error) => self.app.ui.status = error,
|
||||
}
|
||||
}
|
||||
}
|
||||
egui::Rect::from_min_max(egui::pos2(split, header.top()), header.max)
|
||||
} else {
|
||||
header
|
||||
};
|
||||
if menu_cell.is_positive() {
|
||||
let rect =
|
||||
egui::Rect::from_center_size(menu_cell.center(), egui::vec2(16.0_f32.min(menu_cell.width()), 16.0_f32.min(menu_cell.height())));
|
||||
crate::panels::panel_menu(self.app, ui, active, rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ struct Window {
|
||||
frames: usize,
|
||||
/// The texts its last frame drew, with their rects in the window.
|
||||
texts: Vec<(String, Rect)>,
|
||||
accessibility: Vec<egui::accesskit::Node>,
|
||||
}
|
||||
|
||||
struct Host {
|
||||
@@ -82,7 +83,14 @@ impl Host {
|
||||
let mut host = host.borrow_mut();
|
||||
let builder = &viewport.builder;
|
||||
let rect = Rect::from_min_size(builder.position.unwrap_or_default(), builder.inner_size.unwrap_or(vec2(300.0, 200.0)));
|
||||
host.windows.entry(id).or_insert_with(|| Window { rect, events: Vec::new(), dropped: Vec::new(), frames: 0, texts: Vec::new() });
|
||||
host.windows.entry(id).or_insert_with(|| Window {
|
||||
rect,
|
||||
events: Vec::new(),
|
||||
dropped: Vec::new(),
|
||||
frames: 0,
|
||||
texts: Vec::new(),
|
||||
accessibility: Vec::new(),
|
||||
});
|
||||
let viewports = host.infos();
|
||||
let (time, focused) = (host.time, host.focus == id);
|
||||
let Some(window) = host.windows.get_mut(&id) else { return };
|
||||
@@ -171,10 +179,13 @@ impl Host {
|
||||
}
|
||||
let mut shown = Vec::new();
|
||||
full.shapes.iter().for_each(|c| texts(&c.shape, &mut shown));
|
||||
let accessibility =
|
||||
full.platform_output.accesskit_update.take().map(|update| update.nodes.into_iter().map(|(_, node)| node).collect()).unwrap_or_default();
|
||||
let mut host = host.borrow_mut();
|
||||
host.child_layer_order.insert(id, layer_order);
|
||||
if let Some(window) = host.windows.get_mut(&id) {
|
||||
window.texts = shown;
|
||||
window.accessibility = accessibility;
|
||||
}
|
||||
host.apply(&full);
|
||||
}
|
||||
@@ -349,6 +360,22 @@ fn open_button_menu(h: &mut Harness, viewport: ViewportId, side: f32, rank: impl
|
||||
menu
|
||||
}
|
||||
|
||||
/// Click a named button, independent of other controls sharing its size or position.
|
||||
fn open_named_button_menu(h: &mut Harness, viewport: ViewportId, name: &str) -> Pos2 {
|
||||
let buttons: Vec<Rect> = h.host.borrow().windows[&viewport]
|
||||
.accessibility
|
||||
.iter()
|
||||
.filter(|node| node.role() == egui::accesskit::Role::Button && node.label() == Some(name))
|
||||
.filter_map(|node| node.bounds())
|
||||
.map(|bounds| Rect::from_min_max(pos2(bounds.x0 as f32, bounds.y0 as f32), pos2(bounds.x1 as f32, bounds.y1 as f32)))
|
||||
.collect();
|
||||
assert_eq!(buttons.len(), 1, "one visible button named {name}");
|
||||
let at = buttons[0].center();
|
||||
h.host.borrow_mut().windows.get_mut(&viewport).unwrap().events.extend([Event::PointerMoved(at), button(at, true), button(at, false)]);
|
||||
h.frames(8);
|
||||
at
|
||||
}
|
||||
|
||||
/// A click on the ≡ button in the OS window `viewport`, the 16 pt square at the right end of the
|
||||
/// panel's tab strip. Returns where the button is.
|
||||
fn open_panel_menu(h: &mut Harness, viewport: ViewportId) -> Pos2 {
|
||||
@@ -421,14 +448,18 @@ fn the_library_submenu_scrolls_inside_a_short_native_window() {
|
||||
assert_submenu_reaches_its_end(&mut h, "swatches", 240.0, "Open Swatch Library", "Default Swatches", "Other Library…");
|
||||
}
|
||||
|
||||
/// The Layers panel's Filter popup, under the topmost 24 pt button: the wheel brings its last row
|
||||
/// The Layers panel's named Filter button opens its popup: the wheel brings its last row
|
||||
/// into a short window.
|
||||
#[test]
|
||||
fn the_layers_filter_popup_scrolls_inside_a_short_native_window() {
|
||||
let mut h = Harness::new();
|
||||
h.ctx.enable_accesskit();
|
||||
let viewport = h.float("layers");
|
||||
resize_window(&mut h, viewport, 160.0);
|
||||
open_button_menu(&mut h, viewport, 24.0, |at| -at.y);
|
||||
// The header's collapse control is also 24 pt square and sits above Filter.
|
||||
assert!(text_rect(&h, viewport, "»").is_some(), "keep the competing header control in this regression");
|
||||
open_named_button_menu(&mut h, viewport, "Filter");
|
||||
assert!(floating_window(&layout_of(&h.app.ui), "layers").is_some(), "Filter must not collapse the panel");
|
||||
let over = text_rect(&h, viewport, "By Object").expect("the popup's By Object heading");
|
||||
assert!(text_rect(&h, viewport, "Hidden").is_none_or(|end| end.bottom() > 160.0), "the popup opens at the top of its list");
|
||||
wheel_down(&mut h, viewport, over.center(), 5000.0);
|
||||
@@ -1542,3 +1573,31 @@ fn wheel_without_a_move_uses_the_floating_viewports_pointer() {
|
||||
h.frame(vec![]);
|
||||
assert_ne!(h.app.run("text.fonts", json!({})).unwrap(), before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn floating_collapse_arrow_in_a_native_window_moves_the_group_to_the_rail() {
|
||||
let mut h = Harness::new();
|
||||
h.app.run("window.panel.float", json!({"panel": "layers", "group": true})).unwrap();
|
||||
h.app.run("window.panel.activate", json!({"panel": "layers"})).unwrap();
|
||||
h.frames(3);
|
||||
let viewport = h.viewport("layers");
|
||||
h.host.borrow_mut().focus = viewport;
|
||||
let arrow = h.host.borrow().windows[&viewport]
|
||||
.texts
|
||||
.iter()
|
||||
.find(|(text, _)| text == "»")
|
||||
.map(|(_, rect)| rect.center())
|
||||
.expect("the native floating header paints a collapse arrow");
|
||||
for pressed in [true, false] {
|
||||
h.host.borrow_mut().windows.get_mut(&viewport).unwrap().events.extend([Event::PointerMoved(arrow), button(arrow, pressed)]);
|
||||
h.frames(1);
|
||||
}
|
||||
h.frames(3);
|
||||
let layout = layout_of(&h.app.ui);
|
||||
assert!(floating_window(&layout, "layers").is_none());
|
||||
assert_eq!(group_of(&layout, &"layers".into()), ["properties", "layers", "libraries"]);
|
||||
assert!(["properties", "layers", "libraries"].iter().all(|p| in_iconic_column(&layout, p)));
|
||||
let frames = h.host.borrow().windows[&viewport].frames;
|
||||
h.frames(3);
|
||||
assert_eq!(h.host.borrow().windows[&viewport].frames, frames, "the emptied palette is no longer rendered");
|
||||
}
|
||||
|
||||
@@ -1036,3 +1036,118 @@ fn other_function_keys_and_keyboard_guards_keep_their_routes() {
|
||||
assert_eq!(saved.session.doc().unwrap().doc.node_count(), 2, "F12 reloads the saved document");
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
fn collapse_to_icons_button(h: &Harness, panel: &str) -> Option<Rect> {
|
||||
let panel = panel.to_string();
|
||||
h.ctx.read_response(area_id().with(("header", &panel)).with(("collapse-to-icons", &panel))).map(|r| r.rect)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn floating_collapse_arrow_moves_the_whole_group_into_the_icon_rail() {
|
||||
let mut h = Harness::new();
|
||||
assert!(collapse_to_icons_button(&h, "properties").is_none(), "docked headers have no extra button");
|
||||
h.app.run("window.panel.float", json!({"panel": "layers", "group": true})).unwrap();
|
||||
h.app.run("window.panel.activate", json!({"panel": "layers"})).unwrap();
|
||||
for _ in 0..3 {
|
||||
h.frame(vec![]);
|
||||
}
|
||||
let before = h.layout();
|
||||
let members = group_of(&before, &"layers".into());
|
||||
let columns = end_columns(&before).len();
|
||||
let arrow = collapse_to_icons_button(&h, "layers").expect("floating header offers collapse to icons");
|
||||
assert!(arrow.width() <= 24.0 && arrow.height() <= 24.0, "a compact header control: {arrow:?}");
|
||||
h.click(arrow.center());
|
||||
let layout = h.layout();
|
||||
assert!(floating_window(&layout, "layers").is_none(), "the old floating window is removed");
|
||||
assert_eq!(group_of(&layout, &"layers".into()), members, "all tabs move together");
|
||||
assert!(members.iter().all(|p| in_iconic_column(&layout, p)));
|
||||
assert_eq!(end_columns(&layout).len(), columns, "reuse the existing icon rail");
|
||||
assert!(h.app.ui.open_panel.is_none(), "the group stays collapsed until its icon is clicked");
|
||||
let icon = h.hit(&Hit::Icon("layers".into())).expect("Layers now has a rail icon");
|
||||
h.click(icon.center());
|
||||
assert_eq!(h.app.ui.open_panel.as_deref(), Some("layers"));
|
||||
assert!(h.tab("layers").is_some(), "the collapsed group can reopen as a flyout");
|
||||
assert!(collapse_to_icons_button(&h, "layers").is_some(), "the reopened flyout also offers collapse to icons");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn floating_collapse_arrow_leaves_other_floating_groups_alone() {
|
||||
let mut h = Harness::new();
|
||||
h.app.run("window.panel.float", json!({"panel": "layers"})).unwrap();
|
||||
h.app.run("window.panel.float", json!({"panel": "color", "below": "layers"})).unwrap();
|
||||
for _ in 0..3 {
|
||||
h.frame(vec![]);
|
||||
}
|
||||
let arrow = collapse_to_icons_button(&h, "layers").expect("floating group has its own arrow");
|
||||
h.click(arrow.center());
|
||||
let layout = h.layout();
|
||||
assert!(in_iconic_column(&layout, "layers"));
|
||||
assert!(floating_window(&layout, "color").is_some(), "another group in the same window stays floating");
|
||||
assert!(!in_iconic_column(&layout, "color"));
|
||||
assert_eq!(main_panels(&layout), ["properties", "libraries"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn floating_collapse_arrow_creates_a_rail_when_none_exists_and_keeps_the_menu() {
|
||||
let mut h = Harness::new();
|
||||
h.app.run("window.panel.float", json!({"panel": "layers"})).unwrap();
|
||||
let mut layout = h.layout();
|
||||
if let Some(Node::Split { second, .. }) = layout.root.as_mut()
|
||||
&& let Node::Dock { columns, .. } = second.as_mut()
|
||||
{
|
||||
columns.retain(|column| !column.iconic);
|
||||
}
|
||||
assert!(icon_column(&end_columns(&layout)).is_none());
|
||||
h.app.ui.docking = Some(layout);
|
||||
for _ in 0..3 {
|
||||
h.frame(vec![]);
|
||||
}
|
||||
let panel = "layers".to_string();
|
||||
let menu =
|
||||
h.ctx.read_response(area_id().with(("header", &panel)).with(("panel-menu", &panel))).expect("the existing panel menu remains available").rect;
|
||||
let arrow = collapse_to_icons_button(&h, "layers").expect("floating group offers collapse");
|
||||
assert!(!menu.intersects(arrow), "menu and arrow have separate hit targets");
|
||||
h.click(menu.center());
|
||||
assert!(egui::Popup::is_any_open(&h.ctx), "the existing menu still opens");
|
||||
h.press(egui::Key::Escape, egui::Modifiers::NONE);
|
||||
h.click(arrow.center());
|
||||
let layout = h.layout();
|
||||
assert!(in_iconic_column(&layout, "layers"));
|
||||
assert!(floating_window(&layout, "layers").is_none());
|
||||
assert!(h.app.ui.open_panel.is_none(), "collapse does not immediately open a flyout");
|
||||
assert_eq!(main_panels(&layout), ["properties", "libraries"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rail_flyout_collapse_arrow_folds_the_panel_back_without_moving_it() {
|
||||
let mut h = Harness::new();
|
||||
let icon = h.hit(&Hit::Icon("swatches".into())).unwrap();
|
||||
h.click(icon.center());
|
||||
let before = h.layout();
|
||||
let arrow = collapse_to_icons_button(&h, "swatches").expect("opened rail panels offer collapse to icons");
|
||||
h.click(arrow.center());
|
||||
assert!(h.app.ui.open_panel.is_none());
|
||||
assert!(craft_ui::docking::flyout::<String>(&h.ctx, area_id()).is_none());
|
||||
assert_eq!(h.layout(), before, "folding a flyout leaves the dock layout intact");
|
||||
let icon = h.hit(&Hit::Icon("swatches".into())).expect("the rail icon stays available");
|
||||
h.click(icon.center());
|
||||
assert_eq!(h.app.ui.open_panel.as_deref(), Some("swatches"));
|
||||
assert!(h.tab("swatches").is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rail_flyout_collapse_arrow_closes_the_opening_icon_after_switching_tabs() {
|
||||
let mut h = Harness::new();
|
||||
h.app.ui.docking = Some(h.layout());
|
||||
let icon = h.hit(&Hit::Icon("swatches".into())).unwrap();
|
||||
h.click(icon.center());
|
||||
let brushes = h.tab("brushes").unwrap();
|
||||
h.click(brushes.center());
|
||||
assert_eq!(h.app.ui.open_panel.as_deref(), Some("swatches"));
|
||||
let before = h.layout();
|
||||
let arrow = collapse_to_icons_button(&h, "brushes").expect("the active tab has a collapse arrow");
|
||||
h.click(arrow.center());
|
||||
assert!(h.app.ui.open_panel.is_none());
|
||||
assert!(craft_ui::docking::flyout::<String>(&h.ctx, area_id()).is_none());
|
||||
assert_eq!(h.layout(), before, "all tabs and their active state stay in the rail");
|
||||
}
|
||||
|
||||
+4
-1
@@ -1,6 +1,6 @@
|
||||
# Gaps
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-07 · **Change:** minor (G9: EPS gradients drawn as abutting strips import as one gradient, without seams; Tools stays beside the canvas with left-side panels; new gradients follow document color mode; Selection QoL: marquee modes and visible feedback, #5313; Indian-language hyphenation review and license allowlist, #4947; opening a slow file shows a progress window, part of #5930; Hindi catalog and contributor loanword preferences; agent wording pass complete; wider native proofreading welcome; an image zoomed in on is separated, colorized or proofed only where it shows (#4155); scores and estimates unchanged; a document's first frame renders on the render worker, so opening a heavy file no longer freezes the window; scores and estimates unchanged; large images in CMYK documents are shrunk before they are separated into ink planes (#4155); scores and estimates unchanged; `.ai` paths with a Calligraphic brush open brushed, with the 6D Art Pen's channels, and Windows reads the pen's tilt and barrel rotation, #372; scores and estimates unchanged; Compound Shapes add-on, beyond Illustrator; scores and estimates unchanged; Contour add-on, beyond Illustrator; scores and estimates unchanged; a slow first frame is waited for instead of restarting without the graphics adapter, and the menu bar no longer waits for the installed fonts' scan; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-08 · **Change:** minor (floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; G9: EPS gradients drawn as abutting strips import as one gradient, without seams; Tools stays beside the canvas with left-side panels; new gradients follow document color mode; Selection QoL: marquee modes and visible feedback, #5313; Indian-language hyphenation review and license allowlist, #4947; opening a slow file shows a progress window, part of #5930; Hindi catalog and contributor loanword preferences; agent wording pass complete; wider native proofreading welcome; an image zoomed in on is separated, colorized or proofed only where it shows (#4155); scores and estimates unchanged; a document's first frame renders on the render worker, so opening a heavy file no longer freezes the window; scores and estimates unchanged; large images in CMYK documents are shrunk before they are separated into ink planes (#4155); scores and estimates unchanged; `.ai` paths with a Calligraphic brush open brushed, with the 6D Art Pen's channels, and Windows reads the pen's tilt and barrel rotation, #372; scores and estimates unchanged; Compound Shapes add-on, beyond Illustrator; scores and estimates unchanged; Contour add-on, beyond Illustrator; scores and estimates unchanged; a slow first frame is waited for instead of restarting without the graphics adapter, and the menu bar no longer waits for the installed fonts' scan; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
@@ -113,6 +113,8 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
|
||||
|
||||
## G1 Interaction fidelity
|
||||
|
||||
- **Panel collapse controls (#5925, in part):** The small » in a floating tab-group header moves that group into the side icon rail, reusing the existing rail or creating one. An opened rail panel has the same control to fold its flyout back into the rail, including after a tab switch. Both controls preserve the tab group and its menu; other floating groups and full-width docked headers stay as they were. Pointer regressions in `crates/ui-egui/src/panel_docking/tests.rs` cover grouped tabs, reopening, separate groups, a missing rail, the menu and flyout closure after switching tabs; `native_tests.rs` covers the native-window click and removal of the empty palette. Scores and estimates unchanged.
|
||||
|
||||
- **Done:** Distance Guides ([public page](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/use-distance-guides-for-accurate-placement.html)): with Smart Guides on and something selected, the Selection tool with Alt/Option held over another object shows the horizontal and vertical distances between them, and over an empty part of an artboard the selection's distances from its four edges, labelled in the General unit; Preferences › Smart Guides › Invoke Distance Guides turns them off. Hover only, as documented: not while moving or nudging. No entry closed; estimates unchanged.
|
||||
|
||||
- **Done (part of #4028):** Recolor Artwork › Assign: double-clicking a row's new color opens the Color Picker, as the [public page](https://helpx.adobe.com/illustrator/desktop/manage-colors/modify-colors/assign-new-colors-to-selected-artwork.html) describes. OK sets the row's new color in the model it was picked in, so a CMYK mix is no longer a hex value converted back; Cancel leaves the row. A Lab new color is kept, but can't be typed yet: the picker's Lab fields work in RGB, as for fill and stroke. **Missing:** Lab entry in the picker, swatch libraries in the picker and the row's right-click menu (Pick from Swatches, Limit to Library).
|
||||
@@ -831,6 +833,7 @@ trap, and the defaults (0.25 pt, 100%, 40%) are VectorCraft's own, the pages giv
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-08 | minor | Floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part); tab groups remain intact. Scores and 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 |
|
||||
| 2026-11-05 | minor | G15: nearby hits on plain long strokes and outlines check their candidate segments directly; a 200,000-anchor middle hit took about 11–13 ms before and 1.0–1.4 ms after in paired runs. An inside-bounds miss fell from 18.7 ms to 1.6 ms. Estimate unchanged; idle budget run still open. |
|
||||
| 2026-11-05 | minor | G15: an image zoomed in on is separated, colorized or proofed only for the tiles in view (#4155); 100% zoom on that file 8.6 s → 0.05 s. Estimate unchanged. |
|
||||
|
||||
+3
-2
@@ -1,6 +1,6 @@
|
||||
# Roadmap: milestones and what's next
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (M4/M7: `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; M4/M7: `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; numeric fields step on wheel hover without taking keyboard focus, #5949) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; M4/M7: `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; M4/M7: `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; numeric fields step on wheel hover without taking keyboard focus, #5949) · **Target:** Adobe Illustrator 2026 (30.x)
|
||||
|
||||
## Feature submission evidence
|
||||
|
||||
@@ -153,7 +153,7 @@ native wording review, layout checks and a web Devanagari font remain. [Notes](l
|
||||
| M12 | Advanced color & art (CMYK/ICC, separations, Gradient Mesh, Live Paint, Image Trace, Graphs) | 🟡 Gradient Mesh, Live Paint, Image Trace (junction-returning pieces retain their positioned boundaries; a simpler contour walk with identical output; an edge's sub-pixel shift is the same from either side; notches read only their own piece; collapsed loops keep few segments; large images trace with a warning; Auto Color keeps rare distinct colours; quick presets trace at once and the Image Trace button opens the adjustment controls; Properties reopens them; tiny fitted contours retain islands and holes; Create Strokes: exact crop EDT, Guo–Hall thinning and maximal-disc graph simplification; 15 presets, the 14 documented ones each with an enhanced version; Ignore Color, Shapes, Auto Grouping and Gradients options; measured in `image-trace-quality.md`; closed outlines retain their interior after straightening; the Flat Logo mode places edges to a fraction of a pixel, restores sharp corners, snaps lines and circles, and refuses gradients; live trace result bounds and selection highlights exclude the retained bitmap except in source-image views), 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) | 🟡 MCP `add_text` exposes existing vertical point, area and path type; broader scripting and batch work remains | 18–27 |
|
||||
| M13 | Automation (Actions ✅ record/playback, persisted; variables, scripting, batch) | 🟡 | 18–27 |
|
||||
| M14 | 1.0 polish (preferences, shortcut editor, workspaces, accessibility, packaging for all OSes) (status messages and readouts use stable responsive allocations sized for localized labels and ordinary coordinates; sample widths cached by language/unit/DPI; regression-tested and contributor-confirmed 2026-10-19; estimates unchanged) | 🟡 Dock groups collapse to their tabs by a double-click on a tab (UI.8); Preferences › General › Constrain Angle acts for the arrow keys, Shift drags and new objects (#394, 2026-10-21); 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; other apps' text shortcut sets imported), workspaces (each keeps its layout changes until Reset <name>, UI.7), Window › Help Bar shown or hidden apart from the status bar and kept in saved workspaces, panels docked by dragging into any dock group (as tabs or a top/bottom split) or into the icon column, and closed from a dock tab's context menu (UI.6), 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); titled PDF exports set `DisplayDocTitle` for viewers and accessibility checkers (#3646); accessibility and broader Windows/Linux packaging/QA pending | 18–28 |
|
||||
| M14 | 1.0 polish (preferences, shortcut editor, workspaces, accessibility, packaging for all OSes) (status messages and readouts use stable responsive allocations sized for localized labels and ordinary coordinates; sample widths cached by language/unit/DPI; regression-tested and contributor-confirmed 2026-10-19; estimates unchanged) | 🟡 Dock groups collapse to their tabs by a double-click on a tab (UI.8); Preferences › General › Constrain Angle acts for the arrow keys, Shift drags and new objects (#394, 2026-10-21); 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; other apps' text shortcut sets imported), workspaces (each keeps its layout changes until Reset <name>, UI.7), Window › Help Bar shown or hidden apart from the status bar and kept in saved workspaces, panels docked by dragging into any dock group (as tabs or a top/bottom split) or into the icon column, and closed from a dock tab's context menu (UI.6), 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); titled PDF exports set `DisplayDocTitle` for viewers and accessibility checkers (#3646); accessibility and broader Windows/Linux packaging/QA pending; floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part), with pointer and native-window regression coverage; estimates unchanged | 18–28 |
|
||||
| — | Interaction fidelity pass (every tool's modifiers, Properties panel per context, isolation, nuance; width-point creation and adjoining adjustment regression-tested; dialog layout reviewed) | ⬜ (new ruler-guide Alt orientation implemented; existing-guide Escape cancellation regression-tested; broader pass remains) | 60–90 |
|
||||
| — | Hardening at scale (big-file corpus, fuzzing, cross-platform + browser QA) | 🟡 PngSuite integrity and bounded fetching verified (#1729); real-world corpus/manual QA remain | 50–80 |
|
||||
| M14 follow-up | Color Picker layout (#2008) | ✅ Compact controls, centered header, stacked actions, aligned fields and screen sampling; narrow windows scroll vertically | Included above |
|
||||
@@ -190,6 +190,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-08 | minor | Floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part); tab groups remain intact. Scores and estimates unchanged. |
|
||||
| 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. |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Parity with Adobe Illustrator
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (`.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; `.ai` and EPS import: older Art brush objects read; 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)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; `.ai` and EPS import: older Art brush objects read; 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
|
||||
|
||||
@@ -233,7 +233,7 @@ dominate; 3D, graphs and automation are specialist). They sum to 105. Feature de
|
||||
| Export for Screens, Asset Export, slices, Save for Web | 3 | 85% | anti-aliasing ghost borders (#983); TIFF choices and settings are implemented | 2–4 | estimated |
|
||||
| Print, colour management, separations, flattener | 3 | 80% | print preview fidelity | 2–4 | estimated |
|
||||
| Automation (Actions, Variables, scripting, batch) | 3 | 60% | a scripting surface (Illustrator: JavaScript/ExtendScript), Batch, image and graph variables, dataset import/export | 8–14 | estimated; MCP `add_text` exposes existing vertical type; score and hours unchanged |
|
||||
| UI chrome (panels, Properties, workspaces, preferences) | 6 | 80% | Properties per context, 3 Preferences options stored but unused (#394, audited 2026-10-30: Open Documents As Tabs, Scratch Disks, legacy vector clipboard), SVG Interactivity, context menus beyond the canvas, accessibility | 13–20 | estimated; floating panels in OS windows of their own on the desktop (#4135), score and hours unchanged; macOS save Format menu implemented (#866), score and hours unchanged; Eyedropper cursor and precise setting checked (#1269), OS visual QA unverified; numeric-field focus and normal text drags regression-tested (#1326, in part); movements below the click threshold still select all; artboard navigation synchronizes the tool selection before destructive actions; status messages and readouts use stable responsive allocations sized for localized labels and ordinary coordinates (sample widths cached by language/unit/DPI; regression and contributor original-file evidence below; scores/hours unchanged); effect dialogs' colour fields show a swatch that opens the Color Picker (#5365), score and hours unchanged |
|
||||
| UI chrome (panels, Properties, workspaces, preferences) | 6 | 80% | Properties per context, 3 Preferences options stored but unused (#394, audited 2026-10-30: Open Documents As Tabs, Scratch Disks, legacy vector clipboard), SVG Interactivity, context menus beyond the canvas, accessibility | 13–20 | estimated; floating panels in OS windows of their own on the desktop (#4135), score and hours unchanged; macOS save Format menu implemented (#866), score and hours unchanged; Eyedropper cursor and precise setting checked (#1269), OS visual QA unverified; numeric-field focus and normal text drags regression-tested (#1326, in part); movements below the click threshold still select all; artboard navigation synchronizes the tool selection before destructive actions; status messages and readouts use stable responsive allocations sized for localized labels and ordinary coordinates (sample widths cached by language/unit/DPI; regression and contributor original-file evidence below; scores/hours unchanged); effect dialogs' colour fields show a swatch that opens the Color Picker (#5365), score and hours unchanged; floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part), scores and hours unchanged |
|
||||
| Libraries, Links, Package | 2 | 82% | shared libraries, linked graphics, gradients and patterns as library items (no cloud by design) | 2–3 | estimated |
|
||||
| Generative AI (Text to Vector, Generative Recolor, Shape Fill, Concept to Vector, Prompt to Edit, Rewrite Text) | 2 | 0% | a pluggable provider and the panels; needs an owner decision on models | 30–60 | measured (absent) |
|
||||
| **Feature depth** | **105** | **~75%** | | **~250–420** | |
|
||||
@@ -539,6 +539,8 @@ since 2026-10-01.
|
||||
|
||||
## Evidence: what ships today, by area
|
||||
|
||||
- **Panel collapse controls (#5925, in part):** The small » in a floating tab-group header moves that group into the side icon rail, reusing the existing rail or creating one. An opened rail panel has the same control to fold its flyout back into the rail, including after a tab switch. Both controls preserve the tab group and its menu; other floating groups and full-width docked headers stay as they were. Pointer regressions in `crates/ui-egui/src/panel_docking/tests.rs` cover grouped tabs, reopening, separate groups, a missing rail, the menu and flyout closure after switching tabs; `native_tests.rs` covers the native-window click and removal of the empty palette. Scores and estimates unchanged.
|
||||
|
||||
- 3D lighting presets: `crates/effects/src/lighting_presets.rs`, `crates/engine/src/cmd/effectcmd/lighting_presets_tests.rs` and the pointer tests in `crates/ui-egui/src/dialogs/revolve/tests.rs` cover recognition, atomic targeting, native round trips, preview, Cancel, reset and Undo.
|
||||
- Window › 3D and Materials panel: `crates/ui-egui/src/panels/three_d.rs` applies a 3D effect by object type (Plane, Extrude, Revolve, Inflate), changes an applied one in place (`effect.setParams` `threeDType`) and edits it with the options dialog's Object, Rotation and Lighting controls, one undo step per change; `crates/engine/src/cmd/effectcmd/three_d_type_tests.rs` and `crates/ui-egui/src/panels/tests_three_d.rs` cover it. No Materials tab.
|
||||
|
||||
@@ -1886,6 +1888,7 @@ Edit › Copy and Cut put the selected ruler guides on the clipboard, with the s
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-08 | minor | Floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part); tab groups remain intact. Scores and estimates unchanged. |
|
||||
| 2026-11-07 | minor | File formats and Type core: `.ai`/EPS type keeps its paragraphs' tab stops, and right-aligned area type sets the text after a line's last tab against the right edge, the text before it at its stops (#6147); evidence added; scores unchanged. |
|
||||
| 2026-11-07 | minor | File formats and Type core: `.ai`/EPS type from the text document lays its lines out closer to the file's page: auto leading per line at the paragraph's share, the paragraph's hyphenation options, possibly more of the last lines the file sets in an area frame of its own, and a line a soft return ends justified (#6137); evidence added; scores unchanged. |
|
||||
| 2026-11-06 | minor | `.ai` and EPS import: Art brushes in the older form of their objects come into the Brushes panel; evidence updated; scores unchanged. |
|
||||
|
||||
+4
-1
@@ -1,6 +1,6 @@
|
||||
# UI and interaction parity
|
||||
|
||||
> **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)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; 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
|
||||
|
||||
@@ -341,6 +341,8 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
|
||||
|
||||
## Panels, Control bar, Properties, context menus
|
||||
|
||||
- **Panel collapse controls (#5925, in part):** The small » in a floating tab-group header moves that group into the side icon rail, reusing the existing rail or creating one. An opened rail panel has the same control to fold its flyout back into the rail, including after a tab switch. Both controls preserve the tab group and its menu; other floating groups and full-width docked headers stay as they were. Pointer regressions in `crates/ui-egui/src/panel_docking/tests.rs` cover grouped tabs, reopening, separate groups, a missing rail, the menu and flyout closure after switching tabs; `native_tests.rs` covers the native-window click and removal of the empty palette. Scores and estimates unchanged.
|
||||
|
||||
| Interaction | Status | Evidence |
|
||||
|---|---|---|
|
||||
| Selection artwork drag hovers a visible document tab to activate it, then drops editable artwork on its canvas | done | `tests_document_drag`, `edit.dropSelection`, lossless resource and one-step Undo regressions; overflow and cross-window drag remain separate |
|
||||
@@ -488,6 +490,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-11-08 | minor | Floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part); tab groups remain intact. Scores and estimates unchanged. |
|
||||
| 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 |
|
||||
|
||||
Reference in New Issue
Block a user