From 58980231b9423c5795055bb935cea18b7d5184ce Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Sun, 10 May 2026 22:52:00 -0400 Subject: [PATCH] refactor: allow opting out of auto corner radii tracking --- src/app/cosmic.rs | 121 +++++++++++++++++++++++++++++++------------- src/core.rs | 109 ++++++++++++++++++++------------------- src/widget/cards.rs | 23 ++++----- 3 files changed, 153 insertions(+), 100 deletions(-) diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index 6fd97110..30fadcdc 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -6,11 +6,15 @@ use std::collections::HashMap; use std::sync::Arc; use super::{Action, Application, ApplicationExt, Subscription}; +#[cfg(all(feature = "wayland", target_os = "linux"))] +use crate::core::Auto; use crate::theme::{THEME, Theme, ThemeType}; use crate::{Core, Element, keyboard_nav}; #[cfg(all(feature = "wayland", target_os = "linux"))] use cctk::sctk::reexports::csd_frame::{WindowManagerCapabilities, WindowState}; use cosmic_theme::ThemeMode; +#[cfg(all(feature = "wayland", target_os = "linux"))] +use enumflags2::BitFlags; #[cfg(not(any(feature = "multi-window", feature = "wayland", target_os = "linux")))] use iced::Application as IcedApplication; #[cfg(all(feature = "wayland", target_os = "linux"))] @@ -754,11 +758,12 @@ impl Cosmic { self.app.core_mut().window.is_maximized = state.intersects(WindowState::MAXIMIZED | WindowState::FULLSCREEN); } - if self.app.core().sync_window_border_radii_to_theme() { + { use iced_winit::platform_specific::commands::corner_radius::corner_radius; 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 .surface_views .get(&id) @@ -772,8 +777,8 @@ impl Cosmic { } }); - let cur_rad = self.app.core().app_type.corners(&theme, rounded); - return Task::batch([corner_radius(id, Some(cur_rad)).discard()]); + let cur_rad = self.app.core().corners(&theme, rounded); + return Task::batch([corner_radius(id, cur_rad).discard()]); } } @@ -864,9 +869,9 @@ impl Cosmic { guard.transparent = new_blur; drop(guard); - let core = self.app.core(); #[cfg(all(feature = "wayland", target_os = "linux"))] { + let core = self.app.core(); let mut cmds = Vec::with_capacity(1 + self.surface_views.len()); let blur = if new_blur { iced::window::enable_blur @@ -942,14 +947,13 @@ impl Cosmic { cosmic_theme.transparent = new_blur; #[cfg(all(feature = "wayland", target_os = "linux"))] - if self.app.core().sync_window_border_radii_to_theme() { + { use iced_winit::platform_specific::commands::corner_radius::corner_radius; - let t = cosmic_theme.cosmic(); + let rounded = self.app.core().sync_window_border_radii_to_theme() + && !self.app.core().window.sharp_corners; - let rounded = !self.app.core().window.sharp_corners; - - let cur_rad = self.app.core().app_type.corners(&cosmic_theme, rounded); + let cur_rad = self.app.core().corners(&cosmic_theme, rounded); // Update radius for the main window let main_window_id = self @@ -957,16 +961,31 @@ impl Cosmic { .core() .main_window_id() .unwrap_or(window::Id::RESERVED); - let mut cmds = - vec![corner_radius(main_window_id, Some(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 for (id, (_, surface_type, _)) in &self.surface_views { - let cur_rad = corners(*surface_type, rounded, &cosmic_theme); - cmds.push(corner_radius(*id, Some(cur_rad)).discard()); + let cur_rad = corners( + *surface_type, + rounded, + &cosmic_theme, + 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); - cmds.push(corner_radius(*id, Some(cur_rad)).discard()); + let cur_rad = corners( + *wrapper, + rounded, + &cosmic_theme, + self.app.core().auto_corner_radius, + ); + if cur_rad.is_none() { + continue; + } + cmds.push(corner_radius(*id, cur_rad).discard()); } return Task::batch(cmds); @@ -1045,12 +1064,12 @@ impl Cosmic { cosmic_theme.set_theme(new_theme.theme_type); cosmic_theme.transparent = new_blur; #[cfg(all(feature = "wayland", target_os = "linux"))] - if self.app.core().sync_window_border_radii_to_theme() { + { use iced_winit::platform_specific::commands::corner_radius::corner_radius; - let rounded = !self.app.core().window.sharp_corners; - let cur_rad = - self.app.core().app_type.corners(&cosmic_theme, rounded); + let rounded = self.app.core().sync_window_border_radii_to_theme() + && !self.app.core().window.sharp_corners; + let cur_rad = self.app.core().corners(&cosmic_theme, rounded); // Update radius for the main window let main_window_id = self @@ -1059,15 +1078,31 @@ impl Cosmic { .main_window_id() .unwrap_or(window::Id::RESERVED); let mut cmds = - vec![corner_radius(main_window_id, Some(cur_rad)).discard()]; + vec![corner_radius(main_window_id, cur_rad).discard()]; // Update radius for each tracked view with the window surface type for (id, (_, surface_type, _)) in &self.surface_views { - let cur_rad = corners(*surface_type, rounded, &cosmic_theme); - cmds.push(corner_radius(*id, Some(cur_rad)).discard()); + let cur_rad = corners( + *surface_type, + rounded, + &cosmic_theme, + 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); - cmds.push(corner_radius(*id, Some(cur_rad)).discard()); + let cur_rad = corners( + *wrapper, + rounded, + &cosmic_theme, + self.app.core().auto_corner_radius, + ); + if cur_rad.is_none() { + continue; + } + cmds.push(corner_radius(*id, cur_rad).discard()); } let core = self.app.core(); @@ -1344,13 +1379,14 @@ impl Cosmic { } Action::Opened(id) => { #[cfg(all(feature = "wayland", target_os = "linux"))] - if self.app.core().sync_window_border_radii_to_theme() { + { use iced_winit::platform_specific::commands::corner_radius::corner_radius; let mut theme = THEME.lock().unwrap(); // 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 .surface_views .get(&id) @@ -1374,6 +1410,7 @@ impl Cosmic { .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 let blur_cmd = if self.app.core().blur(&theme, wrapper) { let blur = if new_blur { @@ -1389,13 +1426,16 @@ impl Cosmic { Task::none() }; - let corner_task = if let Some(s) = self + let corner_task = if let Some((_, cur_rad)) = self .surface_views .get(&id) .map(|s| s.1) .or(self.tracked_surfaces.get(&id).copied()) - { - corner_radius(id, Some(corners(s, rounded, &theme))).discard() + .and_then(|s| { + corners(s, rounded, &theme, self.app.core().auto_corner_radius) + .map(|c| (s, c)) + }) { + corner_radius(id, Some(cur_rad)).discard() } else if id == self .app @@ -1403,11 +1443,11 @@ impl Cosmic { .main_window_id() .unwrap_or(window::Id::RESERVED) { - corner_radius(id, Some(self.app.core().app_type.corners(&theme, rounded))) - .discard() + corner_radius(id, self.app.core().corners(&theme, rounded)).discard() } else { Task::none() }; + return Task::batch([ blur_cmd, corner_task, @@ -1580,9 +1620,18 @@ fn corners( surface_type: SurfaceIdWrapper, rounded: bool, theme: &Theme, -) -> iced_runtime::platform_specific::wayland::CornerRadius { + auto_corner_radius: BitFlags, +) -> Option { + if !match surface_type { + SurfaceIdWrapper::Window(_) => auto_corner_radius.contains(Auto::Window), + SurfaceIdWrapper::LayerSurface(_) => auto_corner_radius.contains(Auto::System), + SurfaceIdWrapper::Popup(_) => auto_corner_radius.contains(Auto::Popup), + _ => false, + } { + return None; + } let theme = theme.cosmic(); - if let SurfaceIdWrapper::Popup(_) = surface_type { + Some(if let SurfaceIdWrapper::Popup(_) = surface_type { let radius_m = theme.radius_m(); iced_runtime::platform_specific::wayland::CornerRadius { top_left: radius_m[0].round() as u32, @@ -1591,7 +1640,7 @@ fn corners( bottom_left: radius_m[3].round() as u32, } } else if let SurfaceIdWrapper::Window(_) = surface_type - && rounded + && !rounded { let radius_0 = theme.radius_0(); iced_runtime::platform_specific::wayland::CornerRadius { @@ -1608,5 +1657,5 @@ fn corners( bottom_right: radius_s[2].round() as u32, bottom_left: radius_s[3].round() as u32, } - } + }) } diff --git a/src/core.rs b/src/core.rs index d52f2fe0..a8691d11 100644 --- a/src/core.rs +++ b/src/core.rs @@ -112,11 +112,10 @@ pub struct Core { pub(crate) menu_bars: HashMap, - #[cfg(all(feature = "wayland", target_os = "linux"))] - pub(crate) sync_window_border_radii_to_theme: bool, - pub(crate) auto_blur: BitFlags, + pub(crate) auto_corner_radius: BitFlags, + pub(crate) app_type: AppType, } @@ -130,45 +129,6 @@ pub enum AppType { Applet, } -impl AppType { - /// Calculate suggested corners for each app type main window - #[cfg(all(feature = "wayland", target_os = "linux"))] - pub fn corners( - &self, - theme: &Theme, - rounded: bool, - ) -> iced_runtime::platform_specific::wayland::CornerRadius { - let theme = theme.cosmic(); - if let Self::Applet = self { - let radius_l = theme.radius_l(); - iced_runtime::platform_specific::wayland::CornerRadius { - top_left: radius_l[0].round() as u32, - top_right: radius_l[1].round() as u32, - bottom_right: radius_l[2].round() as u32, - bottom_left: radius_l[3].round() as u32, - } - } else if let Self::Window = self - && rounded - { - let radius_0 = theme.radius_0(); - iced_runtime::platform_specific::wayland::CornerRadius { - top_left: radius_0[0].round() as u32, - top_right: radius_0[1].round() as u32, - bottom_right: radius_0[2].round() as u32, - bottom_left: radius_0[3].round() as u32, - } - } else { - let radius_s = theme.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 }); - iced_runtime::platform_specific::wayland::CornerRadius { - top_left: radius_s[0].round() as u32, - top_right: radius_s[1].round() as u32, - bottom_right: radius_s[2].round() as u32, - bottom_left: radius_s[3].round() as u32, - } - } - } -} - impl Default for Core { fn default() -> Self { Self { @@ -225,9 +185,8 @@ impl Default for Core { main_window: None, exit_on_main_window_closed: true, menu_bars: HashMap::new(), - #[cfg(all(feature = "wayland", target_os = "linux"))] - sync_window_border_radii_to_theme: true, auto_blur: Auto::System | Auto::Popup | Auto::Window, + auto_corner_radius: Auto::System | Auto::Popup | Auto::Window, app_type: AppType::Window, } } @@ -560,15 +519,13 @@ impl Core { crate::command::toggle_maximize(id) } - // TODO should we emit tasks setting the corner radius or unsetting it if this is changed? - #[cfg(all(feature = "wayland", target_os = "linux"))] - pub fn set_sync_window_border_radii_to_theme(&mut self, sync: bool) { - self.sync_window_border_radii_to_theme = sync; - } - #[cfg(all(feature = "wayland", target_os = "linux"))] pub fn sync_window_border_radii_to_theme(&self) -> bool { - self.sync_window_border_radii_to_theme + match self.app_type { + AppType::Window => self.auto_corner_radius.contains(Auto::Window), + AppType::System => self.auto_corner_radius.contains(Auto::System), + AppType::Applet => false, + } } pub fn set_auto_blur(&mut self, auto_blur: BitFlags) { @@ -579,6 +536,14 @@ impl Core { self.auto_blur } + pub fn set_auto_corner_radius(&mut self, auto_corner_radius: BitFlags) { + self.auto_corner_radius = auto_corner_radius; + } + + pub fn auto_corner_radius(&self) -> BitFlags { + self.auto_corner_radius + } + pub fn set_app_type(&mut self, app_type: AppType) { self.app_type = app_type; } @@ -621,4 +586,46 @@ impl Core { _ => false, } } + + /// Calculate suggested corners for each app type main window + #[must_use] + #[cfg(all(feature = "wayland", target_os = "linux"))] + pub fn corners( + &self, + theme: &Theme, + rounded: bool, + ) -> Option { + if !self.sync_window_border_radii_to_theme() { + return None; + } + let theme = theme.cosmic(); + let ret = if let AppType::Applet = self.app_type { + let radius_l = theme.radius_l(); + iced_runtime::platform_specific::wayland::CornerRadius { + top_left: radius_l[0].round() as u32, + top_right: radius_l[1].round() as u32, + bottom_right: radius_l[2].round() as u32, + bottom_left: radius_l[3].round() as u32, + } + } else if let AppType::Window = self.app_type + && !rounded + { + let radius_0 = theme.radius_0(); + iced_runtime::platform_specific::wayland::CornerRadius { + top_left: radius_0[0].round() as u32, + top_right: radius_0[1].round() as u32, + bottom_right: radius_0[2].round() as u32, + bottom_left: radius_0[3].round() as u32, + } + } else { + let radius_s = theme.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 }); + iced_runtime::platform_specific::wayland::CornerRadius { + top_left: radius_s[0].round() as u32, + top_right: radius_s[1].round() as u32, + bottom_right: radius_s[2].round() as u32, + bottom_left: radius_s[3].round() as u32, + } + }; + Some(ret) + } } diff --git a/src/widget/cards.rs b/src/widget/cards.rs index 00014740..ce1831d0 100644 --- a/src/widget/cards.rs +++ b/src/widget/cards.rs @@ -2,6 +2,7 @@ use std::time::Duration; use crate::anim; +use crate::theme::THEME; use crate::widget::card::style::Style; use crate::widget::icon::{self, Handle}; use crate::widget::{button, column, row, text}; @@ -19,7 +20,6 @@ const TOP_SPACING: u16 = 4; const VERTICAL_SPACING: f32 = 8.0; const PADDING: u16 = 16; const BG_CARD_VISIBLE_HEIGHT: f32 = 4.0; -const BG_CARD_BORDER_RADIUS: f32 = 8.0; const BG_CARD_MARGIN_STEP: f32 = 8.0; /// get an expandable stack of cards @@ -337,14 +337,15 @@ where for i in 1..self.elements.len().min(3) { // height must be 16px for 8px padding // but we only want 4px visible + let guard = THEME.lock().unwrap(); + let radius_xs = guard.cosmic().radius_xs(); let margin = f32::from(u8::try_from(i).unwrap()) * BG_CARD_MARGIN_STEP; - let node = - Node::new(Size::new(width - 2.0 * margin, BG_CARD_BORDER_RADIUS * 2.0)) - .translate(Vector::new( - margin, - size.height - BG_CARD_BORDER_RADIUS * 2.0 + BG_CARD_VISIBLE_HEIGHT, - )); + let node = Node::new(Size::new(width - 2.0 * margin, radius_xs[0] * 2.0)) + .translate(Vector::new( + margin, + size.height - radius_xs[0] * 2.0 + BG_CARD_VISIBLE_HEIGHT, + )); size.height += BG_CARD_VISIBLE_HEIGHT; children.push(node); } @@ -424,6 +425,7 @@ where _ = tree_children.next(); } + let radius_xs = theme.cosmic().radius_xs(); // Draw first to appear behind if fully_unexpanded { let card_layout = layout.next().unwrap(); @@ -434,12 +436,7 @@ where Quad { bounds: layout.bounds(), border: Border { - radius: Radius::from([ - 0.0, - 0.0, - BG_CARD_BORDER_RADIUS, - BG_CARD_BORDER_RADIUS, - ]), + radius: Radius::from([0.0, 0.0, radius_xs[2], radius_xs[3]]), ..Default::default() }, shadow: Shadow::default(),