mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
Fix #455: the toolbar's double arrow and Window › Toolbars switch one/two columns
The toolbar went to two columns whenever the window was shorter than the single column (about 990 pt for Basic, more for Advanced), so on most screens it was always double and the double arrow and the Window menu item flipped a flag nothing read. The column count is now the user's choice alone; a window too short for the tools scrolls them (#84). - window.toolbarColumns takes {double?: bool} (set or toggle, returns the new state) and reports `checked`; the double arrow runs it. - The Window › Toolbars item reads "Double Column" with a check mark; catalogs updated (cs, es, ja, pt-br, zh-hans, zh-hant) and an es @msg row for the new error. - Headless tests: the arrow and the command toggle the column count in a 700 pt window (both fail on main). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
d8e79078e5
commit
297f6e93d3
9 files changed
+79
-20
No files matched your search
+1
-1
@@ -4,7 +4,7 @@ VectorCraft is a clean-room, open-source, pure-Rust reimplementation of the Adob
|
||||
|
||||
This file tracks **how far we are and what's left**. Time estimates are wall-clock hours of continuous Claude Opus 5.5 agent work (including builds and the CI gate), given both for **one agent** and for **4–6 parallel agents** on disjoint crates. They are counted from the remaining work (see [Parity estimate](#parity-estimate)), calibrated against measured throughput, and updated as work lands.
|
||||
|
||||
_Last updated: 2026-10-08 (after M4.14–M4.98, M8.1–M8.20, M14.4–M14.7, the MCP prompts, resource templates, completions and logging, the 2026-10-07 issue fixes, the Layers panel rework and the 2026-10-08 community contributions: Pen and shape-tool modifiers, path editing, Layers, Artboards, Swatches/Brushes and dialogs, Japanese composition and PDF text, Hebrew/Arabic type, interface languages (Spanish among them), the font menu, saved selections, MCP and EPS, the missing-font export warning (#319), open paths in the Shape Builder (#374), PDF/`.ai` import keeping strokes live and hidden and nested layers (#372), per-corner Live Corners (#381), SVG type opening at the size it draws at (#396), the Wayland clipboard with copied-file paste (#398), ruler guides that can be selected, moved and deleted (#414), two batches of Preferences options that were stored but unused (#394), skeleton reads with `document.find` for large documents over MCP (#416) and anchors dragged with Direct Selection snapping to points, segments and smart guides, clipped SVG layers opening as clipping layers (#420), PDF/`.ai` layers read on pages of tens of millions of operators (#419), Document Color Mode putting RGB greys on the black plate on request and converting pattern tiles (#421) and movable dialogs with Offset Path's Joins dropdown (#423) and numeric fields scrubbed by dragging their label (#400), stroke weight presets in the stroke unit (#426), the Appearance panel's Opacity popup (#430), type on a path brackets, flip and Type on a Path Options that keep the type in place (#429), Layers rows dragged with a ghost and dropped on the trash or New Layer (#435), tool flyouts opening on a right or corner press (#436), tool flyouts torn off into floating toolbars (#437), the Shape Builder's erase mode deleting edges too (#439), installed fonts registered outside the font folders and fonts installed while the app runs listed in the font menus (#443), alike circular corners on rectangles that aren't square (#442), tool cursors as OS cursors so the pointer no longer trails over the canvas (#444), artboards copied and pasted with their art (#446), guides dragged out of the rulers snapping to the art's side midpoints and the artboards, and artboard guides (#451); see [Honest assessment](#honest-assessment-2026-10-05))._
|
||||
_Last updated: 2026-10-08 (after M4.14–M4.98, M8.1–M8.20, M14.4–M14.7, the MCP prompts, resource templates, completions and logging, the 2026-10-07 issue fixes, the Layers panel rework and the 2026-10-08 community contributions: Pen and shape-tool modifiers, path editing, Layers, Artboards, Swatches/Brushes and dialogs, Japanese composition and PDF text, Hebrew/Arabic type, interface languages (Spanish among them), the font menu, saved selections, MCP and EPS, the missing-font export warning (#319), open paths in the Shape Builder (#374), PDF/`.ai` import keeping strokes live and hidden and nested layers (#372), per-corner Live Corners (#381), SVG type opening at the size it draws at (#396), the Wayland clipboard with copied-file paste (#398), ruler guides that can be selected, moved and deleted (#414), two batches of Preferences options that were stored but unused (#394), skeleton reads with `document.find` for large documents over MCP (#416) and anchors dragged with Direct Selection snapping to points, segments and smart guides, clipped SVG layers opening as clipping layers (#420), PDF/`.ai` layers read on pages of tens of millions of operators (#419), Document Color Mode putting RGB greys on the black plate on request and converting pattern tiles (#421) and movable dialogs with Offset Path's Joins dropdown (#423) and numeric fields scrubbed by dragging their label (#400), stroke weight presets in the stroke unit (#426), the Appearance panel's Opacity popup (#430), type on a path brackets, flip and Type on a Path Options that keep the type in place (#429), Layers rows dragged with a ghost and dropped on the trash or New Layer (#435), tool flyouts opening on a right or corner press (#436), tool flyouts torn off into floating toolbars (#437), the Shape Builder's erase mode deleting edges too (#439), installed fonts registered outside the font folders and fonts installed while the app runs listed in the font menus (#443), alike circular corners on rectangles that aren't square (#442), tool cursors as OS cursors so the pointer no longer trails over the canvas (#444), artboards copied and pasted with their art (#446), guides dragged out of the rulers snapping to the art's side midpoints and the artboards, artboard guides (#451), and the toolbar's double arrow and Window › Toolbars › Double Column switching between one and two columns in any window height (#455); see [Honest assessment](#honest-assessment-2026-10-05))._
|
||||
|
||||
## Where we are
|
||||
|
||||
|
||||
@@ -491,7 +491,7 @@
|
||||
Tools Nástroje
|
||||
Toolbars Panely nástrojů
|
||||
Advanced Rozšířený
|
||||
Single / Double Column Jeden / dva sloupce
|
||||
Double Column Dva sloupce
|
||||
Actions Akce
|
||||
Asset Export Export prvků
|
||||
Attributes Atributy
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2430,7 +2430,7 @@
|
||||
Simplify… Simplificar…
|
||||
Simulate Colored Paper Simular papel de color
|
||||
Simulate Paper Simular papel
|
||||
Single / Double Column Columna simple / doble
|
||||
Double Column Columna doble
|
||||
Single Left Quotation Mark Comilla simple de apertura
|
||||
Single Quotes: Comillas simples:
|
||||
Single Right Quotation Mark Comilla simple de cierre
|
||||
@@ -3539,3 +3539,4 @@ axis Both Ambos
|
||||
@msg floating must be true or false floating debe ser true o false
|
||||
@msg tool (a tool id) is required se requiere tool (un id de herramienta)
|
||||
@msg {tool} isn't in a toolbar group of several tools {tool} no está en un grupo de la barra de herramientas con varias herramientas
|
||||
@msg double must be true or false double debe ser true o false
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -503,7 +503,7 @@
|
||||
Tools ツール
|
||||
Toolbars ツールバー
|
||||
Advanced 詳細
|
||||
Single / Double Column 1 列/2 列
|
||||
Double Column 2 列
|
||||
Actions アクション
|
||||
Asset Export アセットの書き出し
|
||||
Attributes 属性
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2153,7 +2153,7 @@
|
||||
Simplify… Simplificar…
|
||||
Simulate Colored Paper Simular Papel Colorido
|
||||
Simulate Paper Simular Papel
|
||||
Single / Double Column Coluna Única / Dupla
|
||||
Double Column Coluna Dupla
|
||||
Single Left Quotation Mark Aspa Esquerda Única
|
||||
Single Quotes: Aspas Simples:
|
||||
Single Right Quotation Mark Aspa Direita Única
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -2366,7 +2366,7 @@
|
||||
Simplify… 简化…
|
||||
Simulate Colored Paper 模拟有色纸张
|
||||
Simulate Paper 模拟纸张
|
||||
Single / Double Column 单列/双列
|
||||
Double Column 双列
|
||||
Single Left Quotation Mark 左单引号
|
||||
Single Quotes: 单引号:
|
||||
Single Right Quotation Mark 右单引号
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
@@ -2448,7 +2448,7 @@
|
||||
Simplify… 簡化…
|
||||
Simulate Colored Paper 模擬有色紙張
|
||||
Simulate Paper 模擬紙張
|
||||
Single / Double Column 單欄 / 雙欄
|
||||
Double Column 雙欄
|
||||
Single Left Quotation Mark 左單引號
|
||||
Single Quotes: 單引號:
|
||||
Single Right Quotation Mark 右單引號
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
@@ -197,7 +197,12 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
|
||||
("view.rotateReset", "Reset Rotate View", "Cmd+Shift+1", "{}"),
|
||||
("window.control", "Control", "", "{}"),
|
||||
("window.toolbar", "Tools", "", "{}"),
|
||||
("window.toolbarColumns", "Toolbar: Single/Double Column", "", "{}"),
|
||||
(
|
||||
"window.toolbarColumns",
|
||||
"Toolbar: Single/Double Column",
|
||||
"",
|
||||
"{double?: bool} show the toolbar's tools in two columns (true), one (false) or toggle (omitted), as the double arrow at the top of the toolbar does; returns the new state",
|
||||
),
|
||||
("window.toolbarAdvanced", "Toolbar: Advanced / Basic", "", "{}"),
|
||||
(
|
||||
"window.floatTools",
|
||||
@@ -984,7 +989,14 @@ pub fn run_ui_command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<R
|
||||
}
|
||||
"window.control" => flag(&mut app.ui.control_bar),
|
||||
"window.toolbar" => flag(&mut app.ui.toolbar),
|
||||
"window.toolbarColumns" => flag(&mut app.ui.toolbar_double),
|
||||
"window.toolbarColumns" => {
|
||||
app.ui.toolbar_double = match p.get("double") {
|
||||
None | Some(Value::Null) => !app.ui.toolbar_double,
|
||||
Some(Value::Bool(b)) => *b,
|
||||
Some(_) => return Some(Err("double must be true or false".into())),
|
||||
};
|
||||
Ok(json!(app.ui.toolbar_double))
|
||||
}
|
||||
"window.toolbarAdvanced" => flag(&mut app.ui.toolbar_advanced),
|
||||
"window.floatTools" => {
|
||||
let floating = match p.get("floating") {
|
||||
@@ -1317,6 +1329,7 @@ pub fn checked(app: &VectorcraftApp, id: &str, p: &Value) -> Option<bool> {
|
||||
"window.control" => app.ui.control_bar,
|
||||
"window.toolbar" => app.ui.toolbar,
|
||||
"window.toolbarAdvanced" => app.ui.toolbar_advanced,
|
||||
"window.toolbarColumns" => app.ui.toolbar_double,
|
||||
"window.floatTools" => {
|
||||
let tool = p.get("tool").and_then(Value::as_str).unwrap_or("");
|
||||
app.ui.floating_flyouts.iter().any(|f| f.tools.iter().any(|id| id == tool))
|
||||
@@ -2305,7 +2318,7 @@ pub fn menu_tree() -> Vec<(&'static str, Vec<Item>)> {
|
||||
c("Control", "window.control"),
|
||||
c("Contextual Task Bar", "window.taskBar"),
|
||||
c("Tools", "window.toolbar"),
|
||||
sub("Toolbars", vec![c("Advanced", "window.toolbarAdvanced"), c("Single / Double Column", "window.toolbarColumns")]),
|
||||
sub("Toolbars", vec![c("Advanced", "window.toolbarAdvanced"), c("Double Column", "window.toolbarColumns")]),
|
||||
Sep,
|
||||
panel("Actions", "actions"),
|
||||
panel("Align", "align"),
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! The Tools panel. Default: Illustrator 2026's categorized single-column toolbar (Select, Shapes,
|
||||
//! Draw, Modify, Type, Navigate, Color). Window → Toolbars → Advanced shows every tool group.
|
||||
//! Draw, Modify, Type, Navigate, Color). Window → Toolbars → Advanced shows every tool group; the
|
||||
//! double arrow at the top (or Window → Toolbars → Double Column, `window.toolbarColumns`) lays the
|
||||
//! tools out in one or two columns.
|
||||
//! Bottom: fill/stroke proxy, colour/gradient/none, drawing modes, screen mode, Edit Toolbar.
|
||||
|
||||
use egui::{Color32, CornerRadius, Sense, Stroke, Ui, pos2, vec2};
|
||||
@@ -88,10 +90,8 @@ pub fn remember(app: &mut VectorcraftApp, id: &str) {
|
||||
pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
let t = Tokens::get(ui.ctx());
|
||||
let all = slots(app);
|
||||
let avail = ui.available_height();
|
||||
let labels = all.iter().filter(|s| s.0.is_some()).count() as f32 * 20.0;
|
||||
let need = all.len() as f32 * PITCH + labels + 190.0;
|
||||
let cols = if app.ui.toolbar_double || avail < need { 2 } else { 1 };
|
||||
// The user's choice alone: a window too short for the tools scrolls them.
|
||||
let cols = if app.ui.toolbar_double { 2 } else { 1 };
|
||||
let w = if cols == 2 { 76.0 } else { WIDTH };
|
||||
egui::Panel::left("toolbar")
|
||||
.resizable(false)
|
||||
@@ -108,8 +108,11 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
egui::Rect::from_min_size(hdr.left_top() + vec2(3.0, 2.0), vec2(10.0, 10.0)),
|
||||
if hresp.hovered() { t.text_strong } else { t.text },
|
||||
);
|
||||
if hresp.on_hover_text(tl!("Toggle single/double column")).clicked() {
|
||||
app.ui.toolbar_double = !app.ui.toolbar_double;
|
||||
if hresp.on_hover_text(tl!("Toggle single/double column")).clicked()
|
||||
// It only fails on bad params, which this never sends; show it all the same.
|
||||
&& let Err(e) = app.run("window.toolbarColumns", json!({ "double": cols == 1 }))
|
||||
{
|
||||
app.ui.status = e;
|
||||
}
|
||||
let (grip, _) = ui.allocate_exact_size(vec2(ui.available_width(), 6.0), Sense::hover());
|
||||
for k in 0..6 {
|
||||
@@ -982,10 +985,52 @@ pub(crate) mod tests {
|
||||
|
||||
/// A click at `at`.
|
||||
fn click(app: &mut VectorcraftApp, ctx: &egui::Context, time: f64, at: Pos2, button: PointerButton) {
|
||||
click_in(app, ctx, time, at, button, 1200.0);
|
||||
}
|
||||
|
||||
/// [`click`] in a window `height` points tall; returns the tool buttons' rects after it.
|
||||
fn click_in(app: &mut VectorcraftApp, ctx: &egui::Context, time: f64, at: Pos2, button: PointerButton, height: f32) -> Vec<egui::Rect> {
|
||||
let b = |pressed| Event::PointerButton { pos: at, button, pressed, modifiers: Default::default() };
|
||||
frame(app, ctx, time, vec![Event::PointerMoved(at), b(true)]);
|
||||
frame(app, ctx, time + 0.05, vec![b(false)]);
|
||||
frame(app, ctx, time + 0.1, vec![]);
|
||||
frame_in(app, ctx, time, vec![Event::PointerMoved(at), b(true)], height);
|
||||
frame_in(app, ctx, time + 0.05, vec![b(false)], height);
|
||||
frame_in(app, ctx, time + 0.1, vec![], height)
|
||||
}
|
||||
|
||||
/// How many columns of tool buttons `rects` make.
|
||||
fn columns(rects: &[egui::Rect]) -> usize {
|
||||
let mut lefts: Vec<i32> = rects.iter().map(|r| r.left().round() as i32).collect();
|
||||
lefts.sort_unstable();
|
||||
lefts.dedup();
|
||||
lefts.len()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_double_arrow_toggles_one_and_two_columns_in_a_short_window() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
let ctx = egui::Context::default();
|
||||
// 700 pt: too short for the single column, which scrolls instead of turning into two.
|
||||
assert_eq!(columns(&frame_in(&mut app, &ctx, 0.0, vec![], 700.0)), 1);
|
||||
let arrow = pos2(8.0, 7.0);
|
||||
assert_eq!(columns(&click_in(&mut app, &ctx, 1.0, arrow, PointerButton::Primary, 700.0)), 2);
|
||||
assert!(app.ui.toolbar_double && crate::menus::checked(&app, "window.toolbarColumns", &json!({})) == Some(true));
|
||||
assert_eq!(columns(&click_in(&mut app, &ctx, 2.0, arrow, PointerButton::Primary, 700.0)), 1);
|
||||
assert!(!app.ui.toolbar_double);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_columns_command_toggles_or_sets_the_toolbar_columns() {
|
||||
let mut app = VectorcraftApp::new(Session::new(), Default::default());
|
||||
let ctx = egui::Context::default();
|
||||
assert_eq!(app.run("window.toolbarColumns", json!({})).unwrap(), json!(true));
|
||||
assert_eq!(columns(&frame_in(&mut app, &ctx, 0.0, vec![], 700.0)), 2);
|
||||
assert_eq!(app.run("window.toolbarColumns", json!({"double": true})).unwrap(), json!(true));
|
||||
assert_eq!(app.run("window.toolbarColumns", json!({})).unwrap(), json!(false));
|
||||
assert_eq!(crate::menus::checked(&app, "window.toolbarColumns", &json!({})), Some(false));
|
||||
assert_eq!(columns(&frame_in(&mut app, &ctx, 1.0, vec![], 700.0)), 1);
|
||||
assert!(app.run("window.toolbarColumns", json!({"double": "yes"})).is_err());
|
||||
// Advanced (every tool group) is taller still and keeps the column the user chose too.
|
||||
app.run("window.toolbarAdvanced", json!({})).unwrap();
|
||||
assert_eq!(columns(&frame_in(&mut app, &ctx, 2.0, vec![], 700.0)), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user