summaryrefslogtreecommitdiffstats
path: root/examples/multi_window
diff options
context:
space:
mode:
authorLibravatar Richard <richardsoncusto@gmail.com>2022-07-21 09:52:55 -0300
committerLibravatar bungoboingo <shankern@protonmail.com>2023-01-09 11:27:04 -0800
commit3d901d5f1f8e496651a6f9881fec92bc8998d910 (patch)
tree378842356e789ffddf9c6ced8251c4954b76b5d9 /examples/multi_window
parent2fe58e12619186eb3755491db2bdaf02de297afb (diff)
downloadiced-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.rs379
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 {