Unify shell opacity and add native authentication and system controls

This commit is contained in:
Samuel Onoja committed 2026-09-28 09:26:14 +01:00
1 parent 8832a9bf2e
commit c8e89fb758
35 files changed
+1674 -133

No files matched your search

+19 -32
View File
@@ -190,7 +190,7 @@ pub struct ThemeSettings {
pub focus_ring_gradient: Option<BorderGradient>,
pub shadow_color: RgbaColor,
pub surface_base_color: RgbaColor,
pub panel_opacity: f64,
pub shell_opacity: f64,
pub inactive_dim: InactiveDimSettings,
pub window_radius: f64,
pub shadow_offset_y: f64,
@@ -253,8 +253,6 @@ struct ThemeConfig {
#[serde(default)]
background: crate::wallpaper::WallpaperConfig,
#[serde(default)]
surface: ThemeSurfaceConfig,
#[serde(default)]
colors: ThemeColorsConfig,
#[serde(default)]
geometry: ThemeGeometryConfig,
@@ -331,19 +329,10 @@ struct OverviewTypographyConfig {
font_family: Option<String>,
}
#[derive(Debug, Default, Deserialize)]
struct ThemeSurfaceConfig {
#[serde(default)]
bar: ThemeBarConfig,
}
#[derive(Debug, Default, Deserialize)]
struct ThemeBarConfig {
opacity: Option<f64>,
}
#[derive(Debug, Deserialize)]
struct ThemeMaterialConfig {
#[serde(default = "default_shell_opacity")]
opacity: f64,
#[serde(default)]
style: MaterialStyle,
#[serde(default = "default_backdrop_blur")]
@@ -354,11 +343,16 @@ impl Default for ThemeMaterialConfig {
fn default() -> Self {
Self {
style: MaterialStyle::Solid,
opacity: default_shell_opacity(),
blur_radius: default_backdrop_blur(),
}
}
}
fn default_shell_opacity() -> f64 {
ferese_config::DEFAULT_MATERIAL_OPACITY
}
fn default_backdrop_blur() -> f64 {
12.0
}
@@ -1095,10 +1089,7 @@ impl Config {
&self.theme.colors.surface_base,
"colors.surface_base",
)?,
panel_opacity: unit_theme_value(
self.theme.surface.bar.opacity.unwrap_or(0.78),
"surface.bar.opacity",
)?,
shell_opacity: unit_theme_value(self.theme.material.opacity, "material.opacity")?,
inactive_dim: InactiveDimSettings {
enabled: self.appearance.inactive_dim.enabled,
amount: unit_theme_value(
@@ -2034,7 +2025,7 @@ mod tests {
focus_ring_gradient: None,
shadow_color: RgbaColor([1.0 / 255.0, 2.0 / 255.0, 3.0 / 255.0, 128.0 / 255.0]),
surface_base_color: RgbaColor([17.0 / 255.0, 24.0 / 255.0, 33.0 / 255.0, 1.0,]),
panel_opacity: 0.78,
shell_opacity: 0.78,
inactive_dim: InactiveDimSettings {
enabled: false,
amount: 0.15,
@@ -2125,26 +2116,22 @@ mod tests {
}
#[test]
fn bar_opacity_uses_config_and_rejects_invalid_values() {
assert_eq!(parse("").theme_settings().unwrap().panel_opacity, 0.78);
fn shell_opacity_uses_config_and_rejects_invalid_values() {
assert_eq!(parse("").theme_settings().unwrap().shell_opacity, 0.78);
for opacity in [0.0, 0.65, 1.0] {
assert_eq!(
parse(&format!(
"theme {{ surface {{ bar {{ opacity {opacity}; }} }} }}"
))
.theme_settings()
.unwrap()
.panel_opacity,
parse(&format!("theme {{ material {{ opacity {opacity}; }} }}"))
.theme_settings()
.unwrap()
.shell_opacity,
opacity
);
}
for opacity in ["-0.1", "1.1", "#nan", "#inf"] {
assert!(
Config::parse_source(&format!(
"theme {{ surface {{ bar {{ opacity {opacity}; }} }} }}"
))
.and_then(|config| config.theme_settings())
.is_err()
Config::parse_source(&format!("theme {{ material {{ opacity {opacity}; }} }}"))
.and_then(|config| config.theme_settings())
.is_err()
);
}
}
+11 -9
View File
@@ -37,7 +37,11 @@ pub(crate) struct ResolvedMaterial {
pub shadow: [f64; 3],
}
pub(crate) fn resolve_material(role: SemanticRole, style: MaterialStyle) -> ResolvedMaterial {
pub(crate) fn resolve_material(
role: SemanticRole,
style: MaterialStyle,
opacity: f32,
) -> ResolvedMaterial {
let shadow = match role {
SemanticRole::Panel => [1.0, 5.0, 0.04],
SemanticRole::PanelElevated | SemanticRole::Popover | SemanticRole::Menu => {
@@ -46,11 +50,9 @@ pub(crate) fn resolve_material(role: SemanticRole, style: MaterialStyle) -> Reso
SemanticRole::Hud => [1.0, 4.0, 0.04],
SemanticRole::Notification | SemanticRole::Modal => [3.0, 10.0, 0.09],
};
let opacity = match (style, role) {
(MaterialStyle::Solid, _) => 1.0,
(_, SemanticRole::Panel) => 0.78,
(_, SemanticRole::Hud | SemanticRole::Modal) => 0.88,
_ => 0.84,
let opacity = match style {
MaterialStyle::Solid => 1.0,
MaterialStyle::Translucent => opacity.clamp(0.0, 1.0),
};
ResolvedMaterial {
style,
@@ -430,10 +432,10 @@ mod tests {
SemanticRole::Notification,
SemanticRole::Modal,
] {
let solid = resolve_material(role, MaterialStyle::Solid);
let translucent = resolve_material(role, MaterialStyle::Translucent);
let solid = resolve_material(role, MaterialStyle::Solid, 0.6);
let translucent = resolve_material(role, MaterialStyle::Translucent, 0.6);
assert_eq!(solid.opacity, 1.0);
assert!(translucent.opacity < 1.0);
assert_eq!(translucent.opacity, 0.6);
assert_eq!(solid.shadow, translucent.shadow);
}
}
+3
View File
@@ -71,6 +71,9 @@ pub fn resolve(
) -> WindowRuleResult {
let app_id = app_id.map(normalize_app_id);
let mut result = WindowRuleResult::default();
if app_id.as_deref() == Some("dev.ferese.Authentication") {
result.floating = Some(true);
}
for rule in rules {
let app_id_matches = rule
+11 -12
View File
@@ -1532,7 +1532,11 @@ fn overview_strip_elements(
}
}
let mut color = state.theme_settings.surface_base_color.0;
color[3] = alpha * state.theme_settings.panel_opacity as f32;
color[3] = alpha * crate::effects::resolve_material(
crate::effects::SemanticRole::Panel,
state.theme_settings.material_style,
state.theme_settings.shell_opacity as f32,
).opacity;
if let Some(strip) = overview_chrome_element(
state,
renderer,
@@ -2306,13 +2310,12 @@ fn material_element(
capture_geometry: Rectangle<i32, Logical>,
) -> Option<(AnimatedWindowRenderElement, AnimatedWindowRenderElement)> {
let (role, generation) = crate::effects::surface_role(surface)?;
let mut material = crate::effects::resolve_material(role, state.theme_settings.material_style);
let material = crate::effects::resolve_material(
role,
state.theme_settings.material_style,
state.theme_settings.shell_opacity as f32,
);
let presentation_alpha = crate::effects::surface_opacity(surface);
if role == crate::effects::SemanticRole::Panel
&& material.style == crate::config::MaterialStyle::Translucent
{
material.opacity = state.theme_settings.panel_opacity as f32;
}
let mode = output.current_mode()?;
let scale = output.current_scale().fractional_scale();
let transform = output.current_transform().invert();
@@ -2330,11 +2333,7 @@ fn material_element(
(geometry.loc.x, geometry.loc.y + offset_y.round() as i32).into(),
geometry.size,
);
let background_opacity = if role == crate::effects::SemanticRole::Panel {
material.opacity
} else {
1.0
};
let background_opacity = material.opacity;
let blur = material_blur_radius(
material.style,
material.opacity,