From c3a6aa0a29bf309de8a44e7976bdc31632ef88f2 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Sun, 18 Oct 2026 21:34:14 -0700 Subject: [PATCH] Variables: a data set with no value for any bound variable shows a status message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Choosing a data set in the Variables panel, stepping through data sets with the panel's arrows, and running `dataset.select`, `dataset.next` and `dataset.prev` applied nothing and showed no message when no object was bound to a variable that the data set has a value for. This happens, for example, after Import Data Sets… defines new variables without binding them. The app now shows "nothing in data set A was applied: no object is bound to a variable it has a value for" in the status bar, and the three commands add it to the reply as `warning`. A data set with a value for a bound variable gives no warning even when every object bound to it is locked; `skipped` counts those objects. What applying a data set does is unchanged. Docs: docs/mcp.md, docs/target-app-parity.md. --- crates/engine/src/cmd/variablecmds.rs | 16 ++++++++--- crates/engine/src/tests_variables.rs | 38 ++++++++++++++++++++++++++ crates/ui-egui/src/i18n/cs.tsv | 1 + crates/ui-egui/src/i18n/de.tsv | 1 + crates/ui-egui/src/i18n/el.tsv | 1 + crates/ui-egui/src/i18n/es.tsv | 1 + crates/ui-egui/src/i18n/fi.tsv | 1 + crates/ui-egui/src/i18n/fr.tsv | 1 + crates/ui-egui/src/i18n/hu.tsv | 1 + crates/ui-egui/src/i18n/it.tsv | 1 + crates/ui-egui/src/i18n/ja.tsv | 1 + crates/ui-egui/src/i18n/ka.tsv | 1 + crates/ui-egui/src/i18n/ko.tsv | 1 + crates/ui-egui/src/i18n/pl.tsv | 1 + crates/ui-egui/src/i18n/pt-br.tsv | 1 + crates/ui-egui/src/i18n/ru.tsv | 1 + crates/ui-egui/src/i18n/sv.tsv | 1 + crates/ui-egui/src/i18n/tests.rs | 17 ++++++++++++ crates/ui-egui/src/i18n/uk.tsv | 1 + crates/ui-egui/src/i18n/vi.tsv | 1 + crates/ui-egui/src/i18n/zh-hans.tsv | 1 + crates/ui-egui/src/i18n/zh-hant.tsv | 1 + crates/ui-egui/src/panels/variables.rs | 23 ++++++++++++++++ docs/mcp.md | 6 ++++ docs/target-app-parity.md | 1 + 25 files changed, 116 insertions(+), 4 deletions(-) diff --git a/crates/engine/src/cmd/variablecmds.rs b/crates/engine/src/cmd/variablecmds.rs index 78e080283..2a1fba843 100644 --- a/crates/engine/src/cmd/variablecmds.rs +++ b/crates/engine/src/cmd/variablecmds.rs @@ -459,7 +459,7 @@ fn dataset_list(s: &mut Session, _p: &Value) -> Result { fn apply_dataset(s: &mut Session, name: &str) -> Result { const C: &str = "dataset.select"; - let (applied, skipped, mut typed) = s.edit("Apply Data Set", |d, sel| { + let (applied, skipped, mut typed, bound) = s.edit("Apply Data Set", |d, sel| { let ds = d.variables.dataset(name).cloned().ok_or_else(|| bad(C, format!("no dataset named `{name}`")))?; // One object may be driven by several variables; walk them in definition order, so two // text variables on the same object land the later one's value and both are counted. @@ -467,6 +467,8 @@ fn apply_dataset(s: &mut Session, name: &str) -> Result { for v in &d.variables.variables { pairs.extend(d.variables.objects_of(&v.name).into_iter().map(|id| (id, v.name.clone()))); } + // Whether an object is bound to a variable that this row has a value for (the reply's `warning`). + let bound = pairs.iter().any(|(_, var)| ds.values.contains_key(var)); let mut applied = 0usize; let mut skipped = 0usize; // Collects the type objects whose text the row rewrote, for the reply's `overflow`. @@ -514,12 +516,18 @@ fn apply_dataset(s: &mut Session, name: &str) -> Result { d.variables.active_dataset = Some(name.to_string()); // Every selected object left hidden or locked, such as one the row hid, is deselected. sel.deselect_uneditable(d); - Ok((applied, skipped, typed)) + Ok((applied, skipped, typed, bound)) })?; // Two text variables on one object rewrite it twice; it is named once. typed.sort_unstable(); typed.dedup(); - Ok(super::typecmd::with_overflow(s, &typed, json!({"name": name, "applied": applied, "skipped": skipped}))) + let mut out = super::typecmd::with_overflow(s, &typed, json!({"name": name, "applied": applied, "skipped": skipped})); + // A row with no value for any bound variable applies nothing. The reply then carries a + // `warning`, which the app shows in its status bar. + if !bound { + out["warning"] = json!(format!("nothing in data set {name} was applied: no object is bound to a variable it has a value for")); + } + Ok(out) } fn dataset_select(s: &mut Session, p: &Value) -> Result { @@ -1037,7 +1045,7 @@ pub fn specs() -> Vec { "Select Data Set", ["Window", "Variables"], None, - "{name} apply a dataset in one undo step, deselecting every selected object left hidden or locked (the key object too) → {applied, skipped, overflow?: [id…] (area type and type on a path whose text it rewrote and that no longer fits; left out when all fits)}", + "{name} apply a dataset in one undo step, deselecting every selected object left hidden or locked (the key object too) → {applied, skipped, overflow?: [id…] (area type and type on a path whose text it rewrote and that no longer fits; left out when all fits), warning? (when no object is bound to a variable that the dataset has a value for, so nothing was applied)}", has_doc, dataset_select ), diff --git a/crates/engine/src/tests_variables.rs b/crates/engine/src/tests_variables.rs index 0b5698abc..7d2b1dadb 100644 --- a/crates/engine/src/tests_variables.rs +++ b/crates/engine/src/tests_variables.rs @@ -131,6 +131,44 @@ fn mismatched_and_missing_bindings_are_skipped_and_counted() { assert_eq!(plain(&s, t), "y"); } +/// A row with no value for any bound variable, such as one from Import Data Sets… before anything +/// is bound, applies nothing, and the reply's `warning` names it. A bound variable with a value in +/// the row gives no warning, even when its object is locked and skipped. +#[test] +fn a_row_with_no_value_for_a_bound_variable_warns_that_nothing_was_applied() { + let mut s = session(); + let t = text(&mut s, "x"); + s.execute("dataset.import", &json!({"csv": "name,Name,Title\nA,Ann,Boss\nB,Bob,Chief\n"})).unwrap(); + let warning = |row: &str| format!("nothing in data set {row} was applied: no object is bound to a variable it has a value for"); + let v = s.execute("dataset.select", &json!({"name": "A"})).unwrap(); + assert_eq!((v["applied"].as_u64(), v["skipped"].as_u64(), &v["warning"]), (Some(0), Some(0), &json!(warning("A"))), "{v}"); + let v = s.execute("dataset.next", &json!({})).unwrap(); + assert_eq!(v["warning"], json!(warning("B")), "{v}"); + let v = s.execute("dataset.prev", &json!({})).unwrap(); + assert_eq!(v["warning"], json!(warning("A")), "{v}"); + assert_eq!(plain(&s, t), "x"); + + // A bound variable that the row has no value for still applies nothing. + s.execute("variable.define", &json!({"name": "Other", "kind": "text"})).unwrap(); + s.execute("variable.bind", &json!({"variable": "Other", "ids": [t.0]})).unwrap(); + let v = s.execute("dataset.select", &json!({"name": "B"})).unwrap(); + assert_eq!((v["applied"].as_u64(), v["skipped"].as_u64(), &v["warning"]), (Some(0), Some(1), &json!(warning("B"))), "{v}"); + + // Bound to a variable with a value, the row applies it and gives no warning. + s.execute("variable.bind", &json!({"variable": "Title", "ids": [t.0]})).unwrap(); + let v = s.execute("dataset.select", &json!({"name": "A"})).unwrap(); + assert_eq!(v["applied"].as_u64(), Some(1), "{v}"); + assert!(v.get("warning").is_none(), "{v}"); + assert_eq!(plain(&s, t), "Boss"); + + // Locked art is skipped, and its binding still counts as bound. + s.execute("select.set", &json!({"ids": [t.0]})).unwrap(); + s.execute("object.lock", &json!({})).unwrap(); + let v = s.execute("dataset.select", &json!({"name": "B"})).unwrap(); + assert_eq!(v["applied"].as_u64(), Some(0), "{v}"); + assert!(v.get("warning").is_none(), "{v}"); +} + #[test] fn deleting_a_variable_cleans_bindings_values_and_leaves_datasets_alone() { let mut s = session(); diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 1b06a41ff..1e94a4e49 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -5154,6 +5154,7 @@ mark-style Japanese Japonské @msg choose a destination folder vyberte cílovou složku @msg choose a source folder vyberte zdrojovou složku @msg open a document with data sets otevřete dokument se sadami dat +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for ze sady dat {name} se nic nepoužilo: žádný objekt není svázán s proměnnou, pro kterou má hodnotu @msg the action has no steps akce nemá žádné kroky # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index b133acd60..ba35c8916 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -4732,6 +4732,7 @@ effect Soft Weich @msg choose a destination folder wählen Sie einen Zielordner @msg choose a source folder wählen Sie einen Quellordner @msg open a document with data sets öffnen Sie ein Dokument mit Datensätzen +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for aus dem Datensatz {name} wurde nichts angewendet: kein Objekt ist an eine Variable gebunden, für die er einen Wert hat @msg the action has no steps die Aktion hat keine Schritte # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 51107c65a..12d15eb89 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -5093,6 +5093,7 @@ effect Points Σημεία @msg choose a destination folder επιλέξτε φάκελο προορισμού @msg choose a source folder επιλέξτε φάκελο προέλευσης @msg open a document with data sets ανοίξτε ένα έγγραφο με σύνολα δεδομένων +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for δεν εφαρμόστηκε τίποτα από το σύνολο δεδομένων {name}: κανένα αντικείμενο δεν είναι συνδεδεμένο με μεταβλητή για την οποία έχει τιμή @msg select an Intertwine group επιλέξτε μια ομάδα διαπλοκής @msg span must be "object" or "selection", got {v} το span πρέπει να είναι "object" ή "selection", δόθηκε {v} @msg span: "selection" places a `gradient`, not a swatch span: το "selection" τοποθετεί ένα `gradient`, όχι δείγμα χρώματος diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index c9e8f91a0..4eccbf498 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -5096,6 +5096,7 @@ mark-style Japanese Japonesas @msg choose a destination folder elija una carpeta de destino @msg choose a source folder elija una carpeta de origen @msg open a document with data sets abra un documento con conjuntos de datos +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for no se aplicó nada del conjunto de datos {name}: ningún objeto está vinculado a una variable para la que tenga un valor @msg the action has no steps la acción no tiene pasos # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index a530c2ade..0f04e0cee 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -5105,6 +5105,7 @@ mark-style Japanese Japanilainen @msg choose a destination folder valitse kohdekansio @msg choose a source folder valitse lähdekansio @msg open a document with data sets avaa asiakirja, jossa on tietojoukkoja +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for tietojoukosta {name} ei käytetty mitään: mitään objektia ei ole sidottu muuttujaan, jolle siinä on arvo @msg select an Intertwine group valitse lomitusryhmä @msg span must be "object" or "selection", got {v} span-arvon on oltava "object" tai "selection", saatiin {v} @msg span: "selection" places a `gradient`, not a swatch span: "selection" sijoittaa `gradient`-liukuvärin, ei värimallia diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 15101c71a..3c481eeac 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -5153,6 +5153,7 @@ theme Appearance Apparence @msg choose a destination folder choisissez un dossier de destination @msg choose a source folder choisissez un dossier source @msg open a document with data sets ouvrez un document avec des jeux de données +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for rien du jeu de données {name} n’a été appliqué, car aucun objet n’est lié à une variable pour laquelle il a une valeur @msg the action has no steps l'action n'a aucune étape # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 676064dd7..8cdac2ab6 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -5140,6 +5140,7 @@ imageTrace Outline Körvonal @msg choose a destination folder válasszon célmappát @msg choose a source folder válasszon forrásmappát @msg open a document with data sets nyisson meg egy adatkészleteket tartalmazó dokumentumot +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for ebből az adatkészletből semmi sem lett alkalmazva: {name}; egyetlen objektum sincs olyan változóhoz kötve, amelynek értéke van benne @msg the action has no steps a műveletnek nincsenek lépései # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index f9e6b2a56..3bcae00d2 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -4766,6 +4766,7 @@ lighting Light Luce @msg choose a destination folder scegli una cartella di destinazione @msg choose a source folder scegli una cartella di origine @msg open a document with data sets apri un documento con set di dati +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for non è stato applicato nulla del set di dati {name}: nessun oggetto è associato a una variabile per cui ha un valore @msg the action has no steps l'azione non ha passaggi # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index d28036443..e71c79a67 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -4806,6 +4806,7 @@ lighting Light ライト @msg choose a destination folder 出力先フォルダーを選択してください @msg choose a source folder ソースフォルダーを選択してください @msg open a document with data sets データセットのあるドキュメントを開いてください +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for データセット {name} から何も適用されませんでした: 値のある変数にバインドされたオブジェクトがありません @msg the action has no steps アクションにステップがありません # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 22f4a036d..96432f4d9 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -4848,6 +4848,7 @@ imageTrace Outline კონტური @msg choose a destination folder აირჩიეთ დანიშნულების საქაღალდე @msg choose a source folder აირჩიეთ წყარო საქაღალდე @msg open a document with data sets გახსენით დოკუმენტი მონაცემთა ნაკრებებით +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for მონაცემთა ნაკრები {name}: არაფერი გამოყენებულა; არცერთი ობიექტი არ არის მიბმული ცვლადზე, რომელსაც მასში მნიშვნელობა აქვს @msg the action has no steps მოქმედებას ნაბიჯები არ აქვს Convert to Straight Lines სწორ ხაზებად გარდაქმნა Corner Point Angle Threshold: კუთხოვანი წერტილის კუთხის ზღვარი: diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index d99bd24b9..4b4f34399 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -4729,6 +4729,7 @@ lighting Light 광원 @msg choose a destination folder 대상 폴더를 선택하세요 @msg choose a source folder 소스 폴더를 선택하세요 @msg open a document with data sets 데이터 세트가 있는 문서를 여세요 +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for 데이터 세트 {name}에서 적용된 항목이 없습니다: 값이 있는 변수에 연결된 개체가 없습니다 @msg the action has no steps 액션에 단계가 없습니다 # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index c8998eb85..c909ecf2b 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -5005,6 +5005,7 @@ mark-style Japanese Japońskie @msg choose a destination folder wybierz folder docelowy @msg choose a source folder wybierz folder źródłowy @msg open a document with data sets otwórz dokument z zestawami danych +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for z zestawu danych {name} nic nie zastosowano: żaden obiekt nie jest powiązany ze zmienną, dla której ma on wartość @msg the action has no steps operacja nie ma kroków # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 5497a053e..fa1e08bab 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -5032,6 +5032,7 @@ mark-style Japanese Japonesas @msg choose a destination folder escolha uma pasta de destino @msg choose a source folder escolha uma pasta de origem @msg open a document with data sets abra um documento com conjuntos de dados +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for nada do conjunto de dados {name} foi aplicado: nenhum objeto está vinculado a uma variável para a qual ele tem um valor @msg the action has no steps a ação não tem etapas @msg Angles require two straight segments Ângulos exigem dois segmentos retos @msg Choose a circle or a perfect arc Escolha um círculo ou um arco perfeito diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 0b0164f83..83e357bd7 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -4762,6 +4762,7 @@ lighting Light Свет @msg choose a destination folder выберите папку назначения @msg choose a source folder выберите исходную папку @msg open a document with data sets откройте документ с наборами данных +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for из набора данных {name} ничего не применено: ни один объект не привязан к переменной, для которой в нём есть значение @msg the action has no steps в действии нет шагов # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 79d0ebab1..d4a7144ed 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -5097,6 +5097,7 @@ theme Appearance Utseende @msg choose a destination folder välj en målmapp @msg choose a source folder välj en källmapp @msg open a document with data sets öppna ett dokument med datauppsättningar +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for inget i datauppsättningen {name} användes: inget objekt är bundet till en variabel som den har ett värde för @msg select an Intertwine group markera en sammanflätningsgrupp @msg span must be "object" or "selection", got {v} span måste vara "object" eller "selection", fick {v} @msg span: "selection" places a `gradient`, not a swatch span: "selection" placerar en `gradient`, inte en färgruta diff --git a/crates/ui-egui/src/i18n/tests.rs b/crates/ui-egui/src/i18n/tests.rs index 31ba1f93d..e6171cc24 100644 --- a/crates/ui-egui/src/i18n/tests.rs +++ b/crates/ui-egui/src/i18n/tests.rs @@ -1648,6 +1648,23 @@ fn align_reflect_and_shear_param_errors_are_translated() { } } +/// The warning that a data set with no value for a bound variable gives, which the status bar +/// shows and the scan above doesn't read, is translated in each language of [`COMPLETE_MESSAGES`], +/// with the data set's name kept. +#[test] +fn the_data_set_applied_nothing_warning_is_translated() { + use serde_json::json; + let mut s = vectorcraft_engine::Session::new(); + s.execute("file.new", &json!({})).unwrap(); + s.execute("dataset.import", &json!({"csv": "name,Name\nRow 7,Ann\n"})).unwrap(); + let v = s.execute("dataset.select", &json!({"name": "Row 7"})).unwrap(); + let warning = v["warning"].as_str().unwrap(); + for code in COMPLETE_MESSAGES { + let shown = message(Lang::from_code(code).unwrap(), warning); + assert!(shown.contains("Row 7") && !shown.contains("was applied"), "{code}: {shown}"); + } +} + /// The parameter errors of the blend spine commands (Move Spine Point, Reshape Spine Segment, Add /// Spine Point and Delete Spine Point, which Direct Selection, the Pen and the Anchor Point tool /// send), whose text the scan above doesn't read, are translated whole in each language of diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index d7d490dac..22630b826 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -4746,6 +4746,7 @@ lighting Light Світло @msg choose a destination folder виберіть папку призначення @msg choose a source folder виберіть вихідну папку @msg open a document with data sets відкрийте документ із наборами даних +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for з набору даних {name} нічого не застосовано: жоден об’єкт не прив’язано до змінної, для якої в ньому є значення @msg the action has no steps у дії немає кроків # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 28ef907f2..d9f4e43c2 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -4746,6 +4746,7 @@ theme Appearance Giao diện @msg choose a destination folder hãy chọn thư mục đích @msg choose a source folder hãy chọn thư mục nguồn @msg open a document with data sets hãy mở một tài liệu có tập dữ liệu +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for không có gì trong tập dữ liệu {name} được áp dụng: không có đối tượng nào được gắn với biến số mà tập dữ liệu có giá trị @msg the action has no steps hành động không có bước nào # User Interface › Accent Color diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 378ca236a..802421299 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -4248,6 +4248,7 @@ imageTrace Outline 轮廓 @msg choose a destination folder 请选择目标文件夹 @msg choose a source folder 请选择源文件夹 @msg open a document with data sets 请打开包含数据集的文档 +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for 数据集 {name} 未应用任何内容:没有对象绑定到该数据集中有值的变量 @msg the action has no steps 该动作没有步骤 # Import Shortcut Set (Keyboard Shortcuts › Import…, File › Open of a .kys file) and its commands. Import Shortcut Set 导入快捷键组 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index f0e1b271c..931c883a5 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -2256,6 +2256,7 @@ file-filter Templates 範本 @msg choose a destination folder 請選擇目的地資料夾 @msg choose a source folder 請選擇來源資料夾 @msg open a document with data sets 請開啟含有資料集的文件 +@msg nothing in data set {name} was applied: no object is bound to a variable it has a value for 資料集 {name} 未套用任何內容:沒有物件綁定到該資料集中有值的變數 @msg the action has no steps 動作沒有步驟 @msg cancelled; nothing was changed 已取消;未做任何變更 @msg Save writes {_1}, not {_2}: use file.saveAsTemplate to write it 「儲存」會寫入 {_1},而非 {_2}:若要寫入該格式,請使用 file.saveAsTemplate diff --git a/crates/ui-egui/src/panels/variables.rs b/crates/ui-egui/src/panels/variables.rs index 96f32e132..160d0fe0a 100644 --- a/crates/ui-egui/src/panels/variables.rs +++ b/crates/ui-egui/src/panels/variables.rs @@ -390,6 +390,29 @@ mod tests { assert_eq!(app.run("dataset.list", json!({})).unwrap()["datasets"][0]["matches"], json!(true)); } + /// Choosing a data set, or stepping to one with the arrows, shows a status message when no + /// object is bound to a variable it has a value for, for example after Import Data Sets… + /// defines new variables. With such an object bound, it shows none. + #[test] + fn a_data_set_with_nothing_bound_shows_a_status_message() { + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 300, "height": 200})).unwrap(); + let t = app.session.execute("text.create", &json!({"x": 10, "y": 40, "text": "x"})).unwrap()["id"].as_u64().unwrap(); + app.run("dataset.import", json!({"csv": "name,Name\nA,Ann\nB,Bob\n"})).unwrap(); + let message = |row: &str| format!("nothing in data set {row} was applied: no object is bound to a variable it has a value for"); + app.run("dataset.select", json!({"name": "A"})).unwrap(); + assert_eq!(app.ui.status, message("A")); + app.run("dataset.next", json!({})).unwrap(); + assert_eq!(app.ui.status, message("B")); + app.run("dataset.prev", json!({})).unwrap(); + assert_eq!(app.ui.status, message("A")); + + app.ui.status.clear(); + app.run("variable.bind", json!({"variable": "Name", "ids": [t]})).unwrap(); + app.run("dataset.select", json!({"name": "B"})).unwrap(); + assert_eq!(app.ui.status, ""); + } + #[test] fn the_panel_menu_lists_the_row_commands() { let mut app = app_with_data(); diff --git a/docs/mcp.md b/docs/mcp.md index a74597c53..fc5b03f3c 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -1254,6 +1254,12 @@ defined. Of two variables of the same kind on one object, the one defined last w {"name":"run_command","arguments":{"command":"command.batch","params":{"label":"Offset","commands":[{"command":"object.move","params":{"dx":10,"dy":10,"copy":true}}]}}} ``` +When no object is bound to a variable that the data set has a value for, for example after `dataset.import` defines +new variables without binding them, `dataset.select`, `dataset.next` and `dataset.prev` apply nothing and add +`warning`, which the app's status bar shows: "nothing in data set A was applied: no object is bound to a variable it +has a value for". A data set with a value for a bound variable gives no warning even when every object bound to it is +locked; `skipped` counts those objects. + `dataset.update {all?}` writes what the bound objects hold now into the active data set, for each variable that the data set holds. A variable whose objects report no value, because they are locked, inside a locked layer or group, or deleted, keeps the data set's value. With `all: true` it also adds every other variable that the diff --git a/docs/target-app-parity.md b/docs/target-app-parity.md index b28d5a2c4..b961e9c1f 100644 --- a/docs/target-app-parity.md +++ b/docs/target-app-parity.md @@ -430,6 +430,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur - Variables: Update Data Set writes what the art holds into the active data set for the variables that data set holds, and keeps the data set's value for a variable whose objects are locked or inside a locked layer or group; `dataset.update {all: true}` also adds the other variables that the art reports a value for. - Variables: `dataset.select`, `dataset.next` and `dataset.prev` rewrite type and apply the other values on hidden objects and in hidden layers and groups, which stay hidden unless a visibility value shows them, so a layer shown later holds the selected data set's text; objects that are locked or in a locked layer or group are skipped. - Variables: `dataset.select`, `dataset.next` and `dataset.prev` name the area type and type on a path whose text they rewrote and that doesn't fit, in the reply's `overflow` key, as `text.setText` does. + - Variables: when no object is bound to a variable that the data set has a value for, for example after Import Data Sets… defines new variables without binding them, choosing the data set in the Variables panel or stepping to it with the panel's arrows shows a status message, and `dataset.select`, `dataset.next` and `dataset.prev` add the message to the reply as `warning`. - Variables: Lock Variables (`variable.lock`, in the Variables panel's bottom bar and menu) stops variables from being defined, deleted or renamed until they are unlocked, while objects can still be bound and unbound and data sets still change and apply; the lock is saved with the document. - Batch…: Override Save and Override Export name each file with the extension of the format the step writes, however the step spells the format (`.svg` and `SVG` give `.svg`, `png8` gives `.png`), in `action.batch`. - Actions panel (record/playback, persisted), generic parameter dialogs for every "…" command.