Skip to main content

iced_markdown_editor/
style.rs

1//! Theming.
2//!
3//! An [`EditorStyle`] combines a colour [`Palette`] and [`Typography`].
4//! Use a preset ([`EditorStyle::light`], [`EditorStyle::dark`]), derive
5//! one from an Iced theme ([`EditorStyle::from_theme`]) or change
6//! individual fields.
7
8use iced::{Color, Theme};
9
10/// All colours used by the editor.
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub struct Palette {
13    /// Area around the pages.
14    pub background: Color,
15    /// The sheet.
16    pub page: Color,
17    pub page_border: Color,
18    pub page_shadow: Color,
19    /// Body text.
20    pub ink: Color,
21    pub heading: Color,
22    pub quote: Color,
23    pub quote_bar: Color,
24    /// Text inside code blocks (also the default colour for highlighted code).
25    pub code: Color,
26    pub code_background: Color,
27    pub inline_code_background: Color,
28    pub link: Color,
29    /// List bullets, numbers and empty task checkboxes.
30    pub marker: Color,
31    /// Horizontal rule.
32    pub rule: Color,
33    pub selection: Color,
34    pub caret: Color,
35    pub toolbar_background: Color,
36    pub toolbar_border: Color,
37    pub toolbar_text: Color,
38    pub toolbar_hover: Color,
39    pub toolbar_active: Color,
40    /// Active buttons, checked task checkboxes.
41    pub accent: Color,
42    /// Secondary text (status bar, labels, raw HTML, image descriptions).
43    pub muted: Color,
44}
45
46/// Font sizes and spacing in logical pixels at 100 % zoom.
47#[derive(Debug, Clone, Copy, PartialEq)]
48pub struct Typography {
49    /// Space between pages and around them (not zoomed).
50    pub page_gap: f32,
51    /// Inner padding of the text in full-width mode; not zoomed.
52    pub fill_padding: f32,
53    pub body_size: f32,
54    pub body_line_height: f32,
55    pub paragraph_spacing: f32,
56    /// Sizes of heading levels 1, 2 and 3 (levels 4-6 use level 3).
57    pub heading_sizes: [f32; 3],
58    pub code_size: f32,
59    pub code_line_height: f32,
60    /// Indentation per list level.
61    pub list_indent: f32,
62    /// Indentation per block quote level.
63    pub quote_indent: f32,
64}
65
66impl Default for Typography {
67    fn default() -> Self {
68        Self {
69            page_gap: 28.0,
70            fill_padding: 16.0,
71            body_size: 14.5,
72            body_line_height: 22.0,
73            paragraph_spacing: 10.0,
74            heading_sizes: [27.0, 21.0, 17.0],
75            code_size: 13.0,
76            code_line_height: 20.0,
77            list_indent: 30.0,
78            quote_indent: 22.0,
79        }
80    }
81}
82
83/// Complete editor appearance.
84#[derive(Debug, Clone, Copy, PartialEq)]
85pub struct EditorStyle {
86    pub palette: Palette,
87    pub typography: Typography,
88}
89
90impl Default for EditorStyle {
91    fn default() -> Self {
92        Self::light()
93    }
94}
95
96impl Palette {
97    /// White page on a grey desk, like Word.
98    pub fn light() -> Self {
99        Self {
100            background: Color::from_rgb(0.86, 0.87, 0.89),
101            page: Color::WHITE,
102            page_border: Color::from_rgb(0.80, 0.81, 0.84),
103            page_shadow: Color::from_rgba(0.0, 0.0, 0.0, 0.10),
104            ink: Color::from_rgb(0.11, 0.11, 0.12),
105            heading: Color::from_rgb(0.07, 0.17, 0.33),
106            quote: Color::from_rgb(0.36, 0.38, 0.42),
107            quote_bar: Color::from_rgb(0.78, 0.80, 0.84),
108            code: Color::from_rgb(0.16, 0.18, 0.22),
109            code_background: Color::from_rgb(0.955, 0.958, 0.965),
110            inline_code_background: Color::from_rgb(0.93, 0.935, 0.945),
111            link: Color::from_rgb(0.02, 0.39, 0.76),
112            marker: Color::from_rgb(0.25, 0.26, 0.29),
113            rule: Color::from_rgb(0.76, 0.77, 0.80),
114            selection: Color::from_rgba(0.16, 0.42, 0.86, 0.28),
115            caret: Color::from_rgb(0.11, 0.11, 0.12),
116            toolbar_background: Color::from_rgb(0.97, 0.97, 0.98),
117            toolbar_border: Color::from_rgb(0.82, 0.83, 0.86),
118            toolbar_text: Color::from_rgb(0.20, 0.21, 0.24),
119            toolbar_hover: Color::from_rgb(0.91, 0.92, 0.94),
120            toolbar_active: Color::from_rgb(0.84, 0.89, 0.97),
121            accent: Color::from_rgb(0.10, 0.36, 0.78),
122            muted: Color::from_rgb(0.42, 0.44, 0.48),
123        }
124    }
125
126    /// Dark page for night work.
127    pub fn dark() -> Self {
128        Self {
129            background: Color::from_rgb(0.08, 0.085, 0.10),
130            page: Color::from_rgb(0.145, 0.15, 0.17),
131            page_border: Color::from_rgb(0.22, 0.23, 0.26),
132            page_shadow: Color::from_rgba(0.0, 0.0, 0.0, 0.40),
133            ink: Color::from_rgb(0.88, 0.89, 0.91),
134            heading: Color::from_rgb(0.66, 0.80, 1.0),
135            quote: Color::from_rgb(0.68, 0.70, 0.74),
136            quote_bar: Color::from_rgb(0.35, 0.37, 0.42),
137            code: Color::from_rgb(0.86, 0.87, 0.90),
138            code_background: Color::from_rgb(0.11, 0.115, 0.13),
139            inline_code_background: Color::from_rgb(0.22, 0.23, 0.26),
140            link: Color::from_rgb(0.45, 0.68, 1.0),
141            marker: Color::from_rgb(0.75, 0.76, 0.80),
142            rule: Color::from_rgb(0.35, 0.37, 0.42),
143            selection: Color::from_rgba(0.35, 0.55, 0.95, 0.35),
144            caret: Color::from_rgb(0.92, 0.93, 0.95),
145            toolbar_background: Color::from_rgb(0.12, 0.125, 0.14),
146            toolbar_border: Color::from_rgb(0.22, 0.23, 0.26),
147            toolbar_text: Color::from_rgb(0.85, 0.86, 0.88),
148            toolbar_hover: Color::from_rgb(0.20, 0.21, 0.24),
149            toolbar_active: Color::from_rgb(0.18, 0.26, 0.40),
150            accent: Color::from_rgb(0.45, 0.68, 1.0),
151            muted: Color::from_rgb(0.55, 0.57, 0.62),
152        }
153    }
154}
155
156fn luminance(c: Color) -> f32 {
157    0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b
158}
159
160fn with_alpha(c: Color, a: f32) -> Color {
161    Color { a, ..c }
162}
163
164impl EditorStyle {
165    pub fn light() -> Self {
166        Self {
167            palette: Palette::light(),
168            typography: Typography::default(),
169        }
170    }
171
172    pub fn dark() -> Self {
173        Self {
174            palette: Palette::dark(),
175            typography: Typography::default(),
176        }
177    }
178
179    /// Derives a style from an Iced theme, so the editor matches the rest
180    /// of the application.
181    pub fn from_theme(theme: &Theme) -> Self {
182        let p = theme.palette();
183        let dark = luminance(p.background) < 0.5;
184        let mut style = if dark { Self::dark() } else { Self::light() };
185        let palette = &mut style.palette;
186        palette.page = p.background;
187        palette.ink = p.text;
188        palette.caret = p.text;
189        palette.code = p.text;
190        palette.marker = p.text;
191        palette.toolbar_text = p.text;
192        palette.link = p.primary;
193        palette.accent = p.primary;
194        palette.heading = p.primary;
195        palette.selection = with_alpha(p.primary, 0.30);
196        palette.toolbar_active = with_alpha(p.primary, 0.20);
197        style
198    }
199
200    pub fn with_palette(mut self, palette: Palette) -> Self {
201        self.palette = palette;
202        self
203    }
204
205    pub fn with_typography(mut self, typography: Typography) -> Self {
206        self.typography = typography;
207        self
208    }
209}