Merge pull request #7406 from dotCooCoo/feat/task-bar-blank-canvas

Contextual Task Bar: a blank canvas offers Draw (Pencil) and Place Image
This commit is contained in:
BFlat
2026-11-14 16:54:08 +07:00
committed by GitHub
31 changed files with 208 additions and 19 deletions
+54 -17
View File
@@ -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<vectorcraft_doc::Node>]) -> 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<Pos2> {
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
// (<https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/contextual-task-bar-overview.html>).
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
// (<https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/font-options-overview.html>).
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<Chip> = [
(!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<Chip> =
[(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<f32> = items.iter().map(|(label, ..)| action_width(ui, label)).collect();
let font_w = fonts.then(|| {
let w = crate::panels::character::TASK_BAR_WIDTHS;
+2 -2
View File
@@ -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::<Vec<_>>();
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);
+2
View File
@@ -691,6 +691,8 @@ font-technology Variable Променливи
Crop Image Изрязване на изображение
Image Tools Инструменти за изображения
Remove Background… Премахване на фона…
Draw (Pencil) Рисуване (Молив)
Place Image Вмъкване на изображение
Remove Background Премахване на фона
Background Фон
Edge Color Цвят на ръба
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -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
Can't render this file because it is too large.
+2
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -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
Can't render this file because it is too large.
+2
View File
@@ -665,6 +665,8 @@ font-technology Variable Μεταβλητές
Crop Image Περικοπή εικόνας
Image Tools Εργαλεία εικόνας
Remove Background… Αφαίρεση φόντου…
Draw (Pencil) Σχεδίαση (μολύβι)
Place Image Τοποθέτηση εικόνας
Remove Background Αφαίρεση φόντου
Background Φόντο
Edge Color Χρώμα άκρης
Can't render this file because it is too large.
+2
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -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
Can't render this file because it is too large.
+2
View File
@@ -1140,6 +1140,8 @@ antiAlias Sharp शार्प
Crop Image इमेज क्रॉप करें
Image Tools इमेज टूल
Remove Background… पृष्ठभूमि हटाएँ…
Draw (Pencil) ड्रा (पेंसिल)
Place Image इमेज प्लेस करें
Remove Background पृष्ठभूमि हटाएँ
Background पृष्ठभूमि
Edge Color किनारे का रंग
Can't render this file because it is too large.
+2
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -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
1 # Indonesian (id) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
194
195
196
197
198
199
200
201
+2
View File
@@ -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
Can't render this file because it is too large.
+2
View File
@@ -242,6 +242,8 @@ digits Malayalam マラヤーラム数字
Crop Image 画像の切り抜き
Image Tools 画像ツール
Remove Background… 背景を削除…
Draw (Pencil) 描画 (鉛筆)
Place Image 画像を配置
Remove Background 背景を削除
Background 背景
Edge Color 端の色
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -166,6 +166,8 @@
Crop Image გამოსახულების ჩამოჭრა
Image Tools გამოსახულების ხელსაწყოები
Remove Background… ფონის მოშორება…
Draw (Pencil) ხატვა (ფანქარი)
Place Image გამოსახულების განთავსება
Remove Background ფონის მოშორება
Background ფონი
Edge Color კიდის ფერი
Can't render this file because it is too large.
+2
View File
@@ -235,6 +235,8 @@ digits Malayalam 말라얄람어
Crop Image 이미지 자르기
Image Tools 이미지 도구
Remove Background… 배경 제거…
Draw (Pencil) 그리기(연필)
Place Image 이미지 가져오기
Remove Background 배경 제거
Background 배경
Edge Color 가장자리 색상
Can't render this file because it is too large.
+2
View File
@@ -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
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -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
Can't render this file because it is too large.
+2
View File
@@ -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
Can't render this file because it is too large.
+2
View File
@@ -703,6 +703,8 @@ font-technology Variable Вариативные
Crop Image Обрезать изображение
Image Tools Инструменты изображения
Remove Background… Удалить фон…
Draw (Pencil) Рисовать (Карандаш)
Place Image Поместить изображение
Remove Background Удалить фон
Background Фон
Edge Color Цвет края
Can't render this file because it is too large.
+2
View File
@@ -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
Can't render this file because it is too large.
+2
View File
@@ -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
Can't render this file because it contains an unexpected character in line 5 and column 25.
+2
View File
@@ -699,6 +699,8 @@ font-technology Variable Варіативні
Crop Image Обрізати зображення
Image Tools Інструменти зображення
Remove Background… Видалити фон…
Draw (Pencil) Малювати (Олівець)
Place Image Помістити зображення
Remove Background Видалити фон
Background Фон
Edge Color Колір краю
Can't render this file because it is too large.
+2
View File
@@ -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
Can't render this file because it contains an unexpected character in line 204 and column 5.
+2
View File
@@ -650,6 +650,8 @@ antiAlias Sharp 锐利
Crop Image 裁剪图像
Image Tools 图像工具
Remove Background… 移除背景…
Draw (Pencil) 绘制 (铅笔)
Place Image 置入图像
Remove Background 移除背景
Background 背景
Edge Color 边缘颜色
Can't render this file because it is too large.
+2
View File
@@ -667,6 +667,8 @@ font-technology Variable 可變
Crop Image 裁切影像
Image Tools 影像工具
Remove Background… 移除背景…
Draw (Pencil) 繪製 (鉛筆)
Place Image 置入影像
Remove Background 移除背景
Background 背景
Edge Color 邊緣顏色
Can't render this file because it is too large.
+97
View File
@@ -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"];
+1
View File
@@ -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 (<https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/contextual-task-bar-overview.html>). 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 (<https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/contextual-task-bar-overview.html>): 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: <https://helpx.adobe.com/illustrator/using/creating-type-path.html>) 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 (<https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/contextual-task-bar-overview.html>); 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 (<https://helpx.adobe.com/illustrator/desktop/design-with-text/fonts-and-scripts/font-options-overview.html>). 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 (<https://helpx.adobe.com/illustrator/desktop/get-started/learn-the-basics/workspace-overview.html>) 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`.