From ba2069c057c3acc682b58436c070ca8a6bca760c Mon Sep 17 00:00:00 2001 From: Fahri Meral Date: Fri, 9 Oct 2026 16:47:14 +0300 Subject: [PATCH] variables: panel, window menu entry and translations --- crates/ui-egui/src/i18n/es.tsv | 12 ++++ crates/ui-egui/src/i18n/fr.tsv | 12 ++++ crates/ui-egui/src/i18n/it.tsv | 12 ++++ crates/ui-egui/src/i18n/ja.tsv | 12 ++++ crates/ui-egui/src/i18n/ru.tsv | 12 ++++ crates/ui-egui/src/i18n/uk.tsv | 12 ++++ crates/ui-egui/src/i18n/zh-hans.tsv | 12 ++++ crates/ui-egui/src/i18n/zh-hant.tsv | 12 ++++ crates/ui-egui/src/menus.rs | 2 +- crates/ui-egui/src/panels/mod.rs | 2 + crates/ui-egui/src/panels/variables.rs | 90 ++++++++++++++++++++++++++ crates/ui-egui/src/state.rs | 1 + 12 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 crates/ui-egui/src/panels/variables.rs diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 6746a9179..e2a088896 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -3055,6 +3055,18 @@ V: V: Variable Width Profile Perfil de anchura variable Variables Variables + New Variable Nueva variable + Delete Variable Eliminar variable + List Variables Enumerar variables + Bind Variable Vincular variable + Unbind Variable Desvincular variable + New Data Set Nuevo conjunto de datos + Delete Data Set Eliminar conjunto de datos + List Data Sets Enumerar conjuntos de datos + Select Data Set Seleccionar conjunto de datos + Next Data Set Conjunto de datos siguiente + Previous Data Set Conjunto de datos anterior + Data Sets Conjuntos de datos Variation: Variación: Vector Effects Efectos vectoriales Vector Halftone Semitono vectorial diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 27150a15e..b138f38cb 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -3052,6 +3052,18 @@ V: V : Variable Width Profile Profil de largeur variable Variables Variables + New Variable Nouvelle variable + Delete Variable Supprimer la variable + List Variables Lister les variables + Bind Variable Lier la variable + Unbind Variable Délier la variable + New Data Set Nouveau jeu de données + Delete Data Set Supprimer le jeu de données + List Data Sets Lister les jeux de données + Select Data Set Sélectionner le jeu de données + Next Data Set Jeu de données suivant + Previous Data Set Jeu de données précédent + Data Sets Jeux de données Variation: Variation : Vector Effects Effets vectoriels Vector Halftone Trame de demi-teintes vectorielle diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 4ab058c30..7564f5e07 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -3055,6 +3055,18 @@ V: V: Variable Width Profile Profilo larghezza variabile Variables Variabili + New Variable Nuova variabile + Delete Variable Elimina variabile + List Variables Elenca variabili + Bind Variable Collega variabile + Unbind Variable Scollega variabile + New Data Set Nuovo set di dati + Delete Data Set Elimina set di dati + List Data Sets Elenca set di dati + Select Data Set Seleziona set di dati + Next Data Set Set di dati successivo + Previous Data Set Set di dati precedente + Data Sets Set di dati Variation: Variazione: Vector Effects Effetti vettoriali Vector Halftone Retino vettoriale diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 28a5c5183..373c08a04 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -569,6 +569,18 @@ Paragraph Styles 段落スタイル Tabs タブ Variables 変数 + New Variable 新規変数 + Delete Variable 変数を削除 + List Variables 変数一覧 + Bind Variable 変数をバインド + Unbind Variable 変数のバインドを解除 + New Data Set 新規データセット + Delete Data Set データセットを削除 + List Data Sets データセット一覧 + Select Data Set データセットを選択 + Next Data Set 次のデータセット + Previous Data Set 前のデータセット + Data Sets データセット Brush Libraries ブラシライブラリ Built-in Libraries 内蔵ライブラリ User Defined ユーザー定義 diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index a222cde4a..c3a9b8dba 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -3051,6 +3051,18 @@ V: В: Variable Width Profile Профиль переменной ширины Variables Переменные + New Variable Новая переменная + Delete Variable Удалить переменную + List Variables Перечислить переменные + Bind Variable Привязать переменную + Unbind Variable Отвязать переменную + New Data Set Новый набор данных + Delete Data Set Удалить набор данных + List Data Sets Перечислить наборы данных + Select Data Set Выделить набор данных + Next Data Set Следующий набор данных + Previous Data Set Предыдущий набор данных + Data Sets Наборы данных Variation: Вариация: Vector Effects Векторные эффекты Vector Halftone Векторный полутон diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 05c421b1b..a7ada88fb 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -3047,6 +3047,18 @@ V: В: Variable Width Profile Профіль змінної ширини Variables Змінні + New Variable Нова змінна + Delete Variable Видалити змінну + List Variables Перерахувати змінні + Bind Variable Прив'язати змінну + Unbind Variable Відв'язати змінну + New Data Set Новий набір даних + Delete Data Set Видалити набір даних + List Data Sets Перерахувати набори даних + Select Data Set Виділити набір даних + Next Data Set Наступний набір даних + Previous Data Set Попередній набір даних + Data Sets Набори даних Variation: Варіація: Vector Effects Векторні ефекти Vector Halftone Векторний напівтон diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 7c4663ee6..abfe42fcc 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -2947,6 +2947,18 @@ V: V: Variable Width Profile 变量宽度配置文件 Variables 变量 + New Variable 新建变量 + Delete Variable 删除变量 + List Variables 列出变量 + Bind Variable 绑定变量 + Unbind Variable 解除变量绑定 + New Data Set 新建数据集 + Delete Data Set 删除数据集 + List Data Sets 列出数据集 + Select Data Set 选择数据集 + Next Data Set 下一个数据集 + Previous Data Set 上一个数据集 + Data Sets 数据集 Variation: 变化: Vector Effects 矢量效果 Vector Halftone 矢量半色调 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 077352950..3a2636dec 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -3038,6 +3038,18 @@ V: V: Variable Width Profile 可變寬度描述檔 Variables 變數 + New Variable 新增變數 + Delete Variable 刪除變數 + List Variables 列出變數 + Bind Variable 綁定變數 + Unbind Variable 解除變數綁定 + New Data Set 新增資料集 + Delete Data Set 刪除資料集 + List Data Sets 列出資料集 + Select Data Set 選取資料集 + Next Data Set 下一個資料集 + Previous Data Set 上一個資料集 + Data Sets 資料集 Variation: 變化: Vector Effects 向量效果 Vector Halftone 向量半色調 diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index a5ba1d395..372668b9f 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -2548,7 +2548,7 @@ pub fn menu_tree_named(english_names: bool) -> Vec<(&'static str, Vec)> { panel("Tabs", "tabs"), ], ), - todo("Variables"), + panel("Variables", "variables"), Sep, sub("Brush Libraries", library_placeholders()), sub("Graphic Style Libraries", crate::panels::graphic_styles::window_menu()), diff --git a/crates/ui-egui/src/panels/mod.rs b/crates/ui-egui/src/panels/mod.rs index ec8ef59ae..f89869040 100644 --- a/crates/ui-egui/src/panels/mod.rs +++ b/crates/ui-egui/src/panels/mod.rs @@ -42,6 +42,7 @@ pub mod tabs; pub mod text_styles; pub mod transform; pub mod transparency; +pub mod variables; use egui::{Rect, Sense, Ui, vec2}; use serde_json::{Value, json}; @@ -136,6 +137,7 @@ pub fn show_icon_panel(app: &mut VectorcraftApp, ui: &mut Ui, id: &str) { "tabs" => tabs::show(app, ui), flattener_preview::ID => flattener_preview::show(app, ui), "attributes" => attributes::show(app, ui), + "variables" => variables::show(app, ui), "colorThemes" => color_themes::show(app, ui), links::ID => links::show(app, ui), asset_export::ID => asset_export::show(app, ui), diff --git a/crates/ui-egui/src/panels/variables.rs b/crates/ui-egui/src/panels/variables.rs new file mode 100644 index 000000000..59dba7b09 --- /dev/null +++ b/crates/ui-egui/src/panels/variables.rs @@ -0,0 +1,90 @@ +//! Variables panel: the document's variables (what they drive, what they bind) and +//! its datasets. Clicking a dataset applies it; clicking a variable selects what it +//! binds; the arrows step to the next or previous dataset. Definitions are made with +//! the `variable.*` and `dataset.*` commands (command palette, actions, MCP). + +use egui::Ui; +use serde_json::json; + +use crate::VectorcraftApp; +use crate::widgets; + +pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { + let Some(st) = app.session.active() else { + widgets::dim_label(ui, tl!("No document")); + return; + }; + // Owned snapshots: the rows below run commands, which borrow the app mutably. + let vars: Vec<(String, String, Vec)> = st + .doc + .variables + .variables + .iter() + .map(|v| { + let bound: Vec = st.doc.variables.bindings.iter().filter(|(_, name)| *name == &v.name).map(|(id, _)| id.0).collect(); + (v.name.clone(), v.kind.label().to_string(), bound) + }) + .collect(); + let sets: Vec<(String, usize, bool)> = st + .doc + .variables + .datasets + .iter() + .map(|d| (d.name.clone(), d.values.len(), st.doc.variables.active_dataset.as_deref() == Some(d.name.as_str()))) + .collect(); + widgets::subheader(ui, tl!("Variables")); + if vars.is_empty() { + widgets::dim_label(ui, tl!("None")); + } + for (name, kind, bound) in &vars { + let row = format!("{name} · {kind} · {}", bound.len()); + if ui.selectable_label(false, row).clicked() && !bound.is_empty() { + app.run("select.set", json!({"ids": bound})).ok(); + } + } + ui.horizontal(|ui| { + widgets::subheader(ui, tl!("Data Sets")); + ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { + if ui.small_button("◀").clicked() { + app.run("dataset.prev", json!({})).ok(); + } + if ui.small_button("▶").clicked() { + app.run("dataset.next", json!({})).ok(); + } + }); + }); + if sets.is_empty() { + widgets::dim_label(ui, tl!("None")); + } + for (name, count, active) in &sets { + let row = format!("{}{name} · {count}", if *active { "● " } else { "" }); + if ui.selectable_label(*active, row).clicked() { + app.run("dataset.select", json!({"name": name})).ok(); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn draws_headless() { + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 100, "height": 100})).unwrap(); + app.session.execute("text.create", &json!({"x": 10, "y": 40, "text": "Hi"})).unwrap(); + app.session.execute("variable.define", &json!({"name": "Title", "kind": "text"})).unwrap(); + app.session.execute("dataset.new", &json!({"name": "A", "values": {"Title": "Yo"}})).unwrap(); + let ctx = egui::Context::default(); + let mut out = ctx.run_ui(egui::RawInput::default(), |ui| { + show(&mut app, ui); + }); + out.textures_delta.clear(); + let text = crate::tests_labels::painted_text(&mut app, show); + assert!(text.contains("Title") && text.contains("Data Sets") && text.contains("A · 1"), "{text}"); + // Clicking the dataset applies it. + app.run("dataset.select", json!({"name": "A"})).unwrap(); + let text = crate::tests_labels::painted_text(&mut app, show); + assert!(text.contains("● A"), "{text}"); + } +} diff --git a/crates/ui-egui/src/state.rs b/crates/ui-egui/src/state.rs index 9c75a81fa..994559f37 100644 --- a/crates/ui-egui/src/state.rs +++ b/crates/ui-egui/src/state.rs @@ -138,6 +138,7 @@ pub const ICON_PANELS: &[(&str, &str, &str)] = &[ (crate::panels::links::ID, "Links", "link"), (crate::panels::asset_export::ID, "Asset Export", "share-2"), (crate::panels::css_properties::ID, "CSS Properties", "globe"), + ("variables", "Variables", "dc-list-view"), ]; /// Groups of icon panels separated by dividers in the collapsed column.