//! Types for styling widgets. use std::any::Any; use std::borrow::Cow; use std::collections::hash_map; use std::fmt::Debug; use std::ops::{ Add, Bound, Deref, Div, Mul, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, }; use std::panic::{RefUnwindSafe, UnwindSafe}; use std::sync::Arc; use ahash::AHashMap; use kludgine::cosmic_text::{FamilyOwned, Style, Weight}; use kludgine::figures::units::{Lp, Px, UPx}; use kludgine::figures::{Fraction, IntoSigned, IntoUnsigned, Rect, ScreenScale, Size, Zero}; use kludgine::shapes::CornerRadii; use kludgine::Color; use palette::{IntoColor, Okhsl, OklabHue, 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, Debug, 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(AHashMap::with_capacity(capacity))) } /// Inserts a [`Component`] with a given name. pub fn insert_named(&mut self, name: ComponentName, component: impl IntoComponentValue) { Arc::make_mut(&mut self.0).insert(name, component.into_component_value()); } /// 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); } /// 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(name, component.into_value()); self } /// Returns the associated component for the given name, if found. #[must_use] pub fn get_named(&self, component: &Named) -> Option<&Value> where Named: NamedComponent + ?Sized, { let name = component.name(); self.0.get(&name) } /// 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, { let name = component.name(); self.0 .get(&name) .and_then(|component| { match ::try_from_component(component.get()) { Ok(value) => { if value.requires_invalidation() { component.invalidate_when_changed(context); } else { component.redraw_when_changed(context); } Some(value) } Err(_) => None, } }) .unwrap_or_else(|| component.default_value(context)) } /// 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 { self.insert_named(name, 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; } 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()) } } 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 = hash_map::IntoIter>; type Item = (ComponentName, Value); fn into_iter(self) -> Self::IntoIter { Arc::try_unwrap(self.0) .unwrap_or_else(|err| err.as_ref().clone()) .into_iter() } } // /// An iterator over the owned contents of a [`Styles`] instance. // pub struct StylesIntoIter { // main: hash_map::IntoIter>, // } // impl Iterator for StylesIntoIter { // type Item = (ComponentName, Value); // fn next(&mut self) -> Option { // loop { // if let Some((group, names)) = &mut self.names { // if let Some((name, component)) = names.next() { // return Some((ComponentName::new(group.clone(), name), component)); // } // self.names = None; // } // let (group, names) = self.main.next()?; // self.names = Some((group, names.into_iter())); // } // } // } /// 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), } 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 + RefUnwindSafe + UnwindSafe + Debug + Send + Sync + 'static, { Self::Custom(CustomComponent::new(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