From 0adb43a2348abd6ed13d05ccf412dd9f2fbd1c9e Mon Sep 17 00:00:00 2001 From: Jonathan Johnson Date: Wed, 13 Dec 2023 10:44:34 -0800 Subject: [PATCH] Implemented local styles Closes #86 --- examples/style.rs | 7 ++- src/styles.rs | 106 +++++++++++++++++++++++++++++++++++++------ src/tree.rs | 2 +- src/widgets/style.rs | 23 +++++++++- 4 files changed, 120 insertions(+), 18 deletions(-) diff --git a/examples/style.rs b/examples/style.rs index d2c1ffa..8eae862 100644 --- a/examples/style.rs +++ b/examples/style.rs @@ -1,13 +1,18 @@ -use gooey::styles::components::TextColor; +use gooey::styles::components::{TextColor, TextSize}; use gooey::widget::MakeWidget; use gooey::widgets::stack::Stack; use gooey::widgets::Style; use gooey::Run; +use kludgine::figures::units::Lp; use kludgine::Color; fn main() -> gooey::Result { Stack::rows("Green".and(red_text("Red"))) .with(&TextColor, Color::GREEN) + // Local styles are not inherited. In this situation, the text size is + // being applied to the stack, which has no text. The labels are + // children of the stack, and they will render at the default text size. + .with_local(&TextSize, Lp::inches(10)) .run() } diff --git a/src/styles.rs b/src/styles.rs index 5c402b4..ccac961 100644 --- a/src/styles.rs +++ b/src/styles.rs @@ -49,10 +49,10 @@ impl Styles { } /// Inserts a [`Component`] with a given name. - pub fn insert_named(&mut self, name: ComponentName, component: impl IntoComponentValue) { + pub fn insert_named(&mut self, name: ComponentName, component: impl IntoStoredComponent) { Arc::make_mut(&mut self.0) .components - .insert(name, component.into_component_value()); + .insert(name, component.into_stored_component()); } /// Inserts a [`Component`] using then name provided. @@ -85,7 +85,14 @@ impl Styles { where Value: IntoComponentValue, { - self.insert(name, component.into_value()); + self.insert_named( + name.name().into_owned(), + StoredComponent { + inherited: false, + inheritable: false, + component: component.into_value().into_component_value(), + }, + ); self } @@ -116,7 +123,7 @@ impl Styles { .components .get(&component.name()) .or_else(|| self.0.components.get(&fallback.name())) - .and_then(|component| Self::resolve_component(component, context)) + .and_then(|stored| Self::resolve_component(&stored.component, context)) .unwrap_or_else(|| fallback.default_value(context)) } @@ -163,7 +170,7 @@ impl Styles { self.0 .components .get(&component.name()) - .and_then(|component| Self::resolve_component(component, context)) + .and_then(|stored| Self::resolve_component(&stored.component, context)) } /// Returns the component associated with the given name, or if not found, @@ -183,8 +190,16 @@ impl Styles { /// Inserts all components from `other`, overwriting any existing entries /// with the same [`ComponentName`]. - pub fn append(&mut self, other: Styles) { - for (name, value) in other { + pub fn inherit_from(&mut self, other: Styles) { + for (name, mut value) in Arc::try_unwrap(other.0) + .unwrap_or_else(|err| err.as_ref().clone()) + .components + { + if value.inherited || !value.inheritable { + continue; + } + + value.inherited = true; self.insert_named(name, value); } } @@ -194,11 +209,11 @@ impl Debug for Styles { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut map = f.debug_struct("Styles"); let mut component_name = String::new(); - for (name, component) in &self.0.components { + for (name, stored) in &self.0.components { component_name.clear(); write!(&mut component_name, "{name:?}")?; - map.field(&component_name, component); + map.field(&component_name, &stored.component); } map.finish() } @@ -216,20 +231,56 @@ impl FromIterator<(ComponentName, Component)> for Styles { } impl IntoIterator for Styles { - type IntoIter = hash_map::IntoIter>; + type IntoIter = StylesIntoIter; type Item = (ComponentName, Value); fn into_iter(self) -> Self::IntoIter { - Arc::try_unwrap(self.0) - .unwrap_or_else(|err| err.as_ref().clone()) - .components - .into_iter() + StylesIntoIter { + into_iter: Arc::try_unwrap(self.0) + .unwrap_or_else(|err| err.as_ref().clone()) + .components + .into_iter(), + } + } +} + +/// An iterator that returns the contents of a [`Styles`] collection. +pub struct StylesIntoIter { + into_iter: hash_map::IntoIter, +} + +impl Iterator for StylesIntoIter { + type Item = (ComponentName, Value); + + fn next(&mut self) -> Option { + self.into_iter + .next() + .map(|(name, stored)| (name, stored.component)) } } #[derive(Default, Clone)] struct StyleData { - components: AHashMap>, + components: AHashMap, +} + +/// A [`Component`] that is stored within a [`Styles`] collection. +#[derive(Clone)] +pub struct StoredComponent { + inherited: bool, + inheritable: bool, + component: Value, +} + +impl StoredComponent { + /// Returns a new component that will not be inherited to children widgets. + pub fn local(component: impl IntoComponentValue) -> Self { + Self { + inherited: false, + inheritable: false, + component: component.into_component_value(), + } + } } /// A value that can be converted into a `Value`. @@ -238,6 +289,31 @@ pub trait IntoComponentValue { fn into_component_value(self) -> Value; } +/// A type that can be converted into a [`StoredComponent`]. +pub trait IntoStoredComponent { + /// Returns this value as a stored component. + fn into_stored_component(self) -> StoredComponent; +} + +impl IntoStoredComponent for T +where + T: IntoComponentValue, +{ + fn into_stored_component(self) -> StoredComponent { + StoredComponent { + inherited: false, + inheritable: true, + component: self.into_component_value(), + } + } +} + +impl IntoStoredComponent for StoredComponent { + fn into_stored_component(self) -> StoredComponent { + self + } +} + impl IntoComponentValue for T where T: Into, diff --git a/src/tree.rs b/src/tree.rs index 2744825..6d98634 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -607,7 +607,7 @@ impl Node { fn child_styles(&self) -> Styles { let mut effective_styles = self.effective_styles.clone(); if let Some(associated) = &self.associated_styles { - effective_styles.append(associated.get()); + effective_styles.inherit_from(associated.get()); } effective_styles } diff --git a/src/widgets/style.rs b/src/widgets/style.rs index 301d98b..a40867a 100644 --- a/src/widgets/style.rs +++ b/src/widgets/style.rs @@ -8,7 +8,9 @@ use crate::styles::components::{ LineHeight8, TextSize, TextSize1, TextSize2, TextSize3, TextSize4, TextSize5, TextSize6, TextSize7, TextSize8, }; -use crate::styles::{ComponentDefinition, IntoComponentValue, IntoDynamicComponentValue, Styles}; +use crate::styles::{ + ComponentDefinition, IntoComponentValue, IntoDynamicComponentValue, StoredComponent, Styles, +}; use crate::value::{IntoValue, Value}; use crate::widget::{MakeWidget, WidgetRef, WrapperWidget}; @@ -52,6 +54,25 @@ impl Style { self } + /// Associates a style component with `self`. + #[must_use] + pub fn with_local( + mut self, + name: &C, + component: impl IntoValue, + ) -> Style + where + Value: IntoComponentValue, + { + self.map_styles_mut(|styles| { + styles.insert_named( + name.name().into_owned(), + StoredComponent::local(component.into_value()), + ); + }); + self + } + /// Associates a style component with `self`, resolving its value using /// `dynamic` at runtime. #[must_use]