diff options
author | 2022-11-18 14:16:02 -0800 | |
---|---|---|
committer | 2022-11-29 12:50:57 -0800 | |
commit | 3814fda9d22875bdbc9e11ccfc6dce1f0e7ca5d3 (patch) | |
tree | bd96a890c712cdf47bf53af4763e8376b8a6a3c0 | |
parent | 9a601c095420d3eba23df470becc112b928f9f62 (diff) | |
download | iced-3814fda9d22875bdbc9e11ccfc6dce1f0e7ca5d3.tar.gz iced-3814fda9d22875bdbc9e11ccfc6dce1f0e7ca5d3.tar.bz2 iced-3814fda9d22875bdbc9e11ccfc6dce1f0e7ca5d3.zip |
Add modal example
-rw-r--r-- | Cargo.toml | 35 | ||||
-rw-r--r-- | examples/modal/Cargo.toml | 10 | ||||
-rw-r--r-- | examples/modal/src/main.rs | 454 |
3 files changed, 465 insertions, 34 deletions
@@ -55,40 +55,7 @@ members = [ "style", "wgpu", "winit", - "examples/arc", - "examples/bezier_tool", - "examples/clock", - "examples/color_palette", - "examples/component", - "examples/counter", - "examples/custom_widget", - "examples/download_progress", - "examples/events", - "examples/exit", - "examples/game_of_life", - "examples/geometry", - "examples/integration_opengl", - "examples/integration_wgpu", - "examples/lazy", - "examples/modern_art", - "examples/multitouch", - "examples/pane_grid", - "examples/pick_list", - "examples/pokedex", - "examples/progress_bar", - "examples/qr_code", - "examples/scrollable", - "examples/sierpinski_triangle", - "examples/solar_system", - "examples/stopwatch", - "examples/styling", - "examples/svg", - "examples/system_information", - "examples/todos", - "examples/tooltip", - "examples/tour", - "examples/url_handler", - "examples/websocket", + "examples/*", ] [dependencies] diff --git a/examples/modal/Cargo.toml b/examples/modal/Cargo.toml new file mode 100644 index 00000000..8770acac --- /dev/null +++ b/examples/modal/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "modal" +version = "0.1.0" +authors = ["tarkah <admin@tarkah.dev>"] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../..", features = [] } +iced_native = { path = "../../native" } diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs new file mode 100644 index 00000000..37ac16fd --- /dev/null +++ b/examples/modal/src/main.rs @@ -0,0 +1,454 @@ +use iced::widget::{ + self, button, column, container, horizontal_space, row, text, text_input, +}; +use iced::{ + executor, keyboard, subscription, theme, Alignment, Application, Command, + Element, Event, Length, Settings, Subscription, +}; + +use self::modal::Modal; + +pub fn main() -> iced::Result { + App::run(Settings::default()) +} + +#[derive(Default)] +struct App { + show_modal: bool, + email: String, + password: String, +} + +#[derive(Debug, Clone)] +enum Message { + ShowModal, + HideModal, + Email(String), + Password(String), + Event(Event), +} + +impl Application for App { + type Executor = executor::Default; + type Message = Message; + type Theme = iced::Theme; + type Flags = (); + + fn new(_flags: ()) -> (Self, Command<Message>) { + (App::default(), Command::none()) + } + + fn title(&self) -> String { + String::from("Modal - Iced") + } + + fn subscription(&self) -> Subscription<Self::Message> { + subscription::events().map(Message::Event) + } + + fn update(&mut self, message: Message) -> Command<Message> { + match message { + Message::ShowModal => { + self.show_modal = true; + widget::focus_next() + } + Message::HideModal => { + self.show_modal = false; + self.email.clear(); + self.password.clear(); + Command::none() + } + Message::Email(email) => { + self.email = email; + Command::none() + } + Message::Password(password) => { + self.password = password; + Command::none() + } + Message::Event(event) => { + if let Event::Keyboard(keyboard::Event::KeyPressed { + key_code: keyboard::KeyCode::Tab, + modifiers, + }) = event + { + if modifiers.shift() { + widget::focus_previous() + } else { + widget::focus_next() + } + } else { + Command::none() + } + } + } + } + + fn view(&self) -> Element<Message> { + let content = container( + column![ + row![ + text("Top Left"), + horizontal_space(Length::Fill), + text("Top Right") + ] + .align_items(Alignment::Start) + .height(Length::Fill), + container( + button(text("Show Modal")).on_press(Message::ShowModal) + ) + .center_x() + .center_y() + .width(Length::Fill) + .height(Length::Fill), + row![ + text("Bottom Left"), + horizontal_space(Length::Fill), + text("Bottom Right") + ] + .align_items(Alignment::End) + .height(Length::Fill), + ] + .height(Length::Fill), + ) + .padding(10) + .width(Length::Fill) + .height(Length::Fill); + + if self.show_modal { + let modal = container( + column![ + text("Sign Up").size(24), + column![ + column![ + text("Email").size(12), + text_input( + "abc@123.com", + &self.email, + Message::Email + ) + .padding(5), + ] + .spacing(5), + column![ + text("Password").size(12), + text_input("", &self.password, Message::Password) + .password() + .padding(5), + ] + .spacing(5), + button(text("Submit")).on_press(Message::HideModal), + ] + .spacing(10) + ] + .spacing(20), + ) + .width(Length::Units(300)) + .padding(10) + .style(theme::Container::Box); + + Modal::new(content, modal) + .on_blur(Message::HideModal) + .into() + } else { + content.into() + } + } +} + +mod modal { + use iced_native::alignment::Alignment; + use iced_native::widget::{self, Tree}; + use iced_native::{ + event, layout, mouse, overlay, renderer, Clipboard, Color, Element, + Event, Layout, Length, Point, Rectangle, Shell, Size, Widget, + }; + + /// A widget that centers a modal element over some base element + pub struct Modal<'a, Message, Renderer> { + base: Element<'a, Message, Renderer>, + modal: Element<'a, Message, Renderer>, + on_blur: Option<Message>, + } + + impl<'a, Message, Renderer> Modal<'a, Message, Renderer> { + /// Returns a new [`Modal`] + pub fn new( + base: impl Into<Element<'a, Message, Renderer>>, + modal: impl Into<Element<'a, Message, 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, Renderer> Widget<Message, Renderer> + for Modal<'a, Message, Renderer> + where + Renderer: iced_native::Renderer, + Message: Clone, + { + fn children(&self) -> Vec<Tree> { + vec![Tree::new(&self.base), Tree::new(&self.modal)] + } + + fn diff(&self, tree: &mut Tree) { + tree.diff_children(&[&self.base, &self.modal]); + } + + fn width(&self) -> Length { + self.base.as_widget().width() + } + + fn height(&self) -> Length { + self.base.as_widget().height() + } + + fn layout( + &self, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + self.base.as_widget().layout(renderer, limits) + } + + fn on_event( + &mut self, + state: &mut Tree, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + renderer: &Renderer, + clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + self.base.as_widget_mut().on_event( + &mut state.children[0], + event, + layout, + cursor_position, + renderer, + clipboard, + shell, + ) + } + + fn draw( + &self, + state: &Tree, + renderer: &mut Renderer, + theme: &<Renderer as iced_native::Renderer>::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + ) { + self.base.as_widget().draw( + &state.children[0], + renderer, + theme, + style, + layout, + cursor_position, + viewport, + ); + } + + fn overlay<'b>( + &'b mut self, + state: &'b mut Tree, + layout: Layout<'_>, + _renderer: &Renderer, + ) -> Option<overlay::Element<'b, Message, Renderer>> { + Some(overlay::Element::new( + layout.position(), + Box::new(Overlay { + content: &mut self.modal, + tree: &mut state.children[1], + size: layout.bounds().size(), + on_blur: self.on_blur.clone(), + }), + )) + } + + fn mouse_interaction( + &self, + state: &Tree, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.base.as_widget().mouse_interaction( + &state.children[0], + layout, + cursor_position, + viewport, + renderer, + ) + } + + fn operate( + &self, + state: &mut Tree, + layout: Layout<'_>, + operation: &mut dyn widget::Operation<Message>, + ) { + self.base.as_widget().operate( + &mut state.children[0], + layout, + operation, + ); + } + } + + struct Overlay<'a, 'b, Message, Renderer> { + content: &'b mut Element<'a, Message, Renderer>, + tree: &'b mut Tree, + size: Size, + on_blur: Option<Message>, + } + + impl<'a, 'b, Message, Renderer> overlay::Overlay<Message, Renderer> + for Overlay<'a, 'b, Message, Renderer> + where + Renderer: iced_native::Renderer, + Message: Clone, + { + fn layout( + &self, + renderer: &Renderer, + _bounds: Size, + position: Point, + ) -> layout::Node { + let limits = layout::Limits::new(Size::ZERO, self.size) + .width(Length::Fill) + .height(Length::Fill); + + let mut child = self.content.as_widget().layout(renderer, &limits); + child.align(Alignment::Center, Alignment::Center, limits.max()); + + let mut node = layout::Node::with_children(self.size, vec![child]); + node.move_to(position); + + node + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + 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 !content_bounds.contains(cursor_position) { + shell.publish(message.clone()); + return event::Status::Captured; + } + } + } + + self.content.as_widget_mut().on_event( + self.tree, + event, + layout.children().next().unwrap(), + cursor_position, + renderer, + clipboard, + shell, + ) + } + + fn draw( + &self, + renderer: &mut Renderer, + theme: &Renderer::Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor_position: Point, + ) { + renderer.fill_quad( + renderer::Quad { + bounds: layout.bounds(), + border_radius: 0.0, + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + Color { + a: 0.80, + ..Color::BLACK + }, + ); + + self.content.as_widget().draw( + self.tree, + renderer, + theme, + style, + layout.children().next().unwrap(), + cursor_position, + &layout.bounds(), + ); + } + + fn operate( + &mut self, + layout: Layout<'_>, + operation: &mut dyn widget::Operation<Message>, + ) { + self.content.as_widget().operate( + self.tree, + layout.children().next().unwrap(), + operation, + ); + } + + fn mouse_interaction( + &self, + layout: Layout<'_>, + cursor_position: Point, + viewport: &Rectangle, + renderer: &Renderer, + ) -> mouse::Interaction { + self.content.as_widget().mouse_interaction( + self.tree, + layout.children().next().unwrap(), + cursor_position, + viewport, + renderer, + ) + } + } + + impl<'a, Message, Renderer> From<Modal<'a, Message, Renderer>> + for Element<'a, Message, Renderer> + where + Renderer: 'a + iced_native::Renderer, + Message: 'a + Clone, + { + fn from(modal: Modal<'a, Message, Renderer>) -> Self { + Element::new(modal) + } + } +} |