Variables: a visibility variable shows and hides locked art

variable.bind refused a locked layer, a locked object and objects
inside a locked layer or group for every kind of variable, and
dataset.select skipped them. A visibility variable bound before the
art was locked stopped working, and a locked layer could not be shown
or hidden by a data set.

A visibility variable now binds locked art and objects inside a locked
layer or group. dataset.select, dataset.next and dataset.prev show and
hide them and leave them locked. dataset.list, dataset.capture and
dataset.update read whether they show. Text, image and graph variables
are refused for locked art as before, and a row skips them. Binding
any variable still refuses art inside a hidden layer or group, and for
a visibility variable the error says the art is in a hidden group or
layer.

Docs: docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
dotCooCoo
2026-10-17 10:36:54 -07:00
parent f7de3d6a01
commit cbafe16b61
5 changed files with 144 additions and 45 deletions
+19 -15
View File
@@ -90,8 +90,9 @@ pub struct DataSet {
impl DataSet {
/// Whether the art, as [`Document::captured_values`] read it, still holds what this row
/// says. A value the art can't report (its object is locked or gone) isn't drift: the row
/// keeps it, and applying it again would skip it too.
/// says. A value the art can't report (its object is gone, or is locked or in a locked layer
/// or group and the variable isn't a visibility variable) isn't drift: the row keeps it, and
/// applying it again would skip it too.
pub fn matches(&self, current: &BTreeMap<String, DataValue>) -> bool {
self.values.iter().all(|(k, v)| current.get(k).is_none_or(|c| c == v))
}
@@ -188,26 +189,29 @@ impl Document {
self.variables.bindings = bindings;
}
/// Whether a variable may be bound to the object `id`: it is in the document, neither it nor
/// anything above it is locked, and nothing above it is hidden. Its own visibility doesn't
/// count, as that is what a visibility variable drives.
pub fn variable_target(&self, id: NodeId) -> bool {
self.ancestry(id).is_some_and(|a| a.iter().all(|i| self.node(*i).is_some_and(|n| !n.locked && (n.visible || *i == id))))
/// Whether a variable of `kind` may be bound to the object `id`: it is in the document,
/// nothing above it is hidden, and, unless `kind` is visibility, neither it nor anything above
/// it is locked. Its own visibility doesn't count, as that is what a visibility variable
/// drives.
pub fn variable_target(&self, id: NodeId, kind: VariableKind) -> bool {
let locks = kind != VariableKind::Visibility;
self.ancestry(id).is_some_and(|a| a.iter().all(|i| self.node(*i).is_some_and(|n| !(locks && n.locked) && (n.visible || *i == id))))
}
/// Whether a data set reads and rewrites the bound object `id`: it is in the document and
/// neither it nor anything above it is locked. Hidden objects count, and so do objects in a
/// hidden layer or group.
pub fn dataset_target(&self, id: NodeId) -> bool {
self.ancestry(id).is_some_and(|a| a.iter().all(|i| self.node(*i).is_some_and(|n| !n.locked)))
/// Whether a data set reads and rewrites the bound object `id` for a variable of `kind`: it
/// is in the document and, unless `kind` is visibility, neither it nor anything above it is
/// locked. Hidden objects count, and so do objects in a hidden layer or group.
pub fn dataset_target(&self, id: NodeId, kind: VariableKind) -> bool {
let locks = kind != VariableKind::Visibility;
self.ancestry(id).is_some_and(|a| a.iter().all(|i| self.node(*i).is_some_and(|n| !(locks && n.locked))))
}
/// What the object `id` holds for the variable of `kind`: its characters, whether it shows,
/// or a linked image's path. `None` when the object is gone, is not of that kind, has no
/// link path yet (an embedded image), or is locked or in a locked layer or group (the
/// document's value is whatever it was left at).
/// link path yet (an embedded image), or, unless `kind` is visibility, is locked or in a
/// locked layer or group (the document's value is whatever it was left at).
pub fn variable_value(&self, id: NodeId, kind: VariableKind) -> Option<DataValue> {
if !self.dataset_target(id) {
if !self.dataset_target(id, kind) {
return None;
}
let node = self.node(id)?;