Hand a redrawn widget the mask it inherited, not its own
`ActiveData::mask` is the mask a widget's drawing is clipped to, which is either one it set itself or the one it inherited. `redraw` passed it back as the *inherited* mask, so a `Masked` widget settled on its own was handed its own mask and `set_mask` asserted -- a panic on any local redraw of one, for as long as there has been a local-redraw path. The two are separate facts, so `parent_mask` keeps the second. That also states the question `remap_subtree` was asking. It compared a widget's mask with the one threaded down from its parent to find out whether the widget owned it; the comparison is now between the two fields on the widget, which is the same question asked where the answer lives, and the parameter goes. Checked: fmt, clippy, 87 suite tests including the new one, which panics without this; 18 core unit tests; the release oracle at 100 seeds; the fifteen shrinker cases at 400 seeds of depth 5; and `tabs` renders byte-identical at 1920x1200. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
32542d0c0b
commit
ea6dbae0dc
3 files changed
+35
-14
No files matched your search
@@ -58,7 +58,14 @@ pub struct ActiveData {
|
||||
pub own_align: RegionAlign,
|
||||
/// The movable region whose coordinates `region` uses.
|
||||
pub parent_move: MoveIdx,
|
||||
/// The mask its drawing is clipped to: one it set itself, or the one it
|
||||
/// inherited from whoever drew it.
|
||||
pub mask: MaskIdx,
|
||||
/// That inherited one. The two differ exactly where the widget set a
|
||||
/// mask of its own, which is the one it owns and the one a move rewrites
|
||||
/// -- and the one a redraw of it must not be handed back, since setting
|
||||
/// a mask asserts there is none.
|
||||
pub parent_mask: MaskIdx,
|
||||
pub layer: LayerId,
|
||||
}
|
||||
|
||||
|
||||
+13
-14
@@ -437,6 +437,7 @@ impl UiRenderState {
|
||||
move_idx,
|
||||
parent_move: info.parent_move,
|
||||
mask,
|
||||
parent_mask: info.mask,
|
||||
layer: info.layer,
|
||||
};
|
||||
rsc.on_draw(&active);
|
||||
@@ -623,18 +624,14 @@ impl UiRenderState {
|
||||
return None;
|
||||
}
|
||||
let moved = active.region != region;
|
||||
let (answer, old_region, slot, mask) = (
|
||||
(active.size, active.holds),
|
||||
active.region,
|
||||
active.move_idx,
|
||||
info.mask,
|
||||
);
|
||||
let (answer, old_region, slot) =
|
||||
((active.size, active.holds), active.region, active.move_idx);
|
||||
if moved {
|
||||
if has_region_node {
|
||||
self.moves.set(slot, region);
|
||||
} else {
|
||||
let remap = RegionRemap::new(old_region, region)?;
|
||||
self.remap_subtree(id, &remap, info.parent_move, mask, rsc);
|
||||
self.remap_subtree(id, &remap, info.parent_move, rsc);
|
||||
}
|
||||
}
|
||||
let active = self.active.get_mut(&id).unwrap();
|
||||
@@ -674,7 +671,6 @@ impl UiRenderState {
|
||||
id: WidgetId,
|
||||
remap: &RegionRemap,
|
||||
parent_move: MoveIdx,
|
||||
inherited_mask: MaskIdx,
|
||||
rsc: &mut dyn UiRsc,
|
||||
) {
|
||||
let active = self.active.get_mut(&id).unwrap();
|
||||
@@ -690,16 +686,18 @@ impl UiRenderState {
|
||||
*region = remap.apply(*region);
|
||||
}
|
||||
active.region = remap.apply(active.region);
|
||||
let mask = active.mask;
|
||||
let own_mask = (active.mask != active.parent_mask).then_some(active.mask);
|
||||
let children = active.children.len();
|
||||
if mask != inherited_mask && mask != MaskIdx::NONE {
|
||||
let mask = rsc.ui_mut().masks.get_mut(mask);
|
||||
// A mask the widget set itself moves with it; one it inherited
|
||||
// belongs to the widget that set it, and moves there or not at all.
|
||||
if let Some(idx) = own_mask {
|
||||
let mask = rsc.ui_mut().masks.get_mut(idx);
|
||||
debug_assert_eq!(mask.move_idx, parent_move);
|
||||
mask.region = remap.apply(mask.region);
|
||||
}
|
||||
for index in 0..children {
|
||||
let child = self.active[&id].children[index];
|
||||
self.remap_subtree(child, remap, parent_move, mask, rsc);
|
||||
self.remap_subtree(child, remap, parent_move, rsc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -793,6 +791,7 @@ impl UiRenderState {
|
||||
own_align: rsc.widgets().alignment(id),
|
||||
parent_move: info.parent_move,
|
||||
mask: info.mask,
|
||||
parent_mask: info.mask,
|
||||
layer: info.layer,
|
||||
},
|
||||
);
|
||||
@@ -963,7 +962,7 @@ impl UiRenderState {
|
||||
let Some(parent) = active.parent else {
|
||||
let region = Self::root_region(id, rsc.widgets());
|
||||
let info = DrawInfo {
|
||||
mask: active.mask,
|
||||
mask: active.parent_mask,
|
||||
..self.root_info(region)
|
||||
};
|
||||
#[cfg(feature = "layout-diagnostics")]
|
||||
@@ -990,7 +989,7 @@ impl UiRenderState {
|
||||
depth: active.depth,
|
||||
parent_move: active.parent_move,
|
||||
region_node: rsc.widgets().is_region_node(id),
|
||||
mask: active.mask,
|
||||
mask: active.parent_mask,
|
||||
given_len: active.given_len,
|
||||
offer_len: active.offer_len,
|
||||
px: given_px,
|
||||
|
||||
@@ -613,3 +613,18 @@ fn a_stacks_sizing_child_is_drawn_once_where_it_belongs() {
|
||||
assert_ne!(layer(front.id()), layer(background.id()));
|
||||
assert_eq!(draws.get(), 1);
|
||||
}
|
||||
|
||||
/// A widget's own mask is not the one it inherited, and a redraw of it
|
||||
/// inherits the second: handing back the first is handing it its own mask to
|
||||
/// set a second time, which `set_mask` asserts against.
|
||||
#[test]
|
||||
fn a_masked_widget_redrawn_on_its_own_sets_its_mask_again() {
|
||||
let mut h = Harness::new((400, 200));
|
||||
let inner = rect(Color::BLUE).add(&mut h.rsc);
|
||||
let masked = inner.masked().add(&mut h.rsc);
|
||||
let other = rect(Color::RED).width(100).add(&mut h.rsc);
|
||||
h.set_root((other, masked).span(Dir::RIGHT));
|
||||
h.rsc.widgets_mut().get_dyn_mut(masked.id());
|
||||
h.frame();
|
||||
assert_corners!(h, inner, (100, 0), (400, 200));
|
||||
}
|
||||
Reference in new issue
Block a user