Creating an application with multiple windows

Alright, after a bit more digging, I got it figured out. Based on this post and the example given, here is a bare minimum example as I described as wanting in the above post:

use iced::widget::{button, column, text};
use iced::{Element, Size, Subscription, Task, window};
use std::collections::BTreeMap;

#[derive(Debug, Clone, PartialEq)]
pub enum WindowType {
    MainWindow,
    SecondWindow,
}

impl WindowType {
    pub fn settings(&self) -> window::Settings {
        match self {
            WindowType::MainWindow => window::Settings {
                size: Size::new(400.0, 400.0),
                ..window::Settings::default()
            },
            WindowType::SecondWindow => window::Settings {
                size: Size::new(200.0, 200.0),
                ..window::Settings::default()
            },
        }
    }
}

#[derive(Debug, Clone)]
pub enum MainWindowMessage {
    CreateSecondWindow,
}

#[derive(Debug, Clone)]
pub enum SecondWindowMessage {
    Hello,
}

#[derive(Debug, Clone)]
pub enum Message {
    WindowOpened(window::Id, WindowType),
    WindowClosed(window::Id),
    MainWindow(MainWindowMessage),
    SecondWindow(SecondWindowMessage),
}

struct App {
    windows: BTreeMap<window::Id, WindowType>,
}

impl App {
    fn new() -> (Self, Task<Message>) {
        let window_type = WindowType::MainWindow;

        let (_new_id, task) = window::open(window_type.settings());

        (
            Self {
                windows: BTreeMap::new(),
            },
            task.map(move |id| Message::WindowOpened(id, window_type.clone())),
        )
    }

    fn title(&self, id: window::Id) -> String {
        if let Some(window_type) = self.windows.get(&id) {
            match window_type {
                WindowType::MainWindow => "main window".to_string(),
                WindowType::SecondWindow => "second window".to_string(),
            }
        } else {
            "invalid window".to_string()
        }
    }

    fn update(&mut self, message: Message) -> Task<Message> {
        match message {
            Message::WindowOpened(new_window_id, new_window_type) => {
                self.windows.insert(new_window_id, new_window_type);
            }
            Message::WindowClosed(id) => {
                self.windows.remove(&id);

                if self.windows.is_empty() {
                    return iced::exit();
                }
            }
            Message::MainWindow(main_window_message) => match main_window_message {
                MainWindowMessage::CreateSecondWindow => {
                    let new_window_type = WindowType::SecondWindow;

                    // if the window type already exists, do not create a new window and focus the existing one
                    for (window_id, window_type) in &self.windows {
                        if new_window_type == *window_type {
                            return window::gain_focus(*window_id);
                        }
                    }

                    let (_new_id, task) = window::open(new_window_type.settings());

                    return task.map(move |id| Message::WindowOpened(id, new_window_type.clone()));
                }
            },
            Message::SecondWindow(second_window_message) => match second_window_message {
                SecondWindowMessage::Hello => {
                    println!("hello!");
                }
            },
        }

        Task::none()
    }

    fn view(&'_ self, id: window::Id) -> Element<'_, Message> {
        if let Some(window_type) = self.windows.get(&id) {
            match window_type {
                WindowType::MainWindow => column![
                    button("launch second window")
                        .on_press(Message::MainWindow(MainWindowMessage::CreateSecondWindow))
                ]
                .into(),
                WindowType::SecondWindow => column![
                    text("the second window"),
                    button("hello").on_press(Message::SecondWindow(SecondWindowMessage::Hello))
                ]
                .into(),
            }
        } else {
            column![].into()
        }
    }

    fn subscription(&self) -> Subscription<Message> {
        window::close_events().map(Message::WindowClosed)
    }
}

pub fn main() -> iced::Result {
    iced::daemon(App::title, App::update, App::view)
        .subscription(App::subscription)
        .run_with(App::new)
}

1 Like