diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ad204cf9..fe43e06a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he ### Features +- **core**: The `Render::dirty_phase` method has been added to allow widgets to mark only the paint phase as dirty when it is modified. (#pr @M-Adoo) - **macros**: Added the `part_reader!` macro to generate a partial reader from a reference of a reader. (#688 @M-Adoo) - **macros**: The `simple_declare` now supports the `stateless` meta attribute, `#[simple_declare(stateless)]`. (#pr @M-Adoo) diff --git a/core/src/builtin_widgets/align.rs b/core/src/builtin_widgets/align.rs index 1b2edf200..ffbd927a7 100644 --- a/core/src/builtin_widgets/align.rs +++ b/core/src/builtin_widgets/align.rs @@ -77,8 +77,8 @@ impl Declare for VAlignWidget { fn declarer() -> Self::Builder { FatObj::new(()) } } -impl_compose_child_for_wrap_render!(HAlignWidget); -impl_compose_child_for_wrap_render!(VAlignWidget); +impl_compose_child_for_wrap_render!(HAlignWidget, DirtyPhase::Layout); +impl_compose_child_for_wrap_render!(VAlignWidget, DirtyPhase::Layout); impl WrapRender for HAlignWidget { fn perform_layout(&self, mut clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { diff --git a/core/src/builtin_widgets/anchor.rs b/core/src/builtin_widgets/anchor.rs index 85ca7afc6..a954253fd 100644 --- a/core/src/builtin_widgets/anchor.rs +++ b/core/src/builtin_widgets/anchor.rs @@ -163,7 +163,7 @@ impl Declare for RelativeAnchor { fn declarer() -> Self::Builder { FatObj::new(()) } } -impl_compose_child_for_wrap_render!(RelativeAnchor); +impl_compose_child_for_wrap_render!(RelativeAnchor, DirtyPhase::Layout); impl WrapRender for RelativeAnchor { fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { diff --git a/core/src/builtin_widgets/box_decoration.rs b/core/src/builtin_widgets/box_decoration.rs index a2c061496..580a77574 100644 --- a/core/src/builtin_widgets/box_decoration.rs +++ b/core/src/builtin_widgets/box_decoration.rs @@ -42,7 +42,7 @@ impl BorderSide { pub fn new(width: f32, color: Brush) -> Self { Self { width, color } } } -impl_compose_child_for_wrap_render!(BoxDecoration); +impl_compose_child_for_wrap_render!(BoxDecoration, DirtyPhase::Layout); impl WrapRender for BoxDecoration { fn perform_layout(&self, mut clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { diff --git a/core/src/builtin_widgets/class.rs b/core/src/builtin_widgets/class.rs index 12d312c8a..c1e3b016a 100644 --- a/core/src/builtin_widgets/class.rs +++ b/core/src/builtin_widgets/class.rs @@ -389,9 +389,9 @@ fn class_update(node: &ClassNode, orig: &ClassNode, class: &Class, wnd_id: Windo node.dyn_info_mut().gen_range = GenRange::Single(new_id); let marker = tree.dirty_marker(); - marker.mark(new_id); + marker.mark(new_id, DirtyPhase::Layout); if new_id != orig_id && new_id.ancestor_of(orig_id, tree) { - marker.mark(orig_id); + marker.mark(orig_id, DirtyPhase::Layout); } } diff --git a/core/src/builtin_widgets/constrained_box.rs b/core/src/builtin_widgets/constrained_box.rs index 99879da0c..a2935f2bf 100644 --- a/core/src/builtin_widgets/constrained_box.rs +++ b/core/src/builtin_widgets/constrained_box.rs @@ -12,7 +12,7 @@ impl Declare for ConstrainedBox { fn declarer() -> Self::Builder { FatObj::new(()) } } -impl_compose_child_for_wrap_render!(ConstrainedBox); +impl_compose_child_for_wrap_render!(ConstrainedBox, DirtyPhase::Layout); impl WrapRender for ConstrainedBox { fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { diff --git a/core/src/builtin_widgets/foreground.rs b/core/src/builtin_widgets/foreground.rs index 1d843577a..cb7bb6f6a 100644 --- a/core/src/builtin_widgets/foreground.rs +++ b/core/src/builtin_widgets/foreground.rs @@ -15,7 +15,7 @@ impl Declare for Foreground { fn declarer() -> Self::Builder { FatObj::new(()) } } -impl_compose_child_for_wrap_render!(Foreground); +impl_compose_child_for_wrap_render!(Foreground, DirtyPhase::Paint); impl WrapRender for Foreground { fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { diff --git a/core/src/builtin_widgets/global_anchor.rs b/core/src/builtin_widgets/global_anchor.rs index a692504d9..644bad72c 100644 --- a/core/src/builtin_widgets/global_anchor.rs +++ b/core/src/builtin_widgets/global_anchor.rs @@ -298,7 +298,7 @@ impl<'c> ComposeChild<'c> for GlobalAnchor { } } .into_widget() - .dirty_on(modifies) + .dirty_on(modifies, DirtyPhase::Layout) } } diff --git a/core/src/builtin_widgets/ignore_pointer.rs b/core/src/builtin_widgets/ignore_pointer.rs index 341b72d67..08951522e 100644 --- a/core/src/builtin_widgets/ignore_pointer.rs +++ b/core/src/builtin_widgets/ignore_pointer.rs @@ -6,7 +6,7 @@ pub struct IgnorePointer { pub ignore: bool, } -impl_compose_child_for_wrap_render!(IgnorePointer); +impl_compose_child_for_wrap_render!(IgnorePointer, DirtyPhase::Paint); impl WrapRender for IgnorePointer { #[inline] diff --git a/core/src/builtin_widgets/keep_alive.rs b/core/src/builtin_widgets/keep_alive.rs index f3c82ae4a..a7109cd9d 100644 --- a/core/src/builtin_widgets/keep_alive.rs +++ b/core/src/builtin_widgets/keep_alive.rs @@ -32,7 +32,7 @@ impl<'c> ComposeChild<'c> for KeepAlive { { this.silent().wid = Some($w.track_id()); } w .into_widget() - .dirty_on(this.raw_modifies()) + .dirty_on(this.raw_modifies(), DirtyPhase::Layout) .try_unwrap_state_and_attach(this) } diff --git a/core/src/builtin_widgets/opacity.rs b/core/src/builtin_widgets/opacity.rs index 964e309a2..fc9b21986 100644 --- a/core/src/builtin_widgets/opacity.rs +++ b/core/src/builtin_widgets/opacity.rs @@ -16,7 +16,7 @@ impl Default for Opacity { fn default() -> Self { Self { opacity: 1.0 } } } -impl_compose_child_for_wrap_render!(Opacity); +impl_compose_child_for_wrap_render!(Opacity, DirtyPhase::Paint); impl WrapRender for Opacity { fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { diff --git a/core/src/builtin_widgets/smooth_layout.rs b/core/src/builtin_widgets/smooth_layout.rs index 6d0870ea3..e232f536b 100644 --- a/core/src/builtin_widgets/smooth_layout.rs +++ b/core/src/builtin_widgets/smooth_layout.rs @@ -194,7 +194,7 @@ macro_rules! smooth_size_widget_impl { } } - impl_compose_child!($name, true); + impl_compose_child!($name, DirtyPhase::Layout); }; } @@ -264,12 +264,12 @@ macro_rules! smooth_pos_widget_impl { } } - impl_compose_child!($name, false); + impl_compose_child!($name, DirtyPhase::Paint); }; } macro_rules! impl_compose_child { - ($name:ty, $dirty_self:literal) => { + ($name:ty, $dirty:expr) => { impl<'c> ComposeChild<'c> for $name { type Child = Widget<'c>; @@ -297,9 +297,7 @@ macro_rules! impl_compose_child { if marker.is_dirty(id) { inner.set_force_layout(true) } - if $dirty_self { - marker.mark(id); - } + marker.mark(id, $dirty); }) }) .unsubscribe_when_dropped(); @@ -308,7 +306,7 @@ macro_rules! impl_compose_child { .into_widget() .attach_anonymous_data(h); - WrapRender::combine_child(this, child) + WrapRender::combine_child(this, child, $dirty) } } }; diff --git a/core/src/builtin_widgets/transform_widget.rs b/core/src/builtin_widgets/transform_widget.rs index 73d6f7f44..ccb671638 100644 --- a/core/src/builtin_widgets/transform_widget.rs +++ b/core/src/builtin_widgets/transform_widget.rs @@ -11,7 +11,7 @@ impl Declare for TransformWidget { fn declarer() -> Self::Builder { FatObj::new(()) } } -impl_compose_child_for_wrap_render!(TransformWidget); +impl_compose_child_for_wrap_render!(TransformWidget, DirtyPhase::Paint); impl WrapRender for TransformWidget { #[inline] diff --git a/core/src/builtin_widgets/visibility.rs b/core/src/builtin_widgets/visibility.rs index 738909cc8..d6af3c641 100644 --- a/core/src/builtin_widgets/visibility.rs +++ b/core/src/builtin_widgets/visibility.rs @@ -33,7 +33,7 @@ struct VisibilityRender { display: bool, } -impl_compose_child_for_wrap_render!(VisibilityRender); +impl_compose_child_for_wrap_render!(VisibilityRender, DirtyPhase::Layout); impl WrapRender for VisibilityRender { #[inline] diff --git a/core/src/lib.rs b/core/src/lib.rs index e51254989..d7d035a85 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -64,7 +64,7 @@ pub mod prelude { ticker::{Duration, Instant}, widget::*, widget_children::*, - widget_tree::{BoxClamp, LayoutInfo, TrackId, WidgetId}, + widget_tree::{BoxClamp, DirtyPhase, LayoutInfo, TrackId, WidgetId}, window::Window, }; pub use crate::{timer, *}; diff --git a/core/src/overlay.rs b/core/src/overlay.rs index 2441da564..45c33a636 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -186,7 +186,7 @@ impl Overlay { let tree = wnd.tree_mut(); let root = tree.root(); wid.dispose_subtree(tree); - tree.dirty_marker().mark(root); + tree.dirty_marker().mark(root, DirtyPhase::Layout); }); } } @@ -239,7 +239,7 @@ impl Overlay { let tree = wnd.tree_mut(); tree.root().append(wid, tree); wid.on_mounted_subtree(tree); - tree.dirty_marker().mark(wid); + tree.dirty_marker().mark(wid, DirtyPhase::Layout); self.0.borrow_mut().showing = Some(ShowingInfo { generator: gen.into(), wnd_id: wnd.id() }); diff --git a/core/src/pipe.rs b/core/src/pipe.rs index 3b3957096..d8e9164e2 100644 --- a/core/src/pipe.rs +++ b/core/src/pipe.rs @@ -127,7 +127,7 @@ pub(crate) trait InnerPipe: Pipe + Sized { old.dispose_subtree(tree); new.on_mounted_subtree(tree); - tree.dirty_marker().mark(new); + tree.dirty_marker().mark(new, DirtyPhase::Layout); if without_ctx { BuildCtx::clear(); @@ -201,7 +201,7 @@ pub(crate) trait InnerPipe: Pipe + Sized { old.iter().for_each(|id| id.dispose_subtree(tree)); new.iter().for_each(|w| { w.on_mounted_subtree(tree); - tree.dirty_marker().mark(*w); + tree.dirty_marker().mark(*w, DirtyPhase::Layout); }); if without_ctx { @@ -307,7 +307,7 @@ pub(crate) trait InnerPipe: Pipe + Sized { } } - tree.dirty_marker().mark(p); + tree.dirty_marker().mark(p, DirtyPhase::Layout); if without_ctx { BuildCtx::clear(); diff --git a/core/src/state.rs b/core/src/state.rs index e7e85079d..076d36c93 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -400,7 +400,7 @@ where let modifies = s.raw_modifies(); ReaderRender(s.clone_reader()) .into_widget() - .dirty_on(modifies) + .dirty_on(modifies, s.read().dirty_phase()) } }, } diff --git a/core/src/widget.rs b/core/src/widget.rs index d5f47b783..97a17dca2 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -53,6 +53,11 @@ pub trait Render: 'static { HitTest { hit, can_hit_child: hit || !self.only_sized_by_parent() } } + /// By default, this function returns a `Layout` phase to indicate that the + /// widget should be marked as dirty when modified. When the layout phase is + /// marked as dirty, the paint phase will also be affected. + fn dirty_phase(&self) -> DirtyPhase { DirtyPhase::Layout } + /// Return a transform to map the coordinate from its parent to this widget. fn get_transform(&self) -> Option { None } } @@ -174,7 +179,9 @@ impl<'w> Widget<'w> { /// # Panic /// This method only works within a build process; otherwise, it will /// result in a panic. - pub fn dirty_on(self, upstream: CloneableBoxOp<'static, ModifyScope, Infallible>) -> Self { + pub fn dirty_on( + self, upstream: CloneableBoxOp<'static, ModifyScope, Infallible>, dirty: DirtyPhase, + ) -> Self { let track = TrackWidgetId::default(); let id = track.track_id(); @@ -184,7 +191,7 @@ impl<'w> Widget<'w> { .filter(|b| b.contains(ModifyScope::FRAMEWORK)) .subscribe(move |_| { if let Some(id) = id.get() { - marker.mark(id); + marker.mark(id, dirty); } }) .unsubscribe_when_dropped(); diff --git a/core/src/widget_tree.rs b/core/src/widget_tree.rs index f9a014ca9..aea7f3559 100644 --- a/core/src/widget_tree.rs +++ b/core/src/widget_tree.rs @@ -1,4 +1,4 @@ -use std::{cell::RefCell, cmp::Reverse, collections::HashSet, mem::MaybeUninit}; +use std::{cell::RefCell, cmp::Reverse, mem::MaybeUninit}; pub mod widget_id; use indextree::Arena; @@ -10,7 +10,16 @@ pub use layout_info::*; use self::widget::widget_id::new_node; use crate::{overlay::ShowingOverlays, prelude::*, render_helper::PureRender, window::WindowId}; -pub(crate) type DirtySet = Sc>>; +/// This enum defines the dirty phases of the widget. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum DirtyPhase { + /// Indicates that the widget needs to be relayouted. + Layout, + /// Indicates that the widget needs to be repainted. + Paint, +} + +pub(crate) type DirtySet = Sc>>; pub(crate) struct WidgetTree { pub(crate) root: WidgetId, @@ -48,7 +57,7 @@ impl WidgetTree { let root = BuildCtx::get_mut().build(root); self.root = root; - self.dirty_marker().mark(root); + self.dirty_marker().mark(root, DirtyPhase::Layout); root.on_mounted_subtree(self); root } @@ -139,23 +148,16 @@ impl WidgetTree { let mut needs_layout = vec![]; - let dirty_widgets = { - let mut state_changed = self.dirty_set.borrow_mut(); - let dirty_widgets = state_changed.clone(); - state_changed.clear(); - dirty_widgets - }; - - for id in dirty_widgets.iter() { - if id.is_dropped(self) { + for (id, dirty) in self.dirty_set.borrow_mut().drain() { + if id.is_dropped(self) || dirty != DirtyPhase::Layout { continue; } - if let Some(info) = self.store.get_mut(id) { + if let Some(info) = self.store.get_mut(&id) { info.size.take(); } - let mut relayout_root = *id; + let mut relayout_root = id; // All ancestors of this render widget should relayout until the one which only // sized by parent. for p in id.0.ancestors(&self.arena).skip(1).map(WidgetId) { @@ -237,12 +239,22 @@ impl WidgetTree { } impl DirtyMarker { - // todo: split layout and paint dirty. - - /// Mark the widget as dirty, return true if it is not already dirty. - pub(crate) fn mark(&self, id: WidgetId) -> bool { self.0.borrow_mut().insert(id) } + /// Mark the widget as dirty and return true if the widget was not already + /// marked as dirty in this phase previously. + pub(crate) fn mark(&self, id: WidgetId, scope: DirtyPhase) -> bool { + let mut map = self.0.borrow_mut(); + if let Some(s) = map.get_mut(&id) { + if *s == DirtyPhase::Paint && scope == DirtyPhase::Layout { + *s = scope; + return true; + } + false + } else { + map.insert(id, scope).is_none() + } + } - pub(crate) fn is_dirty(&self, id: WidgetId) -> bool { self.0.borrow().contains(&id) } + pub(crate) fn is_dirty(&self, id: WidgetId) -> bool { self.0.borrow().contains_key(&id) } } #[simple_declare] @@ -389,10 +401,12 @@ mod tests { assert_eq!(tree.count(tree.content_root()), 11); let root = tree.root(); - tree.dirty_marker().mark(root); + tree.dirty_marker().mark(root, DirtyPhase::Layout); let new_root = empty_node(&mut tree.arena); root.insert_after(new_root, tree); - tree.dirty_marker().mark(new_root); + tree + .dirty_marker() + .mark(new_root, DirtyPhase::Layout); tree.detach(root); tree.remove_subtree(root); @@ -467,4 +481,46 @@ mod tests { let len_1_widget = wnd.painter.borrow_mut().finish().len(); assert_eq!(len_1_widget, len_100_widget); } + + #[test] + fn paint_phase_dirty() { + reset_test_env!(); + + #[derive(Default)] + struct DirtyPaintOnly { + paint_cnt: std::cell::Cell, + } + + impl Render for DirtyPaintOnly { + fn perform_layout(&self, clamp: BoxClamp, _: &mut LayoutCtx) -> Size { clamp.max } + + fn paint(&self, _: &mut PaintingCtx) { self.paint_cnt.set(self.paint_cnt.get() + 1); } + + fn dirty_phase(&self) -> DirtyPhase { DirtyPhase::Paint } + } + + let paint_cnt = Stateful::new(DirtyPaintOnly::default()); + let c_paint_cnt = paint_cnt.clone_writer(); + + let (layout_cnt, w_layout_cnt) = split_value(0); + + let mut wnd = TestWindow::new(fat_obj! { + on_performed_layout: move |_| *$w_layout_cnt.write() += 1, + @ { paint_cnt.clone_writer() } + }); + + wnd.draw_frame(); + + assert_eq!(*layout_cnt.read(), 1); + assert_eq!(c_paint_cnt.read().paint_cnt.get(), 1); + + { + let _ = &mut *c_paint_cnt.write(); + } + + wnd.draw_frame(); + + assert_eq!(*layout_cnt.read(), 1); + assert_eq!(c_paint_cnt.read().paint_cnt.get(), 2); + } } diff --git a/core/src/window.rs b/core/src/window.rs index a5db7e0ac..a8ca62707 100644 --- a/core/src/window.rs +++ b/core/src/window.rs @@ -280,7 +280,7 @@ impl Window { if self.painter.borrow().viewport().size != size { let tree = self.tree_mut(); let root = tree.root(); - tree.dirty_marker().mark(root); + tree.dirty_marker().mark(root, DirtyPhase::Layout); tree.store.remove(root); let mut painter = self.painter.borrow_mut(); painter.set_viewport(Rect::from_size(size)); diff --git a/core/src/wrap_render.rs b/core/src/wrap_render.rs index 1db91bbc8..431db606a 100644 --- a/core/src/wrap_render.rs +++ b/core/src/wrap_render.rs @@ -14,7 +14,9 @@ use crate::prelude::*; /// child size, it can be implemented as a `WrapRender` instead of `Render`, /// eliminating the need to allocate a node in the widget tree. pub trait WrapRender { - fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size; + fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { + host.perform_layout(clamp, ctx) + } fn paint(&self, host: &dyn Render, ctx: &mut PaintingCtx) { host.paint(ctx) } @@ -29,7 +31,9 @@ pub trait WrapRender { fn get_transform(&self, host: &dyn Render) -> Option { host.get_transform() } - fn combine_child(this: impl StateWriter, mut child: Widget) -> Widget + fn combine_child( + this: impl StateWriter, mut child: Widget, dirty: DirtyPhase, + ) -> Widget where Self: Sized + 'static, { @@ -39,7 +43,7 @@ pub trait WrapRender { let reader = match this.into_reader() { Ok(r) => r, Err(s) => { - child = child.dirty_on(s.raw_modifies()); + child = child.dirty_on(s.raw_modifies(), dirty); s.clone_reader() } }; @@ -99,6 +103,8 @@ impl Render for RenderPair { .hit_test(self.host.as_render(), ctx, pos) } + fn dirty_phase(&self) -> DirtyPhase { self.host.dirty_phase() } + fn get_transform(&self) -> Option { self.wrapper.get_transform(self.host.as_render()) } } @@ -128,11 +134,11 @@ where #[macro_export] macro_rules! impl_compose_child_for_wrap_render { - ($name:ty) => { + ($name:ty, $dirty:expr) => { impl<'c> ComposeChild<'c> for $name { type Child = Widget<'c>; fn compose_child(this: impl StateWriter, child: Self::Child) -> Widget<'c> { - WrapRender::combine_child(this, child) + WrapRender::combine_child(this, child, $dirty) } } }; diff --git a/themes/material/src/state_layer.rs b/themes/material/src/state_layer.rs index f4bf50fca..c486f8a60 100644 --- a/themes/material/src/state_layer.rs +++ b/themes/material/src/state_layer.rs @@ -68,15 +68,11 @@ impl<'c, const M: u8> ComposeChild<'c> for StateLayer { type Child = Widget<'c>; fn compose_child(this: impl StateWriter, child: Self::Child) -> Widget<'c> { - WrapRender::combine_child(this, child) + WrapRender::combine_child(this, child, DirtyPhase::Paint) } } impl WrapRender for StateLayer { - fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { - host.perform_layout(clamp, ctx) - } - fn paint(&self, host: &dyn Render, ctx: &mut PaintingCtx) { if self.draw_opacity > 0. { // record transform and fill brush for draw layer used, because the host widget diff --git a/widgets/src/icon.rs b/widgets/src/icon.rs index a3df9d039..da53a0a43 100644 --- a/widgets/src/icon.rs +++ b/widgets/src/icon.rs @@ -83,7 +83,7 @@ impl<'c> ComposeChild<'c> for Icon { } struct IconText; -impl_compose_child_for_wrap_render!(IconText); +impl_compose_child_for_wrap_render!(IconText, DirtyPhase::Layout); impl WrapRender for IconText { fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { diff --git a/widgets/src/layout/expanded.rs b/widgets/src/layout/expanded.rs index 87991decf..22eb5e9c3 100644 --- a/widgets/src/layout/expanded.rs +++ b/widgets/src/layout/expanded.rs @@ -57,7 +57,7 @@ impl<'c> ComposeChild<'c> for Expanded { let data: Box = match this.try_into_value() { Ok(this) => Box::new(Queryable(this)), Err(this) => { - child = child.dirty_on(this.raw_modifies()); + child = child.dirty_on(this.raw_modifies(), DirtyPhase::Layout); Box::new(this) } }; diff --git a/widgets/src/layout/fractionally.rs b/widgets/src/layout/fractionally.rs index e0e9d3267..ade9dba88 100644 --- a/widgets/src/layout/fractionally.rs +++ b/widgets/src/layout/fractionally.rs @@ -40,7 +40,7 @@ impl Render for FractionallySizedBox { fn perform_layout(&self, clamp: BoxClamp, _: &mut LayoutCtx) -> Size { self.size(clamp) } } -ribir_core::impl_compose_child_for_wrap_render!(FractionallySizedBox); +ribir_core::impl_compose_child_for_wrap_render!(FractionallySizedBox, DirtyPhase::Layout); impl WrapRender for FractionallySizedBox { fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { @@ -66,7 +66,7 @@ impl Render for FractionallyWidthBox { } } -ribir_core::impl_compose_child_for_wrap_render!(FractionallyWidthBox); +ribir_core::impl_compose_child_for_wrap_render!(FractionallyWidthBox, DirtyPhase::Layout); impl WrapRender for FractionallyWidthBox { fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size { @@ -91,7 +91,7 @@ impl Render for FractionallyHeightBox { } } -ribir_core::impl_compose_child_for_wrap_render!(FractionallyHeightBox); +ribir_core::impl_compose_child_for_wrap_render!(FractionallyHeightBox, DirtyPhase::Layout); impl WrapRender for FractionallyHeightBox { fn perform_layout(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut LayoutCtx) -> Size {