use super::{Status, StyleFn};
use iced_core::{Background, Color, Theme, border::Radius};
#[derive(Clone, Copy, Debug)]
pub struct Style {
pub background: Option<Background>,
pub border_color: Option<Color>,
pub border_width: f32,
pub tab_border_radius: Radius,
pub tab_label_background: Background,
pub tab_label_border_color: Color,
pub tab_label_border_width: f32,
pub icon_color: Color,
pub icon_background: Option<Background>,
pub icon_border_radius: Radius,
pub text_color: Color,
}
impl Default for Style {
fn default() -> Self {
Self {
background: None,
border_color: None,
border_width: 0.0,
tab_border_radius: Radius::default(),
tab_label_background: Background::Color([0.87, 0.87, 0.87].into()),
tab_label_border_color: [0.7, 0.7, 0.7].into(),
tab_label_border_width: 1.0,
icon_color: Color::BLACK,
icon_background: Some(Background::Color(Color::TRANSPARENT)),
icon_border_radius: 4.0.into(),
text_color: Color::BLACK,
}
}
}
pub trait Catalog {
type Class<'a>;
fn default<'a>() -> Self::Class<'a>;
fn style(&self, class: &Self::Class<'_>, status: Status) -> Style;
}
impl Catalog for Theme {
type Class<'a> = StyleFn<'a, Self, Style>;
fn default<'a>() -> Self::Class<'a> {
Box::new(primary)
}
fn style(&self, class: &Self::Class<'_>, status: Status) -> Style {
class(self, status)
}
}
#[must_use]
pub fn primary(theme: &Theme, status: Status) -> Style {
let mut base = Style::default();
let palette = theme.extended_palette();
base.text_color = palette.background.base.text;
match status {
Status::Disabled => {
base.tab_label_background = Background::Color(palette.background.strong.color);
}
Status::Hovered => {
base.tab_label_background = Background::Color(palette.primary.strong.color);
}
_ => {
base.tab_label_background = Background::Color(palette.primary.base.color);
}
}
base
}
#[must_use]
pub fn dark(_theme: &Theme, status: Status) -> Style {
let mut base = Style {
tab_label_background: Background::Color([0.1, 0.1, 0.1].into()),
tab_label_border_color: [0.3, 0.3, 0.3].into(),
icon_color: Color::WHITE,
text_color: Color::WHITE,
..Default::default()
};
if status == Status::Disabled {
base.tab_label_background = Background::Color([0.13, 0.13, 0.13].into());
}
base
}
#[must_use]
pub fn red(_theme: &Theme, status: Status) -> Style {
let mut base = Style {
tab_label_background: Background::Color([1.0, 0.0, 0.0].into()),
tab_label_border_color: Color::TRANSPARENT,
tab_label_border_width: 0.0,
icon_color: Color::WHITE,
text_color: Color::WHITE,
..Default::default()
};
if status == Status::Disabled {
base.tab_label_background = Background::Color([0.13, 0.13, 0.13].into());
base.icon_color = Color::BLACK;
base.text_color = Color::BLACK;
}
base
}
#[must_use]
pub fn blue(_theme: &Theme, status: Status) -> Style {
let mut base = Style {
tab_label_background: Background::Color([0.0, 0.0, 1.0].into()),
tab_label_border_color: [0.0, 0.0, 1.0].into(),
icon_color: Color::WHITE,
text_color: Color::WHITE,
..Default::default()
};
if status == Status::Disabled {
base.tab_label_background = Background::Color([0.5, 0.5, 1.0].into());
base.tab_label_border_color = [0.5, 0.5, 1.0].into();
}
base
}
#[must_use]
pub fn green(_theme: &Theme, status: Status) -> Style {
let mut base = Style {
tab_label_background: Color::WHITE.into(),
icon_color: [0.0, 0.5, 0.0].into(),
text_color: [0.0, 0.5, 0.0].into(),
..Default::default()
};
match status {
Status::Disabled => {
base.icon_color = [0.7, 0.7, 0.7].into();
base.text_color = [0.7, 0.7, 0.7].into();
base.tab_label_border_color = [0.7, 0.7, 0.7].into();
}
_ => {
base.tab_label_border_color = [0.0, 0.5, 0.0].into();
}
}
base
}
#[must_use]
pub fn purple(_theme: &Theme, status: Status) -> Style {
let mut base = Style {
tab_label_background: Color::WHITE.into(),
tab_label_border_color: Color::TRANSPARENT,
icon_color: [0.7, 0.0, 1.0].into(),
text_color: [0.7, 0.0, 1.0].into(),
..Default::default()
};
if status == Status::Disabled {
base.icon_color = Color::BLACK;
base.text_color = Color::BLACK;
}
base
}
#[cfg(test)]
mod tests {
use super::*;
use iced_core::{Background, Theme};
#[test]
fn style_default() {
let style = Style::default();
assert!(style.background.is_none());
assert!(style.border_color.is_none());
assert_eq!(style.border_width, 0.0);
assert!(matches!(style.tab_label_background, Background::Color(_)));
assert_eq!(style.tab_label_border_width, 1.0);
assert_eq!(style.icon_color, Color::BLACK);
assert!(style.icon_background.is_some());
assert_eq!(style.text_color, Color::BLACK);
}
#[test]
fn primary_theme_active() {
let theme = Theme::TokyoNight;
let style = primary(&theme, Status::Active);
assert!(matches!(style.tab_label_background, Background::Color(_)));
}
#[test]
fn primary_theme_hovered() {
let theme = Theme::TokyoNight;
let style = primary(&theme, Status::Hovered);
assert!(matches!(style.tab_label_background, Background::Color(_)));
}
#[test]
fn primary_theme_disabled() {
let theme = Theme::TokyoNight;
let style = primary(&theme, Status::Disabled);
assert!(matches!(style.tab_label_background, Background::Color(_)));
}
#[test]
fn primary_theme_focused() {
let theme = Theme::TokyoNight;
let style = primary(&theme, Status::Focused);
assert!(matches!(style.tab_label_background, Background::Color(_)));
}
#[test]
fn primary_changes_background_by_status() {
let theme = Theme::TokyoNight;
let active = primary(&theme, Status::Active);
let hovered = primary(&theme, Status::Hovered);
let disabled = primary(&theme, Status::Disabled);
assert_ne!(
format!("{:?}", active.tab_label_background),
format!("{:?}", hovered.tab_label_background)
);
assert_ne!(
format!("{:?}", active.tab_label_background),
format!("{:?}", disabled.tab_label_background)
);
}
#[test]
fn dark_theme_active() {
let theme = Theme::TokyoNight;
let style = dark(&theme, Status::Active);
assert_eq!(style.icon_color, Color::WHITE);
assert_eq!(style.text_color, Color::WHITE);
}
#[test]
fn dark_theme_disabled() {
let theme = Theme::TokyoNight;
let style = dark(&theme, Status::Disabled);
assert_eq!(style.icon_color, Color::WHITE);
assert_eq!(style.text_color, Color::WHITE);
}
#[test]
fn dark_theme_changes_background_when_disabled() {
let theme = Theme::TokyoNight;
let active = dark(&theme, Status::Active);
let disabled = dark(&theme, Status::Disabled);
assert_ne!(
format!("{:?}", active.tab_label_background),
format!("{:?}", disabled.tab_label_background)
);
}
#[test]
fn red_theme_active() {
let theme = Theme::TokyoNight;
let style = red(&theme, Status::Active);
assert_eq!(
style.tab_label_background,
Background::Color([1.0, 0.0, 0.0].into())
);
assert_eq!(style.icon_color, Color::WHITE);
assert_eq!(style.text_color, Color::WHITE);
assert_eq!(style.tab_label_border_width, 0.0);
}
#[test]
fn red_theme_disabled() {
let theme = Theme::TokyoNight;
let style = red(&theme, Status::Disabled);
assert_eq!(style.icon_color, Color::BLACK);
assert_eq!(style.text_color, Color::BLACK);
}
#[test]
fn blue_theme_active() {
let theme = Theme::TokyoNight;
let style = blue(&theme, Status::Active);
assert_eq!(
style.tab_label_background,
Background::Color([0.0, 0.0, 1.0].into())
);
assert_eq!(style.icon_color, Color::WHITE);
assert_eq!(style.text_color, Color::WHITE);
}
#[test]
fn blue_theme_disabled() {
let theme = Theme::TokyoNight;
let style = blue(&theme, Status::Disabled);
assert_eq!(
style.tab_label_background,
Background::Color([0.5, 0.5, 1.0].into())
);
assert_eq!(style.tab_label_border_color, [0.5, 0.5, 1.0].into());
}
#[test]
fn green_theme_active() {
let theme = Theme::TokyoNight;
let style = green(&theme, Status::Active);
assert_eq!(style.tab_label_background, Background::Color(Color::WHITE));
assert_eq!(style.icon_color, [0.0, 0.5, 0.0].into());
assert_eq!(style.text_color, [0.0, 0.5, 0.0].into());
assert_eq!(style.tab_label_border_color, [0.0, 0.5, 0.0].into());
}
#[test]
fn green_theme_disabled() {
let theme = Theme::TokyoNight;
let style = green(&theme, Status::Disabled);
assert_eq!(style.icon_color, [0.7, 0.7, 0.7].into());
assert_eq!(style.text_color, [0.7, 0.7, 0.7].into());
assert_eq!(style.tab_label_border_color, [0.7, 0.7, 0.7].into());
}
#[test]
fn purple_theme_active() {
let theme = Theme::TokyoNight;
let style = purple(&theme, Status::Active);
assert_eq!(style.tab_label_background, Background::Color(Color::WHITE));
assert_eq!(style.icon_color, [0.7, 0.0, 1.0].into());
assert_eq!(style.text_color, [0.7, 0.0, 1.0].into());
assert_eq!(style.tab_label_border_color, Color::TRANSPARENT);
}
#[test]
fn purple_theme_disabled() {
let theme = Theme::TokyoNight;
let style = purple(&theme, Status::Disabled);
assert_eq!(style.icon_color, Color::BLACK);
assert_eq!(style.text_color, Color::BLACK);
}
#[test]
fn catalog_default_class() {
let _class = <Theme as Catalog>::default();
}
#[test]
fn catalog_style() {
let theme = Theme::TokyoNight;
let class = <Theme as Catalog>::default();
let style = theme.style(&class, Status::Active);
assert!(matches!(style.tab_label_background, Background::Color(_)));
}
#[test]
fn catalog_style_with_different_statuses() {
let theme = Theme::TokyoNight;
let class = <Theme as Catalog>::default();
let active = theme.style(&class, Status::Active);
let hovered = theme.style(&class, Status::Hovered);
let disabled = theme.style(&class, Status::Disabled);
assert!(matches!(active.tab_label_background, Background::Color(_)));
assert!(matches!(hovered.tab_label_background, Background::Color(_)));
assert!(matches!(
disabled.tab_label_background,
Background::Color(_)
));
}
#[test]
fn icon_border_radius_is_set() {
let style = Style::default();
assert_eq!(style.icon_border_radius.top_left, 4.0);
assert_eq!(style.icon_border_radius.top_right, 4.0);
assert_eq!(style.icon_border_radius.bottom_left, 4.0);
assert_eq!(style.icon_border_radius.bottom_right, 4.0);
}
#[test]
fn different_themes_produce_different_styles() {
let theme = Theme::TokyoNight;
let status = Status::Active;
let _primary_style = primary(&theme, status);
let dark_style = dark(&theme, status);
let red_style = red(&theme, status);
let blue_style = blue(&theme, status);
let green_style = green(&theme, status);
let purple_style = purple(&theme, status);
assert_ne!(
format!("{:?}", dark_style.tab_label_background),
format!("{:?}", red_style.tab_label_background)
);
assert_ne!(
format!("{:?}", red_style.tab_label_background),
format!("{:?}", blue_style.tab_label_background)
);
assert_ne!(
format!("{:?}", blue_style.tab_label_background),
format!("{:?}", green_style.tab_label_background)
);
assert_ne!(green_style.text_color, purple_style.text_color);
}
}