diff --git a/core/src/ui/active.rs b/core/src/ui/active.rs index 98974c9..4ac8e47 100644 --- a/core/src/ui/active.rs +++ b/core/src/ui/active.rs @@ -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, } diff --git a/core/src/ui/render_state.rs b/core/src/ui/render_state.rs index 7ebe602..3d4ac64 100644 --- a/core/src/ui/render_state.rs +++ b/core/src/ui/render_state.rs @@ -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, diff --git a/tests/cases/retained.rs b/tests/cases/retained.rs index 827b3b0..06dcd72 100644 --- a/tests/cases/retained.rs +++ b/tests/cases/retained.rs @@ -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)); +}