1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
pub mod widgets;
pub use widgets::button::Button;

use egui::style::{Selection, WidgetVisuals, Widgets};
use egui::{epaint, Color32, Context, Stroke, Style, Ui, Visuals};
use material_colors::Argb;
use std::str::FromStr;

#[derive(Clone, Default, Debug)]
pub struct MaterialColors {
    pub dark: bool,
    pub base_color: String,
    pub zoom: f32,
    pub primary: Color32,
    pub on_primary: Color32,
    pub primary_container: Color32,
    pub on_primary_container: Color32,
    pub inverse_primary: Color32,
    pub primary_fixed: Color32,
    pub primary_fixed_dim: Color32,
    pub on_primary_fixed: Color32,
    pub on_primary_fixed_variant: Color32,
    pub secondary: Color32,
    pub on_secondary: Color32,
    pub secondary_container: Color32,
    pub on_secondary_container: Color32,
    pub secondary_fixed: Color32,
    pub secondary_fixed_dim: Color32,
    pub on_secondary_fixed: Color32,
    pub on_secondary_fixed_variant: Color32,
    pub tertiary: Color32,
    pub on_tertiary: Color32,
    pub tertiary_container: Color32,
    pub on_tertiary_container: Color32,
    pub tertiary_fixed: Color32,
    pub tertiary_fixed_dim: Color32,
    pub on_tertiary_fixed: Color32,
    pub on_tertiary_fixed_variant: Color32,
    pub error: Color32,
    pub on_error: Color32,
    pub error_container: Color32,
    pub on_error_container: Color32,
    pub surface_dim: Color32,
    pub surface: Color32,
    pub surface_bright: Color32,
    pub surface_container_lowest: Color32,
    pub surface_container_low: Color32,
    pub surface_container: Color32,
    pub surface_container_high: Color32,
    pub surface_container_highest: Color32,
    pub on_surface: Color32,
    pub on_surface_variant: Color32,
    pub outline: Color32,
    pub outline_variant: Color32,
    pub inverse_surface: Color32,
    pub inverse_on_surface: Color32,
    pub surface_variant: Color32,
    pub background: Color32,
    pub on_background: Color32,
    pub shadow: Color32,
    pub scrim: Color32,
}

fn c(i: Argb) -> Color32 {
    Color32::from_rgba_premultiplied(i.red, i.green, i.blue, i.alpha)
}

impl MaterialColors {
    pub fn rebuild(&mut self) -> Self {
        *self = Self::new(self.base_color.clone(), self.dark, self.zoom).clone();
        self.clone()
    }
    pub fn new(base_color: String, dark: bool, zoom: f32) -> Self {
        let data = material_colors::theme_from_source_color(
            Argb::from_str(&base_color).unwrap(),
            Default::default(),
        );

        let scheme = match dark {
            true => data.schemes.dark,
            false => data.schemes.light,
        };

        Self {
            base_color,
            dark,
            zoom,

            // primary
            primary: c(scheme.primary),
            on_primary: c(scheme.on_primary),
            primary_container: c(scheme.primary_container),
            on_primary_container: c(scheme.on_primary_container),
            inverse_primary: c(scheme.inverse_primary),
            primary_fixed: c(scheme.primary_fixed),
            primary_fixed_dim: c(scheme.primary_fixed_dim),
            on_primary_fixed: c(scheme.on_primary_fixed),
            on_primary_fixed_variant: c(scheme.on_primary_fixed_variant),
            // secondary
            secondary: c(scheme.secondary),
            on_secondary: c(scheme.on_secondary),
            secondary_container: c(scheme.secondary_container),
            on_secondary_container: c(scheme.on_secondary_container),
            secondary_fixed: c(scheme.secondary_fixed),
            secondary_fixed_dim: c(scheme.secondary_fixed_dim),
            on_secondary_fixed: c(scheme.on_secondary_fixed),
            on_secondary_fixed_variant: c(scheme.on_secondary_fixed_variant),
            // tertiary
            tertiary: c(scheme.tertiary),
            on_tertiary: c(scheme.on_tertiary),
            tertiary_container: c(scheme.tertiary_container),
            on_tertiary_container: c(scheme.on_tertiary_container),
            tertiary_fixed: c(scheme.tertiary_fixed),
            tertiary_fixed_dim: c(scheme.tertiary_fixed_dim),
            on_tertiary_fixed: c(scheme.on_tertiary_fixed),
            on_tertiary_fixed_variant: c(scheme.on_tertiary_fixed_variant),
            // error
            error: c(scheme.error),
            on_error: c(scheme.on_error),
            error_container: c(scheme.error_container),
            on_error_container: c(scheme.on_error_container),
            // surface
            surface_dim: c(scheme.surface_dim),
            surface: c(scheme.surface),
            surface_bright: c(scheme.surface_bright),
            surface_container_lowest: c(scheme.surface_container_lowest),
            surface_container_low: c(scheme.surface_container_low),
            surface_container: c(scheme.surface_container),
            surface_container_high: c(scheme.surface_container_high),
            surface_container_highest: c(scheme.surface_container_highest),
            on_surface: c(scheme.on_surface),
            on_surface_variant: c(scheme.on_surface_variant),
            // outline
            outline: c(scheme.outline),
            outline_variant: c(scheme.outline_variant),
            // inverse
            inverse_surface: c(scheme.inverse_surface),
            inverse_on_surface: c(scheme.inverse_on_surface),
            surface_variant: c(scheme.surface_variant),
            background: c(scheme.background),
            on_background: c(scheme.on_background),
            shadow: c(scheme.shadow),
            scrim: c(scheme.scrim),
        }
    }
    /// exports the default M3 theme
    /// this function applies M3 themes to egui assets, specific implementation is subject to change
    /// CANNOT apply zoom
    pub fn export(&self) -> Style {
        let p = self;

        let visuals = Visuals {
            // override_text_color: Some(p.primary),
            collapsing_header_frame: true,
            override_text_color: None,
            hyperlink_color: p.on_primary,
            // background
            faint_bg_color: p.surface_container,
            extreme_bg_color: p.surface_variant,
            code_bg_color: p.surface_dim,

            window_fill: p.surface_container_highest,
            panel_fill: p.surface,

            warn_fg_color: p.error_container,
            error_fg_color: p.error,

            // window_stroke: Stroke {
            //     color: p.secondary,
            //     width: 10.5,
            // },

            window_highlight_topmost: false,
            // todo
            widgets: widgets(p.clone()),
            window_shadow: epaint::Shadow {
                color: p.shadow,
                ..Default::default()
            },

            popup_shadow: epaint::Shadow {
                color: p.shadow,
                ..Default::default()
            },

            // todo selection represents a kind of accent color for widgets outside of the widgets config
            // this is the subject of an issue https://github.com/emilk/egui/issues/4227
            selection: Selection {
                bg_fill: p.secondary,
                stroke: Stroke {
                    width: 1.5,
                    color: p.on_secondary,
                },
            },

            ..Default::default()
        };

        Style {
            visuals,

            ..Default::default()
        }
    }
    /// applies generated theming values to ctx
    /// this also applies zoom value: must be stored persistently
    pub fn apply_zoom(&mut self, ctx: &Context, first_run: &mut bool) {
        if *first_run {
            ctx.set_zoom_factor(self.zoom);
            *first_run = false;
        }
        ctx.set_style(self.export());
    }

    /// applies generated theming values to ctx
    pub fn apply(&mut self, ctx: &Context) {
        ctx.set_style(self.export());
    }
    /// applies generated values to Ui
    /// CANNOT apply zoom
    pub fn apply_ui(&self, ui: &mut Ui) {
        ui.set_style(self.export())
    }

    pub fn error_apply(&self, ui: &mut Ui) {
        let widgets = &mut ui.style_mut().visuals.widgets;
        // 'error buttons' are not documented in M3, so its unclear which arrangement is better
        let active = (self.error, self.on_error);
        let rest = (self.error_container, self.on_error_container);
        // widgets
        widget_maker_mut(&mut widgets.hovered, rest.0, rest.1);
        widget_maker_mut(&mut widgets.inactive, rest.0, rest.1);
        widget_maker_mut(&mut widgets.open, active.0, active.1);
        widget_maker_mut(&mut widgets.active, active.0, active.1);
    }
}
fn widgets(p: MaterialColors) -> Widgets {
    let mut old = match p.dark {
        true => Visuals::dark(),
        false => Visuals::light(),
    }
    .widgets;

    //   colors.visuals.widgets.noninteractive.bg_stroke.color = Color32::RED;
    widget_maker_mut(&mut old.noninteractive, p.surface, p.on_surface);

    old.noninteractive.bg_stroke.color = p.surface_variant;
    old.noninteractive.bg_stroke.width = 4.;

    widget_maker_mut(
        &mut old.inactive,
        p.primary_container,
        p.on_primary_container,
    );
    widget_maker_mut(
        &mut old.hovered,
        p.tertiary_container,
        p.on_tertiary_container,
    );
    widget_maker_mut(&mut old.active, p.tertiary, p.on_tertiary);
    widget_maker_mut(&mut old.open, p.primary_container, p.on_primary_container);
    old
}
fn widget_maker_mut(old: &mut WidgetVisuals, fill: Color32, text: Color32) {
    old.bg_fill = fill;
    old.weak_bg_fill = fill;
    old.fg_stroke.color = text;
    old.bg_stroke.color = fill;
    old.expansion = 0.;
}