diff options
| author | 2024-03-25 22:21:22 +0100 | |
|---|---|---|
| committer | 2024-03-25 22:21:22 +0100 | |
| commit | eae4065300e9997db12268be55eceb4e4c294b1e (patch) | |
| tree | 677326f37ac8bbf922c18369d0f1d4955852fb18 /widget/src/overlay | |
| parent | a2a8381a49ac2dd1cd65eb382b9ee02bbfa17286 (diff) | |
| parent | 74373cb086da6097eae7d2e8bd6348aaf7c43857 (diff) | |
| download | iced-eae4065300e9997db12268be55eceb4e4c294b1e.tar.gz iced-eae4065300e9997db12268be55eceb4e4c294b1e.tar.bz2 iced-eae4065300e9997db12268be55eceb4e4c294b1e.zip | |
Merge pull request #2350 from iced-rs/theming-revolutions
Theming Revolutions
Diffstat (limited to '')
| -rw-r--r-- | widget/src/overlay/menu.rs | 177 | 
1 files changed, 96 insertions, 81 deletions
| diff --git a/widget/src/overlay/menu.rs b/widget/src/overlay/menu.rs index 0364f980..d76caa8a 100644 --- a/widget/src/overlay/menu.rs +++ b/widget/src/overlay/menu.rs @@ -1,5 +1,4 @@  //! Build and show dropdown menus. -use crate::container::{self, Container};  use crate::core::alignment;  use crate::core::event::{self, Event};  use crate::core::layout::{self, Layout}; @@ -20,12 +19,15 @@ use crate::scrollable::{self, Scrollable};  #[allow(missing_debug_implementations)]  pub struct Menu<      'a, +    'b,      T,      Message,      Theme = crate::Theme,      Renderer = crate::Renderer,  > where +    Theme: Catalog,      Renderer: text::Renderer, +    'b: 'a,  {      state: &'a mut State,      options: &'a [T], @@ -38,15 +40,17 @@ pub struct Menu<      text_line_height: text::LineHeight,      text_shaping: text::Shaping,      font: Option<Renderer::Font>, -    style: &'a Style<'a, Theme>, +    class: &'a <Theme as Catalog>::Class<'b>,  } -impl<'a, T, Message, Theme, Renderer> Menu<'a, T, Message, Theme, Renderer> +impl<'a, 'b, T, Message, Theme, Renderer> +    Menu<'a, 'b, T, Message, Theme, Renderer>  where      T: ToString + Clone,      Message: 'a, -    Theme: 'a, +    Theme: Catalog + 'a,      Renderer: text::Renderer + 'a, +    'b: 'a,  {      /// Creates a new [`Menu`] with the given [`State`], a list of options,      /// the message to produced when an option is selected, and its [`Style`]. @@ -56,7 +60,7 @@ where          hovered_option: &'a mut Option<usize>,          on_selected: impl FnMut(T) -> Message + 'a,          on_option_hovered: Option<&'a dyn Fn(T) -> Message>, -        style: &'a Style<'a, Theme>, +        class: &'a <Theme as Catalog>::Class<'b>,      ) -> Self {          Menu {              state, @@ -70,7 +74,7 @@ where              text_line_height: text::LineHeight::default(),              text_shaping: text::Shaping::Basic,              font: None, -            style, +            class,          }      } @@ -153,27 +157,29 @@ impl Default for State {      }  } -struct Overlay<'a, Message, Theme, Renderer> +struct Overlay<'a, 'b, Message, Theme, Renderer>  where +    Theme: Catalog,      Renderer: crate::core::Renderer,  {      position: Point,      state: &'a mut Tree, -    container: Container<'a, Message, Theme, Renderer>, +    list: Scrollable<'a, Message, Theme, Renderer>,      width: f32,      target_height: f32, -    style: &'a Style<'a, Theme>, +    class: &'a <Theme as Catalog>::Class<'b>,  } -impl<'a, Message, Theme, Renderer> Overlay<'a, Message, Theme, Renderer> +impl<'a, 'b, Message, Theme, Renderer> Overlay<'a, 'b, Message, Theme, Renderer>  where      Message: 'a, -    Theme: 'a, +    Theme: Catalog + scrollable::Catalog + 'a,      Renderer: text::Renderer + 'a, +    'b: 'a,  {      pub fn new<T>(          position: Point, -        menu: Menu<'a, T, Message, Theme, Renderer>, +        menu: Menu<'a, 'b, T, Message, Theme, Renderer>,          target_height: f32,      ) -> Self      where @@ -191,46 +197,43 @@ where              text_size,              text_line_height,              text_shaping, -            style, +            class,          } = menu; -        let container = Container::with_style( -            Scrollable::with_direction_and_style( -                List { -                    options, -                    hovered_option, -                    on_selected, -                    on_option_hovered, -                    font, -                    text_size, -                    text_line_height, -                    text_shaping, -                    padding, -                    style: &style.list, -                }, -                scrollable::Direction::default(), -                &style.scrollable, -            ), -            container::transparent, +        let list = Scrollable::with_direction( +            List { +                options, +                hovered_option, +                on_selected, +                on_option_hovered, +                font, +                text_size, +                text_line_height, +                text_shaping, +                padding, +                class, +            }, +            scrollable::Direction::default(),          ); -        state.tree.diff(&container as &dyn Widget<_, _, _>); +        state.tree.diff(&list as &dyn Widget<_, _, _>);          Self {              position,              state: &mut state.tree, -            container, +            list,              width,              target_height, -            style, +            class,          }      }  } -impl<'a, Message, Theme, Renderer> +impl<'a, 'b, Message, Theme, Renderer>      crate::core::Overlay<Message, Theme, Renderer> -    for Overlay<'a, Message, Theme, Renderer> +    for Overlay<'a, 'b, Message, Theme, Renderer>  where +    Theme: Catalog,      Renderer: text::Renderer,  {      fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node { @@ -251,7 +254,7 @@ where          )          .width(self.width); -        let node = self.container.layout(self.state, renderer, &limits); +        let node = self.list.layout(self.state, renderer, &limits);          let size = node.size();          node.move_to(if space_below > space_above { @@ -272,7 +275,7 @@ where      ) -> event::Status {          let bounds = layout.bounds(); -        self.container.on_event( +        self.list.on_event(              self.state, event, layout, cursor, renderer, clipboard, shell,              &bounds,          ) @@ -285,7 +288,7 @@ where          viewport: &Rectangle,          renderer: &Renderer,      ) -> mouse::Interaction { -        self.container +        self.list              .mouse_interaction(self.state, layout, cursor, viewport, renderer)      } @@ -293,30 +296,32 @@ where          &self,          renderer: &mut Renderer,          theme: &Theme, -        style: &renderer::Style, +        defaults: &renderer::Style,          layout: Layout<'_>,          cursor: mouse::Cursor,      ) {          let bounds = layout.bounds(); -        let appearance = (self.style.list)(theme); +        let style = Catalog::style(theme, self.class);          renderer.fill_quad(              renderer::Quad {                  bounds, -                border: appearance.border, +                border: style.border,                  ..renderer::Quad::default()              }, -            appearance.background, +            style.background,          ); -        self.container -            .draw(self.state, renderer, theme, style, layout, cursor, &bounds); +        self.list.draw( +            self.state, renderer, theme, defaults, layout, cursor, &bounds, +        );      }  } -struct List<'a, T, Message, Theme, Renderer> +struct List<'a, 'b, T, Message, Theme, Renderer>  where +    Theme: Catalog,      Renderer: text::Renderer,  {      options: &'a [T], @@ -328,13 +333,14 @@ where      text_line_height: text::LineHeight,      text_shaping: text::Shaping,      font: Option<Renderer::Font>, -    style: &'a dyn Fn(&Theme) -> Appearance, +    class: &'a <Theme as Catalog>::Class<'b>,  } -impl<'a, T, Message, Theme, Renderer> Widget<Message, Theme, Renderer> -    for List<'a, T, Message, Theme, Renderer> +impl<'a, 'b, T, Message, Theme, Renderer> Widget<Message, Theme, Renderer> +    for List<'a, 'b, T, Message, Theme, Renderer>  where      T: Clone + ToString, +    Theme: Catalog,      Renderer: text::Renderer,  {      fn size(&self) -> Size<Length> { @@ -477,7 +483,7 @@ where          _cursor: mouse::Cursor,          viewport: &Rectangle,      ) { -        let appearance = (self.style)(theme); +        let style = Catalog::style(theme, self.class);          let bounds = layout.bounds();          let text_size = @@ -507,14 +513,14 @@ where                  renderer.fill_quad(                      renderer::Quad {                          bounds: Rectangle { -                            x: bounds.x + appearance.border.width, -                            width: bounds.width - appearance.border.width * 2.0, +                            x: bounds.x + style.border.width, +                            width: bounds.width - style.border.width * 2.0,                              ..bounds                          }, -                        border: Border::rounded(appearance.border.radius), +                        border: Border::rounded(style.border.radius),                          ..renderer::Quad::default()                      }, -                    appearance.selected_background, +                    style.selected_background,                  );              } @@ -531,9 +537,9 @@ where                  },                  Point::new(bounds.x + self.padding.left, bounds.center_y()),                  if is_selected { -                    appearance.selected_text_color +                    style.selected_text_color                  } else { -                    appearance.text_color +                    style.text_color                  },                  *viewport,              ); @@ -541,23 +547,24 @@ where      }  } -impl<'a, T, Message, Theme, Renderer> -    From<List<'a, T, Message, Theme, Renderer>> +impl<'a, 'b, T, Message, Theme, Renderer> +    From<List<'a, 'b, T, Message, Theme, Renderer>>      for Element<'a, Message, Theme, Renderer>  where      T: ToString + Clone,      Message: 'a, -    Theme: 'a, +    Theme: 'a + Catalog,      Renderer: 'a + text::Renderer, +    'b: 'a,  { -    fn from(list: List<'a, T, Message, Theme, Renderer>) -> Self { +    fn from(list: List<'a, 'b, T, Message, Theme, Renderer>) -> Self {          Element::new(list)      }  }  /// The appearance of a [`Menu`].  #[derive(Debug, Clone, Copy)] -pub struct Appearance { +pub struct Style {      /// The [`Background`] of the menu.      pub background: Background,      /// The [`Border`] of the menu. @@ -570,35 +577,43 @@ pub struct Appearance {      pub selected_background: Background,  } -/// The style of the different parts of a [`Menu`]. -#[allow(missing_debug_implementations)] -pub struct Style<'a, Theme> { -    /// The style of the list of the [`Menu`]. -    pub list: Box<dyn Fn(&Theme) -> Appearance + 'a>, -    /// The style of the [`Scrollable`] of the [`Menu`]. -    pub scrollable: scrollable::Style<'a, Theme>, -} +/// The theme catalog of a [`Menu`]. +pub trait Catalog: scrollable::Catalog { +    /// The item class of the [`Catalog`]. +    type Class<'a>; + +    /// The default class produced by the [`Catalog`]. +    fn default<'a>() -> <Self as Catalog>::Class<'a>; -/// The default style of a [`Menu`]. -pub trait DefaultStyle: Sized { -    /// Returns the default style of a [`Menu`]. -    fn default_style() -> Style<'static, Self>; +    /// The default class for the scrollable of the [`Menu`]. +    fn default_scrollable<'a>() -> <Self as scrollable::Catalog>::Class<'a> { +        <Self as scrollable::Catalog>::default() +    } + +    /// The [`Style`] of a class with the given status. +    fn style(&self, class: &<Self as Catalog>::Class<'_>) -> Style;  } -impl DefaultStyle for Theme { -    fn default_style() -> Style<'static, Self> { -        Style { -            list: Box::new(default), -            scrollable: Box::new(scrollable::default), -        } +/// A styling function for a [`Menu`]. +pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme) -> Style + 'a>; + +impl Catalog for Theme { +    type Class<'a> = StyleFn<'a, Self>; + +    fn default<'a>() -> StyleFn<'a, Self> { +        Box::new(default) +    } + +    fn style(&self, class: &StyleFn<'_, Self>) -> Style { +        class(self)      }  }  /// The default style of the list of a [`Menu`]. -pub fn default(theme: &Theme) -> Appearance { +pub fn default(theme: &Theme) -> Style {      let palette = theme.extended_palette(); -    Appearance { +    Style {          background: palette.background.weak.color.into(),          border: Border {              width: 1.0, | 
