mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-25 10:17:02 -06:00
Merge pull request #2318 from dotCooCoo/fix/data-set-reports-overflow
Variables: selecting a data set reports the area type it overflows
This commit is contained in:
@@ -430,7 +430,7 @@ fn dataset_list(s: &mut Session, _p: &Value) -> Result<Value> {
|
||||
|
||||
fn apply_dataset(s: &mut Session, name: &str) -> Result<Value> {
|
||||
const C: &str = "dataset.select";
|
||||
let (applied, skipped) = s.edit("Apply Data Set", |d, sel| {
|
||||
let (applied, skipped, mut typed) = 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.
|
||||
@@ -462,6 +462,8 @@ fn apply_dataset(s: &mut Session, name: &str) -> Result<Value> {
|
||||
});
|
||||
let mut applied = 0usize;
|
||||
let mut skipped = 0usize;
|
||||
// Collects the type objects whose text the row rewrote, for the reply's `overflow`.
|
||||
let mut typed = Vec::new();
|
||||
for (id, var) in pairs {
|
||||
// A binding whose variable is gone, whose value this row doesn't carry, or whose
|
||||
// value doesn't fit the variable's kind, is left alone and counted, never
|
||||
@@ -483,7 +485,10 @@ fn apply_dataset(s: &mut Session, name: &str) -> Result<Value> {
|
||||
let done = match (kind, &value) {
|
||||
// The Type tool's own whole-range replace, so a dataset's text is styled and
|
||||
// laid out exactly as typing it would be.
|
||||
(VariableKind::Text, DataValue::Text(text)) => set_plain_text(d, id, text).is_ok(),
|
||||
(VariableKind::Text, DataValue::Text(text)) => set_plain_text(d, id, text).is_ok_and(|()| {
|
||||
typed.push(id);
|
||||
true
|
||||
}),
|
||||
(VariableKind::Visibility, DataValue::Visible(show)) => d.node_mut(id).is_some_and(|n| {
|
||||
n.visible = *show;
|
||||
true
|
||||
@@ -502,9 +507,12 @@ fn apply_dataset(s: &mut Session, name: &str) -> Result<Value> {
|
||||
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))
|
||||
Ok((applied, skipped, typed))
|
||||
})?;
|
||||
Ok(json!({"name": name, "applied": applied, "skipped": skipped}))
|
||||
// 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})))
|
||||
}
|
||||
|
||||
fn dataset_select(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
@@ -912,7 +920,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"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}",
|
||||
"{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)}",
|
||||
has_doc,
|
||||
dataset_select
|
||||
),
|
||||
|
||||
@@ -82,6 +82,38 @@ fn dataset_next_and_prev_wrap_around() {
|
||||
assert_eq!(v["active"], "B");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn applying_a_dataset_reports_the_area_type_it_overflows() {
|
||||
let mut s = session();
|
||||
let area = |s: &mut Session, y: u32, content: &str| {
|
||||
let r = s.execute("text.create", &json!({"x": 10, "y": y, "text": content, "size": 12, "area": {"width": 120, "height": 40}})).unwrap();
|
||||
NodeId(r["id"].as_u64().unwrap())
|
||||
};
|
||||
let long = "word ".repeat(60);
|
||||
let a = area(&mut s, 10, "x");
|
||||
let p = text(&mut s, "x");
|
||||
// Area type that already overflows, bound to no variable, isn't named.
|
||||
area(&mut s, 200, &long);
|
||||
s.execute("variable.define", &json!({"name": "Name", "kind": "text"})).unwrap();
|
||||
s.execute("variable.define", &json!({"name": "Title", "kind": "text"})).unwrap();
|
||||
s.execute("variable.bind", &json!({"variable": "Name", "ids": [a.0, p.0]})).unwrap();
|
||||
s.execute("variable.bind", &json!({"variable": "Title", "ids": [a.0]})).unwrap();
|
||||
s.execute("dataset.new", &json!({"name": "Short", "values": {"Name": "Ann", "Title": "Ann"}})).unwrap();
|
||||
s.execute("dataset.new", &json!({"name": "Long", "values": {"Name": long, "Title": long}})).unwrap();
|
||||
|
||||
// The area type is named once though two variables rewrote it; the point type is never named.
|
||||
let v = s.execute("dataset.select", &json!({"name": "Long"})).unwrap();
|
||||
assert_eq!((v["applied"].as_u64(), &v["overflow"]), (Some(3), &json!([a.0])), "{v}");
|
||||
let v = s.execute("dataset.select", &json!({"name": "Short"})).unwrap();
|
||||
assert!(v.get("overflow").is_none(), "{v}");
|
||||
let v = s.execute("dataset.next", &json!({})).unwrap();
|
||||
assert_eq!((&v["name"], &v["overflow"]), (&json!("Long"), &json!([a.0])), "{v}");
|
||||
let v = s.execute("dataset.prev", &json!({})).unwrap();
|
||||
assert!(v.get("overflow").is_none(), "{v}");
|
||||
let v = s.execute("dataset.prev", &json!({})).unwrap();
|
||||
assert_eq!((&v["name"], &v["overflow"]), (&json!("Long"), &json!([a.0])), "{v}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mismatched_and_missing_bindings_are_skipped_and_counted() {
|
||||
let mut s = session();
|
||||
|
||||
+3
-1
@@ -1002,7 +1002,9 @@ already walks Image Trace. Short agent entry points:
|
||||
- Image Trace: `imageTrace.presets`, `imageTrace.make {id?, preset?, params?, view?}` (or `imageTrace.makeAndExpand`),
|
||||
`imageTrace.setView`, `imageTrace.expand`, `imageTrace.release`.
|
||||
|
||||
`dataset.select`, `dataset.next` and `dataset.prev` reply `{name, applied, skipped}`. They skip an object that is
|
||||
`dataset.select`, `dataset.next` and `dataset.prev` reply `{name, applied, skipped}`. They add `overflow: [id…]`
|
||||
when area type or type on a path whose text they rewrote doesn't fit, as `text.setText` does (see
|
||||
[Fitting area type](#fitting-area-type-auto-size-shrink-text-to-fit-overflow)). They skip an object that is
|
||||
locked or inside a hidden or locked layer or group. They apply the data set's visibility values that show objects
|
||||
first, outermost object first, then its other values in the order the variables are defined, then its visibility
|
||||
values that hide objects, innermost object first. A data set that shows a group sets the objects inside it in the
|
||||
|
||||
@@ -407,6 +407,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- **Architecture:** 19+ crates with enforced layering (`cargo xtask layers`). Every action is a command (~400 engine + ~50 UI). Undo is unlimited via structural sharing. `command.batch` runs several commands as one transaction.
|
||||
- **Automation:**
|
||||
- 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 hidden or 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` 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.
|
||||
- Actions panel (record/playback, persisted), generic parameter dialogs for every "…" command.
|
||||
- Variables (data merge): text, visibility and image variables bound to objects (an object may carry several), dataset rows applied in one undo step (`variable.*`, `dataset.*`) from a Variables panel and Window › Variables, with dialogs to define, bind and edit, Make Text/Visibility/Image Dynamic, Capture/Update Data Set, CSV import/export (`dataset.import` / `dataset.export`), and a row in italics when an edit leaves the art not matching it; an image dataset value is a file path and apply keeps the object's bounds; bindings let go of deleted art and locked art is left alone; every command, like the rest, is drivable over MCP and the CLI.
|
||||
- JSON-lines control channel with real egui pointer and keyboard injection.
|
||||
|
||||
Reference in New Issue
Block a user