diff --git a/cosmic-theme/src/model/theme.rs b/cosmic-theme/src/model/theme.rs index 53ea95b7..56654480 100644 --- a/cosmic-theme/src/model/theme.rs +++ b/cosmic-theme/src/model/theme.rs @@ -90,7 +90,8 @@ pub struct Theme { /// cosmic-comp custom window hint color pub window_hint: Option, /// enables blurred transparency - pub is_frosted: bool, + /// If None, frosted effect is disabled. + pub frosted: Option, /// shade color for dialogs #[serde(with = "color_serde")] #[cosmic_config_entry(with = ColorRepr)] @@ -814,7 +815,7 @@ pub struct ThemeBuilder { #[cosmic_config_entry(with = ColorReprOption)] pub destructive: Option, /// enabled blurred transparency - pub is_frosted: bool, // TODO handle + pub frosted: Option, /// cosmic-comp window gaps size (outer, inner) pub gaps: (u32, u32), /// cosmic-comp active hint window outline width @@ -840,7 +841,7 @@ impl Default for ThemeBuilder { success: Default::default(), warning: Default::default(), destructive: Default::default(), - is_frosted: false, + frosted: None, // cosmic-comp theme settings gaps: (0, 8), active_hint: 3, @@ -986,9 +987,11 @@ impl ThemeBuilder { gaps, active_hint, window_hint, - is_frosted, + frosted, } = self; + let container_alpha = frosted.map_or(1.0, |f| f.alpha()); + let is_dark = palette.is_dark(); let is_high_contrast = palette.is_high_contrast(); @@ -1034,12 +1037,14 @@ impl ThemeBuilder { NonZeroUsize::new(100).unwrap(), ); - let bg = if let Some(bg_color) = bg_color { + let mut bg = if let Some(bg_color) = bg_color { bg_color } else { p_ref.gray_1 }; + bg.alpha = container_alpha; + let step_array = steps(bg, NonZeroUsize::new(100).unwrap()); let bg_index = color_index(bg, step_array.len()); @@ -1070,11 +1075,12 @@ impl ThemeBuilder { ); let primary = { - let container_bg = if let Some(primary_container_bg_color) = primary_container_bg { + let mut container_bg = if let Some(primary_container_bg_color) = primary_container_bg { primary_container_bg_color } else { get_surface_color(bg_index, 5, &step_array, is_dark, &control_steps_array[1]) }; + container_bg.alpha = container_alpha; let step_array = steps(container_bg, NonZeroUsize::new(100).unwrap()); let base_index: usize = color_index(container_bg, step_array.len()); @@ -1191,11 +1197,13 @@ impl ThemeBuilder { ), primary, secondary: { - let container_bg = if let Some(secondary_container_bg) = secondary_container_bg { + let mut container_bg = if let Some(secondary_container_bg) = secondary_container_bg + { secondary_container_bg } else { get_surface_color(bg_index, 10, &step_array, is_dark, &control_steps_array[2]) }; + container_bg.alpha = container_alpha; let step_array = steps(container_bg, NonZeroUsize::new(100).unwrap()); let base_index = color_index(container_bg, step_array.len()); @@ -1347,7 +1355,7 @@ impl ThemeBuilder { gaps, active_hint, window_hint, - is_frosted, + frosted, accent_text, control_tint: neutral_tint, text_tint, @@ -1369,3 +1377,69 @@ impl ThemeBuilder { Config::new(LIGHT_THEME_BUILDER_ID, Self::VERSION) } } + +#[repr(u8)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum BlurStrength { + ExtremelyLow = 1, + ExtremelyLow2, + VeryLow, + VeryLow2, + Low, + Low2, + Medium, + Medium2, + High, + High2, + VeryHigh, + VeryHigh2, + ExtremelyHigh, + ExtremelyHigh2, +} + +impl BlurStrength { + /// Get the alpha value corresponding to the blur strength + /// Lower alpha values correspond to stronger blur effects, and higher alpha values correspond to weaker blur effects. The mapping is as follows: + pub fn alpha(&self) -> f32 { + match self { + Self::ExtremelyLow => 0.95, + Self::ExtremelyLow2 => 0.85, + Self::VeryLow => 0.8, + Self::VeryLow2 => 0.75, + Self::Low => 0.7, + Self::Low2 => 0.65, + Self::Medium => 0.6, + Self::Medium2 => 0.55, + Self::High => 0.5, + Self::High2 => 0.45, + Self::VeryHigh => 0.4, + Self::VeryHigh2 => 0.35, + Self::ExtremelyHigh => 0.2, + Self::ExtremelyHigh2 => 0.05, + } + } +} + +impl TryFrom for BlurStrength { + type Error = (); + + fn try_from(value: u8) -> Result { + match value { + 1 => Ok(BlurStrength::ExtremelyLow), + 2 => Ok(BlurStrength::ExtremelyLow2), + 3 => Ok(BlurStrength::VeryLow), + 4 => Ok(BlurStrength::VeryLow2), + 5 => Ok(BlurStrength::Low), + 6 => Ok(BlurStrength::Low2), + 7 => Ok(BlurStrength::Medium), + 8 => Ok(BlurStrength::Medium2), + 9 => Ok(BlurStrength::High), + 10 => Ok(BlurStrength::High2), + 11 => Ok(BlurStrength::VeryHigh), + 12 => Ok(BlurStrength::VeryHigh2), + 13 => Ok(BlurStrength::ExtremelyHigh), + 14 => Ok(BlurStrength::ExtremelyHigh2), + _ => Err(()), + } + } +} diff --git a/iced b/iced index 7fd263d9..46b65a3c 160000 --- a/iced +++ b/iced @@ -1 +1 @@ -Subproject commit 7fd263d99e6ae1b07e51f25bda3367f7463806b1 +Subproject commit 46b65a3c3e2fb6f1627d117ca061743417806aaf diff --git a/src/app/cosmic.rs b/src/app/cosmic.rs index 030ed041..6b7b04ba 100644 --- a/src/app/cosmic.rs +++ b/src/app/cosmic.rs @@ -773,10 +773,31 @@ impl Cosmic { if a.distance_squared(*t_inner.accent_color()) > 0.00001 { theme = Theme::system(Arc::new(t_inner.with_accent(a))); } - }; + } } + let new_blur = theme.cosmic().frosted.is_some(); THEME.lock().unwrap().set_theme(theme.theme_type); + + let core = self.app.core(); + if core.auto_blur { + let mut cmds = Vec::with_capacity(1 + self.tracked_windows.len()); + let blur = if new_blur { + iced::window::enable_blur + } else { + iced::window::disable_blur + }; + cmds.push(blur( + self.app + .core() + .main_window_id() + .unwrap_or(window::Id::RESERVED), + )); + for id in &self.tracked_windows { + cmds.push(blur(*id)); + } + return Task::batch(cmds); + } } Action::SystemThemeChange(keys, theme) => { @@ -809,6 +830,8 @@ impl Cosmic { theme }; new_theme.theme_type.prefer_dark(prefer_dark); + // TODO adjust theme container alphas to remove transparency? + // if auto-blur is disabled & theme is frosted, should we make container colors in theme opaque? cosmic_theme.set_theme(new_theme.theme_type); #[cfg(all(feature = "wayland", target_os = "linux"))] @@ -873,7 +896,7 @@ impl Cosmic { } } // Update radius for all tracked windows - for id in self.tracked_windows.iter() { + for id in &self.tracked_windows { cmds.push( corner_radius( *id, @@ -956,6 +979,9 @@ impl Cosmic { // Only apply update if the theme is set to load a system theme if let ThemeType::System { .. } = cosmic_theme.theme_type { + // TODO adjust theme container alphas to remove transparency? + // if auto-blur is disabled & theme is frosted, should we make container colors in theme opaque? + let new_blur = new_theme.cosmic().frosted.is_some(); cosmic_theme.set_theme(new_theme.theme_type); #[cfg(all(feature = "wayland", target_os = "linux"))] if self.app.core().sync_window_border_radii_to_theme() { @@ -1019,7 +1045,7 @@ impl Cosmic { } } // Update radius for all tracked windows - for id in self.tracked_windows.iter() { + for id in &self.tracked_windows { cmds.push( corner_radius( *id, @@ -1039,6 +1065,25 @@ impl Cosmic { ); } + let core = self.app.core(); + if core.auto_blur { + let blur = if new_blur { + iced::window::enable_blur + } else { + iced::window::disable_blur + }; + + cmds.push(blur( + self.app + .core() + .main_window_id() + .unwrap_or(window::Id::RESERVED), + )); + for id in &self.tracked_windows { + cmds.push(blur(*id)); + } + } + return Task::batch(cmds); } } @@ -1147,7 +1192,26 @@ impl Cosmic { // Only apply update if the theme is set to load a system theme if let ThemeType::System { theme: _, .. } = cosmic_theme.theme_type { + let mut cmds = Vec::with_capacity(1 + self.tracked_windows.len()); + + if core.auto_blur { + let blur = if new_theme.cosmic().frosted.is_some() { + iced::window::enable_blur + } else { + iced::window::disable_blur + }; + cmds.push(blur( + self.app + .core() + .main_window_id() + .unwrap_or(window::Id::RESERVED), + )); + for id in &self.tracked_windows { + cmds.push(blur(*id)); + } + } cosmic_theme.set_theme(new_theme.theme_type); + return Task::batch(cmds); } } } @@ -1263,8 +1327,24 @@ impl Cosmic { }; // TODO do we need per window sharp corners? let rounded = !self.app.core().window.sharp_corners; - + let core = self.app.core(); + let blur_cmd = if core.auto_blur { + let blur = if t.frosted.is_some() { + iced::window::enable_blur + } else { + iced::window::disable_blur + }; + let mut cmds = Vec::with_capacity(1 + self.tracked_windows.len()); + cmds.push(blur(id)); + for id in &self.tracked_windows { + cmds.push(blur(*id)); + } + Task::batch(cmds) + } else { + Task::none() + }; return Task::batch([ + blur_cmd, corner_radius( id, if rounded { diff --git a/src/core.rs b/src/core.rs index 970a5351..2ea0890b 100644 --- a/src/core.rs +++ b/src/core.rs @@ -101,6 +101,8 @@ pub struct Core { #[cfg(all(feature = "wayland", target_os = "linux"))] pub(crate) sync_window_border_radii_to_theme: bool, + + pub(crate) auto_blur: bool, } impl Default for Core { @@ -161,6 +163,7 @@ impl Default for Core { menu_bars: HashMap::new(), #[cfg(all(feature = "wayland", target_os = "linux"))] sync_window_border_radii_to_theme: true, + auto_blur: true, } } } @@ -502,4 +505,8 @@ impl Core { pub fn sync_window_border_radii_to_theme(&self) -> bool { self.sync_window_border_radii_to_theme } + + pub fn set_auto_blur(&mut self, auto_blur: bool) { + self.auto_blur = auto_blur; + } } diff --git a/src/theme/style/iced.rs b/src/theme/style/iced.rs index 4633477d..3a57408a 100644 --- a/src/theme/style/iced.rs +++ b/src/theme/style/iced.rs @@ -565,6 +565,9 @@ impl iced_container::Catalog for Theme { Container::ContextDrawer => { let mut a = Container::primary(cosmic); + if let Some(Background::Color(ref mut color)) = a.background { + color.a = 1.; + } if cosmic.is_high_contrast { a.border.width = 1.;