diff --git a/compositor/src/winit.rs b/compositor/src/winit.rs index c5d17d7..a92cd17 100644 --- a/compositor/src/winit.rs +++ b/compositor/src/winit.rs @@ -372,13 +372,14 @@ uniform float material_radius; uniform vec2 texture_size; uniform float blur_radius; uniform float presentation_alpha; +uniform float background_opacity; uniform vec4 tint; varying vec2 v_coords; void main() { vec2 half_size = visible_rect.zw * 0.5; vec2 d = abs(gl_FragCoord.xy - visible_rect.xy - half_size) - (half_size - vec2(material_radius)); float sdf = length(max(d, 0.0)) + min(max(d.x, d.y), 0.0) - material_radius; - float coverage = alpha * presentation_alpha * (1.0 - smoothstep(-0.5, 0.5, sdf)); + float coverage = alpha * presentation_alpha * background_opacity * (1.0 - smoothstep(-0.5, 0.5, sdf)); if (coverage <= 0.0) { gl_FragColor = vec4(0.0); return; } // Denser Gaussian sampling preserves the same radius and softness without // the visible grid left by widely spaced taps on detailed wallpapers. @@ -578,6 +579,7 @@ pub(crate) struct MaterialProgram(GlesPixelProgram); #[derive(Clone, Debug, PartialEq)] struct MaterialParameters { presentation_alpha: f32, + background_opacity: f32, blur: f32, scene_generation: u64, sample_geometry: Rectangle, @@ -2328,11 +2330,16 @@ fn material_element( (geometry.loc.x, geometry.loc.y + offset_y.round() as i32).into(), geometry.size, ); - let blur = if material.style == crate::config::MaterialStyle::Translucent { - state.theme_settings.backdrop_blur + let background_opacity = if role == crate::effects::SemanticRole::Panel { + material.opacity } else { - 0.0 + 1.0 }; + let blur = material_blur_radius( + material.style, + material.opacity, + state.theme_settings.backdrop_blur, + ); let output_size = output .current_transform() .transform_size(mode.size) @@ -2343,6 +2350,7 @@ fn material_element( let sample_physical = sample_geometry.to_physical_precise_round(scale); let parameters = MaterialParameters { presentation_alpha, + background_opacity, blur: (blur * scale) as f32, scene_generation: if blur > 0.0 { state.backdrop_generation @@ -2485,6 +2493,14 @@ fn material_element( Some((background, cached.shadow.clone().into())) } +fn material_blur_radius(style: crate::config::MaterialStyle, opacity: f32, radius: f64) -> f64 { + if style == crate::config::MaterialStyle::Translucent && opacity > 0.0 && opacity < 1.0 { + radius + } else { + 0.0 + } +} + fn blur_program(state: &mut Ferese, renderer: &mut GlesRenderer) -> Option { let context = renderer.context_id().erased(); if let Some(program) = state.blur_programs.get(&context) { @@ -2496,6 +2512,7 @@ fn blur_program(state: &mut Ferese, renderer: &mut GlesRenderer) -> Option Vec> { .into_owned(), Uniform::new("blur_radius", p.blur).into_owned(), Uniform::new("presentation_alpha", p.presentation_alpha).into_owned(), + Uniform::new("background_opacity", p.background_opacity).into_owned(), Uniform::new("tint", p.tint).into_owned(), ] } @@ -2975,6 +2993,27 @@ mod tests { assert!(removal_damage.iter().any(|rect| rect.contains((300, 180)))); } + #[test] + fn fully_transparent_and_opaque_materials_skip_blur() { + use crate::config::MaterialStyle; + assert_eq!( + super::material_blur_radius(MaterialStyle::Translucent, 0.0, 18.0), + 0.0 + ); + assert_eq!( + super::material_blur_radius(MaterialStyle::Translucent, 1.0, 18.0), + 0.0 + ); + assert_eq!( + super::material_blur_radius(MaterialStyle::Translucent, 0.79, 18.0), + 18.0 + ); + assert_eq!( + super::material_blur_radius(MaterialStyle::Solid, 0.79, 18.0), + 0.0 + ); + } + #[test] fn close_transform_scales_about_the_window_center() { let rect = ferese_layout::Rect::new(100.0, 50.0, 400.0, 300.0); diff --git a/docs/configuration.md b/docs/configuration.md index f45372b..4a79ca5 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -162,7 +162,7 @@ these six visually distinct. | Section / key | Type | Default | Meaning | | --- | --- | --- | --- | | `theme.surface.bar.background` | color | `"#1C202EF2"` | Shell fallback background; compositor materials use `surface_base` | -| `theme.surface.bar.opacity` | number 0–1 | omitted | Material opacity defaults to `0.78`; fallback retains background alpha | +| `theme.surface.bar.opacity` | number 0–1 | omitted | Translucent bar background opacity, including blur; defaults to `0.78`. Text/icons stay opaque; 0 and 1 skip blur. Fallback retains background alpha | | `theme.surface.bar.text_primary` | color | `"#F0F3FA"` | Bar text/icons | | `theme.surface.bar.text_muted` | color | `"#AAB4C7"` | Inactive bar foreground | | `theme.surface.popover.opacity` | number 0–1 | `0.96` | Shell popover fill alpha | diff --git a/shell/ferese-settings/src/schema.rs b/shell/ferese-settings/src/schema.rs index da7d1a8..009198c 100644 --- a/shell/ferese-settings/src/schema.rs +++ b/shell/ferese-settings/src/schema.rs @@ -730,9 +730,9 @@ pub fn fields(page: Page) -> Vec { range( "theme.surface.bar.opacity", "Opacity", - "Lower values reveal more of the background.", + "Background opacity for translucent bars, with or without blur. Text and icons stay opaque.", 0.78, - 0.2, + 0.0, 1.0, 0.01, "",