improv: allow overrides for corner radius or blur that apply on creation as well

This commit is contained in:
Ashley Wulber 2026-05-15 14:27:17 -04:00 committed by Ashley Wulber
parent f268bb86cf
commit ac4a1ab259
9 changed files with 415 additions and 285 deletions

View file

@ -101,6 +101,7 @@ impl cosmic::Application for Window {
Message::Surface(destroy_popup(id)) Message::Surface(destroy_popup(id))
} else { } else {
Message::Surface(app_popup::<Window>( Message::Surface(app_popup::<Window>(
|_| Default::default(),
move |state: &mut Window| { move |state: &mut Window| {
let new_id = Id::unique(); let new_id = Id::unique();
state.popup = Some(new_id); state.popup = Some(new_id);

View file

@ -8,6 +8,8 @@ use std::sync::Arc;
use super::{Action, Application, ApplicationExt, Subscription}; use super::{Action, Application, ApplicationExt, Subscription};
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
use crate::core::Auto; use crate::core::Auto;
#[cfg(all(feature = "wayland", target_os = "linux"))]
use crate::surface::action::LiveSettings;
use crate::theme::{THEME, Theme, ThemeType}; use crate::theme::{THEME, Theme, ThemeType};
use crate::{Core, Element, keyboard_nav}; use crate::{Core, Element, keyboard_nav};
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
@ -21,7 +23,7 @@ use iced::Application as IcedApplication;
use iced::event::wayland; use iced::event::wayland;
use iced::{Task, theme, window}; use iced::{Task, theme, window};
use iced_futures::event::listen_with; use iced_futures::event::listen_with;
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(feature = "winit")]
use iced_winit::SurfaceIdWrapper; use iced_winit::SurfaceIdWrapper;
use palette::color_difference::EuclideanDistance; use palette::color_difference::EuclideanDistance;
@ -87,16 +89,22 @@ fn init_windowing_system<M>(handle: window::raw_window_handle::WindowHandle) ->
#[derive(Default)] #[derive(Default)]
pub struct Cosmic<App: Application> { pub struct Cosmic<App: Application> {
pub app: App, pub app: App,
#[cfg(all(feature = "wayland", target_os = "linux"))]
pub surface_views: HashMap< pub surface_views: HashMap<
window::Id, window::Id,
( (
Option<window::Id>, Option<window::Id>,
SurfaceIdWrapper, SurfaceIdWrapper,
Box<dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>>>, Box<dyn for<'a> Fn(&'a App) -> crate::surface::action::LiveSettings>,
Option<
Box<
dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>>
+ Send
+ Sync
+ 'static,
>,
>,
), ),
>, >,
pub tracked_surfaces: HashMap<window::Id, iced_winit::SurfaceIdWrapper>,
pub opened_surfaces: HashMap<window::Id, u32>, pub opened_surfaces: HashMap<window::Id, u32>,
blur_enabled: bool, blur_enabled: bool,
} }
@ -144,7 +152,7 @@ where
#[cfg(feature = "surface-message")] #[cfg(feature = "surface-message")]
match _surface_message { match _surface_message {
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
crate::surface::Action::AppSubsurface(settings, view) => { crate::surface::Action::AppSubsurface(settings, live_settings, view) => {
let Some(settings) = std::sync::Arc::try_unwrap(settings) let Some(settings) = std::sync::Arc::try_unwrap(settings)
.ok() .ok()
.and_then(|s| s.downcast::<Box<dyn Fn(&mut T) -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings + Send + Sync>>().ok()) else { .and_then(|s| s.downcast::<Box<dyn Fn(&mut T) -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings + Send + Sync>>().ok()) else {
@ -152,7 +160,9 @@ where
return Task::none(); return Task::none();
}; };
if let Some(view) = view.and_then(|view| { let settings = settings(&mut self.app);
let 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 for<'a> Fn(&'a T) -> Element<'a, crate::Action<T::Message>> dyn for<'a> Fn(&'a T) -> Element<'a, crate::Action<T::Message>>
+ Send + Send
@ -165,25 +175,18 @@ where
None None
} }
} }
}) { });
let settings = settings(&mut self.app); self.get_subsurface(settings, view.map(|v| *v))
self.get_subsurface(settings, *view)
} else {
let settings = settings(&mut self.app);
self.tracked_surfaces
.insert(settings.id, SurfaceIdWrapper::Subsurface(settings.id));
iced_winit::commands::subsurface::get_subsurface(settings)
}
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
crate::surface::Action::Subsurface(settings, view) => { crate::surface::Action::Subsurface(settings, live_settings, view) => {
let Some(settings) = std::sync::Arc::try_unwrap(settings) let Some(settings) = std::sync::Arc::try_unwrap(settings)
.ok() .ok()
.and_then(|s| s.downcast::<Box<dyn Fn() -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings + Send + Sync>>().ok()) else { .and_then(|s| s.downcast::<Box<dyn Fn() -> iced_runtime::platform_specific::wayland::subsurface::SctkSubsurfaceSettings + Send + Sync>>().ok()) else {
tracing::error!("Invalid settings for subsurface"); tracing::error!("Invalid settings for subsurface");
return Task::none(); return Task::none();
}; };
let settings = settings();
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<
@ -197,26 +200,32 @@ where
} }
} }
}) { }) {
let settings = settings(); self.get_subsurface(settings, Some(Box::new(move |_| view())))
self.get_subsurface(settings, Box::new(move |_| view()))
} else { } else {
let settings = settings(); self.get_subsurface(settings, None)
self.tracked_surfaces
.insert(settings.id, SurfaceIdWrapper::Subsurface(settings.id));
iced_winit::commands::subsurface::get_subsurface(settings)
} }
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
crate::surface::Action::AppPopup(settings, view) => { crate::surface::Action::AppPopup(settings, live_settings, view) => {
let Some(settings) = std::sync::Arc::try_unwrap(settings) let Some(settings) = std::sync::Arc::try_unwrap(settings)
.ok() .ok()
.and_then(|s| s.downcast::<Box<dyn Fn(&mut T) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send + Sync>>().ok()) else { .and_then(|s| s.downcast::<Box<dyn Fn(&mut T) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send + Sync>>().ok()) else {
tracing::error!("Invalid settings for popup"); tracing::error!("Invalid settings for popup");
return Task::none(); return Task::none();
}; };
let Some(live_settings) =
std::sync::Arc::try_unwrap(live_settings)
.ok()
.and_then(|s| {
s.downcast::<Box<dyn Fn(&T) -> LiveSettings + Send + Sync>>()
.ok()
})
else {
tracing::error!("Invalid live settings for popup");
return Task::none();
};
if let Some(view) = view.and_then(|view| { let 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 for<'a> Fn(&'a T) -> Element<'a, crate::Action<T::Message>> dyn for<'a> Fn(&'a T) -> Element<'a, crate::Action<T::Message>>
+ Send + Send
@ -228,16 +237,10 @@ where
None None
} }
} }
}) { });
let settings = settings(&mut self.app); let settings = settings(&mut self.app);
self.get_popup(settings, *view) self.get_popup(settings, *live_settings, view.map(|v| *v))
} else {
let settings = settings(&mut self.app);
self.tracked_surfaces
.insert(settings.id, SurfaceIdWrapper::Popup(settings.id));
iced_winit::commands::popup::get_popup(settings)
}
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
crate::surface::Action::DestroyPopup(id) => { crate::surface::Action::DestroyPopup(id) => {
@ -270,7 +273,7 @@ where
iced::Task::none() iced::Task::none()
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
crate::surface::Action::Popup(settings, view) => { crate::surface::Action::Popup(settings, live_settings, view) => {
let Some(settings) = std::sync::Arc::try_unwrap(settings) let Some(settings) = std::sync::Arc::try_unwrap(settings)
.ok() .ok()
.and_then(|s| s.downcast::<Box<dyn Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send + Sync>>().ok()) else { .and_then(|s| s.downcast::<Box<dyn Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings + Send + Sync>>().ok()) else {
@ -278,6 +281,20 @@ where
return Task::none(); return Task::none();
}; };
let Some(live_settings) =
std::sync::Arc::try_unwrap(live_settings)
.ok()
.and_then(|s| {
s.downcast::<Box<dyn Fn() -> LiveSettings + Send + Sync>>()
.ok()
})
else {
tracing::error!("Invalid live settings for popup");
return Task::none();
};
let settings = settings();
let live_settings = Box::new(move |app: &T| live_settings());
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() -> Element<'static, crate::Action<T::Message>> + Send + Sync, dyn Fn() -> Element<'static, crate::Action<T::Message>> + Send + Sync,
@ -289,18 +306,13 @@ where
} }
} }
}) { }) {
let settings = settings(); self.get_popup(settings, live_settings, Some(Box::new(move |_| view())))
self.get_popup(settings, Box::new(move |_| view()))
} else { } else {
let settings = settings(); self.get_popup(settings, live_settings, None)
self.tracked_surfaces
.insert(settings.id, SurfaceIdWrapper::Popup(settings.id));
iced_winit::commands::popup::get_popup(settings)
} }
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
crate::surface::Action::AppWindow(id, settings, view) => { crate::surface::Action::AppWindow(id, settings, live_settings, view) => {
let Some(settings) = std::sync::Arc::try_unwrap(settings).ok().and_then(|s| { let Some(settings) = std::sync::Arc::try_unwrap(settings).ok().and_then(|s| {
s.downcast::<Box<dyn Fn(&mut T) -> iced::window::Settings + Send + Sync>>() s.downcast::<Box<dyn Fn(&mut T) -> iced::window::Settings + Send + Sync>>()
.ok() .ok()
@ -308,8 +320,19 @@ where
tracing::error!("Invalid settings for AppWindow"); tracing::error!("Invalid settings for AppWindow");
return Task::none(); return Task::none();
}; };
let Some(live_settings) =
std::sync::Arc::try_unwrap(live_settings)
.ok()
.and_then(|s| {
s.downcast::<Box<dyn Fn(&T) -> LiveSettings + Send + Sync>>()
.ok()
})
else {
tracing::error!("Invalid live settings for popup");
return Task::none();
};
if let Some(view) = view.and_then(|view| { let 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 for<'a> Fn(&'a T) -> Element<'a, crate::Action<T::Message>> dyn for<'a> Fn(&'a T) -> Element<'a, crate::Action<T::Message>>
+ Send + Send
@ -321,24 +344,13 @@ where
None None
} }
} }
}) { });
let settings = settings(&mut self.app); let settings = settings(&mut self.app);
self.get_window(id, settings, *view) self.get_window(id, settings, *live_settings, view.map(|v| *v))
} else {
let settings = settings(&mut self.app);
self.tracked_surfaces
.insert(id, SurfaceIdWrapper::Window(id));
iced_runtime::task::oneshot(|channel| {
iced_runtime::Action::Window(iced_runtime::window::Action::Open(
id, settings, channel,
))
})
.discard()
}
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
crate::surface::Action::Window(id, settings, view) => { crate::surface::Action::Window(id, settings, live_settings, view) => {
let Some(settings) = std::sync::Arc::try_unwrap(settings).ok().and_then(|s| { let Some(settings) = std::sync::Arc::try_unwrap(settings).ok().and_then(|s| {
s.downcast::<Box<dyn Fn() -> iced::window::Settings + Send + Sync>>() s.downcast::<Box<dyn Fn() -> iced::window::Settings + Send + Sync>>()
.ok() .ok()
@ -347,6 +359,18 @@ where
return Task::none(); return Task::none();
}; };
let Some(live_settings) =
std::sync::Arc::try_unwrap(live_settings)
.ok()
.and_then(|s| {
s.downcast::<Box<dyn Fn() -> LiveSettings + Send + Sync>>()
.ok()
})
else {
tracing::error!("Invalid live settings for popup");
return Task::none();
};
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() -> Element<'static, crate::Action<T::Message>> + Send + Sync, dyn Fn() -> Element<'static, crate::Action<T::Message>> + Send + Sync,
@ -360,11 +384,14 @@ where
}) { }) {
let settings = settings(); let settings = settings();
self.get_window(id, settings, Box::new(move |_| view())) self.get_window(
id,
settings,
Box::new(move |_| live_settings()),
Some(Box::new(move |_| view())),
)
} else { } else {
let settings = settings(); let settings = settings();
self.tracked_surfaces
.insert(id, SurfaceIdWrapper::Window(id));
iced_runtime::task::oneshot(|channel| { iced_runtime::task::oneshot(|channel| {
iced_runtime::Action::Window(iced_runtime::window::Action::Open( iced_runtime::Action::Window(iced_runtime::window::Action::Open(
@ -380,15 +407,26 @@ where
f().map(|sm| crate::Action::Cosmic(Action::Surface(sm))) f().map(|sm| crate::Action::Cosmic(Action::Surface(sm)))
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
crate::surface::Action::AppLayerShell(settings, view) => { crate::surface::Action::AppLayerShell(settings, live_settings, view) => {
let Some(settings) = std::sync::Arc::try_unwrap(settings) let Some(settings) = std::sync::Arc::try_unwrap(settings)
.ok() .ok()
.and_then(|s| s.downcast::<Box<dyn Fn(&mut T) -> iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings + Send + Sync>>().ok()) else { .and_then(|s| s.downcast::<Box<dyn Fn(&mut T) -> iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings + Send + Sync>>().ok()) else {
tracing::error!("Invalid settings for layer surface"); tracing::error!("Invalid settings for layer surface");
return Task::none(); return Task::none();
}; };
let Some(live_settings) =
std::sync::Arc::try_unwrap(live_settings)
.ok()
.and_then(|s| {
s.downcast::<Box<dyn Fn(&T) -> LiveSettings + Send + Sync>>()
.ok()
})
else {
tracing::error!("Invalid live settings for popup");
return Task::none();
};
if let Some(view) = view.and_then(|view| { let 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 for<'a> Fn(&'a T) -> Element<'a, crate::Action<T::Message>> dyn for<'a> Fn(&'a T) -> Element<'a, crate::Action<T::Message>>
+ Send + Send
@ -400,19 +438,14 @@ where
None None
} }
} }
}) { });
let settings = settings(&mut self.app); let settings = settings(&mut self.app);
self.get_layer_shell(settings, *view) self.get_layer_shell(settings, *live_settings, view.map(|v| *v))
} else {
let settings = settings(&mut self.app);
self.tracked_surfaces
.insert(settings.id, SurfaceIdWrapper::LayerSurface(settings.id));
iced_winit::commands::layer_surface::get_layer_surface(settings)
}
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
crate::surface::Action::LayerShell(settings, view) => { crate::surface::Action::LayerShell(settings, live_settings, view) => {
let Some(settings) = std::sync::Arc::try_unwrap(settings) let Some(settings) = std::sync::Arc::try_unwrap(settings)
.ok() .ok()
.and_then(|s| s.downcast::<Box<dyn Fn() -> iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings + Send + Sync>>().ok()) else { .and_then(|s| s.downcast::<Box<dyn Fn() -> iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings + Send + Sync>>().ok()) else {
@ -420,6 +453,21 @@ where
return Task::none(); return Task::none();
}; };
let Some(live_settings) =
std::sync::Arc::try_unwrap(live_settings)
.ok()
.and_then(|s| {
s.downcast::<Box<dyn Fn() -> LiveSettings + Send + Sync>>()
.ok()
})
else {
tracing::error!("Invalid live settings for popup");
return Task::none();
};
let settings = settings();
let live_settings = live_settings();
let live_settings = Box::new(move |_app: &T| live_settings);
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() -> Element<'static, crate::Action<T::Message>> + Send + Sync, dyn Fn() -> Element<'static, crate::Action<T::Message>> + Send + Sync,
@ -431,16 +479,9 @@ where
} }
} }
}) { }) {
let settings = settings(); self.get_layer_shell(settings, live_settings, Some(Box::new(move |_| view())))
self.get_layer_shell(settings, Box::new(move |_| view()))
} else { } else {
let settings = settings(); self.get_layer_shell(settings, live_settings, None)
self.tracked_surfaces.insert(
settings.id,
iced_winit::SurfaceIdWrapper::LayerSurface(settings.id),
);
iced_winit::commands::layer_surface::get_layer_surface(settings)
} }
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
@ -657,8 +698,7 @@ where
#[cfg(feature = "multi-window")] #[cfg(feature = "multi-window")]
pub fn view(&self, id: window::Id) -> Element<'_, crate::Action<T::Message>> { pub fn view(&self, id: window::Id) -> Element<'_, crate::Action<T::Message>> {
#[cfg(all(feature = "wayland", target_os = "linux"))] if let Some((_, _, _, Some(v))) = self.surface_views.get(&id) {
if let Some((_, _, v)) = self.surface_views.get(&id) {
return v(&self.app); return v(&self.app);
} }
if self if self
@ -762,20 +802,8 @@ impl<T: Application> Cosmic<T> {
use iced_winit::platform_specific::commands::corner_radius::corner_radius; use iced_winit::platform_specific::commands::corner_radius::corner_radius;
let theme = THEME.lock().unwrap(); let theme = THEME.lock().unwrap();
let rounded = (!self.app.core().window.sharp_corners let rounded = !self.app.core().window.sharp_corners
&& self.app.core().sync_window_border_radii_to_theme()) && self.app.core().sync_window_border_radii_to_theme();
|| self
.surface_views
.get(&id)
.is_some_and(|(_, surface_type, _)| {
if let SurfaceIdWrapper::Popup(_)
| SurfaceIdWrapper::LayerSurface(_) = surface_type
{
true
} else {
false
}
});
let cur_rad = self.app.core().corners(&theme, rounded); let cur_rad = self.app.core().corners(&theme, rounded);
return Task::batch([corner_radius(id, cur_rad).discard()]); return Task::batch([corner_radius(id, cur_rad).discard()]);
@ -887,13 +915,12 @@ impl<T: Application> Cosmic<T> {
)); ));
} }
for (id, wrapper, ..) in &self.surface_views { for (id, wrapper, ..) in &self.surface_views {
if core.blur(&theme, Some(wrapper.1)) { let overriden = wrapper.2(&self.app);
cmds.push(blur(*id)); if core.blur(&theme, Some(wrapper.1)) || overriden.blur.unwrap_or_default()
} {
}
for (id, wrapper) in &self.tracked_surfaces {
if core.blur(&theme, Some(*wrapper)) {
cmds.push(blur(*id)); cmds.push(blur(*id));
} else if overriden.blur.is_some_and(|b| !b) {
cmds.push(iced::window::disable_blur(*id));
} }
} }
return Task::batch(cmds); return Task::batch(cmds);
@ -963,25 +990,18 @@ impl<T: Application> Cosmic<T> {
.unwrap_or(window::Id::RESERVED); .unwrap_or(window::Id::RESERVED);
let mut cmds = vec![corner_radius(main_window_id, cur_rad).discard()]; let mut cmds = vec![corner_radius(main_window_id, cur_rad).discard()];
// Update radius for each tracked view with the window surface type // Update radius for each tracked view with the window surface type
for (id, (_, surface_type, _)) in &self.surface_views { for (id, (_, surface_type, live_settings, _)) in &self.surface_views {
let cur_rad = corners( let overriden = live_settings(&self.app);
let cur_rad = if let Some(c) = overriden.corners {
Some(c)
} else {
corners(
*surface_type, *surface_type,
rounded, rounded,
&cosmic_theme, &cosmic_theme,
self.app.core().auto_corner_radius, self.app.core().auto_corner_radius,
); )
if cur_rad.is_none() { };
continue;
}
cmds.push(corner_radius(*id, cur_rad).discard());
}
for (id, wrapper) in &self.tracked_surfaces {
let cur_rad = corners(
*wrapper,
rounded,
&cosmic_theme,
self.app.core().auto_corner_radius,
);
if cur_rad.is_none() { if cur_rad.is_none() {
continue; continue;
} }
@ -1080,25 +1100,19 @@ impl<T: Application> Cosmic<T> {
let mut cmds = let mut cmds =
vec![corner_radius(main_window_id, cur_rad).discard()]; vec![corner_radius(main_window_id, cur_rad).discard()];
// Update radius for each tracked view with the window surface type // Update radius for each tracked view with the window surface type
for (id, (_, surface_type, _)) in &self.surface_views { for (id, (_, surface_type, live_settings, _)) in &self.surface_views
let cur_rad = corners( {
let overriden = live_settings(&self.app);
let cur_rad = if let Some(c) = overriden.corners {
Some(c)
} else {
corners(
*surface_type, *surface_type,
rounded, rounded,
&cosmic_theme, &cosmic_theme,
self.app.core().auto_corner_radius, self.app.core().auto_corner_radius,
); )
if cur_rad.is_none() { };
continue;
}
cmds.push(corner_radius(*id, cur_rad).discard());
}
for (id, wrapper) in &self.tracked_surfaces {
let cur_rad = corners(
*wrapper,
rounded,
&cosmic_theme,
self.app.core().auto_corner_radius,
);
if cur_rad.is_none() { if cur_rad.is_none() {
continue; continue;
} }
@ -1121,13 +1135,13 @@ impl<T: Application> Cosmic<T> {
)); ));
} }
for (id, wrapper, ..) in &self.surface_views { for (id, wrapper, ..) in &self.surface_views {
if core.blur(&cosmic_theme, Some(wrapper.1)) { let overriden = wrapper.2(&self.app);
cmds.push(blur(*id)); if core.blur(&cosmic_theme, Some(wrapper.1))
} || overriden.blur.unwrap_or_default()
} {
for (id, wrapper) in &self.tracked_surfaces {
if core.blur(&cosmic_theme, Some(*wrapper)) {
cmds.push(blur(*id)); cmds.push(blur(*id));
} else if overriden.blur.is_some_and(|b| !b) {
cmds.push(iced::window::disable_blur(*id));
} }
} }
@ -1171,12 +1185,9 @@ impl<T: Application> Cosmic<T> {
*v == 0 *v == 0
}) { }) {
self.opened_surfaces.remove(&id); self.opened_surfaces.remove(&id);
#[cfg(all(feature = "wayland", target_os = "linux"))]
self.surface_views.remove(&id); self.surface_views.remove(&id);
} }
self.tracked_surfaces.remove(&id); self.surface_views.shrink_to(self.surface_views.len() * 2);
self.tracked_surfaces
.shrink_to(self.tracked_surfaces.len() * 2);
let mut ret = if let Some(msg) = self.app.on_close_requested(id) { let mut ret = if let Some(msg) = self.app.on_close_requested(id) {
self.app.update(msg) self.app.update(msg)
@ -1247,6 +1258,7 @@ impl<T: Application> Cosmic<T> {
new_theme.transparent = new_blur; new_theme.transparent = new_blur;
} }
core.system_theme = new_theme.clone(); core.system_theme = new_theme.clone();
let core = self.app.core();
{ {
let mut cosmic_theme = THEME.lock().unwrap(); let mut cosmic_theme = THEME.lock().unwrap();
@ -1266,14 +1278,15 @@ impl<T: Application> Cosmic<T> {
core.main_window_id().unwrap_or(window::Id::RESERVED), core.main_window_id().unwrap_or(window::Id::RESERVED),
)); ));
} }
for (id, wrapper, ..) in &self.surface_views { for (id, wrapper, ..) in &self.surface_views {
if core.blur(&cosmic_theme, Some(wrapper.1)) { let overriden = wrapper.2(&self.app);
cmds.push(blur(*id)); if core.blur(&cosmic_theme, Some(wrapper.1))
} || overriden.blur.unwrap_or_default()
} {
for (id, wrapper) in &self.tracked_surfaces {
if core.blur(&cosmic_theme, Some(*wrapper)) {
cmds.push(blur(*id)); cmds.push(blur(*id));
} else if overriden.blur.is_some_and(|b| !b) {
cmds.push(iced::window::disable_blur(*id));
} }
} }
} }
@ -1339,6 +1352,7 @@ impl<T: Application> Cosmic<T> {
parent, parent,
SurfaceIdWrapper::Subsurface(_) | SurfaceIdWrapper::Popup(_), SurfaceIdWrapper::Subsurface(_) | SurfaceIdWrapper::Popup(_),
_, _,
_,
)) = self.surface_views.get(&f) )) = self.surface_views.get(&f)
{ {
// If the parent is already focused, push the new focus // If the parent is already focused, push the new focus
@ -1355,7 +1369,7 @@ impl<T: Application> Cosmic<T> {
cur = self cur = self
.surface_views .surface_views
.get(&p) .get(&p)
.and_then(|(parent, _, _)| *parent); .and_then(|(parent, _, _, _)| *parent);
} }
parent_chain.reverse(); parent_chain.reverse();
self.app.core_mut().focused_window = parent_chain; self.app.core_mut().focused_window = parent_chain;
@ -1387,15 +1401,14 @@ impl<T: Application> Cosmic<T> {
// TODO do we need per window sharp corners? // TODO do we need per window sharp corners?
let rounded = (!self.app.core().window.sharp_corners let rounded = (!self.app.core().window.sharp_corners
&& self.app.core().sync_window_border_radii_to_theme()) && self.app.core().sync_window_border_radii_to_theme())
|| self || self.surface_views.get(&id).is_some_and(
.surface_views |(_, surface_type, live_settings, _)| {
.get(&id)
.is_some_and(|(_, surface_type, _)| {
matches!( matches!(
surface_type, surface_type,
SurfaceIdWrapper::Popup(_) | SurfaceIdWrapper::LayerSurface(_) SurfaceIdWrapper::Popup(_) | SurfaceIdWrapper::LayerSurface(_)
) )
}); },
);
let new_blur = self.blur_enabled && { let new_blur = self.blur_enabled && {
let t = theme.cosmic(); let t = theme.cosmic();
match self.app.core().app_type() { match self.app.core().app_type() {
@ -1405,11 +1418,7 @@ impl<T: Application> Cosmic<T> {
} }
}; };
let wrapper = self let wrapper = self.surface_views.get(&id).map(|s| s.1);
.surface_views
.get(&id)
.map(|s| s.1)
.or(self.tracked_surfaces.get(&id).copied());
// this will blur untracked windows as if they were the main window // this will blur untracked windows as if they were the main window
let blur_cmd = if self.app.core().blur(&theme, wrapper) { let blur_cmd = if self.app.core().blur(&theme, wrapper) {
@ -1426,15 +1435,18 @@ impl<T: Application> Cosmic<T> {
Task::none() Task::none()
}; };
let corner_task = if let Some((_, cur_rad)) = self let corner_task = if let Some((_, cur_rad)) =
.surface_views self.surface_views.get(&id).map(|s| (s.1, &s.2)).and_then(
.get(&id) |(s, overriden)| {
.map(|s| s.1) let overriden = overriden(&self.app);
.or(self.tracked_surfaces.get(&id).copied()) if let Some(c) = overriden.corners {
.and_then(|s| { Some((s, c))
} else {
corners(s, rounded, &theme, self.app.core().auto_corner_radius) corners(s, rounded, &theme, self.app.core().auto_corner_radius)
.map(|c| (s, c)) .map(|c| (s, c))
}) { }
},
) {
corner_radius(id, Some(cur_rad)).discard() corner_radius(id, Some(cur_rad)).discard()
} else if id } else if id
== self == self
@ -1490,13 +1502,13 @@ impl<T: Application> Cosmic<T> {
)); ));
} }
for (id, wrapper, ..) in &self.surface_views { for (id, wrapper, ..) in &self.surface_views {
if self.app.core().blur(&t, Some(wrapper.1)) { let overriden = wrapper.2(&self.app);
cmds.push(blur(*id)); if self.app.core().blur(&t, Some(wrapper.1))
} || overriden.blur.unwrap_or_default()
} {
for (id, wrapper) in &self.tracked_surfaces {
if self.app.core().blur(&t, Some(*wrapper)) {
cmds.push(blur(*id)); cmds.push(blur(*id));
} else if overriden.blur.is_some_and(|b| !b) {
cmds.push(iced::window::disable_blur(*id));
} }
} }
return Task::batch(cmds); return Task::batch(cmds);
@ -1513,35 +1525,79 @@ impl<App: Application> Cosmic<App> {
pub fn new(app: App) -> Self { pub fn new(app: App) -> Self {
Self { Self {
app, app,
#[cfg(all(feature = "wayland", target_os = "linux"))]
surface_views: HashMap::new(), surface_views: HashMap::new(),
opened_surfaces: HashMap::new(), opened_surfaces: HashMap::new(),
tracked_surfaces: HashMap::new(),
blur_enabled: false, blur_enabled: false,
} }
} }
#[cfg(all(feature = "wayland", feature = "winit", target_os = "linux"))]
/// Apply live setting overrides for a surface
pub fn apply_live_settings(
&mut self,
id_wrapper: SurfaceIdWrapper,
live_settings: &LiveSettings,
) -> Task<crate::Action<App::Message>> {
use iced_winit::commands::corner_radius;
let id = id_wrapper.inner();
let mut cmds = Vec::with_capacity(2);
let t = THEME.try_lock().unwrap();
if let Some(blur) = live_settings.blur {
let blur_cmd = if blur {
iced::window::enable_blur
} else {
iced::window::disable_blur
};
cmds.push(blur_cmd(id));
} else if self.app.core().blur(&*t, Some(id_wrapper)) {
cmds.push(iced::window::enable_blur(id));
}
if let Some(corners) = live_settings.corners {
cmds.push(corner_radius::corner_radius(id, Some(corners)).discard());
} else {
let rounded = !self.app.core().window.sharp_corners
&& self.app.core().sync_window_border_radii_to_theme();
if let Some(cur_rad) =
corners(id_wrapper, rounded, &*t, self.app.core().auto_corner_radius)
{
cmds.push(corner_radius::corner_radius(id, Some(cur_rad)).discard())
}
}
Task::batch(cmds)
}
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
/// Create a subsurface /// Create a subsurface
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< view: Option<
dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>> + Send + Sync, Box<dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>> + Send + Sync>,
>, >,
) -> Task<crate::Action<App::Message>> { ) -> Task<crate::Action<App::Message>> {
use iced_winit::commands::subsurface::get_subsurface; use iced_winit::commands::subsurface::get_subsurface;
*self.opened_surfaces.entry(settings.id).or_insert_with(|| 0) += 1; *self.opened_surfaces.entry(settings.id).or_insert_with(|| 0) += 1;
let live_settings_task = self.apply_live_settings(
SurfaceIdWrapper::Subsurface(settings.id),
&LiveSettings {
blur: Some(self.blur_enabled),
corners: None,
},
);
self.surface_views.insert( self.surface_views.insert(
settings.id, settings.id,
( (
Some(settings.parent), Some(settings.parent),
SurfaceIdWrapper::Subsurface(settings.id), SurfaceIdWrapper::Subsurface(settings.id),
Box::new(|_| LiveSettings::default()),
view, view,
), ),
); );
get_subsurface(settings) Task::batch([get_subsurface(settings), live_settings_task])
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
@ -1549,21 +1605,27 @@ 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< live_settings: Box<dyn for<'a> Fn(&'a App) -> LiveSettings + Send + Sync>,
dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>> + Send + Sync, view: Option<
Box<dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>> + Send + Sync>,
>, >,
) -> Task<crate::Action<App::Message>> { ) -> Task<crate::Action<App::Message>> {
use iced_winit::commands::popup::get_popup; use iced_winit::commands::popup::get_popup;
*self.opened_surfaces.entry(settings.id).or_insert_with(|| 0) += 1; *self.opened_surfaces.entry(settings.id).or_insert_with(|| 0) += 1;
let live_settings_task = self.apply_live_settings(
SurfaceIdWrapper::Popup(settings.id),
&live_settings(&self.app),
);
self.surface_views.insert( self.surface_views.insert(
settings.id, settings.id,
( (
Some(settings.parent), Some(settings.parent),
SurfaceIdWrapper::Popup(settings.id), SurfaceIdWrapper::Popup(settings.id),
live_settings,
view, view,
), ),
); );
get_popup(settings) Task::batch([get_popup(settings), live_settings_task])
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
@ -1572,46 +1634,62 @@ impl<App: Application> Cosmic<App> {
&mut self, &mut self,
id: iced::window::Id, id: iced::window::Id,
settings: iced::window::Settings, settings: iced::window::Settings,
view: Box< live_settings: Box<dyn for<'a> Fn(&'a App) -> LiveSettings + Send + Sync>,
dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>> + Send + Sync,
view: Option<
Box<dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>> + Send + Sync>,
>, >,
) -> Task<crate::Action<App::Message>> { ) -> Task<crate::Action<App::Message>> {
use iced_winit::SurfaceIdWrapper; use iced_winit::SurfaceIdWrapper;
*self.opened_surfaces.entry(id).or_insert(0) += 1; *self.opened_surfaces.entry(id).or_insert(0) += 1;
let live_settings_task =
self.apply_live_settings(SurfaceIdWrapper::Window(id), &live_settings(&self.app));
self.surface_views.insert( self.surface_views.insert(
id, id,
( (
None, // TODO parent for window, platform specific option maybe? None, // TODO parent for window, platform specific option maybe?
SurfaceIdWrapper::Window(id), SurfaceIdWrapper::Window(id),
live_settings,
view, view,
), ),
); );
Task::batch([
iced_runtime::task::oneshot(|channel| { iced_runtime::task::oneshot(|channel| {
iced_runtime::Action::Window(iced_runtime::window::Action::Open(id, settings, channel)) iced_runtime::Action::Window(iced_runtime::window::Action::Open(
id, settings, channel,
))
}) })
.discard() .discard(),
live_settings_task,
])
} }
#[cfg(all(feature = "wayland", target_os = "linux"))] #[cfg(all(feature = "wayland", target_os = "linux"))]
pub fn get_layer_shell( pub fn get_layer_shell(
&mut self, &mut self,
settings: iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings, settings: iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings,
view: Box< live_settings: Box<dyn for<'a> Fn(&'a App) -> LiveSettings + Send + Sync>,
dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>> + Send + Sync, view: Option<
Box<dyn for<'a> Fn(&'a App) -> Element<'a, crate::Action<App::Message>> + Send + Sync>,
>, >,
) -> Task<crate::Action<App::Message>> { ) -> Task<crate::Action<App::Message>> {
use iced_winit::SurfaceIdWrapper; use iced_winit::SurfaceIdWrapper;
use iced_winit::platform_specific::commands::layer_surface::get_layer_surface; use iced_winit::platform_specific::commands::layer_surface::get_layer_surface;
*self.opened_surfaces.entry(settings.id).or_insert(0) += 1; *self.opened_surfaces.entry(settings.id).or_insert(0) += 1;
let live_settings_task = self.apply_live_settings(
SurfaceIdWrapper::LayerSurface(settings.id),
&live_settings(&self.app),
);
self.surface_views.insert( self.surface_views.insert(
settings.id, settings.id,
( (
None, // TODO parent for layer shell, platform specific option maybe? None, // TODO parent for layer shell, platform specific option maybe?
SurfaceIdWrapper::LayerSurface(settings.id), SurfaceIdWrapper::LayerSurface(settings.id),
live_settings,
view, view,
), ),
); );
get_layer_surface(settings) Task::batch([get_layer_surface(settings), live_settings_task])
} }
} }

View file

@ -5,7 +5,9 @@ use super::Action;
#[cfg(feature = "winit")] #[cfg(feature = "winit")]
use crate::Application; use crate::Application;
use iced::window; use iced::{Rectangle, window};
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
use iced_runtime::platform_specific::wayland::CornerRadius;
use std::any::Any; use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
@ -34,18 +36,30 @@ pub fn destroy_layer_shell(id: iced_core::window::Id) -> Action {
Action::DestroyLayerShell(id) Action::DestroyLayerShell(id)
} }
#[derive(Debug, Default, Copy, Clone)]
pub struct LiveSettings {
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))] #[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
#[must_use] /// Override the default corner radius value for the surface type.
pub fn app_window<App: Application>( pub corners: Option<CornerRadius>,
settings: impl Fn(&mut App) -> window::Settings + Send + Sync + 'static, /// Override the default blur setting for the surface type.
view: Option< pub blur: Option<bool>,
}
type BoxedView<App> = Option<
Box< Box<
dyn for<'a> Fn(&'a App) -> crate::Element<'a, crate::Action<App::Message>> dyn Fn(&App) -> crate::Element<'_, crate::Action<<App as Application>::Message>>
+ Send + Send
+ Sync + Sync
+ 'static, + 'static,
>, >,
>, >;
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
#[must_use]
pub fn app_window<App: Application>(
live_settings: impl Fn(&App) -> LiveSettings + Send + Sync + 'static,
settings: impl Fn(&mut App) -> window::Settings + Send + Sync + 'static,
view: BoxedView<App>,
) -> (window::Id, Action) { ) -> (window::Id, Action) {
let id = window::Id::unique(); let id = window::Id::unique();
@ -53,11 +67,16 @@ pub fn app_window<App: Application>(
Box::new(settings); Box::new(settings);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
let boxed_live: Box<dyn Fn(&App) -> LiveSettings + Send + Sync + 'static> =
Box::new(live_settings);
let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
( (
id, id,
Action::AppWindow( Action::AppWindow(
id, id,
Arc::new(boxed), Arc::new(boxed),
Arc::new(boxed_live),
view.map(|view| { view.map(|view| {
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
Arc::new(boxed) Arc::new(boxed)
@ -70,6 +89,7 @@ pub fn app_window<App: Application>(
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))] #[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
#[must_use] #[must_use]
pub fn simple_window<Message: 'static>( pub fn simple_window<Message: 'static>(
live_settings: impl Fn() -> LiveSettings + Send + Sync + 'static,
settings: impl Fn() -> window::Settings + Send + Sync + 'static, settings: impl Fn() -> window::Settings + Send + Sync + 'static,
view: Option< view: Option<
impl Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static, impl Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static,
@ -80,11 +100,15 @@ pub fn simple_window<Message: 'static>(
let boxed: Box<dyn Fn() -> window::Settings + Send + Sync + 'static> = Box::new(settings); let boxed: Box<dyn Fn() -> window::Settings + Send + Sync + 'static> = Box::new(settings);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
let boxed_live: Box<dyn Fn() -> LiveSettings + Send + Sync + 'static> = Box::new(live_settings);
let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
( (
id, id,
Action::Window( Action::Window(
id, id,
Arc::new(boxed), Arc::new(boxed),
Arc::new(boxed_live),
view.map(|view| { view.map(|view| {
let boxed: Box< let boxed: Box<
dyn Fn() -> crate::Element<'static, crate::Action<Message>> dyn Fn() -> crate::Element<'static, crate::Action<Message>>
@ -102,18 +126,12 @@ pub fn simple_window<Message: 'static>(
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))] #[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
#[must_use] #[must_use]
pub fn app_popup<App: Application>( pub fn app_popup<App: Application>(
live_settings: impl Fn(&App) -> LiveSettings + Send + Sync + 'static,
settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings settings: impl Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
+ Send + Send
+ Sync + Sync
+ 'static, + 'static,
view: Option< view: BoxedView<App>,
Box<
dyn for<'a> Fn(&'a App) -> crate::Element<'a, crate::Action<App::Message>>
+ Send
+ Sync
+ 'static,
>,
>,
) -> Action { ) -> Action {
let boxed: Box< let boxed: Box<
dyn Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings dyn Fn(&mut App) -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
@ -123,8 +141,13 @@ pub fn app_popup<App: Application>(
> = Box::new(settings); > = Box::new(settings);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
let boxed_live: Box<dyn Fn(&App) -> LiveSettings + Send + Sync + 'static> =
Box::new(live_settings);
let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
Action::AppPopup( Action::AppPopup(
Arc::new(boxed), Arc::new(boxed),
Arc::new(boxed_live),
view.map(|view| { view.map(|view| {
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
Arc::new(boxed) Arc::new(boxed)
@ -154,6 +177,7 @@ pub fn simple_subsurface<Message: 'static, V>(
Action::Subsurface( Action::Subsurface(
Arc::new(boxed), Arc::new(boxed),
Arc::new(Box::new(LiveSettings::default)),
view.map(|view| { view.map(|view| {
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
Arc::new(boxed) Arc::new(boxed)
@ -165,6 +189,7 @@ pub fn simple_subsurface<Message: 'static, V>(
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))] #[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
#[must_use] #[must_use]
pub fn simple_popup<Message: 'static>( pub fn simple_popup<Message: 'static>(
live_settings: impl Fn() -> LiveSettings + Send + Sync + 'static,
settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings settings: impl Fn() -> iced_runtime::platform_specific::wayland::popup::SctkPopupSettings
+ Send + Send
+ Sync + Sync
@ -181,8 +206,12 @@ pub fn simple_popup<Message: 'static>(
> = Box::new(settings); > = Box::new(settings);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
let boxed_live: Box<dyn Fn() -> LiveSettings + Send + Sync + 'static> = Box::new(live_settings);
let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
Action::Popup( Action::Popup(
Arc::new(boxed), Arc::new(boxed),
Arc::new(boxed_live),
view.map(|view| { view.map(|view| {
let boxed: Box< let boxed: Box<
dyn Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static, dyn Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static,
@ -204,14 +233,7 @@ pub fn subsurface<App: Application>(
+ Sync + Sync
+ 'static, + 'static,
// XXX Boxed trait object is required for less cumbersome type inference, but we box it anyways. // XXX Boxed trait object is required for less cumbersome type inference, but we box it anyways.
view: Option< view: BoxedView<App>,
Box<
dyn for<'a> Fn(&'a App) -> crate::Element<'a, crate::Action<App::Message>>
+ Send
+ Sync
+ 'static,
>,
>,
) -> Action { ) -> Action {
let boxed: Box< let boxed: Box<
dyn Fn( dyn Fn(
@ -226,6 +248,7 @@ pub fn subsurface<App: Application>(
Action::AppSubsurface( Action::AppSubsurface(
Arc::new(boxed), Arc::new(boxed),
Arc::new(Box::new(|_: &App| LiveSettings::default())),
view.map(|view| { view.map(|view| {
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
Arc::new(boxed) Arc::new(boxed)
@ -236,6 +259,7 @@ pub fn subsurface<App: Application>(
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))] #[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
#[must_use] #[must_use]
pub fn simple_layer_shell<Message: 'static>( pub fn simple_layer_shell<Message: 'static>(
live_settings: impl Fn() -> LiveSettings + Send + Sync + 'static,
settings: impl Fn() settings: impl Fn()
-> iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings -> iced_runtime::platform_specific::wayland::layer_surface::SctkLayerSurfaceSettings
+ Send + Send
@ -253,9 +277,11 @@ pub fn simple_layer_shell<Message: 'static>(
+ 'static, + 'static,
> = Box::new(settings); > = Box::new(settings);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
let boxed_live: Box<dyn Fn() -> LiveSettings + Send + Sync + 'static> = Box::new(live_settings);
let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
Action::LayerShell( Action::LayerShell(
Arc::new(boxed), Arc::new(boxed),
Arc::new(boxed_live),
view.map(|view| { view.map(|view| {
let boxed: Box< let boxed: Box<
dyn Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static, dyn Fn() -> crate::Element<'static, crate::Action<Message>> + Send + Sync + 'static,
@ -268,7 +294,8 @@ pub fn simple_layer_shell<Message: 'static>(
#[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))] #[cfg(all(feature = "wayland", target_os = "linux", feature = "winit"))]
#[must_use] #[must_use]
pub fn layer_shell<App: Application>( pub fn app_layer_shell<App: Application>(
live_settings: impl Fn(&App) -> LiveSettings + Send + Sync + 'static,
settings: impl Fn( settings: impl Fn(
&mut App, &mut App,
) )
@ -277,14 +304,7 @@ pub fn layer_shell<App: Application>(
+ Sync + Sync
+ 'static, + 'static,
// XXX Boxed trait object is required for less cumbersome type inference, but we box it anyways. // XXX Boxed trait object is required for less cumbersome type inference, but we box it anyways.
view: Option< view: BoxedView<App>,
Box<
dyn for<'a> Fn(&'a App) -> crate::Element<'a, crate::Action<App::Message>>
+ Send
+ Sync
+ 'static,
>,
>,
) -> Action { ) -> Action {
let boxed: Box< let boxed: Box<
dyn Fn( dyn Fn(
@ -297,8 +317,13 @@ pub fn layer_shell<App: Application>(
> = Box::new(settings); > = Box::new(settings);
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
let boxed_live: Box<dyn Fn(&App) -> LiveSettings + Send + Sync + 'static> =
Box::new(live_settings);
let boxed_live: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed_live);
Action::AppLayerShell( Action::AppLayerShell(
Arc::new(boxed), Arc::new(boxed),
Arc::new(boxed_live),
view.map(|view| { view.map(|view| {
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(view);
Arc::new(boxed) Arc::new(boxed)

View file

@ -4,34 +4,24 @@
pub mod action; pub mod action;
use iced::{Limits, Size, Task}; use iced::{Limits, Size, Task};
use std::future::Future; use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
type BoxedSetting = Arc<Box<dyn Any + Send + Sync + 'static>>;
/// Ignore this message in your application. It will be intercepted. /// Ignore this message in your application. It will be intercepted.
#[derive(Clone)] #[derive(Clone)]
pub enum Action { pub enum Action {
/// Create a subsurface with a view function accepting the App as a parameter /// Create a subsurface with a view function accepting the App as a parameter
AppSubsurface( AppSubsurface(BoxedSetting, BoxedSetting, Option<BoxedSetting>),
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>,
),
/// Create a subsurface with a view function /// Create a subsurface with a view function
Subsurface( Subsurface(BoxedSetting, BoxedSetting, Option<BoxedSetting>),
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>,
),
/// Destroy a subsurface with a view function /// Destroy a subsurface with a view function
DestroySubsurface(iced::window::Id), DestroySubsurface(iced::window::Id),
/// Create a popup with a view function accepting the App as a parameter /// Create a popup with a view function accepting the App as a parameter
AppPopup( AppPopup(BoxedSetting, BoxedSetting, Option<BoxedSetting>),
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>,
),
/// Create a popup /// Create a popup
Popup( Popup(BoxedSetting, BoxedSetting, Option<BoxedSetting>),
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>,
),
/// Destroy a subsurface with a view function /// Destroy a subsurface with a view function
DestroyPopup(iced::window::Id), DestroyPopup(iced::window::Id),
/// Destroys the global tooltip popup subsurface /// Destroys the global tooltip popup subsurface
@ -40,29 +30,25 @@ pub enum Action {
/// Create a window with a view function accepting the App as a parameter /// Create a window with a view function accepting the App as a parameter
AppWindow( AppWindow(
iced::window::Id, iced::window::Id,
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>, BoxedSetting,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>, BoxedSetting,
Option<BoxedSetting>,
), ),
/// Create a window with a view function /// Create a window with a view function
Window( Window(
iced::window::Id, iced::window::Id,
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>, BoxedSetting,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>, BoxedSetting,
Option<BoxedSetting>,
), ),
/// Destroy a window /// Destroy a window
DestroyWindow(iced::window::Id), DestroyWindow(iced::window::Id),
/// Create a layer shell surface with a view function accepting the App as a parameter /// Create a layer shell surface with a view function accepting the App as a parameter
AppLayerShell( AppLayerShell(BoxedSetting, BoxedSetting, Option<BoxedSetting>),
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>,
),
/// Create a layer shell surface with a view function /// Create a layer shell surface with a view function
LayerShell( LayerShell(BoxedSetting, BoxedSetting, Option<BoxedSetting>),
std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>,
Option<std::sync::Arc<Box<dyn std::any::Any + Send + Sync>>>,
),
/// Destroy a layer shell surface /// Destroy a layer shell surface
DestroyLayerShell(iced::window::Id), DestroyLayerShell(iced::window::Id),
@ -89,21 +75,33 @@ impl std::fmt::Debug for Action {
#[cold] #[cold]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::AppSubsurface(arg0, arg1) => f Self::AppSubsurface(arg0, arg1, arg2) => f
.debug_tuple("AppSubsurface") .debug_tuple("AppSubsurface")
.field(arg0) .field(arg0)
.field(arg1) .field(arg1)
.field(arg2)
.finish(),
Self::Subsurface(arg0, arg1, arg2) => f
.debug_tuple("Subsurface")
.field(arg0)
.field(arg1)
.field(arg2)
.finish(), .finish(),
Self::Subsurface(arg0, arg1) => {
f.debug_tuple("Subsurface").field(arg0).field(arg1).finish()
}
Self::DestroySubsurface(arg0) => { Self::DestroySubsurface(arg0) => {
f.debug_tuple("DestroySubsurface").field(arg0).finish() f.debug_tuple("DestroySubsurface").field(arg0).finish()
} }
Self::AppPopup(arg0, arg1) => { Self::AppPopup(arg0, arg1, arg2) => f
f.debug_tuple("AppPopup").field(arg0).field(arg1).finish() .debug_tuple("AppPopup")
} .field(arg0)
Self::Popup(arg0, arg1) => f.debug_tuple("Popup").field(arg0).field(arg1).finish(), .field(arg1)
.field(arg2)
.finish(),
Self::Popup(arg0, arg1, arg2) => f
.debug_tuple("Popup")
.field(arg0)
.field(arg1)
.field(arg2)
.finish(),
Self::DestroyPopup(arg0) => f.debug_tuple("DestroyPopup").field(arg0).finish(), Self::DestroyPopup(arg0) => f.debug_tuple("DestroyPopup").field(arg0).finish(),
Self::DestroyTooltipPopup => f.debug_tuple("DestroyTooltipPopup").finish(), Self::DestroyTooltipPopup => f.debug_tuple("DestroyTooltipPopup").finish(),
Self::ResponsiveMenuBar { Self::ResponsiveMenuBar {
@ -117,28 +115,34 @@ impl std::fmt::Debug for Action {
.field("size", size) .field("size", size)
.finish(), .finish(),
Self::Ignore => write!(f, "Ignore"), Self::Ignore => write!(f, "Ignore"),
Self::AppWindow(id, arg0, arg1) => f Self::AppWindow(id, arg0, arg1, arg2) => f
.debug_tuple("AppWindow") .debug_tuple("AppWindow")
.field(id) .field(id)
.field(arg0) .field(arg0)
.field(arg1) .field(arg1)
.field(arg2)
.finish(), .finish(),
Self::Window(id, arg0, arg1) => f Self::Window(id, arg0, arg1, arg2) => f
.debug_tuple("Window") .debug_tuple("Window")
.field(id) .field(id)
.field(arg0) .field(arg0)
.field(arg1) .field(arg1)
.field(arg2)
.finish(), .finish(),
Self::DestroyWindow(arg0) => f.debug_tuple("DestroyWindow").field(arg0).finish(), Self::DestroyWindow(arg0) => f.debug_tuple("DestroyWindow").field(arg0).finish(),
Self::Task(_) => f.debug_tuple("Future").finish(), Self::Task(_) => f.debug_tuple("Future").finish(),
Self::AppLayerShell(any, any1) => f Self::AppLayerShell(arg, arg1, arg2) => f
.debug_tuple("AppLayerShell") .debug_tuple("AppLayerShell")
.field(any) .field(arg)
.field(any1) .field(arg1)
.field(arg2)
.finish(),
Self::LayerShell(arg, arg1, arg2) => f
.debug_tuple("LayerShell")
.field(arg)
.field(arg1)
.field(arg2)
.finish(), .finish(),
Self::LayerShell(any, any1) => {
f.debug_tuple("LayerShell").field(any).field(any1).finish()
}
Self::DestroyLayerShell(arg0) => { Self::DestroyLayerShell(arg0) => {
f.debug_tuple("DestroyLayerShell").field(arg0).finish() f.debug_tuple("DestroyLayerShell").field(arg0).finish()
} }

View file

@ -81,7 +81,7 @@ impl<Message: Clone + 'static> ContextMenu<'_, Message> {
my_state: &mut LocalState, my_state: &mut LocalState,
) { ) {
if self.window_id != window::Id::NONE && self.on_surface_action.is_some() { if self.window_id != window::Id::NONE && self.on_surface_action.is_some() {
use crate::surface::action::destroy_popup; use crate::surface::action::{LiveSettings, destroy_popup};
use crate::widget::menu::Menu; use crate::widget::menu::Menu;
use iced_runtime::platform_specific::wayland::popup::{ use iced_runtime::platform_specific::wayland::popup::{
SctkPopupSettings, SctkPositioner, SctkPopupSettings, SctkPositioner,
@ -187,6 +187,7 @@ impl<Message: Clone + 'static> ContextMenu<'_, Message> {
let parent = self.window_id; let parent = self.window_id;
shell.publish((self.on_surface_action.as_ref().unwrap())( shell.publish((self.on_surface_action.as_ref().unwrap())(
crate::surface::action::simple_popup( crate::surface::action::simple_popup(
|| LiveSettings::default(),
move || SctkPopupSettings { move || SctkPopupSettings {
parent, parent,
id, id,

View file

@ -580,6 +580,8 @@ pub fn update<
use iced_runtime::platform_specific::wayland::popup::{ use iced_runtime::platform_specific::wayland::popup::{
SctkPopupSettings, SctkPositioner, SctkPopupSettings, SctkPositioner,
}; };
use crate::surface::action::LiveSettings;
let bounds = layout.bounds(); let bounds = layout.bounds();
let anchor_rect = Rectangle { let anchor_rect = Rectangle {
x: bounds.x as i32, x: bounds.x as i32,
@ -606,6 +608,7 @@ pub fn update<
let on_surface_action_clone = on_surface_action.clone(); let on_surface_action_clone = on_surface_action.clone();
let translation = layout.virtual_offset(); let translation = layout.virtual_offset();
let get_popup_action = surface::action::simple_popup::<AppMessage>( let get_popup_action = surface::action::simple_popup::<AppMessage>(
|| LiveSettings::default(),
move || { move || {
SctkPopupSettings { SctkPopupSettings {
parent, parent,

View file

@ -385,7 +385,7 @@ where
my_state: &mut MenuBarState, my_state: &mut MenuBarState,
) { ) {
if self.window_id != window::Id::NONE && self.on_surface_action.is_some() { if self.window_id != window::Id::NONE && self.on_surface_action.is_some() {
use crate::surface::action::destroy_popup; use crate::surface::action::{LiveSettings, destroy_popup};
use iced_runtime::platform_specific::wayland::popup::{ use iced_runtime::platform_specific::wayland::popup::{
SctkPopupSettings, SctkPositioner, SctkPopupSettings, SctkPositioner,
}; };
@ -501,6 +501,7 @@ where
}; };
let parent = self.window_id; let parent = self.window_id;
shell.publish((surface_action)(crate::surface::action::simple_popup( shell.publish((surface_action)(crate::surface::action::simple_popup(
|| LiveSettings::default(),
move || SctkPopupSettings { move || SctkPopupSettings {
parent, parent,
id, id,

View file

@ -988,6 +988,8 @@ impl<Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, crate::
use iced_runtime::platform_specific::wayland::popup::{ use iced_runtime::platform_specific::wayland::popup::{
SctkPopupSettings, SctkPositioner, SctkPopupSettings, SctkPositioner,
}; };
use crate::surface::action::LiveSettings;
let overlay_offset = Point::ORIGIN - viewport.position(); let overlay_offset = Point::ORIGIN - viewport.position();
let overlay_cursor = cursor.position().unwrap_or_default() - overlay_offset; let overlay_cursor = cursor.position().unwrap_or_default() - overlay_offset;
@ -1103,6 +1105,7 @@ impl<Message: std::clone::Clone + 'static> Widget<Message, crate::Theme, crate::
let parent = self.window_id; let parent = self.window_id;
shell.publish((self.on_surface_action.as_ref().unwrap())( shell.publish((self.on_surface_action.as_ref().unwrap())(
crate::surface::action::simple_popup( crate::surface::action::simple_popup(
|| LiveSettings::default(),
move || SctkPopupSettings { move || SctkPopupSettings {
parent, parent,
id: popup_id, id: popup_id,

View file

@ -21,6 +21,8 @@ use iced_core::{
Vector, Widget, layout, mouse, overlay, renderer, svg, touch, Vector, Widget, layout, mouse, overlay, renderer, svg, touch,
}; };
use crate::surface::action::LiveSettings;
pub use super::{Catalog, Style}; pub use super::{Catalog, Style};
/// Internally defines different button widget variants. /// Internally defines different button widget variants.
@ -521,8 +523,15 @@ pub fn update<'a, Message: Clone + 'static, TopLevelMessage: Clone + 'static>(
> = Box::new(move || s(bounds)); > = Box::new(move || s(bounds));
let boxed: Box<dyn Any + Send + Sync + 'static> = let boxed: Box<dyn Any + Send + Sync + 'static> =
Box::new(boxed); Box::new(boxed);
let boxed_live: Box<
dyn Fn() -> LiveSettings + Send + Sync + 'static,
> = Box::new(move || Default::default());
let boxed_live: Box<dyn Any + Send + Sync + 'static> =
Box::new(boxed_live);
crate::surface::Action::Popup( crate::surface::Action::Popup(
Arc::new(boxed), Arc::new(boxed),
Arc::new(boxed_live),
Some({ Some({
let boxed: Box< let boxed: Box<
dyn Fn() -> crate::Element< dyn Fn() -> crate::Element<
@ -553,9 +562,14 @@ pub fn update<'a, Message: Clone + 'static, TopLevelMessage: Clone + 'static>(
+ 'static, + 'static,
> = Box::new(move || s(bounds)); > = Box::new(move || s(bounds));
let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed); let boxed: Box<dyn Any + Send + Sync + 'static> = Box::new(boxed);
let boxed_live: Box<dyn Fn() -> LiveSettings + Send + Sync + 'static> =
Box::new(move || Default::default());
let boxed_live: Box<dyn Any + Send + Sync + 'static> =
Box::new(boxed_live);
let sm = crate::surface::Action::Popup( let sm = crate::surface::Action::Popup(
Arc::new(boxed), Arc::new(boxed),
Arc::new(boxed_live),
Some({ Some({
let boxed: Box< let boxed: Box<
dyn Fn() -> crate::Element< dyn Fn() -> crate::Element<