diff options
Diffstat (limited to '')
| -rw-r--r-- | widget/src/button.rs | 85 | ||||
| -rw-r--r-- | widget/src/checkbox.rs | 99 | ||||
| -rw-r--r-- | widget/src/helpers.rs | 4 | ||||
| -rw-r--r-- | widget/src/svg.rs | 89 | 
4 files changed, 164 insertions, 113 deletions
diff --git a/widget/src/button.rs b/widget/src/button.rs index 8754622f..80a18e82 100644 --- a/widget/src/button.rs +++ b/widget/src/button.rs @@ -1,5 +1,4 @@  //! Allow your users to perform actions by pressing a button. -use crate::core::closure;  use crate::core::event::{self, Event};  use crate::core::layout;  use crate::core::mouse; @@ -58,7 +57,7 @@ where      height: Length,      padding: Padding,      clip: bool, -    style: Theme::Item<'a>, +    class: Theme::Class<'a>,  }  impl<'a, Message, Theme, Renderer> Button<'a, Message, Theme, Renderer> @@ -80,7 +79,7 @@ where              height: size.height.fluid(),              padding: DEFAULT_PADDING,              clip: false, -            style: Theme::default(), +            class: Theme::default(),          }      } @@ -119,18 +118,30 @@ where          self      } -    /// Sets the style variant of this [`Button`]. -    pub fn style(mut self, style: impl Into<Theme::Item<'a>>) -> Self { -        self.style = style.into(); -        self -    } -      /// Sets whether the contents of the [`Button`] should be clipped on      /// overflow.      pub fn clip(mut self, clip: bool) -> Self {          self.clip = clip;          self      } + +    /// Sets the style of this [`Button`]. +    #[must_use] +    pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self +    where +        Theme::Class<'a>: From<StyleFn<'a, Theme>>, +    { +        self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); +        self +    } + +    /// Sets the style class of this [`Button`]. +    #[cfg(feature = "advanced")] +    #[must_use] +    pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self { +        self.class = class.into(); +        self +    }  }  #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] @@ -302,19 +313,19 @@ where              Status::Active          }; -        let styling = theme.style(&self.style, status); +        let style = theme.style(&self.class, status); -        if styling.background.is_some() -            || styling.border.width > 0.0 -            || styling.shadow.color.a > 0.0 +        if style.background.is_some() +            || style.border.width > 0.0 +            || style.shadow.color.a > 0.0          {              renderer.fill_quad(                  renderer::Quad {                      bounds, -                    border: styling.border, -                    shadow: styling.shadow, +                    border: style.border, +                    shadow: style.shadow,                  }, -                styling +                style                      .background                      .unwrap_or(Background::Color(Color::TRANSPARENT)),              ); @@ -331,7 +342,7 @@ where              renderer,              theme,              &renderer::Style { -                text_color: styling.text_color, +                text_color: style.text_color,              },              content_layout,              cursor, @@ -405,7 +416,7 @@ pub enum Status {      Disabled,  } -/// The appearance of a button. +/// The style of a button.  #[derive(Debug, Clone, Copy, PartialEq)]  pub struct Style {      /// The [`Background`] of the button. @@ -419,7 +430,7 @@ pub struct Style {  }  impl Style { -    /// Updates the [`Appearance`] with the given [`Background`]. +    /// Updates the [`Style`] with the given [`Background`].      pub fn with_background(self, background: impl Into<Background>) -> Self {          Self {              background: Some(background.into()), @@ -428,7 +439,7 @@ impl Style {      }  } -impl std::default::Default for Style { +impl Default for Style {      fn default() -> Self {          Self {              background: None, @@ -441,30 +452,30 @@ impl std::default::Default for Style {  /// The theme catalog of a [`Button`].  pub trait Catalog: Sized { -    /// The item type of this [`Catalog`]. -    type Item<'a>; +    /// The item class of this [`Catalog`]. +    type Class<'a>; -    /// The default item produced by this [`Catalog`]. -    fn default<'a>() -> Self::Item<'a>; +    /// The default class produced by this [`Catalog`]. +    fn default<'a>() -> Self::Class<'a>; -    /// The [`Style`] of an item with the given status. -    fn style(&self, item: &Self::Item<'_>, status: Status) -> Style; +    /// The [`Style`] of a class with the given status. +    fn style(&self, item: &Self::Class<'_>, status: Status) -> Style;  } -/// The item of a button [`Catalog`] for the built-in [`Theme`]. +/// A styling function for a [`Button`].  ///  /// This is just a boxed closure: `Fn(&Theme, Status) -> Style`. -pub type Item<'a, Theme> = closure::Binary<'a, Theme, Status, Style>; +pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Style + 'a>;  impl Catalog for Theme { -    type Item<'a> = Item<'a, Self>; +    type Class<'a> = StyleFn<'a, Self>; -    fn default<'a>() -> Self::Item<'a> { -        closure::Binary::from(primary) +    fn default<'a>() -> Self::Class<'a> { +        Box::new(primary)      } -    fn style(&self, item: &Self::Item<'_>, status: Status) -> Style { -        (item.0)(self, status) +    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { +        class(self, status)      }  } @@ -556,12 +567,12 @@ fn styled(pair: palette::Pair) -> Style {      }  } -fn disabled(appearance: Style) -> Style { +fn disabled(style: Style) -> Style {      Style { -        background: appearance +        background: style              .background              .map(|background| background.scale_alpha(0.5)), -        text_color: appearance.text_color.scale_alpha(0.5), -        ..appearance +        text_color: style.text_color.scale_alpha(0.5), +        ..style      }  } diff --git a/widget/src/checkbox.rs b/widget/src/checkbox.rs index 15fb8f58..a90914b8 100644 --- a/widget/src/checkbox.rs +++ b/widget/src/checkbox.rs @@ -39,6 +39,7 @@ pub struct Checkbox<      Renderer = crate::Renderer,  > where      Renderer: text::Renderer, +    Theme: Catalog,  {      is_checked: bool,      on_toggle: Option<Box<dyn Fn(bool) -> Message + 'a>>, @@ -51,12 +52,13 @@ pub struct Checkbox<      text_shaping: text::Shaping,      font: Option<Renderer::Font>,      icon: Icon<Renderer::Font>, -    style: Style<'a, Theme>, +    class: Theme::Class<'a>,  }  impl<'a, Message, Theme, Renderer> Checkbox<'a, Message, Theme, Renderer>  where      Renderer: text::Renderer, +    Theme: Catalog,  {      /// The default size of a [`Checkbox`].      const DEFAULT_SIZE: f32 = 16.0; @@ -69,10 +71,7 @@ where      /// It expects:      ///   * the label of the [`Checkbox`]      ///   * a boolean describing whether the [`Checkbox`] is checked or not -    pub fn new(label: impl Into<String>, is_checked: bool) -> Self -    where -        Theme: DefaultStyle + 'a, -    { +    pub fn new(label: impl Into<String>, is_checked: bool) -> Self {          Checkbox {              is_checked,              on_toggle: None, @@ -91,7 +90,7 @@ where                  line_height: text::LineHeight::default(),                  shaping: text::Shaping::Basic,              }, -            style: Box::new(Theme::default_style), +            class: Theme::default(),          }      } @@ -173,12 +172,21 @@ where          self      } -    /// Sets the style of the [`Checkbox`]. -    pub fn style( -        mut self, -        style: impl Fn(&Theme, Status) -> Appearance + 'a, -    ) -> Self { -        self.style = Box::new(style); +    /// Sets the style of this [`Checkbox`]. +    #[must_use] +    pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self +    where +        Theme::Class<'a>: From<StyleFn<'a, Theme>>, +    { +        self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); +        self +    } + +    /// Sets the style class of this [`Checkbox`]. +    #[cfg(feature = "advanced")] +    #[must_use] +    pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self { +        self.class = class.into();          self      }  } @@ -187,6 +195,7 @@ impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>      for Checkbox<'a, Message, Theme, Renderer>  where      Renderer: text::Renderer, +    Theme: Catalog,  {      fn tag(&self) -> tree::Tag {          tree::Tag::of::<widget::text::State<Renderer::Paragraph>>() @@ -285,7 +294,7 @@ where          tree: &Tree,          renderer: &mut Renderer,          theme: &Theme, -        style: &renderer::Style, +        defaults: &renderer::Style,          layout: Layout<'_>,          cursor: mouse::Cursor,          viewport: &Rectangle, @@ -304,7 +313,7 @@ where              Status::Active { is_checked }          }; -        let appearance = (self.style)(theme, status); +        let style = theme.style(&self.class, status);          {              let layout = children.next().unwrap(); @@ -313,10 +322,10 @@ where              renderer.fill_quad(                  renderer::Quad {                      bounds, -                    border: appearance.border, +                    border: style.border,                      ..renderer::Quad::default()                  }, -                appearance.background, +                style.background,              );              let Icon { @@ -341,7 +350,7 @@ where                          shaping: *shaping,                      },                      bounds.center(), -                    appearance.icon_color, +                    style.icon_color,                      *viewport,                  );              } @@ -352,11 +361,11 @@ where              crate::text::draw(                  renderer, -                style, +                defaults,                  label_layout,                  tree.state.downcast_ref(),                  crate::text::Appearance { -                    color: appearance.text_color, +                    color: style.text_color,                  },                  viewport,              ); @@ -368,7 +377,7 @@ impl<'a, Message, Theme, Renderer> From<Checkbox<'a, Message, Theme, Renderer>>      for Element<'a, Message, Theme, Renderer>  where      Message: 'a, -    Theme: 'a, +    Theme: 'a + Catalog,      Renderer: 'a + text::Renderer,  {      fn from( @@ -413,9 +422,9 @@ pub enum Status {      },  } -/// The appearance of a checkbox. +/// The style of a checkbox.  #[derive(Debug, Clone, Copy)] -pub struct Appearance { +pub struct Style {      /// The [`Background`] of the checkbox.      pub background: Background,      /// The icon [`Color`] of the checkbox. @@ -426,29 +435,37 @@ pub struct Appearance {      pub text_color: Option<Color>,  } -/// The style of a [`Checkbox`]. -pub type Style<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Appearance + 'a>; +/// The theme catalog of a [`Checkbox`]. +pub trait Catalog: Sized { +    /// The item class of this [`Catalog`]. +    type Class<'a>; -/// The default style of a [`Checkbox`]. -pub trait DefaultStyle { -    /// Returns the default style of a [`Checkbox`]. -    fn default_style(&self, status: Status) -> Appearance; +    /// The default class produced by this [`Catalog`]. +    fn default<'a>() -> Self::Class<'a>; + +    /// The [`Style`] of a class with the given status. +    fn style(&self, item: &Self::Class<'_>, status: Status) -> Style;  } -impl DefaultStyle for Theme { -    fn default_style(&self, status: Status) -> Appearance { -        primary(self, status) +/// A styling function for a [`Checkbox`]. +/// +/// This is just a boxed closure: `Fn(&Theme, Status) -> Style`. +pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Style + 'a>; + +impl Catalog for Theme { +    type Class<'a> = StyleFn<'a, Self>; + +    fn default<'a>() -> Self::Class<'a> { +        Box::new(primary)      } -} -impl DefaultStyle for Appearance { -    fn default_style(&self, _status: Status) -> Appearance { -        *self +    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { +        class(self, status)      }  }  /// A primary checkbox; denoting a main toggle. -pub fn primary(theme: &Theme, status: Status) -> Appearance { +pub fn primary(theme: &Theme, status: Status) -> Style {      let palette = theme.extended_palette();      match status { @@ -474,7 +491,7 @@ pub fn primary(theme: &Theme, status: Status) -> Appearance {  }  /// A secondary checkbox; denoting a complementary toggle. -pub fn secondary(theme: &Theme, status: Status) -> Appearance { +pub fn secondary(theme: &Theme, status: Status) -> Style {      let palette = theme.extended_palette();      match status { @@ -500,7 +517,7 @@ pub fn secondary(theme: &Theme, status: Status) -> Appearance {  }  /// A success checkbox; denoting a positive toggle. -pub fn success(theme: &Theme, status: Status) -> Appearance { +pub fn success(theme: &Theme, status: Status) -> Style {      let palette = theme.extended_palette();      match status { @@ -526,7 +543,7 @@ pub fn success(theme: &Theme, status: Status) -> Appearance {  }  /// A danger checkbox; denoting a negaive toggle. -pub fn danger(theme: &Theme, status: Status) -> Appearance { +pub fn danger(theme: &Theme, status: Status) -> Style {      let palette = theme.extended_palette();      match status { @@ -556,8 +573,8 @@ fn styled(      base: palette::Pair,      accent: palette::Pair,      is_checked: bool, -) -> Appearance { -    Appearance { +) -> Style { +    Style {          background: Background::Color(if is_checked {              accent.color          } else { diff --git a/widget/src/helpers.rs b/widget/src/helpers.rs index e60096d1..4c4d1012 100644 --- a/widget/src/helpers.rs +++ b/widget/src/helpers.rs @@ -161,7 +161,7 @@ pub fn checkbox<'a, Message, Theme, Renderer>(      is_checked: bool,  ) -> Checkbox<'a, Message, Theme, Renderer>  where -    Theme: checkbox::DefaultStyle + 'a, +    Theme: checkbox::Catalog + 'a,      Renderer: core::text::Renderer,  {      Checkbox::new(label, is_checked) @@ -367,7 +367,7 @@ pub fn svg<'a, Theme>(      handle: impl Into<core::svg::Handle>,  ) -> crate::Svg<'a, Theme>  where -    Theme: crate::svg::DefaultStyle + 'a, +    Theme: crate::svg::Catalog,  {      crate::Svg::new(handle)  } diff --git a/widget/src/svg.rs b/widget/src/svg.rs index 1ac07ade..d2fd0aee 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -20,36 +20,36 @@ pub use crate::core::svg::Handle;  /// [`Svg`] images can have a considerable rendering cost when resized,  /// specially when they are complex.  #[allow(missing_debug_implementations)] -pub struct Svg<'a, Theme = crate::Theme> { +pub struct Svg<'a, Theme = crate::Theme> +where +    Theme: Catalog, +{      handle: Handle,      width: Length,      height: Length,      content_fit: ContentFit, -    style: Style<'a, Theme>, +    class: Theme::Class<'a>,  } -impl<'a, Theme> Svg<'a, Theme> { +impl<'a, Theme> Svg<'a, Theme> +where +    Theme: Catalog, +{      /// Creates a new [`Svg`] from the given [`Handle`]. -    pub fn new(handle: impl Into<Handle>) -> Self -    where -        Theme: DefaultStyle + 'a, -    { +    pub fn new(handle: impl Into<Handle>) -> Self {          Svg {              handle: handle.into(),              width: Length::Fill,              height: Length::Shrink,              content_fit: ContentFit::Contain, -            style: Box::new(Theme::default_style), +            class: Theme::default(),          }      }      /// Creates a new [`Svg`] that will display the contents of the file at the      /// provided path.      #[must_use] -    pub fn from_path(path: impl Into<PathBuf>) -> Self -    where -        Theme: DefaultStyle + 'a, -    { +    pub fn from_path(path: impl Into<PathBuf>) -> Self {          Self::new(Handle::from_path(path))      } @@ -78,13 +78,21 @@ impl<'a, Theme> Svg<'a, Theme> {          }      } -    /// Sets the style variant of this [`Svg`]. +    /// Sets the style of this [`Svg`]. +    #[must_use] +    pub fn style(mut self, style: impl Fn(&Theme, Status) -> Style + 'a) -> Self +    where +        Theme::Class<'a>: From<StyleFn<'a, Theme>>, +    { +        self.class = (Box::new(style) as StyleFn<'a, Theme>).into(); +        self +    } + +    /// Sets the style class of this [`Svg`]. +    #[cfg(feature = "advanced")]      #[must_use] -    pub fn style( -        mut self, -        style: impl Fn(&Theme, Status) -> Appearance + 'a, -    ) -> Self { -        self.style = Box::new(style); +    pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self { +        self.class = class.into();          self      }  } @@ -93,6 +101,7 @@ impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>      for Svg<'a, Theme>  where      Renderer: svg::Renderer, +    Theme: Catalog,  {      fn size(&self) -> Size<Length> {          Size { @@ -167,7 +176,7 @@ where                  Status::Idle              }; -            let appearance = (self.style)(theme, status); +            let appearance = theme.style(&self.class, status);              renderer.draw(                  self.handle.clone(), @@ -189,7 +198,7 @@ where  impl<'a, Message, Theme, Renderer> From<Svg<'a, Theme>>      for Element<'a, Message, Theme, Renderer>  where -    Theme: 'a, +    Theme: Catalog + 'a,      Renderer: svg::Renderer + 'a,  {      fn from(icon: Svg<'a, Theme>) -> Element<'a, Message, Theme, Renderer> { @@ -208,7 +217,7 @@ pub enum Status {  /// The appearance of an [`Svg`].  #[derive(Debug, Clone, Copy, PartialEq, Default)] -pub struct Appearance { +pub struct Style {      /// The [`Color`] filter of an [`Svg`].      ///      /// Useful for coloring a symbolic icon. @@ -217,23 +226,37 @@ pub struct Appearance {      pub color: Option<Color>,  } -/// The style of an [`Svg`]. -pub type Style<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Appearance + 'a>; +/// The theme catalog of an [`Svg`]. +pub trait Catalog { +    /// The item class of this [`Catalog`]. +    type Class<'a>; + +    /// The default class produced by this [`Catalog`]. +    fn default<'a>() -> Self::Class<'a>; -/// The default style of an [`Svg`]. -pub trait DefaultStyle { -    /// Returns the default style of an [`Svg`]. -    fn default_style(&self, status: Status) -> Appearance; +    /// The [`Style`] of a class with the given status. +    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style;  } -impl DefaultStyle for Theme { -    fn default_style(&self, _status: Status) -> Appearance { -        Appearance::default() +impl Catalog for Theme { +    type Class<'a> = StyleFn<'a, Self>; + +    fn default<'a>() -> Self::Class<'a> { +        Box::new(|_theme, _status| Style::default()) +    } + +    fn style(&self, class: &Self::Class<'_>, status: Status) -> Style { +        class(self, status)      }  } -impl DefaultStyle for Appearance { -    fn default_style(&self, _status: Status) -> Appearance { -        *self +/// A styling function for an [`Svg`]. +/// +/// This is just a boxed closure: `Fn(&Theme, Status) -> Style`. +pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme, Status) -> Style + 'a>; + +impl<'a, Theme> From<Style> for StyleFn<'a, Theme> { +    fn from(style: Style) -> Self { +        Box::new(move |_theme, _status| style)      }  }  | 
