Merge #1033: M3: a variant switch takes the new variant's resizing unless the instance set its own (@MortenHusted)

This commit is contained in:
bflatastic
2026-10-24 09:13:33 +07:00
2 changed files with 80 additions and 0 deletions
+22
View File
@@ -354,6 +354,27 @@ fn carry_overrides(from: &Node, to: &mut Node, what: Carry) {
}
}
/// An instance switched from variant `was` to `new`: its resizing (sizing and min / max per axis)
/// that is still `was`'s follows `new`; what the instance set itself stays in `own`.
fn follow_variant_resizing(own: &mut ChildLayout, was: &ChildLayout, new: &ChildLayout) {
if own.sizing_h == was.sizing_h {
own.sizing_h = new.sizing_h;
}
if own.sizing_v == was.sizing_v {
own.sizing_v = new.sizing_v;
}
for (o, w, n) in [
(&mut own.min_w, was.min_w, new.min_w),
(&mut own.max_w, was.max_w, new.max_w),
(&mut own.min_h, was.min_h, new.min_h),
(&mut own.max_h, was.max_h, new.max_h),
] {
if *o == w {
*o = n;
}
}
}
/// Copy the values of property group `g` of `from` onto `to`, with the variables bound to them.
pub(crate) fn copy_group(from: &Node, to: &mut Node, g: &str) {
if from.bound.iter().chain(to.bound.iter()).any(|(k, _)| binding_group(k) == Some(g)) {
@@ -799,6 +820,7 @@ fn sync_instance_depth(doc: &mut Document, inst: NodeId, depth: usize, spare: BT
if let Some(prev) = root.source.and_then(|p| doc.node(p)).filter(|p| sibling_variants(doc, p.id, component)) {
let differs = diff_groups(prev, &comp);
root.overrides.retain(|g| g == "name" || !differs.contains(&g.as_str()));
follow_variant_resizing(&mut fresh.child_layout, &prev.child_layout, &comp.child_layout);
}
carry_overrides(&root, &mut fresh, Carry::Root);
if let Some(r) = doc.node_mut(inst) {
@@ -657,6 +657,64 @@ fn switching_variants_keeps_overrides_where_the_variants_agreed() {
healthy(&s);
}
/// A "Layout" set: Row (horizontal, hugging, a 40 wide "A") and Stacked (vertical, fixed 300 wide,
/// "A" filling it). Returns (set, row, stacked).
fn layout_set(s: &mut Session) -> (NodeId, NodeId, NodeId) {
let mut variants = Vec::new();
for (k, (name, props, child)) in [
("Layout=Row", json!({"layout": "horizontal", "gap": 8, "sizingH": "hug", "sizingV": "hug", "fills": []}), json!({})),
(
"Layout=Stacked",
json!({"layout": "vertical", "gap": 8, "sizingH": "fixed", "width": 300, "sizingV": "hug", "fills": []}),
json!({"sizingH": "fill"}),
),
]
.into_iter()
.enumerate()
{
let c = idv(&ex(s, "node.create", json!({"type": "component", "name": name, "x": k as f64 * 400.0, "props": props})));
ex(s, "node.create", json!({"type": "rectangle", "parent": c, "name": "A", "width": 40, "height": 20, "props": child}));
variants.push(c);
}
let set = idv(&ex(s, "component.combineVariants", json!({"ids": variants.clone()})));
(set, variants[0], variants[1])
}
#[test]
fn switching_variants_takes_the_new_variants_resizing_unless_the_instance_set_its_own() {
let mut s = Session::new();
let (set, row, stacked) = layout_set(&mut s);
let i = idv(&ex(&mut s, "instance.create", json!({"component": set, "x": 0, "y": 200})));
assert_eq!(component_of(&s, i), row);
ex(&mut s, "instance.setProperties", json!({"ids": [i], "properties": {"Layout": "Stacked"}}));
assert_eq!(component_of(&s, i), stacked);
let direct = idv(&ex(&mut s, "instance.create", json!({"component": stacked, "x": 0, "y": 400})));
let (n, d) = (node(&s, i), node(&s, direct));
assert_eq!(n.child_layout.sizing_h, Sizing::Fixed, "Row's Hug was never the instance's own: it takes Stacked's Fixed");
assert_eq!((n.width, n.child_layout.sizing_h), (d.width, d.child_layout.sizing_h), "the same as an instance made from Stacked");
let a = layer(&s, i, "A").unwrap();
assert_eq!((node(&s, a).child_layout.sizing_h, node(&s, a).width), (Sizing::Fill, 300.0), "A fills the 300 wide instance");
// Filling an auto layout parent afterwards leaves A filling, with nothing turned into an override.
let col = idv(&ex(
&mut s,
"node.create",
json!({"type": "frame", "name": "Column", "x": 600, "y": 0, "width": 500, "props": {"layout": "vertical", "sizingH": "fixed", "sizingV": "hug", "fills": []}}),
));
ex(&mut s, "node.reparent", json!({"ids": [i], "parent": col}));
ex(&mut s, "node.set", json!({"ids": [i], "props": {"sizingH": "fill"}}));
let a = layer(&s, i, "A").unwrap();
assert_eq!(node(&s, a).child_layout.sizing_h, Sizing::Fill, "A still fills");
assert!(!node(&s, a).overrides.contains("resizing"), "A's resizing is still the main's");
assert_eq!(node(&s, a).width, node(&s, i).width);
// Resizing the instance set itself stays across a switch.
let own = idv(&ex(&mut s, "instance.create", json!({"component": set, "x": 0, "y": 600})));
ex(&mut s, "node.set", json!({"ids": [own], "props": {"minWidth": 120}}));
ex(&mut s, "instance.setProperties", json!({"ids": [own], "properties": {"Layout": "Stacked"}}));
assert_eq!(node(&s, own).child_layout.min_w, Some(120.0), "its own min width stays");
assert_eq!(node(&s, own).child_layout.sizing_h, Sizing::Fixed, "its sizing, still Row's, follows Stacked");
healthy(&s);
}
#[test]
fn variant_combinations_order_default_and_conflicts() {
let mut s = Session::new();