Skip to content

Commit

Permalink
feat(core): 🎸 add built-in providers
Browse files Browse the repository at this point in the history
  • Loading branch information
M-Adoo committed Jan 10, 2025
1 parent bdf7bf0 commit 35178b5
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he

- **core**: The `Render::dirty_phase` method has been added to allow widgets to mark only the paint phase as dirty when it is modified. (#689 @M-Adoo)
- **core**: Supports `Provider` to dirty the tree if it's a state writer. (#689 @M-Adoo)
- **core**: Added the built-in field `providers` to provide data to its descendants. (#pr @M-Adoo)
- **macros**: Added the `part_reader!` macro to generate a partial reader from a reference of a reader. (#688 @M-Adoo)
- **macros**: The `simple_declare` now supports the `stateless` meta attribute, `#[simple_declare(stateless)]`. (#688 @M-Adoo)

Expand Down
44 changes: 38 additions & 6 deletions core/src/builtin_widgets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,9 @@ pub struct FatObj<T> {
painting_style: Option<State<PaintingStyleWidget>>,
text_style: Option<State<TextStyleWidget>>,
keep_alive: Option<State<KeepAlive>>,
tooltips: Option<State<Tooltips>>,
keep_alive_unsubscribe_handle: Option<Box<dyn Any>>,
tooltips: Option<State<Tooltips>>,
providers: Option<SmallVec<[Provider; 1]>>,
}

/// Create a function widget that uses an empty `FatObj` as the host object.
Expand Down Expand Up @@ -187,6 +188,7 @@ impl<T> FatObj<T> {
tooltips: self.tooltips,
keep_alive: self.keep_alive,
keep_alive_unsubscribe_handle: self.keep_alive_unsubscribe_handle,
providers: self.providers,
}
}

Expand Down Expand Up @@ -770,7 +772,7 @@ impl<T> FatObj<T> {
self.declare_builtin_init(v, Self::get_fitted_box_widget, |m, v| m.box_fit = v)
}

/// Initializes the painting style of this widget.
/// Provide a painting style to this widget.
pub fn painting_style<const M: usize>(self, v: impl DeclareInto<PaintingStyle, M>) -> Self {
self.declare_builtin_init(v, Self::get_painting_style_widget, |m, v| m.painting_style = v)
}
Expand Down Expand Up @@ -923,6 +925,16 @@ impl<T> FatObj<T> {
self
}

/// Initializes the providers of the widget.
pub fn providers(mut self, providers: impl Into<SmallVec<[Provider; 1]>>) -> Self {
if let Some(vec) = self.providers.as_mut() {
vec.extend(providers.into());
} else {
self.providers = Some(providers.into());
}
self
}

fn declare_builtin_init<V: 'static, B: 'static, const M: usize>(
mut self, init: impl DeclareInto<V, M>, get_builtin: impl FnOnce(&mut Self) -> &State<B>,
set_value: fn(&mut B, V),
Expand Down Expand Up @@ -964,7 +976,7 @@ where
}

impl<'a> FatObj<Widget<'a>> {
fn compose(self) -> Widget<'a> {
fn compose(mut self) -> Widget<'a> {
macro_rules! compose_builtin_widgets {
($host: ident + [$($field: ident),*]) => {
$(
Expand All @@ -975,6 +987,19 @@ impl<'a> FatObj<Widget<'a>> {
};
}
let mut host = self.host;
if let Some(painting_style) = self.painting_style {
self
.providers
.get_or_insert_default()
.push(PaintingStyleWidget::into_provider(painting_style));
}
if let Some(text_style) = self.text_style {
self
.providers
.get_or_insert_default()
.push(TextStyleWidget::into_provider(text_style));
}

compose_builtin_widgets!(
host
+ [
Expand All @@ -983,11 +1008,18 @@ impl<'a> FatObj<Widget<'a>> {
fitted_box,
foreground,
box_decoration,
painting_style,
text_style,
scrollable,
layout_box,
class,
class
]
);
if let Some(providers) = self.providers {
host = Providers::new(providers).with_child(host);
}

compose_builtin_widgets!(
host
+ [
constrained_box,
tooltips,
margin,
Expand Down
15 changes: 8 additions & 7 deletions core/src/builtin_widgets/painting_style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,18 @@ impl<'c> ComposeChild<'c> for PaintingStyleWidget {
type Child = Widget<'c>;

fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
// We need to provide the text style for the children to access.
let provider = match this.try_into_value() {
Providers::new([Self::into_provider(this)]).with_child(child)
}
}

impl PaintingStyleWidget {
pub fn into_provider(this: impl StateWriter<Value = Self>) -> Provider {
match this.try_into_value() {
Ok(this) => Provider::new(this.painting_style),
Err(this) => Provider::value_of_writer(
this.map_writer(|w| PartData::from_ref_mut(&mut w.painting_style)),
Some(DirtyPhase::LayoutSubtree),
),
};

Providers::new([provider])
.with_child(child)
.into_widget()
}
}
}
11 changes: 7 additions & 4 deletions core/src/builtin_widgets/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ pub struct Providers {
providers: RefCell<SmallVec<[Provider; 1]>>,
}

/// Macro used to generate a function widget using `BuildVariants` as the root
/// Macro used to generate a function widget using `Providers` as the root
/// widget.
#[macro_export]
macro_rules! providers {
Expand Down Expand Up @@ -297,9 +297,12 @@ impl Declare for Providers {
}

impl ProvidersDeclarer {
pub fn providers(mut self, variants: impl Into<SmallVec<[Provider; 1]>>) -> Self {
assert!(self.providers.is_none(), "Providers already initialized");
self.providers = Some(variants.into());
pub fn providers(mut self, providers: impl Into<SmallVec<[Provider; 1]>>) -> Self {
if let Some(vec) = self.providers.as_mut() {
vec.extend(providers.into());
} else {
self.providers = Some(providers.into());
}
self
}
}
Expand Down
15 changes: 8 additions & 7 deletions core/src/builtin_widgets/text_style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ impl<'c> ComposeChild<'c> for TextStyleWidget {
type Child = Widget<'c>;

fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
// We need to provide the text style for the children to access.
let provider = match this.try_into_value() {
Providers::new([Self::into_provider(this)]).with_child(child)
}
}

impl TextStyleWidget {
pub fn into_provider(this: impl StateWriter<Value = Self>) -> Provider {
match this.try_into_value() {
Ok(this) => Provider::new(this.text_style),
Err(this) => Provider::value_of_writer(
this.map_writer(|w| PartData::from_ref_mut(&mut w.text_style)),
Some(DirtyPhase::LayoutSubtree),
),
};

Providers::new([provider])
.with_child(child)
.into_widget()
}
}
}

0 comments on commit 35178b5

Please sign in to comment.