summaryrefslogtreecommitdiffstats
path: root/native/src/overlay
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--core/src/overlay.rs (renamed from native/src/overlay.rs)25
-rw-r--r--core/src/overlay/element.rs (renamed from native/src/overlay/element.rs)83
-rw-r--r--core/src/overlay/group.rs (renamed from native/src/overlay/group.rs)47
-rw-r--r--widget/src/overlay/menu.rs (renamed from native/src/overlay/menu.rs)219
4 files changed, 218 insertions, 156 deletions
diff --git a/native/src/overlay.rs b/core/src/overlay.rs
index 6cada416..2e05db93 100644
--- a/native/src/overlay.rs
+++ b/core/src/overlay.rs
@@ -2,11 +2,8 @@
mod element;
mod group;
-pub mod menu;
-
pub use element::Element;
pub use group::Group;
-pub use menu::Menu;
use crate::event::{self, Event};
use crate::layout;
@@ -41,7 +38,7 @@ where
theme: &Renderer::Theme,
style: &renderer::Style,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
);
/// Applies a [`widget::Operation`] to the [`Overlay`].
@@ -69,7 +66,7 @@ where
&mut self,
_event: Event,
_layout: Layout<'_>,
- _cursor_position: Point,
+ _cursor: mouse::Cursor,
_renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
_shell: &mut Shell<'_, Message>,
@@ -83,7 +80,7 @@ where
fn mouse_interaction(
&self,
_layout: Layout<'_>,
- _cursor_position: Point,
+ _cursor: mouse::Cursor,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
@@ -94,9 +91,23 @@ where
///
/// By default, it returns true if the bounds of the `layout` contain
/// the `cursor_position`.
- fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
+ fn is_over(
+ &self,
+ layout: Layout<'_>,
+ _renderer: &Renderer,
+ cursor_position: Point,
+ ) -> bool {
layout.bounds().contains(cursor_position)
}
+
+ /// Returns the nested overlay of the [`Overlay`], if there is any.
+ fn overlay<'a>(
+ &'a mut self,
+ _layout: Layout<'_>,
+ _renderer: &Renderer,
+ ) -> Option<Element<'a, Message, Renderer>> {
+ None
+ }
}
/// Returns a [`Group`] of overlay [`Element`] children.
diff --git a/native/src/overlay/element.rs b/core/src/overlay/element.rs
index 237d25d1..c2134343 100644
--- a/native/src/overlay/element.rs
+++ b/core/src/overlay/element.rs
@@ -68,35 +68,25 @@ where
&mut self,
event: Event,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
- self.overlay.on_event(
- event,
- layout,
- cursor_position,
- renderer,
- clipboard,
- shell,
- )
+ self.overlay
+ .on_event(event, layout, cursor, renderer, clipboard, shell)
}
/// Returns the current [`mouse::Interaction`] of the [`Element`].
pub fn mouse_interaction(
&self,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
- self.overlay.mouse_interaction(
- layout,
- cursor_position,
- viewport,
- renderer,
- )
+ self.overlay
+ .mouse_interaction(layout, cursor, viewport, renderer)
}
/// Draws the [`Element`] and its children using the given [`Layout`].
@@ -106,10 +96,9 @@ where
theme: &Renderer::Theme,
style: &renderer::Style,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
) {
- self.overlay
- .draw(renderer, theme, style, layout, cursor_position)
+ self.overlay.draw(renderer, theme, style, layout, cursor)
}
/// Applies a [`widget::Operation`] to the [`Element`].
@@ -123,8 +112,22 @@ where
}
/// Returns true if the cursor is over the [`Element`].
- pub fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
- self.overlay.is_over(layout, cursor_position)
+ pub fn is_over(
+ &self,
+ layout: Layout<'_>,
+ renderer: &Renderer,
+ cursor_position: Point,
+ ) -> bool {
+ self.overlay.is_over(layout, renderer, cursor_position)
+ }
+
+ /// Returns the nested overlay of the [`Element`], if there is any.
+ pub fn overlay<'b>(
+ &'b mut self,
+ layout: Layout<'_>,
+ renderer: &Renderer,
+ ) -> Option<Element<'b, Message, Renderer>> {
+ self.overlay.overlay(layout, renderer)
}
}
@@ -215,7 +218,7 @@ where
&mut self,
event: Event,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, B>,
@@ -226,7 +229,7 @@ where
let event_status = self.content.on_event(
event,
layout,
- cursor_position,
+ cursor,
renderer,
clipboard,
&mut local_shell,
@@ -240,16 +243,12 @@ where
fn mouse_interaction(
&self,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
- self.content.mouse_interaction(
- layout,
- cursor_position,
- viewport,
- renderer,
- )
+ self.content
+ .mouse_interaction(layout, cursor, viewport, renderer)
}
fn draw(
@@ -258,13 +257,27 @@ where
theme: &Renderer::Theme,
style: &renderer::Style,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
) {
- self.content
- .draw(renderer, theme, style, layout, cursor_position)
+ self.content.draw(renderer, theme, style, layout, cursor)
}
- fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
- self.content.is_over(layout, cursor_position)
+ fn is_over(
+ &self,
+ layout: Layout<'_>,
+ renderer: &Renderer,
+ cursor_position: Point,
+ ) -> bool {
+ self.content.is_over(layout, renderer, cursor_position)
+ }
+
+ fn overlay<'b>(
+ &'b mut self,
+ layout: Layout<'_>,
+ renderer: &Renderer,
+ ) -> Option<Element<'b, B, Renderer>> {
+ self.content
+ .overlay(layout, renderer)
+ .map(|overlay| overlay.map(self.mapper))
}
}
diff --git a/native/src/overlay/group.rs b/core/src/overlay/group.rs
index 1126f0cf..deffaad0 100644
--- a/native/src/overlay/group.rs
+++ b/core/src/overlay/group.rs
@@ -1,12 +1,10 @@
-use iced_core::{Point, Rectangle, Size};
-
use crate::event;
use crate::layout;
use crate::mouse;
use crate::overlay;
use crate::renderer;
use crate::widget;
-use crate::{Clipboard, Event, Layout, Overlay, Shell};
+use crate::{Clipboard, Event, Layout, Overlay, Point, Rectangle, Shell, Size};
/// An [`Overlay`] container that displays multiple overlay [`overlay::Element`]
/// children.
@@ -83,7 +81,7 @@ where
&mut self,
event: Event,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
@@ -95,7 +93,7 @@ where
child.on_event(
event.clone(),
layout,
- cursor_position,
+ cursor,
renderer,
clipboard,
shell,
@@ -110,17 +108,17 @@ where
theme: &<Renderer as crate::Renderer>::Theme,
style: &renderer::Style,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
) {
for (child, layout) in self.children.iter().zip(layout.children()) {
- child.draw(renderer, theme, style, layout, cursor_position);
+ child.draw(renderer, theme, style, layout, cursor);
}
}
fn mouse_interaction(
&self,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
@@ -128,12 +126,7 @@ where
.iter()
.zip(layout.children())
.map(|(child, layout)| {
- child.mouse_interaction(
- layout,
- cursor_position,
- viewport,
- renderer,
- )
+ child.mouse_interaction(layout, cursor, viewport, renderer)
})
.max()
.unwrap_or_default()
@@ -154,11 +147,33 @@ where
});
}
- fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool {
+ fn is_over(
+ &self,
+ layout: Layout<'_>,
+ renderer: &Renderer,
+ cursor_position: Point,
+ ) -> bool {
self.children
.iter()
.zip(layout.children())
- .any(|(child, layout)| child.is_over(layout, cursor_position))
+ .any(|(child, layout)| {
+ child.is_over(layout, renderer, cursor_position)
+ })
+ }
+
+ fn overlay<'b>(
+ &'b mut self,
+ layout: Layout<'_>,
+ renderer: &Renderer,
+ ) -> Option<overlay::Element<'b, Message, Renderer>> {
+ let children = self
+ .children
+ .iter_mut()
+ .zip(layout.children())
+ .filter_map(|(child, layout)| child.overlay(layout, renderer))
+ .collect::<Vec<_>>();
+
+ (!children.is_empty()).then(|| Group::with_children(children).overlay())
}
}
diff --git a/native/src/overlay/menu.rs b/widget/src/overlay/menu.rs
index 50f741ef..ccf4dfb5 100644
--- a/native/src/overlay/menu.rs
+++ b/widget/src/overlay/menu.rs
@@ -1,25 +1,25 @@
//! Build and show dropdown menus.
-use crate::alignment;
-use crate::event::{self, Event};
-use crate::layout;
-use crate::mouse;
-use crate::overlay;
-use crate::renderer;
-use crate::text::{self, Text};
-use crate::touch;
-use crate::widget::container::{self, Container};
-use crate::widget::scrollable::{self, Scrollable};
-use crate::widget::Tree;
-use crate::{
- Clipboard, Color, Element, Layout, Length, Padding, Pixels, Point,
- Rectangle, Shell, Size, Vector, Widget,
+use crate::container::{self, Container};
+use crate::core::alignment;
+use crate::core::event::{self, Event};
+use crate::core::layout::{self, Layout};
+use crate::core::mouse;
+use crate::core::overlay;
+use crate::core::renderer;
+use crate::core::text::{self, Text};
+use crate::core::touch;
+use crate::core::widget::Tree;
+use crate::core::{
+ Clipboard, Color, Length, Padding, Pixels, Point, Rectangle, Size, Vector,
};
+use crate::core::{Element, Shell, Widget};
+use crate::scrollable::{self, Scrollable};
pub use iced_style::menu::{Appearance, StyleSheet};
/// A list of selectable options.
#[allow(missing_debug_implementations)]
-pub struct Menu<'a, T, Renderer>
+pub struct Menu<'a, T, Message, Renderer = crate::Renderer>
where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
@@ -27,17 +27,20 @@ where
state: &'a mut State,
options: &'a [T],
hovered_option: &'a mut Option<usize>,
- last_selection: &'a mut Option<T>,
+ on_selected: Box<dyn FnMut(T) -> Message + 'a>,
width: f32,
padding: Padding,
text_size: Option<f32>,
- font: Renderer::Font,
+ text_line_height: text::LineHeight,
+ text_shaping: text::Shaping,
+ font: Option<Renderer::Font>,
style: <Renderer::Theme as StyleSheet>::Style,
}
-impl<'a, T, Renderer> Menu<'a, T, Renderer>
+impl<'a, T, Message, Renderer> Menu<'a, T, Message, Renderer>
where
T: ToString + Clone,
+ Message: 'a,
Renderer: text::Renderer + 'a,
Renderer::Theme:
StyleSheet + container::StyleSheet + scrollable::StyleSheet,
@@ -48,17 +51,19 @@ where
state: &'a mut State,
options: &'a [T],
hovered_option: &'a mut Option<usize>,
- last_selection: &'a mut Option<T>,
+ on_selected: impl FnMut(T) -> Message + 'a,
) -> Self {
Menu {
state,
options,
hovered_option,
- last_selection,
+ on_selected: Box::new(on_selected),
width: 0.0,
padding: Padding::ZERO,
text_size: None,
- font: Default::default(),
+ text_line_height: text::LineHeight::default(),
+ text_shaping: text::Shaping::Basic,
+ font: None,
style: Default::default(),
}
}
@@ -81,9 +86,24 @@ where
self
}
+ /// Sets the text [`LineHeight`] of the [`Menu`].
+ pub fn text_line_height(
+ mut self,
+ line_height: impl Into<text::LineHeight>,
+ ) -> Self {
+ self.text_line_height = line_height.into();
+ self
+ }
+
+ /// Sets the [`text::Shaping`] strategy of the [`Menu`].
+ pub fn text_shaping(mut self, shaping: text::Shaping) -> Self {
+ self.text_shaping = shaping;
+ self
+ }
+
/// Sets the font of the [`Menu`].
- pub fn font(mut self, font: Renderer::Font) -> Self {
- self.font = font;
+ pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
+ self.font = Some(font.into());
self
}
@@ -102,7 +122,7 @@ where
/// The `target_height` will be used to display the menu either on top
/// of the target or under it, depending on the screen position and the
/// dimensions of the [`Menu`].
- pub fn overlay<Message: 'a>(
+ pub fn overlay(
self,
position: Point,
target_height: f32,
@@ -137,7 +157,7 @@ impl Default for State {
struct Overlay<'a, Message, Renderer>
where
- Renderer: crate::Renderer,
+ Renderer: crate::core::Renderer,
Renderer::Theme: StyleSheet + container::StyleSheet,
{
state: &'a mut Tree,
@@ -155,7 +175,10 @@ where
Renderer::Theme:
StyleSheet + container::StyleSheet + scrollable::StyleSheet,
{
- pub fn new<T>(menu: Menu<'a, T, Renderer>, target_height: f32) -> Self
+ pub fn new<T>(
+ menu: Menu<'a, T, Message, Renderer>,
+ target_height: f32,
+ ) -> Self
where
T: Clone + ToString,
{
@@ -163,20 +186,24 @@ where
state,
options,
hovered_option,
- last_selection,
+ on_selected,
width,
padding,
font,
text_size,
+ text_line_height,
+ text_shaping,
style,
} = menu;
let container = Container::new(Scrollable::new(List {
options,
hovered_option,
- last_selection,
+ on_selected,
font,
text_size,
+ text_line_height,
+ text_shaping,
padding,
style: style.clone(),
}));
@@ -193,7 +220,7 @@ where
}
}
-impl<'a, Message, Renderer> crate::Overlay<Message, Renderer>
+impl<'a, Message, Renderer> crate::core::Overlay<Message, Renderer>
for Overlay<'a, Message, Renderer>
where
Renderer: text::Renderer,
@@ -236,36 +263,25 @@ where
&mut self,
event: Event,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> event::Status {
self.container.on_event(
- self.state,
- event,
- layout,
- cursor_position,
- renderer,
- clipboard,
- shell,
+ self.state, event, layout, cursor, renderer, clipboard, shell,
)
}
fn mouse_interaction(
&self,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
- self.container.mouse_interaction(
- self.state,
- layout,
- cursor_position,
- viewport,
- renderer,
- )
+ self.container
+ .mouse_interaction(self.state, layout, cursor, viewport, renderer)
}
fn draw(
@@ -274,7 +290,7 @@ where
theme: &Renderer::Theme,
style: &renderer::Style,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
) {
let appearance = theme.appearance(&self.style);
let bounds = layout.bounds();
@@ -284,39 +300,34 @@ where
bounds,
border_color: appearance.border_color,
border_width: appearance.border_width,
- border_radius: appearance.border_radius.into(),
+ border_radius: appearance.border_radius,
},
appearance.background,
);
- self.container.draw(
- self.state,
- renderer,
- theme,
- style,
- layout,
- cursor_position,
- &bounds,
- );
+ self.container
+ .draw(self.state, renderer, theme, style, layout, cursor, &bounds);
}
}
-struct List<'a, T, Renderer>
+struct List<'a, T, Message, Renderer>
where
Renderer: text::Renderer,
Renderer::Theme: StyleSheet,
{
options: &'a [T],
hovered_option: &'a mut Option<usize>,
- last_selection: &'a mut Option<T>,
+ on_selected: Box<dyn FnMut(T) -> Message + 'a>,
padding: Padding,
text_size: Option<f32>,
- font: Renderer::Font,
+ text_line_height: text::LineHeight,
+ text_shaping: text::Shaping,
+ font: Option<Renderer::Font>,
style: <Renderer::Theme as StyleSheet>::Style,
}
impl<'a, T, Message, Renderer> Widget<Message, Renderer>
- for List<'a, T, Renderer>
+ for List<'a, T, Message, Renderer>
where
T: Clone + ToString,
Renderer: text::Renderer,
@@ -341,10 +352,13 @@ where
let text_size =
self.text_size.unwrap_or_else(|| renderer.default_size());
+ let text_line_height =
+ self.text_line_height.to_absolute(Pixels(text_size));
+
let size = {
let intrinsic = Size::new(
0.0,
- (text_size + self.padding.vertical())
+ (f32::from(text_line_height) + self.padding.vertical())
* self.options.len() as f32,
);
@@ -359,55 +373,57 @@ where
_state: &mut Tree,
event: Event,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
- _shell: &mut Shell<'_, Message>,
+ shell: &mut Shell<'_, Message>,
) -> event::Status {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
- let bounds = layout.bounds();
-
- if bounds.contains(cursor_position) {
+ if cursor.is_over(layout.bounds()) {
if let Some(index) = *self.hovered_option {
if let Some(option) = self.options.get(index) {
- *self.last_selection = Some(option.clone());
+ shell.publish((self.on_selected)(option.clone()));
+ return event::Status::Captured;
}
}
}
}
Event::Mouse(mouse::Event::CursorMoved { .. }) => {
- let bounds = layout.bounds();
-
- if bounds.contains(cursor_position) {
+ if let Some(cursor_position) =
+ cursor.position_in(layout.bounds())
+ {
let text_size = self
.text_size
.unwrap_or_else(|| renderer.default_size());
- *self.hovered_option = Some(
- ((cursor_position.y - bounds.y)
- / (text_size + self.padding.vertical()))
- as usize,
- );
+ let option_height = f32::from(
+ self.text_line_height.to_absolute(Pixels(text_size)),
+ ) + self.padding.vertical();
+
+ *self.hovered_option =
+ Some((cursor_position.y / option_height) as usize);
}
}
Event::Touch(touch::Event::FingerPressed { .. }) => {
- let bounds = layout.bounds();
-
- if bounds.contains(cursor_position) {
+ if let Some(cursor_position) =
+ cursor.position_in(layout.bounds())
+ {
let text_size = self
.text_size
.unwrap_or_else(|| renderer.default_size());
- *self.hovered_option = Some(
- ((cursor_position.y - bounds.y)
- / (text_size + self.padding.vertical()))
- as usize,
- );
+ let option_height = f32::from(
+ self.text_line_height.to_absolute(Pixels(text_size)),
+ ) + self.padding.vertical();
+
+ *self.hovered_option =
+ Some((cursor_position.y / option_height) as usize);
if let Some(index) = *self.hovered_option {
if let Some(option) = self.options.get(index) {
- *self.last_selection = Some(option.clone());
+ shell.publish((self.on_selected)(option.clone()));
+ return event::Status::Captured;
}
}
}
@@ -422,11 +438,11 @@ where
&self,
_state: &Tree,
layout: Layout<'_>,
- cursor_position: Point,
+ cursor: mouse::Cursor,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
- let is_mouse_over = layout.bounds().contains(cursor_position);
+ let is_mouse_over = cursor.is_over(layout.bounds());
if is_mouse_over {
mouse::Interaction::Pointer
@@ -442,7 +458,7 @@ where
theme: &Renderer::Theme,
_style: &renderer::Style,
layout: Layout<'_>,
- _cursor_position: Point,
+ _cursor: mouse::Cursor,
viewport: &Rectangle,
) {
let appearance = theme.appearance(&self.style);
@@ -450,12 +466,13 @@ where
let text_size =
self.text_size.unwrap_or_else(|| renderer.default_size());
- let option_height = (text_size + self.padding.vertical()) as usize;
+ let option_height =
+ f32::from(self.text_line_height.to_absolute(Pixels(text_size)))
+ + self.padding.vertical();
let offset = viewport.y - bounds.y;
- let start = (offset / option_height as f32) as usize;
- let end =
- ((offset + viewport.height) / option_height as f32).ceil() as usize;
+ let start = (offset / option_height) as usize;
+ let end = ((offset + viewport.height) / option_height).ceil() as usize;
let visible_options = &self.options[start..end.min(self.options.len())];
@@ -465,18 +482,22 @@ where
let bounds = Rectangle {
x: bounds.x,
- y: bounds.y + (option_height * i) as f32,
+ y: bounds.y + (option_height * i as f32),
width: bounds.width,
- height: text_size + self.padding.vertical(),
+ height: option_height,
};
if is_selected {
renderer.fill_quad(
renderer::Quad {
- bounds,
+ bounds: Rectangle {
+ x: bounds.x + appearance.border_width,
+ width: bounds.width - appearance.border_width * 2.0,
+ ..bounds
+ },
border_color: Color::TRANSPARENT,
border_width: 0.0,
- border_radius: appearance.border_radius.into(),
+ border_radius: appearance.border_radius,
},
appearance.selected_background,
);
@@ -491,7 +512,8 @@ where
..bounds
},
size: text_size,
- font: self.font.clone(),
+ line_height: self.text_line_height,
+ font: self.font.unwrap_or_else(|| renderer.default_font()),
color: if is_selected {
appearance.selected_text_color
} else {
@@ -499,12 +521,13 @@ where
},
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Center,
+ shaping: self.text_shaping,
});
}
}
}
-impl<'a, T, Message, Renderer> From<List<'a, T, Renderer>>
+impl<'a, T, Message, Renderer> From<List<'a, T, Message, Renderer>>
for Element<'a, Message, Renderer>
where
T: ToString + Clone,
@@ -512,7 +535,7 @@ where
Renderer: 'a + text::Renderer,
Renderer::Theme: StyleSheet,
{
- fn from(list: List<'a, T, Renderer>) -> Self {
+ fn from(list: List<'a, T, Message, Renderer>) -> Self {
Element::new(list)
}
}