use iced::{theme, Color, Theme};
use serde::{Deserialize, Serialize};
mod catalog;
pub mod layout;
pub use layout::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum ThemeKind {
#[default]
Dark,
Light,
}
impl ThemeKind {
pub const ALL: [ThemeKind; 2] = [ThemeKind::Dark, ThemeKind::Light];
pub fn label(self) -> &'static str {
match self {
ThemeKind::Dark => "Dark",
ThemeKind::Light => "Light",
}
}
pub fn palette(self) -> Palette {
match self {
ThemeKind::Dark => Palette::dark(),
ThemeKind::Light => Palette::light(),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Palette {
pub bg: Color,
pub bg_secondary: Color,
pub bg_tertiary: Color,
pub bg_hover: Color,
pub bg_current: Color,
pub accent: Color,
pub accent_hover: Color,
pub button: Color,
pub button_hover: Color,
pub fg: Color,
pub fg_accent: Color,
pub fg_secondary: Color,
pub fg_muted: Color,
pub overlay: Color,
pub danger: Color,
pub danger_hover: Color,
}
impl Default for Palette {
fn default() -> Self {
Self::dark()
}
}
fn blend_channels(c1: f32, c2: f32, ratio: f32) -> f32 {
c1 * (1.0 - ratio) + c2 * ratio
}
pub fn blend_colors(c1: Color, c2: Color, ratio: f32) -> Color {
Color::from_rgba(
blend_channels(c1.r, c2.r, ratio),
blend_channels(c1.g, c2.g, ratio),
blend_channels(c1.b, c2.b, ratio),
blend_channels(c1.a, c2.a, ratio),
)
}
impl Palette {
pub const fn dark() -> Self {
Self {
bg: Color::from_rgb8(0x14, 0x14, 0x18),
bg_secondary: Color::from_rgb8(0x1a, 0x1a, 0x20),
bg_tertiary: Color::from_rgb8(0x08, 0x08, 0x0e),
bg_hover: Color::from_rgb8(0x2a, 0x2a, 0x34),
bg_current: Color::from_rgb8(0x0a, 0x3a, 0x20),
accent: Color::from_rgb8(0x14, 0xc8, 0x84),
accent_hover: Color::from_rgb8(0x10, 0xba, 0x70),
button: Color::from_rgb8(0x2a, 0x2a, 0x34),
button_hover: Color::from_rgb8(0x3a, 0x3a, 0x44),
fg: Color::from_rgb8(0xe0, 0xe0, 0xe0),
fg_accent: Color::from_rgb8(0x14, 0xc8, 0x84),
fg_secondary: Color::from_rgb8(0x88, 0x88, 0xa0),
fg_muted: Color::from_rgb8(0x55, 0x55, 0x60),
overlay: Color::from_rgba8(0, 0, 0, 0.7),
danger: Color::from_rgb8(0xc0, 0x40, 0x40),
danger_hover: Color::from_rgb8(0xc0, 0x30, 0x30),
}
}
pub const fn light() -> Self {
Self {
bg: Color::from_rgb8(0xf6, 0xf6, 0xfa),
bg_secondary: Color::from_rgb8(0xea, 0xea, 0xf0),
bg_tertiary: Color::from_rgb8(0xdf, 0xdf, 0xe8),
bg_hover: Color::from_rgb8(0xd5, 0xd5, 0xe0),
bg_current: Color::from_rgb8(0xa0, 0xf0, 0xa0),
accent: Color::from_rgb8(0x84, 0xe8, 0xa4),
accent_hover: Color::from_rgb8(0x60, 0xe8, 0x80),
button: Color::from_rgb8(0xc8, 0xc8, 0xd2),
button_hover: Color::from_rgb8(0xb8, 0xb8, 0xc8),
fg: Color::from_rgb8(0x18, 0x18, 0x20),
fg_accent: Color::from_rgb8(0x04, 0x98, 0x34),
fg_secondary: Color::from_rgb8(0x55, 0x55, 0x66),
fg_muted: Color::from_rgb8(0x88, 0x88, 0x99),
overlay: Color::from_rgba8(0, 0, 0, 0.5),
danger: Color::from_rgb8(0xf0, 0x70, 0x70),
danger_hover: Color::from_rgb8(0xe0, 0x60, 0x60),
}
}
}
impl From<ThemeKind> for Palette {
fn from(kind: ThemeKind) -> Self {
kind.palette()
}
}
#[derive(Debug, Clone)]
pub struct AppTheme {
pub inner: Theme,
pub palette: Palette,
}
impl AppTheme {
pub fn new(palette: Palette) -> Self {
Self {
inner: Theme::custom_with_fn(
"goosemusic",
iced::theme::Palette {
background: palette.bg,
text: palette.fg,
primary: palette.accent,
success: palette.accent,
warning: palette.fg_secondary,
danger: palette.danger,
},
|_| {
iced::theme::palette::Extended::generate(iced::theme::Palette {
background: palette.bg,
text: palette.fg,
primary: palette.accent,
success: palette.accent,
warning: palette.fg_secondary,
danger: palette.danger,
})
},
),
palette,
}
}
}
impl From<Palette> for AppTheme {
fn from(palette: Palette) -> Self {
Self::new(palette)
}
}
impl theme::Base for AppTheme {
fn default(_preference: theme::Mode) -> Self {
Self::new(Palette::default())
}
fn mode(&self) -> theme::Mode {
self.inner.mode()
}
fn base(&self) -> theme::Style {
theme::Style {
background_color: self.palette.bg,
text_color: self.palette.fg,
}
}
fn palette(&self) -> Option<iced::theme::Palette> {
Some(self.palette.into())
}
fn name(&self) -> &'static str {
"goosemusic"
}
}
impl From<Palette> for iced::theme::Palette {
fn from(p: Palette) -> Self {
Self {
background: p.bg,
text: p.fg,
primary: p.accent,
success: p.accent,
warning: p.fg_secondary,
danger: p.danger,
}
}
}