diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fd887812..dabb8e91b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,14 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he ## [@Unreleased] - @ReleaseDate +### Features + +- **core**: Added support to query a `WriteRef` from a state, enabling users to modify the state after attaching it to a widget. (#601 @M-Adoo) + +### Changed + +- **core**: Render widgets no longer need to implement the `Query` trait. Data can only be queried if it's a state or wrapped with `Queryable`. (#601 @M-Adoo) + ## [0.4.0-alpha.1](https://github.com/RibirX/Ribir/compare/ribir-v0.3.0-beta.2...ribir-v0.4.0-alpha.1) - 2024-06-12 ### Changed diff --git a/core/src/builtin_widgets/align.rs b/core/src/builtin_widgets/align.rs index 3cee3f17e..8a3bd819e 100644 --- a/core/src/builtin_widgets/align.rs +++ b/core/src/builtin_widgets/align.rs @@ -54,13 +54,13 @@ pub enum VAlign { } /// A widget that align its child in x-axis, base on child's width. -#[derive(Query, SingleChild, Default)] +#[derive(SingleChild, Default)] pub struct HAlignWidget { pub h_align: HAlign, } /// A widget that align its child in y-axis, base on child's height. -#[derive(Query, SingleChild, Default)] +#[derive(SingleChild, Default)] pub struct VAlignWidget { pub v_align: VAlign, } diff --git a/core/src/builtin_widgets/anchor.rs b/core/src/builtin_widgets/anchor.rs index f5f07f106..22aa36e24 100644 --- a/core/src/builtin_widgets/anchor.rs +++ b/core/src/builtin_widgets/anchor.rs @@ -155,7 +155,7 @@ impl Anchor { } /// Widget use to anchor child constraints relative to parent widget. -#[derive(Query, SingleChild, Default)] +#[derive(SingleChild, Default)] pub struct RelativeAnchor { pub anchor: Anchor, } diff --git a/core/src/builtin_widgets/box_decoration.rs b/core/src/builtin_widgets/box_decoration.rs index a079c19bd..5be1cad5e 100644 --- a/core/src/builtin_widgets/box_decoration.rs +++ b/core/src/builtin_widgets/box_decoration.rs @@ -1,7 +1,7 @@ use crate::prelude::*; /// The BoxDecoration provides a variety of ways to draw a box. -#[derive(SingleChild, Default, Clone, Query)] +#[derive(SingleChild, Default, Clone)] pub struct BoxDecoration { /// The background of the box. pub background: Option, diff --git a/core/src/builtin_widgets/clip.rs b/core/src/builtin_widgets/clip.rs index bdd89b221..993935944 100644 --- a/core/src/builtin_widgets/clip.rs +++ b/core/src/builtin_widgets/clip.rs @@ -7,7 +7,7 @@ pub enum ClipType { Path(Path), } -#[derive(SingleChild, Query, Clone, Declare)] +#[derive(SingleChild, Clone, Declare)] pub struct Clip { #[declare(default)] pub clip: ClipType, diff --git a/core/src/builtin_widgets/container.rs b/core/src/builtin_widgets/container.rs index c874ebed7..38db90e4f 100644 --- a/core/src/builtin_widgets/container.rs +++ b/core/src/builtin_widgets/container.rs @@ -1,7 +1,7 @@ use crate::prelude::*; /// Widget with fixed size as a container for its child. -#[derive(Declare, Query, SingleChild)] +#[derive(Declare, SingleChild)] pub struct Container { pub size: Size, } diff --git a/core/src/builtin_widgets/fitted_box.rs b/core/src/builtin_widgets/fitted_box.rs index ce8109dc6..b73dd23a6 100644 --- a/core/src/builtin_widgets/fitted_box.rs +++ b/core/src/builtin_widgets/fitted_box.rs @@ -30,7 +30,7 @@ pub enum BoxFit { } /// Widget set how its child should be scale to fit its box. -#[derive(Query, SingleChild, Default)] +#[derive(SingleChild, Default)] pub struct FittedBox { pub box_fit: BoxFit, scale_cache: Cell, diff --git a/core/src/builtin_widgets/focus_node.rs b/core/src/builtin_widgets/focus_node.rs index 7539bc59f..8995c02f4 100644 --- a/core/src/builtin_widgets/focus_node.rs +++ b/core/src/builtin_widgets/focus_node.rs @@ -1,6 +1,6 @@ use crate::{events::focus_mgr::FocusHandle, prelude::*}; -#[derive(Query, Default)] +#[derive(Default)] pub struct RequestFocus { handle: Option, } @@ -22,7 +22,7 @@ impl ComposeChild for RequestFocus { } } .build(ctx!()) - .attach_state_data(this, ctx!()) + .try_unwrap_state_and_attach(this, ctx!()) } } } @@ -69,13 +69,11 @@ mod tests { let id = tree.content_root(); let node = id.get(&tree.arena).unwrap(); let mut cnt = 0; - node.query_type_inside_first(|b: &MixBuiltin| { + node.query_all_iter::().for_each(|b| { if b.contain_flag(BuiltinFlags::Focus) { cnt += 1; } - true }); - assert_eq!(cnt, 1); } } diff --git a/core/src/builtin_widgets/focus_scope.rs b/core/src/builtin_widgets/focus_scope.rs index a6342d368..37824674b 100644 --- a/core/src/builtin_widgets/focus_scope.rs +++ b/core/src/builtin_widgets/focus_scope.rs @@ -1,6 +1,6 @@ use crate::{events::focus_mgr::FocusType, prelude::*}; -#[derive(Declare, Query, Clone, Default)] +#[derive(Declare, Clone, Default)] pub struct FocusScope { /// If true, the descendants can not be focused. /// Default value is false, then the hold FocusScope subtree can be focused @@ -23,7 +23,7 @@ impl ComposeChild for FocusScope { on_disposed: move|e| e.window().remove_focus_node(e.id, FocusType::Scope), } .build(ctx!()) - .attach_state_data(this, ctx!()) + .try_unwrap_state_and_attach(this, ctx!()) } } } diff --git a/core/src/builtin_widgets/global_anchor.rs b/core/src/builtin_widgets/global_anchor.rs index d109fd8f9..5ae17094c 100644 --- a/core/src/builtin_widgets/global_anchor.rs +++ b/core/src/builtin_widgets/global_anchor.rs @@ -2,7 +2,7 @@ use std::rc::Rc; use crate::{prelude::*, ticker::FrameMsg}; -#[derive(Query, Default)] +#[derive(Default)] pub struct GlobalAnchor { pub global_anchor: Anchor, } diff --git a/core/src/builtin_widgets/ignore_pointer.rs b/core/src/builtin_widgets/ignore_pointer.rs index 67026a1dd..305fdd8ed 100644 --- a/core/src/builtin_widgets/ignore_pointer.rs +++ b/core/src/builtin_widgets/ignore_pointer.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -#[derive(Declare, SingleChild, Query, Clone)] +#[derive(Declare, SingleChild, Clone)] pub struct IgnorePointer { #[declare(default = true)] pub ignore: bool, diff --git a/core/src/builtin_widgets/image_widget.rs b/core/src/builtin_widgets/image_widget.rs index a3ee40634..98762897e 100644 --- a/core/src/builtin_widgets/image_widget.rs +++ b/core/src/builtin_widgets/image_widget.rs @@ -15,7 +15,3 @@ impl Render for Resource { } } } - -impl Query for Resource { - crate::widget::impl_query_self_only!(); -} diff --git a/core/src/builtin_widgets/keep_alive.rs b/core/src/builtin_widgets/keep_alive.rs index 83334081f..a17e6f96b 100644 --- a/core/src/builtin_widgets/keep_alive.rs +++ b/core/src/builtin_widgets/keep_alive.rs @@ -12,7 +12,7 @@ use crate::prelude::*; /// dropped. /// /// It's useful when you need run a leave animation for a widget. -#[derive(Query, Default)] +#[derive(Default)] pub struct KeepAlive { pub keep_alive: bool, } @@ -29,7 +29,7 @@ impl ComposeChild for KeepAlive { fn compose_child(this: impl StateWriter, child: Self::Child) -> impl WidgetBuilder { fn_widget! { let modifies = this.raw_modifies(); - child.attach_state_data(this, ctx!()).dirty_subscribe(modifies, ctx!()) + child.try_unwrap_state_and_attach(this, ctx!()).dirty_subscribe(modifies, ctx!()) } } } diff --git a/core/src/builtin_widgets/key.rs b/core/src/builtin_widgets/key.rs index 06d1ea754..e4e0ccfd7 100644 --- a/core/src/builtin_widgets/key.rs +++ b/core/src/builtin_widgets/key.rs @@ -3,7 +3,7 @@ use std::{ fmt::Debug, }; -use crate::prelude::*; +use crate::{data_widget::Queryable, prelude::*}; /// `Key` help `Ribir` to track if two widget is a same widget in two frames. /// Abstract all builtin key into a same type. @@ -127,15 +127,11 @@ impl ComposeChild for KeyWidget { fn compose_child(this: impl StateWriter, child: Self::Child) -> impl WidgetBuilder { fn_widget! { let data: Box = Box::new(this); - child.attach_data(data, ctx!()).build(ctx!()) + child.attach_data(Queryable(data), ctx!()).build(ctx!()) } } } -impl Query for Box { - crate::widget::impl_query_self_only!(); -} - impl KeyWidget where V: Clone + PartialEq, diff --git a/core/src/builtin_widgets/margin.rs b/core/src/builtin_widgets/margin.rs index 670ad3578..a3199ede9 100644 --- a/core/src/builtin_widgets/margin.rs +++ b/core/src/builtin_widgets/margin.rs @@ -9,7 +9,7 @@ pub struct EdgeInsets { } /// A widget that create space around its child. -#[derive(SingleChild, Default, Query, Clone, PartialEq)] +#[derive(SingleChild, Default, Clone, PartialEq)] pub struct Margin { pub margin: EdgeInsets, } diff --git a/core/src/builtin_widgets/mix_builtin.rs b/core/src/builtin_widgets/mix_builtin.rs index acf882cd6..a1a6349c5 100644 --- a/core/src/builtin_widgets/mix_builtin.rs +++ b/core/src/builtin_widgets/mix_builtin.rs @@ -3,7 +3,7 @@ use std::{cell::Cell, convert::Infallible}; use rxrust::prelude::*; use self::focus_mgr::FocusType; -use crate::prelude::*; +use crate::{data_widget::Queryable, prelude::*}; const MULTI_TAP_DURATION: Duration = Duration::from_millis(250); @@ -41,7 +41,7 @@ bitflags! { pub type EventSubject = MutRefItemSubject<'static, Event, Infallible>; -#[derive(Default, Query)] +#[derive(Default)] pub struct MixBuiltin { flags: Cell, subject: EventSubject, @@ -349,28 +349,31 @@ fn life_fn_once_to_fn_mut( impl ComposeChild for MixBuiltin { type Child = Widget; #[inline] - fn compose_child(this: impl StateWriter, child: Self::Child) -> impl WidgetBuilder { + fn compose_child( + this: impl StateWriter, mut child: Self::Child, + ) -> impl WidgetBuilder { move |ctx: &BuildCtx| match this.try_into_value() { Ok(this) => { let mut this = Some(this); - child + if let Some(m) = child .id() .assert_get(&ctx.tree.borrow().arena) - .query_most_outside(|m: &MixBuiltin| { - let this = this.take().unwrap(); - if !m.contain_flag(BuiltinFlags::Focus) && this.contain_flag(BuiltinFlags::Focus) { - this.callbacks_for_focus_node(); - } - m.merge(this) - }); + .query_ref::() + { + let this = unsafe { this.take().unwrap_unchecked() }; + if !m.contain_flag(BuiltinFlags::Focus) && this.contain_flag(BuiltinFlags::Focus) { + this.callbacks_for_focus_node(); + } + m.merge(this); + } + // We do not use an else branch here, due to the borrow conflict of the `ctx`. if let Some(this) = this { if this.contain_flag(BuiltinFlags::Focus) { this.callbacks_for_focus_node(); } - child.attach_data(this, ctx) - } else { - child + child = child.attach_data(Queryable(this), ctx); } + child } Err(this) => { if this.read().contain_flag(BuiltinFlags::Focus) { diff --git a/core/src/builtin_widgets/opacity.rs b/core/src/builtin_widgets/opacity.rs index 37c4799f4..0ceb9b156 100644 --- a/core/src/builtin_widgets/opacity.rs +++ b/core/src/builtin_widgets/opacity.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -#[derive(Query, Clone, SingleChild)] +#[derive(Clone, SingleChild)] pub struct Opacity { pub opacity: f32, } diff --git a/core/src/builtin_widgets/padding.rs b/core/src/builtin_widgets/padding.rs index 6a0faa3b3..046c54ba8 100644 --- a/core/src/builtin_widgets/padding.rs +++ b/core/src/builtin_widgets/padding.rs @@ -1,7 +1,7 @@ use crate::prelude::*; /// A widget that insets its child by the given padding. -#[derive(SingleChild, Query, Clone, Default)] +#[derive(SingleChild, Clone, Default)] pub struct Padding { pub padding: EdgeInsets, } diff --git a/core/src/builtin_widgets/scrollable.rs b/core/src/builtin_widgets/scrollable.rs index 17e03633f..ad9d57772 100644 --- a/core/src/builtin_widgets/scrollable.rs +++ b/core/src/builtin_widgets/scrollable.rs @@ -176,7 +176,7 @@ mod tests { test_assert(Scrollable::Both, 100., 100., 0., 0.); } - #[derive(SingleChild, Query, Declare, Clone)] + #[derive(SingleChild, Declare, Clone)] pub struct FixedBox { pub size: Size, } diff --git a/core/src/builtin_widgets/svg.rs b/core/src/builtin_widgets/svg.rs index 52eccb121..7d22514bf 100644 --- a/core/src/builtin_widgets/svg.rs +++ b/core/src/builtin_widgets/svg.rs @@ -10,7 +10,3 @@ impl Render for Svg { painter.draw_svg(self); } } - -impl Query for Svg { - crate::widget::impl_query_self_only!(); -} diff --git a/core/src/builtin_widgets/theme.rs b/core/src/builtin_widgets/theme.rs index 4ff1f0d5e..ebdf8703f 100644 --- a/core/src/builtin_widgets/theme.rs +++ b/core/src/builtin_widgets/theme.rs @@ -7,7 +7,7 @@ use ribir_algo::Sc; pub use ribir_algo::{CowArc, Resource}; use ribir_macros::Declare; -use crate::{fill_svgs, prelude::*}; +use crate::{data_widget::Queryable, fill_svgs, prelude::*}; mod palette; pub use palette::*; @@ -61,7 +61,6 @@ pub struct InheritTheme { pub font_files: Option>, } -#[derive(Query)] pub enum Theme { Full(FullTheme), Inherit(InheritTheme), @@ -92,7 +91,7 @@ impl ComposeChild for ThemeWidget { // node, because the subtree may be hold its id. // // A `Void` is cheap for a theme. - let p = Void.build(ctx!()).attach_data(theme, ctx!()); + let p = Void.build(ctx!()).attach_data(Queryable(theme), ctx!()); // shadow the context with the theme. let ctx = BuildCtx::new_with_data(Some(p.id()), ctx!().tree, themes); let child = child.gen_widget(&ctx); diff --git a/core/src/builtin_widgets/transform_widget.rs b/core/src/builtin_widgets/transform_widget.rs index a8eb6b6e8..0e62542f4 100644 --- a/core/src/builtin_widgets/transform_widget.rs +++ b/core/src/builtin_widgets/transform_widget.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -#[derive(SingleChild, Query, Clone, Default)] +#[derive(SingleChild, Clone, Default)] pub struct TransformWidget { pub transform: Transform, } diff --git a/core/src/builtin_widgets/unconstrained_box.rs b/core/src/builtin_widgets/unconstrained_box.rs index bfb6b67fc..d44e01bc3 100644 --- a/core/src/builtin_widgets/unconstrained_box.rs +++ b/core/src/builtin_widgets/unconstrained_box.rs @@ -1,6 +1,6 @@ use crate::prelude::*; -#[derive(Declare, Query, SingleChild)] +#[derive(Declare, SingleChild)] /// A widget that imposes no constraints on its child, allowing it to layout and /// display as its "natural" size. Its size is equal to its child then clamp by /// parent. diff --git a/core/src/builtin_widgets/visibility.rs b/core/src/builtin_widgets/visibility.rs index ca66e0036..6ed17da6f 100644 --- a/core/src/builtin_widgets/visibility.rs +++ b/core/src/builtin_widgets/visibility.rs @@ -27,7 +27,7 @@ impl ComposeChild for Visibility { } } -#[derive(SingleChild, Query, Declare, Clone)] +#[derive(SingleChild, Declare, Clone)] struct VisibilityRender { display: bool, } diff --git a/core/src/builtin_widgets/void.rs b/core/src/builtin_widgets/void.rs index a91f9371c..e55a18b07 100644 --- a/core/src/builtin_widgets/void.rs +++ b/core/src/builtin_widgets/void.rs @@ -4,7 +4,7 @@ use crate::prelude::*; /// node in `widget!` macro, or hold a place in tree. When it have a child /// itself will be dropped when build tree, otherwise as a render widget but do /// nothing. -#[derive(SingleChild, Query, Declare)] +#[derive(SingleChild, Declare)] pub struct Void; impl Render for Void { diff --git a/core/src/context/build_ctx.rs b/core/src/context/build_ctx.rs index f2687b9d4..0f9580673 100644 --- a/core/src/context/build_ctx.rs +++ b/core/src/context/build_ctx.rs @@ -4,6 +4,7 @@ use std::{ }; use ribir_algo::Sc; +use widget_id::RenderQueryable; use crate::{ prelude::*, @@ -73,11 +74,11 @@ impl<'a> BuildCtx<'a> { } /// Get the widget back of `id`, panic if not exist. - pub(crate) fn assert_get(&self, id: WidgetId) -> Ref { + pub(crate) fn assert_get(&self, id: WidgetId) -> Ref { Ref::map(self.tree.borrow(), |tree| id.assert_get(&tree.arena)) } - pub(crate) fn alloc_widget(&self, widget: Box) -> WidgetId { + pub(crate) fn alloc_widget(&self, widget: Box) -> WidgetId { new_node(&mut self.tree.borrow_mut().arena, widget) } @@ -122,11 +123,13 @@ impl<'a> BuildCtx<'a> { let arena = &self.tree.borrow().arena; p.ancestors(arena).any(|p| { - p.assert_get(arena) - .query_type_inside_first(|t: &Sc| { - themes.push(t.clone()); - matches!(t.deref(), Theme::Inherit(_)) - }); + for t in p.assert_get(arena).query_all_iter::>() { + themes.push(t.clone()); + if matches!(&**t, Theme::Full(_)) { + break; + } + } + matches!(themes.last().map(Sc::deref), Some(Theme::Full(_))) }); themes diff --git a/core/src/context/widget_ctx.rs b/core/src/context/widget_ctx.rs index 5293c795e..afaad8547 100644 --- a/core/src/context/widget_ctx.rs +++ b/core/src/context/widget_ctx.rs @@ -72,7 +72,6 @@ pub trait WidgetCtx { pub(crate) trait WidgetCtxImpl { fn id(&self) -> WidgetId; - // todo: return sc instead of rc fn current_wnd(&self) -> Rc; #[inline] @@ -201,7 +200,8 @@ impl WidgetCtx for T { ) -> Option { self.with_tree(|tree| { id.assert_get(&tree.arena) - .query_most_outside(callback) + .query_ref::() + .map(|r| callback(&r)) }) } diff --git a/core/src/data_widget.rs b/core/src/data_widget.rs index 5546042d7..6da88e370 100644 --- a/core/src/data_widget.rs +++ b/core/src/data_widget.rs @@ -1,37 +1,41 @@ //! Data widget help attach data to a widget and get a new widget which behavior //! is same as origin widget. -use crate::{ - prelude::*, - render_helper::{RenderProxy, RenderTarget}, -}; +use widget_id::RenderQueryable; -pub struct DataWidget { - render: Box, +use crate::{prelude::*, render_helper::RenderProxy}; + +pub(crate) struct DataAttacher { + render: Box, data: D, } +/// This is a wrapper for a data that makes it queryable. +pub struct Queryable(pub T); + /// A wrapper widget which can attach any data to a widget and not care about /// what the data is. -pub struct AnonymousWrapper { - render: Box, +pub(crate) struct AnonymousAttacher { + render: Box, _data: Box, } -impl DataWidget { - pub(crate) fn attach(render: Box, data: D) -> Box { - Box::new(RenderProxy::new(DataWidget { render, data })) +impl DataAttacher { + pub(crate) fn new(render: Box, data: D) -> Self { + DataAttacher { render, data } } } -impl AnonymousWrapper { +impl AnonymousAttacher { #[inline] - pub fn new(render: Box, data: Box) -> Self { - AnonymousWrapper { render, _data: data } + pub fn new(render: Box, data: Box) -> Self { + AnonymousAttacher { render, _data: data } } } +// fixme: These APIs should be private, use Provide instead. impl Widget { + /// Attach data to a widget and user can query it. pub fn attach_data(self, data: D, ctx: &BuildCtx) -> Widget { let arena = &mut ctx.tree.borrow_mut().arena; self.id().attach_data(data, arena); @@ -39,15 +43,19 @@ impl Widget { self } - pub fn attach_state_data( - self, data: impl StateReader, ctx: &BuildCtx, + /// Attach a state to a widget and try to unwrap it before attaching. + /// + /// User can query the state or its value type. + pub fn try_unwrap_state_and_attach( + self, data: impl StateWriter, ctx: &BuildCtx, ) -> Widget { match data.try_into_value() { - Ok(data) => self.attach_data(data, ctx), + Ok(data) => self.attach_data(Queryable(data), ctx), Err(data) => self.attach_data(data, ctx), } } + /// Attach anonymous data to a widget and user can't query it. pub fn attach_anonymous_data(self, data: impl Any, ctx: &BuildCtx) -> Widget { let arena = &mut ctx.tree.borrow_mut().arena; self.id().attach_anonymous_data(data, arena); @@ -55,35 +63,57 @@ impl Widget { } } -impl RenderTarget for DataWidget { - type Target = dyn Render; +impl RenderProxy for DataAttacher { + type R = dyn RenderQueryable; - #[inline] - fn proxy(&self, f: impl FnOnce(&Self::Target) -> V) -> V { f(&*self.render) } + type Target<'r> = &'r dyn RenderQueryable + where + Self: 'r; + + fn proxy(&self) -> Self::Target<'_> { self.render.as_ref() } } -impl Query for DataWidget { - fn query_inside_first( - &self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { - self.render.query_inside_first(type_id, callback) - && self.data.query_inside_first(type_id, callback) +impl Query for DataAttacher { + fn query_all(&self, type_id: TypeId) -> smallvec::SmallVec<[QueryHandle; 1]> { + let mut types = self.render.query_all(type_id); + types.extend(self.data.query_all(type_id)); + types } - fn query_outside_first( - &self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { - self.data.query_outside_first(type_id, callback) - && self.render.query_outside_first(type_id, callback) + fn query(&self, type_id: TypeId) -> Option { + self + .data + .query(type_id) + .or_else(|| self.render.query(type_id)) } } -impl Query for AnonymousWrapper { - crate::widget::impl_proxy_query!(render); -} +impl Query for AnonymousAttacher { + #[inline] + fn query_all(&self, type_id: TypeId) -> smallvec::SmallVec<[QueryHandle; 1]> { + self.render.query_all(type_id) + } -impl RenderTarget for AnonymousWrapper { - type Target = dyn Render; #[inline] - fn proxy(&self, f: impl FnOnce(&Self::Target) -> V) -> V { f(&*self.render) } + fn query(&self, type_id: TypeId) -> Option { self.render.query(type_id) } +} + +impl RenderProxy for AnonymousAttacher { + type R = dyn RenderQueryable; + + type Target<'r> = &'r dyn RenderQueryable + where + Self: 'r; + + fn proxy(&self) -> Self::Target<'_> { self.render.as_ref() } +} + +impl Query for Queryable { + fn query_all(&self, type_id: TypeId) -> smallvec::SmallVec<[QueryHandle; 1]> { + self.query(type_id).into_iter().collect() + } + + fn query(&self, type_id: TypeId) -> Option { + (type_id == self.0.type_id()).then(|| QueryHandle::new(&self.0)) + } } diff --git a/core/src/events/dispatcher.rs b/core/src/events/dispatcher.rs index 6cb0d89fc..318d949d5 100644 --- a/core/src/events/dispatcher.rs +++ b/core/src/events/dispatcher.rs @@ -159,14 +159,9 @@ impl Dispatcher { let nearest_focus = self.pointer_down_uid.and_then(|wid| { wid.ancestors(&tree.arena).find(|id| { - let mut is_focus_node = false; - if let Some(w) = id.get(&tree.arena) { - w.query_type_outside_first(|m: &MixBuiltin| { - is_focus_node |= m.contain_flag(BuiltinFlags::Focus); - !is_focus_node - }); - } - is_focus_node + id.get(&tree.arena) + .and_then(|w| w.query_ref::()) + .map_or(false, |m| m.contain_flag(BuiltinFlags::Focus)) }) }); if let Some(focus_id) = nearest_focus { diff --git a/core/src/events/focus_mgr.rs b/core/src/events/focus_mgr.rs index 45c6e5e14..c6a9e179f 100644 --- a/core/src/events/focus_mgr.rs +++ b/core/src/events/focus_mgr.rs @@ -339,16 +339,11 @@ impl FocusManager { .find_map(|wid| self.node_ids.get(&wid).copied())?; self.scope_list(node_id).find(|id| { - let mut has_ignore = false; - self.get(*id).and_then(|n| n.wid).map(|wid| { - wid - .get(arena)? - .query_most_inside(|s: &FocusScope| { - has_ignore = s.skip_descendants; - !has_ignore - }) - }); - has_ignore + self + .get(*id) + .and_then(|n| n.wid) + .and_then(|wid| wid.get(arena)?.query_ref::()) + .map_or(false, |s| s.skip_descendants) }) } @@ -357,21 +352,22 @@ impl FocusManager { let tree = wnd.widget_tree.borrow(); scope_id .and_then(|id| id.get(&tree.arena)) - .and_then(|r| r.query_most_inside(|s: &FocusScope| s.clone())) + .and_then(|r| r.query_ref::().map(|s| s.clone())) .unwrap_or_default() } fn tab_index(&self, node_id: NodeId) -> i16 { let wnd = self.window(); - let get_index = || { - let wid = self.get(node_id)?.wid?; - let tree = wnd.widget_tree.borrow(); - let r = wid.get(&tree.arena)?; - r.query_most_outside(|s: &MixBuiltin| s.get_tab_index()) - }; - - get_index().unwrap_or(0) + self + .get(node_id) + .and_then(|n| n.wid) + .and_then(|wid| { + let tree = wnd.widget_tree.borrow(); + let m = wid.get(&tree.arena)?.query_ref::()?; + Some(m.get_tab_index()) + }) + .unwrap_or_default() } fn insert_node(&mut self, parent: NodeId, node_id: NodeId, wid: WidgetId, arena: &TreeArena) { diff --git a/core/src/lib.rs b/core/src/lib.rs index 2b2340908..643513d91 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -22,6 +22,7 @@ pub mod widget_children; pub mod window; pub use rxrust; pub mod overlay; +pub mod query; pub mod prelude { pub use log; @@ -34,14 +35,13 @@ pub mod prelude { pub use ribir_text::*; #[doc(hidden)] pub use rxrust::prelude::*; + pub use smallvec; #[doc(no_inline)] pub use crate::builtin_widgets::*; #[doc(no_inline)] pub use crate::context::*; #[doc(no_inline)] - pub use crate::data_widget::{AnonymousWrapper, DataWidget}; - #[doc(no_inline)] pub use crate::declare::*; #[doc(no_inline)] pub use crate::events::*; @@ -63,6 +63,7 @@ pub mod prelude { pub use crate::window::Window; pub use crate::{ animation::*, + query::*, ticker::{Duration, Instant}, }; } diff --git a/core/src/overlay.rs b/core/src/overlay.rs index eeb5f194b..eef557534 100644 --- a/core/src/overlay.rs +++ b/core/src/overlay.rs @@ -1,7 +1,5 @@ use std::{cell::RefCell, mem::replace, rc::Rc}; -use ribir_macros::Query; - use crate::prelude::*; #[derive(Clone)] @@ -285,7 +283,6 @@ impl OverlayState { fn close_handle(&self) -> OverlayCloseHandle { OverlayCloseHandle(self.clone()) } } -#[derive(Query)] pub(crate) struct OverlayRoot {} impl Render for OverlayRoot { diff --git a/core/src/pipe.rs b/core/src/pipe.rs index ecf5524b6..3c886499f 100644 --- a/core/src/pipe.rs +++ b/core/src/pipe.rs @@ -6,11 +6,12 @@ use std::{ use ribir_algo::Sc; use rxrust::ops::box_it::BoxOp; +use widget_id::RenderQueryable; use crate::{ builtin_widgets::key::AnyKey, prelude::*, - render_helper::{RenderProxy, RenderTarget}, + render_helper::{PureRender, RenderProxy}, ticker::FrameMsg, }; @@ -661,34 +662,42 @@ fn update_children_key_status(old: WidgetId, new: WidgetId, ctx: &BuildCtx) { match (o_first == o_last, n_first == n_last) { (true, true) => update_key_status_single(o_first, n_first, ctx), (true, false) => { - inspect_key(o_first, ctx, |old_key| { + if let Some(old_key) = ctx + .assert_get(o_first) + .query_ref::>() + { let o_key = old_key.key(); new.children(tree).any(|n| { - inspect_key(n, ctx, |new_key| { + if let Some(new_key) = ctx.assert_get(n).query_ref::>() { let same_key = o_key == new_key.key(); if same_key { - update_key_states(old_key, o_first, new_key, n, ctx); + update_key_states(&**old_key, o_first, &**new_key, n, ctx); } same_key - }) - .unwrap_or(false) + } else { + false + } }); - }); + } } (false, true) => { - inspect_key(n_first, ctx, |new_key| { + if let Some(new_key) = ctx + .assert_get(n_first) + .query_ref::>() + { let n_key = new_key.key(); old.children(tree).any(|o| { - inspect_key(o, ctx, |old_key| { + if let Some(old_key) = ctx.assert_get(o).query_ref::>() { let same_key = old_key.key() == n_key; if same_key { - update_key_states(old_key, o, new_key, n_first, ctx); + update_key_states(&**old_key, o, &**new_key, n_first, ctx); } same_key - }) - .unwrap_or(false) - }) - }); + } else { + false + } + }); + } } (false, false) => update_key_state_multi(old.children(tree), new.children(tree), ctx), } @@ -697,13 +706,13 @@ fn update_children_key_status(old: WidgetId, new: WidgetId, ctx: &BuildCtx) { } fn update_key_status_single(old: WidgetId, new: WidgetId, ctx: &BuildCtx) { - inspect_key(old, ctx, |old_key| { - inspect_key(new, ctx, |new_key| { + if let Some(old_key) = ctx.assert_get(old).query_ref::>() { + if let Some(new_key) = ctx.assert_get(new).query_ref::>() { if old_key.key() == new_key.key() { - update_key_states(old_key, old, new_key, new, ctx) + update_key_states(&**old_key, old, &**new_key, new, ctx); } - }) - }); + } + } } fn update_key_state_multi( @@ -711,28 +720,24 @@ fn update_key_state_multi( ) { let mut old_key_list = ahash::HashMap::default(); for o in old { - inspect_key(o, ctx, |old_key: &dyn AnyKey| { + if let Some(old_key) = ctx.assert_get(o).query_ref::>() { old_key_list.insert(old_key.key(), o); - }); + } } if !old_key_list.is_empty() { for n in new { - inspect_key(n, ctx, |new_key| { + if let Some(new_key) = ctx.assert_get(n).query_ref::>() { if let Some(o) = old_key_list.get(&new_key.key()).copied() { - inspect_key(o, ctx, |old_key| update_key_states(old_key, o, new_key, n, ctx)); + if let Some(old_key) = ctx.assert_get(o).query_ref::>() { + update_key_states(&**old_key, o, &**new_key, n, ctx); + } } - }); + } } } } -fn inspect_key(id: WidgetId, ctx: &BuildCtx, mut cb: impl FnMut(&dyn AnyKey) -> R) -> Option { - ctx - .assert_get(id) - .query_most_outside::, _>(|key_widget| cb(key_widget.deref())) -} - fn update_key_states( old_key: &dyn AnyKey, old: WidgetId, new_key: &dyn AnyKey, new: WidgetId, ctx: &BuildCtx, ) { @@ -773,7 +778,7 @@ impl MultiChild for Box> {} struct PipeNode(Sc>); struct InnerPipeNode { - data: Box, + data: Box, dyn_info: Box, } trait DynWidgetInfo: Any { @@ -929,7 +934,7 @@ impl PipeNode { let inner_node = InnerPipeNode { data: r, dyn_info }; let p = Self(Sc::new(UnsafeCell::new(inner_node))); pipe_node = Some(p.clone()); - Box::new(RenderProxy::new(p)) + Box::new(p) }); // Safety: init before. @@ -964,7 +969,7 @@ impl PipeNode { // if the subscription is closed, we can cancel and unwrap the `PipeNode` // immediately. if u.is_closed() { - let v = std::mem::replace(&mut self.as_mut().data, Box::new(Void)); + let v = std::mem::replace(&mut self.as_mut().data, Box::new(PureRender(Void))); *id.get_node_mut(tree).unwrap() = v; } else { id.attach_anonymous_data(u.unsubscribe_when_dropped(), tree) @@ -977,25 +982,29 @@ fn set_pos_of_multi(widgets: &[WidgetId], ctx: &BuildCtx) { widgets.iter().enumerate().for_each(|(pos, wid)| { wid .assert_get(arena) - .query_type_outside_first(|info: &DynInfo| { - info.set_pos_of_multi(pos); - true - }); + .query_all_iter::() + .for_each(|info| info.set_pos_of_multi(pos)) }); } fn query_info_outside_until( id: WidgetId, to: &Sc, ctx: &BuildCtx, mut cb: impl FnMut(&DynInfo), ) { - id.assert_get(&ctx.tree.borrow().arena) - .query_type_outside_first(|info: &DynInfo| { - cb(info); - - info - .as_any() - .downcast_ref::>() - .map_or(true, |info| !Sc::ptr_eq(info, to)) - }); + for info in id + .assert_get(&ctx.tree.borrow().arena) + .query_all_iter::() + .rev() + { + cb(&info); + + if info + .as_any() + .downcast_ref::>() + .map_or(false, |info| Sc::ptr_eq(info, to)) + { + break; + } + } } fn pipe_priority_value(info: &Sc, handle: BuildCtxHandle) -> i64 @@ -1017,29 +1026,34 @@ where } impl Query for PipeNode { - fn query_inside_first( - &self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { + fn query_all(&self, type_id: TypeId) -> smallvec::SmallVec<[QueryHandle; 1]> { let p = self.as_ref(); - p.data.query_inside_first(type_id, callback) && p.dyn_info.query_inside_first(type_id, callback) + let mut types = p.data.query_all(type_id); + if type_id == TypeId::of::() { + types.push(QueryHandle::new(&p.dyn_info)); + } + + types } - fn query_outside_first( - &self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { + fn query(&self, type_id: TypeId) -> Option { let p = self.as_ref(); - p.dyn_info.query_outside_first(type_id, callback) - && p.data.query_outside_first(type_id, callback) + if type_id == TypeId::of::() { + Some(QueryHandle::new(&p.dyn_info)) + } else { + p.data.query(type_id) + } } } -impl Query for Box { - crate::widget::impl_query_self_only!(); -} +impl RenderProxy for PipeNode { + type R = dyn RenderQueryable; + + type Target<'r> = &'r dyn RenderQueryable + where + Self: 'r; -impl RenderTarget for PipeNode { - type Target = dyn Render; - fn proxy(&self, f: impl FnOnce(&Self::Target) -> V) -> V { f(&*self.as_ref().data) } + fn proxy(&self) -> Self::Target<'_> { &*self.as_ref().data } } #[cfg(test)] @@ -1417,7 +1431,7 @@ mod tests { } } - #[derive(Declare, Query)] + #[derive(Declare)] struct TaskWidget { trigger: u32, paint_cnt: Rc>, diff --git a/core/src/query.rs b/core/src/query.rs new file mode 100644 index 000000000..2a28b70ce --- /dev/null +++ b/core/src/query.rs @@ -0,0 +1,257 @@ +use std::any::{Any, TypeId}; + +use smallvec::SmallVec; + +use crate::state::{ + MapReader, MapWriterAsReader, ReadRef, Reader, StateReader, StateWriter, WriteRef, +}; + +/// A type can composed by many types, this trait help us to query the type and +/// the inner type by its type id +pub trait Query: Any { + /// Queries all types that match the provided type id, returning their handles + /// in an inside-to-outside order. + fn query_all(&self, type_id: TypeId) -> SmallVec<[QueryHandle; 1]>; + + /// Queries the type that matches the provided type id, returning its handle. + /// This method always returns the outermost type. + fn query(&self, type_id: TypeId) -> Option; +} + +/// A dynamic handle to a query result of a data, so we can use it in a trait +/// object. +pub struct QueryHandle<'a>(InnerHandle<'a>); + +/// A reference to a query result of a data, it's similar to `&T`. +pub struct QueryRef<'a, T> { + pub(crate) type_ref: &'a T, + pub(crate) _data: Option>, +} + +impl<'a> QueryHandle<'a> { + pub fn new(r: &'a dyn Any) -> Self { QueryHandle(InnerHandle::Ref(r)) } + + /// Downcast the to type `T` and returns a reference to it, + /// return `None` if the type not match + pub fn downcast_ref(&self) -> Option<&T> { + match self.0 { + InnerHandle::Ref(r) => r.downcast_ref::(), + InnerHandle::Owned(ref o) => query_downcast_ref(o.as_ref()), + } + } + + /// Attempts to downcast to type `T` and returns a mutable reference + /// to it. If the types do not match, it returns `None`. + /// + /// This method can only return a mutable reference if the handle points + /// to a `WriteRef`. This is because in Ribir, the final tree is immutable by + /// default. Any modifications to the tree can only be made through the + /// `WriteRef` of the `StateWriter`. + pub fn downcast_mut(&mut self) -> Option<&mut T> { + let InnerHandle::Owned(ref mut o) = self.0 else { + return None; + }; + (o.query_type() == TypeId::of::>()).then(|| { + // SAFETY: the creater guarantees that the query type is `WriteRef`, + unsafe { &mut **(o.as_mut() as *mut dyn QueryResult as *mut WriteRef<'a, T>) } + }) + } + + pub(crate) fn owned(o: Box) -> Self { QueryHandle(InnerHandle::Owned(o)) } + + pub fn into_ref(self) -> Option> { + match self.0 { + InnerHandle::Ref(r) if r.type_id() == TypeId::of::() => { + Some(QueryRef { type_ref: r.downcast_ref::().unwrap(), _data: None }) + } + InnerHandle::Owned(o) => { + let r = query_downcast_ref(o.as_ref()); + // hold the _data to keep the data alive + r.map(|r| QueryRef { type_ref: r, _data: Some(o) }) + } + _ => None, + } + } + + pub fn into_mut(self) -> Option> { + let InnerHandle::Owned(o) = self.0 else { + return None; + }; + (o.query_type() == TypeId::of::>()).then(|| { + // SAFETY: the creater guarantees that the query type is `WriteRef`, + let w_r = unsafe { + let ptr = Box::into_raw(o); + Box::from_raw(ptr as *mut WriteRef<'a, T>) + }; + *w_r + }) + } +} + +fn query_downcast_ref<'a, T: Any>(q: &(dyn QueryResult + 'a)) -> Option<&'a T> { + let q_type = q.query_type(); + if q_type == TypeId::of::() { + // SAFETY: the creater guarantees that the query type is `T`, + let t = unsafe { &*(q as *const dyn QueryResult as *const T) }; + Some(t) + } else if q_type == TypeId::of::>() { + // SAFETY: the creater guarantees that the query type is `WriteRef`, + let wr = unsafe { &*(q as *const dyn QueryResult as *const WriteRef<'a, T>) }; + Some(wr) + } else if q_type == TypeId::of::>() { + // SAFETY: the creater guarantees that the query type is `WriteRef`, + let rr = unsafe { &*(q as *const dyn QueryResult as *const ReadRef<'a, T>) }; + Some(rr) + } else { + None + } +} +enum InnerHandle<'a> { + Ref(&'a dyn Any), + Owned(Box), +} + +pub(crate) trait QueryResult { + fn query_type(&self) -> TypeId; +} + +impl<'a> QueryResult for &'a dyn Any { + fn query_type(&self) -> TypeId { Any::type_id(*self) } +} + +impl<'a, T: Any> QueryResult for WriteRef<'a, T> { + fn query_type(&self) -> TypeId { TypeId::of::>() } +} + +impl<'a, T: Any> QueryResult for ReadRef<'a, T> { + fn query_type(&self) -> TypeId { TypeId::of::>() } +} + +impl<'a, T> std::ops::Deref for QueryRef<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { self.type_ref } +} + +impl Query for T +where + T::Value: 'static + Sized, +{ + fn query_all(&self, type_id: TypeId) -> smallvec::SmallVec<[QueryHandle; 1]> { + // The value of the writer and the writer itself cannot be queried + // at the same time. + self.query(type_id).into_iter().collect() + } + + fn query(&self, type_id: TypeId) -> Option { + if type_id == TypeId::of::() { + Some(QueryHandle::owned(Box::new(self.write()))) + } else if type_id == self.type_id() { + Some(QueryHandle::new(self)) + } else { + None + } + } +} + +macro_rules! impl_query_for_reader { + () => { + // The value of the reader and the reader itself cannot be queried + // at the same time. + fn query_all(&self, type_id: TypeId) -> SmallVec<[QueryHandle; 1]> { + self.query(type_id).into_iter().collect() + } + + fn query(&self, type_id: TypeId) -> Option { + if type_id == TypeId::of::() { + Some(QueryHandle::owned(Box::new(self.read()))) + } else if type_id == self.type_id() { + Some(QueryHandle::new(self)) + } else { + None + } + } + }; +} + +impl Query for MapReader +where + Self: StateReader, + V: Any, +{ + impl_query_for_reader!(); +} + +impl Query for MapWriterAsReader +where + Self: StateReader, + V: Any, +{ + impl_query_for_reader!(); +} + +impl Query for Reader { + impl_query_for_reader!(); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + data_widget::Queryable, + reset_test_env, + state::{PartData, State, StateReader, StateWriter}, + }; + + #[test] + fn query_ref() { + reset_test_env!(); + + struct X; + let x = Queryable(X); + let mut h = x.query(TypeId::of::()).unwrap(); + assert!(h.downcast_ref::().is_some()); + assert!(h.downcast_mut::().is_none()); + assert!(h.into_ref::().is_some()); + let h = x.query(TypeId::of::()).unwrap(); + assert!(h.into_mut::().is_none()); + } + + #[test] + fn query_state() { + reset_test_env!(); + + let x = State::value(0i32); + let mut h = x.query(TypeId::of::()).unwrap(); + assert!(h.downcast_ref::().is_some()); + assert!(h.downcast_mut::().is_some()); + assert!(h.into_mut::().is_some()); + } + + #[test] + fn query_split_state() { + reset_test_env!(); + + struct X { + a: i32, + _b: i32, + } + + let x = State::value(X { a: 0, _b: 1 }); + let y = x.split_writer(|x| PartData::from_ref_mut(&mut x.a)); + let mut h = y.query(TypeId::of::()).unwrap(); + assert!(h.downcast_ref::().is_some()); + assert!(h.downcast_mut::().is_some()); + } + + #[test] + fn query_reader_only() { + reset_test_env!(); + + let x = State::value(0i32).clone_reader(); + let mut h = x.query(TypeId::of::()).unwrap(); + assert!(h.downcast_ref::().is_some()); + assert!(h.downcast_mut::().is_none()); + assert!(h.into_mut::().is_none()); + } +} diff --git a/core/src/render_helper.rs b/core/src/render_helper.rs index 73ea05332..770f238af 100644 --- a/core/src/render_helper.rs +++ b/core/src/render_helper.rs @@ -1,72 +1,91 @@ use std::cell::RefCell; use ribir_algo::Sc; -use state_cell::StateCell; +use state_cell::{ReadRef, StateCell}; use crate::prelude::*; -pub(crate) trait RenderTarget { - type Target: Render + ?Sized; - fn proxy(&self, f: impl FnOnce(&Self::Target) -> V) -> V; +pub trait RenderProxy { + type R: ?Sized + Render; + type Target<'r>: Deref + where + Self: 'r; + + fn proxy(&self) -> Self::Target<'_>; } -pub(crate) struct RenderProxy(R); +pub(crate) struct PureRender(pub R); -impl RenderProxy { - #[inline] - pub fn new(render: R) -> Self { Self(render) } -} +impl Query for PureRender { + fn query_all(&self, _: TypeId) -> smallvec::SmallVec<[QueryHandle; 1]> { + smallvec::SmallVec::new() + } -impl SingleChild for RenderProxy -where - R: RenderTarget, - R::Target: SingleChild, -{ + fn query(&self, _: TypeId) -> Option { None } } -impl MultiChild for RenderProxy -where - R: RenderTarget, - R::Target: MultiChild, -{ +impl RenderProxy for PureRender { + type R = R; + type Target<'r> =&'r R where Self: 'r; + + fn proxy(&self) -> Self::Target<'_> { &self.0 } } -impl Render for RenderProxy { +impl Render for T { #[inline] fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size { - self.0.proxy(|r| r.perform_layout(clamp, ctx)) + self.proxy().perform_layout(clamp, ctx) } #[inline] - fn paint(&self, ctx: &mut PaintingCtx) { self.0.proxy(|r| r.paint(ctx)) } + fn paint(&self, ctx: &mut PaintingCtx) { self.proxy().paint(ctx) } #[inline] - fn only_sized_by_parent(&self) -> bool { self.0.proxy(|r| r.only_sized_by_parent()) } + fn only_sized_by_parent(&self) -> bool { self.proxy().only_sized_by_parent() } #[inline] - fn hit_test(&self, ctx: &HitTestCtx, pos: Point) -> HitTest { - self.0.proxy(|r| r.hit_test(ctx, pos)) - } + fn hit_test(&self, ctx: &HitTestCtx, pos: Point) -> HitTest { self.proxy().hit_test(ctx, pos) } #[inline] - fn get_transform(&self) -> Option { self.0.proxy(|r| r.get_transform()) } + fn get_transform(&self) -> Option { self.proxy().get_transform() } } -impl Query for RenderProxy { - crate::widget::impl_proxy_query!(0); +impl RenderProxy for RefCell { + type R = R; + + type Target<'r> = std::cell::Ref<'r, R> + where + Self: 'r; + + fn proxy(&self) -> Self::Target<'_> { self.borrow() } } -impl RenderTarget for RefCell { - type Target = R; - fn proxy(&self, f: impl FnOnce(&Self::Target) -> V) -> V { f(&*self.borrow()) } +impl RenderProxy for StateCell { + type R = R; + + type Target<'r> = ReadRef<'r, R> + where + Self: 'r; + + fn proxy(&self) -> Self::Target<'_> { self.read() } } -impl RenderTarget for StateCell { - type Target = R; - fn proxy(&self, f: impl FnOnce(&Self::Target) -> V) -> V { f(&*self.read()) } +impl RenderProxy for Sc { + type R = R; + + type Target<'r> = &'r R + where + Self: 'r; + + fn proxy(&self) -> Self::Target<'_> { self } } -impl RenderTarget for Sc { - type Target = R::Target; - fn proxy(&self, f: impl FnOnce(&Self::Target) -> V) -> V { self.deref().proxy(f) } +impl RenderProxy for Resource { + type R = R; + + type Target<'r> = &'r R + where + Self: 'r; + + fn proxy(&self) -> Self::Target<'_> { self } } diff --git a/core/src/state.rs b/core/src/state.rs index 72a65fc64..0a75b414a 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -15,8 +15,8 @@ pub use map_state::*; pub use prior_op::*; use rxrust::ops::box_it::{BoxOp, CloneableBoxOp}; pub use splitted_state::*; -pub use state_cell::PartData; -use state_cell::{StateCell, ValueMutRef, ValueRef}; +pub use state_cell::{PartData, ReadRef}; +use state_cell::{StateCell, ValueMutRef}; pub use stateful::*; pub use watcher::*; @@ -148,10 +148,6 @@ pub trait StateWriter: StateWatcher { } } -/// Wraps a borrowed reference to a value in a state. -/// A wrapper type for an immutably borrowed value from a `StateReader`. -pub struct ReadRef<'a, V>(ValueRef<'a, V>); - pub struct WriteRef<'a, V> { value: ValueMutRef<'a, V>, control: &'a dyn WriterControl, @@ -180,7 +176,7 @@ impl StateReader for State { fn read(&self) -> ReadRef { match self.inner_ref() { - InnerState::Data(w) => ReadRef::new(w.read()), + InnerState::Data(w) => w.read(), InnerState::Stateful(w) => w.read(), } } @@ -266,51 +262,14 @@ impl State { } } -impl<'a, V> ReadRef<'a, V> { - pub(crate) fn new(r: ValueRef<'a, V>) -> ReadRef<'a, V> { ReadRef(r) } - - /// Make a new `ReadRef` by mapping the value of the current `ReadRef`. - pub fn map(mut r: ReadRef<'a, V>, f: impl FnOnce(&V) -> PartData) -> ReadRef<'a, U> { - ReadRef(ValueRef { value: f(&mut r.0.value), borrow: r.0.borrow }) - } - - /// Split the current `ReadRef` into two `ReadRef`s by mapping the value to - /// two parts. - pub fn map_split( - orig: ReadRef<'a, V>, f: impl FnOnce(&V) -> (PartData, PartData), - ) -> (ReadRef<'a, U>, ReadRef<'a, W>) { - let (a, b) = f(&*orig); - let borrow = orig.0.borrow.clone(); - - (ReadRef(ValueRef { value: a, borrow: borrow.clone() }), ReadRef(ValueRef { value: b, borrow })) - } - - pub(crate) fn mut_as_ref_map( - orig: ReadRef<'a, V>, f: impl FnOnce(&mut V) -> PartData, - ) -> ReadRef<'a, U> { - let ValueRef { value, borrow } = orig.0; - let value = match value { - PartData::PartRef(mut ptr) => unsafe { - // Safety: This method is used to map a state to a part of it. Although a `&mut - // T` is passed to the closure, it is the user's responsibility to - // ensure that the closure does not modify the state. - f(ptr.as_mut()) - }, - PartData::PartData(mut data) => f(&mut data), - }; - - ReadRef(ValueRef { value, borrow }) - } -} - impl<'a, V> WriteRef<'a, V> { pub fn map(mut orig: WriteRef<'a, V>, part_map: M) -> WriteRef<'a, U> where M: Fn(&mut V) -> PartData, { - let value = part_map(&mut orig.value); + let inner = part_map(&mut orig.value); let borrow = orig.value.borrow.clone(); - let value = ValueMutRef { value, borrow }; + let value = ValueMutRef { inner, borrow }; WriteRef { value, modified: false, modify_scope: orig.modify_scope, control: orig.control } } @@ -324,8 +283,8 @@ impl<'a, V> WriteRef<'a, V> { let WriteRef { control, modify_scope, modified, .. } = orig; let (a, b) = f(&mut *orig.value); let borrow = orig.value.borrow.clone(); - let a = ValueMutRef { value: a, borrow: borrow.clone() }; - let b = ValueMutRef { value: b, borrow }; + let a = ValueMutRef { inner: a, borrow: borrow.clone() }; + let b = ValueMutRef { inner: b, borrow }; ( WriteRef { value: a, modified, modify_scope, control }, WriteRef { value: b, modified, modify_scope, control }, @@ -339,14 +298,6 @@ impl<'a, V> WriteRef<'a, V> { pub fn forget_modifies(&mut self) -> bool { std::mem::replace(&mut self.modified, false) } } -impl<'a, W> Deref for ReadRef<'a, W> { - type Target = W; - - #[track_caller] - #[inline] - fn deref(&self) -> &Self::Target { &self.0 } -} - impl<'a, W> Deref for WriteRef<'a, W> { type Target = W; #[track_caller] @@ -451,25 +402,6 @@ impl MultiParent for State { } } -impl Query for T -where - T::Value: 'static + Sized, -{ - #[inline] - fn query_inside_first( - &self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { - self.query_outside_first(type_id, callback) - } - - fn query_outside_first( - &self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { - let any: &T::Value = &self.read(); - if type_id == any.type_id() { callback(any) } else { true } - } -} - macro_rules! impl_compose_builder { ($name:ident) => { impl ComposeBuilder for $name diff --git a/core/src/state/map_state.rs b/core/src/state/map_state.rs index 943c12d65..10a5dbe60 100644 --- a/core/src/state/map_state.rs +++ b/core/src/state/map_state.rs @@ -7,7 +7,7 @@ use super::{ }; use crate::{ context::BuildCtx, - render_helper::{RenderProxy, RenderTarget}, + render_helper::RenderProxy, widget::{Render, RenderBuilder, Widget}, }; @@ -163,52 +163,36 @@ where fn origin_writer(&self) -> &Self::OriginWriter { &self.origin } } -impl RenderTarget for MapReader +impl RenderProxy for MapReader where S: StateReader, F: Fn(&S::Value) -> PartData + Clone + 'static, V: Render, { - type Target = V; + type R = V; - fn proxy(&self, f: impl FnOnce(&Self::Target) -> T) -> T { - let v = self.read(); - f(&*v) - } + type Target<'r> = ReadRef<'r, V> + where + Self: 'r; + + #[inline] + fn proxy(&self) -> Self::Target<'_> { self.read() } } -impl RenderTarget for MapWriterAsReader +impl RenderProxy for MapWriterAsReader where S: StateReader, F: Fn(&mut S::Value) -> PartData + Clone + 'static, V: Render, { - type Target = V; + type R = V; - fn proxy(&self, f: impl FnOnce(&Self::Target) -> T) -> T { - let v = self.read(); - f(&*v) - } -} + type Target<'r> = ReadRef<'r, V> -impl RenderBuilder for MapReader -where - S: StateReader, - F: Fn(&S::Value) -> PartData + Clone + 'static, - V: Render, -{ - #[inline] - fn build(self, ctx: &BuildCtx) -> Widget { RenderProxy::new(self).build(ctx) } -} + where + Self: 'r; -impl RenderBuilder for MapWriterAsReader -where - S: StateReader, - F: Fn(&mut S::Value) -> PartData + Clone + 'static, - V: Render, -{ - #[inline] - fn build(self, ctx: &BuildCtx) -> Widget { RenderProxy::new(self).build(ctx) } + fn proxy(&self) -> Self::Target<'_> { self.read() } } impl RenderBuilder for MapWriter diff --git a/core/src/state/splitted_state.rs b/core/src/state/splitted_state.rs index ec841e1fc..d54416d56 100644 --- a/core/src/state/splitted_state.rs +++ b/core/src/state/splitted_state.rs @@ -153,7 +153,7 @@ where orig.modify_scope.remove(ModifyScope::FRAMEWORK); orig.modified = true; let value = - ValueMutRef { value: (self.splitter)(&mut orig.value), borrow: orig.value.borrow.clone() }; + ValueMutRef { inner: (self.splitter)(&mut orig.value), borrow: orig.value.borrow.clone() }; WriteRef { value, modified: false, modify_scope, control: self } } diff --git a/core/src/state/state_cell.rs b/core/src/state/state_cell.rs index 0a5c3f934..ce3caa87a 100644 --- a/core/src/state/state_cell.rs +++ b/core/src/state/state_cell.rs @@ -33,7 +33,7 @@ impl StateCell { } #[track_caller] - pub(crate) fn read(&self) -> ValueRef { + pub(crate) fn read(&self) -> ReadRef { let borrow = &self.borrow_flag; let b = borrow.get().wrapping_add(1); borrow.set(b); @@ -57,8 +57,8 @@ impl StateCell { // SAFETY: `BorrowRef` ensures that there is only immutable access // to the value while borrowed. - let value = PartData::PartRef(unsafe { NonNull::new_unchecked(self.data.get()) }); - ValueRef { value, borrow: BorrowRef { borrow } } + let inner = PartData::PartRef(unsafe { NonNull::new_unchecked(self.data.get()) }); + ReadRef { inner, borrow: BorrowRef { borrow } } } pub(crate) fn write(&self) -> ValueMutRef<'_, W> { @@ -84,8 +84,8 @@ impl StateCell { borrow.set(UNUSED - 1); let v_ref = BorrowRefMut { borrow }; - let value = PartData::PartRef(unsafe { NonNull::new_unchecked(self.data.get()) }); - ValueMutRef { value, borrow: v_ref } + let inner = PartData::PartRef(unsafe { NonNull::new_unchecked(self.data.get()) }); + ValueMutRef { inner, borrow: v_ref } } pub(crate) fn is_unused(&self) -> bool { self.borrow_flag.get() == UNUSED } @@ -114,13 +114,13 @@ impl PartData { /// Caller should ensure that the data is not a copy. pub fn from_data(ptr_data: T) -> Self { PartData::PartData(ptr_data) } } -pub(crate) struct ValueRef<'a, T> { - pub(crate) value: PartData, +pub struct ReadRef<'a, T> { + pub(crate) inner: PartData, pub(crate) borrow: BorrowRef<'a>, } pub(crate) struct ValueMutRef<'a, T> { - pub(crate) value: PartData, + pub(crate) inner: PartData, pub(crate) borrow: BorrowRefMut<'a>, } @@ -201,19 +201,54 @@ impl BorrowRef<'_> { } } -impl<'a, T> Deref for ValueRef<'a, T> { +impl<'a, V> ReadRef<'a, V> { + /// Make a new `ReadRef` by mapping the value of the current `ReadRef`. + pub fn map(mut r: ReadRef<'a, V>, f: impl FnOnce(&V) -> PartData) -> ReadRef<'a, U> { + ReadRef { inner: f(&mut r.inner), borrow: r.borrow } + } + + /// Split the current `ReadRef` into two `ReadRef`s by mapping the value to + /// two parts. + pub fn map_split( + orig: ReadRef<'a, V>, f: impl FnOnce(&V) -> (PartData, PartData), + ) -> (ReadRef<'a, U>, ReadRef<'a, W>) { + let (a, b) = f(&*orig); + let borrow = orig.borrow.clone(); + + (ReadRef { inner: a, borrow: borrow.clone() }, ReadRef { inner: b, borrow }) + } + + pub(crate) fn mut_as_ref_map( + orig: ReadRef<'a, V>, f: impl FnOnce(&mut V) -> PartData, + ) -> ReadRef<'a, U> { + let ReadRef { inner: value, borrow } = orig; + let value = match value { + PartData::PartRef(mut ptr) => unsafe { + // Safety: This method is used to map a state to a part of it. Although a `&mut + // T` is passed to the closure, it is the user's responsibility to + // ensure that the closure does not modify the state. + f(ptr.as_mut()) + }, + PartData::PartData(mut data) => f(&mut data), + }; + + ReadRef { inner: value, borrow } + } +} + +impl<'a, T> Deref for ReadRef<'a, T> { type Target = T; #[inline] - fn deref(&self) -> &Self::Target { &self.value } + fn deref(&self) -> &Self::Target { &self.inner } } impl<'a, T> Deref for ValueMutRef<'a, T> { type Target = T; #[inline] - fn deref(&self) -> &Self::Target { &self.value } + fn deref(&self) -> &Self::Target { &self.inner } } impl<'a, T> DerefMut for ValueMutRef<'a, T> { #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { &mut self.value } + fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } diff --git a/core/src/state/stateful.rs b/core/src/state/stateful.rs index ed45b52fe..9d6c34d13 100644 --- a/core/src/state/stateful.rs +++ b/core/src/state/stateful.rs @@ -4,7 +4,7 @@ use ribir_algo::Sc; use rxrust::{ops::box_it::CloneableBoxOp, prelude::*}; use super::{state_cell::StateCell, WriterControl}; -use crate::{prelude::*, render_helper::RenderProxy}; +use crate::prelude::*; /// Stateful object use to watch the modifies of the inner data. pub struct Stateful { @@ -51,7 +51,7 @@ impl StateReader for Stateful { type Reader = Reader; #[inline] - fn read(&self) -> ReadRef { ReadRef::new(self.data.read()) } + fn read(&self) -> ReadRef { self.data.read() } #[inline] fn clone_reader(&self) -> Self::Reader { Reader(self.data.clone()) } @@ -105,7 +105,7 @@ impl StateReader for Reader { type Reader = Self; #[inline] - fn read(&self) -> ReadRef { ReadRef::new(self.0.read()) } + fn read(&self) -> ReadRef { self.0.read() } #[inline] fn clone_reader(&self) -> Self { Reader(self.0.clone()) } @@ -222,7 +222,7 @@ impl RenderBuilder for Stateful { match self.try_into_value() { Ok(r) => r.build(ctx), Err(s) => { - let w = RenderProxy::new(s.data.clone()).build(ctx); + let w = s.data.clone().build(ctx); w.dirty_subscribe(s.raw_modifies(), ctx) } } diff --git a/core/src/test_helper.rs b/core/src/test_helper.rs index 1d86baef5..9ac57d94c 100644 --- a/core/src/test_helper.rs +++ b/core/src/test_helper.rs @@ -187,7 +187,7 @@ impl TestShellWindow { } } -#[derive(Declare, Query, MultiChild)] +#[derive(Declare, MultiChild)] pub struct MockStack { child_pos: Vec, } @@ -216,10 +216,10 @@ impl Render for MockStack { fn paint(&self, _: &mut PaintingCtx) {} } -#[derive(Declare, Query, MultiChild, Default)] +#[derive(Declare, MultiChild, Default)] pub struct MockMulti; -#[derive(Declare, Query, Clone, SingleChild)] +#[derive(Declare, Clone, SingleChild)] pub struct MockBox { pub size: Size, } diff --git a/core/src/widget.rs b/core/src/widget.rs index b82c5db46..5d9ef102d 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -1,16 +1,16 @@ +use std::convert::Infallible; #[doc(hidden)] pub use std::{ any::{Any, TypeId}, marker::PhantomData, ops::Deref, }; -use std::{cell::RefCell, convert::Infallible}; -use ribir_algo::Sc; use rxrust::ops::box_it::CloneableBoxOp; +use widget_id::RenderQueryable; pub(crate) use crate::widget_tree::*; -use crate::{context::*, prelude::*}; +use crate::{context::*, prelude::*, render_helper::PureRender}; pub trait Compose: Sized { /// Describes the part of the user interface represented by this widget. /// Called by framework, should never directly call it. @@ -24,7 +24,7 @@ pub struct HitTest { /// RenderWidget is a widget which want to paint something or do a layout to /// calc itself size and update children positions. -pub trait Render: Query { +pub trait Render: 'static { /// Do the work of computing the layout for this widget, and return the /// size it need. /// @@ -65,76 +65,6 @@ pub type BoxedWidget = Box FnOnce(&'a BuildCtx<'b>) -> Widget>; /// widget. pub struct GenWidget(Box FnMut(&'a BuildCtx<'b>) -> Widget>); -/// A type can composed by many types, this trait help us to query the type and -/// the inner type by its type id, and call the callback one by one with a `& -/// dyn Any` of the target type. You can control if you want to continue query -/// by return `true` or `false` in the callback. -pub trait Query: Any { - /// Query the type in a inside first order, and apply the callback to it. - /// return what the callback return, hint if the query should continue. - fn query_inside_first(&self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool) - -> bool; - /// Query the type in a outside first order, and apply the callback to it, - /// return what the callback return, hint if the query should continue. - fn query_outside_first( - &self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool; -} - -impl<'a> dyn Render + 'a { - #[inline] - pub fn query_type_inside_first(&self, mut callback: impl FnMut(&T) -> bool) -> bool { - self - .query_inside_first(TypeId::of::(), &mut |a| a.downcast_ref().map_or(true, &mut callback)) - } - - #[inline] - pub fn query_type_outside_first(&self, mut callback: impl FnMut(&T) -> bool) -> bool { - Query::query_outside_first(self, TypeId::of::(), &mut |a| { - a.downcast_ref().map_or(true, &mut callback) - }) - } - - /// Query the most inside type match `T`, and apply the callback to it, return - /// what the callback return. - pub fn query_most_inside(&self, callback: impl FnOnce(&T) -> R) -> Option { - let mut callback = Some(callback); - let mut res = None; - self.query_type_inside_first(|a| { - let cb = callback.take().expect("should only call once"); - res = Some(cb(a)); - false - }); - res - } - - /// Query the most outside type match `T`, and apply the callback to it, - /// return what the callback return. - pub fn query_most_outside(&self, callback: impl FnOnce(&T) -> R) -> Option { - let mut callback = Some(callback); - let mut res = None; - self.query_type_outside_first(|a| { - let cb = callback.take().expect("should only call once"); - res = Some(cb(a)); - false - }); - res - } - - /// return if this object contain type `T` - pub fn contain_type(&self) -> bool { - let mut hit = false; - self.query_type_outside_first(|_: &T| { - hit = true; - false - }); - hit - } - - /// return if this object is type `T` - pub fn is(&self) -> bool { self.type_id() == TypeId::of::() } -} - /// Trait to build a indirect widget into widget tree with `BuildCtx` in the /// build phase. You should not implement this trait directly, framework will /// auto implement this. @@ -220,7 +150,7 @@ impl Widget { pub(crate) fn id(&self) -> WidgetId { self.id } - pub(crate) fn new(w: Box, ctx: &BuildCtx) -> Self { + pub(crate) fn new(w: Box, ctx: &BuildCtx) -> Self { Self::from_id(ctx.alloc_widget(w), ctx) } @@ -258,122 +188,13 @@ impl Widget + 'static> From for GenWidget { fn from(f: F) -> Self { Self::new(f) } } -/// only query the inner object, not query self. -macro_rules! impl_proxy_query { - ($($t:tt)*) => { - #[inline] - fn query_inside_first( - &self, - type_id: TypeId, - callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { - self.$($t)*.query_inside_first(type_id, callback) - } - - #[inline] - fn query_outside_first( - &self, - type_id: TypeId, - callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { - self.$($t)*.query_outside_first(type_id, callback) - } - } -} - -/// query self and proxy to the inner object. -macro_rules! impl_proxy_and_self_query { - ($($t:tt)*) => { - fn query_inside_first( - &self, - type_id: TypeId, - callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { - if !self.$($t)*.query_inside_first(type_id, callback) { - return false - } - - type_id != self.type_id() || callback(self) - } - - fn query_outside_first( - &self, - type_id: TypeId, - callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { - if type_id == self.type_id() && !callback(self) { - return false - } - self.$($t)*.query_outside_first(type_id, callback) - } - } -} - -/// query self only. -macro_rules! impl_query_self_only { - () => { - #[inline] - fn query_inside_first( - &self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { - self.query_outside_first(type_id, callback) - } - - #[inline] - fn query_outside_first( - &self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool, - ) -> bool { - if type_id == self.type_id() { callback(self) } else { true } - } - }; -} -pub(crate) use impl_proxy_and_self_query; -pub(crate) use impl_proxy_query; -pub(crate) use impl_query_self_only; - -macro_rules! impl_proxy_render { - ( - proxy $($mem: tt $(($($args: ident),*))?).*, - $name: ty $(,<$($($lf: lifetime)? $($p: ident)?), *>)? - $(,where $($w:tt)*)? - ) => { - impl $(<$($($lf)? $($p)?),*>)? Render for $name $(where $($w)*)? { - #[inline] - fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size { - self.$($mem $(($($args),*))?).*.perform_layout(clamp, ctx) - } - - #[inline] - fn paint(&self, ctx: &mut PaintingCtx) { - self.$($mem $(($($args),*))?).*.paint(ctx) - } - - #[inline] - fn only_sized_by_parent(&self) -> bool { - self.$($mem $(($($args),*))?).*.only_sized_by_parent() - } - - #[inline] - fn hit_test(&self, ctx: &HitTestCtx, pos: Point) -> HitTest { - self.$($mem $(($($args),*))?).*.hit_test(ctx, pos) - } - - #[inline] - fn get_transform(&self) -> Option { - self.$($mem $(($($args),*))?).*.get_transform() - } - } - }; -} - impl ComposeBuilder for C { #[inline] fn build(self, ctx: &BuildCtx) -> Widget { Compose::compose(State::value(self)).build(ctx) } } impl RenderBuilder for R { - #[inline] - fn build(self, ctx: &BuildCtx) -> Widget { Widget::new(Box::new(self), ctx) } + fn build(self, ctx: &BuildCtx) -> Widget { Widget::new(Box::new(PureRender(self)), ctx) } } impl> + 'static, C> ComposeChildBuilder for W { @@ -383,22 +204,6 @@ impl> + 'static, C> ComposeChildBuilder for W } } -impl Query for Resource { - impl_proxy_and_self_query!(deref()); -} -impl Query for Sc { - impl_proxy_and_self_query!(deref()); -} -impl Query for RefCell { - impl_proxy_and_self_query!(borrow()); -} - -impl Query for StateCell { - impl_proxy_and_self_query!(read()); -} - -impl_proxy_render!(proxy deref(), Resource, , where T: Render + 'static); - pub(crate) fn hit_test_impl(ctx: &HitTestCtx, pos: Point) -> bool { ctx .box_rect() @@ -451,8 +256,6 @@ pub(crate) use multi_build_replace_impl; pub(crate) use multi_build_replace_impl_include_self; pub(crate) use repeat_and_replace; -use self::state_cell::StateCell; - impl Drop for Widget { fn drop(&mut self) { log::warn!("widget allocated but never used: {:?}", self.id); @@ -461,36 +264,3 @@ impl Drop for Widget { .with_ctx(|ctx| ctx.tree.borrow_mut().remove_subtree(self.id)); } } - -#[cfg(test)] -mod tests { - use super::*; - - macro_rules! impl_wrap_test { - ($name:ident) => { - paste::paste! { - #[test] - fn [<$name:lower _support_query>]() { - let warp = $name::new(Void); - let void_tid = Void.type_id(); - let w_tid = warp.type_id(); - let mut hit = 0; - - let mut hit_fn = |_: &dyn Any| { - hit += 1; - true - }; - - warp.query_inside_first(void_tid, &mut hit_fn); - warp.query_outside_first(void_tid, &mut hit_fn); - warp.query_inside_first(w_tid, &mut hit_fn); - warp.query_outside_first(w_tid, &mut hit_fn); - assert_eq!(hit, 4); - } - } - }; - } - impl_wrap_test!(Sc); - impl_wrap_test!(RefCell); - impl_wrap_test!(Resource); -} diff --git a/core/src/widget_tree.rs b/core/src/widget_tree.rs index 8f3de3887..0857f9283 100644 --- a/core/src/widget_tree.rs +++ b/core/src/widget_tree.rs @@ -7,13 +7,14 @@ use std::{ }; pub mod widget_id; +use widget_id::RenderQueryable; pub(crate) use widget_id::TreeArena; pub use widget_id::WidgetId; mod layout_info; pub use layout_info::*; use self::widget::widget_id::new_node; -use crate::{overlay::OverlayRoot, prelude::*}; +use crate::{overlay::OverlayRoot, prelude::*, render_helper::PureRender}; pub(crate) type DirtySet = Rc>>; @@ -186,9 +187,9 @@ impl WidgetTree { pub(crate) fn get_many_mut( &mut self, ids: &[WidgetId; N], - ) -> [&mut Box; N] { + ) -> [&mut Box; N] { unsafe { - let mut outs: MaybeUninit<[&mut Box; N]> = MaybeUninit::uninit(); + let mut outs: MaybeUninit<[&mut Box; N]> = MaybeUninit::uninit(); let outs_ptr = outs.as_mut_ptr(); for (idx, wid) in ids.iter().enumerate() { let arena = &mut *(&mut self.arena as *mut TreeArena); @@ -208,7 +209,7 @@ impl Default for WidgetTree { let mut arena = TreeArena::new(); Self { - root: new_node(&mut arena, Box::new(OverlayRoot {})), + root: new_node(&mut arena, Box::new(PureRender(OverlayRoot {}))), wnd: Weak::new(), arena, store: LayoutStore::default(), @@ -231,7 +232,7 @@ mod tests { pub(crate) fn content_root(&self) -> WidgetId { self.root.first_child(&self.arena).unwrap() } } - fn empty_node(arena: &mut TreeArena) -> WidgetId { new_node(arena, Box::new(Void)) } + fn empty_node(arena: &mut TreeArena) -> WidgetId { new_node(arena, Box::new(PureRender(Void))) } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] #[test] diff --git a/core/src/widget_tree/layout_info.rs b/core/src/widget_tree/layout_info.rs index b5126e634..27bed9a60 100644 --- a/core/src/widget_tree/layout_info.rs +++ b/core/src/widget_tree/layout_info.rs @@ -307,7 +307,7 @@ mod tests { use super::*; use crate::{prelude::*, reset_test_env, test_helper::*}; - #[derive(Declare, Clone, Query, SingleChild)] + #[derive(Declare, Clone, SingleChild)] struct OffsetBox { pub offset: Point, pub size: Size, @@ -468,7 +468,7 @@ mod tests { fn layout_visit_prev_position() { reset_test_env!(); - #[derive(Declare, Query)] + #[derive(Declare)] struct MockWidget { pos: Cell, size: Size, diff --git a/core/src/widget_tree/widget_id.rs b/core/src/widget_tree/widget_id.rs index d8a84d0ed..582e4e028 100644 --- a/core/src/widget_tree/widget_id.rs +++ b/core/src/widget_tree/widget_id.rs @@ -1,13 +1,12 @@ -use std::any::Any; +use std::any::{Any, TypeId}; use indextree::{Arena, Node, NodeId}; -use super::WidgetTree; +use super::{Query, QueryHandle, QueryRef, WidgetTree, WriteRef}; use crate::{ context::{PaintingCtx, WidgetCtx}, - prelude::{AnonymousWrapper, DataWidget}, - render_helper::RenderProxy, - widget::{Query, Render}, + data_widget::{AnonymousAttacher, DataAttacher}, + widget::Render, window::DelayEvent, }; @@ -15,15 +14,19 @@ use crate::{ pub struct WidgetId(pub(crate) NodeId); -pub(crate) type TreeArena = Arena>; +pub trait RenderQueryable: Render + Query {} + +impl RenderQueryable for T {} + +pub(crate) type TreeArena = Arena>; impl WidgetId { /// Returns a reference to the node data. - pub(crate) fn get<'a, 'b>(self, tree: &'a TreeArena) -> Option<&'a (dyn Render + 'b)> { + pub(crate) fn get<'a, 'b>(self, tree: &'a TreeArena) -> Option<&'a (dyn RenderQueryable + 'b)> { tree.get(self.0).map(|n| &**n.get()) } - pub(crate) fn get_node_mut(self, tree: &mut TreeArena) -> Option<&mut Box> { + pub(crate) fn get_node_mut(self, tree: &mut TreeArena) -> Option<&mut Box> { tree.get_mut(self.0).map(|n| n.get_mut()) } @@ -133,12 +136,12 @@ impl WidgetId { } fn node_feature( - self, tree: &TreeArena, method: impl FnOnce(&Node>) -> Option, + self, tree: &TreeArena, method: impl FnOnce(&Node>) -> Option, ) -> Option { tree.get(self.0).and_then(method).map(WidgetId) } - pub(crate) fn assert_get<'a, 'b>(self, tree: &'a TreeArena) -> &'a (dyn Render + 'b) { + pub(crate) fn assert_get<'a, 'b>(self, tree: &'a TreeArena) -> &'a (dyn RenderQueryable + 'b) { self .get(tree) .expect("Widget not exists in the `tree`") @@ -147,7 +150,8 @@ impl WidgetId { /// We assume the `f` wrap the widget into a new widget, and keep the old /// widget as part of the new widget, otherwise, undefined behavior. pub(crate) fn wrap_node( - self, tree: &mut TreeArena, f: impl FnOnce(Box) -> Box, + self, tree: &mut TreeArena, + f: impl FnOnce(Box) -> Box, ) { let node = self.get_node_mut(tree).unwrap(); unsafe { @@ -158,14 +162,11 @@ impl WidgetId { } pub(crate) fn attach_data(self, data: impl Query, tree: &mut TreeArena) { - self.wrap_node(tree, |node| DataWidget::attach(node, data)); + self.wrap_node(tree, |node| Box::new(DataAttacher::new(node, data))); } - pub fn attach_anonymous_data(self, data: impl Any, tree: &mut TreeArena) { - self.wrap_node(tree, |render| { - let r = RenderProxy::new(AnonymousWrapper::new(render, Box::new(data))); - Box::new(r) - }); + pub(crate) fn attach_anonymous_data(self, data: impl Any, tree: &mut TreeArena) { + self.wrap_node(tree, |render| Box::new(AnonymousAttacher::new(render, Box::new(data)))); } pub(crate) fn paint_subtree(self, ctx: &mut PaintingCtx) { @@ -213,6 +214,41 @@ impl WidgetId { } } -pub(crate) fn new_node(arena: &mut TreeArena, node: Box) -> WidgetId { +pub(crate) fn new_node(arena: &mut TreeArena, node: Box) -> WidgetId { WidgetId(arena.new_node(node)) } + +impl<'a> dyn RenderQueryable + 'a { + /// Return a iterator of all reference of type `T` in this node. + pub fn query_all_iter(&self) -> impl DoubleEndedIterator> { + self + .query_all(TypeId::of::()) + .into_iter() + .filter_map(QueryHandle::into_ref) + } + + /// Return a iterator of all mutable reference of type `T` in this node. + pub fn query_all_iter_write(&self) -> impl DoubleEndedIterator> { + self + .query_all(TypeId::of::()) + .into_iter() + .filter_map(QueryHandle::into_mut) + } + + /// Query the outermost of reference of type `T` in this node. + pub fn query_write(&self) -> Option> { + self + .query(TypeId::of::()) + .and_then(QueryHandle::into_mut) + } + + /// Query the outermost of reference of type `T` in this node. + pub fn query_ref(&self) -> Option> { + self + .query(TypeId::of::()) + .and_then(QueryHandle::into_ref) + } + + /// return if this object contain type `T` + pub fn contain_type(&self) -> bool { self.query(TypeId::of::()).is_some() } +} diff --git a/core/src/window.rs b/core/src/window.rs index a876ff0a4..0c5252d46 100644 --- a/core/src/window.rs +++ b/core/src/window.rs @@ -340,8 +340,8 @@ impl Window { let tree = self.widget_tree.borrow(); let drop_conditional = wid .assert_get(&self.widget_tree.borrow().arena) - .query_most_outside(|d: &KeepAlive| !d.keep_alive) - .unwrap_or(true); + .query_ref::() + .map_or(true, |d| !d.keep_alive); let parent_dropped = parent.map_or(false, |p| { p.is_dropped(&tree.arena) || p.ancestors(&tree.arena).last() != Some(tree.root()) }); @@ -547,12 +547,12 @@ impl Window { // no way to mut access the inner data of node or destroy the node. let tree = unsafe { &*(&*self.widget_tree.borrow() as *const WidgetTree) }; id.assert_get(&tree.arena) - .query_type_inside_first(|m: &MixBuiltin| { + .query_all_iter::() + .for_each(|m| { if m.contain_flag(e.flags()) { m.dispatch(e); } - true - }); + }) } fn top_down_emit(&self, e: &mut Event, bottom: WidgetId, up: Option) { @@ -564,7 +564,9 @@ impl Window { path.iter().rev().all(|id| { id.assert_get(&tree.arena) - .query_type_outside_first(|m: &MixBuiltin| { + .query_all_iter::() + .rev() + .all(|m| { if m.contain_flag(e.flags()) { e.set_current_target(*id); m.dispatch(e); @@ -585,7 +587,8 @@ impl Window { .take_while(|id| Some(*id) != up) .all(|id| { id.assert_get(&tree.arena) - .query_type_inside_first(|m: &MixBuiltin| { + .query_all_iter::() + .all(|m| { if m.contain_flag(e.flags()) { e.set_current_target(id); m.dispatch(e); diff --git a/examples/wordle_game/src/wordle.rs b/examples/wordle_game/src/wordle.rs index 3d4e97fab..d964a56ec 100644 --- a/examples/wordle_game/src/wordle.rs +++ b/examples/wordle_game/src/wordle.rs @@ -5,7 +5,6 @@ use std::{ }; use rand::prelude::*; -use ribir::prelude::*; #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum CharHint { @@ -30,7 +29,7 @@ impl GameStatus { } } -#[derive(Debug, Query, Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub struct WordleChar { pub char: char, pub hint: Option, @@ -93,7 +92,6 @@ impl WordleGuessing { } } -#[derive(Query)] pub struct Wordle { word: String, max_rounds: usize, diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 44c55a069..5bf6aa080 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -32,7 +32,7 @@ macro_rules! ok { pub(crate) use ok; #[proc_macro_derive(SingleChild)] -pub fn single_marco_derive(input: TokenStream) -> TokenStream { +pub fn single_child_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let name = input.ident; @@ -42,41 +42,8 @@ pub fn single_marco_derive(input: TokenStream) -> TokenStream { .into() } -#[proc_macro_derive(Query)] -pub fn query_derive(input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as DeriveInput); - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let name = input.ident; - quote! { - impl #impl_generics Query for #name #ty_generics #where_clause { - #[inline] - fn query_inside_first( - &self, - type_id: TypeId, - callback: &mut dyn FnMut(&dyn Any) -> bool - )-> bool { - self.query_outside_first(type_id, callback) - } - - #[inline] - fn query_outside_first( - &self, - type_id: TypeId, - callback: &mut dyn FnMut(&dyn Any) -> bool - ) -> bool{ - if type_id == self.type_id() { - callback(self) - } else { - true - } - } - } - } - .into() -} - #[proc_macro_derive(MultiChild)] -pub fn multi_macro_derive(input: TokenStream) -> TokenStream { +pub fn multi_child_derive(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let name = input.ident; diff --git a/widgets/src/divider.rs b/widgets/src/divider.rs index a866b3c3c..aee24e5e5 100644 --- a/widgets/src/divider.rs +++ b/widgets/src/divider.rs @@ -37,7 +37,7 @@ use crate::prelude::*; /// } /// }; /// ``` -#[derive(Default, Query, Declare)] +#[derive(Default, Declare)] pub struct Divider { #[declare(default = 1.)] // Extent of divider diff --git a/widgets/src/grid_view.rs b/widgets/src/grid_view.rs index df0ea042a..786cb63dc 100644 --- a/widgets/src/grid_view.rs +++ b/widgets/src/grid_view.rs @@ -2,7 +2,7 @@ use ribir_core::prelude::*; use crate::layout::Direction; -#[derive(MultiChild, Query)] +#[derive(MultiChild)] pub struct GridView { axis_dir: Direction, cross_axis_cnt: u32, diff --git a/widgets/src/input.rs b/widgets/src/input.rs index 2f8c7d404..e34d7d4f9 100644 --- a/widgets/src/input.rs +++ b/widgets/src/input.rs @@ -359,7 +359,7 @@ where let placeholder = @ { placeholder.map(move |holder| @Text { visible: pipe!(SelectableText::text(&*$this).is_empty()), - text: pipe!((*$holder).0.clone()), + text: pipe!($holder.0.clone()), }) }; diff --git a/widgets/src/layout/constrained_box.rs b/widgets/src/layout/constrained_box.rs index 5c5082ed7..e0f5cd8e1 100644 --- a/widgets/src/layout/constrained_box.rs +++ b/widgets/src/layout/constrained_box.rs @@ -1,7 +1,7 @@ use ribir_core::prelude::*; /// a widget that imposes additional constraints clamp on its child. -#[derive(SingleChild, Query, Declare, Clone)] +#[derive(SingleChild, Declare, Clone)] pub struct ConstrainedBox { pub clamp: BoxClamp, } diff --git a/widgets/src/layout/expanded.rs b/widgets/src/layout/expanded.rs index 68a25cbfc..523ffe427 100644 --- a/widgets/src/layout/expanded.rs +++ b/widgets/src/layout/expanded.rs @@ -5,7 +5,7 @@ use super::ConstrainedBox; /// A widget that expanded a child of `Flex`, so that the child fills the /// available space. If multiple children are expanded, the available space is /// divided among them according to the flex factor. -#[derive(Clone, PartialEq, Query, Declare)] +#[derive(Clone, PartialEq, Declare)] pub struct Expanded { #[declare(default = 1.)] pub flex: f32, @@ -23,7 +23,7 @@ impl ComposeChild for Expanded { }, @{ child } } - .attach_state_data(this, ctx!()) + .try_unwrap_state_and_attach(this, ctx!()) } } } diff --git a/widgets/src/layout/flex.rs b/widgets/src/layout/flex.rs index 5afd3d3ed..675f507b6 100644 --- a/widgets/src/layout/flex.rs +++ b/widgets/src/layout/flex.rs @@ -29,7 +29,7 @@ pub enum JustifyContent { SpaceEvenly, } -#[derive(Default, MultiChild, Declare, Query, Clone, PartialEq)] +#[derive(Default, MultiChild, Declare, Clone, PartialEq)] pub struct Flex { /// Reverse the main axis. #[declare(default)] diff --git a/widgets/src/layout/only_sized_by_parent.rs b/widgets/src/layout/only_sized_by_parent.rs index fe04bad32..979347674 100644 --- a/widgets/src/layout/only_sized_by_parent.rs +++ b/widgets/src/layout/only_sized_by_parent.rs @@ -3,7 +3,7 @@ use ribir_core::prelude::*; /// OnlySizedByParent implies that the parent is the only input into determining /// the widget's size, so layout changes to the subtree do not trigger a parent /// relayout. -#[derive(SingleChild, Query, Declare)] +#[derive(SingleChild, Declare)] pub struct OnlySizedByParent {} impl Render for OnlySizedByParent { diff --git a/widgets/src/layout/sized_box.rs b/widgets/src/layout/sized_box.rs index 67aa628ac..8cb899066 100644 --- a/widgets/src/layout/sized_box.rs +++ b/widgets/src/layout/sized_box.rs @@ -4,7 +4,7 @@ use ribir_core::prelude::*; /// /// This widget forces its child to have a specific width and/or height /// (assuming values are permitted by the parent of this widget). -#[derive(SingleChild, Query, Declare, Clone)] +#[derive(SingleChild, Declare, Clone)] pub struct SizedBox { /// The specified size of the box. pub size: Size, diff --git a/widgets/src/layout/stack.rs b/widgets/src/layout/stack.rs index 27f7fde25..86bd1e67f 100644 --- a/widgets/src/layout/stack.rs +++ b/widgets/src/layout/stack.rs @@ -1,7 +1,7 @@ use ribir_core::prelude::*; /// A widget that overlap children align with left top. -#[derive(MultiChild, Query, Declare)] +#[derive(MultiChild, Declare)] pub struct Stack { #[declare(default)] fit: StackFit, diff --git a/widgets/src/path.rs b/widgets/src/path.rs index 3ae6845f8..16c61064b 100644 --- a/widgets/src/path.rs +++ b/widgets/src/path.rs @@ -2,7 +2,7 @@ use ribir_core::prelude::*; /// Widget just use as a paint kit for a path and not care about its size. Use /// `[PathWidget]!` instead of. -#[derive(Declare, Query, Clone)] +#[derive(Declare, Clone)] pub struct PathPaintKit { pub path: Path, pub brush: Brush, @@ -39,7 +39,7 @@ impl Render for PathPaintKit { } } -#[derive(Declare, Query)] +#[derive(Declare)] /// A path widget which size careful and can process events only if user hit at /// the path self, not its size cover area. pub struct PathWidget { diff --git a/widgets/src/text.rs b/widgets/src/text.rs index 3a526feaf..fff2fa1ed 100644 --- a/widgets/src/text.rs +++ b/widgets/src/text.rs @@ -1,7 +1,7 @@ use ribir_core::prelude::*; /// The text widget display text with a single style. -#[derive(Debug, Declare, Query, Clone, PartialEq)] +#[derive(Debug, Declare, Clone, PartialEq)] pub struct Text { pub text: CowArc, #[declare(default = Palette::of(ctx!()).on_surface_variant())] diff --git a/widgets/src/transform_box.rs b/widgets/src/transform_box.rs index 0dfcb993c..83e71fe12 100644 --- a/widgets/src/transform_box.rs +++ b/widgets/src/transform_box.rs @@ -1,6 +1,6 @@ use ribir_core::prelude::*; -#[derive(SingleChild, Query, Declare, Clone)] +#[derive(SingleChild, Declare, Clone)] pub struct TransformBox { pub matrix: Transform, }