M3: a variable mode set on a layer inside an instance is an override

A variable mode chosen on a layer inside an instance (the icon inside a
Close button, recoloured through an icon colour collection) wasn't an
override group. A full rebuild of the instance starts each layer over
from its main and carries only override groups, so the mode fell back
to the main's. Library import always rebuilds, as do main edits such as
a new auto layout direction, so a modal's recoloured close icon came
into other files in its default colour.

"modes" is now an override group: setting a mode inside an instance
records it, rebuilds carry it, rebasing a nested instance finds it, the
component panel lists it, and resetting it brings back the main's mode.
A top-level instance's own modes stay its own and record no override,
like its resizing.
This commit is contained in:
MortenHusted
2026-10-22 13:41:56 +02:00
parent 9ec80bf2c2
commit 6394e96005
6 changed files with 119 additions and 10 deletions
+49
View File
@@ -126,3 +126,52 @@ fn detaching_keeps_the_mode_it_took_from_its_main() {
assert_eq!(fill(&s, i), "0000FF", "the detached frame keeps mode B");
assert_eq!(s.doc().unwrap().node(i).unwrap().var_modes.len(), 1);
}
/// A "Button" holding an instance of a swatch "Icon" on mode A, and a "Modal" holding a Button
/// instance whose icon copy was set to mode B: (the Button main, its icon layer, the copy in Modal).
fn modal(s: &mut Session) -> (NodeId, NodeId, NodeId) {
tone(s);
let icon = swatch(s, "Icon", None);
let frame = id(run(s, "frame.create", json!({"x": 100, "y": 0, "width": 60, "height": 40, "name": "Button"})));
let button_icon = id(run(s, "instance.create", json!({"component": icon, "x": 10, "y": 10, "parent": frame})));
set_mode(s, button_icon, json!("A"));
let button = id(run(s, "component.create", json!({"ids": [frame]})));
let outer = id(run(s, "frame.create", json!({"x": 300, "y": 0, "width": 200, "height": 100, "name": "Modal"})));
let close = id(run(s, "instance.create", json!({"component": button, "x": 10, "y": 10, "parent": outer})));
let nested = s.doc().unwrap().node(close).unwrap().children[0];
set_mode(s, nested, json!("B"));
run(s, "component.create", json!({"ids": [outer]}));
(button, button_icon, nested)
}
#[test]
fn a_mode_set_on_a_layer_inside_an_instance_is_an_override() {
let mut s = Session::new();
let (button, button_icon, nested) = modal(&mut s);
assert_eq!(fill(&s, nested), "0000FF");
// An edit the copies can't take as they are rebuilds the Button instance.
run(&mut s, "node.set", json!({"ids": [button], "layout": "horizontal"}));
assert_eq!(fill(&s, nested), "0000FF", "a rebuild keeps the mode set on the copy");
// The main's layer choosing another mode doesn't reach it either.
set_mode(&mut s, button_icon, json!("B"));
set_mode(&mut s, button_icon, Value::Null);
assert_eq!(fill(&s, nested), "0000FF");
run(&mut s, "component.resetOverride", json!({"id": nested, "group": "modes"}));
assert_eq!(fill(&s, nested), "FF0000", "reset: the main's layer is on Auto (mode A)");
assert!(s.doc().unwrap().node(nested).unwrap().var_modes.is_empty());
}
#[test]
fn a_mode_set_on_an_icon_inside_a_placed_instance_survives_main_edits() {
let mut s = Session::new();
let (button, _, _) = modal(&mut s);
// A Button placed on the page, its icon recoloured through the icon's mode.
let placed = instance(&mut s, button);
let icon = s.doc().unwrap().node(placed).unwrap().children[0];
set_mode(&mut s, icon, json!("B"));
assert!(s.doc().unwrap().node(icon).unwrap().overrides.contains("modes"), "it shows as an override");
run(&mut s, "node.set", json!({"ids": [button], "layout": "vertical"}));
assert_eq!(fill(&s, icon), "0000FF", "a rebuild of the Button keeps it");
run(&mut s, "component.resetInstance", json!({"ids": [placed]}));
assert_eq!(fill(&s, icon), "FF0000", "reset all brings back the main's mode");
}