mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-26 02:37:01 -06:00
G6: image variables swap linked files and keep bounds
Add VariableKind::Image with path values on datasets, makeImageDynamic, panel/dialog UI, and MCP docs so data merge can drive placed images.
This commit is contained in:
committed by
bflatastic
parent
c8310edea1
commit
41c8bd2a2d
@@ -1,9 +1,10 @@
|
||||
//! Variables (data merge): named values bound to objects, applied per dataset.
|
||||
//!
|
||||
//! One template document produces many variants: bind a type object to a `text`
|
||||
//! variable and anything to a `visibility` variable, put a row of values in each
|
||||
//! dataset, and `dataset.select` swaps the whole document to that row in one undo
|
||||
//! step. Agents reach every command through `run_command`, like all engine commands.
|
||||
//! variable, anything to a `visibility` variable, and a linked image to an `image`
|
||||
//! variable, put a row of values in each dataset, and `dataset.select` swaps the
|
||||
//! whole document to that row in one undo step. Agents reach every command through
|
||||
//! `run_command`, like all engine commands.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
||||
@@ -50,7 +51,8 @@ fn names_param(p: &Value, cmd: &str, fallback: Option<String>, nothing: &str) ->
|
||||
}
|
||||
|
||||
/// Refuse what a variable of `kind` can't drive: art that is locked or on a hidden or locked
|
||||
/// layer is not the document's to rewrite, and a text variable only drives type.
|
||||
/// layer is not the document's to rewrite; a text variable only drives type; an image variable
|
||||
/// only drives an image object.
|
||||
fn check_bindable(doc: &vectorcraft_doc::Document, ids: &[NodeId], kind: VariableKind, cmd: &str) -> Result<()> {
|
||||
for id in ids {
|
||||
match doc.node(*id) {
|
||||
@@ -59,6 +61,9 @@ fn check_bindable(doc: &vectorcraft_doc::Document, ids: &[NodeId], kind: Variabl
|
||||
Some(n) if kind == VariableKind::Text && !matches!(n.kind, NodeKind::Text(_)) => {
|
||||
return Err(bad(cmd, format!("node {} is not type", id.0)));
|
||||
}
|
||||
Some(n) if kind == VariableKind::Image && !matches!(n.kind, NodeKind::Image(_)) => {
|
||||
return Err(bad(cmd, format!("node {} is not an image", id.0)));
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
@@ -83,6 +88,8 @@ fn dataset_value(cmd: &str, var: &str, kind: VariableKind, v: &Value) -> Result<
|
||||
match kind {
|
||||
VariableKind::Text => v.as_str().map(|t| DataValue::Text(t.to_string())),
|
||||
VariableKind::Visibility => v.as_bool().map(DataValue::Visible),
|
||||
// An image value is a path string; empty is not a file to open.
|
||||
VariableKind::Image => v.as_str().map(str::trim).filter(|t| !t.is_empty()).map(|t| DataValue::Image(t.to_string())),
|
||||
}
|
||||
.ok_or_else(|| bad(cmd, format!("value for `{var}` must match its variable kind")))
|
||||
}
|
||||
@@ -104,7 +111,7 @@ fn values_param(vars: &Variables, p: &Value, cmd: &str) -> Result<BTreeMap<Strin
|
||||
fn define(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
const C: &str = "variable.define";
|
||||
let name = var_name(p, C)?;
|
||||
let kind = str_param(p, "kind").and_then(VariableKind::parse).ok_or_else(|| bad(C, "`kind` must be `text` or `visibility`"))?;
|
||||
let kind = str_param(p, "kind").and_then(VariableKind::parse).ok_or_else(|| bad(C, "`kind` must be `text`, `visibility` or `image`"))?;
|
||||
if s.doc()?.doc.variables.variable(&name).is_some() {
|
||||
return Err(bad(C, format!("variable `{name}` is already defined")));
|
||||
}
|
||||
@@ -147,7 +154,7 @@ fn rename(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({"from": from, "name": to}))
|
||||
}
|
||||
|
||||
/// Make Text Dynamic / Make Visibility Dynamic: define a variable of that kind and bind the
|
||||
/// Make Text / Visibility / Image Dynamic: define a variable of that kind and bind the
|
||||
/// selection to it in one step, naming it after what it drives — the panel's buttons.
|
||||
fn make_dynamic(s: &mut Session, kind: VariableKind, stem: &str) -> Result<Value> {
|
||||
// Errors name the command that ran: `variable.make{stem}Dynamic`.
|
||||
@@ -463,6 +470,8 @@ fn apply_dataset(s: &mut Session, name: &str) -> Result<Value> {
|
||||
n.visible = *show;
|
||||
true
|
||||
}),
|
||||
// Swap the linked file; keep the object's bounds. A missing path skips.
|
||||
(VariableKind::Image, DataValue::Image(path)) => links::set_image_from_path(d, id, path).is_ok(),
|
||||
_ => false,
|
||||
};
|
||||
if done {
|
||||
@@ -502,7 +511,15 @@ fn dataset_step(s: &mut Session, cmd: &str, forward: bool) -> Result<Value> {
|
||||
|
||||
pub fn specs() -> Vec<CommandSpec> {
|
||||
vec![
|
||||
cmd!("variable.define", "New Variable…", ["Window", "Variables"], None, "{name, kind: text|visibility} define a variable", has_doc, define),
|
||||
cmd!(
|
||||
"variable.define",
|
||||
"New Variable…",
|
||||
["Window", "Variables"],
|
||||
None,
|
||||
"{name, kind: text|visibility|image} define a variable",
|
||||
has_doc,
|
||||
define
|
||||
),
|
||||
cmd!(
|
||||
"variable.delete",
|
||||
"Delete Variable",
|
||||
@@ -540,12 +557,21 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
has_selection,
|
||||
|s, _| make_dynamic(s, VariableKind::Visibility, "Visibility")
|
||||
),
|
||||
cmd!(
|
||||
"variable.makeImageDynamic",
|
||||
"Make Image Dynamic",
|
||||
["Window", "Variables"],
|
||||
None,
|
||||
"{} define an image variable over the selection, named after it and bound to it",
|
||||
has_selection,
|
||||
|s, _| make_dynamic(s, VariableKind::Image, "Image")
|
||||
),
|
||||
cmd!(
|
||||
"variable.bind",
|
||||
"Bind Variable",
|
||||
["Window", "Variables"],
|
||||
None,
|
||||
"{variable, ids?} bind objects (default: the selection) to a variable; text needs type, locked art is refused. An object may hold several variables, and binding it to one it already has changes nothing → {bound, already}",
|
||||
"{variable, ids?} bind objects (default: the selection) to a variable; text needs type, image needs an image object, locked art is refused. An object may hold several variables, and binding it to one it already has changes nothing → {bound, already}",
|
||||
has_doc,
|
||||
bind
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user