diff options
| author | 2022-12-13 09:31:57 +0100 | |
|---|---|---|
| committer | 2022-12-13 09:31:57 +0100 | |
| commit | 2e6d90f141217bad83eacd392562c13d7485881f (patch) | |
| tree | baa2c507076073aed4fd24abc9c7a7949d85c039 /style | |
| parent | ba95042fff378213f5029b2b164d79e768482a47 (diff) | |
| parent | 02182eea45537c9eb5b2bddfdff822bb8a3d143d (diff) | |
| download | iced-2e6d90f141217bad83eacd392562c13d7485881f.tar.gz iced-2e6d90f141217bad83eacd392562c13d7485881f.tar.bz2 iced-2e6d90f141217bad83eacd392562c13d7485881f.zip | |
Merge branch 'master' into feat/slider-orientation
Diffstat (limited to '')
| -rw-r--r-- | style/Cargo.toml | 4 | ||||
| -rw-r--r-- | style/src/application.rs | 14 | ||||
| -rw-r--r-- | style/src/button.rs | 23 | ||||
| -rw-r--r-- | style/src/checkbox.rs | 17 | ||||
| -rw-r--r-- | style/src/container.rs | 12 | ||||
| -rw-r--r-- | style/src/lib.rs | 2 | ||||
| -rw-r--r-- | style/src/menu.rs | 15 | ||||
| -rw-r--r-- | style/src/pane_grid.rs | 10 | ||||
| -rw-r--r-- | style/src/pick_list.rs | 26 | ||||
| -rw-r--r-- | style/src/progress_bar.rs | 11 | ||||
| -rw-r--r-- | style/src/radio.rs | 16 | ||||
| -rw-r--r-- | style/src/rule.rs | 7 | ||||
| -rw-r--r-- | style/src/scrollable.rs | 20 | ||||
| -rw-r--r-- | style/src/slider.rs | 31 | ||||
| -rw-r--r-- | style/src/svg.rs | 23 | ||||
| -rw-r--r-- | style/src/text.rs | 8 | ||||
| -rw-r--r-- | style/src/text_input.rs | 24 | ||||
| -rw-r--r-- | style/src/theme.rs | 861 | ||||
| -rw-r--r-- | style/src/theme/palette.rs | 61 | ||||
| -rw-r--r-- | style/src/toggler.rs | 19 | 
20 files changed, 862 insertions, 342 deletions
| diff --git a/style/Cargo.toml b/style/Cargo.toml index 4a482a4f..9f7d904a 100644 --- a/style/Cargo.toml +++ b/style/Cargo.toml @@ -1,6 +1,6 @@  [package]  name = "iced_style" -version = "0.4.0" +version = "0.5.1"  authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]  edition = "2021"  description = "The default set of styles of Iced" @@ -11,7 +11,7 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]  categories = ["gui"]  [dependencies.iced_core] -version = "0.5" +version = "0.6"  path = "../core"  features = ["palette"] diff --git a/style/src/application.rs b/style/src/application.rs index d48c6a34..e9a1f4ff 100644 --- a/style/src/application.rs +++ b/style/src/application.rs @@ -1,13 +1,23 @@ +//! Change the appearance of an application.  use iced_core::Color; +/// A set of rules that dictate the style of an application.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default; -    fn appearance(&self, style: Self::Style) -> Appearance; +    /// Returns the [`Appearance`] of the application for the provided [`Style`]. +    /// +    /// [`Style`]: Self::Style +    fn appearance(&self, style: &Self::Style) -> Appearance;  } +/// The appearance of an application.  #[derive(Debug, Clone, Copy, PartialEq)]  pub struct Appearance { +    /// The background [`Color`] of the application.      pub background_color: Color, + +    /// The default text [`Color`] of the application.      pub text_color: Color,  } diff --git a/style/src/button.rs b/style/src/button.rs index c63a6b71..a564a2b7 100644 --- a/style/src/button.rs +++ b/style/src/button.rs @@ -1,14 +1,20 @@ -//! Allow your users to perform actions by pressing a button. +//! Change the apperance of a button.  use iced_core::{Background, Color, Vector};  /// The appearance of a button.  #[derive(Debug, Clone, Copy)]  pub struct Appearance { +    /// The amount of offset to apply to the shadow of the button.      pub shadow_offset: Vector, +    /// The [`Background`] of the button.      pub background: Option<Background>, +    /// The border radius of the button.      pub border_radius: f32, +    /// The border width of the button.      pub border_width: f32, +    /// The border [`Color`] of the button.      pub border_color: Color, +    /// The text [`Color`] of the button.      pub text_color: Color,  } @@ -27,11 +33,14 @@ impl std::default::Default for Appearance {  /// A set of rules that dictate the style of a button.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default; -    fn active(&self, style: Self::Style) -> Appearance; +    /// Produces the active [`Appearance`] of a button. +    fn active(&self, style: &Self::Style) -> Appearance; -    fn hovered(&self, style: Self::Style) -> Appearance { +    /// Produces the hovered [`Appearance`] of a button. +    fn hovered(&self, style: &Self::Style) -> Appearance {          let active = self.active(style);          Appearance { @@ -40,14 +49,16 @@ pub trait StyleSheet {          }      } -    fn pressed(&self, style: Self::Style) -> Appearance { +    /// Produces the pressed [`Appearance`] of a button. +    fn pressed(&self, style: &Self::Style) -> Appearance {          Appearance {              shadow_offset: Vector::default(),              ..self.active(style)          }      } -    fn disabled(&self, style: Self::Style) -> Appearance { +    /// Produces the disabled [`Appearance`] of a button. +    fn disabled(&self, style: &Self::Style) -> Appearance {          let active = self.active(style);          Appearance { diff --git a/style/src/checkbox.rs b/style/src/checkbox.rs index ba54b0a2..827b3225 100644 --- a/style/src/checkbox.rs +++ b/style/src/checkbox.rs @@ -1,22 +1,31 @@ -//! Show toggle controls using checkboxes. +//! Change the appearance of a checkbox.  use iced_core::{Background, Color};  /// The appearance of a checkbox.  #[derive(Debug, Clone, Copy)]  pub struct Appearance { +    /// The [`Background`] of the checkbox.      pub background: Background, +    /// The checkmark [`Color`] of the checkbox.      pub checkmark_color: Color, +    /// The border radius of the checkbox.      pub border_radius: f32, +    /// The border width of the checkbox.      pub border_width: f32, +    /// The border [`Color`] of the checkbox.      pub border_color: Color, +    /// The text [`Color`] of the checkbox.      pub text_color: Option<Color>,  }  /// A set of rules that dictate the style of a checkbox.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default; -    fn active(&self, style: Self::Style, is_checked: bool) -> Appearance; +    /// Produces the active [`Appearance`] of a checkbox. +    fn active(&self, style: &Self::Style, is_checked: bool) -> Appearance; -    fn hovered(&self, style: Self::Style, is_checked: bool) -> Appearance; +    /// Produces the hovered [`Appearance`] of a checkbox. +    fn hovered(&self, style: &Self::Style, is_checked: bool) -> Appearance;  } diff --git a/style/src/container.rs b/style/src/container.rs index 184310fa..560b2d5b 100644 --- a/style/src/container.rs +++ b/style/src/container.rs @@ -1,13 +1,18 @@ -//! Decorate content and apply alignment. +//! Change the appearance of a container.  use iced_core::{Background, Color};  /// The appearance of a container.  #[derive(Debug, Clone, Copy)]  pub struct Appearance { +    /// The text [`Color`] of the container.      pub text_color: Option<Color>, +    /// The [`Background`] of the container.      pub background: Option<Background>, +    /// The border radius of the container.      pub border_radius: f32, +    /// The border width of the container.      pub border_width: f32, +    /// The border [`Color`] of the container.      pub border_color: Color,  } @@ -25,8 +30,9 @@ impl std::default::Default for Appearance {  /// A set of rules that dictate the [`Appearance`] of a container.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default;      /// Produces the [`Appearance`] of a container. -    fn appearance(&self, style: Self::Style) -> Appearance; +    fn appearance(&self, style: &Self::Style) -> Appearance;  } diff --git a/style/src/lib.rs b/style/src/lib.rs index 0dde9582..59eb1eb8 100644 --- a/style/src/lib.rs +++ b/style/src/lib.rs @@ -15,6 +15,7 @@      clippy::new_without_default,      clippy::useless_conversion  )] +#![deny(missing_docs, unused_results)]  #![forbid(unsafe_code, rust_2018_idioms)]  #![allow(clippy::inherent_to_string, clippy::type_complexity)]  pub use iced_core::{Background, Color}; @@ -31,6 +32,7 @@ pub mod radio;  pub mod rule;  pub mod scrollable;  pub mod slider; +pub mod svg;  pub mod text;  pub mod text_input;  pub mod theme; diff --git a/style/src/menu.rs b/style/src/menu.rs index 6ef3e2a2..7d878748 100644 --- a/style/src/menu.rs +++ b/style/src/menu.rs @@ -1,19 +1,30 @@ +//! Change the appearance of menus.  use iced_core::{Background, Color};  /// The appearance of a menu.  #[derive(Debug, Clone, Copy)]  pub struct Appearance { +    /// The text [`Color`] of the menu.      pub text_color: Color, +    /// The [`Background`] of the menu.      pub background: Background, +    /// The border width of the menu.      pub border_width: f32, +    /// The border radius of the menu.      pub border_radius: f32, +    /// The border [`Color`] of the menu.      pub border_color: Color, +    /// The text [`Color`] of a selected option in the menu.      pub selected_text_color: Color, +    /// The background [`Color`] of a selected option in the menu.      pub selected_background: Background,  } +/// The style sheet of a menu.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default + Clone; -    fn appearance(&self, style: Self::Style) -> Appearance; +    /// Produces the [`Appearance`] of a menu. +    fn appearance(&self, style: &Self::Style) -> Appearance;  } diff --git a/style/src/pane_grid.rs b/style/src/pane_grid.rs index 5bae353f..fd8fc05f 100644 --- a/style/src/pane_grid.rs +++ b/style/src/pane_grid.rs @@ -1,16 +1,16 @@ -//! Let your users split regions of your application and organize layout -//! dynamically. +//! Change the appearance of a pane grid.  use iced_core::Color;  /// A set of rules that dictate the style of a container.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default;      /// The [`Line`] to draw when a split is picked. -    fn picked_split(&self, style: Self::Style) -> Option<Line>; +    fn picked_split(&self, style: &Self::Style) -> Option<Line>;      /// The [`Line`] to draw when a split is hovered. -    fn hovered_split(&self, style: Self::Style) -> Option<Line>; +    fn hovered_split(&self, style: &Self::Style) -> Option<Line>;  }  /// A line. diff --git a/style/src/pick_list.rs b/style/src/pick_list.rs index 2bafe932..8d93dff2 100644 --- a/style/src/pick_list.rs +++ b/style/src/pick_list.rs @@ -1,29 +1,33 @@ +//! Change the appearance of a pick list.  use iced_core::{Background, Color}; -use crate::container; -use crate::menu; -use crate::scrollable; -  /// The appearance of a pick list.  #[derive(Debug, Clone, Copy)]  pub struct Appearance { +    /// The text [`Color`] of the pick list.      pub text_color: Color, +    /// The placeholder [`Color`] of the pick list.      pub placeholder_color: Color, +    /// The [`Background`] of the pick list.      pub background: Background, +    /// The border radius of the pick list.      pub border_radius: f32, +    /// The border width of the pick list.      pub border_width: f32, +    /// The border color of the pick list.      pub border_color: Color, +    /// The size of the arrow icon of the pick list.      pub icon_size: f32,  }  /// A set of rules that dictate the style of a container. -pub trait StyleSheet: -    container::StyleSheet + menu::StyleSheet + scrollable::StyleSheet -{ -    type Style: Default + Copy + Into<<Self as menu::StyleSheet>::Style>; +pub trait StyleSheet { +    /// The supported style of the [`StyleSheet`]. +    type Style: Default + Clone; -    fn active(&self, style: <Self as StyleSheet>::Style) -> Appearance; +    /// Produces the active [`Appearance`] of a pick list. +    fn active(&self, style: &<Self as StyleSheet>::Style) -> Appearance; -    /// Produces the style of a container. -    fn hovered(&self, style: <Self as StyleSheet>::Style) -> Appearance; +    /// Produces the hovered [`Appearance`] of a pick list. +    fn hovered(&self, style: &<Self as StyleSheet>::Style) -> Appearance;  } diff --git a/style/src/progress_bar.rs b/style/src/progress_bar.rs index 768e7c9c..fb1819fc 100644 --- a/style/src/progress_bar.rs +++ b/style/src/progress_bar.rs @@ -1,17 +1,22 @@ -//! Provide progress feedback to your users. +//! Change the appearance of a progress bar.  use iced_core::Background;  /// The appearance of a progress bar.  #[derive(Debug, Clone, Copy)]  pub struct Appearance { +    /// The [`Background`] of the progress bar.      pub background: Background, +    /// The [`Background`] of the bar of the progress bar.      pub bar: Background, +    /// The border radius of the progress bar.      pub border_radius: f32,  }  /// A set of rules that dictate the style of a progress bar.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default; -    fn appearance(&self, style: Self::Style) -> Appearance; +    /// Produces the [`Appearance`] of the progress bar. +    fn appearance(&self, style: &Self::Style) -> Appearance;  } diff --git a/style/src/radio.rs b/style/src/radio.rs index d14ea33e..06c49029 100644 --- a/style/src/radio.rs +++ b/style/src/radio.rs @@ -1,21 +1,29 @@ -//! Create choices using radio buttons. +//! Change the appearance of radio buttons.  use iced_core::{Background, Color};  /// The appearance of a radio button.  #[derive(Debug, Clone, Copy)]  pub struct Appearance { +    /// The [`Background`] of the radio button.      pub background: Background, +    /// The [`Color`] of the dot of the radio button.      pub dot_color: Color, +    /// The border width of the radio button.      pub border_width: f32, +    /// The border [`Color`] of the radio button.      pub border_color: Color, +    /// The text [`Color`] of the radio button.      pub text_color: Option<Color>,  }  /// A set of rules that dictate the style of a radio button.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default; -    fn active(&self, style: Self::Style, is_selected: bool) -> Appearance; +    /// Produces the active [`Appearance`] of a radio button. +    fn active(&self, style: &Self::Style, is_selected: bool) -> Appearance; -    fn hovered(&self, style: Self::Style, is_selected: bool) -> Appearance; +    /// Produces the hovered [`Appearance`] of a radio button. +    fn hovered(&self, style: &Self::Style, is_selected: bool) -> Appearance;  } diff --git a/style/src/rule.rs b/style/src/rule.rs index af334912..b7380747 100644 --- a/style/src/rule.rs +++ b/style/src/rule.rs @@ -1,4 +1,4 @@ -//! Display a horizontal or vertical rule for dividing content. +//! Change the appearance of a rule.  use iced_core::Color;  /// The appearance of a rule. @@ -16,10 +16,11 @@ pub struct Appearance {  /// A set of rules that dictate the style of a rule.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default;      /// Produces the style of a rule. -    fn style(&self, style: Self::Style) -> Appearance; +    fn appearance(&self, style: &Self::Style) -> Appearance;  }  /// The fill mode of a rule. diff --git a/style/src/scrollable.rs b/style/src/scrollable.rs index 8da7409c..c6d7d537 100644 --- a/style/src/scrollable.rs +++ b/style/src/scrollable.rs @@ -1,37 +1,47 @@ -//! Navigate an endless amount of content with a scrollbar. +//! Change the appearance of a scrollable.  use iced_core::{Background, Color};  /// The appearance of a scrollable.  #[derive(Debug, Clone, Copy)]  pub struct Scrollbar { +    /// The [`Background`] of a scrollable.      pub background: Option<Background>, +    /// The border radius of a scrollable.      pub border_radius: f32, +    /// The border width of a scrollable.      pub border_width: f32, +    /// The border [`Color`] of a scrollable.      pub border_color: Color, +    /// The appearance of the [`Scroller`] of a scrollable.      pub scroller: Scroller,  }  /// The appearance of the scroller of a scrollable.  #[derive(Debug, Clone, Copy)]  pub struct Scroller { +    /// The [`Color`] of the scroller.      pub color: Color, +    /// The border radius of the scroller.      pub border_radius: f32, +    /// The border width of the scroller.      pub border_width: f32, +    /// The border [`Color`] of the scroller.      pub border_color: Color,  }  /// A set of rules that dictate the style of a scrollable.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default;      /// Produces the style of an active scrollbar. -    fn active(&self, style: Self::Style) -> Scrollbar; +    fn active(&self, style: &Self::Style) -> Scrollbar;      /// Produces the style of an hovered scrollbar. -    fn hovered(&self, style: Self::Style) -> Scrollbar; +    fn hovered(&self, style: &Self::Style) -> Scrollbar;      /// Produces the style of a scrollbar that is being dragged. -    fn dragging(&self, style: Self::Style) -> Scrollbar { +    fn dragging(&self, style: &Self::Style) -> Scrollbar {          self.hovered(style)      }  } diff --git a/style/src/slider.rs b/style/src/slider.rs index 0ff0449b..4b52fad3 100644 --- a/style/src/slider.rs +++ b/style/src/slider.rs @@ -1,39 +1,56 @@ -//! Display an interactive selector of a single value from a range of values. +//! Change the apperance of a slider.  use iced_core::Color;  /// The appearance of a slider.  #[derive(Debug, Clone, Copy)]  pub struct Appearance { +    /// The colors of the rail of the slider.      pub rail_colors: (Color, Color), +    /// The appearance of the [`Handle`] of the slider.      pub handle: Handle,  }  /// The appearance of the handle of a slider.  #[derive(Debug, Clone, Copy)]  pub struct Handle { +    /// The shape of the handle.      pub shape: HandleShape, +    /// The [`Color`] of the handle.      pub color: Color, +    /// The border width of the handle.      pub border_width: f32, +    /// The border [`Color`] of the handle.      pub border_color: Color,  }  /// The shape of the handle of a slider.  #[derive(Debug, Clone, Copy)]  pub enum HandleShape { -    Circle { radius: f32 }, -    Rectangle { width: u16, border_radius: f32 }, +    /// A circular handle. +    Circle { +        /// The radius of the circle. +        radius: f32, +    }, +    /// A rectangular shape. +    Rectangle { +        /// The width of the rectangle. +        width: u16, +        /// The border radius of the corners of the rectangle. +        border_radius: f32, +    },  }  /// A set of rules that dictate the style of a slider.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default;      /// Produces the style of an active slider. -    fn active(&self, style: Self::Style) -> Appearance; +    fn active(&self, style: &Self::Style) -> Appearance;      /// Produces the style of an hovered slider. -    fn hovered(&self, style: Self::Style) -> Appearance; +    fn hovered(&self, style: &Self::Style) -> Appearance;      /// Produces the style of a slider that is being dragged. -    fn dragging(&self, style: Self::Style) -> Appearance; +    fn dragging(&self, style: &Self::Style) -> Appearance;  } diff --git a/style/src/svg.rs b/style/src/svg.rs new file mode 100644 index 00000000..9378c1a7 --- /dev/null +++ b/style/src/svg.rs @@ -0,0 +1,23 @@ +//! Change the appearance of a svg. + +use iced_core::Color; + +/// The appearance of an SVG. +#[derive(Debug, Default, Clone, Copy)] +pub struct Appearance { +    /// The [`Color`] filter of an SVG. +    /// +    /// Useful for coloring a symbolic icon. +    /// +    /// `None` keeps the original color. +    pub color: Option<Color>, +} + +/// The stylesheet of a svg. +pub trait StyleSheet { +    /// The supported style of the [`StyleSheet`]. +    type Style: Default; + +    /// Produces the [`Appearance`] of the svg. +    fn appearance(&self, style: &Self::Style) -> Appearance; +} diff --git a/style/src/text.rs b/style/src/text.rs index 6e3aeef8..f31c2306 100644 --- a/style/src/text.rs +++ b/style/src/text.rs @@ -1,12 +1,20 @@ +//! Change the appearance of text.  use iced_core::Color; +/// The style sheet of some text.  pub trait StyleSheet { +    /// The supported style of the [`StyleSheet`].      type Style: Default + Copy; +    /// Produces the [`Appearance`] of some text.      fn appearance(&self, style: Self::Style) -> Appearance;  } +/// The apperance of some text.  #[derive(Debug, Clone, Copy, Default)]  pub struct Appearance { +    /// The [`Color`] of the text. +    /// +    /// The default, `None`, means using the inherited color.      pub color: Option<Color>,  } diff --git a/style/src/text_input.rs b/style/src/text_input.rs index af86617b..d97016dc 100644 --- a/style/src/text_input.rs +++ b/style/src/text_input.rs @@ -1,33 +1,41 @@ -//! Display fields that can be filled with text. +//! Change the appearance of a text input.  use iced_core::{Background, Color};  /// The appearance of a text input.  #[derive(Debug, Clone, Copy)]  pub struct Appearance { +    /// The [`Background`] of the text input.      pub background: Background, +    /// The border radius of the text input.      pub border_radius: f32, +    /// The border width of the text input.      pub border_width: f32, +    /// The border [`Color`] of the text input.      pub border_color: Color,  }  /// A set of rules that dictate the style of a text input.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default;      /// Produces the style of an active text input. -    fn active(&self, style: Self::Style) -> Appearance; +    fn active(&self, style: &Self::Style) -> Appearance;      /// Produces the style of a focused text input. -    fn focused(&self, style: Self::Style) -> Appearance; +    fn focused(&self, style: &Self::Style) -> Appearance; -    fn placeholder_color(&self, style: Self::Style) -> Color; +    /// Produces the [`Color`] of the placeholder of a text input. +    fn placeholder_color(&self, style: &Self::Style) -> Color; -    fn value_color(&self, style: Self::Style) -> Color; +    /// Produces the [`Color`] of the value of a text input. +    fn value_color(&self, style: &Self::Style) -> Color; -    fn selection_color(&self, style: Self::Style) -> Color; +    /// Produces the [`Color`] of the selection of a text input. +    fn selection_color(&self, style: &Self::Style) -> Color;      /// Produces the style of an hovered text input. -    fn hovered(&self, style: Self::Style) -> Appearance { +    fn hovered(&self, style: &Self::Style) -> Appearance {          self.focused(style)      }  } diff --git a/style/src/theme.rs b/style/src/theme.rs index ea538c3a..271d9a29 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -1,5 +1,7 @@ +//! Use the built-in theme and styles.  pub mod palette; +use self::palette::Extended;  pub use self::palette::Palette;  use crate::application; @@ -14,56 +16,83 @@ use crate::radio;  use crate::rule;  use crate::scrollable;  use crate::slider; +use crate::svg;  use crate::text;  use crate::text_input;  use crate::toggler; -use iced_core::{Background, Color}; +use iced_core::{Background, Color, Vector}; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +use std::rc::Rc; + +/// A built-in theme. +#[derive(Debug, Clone, PartialEq, Default)]  pub enum Theme { +    /// The built-in light variant. +    #[default]      Light, +    /// The built-in dark variant.      Dark, +    /// A [`Theme`] that uses a [`Custom`] palette. +    Custom(Box<Custom>),  }  impl Theme { -    pub fn palette(self) -> Palette { +    /// Creates a new custom [`Theme`] from the given [`Palette`]. +    pub fn custom(palette: Palette) -> Self { +        Self::Custom(Box::new(Custom::new(palette))) +    } + +    /// Returns the [`Palette`] of the [`Theme`]. +    pub fn palette(&self) -> Palette {          match self {              Self::Light => Palette::LIGHT,              Self::Dark => Palette::DARK, +            Self::Custom(custom) => custom.palette,          }      } +    /// Returns the [`palette::Extended`] of the [`Theme`].      pub fn extended_palette(&self) -> &palette::Extended {          match self {              Self::Light => &palette::EXTENDED_LIGHT,              Self::Dark => &palette::EXTENDED_DARK, +            Self::Custom(custom) => &custom.extended,          }      }  } -impl Default for Theme { -    fn default() -> Self { -        Self::Light +/// A [`Theme`] with a customized [`Palette`]. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Custom { +    palette: Palette, +    extended: Extended, +} + +impl Custom { +    /// Creates a [`Custom`] theme from the given [`Palette`]. +    pub fn new(palette: Palette) -> Self { +        Self { +            palette, +            extended: Extended::generate(palette), +        }      }  } -#[derive(Debug, Clone, Copy)] +/// The style of an application. +#[derive(Default)]  pub enum Application { +    /// The default style. +    #[default]      Default, -    Custom(fn(Theme) -> application::Appearance), -} - -impl Default for Application { -    fn default() -> Self { -        Self::Default -    } +    /// A custom style. +    Custom(Box<dyn application::StyleSheet<Style = Theme>>),  }  impl application::StyleSheet for Theme {      type Style = Application; -    fn appearance(&self, style: Self::Style) -> application::Appearance { +    fn appearance(&self, style: &Self::Style) -> application::Appearance {          let palette = self.extended_palette();          match style { @@ -71,33 +100,49 @@ impl application::StyleSheet for Theme {                  background_color: palette.background.base.color,                  text_color: palette.background.base.text,              }, -            Application::Custom(f) => f(*self), +            Application::Custom(custom) => custom.appearance(self),          }      }  } -/* - * Button - */ -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +impl application::StyleSheet for fn(&Theme) -> application::Appearance { +    type Style = Theme; + +    fn appearance(&self, style: &Self::Style) -> application::Appearance { +        (self)(style) +    } +} + +impl From<fn(&Theme) -> application::Appearance> for Application { +    fn from(f: fn(&Theme) -> application::Appearance) -> Self { +        Self::Custom(Box::new(f)) +    } +} + +/// The style of a button. +#[derive(Default)]  pub enum Button { +    /// The primary style. +    #[default]      Primary, +    /// The secondary style.      Secondary, +    /// The positive style.      Positive, +    /// The destructive style.      Destructive, +    /// The text style. +    /// +    /// Useful for links!      Text, -} - -impl Default for Button { -    fn default() -> Self { -        Self::Primary -    } +    /// A custom style. +    Custom(Box<dyn button::StyleSheet<Style = Theme>>),  }  impl button::StyleSheet for Theme {      type Style = Button; -    fn active(&self, style: Self::Style) -> button::Appearance { +    fn active(&self, style: &Self::Style) -> button::Appearance {          let palette = self.extended_palette();          let appearance = button::Appearance { @@ -120,19 +165,25 @@ impl button::StyleSheet for Theme {                  text_color: palette.background.base.text,                  ..appearance              }, +            Button::Custom(custom) => custom.active(self),          }      } -    fn hovered(&self, style: Self::Style) -> button::Appearance { -        let active = self.active(style); +    fn hovered(&self, style: &Self::Style) -> button::Appearance {          let palette = self.extended_palette(); +        if let Button::Custom(custom) = style { +            return custom.hovered(self); +        } + +        let active = self.active(style); +          let background = match style {              Button::Primary => Some(palette.primary.base.color),              Button::Secondary => Some(palette.background.strong.color),              Button::Positive => Some(palette.success.strong.color),              Button::Destructive => Some(palette.danger.strong.color), -            Button::Text => None, +            Button::Text | Button::Custom(_) => None,          };          button::Appearance { @@ -140,23 +191,56 @@ impl button::StyleSheet for Theme {              ..active          }      } + +    fn pressed(&self, style: &Self::Style) -> button::Appearance { +        if let Button::Custom(custom) = style { +            return custom.pressed(self); +        } + +        button::Appearance { +            shadow_offset: Vector::default(), +            ..self.active(style) +        } +    } + +    fn disabled(&self, style: &Self::Style) -> button::Appearance { +        if let Button::Custom(custom) = style { +            return custom.disabled(self); +        } + +        let active = self.active(style); + +        button::Appearance { +            shadow_offset: Vector::default(), +            background: active.background.map(|background| match background { +                Background::Color(color) => Background::Color(Color { +                    a: color.a * 0.5, +                    ..color +                }), +            }), +            text_color: Color { +                a: active.text_color.a * 0.5, +                ..active.text_color +            }, +            ..active +        } +    }  } -/* - * Checkbox - */ -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +/// The style of a checkbox. +#[derive(Default)]  pub enum Checkbox { +    /// The primary style. +    #[default]      Primary, +    /// The secondary style.      Secondary, +    /// The success style.      Success, +    /// The danger style.      Danger, -} - -impl Default for Checkbox { -    fn default() -> Self { -        Self::Primary -    } +    /// A custom style. +    Custom(Box<dyn checkbox::StyleSheet<Style = Theme>>),  }  impl checkbox::StyleSheet for Theme { @@ -164,7 +248,7 @@ impl checkbox::StyleSheet for Theme {      fn active(          &self, -        style: Self::Style, +        style: &Self::Style,          is_checked: bool,      ) -> checkbox::Appearance {          let palette = self.extended_palette(); @@ -194,12 +278,13 @@ impl checkbox::StyleSheet for Theme {                  palette.danger.base,                  is_checked,              ), +            Checkbox::Custom(custom) => custom.active(self, is_checked),          }      }      fn hovered(          &self, -        style: Self::Style, +        style: &Self::Style,          is_checked: bool,      ) -> checkbox::Appearance {          let palette = self.extended_palette(); @@ -229,6 +314,7 @@ impl checkbox::StyleSheet for Theme {                  palette.danger.base,                  is_checked,              ), +            Checkbox::Custom(custom) => custom.hovered(self, is_checked),          }      }  } @@ -253,32 +339,28 @@ fn checkbox_appearance(      }  } -/* - * Container - */ -#[derive(Clone, Copy)] +/// The style of a container. +#[derive(Default)]  pub enum Container { +    /// No style. +    #[default]      Transparent, +    /// A simple box.      Box, -    Custom(fn(&Theme) -> container::Appearance), -} - -impl Default for Container { -    fn default() -> Self { -        Self::Transparent -    } +    /// A custom style. +    Custom(Box<dyn container::StyleSheet<Style = Theme>>),  }  impl From<fn(&Theme) -> container::Appearance> for Container {      fn from(f: fn(&Theme) -> container::Appearance) -> Self { -        Self::Custom(f) +        Self::Custom(Box::new(f))      }  }  impl container::StyleSheet for Theme {      type Style = Container; -    fn appearance(&self, style: Self::Style) -> container::Appearance { +    fn appearance(&self, style: &Self::Style) -> container::Appearance {          match style {              Container::Transparent => Default::default(),              Container::Box => { @@ -292,257 +374,389 @@ impl container::StyleSheet for Theme {                      border_color: Color::TRANSPARENT,                  }              } -            Container::Custom(f) => f(self), +            Container::Custom(custom) => custom.appearance(self),          }      }  } -/* - * Slider - */ -impl slider::StyleSheet for Theme { -    type Style = (); +impl container::StyleSheet for fn(&Theme) -> container::Appearance { +    type Style = Theme; -    fn active(&self, _style: Self::Style) -> slider::Appearance { -        let palette = self.extended_palette(); +    fn appearance(&self, style: &Self::Style) -> container::Appearance { +        (self)(style) +    } +} -        let handle = slider::Handle { -            shape: slider::HandleShape::Rectangle { -                width: 8, -                border_radius: 4.0, -            }, -            color: Color::WHITE, -            border_color: Color::WHITE, -            border_width: 1.0, -        }; +/// The style of a slider. +#[derive(Default)] +pub enum Slider { +    /// The default style. +    #[default] +    Default, +    /// A custom style. +    Custom(Box<dyn slider::StyleSheet<Style = Theme>>), +} -        slider::Appearance { -            rail_colors: (palette.primary.base.color, Color::TRANSPARENT), -            handle: slider::Handle { -                color: palette.background.base.color, -                border_color: palette.primary.base.color, -                ..handle -            }, +impl slider::StyleSheet for Theme { +    type Style = Slider; + +    fn active(&self, style: &Self::Style) -> slider::Appearance { +        match style { +            Slider::Default => { +                let palette = self.extended_palette(); + +                let handle = slider::Handle { +                    shape: slider::HandleShape::Rectangle { +                        width: 8, +                        border_radius: 4.0, +                    }, +                    color: Color::WHITE, +                    border_color: Color::WHITE, +                    border_width: 1.0, +                }; + +                slider::Appearance { +                    rail_colors: ( +                        palette.primary.base.color, +                        Color::TRANSPARENT, +                    ), +                    handle: slider::Handle { +                        color: palette.background.base.color, +                        border_color: palette.primary.base.color, +                        ..handle +                    }, +                } +            } +            Slider::Custom(custom) => custom.active(self),          }      } -    fn hovered(&self, style: Self::Style) -> slider::Appearance { -        let active = self.active(style); -        let palette = self.extended_palette(); +    fn hovered(&self, style: &Self::Style) -> slider::Appearance { +        match style { +            Slider::Default => { +                let active = self.active(style); +                let palette = self.extended_palette(); -        slider::Appearance { -            handle: slider::Handle { -                color: palette.primary.weak.color, -                ..active.handle -            }, -            ..active +                slider::Appearance { +                    handle: slider::Handle { +                        color: palette.primary.weak.color, +                        ..active.handle +                    }, +                    ..active +                } +            } +            Slider::Custom(custom) => custom.hovered(self),          }      } -    fn dragging(&self, style: Self::Style) -> slider::Appearance { -        let active = self.active(style); -        let palette = self.extended_palette(); +    fn dragging(&self, style: &Self::Style) -> slider::Appearance { +        match style { +            Slider::Default => { +                let active = self.active(style); +                let palette = self.extended_palette(); -        slider::Appearance { -            handle: slider::Handle { -                color: palette.primary.base.color, -                ..active.handle -            }, -            ..active +                slider::Appearance { +                    handle: slider::Handle { +                        color: palette.primary.base.color, +                        ..active.handle +                    }, +                    ..active +                } +            } +            Slider::Custom(custom) => custom.dragging(self),          }      }  } -/* - * Menu - */ +/// The style of a menu. +#[derive(Clone, Default)] +pub enum Menu { +    /// The default style. +    #[default] +    Default, +    /// A custom style. +    Custom(Rc<dyn menu::StyleSheet<Style = Theme>>), +} +  impl menu::StyleSheet for Theme { -    type Style = (); +    type Style = Menu; -    fn appearance(&self, _style: Self::Style) -> menu::Appearance { -        let palette = self.extended_palette(); +    fn appearance(&self, style: &Self::Style) -> menu::Appearance { +        match style { +            Menu::Default => { +                let palette = self.extended_palette(); -        menu::Appearance { -            text_color: palette.background.weak.text, -            background: palette.background.weak.color.into(), -            border_width: 1.0, -            border_radius: 0.0, -            border_color: palette.background.strong.color, -            selected_text_color: palette.primary.strong.text, -            selected_background: palette.primary.strong.color.into(), +                menu::Appearance { +                    text_color: palette.background.weak.text, +                    background: palette.background.weak.color.into(), +                    border_width: 1.0, +                    border_radius: 0.0, +                    border_color: palette.background.strong.color, +                    selected_text_color: palette.primary.strong.text, +                    selected_background: palette.primary.strong.color.into(), +                } +            } +            Menu::Custom(custom) => custom.appearance(self),          }      }  } -/* - * Pick List - */ +impl From<PickList> for Menu { +    fn from(pick_list: PickList) -> Self { +        match pick_list { +            PickList::Default => Self::Default, +            PickList::Custom(_, menu) => Self::Custom(menu), +        } +    } +} + +/// The style of a pick list. +#[derive(Clone, Default)] +pub enum PickList { +    /// The default style. +    #[default] +    Default, +    /// A custom style. +    Custom( +        Rc<dyn pick_list::StyleSheet<Style = Theme>>, +        Rc<dyn menu::StyleSheet<Style = Theme>>, +    ), +} +  impl pick_list::StyleSheet for Theme { -    type Style = (); +    type Style = PickList; -    fn active(&self, _style: ()) -> pick_list::Appearance { -        let palette = self.extended_palette(); +    fn active(&self, style: &Self::Style) -> pick_list::Appearance { +        match style { +            PickList::Default => { +                let palette = self.extended_palette(); -        pick_list::Appearance { -            text_color: palette.background.weak.text, -            background: palette.background.weak.color.into(), -            placeholder_color: palette.background.strong.color, -            border_radius: 2.0, -            border_width: 1.0, -            border_color: palette.background.strong.color, -            icon_size: 0.7, +                pick_list::Appearance { +                    text_color: palette.background.weak.text, +                    background: palette.background.weak.color.into(), +                    placeholder_color: palette.background.strong.color, +                    border_radius: 2.0, +                    border_width: 1.0, +                    border_color: palette.background.strong.color, +                    icon_size: 0.7, +                } +            } +            PickList::Custom(custom, _) => custom.active(self),          }      } -    fn hovered(&self, _style: ()) -> pick_list::Appearance { -        let palette = self.extended_palette(); +    fn hovered(&self, style: &Self::Style) -> pick_list::Appearance { +        match style { +            PickList::Default => { +                let palette = self.extended_palette(); -        pick_list::Appearance { -            text_color: palette.background.weak.text, -            background: palette.background.weak.color.into(), -            placeholder_color: palette.background.strong.color, -            border_radius: 2.0, -            border_width: 1.0, -            border_color: palette.primary.strong.color, -            icon_size: 0.7, +                pick_list::Appearance { +                    text_color: palette.background.weak.text, +                    background: palette.background.weak.color.into(), +                    placeholder_color: palette.background.strong.color, +                    border_radius: 2.0, +                    border_width: 1.0, +                    border_color: palette.primary.strong.color, +                    icon_size: 0.7, +                } +            } +            PickList::Custom(custom, _) => custom.hovered(self),          }      }  } -/* - * Radio - */ +/// The style of a radio button. +#[derive(Default)] +pub enum Radio { +    /// The default style. +    #[default] +    Default, +    /// A custom style. +    Custom(Box<dyn radio::StyleSheet<Style = Theme>>), +} +  impl radio::StyleSheet for Theme { -    type Style = (); +    type Style = Radio;      fn active(          &self, -        _style: Self::Style, -        _is_selected: bool, +        style: &Self::Style, +        is_selected: bool,      ) -> radio::Appearance { -        let palette = self.extended_palette(); +        match style { +            Radio::Default => { +                let palette = self.extended_palette(); -        radio::Appearance { -            background: Color::TRANSPARENT.into(), -            dot_color: palette.primary.strong.color, -            border_width: 1.0, -            border_color: palette.primary.strong.color, -            text_color: None, +                radio::Appearance { +                    background: Color::TRANSPARENT.into(), +                    dot_color: palette.primary.strong.color, +                    border_width: 1.0, +                    border_color: palette.primary.strong.color, +                    text_color: None, +                } +            } +            Radio::Custom(custom) => custom.active(self, is_selected),          }      }      fn hovered(          &self, -        style: Self::Style, +        style: &Self::Style,          is_selected: bool,      ) -> radio::Appearance { -        let active = self.active(style, is_selected); -        let palette = self.extended_palette(); +        match style { +            Radio::Default => { +                let active = self.active(style, is_selected); +                let palette = self.extended_palette(); -        radio::Appearance { -            dot_color: palette.primary.strong.color, -            background: palette.primary.weak.color.into(), -            ..active +                radio::Appearance { +                    dot_color: palette.primary.strong.color, +                    background: palette.primary.weak.color.into(), +                    ..active +                } +            } +            Radio::Custom(custom) => custom.hovered(self, is_selected),          }      }  } -/* - * Toggler - */ +/// The style of a toggler. +#[derive(Default)] +pub enum Toggler { +    /// The default style. +    #[default] +    Default, +    /// A custom style. +    Custom(Box<dyn toggler::StyleSheet<Style = Theme>>), +} +  impl toggler::StyleSheet for Theme { -    type Style = (); +    type Style = Toggler;      fn active(          &self, -        _style: Self::Style, +        style: &Self::Style,          is_active: bool,      ) -> toggler::Appearance { -        let palette = self.extended_palette(); +        match style { +            Toggler::Default => { +                let palette = self.extended_palette(); -        toggler::Appearance { -            background: if is_active { -                palette.primary.strong.color -            } else { -                palette.background.strong.color -            }, -            background_border: None, -            foreground: if is_active { -                palette.primary.strong.text -            } else { -                palette.background.base.color -            }, -            foreground_border: None, +                toggler::Appearance { +                    background: if is_active { +                        palette.primary.strong.color +                    } else { +                        palette.background.strong.color +                    }, +                    background_border: None, +                    foreground: if is_active { +                        palette.primary.strong.text +                    } else { +                        palette.background.base.color +                    }, +                    foreground_border: None, +                } +            } +            Toggler::Custom(custom) => custom.active(self, is_active),          }      }      fn hovered(          &self, -        style: Self::Style, +        style: &Self::Style,          is_active: bool,      ) -> toggler::Appearance { -        let palette = self.extended_palette(); +        match style { +            Toggler::Default => { +                let palette = self.extended_palette(); -        toggler::Appearance { -            foreground: if is_active { -                Color { -                    a: 0.5, -                    ..palette.primary.strong.text +                toggler::Appearance { +                    foreground: if is_active { +                        Color { +                            a: 0.5, +                            ..palette.primary.strong.text +                        } +                    } else { +                        palette.background.weak.color +                    }, +                    ..self.active(style, is_active)                  } -            } else { -                palette.background.weak.color -            }, -            ..self.active(style, is_active) +            } +            Toggler::Custom(custom) => custom.hovered(self, is_active),          }      }  } -/* - * Pane Grid - */ +/// The style of a pane grid. +#[derive(Default)] +pub enum PaneGrid { +    /// The default style. +    #[default] +    Default, +    /// A custom style. +    Custom(Box<dyn pane_grid::StyleSheet<Style = Theme>>), +} +  impl pane_grid::StyleSheet for Theme { -    type Style = (); +    type Style = PaneGrid; -    fn picked_split(&self, _style: Self::Style) -> Option<pane_grid::Line> { -        let palette = self.extended_palette(); +    fn picked_split(&self, style: &Self::Style) -> Option<pane_grid::Line> { +        match style { +            PaneGrid::Default => { +                let palette = self.extended_palette(); -        Some(pane_grid::Line { -            color: palette.primary.strong.color, -            width: 2.0, -        }) +                Some(pane_grid::Line { +                    color: palette.primary.strong.color, +                    width: 2.0, +                }) +            } +            PaneGrid::Custom(custom) => custom.picked_split(self), +        }      } -    fn hovered_split(&self, _style: Self::Style) -> Option<pane_grid::Line> { -        let palette = self.extended_palette(); +    fn hovered_split(&self, style: &Self::Style) -> Option<pane_grid::Line> { +        match style { +            PaneGrid::Default => { +                let palette = self.extended_palette(); -        Some(pane_grid::Line { -            color: palette.primary.base.color, -            width: 2.0, -        }) +                Some(pane_grid::Line { +                    color: palette.primary.base.color, +                    width: 2.0, +                }) +            } +            PaneGrid::Custom(custom) => custom.hovered_split(self), +        }      }  } -/* - * Progress Bar - */ -#[derive(Clone, Copy)] +/// The style of a progress bar. +#[derive(Default)]  pub enum ProgressBar { +    /// The primary style. +    #[default]      Primary, +    /// The success style.      Success, +    /// The danger style.      Danger, -    Custom(fn(&Theme) -> progress_bar::Appearance), +    /// A custom style. +    Custom(Box<dyn progress_bar::StyleSheet<Style = Theme>>),  } -impl Default for ProgressBar { -    fn default() -> Self { -        Self::Primary +impl From<fn(&Theme) -> progress_bar::Appearance> for ProgressBar { +    fn from(f: fn(&Theme) -> progress_bar::Appearance) -> Self { +        Self::Custom(Box::new(f))      }  }  impl progress_bar::StyleSheet for Theme {      type Style = ProgressBar; -    fn appearance(&self, style: Self::Style) -> progress_bar::Appearance { +    fn appearance(&self, style: &Self::Style) -> progress_bar::Appearance { +        if let ProgressBar::Custom(custom) = style { +            return custom.appearance(self); +        } +          let palette = self.extended_palette();          let from_palette = |bar: Color| progress_bar::Appearance { @@ -555,30 +769,39 @@ impl progress_bar::StyleSheet for Theme {              ProgressBar::Primary => from_palette(palette.primary.base.color),              ProgressBar::Success => from_palette(palette.success.base.color),              ProgressBar::Danger => from_palette(palette.danger.base.color), -            ProgressBar::Custom(f) => f(self), +            ProgressBar::Custom(custom) => custom.appearance(self),          }      }  } -/* - * Rule - */ -#[derive(Clone, Copy)] +impl progress_bar::StyleSheet for fn(&Theme) -> progress_bar::Appearance { +    type Style = Theme; + +    fn appearance(&self, style: &Self::Style) -> progress_bar::Appearance { +        (self)(style) +    } +} + +/// The style of a rule. +#[derive(Default)]  pub enum Rule { +    /// The default style. +    #[default]      Default, -    Custom(fn(&Theme) -> rule::Appearance), +    /// A custom style. +    Custom(Box<dyn rule::StyleSheet<Style = Theme>>),  } -impl Default for Rule { -    fn default() -> Self { -        Self::Default +impl From<fn(&Theme) -> rule::Appearance> for Rule { +    fn from(f: fn(&Theme) -> rule::Appearance) -> Self { +        Self::Custom(Box::new(f))      }  }  impl rule::StyleSheet for Theme {      type Style = Rule; -    fn style(&self, style: Self::Style) -> rule::Appearance { +    fn appearance(&self, style: &Self::Style) -> rule::Appearance {          let palette = self.extended_palette();          match style { @@ -588,66 +811,130 @@ impl rule::StyleSheet for Theme {                  radius: 0.0,                  fill_mode: rule::FillMode::Full,              }, -            Rule::Custom(f) => f(self), +            Rule::Custom(custom) => custom.appearance(self),          }      }  } -/* - * Scrollable +impl rule::StyleSheet for fn(&Theme) -> rule::Appearance { +    type Style = Theme; + +    fn appearance(&self, style: &Self::Style) -> rule::Appearance { +        (self)(style) +    } +} + +/** + * Svg   */ +#[derive(Default)] +pub enum Svg { +    /// No filtering to the rendered SVG. +    #[default] +    Default, +    /// A custom style. +    Custom(Box<dyn svg::StyleSheet<Style = Theme>>), +} + +impl Svg { +    /// Creates a custom [`Svg`] style. +    pub fn custom_fn(f: fn(&Theme) -> svg::Appearance) -> Self { +        Self::Custom(Box::new(f)) +    } +} + +impl svg::StyleSheet for Theme { +    type Style = Svg; + +    fn appearance(&self, style: &Self::Style) -> svg::Appearance { +        match style { +            Svg::Default => Default::default(), +            Svg::Custom(custom) => custom.appearance(self), +        } +    } +} + +impl svg::StyleSheet for fn(&Theme) -> svg::Appearance { +    type Style = Theme; + +    fn appearance(&self, style: &Self::Style) -> svg::Appearance { +        (self)(style) +    } +} + +/// The style of a scrollable. +#[derive(Default)] +pub enum Scrollable { +    /// The default style. +    #[default] +    Default, +    /// A custom style. +    Custom(Box<dyn scrollable::StyleSheet<Style = Theme>>), +} +  impl scrollable::StyleSheet for Theme { -    type Style = (); +    type Style = Scrollable; -    fn active(&self, _style: Self::Style) -> scrollable::Scrollbar { -        let palette = self.extended_palette(); +    fn active(&self, style: &Self::Style) -> scrollable::Scrollbar { +        match style { +            Scrollable::Default => { +                let palette = self.extended_palette(); -        scrollable::Scrollbar { -            background: palette.background.weak.color.into(), -            border_radius: 2.0, -            border_width: 0.0, -            border_color: Color::TRANSPARENT, -            scroller: scrollable::Scroller { -                color: palette.background.strong.color, -                border_radius: 2.0, -                border_width: 0.0, -                border_color: Color::TRANSPARENT, -            }, +                scrollable::Scrollbar { +                    background: palette.background.weak.color.into(), +                    border_radius: 2.0, +                    border_width: 0.0, +                    border_color: Color::TRANSPARENT, +                    scroller: scrollable::Scroller { +                        color: palette.background.strong.color, +                        border_radius: 2.0, +                        border_width: 0.0, +                        border_color: Color::TRANSPARENT, +                    }, +                } +            } +            Scrollable::Custom(custom) => custom.active(self),          }      } -    fn hovered(&self, _style: Self::Style) -> scrollable::Scrollbar { -        let palette = self.extended_palette(); +    fn hovered(&self, style: &Self::Style) -> scrollable::Scrollbar { +        match style { +            Scrollable::Default => { +                let palette = self.extended_palette(); -        scrollable::Scrollbar { -            background: palette.background.weak.color.into(), -            border_radius: 2.0, -            border_width: 0.0, -            border_color: Color::TRANSPARENT, -            scroller: scrollable::Scroller { -                color: palette.primary.strong.color, -                border_radius: 2.0, -                border_width: 0.0, -                border_color: Color::TRANSPARENT, -            }, +                scrollable::Scrollbar { +                    background: palette.background.weak.color.into(), +                    border_radius: 2.0, +                    border_width: 0.0, +                    border_color: Color::TRANSPARENT, +                    scroller: scrollable::Scroller { +                        color: palette.primary.strong.color, +                        border_radius: 2.0, +                        border_width: 0.0, +                        border_color: Color::TRANSPARENT, +                    }, +                } +            } +            Scrollable::Custom(custom) => custom.hovered(self), +        } +    } + +    fn dragging(&self, style: &Self::Style) -> scrollable::Scrollbar { +        match style { +            Scrollable::Default => self.hovered(style), +            Scrollable::Custom(custom) => custom.dragging(self),          }      }  } -/* - * Text - */ -#[derive(Clone, Copy)] +/// The style of text. +#[derive(Clone, Copy, Default)]  pub enum Text { +    /// The default style. +    #[default]      Default, +    /// Colored text.      Color(Color), -    Custom(fn(&Theme) -> text::Appearance), -} - -impl Default for Text { -    fn default() -> Self { -        Self::Default -    }  }  impl From<Color> for Text { @@ -663,18 +950,28 @@ impl text::StyleSheet for Theme {          match style {              Text::Default => Default::default(),              Text::Color(c) => text::Appearance { color: Some(c) }, -            Text::Custom(f) => f(self),          }      }  } -/* - * Text Input - */ +/// The style of a text input. +#[derive(Default)] +pub enum TextInput { +    /// The default style. +    #[default] +    Default, +    /// A custom style. +    Custom(Box<dyn text_input::StyleSheet<Style = Theme>>), +} +  impl text_input::StyleSheet for Theme { -    type Style = (); +    type Style = TextInput; + +    fn active(&self, style: &Self::Style) -> text_input::Appearance { +        if let TextInput::Custom(custom) = style { +            return custom.active(self); +        } -    fn active(&self, _style: Self::Style) -> text_input::Appearance {          let palette = self.extended_palette();          text_input::Appearance { @@ -685,7 +982,11 @@ impl text_input::StyleSheet for Theme {          }      } -    fn hovered(&self, _style: Self::Style) -> text_input::Appearance { +    fn hovered(&self, style: &Self::Style) -> text_input::Appearance { +        if let TextInput::Custom(custom) = style { +            return custom.hovered(self); +        } +          let palette = self.extended_palette();          text_input::Appearance { @@ -696,7 +997,11 @@ impl text_input::StyleSheet for Theme {          }      } -    fn focused(&self, _style: Self::Style) -> text_input::Appearance { +    fn focused(&self, style: &Self::Style) -> text_input::Appearance { +        if let TextInput::Custom(custom) = style { +            return custom.focused(self); +        } +          let palette = self.extended_palette();          text_input::Appearance { @@ -707,19 +1012,31 @@ impl text_input::StyleSheet for Theme {          }      } -    fn placeholder_color(&self, _style: Self::Style) -> Color { +    fn placeholder_color(&self, style: &Self::Style) -> Color { +        if let TextInput::Custom(custom) = style { +            return custom.placeholder_color(self); +        } +          let palette = self.extended_palette();          palette.background.strong.color      } -    fn value_color(&self, _style: Self::Style) -> Color { +    fn value_color(&self, style: &Self::Style) -> Color { +        if let TextInput::Custom(custom) = style { +            return custom.value_color(self); +        } +          let palette = self.extended_palette();          palette.background.base.text      } -    fn selection_color(&self, _style: Self::Style) -> Color { +    fn selection_color(&self, style: &Self::Style) -> Color { +        if let TextInput::Custom(custom) = style { +            return custom.selection_color(self); +        } +          let palette = self.extended_palette();          palette.primary.weak.color diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs index 4fb5e4c8..0f15494b 100644 --- a/style/src/theme/palette.rs +++ b/style/src/theme/palette.rs @@ -1,18 +1,26 @@ +//! Define the colors of a theme.  use iced_core::Color;  use once_cell::sync::Lazy;  use palette::{FromColor, Hsl, Mix, RelativeContrast, Srgb}; +/// A color palette.  #[derive(Debug, Clone, Copy, PartialEq)]  pub struct Palette { +    /// The background [`Color`] of the [`Palette`].      pub background: Color, +    /// The text [`Color`] of the [`Palette`].      pub text: Color, +    /// The primary [`Color`] of the [`Palette`].      pub primary: Color, +    /// The success [`Color`] of the [`Palette`].      pub success: Color, +    /// The danger [`Color`] of the [`Palette`].      pub danger: Color,  }  impl Palette { +    /// The built-in light variant of a [`Palette`].      pub const LIGHT: Self = Self {          background: Color::WHITE,          text: Color::BLACK, @@ -33,6 +41,7 @@ impl Palette {          ),      }; +    /// The built-in dark variant of a [`Palette`].      pub const DARK: Self = Self {          background: Color::from_rgb(              0x20 as f32 / 255.0, @@ -58,20 +67,31 @@ impl Palette {      };  } +/// An extended set of colors generated from a [`Palette`]. +#[derive(Debug, Clone, Copy, PartialEq)]  pub struct Extended { +    /// The set of background colors.      pub background: Background, +    /// The set of primary colors.      pub primary: Primary, +    /// The set of secondary colors.      pub secondary: Secondary, +    /// The set of success colors.      pub success: Success, +    /// The set of danger colors.      pub danger: Danger,  } +/// The built-in light variant of an [`Extended`] palette.  pub static EXTENDED_LIGHT: Lazy<Extended> =      Lazy::new(|| Extended::generate(Palette::LIGHT)); + +/// The built-in dark variant of an [`Extended`] palette.  pub static EXTENDED_DARK: Lazy<Extended> =      Lazy::new(|| Extended::generate(Palette::DARK));  impl Extended { +    /// Generates an [`Extended`] palette from a simple [`Palette`].      pub fn generate(palette: Palette) -> Self {          Self {              background: Background::new(palette.background, palette.text), @@ -95,13 +115,22 @@ impl Extended {      }  } -#[derive(Debug, Clone, Copy)] +/// A pair of background and text colors. +#[derive(Debug, Clone, Copy, PartialEq)]  pub struct Pair { +    /// The background color.      pub color: Color, + +    /// The text color. +    /// +    /// It's guaranteed to be readable on top of the background [`color`]. +    /// +    /// [`color`]: Self::color      pub text: Color,  }  impl Pair { +    /// Creates a new [`Pair`] from a background [`Color`] and some text [`Color`].      pub fn new(color: Color, text: Color) -> Self {          Self {              color, @@ -110,13 +139,19 @@ impl Pair {      }  } +/// A set of background colors. +#[derive(Debug, Clone, Copy, PartialEq)]  pub struct Background { +    /// The base background color.      pub base: Pair, +    /// A weaker version of the base background color.      pub weak: Pair, +    /// A stronger version of the base background color.      pub strong: Pair,  }  impl Background { +    /// Generates a set of [`Background`] colors from the base and text colors.      pub fn new(base: Color, text: Color) -> Self {          let weak = mix(base, text, 0.15);          let strong = mix(base, text, 0.40); @@ -129,13 +164,19 @@ impl Background {      }  } +/// A set of primary colors. +#[derive(Debug, Clone, Copy, PartialEq)]  pub struct Primary { +    /// The base primary color.      pub base: Pair, +    /// A weaker version of the base primary color.      pub weak: Pair, +    /// A stronger version of the base primary color.      pub strong: Pair,  }  impl Primary { +    /// Generates a set of [`Primary`] colors from the base, background, and text colors.      pub fn generate(base: Color, background: Color, text: Color) -> Self {          let weak = mix(base, background, 0.4);          let strong = deviate(base, 0.1); @@ -148,13 +189,19 @@ impl Primary {      }  } +/// A set of secondary colors. +#[derive(Debug, Clone, Copy, PartialEq)]  pub struct Secondary { +    /// The base secondary color.      pub base: Pair, +    /// A weaker version of the base secondary color.      pub weak: Pair, +    /// A stronger version of the base secondary color.      pub strong: Pair,  }  impl Secondary { +    /// Generates a set of [`Secondary`] colors from the base and text colors.      pub fn generate(base: Color, text: Color) -> Self {          let base = mix(base, text, 0.2);          let weak = mix(base, text, 0.1); @@ -168,13 +215,19 @@ impl Secondary {      }  } +/// A set of success colors. +#[derive(Debug, Clone, Copy, PartialEq)]  pub struct Success { +    /// The base success color.      pub base: Pair, +    /// A weaker version of the base success color.      pub weak: Pair, +    /// A stronger version of the base success color.      pub strong: Pair,  }  impl Success { +    /// Generates a set of [`Success`] colors from the base, background, and text colors.      pub fn generate(base: Color, background: Color, text: Color) -> Self {          let weak = mix(base, background, 0.4);          let strong = deviate(base, 0.1); @@ -187,13 +240,19 @@ impl Success {      }  } +/// A set of danger colors. +#[derive(Debug, Clone, Copy, PartialEq)]  pub struct Danger { +    /// The base danger color.      pub base: Pair, +    /// A weaker version of the base danger color.      pub weak: Pair, +    /// A stronger version of the base danger color.      pub strong: Pair,  }  impl Danger { +    /// Generates a set of [`Danger`] colors from the base, background, and text colors.      pub fn generate(base: Color, background: Color, text: Color) -> Self {          let weak = mix(base, background, 0.4);          let strong = deviate(base, 0.1); diff --git a/style/src/toggler.rs b/style/src/toggler.rs index 0acf8e97..abc73f2a 100644 --- a/style/src/toggler.rs +++ b/style/src/toggler.rs @@ -1,20 +1,31 @@ -//! Show toggle controls using togglers. +//! Change the appearance of a toggler.  use iced_core::Color;  /// The appearance of a toggler.  #[derive(Debug, Clone, Copy)]  pub struct Appearance { +    /// The background [`Color`] of the toggler.      pub background: Color, +    /// The [`Color`] of the background border of the toggler.      pub background_border: Option<Color>, +    /// The foreground [`Color`] of the toggler.      pub foreground: Color, +    /// The [`Color`] of the foreground border of the toggler.      pub foreground_border: Option<Color>,  }  /// A set of rules that dictate the style of a toggler.  pub trait StyleSheet { -    type Style: Default + Copy; +    /// The supported style of the [`StyleSheet`]. +    type Style: Default; -    fn active(&self, style: Self::Style, is_active: bool) -> Appearance; +    /// Returns the active [`Appearance`] of the toggler for the provided [`Style`]. +    /// +    /// [`Style`]: Self::Style +    fn active(&self, style: &Self::Style, is_active: bool) -> Appearance; -    fn hovered(&self, style: Self::Style, is_active: bool) -> Appearance; +    /// Returns the hovered [`Appearance`] of the toggler for the provided [`Style`]. +    /// +    /// [`Style`]: Self::Style +    fn hovered(&self, style: &Self::Style, is_active: bool) -> Appearance;  } | 
