// Copyright 2025 System76 // SPDX-License-Identifier: MPL-2.0 pub mod action; #[cfg(wayland_platform)] pub mod corner_radius; use iced::{Limits, Size, Task}; use std::any::Any; use std::sync::Arc; type BoxedSetting = Arc>; /// Produces the content of a surface created from within a widget. /// /// Typed on the message the widget publishes. pub type View = Arc crate::Element<'static, crate::Action> + Send + Sync + 'static>; /// Ignore this message in your application. It will be intercepted. /// /// `M` is the message type of whoever created the action. The ones prefixed with `App` take the /// application itself and are type-erased, the others carry a [`View`] typed on `M`. #[derive(Clone)] pub enum Action { /// Create a subsurface with a view function accepting the App as a parameter AppSubsurface(BoxedSetting, BoxedSetting, Option), /// Create a subsurface with a view function Subsurface(BoxedSetting, BoxedSetting, Option>), /// Destroy a subsurface with a view function DestroySubsurface(iced::window::Id), /// Create a popup with a view function accepting the App as a parameter AppPopup(BoxedSetting, BoxedSetting, Option), /// Create a popup Popup(BoxedSetting, BoxedSetting, Option>), /// Destroy a subsurface with a view function DestroyPopup(iced::window::Id), /// Destroys the global tooltip popup subsurface DestroyTooltipPopup, /// Create a window with a view function accepting the App as a parameter AppWindow( iced::window::Id, BoxedSetting, BoxedSetting, Option, ), /// Create a window with a view function Window( iced::window::Id, BoxedSetting, BoxedSetting, Option>, ), /// Destroy a window DestroyWindow(iced::window::Id), /// Create a layer shell surface with a view function accepting the App as a parameter AppLayerShell(BoxedSetting, BoxedSetting, Option), /// Create a layer shell surface with a view function LayerShell(BoxedSetting, BoxedSetting, Option>), /// Destroy a layer shell surface DestroyLayerShell(iced::window::Id), #[cfg(wayland_platform)] /// Create a lock shell surface with a view function Lock( iced::window::Id, cctk::wayland_client::protocol::wl_output::WlOutput, BoxedSetting, Option>, ), /// Destroy a lock surface DestroyLock(iced::window::Id), /// Responsive menu bar update ResponsiveMenuBar { /// Id of the menu bar menu_bar: crate::widget::Id, /// Limits of the menu bar limits: Limits, /// Requested Full Size for expanded menu bar size: Size, }, Ignore, SyncLiveSettings(iced::window::Id), Task(Arc Task> + Send + Sync>), } impl Action { /// Re-type the action for a component whose messages are wrapped by `f`. /// /// Similar to [`iced::Element::map`]. A component that maps a widget's messages must /// map the widget's surface actions too. #[must_use] pub fn map(self, f: impl Fn(M) -> N + Clone + Send + Sync + 'static) -> Action { self.map_actions(move |action| action.map(f.clone())) } fn map_actions( self, g: impl Fn(crate::Action) -> crate::Action + Clone + Send + Sync + 'static, ) -> Action { let map_view = |view: Option>| -> Option> { let view = view?; let g = g.clone(); Some(Arc::new(move || view().map(g.clone()))) }; match self { Action::AppSubsurface(a, b, c) => Action::AppSubsurface(a, b, c), Action::Subsurface(a, b, view) => Action::Subsurface(a, b, map_view(view)), Action::DestroySubsurface(id) => Action::DestroySubsurface(id), Action::AppPopup(a, b, c) => Action::AppPopup(a, b, c), Action::Popup(a, b, view) => Action::Popup(a, b, map_view(view)), Action::DestroyPopup(id) => Action::DestroyPopup(id), Action::DestroyTooltipPopup => Action::DestroyTooltipPopup, Action::AppWindow(id, a, b, c) => Action::AppWindow(id, a, b, c), Action::Window(id, a, b, view) => Action::Window(id, a, b, map_view(view)), Action::DestroyWindow(id) => Action::DestroyWindow(id), Action::AppLayerShell(a, b, c) => Action::AppLayerShell(a, b, c), Action::LayerShell(a, b, view) => Action::LayerShell(a, b, map_view(view)), Action::DestroyLayerShell(id) => Action::DestroyLayerShell(id), #[cfg(wayland_platform)] Action::Lock(id, output, a, view) => Action::Lock(id, output, a, map_view(view)), Action::DestroyLock(id) => Action::DestroyLock(id), Action::ResponsiveMenuBar { menu_bar, limits, size, } => Action::ResponsiveMenuBar { menu_bar, limits, size, }, Action::Ignore => Action::Ignore, Action::SyncLiveSettings(id) => Action::SyncLiveSettings(id), Action::Task(task) => Action::Task(Arc::new(move || { let g = g.clone(); task().map(move |action| action.map_actions(g.clone())) })), } } } impl Action> { #[must_use] pub fn flatten(self) -> Action { self.map_actions(crate::Action::flatten) } } #[cfg(feature = "winit")] pub fn surface_task(action: Action) -> Task> { crate::task::message(crate::Action::Surface(action)) } impl std::fmt::Debug for Action { #[cold] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::AppSubsurface(arg0, arg1, arg2) => f .debug_tuple("AppSubsurface") .field(arg0) .field(arg1) .field(arg2) .finish(), Self::Subsurface(arg0, arg1, view) => f .debug_tuple("Subsurface") .field(arg0) .field(arg1) .field(&view.as_ref().map(|_| "view")) .finish(), Self::DestroySubsurface(arg0) => { f.debug_tuple("DestroySubsurface").field(arg0).finish() } Self::AppPopup(arg0, arg1, arg2) => f .debug_tuple("AppPopup") .field(arg0) .field(arg1) .field(arg2) .finish(), Self::Popup(arg0, arg1, view) => f .debug_tuple("Popup") .field(arg0) .field(arg1) .field(&view.as_ref().map(|_| "view")) .finish(), Self::DestroyPopup(arg0) => f.debug_tuple("DestroyPopup").field(arg0).finish(), Self::DestroyTooltipPopup => f.debug_tuple("DestroyTooltipPopup").finish(), Self::ResponsiveMenuBar { menu_bar, limits, size, } => f .debug_struct("ResponsiveMenuBar") .field("menu_bar", menu_bar) .field("limits", limits) .field("size", size) .finish(), Self::Ignore => write!(f, "Ignore"), Self::AppWindow(id, arg0, arg1, arg2) => f .debug_tuple("AppWindow") .field(id) .field(arg0) .field(arg1) .field(arg2) .finish(), Self::Window(id, arg0, arg1, view) => f .debug_tuple("Window") .field(id) .field(arg0) .field(arg1) .field(&view.as_ref().map(|_| "view")) .finish(), Self::DestroyWindow(arg0) => f.debug_tuple("DestroyWindow").field(arg0).finish(), Self::Task(_) => f.debug_tuple("Future").finish(), Self::AppLayerShell(arg, arg1, arg2) => f .debug_tuple("AppLayerShell") .field(arg) .field(arg1) .field(arg2) .finish(), Self::LayerShell(arg0, arg1, view) => f .debug_tuple("LayerShell") .field(arg0) .field(arg1) .field(&view.as_ref().map(|_| "view")) .finish(), Self::DestroyLayerShell(arg0) => { f.debug_tuple("DestroyLayerShell").field(arg0).finish() } #[cfg(wayland_platform)] Self::Lock(id, output, arg0, view) => f .debug_tuple("Lock") .field(id) .field(output) .field(arg0) .field(&view.as_ref().map(|_| "view")) .finish(), Self::DestroyLock(arg0) => f.debug_tuple("DestroyLock").field(arg0).finish(), Self::SyncLiveSettings(arg0) => f.debug_tuple("SyncLiveSettings").field(arg0).finish(), } } }