fix: explicit lifetimes for view closures

This commit is contained in:
Ashley Wulber 2025-02-21 14:29:12 -05:00
parent 343496841e
commit ab438b87f9
No known key found for this signature in database
GPG key ID: 5216D4F46A90A820
6 changed files with 127 additions and 93 deletions

View file

@ -105,7 +105,9 @@ libc = { version = "0.2.155", optional = true }
license = { version = "3.5.1", optional = true } license = { version = "3.5.1", optional = true }
mime = { version = "0.3.17", optional = true } mime = { version = "0.3.17", optional = true }
palette = "0.7.3" 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 = [ rustix = { version = "0.38.34", features = [
"pipe", "pipe",
"process", "process",

View file

@ -1,6 +1,7 @@
use cosmic::app::Core; use cosmic::app::Core;
use cosmic::iced::event::listen_with;
use cosmic::iced::window::Id; 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::core::window;
use cosmic::iced_runtime::platform_specific::wayland::popup::SctkPopupSettings; use cosmic::iced_runtime::platform_specific::wayland::popup::SctkPopupSettings;
use cosmic::iced_runtime::platform_specific::wayland::subsurface; use cosmic::iced_runtime::platform_specific::wayland::subsurface;
@ -114,7 +115,7 @@ impl cosmic::Application for Window {
self.example_row = toggled; self.example_row = toggled;
} }
Message::Surface(surface_message) => {} Message::Surface(_) => {}
Message::Selected(i) => { Message::Selected(i) => {
self.selected = Some(i); self.selected = Some(i);
return cosmic::task::message(cosmic::app::message::destroy_popup::<Window>( return cosmic::task::message(cosmic::app::message::destroy_popup::<Window>(
@ -123,15 +124,13 @@ impl cosmic::Application for Window {
} }
Message::OpenDropdown(sctk_popup_settings, view) => { Message::OpenDropdown(sctk_popup_settings, view) => {
self.dropdown_id = sctk_popup_settings.id; self.dropdown_id = sctk_popup_settings.id;
return cosmic::task::message(cosmic::app::message::app_popup::< return cosmic::task::message(cosmic::app::message::app_popup::<Window>(
Window,
>(
move |_: &mut Window| sctk_popup_settings.clone(), move |_: &mut Window| sctk_popup_settings.clone(),
Some( Some(Box::new(
move |_: &Window| -> cosmic::Element<'static, cosmic::app::Message<Message>> { move |_: &Window| -> cosmic::Element<'_, cosmic::app::Message<Message>> {
view().map(cosmic::app::Message::App) view().map(cosmic::app::Message::App)
}, },
), )),
)); ));
} }
Message::PopupCloseRequested(id) => { Message::PopupCloseRequested(id) => {
@ -169,13 +168,16 @@ impl cosmic::Application for Window {
popup_settings popup_settings
}, },
Some( Some(
Box::new(
move |state: &Window| -> cosmic::Element< move |state: &Window| -> cosmic::Element<
'static, '_,
cosmic::app::Message<Message>, cosmic::app::Message<Message>,
> { > {
{ {
let content_list = list_column().padding(5).spacing(0).add( let content_list = list_column()
settings::item( .padding(5)
.spacing(0)
.add(settings::item(
"Example row", "Example row",
cosmic::widget::container( cosmic::widget::container(
toggler(state.example_row).on_toggle(|value| { toggler(state.example_row).on_toggle(|value| {
@ -183,8 +185,18 @@ impl cosmic::Application for Window {
}), }),
) )
.height(Length::Fixed(50.)), .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) .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( Element::from(
state.core.applet.popup_container(content_list), state.core.applet.popup_container(content_list),
@ -193,33 +205,30 @@ impl cosmic::Application for Window {
} }
}, },
), ),
),
) )
}, },
), ),
|_| { |_| {
cosmic::app::message::subsurface( cosmic::app::message::subsurface(
|app: &mut Window| { |app: &mut Window| subsurface::SctkSubsurfaceSettings {
subsurface::SctkSubsurfaceSettings {
parent: window::Id::RESERVED, parent: window::Id::RESERVED,
id: app.subsurface_id, id: app.subsurface_id,
loc: iced_core::Point { x: -100., y: 0. }, loc: iced_core::Point { x: -100., y: 0. },
size: Some((10., 10.).into()), size: Some((10., 10.).into()),
z: 1, z: 1,
} steal_keyboard_focus: true,
}, },
Some(|_: &Window| { Some(Box::new(|_: &'_ Window| {
autosize::autosize( Element::from(autosize::autosize(
layer_container(cosmic::widget::text("hello")) layer_container(cosmic::widget::text("hello"))
.layer(cosmic::cosmic_theme::Layer::Background), .layer(cosmic::cosmic_theme::Layer::Background),
SUBSURFACE_ID.clone(), SUBSURFACE_ID.clone(),
) ))
.into() })),
}),
) )
}, },
cosmic::app::message::destroy_subsurface::<Window>( cosmic::app::message::destroy_subsurface::<Window>(self.subsurface_id),
self.subsurface_id
),
) )
.into() .into()
} }
@ -231,4 +240,13 @@ impl cosmic::Application for Window {
fn style(&self) -> Option<cosmic::iced_runtime::Appearance> { fn style(&self) -> Option<cosmic::iced_runtime::Appearance> {
Some(cosmic::applet::style()) Some(cosmic::applet::style())
} }
fn subscription(&self) -> cosmic::iced::Subscription<Self::Message> {
listen_with(|e, status, id| {
if matches!(e, iced::event::Event::Keyboard(_)) {
dbg!(e, id);
}
None
})
}
} }

View file

@ -7,6 +7,7 @@ use apply::Apply;
use cosmic::app::{Core, Settings, Task}; use cosmic::app::{Core, Settings, Task};
use cosmic::dialog::file_chooser::{self, FileFilter}; use cosmic::dialog::file_chooser::{self, FileFilter};
use cosmic::iced_core::Length; use cosmic::iced_core::Length;
use cosmic::surface_message::{SurfaceMessage, SurfaceMessageHandler};
use cosmic::widget::button; use cosmic::widget::button;
use cosmic::{executor, iced, ApplicationExt, Element}; use cosmic::{executor, iced, ApplicationExt, Element};
use std::sync::Arc; use std::sync::Arc;
@ -34,6 +35,22 @@ pub enum Message {
OpenError(Arc<file_chooser::Error>), OpenError(Arc<file_chooser::Error>),
OpenFile, OpenFile,
Selected(Url), Selected(Url),
Surface(SurfaceMessage),
}
impl SurfaceMessageHandler for Message {
fn to_surface_message(self) -> cosmic::surface_message::MessageWrapper<Self> {
match self {
Message::Surface(m) => cosmic::surface_message::MessageWrapper::Surface(m),
m => cosmic::surface_message::MessageWrapper::Message(m),
}
}
}
impl From<SurfaceMessage> for Message {
fn from(value: SurfaceMessage) -> Self {
Message::Surface(value)
}
} }
/// The [`App`] stores application-specific state. /// The [`App`] stores application-specific state.
@ -91,13 +108,11 @@ impl cosmic::Application for App {
Message::Cancelled => { Message::Cancelled => {
eprintln!("open file dialog cancelled"); eprintln!("open file dialog cancelled");
} }
Message::FileRead(url, contents) => { Message::FileRead(url, contents) => {
eprintln!("read file"); eprintln!("read file");
self.selected_file = Some(url); self.selected_file = Some(url);
self.file_contents = contents; self.file_contents = contents;
} }
Message::Selected(url) => { Message::Selected(url) => {
eprintln!("selected file"); eprintln!("selected file");
@ -142,8 +157,6 @@ impl cosmic::Application for App {
Message::FileRead(url, contents) Message::FileRead(url, contents)
}); });
} }
// Creates a new open dialog.
Message::OpenFile => { Message::OpenFile => {
return cosmic::task::future(async move { return cosmic::task::future(async move {
eprintln!("opening new dialog"); 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) => { Message::Error(why) => {
self.error_status = Some(why); self.error_status = Some(why);
} }
// Displays an error in the application's warning bar.
Message::OpenError(why) => { Message::OpenError(why) => {
if let Some(why) = Arc::into_inner(why) { if let Some(why) = Arc::into_inner(why) {
let mut source: &dyn std::error::Error = &why; let mut source: &dyn std::error::Error = &why;
@ -190,10 +199,10 @@ impl cosmic::Application for App {
self.error_status = Some(string); self.error_status = Some(string);
} }
} }
Message::CloseError => { Message::CloseError => {
self.error_status = None; self.error_status = None;
} }
Message::Surface(surface_message) => {}
} }
Task::none() Task::none()

View file

@ -86,8 +86,10 @@ pub enum Message {
pub struct Cosmic<App: Application> { pub struct Cosmic<App: Application> {
pub app: App, pub app: App,
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
pub surface_views: pub surface_views: HashMap<
HashMap<window::Id, Box<dyn Fn(&App) -> Element<'static, super::Message<App::Message>>>>, window::Id,
Box<dyn for<'a> Fn(&'a App) -> Element<'a, super::Message<App::Message>>>,
>,
} }
impl<T: Application> Cosmic<T> impl<T: Application> Cosmic<T>
@ -138,7 +140,10 @@ where
if let Some(view) = view.and_then(|view| { if let Some(view) = view.and_then(|view| {
match std::sync::Arc::try_unwrap(view).ok()?.downcast::<Box< match std::sync::Arc::try_unwrap(view).ok()?.downcast::<Box<
dyn Fn(&T) -> Element<'static, super::Message<T::Message>> dyn for<'a> Fn(
&'a T,
)
-> Element<'a, super::Message<T::Message>>
+ Send + Send
+ Sync, + Sync,
>>( >>(
@ -170,7 +175,10 @@ where
if let Some(view) = view.and_then(|view| { if let Some(view) = view.and_then(|view| {
match std::sync::Arc::try_unwrap(view).ok()?.downcast::<Box< match std::sync::Arc::try_unwrap(view).ok()?.downcast::<Box<
dyn Fn(&T) -> Element<'static, super::Message<T::Message>> dyn for<'a> Fn(
&'a T,
)
-> Element<'a, super::Message<T::Message>>
+ Send + Send
+ Sync, + Sync,
>>( >>(
@ -840,7 +848,9 @@ impl<App: Application> Cosmic<App> {
pub fn get_subsurface( pub fn get_subsurface(
&mut self, &mut self,
settings: iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings, settings: iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings,
view: Box<dyn Fn(&App) -> Element<'static, super::Message<App::Message>> + Send + Sync>, view: Box<
dyn for<'a> Fn(&'a App) -> Element<'a, super::Message<App::Message>> + Send + Sync,
>,
) -> Task<super::Message<App::Message>> { ) -> Task<super::Message<App::Message>> {
use iced_winit::commands::subsurface::get_subsurface; use iced_winit::commands::subsurface::get_subsurface;
@ -853,7 +863,9 @@ impl<App: Application> Cosmic<App> {
pub fn get_popup( pub fn get_popup(
&mut self, &mut self,
settings: iced_runtime::platform_specific::wayland::popup::SctkPopupSettings, settings: iced_runtime::platform_specific::wayland::popup::SctkPopupSettings,
view: Box<dyn Fn(&App) -> Element<'static, super::Message<App::Message>> + Send + Sync>, view: Box<
dyn for<'a> Fn(&'a App) -> Element<'a, super::Message<App::Message>> + Send + Sync,
>,
) -> Task<super::Message<App::Message>> { ) -> Task<super::Message<App::Message>> {
use iced_winit::commands::popup::get_popup; use iced_winit::commands::popup::get_popup;

View file

@ -81,11 +81,13 @@ pub mod message {
+ Sync + Sync
+ 'static, + 'static,
view: Option< view: Option<
impl Fn(&App) -> crate::Element<'static, super::Message<App::Message>> Box<
dyn for<'a> Fn(&'a App) -> crate::Element<'a, super::Message<App::Message>>
+ Send + Send
+ Sync + Sync
+ 'static, + 'static,
>, >,
>,
) -> App::Message ) -> App::Message
where where
App::Message: SurfaceMessageHandler + From<SurfaceMessage>, App::Message: SurfaceMessageHandler + From<SurfaceMessage>,
@ -104,13 +106,7 @@ pub mod message {
App::Message::from(SurfaceMessage::AppPopup( App::Message::from(SurfaceMessage::AppPopup(
Arc::new(boxed), Arc::new(boxed),
view.map(|view| { view.map(|view| {
let boxed: Box< let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
dyn Fn(&App) -> crate::Element<'static, super::Message<App::Message>>
+ Send
+ Sync
+ 'static,
> = Box::new(view);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
Arc::new(boxed) Arc::new(boxed)
}), }),
)) ))
@ -118,7 +114,7 @@ pub mod message {
/// Used to create a popup message from within a widget. /// Used to create a popup message from within a widget.
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
pub fn simple_popup<Message>( pub fn simple_popup<Message, V>(
settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
+ Send + Send
+ Sync + Sync
@ -129,6 +125,7 @@ pub mod message {
) -> Message ) -> Message
where where
Message: SurfaceMessageHandler + From<SurfaceMessage> + 'static, Message: SurfaceMessageHandler + From<SurfaceMessage> + 'static,
V:,
{ {
use std::{any::Any, sync::Arc}; use std::{any::Any, sync::Arc};
@ -173,19 +170,21 @@ pub mod message {
#[cfg(feature = "wayland")] #[cfg(feature = "wayland")]
pub fn subsurface<App: super::Application>( pub fn subsurface<App: super::Application>(
settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings + Send + Sync + 'static, 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< view: Option<
impl Fn(&App) -> crate::Element<'static, super::Message<App::Message>> Box<
dyn for<'a> Fn(&'a App) -> crate::Element<'a, super::Message<App::Message>>
+ Send + Send
+ Sync + Sync
+ 'static, + 'static,
>, >,
>,
) -> App::Message ) -> App::Message
where where
App::Message: SurfaceMessageHandler + From<SurfaceMessage>, App::Message: SurfaceMessageHandler + From<SurfaceMessage>,
{ {
use crate::surface_message::SurfaceMessage;
use std::{any::Any, sync::Arc}; use std::{any::Any, sync::Arc};
use crate::surface_message::{SurfaceMessage, SurfaceMessageHandler};
let boxed: Box< let boxed: Box<
dyn Fn( dyn Fn(
&mut App, &mut App,
@ -200,13 +199,7 @@ pub mod message {
App::Message::from(SurfaceMessage::Subsurface( App::Message::from(SurfaceMessage::Subsurface(
Arc::new(boxed), Arc::new(boxed),
view.map(|view| { view.map(|view| {
let boxed: Box< let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
dyn Fn(&App) -> crate::Element<'static, super::Message<App::Message>>
+ Send
+ Sync
+ 'static,
> = Box::new(view);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
Arc::new(boxed) Arc::new(boxed)
}), }),
)) ))

View file

@ -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::{Limits, Size};
use iced_core::event::{self, Event}; use iced_core::event::{self, Event};