libcosmic-yoda/cosmic-theme/src/model/derivation.rs
2025-07-21 10:52:22 -04:00

203 lines
5.5 KiB
Rust

use palette::{Srgba, WithAlpha};
use serde::{Deserialize, Serialize};
use crate::composite::over;
/// Theme Container colors of a theme, can be a theme background container, primary container, or secondary container
#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq)]
#[must_use]
pub struct Container {
/// the color of the container
pub base: Srgba,
/// the color of components in the container
pub component: Component,
/// the color of dividers in the container
pub divider: Srgba,
/// the color of text in the container
pub on: Srgba,
/// the color of @small_widget_container
pub small_widget: Srgba,
}
impl Container {
pub(crate) fn new(
component: Component,
base: Srgba,
on: Srgba,
mut small_widget: Srgba,
is_high_contrast: bool,
) -> Self {
let divider_c = on.with_alpha(if is_high_contrast { 0.5 } else { 0.2 });
small_widget.alpha = 0.25;
Self {
base,
component,
divider: over(divider_c, base),
on,
small_widget,
}
}
}
/// The colors for a widget of the Cosmic theme
#[derive(Clone, PartialEq, Debug, Default, Deserialize, Serialize)]
#[must_use]
pub struct Component {
/// The base color of the widget
pub base: Srgba,
/// The color of the widget when it is hovered
pub hover: Srgba,
/// the color of the widget when it is pressed
pub pressed: Srgba,
/// the color of the widget when it is selected
pub selected: Srgba,
/// the color of the widget when it is selected
pub selected_text: Srgba,
/// the color of the widget when it is focused
pub focus: Srgba,
/// the color of dividers for this widget
pub divider: Srgba,
/// the color of text for this widget
pub on: Srgba,
// the color of text with opacity 80 for this widget
// pub text_opacity_80: Srgba,
/// the color of the widget when it is disabled
pub disabled: Srgba,
/// the color of text in the widget when it is disabled
pub on_disabled: Srgba,
/// the color of the border for the widget
pub border: Srgba,
/// the color of the border for the widget when it is disabled
pub disabled_border: Srgba,
}
#[allow(clippy::must_use_candidate)]
#[allow(clippy::doc_markdown)]
impl Component {
#[inline]
/// get @hover_state_color
pub fn hover_state_color(&self) -> Srgba {
self.hover
}
#[inline]
/// get @pressed_state_color
pub fn pressed_state_color(&self) -> Srgba {
self.pressed
}
#[inline]
/// get @selected_state_color
pub fn selected_state_color(&self) -> Srgba {
self.selected
}
#[inline]
/// get @selected_state_text_color
pub fn selected_state_text_color(&self) -> Srgba {
self.selected_text
}
#[inline]
/// get @focus_color
pub fn focus_color(&self) -> Srgba {
self.focus
}
/// helper for producing a component from a base color a neutral and an accent
pub fn colored_component(
base: Srgba,
neutral: Srgba,
accent: Srgba,
hovered: Srgba,
pressed: Srgba,
) -> Self {
let mut base_50 = base;
base_50.alpha *= 0.5;
let on_20 = neutral;
let on_50 = on_20.with_alpha(0.5);
Component {
base,
hover: over(hovered, base),
pressed: over(pressed, base),
selected: over(hovered, base),
selected_text: accent,
divider: on_20,
on: neutral,
disabled: over(base_50, base),
on_disabled: over(on_50, base),
focus: accent,
border: base,
disabled_border: base_50,
}
}
/// helper for producing a button component
pub fn colored_button(
base: Srgba,
overlay: Srgba,
on_button: Srgba,
accent: Srgba,
hovered: Srgba,
pressed: Srgba,
) -> Self {
let mut component = Component::colored_component(base, overlay, accent, hovered, pressed);
component.on = on_button;
let on_disabled = on_button.with_alpha(0.5);
component.on_disabled = on_disabled;
component
}
/// helper for producing a component color theme
#[allow(clippy::self_named_constructors)]
pub fn component(
base: Srgba,
accent: Srgba,
on_component: Srgba,
hovered: Srgba,
pressed: Srgba,
is_high_contrast: bool,
border: Srgba,
) -> Self {
let mut base_50 = base;
base_50.alpha *= 0.5;
let on_20 = on_component.with_alpha(0.2);
let on_50 = on_20.with_alpha(0.5);
let mut disabled_border = border;
disabled_border.alpha *= 0.5;
Component {
base,
hover: if base.alpha < 0.001 {
hovered
} else {
over(hovered, base)
},
pressed: if base.alpha < 0.001 {
pressed
} else {
over(pressed, base)
},
selected: if base.alpha < 0.001 {
hovered
} else {
over(hovered, base)
},
selected_text: accent,
focus: accent,
divider: if is_high_contrast { on_50 } else { on_20 },
on: on_component,
disabled: over(base_50, base),
on_disabled: over(on_50, base),
border,
disabled_border,
}
}
}