From a6bac3298b4fb7043742f22cad9b20128ec13a6c Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 16 Oct 2026 12:21:31 -0700 Subject: [PATCH] Contextual Task Bar: the Artboard tool offers Resize, Duplicate, Rearrange All and Export The Contextual Task Bar showed only with the selection tools and selected objects. With the Artboard tool it never showed. With the Artboard tool, the bar now sits under the selected artboards and offers Resize, which opens Artboard Options for the active artboard; Duplicate, which copies each selected artboard in one undo step and selects the copies; Rearrange All, with two or more artboards, which opens Rearrange All Artboards; and Export, which opens Export for Screens with the selected artboards checked. The objects' Fill, Recolor Artwork and Lock buttons are not shown with the Artboard tool. "Resize" has a row in every catalog. Docs: docs/target-app-parity.md. --- crates/ui-egui/src/canvas.rs | 233 ++++++++++++++++++++++------ crates/ui-egui/src/i18n/bg.tsv | 1 + crates/ui-egui/src/i18n/cs.tsv | 1 + crates/ui-egui/src/i18n/de.tsv | 1 + crates/ui-egui/src/i18n/el.tsv | 1 + crates/ui-egui/src/i18n/es.tsv | 1 + crates/ui-egui/src/i18n/fi.tsv | 1 + crates/ui-egui/src/i18n/fr.tsv | 1 + crates/ui-egui/src/i18n/hu.tsv | 1 + crates/ui-egui/src/i18n/it.tsv | 1 + crates/ui-egui/src/i18n/ja.tsv | 1 + crates/ui-egui/src/i18n/ko.tsv | 1 + crates/ui-egui/src/i18n/pl.tsv | 1 + crates/ui-egui/src/i18n/pt-br.tsv | 1 + crates/ui-egui/src/i18n/ru.tsv | 1 + crates/ui-egui/src/i18n/sv.tsv | 1 + crates/ui-egui/src/i18n/uk.tsv | 1 + crates/ui-egui/src/i18n/vi.tsv | 1 + crates/ui-egui/src/i18n/zh-hans.tsv | 1 + crates/ui-egui/src/i18n/zh-hant.tsv | 1 + docs/target-app-parity.md | 3 +- 21 files changed, 205 insertions(+), 50 deletions(-) diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index a7664f6ec..88e9bf265 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -2722,17 +2722,28 @@ pub(crate) fn has_colors(doc: &vectorcraft_doc::Document, ids: &[vectorcraft_doc }) } +/// The bounds of the artboards the Artboard tool has selected ([`crate::panels::artboards::picked`]). +fn picked_artboards_bounds(app: &VectorcraftApp) -> Option { + let st = app.session.active()?; + let picked = crate::panels::artboards::picked(app, st.doc.artboards.len()); + picked.iter().filter_map(|i| st.doc.artboards.get(*i)).map(|a| a.rect).reduce(|a, b| a.union(b)) +} + /// The Contextual Task Bar: a floating pill under the selection with the most likely next actions. +/// With the Artboard tool it sits under the selected artboards and offers Resize (Artboard +/// Options), Duplicate, Rearrange All (with two or more artboards) and Export (Export for Screens). /// Its handle drags it anywhere on the canvas; unpinned it keeps that offset as it follows the /// selection, pinned it stays put ([`crate::state::TaskBarPlace`]). fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { let t = Tokens::get(ui.ctx()); - if app.session.active().is_none_or(|st| st.selection.is_empty()) - || !matches!(app.session.tool_id(), "selection" | "directSelection" | "groupSelection") + let on_artboards = app.session.tool_id() == "artboard"; + if !on_artboards + && (app.session.active().is_none_or(|st| st.selection.is_empty()) + || !matches!(app.session.tool_id(), "selection" | "directSelection" | "groupSelection")) { return; } - let Some(b) = app.selection_bounds() else { return }; + let Some(b) = (if on_artboards { picked_artboards_bounds(app) } else { app.selection_bounds() }) else { return }; let Some(st) = app.session.active() else { return }; let n = st.selection.len(); let first = st.selection.objects.first().and_then(|id| st.doc.node(*id)).cloned(); @@ -2745,35 +2756,47 @@ fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { f.name.as_deref() == Some("Image Trace") && f.children().is_some_and(|c| c.first().is_some_and(|i| matches!(i.kind, NodeKind::Image(_)))) }); let mut items: Vec<(&str, &str, &str)> = vec![]; // (label, icon, command) - // As in the reference app, an image offers Image Trace and Crop Image, and traced art offers - // Expand (). - if n == 1 && is_image { - items.push((tl!("Image Trace"), "image", TASK_BAR_IMAGE_TRACE)); - items.push((tl!("Crop Image"), "dc-pf-crop", "ui.cropImage")); + if on_artboards { + // As in the reference app, the Artboard tool offers to resize, duplicate and export the + // artboards (), + // and to rearrange them (). + items.push((tl!("Resize"), "move-diagonal-2", TASK_BAR_ARTBOARD_OPTIONS)); + items.push((tl!("Duplicate"), "copy", TASK_BAR_DUPLICATE_ARTBOARDS)); + if st.doc.artboards.len() > 1 { + items.push((tl!("Rearrange All"), "dc-rearrange", TASK_BAR_REARRANGE_ARTBOARDS)); + } + items.push((tl!("Export"), "share-2", "file.exportForScreens")); + } else { + // As in the reference app, an image offers Image Trace and Crop Image, and traced art offers + // Expand (). + if n == 1 && is_image { + items.push((tl!("Image Trace"), "image", TASK_BAR_IMAGE_TRACE)); + items.push((tl!("Crop Image"), "dc-pf-crop", "ui.cropImage")); + } + // Direct-selected anchors take the place of a path's Offset Path and Simplify. + let anchors = !st.selection.anchors.is_empty(); + if anchors { + items.push((tl!("Remove Anchor Points"), "pen-tool-delete", "path.removeAnchors")); + items.push((tl!("Cut Path"), "scissors", "path.cutAtAnchors")); + } + if n > 1 { + items.push((tl!("Group"), "group", "object.group")); + items.push((tl!("Unite"), "squares-unite", "object.pathfinder.unite")); + } else if is_trace { + items.push((tl!("Expand"), "maximize-2", "imageTrace.expand")); + } else if is_group { + items.push((tl!("Ungroup"), "ungroup", "object.ungroup")); + items.push((tl!("Isolate"), "square-dashed", "object.isolate")); + } else if is_text { + items.push((tl!("Create Outlines"), "type", "type.createOutlines")); + } else if is_image { + // Offset Path and Simplify don't apply to an image. + } else if !anchors { + items.push((tl!("Offset Path"), "square-dashed", "object.path.offsetPath")); + items.push((tl!("Simplify"), "spline", "object.path.simplify")); + } + items.push((tl!("Duplicate"), "copy", "edit.duplicate")); } - // Direct-selected anchors take the place of a path's Offset Path and Simplify. - let anchors = !st.selection.anchors.is_empty(); - if anchors { - items.push((tl!("Remove Anchor Points"), "pen-tool-delete", "path.removeAnchors")); - items.push((tl!("Cut Path"), "scissors", "path.cutAtAnchors")); - } - if n > 1 { - items.push((tl!("Group"), "group", "object.group")); - items.push((tl!("Unite"), "squares-unite", "object.pathfinder.unite")); - } else if is_trace { - items.push((tl!("Expand"), "maximize-2", "imageTrace.expand")); - } else if is_group { - items.push((tl!("Ungroup"), "ungroup", "object.ungroup")); - items.push((tl!("Isolate"), "square-dashed", "object.isolate")); - } else if is_text { - items.push((tl!("Create Outlines"), "type", "type.createOutlines")); - } else if is_image { - // Offset Path and Simplify don't apply to an image. - } else if !anchors { - items.push((tl!("Offset Path"), "square-dashed", "object.path.offsetPath")); - items.push((tl!("Simplify"), "spline", "object.path.simplify")); - } - items.push((tl!("Duplicate"), "copy", "edit.duplicate")); let fill = first.as_ref().map(|f| f.appearance.fill_paint()).unwrap_or_default(); let colored = has_colors(&st.doc, &st.selection.objects); let doc = st.uid; @@ -2840,24 +2863,27 @@ fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { run = Some((*cmd, json!({}))); } } - let (r, resp) = ui.allocate_exact_size(vec2(26.0, 30.0), Sense::click()); - widgets::paint_chip(ui, egui::Rect::from_center_size(r.center(), vec2(16.0, 16.0)), &fill); - ui.painter().rect_stroke( - egui::Rect::from_center_size(r.center(), vec2(16.0, 16.0)), - 0.0, - Stroke::new(1.0, t.button_border), - StrokeKind::Outside, - ); - if resp.on_hover_text(tl!("Fill")).clicked() { - app.session.fill_active = true; - app.ui.open_panel = Some("swatches".into()); - } - // Recolor Artwork, when the selection has colours to recolour. - if colored && widgets::icon_button(ui, "palette", tl!("Recolor Artwork"), false, 30.0).clicked() { - run = Some(("ui.recolorDialog", json!({}))); - } - if widgets::icon_button(ui, "lock", tl!("Lock (⌘2)"), false, 30.0).clicked() { - run = Some(("object.lock", json!({}))); + // The selected objects' Fill, Recolor Artwork and Lock, left out with the Artboard tool. + if !on_artboards { + let (r, resp) = ui.allocate_exact_size(vec2(26.0, 30.0), Sense::click()); + widgets::paint_chip(ui, egui::Rect::from_center_size(r.center(), vec2(16.0, 16.0)), &fill); + ui.painter().rect_stroke( + egui::Rect::from_center_size(r.center(), vec2(16.0, 16.0)), + 0.0, + Stroke::new(1.0, t.button_border), + StrokeKind::Outside, + ); + if resp.on_hover_text(tl!("Fill")).clicked() { + app.session.fill_active = true; + app.ui.open_panel = Some("swatches".into()); + } + // Recolor Artwork, when the selection has colours to recolour. + if colored && widgets::icon_button(ui, "palette", tl!("Recolor Artwork"), false, 30.0).clicked() { + run = Some(("ui.recolorDialog", json!({}))); + } + if widgets::icon_button(ui, "lock", tl!("Lock (⌘2)"), false, 30.0).clicked() { + run = Some(("object.lock", json!({}))); + } } let more = widgets::icon_button(ui, "ellipsis", tl!("More Options"), false, 30.0); egui::Popup::menu(&more).show(|ui| { @@ -2894,6 +2920,11 @@ fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { // Image Trace traces the image at once with the Default preset. As a quick trace from // Properties or the Control bar, it leaves the Image Trace panel as it was. Some((TASK_BAR_IMAGE_TRACE, _)) => crate::menus::invoke(app, "imageTrace.make", json!({"preset": "Default"})), + // The Artboard tool's buttons: Artboard Options for the active artboard, as Properties' + // icon opens it; Duplicate for every selected artboard; Rearrange All Artboards. + Some((TASK_BAR_ARTBOARD_OPTIONS, _)) => crate::menus::invoke(app, "tool.options", json!({"tool": "artboard"})), + Some((TASK_BAR_DUPLICATE_ARTBOARDS, _)) => crate::panels::artboards::duplicate_picked(app), + Some((TASK_BAR_REARRANGE_ARTBOARDS, _)) => crate::panels::artboards::open_rearrange(app), Some((c, p)) => crate::menus::invoke(app, c, p), None => {} } @@ -2901,6 +2932,12 @@ fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { /// The Contextual Task Bar's Image Trace button: a trace with the Default preset. const TASK_BAR_IMAGE_TRACE: &str = "taskBar.imageTrace"; +/// The Contextual Task Bar's Resize button with the Artboard tool: Artboard Options. +const TASK_BAR_ARTBOARD_OPTIONS: &str = "taskBar.artboardOptions"; +/// The Contextual Task Bar's Duplicate button with the Artboard tool. +const TASK_BAR_DUPLICATE_ARTBOARDS: &str = "taskBar.duplicateArtboards"; +/// The Contextual Task Bar's Rearrange All button with the Artboard tool. +const TASK_BAR_REARRANGE_ARTBOARDS: &str = "taskBar.rearrangeArtboards"; #[cfg(test)] mod tests { @@ -6033,6 +6070,104 @@ mod tests { assert_eq!(images, 0, "only the traced paths are kept"); } + /// The labels the Contextual Task Bar draws in a headless frame (after one to lay it out), with + /// their rects; none while it isn't shown. Icons, which draw as ⚠ without an image loader, are + /// left out. + fn task_bar_labels(app: &mut VectorcraftApp, ctx: &egui::Context) -> Vec<(String, egui::Rect)> { + frame(app, ctx, vec![]); + let raw = egui::RawInput { screen_rect: Some(egui::Rect::from_min_size(Pos2::ZERO, vec2(800.0, 600.0))), ..Default::default() }; + let mut out = ctx.run_ui(raw, |ui| show(app, ui)); + out.textures_delta.clear(); + let Some(bar) = task_bar_rect(ctx) else { return vec![] }; + out.shapes + .iter() + .filter_map(|c| match &c.shape { + egui::Shape::Text(t) if bar.contains(t.visual_bounding_rect().center()) && t.galley.text() != "⚠" => { + Some((t.galley.text().to_string(), t.visual_bounding_rect())) + } + _ => None, + }) + .collect() + } + + /// With the Artboard tool, the Contextual Task Bar sits under the selected artboards and offers + /// Resize, Duplicate, Rearrange All (with two or more artboards) and Export instead of the + /// selected objects' actions. With the Selection tool it offers the objects' actions again, and + /// with nothing selected it doesn't show. + #[test] + fn the_task_bar_offers_the_artboard_tools_actions() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 200, "height": 100, "artboards": 3})).unwrap(); + app.select_tool("selection"); + let ctx = egui::Context::default(); + let names = |app: &mut VectorcraftApp| task_bar_labels(app, &ctx).into_iter().map(|(t, _)| t).collect::>(); + assert!(names(&mut app).is_empty() && task_bar_rect(&ctx).is_none(), "nothing selected: no bar"); + + app.select_tool("artboard"); + crate::panels::artboards::select(&mut app, 1); + // Artboard 2 in the middle of the window, at 100%. + let b = app.session.active().unwrap().doc.artboards[1].rect; + let view = app.view_mut().unwrap(); + (view.center, view.zoom) = (b.center(), 1.0); + assert_eq!(names(&mut app), ["Resize", "Duplicate", "Rearrange All", "Export"]); + // Under artboard 2. + let xf = Xf::new(app.canvas_rect.unwrap(), app.view().unwrap()); + let bar = task_bar_rect(&ctx).unwrap(); + let center = xf.to_screen(b.center()); + assert!(bar.top() > xf.to_screen(Point::new(b.x0, b.y1)).y && bar.x_range().contains(center.x), "{bar:?} under {b:?}"); + + // A selected object with the Selection tool: its own actions, none of the artboards'. + app.select_tool("selection"); + app.session.execute("shape.rectangle", &json!({"x": 20, "y": 20, "width": 40, "height": 40})).unwrap(); + assert_eq!(names(&mut app), ["Offset Path", "Simplify", "Duplicate"]); + + // One artboard: nothing to rearrange. + app.session.execute("file.new", &json!({"width": 200, "height": 100})).unwrap(); + app.select_tool("artboard"); + assert_eq!(names(&mut app), ["Resize", "Duplicate", "Export"]); + } + + /// The Artboard tool's Contextual Task Bar buttons act on its selected artboards: Resize opens + /// Artboard Options for the active one, Rearrange All opens Rearrange All Artboards, Export opens + /// Export for Screens with the selected artboards checked, and Duplicate copies each selected + /// artboard in one undo step and selects the copies. + #[test] + fn the_artboard_tools_task_bar_buttons_act_on_the_selected_artboards() { + let mut app = VectorcraftApp::new(Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 200, "height": 100, "artboards": 3})).unwrap(); + app.select_tool("artboard"); + crate::panels::artboards::select(&mut app, 2); + app.session.set_tool_option("selected", &json!([2, 0])); + let ctx = egui::Context::default(); + let press = |app: &mut VectorcraftApp, label: &str| { + let labels = task_bar_labels(app, &ctx); + let at = labels.iter().find(|(t, _)| t == label).unwrap_or_else(|| panic!("{label} in {labels:?}")).1.center(); + let button = + |pressed| egui::Event::PointerButton { pos: at, button: egui::PointerButton::Primary, pressed, modifiers: Default::default() }; + frame(app, &ctx, vec![egui::Event::PointerMoved(at)]); + frame(app, &ctx, vec![button(true)]); + frame(app, &ctx, vec![button(false)]); + app.ui.dialog.take() + }; + let d = press(&mut app, "Resize").expect("Artboard Options"); + assert_eq!((d.kind.as_str(), d.f64("index", 9.0)), ("artboardOptions", 2.0)); + let d = press(&mut app, "Rearrange All").expect("Rearrange All Artboards"); + assert_eq!(d.kind, crate::dialogs::rearrange_artboards::KIND); + let d = press(&mut app, "Export").expect("Export for Screens"); + assert_eq!((d.kind.as_str(), &d.fields["boards"]), (crate::dialogs::EXPORT_FOR_SCREENS, &json!([true, false, true]))); + + let boards = + |app: &VectorcraftApp| app.session.active().unwrap().doc.artboards.iter().map(|a| (a.name.clone(), a.rect.size())).collect::>(); + let before = boards(&app); + assert!(press(&mut app, "Duplicate").is_none()); + let after = boards(&app); + assert_eq!(after[..3], before[..]); + assert_eq!(after[3..], [("Artboard 1 copy".to_string(), before[0].1), ("Artboard 3 copy".to_string(), before[2].1)]); + assert_eq!(app.session.tool_options()["selected"], json!([4, 3]), "the copies are selected, artboard 3's copy active"); + app.run("edit.undo", json!({})).unwrap(); + assert_eq!(boards(&app), before, "one undo step"); + } + /// The Contextual Task Bar's handle drags it: unpinned it keeps that offset as it follows the /// selection, pinned it stays put when the selection changes, unpinned again it follows from /// there, and Reset Bar Position puts it back under the selection. It never leaves the canvas, diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 07acc690a..f428a745c 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -539,6 +539,7 @@ Contextual Task Bar Контекстна лента със задачи Pin Bar Position Закачане на позицията на лентата Reset Bar Position Нулиране на позицията на лентата + Resize Преоразмеряване Contrast Контраст Control Контрол Convert Конвертиране diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 2dce66934..9291096a7 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -578,6 +578,7 @@ Contextual Task Bar Kontextový panel úloh Pin Bar Position Připnout polohu panelu Reset Bar Position Obnovit polohu panelu + Resize Změnit velikost Tools Nástroje Toolbars Panely nástrojů Advanced Rozšířené diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index bcf6bbaea..6dfe3c698 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -539,6 +539,7 @@ Contextual Task Bar Kontextbezogene Taskleiste Pin Bar Position Position der Leiste fixieren Reset Bar Position Position der Leiste zurücksetzen + Resize Größe ändern Contrast Kontrast Control Steuerung Convert Konvertieren diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 49fb28f05..f9e32919f 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -513,6 +513,7 @@ Contextual Task Bar Γραμμή εργασιών βάσει περιβάλλοντος Pin Bar Position Σταθεροποίηση θέσης γραμμής Reset Bar Position Επαναφορά θέσης γραμμής + Resize Αλλαγή μεγέθους Contrast Αντίθεση Control Έλεγχος Convert Μετατροπή diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 921922338..00d9b6d8d 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -554,6 +554,7 @@ Contextual Task Bar Barra de tareas contextual Pin Bar Position Fijar posición de la barra Reset Bar Position Restablecer posición de la barra + Resize Cambiar tamaño Contrast Contraste Control Control Convert Convertir diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index e269f1e92..3c8b7c130 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -569,6 +569,7 @@ Contextual Task Bar Kontekstuaalinen tehtäväpalkki Pin Bar Position Kiinnitä palkin sijainti Reset Bar Position Palauta palkin sijainti + Resize Muuta kokoa Tools Työkalut Toolbars Työkalurivit Advanced Lisäasetukset diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 8beb15634..b8e18521e 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -555,6 +555,7 @@ Contextual Task Bar Barre des tâches contextuelle Pin Bar Position Épingler la position de la barre Reset Bar Position Réinitialiser la position de la barre + Resize Redimensionner Contrast Contraste Control Contrôle Convert Convertir diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 1c274251d..1c28d5cd6 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -3008,6 +3008,7 @@ mark-style Japanese Japán Reshape Spine Segment Gerincszakasz átformálása Reshape Tool Átformálás Reshape Type Area Szövegterület átformálása + Resize Átméretezés Resize panel window Panelablak átméretezése Resize panels Panelek átméretezése Resolution: Felbontás: diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 740380524..8e81b3fee 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -554,6 +554,7 @@ Contextual Task Bar Barra attività contestuale Pin Bar Position Fissa posizione barra Reset Bar Position Ripristina posizione barra + Resize Ridimensiona Contrast Contrasto Control Controllo Convert Converti diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 90b6fced7..3f30506bc 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -556,6 +556,7 @@ Contextual Task Bar コンテキストタスクバー Pin Bar Position バーの位置を固定 Reset Bar Position バーの位置をリセット + Resize サイズを変更 Tools ツール Toolbars ツールバー Advanced 詳細 diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 5669ab573..3d43b0bc3 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -554,6 +554,7 @@ Contextual Task Bar 상황별 작업 표시줄 Pin Bar Position 표시줄 위치 고정 Reset Bar Position 표시줄 위치 재설정 + Resize 크기 조절 Tools 도구 Toolbars 도구 모음 Advanced 고급 diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index b6f68e556..8ef8cefe3 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -528,6 +528,7 @@ Contextual Task Bar Kontekstowy pasek zadań Pin Bar Position Przypnij położenie paska Reset Bar Position Resetuj położenie paska + Resize Zmień rozmiar Tools Narzędzia Toolbars Paski narzędzi Advanced Zaawansowany diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index da7197b4b..08c059363 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -509,6 +509,7 @@ Contextual Task Bar Barra de Tarefas Contextual Pin Bar Position Fixar Posição da Barra Reset Bar Position Redefinir Posição da Barra + Resize Redimensionar Control Controle Convert Converter Convert All Spot Colors to Process Converter Todas as Cores Spot em Processo diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index b264be235..f62ba11c5 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -552,6 +552,7 @@ Contextual Task Bar Контекстная панель задач Pin Bar Position Закрепить положение панели Reset Bar Position Сбросить положение панели + Resize Изменить размер Contrast Контраст Control Control Convert Преобразовать diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 035c62cc8..f95092ebb 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -541,6 +541,7 @@ Contextual Task Bar Kontextuellt aktivitetsfält Pin Bar Position Fäst fältets position Reset Bar Position Återställ fältets position + Resize Ändra storlek Contrast Kontrast Control Kontroll Convert Konvertera diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 330f39db5..65033a930 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -548,6 +548,7 @@ Contextual Task Bar Контекстна панель завдань Pin Bar Position Закріпити положення панелі Reset Bar Position Скинути положення панелі + Resize Змінити розмір Contrast Контраст Control Панель керування Convert Перетворити diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 22106e438..c3618f4ec 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -2753,6 +2753,7 @@ mark-style Japanese Kiểu Nhật Reshape Spine Segment Định hình lại đoạn trục Reshape Tool Công cụ Định hình lại Reshape Type Area Định hình lại vùng chữ + Resize Thay đổi kích thước Resize panel window Thay đổi kích thước cửa sổ bảng Resize panels Thay đổi kích thước bảng Resolution: Độ phân giải: diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index cdd384790..17bd30ed9 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -509,6 +509,7 @@ Contextual Task Bar 上下文任务栏 Pin Bar Position 固定任务栏位置 Reset Bar Position 重置任务栏位置 + Resize 调整大小 Contrast 对比 Control 控制栏 Convert 转换 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 6e83953d5..1bcfcf028 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -523,6 +523,7 @@ Contextual Task Bar 快捷工作列 Pin Bar Position 釘選工作列位置 Reset Bar Position 重設工作列位置 + Resize 調整大小 Contrast 對比 Control 控制 Convert 轉換 diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index e2b73a0d5..58335bb85 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -432,8 +432,9 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Hint bar, contextual task bar, 19 dock panels with ≡ menus. - Numeric fields keep the pointer's pressed character as the text-drag start when movement exceeds the click threshold (#1326, in part); a completed focus click selects the unit too. Pixel/point, plain and optional fields are regression-tested in both directions, before/after focus, with replacement text; keyboard focus, double-click, arrows and wheel keep their prior behavior. Native egui input confirms partial replacement gives 17 px instead of 700 px, one Undo/Redo, window captures and an exact saved-document read in a separate CLI process. Short movements still counted as clicks can select all; that case remains open. - Unit fields and length strings take `%` in arithmetic: `3cm*50%` reads as 1.5 cm, and `50pt+25%` reads as 62.5 pt. - - The contextual task bar's handle drags it anywhere on the canvas (kept whole on it as the window resizes); unpinned it keeps that offset as it follows the selection, and its More Options menu hides it, pins its position across selection changes (`window.taskBar.pin`), resets it under the selection (`window.taskBar.reset`) and shows the Properties panel. As in Illustrator the position isn't kept across launches. Not done: the bar's tool-specific variants (only the selection tools show it). + - The contextual task bar's handle drags it anywhere on the canvas (kept whole on it as the window resizes); unpinned it keeps that offset as it follows the selection, and its More Options menu hides it, pins its position across selection changes (`window.taskBar.pin`), resets it under the selection (`window.taskBar.reset`) and shows the Properties panel. As in Illustrator the position isn't kept across launches. Not done: the bar's variants for other tools (only the selection tools and the Artboard tool show it). - With one image selected, the contextual task bar offers Image Trace, which traces the image at once with the Default preset (the Image Trace panel is left as it was), and Crop Image; with an Image Trace object it offers Expand, which keeps only the traced paths. As in Illustrator ("Selecting images displays options like Image Trace, Retype, and Crop Image"; "You can also quickly expand traced artwork from the bar": [Contextual Task Bar announcement](https://community.adobe.com/announcements-651/contextual-task-bar-in-illustrator-2025-quick-actions-when-you-need-them-1546580)). Offset Path and Simplify, which don't apply to an image, aren't offered for one. Not done: Retype, which the app doesn't have. + - With the Artboard tool, the contextual task bar sits under the selected artboards and offers Resize (Artboard Options for the active artboard), Duplicate (each selected artboard, in one undo step, the copies then selected), Rearrange All (with two or more artboards) and Export (Export for Screens with the selected artboards checked). As in Illustrator ("the Contextual Task Bar reveals options to resize, duplicate, and export artboards": the [Contextual Task Bar announcement](https://community.adobe.com/questions-652/contextual-task-bar-in-illustrator-2025-quick-actions-when-you-need-them-1546580); Rearrange and Export on the bar: [Managing Artboards in Illustrator](https://creativepro.com/managing-artboards-in-illustrator/)). - Tool flyouts tear off: dragging or clicking the tear-off bar on a flyout's right side (or releasing the long press over it) floats its group as a strip of tool buttons, moved by the same bar and put back in the toolbar by the × at its top (`window.floatTools`). While a group floats, the presses that would open its flyout raise the strip instead. Floating strips are kept with the preferences and in user workspaces. Not done: turning a strip vertical, docking it. - Screen readers get a name for the Tools panel's tool buttons, flyout rows, floating strips and tear-off bars, and for the icon buttons, text buttons, toggles, checkboxes, radio buttons, tabs, chips, sliders, angle dials and combo boxes that panels and dialogs draw with `crates/ui-egui/src/widgets.rs`: the text, label or tool tip each one shows, or a name of its own where it shows none (the angle dial, the spinner, the preset arrow, the color wheel and the Reference Point's squares). The tool buttons, toggles, checkboxes, radio buttons, tabs and the Reference Point's nine squares also report whether they are on, an icon button that is lit, such as the chosen Align To button, reports that it is pressed, and the angle dial reports its angle. The status bar's First, Previous, Next and Last Artboard buttons have tool tips. - Panels undock (#495): a dock tab, a panel icon or a popped-out panel's title dragged out of the dock floats that panel inside the window, and the strip right of the dock's tabs floats the whole tabbed group. A floating group moves by its title bar, a tab dragged out of it floats on its own, and dropped on another group's title bar or tabs it stacks with that group; dropped on the dock, which lights up under the pointer, its panels go back where they live (the tabbed group or the icon column), as its × and the panel menus' Float Panel / Dock Panel do (`window.panel.float`, `window.panel.dock`). The Tools panel floats by its title bar and docks again on the window's left edge. Floating groups stack into sets: dropped on the upper or lower half of another group (a line shows where), a group joins it right above or below it, and the set's groups stack top to bottom, each showing one panel, moving together by the top group's title bar, whose × docks them all; a group dragged by its tab strip leaves the set (`window.panel.float {below, above}`). A double-click on a tab collapses a floating group to its tabs (`collapsed`). Sets dropped on the left edge of the icon column or of another dock column dock as dock columns (a multi-column dock, `window.panel.dock {column}`), whose groups stack, tab and tear out as a floating set's do, and whose top strip floats the column again. Floating panels, their sets and the Tools panel's position are kept with the preferences and in user workspaces, and clamped into a smaller window. Not done: panels in separate OS windows, docking a panel into the default tabbed group it doesn't belong to, reordering tabs by dragging, collapsing a set or dock column to icons, dock columns right of the default dock, resizing floating panels and the groups of a set.