From ad65f45f5b0fa2fc72bf98019a91602601e6eee0 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 18 Feb 2025 00:59:11 -0500 Subject: [PATCH] refactor: SurfaceMessage --- examples/applet/src/window.rs | 14 +-- examples/application/src/main.rs | 8 +- src/app/core.rs | 5 +- src/app/cosmic.rs | 50 +++++++++-- src/app/mod.rs | 134 +++++++++++++---------------- src/applet/mod.rs | 3 +- src/lib.rs | 2 + src/surface_message.rs | 81 +++++++++++++++++ src/widget/responsive_container.rs | 2 +- 9 files changed, 201 insertions(+), 98 deletions(-) create mode 100644 src/surface_message.rs diff --git a/examples/applet/src/window.rs b/examples/applet/src/window.rs index d62afad9..0936d42d 100644 --- a/examples/applet/src/window.rs +++ b/examples/applet/src/window.rs @@ -1,10 +1,10 @@ -use cosmic::app::message::{SurfaceMessage, SurfaceMessageHandler}; use cosmic::app::Core; use cosmic::iced::window::Id; use cosmic::iced::{Length, Limits, Task}; use cosmic::iced_runtime::core::window; use cosmic::iced_runtime::platform_specific::wayland::popup::SctkPopupSettings; use cosmic::iced_runtime::platform_specific::wayland::subsurface; +use cosmic::surface_message::{SurfaceMessage, SurfaceMessageHandler}; use cosmic::widget::dropdown::DropdownView; use cosmic::widget::{autosize, dropdown, layer_container, list_column, settings, toggler}; use cosmic::{iced_core, Element}; @@ -63,10 +63,10 @@ impl std::fmt::Debug for Message { } impl SurfaceMessageHandler for Message { - fn to_surface_message(self) -> cosmic::app::message::MessageWrapper { + fn to_surface_message(self) -> cosmic::surface_message::MessageWrapper { match self { - Message::Surface(msg) => cosmic::app::message::MessageWrapper::Surface(msg), - msg => cosmic::app::message::MessageWrapper::Message(msg), + Message::Surface(msg) => cosmic::surface_message::MessageWrapper::Surface(msg), + msg => cosmic::surface_message::MessageWrapper::Message(msg), } } } @@ -123,7 +123,7 @@ impl cosmic::Application for Window { } Message::OpenDropdown(sctk_popup_settings, view) => { self.dropdown_id = sctk_popup_settings.id; - return cosmic::task::message(cosmic::app::message::get_popup::< + return cosmic::task::message(cosmic::app::message::app_popup::< Window, >( move |_: &mut Window| sctk_popup_settings.clone(), @@ -148,7 +148,7 @@ impl cosmic::Application for Window { if let Some(id) = self.popup { cosmic::app::message::destroy_popup::(id) } else { - cosmic::app::message::get_popup::( + cosmic::app::message::app_popup::( |state: &mut Window| { let new_id = Id::unique(); state.popup = Some(new_id); @@ -198,7 +198,7 @@ impl cosmic::Application for Window { }, ), |_| { - cosmic::app::message::get_subsurface( + cosmic::app::message::subsurface( |app: &mut Window| { subsurface::SctkSubsurfaceSettings { parent: window::Id::RESERVED, diff --git a/examples/application/src/main.rs b/examples/application/src/main.rs index 6d937cb7..cb709b5f 100644 --- a/examples/application/src/main.rs +++ b/examples/application/src/main.rs @@ -6,12 +6,12 @@ use std::collections::HashMap; use std::sync::LazyLock; -use cosmic::app::message::{SurfaceMessage, SurfaceMessageHandler}; use cosmic::app::{Core, Settings, Task}; use cosmic::iced::alignment::{Horizontal, Vertical}; use cosmic::iced::widget::column; use cosmic::iced::Length; use cosmic::iced_core::Size; +use cosmic::surface_message::{MessageWrapper, SurfaceMessage, SurfaceMessageHandler}; use cosmic::widget::icon::{from_name, Handle}; use cosmic::widget::menu::KeyBind; use cosmic::widget::{ @@ -88,10 +88,10 @@ pub enum Message { } impl SurfaceMessageHandler for Message { - fn to_surface_message(self) -> cosmic::app::message::MessageWrapper { + fn to_surface_message(self) -> MessageWrapper { match self { - Message::Surface(msg) => cosmic::app::message::MessageWrapper::Surface(msg), - msg => cosmic::app::message::MessageWrapper::Message(msg), + Message::Surface(msg) => MessageWrapper::Surface(msg), + msg => MessageWrapper::Message(msg), } } } diff --git a/src/app/core.rs b/src/app/core.rs index 36feb0f0..151e94fe 100644 --- a/src/app/core.rs +++ b/src/app/core.rs @@ -4,6 +4,7 @@ use std::{cell::OnceCell, collections::HashMap}; use crate::{ + surface_message::SurfaceMessage, theme::menu_bar, widget::{ button, icon, @@ -18,9 +19,9 @@ use iced_core::{window::Id, Element}; use palette::Srgba; use slotmap::Key; -use crate::Theme; +use crate::{surface_message::SurfaceMessageHandler, Theme}; -use super::{message::SurfaceMessage, Renderer, Task}; +use super::{Renderer, Task}; /// Status of the nav bar and its panels. #[derive(Clone)] diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index 1f7d19ac..d65eb5b3 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use super::{Application, ApplicationExt, Core, Subscription}; use crate::config::CosmicTk; +use crate::surface_message::{MessageWrapper, SurfaceMessage, SurfaceMessageHandler}; use crate::theme::{Theme, ThemeType, THEME}; use crate::widget::nav_bar; use crate::{keyboard_nav, Element}; @@ -91,7 +92,7 @@ pub struct Cosmic { impl Cosmic where - T::Message: Send + 'static + super::message::SurfaceMessageHandler, + T::Message: Send + 'static + crate::surface_message::SurfaceMessageHandler, { pub fn init( (mut core, flags): (Core, T::Flags), @@ -123,11 +124,11 @@ where ) -> iced::Task> { let message = match message { super::Message::App(message) => { - match super::message::SurfaceMessageHandler::to_surface_message(message) { - super::message::MessageWrapper::Surface(surface_message) => { + match SurfaceMessageHandler::to_surface_message(message) { + MessageWrapper::Surface(surface_message) => { #[cfg(feature = "wayland")] match surface_message { - super::message::SurfaceMessage::Subsurface(settings, view) => { + SurfaceMessage::Subsurface(settings, view) => { let Some(settings) = std::sync::Arc::try_unwrap(settings) .ok() .and_then(|s| s.downcast:: iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings + Send + Sync>>().ok()) else { @@ -159,7 +160,7 @@ where )) } } - super::message::SurfaceMessage::Popup(settings, view) => { + SurfaceMessage::AppPopup(settings, view) => { let Some(settings) = std::sync::Arc::try_unwrap(settings) .ok() .and_then(|s| s.downcast:: iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send + Sync>>().ok()) else { @@ -191,14 +192,14 @@ where } } #[cfg(feature = "wayland")] - super::message::SurfaceMessage::DestroyPopup(id) => { + SurfaceMessage::DestroyPopup(id) => { iced_winit::commands::popup::destroy_popup(id) } #[cfg(feature = "wayland")] - super::message::SurfaceMessage::DestroySubsurface(id) => { + SurfaceMessage::DestroySubsurface(id) => { iced_winit::commands::subsurface::destroy_subsurface(id) } - super::message::SurfaceMessage::ResponsiveMenuBar { + SurfaceMessage::ResponsiveMenuBar { menu_bar, limits, size, @@ -207,11 +208,42 @@ where core.menu_bars.insert(menu_bar, (limits, size)); iced::Task::none() } + SurfaceMessage::Popup(settings, view) => { + let Some(settings) = std::sync::Arc::try_unwrap(settings) + .ok() + .and_then(|s| s.downcast:: iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send + Sync>>().ok()) else { + tracing::error!("Invalid settings for popup"); + return Task::none(); + }; + + if let Some(view) = view.and_then(|view| { + match std::sync::Arc::try_unwrap(view).ok()?.downcast:: Element<'static, super::Message> + + Send + + Sync, + >>( + ) { + Ok(v) => Some(v), + Err(err) => { + tracing::error!( + "Invalid view for subsurface view: {err:?}" + ); + None + } + } + }) { + let settings = settings(); + + self.get_popup(settings, Box::new(move |_| view())) + } else { + iced_winit::commands::popup::get_popup(settings()) + } + } } #[cfg(not(feature = "wayland"))] iced::Task::none() } - super::message::MessageWrapper::Message(message) => self.app.update(message), + MessageWrapper::Message(message) => self.app.update(message), } } super::Message::Cosmic(message) => self.cosmic_update(message), diff --git a/src/app/mod.rs b/src/app/mod.rs index fbe3f2fe..e7117af8 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -6,6 +6,8 @@ //! Check out our [application](https://github.com/pop-os/libcosmic/tree/master/examples/application) //! example in our repository. +use crate::surface_message::{MessageWrapper, SurfaceMessage, SurfaceMessageHandler}; + pub mod command; pub mod context_drawer; mod core; @@ -15,78 +17,10 @@ pub(crate) mod multi_window; pub mod settings; pub mod message { + use crate::surface_message::{MessageWrapper, SurfaceMessage, SurfaceMessageHandler}; + use iced::{Limits, Size}; - pub enum MessageWrapper { - Surface(SurfaceMessage), - Message(M), - } - - pub trait SurfaceMessageHandler: Sized { - fn to_surface_message(self) -> MessageWrapper; - } - - #[cfg(not(feature = "wayland"))] - impl SurfaceMessageHandler for M { - fn to_surface_message(self) -> MessageWrapper { - MessageWrapper::Message(self) - } - } - - /// Ignore this message in your application. It will be intercepted. - #[derive(Clone)] - pub enum SurfaceMessage { - /// Create a subsurface with a view function - Subsurface( - std::sync::Arc>, - Option>>, - ), - /// Destroy a subsurface with a view function - DestroySubsurface(iced::window::Id), - /// Create a popup with a view function - Popup( - std::sync::Arc>, - Option>>, - ), - /// Destroy a subsurface with a view function - DestroyPopup(iced::window::Id), - /// Responsive menu bar update - ResponsiveMenuBar { - /// Id of the menu bar - menu_bar: crate::widget::Id, - /// Limits of the menu bar - limits: Limits, - /// Requested Full Size for expanded menu bar - size: Size, - }, - } - - #[cfg(feature = "wayland")] - impl std::fmt::Debug for SurfaceMessage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Subsurface(arg0, arg1) => { - f.debug_tuple("Subsurface").field(arg0).field(arg1).finish() - } - Self::DestroySubsurface(arg0) => { - f.debug_tuple("DestroySubsurface").field(arg0).finish() - } - Self::Popup(arg0, arg1) => f.debug_tuple("Popup").field(arg0).field(arg1).finish(), - Self::DestroyPopup(arg0) => f.debug_tuple("DestroyPopup").field(arg0).finish(), - Self::ResponsiveMenuBar { - menu_bar, - limits, - size, - } => f - .debug_struct("ResponsiveMenuBar") - .field("menu_bar", menu_bar) - .field("limits", limits) - .field("size", size) - .finish(), - } - } - } - #[derive(Clone, Debug)] #[must_use] pub enum Message { @@ -122,6 +56,15 @@ pub mod message { App::Message::from(surface_msg) } + /// Used to produce a destroy popup message from within a widget. + #[cfg(feature = "wayland")] + pub fn destroy_popup_simple(id: iced_core::window::Id) -> Message + where + Message: SurfaceMessageHandler + From + 'static, + { + Message::from(SurfaceMessage::DestroyPopup(id)) + } + #[cfg(feature = "wayland")] pub fn destroy_subsurface(id: iced_core::window::Id) -> App::Message where @@ -132,7 +75,7 @@ pub mod message { } #[cfg(feature = "wayland")] - pub fn get_popup( + pub fn app_popup( settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send + Sync @@ -148,6 +91,8 @@ pub mod message { App::Message: SurfaceMessageHandler + From, { use std::{any::Any, sync::Arc}; + + use crate::surface_message::{SurfaceMessage, SurfaceMessageHandler}; let boxed: Box< dyn Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send @@ -156,7 +101,7 @@ pub mod message { > = Box::new(settings); let boxed: Box = Box::new(boxed); - App::Message::from(SurfaceMessage::Popup( + App::Message::from(SurfaceMessage::AppPopup( Arc::new(boxed), view.map(|view| { let boxed: Box< @@ -171,6 +116,46 @@ pub mod message { )) } + /// Used to create a popup message from within a widget. + #[cfg(feature = "wayland")] + pub fn simple_popup( + settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + + Send + + Sync + + 'static, + view: Option< + impl Fn() -> crate::Element<'static, crate::app::Message> + Send + Sync + 'static, + >, + ) -> Message + where + Message: SurfaceMessageHandler + From + 'static, + { + use std::{any::Any, sync::Arc}; + + use crate::surface_message::{SurfaceMessage, SurfaceMessageHandler}; + let boxed: Box< + dyn Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + + Send + + Sync + + 'static, + > = Box::new(settings); + let boxed: Box = Box::new(boxed); + + Message::from(SurfaceMessage::Popup( + Arc::new(boxed), + view.map(|view| { + let boxed: Box< + dyn Fn() -> crate::Element<'static, super::Message> + + Send + + Sync + + 'static, + > = Box::new(view); + let boxed: Box = Box::new(boxed); + Arc::new(boxed) + }), + )) + } + // TODO could this somehow be used by widgets? It is the wrong message type though. // #[cfg(feature = "wayland")] // pub fn get_simple_popup( @@ -186,7 +171,7 @@ pub mod message { // } #[cfg(feature = "wayland")] - pub fn get_subsurface( + pub fn subsurface( settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings + Send + Sync + 'static, view: Option< impl Fn(&App) -> crate::Element<'static, super::Message> @@ -199,6 +184,8 @@ pub mod message { App::Message: SurfaceMessageHandler + From, { use std::{any::Any, sync::Arc}; + + use crate::surface_message::{SurfaceMessage, SurfaceMessageHandler}; let boxed: Box< dyn Fn( &mut App, @@ -245,7 +232,6 @@ use context_drawer::ContextDrawer; use iced::window; use iced::{Length, Subscription}; pub use message::Message; -use message::SurfaceMessageHandler; use url::Url; #[cfg(feature = "single-instance")] use { diff --git a/src/applet/mod.rs b/src/applet/mod.rs index 6e3af6df..9b2aa130 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -2,7 +2,7 @@ pub mod token; use crate::{ - app::{self, iced_settings, message::SurfaceMessageHandler, Core}, + app::{self, iced_settings, Core}, cctk::sctk, iced::{ self, @@ -11,6 +11,7 @@ use crate::{ window, Color, Length, Limits, Rectangle, }, iced_widget, + surface_message::SurfaceMessageHandler, theme::{self, system_dark, system_light, Button, THEME}, widget::{ self, diff --git a/src/lib.rs b/src/lib.rs index bd4651ac..441f0894 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,6 +85,8 @@ pub mod process; #[cfg(feature = "wayland")] pub use cctk; +pub mod surface_message; + pub mod theme; #[doc(inline)] diff --git a/src/surface_message.rs b/src/surface_message.rs new file mode 100644 index 00000000..7a623ae2 --- /dev/null +++ b/src/surface_message.rs @@ -0,0 +1,81 @@ +use iced::Size; + +use iced::Limits; + +pub enum MessageWrapper { + Surface(SurfaceMessage), + Message(M), +} + +pub trait SurfaceMessageHandler: Sized { + fn to_surface_message(self) -> MessageWrapper; +} + +#[cfg(not(feature = "wayland"))] +impl SurfaceMessageHandler for M { + fn to_surface_message(self) -> MessageWrapper { + MessageWrapper::Message(self) + } +} + +/// Ignore this message in your application. It will be intercepted. +#[derive(Clone)] +pub enum SurfaceMessage { + /// Create a subsurface with a view function + Subsurface( + std::sync::Arc>, + Option>>, + ), + /// Destroy a subsurface with a view function + DestroySubsurface(iced::window::Id), + /// Create a popup with a view function accepting the App as a parameter + AppPopup( + std::sync::Arc>, + Option>>, + ), + /// Create a popup + Popup( + std::sync::Arc>, + Option>>, + ), + /// Destroy a subsurface with a view function + DestroyPopup(iced::window::Id), + /// Responsive menu bar update + ResponsiveMenuBar { + /// Id of the menu bar + menu_bar: crate::widget::Id, + /// Limits of the menu bar + limits: Limits, + /// Requested Full Size for expanded menu bar + size: Size, + }, +} + +#[cfg(feature = "wayland")] +impl std::fmt::Debug for SurfaceMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Subsurface(arg0, arg1) => { + f.debug_tuple("Subsurface").field(arg0).field(arg1).finish() + } + Self::DestroySubsurface(arg0) => { + f.debug_tuple("DestroySubsurface").field(arg0).finish() + } + Self::AppPopup(arg0, arg1) => { + f.debug_tuple("AppPopup").field(arg0).field(arg1).finish() + } + Self::Popup(arg0, arg1) => f.debug_tuple("Popup").field(arg0).field(arg1).finish(), + Self::DestroyPopup(arg0) => f.debug_tuple("DestroyPopup").field(arg0).finish(), + Self::ResponsiveMenuBar { + menu_bar, + limits, + size, + } => f + .debug_struct("ResponsiveMenuBar") + .field("menu_bar", menu_bar) + .field("limits", limits) + .field("size", size) + .finish(), + } + } +} diff --git a/src/widget/responsive_container.rs b/src/widget/responsive_container.rs index 32f4bbe0..d1c318ec 100644 --- a/src/widget/responsive_container.rs +++ b/src/widget/responsive_container.rs @@ -9,7 +9,7 @@ use iced_core::renderer; use iced_core::widget::{tree, Id, Tree}; use iced_core::{Clipboard, Element, Layout, Length, Rectangle, Shell, Vector, Widget}; -use crate::app::message::SurfaceMessage; +use crate::surface_message::SurfaceMessage; pub(crate) fn responsive_container<'a, Message: 'static + From, Theme, E>( content: E,