diff --git a/CHANGELOG.md b/CHANGELOG.md index 5556755f0..1dc5c91ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -290,10 +290,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he ```rust let state = Stateful::value(0132); providers!{ - providers: smallvec![ - Provider::new(state.clone_writer()), - Provider::value_of_state(state) - ], + providers: [Provider::value_of_reader(state)], @SizedBox { size: Size::new(1.,1.), on_tap: |e| { @@ -304,7 +301,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he @Text { text: { // Access the provider in any descendants - let v = Provider::of::>(ctx!()); + let v = Provider::state_of::>(BuildCtx::get()); let v = v.unwrap().clone_writer(); pipe!($v.to_string()) } diff --git a/core/src/builtin_widgets/providers.rs b/core/src/builtin_widgets/providers.rs index ed77742fc..abb918628 100644 --- a/core/src/builtin_widgets/providers.rs +++ b/core/src/builtin_widgets/providers.rs @@ -199,10 +199,49 @@ pub struct ProviderCtx { } impl Provider { - /// Establish a provider for `T`. + /// Establish a provider for `T` using [`Provider::of`]. + /// + /// ## Example + /// + /// ``` + /// use ribir_core::prelude::*; + /// + /// let w = providers! { + /// providers: [Provider::new(1i32)], + /// @ { + /// assert_eq!(*Provider::of::(BuildCtx::get()).unwrap(), 1); + /// Void + /// } + /// }; + /// ``` pub fn new(value: T) -> Provider { Provider::Setup(Box::new(Setup::new(value))) } - /// Establish a provider for the `Value` of a reader. + /// Establish a provider for the value of a reader. It will clone the reader + /// to create the provider to prevent any writer leaks. + /// + /// Obtain the value using [`Provider::of`], and if you want to access the + /// reader, using [`Provider::state_of`]. + /// + /// ## Example + /// + /// ``` + /// use ribir_core::prelude::*; + /// + /// let w = providers! { + /// providers: [Provider::value_of_reader(Stateful::new(1i32))], + /// @ { + /// let ctx = BuildCtx::get(); + /// // Obtain the value + /// assert_eq!(*Provider::of::(ctx).unwrap(), 1); + /// + /// // Obtain the reader + /// let reader = Provider::state_of:: + /// < as StateReader>::Reader>(ctx); + /// assert_eq!(*reader.unwrap().read(), 1); + /// Void + /// } + /// }; + /// ``` pub fn value_of_reader(value: R) -> Provider where R: StateReader, @@ -214,18 +253,45 @@ impl Provider { } } - /// Establish a provider for the `Value` of a writer. If you create this - /// provider using a writer, you can access a write reference of the value - /// through [`Provider::write_of`]. + /// Establish a provider for the value of a writer. + /// + /// Obtain the value using [`Provider::of`], get the write reference of the + /// writer using [`Provider::write_of`], and if you need to access the writer, + /// use [`Provider::state_of`]. + /// + /// The `dirty` parameter is used to specify the affected dirty phase when + /// modifying the writer's value. Depending on how your descendants use it, if + /// they rely on the writer's value for painting or layout, a dirty phase + /// should be passed; otherwise, `None` can be passed. + /// + /// Generally, if your provider affects the layout, it impacts the entire + /// subtree because the entire subtree can access and use the provider. In + /// such cases, pass `Some(DirtyPhase::LayoutSubtree)`. /// - /// The `dirty` parameter is utilized to specify the dirty phase affected when - /// the value of writer is modified. It depends on how your descendants - /// utilize it; if they rely on the writer's value for painting or layout, a - /// dirty phase should be passed, otherwise, you can pass `None`. + /// ## Example /// - /// In general, if your provider affects the layout, it impacts the entire - /// subtree. This is because the entire subtree can access the provider and - /// utilize it, so you should pass `Some(DirtyPhase::LayoutSubtree)`. + /// ``` + /// use ribir_core::prelude::*; + /// + /// let w = providers! { + /// providers: [Provider::value_of_writer(Stateful::new(1i32), None)], + /// @ { + /// let ctx = BuildCtx::get(); + /// // Obtain the value + /// assert_eq!(*Provider::of::(ctx).unwrap(), 1); + /// + /// // Obtain the writer reference + /// let w_value = Provider::write_of::(ctx); + /// *w_value.unwrap() = 2; + /// + /// // Obtain the writer + /// let writer = Provider::state_of::>(ctx); + /// assert_eq!(*writer.unwrap().write(), 2); + /// + /// Void + /// } + /// }; + /// ``` pub fn value_of_writer( value: impl StateWriter + Query, dirty: Option, ) -> Provider { @@ -247,14 +313,26 @@ impl Provider { } } - /// Access the provider of `P` within the context. - pub fn of(ctx: &impl AsRef) -> Option> { - ctx.as_ref().get_provider::

() + /// Obtain the provider of `V` from the context where `V` is created using + /// [`Provider::new`], or where it is the value of a state created with + /// [`Provider::value_of_reader`] or [`Provider::value_of_writer`]. + pub fn of(ctx: &impl AsRef) -> Option> { + ctx.as_ref().get_provider::() + } + + /// Obtain the write reference of the writer's value from the context in + /// which the provider is created using [`Provider::value_of_writer`]. + pub fn write_of(ctx: &impl AsRef) -> Option> { + ctx.as_ref().get_provider_write::() } - /// Access the write reference of `P` within the context. - pub fn write_of(ctx: &impl AsRef) -> Option> { - ctx.as_ref().get_provider_write::

() + /// Obtain the state of `S` from the context where `S` is created using + /// [`Provider::value_of_reader`] or [`Provider::value_of_writer`]. + pub fn state_of(ctx: &impl AsRef) -> Option> + where + S: StateReader, + { + ctx.as_ref().get_provider_state::() } /// Setup the provider to the context. @@ -438,6 +516,18 @@ impl ProviderCtx { .and_then(QueryHandle::into_mut) } + pub fn get_provider_state(&self) -> Option> + where + S: StateReader, + { + let info = Provider::info::(); + self + .data + .get(&info) + .and_then(|q| q.query_write(&QueryId::of::())) + .and_then(QueryHandle::into_ref) + } + pub(crate) fn remove_key_value_if( &mut self, f: impl Fn(&TypeInfo) -> bool, ) -> Vec<(TypeInfo, Box)> { diff --git a/themes/material/src/classes/scrollbar_cls.rs b/themes/material/src/classes/scrollbar_cls.rs index 6cdb0e6bb..c8f9e4ce1 100644 --- a/themes/material/src/classes/scrollbar_cls.rs +++ b/themes/material/src/classes/scrollbar_cls.rs @@ -33,7 +33,6 @@ pub(super) fn init(classes: &mut Classes) { fn style_track(w: Widget, is_hor: bool) -> Widget { rdl! { - let scroll = Provider::of::>(BuildCtx::get()).unwrap(); let mut w = FatObj::new(w); if is_hor { w = w.v_align(VAlign::Bottom); @@ -75,6 +74,9 @@ fn style_track(w: Widget, is_hor: bool) -> Widget { fade = Some(u); }; + let scroll = Provider::state_of::>(BuildCtx::get()) + .unwrap() + .clone_writer(); let u = if is_hor { watch!(($scroll).get_scroll_pos().x) .distinct_until_changed() diff --git a/widgets/src/scrollbar.rs b/widgets/src/scrollbar.rs index f4f8d6b17..f6812ad4d 100644 --- a/widgets/src/scrollbar.rs +++ b/widgets/src/scrollbar.rs @@ -63,10 +63,7 @@ impl<'c> ComposeChild<'c> for Scrollbar { // scroll states or enables descendants to trigger scrolling to a different // position. providers! { - providers: smallvec::smallvec![ - Provider::new(scroll.clone_writer()), - Provider::value_of_writer(scroll.clone_writer(), None), - ], + providers: [Provider::value_of_writer(scroll.clone_writer(), None)], @ { let h_scrollbar = distinct_pipe!($scroll.is_x_scrollable()) .map(move |need_bar| need_bar.then(||{