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