From ab438b87f9c46e92b8025fbb86774a97d52408ee Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Fri, 21 Feb 2025 14:29:12 -0500 Subject: [PATCH] fix: explicit lifetimes for view closures --- Cargo.toml | 4 +- examples/applet/src/window.rs | 120 +++++++++++++++++------------ examples/open-dialog/src/main.rs | 27 ++++--- src/app/cosmic.rs | 24 ++++-- src/app/mod.rs | 43 +++++------ src/widget/responsive_container.rs | 2 +- 6 files changed, 127 insertions(+), 93 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8be32689..ee0d6bd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -105,7 +105,9 @@ libc = { version = "0.2.155", optional = true } license = { version = "3.5.1", optional = true } mime = { version = "0.3.17", optional = true } palette = "0.7.3" -rfd = { version = "0.14.0", default-features = false, features = ["xdg-portal"], optional = true } +rfd = { version = "0.14.0", default-features = false, features = [ + "xdg-portal", +], optional = true } rustix = { version = "0.38.34", features = [ "pipe", "process", diff --git a/examples/applet/src/window.rs b/examples/applet/src/window.rs index 91ed3b01..273ef626 100644 --- a/examples/applet/src/window.rs +++ b/examples/applet/src/window.rs @@ -1,6 +1,7 @@ use cosmic::app::Core; +use cosmic::iced::event::listen_with; use cosmic::iced::window::Id; -use cosmic::iced::{Length, Limits, Task}; +use cosmic::iced::{self, 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; @@ -114,7 +115,7 @@ impl cosmic::Application for Window { self.example_row = toggled; } - Message::Surface(surface_message) => {} + Message::Surface(_) => {} Message::Selected(i) => { self.selected = Some(i); return cosmic::task::message(cosmic::app::message::destroy_popup::( @@ -123,16 +124,14 @@ 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::app_popup::< - Window, - >( - move |_: &mut Window| sctk_popup_settings.clone(), - Some( - move |_: &Window| -> cosmic::Element<'static, cosmic::app::Message> { - view().map(cosmic::app::Message::App) - }, - ), - )); + return cosmic::task::message(cosmic::app::message::app_popup::( + move |_: &mut Window| sctk_popup_settings.clone(), + Some(Box::new( + move |_: &Window| -> cosmic::Element<'_, cosmic::app::Message> { + view().map(cosmic::app::Message::App) + }, + )), + )); } Message::PopupCloseRequested(id) => { return cosmic::task::message(cosmic::app::message::destroy_popup::(id)); @@ -169,57 +168,67 @@ impl cosmic::Application for Window { 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.)), - ), - ).add(dropdown(&["1", "asdf", "hello", "test"], state.selected, Message::Selected).with_popup(state.popup.unwrap_or(Id::NONE), Message::OpenDropdown).on_close_popup(Message::PopupCloseRequested) - ); - Element::from( - state.core.applet.popup_container(content_list), - ) - .map(cosmic::app::Message::App) - } - }, + Box::new( + move |state: &Window| -> cosmic::Element< + '_, + 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.)), + )) + .add( + dropdown( + &["1", "asdf", "hello", "test"], + state.selected, + Message::Selected, + ) + .with_popup( + state.popup.unwrap_or(Id::NONE), + Message::OpenDropdown, + ) + .on_close_popup(Message::PopupCloseRequested), + ); + Element::from( + state.core.applet.popup_container(content_list), + ) + .map(cosmic::app::Message::App) + } + }, + ), ), ) }, ), |_| { cosmic::app::message::subsurface( - |app: &mut Window| { - subsurface::SctkSubsurfaceSettings { - parent: window::Id::RESERVED, - id: app.subsurface_id, - loc: iced_core::Point { x: -100., y: 0. }, - size: Some((10., 10.).into()), - z: 1, - } + |app: &mut Window| subsurface::SctkSubsurfaceSettings { + parent: window::Id::RESERVED, + id: app.subsurface_id, + loc: iced_core::Point { x: -100., y: 0. }, + size: Some((10., 10.).into()), + z: 1, + steal_keyboard_focus: true, }, - Some(|_: &Window| { - autosize::autosize( + Some(Box::new(|_: &'_ Window| { + Element::from(autosize::autosize( layer_container(cosmic::widget::text("hello")) .layer(cosmic::cosmic_theme::Layer::Background), SUBSURFACE_ID.clone(), - ) - .into() - }), + )) + })), ) }, - cosmic::app::message::destroy_subsurface::( - self.subsurface_id - ), + cosmic::app::message::destroy_subsurface::(self.subsurface_id), ) .into() } @@ -231,4 +240,13 @@ impl cosmic::Application for Window { fn style(&self) -> Option { Some(cosmic::applet::style()) } + + fn subscription(&self) -> cosmic::iced::Subscription { + listen_with(|e, status, id| { + if matches!(e, iced::event::Event::Keyboard(_)) { + dbg!(e, id); + } + None + }) + } } diff --git a/examples/open-dialog/src/main.rs b/examples/open-dialog/src/main.rs index 5ae2df47..7aff13ce 100644 --- a/examples/open-dialog/src/main.rs +++ b/examples/open-dialog/src/main.rs @@ -7,6 +7,7 @@ use apply::Apply; use cosmic::app::{Core, Settings, Task}; use cosmic::dialog::file_chooser::{self, FileFilter}; use cosmic::iced_core::Length; +use cosmic::surface_message::{SurfaceMessage, SurfaceMessageHandler}; use cosmic::widget::button; use cosmic::{executor, iced, ApplicationExt, Element}; use std::sync::Arc; @@ -34,6 +35,22 @@ pub enum Message { OpenError(Arc), OpenFile, Selected(Url), + Surface(SurfaceMessage), +} + +impl SurfaceMessageHandler for Message { + fn to_surface_message(self) -> cosmic::surface_message::MessageWrapper { + match self { + Message::Surface(m) => cosmic::surface_message::MessageWrapper::Surface(m), + m => cosmic::surface_message::MessageWrapper::Message(m), + } + } +} + +impl From for Message { + fn from(value: SurfaceMessage) -> Self { + Message::Surface(value) + } } /// The [`App`] stores application-specific state. @@ -91,13 +108,11 @@ impl cosmic::Application for App { Message::Cancelled => { eprintln!("open file dialog cancelled"); } - Message::FileRead(url, contents) => { eprintln!("read file"); self.selected_file = Some(url); self.file_contents = contents; } - Message::Selected(url) => { eprintln!("selected file"); @@ -142,8 +157,6 @@ impl cosmic::Application for App { Message::FileRead(url, contents) }); } - - // Creates a new open dialog. Message::OpenFile => { return cosmic::task::future(async move { eprintln!("opening new dialog"); @@ -169,13 +182,9 @@ impl cosmic::Application for App { } }); } - - // Displays an error in the application's warning bar. Message::Error(why) => { self.error_status = Some(why); } - - // Displays an error in the application's warning bar. Message::OpenError(why) => { if let Some(why) = Arc::into_inner(why) { let mut source: &dyn std::error::Error = &why; @@ -190,10 +199,10 @@ impl cosmic::Application for App { self.error_status = Some(string); } } - Message::CloseError => { self.error_status = None; } + Message::Surface(surface_message) => {} } Task::none() diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index d65eb5b3..f2465934 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -86,8 +86,10 @@ pub enum Message { pub struct Cosmic { pub app: App, #[cfg(feature = "wayland")] - pub surface_views: - HashMap Element<'static, super::Message>>>, + pub surface_views: HashMap< + window::Id, + Box Fn(&'a App) -> Element<'a, super::Message>>, + >, } impl Cosmic @@ -138,7 +140,10 @@ where if let Some(view) = view.and_then(|view| { match std::sync::Arc::try_unwrap(view).ok()?.downcast:: Element<'static, super::Message> + dyn for<'a> Fn( + &'a T, + ) + -> Element<'a, super::Message> + Send + Sync, >>( @@ -170,7 +175,10 @@ where if let Some(view) = view.and_then(|view| { match std::sync::Arc::try_unwrap(view).ok()?.downcast:: Element<'static, super::Message> + dyn for<'a> Fn( + &'a T, + ) + -> Element<'a, super::Message> + Send + Sync, >>( @@ -840,7 +848,9 @@ impl Cosmic { pub fn get_subsurface( &mut self, settings: iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings, - view: Box Element<'static, super::Message> + Send + Sync>, + view: Box< + dyn for<'a> Fn(&'a App) -> Element<'a, super::Message> + Send + Sync, + >, ) -> Task> { use iced_winit::commands::subsurface::get_subsurface; @@ -853,7 +863,9 @@ impl Cosmic { pub fn get_popup( &mut self, settings: iced_runtime::platform_specific::wayland::popup::SctkPopupSettings, - view: Box Element<'static, super::Message> + Send + Sync>, + view: Box< + dyn for<'a> Fn(&'a App) -> Element<'a, super::Message> + Send + Sync, + >, ) -> Task> { use iced_winit::commands::popup::get_popup; diff --git a/src/app/mod.rs b/src/app/mod.rs index e7117af8..aa86eb3e 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -81,10 +81,12 @@ pub mod message { + Sync + 'static, view: Option< - impl Fn(&App) -> crate::Element<'static, super::Message> - + Send - + Sync - + 'static, + Box< + dyn for<'a> Fn(&'a App) -> crate::Element<'a, super::Message> + + Send + + Sync + + 'static, + >, >, ) -> App::Message where @@ -104,13 +106,7 @@ pub mod message { App::Message::from(SurfaceMessage::AppPopup( Arc::new(boxed), view.map(|view| { - let boxed: Box< - dyn Fn(&App) -> crate::Element<'static, super::Message> - + Send - + Sync - + 'static, - > = Box::new(view); - let boxed: Box = Box::new(boxed); + let boxed: Box = Box::new(view); Arc::new(boxed) }), )) @@ -118,7 +114,7 @@ pub mod message { /// Used to create a popup message from within a widget. #[cfg(feature = "wayland")] - pub fn simple_popup( + pub fn simple_popup( settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send + Sync @@ -129,6 +125,7 @@ pub mod message { ) -> Message where Message: SurfaceMessageHandler + From + 'static, + V:, { use std::{any::Any, sync::Arc}; @@ -173,19 +170,21 @@ pub mod message { #[cfg(feature = "wayland")] pub fn subsurface( settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings + Send + Sync + 'static, + // XXX Boxed trait object is required for less cumbersome type inference, but we box it anyways. view: Option< - impl Fn(&App) -> crate::Element<'static, super::Message> - + Send - + Sync - + 'static, + Box< + dyn for<'a> Fn(&'a App) -> crate::Element<'a, super::Message> + + Send + + Sync + + 'static, + >, >, ) -> App::Message where App::Message: SurfaceMessageHandler + From, { + use crate::surface_message::SurfaceMessage; use std::{any::Any, sync::Arc}; - - use crate::surface_message::{SurfaceMessage, SurfaceMessageHandler}; let boxed: Box< dyn Fn( &mut App, @@ -200,13 +199,7 @@ pub mod message { App::Message::from(SurfaceMessage::Subsurface( Arc::new(boxed), view.map(|view| { - let boxed: Box< - dyn Fn(&App) -> crate::Element<'static, super::Message> - + Send - + Sync - + 'static, - > = Box::new(view); - let boxed: Box = Box::new(boxed); + let boxed: Box = Box::new(view); Arc::new(boxed) }), )) diff --git a/src/widget/responsive_container.rs b/src/widget/responsive_container.rs index d1c318ec..27322f2c 100644 --- a/src/widget/responsive_container.rs +++ b/src/widget/responsive_container.rs @@ -1,4 +1,4 @@ -//! Autosize Container, which will resize the window to its contents. +//! Responsive Container, which will notify of size changes. use iced::{Limits, Size}; use iced_core::event::{self, Event};