From c66e4aafd0c79a34ea599da347c58c50d32408c1 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 7 Dec 2023 15:27:52 -0500 Subject: [PATCH] update to support winit multi-window --- Cargo.toml | 3 ++ examples/application/src/main.rs | 3 +- examples/cosmic/src/main.rs | 2 +- examples/cosmic/src/window.rs | 8 ++-- examples/open-dialog/src/main.rs | 7 ++- iced | 2 +- src/app/command.rs | 29 +++++++------ src/app/core.rs | 7 ++- src/app/cosmic.rs | 60 +++++++++++++++----------- src/app/mod.rs | 64 +++++++++++++++++++--------- src/app/settings.rs | 4 +- src/command/mod.rs | 49 ++++++++++----------- src/widget/context_drawer/overlay.rs | 8 ++-- 13 files changed, 149 insertions(+), 97 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aeb86e1a..83948625 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,10 @@ wayland = [ "iced/wayland", "iced_sctk", "cctk", + "multi-window", ] +# multi-window support +multi-window = ["iced_runtime/multi-window", "iced/multi-window", "iced_winit?/multi-window"] # Render with wgpu wgpu = ["iced/wgpu", "iced_wgpu"] # X11 window support via winit diff --git a/examples/application/src/main.rs b/examples/application/src/main.rs index d8abad60..a84eca1c 100644 --- a/examples/application/src/main.rs +++ b/examples/application/src/main.rs @@ -4,6 +4,7 @@ //! Application API example use cosmic::app::{Command, Core, Settings}; +use cosmic::iced_core::Size; use cosmic::widget::nav_bar; use cosmic::{executor, iced, ApplicationExt, Element}; @@ -43,7 +44,7 @@ fn main() -> Result<(), Box> { .default_icon_theme("Pop") .default_text_size(16.0) .scale_factor(1.0) - .size((1024, 768)) + .size(Size::new(1024., 768.)) .theme(cosmic::Theme::dark()); cosmic::app::run::(settings, input)?; diff --git a/examples/cosmic/src/main.rs b/examples/cosmic/src/main.rs index f180b100..d864f7e5 100644 --- a/examples/cosmic/src/main.rs +++ b/examples/cosmic/src/main.rs @@ -15,6 +15,6 @@ pub fn main() -> cosmic::iced::Result { env_logger::init_from_env(env); cosmic::icon_theme::set_default("Pop"); let mut settings = Settings::default(); - settings.window.min_size = Some((600, 300)); + settings.window.min_size = Some(cosmic::iced::Size::new(600., 300.)); Window::run(settings) } diff --git a/examples/cosmic/src/window.rs b/examples/cosmic/src/window.rs index c8e67484..8f621c19 100644 --- a/examples/cosmic/src/window.rs +++ b/examples/cosmic/src/window.rs @@ -436,10 +436,10 @@ impl Application for Window { Message::ToggleNavBarCondensed => { self.nav_bar_toggled_condensed = !self.nav_bar_toggled_condensed } - Message::Drag => return drag(), - Message::Close => return close(), - Message::Minimize => return minimize(true), - Message::Maximize => return toggle_maximize(), + Message::Drag => return drag(window::Id::MAIN), + Message::Close => return close(window::Id::MAIN), + Message::Minimize => return minimize(window::Id::MAIN, true), + Message::Maximize => return toggle_maximize(window::Id::MAIN), Message::InputChanged => {} diff --git a/examples/open-dialog/src/main.rs b/examples/open-dialog/src/main.rs index a9901413..f14379fc 100644 --- a/examples/open-dialog/src/main.rs +++ b/examples/open-dialog/src/main.rs @@ -16,7 +16,7 @@ use url::Url; #[rustfmt::skip] fn main() -> Result<(), Box> { let settings = Settings::default() - .size((1024, 768)); + .size(cosmic::iced::Size::new(1024.0, 768.0)); cosmic::app::run::(settings, ())?; @@ -77,7 +77,10 @@ impl cosmic::Application for App { }; app.set_header_title("Open a file".into()); - let cmd = app.set_window_title("COSMIC OpenDialog Demo".into()); + let cmd = app.set_window_title( + "COSMIC OpenDialog Demo".into(), + cosmic::iced::window::Id::MAIN, + ); (app, cmd) } diff --git a/iced b/iced index 33b2fd96..4521d6e5 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit 33b2fd967ada2d2c86eb1b57eb4997719774499e +Subproject commit 4521d6e584b1cddd00d8d1e7c20784cfd5bcdea9 diff --git a/src/app/command.rs b/src/app/command.rs index 74554c9c..788708dc 100644 --- a/src/app/command.rs +++ b/src/app/command.rs @@ -1,6 +1,8 @@ // Copyright 2023 System76 // SPDX-License-Identifier: MPL-2.0 +use iced::window; + /// Asynchronous actions for COSMIC applications. use super::Message; @@ -27,16 +29,16 @@ pub mod message { } } -pub fn drag() -> iced::Command> { - crate::command::drag().map(Message::Cosmic) +pub fn drag(id: Option) -> iced::Command> { + crate::command::drag(id).map(Message::Cosmic) } -pub fn fullscreen() -> iced::Command> { - crate::command::fullscreen().map(Message::Cosmic) +pub fn fullscreen(id: Option) -> iced::Command> { + crate::command::fullscreen(id).map(Message::Cosmic) } -pub fn minimize() -> iced::Command> { - crate::command::minimize().map(Message::Cosmic) +pub fn minimize(id: Option) -> iced::Command> { + crate::command::minimize(id).map(Message::Cosmic) } pub fn set_scaling_factor(factor: f32) -> iced::Command> { @@ -47,14 +49,17 @@ pub fn set_theme(theme: crate::Theme) -> iced::Command(title: String) -> iced::Command> { - crate::command::set_title(title).map(Message::Cosmic) +pub fn set_title( + id: Option, + title: String, +) -> iced::Command> { + crate::command::set_title(id, title).map(Message::Cosmic) } -pub fn set_windowed() -> iced::Command> { - crate::command::set_windowed().map(Message::Cosmic) +pub fn set_windowed(id: Option) -> iced::Command> { + crate::command::set_windowed(id).map(Message::Cosmic) } -pub fn toggle_fullscreen() -> iced::Command> { - crate::command::toggle_fullscreen().map(Message::Cosmic) +pub fn toggle_fullscreen(id: Option) -> iced::Command> { + crate::command::toggle_fullscreen(id).map(Message::Cosmic) } diff --git a/src/app/core.rs b/src/app/core.rs index d401f014..6f7ee5e5 100644 --- a/src/app/core.rs +++ b/src/app/core.rs @@ -1,8 +1,11 @@ // Copyright 2023 System76 // SPDX-License-Identifier: MPL-2.0 +use std::collections::HashMap; + use cosmic_config::CosmicConfigEntry; use cosmic_theme::ThemeMode; +use iced_core::window::Id; use crate::Theme; @@ -56,7 +59,7 @@ pub struct Core { /// Theme mode pub(super) system_theme_mode: ThemeMode, - pub(super) title: String, + pub(super) title: HashMap, pub window: Window, @@ -78,7 +81,7 @@ impl Default for Core { toggled_condensed: true, }, scale_factor: 1.0, - title: String::new(), + title: HashMap::new(), theme_sub_counter: 0, system_theme: crate::theme::active(), system_theme_mode: ThemeMode::config() diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index 33042725..46f5c240 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -12,7 +12,13 @@ use cosmic_theme::ThemeMode; use iced::event::wayland::{self, WindowEvent}; #[cfg(feature = "wayland")] use iced::event::PlatformSpecific; +#[cfg(all(feature = "winit", feature = "multi-window"))] +use iced::multi_window::Application as IcedApplication; +#[cfg(feature = "wayland")] +use iced::wayland::Application as IcedApplication; use iced::window; +#[cfg(not(feature = "multi-window"))] +use iced::Application as IcedApplication; use iced_futures::event::listen_raw; #[cfg(not(feature = "wayland"))] use iced_runtime::command::Action; @@ -67,7 +73,7 @@ pub(crate) struct Cosmic { pub(crate) should_exit: bool, } -impl iced::Application for Cosmic +impl IcedApplication for Cosmic where T::Message: Send + 'static, { @@ -82,17 +88,16 @@ where (Self::new(model), command) } - #[cfg(feature = "wayland")] - fn close_requested(&self, id: window::Id) -> Self::Message { - self.app - .on_close_requested(id) - .map_or(super::Message::None, super::Message::App) - } - + #[cfg(not(feature = "multi-window"))] fn title(&self) -> String { self.app.title().to_string() } + #[cfg(feature = "multi-window")] + fn title(&self, id: window::Id) -> String { + self.app.title(id).to_string() + } + fn update(&mut self, message: Self::Message) -> iced::Command { match message { super::Message::App(message) => self.app.update(message), @@ -103,13 +108,14 @@ where } } + #[cfg(not(feature = "multi-window"))] fn scale_factor(&self) -> f64 { f64::from(self.app.core().scale_factor()) } - #[cfg(feature = "wayland")] - fn should_exit(&self) -> bool { - self.should_exit || self.app.should_exit() + #[cfg(feature = "multi-window")] + fn scale_factor(&self, id: window::Id) -> f64 { + f64::from(self.app.core().scale_factor()) } fn style(&self) -> ::Style { @@ -191,13 +197,19 @@ where ]) } + #[cfg(not(feature = "multi-window"))] fn theme(&self) -> Self::Theme { crate::theme::active() } - #[cfg(feature = "wayland")] + #[cfg(feature = "multi-window")] + fn theme(&self, id: window::Id) -> Self::Theme { + crate::theme::active() + } + + #[cfg(feature = "multi-window")] fn view(&self, id: window::Id) -> Element { - if id != window::Id(0) { + if id != window::Id::MAIN { return self.app.view_window(id).map(super::Message::App); } @@ -208,7 +220,7 @@ where } } - #[cfg(not(feature = "wayland"))] + #[cfg(not(feature = "multi-window"))] fn view(&self) -> Element { self.app.view_main() } @@ -224,14 +236,14 @@ impl Cosmic { #[cfg(not(feature = "wayland"))] #[allow(clippy::unused_self)] pub fn close(&mut self) -> iced::Command> { - iced::Command::single(Action::Window(WindowAction::Close)) + iced::Command::single(Action::Window(WindowAction::Close(window::Id::MAIN))) } #[allow(clippy::too_many_lines)] fn cosmic_update(&mut self, message: Message) -> iced::Command> { match message { Message::WindowResize(id, width, height) => { - if window::Id(0) == id { + if window::Id::MAIN == id { self.app.core_mut().set_window_width(width); self.app.core_mut().set_window_height(height); } @@ -241,7 +253,7 @@ impl Cosmic { #[cfg(feature = "wayland")] Message::WindowState(id, state) => { - if window::Id(0) == id { + if window::Id::MAIN == id { self.app.core_mut().window.sharp_corners = state.intersects( WindowState::MAXIMIZED | WindowState::FULLSCREEN @@ -256,7 +268,7 @@ impl Cosmic { #[cfg(feature = "wayland")] Message::WmCapabilities(id, capabilities) => { - if window::Id(0) == id { + if window::Id::MAIN == id { self.app.core_mut().window.can_fullscreen = capabilities.contains(WindowManagerCapabilities::FULLSCREEN); self.app.core_mut().window.show_maximize = @@ -281,25 +293,25 @@ impl Cosmic { keyboard_nav::Message::Escape => return self.app.on_escape(), keyboard_nav::Message::Search => return self.app.on_search(), - keyboard_nav::Message::Fullscreen => return command::toggle_fullscreen(), + keyboard_nav::Message::Fullscreen => return command::toggle_fullscreen(None), }, Message::ContextDrawer(show) => { self.app.core_mut().window.show_context = show; } - Message::Drag => return command::drag(), + Message::Drag => return command::drag(None), - Message::Minimize => return command::minimize(), + Message::Minimize => return command::minimize(None), Message::Maximize => { if self.app.core().window.sharp_corners { self.app.core_mut().window.sharp_corners = false; - return command::set_windowed(); + return command::set_windowed(None); } self.app.core_mut().window.sharp_corners = true; - return command::fullscreen(); + return command::fullscreen(None); } Message::NavBar(key) => { @@ -370,7 +382,7 @@ impl Cosmic { Message::Activate(_token) => { #[cfg(feature = "wayland")] return iced_sctk::commands::activation::activate( - iced::window::Id::default(), + iced::window::Id::MAIN, #[allow(clippy::used_underscore_binding)] _token, ); diff --git a/src/app/mod.rs b/src/app/mod.rs index 5a487fe8..17d37dbf 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -47,6 +47,9 @@ use crate::theme::THEME; use crate::widget::{context_drawer, nav_bar}; use apply::Apply; use iced::Subscription; +#[cfg(all(feature = "winit", feature = "multi-window"))] +use iced::{multi_window::Application as IcedApplication, window}; +#[cfg(any(not(feature = "winit"), not(feature = "multi-window")))] use iced::{window, Application as IcedApplication}; pub use message::Message; use url::Url; @@ -70,8 +73,8 @@ pub(crate) fn iced_settings( let mut core = Core::default(); core.debug = settings.debug; core.set_scale_factor(settings.scale_factor); - core.set_window_width(settings.size.0); - core.set_window_height(settings.size.1); + core.set_window_width(settings.size.width as u32); + core.set_window_height(settings.size.height as u32); THEME.with(move |t| { let mut cosmic_theme = t.borrow_mut(); @@ -98,7 +101,7 @@ pub(crate) fn iced_settings( autosize: settings.autosize, client_decorations: settings.client_decorations, resizable: settings.resizable, - size: settings.size, + size: (settings.size.width as u32, settings.size.height as u32).into(), size_limits: settings.size_limits, title: None, transparent: settings.transparent, @@ -428,11 +431,6 @@ where /// Called before closing the application. fn on_app_exit(&mut self) {} - #[cfg(feature = "wayland")] - fn should_exit(&self) -> bool { - false - } - /// Called when a window requests to be closed. fn on_close_requested(&self, id: window::Id) -> Option { None @@ -471,7 +469,7 @@ where /// Constructs views for other windows. fn view_window(&self, id: window::Id) -> Element { - panic!("no view for window {}", id.0); + panic!("no view for window {:?}", id); } /// Overrides the default style for applications @@ -499,10 +497,15 @@ pub trait ApplicationExt: Application { /// Minimizes the window. fn minimize(&mut self) -> iced::Command>; - /// Get the title of the main window. + + #[cfg(not(feature = "multi-window"))] fn title(&self) -> &str; + #[cfg(feature = "multi-window")] + /// Get the title of a window. + fn title(&self, id: window::Id) -> &str; + /// Set the context drawer title. fn set_context_title(&mut self, title: String) { self.core_mut().set_context_title(title); @@ -513,39 +516,60 @@ pub trait ApplicationExt: Application { self.core_mut().set_header_title(title); } + #[cfg(not(feature = "multi-window"))] /// Set the title of the main window. fn set_window_title(&mut self, title: String) -> iced::Command>; + #[cfg(feature = "multi-window")] + /// Set the title of a window. + fn set_window_title( + &mut self, + title: String, + id: window::Id, + ) -> iced::Command>; + /// View template for the main window. fn view_main(&self) -> Element>; } impl ApplicationExt for App { fn drag(&mut self) -> iced::Command> { - command::drag() + command::drag(Some(window::Id::MAIN)) } fn fullscreen(&mut self) -> iced::Command> { - command::fullscreen() + command::fullscreen(Some(window::Id::MAIN)) } fn minimize(&mut self) -> iced::Command> { - command::minimize() + command::minimize(Some(window::Id::MAIN)) } + #[cfg(feature = "multi-window")] + fn title(&self, id: window::Id) -> &str { + self.core().title.get(&id).map(|s| s.as_str()).unwrap_or("") + } + + #[cfg(not(feature = "multi-window"))] fn title(&self) -> &str { - &self.core().title + &self.core().window.header_title } - #[cfg(feature = "wayland")] - fn set_window_title(&mut self, title: String) -> iced::Command> { - self.core_mut().title = title.clone(); - command::set_title(title) + #[cfg(feature = "multi-window")] + fn set_window_title( + &mut self, + title: String, + id: window::Id, + ) -> iced::Command> { + self.core_mut().title.insert(id, title.clone()); + command::set_title(Some(id), title) } - #[cfg(not(feature = "wayland"))] + #[cfg(not(feature = "multi-window"))] fn set_window_title(&mut self, title: String) -> iced::Command> { - self.core_mut().title = title.clone(); + self.core_mut() + .title + .insert(window::Id::MAIN, title.clone()); iced::Command::none() } diff --git a/src/app/settings.rs b/src/app/settings.rs index 21d649f7..c54b52d4 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -48,7 +48,7 @@ pub struct Settings { pub(crate) scale_factor: f32, /// Initial size of the window. - pub(crate) size: (u32, u32), + pub(crate) size: iced::Size, /// Limitations of the window size #[cfg(feature = "wayland")] @@ -91,7 +91,7 @@ impl Default for Settings { .ok() .and_then(|scale| scale.parse::().ok()) .unwrap_or(1.0), - size: (1024, 768), + size: iced::Size::new(1024.0, 768.0), #[cfg(feature = "wayland")] size_limits: Limits::NONE.min_height(1.0).min_width(1.0), theme: crate::theme::system_preference(), diff --git a/src/command/mod.rs b/src/command/mod.rs index 032e5323..3a918a30 100644 --- a/src/command/mod.rs +++ b/src/command/mod.rs @@ -3,7 +3,6 @@ //! Create asynchronous actions to be performed in the background. -#[cfg(feature = "wayland")] use iced::window; use iced::Command; use iced_core::window::Mode; @@ -33,45 +32,45 @@ pub fn message(message: M) -> Command { /// Initiates a window drag. #[cfg(feature = "wayland")] -pub fn drag() -> Command { - iced_sctk::commands::window::start_drag_window(window::Id(0)) +pub fn drag(id: Option) -> Command { + iced_sctk::commands::window::start_drag_window(id.unwrap_or(window::Id::MAIN)) } /// Initiates a window drag. #[cfg(not(feature = "wayland"))] -pub fn drag() -> Command { - iced_runtime::window::drag() +pub fn drag(id: Option) -> Command { + iced_runtime::window::drag(id.unwrap_or(window::Id::MAIN)) } /// Fullscreens the window. #[cfg(feature = "wayland")] -pub fn fullscreen() -> Command { - iced_sctk::commands::window::set_mode_window(window::Id(0), Mode::Fullscreen) +pub fn fullscreen(id: Option) -> Command { + iced_sctk::commands::window::set_mode_window(id.unwrap_or(window::Id::MAIN), Mode::Fullscreen) } /// Fullscreens the window. #[cfg(not(feature = "wayland"))] -pub fn fullscreen() -> Command { - iced_runtime::window::change_mode(Mode::Fullscreen) +pub fn fullscreen(id: Option) -> Command { + iced_runtime::window::change_mode(id.unwrap_or(window::Id::MAIN), Mode::Fullscreen) } /// Minimizes the window. #[cfg(feature = "wayland")] -pub fn minimize() -> Command { - iced_sctk::commands::window::set_mode_window(window::Id(0), Mode::Hidden) +pub fn minimize(id: Option) -> Command { + iced_sctk::commands::window::set_mode_window(id.unwrap_or(window::Id::MAIN), Mode::Hidden) } /// Minimizes the window. #[cfg(not(feature = "wayland"))] -pub fn minimize() -> Command { - iced_runtime::window::minimize(true) +pub fn minimize(id: Option) -> Command { + iced_runtime::window::minimize(id.unwrap_or(window::Id::MAIN), true) } /// Sets the title of a window. #[cfg(feature = "wayland")] -pub fn set_title(title: String) -> Command { +pub fn set_title(id: Option, title: String) -> Command { window_action(WindowAction::Title { - id: window::Id(0), + id: id.unwrap_or(window::Id::MAIN), title, }) } @@ -79,32 +78,34 @@ pub fn set_title(title: String) -> Command { /// Sets the title of a window. #[cfg(not(feature = "wayland"))] #[allow(unused_variables, clippy::needless_pass_by_value)] -pub fn set_title(title: String) -> Command { +pub fn set_title(id: Option, title: String) -> Command { Command::none() } /// Sets the window mode to windowed. #[cfg(feature = "wayland")] -pub fn set_windowed() -> Command { - iced_sctk::commands::window::set_mode_window(window::Id(0), Mode::Windowed) +pub fn set_windowed(id: Option) -> Command { + iced_sctk::commands::window::set_mode_window(id.unwrap_or(window::Id::MAIN), Mode::Windowed) } /// Sets the window mode to windowed. #[cfg(not(feature = "wayland"))] -pub fn set_windowed() -> Command { - iced_runtime::window::change_mode(Mode::Windowed) +pub fn set_windowed(id: Option) -> Command { + iced_runtime::window::change_mode(id.unwrap_or(window::Id::MAIN), Mode::Windowed) } /// Toggles the windows' maximization state. #[cfg(feature = "wayland")] -pub fn toggle_fullscreen() -> Command { - window_action(WindowAction::ToggleFullscreen { id: window::Id(0) }) +pub fn toggle_fullscreen(id: Option) -> Command { + window_action(WindowAction::ToggleFullscreen { + id: id.unwrap_or(window::Id::MAIN), + }) } /// Toggles the windows' maximization state. #[cfg(not(feature = "wayland"))] -pub fn toggle_fullscreen() -> Command { - iced_runtime::window::toggle_maximize() +pub fn toggle_fullscreen(id: Option) -> Command { + iced_runtime::window::toggle_maximize(id.unwrap_or(window::Id::MAIN)) } /// Creates a command to apply an action to a window. diff --git a/src/widget/context_drawer/overlay.rs b/src/widget/context_drawer/overlay.rs index 9452acc9..a66f3900 100644 --- a/src/widget/context_drawer/overlay.rs +++ b/src/widget/context_drawer/overlay.rs @@ -31,10 +31,10 @@ where .width(self.width) .height(bounds.height - 8.0 - position.y); - let mut node = - self.content - .as_widget() - .layout(&mut self.tree, renderer, &limits); + let mut node = self + .content + .as_widget() + .layout(&mut self.tree, renderer, &limits); let node_size = node.size(); node.move_to(Point {