diff options
| author | 2022-12-13 09:31:57 +0100 | |
|---|---|---|
| committer | 2022-12-13 09:31:57 +0100 | |
| commit | 2e6d90f141217bad83eacd392562c13d7485881f (patch) | |
| tree | baa2c507076073aed4fd24abc9c7a7949d85c039 /examples/modal | |
| parent | ba95042fff378213f5029b2b164d79e768482a47 (diff) | |
| parent | 02182eea45537c9eb5b2bddfdff822bb8a3d143d (diff) | |
| download | iced-2e6d90f141217bad83eacd392562c13d7485881f.tar.gz iced-2e6d90f141217bad83eacd392562c13d7485881f.tar.bz2 iced-2e6d90f141217bad83eacd392562c13d7485881f.zip  | |
Merge branch 'master' into feat/slider-orientation
Diffstat (limited to '')
| -rw-r--r-- | examples/modal/Cargo.toml | 10 | ||||
| -rw-r--r-- | examples/modal/src/main.rs | 475 | 
2 files changed, 485 insertions, 0 deletions
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..2f20795c --- /dev/null +++ b/examples/modal/src/main.rs @@ -0,0 +1,475 @@ +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), +    Submit, +    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.hide_modal(); +                Command::none() +            } +            Message::Email(email) => { +                self.email = email; +                Command::none() +            } +            Message::Password(password) => { +                self.password = password; +                Command::none() +            } +            Message::Submit => { +                if !self.email.is_empty() && !self.password.is_empty() { +                    self.hide_modal(); +                } + +                Command::none() +            } +            Message::Event(event) => match event { +                Event::Keyboard(keyboard::Event::KeyPressed { +                    key_code: keyboard::KeyCode::Tab, +                    modifiers, +                }) => { +                    if modifiers.shift() { +                        widget::focus_previous() +                    } else { +                        widget::focus_next() +                    } +                } +                Event::Keyboard(keyboard::Event::KeyPressed { +                    key_code: keyboard::KeyCode::Escape, +                    .. +                }) => { +                    self.hide_modal(); +                    Command::none() +                } +                _ => 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 +                            ) +                            .on_submit(Message::Submit) +                            .padding(5), +                        ] +                        .spacing(5), +                        column![ +                            text("Password").size(12), +                            text_input("", &self.password, Message::Password) +                                .on_submit(Message::Submit) +                                .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() +        } +    } +} + +impl App { +    fn hide_modal(&mut self) { +        self.show_modal = false; +        self.email.clear(); +        self.password.clear(); +    } +} + +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: renderer::BorderRadius::from(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) +        } +    } +}  | 
