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