Skip to main content

sequoia_style/
lib.rs

1#![forbid(unsafe_code)]
2
3mod avatar_style;
4mod badge_style;
5mod box_style;
6mod button_style;
7mod checkbox_style;
8mod chip_style;
9mod context_menu_style;
10mod dialog_style;
11mod divider_style;
12mod drag_value_style;
13mod drawer_style;
14mod effect;
15mod filter;
16mod floating_menu_style;
17mod focus_ring_style;
18mod form_style;
19mod kbd_style;
20mod modal_style;
21mod radio_style;
22mod resize_handle_style;
23mod sheet_style;
24mod slider_style;
25mod split_panel_style;
26mod switch_style;
27mod table_tree_style;
28mod text_area_style;
29mod text_field_style;
30mod text_style;
31mod toast_style;
32mod tokens;
33mod tooltip_style;
34
35pub use avatar_style::AvatarStyle;
36pub use badge_style::BadgeStyle;
37pub use box_style::{BorderSide, BoxStyle, ShadowSpec};
38pub use button_style::ButtonStyle;
39pub use checkbox_style::CheckboxStyle;
40pub use chip_style::ChipStyle;
41pub use context_menu_style::ContextMenuStyle;
42pub use dialog_style::{DIALOG_DANGER, DialogStyle};
43pub use divider_style::DividerStyle;
44pub use drag_value_style::DragValueStyle;
45pub use drawer_style::DrawerStyle;
46pub use effect::Backdrop;
47pub use filter::{
48    Effect, Filter, GradientAlphas, Mask, VERTICAL, backdrop_blur, blur, gradient, gradient_angled,
49    gradient_mask,
50};
51pub use floating_menu_style::FloatingMenuStyle;
52pub use focus_ring_style::FocusRingStyle;
53pub use form_style::{FORM_ERROR, FormStyle};
54pub use kbd_style::KbdStyle;
55pub use modal_style::ModalStyle;
56pub use radio_style::RadioStyle;
57pub use resize_handle_style::ResizeHandleStyle;
58pub use sheet_style::SheetStyle;
59pub use slider_style::SliderStyle;
60pub use split_panel_style::SplitPanelStyle;
61pub use switch_style::SwitchStyle;
62pub use table_tree_style::{TableStyle, TreeStyle};
63pub use text_area_style::TextAreaStyle;
64pub use text_field_style::TextFieldStyle;
65pub use text_style::TextStyle;
66pub use toast_style::ToastStyle;
67pub use tokens::TokensError;
68pub use tooltip_style::TooltipStyle;
69
70use sequoia_core::Color;
71
72#[derive(Clone, Copy, Debug, PartialEq)]
73#[non_exhaustive]
74pub struct Style {
75    pub background: Option<Color>,
76
77    pub radius: f32,
78
79    pub color: Color,
80
81    pub font_size: f32,
82
83    pub line_height_factor: f32,
84}
85
86impl Default for Style {
87    fn default() -> Self {
88        Self {
89            background: None,
90            radius: 0.0,
91            color: Color::rgb8(0xE6, 0xE6, 0xE6),
92            font_size: 14.0,
93            line_height_factor: 1.35,
94        }
95    }
96}
97
98#[derive(Clone, Copy, Debug, PartialEq)]
99#[non_exhaustive]
100pub struct Theme {
101    pub background: Color,
102
103    pub surface: Color,
104
105    pub border: Color,
106
107    pub text: Color,
108
109    pub text_secondary: Color,
110
111    pub accent: Color,
112
113    pub accent_hover: Color,
114
115    pub accent_pressed: Color,
116
117    pub disabled: Color,
118
119    pub radius: f32,
120
121    pub font_size: f32,
122}
123
124impl Theme {
125    pub const fn dark() -> Self {
126        Self {
127            background: Color::rgb8(0x19, 0x19, 0x19),
128            surface: Color::rgb8(0x26, 0x26, 0x26),
129            border: Color::rgb8(0x45, 0x45, 0x45),
130            text: Color::rgb8(0xD4, 0xD4, 0xD4),
131            text_secondary: Color::rgb8(0x9a, 0x9a, 0x9a),
132            accent: Color::rgb8(0x35, 0xC2, 0xF1),
133            accent_hover: Color::rgb8(0x5a, 0xD3, 0xF5),
134            accent_pressed: Color::rgb8(0x1e, 0x9a, 0xCD),
135            disabled: Color::rgb8(0x6B, 0x6B, 0x6B),
136            radius: 3.0,
137            font_size: 12.0,
138        }
139    }
140
141    pub const fn dcc_dark_panel() -> Self {
142        Self::dark()
143    }
144
145    pub const fn light() -> Self {
146        Self {
147            background: Color::rgb8(0xF5, 0xF5, 0xF5),
148            surface: Color::rgb8(0xFF, 0xFF, 0xFF),
149            border: Color::rgb8(0xD4, 0xD4, 0xD4),
150            text: Color::rgb8(0x1A, 0x1A, 0x1A),
151            text_secondary: Color::rgb8(0x5A, 0x5A, 0x5A),
152            accent: Color::rgb8(0x0D, 0x6E, 0xD8),
153            accent_hover: Color::rgb8(0x2B, 0x88, 0xE5),
154            accent_pressed: Color::rgb8(0x0B, 0x5C, 0xB5),
155            disabled: Color::rgb8(0xA8, 0xA8, 0xA8),
156            radius: 3.0,
157            font_size: 12.0,
158        }
159    }
160}
161
162pub const ACCENT_ACTIVE: Color = Color::rgb8(0xE8, 0x84, 0x2C);
163
164#[derive(Clone, Copy, Debug, Default, PartialEq)]
165#[non_exhaustive]
166pub struct VisualState {
167    pub hovered: bool,
168
169    pub pressed: bool,
170
171    pub focused: bool,
172
173    pub disabled: bool,
174
175    pub selected: bool,
176}
177
178pub fn resolve_accent(theme: &Theme, state: VisualState) -> Color {
179    if state.disabled {
180        theme.disabled
181    } else if state.pressed {
182        theme.accent_pressed
183    } else if state.hovered {
184        theme.accent_hover
185    } else {
186        theme.accent
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193
194    #[test]
195    fn theme_dark_is_sane() {
196        let t = Theme::dark();
197        assert_ne!(t.background, t.surface);
198        assert_ne!(t.accent, t.accent_hover);
199    }
200
201    #[test]
202    fn theme_light_is_sane_and_distinct() {
203        let l = Theme::light();
204        let d = Theme::dark();
205        assert_ne!(l.background, d.background);
206        assert_ne!(l.text, d.text);
207        assert_ne!(l.background, l.surface);
208        assert_ne!(l.accent, l.accent_hover);
209    }
210
211    #[test]
212    fn resolve_accent_by_state() {
213        let t = Theme::dark();
214        assert_eq!(resolve_accent(&t, VisualState::default()), t.accent);
215        assert_eq!(
216            resolve_accent(
217                &t,
218                VisualState {
219                    hovered: true,
220                    ..Default::default()
221                }
222            ),
223            t.accent_hover
224        );
225        assert_eq!(
226            resolve_accent(
227                &t,
228                VisualState {
229                    pressed: true,
230                    ..Default::default()
231                }
232            ),
233            t.accent_pressed
234        );
235        assert_eq!(
236            resolve_accent(
237                &t,
238                VisualState {
239                    disabled: true,
240                    ..Default::default()
241                }
242            ),
243            t.disabled
244        );
245    }
246}