Skip to main content

egui_elements/
editor.rs

1//! Live theme editor window.
2//!
3//! [`ThemeEditor::show`] mutates `theme` in place and only returns `Some`
4//! when the inner switcher picks a new [`crate::theme::ThemeKind`]. Always
5//! call [`crate::theme::Theme::install`] after `show`, not only on `Some`.
6
7use egui::{
8   Align, Button, CollapsingHeader, Color32, ComboBox, CornerRadius, DragValue, Frame, Layout,
9   Margin, Order, Popup, PopupCloseBehavior, Rect, Response, RichText, ScrollArea, Sense,
10   SetOpenCommand, Shadow, Slider, Stroke, StrokeKind, TextEdit, Ui, Vec2, Window,
11   color_picker::{Alpha, color_edit_button_srgba},
12   ecolor::HexColor,
13   vec2,
14};
15
16use crate::theme::{Theme, ThemeColors};
17use crate::utils::{self, Hsla};
18use crate::visuals::*;
19
20const DARK: Color32 = Color32::from_rgba_premultiplied(22, 22, 30, 255);
21
22/// Identify which state of the widget we should edit
23#[derive(Clone, PartialEq)]
24pub enum WidgetState {
25   /// Not interactive.
26   NonInteractive,
27   /// Idle, enabled.
28   Inactive,
29   /// Pointer over the widget.
30   Hovered,
31   /// Pointer down / focused.
32   Active,
33   /// Popup or combo is open.
34   Open,
35}
36
37/// Named palette slot, holding the current [`Color32`] for that slot.
38#[derive(Clone, PartialEq)]
39pub enum Color {
40   Bg(Color32),
41   WidgetBG(Color32),
42   Hover(Color32),
43   Text(Color32),
44   TextMuted(Color32),
45   Highlight(Color32),
46   Border(Color32),
47   Accent(Color32),
48   Error(Color32),
49   Warning(Color32),
50   Success(Color32),
51   Info(Color32),
52}
53
54impl Color {
55   /// Every named slot, filled from `theme`.
56   pub fn all_colors_from(theme: &ThemeColors) -> Vec<Color> {
57      vec![
58         Color::Bg(theme.bg),
59         Color::WidgetBG(theme.widget_bg),
60         Color::Hover(theme.hover),
61         Color::Text(theme.text),
62         Color::TextMuted(theme.text_muted),
63         Color::Highlight(theme.highlight),
64         Color::Border(theme.border),
65         Color::Accent(theme.accent),
66         Color::Error(theme.error),
67         Color::Warning(theme.warning),
68         Color::Success(theme.success),
69         Color::Info(theme.info),
70      ]
71   }
72
73   /// Short label for this slot.
74   pub fn to_str(&self) -> &'static str {
75      match self {
76         Color::Bg(_) => "Bg",
77         Color::WidgetBG(_) => "WidgetBG",
78         Color::Hover(_) => "Hover",
79         Color::Text(_) => "Text",
80         Color::TextMuted(_) => "Text Muted",
81         Color::Highlight(_) => "Highlight",
82         Color::Border(_) => "Border",
83         Color::Accent(_) => "Accent",
84         Color::Error(_) => "Error",
85         Color::Warning(_) => "Warning",
86         Color::Success(_) => "Success",
87         Color::Info(_) => "Info",
88      }
89   }
90
91   /// The wrapped [`Color32`].
92   pub fn color32(&self) -> Color32 {
93      match self {
94         Color::Bg(color) => *color,
95         Color::WidgetBG(color) => *color,
96         Color::Hover(color) => *color,
97         Color::Text(color) => *color,
98         Color::TextMuted(color) => *color,
99         Color::Highlight(color) => *color,
100         Color::Border(color) => *color,
101         Color::Accent(color) => *color,
102         Color::Error(color) => *color,
103         Color::Warning(color) => *color,
104         Color::Success(color) => *color,
105         Color::Info(color) => *color,
106      }
107   }
108
109   /// Slot name if `color` matches a palette entry, otherwise `"Unknown"`.
110   pub fn name_from(color: Color32, theme_colors: &ThemeColors) -> &'static str {
111      if color == theme_colors.bg {
112         "Bg"
113      } else if color == theme_colors.widget_bg {
114         "WidgetBG"
115      } else if color == theme_colors.hover {
116         "Hover"
117      } else if color == theme_colors.text {
118         "Text"
119      } else if color == theme_colors.text_muted {
120         "Text Muted"
121      } else if color == theme_colors.highlight {
122         "Highlight"
123      } else if color == theme_colors.border {
124         "Border"
125      } else if color == theme_colors.accent {
126         "Accent"
127      } else if color == theme_colors.error {
128         "Error"
129      } else if color == theme_colors.warning {
130         "Warning"
131      } else if color == theme_colors.success {
132         "Success"
133      } else if color == theme_colors.info {
134         "Info"
135      } else {
136         "Unknown"
137      }
138   }
139}
140
141impl WidgetState {
142   /// Convert the state to a string
143   pub fn to_str(&self) -> &'static str {
144      match self {
145         WidgetState::NonInteractive => "Non-interactive",
146         WidgetState::Inactive => "Inactive",
147         WidgetState::Hovered => "Hovered",
148         WidgetState::Active => "Active",
149         WidgetState::Open => "Open",
150      }
151   }
152
153   /// Convert the state to a vector
154   pub fn to_vec(&self) -> Vec<WidgetState> {
155      let non_interactive = Self::NonInteractive;
156      let inactive = Self::Inactive;
157      let hovered = Self::Hovered;
158      let active = Self::Active;
159      let open = Self::Open;
160
161      vec![non_interactive, inactive, hovered, active, open]
162   }
163}
164
165/// In-app editor for a [`Theme`].
166///
167/// Toggle [`ThemeEditor::open`], then call [`ThemeEditor::show`] every
168/// frame. Always install the (possibly mutated) theme afterwards.
169#[derive(Clone)]
170pub struct ThemeEditor {
171   /// When `false`, [`ThemeEditor::show`] is a no-op.
172   pub open: bool,
173   /// The current widget state being edited
174   pub widget_state: WidgetState,
175   /// Shared HSLA popup used by the color rows.
176   pub hsla_edit_button: HslaEditButton,
177   /// Currently selected named slot in the palette list.
178   pub color: Color,
179   /// Editor window background.
180   pub bg_color: Color32,
181   /// Minimum `(width, height)` of the editor contents.
182   pub size: (f32, f32),
183}
184
185impl ThemeEditor {
186   /// Closed editor with default size and Tokyo-Night-ish preview bg.
187   pub fn new() -> Self {
188      Self {
189         open: false,
190         widget_state: WidgetState::NonInteractive,
191         hsla_edit_button: HslaEditButton::new(),
192         color: Color::Bg(Color32::TRANSPARENT),
193         bg_color: DARK,
194         size: (300.0, 300.0),
195      }
196   }
197
198   /// Show the theme editor in a window
199   ///
200   /// Returns the new theme if we change it
201   pub fn show(&mut self, theme: &mut Theme, ui: &mut Ui) -> Option<Theme> {
202      if !self.open {
203         return None;
204      }
205
206      let mut open = self.open;
207      let mut new_theme = None;
208      let frame = Frame::window(ui.style()).fill(self.bg_color);
209
210      Window::new("Theme Editor")
211         .open(&mut open)
212         .resizable([true, true])
213         .frame(frame)
214         .show(ui.ctx(), |ui| {
215            ui.set_min_width(self.size.0);
216            ui.set_min_height(self.size.1);
217            ui.spacing_mut().button_padding = vec2(10.0, 8.0);
218
219            new_theme = utils::theme_switcher(theme, ui);
220
221            ui.add_space(20.0);
222
223            ScrollArea::vertical().show(ui, |ui| {
224               ui.set_width(self.size.0);
225               ui.set_height(self.size.1);
226               self.ui(theme, ui);
227            });
228         });
229      self.open = open;
230      new_theme
231   }
232
233   /// Show the ui for the theme editor
234   pub fn ui(&mut self, theme: &mut Theme, ui: &mut Ui) {
235      ui.vertical_centered(|ui| {
236         ui.spacing_mut().item_spacing.y = 10.0;
237         let colors = theme.colors.clone();
238
239         CollapsingHeader::new("Theme Frames").show(ui, |ui| {
240            CollapsingHeader::new("Window Frame").show(ui, |ui| {
241               self.frame_settings(
242                  "window_frame",
243                  &mut theme.window_frame,
244                  &colors,
245                  ui,
246               );
247            });
248
249            CollapsingHeader::new("Frame 1").show(ui, |ui| {
250               self.frame_settings("frame1", &mut theme.frame1, &colors, ui);
251            });
252
253            CollapsingHeader::new("Frame 2").show(ui, |ui| {
254               self.frame_settings("frame2", &mut theme.frame2, &colors, ui);
255            });
256         });
257
258         CollapsingHeader::new("Custom Widgets Visuals").show(ui, |ui| {
259            CollapsingHeader::new("Button").show(ui, |ui| {
260               CollapsingHeader::new("Button Visuals 1").show(ui, |ui| {
261                  self.button_visuals(colors, &mut theme.visuals.button_visuals, ui);
262               });
263            });
264
265            CollapsingHeader::new("Label").show(ui, |ui| {
266               CollapsingHeader::new("Label Visuals 1").show(ui, |ui| {
267                  self.button_visuals(colors, &mut theme.visuals.label_visuals, ui);
268               });
269            });
270
271            CollapsingHeader::new("Combo Box").show(ui, |ui| {
272               CollapsingHeader::new("Combo Box Visuals 1").show(ui, |ui| {
273                  self.combo_box_visuals(colors, &mut theme.visuals.combo_box_visuals, ui);
274               });
275            });
276
277            CollapsingHeader::new("Text Edit").show(ui, |ui| {
278               CollapsingHeader::new("Text Edit Visuals 1").show(ui, |ui| {
279                  self.text_edit_visuals(colors, &mut theme.visuals.text_edit_visuals, ui);
280               });
281            });
282         });
283
284         CollapsingHeader::new("Theme Colors").show(ui, |ui| {
285            let old_colors = theme.colors;
286
287            ui.label("BG");
288            self.hsla_edit_button.show("bg", ui, &mut theme.colors.bg);
289
290            ui.label("WidgetBG");
291            self.hsla_edit_button.show("widgetbg", ui, &mut theme.colors.widget_bg);
292
293            ui.label("Hover");
294            self.hsla_edit_button.show("hover", ui, &mut theme.colors.hover);
295
296            ui.label("Text");
297            self.hsla_edit_button.show("text1", ui, &mut theme.colors.text);
298
299            ui.label("Text Muted");
300            self.hsla_edit_button.show("text_muted1", ui, &mut theme.colors.text_muted);
301
302            ui.label("Highlight");
303            self.hsla_edit_button.show("highlight1", ui, &mut theme.colors.highlight);
304
305            ui.label("Border");
306            self.hsla_edit_button.show("border1", ui, &mut theme.colors.border);
307
308            ui.label("Accent");
309            self.hsla_edit_button.show("accent", ui, &mut theme.colors.accent);
310
311            ui.label("Error");
312            self.hsla_edit_button.show("error1", ui, &mut theme.colors.error);
313
314            ui.label("Warning");
315            self.hsla_edit_button.show("warning1", ui, &mut theme.colors.warning);
316
317            ui.label("Success");
318            self.hsla_edit_button.show("success1", ui, &mut theme.colors.success);
319
320            ui.label("Info");
321            self.hsla_edit_button.show("info1", ui, &mut theme.colors.info);
322
323            theme.remap_derived_frames(&old_colors);
324         });
325
326         CollapsingHeader::new("Typography").show(ui, |ui| {
327            ui.label("Very Small");
328            ui.add(Slider::new(&mut theme.typography.very_small, 0.0..=100.0).text("Size"));
329
330            ui.label("Small");
331            ui.add(Slider::new(&mut theme.typography.small, 0.0..=100.0).text("Size"));
332
333            ui.label("Normal");
334            ui.add(Slider::new(&mut theme.typography.normal, 0.0..=100.0).text("Size"));
335
336            ui.label("Large");
337            ui.add(Slider::new(&mut theme.typography.large, 0.0..=100.0).text("Size"));
338
339            ui.label("Very Large");
340            ui.add(Slider::new(&mut theme.typography.very_large, 0.0..=100.0).text("Size"));
341
342            ui.label("Heading");
343            ui.add(Slider::new(&mut theme.typography.heading, 0.0..=100.0).text("Size"));
344         });
345
346         /*
347         CollapsingHeader::new("Other Colors").show(ui, |ui| {
348            ui.label("Selection Stroke");
349            ui.add(
350               Slider::new(
351                  &mut theme.style.visuals.selection.stroke.width,
352                  0.0..=10.0,
353               )
354               .text("Stroke Width"),
355            );
356            ui.label("Selection Stroke Color");
357            self.hsla_edit_button.show(
358               "selection_stroke_color1",
359               ui,
360               &mut theme.style.visuals.selection.stroke.color,
361            );
362
363            ui.label("Selection Bg Fill");
364            self.hsla_edit_button.show(
365               "selection_bg_fill1",
366               ui,
367               &mut theme.style.visuals.selection.bg_fill,
368            );
369
370            ui.label("Hyperlink Color");
371            self.hsla_edit_button.show(
372               "hyperlink_color1",
373               ui,
374               &mut theme.style.visuals.hyperlink_color,
375            );
376
377            ui.label("Faint Background Color");
378            self.hsla_edit_button.show(
379               "faint_bg_color1",
380               ui,
381               &mut theme.style.visuals.faint_bg_color,
382            );
383
384            ui.label("Extreme Background Color");
385            self.hsla_edit_button.show(
386               "extreme_bg_color1",
387               ui,
388               &mut theme.style.visuals.extreme_bg_color,
389            );
390
391            ui.label("Code Background Color");
392            self.hsla_edit_button.show(
393               "code_bg_color1",
394               ui,
395               &mut theme.style.visuals.code_bg_color,
396            );
397
398            ui.label("Warning Text Color");
399            self.hsla_edit_button.show(
400               "warn_fg_color1",
401               ui,
402               &mut theme.style.visuals.warn_fg_color,
403            );
404
405            ui.label("Error Text Color");
406            self.hsla_edit_button.show(
407               "error_fg_color1",
408               ui,
409               &mut theme.style.visuals.error_fg_color,
410            );
411
412            ui.label("Panel Fill Color");
413            self.hsla_edit_button.show(
414               "panel_fill1",
415               ui,
416               &mut theme.style.visuals.panel_fill,
417            );
418         });
419
420         CollapsingHeader::new("Window Visuals").show(ui, |ui| {
421            ui.label("Window Rounding");
422            edit_corner_radius(&mut theme.style.visuals.window_corner_radius, ui);
423
424            ui.label("Window Shadow");
425            edit_shadow(&mut theme.style.visuals.window_shadow, ui);
426
427            ui.label("Window Fill Color");
428            self.hsla_edit_button.show(
429               "window_fill1",
430               ui,
431               &mut theme.style.visuals.window_fill,
432            );
433
434            ui.label("Window Stroke");
435            self.edit_stroke(
436               "window_stroke",
437               &colors,
438               &mut theme.style.visuals.window_stroke,
439               ui,
440            );
441
442            ui.label("Window Highlight Topmost");
443            ui.checkbox(
444               &mut theme.style.visuals.window_highlight_topmost,
445               "Highlight Topmost",
446            );
447         });
448
449         CollapsingHeader::new("Popup Shadow").show(ui, |ui| {
450            edit_shadow(&mut theme.style.visuals.popup_shadow, ui);
451         });
452
453         CollapsingHeader::new("Menu Rounding").show(ui, |ui| {
454            edit_corner_radius(&mut theme.style.visuals.menu_corner_radius, ui);
455         });
456
457         CollapsingHeader::new("Widget Visuals").show(ui, |ui| {
458            self.widget_settings(theme, ui);
459         });
460
461         CollapsingHeader::new("Other Settings").show(ui, |ui| {
462            ui.label("Resize Corner Size");
463            ui.add(
464               Slider::new(
465                  &mut theme.style.visuals.resize_corner_size,
466                  0.0..=100.0,
467               )
468               .text("Corner Size"),
469            );
470
471            ui.label("Button Frame");
472            ui.checkbox(
473               &mut theme.style.visuals.button_frame,
474               "Button Frame",
475            );
476         });
477          */
478
479         CollapsingHeader::new("Tessellation").show(ui, |ui| {
480            self.tesellation_settings(theme, ui);
481         });
482      });
483   }
484
485   fn tesellation_settings(&mut self, theme: &Theme, ui: &mut Ui) {
486      let text_size = theme.typography.normal;
487
488      let mut options = ui.ctx().tessellation_options(|options| options.clone());
489
490      let text = RichText::new("Feathering").size(text_size);
491
492      ui.checkbox(&mut options.feathering, text);
493
494      ui.add(
495         DragValue::new(&mut options.feathering_size_in_pixels)
496            .speed(0.1)
497            .range(0.0..=100.0),
498      );
499
500      let text = RichText::new("Coarse tessellation culling").size(text_size);
501      ui.checkbox(&mut options.coarse_tessellation_culling, text);
502
503      let text = RichText::new("Precomputed discs").size(text_size);
504      ui.checkbox(&mut options.prerasterized_discs, text);
505
506      let text = RichText::new("Round text to pixels").size(text_size);
507      ui.checkbox(&mut options.round_text_to_pixels, text);
508
509      let text = RichText::new("Round line segments to pixels").size(text_size);
510      ui.checkbox(&mut options.round_line_segments_to_pixels, text);
511
512      let text = RichText::new("Round rects to pixels").size(text_size);
513      ui.checkbox(&mut options.round_rects_to_pixels, text);
514
515      let text = RichText::new("Debug paint text rects").size(text_size);
516      ui.checkbox(&mut options.debug_paint_text_rects, text);
517
518      let text = RichText::new("Debug paint clip rects").size(text_size);
519      ui.checkbox(&mut options.debug_paint_clip_rects, text);
520
521      let text = RichText::new("Debug ignore clip rects").size(text_size);
522      ui.checkbox(&mut options.debug_ignore_clip_rects, text);
523
524      let text = RichText::new("Bezier tolerance").size(text_size);
525      ui.label(text);
526      ui.add(DragValue::new(&mut options.bezier_tolerance).speed(0.1).range(0.0..=1.0));
527
528      let text = RichText::new("Epsilon").size(text_size);
529      ui.label(text);
530      ui.add(DragValue::new(&mut options.epsilon).speed(0.1).range(0.0..=1.0));
531
532      let text = RichText::new("Parallel tessellation").size(text_size);
533      ui.checkbox(&mut options.parallel_tessellation, text);
534
535      let text = RichText::new("Validate meshes").size(text_size);
536      ui.checkbox(&mut options.validate_meshes, text);
537
538      ui.ctx().tessellation_options_mut(|options_mut| {
539         *options_mut = options;
540      });
541   }
542
543   fn button_visuals(&mut self, colors: ThemeColors, visuals: &mut ButtonVisuals, ui: &mut Ui) {
544      let text = RichText::new("Button Visuals");
545      ui.label(text);
546
547      ui.label("Text Color");
548      ui.horizontal(|ui| {
549         let color = self.color_select("1", visuals.text, &colors, ui);
550         if let Some(color) = color {
551            visuals.text = color.color32();
552         }
553
554         self.hsla_edit_button.show("text1", ui, &mut visuals.text);
555      });
556
557      ui.label("Background Color");
558      ui.horizontal(|ui| {
559         let color = self.color_select("2", visuals.bg, &colors, ui);
560         if let Some(color) = color {
561            visuals.bg = color.color32();
562         }
563
564         self.hsla_edit_button.show("bg1", ui, &mut visuals.bg);
565      });
566
567      ui.label("Background Hover Color");
568      ui.horizontal(|ui| {
569         let color = self.color_select("3", visuals.bg_hover, &colors, ui);
570         if let Some(color) = color {
571            visuals.bg_hover = color.color32();
572         }
573
574         self.hsla_edit_button.show("bg_hover1", ui, &mut visuals.bg_hover);
575      });
576
577      ui.label("Background Click Color");
578      ui.horizontal(|ui| {
579         let color = self.color_select("4", visuals.bg_click, &colors, ui);
580         if let Some(color) = color {
581            visuals.bg_click = color.color32();
582         }
583
584         self.hsla_edit_button.show("bg_click1", ui, &mut visuals.bg_click);
585      });
586
587      ui.label("Background Selected");
588      ui.horizontal(|ui| {
589         let color = self.color_select("5", visuals.bg_selected, &colors, ui);
590
591         if let Some(color) = color {
592            visuals.bg_selected = color.color32();
593         }
594
595         self.hsla_edit_button.show("bg_selected1", ui, &mut visuals.bg_selected);
596      });
597
598      ui.label("Border Color");
599      ui.horizontal(|ui| {
600         let color = self.color_select("6", visuals.border.color, &colors, ui);
601
602         if let Some(color) = color {
603            visuals.border.color = color.color32();
604         }
605
606         self.hsla_edit_button.show("border1", ui, &mut visuals.border.color);
607      });
608
609      ui.label("Border Hover Color");
610      ui.horizontal(|ui| {
611         let color = self.color_select("7", visuals.border_hover.color, &colors, ui);
612         if let Some(color) = color {
613            visuals.border_hover.color = color.color32();
614         }
615
616         self.hsla_edit_button.show(
617            "border_hover1",
618            ui,
619            &mut visuals.border_hover.color,
620         );
621      });
622
623      ui.label("Border Click Color");
624      ui.horizontal(|ui| {
625         let color = self.color_select("8", visuals.border_click.color, &colors, ui);
626         if let Some(color) = color {
627            visuals.border_click.color = color.color32();
628         }
629
630         self.hsla_edit_button.show(
631            "border_click1",
632            ui,
633            &mut visuals.border_click.color,
634         );
635      });
636
637      ui.label("Corner Radius");
638      ui.add(Slider::new(&mut visuals.corner_radius.ne, 0..=100).text("NE"));
639      ui.add(Slider::new(&mut visuals.corner_radius.nw, 0..=100).text("NW"));
640      ui.add(Slider::new(&mut visuals.corner_radius.se, 0..=100).text("SE"));
641      ui.add(Slider::new(&mut visuals.corner_radius.sw, 0..=100).text("SW"));
642
643      ui.label("Shadow");
644      ui.horizontal(|ui| {
645         let color = self.color_select("9", visuals.shadow.color, &colors, ui);
646         if let Some(color) = color {
647            visuals.shadow.color = color.color32();
648         }
649
650         /*
651         self.hsla_edit_button.show(
652            "shadow_color1",
653            ui,
654            &mut colors.button_visuals.shadow.color,
655         );
656         */
657
658         color_edit_button_srgba(
659            ui,
660            &mut visuals.shadow.color,
661            Alpha::BlendOrAdditive,
662         );
663      });
664
665      ui.label("Shadow Offset");
666      ui.add(Slider::new(&mut visuals.shadow.offset[0], -100..=100).text("Offset X"));
667      ui.add(Slider::new(&mut visuals.shadow.offset[1], -100..=100).text("Offset Y"));
668
669      ui.label("Shadow Blur");
670      ui.add(Slider::new(&mut visuals.shadow.blur, 0..=100).text("Blur"));
671
672      ui.label("Shadow Spread");
673      ui.add(Slider::new(&mut visuals.shadow.spread, 0..=100).text("Spread"));
674   }
675
676   fn combo_box_visuals(
677      &mut self,
678      colors: ThemeColors,
679      visuals: &mut ComboBoxVisuals,
680      ui: &mut Ui,
681   ) {
682      ui.label("Background Color");
683      ui.horizontal(|ui| {
684         let color = self.color_select("2", visuals.bg, &colors, ui);
685         if let Some(color) = color {
686            visuals.bg = color.color32();
687         }
688
689         self.hsla_edit_button.show("bg1", ui, &mut visuals.bg);
690      });
691
692      ui.label("Background Hover Color");
693      ui.horizontal(|ui| {
694         let color = self.color_select("3", visuals.bg_hover, &colors, ui);
695         if let Some(color) = color {
696            visuals.bg_hover = color.color32();
697         }
698
699         self.hsla_edit_button.show("bg_hover1", ui, &mut visuals.bg_hover);
700      });
701
702      ui.label("Border Color");
703      ui.horizontal(|ui| {
704         let color = self.color_select("4", visuals.border.color, &colors, ui);
705
706         if let Some(color) = color {
707            visuals.border.color = color.color32();
708         }
709
710         self.hsla_edit_button.show("border1", ui, &mut visuals.border.color);
711      });
712
713      ui.label("Border Hover Color");
714      ui.horizontal(|ui| {
715         let color = self.color_select("5", visuals.border_hover.color, &colors, ui);
716         if let Some(color) = color {
717            visuals.border_hover.color = color.color32();
718         }
719
720         self.hsla_edit_button.show(
721            "border_hover1",
722            ui,
723            &mut visuals.border_hover.color,
724         );
725      });
726
727      ui.label("Border Open Color");
728      ui.horizontal(|ui| {
729         let color = self.color_select("6", visuals.border_open.color, &colors, ui);
730         if let Some(color) = color {
731            visuals.border_open.color = color.color32();
732         }
733
734         self.hsla_edit_button.show("border_open1", ui, &mut visuals.border_open.color);
735      });
736
737      ui.label("Corner Radius");
738      ui.add(Slider::new(&mut visuals.corner_radius.ne, 0..=100).text("NE"));
739      ui.add(Slider::new(&mut visuals.corner_radius.nw, 0..=100).text("NW"));
740      ui.add(Slider::new(&mut visuals.corner_radius.se, 0..=100).text("SE"));
741      ui.add(Slider::new(&mut visuals.corner_radius.sw, 0..=100).text("SW"));
742
743      ui.label("Shadow");
744      ui.horizontal(|ui| {
745         let color = self.color_select("9", visuals.shadow.color, &colors, ui);
746         if let Some(color) = color {
747            visuals.shadow.color = color.color32();
748         }
749
750         color_edit_button_srgba(
751            ui,
752            &mut visuals.shadow.color,
753            Alpha::BlendOrAdditive,
754         );
755      });
756
757      ui.label("Shadow Offset");
758      ui.add(Slider::new(&mut visuals.shadow.offset[0], -100..=100).text("Offset X"));
759      ui.add(Slider::new(&mut visuals.shadow.offset[1], -100..=100).text("Offset Y"));
760
761      ui.label("Shadow Blur");
762      ui.add(Slider::new(&mut visuals.shadow.blur, 0..=100).text("Blur"));
763
764      ui.label("Shadow Spread");
765      ui.add(Slider::new(&mut visuals.shadow.spread, 0..=100).text("Spread"));
766   }
767
768   fn text_edit_visuals(
769      &mut self,
770      colors: ThemeColors,
771      visuals: &mut TextEditVisuals,
772      ui: &mut Ui,
773   ) {
774      ui.label("Text Color");
775      ui.horizontal(|ui| {
776         let color = self.color_select("1", visuals.text, &colors, ui);
777         if let Some(color) = color {
778            visuals.text = color.color32();
779         }
780
781         self.hsla_edit_button.show("text1", ui, &mut visuals.text);
782      });
783
784      ui.label("Background Color");
785      ui.horizontal(|ui| {
786         let color = self.color_select("2", visuals.bg, &colors, ui);
787         if let Some(color) = color {
788            visuals.bg = color.color32();
789         }
790
791         self.hsla_edit_button.show("bg1", ui, &mut visuals.bg);
792      });
793
794      ui.label("Border Color");
795      ui.horizontal(|ui| {
796         let color = self.color_select("3", visuals.border.color, &colors, ui);
797
798         if let Some(color) = color {
799            visuals.border.color = color.color32();
800         }
801
802         self.hsla_edit_button.show("border1", ui, &mut visuals.border.color);
803      });
804
805      ui.label("Border Hover Color");
806      ui.horizontal(|ui| {
807         let color = self.color_select("4", visuals.border_hover.color, &colors, ui);
808         if let Some(color) = color {
809            visuals.border_hover.color = color.color32();
810         }
811
812         self.hsla_edit_button.show(
813            "border_hover1",
814            ui,
815            &mut visuals.border_hover.color,
816         );
817      });
818
819      ui.label("Border Open Color");
820      ui.horizontal(|ui| {
821         let color = self.color_select("5", visuals.border_open.color, &colors, ui);
822         if let Some(color) = color {
823            visuals.border_open.color = color.color32();
824         }
825
826         self.hsla_edit_button.show("border_open1", ui, &mut visuals.border_open.color);
827      });
828
829      ui.label("Corner Radius");
830      ui.add(Slider::new(&mut visuals.corner_radius.ne, 0..=100).text("NE"));
831      ui.add(Slider::new(&mut visuals.corner_radius.nw, 0..=100).text("NW"));
832      ui.add(Slider::new(&mut visuals.corner_radius.se, 0..=100).text("SE"));
833      ui.add(Slider::new(&mut visuals.corner_radius.sw, 0..=100).text("SW"));
834
835      ui.label("Shadow");
836      ui.horizontal(|ui| {
837         let color = self.color_select("6", visuals.shadow.color, &colors, ui);
838         if let Some(color) = color {
839            visuals.shadow.color = color.color32();
840         }
841
842         color_edit_button_srgba(
843            ui,
844            &mut visuals.shadow.color,
845            Alpha::BlendOrAdditive,
846         );
847      });
848
849      ui.label("Shadow Offset");
850      ui.add(Slider::new(&mut visuals.shadow.offset[0], -100..=100).text("Offset X"));
851      ui.add(Slider::new(&mut visuals.shadow.offset[1], -100..=100).text("Offset Y"));
852
853      ui.label("Shadow Blur");
854      ui.add(Slider::new(&mut visuals.shadow.blur, 0..=100).text("Blur"));
855
856      ui.label("Shadow Spread");
857      ui.add(Slider::new(&mut visuals.shadow.spread, 0..=100).text("Spread"));
858   }
859
860   /*
861   fn widget_settings(&mut self, theme: &mut Theme, ui: &mut Ui) {
862       self.select_widget_state(ui);
863
864       let widget_visuals = match self.widget_state {
865           WidgetState::NonInteractive => &mut theme.style.visuals.widgets.noninteractive,
866           WidgetState::Inactive => &mut theme.style.visuals.widgets.inactive,
867           WidgetState::Hovered => &mut theme.style.visuals.widgets.hovered,
868           WidgetState::Active => &mut theme.style.visuals.widgets.active,
869           WidgetState::Open => &mut theme.style.visuals.widgets.open,
870       };
871
872       ui.label("Background Fill Color");
873
874       ui.horizontal(|ui| {
875           let color = self.color_select("1", widget_visuals.bg_fill, &theme.colors, ui);
876           if let Some(color) = color {
877               widget_visuals.bg_fill = color.color32();
878           }
879
880           self.hsla_edit_button
881               .show("bg_fill1", ui, &mut widget_visuals.bg_fill);
882       });
883
884       ui.label("Weak Background Fill Color");
885
886       ui.horizontal(|ui| {
887           let color = self.color_select("2", widget_visuals.weak_bg_fill, &theme.colors, ui);
888           if let Some(color) = color {
889               widget_visuals.weak_bg_fill = color.color32();
890           }
891
892           self.hsla_edit_button
893               .show("weak_bg_fill1", ui, &mut widget_visuals.weak_bg_fill);
894       });
895
896       ui.label("Background Stroke Width");
897       ui.add(Slider::new(&mut widget_visuals.bg_stroke.width, 0.0..=10.0));
898
899       ui.label("Background Stroke Color");
900       ui.horizontal(|ui| {
901           let color = self.color_select("3", widget_visuals.bg_stroke.color, &theme.colors, ui);
902           if let Some(color) = color {
903               widget_visuals.bg_stroke.color = color.color32();
904           }
905
906           self.hsla_edit_button
907               .show("bg_stroke_color1", ui, &mut widget_visuals.bg_stroke.color);
908       });
909
910       ui.label("Rounding");
911       edit_corner_radius(&mut widget_visuals.corner_radius, ui);
912
913       ui.label("Foreground Stroke Width");
914       ui.add(Slider::new(&mut widget_visuals.fg_stroke.width, 0.0..=10.0));
915
916       ui.label("Foreground Stroke Color");
917       ui.horizontal(|ui| {
918           let color = self.color_select("4", widget_visuals.fg_stroke.color, &theme.colors, ui);
919
920           if let Some(color) = color {
921               widget_visuals.fg_stroke.color = color.color32();
922           }
923
924           self.hsla_edit_button
925               .show("fg_stroke_color1", ui, &mut widget_visuals.fg_stroke.color);
926       });
927
928       ui.label("Expansion");
929       ui.add(Slider::new(&mut widget_visuals.expansion, 0.0..=100.0).text("Expansion"));
930   }
931   */
932
933   fn frame_settings(&mut self, id: &str, frame: &mut Frame, colors: &ThemeColors, ui: &mut Ui) {
934      CollapsingHeader::new("Inner & Outter Margin").show(ui, |ui| {
935         ui.label("Inner Margin");
936         edit_margin(&mut frame.inner_margin, ui);
937
938         ui.label("Outter Margin");
939         edit_margin(&mut frame.outer_margin, ui);
940      });
941
942      ui.label("Rounding");
943      edit_corner_radius(&mut frame.corner_radius, ui);
944
945      ui.label("Shadow");
946      edit_shadow(&mut frame.shadow, ui);
947
948      ui.label("Fill Color");
949      self.hsla_edit_button.show(&format!("{id}_fill"), ui, &mut frame.fill);
950
951      ui.label("Stroke Width & Color");
952      self.edit_stroke(id, colors, &mut frame.stroke, ui);
953   }
954
955   fn _select_widget_state(&mut self, ui: &mut Ui) {
956      ComboBox::from_label("")
957         .selected_text(self.widget_state.to_str())
958         .show_ui(ui, |ui| {
959            for widget in self.widget_state.to_vec() {
960               let value = ui.selectable_value(
961                  &mut self.widget_state,
962                  widget.clone(),
963                  widget.to_str(),
964               );
965
966               if value.clicked() {
967                  self.widget_state = widget;
968               }
969            }
970         });
971   }
972
973   fn color_select(
974      &mut self,
975      id: &str,
976      current_color: Color32,
977      colors: &ThemeColors,
978      ui: &mut Ui,
979   ) -> Option<Color> {
980      let all_colors = Color::all_colors_from(colors);
981
982      let mut selected_color = None;
983      let current_color_name = Color::name_from(current_color, colors);
984
985      ComboBox::from_id_salt(id).selected_text(current_color_name).show_ui(ui, |ui| {
986         for color in all_colors {
987            let value = ui.selectable_value(&mut self.color, color.clone(), color.to_str());
988
989            if value.clicked() {
990               selected_color = Some(color);
991            }
992         }
993      });
994      selected_color
995   }
996
997   fn edit_stroke(&mut self, id: &str, colors: &ThemeColors, stroke: &mut Stroke, ui: &mut Ui) {
998      ui.add(Slider::new(&mut stroke.width, 0.0..=100.0).text("Stroke Width"));
999
1000      ui.label("Stroke Color");
1001
1002      ui.horizontal(|ui| {
1003         let color = self.color_select(
1004            &format!("{id}_stroke_pal"),
1005            stroke.color,
1006            colors,
1007            ui,
1008         );
1009         if let Some(color) = color {
1010            stroke.color = color.color32();
1011         }
1012
1013         color_edit_button_srgba(ui, &mut stroke.color, Alpha::BlendOrAdditive);
1014
1015         self.hsla_edit_button.show(&format!("{id}_stroke"), ui, &mut stroke.color);
1016      });
1017   }
1018}
1019
1020fn edit_margin(margin: &mut Margin, ui: &mut Ui) {
1021   ui.add(Slider::new(&mut margin.top, 0..=127).text("Top"));
1022   ui.add(Slider::new(&mut margin.bottom, 0..=127).text("Bottom"));
1023   ui.add(Slider::new(&mut margin.left, 0..=127).text("Left"));
1024   ui.add(Slider::new(&mut margin.right, 0..=127).text("Right"));
1025}
1026
1027fn edit_corner_radius(corner_radius: &mut CornerRadius, ui: &mut Ui) {
1028   ui.add(Slider::new(&mut corner_radius.nw, 0..=255).text("Top Left"));
1029   ui.add(Slider::new(&mut corner_radius.ne, 0..=255).text("Top Right"));
1030   ui.add(Slider::new(&mut corner_radius.sw, 0..=255).text("Bottom Left"));
1031   ui.add(Slider::new(&mut corner_radius.se, 0..=255).text("Bottom Right"));
1032}
1033
1034fn edit_shadow(shadow: &mut Shadow, ui: &mut Ui) {
1035   ui.add(Slider::new(&mut shadow.offset[0], -128..=127).text("Offset X"));
1036   ui.add(Slider::new(&mut shadow.offset[1], -128..=127).text("Offset Y"));
1037   ui.add(Slider::new(&mut shadow.blur, 0..=255).text("Blur"));
1038   ui.add(Slider::new(&mut shadow.spread, 0..=255).text("Spread"));
1039
1040   ui.label("Shadow Color");
1041   color_edit_button_srgba(ui, &mut shadow.color, Alpha::BlendOrAdditive);
1042}
1043
1044/// Swatch button that opens an HSLA / hex editor popup.
1045#[derive(Clone)]
1046pub struct HslaEditButton {
1047   from_hex_text: String,
1048}
1049
1050impl HslaEditButton {
1051   /// Empty hex field.
1052   pub fn new() -> Self {
1053      Self {
1054         from_hex_text: String::new(),
1055      }
1056   }
1057
1058   /// Paint the swatch and popup. Mutates `color32` when the user edits it.
1059   pub fn show(&mut self, id: &str, ui: &mut Ui, color32: &mut Color32) -> Response {
1060      let stroke = Stroke::new(1.0, Color32::GRAY);
1061      let button_size = Vec2::new(50.0, 20.0);
1062      let (rect, mut response) = ui.allocate_exact_size(button_size, Sense::click());
1063      ui.painter().rect_filled(rect, 4.0, *color32);
1064      ui.painter().rect_stroke(rect, 4.0, stroke, StrokeKind::Inside);
1065
1066      let popup_id = ui.make_persistent_id(id);
1067
1068      let set_command = if response.clicked() {
1069         Some(SetOpenCommand::Toggle)
1070      } else {
1071         None
1072      };
1073
1074      let close_behavior = PopupCloseBehavior::CloseOnClickOutside;
1075      response.layer_id.order = Order::Debug;
1076
1077      let popup = Popup::from_response(&response)
1078         .close_behavior(close_behavior)
1079         .open_memory(set_command);
1080
1081      let working_id = popup_id.with("working_hsla");
1082      let mut working_hsla = ui
1083         .memory(|mem| mem.data.get_temp(working_id))
1084         .unwrap_or_else(|| Hsla::from_color32(*color32));
1085
1086      let popup_res = popup.show(|ui| self.hsla_picker_ui(ui, &mut working_hsla));
1087
1088      if let Some(inner) = popup_res {
1089         // if color changed
1090         if inner.inner {
1091            ui.memory_mut(|mem| mem.data.insert_temp(working_id, working_hsla));
1092            *color32 = working_hsla.to_color32();
1093            response.mark_changed();
1094         }
1095      } else {
1096         ui.memory_mut(|mem| mem.data.remove::<Hsla>(working_id));
1097      }
1098
1099      response
1100   }
1101
1102   // The core HSLA picker UI (sliders, 2D square, preview). Returns true if changed.
1103   fn hsla_picker_ui(&mut self, ui: &mut Ui, hsla: &mut Hsla) -> bool {
1104      let mut changed = false;
1105      let stroke = Stroke::new(1.0, Color32::GRAY);
1106
1107      ui.horizontal(|ui| {
1108         ui.set_width(200.0);
1109
1110         // Left: 2D S-L square + hue slider below it
1111         ui.vertical(|ui| {
1112            changed |= sl_2d_picker(ui, hsla);
1113            changed |= hue_slider(ui, hsla);
1114            changed |= alpha_slider(ui, hsla);
1115         });
1116
1117         // Right: Preview + numeric controls
1118         ui.vertical(|ui| {
1119            // Preview rect
1120            let preview_size = Vec2::new(80.0, 80.0);
1121            let (rect, _) = ui.allocate_exact_size(preview_size, Sense::hover());
1122            ui.painter().rect_filled(rect, 4.0, hsla.to_color32());
1123            ui.painter().rect_stroke(rect, 4.0, stroke, StrokeKind::Inside);
1124
1125            ui.label(RichText::new("Preview").strong());
1126
1127            // Numeric sliders for precision
1128            ui.add_space(10.0);
1129            changed |= ui.add(Slider::new(&mut hsla.h, 0.0..=360.0).text("Hue")).changed();
1130            changed |= ui.add(Slider::new(&mut hsla.s, 0.0..=100.0).text("Saturation")).changed();
1131            changed |= ui.add(Slider::new(&mut hsla.l, 0.0..=100.0).text("Lightness")).changed();
1132            changed |= ui.add(Slider::new(&mut hsla.a, 0.0..=1.0).text("Alpha")).changed();
1133         });
1134
1135         ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
1136            ui.vertical(|ui| {
1137               // RGBA copy button
1138               ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
1139                  let (r, g, b, a) = hsla.to_rgba_components();
1140                  let text = RichText::new(format!("RGBA ({r}, {g}, {b}, {a})"));
1141                  let button = Button::new(text).min_size(vec2(160.0, 15.0));
1142                  if ui.add(button).clicked() {
1143                     ui.ctx().copy_text(format!("({r}, {g}, {b}, {a})"));
1144                  }
1145               });
1146
1147               // HEX copy button
1148               ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
1149                  let hex_color = HexColor::Hex6(hsla.to_color32());
1150                  let text = RichText::new(format!("HEX {}", hex_color));
1151                  let button = Button::new(text).min_size(vec2(160.0, 15.0));
1152                  if ui.add(button).clicked() {
1153                     ui.ctx().copy_text(format!("{}", hex_color));
1154                  }
1155               });
1156
1157               // From RBG to HSLA
1158               ui.with_layout(Layout::left_to_right(Align::Min), |ui| {
1159                  let text = RichText::new("Convert From HEX");
1160                  let button = Button::new(text).small();
1161                  ui.add(TextEdit::singleline(&mut self.from_hex_text));
1162                  if ui.add(button).clicked() {
1163                     let new_color = Hsla::from_hex(&self.from_hex_text);
1164                     if let Some(new_color) = new_color {
1165                        *hsla = new_color;
1166                        changed = true;
1167                     }
1168                  }
1169               });
1170            });
1171         });
1172      });
1173
1174      changed
1175   }
1176}
1177
1178// 2D picker for Saturation (x) and Lightness (y)
1179fn sl_2d_picker(ui: &mut Ui, hsla: &mut Hsla) -> bool {
1180   let size = Vec2::new(150.0, 150.0);
1181   let (rect, response) = ui.allocate_exact_size(size, Sense::drag());
1182
1183   let mut changed = false;
1184
1185   if response.dragged() {
1186      if let Some(pos) = response.hover_pos() {
1187         let relative = pos - rect.min;
1188         hsla.s = (relative.x / size.x).clamp(0.0, 1.0) * 100.0;
1189         hsla.l = (1.0 - (relative.y / size.y)).clamp(0.0, 1.0) * 100.0; // Top: high L, bottom: low L
1190         changed = true;
1191      }
1192   }
1193
1194   // Paint gradient background (grid of small rects for simplicity)
1195   let painter = ui.painter();
1196   const RES: usize = 64; // Higher for smoother, but 64 is fast and looks good
1197   let cell_size = size / RES as f32;
1198   for i in 0..RES {
1199      for j in 0..RES {
1200         let s = (i as f32 / (RES - 1) as f32) * 100.0;
1201         let l = (1.0 - (j as f32 / (RES - 1) as f32)) * 100.0; // Top: l=100, bottom: l=0
1202         let temp_hsla = Hsla {
1203            h: hsla.h,
1204            s,
1205            l,
1206            a: 1.0,
1207         };
1208         let color = temp_hsla.to_color32();
1209
1210         let min = rect.min + Vec2::new(i as f32 * cell_size.x, j as f32 * cell_size.y);
1211         let cell_rect = Rect::from_min_size(min, cell_size);
1212         painter.rect_filled(cell_rect, 0.0, color);
1213      }
1214   }
1215
1216   // Draw cursor at current position
1217   let x = (hsla.s / 100.0) * size.x;
1218   let y = (1.0 - hsla.l / 100.0) * size.y;
1219   let cursor_pos = rect.min + Vec2::new(x, y);
1220   painter.circle_stroke(cursor_pos, 5.0, Stroke::new(1.0, Color32::WHITE));
1221   painter.circle_stroke(cursor_pos, 5.0, Stroke::new(1.0, Color32::BLACK));
1222
1223   // Outline the square
1224   painter.rect_stroke(
1225      rect,
1226      0.0,
1227      Stroke::new(1.0, Color32::GRAY),
1228      StrokeKind::Inside,
1229   );
1230
1231   changed
1232}
1233
1234// Hue gradient slider
1235fn hue_slider(ui: &mut Ui, hsla: &mut Hsla) -> bool {
1236   let size = Vec2::new(150.0, 20.0);
1237   let (rect, response) = ui.allocate_exact_size(size, Sense::drag());
1238
1239   let mut changed = false;
1240
1241   if response.dragged() {
1242      if let Some(pos) = response.hover_pos() {
1243         let relative_x = (pos.x - rect.min.x) / size.x;
1244         hsla.h = relative_x.clamp(0.0, 1.0) * 360.0;
1245         changed = true;
1246      }
1247   }
1248
1249   // Paint rainbow gradient
1250   let painter = ui.painter();
1251   const RES: usize = 128; // Smooth horizontal gradient
1252   let cell_width = size.x / RES as f32;
1253   for i in 0..RES {
1254      let h = (i as f32 / (RES - 1) as f32) * 360.0;
1255      let temp_hsla = Hsla {
1256         h,
1257         s: 100.0,
1258         l: 50.0,
1259         a: 1.0,
1260      }; // Full sat, mid light for vibrant rainbow
1261      let color = temp_hsla.to_color32();
1262
1263      let min = rect.min + Vec2::new(i as f32 * cell_width, 0.0);
1264      let cell_rect = Rect::from_min_size(min, Vec2::new(cell_width, size.y));
1265      painter.rect_filled(cell_rect, 0.0, color);
1266   }
1267
1268   // Cursor indicator (vertical line)
1269   let x = (hsla.h / 360.0) * size.x;
1270   let line_start = rect.min + Vec2::new(x, 0.0);
1271   let line_end = rect.min + Vec2::new(x, size.y);
1272   painter.line_segment(
1273      [line_start, line_end],
1274      Stroke::new(2.0, Color32::WHITE),
1275   );
1276
1277   // Outline
1278   painter.rect_stroke(
1279      rect,
1280      4.0,
1281      Stroke::new(1.0, Color32::GRAY),
1282      StrokeKind::Inside,
1283   );
1284
1285   changed
1286}
1287
1288fn alpha_slider(ui: &mut Ui, hsla: &mut Hsla) -> bool {
1289   let size = Vec2::new(150.0, 20.0);
1290   let (rect, response) = ui.allocate_exact_size(size, Sense::drag());
1291   let mut changed = false;
1292
1293   if response.dragged() {
1294      if let Some(pos) = response.hover_pos() {
1295         let relative_x = (pos.x - rect.min.x) / size.x;
1296         hsla.a = relative_x.clamp(0.0, 1.0);
1297         changed = true;
1298      }
1299   }
1300
1301   let painter = ui.painter();
1302   // Paint checkerboard FIRST for transparency visibility
1303   let checker_size = 5.0;
1304
1305   for x in (0..=((size.x / checker_size) as usize)).step_by(1) {
1306      for y in (0..=((size.y / checker_size) as usize)).step_by(1) {
1307         let color = if (x + y) % 2 == 0 {
1308            Color32::GRAY
1309         } else {
1310            Color32::LIGHT_GRAY
1311         };
1312         let min = rect.min + Vec2::new(x as f32 * checker_size, y as f32 * checker_size);
1313         let cell_rect = Rect::from_min_size(min, Vec2::splat(checker_size)).intersect(rect);
1314         painter.rect_filled(cell_rect, 0.0, color);
1315      }
1316   }
1317
1318   // Then paint gradient on top
1319   const RES: usize = 64;
1320   let cell_width = size.x / RES as f32;
1321
1322   for i in 0..RES {
1323      let a = i as f32 / (RES - 1) as f32;
1324      let temp_hsla = Hsla {
1325         h: hsla.h,
1326         s: hsla.s,
1327         l: hsla.l,
1328         a,
1329      };
1330
1331      let color = temp_hsla.to_color32();
1332      let min = rect.min + Vec2::new(i as f32 * cell_width, 0.0);
1333      let cell_rect = Rect::from_min_size(min, Vec2::new(cell_width, size.y));
1334      painter.rect_filled(cell_rect, 0.0, color);
1335   }
1336
1337   // Cursor line
1338   let x = hsla.a * size.x;
1339   let line_start = rect.min + Vec2::new(x, 0.0);
1340   let line_end = rect.min + Vec2::new(x, size.y);
1341
1342   painter.line_segment(
1343      [line_start, line_end],
1344      Stroke::new(2.0, Color32::WHITE),
1345   );
1346
1347   // Outline
1348   painter.rect_stroke(
1349      rect,
1350      4.0,
1351      Stroke::new(1.0, Color32::GRAY),
1352      StrokeKind::Inside,
1353   );
1354   changed
1355}