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