summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/events/src/main.rs8
-rw-r--r--examples/integration_opengl/src/main.rs1
-rw-r--r--examples/integration_wgpu/src/main.rs5
-rw-r--r--examples/multi_window/Cargo.toml13
-rw-r--r--examples/multi_window/src/main.rs622
-rw-r--r--examples/solar_system/src/main.rs2
6 files changed, 644 insertions, 7 deletions
diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs
index 1b97018e..b57010c7 100644
--- a/examples/events/src/main.rs
+++ b/examples/events/src/main.rs
@@ -25,7 +25,7 @@ struct Events {
enum Message {
EventOccurred(iced_native::Event),
Toggled(bool),
- Exit,
+ Exit(window::Id),
}
impl Application for Events {
@@ -54,8 +54,8 @@ impl Application for Events {
Command::none()
}
Message::EventOccurred(event) => {
- if let Event::Window(window::Event::CloseRequested) = event {
- window::close()
+ if let Event::Window(id, window::Event::CloseRequested) = event {
+ window::close(id)
} else {
Command::none()
}
@@ -65,7 +65,7 @@ impl Application for Events {
Command::none()
}
- Message::Exit => window::close(),
+ Message::Exit(id) => window::close(id),
}
}
diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs
index f161c8a0..4dd3a4a9 100644
--- a/examples/integration_opengl/src/main.rs
+++ b/examples/integration_opengl/src/main.rs
@@ -107,6 +107,7 @@ pub fn main() {
// Map window event to iced event
if let Some(event) = iced_winit::conversion::window_event(
+ iced_winit::window::Id::MAIN,
&event,
windowed_context.window().scale_factor(),
modifiers,
diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs
index 2a56b6fa..1f42013b 100644
--- a/examples/integration_wgpu/src/main.rs
+++ b/examples/integration_wgpu/src/main.rs
@@ -6,8 +6,8 @@ use scene::Scene;
use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport};
use iced_winit::{
- conversion, futures, program, renderer, winit, Clipboard, Color, Debug,
- Size,
+ conversion, futures, program, renderer, window, winit, Clipboard, Color,
+ Debug, Size,
};
use winit::{
@@ -169,6 +169,7 @@ pub fn main() {
// Map window event to iced event
if let Some(event) = iced_winit::conversion::window_event(
+ window::Id::MAIN,
&event,
window.scale_factor(),
modifiers,
diff --git a/examples/multi_window/Cargo.toml b/examples/multi_window/Cargo.toml
new file mode 100644
index 00000000..0bb83f37
--- /dev/null
+++ b/examples/multi_window/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "multi_window"
+version = "0.1.0"
+authors = ["Richard Custodio <richardsoncusto@gmail.com>"]
+edition = "2021"
+publish = false
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+iced = { path = "../..", features = ["debug", "multi_window", "tokio"] }
+env_logger = "0.10.0"
+iced_native = { path = "../../native" }
+iced_lazy = { path = "../../lazy" }
diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs
new file mode 100644
index 00000000..60f32a7d
--- /dev/null
+++ b/examples/multi_window/src/main.rs
@@ -0,0 +1,622 @@
+use iced::alignment::{self, Alignment};
+use iced::{executor, time};
+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, 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 iced_native::widget::scrollable::{Properties, RelativeOffset};
+use iced_native::window::Id;
+use std::collections::HashMap;
+use std::time::{Duration, Instant};
+
+pub fn main() -> iced::Result {
+ env_logger::init();
+
+ Example::run(Settings::default())
+}
+
+struct Example {
+ windows: HashMap<window::Id, Window>,
+ panes_created: usize,
+ count: usize,
+ _focused: window::Id,
+}
+
+#[derive(Debug)]
+struct Window {
+ title: String,
+ scale: f64,
+ panes: pane_grid::State<Pane>,
+ focus: Option<pane_grid::Pane>,
+}
+
+#[derive(Debug, Clone)]
+enum Message {
+ Window(window::Id, WindowMessage),
+ CountIncremented(Instant),
+}
+
+#[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),
+ CloseWindow,
+ SnapToggle,
+}
+
+impl Application for Example {
+ type Executor = executor::Default;
+ type Message = Message;
+ type Theme = Theme;
+ type Flags = ();
+
+ fn new(_flags: ()) -> (Self, Command<Message>) {
+ let (panes, _) =
+ pane_grid::State::new(Pane::new(0, pane_grid::Axis::Horizontal));
+ let window = Window {
+ panes,
+ focus: None,
+ title: String::from("Default window"),
+ scale: 1.0,
+ };
+
+ (
+ Example {
+ windows: HashMap::from([(window::Id::MAIN, window)]),
+ panes_created: 1,
+ count: 0,
+ _focused: window::Id::MAIN,
+ },
+ Command::none(),
+ )
+ }
+
+ fn title(&self, window: window::Id) -> String {
+ self.windows
+ .get(&window)
+ .map(|w| w.title.clone())
+ .unwrap_or(String::from("New Window"))
+ }
+
+ fn update(&mut self, message: Message) -> Command<Message> {
+ match message {
+ Message::Window(id, message) => match message {
+ WindowMessage::SnapToggle => {
+ let window = self.windows.get_mut(&id).unwrap();
+
+ if let Some(focused) = &window.focus {
+ let pane = window.panes.get_mut(focused).unwrap();
+
+ let cmd = scrollable::snap_to(
+ pane.scrollable_id.clone(),
+ if pane.snapped {
+ RelativeOffset::START
+ } else {
+ RelativeOffset::END
+ },
+ );
+
+ pane.snapped = !pane.snapped;
+ return cmd;
+ }
+ }
+ WindowMessage::Split(axis, pane) => {
+ let window = self.windows.get_mut(&id).unwrap();
+ let result = window.panes.split(
+ axis,
+ &pane,
+ Pane::new(self.panes_created, axis),
+ );
+
+ if let Some((pane, _)) = result {
+ window.focus = Some(pane);
+ }
+
+ self.panes_created += 1;
+ }
+ 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, axis),
+ );
+
+ if let Some((pane, _)) = result {
+ window.focus = Some(pane);
+ }
+
+ self.panes_created += 1;
+ }
+ }
+ WindowMessage::FocusAdjacent(direction) => {
+ let window = self.windows.get_mut(&id).unwrap();
+ if let Some(pane) = window.focus {
+ if let Some(adjacent) =
+ window.panes.adjacent(&pane, direction)
+ {
+ window.focus = Some(adjacent);
+ }
+ }
+ }
+ WindowMessage::Clicked(pane) => {
+ let window = self.windows.get_mut(&id).unwrap();
+ window.focus = Some(pane);
+ }
+ WindowMessage::CloseWindow => {
+ let _ = self.windows.remove(&id);
+ return window::close(id);
+ }
+ WindowMessage::Resized(pane_grid::ResizeEvent { split, ratio }) => {
+ let window = self.windows.get_mut(&id).unwrap();
+ window.panes.resize(&split, ratio);
+ }
+ 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);
+ }
+ }
+ }
+ 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()),
+ scale: 1.0 + (self.windows.len() as f64 / 10.0),
+ };
+
+ let window_id = window::Id::new(self.windows.len());
+ self.windows.insert(window_id, window);
+ return window::spawn(window_id, Default::default());
+ }
+ }
+ WindowMessage::Dragged(pane_grid::DragEvent::Dropped {
+ pane,
+ target,
+ }) => {
+ let window = self.windows.get_mut(&id).unwrap();
+ window.panes.swap(&pane, &target);
+ }
+ // 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;
+ }
+ }
+ WindowMessage::Close(pane) => {
+ let window = self.windows.get_mut(&id).unwrap();
+ if let Some((_, sibling)) = window.panes.close(&pane) {
+ window.focus = Some(sibling);
+ }
+ }
+ 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)) =
+ window.panes.close(&pane)
+ {
+ window.focus = Some(sibling);
+ }
+ }
+ }
+ }
+ }
+ },
+ Message::CountIncremented(_) => {
+ self.count += 1;
+ }
+ }
+
+ Command::none()
+ }
+
+ fn subscription(&self) -> Subscription<Message> {
+ Subscription::batch(vec![
+ 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).map(|message| {
+ Message::Window(window::Id::new(0usize), message)
+ })
+ } // TODO(derezzedex)
+ _ => None,
+ }
+ }),
+ time::every(Duration::from_secs(1)).map(Message::CountIncremented),
+ ])
+ }
+
+ 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("Close"))
+ .on_press(WindowMessage::CloseWindow)
+ .style(theme::Button::Destructive),
+ ]
+ .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,
+ pane.scrollable_id.clone(),
+ self.count,
+ total_panes,
+ pane.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(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()
+ }
+
+ fn close_requested(&self, window: window::Id) -> Self::Message {
+ Message::Window(window, WindowMessage::CloseWindow)
+ }
+
+ fn scale_factor(&self, window: Id) -> f64 {
+ self.windows.get(&window).map(|w| w.scale).unwrap_or(1.0)
+ }
+}
+
+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<WindowMessage> {
+ 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(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)
+ }
+}
+
+#[derive(Debug)]
+struct Pane {
+ id: usize,
+ pub scrollable_id: scrollable::Id,
+ pub axis: pane_grid::Axis,
+ pub is_pinned: bool,
+ pub is_moving: bool,
+ pub snapped: bool,
+}
+
+impl Pane {
+ fn new(id: usize, axis: pane_grid::Axis) -> Self {
+ Self {
+ id,
+ scrollable_id: scrollable::Id::unique(),
+ axis,
+ is_pinned: false,
+ is_moving: false,
+ snapped: false,
+ }
+ }
+}
+
+fn view_content<'a>(
+ pane: pane_grid::Pane,
+ scrollable_id: scrollable::Id,
+ count: usize,
+ total_panes: usize,
+ is_pinned: bool,
+ size: Size,
+) -> Element<'a, WindowMessage> {
+ let button = |label, message| {
+ button(
+ text(label)
+ .width(Length::Fill)
+ .horizontal_alignment(alignment::Horizontal::Center)
+ .size(16),
+ )
+ .width(Length::Fill)
+ .padding(8)
+ .on_press(message)
+ };
+
+ let mut controls = column![
+ button(
+ "Split horizontally",
+ WindowMessage::Split(pane_grid::Axis::Horizontal, pane),
+ ),
+ button(
+ "Split vertically",
+ WindowMessage::Split(pane_grid::Axis::Vertical, pane),
+ ),
+ button("Snap", WindowMessage::SnapToggle,)
+ ]
+ .spacing(5)
+ .max_width(150);
+
+ if total_panes > 1 && !is_pinned {
+ controls = controls.push(
+ button("Close", WindowMessage::Close(pane))
+ .style(theme::Button::Destructive),
+ );
+ }
+
+ let content = column![
+ text(format!("{}x{}", size.width, size.height)).size(24),
+ controls,
+ text(format!("{count}")).size(48),
+ ]
+ .width(Length::Fill)
+ .height(800)
+ .spacing(10)
+ .align_items(Alignment::Center);
+
+ container(
+ scrollable(content)
+ .height(Length::Fill)
+ .vertical_scroll(Properties::new())
+ .id(scrollable_id),
+ )
+ .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,
+ 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 {
+ 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);
+ }
+
+ content.into()
+}
+
+mod style {
+ use iced::widget::container;
+ use iced::Theme;
+
+ pub fn title_bar_active(theme: &Theme) -> container::Appearance {
+ let palette = theme.extended_palette();
+
+ container::Appearance {
+ text_color: Some(palette.background.strong.text),
+ background: Some(palette.background.strong.color.into()),
+ ..Default::default()
+ }
+ }
+
+ pub fn title_bar_focused(theme: &Theme) -> container::Appearance {
+ let palette = theme.extended_palette();
+
+ container::Appearance {
+ text_color: Some(palette.primary.strong.text),
+ background: Some(palette.primary.strong.color.into()),
+ ..Default::default()
+ }
+ }
+
+ pub fn pane_active(theme: &Theme) -> container::Appearance {
+ let palette = theme.extended_palette();
+
+ container::Appearance {
+ background: Some(palette.background.weak.color.into()),
+ border_width: 2.0,
+ border_color: palette.background.strong.color,
+ ..Default::default()
+ }
+ }
+
+ pub fn pane_focused(theme: &Theme) -> container::Appearance {
+ let palette = theme.extended_palette();
+
+ container::Appearance {
+ background: Some(palette.background.weak.color.into()),
+ border_width: 2.0,
+ border_color: palette.primary.strong.color,
+ ..Default::default()
+ }
+ }
+}
diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs
index 9a4ee754..eb461bb0 100644
--- a/examples/solar_system/src/main.rs
+++ b/examples/solar_system/src/main.rs
@@ -89,7 +89,7 @@ impl Application for SolarSystem {
}
fn subscription(&self) -> Subscription<Message> {
- window::frames().map(Message::Tick)
+ window::frames().map(|frame| Message::Tick(frame.at))
}
}