diff --git a/CHANGELOG.md b/CHANGELOG.md index d1fa6aff2..e1578169f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,10 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he ## [@Unreleased] - @ReleaseDate +### Features + +- **core**: ensure object safety of `StateReader`, `StateWatcher` and `StateWriter` (#692 @M-Adoo) + ## [0.4.0-alpha.23] - 2025-01-15 ### Features diff --git a/core/src/animation/animate_state.rs b/core/src/animation/animate_state.rs index e3d675745..491744c9c 100644 --- a/core/src/animation/animate_state.rs +++ b/core/src/animation/animate_state.rs @@ -34,7 +34,7 @@ pub trait AnimateState: AnimateStateSetter { .state(self) .finish(); - let c_animate = animate.clone_writer(); + let c_animate = animate.as_stateful().clone_writer(); let init_value = observable::of(state.get()); state .animate_state_modifies() @@ -66,7 +66,7 @@ where S: StateWriter, S::Value: Clone, { - type C = S::Writer; + type C = S; type Value = S::Value; #[inline] diff --git a/core/src/builtin_widgets/mix_builtin.rs b/core/src/builtin_widgets/mix_builtin.rs index a8b3af71f..affa0e16b 100644 --- a/core/src/builtin_widgets/mix_builtin.rs +++ b/core/src/builtin_widgets/mix_builtin.rs @@ -487,7 +487,7 @@ impl Default for MixBuiltin { impl Clone for MixBuiltin { fn clone(&self) -> Self { - let flags = State::stateful(self.flags.clone_writer()); + let flags = self.flags.clone_writer(); Self { flags, subject: self.subject.clone() } } } diff --git a/core/src/builtin_widgets/providers.rs b/core/src/builtin_widgets/providers.rs index fbad0458b..e5fd73833 100644 --- a/core/src/builtin_widgets/providers.rs +++ b/core/src/builtin_widgets/providers.rs @@ -52,9 +52,19 @@ //! }; //! ``` //! -//! State can be provided as either its value or itself. When providing a writer -//! as its value, you can access its write reference using -//! [`Provider::write_of`] to modify the state. +//! ## Providing State +//! +//! State can be provided in unique manner, allowing both its value and the +//! self reference to be accessed through a single provider. You can choose to +//! provide a writer, watcher, or reader using [`Provider::value_of_writer`], +//! [`Provider::value_of_watcher`], and [`Provider::value_of_reader`] +//! respectively. +//! +//! The value can be accessed using [`Provider::of`], and the state itself can +//! be accessed using [`Provider::state_of`]. +//! +//! If a writer is provided, the write reference can be accessed using +//! [`Provider::write_of`] to make writer reference to the state. //! //! ``` //! use ribir::prelude::*; @@ -64,9 +74,7 @@ //! //! providers! { //! providers: smallvec![ -//! // Providing a `Stateful` -//! Provider::new(state.clone_writer()), -//! // Providing an `i32` +//! // Providing an writer of `i32` //! Provider::value_of_writer(state, None), //! ], //! @ { @@ -74,9 +82,9 @@ //! // Accessing the state value //! let value = *Provider::of::(ctx).unwrap(); //! assert_eq!(value, 1); -//! // Accessing the state itself //! { -//! let _state = Provider::of::>(ctx).unwrap(); +//! // Accessing the state itself +//! let _state = Provider::state_of::>(ctx).unwrap(); //! } //! // Accessing the write reference of the state to modify the value //! let mut value = Provider::write_of::(ctx).unwrap(); @@ -86,6 +94,32 @@ //! }; //! ``` //! +//! If the type of the state is unknown, we can provide it using a boxed state +//! trait. +//! +//! ``` +//! use ribir::prelude::*; +//! use smallvec::smallvec; +//! +//! fn provide_box_state(writer: impl StateWriter) { +//! let writer: Box> = Box::new(writer); +//! let _ = providers! { +//! providers: [Provider::value_of_writer(writer, None)], +//! @ { +//! let ctx = BuildCtx::get(); +//! // Accessing the state value +//! let value = *Provider::of::(ctx).unwrap(); +//! assert_eq!(value, 1); +//! // Accessing the state itself +//! { +//! let _state = Provider::state_of::>>(ctx).unwrap(); +//! } +//! @Text { text: "Both state and its value provided!" } +//! } +//! }; +//! } +//! ``` +//! //! ## Scope of Providers in the Build Process //! //! Providers are visible to their descendants. However, in the build process, @@ -112,7 +146,6 @@ //! ``` //! //! The correct approach is as follows: -//! //! ``` //! use ribir::prelude::*; //! @@ -229,22 +262,42 @@ impl Provider { /// assert_eq!(*Provider::of::(ctx).unwrap(), 1); /// /// // Obtain the reader - /// let reader = Provider::state_of:: - /// < as StateReader>::Reader>(ctx); + /// let reader = Provider::state_of::>(ctx); /// assert_eq!(*reader.unwrap().read(), 1); /// Void /// } /// }; /// ``` - pub fn value_of_reader(value: R) -> Provider - where - R: StateReader, - R::Value: Sized + 'static, - { - match value.try_into_value() { - Ok(v) => Provider::Setup(Box::new(Setup::new(v))), - Err(v) => Provider::Setup(Box::new(Setup::from_state(v.clone_reader()))), - } + pub fn value_of_reader(value: impl StateReader) -> Provider { + Provider::Setup(Box::new(Setup::from_state(value.clone_reader()))) + } + + /// Establish a provider for the value of a watcher. It will clone the watcher + /// to create the provider to prevent any writer leaks. + /// + /// Obtain the value using [`Provider::of`], and if you want to access the + /// watcher, using [`Provider::state_of`]. + /// + /// ## Example + /// + /// ``` + /// use ribir_core::prelude::*; + /// + /// let w = providers! { + /// providers: [Provider::value_of_watcher(Stateful::new(1i32))], + /// @ { + /// let ctx = BuildCtx::get(); + /// // Obtain the value + /// assert_eq!(*Provider::of::(ctx).unwrap(), 1); + /// // Obtain the watcher + /// let watcher = Provider::state_of::>>(ctx); + /// assert_eq!(*watcher.unwrap().read(), 1); + /// Void + /// } + /// }; + /// ``` + pub fn value_of_watcher(value: impl StateWatcher) -> Provider { + Provider::Setup(Box::new(Setup::from_state(value.clone_watcher()))) } /// Establish a provider for the value of a writer. @@ -289,21 +342,16 @@ impl Provider { pub fn value_of_writer( value: impl StateWriter + Query, dirty: Option, ) -> Provider { - match value.try_into_value() { - Ok(v) => Provider::Setup(Box::new(Setup::new(v))), - Err(this) => { - if let Some(dirty) = dirty { - let writer = WriterSetup { - modifies: this.raw_modifies(), - info: Provider::info::(), - value: Box::new(this), - dirty, - }; - Provider::Setup(Box::new(writer)) - } else { - Provider::Setup(Box::new(Setup::from_state(this))) - } - } + if let Some(dirty) = dirty { + let writer = WriterSetup { + modifies: value.raw_modifies(), + info: Provider::info::(), + value: Box::new(value), + dirty, + }; + Provider::Setup(Box::new(writer)) + } else { + Provider::Setup(Box::new(Setup::from_state(value))) } } @@ -514,7 +562,7 @@ impl ProviderCtx { self .data .get(&info) - .and_then(|q| q.query_write(&QueryId::of::())) + .and_then(|q| q.query(&QueryId::of::())) .and_then(QueryHandle::into_ref) } @@ -839,7 +887,7 @@ mod tests { let (value, w_value) = split_value(0); let w = providers! { - providers: smallvec![Provider::new(1i32)], + providers: [Provider::new(1i32)], @MockBox { size: Size::new(1.,1.), @ { @@ -864,7 +912,7 @@ mod tests { let (value2, w_value2) = split_value(0); let w = mock_multi! { @Providers { - providers: smallvec![Provider::new(1i32)], + providers: [Provider::new(1i32)], @ { let v = Provider::of::(BuildCtx::get()).unwrap(); *$w_value1.write() = *v; @@ -896,7 +944,7 @@ mod tests { let (trigger, w_trigger) = split_value(true); let w = providers! { - providers: smallvec![Provider::new(w_value.clone_writer())], + providers: [Provider::new(w_value.clone_writer())], @ { // We do not allow the use of the build context in the pipe at the moment. let value = Provider::of::>(BuildCtx::get()) @@ -970,4 +1018,82 @@ mod tests { assert_eq!(cnt.read().layout_cnt.get(), 2); assert_eq!(cnt.read().paint_cnt.get(), 3); } + + #[test] + fn boxed_reader() { + reset_test_env!(); + + let w = fn_widget! { + let boxed_reader: Box> = Box::new(Stateful::new(1i32)); + + @Providers { + providers: [Provider::value_of_reader(boxed_reader)], + @ { + let v = Provider::of::(BuildCtx::get()).unwrap(); + assert_eq!(*v, 1); + + let v = Provider::state_of::>>(BuildCtx::get()).unwrap(); + assert_eq!(*v.read(), 1); + Void + } + } + }; + + let mut wnd = TestWindow::new(w); + wnd.draw_frame(); + } + + #[test] + fn boxed_watcher() { + reset_test_env!(); + + let w = fn_widget! { + let boxed_watcher: Box> = Box::new(Stateful::new(1i32)); + + @Providers { + providers: [Provider::value_of_watcher(boxed_watcher)], + @ { + let v = Provider::of::(BuildCtx::get()).unwrap(); + assert_eq!(*v, 1); + + let v = Provider::state_of::>>( + BuildCtx::get() + ) + .unwrap(); + assert_eq!(*v.read(), 1); + Void + } + } + }; + + let mut wnd = TestWindow::new(w); + wnd.draw_frame(); + } + + #[test] + fn boxed_writer() { + reset_test_env!(); + + let w = fn_widget! { + let boxed_writer: Box> = Box::new(Stateful::new(1i32)); + + @Providers { + providers: [Provider::value_of_writer(boxed_writer, None)], + @ { + let v = Provider::of::(BuildCtx::get()).unwrap(); + assert_eq!(*v, 1); + + let v = Provider::state_of::>>( + BuildCtx::get() + ) + .unwrap(); + assert_eq!(*v.read(), 1); + Void + } + } + }; + + let mut wnd = TestWindow::new(w); + wnd.draw_frame(); + } } diff --git a/core/src/query.rs b/core/src/query.rs index f6483a895..8e7e0d353 100644 --- a/core/src/query.rs +++ b/core/src/query.rs @@ -2,9 +2,7 @@ use std::any::{Any, TypeId}; use smallvec::SmallVec; -use crate::state::{ - MapReader, MapWriterAsReader, ReadRef, Reader, StateReader, StateWriter, WriteRef, -}; +use crate::state::*; /// A type can composed by many types, this trait help us to query the type and /// the inner type by type id. @@ -282,23 +280,26 @@ macro_rules! impl_query_for_reader { }; } -impl Query for MapReader +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 Box> { + impl_query_for_reader!(); +} + +impl> Query for Watcher { impl_query_for_reader!(); } -impl Query for Reader { +impl Query for Box> { impl_query_for_reader!(); } diff --git a/core/src/state.rs b/core/src/state.rs index 8e4c4a37f..7929ee3c7 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -22,15 +22,20 @@ use crate::{prelude::*, render_helper::RenderProxy}; pub trait StateReader: 'static { /// The value type of this state. type Value: ?Sized; - /// The origin state type that this state map or split from . Otherwise - /// return itself. - type OriginReader: StateReader; - type Reader: StateReader; + type Reader: StateReader + where + Self: Sized; /// Return a reference of this state. fn read(&self) -> ReadRef; - /// get a clone of this state that only can read. - fn clone_reader(&self) -> Self::Reader; + + /// Return a boxed reader of this state. + fn clone_boxed_reader(&self) -> Box>; + + /// Return a cloned reader of this state. + fn clone_reader(&self) -> Self::Reader + where + Self: Sized; /// Maps an reader to another by applying a function to a contained /// value. The return reader is just a shortcut to access part of the origin /// reader. @@ -43,22 +48,27 @@ pub trait StateReader: 'static { fn map_reader(&self, map: F) -> MapReader where F: Fn(&Self::Value) -> PartRef + Clone, + Self: Sized, { MapReader { origin: self.clone_reader(), part_map: map } } - /// Return the origin reader that this state map or split from . Otherwise - /// return itself. - fn origin_reader(&self) -> &Self::OriginReader; /// try convert this state into the value, if there is no other share this /// state, otherwise return an error with self. fn try_into_value(self) -> Result where Self: Sized, - Self::Value: Sized; + Self::Value: Sized, + { + Err(self) + } } pub trait StateWatcher: StateReader { + type Watcher: StateWatcher + where + Self: Sized; + /// Return a modifies `Rx` stream of the state, user can subscribe it to /// response the state changes. fn modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { @@ -72,16 +82,17 @@ pub trait StateWatcher: StateReader { /// `modifies` instead if you only want to response the data changes. fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, Infallible>; - /// Clone a reader that can be used to observe the modifies of the state. - fn clone_watcher(&self) -> Watcher { - Watcher::new(self.clone_reader(), self.raw_modifies()) - } + /// Clone a boxed watcher that can be used to observe the modifies of the + /// state. + fn clone_boxed_watcher(&self) -> Box>; + + /// Clone a watcher that can be used to observe the modifies of the state. + fn clone_watcher(&self) -> Self::Watcher + where + Self: Sized; } pub trait StateWriter: StateWatcher { - type Writer: StateWriter; - type OriginWriter: StateWriter; - /// Convert the writer to a reader if no other writers exist. fn into_reader(self) -> Result where @@ -98,10 +109,14 @@ pub trait StateWriter: StateWatcher { /// state and then modifies it back to trigger the view update. Use it only /// if you know how a shallow reference works. fn shallow(&self) -> WriteRef; - /// Clone this state writer. - fn clone_writer(&self) -> Self::Writer; - /// Return the origin writer that this state map or split from. - fn origin_writer(&self) -> &Self::OriginWriter; + + /// Clone a boxed writer of this state. + fn clone_boxed_writer(&self) -> Box>; + + /// Clone a writer of this state. + fn clone_writer(&self) -> Self + where + Self: Sized; /// Return a new writer that be part of the origin writer by applying a /// function to the contained value. /// @@ -121,9 +136,10 @@ pub trait StateWriter: StateWatcher { /// modified within this function. Therefore, if the original data is /// modified, no downstream will be notified. #[inline] - fn split_writer(&self, mut_map: W) -> SplittedWriter + fn split_writer(&self, mut_map: W) -> SplittedWriter where W: Fn(&mut Self::Value) -> PartMut + Clone, + Self: Sized, { SplittedWriter::new(self.clone_writer(), mut_map) } @@ -140,9 +156,10 @@ pub trait StateWriter: StateWatcher { /// modified within this function. Therefore, if the original data is /// modified, no downstream will be notified. #[inline] - fn map_writer(&self, part_map: M) -> MapWriter + fn map_writer(&self, part_map: M) -> MapWriter where M: Fn(&mut Self::Value) -> PartMut + Clone, + Self: Sized, { let origin = self.clone_writer(); MapWriter { origin, part_map } @@ -166,7 +183,6 @@ pub(crate) enum InnerState { impl StateReader for State { type Value = T; - type OriginReader = Self; type Reader = Reader; fn read(&self) -> ReadRef { @@ -177,10 +193,12 @@ impl StateReader for State { } #[inline] - fn clone_reader(&self) -> Self::Reader { self.as_stateful().clone_reader() } + fn clone_boxed_reader(&self) -> Box> { + Box::new(self.clone_reader()) + } #[inline] - fn origin_reader(&self) -> &Self::OriginReader { self } + fn clone_reader(&self) -> Self::Reader { self.as_stateful().clone_reader() } fn try_into_value(self) -> Result { match self.0.into_inner() { @@ -191,16 +209,24 @@ impl StateReader for State { } impl StateWatcher for State { + type Watcher = Watcher; + #[inline] + fn clone_boxed_watcher(&self) -> Box> { + Box::new(self.clone_watcher()) + } + #[inline] fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, Infallible> { self.as_stateful().raw_modifies() } + + #[inline] + fn clone_watcher(&self) -> Watcher { + Watcher::new(self.clone_reader(), self.raw_modifies()) + } } impl StateWriter for State { - type Writer = Stateful; - type OriginWriter = Self; - fn into_reader(self) -> Result where Self: Sized, @@ -221,10 +247,12 @@ impl StateWriter for State { fn shallow(&self) -> WriteRef { self.as_stateful().shallow() } #[inline] - fn clone_writer(&self) -> Self::Writer { self.as_stateful().clone_writer() } + fn clone_boxed_writer(&self) -> Box> { + Box::new(self.clone_writer()) + } #[inline] - fn origin_writer(&self) -> &Self::OriginWriter { self } + fn clone_writer(&self) -> Self { State::stateful(self.as_stateful().clone_writer()) } } impl State { @@ -234,6 +262,12 @@ impl State { pub fn value(value: W) -> Self { State(UnsafeCell::new(InnerState::Data(StateCell::new(value)))) } + pub fn into_stateful(self) -> Stateful + where + W: 'static, + { + self.as_stateful().clone_writer() + } pub fn as_stateful(&self) -> &Stateful { match self.inner_ref() { InnerState::Data(w) => { @@ -427,6 +461,104 @@ impl IntoWidgetStrict<'static, COMPOSE> for State { fn into_widget_strict(self) -> Widget<'static> { Compose::compose(self) } } +impl StateReader for Box> { + type Value = V; + type Reader = Self; + + #[inline] + fn read(&self) -> ReadRef<'_, V> { (**self).read() } + + #[inline] + fn clone_boxed_reader(&self) -> Box> { + (**self).clone_boxed_reader() + } + + fn clone_reader(&self) -> Self::Reader { self.clone_boxed_reader() } +} + +impl StateReader for Box> { + type Value = V; + type Reader = Box>; + + #[inline] + fn read(&self) -> ReadRef<'_, V> { (**self).read() } + + #[inline] + fn clone_boxed_reader(&self) -> Box> { + (**self).clone_boxed_reader() + } + + #[inline] + fn clone_reader(&self) -> Self::Reader { self.clone_boxed_reader() } +} + +impl StateWatcher for Box> { + type Watcher = Box>; + + #[inline] + fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, Infallible> { + (**self).raw_modifies() + } + + #[inline] + fn clone_boxed_watcher(&self) -> Box> { + (**self).clone_boxed_watcher() + } + + #[inline] + fn clone_watcher(&self) -> Self::Watcher { self.clone_boxed_watcher() } +} + +impl StateReader for Box> { + type Value = V; + type Reader = Box>; + + #[inline] + fn read(&self) -> ReadRef<'_, V> { (**self).read() } + + #[inline] + fn clone_boxed_reader(&self) -> Box> { + (**self).clone_boxed_reader() + } + + #[inline] + fn clone_reader(&self) -> Self::Reader { self.clone_boxed_reader() } +} + +impl StateWatcher for Box> { + type Watcher = Box>; + #[inline] + fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, Infallible> { + (**self).raw_modifies() + } + + #[inline] + fn clone_boxed_watcher(&self) -> Box> { + (**self).clone_boxed_watcher() + } + + #[inline] + fn clone_watcher(&self) -> Self::Watcher { self.clone_boxed_watcher() } +} + +impl StateWriter for Box> { + #[inline] + fn into_reader(self) -> Result { Err(self) } + #[inline] + fn write(&self) -> WriteRef { (**self).write() } + #[inline] + fn silent(&self) -> WriteRef { (**self).silent() } + #[inline] + fn shallow(&self) -> WriteRef { (**self).shallow() } + #[inline] + fn clone_boxed_writer(&self) -> Box> { + (**self).clone_boxed_writer() + } + + #[inline] + fn clone_writer(&self) -> Self { self.clone_boxed_writer() } +} + #[cfg(test)] mod tests { use std::cell::Cell; diff --git a/core/src/state/map_state.rs b/core/src/state/map_state.rs index 6e11709d5..34498726d 100644 --- a/core/src/state/map_state.rs +++ b/core/src/state/map_state.rs @@ -13,70 +13,26 @@ pub struct MapWriter { pub(super) part_map: WM, } -pub struct MapWriterAsReader { - pub(super) origin: W, - pub(super) part_map: M, -} - -impl StateReader for MapReader +impl StateReader for MapReader where Self: 'static, S: StateReader, - M: Fn(&S::Value) -> PartRef + Clone, + M: MapReaderFn, { type Value = V; - type OriginReader = S; type Reader = MapReader; #[inline] - fn read(&self) -> ReadRef { ReadRef::map(self.origin.read(), &self.part_map) } + fn read(&self) -> ReadRef { self.part_map.call(self.origin.read()) } #[inline] - fn clone_reader(&self) -> Self::Reader { - MapReader { origin: self.origin.clone_reader(), part_map: self.part_map.clone() } - } - - #[inline] - fn origin_reader(&self) -> &Self::OriginReader { &self.origin } - - #[inline] - fn try_into_value(self) -> Result - where - Self::Value: Sized, - { - Err(self) - } -} - -impl StateReader for MapWriterAsReader -where - Self: 'static, - S: StateReader, - M: Fn(&mut S::Value) -> PartMut + Clone, -{ - type Value = V; - type OriginReader = S; - type Reader = MapWriterAsReader; - - #[inline] - fn read(&self) -> ReadRef { - ReadRef::mut_as_ref_map(self.origin.read(), &self.part_map) + fn clone_boxed_reader(&self) -> Box> { + Box::new(self.clone_reader()) } #[inline] fn clone_reader(&self) -> Self::Reader { - MapWriterAsReader { origin: self.origin.clone_reader(), part_map: self.part_map.clone() } - } - - #[inline] - fn origin_reader(&self) -> &Self::OriginReader { &self.origin } - - #[inline] - fn try_into_value(self) -> Result - where - Self::Value: Sized, - { - Err(self) + MapReader { origin: self.origin.clone_reader(), part_map: self.part_map.clone() } } } @@ -87,8 +43,7 @@ where M: Fn(&mut S::Value) -> PartMut + Clone, { type Value = V; - type OriginReader = S; - type Reader = MapWriterAsReader; + type Reader = MapReader>; #[inline] fn read(&self) -> ReadRef { @@ -96,19 +51,16 @@ where } #[inline] - fn clone_reader(&self) -> Self::Reader { - MapWriterAsReader { origin: self.origin.clone_reader(), part_map: self.part_map.clone() } + fn clone_boxed_reader(&self) -> Box> { + Box::new(self.clone_reader()) } #[inline] - fn origin_reader(&self) -> &Self::OriginReader { &self.origin } - - #[inline] - fn try_into_value(self) -> Result - where - Self::Value: Sized, - { - Err(self) + fn clone_reader(&self) -> Self::Reader { + MapReader { + origin: self.origin.clone_reader(), + part_map: WriterMapReaderFn(self.part_map.clone()), + } } } @@ -118,10 +70,21 @@ where W: StateWriter, M: Fn(&mut W::Value) -> PartMut + Clone, { + type Watcher = Watcher; + #[inline] + fn clone_boxed_watcher(&self) -> Box> { + Box::new(self.clone_watcher()) + } + #[inline] fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, Infallible> { self.origin.raw_modifies() } + + #[inline] + fn clone_watcher(&self) -> Watcher { + Watcher::new(self.clone_reader(), self.raw_modifies()) + } } impl StateWriter for MapWriter @@ -130,13 +93,10 @@ where W: StateWriter, M: Fn(&mut W::Value) -> PartMut + Clone, { - type Writer = MapWriter; - type OriginWriter = W; - fn into_reader(self) -> Result { let Self { origin, part_map } = self; match origin.into_reader() { - Ok(origin) => Ok(MapWriterAsReader { origin, part_map }), + Ok(origin) => Ok(MapReader { origin, part_map: WriterMapReaderFn(part_map) }), Err(origin) => Err(Self { origin, part_map }), } } @@ -153,12 +113,14 @@ where } #[inline] - fn clone_writer(&self) -> Self::Writer { - MapWriter { origin: self.origin.clone_writer(), part_map: self.part_map.clone() } + fn clone_boxed_writer(&self) -> Box> { + Box::new(self.clone_writer()) } #[inline] - fn origin_writer(&self) -> &Self::OriginWriter { &self.origin } + fn clone_writer(&self) -> Self { + MapWriter { origin: self.origin.clone_writer(), part_map: self.part_map.clone() } + } } impl RenderProxy for MapReader @@ -172,16 +134,6 @@ where fn proxy(&self) -> impl Deref { self.read() } } -impl RenderProxy for MapWriterAsReader -where - Self: 'static, - S: StateReader, - F: Fn(&mut S::Value) -> PartMut + Clone, - V: Render, -{ - fn proxy(&self) -> impl Deref { self.read() } -} - impl<'w, S, F> IntoWidgetStrict<'w, RENDER> for MapWriter where Self: StateWriter, @@ -196,3 +148,31 @@ where { fn into_widget_strict(self) -> Widget<'static> { Compose::compose(self) } } + +trait MapReaderFn: Clone { + type Output: ?Sized; + fn call<'a>(&self, input: ReadRef<'a, Input>) -> ReadRef<'a, Self::Output>; +} + +impl MapReaderFn for F +where + F: Fn(&Input) -> PartRef + Clone, +{ + type Output = Output; + fn call<'a>(&self, input: ReadRef<'a, Input>) -> ReadRef<'a, Self::Output> { + ReadRef::map(input, self) + } +} + +impl MapReaderFn for WriterMapReaderFn +where + F: Fn(&mut Input) -> PartMut + Clone, +{ + type Output = Output; + fn call<'a>(&self, input: ReadRef<'a, Input>) -> ReadRef<'a, Self::Output> { + ReadRef::mut_as_ref_map(input, &self.0) + } +} + +#[derive(Clone)] +pub struct WriterMapReaderFn(pub(crate) F); diff --git a/core/src/state/splitted_state.rs b/core/src/state/splitted_state.rs index e4738401e..56401410e 100644 --- a/core/src/state/splitted_state.rs +++ b/core/src/state/splitted_state.rs @@ -19,8 +19,7 @@ where W: Fn(&mut O::Value) -> PartMut + Clone, { type Value = V; - type OriginReader = O; - type Reader = MapWriterAsReader; + type Reader = MapReader>; #[track_caller] fn read(&self) -> ReadRef { @@ -28,19 +27,16 @@ where } #[inline] - fn clone_reader(&self) -> Self::Reader { - MapWriterAsReader { origin: self.origin.clone_reader(), part_map: self.splitter.clone() } + fn clone_boxed_reader(&self) -> Box> { + Box::new(self.clone_reader()) } #[inline] - fn origin_reader(&self) -> &Self::OriginReader { &self.origin } - - #[inline] - fn try_into_value(self) -> Result - where - Self::Value: Sized, - { - Err(self) + fn clone_reader(&self) -> Self::Reader { + MapReader { + origin: self.origin.clone_reader(), + part_map: WriterMapReaderFn(self.splitter.clone()), + } } } @@ -50,9 +46,20 @@ where O: StateWriter, W: Fn(&mut O::Value) -> PartMut + Clone, { + type Watcher = Watcher; + + #[inline] + fn clone_boxed_watcher(&self) -> Box> { + Box::new(self.clone_watcher()) + } fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, std::convert::Infallible> { self.info.notifier.raw_modifies().box_it() } + + #[inline] + fn clone_watcher(&self) -> Watcher { + Watcher::new(self.clone_reader(), self.raw_modifies()) + } } impl StateWriter for SplittedWriter @@ -61,9 +68,6 @@ where O: StateWriter, W: Fn(&mut O::Value) -> PartMut + Clone, { - type Writer = SplittedWriter; - type OriginWriter = O; - fn into_reader(self) -> Result { if self.info.writer_count.get() == 1 { Ok(self.clone_reader()) } else { Err(self) } } @@ -77,7 +81,12 @@ where #[inline] fn shallow(&self) -> WriteRef { self.split_ref(self.origin.shallow()) } - fn clone_writer(&self) -> Self::Writer { + #[inline] + fn clone_boxed_writer(&self) -> Box> { + Box::new(self.clone_writer()) + } + + fn clone_writer(&self) -> Self { self.info.inc_writer(); SplittedWriter { origin: self.origin.clone_writer(), @@ -85,9 +94,6 @@ where info: self.info.clone(), } } - - #[inline] - fn origin_writer(&self) -> &Self::OriginWriter { &self.origin } } impl SplittedWriter diff --git a/core/src/state/stateful.rs b/core/src/state/stateful.rs index 35b0ce158..5830ddc43 100644 --- a/core/src/state/stateful.rs +++ b/core/src/state/stateful.rs @@ -45,17 +45,18 @@ pub(crate) struct WriterInfo { impl StateReader for Stateful { type Value = W; - type OriginReader = Self; type Reader = Reader; #[inline] fn read(&self) -> ReadRef { self.data.read() } #[inline] - fn clone_reader(&self) -> Self::Reader { Reader(self.data.clone()) } + fn clone_boxed_reader(&self) -> Box> { + Box::new(self.clone_reader()) + } #[inline] - fn origin_reader(&self) -> &Self::OriginReader { self } + fn clone_reader(&self) -> Self::Reader { Reader(self.data.clone()) } fn try_into_value(self) -> Result { if self.data.ref_count() == 1 { @@ -71,16 +72,25 @@ impl StateReader for Stateful { } impl StateWatcher for Stateful { + type Watcher = Watcher; + + #[inline] + fn clone_boxed_watcher(&self) -> Box> { + Box::new(self.clone_watcher()) + } + #[inline] fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, Infallible> { self.info.notifier.raw_modifies() } + + #[inline] + fn clone_watcher(&self) -> Watcher { + Watcher::new(self.clone_reader(), self.raw_modifies()) + } } impl StateWriter for Stateful { - type Writer = Self; - type OriginWriter = Self; - fn into_reader(self) -> Result where Self: Sized, @@ -98,25 +108,28 @@ impl StateWriter for Stateful { fn shallow(&self) -> WriteRef { self.write_ref(ModifyScope::FRAMEWORK) } #[inline] - fn clone_writer(&self) -> Self::Writer { self.clone() } + fn clone_boxed_writer(&self) -> Box> { + Box::new(self.clone_writer()) + } #[inline] - fn origin_writer(&self) -> &Self::OriginWriter { self } + fn clone_writer(&self) -> Self { self.clone() } } impl StateReader for Reader { type Value = W; - type OriginReader = Self; type Reader = Self; #[inline] fn read(&self) -> ReadRef { self.0.read() } #[inline] - fn clone_reader(&self) -> Self { Reader(self.0.clone()) } + fn clone_boxed_reader(&self) -> Box> { + Box::new(self.clone_reader()) + } #[inline] - fn origin_reader(&self) -> &Self::OriginReader { self } + fn clone_reader(&self) -> Self { Reader(self.0.clone()) } fn try_into_value(self) -> Result { if self.0.ref_count() == 1 { diff --git a/core/src/state/watcher.rs b/core/src/state/watcher.rs index cbcee62e2..db4caeb98 100644 --- a/core/src/state/watcher.rs +++ b/core/src/state/watcher.rs @@ -24,16 +24,17 @@ impl From>> for Reader { impl StateReader for Watcher { type Value = R::Value; type Reader = R::Reader; - type OriginReader = R::OriginReader; #[inline] fn read(&self) -> ReadRef { self.reader.read() } #[inline] - fn clone_reader(&self) -> Self::Reader { self.reader.clone_reader() } + fn clone_boxed_reader(&self) -> Box> { + Box::new(self.clone_reader()) + } #[inline] - fn origin_reader(&self) -> &Self::OriginReader { self.reader.origin_reader() } + fn clone_reader(&self) -> Self::Reader { self.reader.clone_reader() } #[inline] fn try_into_value(self) -> Result @@ -48,8 +49,19 @@ impl StateReader for Watcher { } impl StateWatcher for Watcher { + type Watcher = Watcher; + + #[inline] + fn clone_boxed_watcher(&self) -> Box> { + Box::new(self.clone_watcher()) + } + #[inline] fn raw_modifies(&self) -> CloneableBoxOp<'static, ModifyScope, Infallible> { self.modifies_observable.clone() } + + fn clone_watcher(&self) -> Watcher { + Watcher::new(self.clone_reader(), self.raw_modifies()) + } } diff --git a/docs/en/get_started/quick_start.md b/docs/en/get_started/quick_start.md index d4097f05c..ad0841700 100644 --- a/docs/en/get_started/quick_start.md +++ b/docs/en/get_started/quick_start.md @@ -747,28 +747,6 @@ let count_reader = state.map_reader(|d| &d.count); But Ribir doesn't provide a ``split_reader``, because separating a read-only sub-state is equivalent to converting a read-only sub-state. -### The origin state of the sub-state - -Each state can get where it came from through `origin_reader` and `origin_writer`. The root state's origin state is itself, and the sub-state's origin state is where it splits off from. - -```rust -use ribir::prelude::*; - -struct AppData { - count: usize, -} - -let state: State = State::value(AppData { count: 0 }); -let split_count = state.split_writer(|d| PartMut::new(&mut d.count)); - -// the root state's origin state is itself -let _: &State = state.origin_reader(); -let _: &State = state.origin_writer(); - -// the sub-state's origin state is where it splits from -let _: &Stateful = split_count.origin_reader(); -let _: &Stateful = split_count.origin_writer(); -``` ## The next step diff --git a/docs/zh/get_started/quick_start.md b/docs/zh/get_started/quick_start.md index 1901b22f4..008ab9725 100644 --- a/docs/zh/get_started/quick_start.md +++ b/docs/zh/get_started/quick_start.md @@ -749,29 +749,6 @@ let count_reader = state.map_reader(|d| &d.count); 但 Ribir 并没有提供一个 `split_reader`,因为分离一个只读的子状态,其意义等同于转换一个只读子状态。 -### 溯源状态 - -任何状态都可以通过 `origin_reader` 和 `origin_writer` 来获得当前状态的来源。根状态的源状态是自己,而子状态的源状态是转换或分离出它的父状态。 - - -```rust -use ribir::prelude::*; - -struct AppData { - count: usize, -} - -let state: State = State::value(AppData { count: 0 }); -let split_count = state.split_writer(|d| PartMut::new(&mut d.count)); - -// 根状态的源状态是它自己 -let _: &State = state.origin_reader(); -let _: &State = state.origin_writer(); - -// 子状态的源状态是它的父亲 -let _: &Stateful = split_count.origin_reader(); -let _: &Stateful = split_count.origin_writer(); -``` ## 下一步 diff --git a/examples/todos/src/ui.rs b/examples/todos/src/ui.rs index 0fc1c699f..f9763728e 100644 --- a/examples/todos/src/ui.rs +++ b/examples/todos/src/ui.rs @@ -4,7 +4,8 @@ use crate::todos::{Task, Todos}; impl Compose for Todos { fn compose(this: impl StateWriter) -> Widget<'static> { - fn_widget! { + providers! { + providers: [Provider::value_of_writer(this.clone_writer(), None)], @Column { align_items: Align::Center, item_gap: 12., @@ -130,9 +131,7 @@ fn input( fn task_item_widget(task: S, stagger: Stateful>>) -> Widget<'static> where S: StateWriter + 'static, - S::OriginWriter: StateWriter, { - let todos = task.origin_writer().clone_writer(); fn_widget! { let id = $task.id(); let mut item = @ListItem { }; @@ -169,7 +168,7 @@ where let icon = FatObj::new(icon); @ $icon { cursor: CursorIcon::Pointer, - on_tap: move |_| $todos.write().remove(id), + on_tap: move |e| Provider::write_of::(e).unwrap().remove(id) }.into_widget() })) } diff --git a/examples/wordle_game/src/ui.rs b/examples/wordle_game/src/ui.rs index 27d690cba..f9f31e2b7 100644 --- a/examples/wordle_game/src/ui.rs +++ b/examples/wordle_game/src/ui.rs @@ -41,7 +41,7 @@ trait WordleExtraWidgets: StateWriter + Sized + 'static { } fn keyboard(self, state_bar: impl StateWriter + 'static) -> Widget<'static> { - let this: ::Writer = self.clone_writer(); + let this = self.clone_writer(); let palette = Palette::of(BuildCtx::get()); let gray = palette.base_of(&palette.surface_variant()); self::column! { diff --git a/widgets/src/progress.rs b/widgets/src/progress.rs index 431232b76..99e714cb0 100644 --- a/widgets/src/progress.rs +++ b/widgets/src/progress.rs @@ -93,7 +93,7 @@ impl Compose for SpinnerProgress { // It is essential to ensure that the spinner is accessible by the class, // as the class may need to perform animations on the spinner. @Providers { - providers: [Provider::new(spinner.clone_writer())], + providers: [Provider::new(spinner.clone_writer().into_stateful())], @ $spinner { class: distinct_pipe! { if $this.value.is_some() {