use egui::Color32;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ThemeMode {
Light,
Dark,
FollowSystem,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum EffectsPolicy {
Full,
Reduced,
None,
}
impl EffectsPolicy {
pub fn allows_transparency(self) -> bool {
matches!(self, EffectsPolicy::Full | EffectsPolicy::Reduced)
}
pub fn allows_blur(self) -> bool {
matches!(self, EffectsPolicy::Full)
}
pub fn allows_decorative_motion(self) -> bool {
matches!(self, EffectsPolicy::Full)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct FocusSpec {
pub hints_enabled: bool,
pub hint_alphabet: [char; 8],
pub revolver_enabled: bool,
pub spatial_nav: bool,
}
impl Default for FocusSpec {
fn default() -> Self {
Self {
hints_enabled: false,
hint_alphabet: ['a', 's', 'd', 'f', 'j', 'k', 'l', ';'],
revolver_enabled: false,
spatial_nav: true,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
pub enum SurfaceSpec {
#[default]
Opaque,
Opacity(f32),
Tint([u8; 4]),
Frosted { blur_radius: f32, tint: [u8; 4] },
}
impl SurfaceSpec {
pub fn resolve(self, policy: EffectsPolicy) -> SurfaceSpec {
match self {
SurfaceSpec::Opaque => SurfaceSpec::Opaque,
_ if !policy.allows_transparency() => SurfaceSpec::Opaque,
SurfaceSpec::Frosted { tint, .. } if !policy.allows_blur() => SurfaceSpec::Tint(tint),
other => other,
}
}
pub fn tint_color(self) -> Option<Color32> {
match self {
SurfaceSpec::Tint(c) | SurfaceSpec::Frosted { tint: c, .. } => {
Some(Color32::from_rgba_unmultiplied(c[0], c[1], c[2], c[3]))
}
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Motion {
pub duration: f32,
pub fast: f32,
}
impl Default for Motion {
fn default() -> Self {
Self { duration: 0.18, fast: 0.10 }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct PerfConfig {
pub frame_budget_ms: f32,
pub overscan_rows: usize,
pub prefer_wgpu: bool,
}
impl Default for PerfConfig {
fn default() -> Self {
Self { frame_budget_ms: 16.7, overscan_rows: 2, prefer_wgpu: true }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn effects_none_disables_everything_decorative() {
let p = EffectsPolicy::None;
assert!(!p.allows_transparency());
assert!(!p.allows_blur());
assert!(!p.allows_decorative_motion());
}
#[test]
fn frosted_degrades_to_tint_without_blur() {
let frosted = SurfaceSpec::Frosted { blur_radius: 8.0, tint: [10, 12, 26, 180] };
assert_eq!(frosted.resolve(EffectsPolicy::Reduced), SurfaceSpec::Tint([10, 12, 26, 180]));
assert_eq!(frosted.resolve(EffectsPolicy::None), SurfaceSpec::Opaque);
assert_eq!(frosted.resolve(EffectsPolicy::Full), frosted);
}
#[test]
fn tint_forced_opaque_under_none() {
let tint = SurfaceSpec::Tint([1, 2, 3, 200]);
assert_eq!(tint.resolve(EffectsPolicy::None), SurfaceSpec::Opaque);
assert_eq!(tint.resolve(EffectsPolicy::Full), tint);
}
}