From a9af065b4ae40a5b93adae1fbcd21c48ac00b9c6 Mon Sep 17 00:00:00 2001 From: Adoo Date: Fri, 8 Nov 2024 12:51:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(core):=20=F0=9F=90=9B=20changing=20`flex`?= =?UTF-8?q?=20does=20not=20relayout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + core/src/builtin_widgets/keep_alive.rs | 2 +- core/src/builtin_widgets/smooth_layout.rs | 2 +- core/src/state.rs | 2 +- core/src/widget.rs | 2 +- core/src/widget_tree/widget_id.rs | 13 ++++--- core/src/wrap_render.rs | 6 +--- widgets/src/layout/expanded.rs | 42 +++++++++++++++++++++-- 8 files changed, 53 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a08b111e..50854b4dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he - **core**: The `Provider` might be missing in a pipe class. (#648 @M-Adoo) - **core**: The child generated by the class may not be mounted. (#648 @M-Adoo) +- **widgets**: Changing the `flex` of `Expanded` does not trigger a relayout. (#652 @M-Adoo) ### Breaking diff --git a/core/src/builtin_widgets/keep_alive.rs b/core/src/builtin_widgets/keep_alive.rs index 1a0dc1587..3ccb45b29 100644 --- a/core/src/builtin_widgets/keep_alive.rs +++ b/core/src/builtin_widgets/keep_alive.rs @@ -29,7 +29,7 @@ impl<'c> ComposeChild<'c> for KeepAlive { let modifies = this.raw_modifies(); child .try_unwrap_state_and_attach(this) - .on_build(|id| id.dirty_subscribe(modifies, BuildCtx::get_mut().tree_mut())) + .on_build(|id| id.dirty_on(modifies)) } } diff --git a/core/src/builtin_widgets/smooth_layout.rs b/core/src/builtin_widgets/smooth_layout.rs index 2164b5eda..614b0ef6a 100644 --- a/core/src/builtin_widgets/smooth_layout.rs +++ b/core/src/builtin_widgets/smooth_layout.rs @@ -101,7 +101,7 @@ macro_rules! smooth_size_widget_impl { fn_widget!{ let modifies = this.read().0.raw_modifies(); WrapRender::combine_child(this, child) - .on_build(move |id, | id.dirty_subscribe(modifies, BuildCtx::get_mut().tree_mut()) ) + .on_build(move |id, | id.dirty_on(modifies) ) }.into_widget() } } diff --git a/core/src/state.rs b/core/src/state.rs index 4d673a14a..3e5cc048e 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -399,7 +399,7 @@ where Err(s) => { let modifies = s.raw_modifies(); let w = ReaderRender(s.clone_reader()).into_widget(); - w.on_build(move |id| id.dirty_subscribe(modifies, BuildCtx::get_mut().tree_mut())) + w.on_build(move |id| id.dirty_on(modifies)) } }, } diff --git a/core/src/widget.rs b/core/src/widget.rs index 525806942..ebd84c9bb 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -191,7 +191,7 @@ impl<'w> Widget<'w> { self.on_build(|id| id.attach_anonymous_data(data, BuildCtx::get_mut().tree_mut())) } - pub(crate) fn attach_data(self, data: Box) -> Self { + pub fn attach_data(self, data: Box) -> Self { self.on_build(|id| id.attach_data(data, BuildCtx::get_mut().tree_mut())) } diff --git a/core/src/widget_tree/widget_id.rs b/core/src/widget_tree/widget_id.rs index b449969de..6529ba529 100644 --- a/core/src/widget_tree/widget_id.rs +++ b/core/src/widget_tree/widget_id.rs @@ -35,11 +35,14 @@ impl WidgetId { tree.arena.get(self.0).map(|n| &**n.get()) } - /// Subscribe the modifies `upstream` to mark the widget dirty when the - /// `upstream` emit a modify event that contains `ModifyScope::FRAMEWORK`. - pub(crate) fn dirty_subscribe( - self, upstream: CloneableBoxOp<'static, ModifyScope, Infallible>, tree: &mut WidgetTree, - ) { + /// Subscribe to the modified `upstream` to mark the widget as dirty when the + /// `upstream` emits a modify event containing `ModifyScope::FRAMEWORK`. + /// + /// # 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>) { + let tree = BuildCtx::get_mut().tree_mut(); let dirty_set = tree.dirty_set.clone(); let h = upstream .filter(|b| b.contains(ModifyScope::FRAMEWORK)) diff --git a/core/src/wrap_render.rs b/core/src/wrap_render.rs index cccf628aa..e71cc595c 100644 --- a/core/src/wrap_render.rs +++ b/core/src/wrap_render.rs @@ -36,7 +36,6 @@ pub trait WrapRender { Self: Sized + 'static, { child.on_build(move |id| { - let mut modifies = None; let tree = BuildCtx::get_mut().tree_mut(); id.wrap_node(tree, |r| match this.try_into_value() { Ok(this) => Box::new(RenderPair { wrapper: Box::new(this), host: r }), @@ -44,16 +43,13 @@ pub trait WrapRender { let reader = match this.into_reader() { Ok(r) => r, Err(s) => { - modifies = Some(s.raw_modifies()); + id.dirty_on(s.raw_modifies()); s.clone_reader() } }; Box::new(RenderPair { wrapper: Box::new(reader), host: r }) } }); - if let Some(modifies) = modifies { - id.dirty_subscribe(modifies, tree); - } }) } } diff --git a/widgets/src/layout/expanded.rs b/widgets/src/layout/expanded.rs index 92bf92a5c..bced17c2e 100644 --- a/widgets/src/layout/expanded.rs +++ b/widgets/src/layout/expanded.rs @@ -16,14 +16,23 @@ pub struct Expanded { impl<'c> ComposeChild<'c> for Expanded { type Child = Widget<'c>; #[inline] - fn compose_child(this: impl StateWriter, child: Self::Child) -> Widget<'c> { - child.try_unwrap_state_and_attach(this) + fn compose_child(this: impl StateWriter, mut child: Self::Child) -> Widget<'c> { + let data: Box = match this.try_into_value() { + Ok(this) => Box::new(Queryable(this)), + Err(this) => { + let modifies = this.raw_modifies(); + child = child.on_build(|id| id.dirty_on(modifies)); + Box::new(this) + } + }; + + child.attach_data(data) } } #[cfg(test)] mod tests { - use ribir_core::test_helper::*; + use ribir_core::{reset_test_env, test_helper::*}; use ribir_dev_helper::*; use super::*; @@ -88,4 +97,31 @@ mod tests { LayoutCase::new(&[0, 5]).with_rect(ribir_geom::rect(100., 50., 50., 50.)), LayoutCase::new(&[0, 6]).with_rect(ribir_geom::rect(150., 50., 200., 50.)) ); + + #[test] + fn modifies_flex() { + reset_test_env!(); + + let (flex, w_flex) = split_value(1f32); + let widget = fn_widget! { + let expanded = @Expanded { flex: 1. }; + watch!(*$flex).subscribe(move |val| $expanded.write().flex = val); + + @Row { + @ $expanded { @ { Void } } + @Expanded { + flex: 1., + @ { Void } + } + @SizedBox { size: Size::new(100., 100.) } + } + }; + + let mut wnd = TestWindow::new_with_size(widget, Size::new(400., 100.)); + wnd.draw_frame(); + LayoutCase::expect_size(&wnd, &[0, 0], Size::new(150., 0.)); + *w_flex.write() = 2.; + wnd.draw_frame(); + LayoutCase::expect_size(&wnd, &[0, 0], Size::new(200., 0.)); + } }