diff options
| author | 2022-03-10 19:25:57 +0700 | |
|---|---|---|
| committer | 2022-03-14 17:44:25 +0700 | |
| commit | 6dd187ff0822230f084e43636b1aabeb1baf06f6 (patch) | |
| tree | 27426980b2882025a3c812ba2401d3bf8f6dca8d /examples/pure | |
| parent | 9f27969d14232355ad628431fb67aa07e42e768f (diff) | |
| download | iced-6dd187ff0822230f084e43636b1aabeb1baf06f6.tar.gz iced-6dd187ff0822230f084e43636b1aabeb1baf06f6.tar.bz2 iced-6dd187ff0822230f084e43636b1aabeb1baf06f6.zip | |
Implement `pure` version of `PaneGrid` widget
Diffstat (limited to '')
| -rw-r--r-- | examples/pure/pane_grid/Cargo.toml | 11 | ||||
| -rw-r--r-- | examples/pure/pane_grid/src/main.rs | 437 | 
2 files changed, 448 insertions, 0 deletions
| diff --git a/examples/pure/pane_grid/Cargo.toml b/examples/pure/pane_grid/Cargo.toml new file mode 100644 index 00000000..a51cdaf0 --- /dev/null +++ b/examples/pure/pane_grid/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "pure_pane_grid" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../../..", features = ["pure", "debug"] } +iced_native = { path = "../../../native" } +iced_lazy = { path = "../../../lazy", features = ["pure"] } diff --git a/examples/pure/pane_grid/src/main.rs b/examples/pure/pane_grid/src/main.rs new file mode 100644 index 00000000..6232d6be --- /dev/null +++ b/examples/pure/pane_grid/src/main.rs @@ -0,0 +1,437 @@ +use iced::alignment::{self, Alignment}; +use iced::executor; +use iced::keyboard; +use iced::pure::pane_grid::{self, PaneGrid}; +use iced::pure::{button, column, container, row, scrollable, text}; +use iced::pure::{Application, Element}; +use iced::{Color, Command, Length, Settings, Size, Subscription}; +use iced_lazy::pure::responsive; +use iced_native::{event, subscription, Event}; + +pub fn main() -> iced::Result { +    Example::run(Settings::default()) +} + +struct Example { +    panes: pane_grid::Map<Pane>, +    panes_created: usize, +    focus: Option<pane_grid::Pane>, +} + +#[derive(Debug, Clone, Copy)] +enum Message { +    Split(pane_grid::Axis, pane_grid::Pane), +    SplitFocused(pane_grid::Axis), +    FocusAdjacent(pane_grid::Direction), +    Clicked(pane_grid::Pane), +    Dragged(pane_grid::DragEvent), +    Resized(pane_grid::ResizeEvent), +    TogglePin(pane_grid::Pane), +    Close(pane_grid::Pane), +    CloseFocused, +} + +impl Application for Example { +    type Message = Message; +    type Executor = executor::Default; +    type Flags = (); + +    fn new(_flags: ()) -> (Self, Command<Message>) { +        let (panes, _) = pane_grid::Map::new(Pane::new(0)); + +        ( +            Example { +                panes, +                panes_created: 1, +                focus: None, +            }, +            Command::none(), +        ) +    } + +    fn title(&self) -> String { +        String::from("Pane grid - Iced") +    } + +    fn update(&mut self, message: Message) -> Command<Message> { +        match message { +            Message::Split(axis, pane) => { +                let result = self.panes.split( +                    axis, +                    &pane, +                    Pane::new(self.panes_created), +                ); + +                if let Some((pane, _)) = result { +                    self.focus = Some(pane); +                } + +                self.panes_created += 1; +            } +            Message::SplitFocused(axis) => { +                if let Some(pane) = self.focus { +                    let result = self.panes.split( +                        axis, +                        &pane, +                        Pane::new(self.panes_created), +                    ); + +                    if let Some((pane, _)) = result { +                        self.focus = Some(pane); +                    } + +                    self.panes_created += 1; +                } +            } +            Message::FocusAdjacent(direction) => { +                if let Some(pane) = self.focus { +                    if let Some(adjacent) = +                        self.panes.adjacent(&pane, direction) +                    { +                        self.focus = Some(adjacent); +                    } +                } +            } +            Message::Clicked(pane) => { +                self.focus = Some(pane); +            } +            Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { +                self.panes.resize(&split, ratio); +            } +            Message::Dragged(pane_grid::DragEvent::Dropped { +                pane, +                target, +            }) => { +                self.panes.swap(&pane, &target); +            } +            Message::Dragged(_) => {} +            Message::TogglePin(pane) => { +                if let Some(Pane { is_pinned, .. }) = self.panes.get_mut(&pane) +                { +                    *is_pinned = !*is_pinned; +                } +            } +            Message::Close(pane) => { +                if let Some((_, sibling)) = self.panes.close(&pane) { +                    self.focus = Some(sibling); +                } +            } +            Message::CloseFocused => { +                if let Some(pane) = self.focus { +                    if let Some(Pane { is_pinned, .. }) = self.panes.get(&pane) +                    { +                        if !is_pinned { +                            if let Some((_, sibling)) = self.panes.close(&pane) +                            { +                                self.focus = Some(sibling); +                            } +                        } +                    } +                } +            } +        } + +        Command::none() +    } + +    fn subscription(&self) -> Subscription<Message> { +        subscription::events_with(|event, status| { +            if let event::Status::Captured = status { +                return None; +            } + +            match event { +                Event::Keyboard(keyboard::Event::KeyPressed { +                    modifiers, +                    key_code, +                }) if modifiers.command() => handle_hotkey(key_code), +                _ => None, +            } +        }) +    } + +    fn view(&self) -> Element<Message> { +        let focus = self.focus; +        let total_panes = self.panes.len(); + +        let pane_grid = PaneGrid::new(&mut self.panes, |id, pane| { +            let is_focused = focus == Some(id); + +            let Pane { id, is_pinned } = pane; + +            let pin_button = +                button(text(if *is_pinned { "Unpin" } else { "Pin" }).size(14)) +                    .on_press(Message::TogglePin(id)) +                    .style(style::Button::Pin) +                    .padding(3); + +            let title = row() +                .push(pin_button) +                .push("Pane") +                .push(text(id.to_string()).color(if is_focused { +                    PANE_ID_COLOR_FOCUSED +                } else { +                    PANE_ID_COLOR_UNFOCUSED +                })) +                .spacing(5); + +            let title_bar = pane_grid::TitleBar::new(title) +                .controls(view_controls(id, total_panes, *is_pinned)) +                .padding(10) +                .style(if is_focused { +                    style::TitleBar::Focused +                } else { +                    style::TitleBar::Active +                }); + +            pane_grid::Content::new(responsive(move |size| { +                view_content(id, total_panes, *is_pinned, size) +            })) +            .title_bar(title_bar) +            .style(if is_focused { +                style::Pane::Focused +            } else { +                style::Pane::Active +            }) +        }) +        .width(Length::Fill) +        .height(Length::Fill) +        .spacing(10) +        .on_click(Message::Clicked) +        .on_drag(Message::Dragged) +        .on_resize(10, Message::Resized); + +        container(pane_grid) +            .width(Length::Fill) +            .height(Length::Fill) +            .padding(10) +            .into() +    } +} + +const PANE_ID_COLOR_UNFOCUSED: Color = Color::from_rgb( +    0xFF as f32 / 255.0, +    0xC7 as f32 / 255.0, +    0xC7 as f32 / 255.0, +); +const PANE_ID_COLOR_FOCUSED: Color = Color::from_rgb( +    0xFF as f32 / 255.0, +    0x47 as f32 / 255.0, +    0x47 as f32 / 255.0, +); + +fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> { +    use keyboard::KeyCode; +    use pane_grid::{Axis, Direction}; + +    let direction = match key_code { +        KeyCode::Up => Some(Direction::Up), +        KeyCode::Down => Some(Direction::Down), +        KeyCode::Left => Some(Direction::Left), +        KeyCode::Right => Some(Direction::Right), +        _ => None, +    }; + +    match key_code { +        KeyCode::V => Some(Message::SplitFocused(Axis::Vertical)), +        KeyCode::H => Some(Message::SplitFocused(Axis::Horizontal)), +        KeyCode::W => Some(Message::CloseFocused), +        _ => direction.map(Message::FocusAdjacent), +    } +} + +struct Pane { +    id: usize, +    pub is_pinned: bool, +} + +impl Pane { +    fn new(id: usize) -> Self { +        Self { +            id, +            is_pinned: false, +        } +    } +} + +fn view_content<'a>( +    pane: pane_grid::Pane, +    total_panes: usize, +    is_pinned: bool, +    size: Size, +) -> Element<'a, Message> { +    let button = |label, message, style| { +        button( +            text(label) +                .width(Length::Fill) +                .horizontal_alignment(alignment::Horizontal::Center) +                .size(16), +        ) +        .width(Length::Fill) +        .padding(8) +        .on_press(message) +        .style(style) +    }; + +    let mut controls = column() +        .spacing(5) +        .max_width(150) +        .push(button( +            "Split horizontally", +            Message::Split(pane_grid::Axis::Horizontal, pane), +            style::Button::Primary, +        )) +        .push(button( +            "Split vertically", +            Message::Split(pane_grid::Axis::Vertical, pane), +            style::Button::Primary, +        )); + +    if total_panes > 1 && !is_pinned { +        controls = controls.push(button( +            "Close", +            Message::Close(pane), +            style::Button::Destructive, +        )); +    } + +    let content = column() +        .width(Length::Fill) +        .spacing(10) +        .align_items(Alignment::Center) +        .push(text(format!("{}x{}", size.width, size.height)).size(24)) +        .push(controls); + +    container(scrollable(content)) +        .width(Length::Fill) +        .height(Length::Fill) +        .padding(5) +        .center_y() +        .into() +} + +fn view_controls<'a>( +    pane: pane_grid::Pane, +    total_panes: usize, +    is_pinned: bool, +) -> Element<'a, Message> { +    let mut button = button(text("Close").size(14)) +        .style(style::Button::Control) +        .padding(3); + +    if total_panes > 1 && !is_pinned { +        button = button.on_press(Message::Close(pane)); +    } + +    button.into() +} + +mod style { +    use crate::PANE_ID_COLOR_FOCUSED; +    use iced::{button, container, Background, Color, Vector}; + +    const SURFACE: Color = Color::from_rgb( +        0xF2 as f32 / 255.0, +        0xF3 as f32 / 255.0, +        0xF5 as f32 / 255.0, +    ); + +    const ACTIVE: Color = Color::from_rgb( +        0x72 as f32 / 255.0, +        0x89 as f32 / 255.0, +        0xDA as f32 / 255.0, +    ); + +    const HOVERED: Color = Color::from_rgb( +        0x67 as f32 / 255.0, +        0x7B as f32 / 255.0, +        0xC4 as f32 / 255.0, +    ); + +    pub enum TitleBar { +        Active, +        Focused, +    } + +    impl container::StyleSheet for TitleBar { +        fn style(&self) -> container::Style { +            let pane = match self { +                Self::Active => Pane::Active, +                Self::Focused => Pane::Focused, +            } +            .style(); + +            container::Style { +                text_color: Some(Color::WHITE), +                background: Some(pane.border_color.into()), +                ..Default::default() +            } +        } +    } + +    pub enum Pane { +        Active, +        Focused, +    } + +    impl container::StyleSheet for Pane { +        fn style(&self) -> container::Style { +            container::Style { +                background: Some(Background::Color(SURFACE)), +                border_width: 2.0, +                border_color: match self { +                    Self::Active => Color::from_rgb(0.7, 0.7, 0.7), +                    Self::Focused => Color::BLACK, +                }, +                ..Default::default() +            } +        } +    } + +    pub enum Button { +        Primary, +        Destructive, +        Control, +        Pin, +    } + +    impl button::StyleSheet for Button { +        fn active(&self) -> button::Style { +            let (background, text_color) = match self { +                Button::Primary => (Some(ACTIVE), Color::WHITE), +                Button::Destructive => { +                    (None, Color::from_rgb8(0xFF, 0x47, 0x47)) +                } +                Button::Control => (Some(PANE_ID_COLOR_FOCUSED), Color::WHITE), +                Button::Pin => (Some(ACTIVE), Color::WHITE), +            }; + +            button::Style { +                text_color, +                background: background.map(Background::Color), +                border_radius: 5.0, +                shadow_offset: Vector::new(0.0, 0.0), +                ..button::Style::default() +            } +        } + +        fn hovered(&self) -> button::Style { +            let active = self.active(); + +            let background = match self { +                Button::Primary => Some(HOVERED), +                Button::Destructive => Some(Color { +                    a: 0.2, +                    ..active.text_color +                }), +                Button::Control => Some(PANE_ID_COLOR_FOCUSED), +                Button::Pin => Some(HOVERED), +            }; + +            button::Style { +                background: background.map(Background::Color), +                ..active +            } +        } +    } +} | 
