mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-14 19:26:56 -04:00
Merge pull request #456 from storytold/fix/issue-455
Fix #455: the toolbar's double arrow and Window › Toolbars switch one/two columns
This commit is contained in:
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