diff options
author | 2022-07-21 09:52:55 -0300 | |
---|---|---|
committer | 2023-01-09 11:27:04 -0800 | |
commit | 3d901d5f1f8e496651a6f9881fec92bc8998d910 (patch) | |
tree | 378842356e789ffddf9c6ced8251c4954b76b5d9 /examples/multi_window | |
parent | 2fe58e12619186eb3755491db2bdaf02de297afb (diff) | |
download | iced-3d901d5f1f8e496651a6f9881fec92bc8998d910.tar.gz iced-3d901d5f1f8e496651a6f9881fec92bc8998d910.tar.bz2 iced-3d901d5f1f8e496651a6f9881fec92bc8998d910.zip |
create multi-windowed `pane_grid` example
Diffstat (limited to 'examples/multi_window')
-rw-r--r-- | examples/multi_window/src/main.rs | 379 |
1 files changed, 278 insertions, 101 deletions
diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs index ae8fa22b..4ad92adb 100644 --- a/examples/multi_window/src/main.rs +++ b/examples/multi_window/src/main.rs @@ -1,36 +1,55 @@ use iced::alignment::{self, Alignment}; use iced::executor; use iced::keyboard; +use iced::multi_window::Application; use iced::theme::{self, Theme}; use iced::widget::pane_grid::{self, PaneGrid}; -use iced::widget::{button, column, container, row, scrollable, text}; -use iced::{ - Application, Color, Command, Element, Length, Settings, Size, Subscription, +use iced::widget::{ + button, column, container, pick_list, row, scrollable, text, text_input, }; +use iced::window; +use iced::{Color, Command, Element, Length, Settings, Size, Subscription}; use iced_lazy::responsive; use iced_native::{event, subscription, Event}; +use std::collections::HashMap; + pub fn main() -> iced::Result { Example::run(Settings::default()) } struct Example { - panes: pane_grid::State<Pane>, + windows: HashMap<window::Id, Window>, panes_created: usize, + _focused: window::Id, +} + +struct Window { + title: String, + panes: pane_grid::State<Pane>, focus: Option<pane_grid::Pane>, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone)] enum Message { + Window(window::Id, WindowMessage), +} + +#[derive(Debug, Clone)] +enum WindowMessage { Split(pane_grid::Axis, pane_grid::Pane), SplitFocused(pane_grid::Axis), FocusAdjacent(pane_grid::Direction), Clicked(pane_grid::Pane), Dragged(pane_grid::DragEvent), + PopOut(pane_grid::Pane), Resized(pane_grid::ResizeEvent), + TitleChanged(String), + ToggleMoving(pane_grid::Pane), TogglePin(pane_grid::Pane), Close(pane_grid::Pane), CloseFocused, + SelectedWindow(pane_grid::Pane, SelectableWindow), } impl Application for Example { @@ -40,93 +59,158 @@ impl Application for Example { type Flags = (); fn new(_flags: ()) -> (Self, Command<Message>) { - let (panes, _) = pane_grid::State::new(Pane::new(0)); + let (panes, _) = + pane_grid::State::new(Pane::new(0, pane_grid::Axis::Horizontal)); + let window = Window { + panes, + focus: None, + title: String::from("Default window"), + }; ( Example { - panes, + windows: HashMap::from([(window::Id::new(0usize), window)]), panes_created: 1, - focus: None, + _focused: window::Id::new(0usize), }, Command::none(), ) } fn title(&self) -> String { - String::from("Pane grid - Iced") + String::from("Multi windowed pane grid - Iced") } fn update(&mut self, message: Message) -> Command<Message> { + let Message::Window(id, message) = message; match message { - Message::Split(axis, pane) => { - let result = self.panes.split( + WindowMessage::Split(axis, pane) => { + let window = self.windows.get_mut(&id).unwrap(); + let result = window.panes.split( axis, &pane, - Pane::new(self.panes_created), + Pane::new(self.panes_created, axis), ); if let Some((pane, _)) = result { - self.focus = Some(pane); + window.focus = Some(pane); } self.panes_created += 1; } - Message::SplitFocused(axis) => { - if let Some(pane) = self.focus { - let result = self.panes.split( + WindowMessage::SplitFocused(axis) => { + let window = self.windows.get_mut(&id).unwrap(); + if let Some(pane) = window.focus { + let result = window.panes.split( axis, &pane, - Pane::new(self.panes_created), + Pane::new(self.panes_created, axis), ); if let Some((pane, _)) = result { - self.focus = Some(pane); + window.focus = Some(pane); } self.panes_created += 1; } } - Message::FocusAdjacent(direction) => { - if let Some(pane) = self.focus { + WindowMessage::FocusAdjacent(direction) => { + let window = self.windows.get_mut(&id).unwrap(); + if let Some(pane) = window.focus { if let Some(adjacent) = - self.panes.adjacent(&pane, direction) + window.panes.adjacent(&pane, direction) { - self.focus = Some(adjacent); + window.focus = Some(adjacent); } } } - Message::Clicked(pane) => { - self.focus = Some(pane); + WindowMessage::Clicked(pane) => { + let window = self.windows.get_mut(&id).unwrap(); + window.focus = Some(pane); } - Message::Resized(pane_grid::ResizeEvent { split, ratio }) => { - self.panes.resize(&split, ratio); + WindowMessage::Resized(pane_grid::ResizeEvent { split, ratio }) => { + let window = self.windows.get_mut(&id).unwrap(); + window.panes.resize(&split, ratio); } - Message::Dragged(pane_grid::DragEvent::Dropped { + WindowMessage::SelectedWindow(pane, selected) => { + let window = self.windows.get_mut(&id).unwrap(); + let (mut pane, _) = window.panes.close(&pane).unwrap(); + pane.is_moving = false; + + if let Some(window) = self.windows.get_mut(&selected.0) { + let (&first_pane, _) = window.panes.iter().next().unwrap(); + let result = + window.panes.split(pane.axis, &first_pane, pane); + + if let Some((pane, _)) = result { + window.focus = Some(pane); + } + + self.panes_created += 1; + } + } + WindowMessage::ToggleMoving(pane) => { + let window = self.windows.get_mut(&id).unwrap(); + if let Some(pane) = window.panes.get_mut(&pane) { + pane.is_moving = !pane.is_moving; + } + } + WindowMessage::TitleChanged(title) => { + let window = self.windows.get_mut(&id).unwrap(); + window.title = title; + } + WindowMessage::PopOut(pane) => { + let window = self.windows.get_mut(&id).unwrap(); + if let Some((popped, sibling)) = window.panes.close(&pane) { + window.focus = Some(sibling); + + let (panes, _) = pane_grid::State::new(popped); + let window = Window { + panes, + focus: None, + title: format!("New window ({})", self.windows.len()), + }; + + self.windows + .insert(window::Id::new(self.windows.len()), window); + } + } + WindowMessage::Dragged(pane_grid::DragEvent::Dropped { pane, target, }) => { - self.panes.swap(&pane, &target); + let window = self.windows.get_mut(&id).unwrap(); + window.panes.swap(&pane, &target); } - Message::Dragged(_) => {} - Message::TogglePin(pane) => { - if let Some(Pane { is_pinned, .. }) = self.panes.get_mut(&pane) + // WindowMessage::Dragged(pane_grid::DragEvent::Picked { pane }) => { + // println!("Picked {pane:?}"); + // } + WindowMessage::Dragged(_) => {} + WindowMessage::TogglePin(pane) => { + let window = self.windows.get_mut(&id).unwrap(); + if let Some(Pane { is_pinned, .. }) = + window.panes.get_mut(&pane) { *is_pinned = !*is_pinned; } } - Message::Close(pane) => { - if let Some((_, sibling)) = self.panes.close(&pane) { - self.focus = Some(sibling); + WindowMessage::Close(pane) => { + let window = self.windows.get_mut(&id).unwrap(); + if let Some((_, sibling)) = window.panes.close(&pane) { + window.focus = Some(sibling); } } - Message::CloseFocused => { - if let Some(pane) = self.focus { - if let Some(Pane { is_pinned, .. }) = self.panes.get(&pane) + WindowMessage::CloseFocused => { + let window = self.windows.get_mut(&id).unwrap(); + if let Some(pane) = window.focus { + if let Some(Pane { is_pinned, .. }) = + window.panes.get(&pane) { if !is_pinned { - if let Some((_, sibling)) = self.panes.close(&pane) + if let Some((_, sibling)) = + window.panes.close(&pane) { - self.focus = Some(sibling); + window.focus = Some(sibling); } } } @@ -147,66 +231,106 @@ impl Application for Example { Event::Keyboard(keyboard::Event::KeyPressed { modifiers, key_code, - }) if modifiers.command() => handle_hotkey(key_code), + }) if modifiers.command() => { + handle_hotkey(key_code).map(|message| { + Message::Window(window::Id::new(0usize), message) + }) + } // TODO(derezzedex) _ => None, } }) } - fn view(&self) -> Element<Message> { - let focus = self.focus; - let total_panes = self.panes.len(); - - let pane_grid = PaneGrid::new(&self.panes, |id, pane| { - let is_focused = focus == Some(id); - - let pin_button = button( - text(if pane.is_pinned { "Unpin" } else { "Pin" }).size(14), - ) - .on_press(Message::TogglePin(id)) - .padding(3); + fn windows(&self) -> Vec<(window::Id, iced::window::Settings)> { + self.windows + .iter() + .map(|(&id, _window)| (id, iced::window::Settings::default())) + .collect() + } - let title = row![ - pin_button, - "Pane", - text(pane.id.to_string()).style(if is_focused { - PANE_ID_COLOR_FOCUSED - } else { - PANE_ID_COLOR_UNFOCUSED - }), + fn view(&self, window_id: window::Id) -> Element<Message> { + if let Some(window) = self.windows.get(&window_id) { + let focus = window.focus; + let total_panes = window.panes.len(); + + let window_controls = row![ + text_input( + "Window title", + &window.title, + WindowMessage::TitleChanged, + ), + button(text("Apply")).style(theme::Button::Primary), ] - .spacing(5); - - let title_bar = pane_grid::TitleBar::new(title) - .controls(view_controls(id, total_panes, pane.is_pinned)) - .padding(10) + .spacing(5) + .align_items(Alignment::Center); + + let pane_grid = PaneGrid::new(&window.panes, |id, pane| { + let is_focused = focus == Some(id); + + let pin_button = button( + text(if pane.is_pinned { "Unpin" } else { "Pin" }).size(14), + ) + .on_press(WindowMessage::TogglePin(id)) + .padding(3); + + let title = row![ + pin_button, + "Pane", + text(pane.id.to_string()).style(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, + pane.is_pinned, + pane.is_moving, + &window.title, + window_id, + &self.windows, + )) + .padding(10) + .style(if is_focused { + style::title_bar_focused + } else { + style::title_bar_active + }); + + pane_grid::Content::new(responsive(move |size| { + view_content(id, total_panes, pane.is_pinned, size) + })) + .title_bar(title_bar) .style(if is_focused { - style::title_bar_focused + style::pane_focused } else { - style::title_bar_active - }); - - pane_grid::Content::new(responsive(move |size| { - view_content(id, total_panes, pane.is_pinned, size) - })) - .title_bar(title_bar) - .style(if is_focused { - style::pane_focused - } else { - style::pane_active + 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) + .spacing(10) + .on_click(WindowMessage::Clicked) + .on_drag(WindowMessage::Dragged) + .on_resize(10, WindowMessage::Resized); + + let content: Element<_> = column![window_controls, pane_grid] + .width(Length::Fill) + .height(Length::Fill) + .padding(10) + .into(); + + return content + .map(move |message| Message::Window(window_id, message)); + } + + container(text("This shouldn't be possible!").size(20)) + .center_x() + .center_y() .into() } } @@ -222,7 +346,7 @@ const PANE_ID_COLOR_FOCUSED: Color = Color::from_rgb( 0x47 as f32 / 255.0, ); -fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> { +fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<WindowMessage> { use keyboard::KeyCode; use pane_grid::{Axis, Direction}; @@ -235,23 +359,44 @@ fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> { }; 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), + KeyCode::V => Some(WindowMessage::SplitFocused(Axis::Vertical)), + KeyCode::H => Some(WindowMessage::SplitFocused(Axis::Horizontal)), + KeyCode::W => Some(WindowMessage::CloseFocused), + _ => direction.map(WindowMessage::FocusAdjacent), + } +} + +#[derive(Debug, Clone)] +struct SelectableWindow(window::Id, String); + +impl PartialEq for SelectableWindow { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Eq for SelectableWindow {} + +impl std::fmt::Display for SelectableWindow { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.1.fmt(f) } } struct Pane { id: usize, + pub axis: pane_grid::Axis, pub is_pinned: bool, + pub is_moving: bool, } impl Pane { - fn new(id: usize) -> Self { + fn new(id: usize, axis: pane_grid::Axis) -> Self { Self { id, + axis, is_pinned: false, + is_moving: false, } } } @@ -261,7 +406,7 @@ fn view_content<'a>( total_panes: usize, is_pinned: bool, size: Size, -) -> Element<'a, Message> { +) -> Element<'a, WindowMessage> { let button = |label, message| { button( text(label) @@ -277,11 +422,11 @@ fn view_content<'a>( let mut controls = column![ button( "Split horizontally", - Message::Split(pane_grid::Axis::Horizontal, pane), + WindowMessage::Split(pane_grid::Axis::Horizontal, pane), ), button( "Split vertically", - Message::Split(pane_grid::Axis::Vertical, pane), + WindowMessage::Split(pane_grid::Axis::Vertical, pane), ) ] .spacing(5) @@ -289,7 +434,7 @@ fn view_content<'a>( if total_panes > 1 && !is_pinned { controls = controls.push( - button("Close", Message::Close(pane)) + button("Close", WindowMessage::Close(pane)) .style(theme::Button::Destructive), ); } @@ -314,16 +459,48 @@ 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)) + is_moving: bool, + window_title: &'a str, + window_id: window::Id, + windows: &HashMap<window::Id, Window>, +) -> Element<'a, WindowMessage> { + let window_selector = { + let options: Vec<_> = windows + .iter() + .map(|(id, window)| SelectableWindow(*id, window.title.clone())) + .collect(); + pick_list( + options, + Some(SelectableWindow(window_id, window_title.to_string())), + move |window| WindowMessage::SelectedWindow(pane, window), + ) + }; + + let mut move_to = button(text("Move to").size(14)).padding(3); + + let mut pop_out = button(text("Pop Out").size(14)).padding(3); + + let mut close = button(text("Close").size(14)) .style(theme::Button::Destructive) .padding(3); if total_panes > 1 && !is_pinned { - button = button.on_press(Message::Close(pane)); + close = close.on_press(WindowMessage::Close(pane)); + pop_out = pop_out.on_press(WindowMessage::PopOut(pane)); + } + + if windows.len() > 1 && total_panes > 1 && !is_pinned { + move_to = move_to.on_press(WindowMessage::ToggleMoving(pane)); + } + + let mut content = row![].spacing(10); + if is_moving { + content = content.push(pop_out).push(window_selector).push(close); + } else { + content = content.push(pop_out).push(move_to).push(close); } - button.into() + content.into() } mod style { |