diff --git a/crates/ui-egui/src/canvas/task_bar.rs b/crates/ui-egui/src/canvas/task_bar.rs index d606f630d5..c870110434 100644 --- a/crates/ui-egui/src/canvas/task_bar.rs +++ b/crates/ui-egui/src/canvas/task_bar.rs @@ -55,6 +55,42 @@ fn selection_tool_with_selection(app: &VectorcraftApp) -> bool { && app.session.active().is_some_and(|st| !st.selection.is_empty()) } +/// Whether the bar shows its blank canvas actions, Draw (Pencil) and Place Image: a selection tool is +/// in use and the active document has no artwork, only layers and guides. +fn blank_canvas(app: &VectorcraftApp) -> bool { + fn no_art(nodes: &[std::sync::Arc]) -> bool { + nodes.iter().all(|n| n.is_guide() || (n.is_layer() && n.children().is_none_or(|c| no_art(c)))) + } + matches!(app.session.tool_id(), "selection" | "directSelection" | "groupSelection") + && app.session.active().is_some_and(|st| st.selection.is_empty() && no_art(&st.doc.layers)) +} + +/// Where the bar's top edge centers, on screen: under the lowest corner on screen of the selected +/// artboards or the selection, centered on them (the view may be rotated); on a blank canvas, which +/// has no selection, [`blank_canvas_anchor`]. None with nothing to sit under. +fn bar_anchor(app: &mut VectorcraftApp, xf: &Xf, on_artboards: bool, blank: bool) -> Option { + if blank { + return Some(blank_canvas_anchor(app, xf)); + } + let b = if on_artboards { picked_artboards_bounds(app) } else { app.selection_bounds() }?; + let lowest = xf.quad(b).iter().map(|c| c.y).fold(f32::NEG_INFINITY, f32::max); + Some(pos2(xf.to_screen(b.center()).x, lowest)) +} + +/// Where the blank canvas bar's top edge centers, on screen: under the bottom of the active artboard +/// when part of it is in view, else the canvas's center. +fn blank_canvas_anchor(app: &VectorcraftApp, xf: &Xf) -> Pos2 { + let board = app.session.active().and_then(|st| { + let last = st.doc.artboards.len().checked_sub(1)?; + st.doc.artboards.get(app.view().map_or(0, |v| v.artboard).min(last)).map(|a| a.rect) + }); + let shown = board.map(|b| (b, xf.quad(b))).filter(|(_, q)| egui::Rect::from_points(q).intersects(xf.rect)); + match shown { + Some((b, q)) => pos2(xf.to_screen(b.center()).x, q.iter().map(|c| c.y).fold(f32::NEG_INFINITY, f32::max)), + None => xf.rect.center(), + } +} + /// Whether a Type tool edits type, or has type selected: its bar then shows only the text /// formatting controls. fn type_tool_on_type(app: &VectorcraftApp) -> bool { @@ -75,6 +111,8 @@ pub(crate) fn task_bar_shows_type(app: &VectorcraftApp) -> bool { /// While a Type tool edits type, or has type selected, the bar shows only those controls and the /// Fill. When one row would be wider than the canvas, the font controls take a row of their own, /// narrowed to fit it, and the actions wrap onto rows under the handle's ([`bar_layout`]). +/// On a blank canvas, a document with no artwork and a selection tool in use, it sits under the +/// active artboard and offers Draw (Pencil) and Place Image. /// 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`]). pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { @@ -83,13 +121,15 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { // Text added with a Type tool shows its formatting controls, as the help page describes // (). let typing = type_tool_on_type(app); - if !(on_artboards || typing || selection_tool_with_selection(app)) { + // A blank canvas offers Draw (Pencil) and Place Image, as the same help page describes. + let blank = !on_artboards && blank_canvas(app); + if !(on_artboards || typing || blank || selection_tool_with_selection(app)) { return; } // Selected type has the font controls too, as the help page describes // (). let fonts = task_bar_shows_type(app); - let Some(b) = (if on_artboards { picked_artboards_bounds(app) } else { app.selection_bounds() }) else { return }; + let Some(anchor) = bar_anchor(app, xf, on_artboards, blank) 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)); @@ -137,6 +177,10 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { items.push((tl!("Rearrange All"), "dc-rearrange", TASK_BAR_REARRANGE_ARTBOARDS, none())); } items.push((tl!("Export"), "share-2", "file.exportForScreens", none())); + } else if blank { + // Draw (Pencil) switches to the Pencil tool; Place Image picks files for the Place dialog. + items.push((tl!("Draw (Pencil)"), "pencil", "tool.select", json!({"tool": "pencil"}))); + items.push((tl!("Place Image"), "image", "file.place", none())); } else if !typing { // The selection tools' object actions. A Type tool's bar leaves them out, Create Outlines // and the type conversions included. @@ -234,27 +278,20 @@ pub(super) fn task_bar(app: &mut VectorcraftApp, ui: &mut Ui, xf: &Xf) { let paints = chip_paints(app, ui.ctx()); let colored = has_colors(&st.doc, &st.selection.objects); let doc = st.uid; - // Under the selection's lowest corner on screen, centered on the selection (the view may be - // rotated). - let lowest = xf.quad(b).iter().map(|c| c.y).fold(f32::NEG_INFINITY, f32::max); - let anchor = pos2(xf.to_screen(b.center()).x, lowest); let bounds = xf.rect.shrink(8.0); // The canvas's width less the frame's margins and stroke. let room = bounds.width() - BAR_FRAME; // On a canvas too narrow for the type controls at their narrowest, the bar leaves them out. let fonts = fonts && room >= crate::panels::character::task_bar_min_room(); // The selected objects' Fill, Stroke, Recolor Artwork (when they have colours to recolour) and - // Lock (a basic shape's properties instead), left out with the Artboard tool. A Type tool's bar - // has the Fill alone. - let chips: Vec = [ - (!on_artboards, Chip::Fill), - (!on_artboards && !typing, Chip::Stroke), - (!on_artboards && !typing && colored, Chip::Recolor), - (!on_artboards && !typing, Chip::Lock), - ] - .into_iter() - .filter_map(|(shown, chip)| shown.then_some(chip)) - .collect(); + // Lock (a basic shape's properties instead), left out with the Artboard tool and on a blank + // canvas, which have no selected objects. A Type tool's bar has the Fill alone. + let objects = !on_artboards && !blank; + let chips: Vec = + [(objects, Chip::Fill), (objects && !typing, Chip::Stroke), (objects && !typing && colored, Chip::Recolor), (objects && !typing, Chip::Lock)] + .into_iter() + .filter_map(|(shown, chip)| shown.then_some(chip)) + .collect(); let widths: Vec = items.iter().map(|(label, ..)| action_width(ui, label)).collect(); let font_w = fonts.then(|| { let w = crate::panels::character::TASK_BAR_WIDTHS; diff --git a/crates/ui-egui/src/canvas/tests.rs b/crates/ui-egui/src/canvas/tests.rs index 2a7162f9a1..4d609f2b5e 100644 --- a/crates/ui-egui/src/canvas/tests.rs +++ b/crates/ui-egui/src/canvas/tests.rs @@ -10333,7 +10333,7 @@ fn task_bar_labels(app: &mut VectorcraftApp, ctx: &egui::Context) -> Vec<(String /// 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. +/// with nothing in the document the blank canvas actions. #[test] fn the_task_bar_offers_the_artboard_tools_actions() { let mut app = VectorcraftApp::new(Session::new(), Default::default()); @@ -10341,7 +10341,7 @@ fn the_task_bar_offers_the_artboard_tools_actions() { 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"); + assert_eq!(names(&mut app), ["Draw (Pencil)", "Place Image"], "a blank canvas"); app.select_tool("artboard"); crate::panels::artboards::select(&mut app, 1); diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 4b5982cecd..f0ab11fc4a 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -691,6 +691,8 @@ font-technology Variable Променливи Crop Image Изрязване на изображение Image Tools Инструменти за изображения Remove Background… Премахване на фона… + Draw (Pencil) Рисуване (Молив) + Place Image Вмъкване на изображение Remove Background Премахване на фона Background Фон Edge Color Цвят на ръба diff --git a/crates/ui-egui/src/i18n/bs.tsv b/crates/ui-egui/src/i18n/bs.tsv index 1576b72373..f73b5a407f 100644 --- a/crates/ui-egui/src/i18n/bs.tsv +++ b/crates/ui-egui/src/i18n/bs.tsv @@ -201,6 +201,8 @@ Crop Image Izreži sliku Image Tools Alati za slike Remove Background… Ukloni pozadinu… + Draw (Pencil) Crtaj (olovka) + Place Image Postavi sliku Remove Background Ukloni pozadinu Background Pozadina Edge Color Boja ruba diff --git a/crates/ui-egui/src/i18n/ca.tsv b/crates/ui-egui/src/i18n/ca.tsv index 99ecf7dd95..ddfed44ad0 100644 --- a/crates/ui-egui/src/i18n/ca.tsv +++ b/crates/ui-egui/src/i18n/ca.tsv @@ -1006,6 +1006,8 @@ antiAlias Sharp Nítid Crop Image Retalla la imatge Image Tools Eines d’imatge Remove Background… Elimina el fons… + Draw (Pencil) Dibuixa (llapis) + Place Image Col·loca una imatge Remove Background Elimina el fons Background Fons Edge Color Color de la vora diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index d58e5bd051..3b46d0600a 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -207,6 +207,8 @@ Crop Image Oříznout obraz Image Tools Nástroje obrazu Remove Background… Odstranit pozadí… + Draw (Pencil) Kreslit (tužka) + Place Image Umístit obraz Remove Background Odstranit pozadí Background Pozadí Edge Color Barva okraje diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 5fad269299..81b6195bdd 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -194,6 +194,8 @@ Crop Image Beskær billede Image Tools Billedværktøjer Remove Background… Fjern baggrund… + Draw (Pencil) Tegn (blyant) + Place Image Placér billede Remove Background Fjern baggrund Background Baggrund Edge Color Kantfarve diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 0143f55457..8f6bc1aeeb 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -684,6 +684,8 @@ font-technology Variable Variabel Crop Image Bild beschneiden Image Tools Bildwerkzeuge Remove Background… Hintergrund entfernen… + Draw (Pencil) Zeichnen (Buntstift) + Place Image Bild platzieren Remove Background Hintergrund entfernen Background Hintergrund Edge Color Randfarbe diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 172e91d0ef..7b61589c18 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -665,6 +665,8 @@ font-technology Variable Μεταβλητές Crop Image Περικοπή εικόνας Image Tools Εργαλεία εικόνας Remove Background… Αφαίρεση φόντου… + Draw (Pencil) Σχεδίαση (μολύβι) + Place Image Τοποθέτηση εικόνας Remove Background Αφαίρεση φόντου Background Φόντο Edge Color Χρώμα άκρης diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 865b2b532f..89ea2c088c 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -705,6 +705,8 @@ font-technology Variable Variables Crop Image Recortar imagen Image Tools Herramientas de imagen Remove Background… Quitar fondo… + Draw (Pencil) Dibujar (Lápiz) + Place Image Colocar imagen Remove Background Quitar fondo Background Fondo Edge Color Color del borde diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 88581f6ace..655936f6c2 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -214,6 +214,8 @@ Crop Image Rajaa kuva Image Tools Kuvatyökalut Remove Background… Poista tausta… + Draw (Pencil) Piirrä (lyijykynä) + Place Image Sijoita kuva Remove Background Poista tausta Background Tausta Edge Color Reunan väri diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 0063c984a1..07209a70dd 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -707,6 +707,8 @@ font-technology Variable Variables Crop Image Recadrer l’image Image Tools Outils d’image Remove Background… Supprimer l’arrière-plan… + Draw (Pencil) Dessiner (Crayon) + Place Image Importer une image Remove Background Supprimer l’arrière-plan Background Arrière-plan Edge Color Couleur du bord diff --git a/crates/ui-egui/src/i18n/hi.tsv b/crates/ui-egui/src/i18n/hi.tsv index a31993c84a..4a11fad73d 100644 --- a/crates/ui-egui/src/i18n/hi.tsv +++ b/crates/ui-egui/src/i18n/hi.tsv @@ -1140,6 +1140,8 @@ antiAlias Sharp शार्प Crop Image इमेज क्रॉप करें Image Tools इमेज टूल Remove Background… पृष्ठभूमि हटाएँ… + Draw (Pencil) ड्रा (पेंसिल) + Place Image इमेज प्लेस करें Remove Background पृष्ठभूमि हटाएँ Background पृष्ठभूमि Edge Color किनारे का रंग diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 7b5d4eb4ae..925670bbc3 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -906,6 +906,8 @@ font-technology Variable Változó Crop Image Kép körülvágása Image Tools Képeszközök Remove Background… Háttér eltávolítása… + Draw (Pencil) Rajzolás (Ceruza) + Place Image Kép elhelyezése Remove Background Háttér eltávolítása Background Háttér Edge Color Szélszín diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index e27253376e..f8069f6a71 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -194,6 +194,8 @@ Crop Image Pangkas gambar Image Tools Alat Gambar Remove Background… Hapus latar belakang… + Draw (Pencil) Menggambar (pensil) + Place Image Tempatkan gambar Remove Background Hapus latar belakang Background Latar belakang Edge Color Warna tepi diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 7d74485aac..af875b4e42 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -705,6 +705,8 @@ font-technology Variable Variabili Crop Image Ritaglia immagine Image Tools Strumenti immagine Remove Background… Rimuovi sfondo… + Draw (Pencil) Disegna (Matita) + Place Image Inserisci immagine Remove Background Rimuovi sfondo Background Sfondo Edge Color Colore del bordo diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 31526809eb..e6d40c9ae5 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -242,6 +242,8 @@ digits Malayalam マラヤーラム数字 Crop Image 画像の切り抜き Image Tools 画像ツール Remove Background… 背景を削除… + Draw (Pencil) 描画 (鉛筆) + Place Image 画像を配置 Remove Background 背景を削除 Background 背景 Edge Color 端の色 diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index a6429f06ee..fc649cd87a 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -166,6 +166,8 @@ Crop Image გამოსახულების ჩამოჭრა Image Tools გამოსახულების ხელსაწყოები Remove Background… ფონის მოშორება… + Draw (Pencil) ხატვა (ფანქარი) + Place Image გამოსახულების განთავსება Remove Background ფონის მოშორება Background ფონი Edge Color კიდის ფერი diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 7141602a3d..71a45e307d 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -235,6 +235,8 @@ digits Malayalam 말라얄람어 Crop Image 이미지 자르기 Image Tools 이미지 도구 Remove Background… 배경 제거… + Draw (Pencil) 그리기(연필) + Place Image 이미지 가져오기 Remove Background 배경 제거 Background 배경 Edge Color 가장자리 색상 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index 476f707d22..a0953094fa 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -712,6 +712,8 @@ Crop Image Afbeelding bijsnijden Image Tools Afbeeldingshulpmiddelen Remove Background… Achtergrond verwijderen… + Draw (Pencil) Tekenen (Potlood) + Place Image Afbeelding plaatsen Remove Background Achtergrond verwijderen Background Achtergrond Edge Color Randkleur diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index db2f6449c6..5b7b3e60ac 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -171,6 +171,8 @@ Crop Image Kadruj obraz Image Tools Narzędzia obrazu Remove Background… Usuń tło… + Draw (Pencil) Rysuj (ołówek) + Place Image Umieść obraz Remove Background Usuń tło Background Tło Edge Color Kolor krawędzi diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 2d5047337a..90825f967f 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -631,6 +631,8 @@ antiAlias Sharp Nítido Crop Image Recortar Imagem Image Tools Ferramentas de imagem Remove Background… Remover Fundo… + Draw (Pencil) Desenhar (Lápis) + Place Image Colocar Imagem Remove Background Remover Fundo Background Fundo Edge Color Cor da Borda diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index f4220735a4..a235e2c9b3 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -703,6 +703,8 @@ font-technology Variable Вариативные Crop Image Обрезать изображение Image Tools Инструменты изображения Remove Background… Удалить фон… + Draw (Pencil) Рисовать (Карандаш) + Place Image Поместить изображение Remove Background Удалить фон Background Фон Edge Color Цвет края diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 1649d0140e..b491d33649 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -693,6 +693,8 @@ font-technology Variable Variabla Crop Image Beskär bild Image Tools Bildverktyg Remove Background… Ta bort bakgrund… + Draw (Pencil) Rita (blyertspenna) + Place Image Placera bild Remove Background Ta bort bakgrund Background Bakgrund Edge Color Kantfärg diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 0fcf935e7e..0ecfe1dd0a 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -685,6 +685,8 @@ font-technology Variable Değişken Crop Image Resmi Kırp Image Tools Resim Araçları Remove Background… Arka Planı Kaldır… + Draw (Pencil) Çiz (Kurşun Kalem) + Place Image Resim Yerleştir Remove Background Arka Planı Kaldır Background Arka Plan Edge Color Kenar Rengi diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 168bd4ac84..c375bedfa2 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -699,6 +699,8 @@ font-technology Variable Варіативні Crop Image Обрізати зображення Image Tools Інструменти зображення Remove Background… Видалити фон… + Draw (Pencil) Малювати (Олівець) + Place Image Помістити зображення Remove Background Видалити фон Background Фон Edge Color Колір краю diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index bfc6b7876a..442690385b 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -806,6 +806,8 @@ font-technology Variable Biến thiên Crop Image Cắt xén ảnh Image Tools Công cụ hình ảnh Remove Background… Xóa nền… + Draw (Pencil) Vẽ (Bút chì) + Place Image Đặt hình ảnh Remove Background Xóa nền Background Nền Edge Color Màu cạnh diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 0d760c21ed..810b1b4463 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -650,6 +650,8 @@ antiAlias Sharp 锐利 Crop Image 裁剪图像 Image Tools 图像工具 Remove Background… 移除背景… + Draw (Pencil) 绘制 (铅笔) + Place Image 置入图像 Remove Background 移除背景 Background 背景 Edge Color 边缘颜色 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 899520f58e..19c572e3b3 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -667,6 +667,8 @@ font-technology Variable 可變 Crop Image 裁切影像 Image Tools 影像工具 Remove Background… 移除背景… + Draw (Pencil) 繪製 (鉛筆) + Place Image 置入影像 Remove Background 移除背景 Background 背景 Edge Color 邊緣顏色 diff --git a/crates/ui-egui/src/tests/taskbar.rs b/crates/ui-egui/src/tests/taskbar.rs index 9768813a70..30757d3f52 100644 --- a/crates/ui-egui/src/tests/taskbar.rs +++ b/crates/ui-egui/src/tests/taskbar.rs @@ -536,6 +536,103 @@ fn the_task_bar_follows_the_active_document() { assert!(!locked(&app, rect), "the button doesn't lock the shape"); } +/// A blank canvas, a new document with a selection tool in use, offers Draw (Pencil) and Place +/// Image centered under the active artboard (under the canvas's center when the artboard is out of +/// view), with the handle and More Options but none of a selection's actions or its Fill, Stroke, +/// Recolor Artwork and Lock. Other tools, artwork in the document and a selection each leave the +/// blank canvas actions out; ruler guides do not count as artwork. +#[test] +fn a_blank_canvas_offers_draw_and_place_image() { + let mut app = app(); + let board = app.session.active().unwrap().doc.artboards[0].rect; + // The artboard at 50% in the middle of the window, after the first frame fits it in. + bar(&mut app); + let view = app.view_mut().unwrap(); + (view.center, view.zoom) = (board.center(), 0.5); + for tool in ["selection", "directSelection", "groupSelection"] { + app.run("tool.select", json!({ "tool": tool })).unwrap(); + let (ctx, texts) = bar_texts(&mut app); + assert!(at(&texts, "Draw (Pencil)").x < at(&texts, "Place Image").x, "{tool}: {texts:?}"); + let shown = canvas::task_bar_rect(&ctx).unwrap(); + let xf = canvas::Xf::new(app.canvas_rect.unwrap(), app.view().unwrap()); + let bottom = xf.quad(board).iter().map(|c| c.y).fold(f32::NEG_INFINITY, f32::max); + let middle = xf.to_screen(board.center()).x; + assert!(shown.top() > bottom && (shown.center().x - middle).abs() < 2.0, "{tool}: {shown:?} centered under {board:?}"); + assert!(app.canvas_rect.unwrap().contains_rect(shown), "{tool}: {shown:?} on the canvas"); + } + // The artboard out of view: under the canvas's center. + app.view_mut().unwrap().center = board.center() + vectorcraft_geom::Vec2::new(5000.0, 5000.0); + let (ctx, _) = bar(&mut app); + let (shown, middle) = (canvas::task_bar_rect(&ctx).unwrap(), app.canvas_rect.unwrap().center()); + assert!((shown.center().x - middle.x).abs() < 2.0 && shown.top() > middle.y && shown.top() < middle.y + 60.0, "{shown:?} under {middle:?}"); + app.view_mut().unwrap().center = board.center(); + + let tree = crate::widgets::accessibility_tests::tree(|ui| canvas::show(&mut app, ui)); + for label in ["Draw (Pencil)", "Place Image", "More Options"] { + assert_eq!(crate::widgets::accessibility_tests::named(&tree, label).role(), egui::accesskit::Role::Button, "{label}"); + } + let named = |label: &str| tree.iter().any(|n| n.label().is_some_and(|l| l.starts_with(label))); + for label in ["Fill", "Stroke", "Recolor Artwork", "Lock", "Duplicate"] { + assert!(!named(label), "{label} is left out"); + } + + for tool in ["pen", "rectangle", "type", "artboard"] { + app.run("tool.select", json!({ "tool": tool })).unwrap(); + let (_, texts) = bar_texts(&mut app); + assert!(!has(&texts, "Draw (Pencil)") && !has(&texts, "Place Image"), "{tool}: {texts:?}"); + } + + app.run("tool.select", json!({"tool": "selection"})).unwrap(); + let rect = id(app.run("shape.rectangle", json!({"x": 40, "y": 40, "width": 80, "height": 60})).unwrap()); + select(&mut app, &[rect]); + let (_, texts) = bar_texts(&mut app); + assert!(has(&texts, "Duplicate") && !has(&texts, "Draw (Pencil)"), "{texts:?}"); + select(&mut app, &[]); + let (ctx, _) = bar(&mut app); + assert!(canvas::task_bar_rect(&ctx).is_none(), "artwork with nothing selected: no bar"); + select(&mut app, &[rect]); + app.run("edit.clear", json!({})).unwrap(); + app.run("guide.add", json!({"pos": 50})).unwrap(); + assert!(has(&bar_texts(&mut app).1, "Draw (Pencil)"), "a ruler guide is not artwork"); +} + +/// Draw (Pencil) switches to the Pencil tool. Place Image picks files for the Place dialog; +/// canceling the pick leaves the document as it was. +#[test] +fn the_blank_canvas_bar_draws_with_the_pencil_and_places_an_image() { + let mut app = app(); + app.run("tool.select", json!({"tool": "selection"})).unwrap(); + click(&mut app, "Draw (Pencil)"); + assert_eq!(app.session.tool_id(), "pencil"); + + app.run("tool.select", json!({"tool": "selection"})).unwrap(); + app.services.pick_open = Some(Box::new(|_: &crate::FilePick| None)); + click(&mut app, "Place Image"); + assert!(app.ui.dialog.is_none(), "canceled: no Place dialog"); + assert!(app.session.active().unwrap().doc.layers.iter().all(|l| l.children().is_none_or(|c| c.is_empty())), "nothing placed"); + + let path = std::env::temp_dir().join("vectorcraft-task-bar-place.png").to_string_lossy().into_owned(); + let picked = path.clone(); + app.services.pick_open = Some(Box::new(move |_: &crate::FilePick| Some(picked.clone()))); + click(&mut app, "Place Image"); + let d = app.ui.dialog.take().expect("the Place dialog"); + assert_eq!((d.kind.as_str(), &d.fields["files"]), (crate::dialogs::place::KIND, &json!([{ "path": path }]))); +} + +/// On a canvas too narrow for one row, the blank canvas bar's actions wrap onto a second row and the +/// bar stays whole on the canvas. +#[test] +fn the_blank_canvas_bar_wraps_on_a_narrow_canvas() { + let mut app = app(); + app.run("tool.select", json!({"tool": "selection"})).unwrap(); + let ctx = egui::Context::default(); + crate::theme::install_fonts(&ctx); + let (texts, r, canvas) = bar_texts_on_screen(&mut app, &ctx, vec2(240.0, 400.0), 3); + assert!(canvas.contains_rect(r), "{r:?} on {canvas:?}"); + let (draw, place) = (at(&texts, "Draw (Pencil)"), at(&texts, "Place Image")); + assert!(place.y > draw.y + 20.0, "Place Image wraps under Draw (Pencil): {draw:?} {place:?}"); +} + /// The Type tools whose bar shows the font and Fill controls. const TYPE_TOOLS: [&str; 6] = ["type", "areaType", "typeOnPath", "verticalType", "verticalAreaType", "verticalTypeOnPath"]; diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index 5628710333..67b7934b4d 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -865,6 +865,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - A click on another object moves the contextual task bar under it without hiding it for the press; it gives way only to a drag (#4681). - 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. This follows Illustrator's bar: for a selected image it includes Image Trace, Retype and Crop Image, and traced artwork can be expanded from it ([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). This follows Illustrator's bar: while the Artboard tool is in use, it has options for resizing, duplicating and exporting 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/)). + - On a blank canvas the contextual task bar offers Draw (Pencil), which switches to the Pencil tool, and Place Image, which picks files for the Place dialog, as the help page lists for a new document (). VectorCraft's own rules: the bar shows them while a selection tool is in use in a document with no artwork (layers and guides only), sits under the active artboard (under the canvas's center when the artboard is out of view), and leaves out the Fill, Stroke, Recolor Artwork and Lock controls. Not done: Generate Vectors. Evidence: `a_blank_canvas_offers_draw_and_place_image`, `the_blank_canvas_bar_draws_with_the_pencil_and_places_an_image` and `the_blank_canvas_bar_wraps_on_a_narrow_canvas` in `crates/ui-egui/src/tests/taskbar.rs`. - The contextual task bar offers more next steps by selection (): an open path Join and Edit Path (the Direct Selection tool), a closed path Edit Path, a compound path Edit Path and Release Compound Path, a clipping group Edit Clipping Path and Release Clipping Mask instead of Ungroup and Isolate, an image selected with vector art Make Clipping Mask instead of Unite, point or area type Convert To Area Type or Convert To Point Type beside Create Outlines, and type on a path, for which the overview names an item to add an effect and rearrange the text, Type on a Path Options (`type.pathOptions`; its options: ) beside Create Outlines. Evidence: `crates/ui-egui/src/tests/taskbar.rs`. - While a Type tool (Type, Area Type, Type on a Path and their vertical versions) adds or edits type, the contextual task bar shows the font family menu, the font style menu, the Font Size combo and the Fill chip (); the Touch Type tool shows no bar, as the same page describes. Type selected with a selection tool shows the same three font controls beside its Create Outlines, conversion and Type on a Path items (). The controls act as the Character panel's do: on the characters a Type tool selects, else on every selected type object, with the size left blank where the sizes differ. VectorCraft's own rules: a Type tool's bar leaves out the object actions, the Stroke chip, Recolor Artwork and Lock; a Type tool shows no bar before it adds type or with other art selected; type selected with other art shows no font controls; the font controls go on a second row when one row would be wider than the canvas, and narrow to fit a canvas narrower than they are; while a Type tool edits type, the Fill chip shows the edited characters' fill ("?" where it differs). Evidence: `the_type_tools_show_font_and_fill_controls`, `the_type_tools_show_no_bar_without_type`, `selected_type_shows_font_controls_beside_its_actions`, `the_task_bars_font_size_sets_what_the_character_panel_would`, `the_type_tools_fill_chip_activates_the_fill`, `the_type_tools_bar_is_named_and_stays_on_the_canvas`, `selected_types_font_controls_take_a_second_row_on_a_narrow_canvas` and `the_type_tools_fill_chip_shows_the_edited_characters_fill` in `crates/ui-egui/src/tests/taskbar.rs`. - The help page describes the contextual task bar as one horizontal row () and gives no rule for a canvas narrower than that row. VectorCraft's own rule: the bar stays whole on the canvas. When its actions don't fit in one row, they wrap, in order, onto rows under the row with the handle, the chips and More Options; an action wider than any row, or one that would need a row past the canvas's height, is listed in More Options instead; when the chips don't fit beside the handle and More Options, they take rows after the actions; with no row to spare for the font controls, the bar leaves them out; a canvas too small for the handle and More Options shows no bar. Evidence: `a_task_bars_actions_wrap_on_a_narrow_canvas` and `a_task_bar_action_wider_than_the_canvas_goes_in_more_options` in `crates/ui-egui/src/tests/taskbar.rs`.