//! Types for styling widgets. use std::any::Any; use std::borrow::Cow; use std::collections::hash_map; use std::fmt::{Debug, Write}; use std::ops::{ Add, AddAssign, Bound, Deref, Div, Mul, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, }; use std::sync::Arc; use ahash::AHashMap; use figures::units::{Lp, Px, UPx}; use figures::{Fraction, IntoSigned, IntoUnsigned, Rect, ScreenScale, Size, Zero}; pub use kludgine::cosmic_text::{FamilyOwned, Style, Weight}; pub use kludgine::shapes::CornerRadii; pub use kludgine::Color; pub use palette::OklabHue; use palette::{IntoColor, Okhsl, Srgb}; use crate::animation::{EasingFunction, ZeroToOne}; use crate::context::WidgetContext; use crate::names::Name; use crate::utils::Lazy; use crate::value::{Dynamic, IntoValue, Value}; #[macro_use] pub mod components; /// A collection of style components organized by their name. #[derive(Clone, Default)] pub struct Styles(Arc); impl Styles { /// Returns an empty collection. #[must_use] pub fn new() -> Self { Self::default() } /// Returns a collection with the capacity to hold up to `capacity` elements /// without reallocating. #[must_use] pub fn with_capacity(capacity: usize) -> Self { Self(Arc::new(StyleData { components: AHashMap::with_capacity(capacity), })) } /// Inserts a [`Component`] with a given name. pub fn insert_named(&mut self, name: ComponentName, component: impl IntoStoredComponent) { Arc::make_mut(&mut self.0) .components .insert(name, component.into_stored_component()); } /// Inserts a [`Component`] using then name provided. pub fn insert(&mut self, name: &impl NamedComponent, component: impl IntoComponentValue) { let name = name.name().into_owned(); self.insert_named(name, component); } /// Inserts a [`Component`] using then name provided, resolving the value /// through `dynamic`. pub fn insert_dynamic( &mut self, name: &impl NamedComponent, dynamic: impl IntoDynamicComponentValue, ) { let component = match dynamic.into_dynamic_component() { Value::Constant(dynamic) => Value::Constant(Component::Dynamic(dynamic)), Value::Dynamic(dynamic) => Value::Dynamic(dynamic.map_each_cloned(Component::Dynamic)), }; self.insert(name, component); } /// Adds a [`Component`] for the name provided and returns self. #[must_use] pub fn with( mut self, name: &C, component: impl IntoValue, ) -> Self where Value: IntoComponentValue, { self.insert_named( name.name().into_owned(), StoredComponent { inherited: false, inheritable: true, component: component.into_value().into_component_value(), }, ); self } /// Adds a [`Component`] using then name provided, resolving the value /// through `dynamic`. This function returns self. #[must_use] pub fn with_dynamic( mut self, name: &C, dynamic: impl IntoDynamicComponentValue, ) -> Self { self.insert_dynamic(name, dynamic); self } /// Returns the associated component for the given name, if found. #[must_use] pub fn get_with_fallback( &self, component: &impl NamedComponent, fallback: &Fallback, context: &WidgetContext<'_, '_>, ) -> Fallback::ComponentType where Fallback: ComponentDefinition + ?Sized, { self.0 .components .get(&component.name()) .or_else(|| self.0.components.get(&fallback.name())) .and_then(|stored| Self::resolve_component(&stored.component, context)) .unwrap_or_else(|| fallback.default_value(context)) } fn resolve_component( component: &Value, context: &WidgetContext<'_, '_>, ) -> Option where T: ComponentType, { let mut resolved = component.get(); loop { match T::try_from_component(resolved) { Ok(value) => { if value.requires_invalidation() { component.invalidate_when_changed(context); } else { component.redraw_when_changed(context); } break Some(value); } Err(Component::Dynamic(dynamic)) => { let Some(new_component) = dynamic.resolve(context) else { break None; }; resolved = new_component; } Err(_) => break None, } } } /// Returns the component associated with the given name, if a value is /// specified. #[must_use] pub fn try_get( &self, component: &Named, context: &WidgetContext<'_, '_>, ) -> Option where Named: ComponentDefinition + ?Sized, { self.0 .components .get(&component.name()) .and_then(|stored| Self::resolve_component(&stored.component, context)) } /// Returns the component associated with the given name, or if not found, /// returns the default value provided by the definition. #[must_use] pub fn get( &self, component: &Named, context: &WidgetContext<'_, '_>, ) -> Named::ComponentType where Named: ComponentDefinition + ?Sized, { self.try_get(component, context) .unwrap_or_else(|| component.default_value(context)) } /// Inserts all components from `other`, overwriting any existing entries /// with the same [`ComponentName`]. 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.inheritable || self.0.components.contains_key(&name) { continue; } value.inherited = true; self.insert_named(name, value); } } /// Returns this collection of styles without any local style definitions. #[must_use] pub fn into_inherited(self) -> Self { if self.0.components.values().any(|stored| !stored.inheritable) { Self(Arc::new(StyleData { components: Arc::try_unwrap(self.0) .unwrap_or_else(|err| err.as_ref().clone()) .components .into_iter() .filter(|(_, stored)| stored.inheritable) .collect(), })) } else { self } } } 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, stored) in &self.0.components { component_name.clear(); write!(&mut component_name, "{name:?}")?; map.field(&component_name, &stored.component); } map.finish() } } impl FromIterator<(ComponentName, Component)> for Styles { fn from_iter>(iter: T) -> Self { let iter = iter.into_iter(); let mut styles = Self::with_capacity(iter.size_hint().0); for (name, component) in iter { styles.insert_named(name, component); } styles } } impl IntoIterator for Styles { type IntoIter = StylesIntoIter; type Item = (ComponentName, Value); fn into_iter(self) -> Self::IntoIter { 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, } /// 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`. pub trait IntoComponentValue { /// Returns `self` stored in a component value. 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, { fn into_component_value(self) -> Value { Value::Constant(self.into()) } } impl IntoComponentValue for Value where T: Clone + Send + 'static, Component: From, { fn into_component_value(self) -> Value { self.map_each(|v| Component::from(v.clone())) } } impl IntoComponentValue for Dynamic where T: Clone + Send + 'static, Component: From, { fn into_component_value(self) -> Value { Value::Dynamic(self.map_each_into()) } } /// A type that can convert into a [`Value`] containing a [`DynamicComponent`]. pub trait IntoDynamicComponentValue { /// Returns this type converted into a dynamic component value. fn into_dynamic_component(self) -> Value; } impl IntoDynamicComponentValue for DynamicComponent { fn into_dynamic_component(self) -> Value { Value::Constant(self) } } impl IntoDynamicComponentValue for T where T: ComponentDefinition + Clone + Send + Sync + 'static, { fn into_dynamic_component(self) -> Value { Value::Constant(DynamicComponent::from(self)) } } impl IntoDynamicComponentValue for Dynamic where T: ComponentDefinition + Clone + Send + Sync + 'static, { fn into_dynamic_component(self) -> Value { Value::Dynamic(self.map_each_into()) } } /// A value of a style component. #[derive(Debug, Clone, PartialEq)] pub enum Component { /// A color. Color(Color), /// A single-dimension measurement. Dimension(Dimension), /// A single-dimension measurement. DimensionRange(DimensionRange), /// A percentage between 0.0 and 1.0. Percent(ZeroToOne), /// An easing function for animations. Easing(EasingFunction), /// A visual ordering to use for layout. VisualOrder(VisualOrder), /// A description of what widgets should be focusable. FocusableWidgets(FocusableWidgets), /// A description of the depth of a /// [`Container`](crate::widgets::Container). ContainerLevel(ContainerLevel), /// A font family. FontFamily(FamilyOwned), /// The weight (boldness) of a font. FontWeight(Weight), /// The style of a font. FontStyle(Style), /// A custom component type. Custom(CustomComponent), /// This component should use the associated value in the named class. Dynamic(DynamicComponent), } impl Component { /// Returns a [`CustomComponent`] created from `component`. /// /// Custom components allow storing nearly any type in the style system. pub fn custom(component: T) -> Self where T: RequireInvalidation + Debug + Send + Sync + 'static, { Self::Custom(CustomComponent::new(component)) } /// Returns a new [`DynamicComponent`] which allows resolving a component at /// runtime. #[must_use] pub fn dynamic(resolve: Func) -> Self where Func: for<'a, 'context, 'widget> Fn(&'a WidgetContext<'context, 'widget>) -> Option + Send + Sync + 'static, T: ComponentType, { Self::Dynamic(DynamicComponent::new(move |context| { resolve(context).map(T::into_component) })) } } impl From for Component { fn from(value: FamilyOwned) -> Self { Self::FontFamily(value) } } impl TryFrom for FamilyOwned { type Error = Component; fn try_from(value: Component) -> Result { match value { Component::FontFamily(family) => Ok(family), other => Err(other), } } } impl RequireInvalidation for FamilyOwned { fn requires_invalidation(&self) -> bool { true } } impl From for Component { fn from(value: Weight) -> Self { Self::FontWeight(value) } } impl TryFrom for Weight { type Error = Component; fn try_from(value: Component) -> Result { match value { Component::FontWeight(weight) => Ok(weight), other => Err(other), } } } impl RequireInvalidation for Weight { fn requires_invalidation(&self) -> bool { true } } impl From