summaryrefslogtreecommitdiffstats
path: root/widget/src/overlay/menu.rs
diff options
context:
space:
mode:
Diffstat (limited to 'widget/src/overlay/menu.rs')
-rw-r--r--widget/src/overlay/menu.rs177
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,