Split shell, settings, and compositor into focused modules
This commit is contained in:
1 parent
2b9f30fa5d
commit
cbf048566a
38 files changed
+9998
-9817
No files matched your search
@@ -0,0 +1,414 @@
|
||||
use super::*;
|
||||
|
||||
impl Config {
|
||||
pub fn theme_settings(&self) -> Result<ThemeSettings, ConfigError> {
|
||||
let border_width = nonnegative_theme_value(self.theme.geometry.border_width, "geometry.border_width")?;
|
||||
let focus_ring_width =
|
||||
nonnegative_theme_value(self.theme.geometry.focus_ring_width, "geometry.focus_ring_width")?;
|
||||
let window_radius = nonnegative_theme_value(self.theme.geometry.window_radius, "geometry.window_radius")?;
|
||||
let shadow_offset_y = finite_theme_value(self.theme.shadow.soft.offset_y, "shadow.soft.offset_y")?;
|
||||
let shadow_blur = nonnegative_theme_value(self.theme.shadow.soft.blur, "shadow.soft.blur")?;
|
||||
let shadow_opacity = unit_theme_value(self.theme.shadow.soft.opacity, "shadow.soft.opacity")?;
|
||||
|
||||
let material_radius = nonnegative_theme_value(
|
||||
self.theme
|
||||
.geometry
|
||||
.shell_radius
|
||||
.or(self.appearance.corner_radius)
|
||||
.or(self.theme.geometry.top_bar_radius)
|
||||
.unwrap_or(14.0),
|
||||
"geometry.shell_radius",
|
||||
)?;
|
||||
Ok(ThemeSettings {
|
||||
border_width,
|
||||
focus_ring_width,
|
||||
border_color: parse_color(&self.theme.colors.border, "colors.border")?,
|
||||
text_primary_color: parse_color(&self.theme.colors.text_primary, "colors.text_primary")?,
|
||||
accent_color: parse_color(&self.theme.colors.accent, "colors.accent")?,
|
||||
border_gradient: self.theme.border.settings("border")?,
|
||||
focus_ring_gradient: self.theme.focus_ring.settings("focus_ring")?,
|
||||
shadow_color: parse_color(&self.theme.colors.shadow, "colors.shadow")?,
|
||||
surface_base_color: parse_color(&self.theme.colors.surface_base, "colors.surface_base")?,
|
||||
bar_background_color: parse_color(
|
||||
self.theme
|
||||
.surface
|
||||
.bar
|
||||
.background
|
||||
.as_deref()
|
||||
.unwrap_or(&self.theme.colors.surface_base),
|
||||
"surface.bar.background",
|
||||
)?,
|
||||
shell_opacity: unit_theme_value(self.theme.material.opacity, "material.opacity")?,
|
||||
inactive_dim: InactiveDimSettings {
|
||||
enabled: self.appearance.inactive_dim.enabled,
|
||||
amount: unit_theme_value(self.appearance.inactive_dim.amount, "appearance.inactive_dim.amount")?,
|
||||
duration_ms: nonnegative_theme_value(
|
||||
self.appearance.inactive_dim.duration_ms,
|
||||
"appearance.inactive_dim.duration_ms",
|
||||
)?,
|
||||
},
|
||||
window_radius,
|
||||
shadow_offset_y,
|
||||
shadow_blur,
|
||||
shadow_opacity,
|
||||
material_style: self.theme.material.style,
|
||||
backdrop_blur: nonnegative_theme_value(self.theme.material.blur_radius, "material.blur_radius")?.min(32.0),
|
||||
material_tint_strength: unit_theme_value(self.theme.material.tint_strength, "material.tint_strength")?,
|
||||
material_radius,
|
||||
panel_radius: material_radius,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn nonnegative_theme_value(value: f64, field: &'static str) -> Result<f64, ConfigError> {
|
||||
if value.is_finite() && value >= 0.0 {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(ConfigError::InvalidThemeValue {
|
||||
field,
|
||||
value: value.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn finite_theme_value(value: f64, field: &'static str) -> Result<f64, ConfigError> {
|
||||
if value.is_finite() {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(ConfigError::InvalidThemeValue {
|
||||
field,
|
||||
value: value.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn unit_theme_value(value: f64, field: &'static str) -> Result<f64, ConfigError> {
|
||||
if value.is_finite() && (0.0..=1.0).contains(&value) {
|
||||
Ok(value)
|
||||
} else {
|
||||
Err(ConfigError::InvalidThemeValue {
|
||||
field,
|
||||
value: value.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse_color(value: &str, field: &'static str) -> Result<RgbaColor, ConfigError> {
|
||||
let digits = value.strip_prefix('#').unwrap_or(value);
|
||||
if !digits.is_ascii() || !matches!(digits.len(), 6 | 8) {
|
||||
return Err(ConfigError::InvalidThemeValue {
|
||||
field,
|
||||
value: value.to_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
let parse_channel = |offset| u8::from_str_radix(&digits[offset..offset + 2], 16).ok();
|
||||
let Some((red, green, blue)) = parse_channel(0)
|
||||
.zip(parse_channel(2))
|
||||
.zip(parse_channel(4))
|
||||
.map(|((red, green), blue)| (red, green, blue))
|
||||
else {
|
||||
return Err(ConfigError::InvalidThemeValue {
|
||||
field,
|
||||
value: value.to_owned(),
|
||||
});
|
||||
};
|
||||
let alpha = if digits.len() == 8 {
|
||||
parse_channel(6).ok_or_else(|| ConfigError::InvalidThemeValue {
|
||||
field,
|
||||
value: value.to_owned(),
|
||||
})?
|
||||
} else {
|
||||
u8::MAX
|
||||
};
|
||||
|
||||
Ok(RgbaColor([
|
||||
f32::from(red) / 255.0,
|
||||
f32::from(green) / 255.0,
|
||||
f32::from(blue) / 255.0,
|
||||
f32::from(alpha) / 255.0,
|
||||
]))
|
||||
}
|
||||
|
||||
pub(super) fn default_border_color() -> String {
|
||||
"#FFFFFF18".to_owned()
|
||||
}
|
||||
|
||||
pub(super) fn default_text_primary_color() -> String {
|
||||
"#F4F7FB".to_owned()
|
||||
}
|
||||
|
||||
pub(super) fn default_surface_base_color() -> String {
|
||||
"#111821".to_owned()
|
||||
}
|
||||
|
||||
pub(super) fn default_accent_color() -> String {
|
||||
"#3D7BE6".to_owned()
|
||||
}
|
||||
|
||||
pub(super) fn default_shadow_color() -> String {
|
||||
"#00000055".to_owned()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct RgbaColor(pub [f32; 4]);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct BorderGradient {
|
||||
pub from: RgbaColor,
|
||||
pub to: RgbaColor,
|
||||
pub angle: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub(super) struct BorderPaintConfig {
|
||||
pub(super) gradient: Option<BorderGradientConfig>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(super) struct BorderGradientConfig {
|
||||
pub(super) from: String,
|
||||
pub(super) to: String,
|
||||
#[serde(default)]
|
||||
pub(super) angle: f64,
|
||||
}
|
||||
|
||||
impl BorderPaintConfig {
|
||||
fn settings(&self, name: &str) -> Result<Option<BorderGradient>, ConfigError> {
|
||||
let (from_name, to_name, angle_name) = if name == "focus_ring" {
|
||||
(
|
||||
"focus_ring.gradient.from",
|
||||
"focus_ring.gradient.to",
|
||||
"focus_ring.gradient.angle",
|
||||
)
|
||||
} else {
|
||||
("border.gradient.from", "border.gradient.to", "border.gradient.angle")
|
||||
};
|
||||
self.gradient
|
||||
.as_ref()
|
||||
.map(|gradient| {
|
||||
Ok(BorderGradient {
|
||||
from: parse_color(&gradient.from, from_name)?,
|
||||
to: parse_color(&gradient.to, to_name)?,
|
||||
angle: finite_theme_value(gradient.angle, angle_name)?.rem_euclid(360.0),
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct ThemeSettings {
|
||||
pub border_width: f64,
|
||||
pub focus_ring_width: f64,
|
||||
pub border_color: RgbaColor,
|
||||
pub accent_color: RgbaColor,
|
||||
pub border_gradient: Option<BorderGradient>,
|
||||
pub focus_ring_gradient: Option<BorderGradient>,
|
||||
pub shadow_color: RgbaColor,
|
||||
pub surface_base_color: RgbaColor,
|
||||
pub bar_background_color: RgbaColor,
|
||||
pub text_primary_color: RgbaColor,
|
||||
pub shell_opacity: f64,
|
||||
pub inactive_dim: InactiveDimSettings,
|
||||
pub window_radius: f64,
|
||||
pub shadow_offset_y: f64,
|
||||
pub shadow_blur: f64,
|
||||
pub shadow_opacity: f64,
|
||||
pub material_style: MaterialStyle,
|
||||
pub backdrop_blur: f64,
|
||||
pub material_tint_strength: f64,
|
||||
pub material_radius: f64,
|
||||
pub panel_radius: f64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum MaterialStyle {
|
||||
Translucent,
|
||||
#[default]
|
||||
Solid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub(super) struct AppearanceConfig {
|
||||
pub(super) corner_radius: Option<f64>,
|
||||
#[serde(default)]
|
||||
pub(super) inactive_dim: InactiveDimConfig,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct InactiveDimSettings {
|
||||
pub enabled: bool,
|
||||
pub amount: f64,
|
||||
pub duration_ms: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub(super) struct InactiveDimConfig {
|
||||
pub(super) enabled: bool,
|
||||
pub(super) amount: f64,
|
||||
pub(super) duration_ms: f64,
|
||||
}
|
||||
|
||||
impl Default for InactiveDimConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
amount: 0.15,
|
||||
duration_ms: 150.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub(super) struct ThemeConfig {
|
||||
#[serde(default)]
|
||||
pub(super) border: BorderPaintConfig,
|
||||
#[serde(default)]
|
||||
pub(super) focus_ring: BorderPaintConfig,
|
||||
#[serde(default)]
|
||||
pub(super) typography: OverviewTypographyConfig,
|
||||
#[serde(default)]
|
||||
pub(super) background: crate::wallpaper::WallpaperConfig,
|
||||
#[serde(default)]
|
||||
pub(super) colors: ThemeColorsConfig,
|
||||
#[serde(default)]
|
||||
pub(super) geometry: ThemeGeometryConfig,
|
||||
#[serde(default)]
|
||||
pub(super) shadow: ThemeShadowConfig,
|
||||
#[serde(default)]
|
||||
pub(super) material: ThemeMaterialConfig,
|
||||
#[serde(default)]
|
||||
pub(super) surface: ThemeSurfaceConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub(super) struct ThemeSurfaceConfig {
|
||||
#[serde(default)]
|
||||
pub(super) bar: ThemeBarConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub(super) struct ThemeBarConfig {
|
||||
pub(super) background: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub(super) struct OverviewTypographyConfig {
|
||||
pub(super) font_family: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(super) struct ThemeMaterialConfig {
|
||||
#[serde(default = "default_shell_opacity")]
|
||||
pub(super) opacity: f64,
|
||||
#[serde(default)]
|
||||
pub(super) style: MaterialStyle,
|
||||
#[serde(default = "default_backdrop_blur")]
|
||||
pub(super) blur_radius: f64,
|
||||
#[serde(default = "default_material_tint_strength")]
|
||||
pub(super) tint_strength: f64,
|
||||
}
|
||||
|
||||
impl Default for ThemeMaterialConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
style: MaterialStyle::Solid,
|
||||
opacity: default_shell_opacity(),
|
||||
blur_radius: default_backdrop_blur(),
|
||||
tint_strength: default_material_tint_strength(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn default_shell_opacity() -> f64 {
|
||||
ferese_config::DEFAULT_MATERIAL_OPACITY
|
||||
}
|
||||
|
||||
fn default_material_tint_strength() -> f64 {
|
||||
0.5
|
||||
}
|
||||
|
||||
fn default_backdrop_blur() -> f64 {
|
||||
12.0
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(super) struct ThemeColorsConfig {
|
||||
#[serde(default = "default_text_primary_color")]
|
||||
pub(super) text_primary: String,
|
||||
#[serde(default = "default_surface_base_color")]
|
||||
pub(super) surface_base: String,
|
||||
#[serde(default = "default_border_color")]
|
||||
pub(super) border: String,
|
||||
#[serde(default = "default_accent_color")]
|
||||
pub(super) accent: String,
|
||||
#[serde(default = "default_shadow_color")]
|
||||
pub(super) shadow: String,
|
||||
}
|
||||
|
||||
impl Default for ThemeColorsConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
surface_base: default_surface_base_color(),
|
||||
text_primary: default_text_primary_color(),
|
||||
border: default_border_color(),
|
||||
accent: default_accent_color(),
|
||||
shadow: default_shadow_color(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub(super) struct ThemeShadowConfig {
|
||||
#[serde(default)]
|
||||
pub(super) soft: SoftShadowConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(super) struct SoftShadowConfig {
|
||||
#[serde(default = "default_shadow_offset_y")]
|
||||
pub(super) offset_y: f64,
|
||||
#[serde(default = "default_shadow_blur")]
|
||||
pub(super) blur: f64,
|
||||
#[serde(default = "default_shadow_opacity")]
|
||||
pub(super) opacity: f64,
|
||||
}
|
||||
|
||||
impl Default for SoftShadowConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
offset_y: default_shadow_offset_y(),
|
||||
blur: default_shadow_blur(),
|
||||
opacity: default_shadow_opacity(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(super) struct ThemeGeometryConfig {
|
||||
#[serde(default = "default_border_width")]
|
||||
pub(super) border_width: f64,
|
||||
#[serde(default = "default_focus_ring_width")]
|
||||
pub(super) focus_ring_width: f64,
|
||||
#[serde(default = "default_window_radius")]
|
||||
pub(super) window_radius: f64,
|
||||
#[serde(default)]
|
||||
pub(super) top_bar_radius: Option<f64>,
|
||||
#[serde(default)]
|
||||
pub(super) shell_radius: Option<f64>,
|
||||
}
|
||||
|
||||
impl Default for ThemeGeometryConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
border_width: default_border_width(),
|
||||
focus_ring_width: default_focus_ring_width(),
|
||||
window_radius: default_window_radius(),
|
||||
top_bar_radius: None,
|
||||
shell_radius: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user