Simplify modal example 🎉
This commit is contained in:
parent
4cd45643d7
commit
4fc342c979
1 changed files with 35 additions and 328 deletions
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue