Implemented local styles

Closes #86
This commit is contained in:
Jonathan Johnson 2023-12-13 10:44:34 -08:00
parent b1177e7c25
commit 0adb43a234
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
4 changed files with 120 additions and 18 deletions

View file

@ -1,13 +1,18 @@
use gooey::styles::components::TextColor; use gooey::styles::components::{TextColor, TextSize};
use gooey::widget::MakeWidget; use gooey::widget::MakeWidget;
use gooey::widgets::stack::Stack; use gooey::widgets::stack::Stack;
use gooey::widgets::Style; use gooey::widgets::Style;
use gooey::Run; use gooey::Run;
use kludgine::figures::units::Lp;
use kludgine::Color; use kludgine::Color;
fn main() -> gooey::Result { fn main() -> gooey::Result {
Stack::rows("Green".and(red_text("Red"))) Stack::rows("Green".and(red_text("Red")))
.with(&TextColor, Color::GREEN) .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() .run()
} }

View file

@ -49,10 +49,10 @@ impl Styles {
} }
/// Inserts a [`Component`] with a given name. /// 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) Arc::make_mut(&mut self.0)
.components .components
.insert(name, component.into_component_value()); .insert(name, component.into_stored_component());
} }
/// Inserts a [`Component`] using then name provided. /// Inserts a [`Component`] using then name provided.
@ -85,7 +85,14 @@ impl Styles {
where where
Value<C::ComponentType>: IntoComponentValue, Value<C::ComponentType>: 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 self
} }
@ -116,7 +123,7 @@ impl Styles {
.components .components
.get(&component.name()) .get(&component.name())
.or_else(|| self.0.components.get(&fallback.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)) .unwrap_or_else(|| fallback.default_value(context))
} }
@ -163,7 +170,7 @@ impl Styles {
self.0 self.0
.components .components
.get(&component.name()) .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, /// 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 /// Inserts all components from `other`, overwriting any existing entries
/// with the same [`ComponentName`]. /// with the same [`ComponentName`].
pub fn append(&mut self, other: Styles) { pub fn inherit_from(&mut self, other: Styles) {
for (name, value) in other { 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); self.insert_named(name, value);
} }
} }
@ -194,11 +209,11 @@ impl Debug for Styles {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut map = f.debug_struct("Styles"); let mut map = f.debug_struct("Styles");
let mut component_name = String::new(); let mut component_name = String::new();
for (name, component) in &self.0.components { for (name, stored) in &self.0.components {
component_name.clear(); component_name.clear();
write!(&mut component_name, "{name:?}")?; write!(&mut component_name, "{name:?}")?;
map.field(&component_name, component); map.field(&component_name, &stored.component);
} }
map.finish() map.finish()
} }
@ -216,20 +231,56 @@ impl FromIterator<(ComponentName, Component)> for Styles {
} }
impl IntoIterator for Styles { impl IntoIterator for Styles {
type IntoIter = hash_map::IntoIter<ComponentName, Value<Component>>; type IntoIter = StylesIntoIter;
type Item = (ComponentName, Value<Component>); type Item = (ComponentName, Value<Component>);
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
Arc::try_unwrap(self.0) StylesIntoIter {
.unwrap_or_else(|err| err.as_ref().clone()) into_iter: Arc::try_unwrap(self.0)
.components .unwrap_or_else(|err| err.as_ref().clone())
.into_iter() .components
.into_iter(),
}
}
}
/// An iterator that returns the contents of a [`Styles`] collection.
pub struct StylesIntoIter {
into_iter: hash_map::IntoIter<ComponentName, StoredComponent>,
}
impl Iterator for StylesIntoIter {
type Item = (ComponentName, Value<Component>);
fn next(&mut self) -> Option<Self::Item> {
self.into_iter
.next()
.map(|(name, stored)| (name, stored.component))
} }
} }
#[derive(Default, Clone)] #[derive(Default, Clone)]
struct StyleData { struct StyleData {
components: AHashMap<ComponentName, Value<Component>>, components: AHashMap<ComponentName, StoredComponent>,
}
/// A [`Component`] that is stored within a [`Styles`] collection.
#[derive(Clone)]
pub struct StoredComponent {
inherited: bool,
inheritable: bool,
component: Value<Component>,
}
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<Component>`. /// A value that can be converted into a `Value<Component>`.
@ -238,6 +289,31 @@ pub trait IntoComponentValue {
fn into_component_value(self) -> Value<Component>; fn into_component_value(self) -> Value<Component>;
} }
/// 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<T> 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<T> IntoComponentValue for T impl<T> IntoComponentValue for T
where where
T: Into<Component>, T: Into<Component>,

View file

@ -607,7 +607,7 @@ impl Node {
fn child_styles(&self) -> Styles { fn child_styles(&self) -> Styles {
let mut effective_styles = self.effective_styles.clone(); let mut effective_styles = self.effective_styles.clone();
if let Some(associated) = &self.associated_styles { if let Some(associated) = &self.associated_styles {
effective_styles.append(associated.get()); effective_styles.inherit_from(associated.get());
} }
effective_styles effective_styles
} }

View file

@ -8,7 +8,9 @@ use crate::styles::components::{
LineHeight8, TextSize, TextSize1, TextSize2, TextSize3, TextSize4, TextSize5, TextSize6, LineHeight8, TextSize, TextSize1, TextSize2, TextSize3, TextSize4, TextSize5, TextSize6,
TextSize7, TextSize8, TextSize7, TextSize8,
}; };
use crate::styles::{ComponentDefinition, IntoComponentValue, IntoDynamicComponentValue, Styles}; use crate::styles::{
ComponentDefinition, IntoComponentValue, IntoDynamicComponentValue, StoredComponent, Styles,
};
use crate::value::{IntoValue, Value}; use crate::value::{IntoValue, Value};
use crate::widget::{MakeWidget, WidgetRef, WrapperWidget}; use crate::widget::{MakeWidget, WidgetRef, WrapperWidget};
@ -52,6 +54,25 @@ impl Style {
self self
} }
/// Associates a style component with `self`.
#[must_use]
pub fn with_local<C: ComponentDefinition>(
mut self,
name: &C,
component: impl IntoValue<C::ComponentType>,
) -> Style
where
Value<C::ComponentType>: 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 /// Associates a style component with `self`, resolving its value using
/// `dynamic` at runtime. /// `dynamic` at runtime.
#[must_use] #[must_use]