Merge pull request #2405 from iced-rs/feature/stack-widget
`Stack` widget
This commit is contained in:
commit
2d01d55cbc
17 changed files with 609 additions and 359 deletions
|
|
@ -54,7 +54,7 @@ impl<'a> Layout<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the [`Layout`] of the children of a [`Node`].
|
/// Returns an iterator over the [`Layout`] of the children of a [`Node`].
|
||||||
pub fn children(self) -> impl Iterator<Item = Layout<'a>> {
|
pub fn children(self) -> impl DoubleEndedIterator<Item = Layout<'a>> {
|
||||||
self.node.children().iter().map(move |node| {
|
self.node.children().iter().map(move |node| {
|
||||||
Layout::with_offset(
|
Layout::with_offset(
|
||||||
Vector::new(self.position.x, self.position.y),
|
Vector::new(self.position.x, self.position.y),
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum Interaction {
|
pub enum Interaction {
|
||||||
#[default]
|
#[default]
|
||||||
|
None,
|
||||||
Idle,
|
Idle,
|
||||||
Pointer,
|
Pointer,
|
||||||
Grab,
|
Grab,
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ where
|
||||||
_viewport: &Rectangle,
|
_viewport: &Rectangle,
|
||||||
_renderer: &Renderer,
|
_renderer: &Renderer,
|
||||||
) -> mouse::Interaction {
|
) -> mouse::Interaction {
|
||||||
mouse::Interaction::Idle
|
mouse::Interaction::None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if the cursor is over the [`Overlay`].
|
/// Returns true if the cursor is over the [`Overlay`].
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ where
|
||||||
_viewport: &Rectangle,
|
_viewport: &Rectangle,
|
||||||
_renderer: &Renderer,
|
_renderer: &Renderer,
|
||||||
) -> mouse::Interaction {
|
) -> mouse::Interaction {
|
||||||
mouse::Interaction::Idle
|
mouse::Interaction::None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the overlay of the [`Widget`], if there is any.
|
/// Returns the overlay of the [`Widget`], if there is any.
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
//! This example showcases an interactive `Canvas` for drawing Bézier curves.
|
//! This example showcases an interactive `Canvas` for drawing Bézier curves.
|
||||||
use iced::widget::{button, column, text};
|
use iced::alignment;
|
||||||
use iced::{Alignment, Element, Length};
|
use iced::widget::{button, container, stack};
|
||||||
|
use iced::{Element, Length, Theme};
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
iced::program("Bezier Tool - Iced", Example::update, Example::view)
|
iced::program("Bezier Tool - Iced", Example::update, Example::view)
|
||||||
|
.theme(|_| Theme::CatppuccinMocha)
|
||||||
.antialiasing(true)
|
.antialiasing(true)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
@ -35,16 +37,18 @@ impl Example {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self) -> Element<Message> {
|
fn view(&self) -> Element<Message> {
|
||||||
column![
|
container(stack![
|
||||||
text("Bezier tool example").width(Length::Shrink).size(50),
|
|
||||||
self.bezier.view(&self.curves).map(Message::AddCurve),
|
self.bezier.view(&self.curves).map(Message::AddCurve),
|
||||||
button("Clear")
|
container(
|
||||||
.style(button::danger)
|
button("Clear")
|
||||||
.on_press(Message::Clear),
|
.style(button::danger)
|
||||||
]
|
.on_press(Message::Clear)
|
||||||
|
)
|
||||||
|
.padding(10)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.align_x(alignment::Horizontal::Right),
|
||||||
|
])
|
||||||
.padding(20)
|
.padding(20)
|
||||||
.spacing(20)
|
|
||||||
.align_items(Alignment::Center)
|
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -139,22 +143,24 @@ mod bezier {
|
||||||
&self,
|
&self,
|
||||||
state: &Self::State,
|
state: &Self::State,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
_theme: &Theme,
|
theme: &Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
) -> Vec<Geometry> {
|
) -> Vec<Geometry> {
|
||||||
let content =
|
let content =
|
||||||
self.state.cache.draw(renderer, bounds.size(), |frame| {
|
self.state.cache.draw(renderer, bounds.size(), |frame| {
|
||||||
Curve::draw_all(self.curves, frame);
|
Curve::draw_all(self.curves, frame, theme);
|
||||||
|
|
||||||
frame.stroke(
|
frame.stroke(
|
||||||
&Path::rectangle(Point::ORIGIN, frame.size()),
|
&Path::rectangle(Point::ORIGIN, frame.size()),
|
||||||
Stroke::default().with_width(2.0),
|
Stroke::default()
|
||||||
|
.with_width(2.0)
|
||||||
|
.with_color(theme.palette().text),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(pending) = state {
|
if let Some(pending) = state {
|
||||||
vec![content, pending.draw(renderer, bounds, cursor)]
|
vec![content, pending.draw(renderer, theme, bounds, cursor)]
|
||||||
} else {
|
} else {
|
||||||
vec![content]
|
vec![content]
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +188,7 @@ mod bezier {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Curve {
|
impl Curve {
|
||||||
fn draw_all(curves: &[Curve], frame: &mut Frame) {
|
fn draw_all(curves: &[Curve], frame: &mut Frame, theme: &Theme) {
|
||||||
let curves = Path::new(|p| {
|
let curves = Path::new(|p| {
|
||||||
for curve in curves {
|
for curve in curves {
|
||||||
p.move_to(curve.from);
|
p.move_to(curve.from);
|
||||||
|
|
@ -190,7 +196,12 @@ mod bezier {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
frame.stroke(&curves, Stroke::default().with_width(2.0));
|
frame.stroke(
|
||||||
|
&curves,
|
||||||
|
Stroke::default()
|
||||||
|
.with_width(2.0)
|
||||||
|
.with_color(theme.palette().text),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,6 +215,7 @@ mod bezier {
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
|
theme: &Theme,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
) -> Geometry {
|
) -> Geometry {
|
||||||
|
|
@ -213,7 +225,12 @@ mod bezier {
|
||||||
match *self {
|
match *self {
|
||||||
Pending::One { from } => {
|
Pending::One { from } => {
|
||||||
let line = Path::line(from, cursor_position);
|
let line = Path::line(from, cursor_position);
|
||||||
frame.stroke(&line, Stroke::default().with_width(2.0));
|
frame.stroke(
|
||||||
|
&line,
|
||||||
|
Stroke::default()
|
||||||
|
.with_width(2.0)
|
||||||
|
.with_color(theme.palette().text),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Pending::Two { from, to } => {
|
Pending::Two { from, to } => {
|
||||||
let curve = Curve {
|
let curve = Curve {
|
||||||
|
|
@ -222,7 +239,7 @@ mod bezier {
|
||||||
control: cursor_position,
|
control: cursor_position,
|
||||||
};
|
};
|
||||||
|
|
||||||
Curve::draw_all(&[curve], &mut frame);
|
Curve::draw_all(&[curve], &mut frame, theme);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ mod loupe {
|
||||||
if cursor.is_over(layout.bounds()) {
|
if cursor.is_over(layout.bounds()) {
|
||||||
mouse::Interaction::ZoomIn
|
mouse::Interaction::ZoomIn
|
||||||
} else {
|
} else {
|
||||||
mouse::Interaction::Idle
|
mouse::Interaction::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,11 @@ use iced::event::{self, Event};
|
||||||
use iced::keyboard;
|
use iced::keyboard;
|
||||||
use iced::keyboard::key;
|
use iced::keyboard::key;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
self, button, column, container, horizontal_space, pick_list, row, text,
|
self, button, column, container, horizontal_space, mouse_area, opaque,
|
||||||
text_input,
|
pick_list, row, stack, text, text_input,
|
||||||
};
|
};
|
||||||
use iced::{Alignment, Command, Element, Length, Subscription};
|
use iced::{Alignment, Color, Command, Element, Length, Subscription};
|
||||||
|
|
||||||
use modal::Modal;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
|
|
@ -121,7 +120,7 @@ impl App {
|
||||||
.height(Length::Fill);
|
.height(Length::Fill);
|
||||||
|
|
||||||
if self.show_modal {
|
if self.show_modal {
|
||||||
let modal = container(
|
let signup = container(
|
||||||
column![
|
column![
|
||||||
text("Sign Up").size(24),
|
text("Sign Up").size(24),
|
||||||
column![
|
column![
|
||||||
|
|
@ -162,9 +161,7 @@ impl App {
|
||||||
.padding(10)
|
.padding(10)
|
||||||
.style(container::rounded_box);
|
.style(container::rounded_box);
|
||||||
|
|
||||||
Modal::new(content, modal)
|
modal(content, signup, Message::HideModal)
|
||||||
.on_blur(Message::HideModal)
|
|
||||||
.into()
|
|
||||||
} else {
|
} else {
|
||||||
content.into()
|
content.into()
|
||||||
}
|
}
|
||||||
|
|
@ -203,326 +200,36 @@ impl fmt::Display for Plan {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod modal {
|
fn modal<'a, Message>(
|
||||||
use iced::advanced::layout::{self, Layout};
|
base: impl Into<Element<'a, Message>>,
|
||||||
use iced::advanced::overlay;
|
content: impl Into<Element<'a, Message>>,
|
||||||
use iced::advanced::renderer;
|
on_blur: Message,
|
||||||
use iced::advanced::widget::{self, Widget};
|
) -> Element<'a, Message>
|
||||||
use iced::advanced::{self, Clipboard, Shell};
|
where
|
||||||
use iced::alignment::Alignment;
|
Message: Clone + 'a,
|
||||||
use iced::event;
|
{
|
||||||
use iced::mouse;
|
stack![
|
||||||
use iced::{Color, Element, Event, Length, Point, Rectangle, Size, Vector};
|
base.into(),
|
||||||
|
mouse_area(
|
||||||
/// A widget that centers a modal element over some base element
|
container(opaque(content))
|
||||||
pub struct Modal<'a, Message, Theme, Renderer> {
|
|
||||||
base: Element<'a, Message, Theme, Renderer>,
|
|
||||||
modal: Element<'a, Message, Theme, Renderer>,
|
|
||||||
on_blur: Option<Message>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message, Theme, Renderer> Modal<'a, Message, Theme, Renderer> {
|
|
||||||
/// Returns a new [`Modal`]
|
|
||||||
pub fn new(
|
|
||||||
base: impl Into<Element<'a, Message, Theme, Renderer>>,
|
|
||||||
modal: impl Into<Element<'a, Message, Theme, Renderer>>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
base: base.into(),
|
|
||||||
modal: modal.into(),
|
|
||||||
on_blur: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the message that will be produces when the background
|
|
||||||
/// of the [`Modal`] is pressed
|
|
||||||
pub fn on_blur(self, on_blur: Message) -> Self {
|
|
||||||
Self {
|
|
||||||
on_blur: Some(on_blur),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
|
||||||
for Modal<'a, Message, Theme, Renderer>
|
|
||||||
where
|
|
||||||
Renderer: advanced::Renderer,
|
|
||||||
Message: Clone,
|
|
||||||
{
|
|
||||||
fn children(&self) -> Vec<widget::Tree> {
|
|
||||||
vec![
|
|
||||||
widget::Tree::new(&self.base),
|
|
||||||
widget::Tree::new(&self.modal),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn diff(&self, tree: &mut widget::Tree) {
|
|
||||||
tree.diff_children(&[&self.base, &self.modal]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> Size<Length> {
|
|
||||||
self.base.as_widget().size()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&self,
|
|
||||||
tree: &mut widget::Tree,
|
|
||||||
renderer: &Renderer,
|
|
||||||
limits: &layout::Limits,
|
|
||||||
) -> layout::Node {
|
|
||||||
self.base.as_widget().layout(
|
|
||||||
&mut tree.children[0],
|
|
||||||
renderer,
|
|
||||||
limits,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_event(
|
|
||||||
&mut self,
|
|
||||||
state: &mut widget::Tree,
|
|
||||||
event: Event,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
renderer: &Renderer,
|
|
||||||
clipboard: &mut dyn Clipboard,
|
|
||||||
shell: &mut Shell<'_, Message>,
|
|
||||||
viewport: &Rectangle,
|
|
||||||
) -> event::Status {
|
|
||||||
self.base.as_widget_mut().on_event(
|
|
||||||
&mut state.children[0],
|
|
||||||
event,
|
|
||||||
layout,
|
|
||||||
cursor,
|
|
||||||
renderer,
|
|
||||||
clipboard,
|
|
||||||
shell,
|
|
||||||
viewport,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
state: &widget::Tree,
|
|
||||||
renderer: &mut Renderer,
|
|
||||||
theme: &Theme,
|
|
||||||
style: &renderer::Style,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
viewport: &Rectangle,
|
|
||||||
) {
|
|
||||||
self.base.as_widget().draw(
|
|
||||||
&state.children[0],
|
|
||||||
renderer,
|
|
||||||
theme,
|
|
||||||
style,
|
|
||||||
layout,
|
|
||||||
cursor,
|
|
||||||
viewport,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overlay<'b>(
|
|
||||||
&'b mut self,
|
|
||||||
state: &'b mut widget::Tree,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
_renderer: &Renderer,
|
|
||||||
translation: Vector,
|
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
|
||||||
Some(overlay::Element::new(Box::new(Overlay {
|
|
||||||
position: layout.position() + translation,
|
|
||||||
content: &mut self.modal,
|
|
||||||
tree: &mut state.children[1],
|
|
||||||
size: layout.bounds().size(),
|
|
||||||
on_blur: self.on_blur.clone(),
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_interaction(
|
|
||||||
&self,
|
|
||||||
state: &widget::Tree,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
viewport: &Rectangle,
|
|
||||||
renderer: &Renderer,
|
|
||||||
) -> mouse::Interaction {
|
|
||||||
self.base.as_widget().mouse_interaction(
|
|
||||||
&state.children[0],
|
|
||||||
layout,
|
|
||||||
cursor,
|
|
||||||
viewport,
|
|
||||||
renderer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(
|
|
||||||
&self,
|
|
||||||
state: &mut widget::Tree,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
renderer: &Renderer,
|
|
||||||
operation: &mut dyn widget::Operation<Message>,
|
|
||||||
) {
|
|
||||||
self.base.as_widget().operate(
|
|
||||||
&mut state.children[0],
|
|
||||||
layout,
|
|
||||||
renderer,
|
|
||||||
operation,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Overlay<'a, 'b, Message, Theme, Renderer> {
|
|
||||||
position: Point,
|
|
||||||
content: &'b mut Element<'a, Message, Theme, Renderer>,
|
|
||||||
tree: &'b mut widget::Tree,
|
|
||||||
size: Size,
|
|
||||||
on_blur: Option<Message>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, Message, Theme, Renderer>
|
|
||||||
overlay::Overlay<Message, Theme, Renderer>
|
|
||||||
for Overlay<'a, 'b, Message, Theme, Renderer>
|
|
||||||
where
|
|
||||||
Renderer: advanced::Renderer,
|
|
||||||
Message: Clone,
|
|
||||||
{
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
renderer: &Renderer,
|
|
||||||
_bounds: Size,
|
|
||||||
) -> layout::Node {
|
|
||||||
let limits = layout::Limits::new(Size::ZERO, self.size)
|
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill);
|
.height(Length::Fill)
|
||||||
|
.center_x()
|
||||||
let child = self
|
.center_y()
|
||||||
.content
|
.style(|_theme| {
|
||||||
.as_widget()
|
container::Style {
|
||||||
.layout(self.tree, renderer, &limits)
|
background: Some(
|
||||||
.align(Alignment::Center, Alignment::Center, limits.max());
|
Color {
|
||||||
|
a: 0.8,
|
||||||
layout::Node::with_children(self.size, vec![child])
|
..Color::BLACK
|
||||||
.move_to(self.position)
|
}
|
||||||
}
|
.into(),
|
||||||
|
),
|
||||||
fn on_event(
|
..container::Style::default()
|
||||||
&mut self,
|
|
||||||
event: Event,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
renderer: &Renderer,
|
|
||||||
clipboard: &mut dyn Clipboard,
|
|
||||||
shell: &mut Shell<'_, Message>,
|
|
||||||
) -> event::Status {
|
|
||||||
let content_bounds = layout.children().next().unwrap().bounds();
|
|
||||||
|
|
||||||
if let Some(message) = self.on_blur.as_ref() {
|
|
||||||
if let Event::Mouse(mouse::Event::ButtonPressed(
|
|
||||||
mouse::Button::Left,
|
|
||||||
)) = &event
|
|
||||||
{
|
|
||||||
if !cursor.is_over(content_bounds) {
|
|
||||||
shell.publish(message.clone());
|
|
||||||
return event::Status::Captured;
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
)
|
||||||
|
.on_press(on_blur)
|
||||||
self.content.as_widget_mut().on_event(
|
]
|
||||||
self.tree,
|
.into()
|
||||||
event,
|
|
||||||
layout.children().next().unwrap(),
|
|
||||||
cursor,
|
|
||||||
renderer,
|
|
||||||
clipboard,
|
|
||||||
shell,
|
|
||||||
&layout.bounds(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
renderer: &mut Renderer,
|
|
||||||
theme: &Theme,
|
|
||||||
style: &renderer::Style,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
) {
|
|
||||||
renderer.fill_quad(
|
|
||||||
renderer::Quad {
|
|
||||||
bounds: layout.bounds(),
|
|
||||||
..renderer::Quad::default()
|
|
||||||
},
|
|
||||||
Color {
|
|
||||||
a: 0.80,
|
|
||||||
..Color::BLACK
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
self.content.as_widget().draw(
|
|
||||||
self.tree,
|
|
||||||
renderer,
|
|
||||||
theme,
|
|
||||||
style,
|
|
||||||
layout.children().next().unwrap(),
|
|
||||||
cursor,
|
|
||||||
&layout.bounds(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn operate(
|
|
||||||
&mut self,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
renderer: &Renderer,
|
|
||||||
operation: &mut dyn widget::Operation<Message>,
|
|
||||||
) {
|
|
||||||
self.content.as_widget().operate(
|
|
||||||
self.tree,
|
|
||||||
layout.children().next().unwrap(),
|
|
||||||
renderer,
|
|
||||||
operation,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_interaction(
|
|
||||||
&self,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
cursor: mouse::Cursor,
|
|
||||||
viewport: &Rectangle,
|
|
||||||
renderer: &Renderer,
|
|
||||||
) -> mouse::Interaction {
|
|
||||||
self.content.as_widget().mouse_interaction(
|
|
||||||
self.tree,
|
|
||||||
layout.children().next().unwrap(),
|
|
||||||
cursor,
|
|
||||||
viewport,
|
|
||||||
renderer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overlay<'c>(
|
|
||||||
&'c mut self,
|
|
||||||
layout: Layout<'_>,
|
|
||||||
renderer: &Renderer,
|
|
||||||
) -> Option<overlay::Element<'c, Message, Theme, Renderer>> {
|
|
||||||
self.content.as_widget_mut().overlay(
|
|
||||||
self.tree,
|
|
||||||
layout.children().next().unwrap(),
|
|
||||||
renderer,
|
|
||||||
Vector::ZERO,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message, Theme, Renderer> From<Modal<'a, Message, Theme, Renderer>>
|
|
||||||
for Element<'a, Message, Theme, Renderer>
|
|
||||||
where
|
|
||||||
Theme: 'a,
|
|
||||||
Message: 'a + Clone,
|
|
||||||
Renderer: 'a + advanced::Renderer,
|
|
||||||
{
|
|
||||||
fn from(modal: Modal<'a, Message, Theme, Renderer>) -> Self {
|
|
||||||
Element::new(modal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ where
|
||||||
caches,
|
caches,
|
||||||
queued_events: Vec::new(),
|
queued_events: Vec::new(),
|
||||||
queued_messages: Vec::new(),
|
queued_messages: Vec::new(),
|
||||||
mouse_interaction: mouse::Interaction::Idle,
|
mouse_interaction: mouse::Interaction::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ where
|
||||||
cache,
|
cache,
|
||||||
queued_events: Vec::new(),
|
queued_events: Vec::new(),
|
||||||
queued_messages: Vec::new(),
|
queued_messages: Vec::new(),
|
||||||
mouse_interaction: mouse::Interaction::Idle,
|
mouse_interaction: mouse::Interaction::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::combo_box::{self, ComboBox};
|
||||||
use crate::container::{self, Container};
|
use crate::container::{self, Container};
|
||||||
use crate::core;
|
use crate::core;
|
||||||
use crate::core::widget::operation;
|
use crate::core::widget::operation;
|
||||||
use crate::core::{Element, Length, Pixels};
|
use crate::core::{Element, Length, Pixels, Widget};
|
||||||
use crate::keyed;
|
use crate::keyed;
|
||||||
use crate::overlay;
|
use crate::overlay;
|
||||||
use crate::pick_list::{self, PickList};
|
use crate::pick_list::{self, PickList};
|
||||||
|
|
@ -21,7 +21,7 @@ use crate::text_input::{self, TextInput};
|
||||||
use crate::toggler::{self, Toggler};
|
use crate::toggler::{self, Toggler};
|
||||||
use crate::tooltip::{self, Tooltip};
|
use crate::tooltip::{self, Tooltip};
|
||||||
use crate::vertical_slider::{self, VerticalSlider};
|
use crate::vertical_slider::{self, VerticalSlider};
|
||||||
use crate::{Column, MouseArea, Row, Space, Themer};
|
use crate::{Column, MouseArea, Row, Space, Stack, Themer};
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
|
|
@ -52,6 +52,19 @@ macro_rules! row {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Stack`] with the given children.
|
||||||
|
///
|
||||||
|
/// [`Stack`]: crate::Stack
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! stack {
|
||||||
|
() => (
|
||||||
|
$crate::Stack::new()
|
||||||
|
);
|
||||||
|
($($x:expr),+ $(,)?) => (
|
||||||
|
$crate::Stack::with_children([$($crate::core::Element::from($x)),+])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new [`Container`] with the provided content.
|
/// Creates a new [`Container`] with the provided content.
|
||||||
///
|
///
|
||||||
/// [`Container`]: crate::Container
|
/// [`Container`]: crate::Container
|
||||||
|
|
@ -98,6 +111,185 @@ where
|
||||||
Row::with_children(children)
|
Row::with_children(children)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`Stack`] with the given children.
|
||||||
|
///
|
||||||
|
/// [`Stack`]: crate::Stack
|
||||||
|
pub fn stack<'a, Message, Theme, Renderer>(
|
||||||
|
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
|
||||||
|
) -> Stack<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: core::Renderer,
|
||||||
|
{
|
||||||
|
Stack::with_children(children)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps the given widget and captures any mouse button presses inside the bounds of
|
||||||
|
/// the widget—therefore making it _opaque_.
|
||||||
|
///
|
||||||
|
/// This helper is meant to be used to mark elements in a [`Stack`] to avoid mouse
|
||||||
|
/// events from passing through layers.
|
||||||
|
///
|
||||||
|
/// [`Stack`]: crate::Stack
|
||||||
|
pub fn opaque<'a, Message, Theme, Renderer>(
|
||||||
|
content: impl Into<Element<'a, Message, Theme, Renderer>>,
|
||||||
|
) -> Element<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Message: 'a,
|
||||||
|
Theme: 'a,
|
||||||
|
Renderer: core::Renderer + 'a,
|
||||||
|
{
|
||||||
|
use crate::core::event::{self, Event};
|
||||||
|
use crate::core::layout::{self, Layout};
|
||||||
|
use crate::core::mouse;
|
||||||
|
use crate::core::renderer;
|
||||||
|
use crate::core::widget::tree::{self, Tree};
|
||||||
|
use crate::core::{Rectangle, Shell, Size};
|
||||||
|
|
||||||
|
struct Opaque<'a, Message, Theme, Renderer> {
|
||||||
|
content: Element<'a, Message, Theme, Renderer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||||
|
for Opaque<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: core::Renderer,
|
||||||
|
{
|
||||||
|
fn tag(&self) -> tree::Tag {
|
||||||
|
self.content.as_widget().tag()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state(&self) -> tree::State {
|
||||||
|
self.content.as_widget().state()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn children(&self) -> Vec<Tree> {
|
||||||
|
self.content.as_widget().children()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff(&self, tree: &mut Tree) {
|
||||||
|
self.content.as_widget().diff(tree);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> Size<Length> {
|
||||||
|
self.content.as_widget().size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size_hint(&self) -> Size<Length> {
|
||||||
|
self.content.as_widget().size_hint()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
self.content.as_widget().layout(tree, renderer, limits)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&self,
|
||||||
|
tree: &Tree,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
theme: &Theme,
|
||||||
|
style: &renderer::Style,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor: mouse::Cursor,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
) {
|
||||||
|
self.content
|
||||||
|
.as_widget()
|
||||||
|
.draw(tree, renderer, theme, style, layout, cursor, viewport);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn operate(
|
||||||
|
&self,
|
||||||
|
state: &mut Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
operation: &mut dyn operation::Operation<Message>,
|
||||||
|
) {
|
||||||
|
self.content
|
||||||
|
.as_widget()
|
||||||
|
.operate(state, layout, renderer, operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(
|
||||||
|
&mut self,
|
||||||
|
state: &mut Tree,
|
||||||
|
event: Event,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor: mouse::Cursor,
|
||||||
|
renderer: &Renderer,
|
||||||
|
clipboard: &mut dyn core::Clipboard,
|
||||||
|
shell: &mut Shell<'_, Message>,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
) -> event::Status {
|
||||||
|
let is_mouse_press = matches!(
|
||||||
|
event,
|
||||||
|
core::Event::Mouse(mouse::Event::ButtonPressed(_))
|
||||||
|
);
|
||||||
|
|
||||||
|
if let core::event::Status::Captured =
|
||||||
|
self.content.as_widget_mut().on_event(
|
||||||
|
state, event, layout, cursor, renderer, clipboard, shell,
|
||||||
|
viewport,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return event::Status::Captured;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_mouse_press && cursor.is_over(layout.bounds()) {
|
||||||
|
event::Status::Captured
|
||||||
|
} else {
|
||||||
|
event::Status::Ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_interaction(
|
||||||
|
&self,
|
||||||
|
state: &core::widget::Tree,
|
||||||
|
layout: core::Layout<'_>,
|
||||||
|
cursor: core::mouse::Cursor,
|
||||||
|
viewport: &core::Rectangle,
|
||||||
|
renderer: &Renderer,
|
||||||
|
) -> core::mouse::Interaction {
|
||||||
|
let interaction = self
|
||||||
|
.content
|
||||||
|
.as_widget()
|
||||||
|
.mouse_interaction(state, layout, cursor, viewport, renderer);
|
||||||
|
|
||||||
|
if interaction == mouse::Interaction::None
|
||||||
|
&& cursor.is_over(layout.bounds())
|
||||||
|
{
|
||||||
|
mouse::Interaction::Idle
|
||||||
|
} else {
|
||||||
|
interaction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overlay<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
state: &'b mut core::widget::Tree,
|
||||||
|
layout: core::Layout<'_>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
translation: core::Vector,
|
||||||
|
) -> Option<core::overlay::Element<'b, Message, Theme, Renderer>>
|
||||||
|
{
|
||||||
|
self.content.as_widget_mut().overlay(
|
||||||
|
state,
|
||||||
|
layout,
|
||||||
|
renderer,
|
||||||
|
translation,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Element::new(Opaque {
|
||||||
|
content: content.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new [`Scrollable`] with the provided content.
|
/// Creates a new [`Scrollable`] with the provided content.
|
||||||
///
|
///
|
||||||
/// [`Scrollable`]: crate::Scrollable
|
/// [`Scrollable`]: crate::Scrollable
|
||||||
|
|
|
||||||
|
|
@ -304,7 +304,7 @@ where
|
||||||
} else if is_mouse_over {
|
} else if is_mouse_over {
|
||||||
mouse::Interaction::Grab
|
mouse::Interaction::Grab
|
||||||
} else {
|
} else {
|
||||||
mouse::Interaction::Idle
|
mouse::Interaction::None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ mod column;
|
||||||
mod mouse_area;
|
mod mouse_area;
|
||||||
mod row;
|
mod row;
|
||||||
mod space;
|
mod space;
|
||||||
|
mod stack;
|
||||||
mod themer;
|
mod themer;
|
||||||
|
|
||||||
pub mod button;
|
pub mod button;
|
||||||
|
|
@ -78,6 +79,8 @@ pub use slider::Slider;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use space::Space;
|
pub use space::Space;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
|
pub use stack::Stack;
|
||||||
|
#[doc(no_inline)]
|
||||||
pub use text::Text;
|
pub use text::Text;
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use text_editor::TextEditor;
|
pub use text_editor::TextEditor;
|
||||||
|
|
|
||||||
|
|
@ -232,7 +232,7 @@ where
|
||||||
);
|
);
|
||||||
|
|
||||||
match (self.interaction, content_interaction) {
|
match (self.interaction, content_interaction) {
|
||||||
(Some(interaction), mouse::Interaction::Idle)
|
(Some(interaction), mouse::Interaction::None)
|
||||||
if cursor.is_over(layout.bounds()) =>
|
if cursor.is_over(layout.bounds()) =>
|
||||||
{
|
{
|
||||||
interaction
|
interaction
|
||||||
|
|
|
||||||
|
|
@ -857,7 +857,7 @@ where
|
||||||
if (mouse_over_x_scrollbar || mouse_over_y_scrollbar)
|
if (mouse_over_x_scrollbar || mouse_over_y_scrollbar)
|
||||||
|| state.scrollers_grabbed()
|
|| state.scrollers_grabbed()
|
||||||
{
|
{
|
||||||
mouse::Interaction::Idle
|
mouse::Interaction::None
|
||||||
} else {
|
} else {
|
||||||
let translation =
|
let translation =
|
||||||
state.translation(self.direction, bounds, content_bounds);
|
state.translation(self.direction, bounds, content_bounds);
|
||||||
|
|
|
||||||
328
widget/src/stack.rs
Normal file
328
widget/src/stack.rs
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
//! Display content on top of other content.
|
||||||
|
use crate::core::event::{self, Event};
|
||||||
|
use crate::core::layout;
|
||||||
|
use crate::core::mouse;
|
||||||
|
use crate::core::overlay;
|
||||||
|
use crate::core::renderer;
|
||||||
|
use crate::core::widget::{Operation, Tree};
|
||||||
|
use crate::core::{
|
||||||
|
Clipboard, Element, Layout, Length, Rectangle, Shell, Size, Vector, Widget,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A container that displays children on top of each other.
|
||||||
|
///
|
||||||
|
/// The first [`Element`] dictates the intrinsic [`Size`] of a [`Stack`] and
|
||||||
|
/// will be displayed as the base layer. Every consecutive [`Element`] will be
|
||||||
|
/// renderer on top; on its own layer.
|
||||||
|
///
|
||||||
|
/// Keep in mind that too much layering will normally produce bad UX as well as
|
||||||
|
/// introduce certain rendering overhead. Use this widget sparingly!
|
||||||
|
#[allow(missing_debug_implementations)]
|
||||||
|
pub struct Stack<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
|
||||||
|
{
|
||||||
|
width: Length,
|
||||||
|
height: Length,
|
||||||
|
children: Vec<Element<'a, Message, Theme, Renderer>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Theme, Renderer> Stack<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: crate::core::Renderer,
|
||||||
|
{
|
||||||
|
/// Creates an empty [`Stack`].
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::from_vec(Vec::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Stack`] with the given capacity.
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
|
Self::from_vec(Vec::with_capacity(capacity))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Stack`] with the given elements.
|
||||||
|
pub fn with_children(
|
||||||
|
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
|
||||||
|
) -> Self {
|
||||||
|
let iterator = children.into_iter();
|
||||||
|
|
||||||
|
Self::with_capacity(iterator.size_hint().0).extend(iterator)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Stack`] from an already allocated [`Vec`].
|
||||||
|
///
|
||||||
|
/// Keep in mind that the [`Stack`] will not inspect the [`Vec`], which means
|
||||||
|
/// it won't automatically adapt to the sizing strategy of its contents.
|
||||||
|
///
|
||||||
|
/// If any of the children have a [`Length::Fill`] strategy, you will need to
|
||||||
|
/// call [`Stack::width`] or [`Stack::height`] accordingly.
|
||||||
|
pub fn from_vec(
|
||||||
|
children: Vec<Element<'a, Message, Theme, Renderer>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
width: Length::Shrink,
|
||||||
|
height: Length::Shrink,
|
||||||
|
children,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the width of the [`Stack`].
|
||||||
|
pub fn width(mut self, width: impl Into<Length>) -> Self {
|
||||||
|
self.width = width.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the height of the [`Stack`].
|
||||||
|
pub fn height(mut self, height: impl Into<Length>) -> Self {
|
||||||
|
self.height = height.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an element to the [`Stack`].
|
||||||
|
pub fn push(
|
||||||
|
mut self,
|
||||||
|
child: impl Into<Element<'a, Message, Theme, Renderer>>,
|
||||||
|
) -> Self {
|
||||||
|
let child = child.into();
|
||||||
|
|
||||||
|
if self.children.is_empty() {
|
||||||
|
let child_size = child.as_widget().size_hint();
|
||||||
|
|
||||||
|
self.width = self.width.enclose(child_size.width);
|
||||||
|
self.height = self.height.enclose(child_size.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.children.push(child);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds an element to the [`Stack`], if `Some`.
|
||||||
|
pub fn push_maybe(
|
||||||
|
self,
|
||||||
|
child: Option<impl Into<Element<'a, Message, Theme, Renderer>>>,
|
||||||
|
) -> Self {
|
||||||
|
if let Some(child) = child {
|
||||||
|
self.push(child)
|
||||||
|
} else {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extends the [`Stack`] with the given children.
|
||||||
|
pub fn extend(
|
||||||
|
self,
|
||||||
|
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
|
||||||
|
) -> Self {
|
||||||
|
children.into_iter().fold(self, Self::push)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Renderer> Default for Stack<'a, Message, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: crate::core::Renderer,
|
||||||
|
{
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||||
|
for Stack<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Renderer: crate::core::Renderer,
|
||||||
|
{
|
||||||
|
fn children(&self) -> Vec<Tree> {
|
||||||
|
self.children.iter().map(Tree::new).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diff(&self, tree: &mut Tree) {
|
||||||
|
tree.diff_children(&self.children);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&self) -> Size<Length> {
|
||||||
|
Size {
|
||||||
|
width: self.width,
|
||||||
|
height: self.height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
renderer: &Renderer,
|
||||||
|
limits: &layout::Limits,
|
||||||
|
) -> layout::Node {
|
||||||
|
if self.children.is_empty() {
|
||||||
|
return layout::Node::new(Size::ZERO);
|
||||||
|
}
|
||||||
|
|
||||||
|
let limits = limits.width(self.width).height(self.height);
|
||||||
|
let base = self.children[0].as_widget().layout(
|
||||||
|
&mut tree.children[0],
|
||||||
|
renderer,
|
||||||
|
&limits,
|
||||||
|
);
|
||||||
|
|
||||||
|
let size = limits.resolve(self.width, self.height, base.size());
|
||||||
|
let limits = layout::Limits::new(Size::ZERO, size);
|
||||||
|
|
||||||
|
let nodes = std::iter::once(base)
|
||||||
|
.chain(self.children[1..].iter().zip(&mut tree.children[1..]).map(
|
||||||
|
|(layer, tree)| {
|
||||||
|
let node =
|
||||||
|
layer.as_widget().layout(tree, renderer, &limits);
|
||||||
|
|
||||||
|
node
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
layout::Node::with_children(size, nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn operate(
|
||||||
|
&self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
operation: &mut dyn Operation<Message>,
|
||||||
|
) {
|
||||||
|
operation.container(None, layout.bounds(), &mut |operation| {
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.zip(&mut tree.children)
|
||||||
|
.zip(layout.children())
|
||||||
|
.for_each(|((child, state), layout)| {
|
||||||
|
child
|
||||||
|
.as_widget()
|
||||||
|
.operate(state, layout, renderer, operation);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(
|
||||||
|
&mut self,
|
||||||
|
tree: &mut Tree,
|
||||||
|
event: Event,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor: mouse::Cursor,
|
||||||
|
renderer: &Renderer,
|
||||||
|
clipboard: &mut dyn Clipboard,
|
||||||
|
shell: &mut Shell<'_, Message>,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
) -> event::Status {
|
||||||
|
self.children
|
||||||
|
.iter_mut()
|
||||||
|
.rev()
|
||||||
|
.zip(tree.children.iter_mut().rev())
|
||||||
|
.zip(layout.children().rev())
|
||||||
|
.map(|((child, state), layout)| {
|
||||||
|
child.as_widget_mut().on_event(
|
||||||
|
state,
|
||||||
|
event.clone(),
|
||||||
|
layout,
|
||||||
|
cursor,
|
||||||
|
renderer,
|
||||||
|
clipboard,
|
||||||
|
shell,
|
||||||
|
viewport,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.find(|&status| status == event::Status::Captured)
|
||||||
|
.unwrap_or(event::Status::Ignored)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_interaction(
|
||||||
|
&self,
|
||||||
|
tree: &Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor: mouse::Cursor,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
renderer: &Renderer,
|
||||||
|
) -> mouse::Interaction {
|
||||||
|
self.children
|
||||||
|
.iter()
|
||||||
|
.rev()
|
||||||
|
.zip(tree.children.iter().rev())
|
||||||
|
.zip(layout.children().rev())
|
||||||
|
.map(|((child, state), layout)| {
|
||||||
|
child.as_widget().mouse_interaction(
|
||||||
|
state, layout, cursor, viewport, renderer,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.find(|&interaction| interaction != mouse::Interaction::None)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&self,
|
||||||
|
tree: &Tree,
|
||||||
|
renderer: &mut Renderer,
|
||||||
|
theme: &Theme,
|
||||||
|
style: &renderer::Style,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
cursor: mouse::Cursor,
|
||||||
|
viewport: &Rectangle,
|
||||||
|
) {
|
||||||
|
if let Some(clipped_viewport) = layout.bounds().intersection(viewport) {
|
||||||
|
for (i, ((layer, state), layout)) in self
|
||||||
|
.children
|
||||||
|
.iter()
|
||||||
|
.zip(&tree.children)
|
||||||
|
.zip(layout.children())
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
if i > 0 {
|
||||||
|
renderer.with_layer(clipped_viewport, |renderer| {
|
||||||
|
layer.as_widget().draw(
|
||||||
|
state,
|
||||||
|
renderer,
|
||||||
|
theme,
|
||||||
|
style,
|
||||||
|
layout,
|
||||||
|
cursor,
|
||||||
|
&clipped_viewport,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
layer.as_widget().draw(
|
||||||
|
state,
|
||||||
|
renderer,
|
||||||
|
theme,
|
||||||
|
style,
|
||||||
|
layout,
|
||||||
|
cursor,
|
||||||
|
&clipped_viewport,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overlay<'b>(
|
||||||
|
&'b mut self,
|
||||||
|
tree: &'b mut Tree,
|
||||||
|
layout: Layout<'_>,
|
||||||
|
renderer: &Renderer,
|
||||||
|
translation: Vector,
|
||||||
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
|
overlay::from_children(
|
||||||
|
&mut self.children,
|
||||||
|
tree,
|
||||||
|
layout,
|
||||||
|
renderer,
|
||||||
|
translation,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, Theme, Renderer> From<Stack<'a, Message, Theme, Renderer>>
|
||||||
|
for Element<'a, Message, Theme, Renderer>
|
||||||
|
where
|
||||||
|
Message: 'a,
|
||||||
|
Theme: 'a,
|
||||||
|
Renderer: crate::core::Renderer + 'a,
|
||||||
|
{
|
||||||
|
fn from(stack: Stack<'a, Message, Theme, Renderer>) -> Self {
|
||||||
|
Self::new(stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -396,7 +396,9 @@ pub fn mouse_interaction(
|
||||||
use mouse::Interaction;
|
use mouse::Interaction;
|
||||||
|
|
||||||
match interaction {
|
match interaction {
|
||||||
Interaction::Idle => winit::window::CursorIcon::Default,
|
Interaction::None | Interaction::Idle => {
|
||||||
|
winit::window::CursorIcon::Default
|
||||||
|
}
|
||||||
Interaction::Pointer => winit::window::CursorIcon::Pointer,
|
Interaction::Pointer => winit::window::CursorIcon::Pointer,
|
||||||
Interaction::Working => winit::window::CursorIcon::Progress,
|
Interaction::Working => winit::window::CursorIcon::Progress,
|
||||||
Interaction::Grab => winit::window::CursorIcon::Grab,
|
Interaction::Grab => winit::window::CursorIcon::Grab,
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ where
|
||||||
exit_on_close_request,
|
exit_on_close_request,
|
||||||
surface,
|
surface,
|
||||||
renderer,
|
renderer,
|
||||||
mouse_interaction: mouse::Interaction::Idle,
|
mouse_interaction: mouse::Interaction::None,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue