From b316d8901b4de7f8bcd75dbbfa08db4f1f3e88f4 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Tue, 31 Dec 2024 17:18:49 -0500 Subject: [PATCH] better surface messages --- examples/applet/src/window.rs | 164 ++++++++++++++++++---------------- src/app/cosmic.rs | 145 +++++++++++++++++------------- src/app/mod.rs | 136 +++++++++++++++++----------- src/applet/mod.rs | 7 +- 4 files changed, 256 insertions(+), 196 deletions(-) diff --git a/examples/applet/src/window.rs b/examples/applet/src/window.rs index e59ee950..1d4a9bcd 100644 --- a/examples/applet/src/window.rs +++ b/examples/applet/src/window.rs @@ -1,5 +1,6 @@ use std::sync::{Arc, Mutex}; +use cosmic::app::message::{SurfaceMessage, SurfaceMessageHandler}; use cosmic::app::Core; use cosmic::iced::application; use cosmic::iced::platform_specific::shell::commands::popup::{destroy_popup, get_popup}; @@ -23,11 +24,24 @@ pub struct Window { #[derive(Clone, Debug)] pub enum Message { - TogglePopup, PopupClosed(Id), ToggleExampleRow(bool), - Hover, - Leave, + Surface(SurfaceMessage), +} + +impl SurfaceMessageHandler for Message { + fn to_surface_message(self) -> cosmic::app::message::MessageWrapper { + match self { + Message::Surface(msg) => cosmic::app::message::MessageWrapper::Surface(msg), + msg => cosmic::app::message::MessageWrapper::Message(msg), + } + } +} + +impl From for Message { + fn from(value: SurfaceMessage) -> Self { + Message::Surface(value) + } } impl cosmic::Application for Window { @@ -58,61 +72,6 @@ impl cosmic::Application for Window { fn update(&mut self, message: Self::Message) -> Task> { match message { - Message::TogglePopup => { - return if let Some(p) = self.popup.take() { - cosmic::task::message(cosmic::app::message::destroy_popup(p)) - } else { - cosmic::task::message( - cosmic::app::message::get_popup( - |state: &mut Window| { - let new_id = Id::unique(); - state.popup = Some(new_id); - let mut popup_settings = state.core.applet.get_popup_settings( - state.core.main_window_id().unwrap(), - new_id, - None, - None, - None, - ); - popup_settings.positioner.size_limits = Limits::NONE - .max_width(372.0) - .min_width(300.0) - .min_height(200.0) - .max_height(1080.0) - .height(500) - .width(500); - popup_settings.positioner.size = Some((500, 500)); - popup_settings - }, - Some( - move |state: &Window| -> cosmic::Element< - 'static, - cosmic::app::Message, - > { - { - let content_list = list_column().padding(5).spacing(0).add( - settings::item( - "Example row", - cosmic::widget::container( - toggler(state.example_row).on_toggle(|value| { - Message::ToggleExampleRow(value) - }), - ) - .height(Length::Fixed(50.)), - ), - ); - - Element::from( - state.core.applet.popup_container(content_list), - ) - .map(cosmic::app::Message::App) - } - }, - ), - ), - ) - }; - } Message::PopupClosed(id) => { if self.popup.as_ref() == Some(&id) { self.popup = None; @@ -121,8 +80,69 @@ impl cosmic::Application for Window { Message::ToggleExampleRow(toggled) => { self.example_row = toggled; } - Message::Hover => { - return cosmic::task::message(cosmic::app::message::get_subsurface( + + Message::Surface(surface_message) => {} + } + Task::none() + } + + fn view(&self) -> Element { + cosmic::widget::wayland::tooltip::widget::Tooltip::new( + self.core.applet.icon_button("display-symbolic").on_press( + if let Some(id) = self.popup { + cosmic::app::message::destroy_popup::(id) + } else { + cosmic::app::message::get_popup::( + |state: &mut Window| { + let new_id = Id::unique(); + state.popup = Some(new_id); + let mut popup_settings = state.core.applet.get_popup_settings( + state.core.main_window_id().unwrap(), + new_id, + None, + None, + None, + ); + popup_settings.positioner.size_limits = Limits::NONE + .max_width(372.0) + .min_width(300.0) + .min_height(200.0) + .max_height(1080.0) + .height(500) + .width(500); + popup_settings.positioner.size = Some((500, 500)); + popup_settings + }, + Some( + move |state: &Window| -> cosmic::Element< + 'static, + cosmic::app::Message, + > { + { + let content_list = list_column().padding(5).spacing(0).add( + settings::item( + "Example row", + cosmic::widget::container( + toggler(state.example_row).on_toggle(|value| { + Message::ToggleExampleRow(value) + }), + ) + .height(Length::Fixed(50.)), + ), + ); + + Element::from( + state.core.applet.popup_container(content_list), + ) + .map(cosmic::app::Message::App) + } + }, + ), + ) + }, + ), + |layout| { + cosmic::app::message::get_subsurface::( |app: &mut Window| { let id = window::Id::unique(); app.subsurface_id = Some(id); @@ -136,25 +156,11 @@ impl cosmic::Application for Window { } }, Some(|app: &Window| layer_container(cosmic::widget::text("hello")).into()), - )); - } - Message::Leave => { - return cosmic::task::message(cosmic::app::message::destroy_subsurface( - self.subsurface_id.unwrap_or(window::Id::NONE), - )); - } - } - Task::none() - } - - fn view(&self) -> Element { - cosmic::widget::wayland::tooltip::widget::Tooltip::new( - self.core - .applet - .icon_button("display-symbolic") - .on_press(Message::TogglePopup), - |layout| Message::Hover, - Message::Leave, + ) + }, + cosmic::app::message::destroy_subsurface::( + self.subsurface_id.unwrap_or(window::Id::NONE), + ), ) .into() } diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index f0ed9ef2..e8f20124 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -91,7 +91,7 @@ pub struct Cosmic { impl Cosmic where - T::Message: Send + 'static, + T::Message: Send + 'static + super::message::SurfaceMessageHandler, { pub fn init( (mut core, flags): (Core, T::Flags), @@ -122,70 +122,93 @@ where message: super::Message, ) -> iced::Task> { let message = match message { - super::Message::App(message) => self.app.update(message), + super::Message::App(message) => { + match super::message::SurfaceMessageHandler::to_surface_message(message) { + super::message::MessageWrapper::Surface(surface_message) => + { + #[cfg(feature = "wayland")] + match surface_message { + super::message::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 { + tracing::error!("Invalid settings for subsurface"); + 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(&mut self.app); + self.get_subsurface(settings, *view) + } else { + iced_winit::commands::subsurface::get_subsurface(settings( + &mut self.app, + )) + } + } + #[cfg(feature = "wayland")] + super::message::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(&mut self.app); + + self.get_popup(settings, *view) + } else { + iced_winit::commands::popup::get_popup(settings(&mut self.app)) + } + } + #[cfg(feature = "wayland")] + super::message::SurfaceMessage::DestroyPopup(id) => { + iced_winit::commands::popup::destroy_popup(id) + } + #[cfg(feature = "wayland")] + super::message::SurfaceMessage::DestroySubsurface(id) => { + iced_winit::commands::subsurface::destroy_subsurface(id) + } + } + } + super::message::MessageWrapper::Message(message) => self.app.update(message), + } + } super::Message::Cosmic(message) => self.cosmic_update(message), super::Message::None => iced::Task::none(), #[cfg(feature = "single-instance")] super::Message::DbusActivation(message) => self.app.dbus_activation(message), - #[cfg(feature = "wayland")] - super::Message::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 { - tracing::error!("Invalid settings for subsurface"); - 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(&mut self.app); - self.get_subsurface(settings, *view) - } else { - iced_winit::commands::subsurface::get_subsurface(settings(&mut self.app)) - } - } - #[cfg(feature = "wayland")] - super::Message::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(&mut self.app); - - self.get_popup(settings, *view) - } else { - iced_winit::commands::popup::get_popup(settings(&mut self.app)) - } - } - #[cfg(feature = "wayland")] - super::Message::DestroyPopup(id) => iced_winit::commands::popup::destroy_popup(id), - #[cfg(feature = "wayland")] - super::Message::DestroySubsurface(id) => { - iced_winit::commands::subsurface::destroy_subsurface(id) - } }; #[cfg(target_env = "gnu")] diff --git a/src/app/mod.rs b/src/app/mod.rs index daad39f3..59d5a118 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -15,7 +15,57 @@ pub(crate) mod multi_window; pub mod settings; pub mod message { + 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) + } + } + #[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), + } + + #[cfg(feature = "wayland")] + impl std::fmt::Debug for SurfaceMessage { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + SurfaceMessage::Subsurface(any, any1) => f.debug_tuple("Subsurface").finish(), + SurfaceMessage::DestroySubsurface(id) => { + f.debug_tuple("DestroySubsurface").field(id).finish() + } + SurfaceMessage::Popup(any, any1) => f.debug_tuple("Popup").finish(), + SurfaceMessage::DestroyPopup(id) => { + f.debug_tuple("DestroyPopup").field(id).finish() + } + } + } + } + + #[derive(Clone, Debug)] #[must_use] pub enum Message { /// Messages from the application, for the application. @@ -27,24 +77,6 @@ pub mod message { DbusActivation(super::DbusActivationMessage), /// Do nothing None, - #[cfg(feature = "wayland")] - /// Create a subsurface with a view function - Subsurface( - std::sync::Arc>, - Option>>, - ), - #[cfg(feature = "wayland")] - /// Destroy a subsurface with a view function - DestroySubsurface(iced::window::Id), - #[cfg(feature = "wayland")] - /// Create a popup with a view function - Popup( - std::sync::Arc>, - Option>>, - ), - #[cfg(feature = "wayland")] - /// Destroy a subsurface with a view function - DestroyPopup(iced::window::Id), } pub const fn app(message: M) -> Message { @@ -60,17 +92,25 @@ pub mod message { } #[cfg(feature = "wayland")] - pub const fn destroy_popup(id: iced_core::window::Id) -> Message { - Message::DestroyPopup(id) + pub fn destroy_popup(id: iced_core::window::Id) -> App::Message + where + App::Message: SurfaceMessageHandler + From, + { + let surface_msg = SurfaceMessage::DestroyPopup(id); + App::Message::from(surface_msg) } #[cfg(feature = "wayland")] - pub const fn destroy_subsurface(id: iced_core::window::Id) -> Message { - Message::DestroySubsurface(id) + pub fn destroy_subsurface(id: iced_core::window::Id) -> App::Message + where + App::Message: SurfaceMessageHandler + From, + { + let surface_msg = SurfaceMessage::DestroySubsurface(id); + App::Message::from(surface_msg) } #[cfg(feature = "wayland")] - pub fn get_popup( + pub fn get_popup( settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send + Sync @@ -81,7 +121,10 @@ pub mod message { + Sync + 'static, >, - ) -> Message { + ) -> App::Message + where + App::Message: SurfaceMessageHandler + From, + { use std::{any::Any, sync::Arc}; let boxed: Box< dyn Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings @@ -91,7 +134,7 @@ pub mod message { > = Box::new(settings); let boxed: Box = Box::new(boxed); - Message::Popup( + App::Message::from(SurfaceMessage::Popup( Arc::new(boxed), view.map(|view| { let boxed: Box< @@ -103,11 +146,11 @@ pub mod message { let boxed: Box = Box::new(boxed); Arc::new(boxed) }), - ) + )) } #[cfg(feature = "wayland")] - pub fn get_subsurface( + pub fn get_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> @@ -115,7 +158,10 @@ pub mod message { + Sync + 'static, >, - ) -> Message { + ) -> App::Message + where + App::Message: SurfaceMessageHandler + From, + { use std::{any::Any, sync::Arc}; let boxed: Box< dyn Fn( @@ -128,7 +174,7 @@ pub mod message { > = Box::new(settings); let boxed: Box = Box::new(boxed); - Message::Subsurface( + App::Message::from(SurfaceMessage::Subsurface( Arc::new(boxed), view.map(|view| { let boxed: Box< @@ -140,7 +186,7 @@ pub mod message { let boxed: Box = Box::new(boxed); Arc::new(boxed) }), - ) + )) } impl From for Message { @@ -148,29 +194,6 @@ pub mod message { Self::App(value) } } - - impl std::fmt::Debug for Message { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::App(arg0) => f.debug_tuple("App").field(arg0).finish(), - Self::Cosmic(arg0) => f.debug_tuple("Cosmic").field(arg0).finish(), - - #[cfg(feature = "single-instance")] - Self::DbusActivation(arg0) => f.debug_tuple("DbusActivation").field(arg0).finish(), - Self::None => write!(f, "None"), - #[cfg(feature = "wayland")] - Self::Subsurface(arg0, arg1) => f.debug_tuple("Subsurface").field(arg1).finish(), - #[cfg(feature = "wayland")] - Self::DestroySubsurface(arg0) => { - f.debug_tuple("DestroySubsurface").field(arg0).finish() - } - #[cfg(feature = "wayland")] - Self::Popup(arg0, arg1) => f.debug_tuple("Popup").field(arg1).finish(), - #[cfg(feature = "wayland")] - Self::DestroyPopup(arg0) => f.debug_tuple("DestroyPopup").field(arg0).finish(), - } - } - } } use std::borrow::Cow; @@ -186,6 +209,7 @@ 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 { @@ -262,12 +286,16 @@ pub(crate) fn iced_settings( /// # Errors /// /// Returns error on application failure. -pub fn run(settings: Settings, flags: App::Flags) -> iced::Result { +pub fn run(settings: Settings, flags: App::Flags) -> iced::Result +where + App::Message: SurfaceMessageHandler, +{ #[cfg(target_env = "gnu")] if let Some(threshold) = settings.default_mmap_threshold { crate::malloc::limit_mmap_threshold(threshold); } + let default_font = settings.default_font; let (settings, mut flags, window_settings) = iced_settings::(settings, flags); #[cfg(not(feature = "multi-window"))] { diff --git a/src/applet/mod.rs b/src/applet/mod.rs index 0ca8a4f1..6c308646 100644 --- a/src/applet/mod.rs +++ b/src/applet/mod.rs @@ -2,7 +2,7 @@ pub mod token; use crate::{ - app::{self, iced_settings, Core}, + app::{self, iced_settings, message::SurfaceMessageHandler, Core}, cctk::sctk, iced::{ self, @@ -376,7 +376,10 @@ impl Context { /// # Errors /// /// Returns error on application failure. -pub fn run(flags: App::Flags) -> iced::Result { +pub fn run(flags: App::Flags) -> iced::Result +where + App::Message: SurfaceMessageHandler, +{ let helper = Context::default(); let mut settings = helper.window_settings();