sequoia-style 0.1.1

Sequoia GUI framework — styles and themes
Documentation
use sequoia_core::Color;

#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct ButtonStyle {
    pub background: Option<Color>,

    pub hover: Option<Color>,

    pub pressed: Option<Color>,

    pub disabled: Option<Color>,

    pub text: Option<Color>,

    pub text_disabled: Option<Color>,

    pub font_size: Option<f32>,

    pub font_weight: Option<u16>,

    pub radius: Option<f32>,

    pub padding: Option<(f32, f32)>,

    pub border_width: Option<f32>,

    pub border_color: Option<Color>,
}

impl ButtonStyle {
    pub const fn new() -> Self {
        Self {
            background: None,
            hover: None,
            pressed: None,
            disabled: None,
            text: None,
            text_disabled: None,
            font_size: None,
            font_weight: None,
            radius: None,
            padding: None,
            border_width: None,
            border_color: None,
        }
    }

    pub const PRIMARY: Self = Self {
        background: Some(Color::rgb8(0x35, 0x6F, 0xD1)),
        hover: Some(Color::rgb8(0x45, 0x82, 0xE0)),
        pressed: Some(Color::rgb8(0x2C, 0x5A, 0xA8)),
        disabled: None,
        text: Some(Color::WHITE),
        text_disabled: None,
        font_size: None,
        font_weight: Some(600),
        radius: None,
        padding: None,
        border_width: None,
        border_color: None,
    };

    pub const SECONDARY: Self = Self {
        background: Some(Color::rgb8(0x33, 0x36, 0x3D)),
        hover: Some(Color::rgb8(0x40, 0x44, 0x4E)),
        pressed: Some(Color::rgb8(0x2A, 0x2D, 0x33)),
        disabled: None,
        text: Some(Color::rgb8(0xE6, 0xE6, 0xE6)),
        text_disabled: None,
        font_size: None,
        font_weight: None,
        radius: None,
        padding: None,
        border_width: None,
        border_color: None,
    };

    pub const DANGER: Self = Self {
        background: Some(Color::rgb8(0xB3, 0x26, 0x1E)),
        hover: Some(Color::rgb8(0xD3, 0x2F, 0x2F)),
        pressed: Some(Color::rgb8(0x8A, 0x1A, 0x14)),
        disabled: None,
        text: Some(Color::WHITE),
        text_disabled: None,
        font_size: None,
        font_weight: Some(600),
        radius: None,
        padding: None,
        border_width: None,
        border_color: None,
    };

    pub const GHOST: Self = Self {
        background: Some(Color::TRANSPARENT),
        hover: Some(Color::rgb8(0x33, 0x36, 0x3D)),
        pressed: Some(Color::rgb8(0x2A, 0x2D, 0x33)),
        disabled: None,
        text: Some(Color::rgb8(0x35, 0x6F, 0xD1)),
        text_disabled: None,
        font_size: None,
        font_weight: None,
        radius: None,
        padding: None,
        border_width: None,
        border_color: None,
    };

    pub const DARK_FILLED: Self = Self {
        background: Some(Color::hex(0x1F1F1F)),
        hover: Some(Color::hex(0x2B2B2B)),
        pressed: Some(Color::hex(0x141414)),
        disabled: None,
        text: Some(Color::hex(0xEDEDED)),
        text_disabled: None,
        font_size: Some(12.0),
        font_weight: Some(500),
        radius: None,
        padding: Some((12.0, 6.0)),
        border_width: None,
        border_color: None,
    };

    pub const OUTLINE_ROSE: Self = Self {
        background: Some(Color::TRANSPARENT),
        hover: Some(Color::hexa(0xB0868626)),
        pressed: Some(Color::hexa(0xB086864D)),
        disabled: None,
        text: Some(Color::hex(0xB08686)),
        text_disabled: None,
        font_size: Some(12.0),
        font_weight: Some(600),
        radius: None,
        padding: Some((12.0, 6.0)),
        border_width: Some(1.0),
        border_color: Some(Color::hex(0xB08686)),
    };

    pub const fn primary() -> Self {
        Self::PRIMARY
    }

    pub const fn secondary() -> Self {
        Self::SECONDARY
    }

    pub const fn danger() -> Self {
        Self::DANGER
    }

    pub const fn ghost() -> Self {
        Self::GHOST
    }

    pub const fn dark_filled() -> Self {
        Self::DARK_FILLED
    }

    pub const fn outline_rose() -> Self {
        Self::OUTLINE_ROSE
    }

    pub const fn background(mut self, color: Color) -> Self {
        self.background = Some(color);
        self
    }

    pub const fn hover(mut self, color: Color) -> Self {
        self.hover = Some(color);
        self
    }

    pub const fn pressed(mut self, color: Color) -> Self {
        self.pressed = Some(color);
        self
    }

    pub const fn disabled(mut self, color: Color) -> Self {
        self.disabled = Some(color);
        self
    }

    pub const fn text(mut self, color: Color) -> Self {
        self.text = Some(color);
        self
    }

    pub const fn text_disabled(mut self, color: Color) -> Self {
        self.text_disabled = Some(color);
        self
    }

    pub const fn font_size(mut self, size: f32) -> Self {
        self.font_size = Some(size);
        self
    }

    pub const fn font_weight(mut self, weight: u16) -> Self {
        self.font_weight = Some(weight);
        self
    }

    pub const fn radius(mut self, radius: f32) -> Self {
        self.radius = Some(radius);
        self
    }

    pub const fn padding_xy(mut self, pad_x: f32, pad_y: f32) -> Self {
        self.padding = Some((pad_x, pad_y));
        self
    }

    pub const fn border_width(mut self, width: f32) -> Self {
        self.border_width = Some(width);
        self
    }

    pub const fn border_color(mut self, color: Color) -> Self {
        self.border_color = Some(color);
        self
    }

    pub const fn border(mut self, width: f32, color: Color) -> Self {
        self.border_width = Some(width);
        self.border_color = Some(color);
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn presets_differ_per_state() {
        assert_ne!(ButtonStyle::PRIMARY.background, ButtonStyle::PRIMARY.hover);
        assert_ne!(ButtonStyle::PRIMARY.hover, ButtonStyle::PRIMARY.pressed);
        assert_ne!(
            ButtonStyle::DANGER.background,
            ButtonStyle::PRIMARY.background
        );
    }

    #[test]
    fn const_composition_in_const_context() {
        const CUSTOM: ButtonStyle = ButtonStyle::PRIMARY.radius(12.0);
        assert_eq!(CUSTOM.radius, Some(12.0));
        assert_eq!(CUSTOM.background, ButtonStyle::PRIMARY.background);
    }

    #[test]
    fn struct_update_syntax_keeps_base() {
        const CUSTOM: ButtonStyle = ButtonStyle {
            background: Some(Color::hex(0xB3261E)),
            ..ButtonStyle::PRIMARY
        };
        assert_eq!(CUSTOM.background, Some(Color::hex(0xB3261E)));
        assert_eq!(CUSTOM.hover, ButtonStyle::PRIMARY.hover);
    }

    #[test]
    fn border_builders_set_both_fields() {
        const S: ButtonStyle = ButtonStyle::new().border(1.0, Color::WHITE);
        assert_eq!(S.border_width, Some(1.0));
        assert_eq!(S.border_color, Some(Color::WHITE));
    }

    #[test]
    fn new_variants_share_metrics_but_differ_in_look() {
        assert_eq!(
            ButtonStyle::DARK_FILLED.padding,
            ButtonStyle::OUTLINE_ROSE.padding
        );
        assert_eq!(ButtonStyle::DARK_FILLED.padding, Some((12.0, 6.0)));
        assert_eq!(ButtonStyle::DARK_FILLED.font_size, Some(12.0));
        assert_eq!(ButtonStyle::OUTLINE_ROSE.font_size, Some(12.0));

        assert_eq!(ButtonStyle::DARK_FILLED.font_weight, Some(500));
        assert_eq!(ButtonStyle::OUTLINE_ROSE.font_weight, Some(600));
        assert_eq!(
            ButtonStyle::DARK_FILLED.background,
            Some(Color::hex(0x1F1F1F))
        );
        assert_eq!(ButtonStyle::DARK_FILLED.text, Some(Color::hex(0xEDEDED)));
        assert_eq!(ButtonStyle::DARK_FILLED.border_width, None);
        assert_eq!(
            ButtonStyle::OUTLINE_ROSE.background,
            Some(Color::TRANSPARENT)
        );
        assert_eq!(ButtonStyle::OUTLINE_ROSE.text, Some(Color::hex(0xB08686)));
        assert_eq!(ButtonStyle::OUTLINE_ROSE.border_width, Some(1.0));
        assert_eq!(
            ButtonStyle::OUTLINE_ROSE.border_color,
            Some(Color::hex(0xB08686))
        );
        assert_ne!(
            ButtonStyle::DARK_FILLED.hover,
            ButtonStyle::DARK_FILLED.background
        );
        assert_ne!(
            ButtonStyle::OUTLINE_ROSE.hover,
            ButtonStyle::OUTLINE_ROSE.background
        );
    }
}