Skip to main content

cosmic/surface/
action.rs

1// Copyright 2025 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4use super::{Action, View};
5#[cfg(feature = "winit")]
6use crate::Application;
7
8use iced::window;
9#[cfg(all(wayland_platform, feature = "winit"))]
10use iced_runtime::platform_specific::wayland::CornerRadius;
11#[cfg(wayland_platform)]
12use iced_runtime::platform_specific::wayland::layer_surface::IcedMargin;
13use std::any::Any;
14
15use std::sync::Arc;
16
17/// Used to produce a destroy popup message from within a widget.
18#[cfg(wayland_platform)]
19#[must_use]
20pub fn destroy_popup<M>(id: iced_core::window::Id) -> Action<M> {
21    Action::DestroyPopup(id)
22}
23
24#[cfg(wayland_platform)]
25#[must_use]
26pub fn destroy_subsurface<M>(id: iced_core::window::Id) -> Action<M> {
27    Action::DestroySubsurface(id)
28}
29
30#[cfg(wayland_platform)]
31#[must_use]
32pub fn destroy_window<M>(id: iced_core::window::Id) -> Action<M> {
33    Action::DestroyWindow(id)
34}
35
36#[cfg(wayland_platform)]
37#[must_use]
38pub fn destroy_layer_shell<M>(id: iced_core::window::Id) -> Action<M> {
39    Action::DestroyLayerShell(id)
40}
41
42#[derive(Debug, Default, Copy, Clone)]
43pub struct LiveSettings {
44    #[cfg(wayland_platform)]
45    /// Override the surface padding value for the surface type.
46    pub padding: Option<IcedMargin>,
47    #[cfg(wayland_platform)]
48    /// Override the default corner radius value for the surface type.
49    pub corners: Option<CornerRadius>,
50    /// Override the default blur setting for the surface type.
51    pub blur: Option<bool>,
52}
53
54#[cfg(wayland_platform)]
55type BoxedView<App> = Option<
56    Box<
57        dyn Fn(&App) -> crate::Element<'_, crate::Action<<App as Application>::Message>>
58            + Send
59            + Sync
60            + 'static,
61    >,
62>;
63
64#[cfg(wayland_platform)]
65#[must_use]
66pub fn app_window<App: Application>(
67    live_settings: impl Fn(&App) -> LiveSettings + Send + Sync + 'static,
68    settings: impl Fn(&mut App) -> window::Settings + Send + Sync + 'static,
69    view: BoxedView<App>,
70) -> (window::Id, Action<App::Message>) {
71    let id = window::Id::unique();
72
73    let boxed: Box<dyn Fn(&mut App) -> window::Settings + Send + Sync + 'static> =
74        Box::new(settings);
75    let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
76
77    let boxed_live: Box<dyn Fn(&App) -> LiveSettings + Send + Sync + 'static> =
78        Box::new(live_settings);
79    let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
80
81    (
82        id,
83        Action::AppWindow(
84            id,
85            Arc::new(boxed),
86            Arc::new(boxed_live),
87            view.map(|view| {
88                let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
89                Arc::new(boxed)
90            }),
91        ),
92    )
93}
94
95/// Used to create a window message from within a widget.
96#[cfg(wayland_platform)]
97#[must_use]
98pub fn simple_window<Message: 'static>(
99    live_settings: impl Fn() -> LiveSettings + Send + Sync + 'static,
100    settings: impl Fn() -> window::Settings + Send + Sync + 'static,
101    view: Option<
102        impl Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static,
103    >,
104) -> (window::Id, Action<Message>) {
105    let id = window::Id::unique();
106
107    let boxed: Box<dyn Fn() -> window::Settings + Send + Sync + 'static> = Box::new(settings);
108    let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
109
110    let boxed_live: Box<dyn Fn() -> LiveSettings + Send + Sync + 'static> = Box::new(live_settings);
111    let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
112
113    (
114        id,
115        Action::Window(
116            id,
117            Arc::new(boxed),
118            Arc::new(boxed_live),
119            view.map(|view| Arc::new(view) as View<Message>),
120        ),
121    )
122}
123
124#[cfg(wayland_platform)]
125#[must_use]
126pub fn app_popup<App: Application>(
127    live_settings: impl Fn(&App) -> LiveSettings + Send + Sync + 'static,
128    settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
129    + Send
130    + Sync
131    + 'static,
132    view: BoxedView<App>,
133) -> Action<App::Message> {
134    let boxed: Box<
135        dyn Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
136            + Send
137            + Sync
138            + 'static,
139    > = Box::new(settings);
140    let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
141
142    let boxed_live: Box<dyn Fn(&App) -> LiveSettings + Send + Sync + 'static> =
143        Box::new(live_settings);
144    let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
145
146    Action::AppPopup(
147        Arc::new(boxed),
148        Arc::new(boxed_live),
149        view.map(|view| {
150            let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
151            Arc::new(boxed)
152        }),
153    )
154}
155
156/// Used to create a subsurface message from within a widget.
157#[cfg(wayland_platform)]
158#[must_use]
159pub fn simple_subsurface<Message: 'static>(
160    settings: impl Fn() -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings
161    + Send
162    + Sync
163    + 'static,
164    view: Option<
165        Box<dyn Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static>,
166    >,
167) -> Action<Message> {
168    let boxed: Box<
169        dyn Fn() -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings
170            + Send
171            + Sync
172            + 'static,
173    > = Box::new(settings);
174    let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
175
176    Action::Subsurface(
177        Arc::new(boxed),
178        Arc::new(Box::new(LiveSettings::default)),
179        view.map(|view| Arc::from(view) as View<Message>),
180    )
181}
182
183/// Used to create a popup message from within a widget.
184#[cfg(wayland_platform)]
185#[must_use]
186pub fn simple_popup<Message: 'static>(
187    live_settings: impl Fn() -> LiveSettings + Send + Sync + 'static,
188    settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
189    + Send
190    + Sync
191    + 'static,
192    view: Option<
193        impl Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static,
194    >,
195) -> Action<Message> {
196    let boxed: Box<
197        dyn Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
198            + Send
199            + Sync
200            + 'static,
201    > = Box::new(settings);
202    let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
203
204    let boxed_live: Box<dyn Fn() -> LiveSettings + Send + Sync + 'static> = Box::new(live_settings);
205    let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
206
207    Action::Popup(
208        Arc::new(boxed),
209        Arc::new(boxed_live),
210        view.map(|view| Arc::new(view) as View<Message>),
211    )
212}
213
214#[cfg(wayland_platform)]
215#[must_use]
216pub fn subsurface<App: Application>(
217    settings: impl Fn(
218        &mut App,
219    )
220        -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings
221    + Send
222    + Sync
223    + 'static,
224    // XXX Boxed trait object is required for less cumbersome type inference, but we box it anyways.
225    view: BoxedView<App>,
226) -> Action<App::Message> {
227    let boxed: Box<
228        dyn Fn(
229                &mut App,
230            )
231                -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings
232            + Send
233            + Sync
234            + 'static,
235    > = Box::new(settings);
236    let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
237
238    Action::AppSubsurface(
239        Arc::new(boxed),
240        Arc::new(Box::new(|_: &App| LiveSettings::default())),
241        view.map(|view| {
242            let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
243            Arc::new(boxed)
244        }),
245    )
246}
247
248#[cfg(wayland_platform)]
249#[must_use]
250pub fn simple_layer_shell<Message: 'static>(
251    live_settings: impl Fn() -> LiveSettings + Send + Sync + 'static,
252    settings: impl Fn()
253        -> iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings
254    + Send
255    + Sync
256    + 'static,
257    view: Option<
258        impl Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static,
259    >,
260) -> Action<Message> {
261    let boxed: Box<
262        dyn Fn()
263                -> iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings
264            + Send
265            + Sync
266            + 'static,
267    > = Box::new(settings);
268    let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
269    let boxed_live: Box<dyn Fn() -> LiveSettings + Send + Sync + 'static> = Box::new(live_settings);
270    let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
271    Action::LayerShell(
272        Arc::new(boxed),
273        Arc::new(boxed_live),
274        view.map(|view| Arc::new(view) as View<Message>),
275    )
276}
277
278#[cfg(wayland_platform)]
279#[must_use]
280pub fn lock<Message: 'static>(
281    live_settings: impl Fn() -> LiveSettings + Send + Sync + 'static,
282    id: window::Id,
283    output: cctk::wayland_client::protocol::wl_output::WlOutput,
284    view: Option<
285        impl Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static,
286    >,
287) -> Action<Message> {
288    let boxed_live: Box<dyn Fn() -> LiveSettings + Send + Sync + 'static> = Box::new(live_settings);
289    let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
290    Action::Lock(
291        id,
292        output,
293        Arc::new(boxed_live),
294        view.map(|view| Arc::new(view) as View<Message>),
295    )
296}
297
298#[cfg(wayland_platform)]
299#[must_use]
300pub fn app_layer_shell<App: Application>(
301    live_settings: impl Fn(&App) -> LiveSettings + Send + Sync + 'static,
302    settings: impl Fn(
303        &mut App,
304    )
305        -> iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings
306    + Send
307    + Sync
308    + 'static,
309    // XXX Boxed trait object is required for less cumbersome type inference, but we box it anyways.
310    view: BoxedView<App>,
311) -> Action<App::Message> {
312    let boxed: Box<
313        dyn Fn(
314                &mut App,
315            )
316                -> iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings
317            + Send
318            + Sync
319            + 'static,
320    > = Box::new(settings);
321    let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
322
323    let boxed_live: Box<dyn Fn(&App) -> LiveSettings + Send + Sync + 'static> =
324        Box::new(live_settings);
325    let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
326
327    Action::AppLayerShell(
328        Arc::new(boxed),
329        Arc::new(boxed_live),
330        view.map(|view| {
331            let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
332            Arc::new(boxed)
333        }),
334    )
335}