1pub mod palette;
3
4pub use palette::Palette;
5
6use crate::application;
7use crate::button;
8use crate::checkbox;
9use crate::container;
10use crate::core::widget::text;
11use crate::menu;
12use crate::pane_grid;
13use crate::pick_list;
14use crate::progress_bar;
15use crate::qr_code;
16use crate::radio;
17use crate::rule;
18use crate::scrollable;
19use crate::slider;
20use crate::svg;
21use crate::text_editor;
22use crate::text_input;
23use crate::toggler;
24
25use crate::core::{Background, Border, Color, Shadow, Vector};
26
27use std::fmt;
28use std::rc::Rc;
29use std::sync::Arc;
30
31#[derive(Debug, Clone, PartialEq, Default)]
33pub enum Theme {
34 #[default]
36 Light,
37 Dark,
39 Dracula,
41 Nord,
43 SolarizedLight,
45 SolarizedDark,
47 GruvboxLight,
49 GruvboxDark,
51 CatppuccinLatte,
53 CatppuccinFrappe,
55 CatppuccinMacchiato,
57 CatppuccinMocha,
59 TokyoNight,
61 TokyoNightStorm,
63 TokyoNightLight,
65 KanagawaWave,
67 KanagawaDragon,
69 KanagawaLotus,
71 Moonfly,
73 Nightfly,
75 Oxocarbon,
77 Custom(Arc<Custom>),
79}
80
81impl Theme {
82 pub const ALL: &'static [Self] = &[
84 Self::Light,
85 Self::Dark,
86 Self::Dracula,
87 Self::Nord,
88 Self::SolarizedLight,
89 Self::SolarizedDark,
90 Self::GruvboxLight,
91 Self::GruvboxDark,
92 Self::CatppuccinLatte,
93 Self::CatppuccinFrappe,
94 Self::CatppuccinMacchiato,
95 Self::CatppuccinMocha,
96 Self::TokyoNight,
97 Self::TokyoNightStorm,
98 Self::TokyoNightLight,
99 Self::KanagawaWave,
100 Self::KanagawaDragon,
101 Self::KanagawaLotus,
102 Self::Moonfly,
103 Self::Nightfly,
104 Self::Oxocarbon,
105 ];
106
107 pub fn custom(name: String, palette: Palette) -> Self {
109 Self::custom_with_fn(name, palette, palette::Extended::generate)
110 }
111
112 pub fn custom_with_fn(
115 name: String,
116 palette: Palette,
117 generate: impl FnOnce(Palette) -> palette::Extended,
118 ) -> Self {
119 Self::Custom(Arc::new(Custom::with_fn(name, palette, generate)))
120 }
121
122 pub fn palette(&self) -> Palette {
124 match self {
125 Self::Light => Palette::LIGHT,
126 Self::Dark => Palette::DARK,
127 Self::Dracula => Palette::DRACULA,
128 Self::Nord => Palette::NORD,
129 Self::SolarizedLight => Palette::SOLARIZED_LIGHT,
130 Self::SolarizedDark => Palette::SOLARIZED_DARK,
131 Self::GruvboxLight => Palette::GRUVBOX_LIGHT,
132 Self::GruvboxDark => Palette::GRUVBOX_DARK,
133 Self::CatppuccinLatte => Palette::CATPPUCCIN_LATTE,
134 Self::CatppuccinFrappe => Palette::CATPPUCCIN_FRAPPE,
135 Self::CatppuccinMacchiato => Palette::CATPPUCCIN_MACCHIATO,
136 Self::CatppuccinMocha => Palette::CATPPUCCIN_MOCHA,
137 Self::TokyoNight => Palette::TOKYO_NIGHT,
138 Self::TokyoNightStorm => Palette::TOKYO_NIGHT_STORM,
139 Self::TokyoNightLight => Palette::TOKYO_NIGHT_LIGHT,
140 Self::KanagawaWave => Palette::KANAGAWA_WAVE,
141 Self::KanagawaDragon => Palette::KANAGAWA_DRAGON,
142 Self::KanagawaLotus => Palette::KANAGAWA_LOTUS,
143 Self::Moonfly => Palette::MOONFLY,
144 Self::Nightfly => Palette::NIGHTFLY,
145 Self::Oxocarbon => Palette::OXOCARBON,
146 Self::Custom(custom) => custom.palette,
147 }
148 }
149
150 pub fn extended_palette(&self) -> &palette::Extended {
152 match self {
153 Self::Light => &palette::EXTENDED_LIGHT,
154 Self::Dark => &palette::EXTENDED_DARK,
155 Self::Dracula => &palette::EXTENDED_DRACULA,
156 Self::Nord => &palette::EXTENDED_NORD,
157 Self::SolarizedLight => &palette::EXTENDED_SOLARIZED_LIGHT,
158 Self::SolarizedDark => &palette::EXTENDED_SOLARIZED_DARK,
159 Self::GruvboxLight => &palette::EXTENDED_GRUVBOX_LIGHT,
160 Self::GruvboxDark => &palette::EXTENDED_GRUVBOX_DARK,
161 Self::CatppuccinLatte => &palette::EXTENDED_CATPPUCCIN_LATTE,
162 Self::CatppuccinFrappe => &palette::EXTENDED_CATPPUCCIN_FRAPPE,
163 Self::CatppuccinMacchiato => {
164 &palette::EXTENDED_CATPPUCCIN_MACCHIATO
165 }
166 Self::CatppuccinMocha => &palette::EXTENDED_CATPPUCCIN_MOCHA,
167 Self::TokyoNight => &palette::EXTENDED_TOKYO_NIGHT,
168 Self::TokyoNightStorm => &palette::EXTENDED_TOKYO_NIGHT_STORM,
169 Self::TokyoNightLight => &palette::EXTENDED_TOKYO_NIGHT_LIGHT,
170 Self::KanagawaWave => &palette::EXTENDED_KANAGAWA_WAVE,
171 Self::KanagawaDragon => &palette::EXTENDED_KANAGAWA_DRAGON,
172 Self::KanagawaLotus => &palette::EXTENDED_KANAGAWA_LOTUS,
173 Self::Moonfly => &palette::EXTENDED_MOONFLY,
174 Self::Nightfly => &palette::EXTENDED_NIGHTFLY,
175 Self::Oxocarbon => &palette::EXTENDED_OXOCARBON,
176 Self::Custom(custom) => &custom.extended,
177 }
178 }
179}
180
181impl fmt::Display for Theme {
182 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
183 match self {
184 Self::Light => write!(f, "Light"),
185 Self::Dark => write!(f, "Dark"),
186 Self::Dracula => write!(f, "Dracula"),
187 Self::Nord => write!(f, "Nord"),
188 Self::SolarizedLight => write!(f, "Solarized Light"),
189 Self::SolarizedDark => write!(f, "Solarized Dark"),
190 Self::GruvboxLight => write!(f, "Gruvbox Light"),
191 Self::GruvboxDark => write!(f, "Gruvbox Dark"),
192 Self::CatppuccinLatte => write!(f, "Catppuccin Latte"),
193 Self::CatppuccinFrappe => write!(f, "Catppuccin Frappé"),
194 Self::CatppuccinMacchiato => write!(f, "Catppuccin Macchiato"),
195 Self::CatppuccinMocha => write!(f, "Catppuccin Mocha"),
196 Self::TokyoNight => write!(f, "Tokyo Night"),
197 Self::TokyoNightStorm => write!(f, "Tokyo Night Storm"),
198 Self::TokyoNightLight => write!(f, "Tokyo Night Light"),
199 Self::KanagawaWave => write!(f, "Kanagawa Wave"),
200 Self::KanagawaDragon => write!(f, "Kanagawa Dragon"),
201 Self::KanagawaLotus => write!(f, "Kanagawa Lotus"),
202 Self::Moonfly => write!(f, "Moonfly"),
203 Self::Nightfly => write!(f, "Nightfly"),
204 Self::Oxocarbon => write!(f, "Oxocarbon"),
205 Self::Custom(custom) => custom.fmt(f),
206 }
207 }
208}
209
210#[derive(Debug, Clone, PartialEq)]
212pub struct Custom {
213 name: String,
214 palette: Palette,
215 extended: palette::Extended,
216}
217
218impl Custom {
219 pub fn new(name: String, palette: Palette) -> Self {
221 Self::with_fn(name, palette, palette::Extended::generate)
222 }
223
224 pub fn with_fn(
227 name: String,
228 palette: Palette,
229 generate: impl FnOnce(Palette) -> palette::Extended,
230 ) -> Self {
231 Self {
232 name,
233 palette,
234 extended: generate(palette),
235 }
236 }
237}
238
239impl fmt::Display for Custom {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 write!(f, "{}", self.name)
242 }
243}
244
245#[derive(Default)]
247pub enum Application {
248 #[default]
250 Default,
251 Custom(Box<dyn application::StyleSheet<Style = Theme>>),
253}
254
255impl Application {
256 pub fn custom(
258 custom: impl application::StyleSheet<Style = Theme> + 'static,
259 ) -> Self {
260 Self::Custom(Box::new(custom))
261 }
262}
263
264impl application::StyleSheet for Theme {
265 type Style = Application;
266
267 fn appearance(&self, style: &Self::Style) -> application::Appearance {
268 let palette = self.extended_palette();
269
270 match style {
271 Application::Default => application::Appearance {
272 background_color: palette.background.base.color,
273 text_color: palette.background.base.text,
274 },
275 Application::Custom(custom) => custom.appearance(self),
276 }
277 }
278}
279
280impl<T: Fn(&Theme) -> application::Appearance> application::StyleSheet for T {
281 type Style = Theme;
282
283 fn appearance(&self, style: &Self::Style) -> application::Appearance {
284 (self)(style)
285 }
286}
287
288#[derive(Default)]
290pub enum Button {
291 #[default]
293 Primary,
294 Secondary,
296 Positive,
298 Destructive,
300 Text,
304 Custom(Box<dyn button::StyleSheet<Style = Theme>>),
306}
307
308impl Button {
309 pub fn custom(
311 style_sheet: impl button::StyleSheet<Style = Theme> + 'static,
312 ) -> Self {
313 Self::Custom(Box::new(style_sheet))
314 }
315}
316
317impl button::StyleSheet for Theme {
318 type Style = Button;
319
320 fn active(&self, style: &Self::Style) -> button::Appearance {
321 let palette = self.extended_palette();
322
323 let appearance = button::Appearance {
324 border: Border::with_radius(2),
325 ..button::Appearance::default()
326 };
327
328 let from_pair = |pair: palette::Pair| button::Appearance {
329 background: Some(pair.color.into()),
330 text_color: pair.text,
331 ..appearance
332 };
333
334 match style {
335 Button::Primary => from_pair(palette.primary.strong),
336 Button::Secondary => from_pair(palette.secondary.base),
337 Button::Positive => from_pair(palette.success.base),
338 Button::Destructive => from_pair(palette.danger.base),
339 Button::Text => button::Appearance {
340 text_color: palette.background.base.text,
341 ..appearance
342 },
343 Button::Custom(custom) => custom.active(self),
344 }
345 }
346
347 fn hovered(&self, style: &Self::Style) -> button::Appearance {
348 let palette = self.extended_palette();
349
350 if let Button::Custom(custom) = style {
351 return custom.hovered(self);
352 }
353
354 let active = self.active(style);
355
356 let background = match style {
357 Button::Primary => Some(palette.primary.base.color),
358 Button::Secondary => Some(palette.background.strong.color),
359 Button::Positive => Some(palette.success.strong.color),
360 Button::Destructive => Some(palette.danger.strong.color),
361 Button::Text | Button::Custom(_) => None,
362 };
363
364 button::Appearance {
365 background: background.map(Background::from),
366 ..active
367 }
368 }
369
370 fn pressed(&self, style: &Self::Style) -> button::Appearance {
371 if let Button::Custom(custom) = style {
372 return custom.pressed(self);
373 }
374
375 button::Appearance {
376 shadow_offset: Vector::default(),
377 ..self.active(style)
378 }
379 }
380
381 fn disabled(&self, style: &Self::Style) -> button::Appearance {
382 if let Button::Custom(custom) = style {
383 return custom.disabled(self);
384 }
385
386 let active = self.active(style);
387
388 button::Appearance {
389 shadow_offset: Vector::default(),
390 background: active.background.map(|background| match background {
391 Background::Color(color) => Background::Color(Color {
392 a: color.a * 0.5,
393 ..color
394 }),
395 Background::Gradient(gradient) => {
396 Background::Gradient(gradient.mul_alpha(0.5))
397 }
398 }),
399 text_color: Color {
400 a: active.text_color.a * 0.5,
401 ..active.text_color
402 },
403 ..active
404 }
405 }
406}
407
408#[derive(Default)]
410pub enum Checkbox {
411 #[default]
413 Primary,
414 Secondary,
416 Success,
418 Danger,
420 Custom(Box<dyn checkbox::StyleSheet<Style = Theme>>),
422}
423
424impl checkbox::StyleSheet for Theme {
425 type Style = Checkbox;
426
427 fn active(
428 &self,
429 style: &Self::Style,
430 is_checked: bool,
431 ) -> checkbox::Appearance {
432 let palette = self.extended_palette();
433
434 match style {
435 Checkbox::Primary => checkbox_appearance(
436 palette.primary.strong.text,
437 palette.background.base,
438 palette.primary.strong,
439 is_checked,
440 ),
441 Checkbox::Secondary => checkbox_appearance(
442 palette.background.base.text,
443 palette.background.base,
444 palette.background.strong,
445 is_checked,
446 ),
447 Checkbox::Success => checkbox_appearance(
448 palette.success.base.text,
449 palette.background.base,
450 palette.success.base,
451 is_checked,
452 ),
453 Checkbox::Danger => checkbox_appearance(
454 palette.danger.base.text,
455 palette.background.base,
456 palette.danger.base,
457 is_checked,
458 ),
459 Checkbox::Custom(custom) => custom.active(self, is_checked),
460 }
461 }
462
463 fn hovered(
464 &self,
465 style: &Self::Style,
466 is_checked: bool,
467 ) -> checkbox::Appearance {
468 let palette = self.extended_palette();
469
470 match style {
471 Checkbox::Primary => checkbox_appearance(
472 palette.primary.strong.text,
473 palette.background.weak,
474 palette.primary.base,
475 is_checked,
476 ),
477 Checkbox::Secondary => checkbox_appearance(
478 palette.background.base.text,
479 palette.background.weak,
480 palette.background.strong,
481 is_checked,
482 ),
483 Checkbox::Success => checkbox_appearance(
484 palette.success.base.text,
485 palette.background.weak,
486 palette.success.base,
487 is_checked,
488 ),
489 Checkbox::Danger => checkbox_appearance(
490 palette.danger.base.text,
491 palette.background.weak,
492 palette.danger.base,
493 is_checked,
494 ),
495 Checkbox::Custom(custom) => custom.hovered(self, is_checked),
496 }
497 }
498
499 fn disabled(
500 &self,
501 style: &Self::Style,
502 is_checked: bool,
503 ) -> checkbox::Appearance {
504 let palette = self.extended_palette();
505
506 match style {
507 Checkbox::Primary => checkbox_appearance(
508 palette.primary.strong.text,
509 palette.background.weak,
510 palette.background.strong,
511 is_checked,
512 ),
513 Checkbox::Secondary => checkbox_appearance(
514 palette.background.strong.color,
515 palette.background.weak,
516 palette.background.weak,
517 is_checked,
518 ),
519 Checkbox::Success => checkbox_appearance(
520 palette.success.base.text,
521 palette.background.weak,
522 palette.success.weak,
523 is_checked,
524 ),
525 Checkbox::Danger => checkbox_appearance(
526 palette.danger.base.text,
527 palette.background.weak,
528 palette.danger.weak,
529 is_checked,
530 ),
531 Checkbox::Custom(custom) => custom.active(self, is_checked),
532 }
533 }
534}
535
536fn checkbox_appearance(
537 icon_color: Color,
538 base: palette::Pair,
539 accent: palette::Pair,
540 is_checked: bool,
541) -> checkbox::Appearance {
542 checkbox::Appearance {
543 background: Background::Color(if is_checked {
544 accent.color
545 } else {
546 base.color
547 }),
548 icon_color,
549 border: Border {
550 radius: 2.0.into(),
551 width: 1.0,
552 color: accent.color,
553 },
554 text_color: None,
555 }
556}
557
558#[derive(Default)]
560pub enum Container {
561 #[default]
563 Transparent,
564 Box,
566 Custom(Box<dyn container::StyleSheet<Style = Theme>>),
568}
569
570impl From<container::Appearance> for Container {
571 fn from(appearance: container::Appearance) -> Self {
572 Self::Custom(Box::new(move |_: &_| appearance))
573 }
574}
575
576impl<T: Fn(&Theme) -> container::Appearance + 'static> From<T> for Container {
577 fn from(f: T) -> Self {
578 Self::Custom(Box::new(f))
579 }
580}
581
582impl container::StyleSheet for Theme {
583 type Style = Container;
584
585 fn appearance(&self, style: &Self::Style) -> container::Appearance {
586 match style {
587 Container::Transparent => container::Appearance::default(),
588 Container::Box => {
589 let palette = self.extended_palette();
590
591 container::Appearance {
592 text_color: None,
593 background: Some(palette.background.weak.color.into()),
594 border: Border::with_radius(2),
595 shadow: Shadow::default(),
596 }
597 }
598 Container::Custom(custom) => custom.appearance(self),
599 }
600 }
601}
602
603impl<T: Fn(&Theme) -> container::Appearance> container::StyleSheet for T {
604 type Style = Theme;
605
606 fn appearance(&self, style: &Self::Style) -> container::Appearance {
607 (self)(style)
608 }
609}
610
611#[derive(Default)]
613pub enum Slider {
614 #[default]
616 Default,
617 Custom(Box<dyn slider::StyleSheet<Style = Theme>>),
619}
620
621impl slider::StyleSheet for Theme {
622 type Style = Slider;
623
624 fn active(&self, style: &Self::Style) -> slider::Appearance {
625 match style {
626 Slider::Default => {
627 let palette = self.extended_palette();
628
629 let handle = slider::Handle {
630 shape: slider::HandleShape::Rectangle {
631 width: 8,
632 border_radius: 4.0.into(),
633 },
634 color: Color::WHITE,
635 border_color: Color::WHITE,
636 border_width: 1.0,
637 };
638
639 slider::Appearance {
640 rail: slider::Rail {
641 colors: (
642 palette.primary.base.color,
643 palette.secondary.base.color,
644 ),
645 width: 4.0,
646 border_radius: 2.0.into(),
647 },
648 handle: slider::Handle {
649 color: palette.background.base.color,
650 border_color: palette.primary.base.color,
651 ..handle
652 },
653 }
654 }
655 Slider::Custom(custom) => custom.active(self),
656 }
657 }
658
659 fn hovered(&self, style: &Self::Style) -> slider::Appearance {
660 match style {
661 Slider::Default => {
662 let active = self.active(style);
663 let palette = self.extended_palette();
664
665 slider::Appearance {
666 handle: slider::Handle {
667 color: palette.primary.weak.color,
668 ..active.handle
669 },
670 ..active
671 }
672 }
673 Slider::Custom(custom) => custom.hovered(self),
674 }
675 }
676
677 fn dragging(&self, style: &Self::Style) -> slider::Appearance {
678 match style {
679 Slider::Default => {
680 let active = self.active(style);
681 let palette = self.extended_palette();
682
683 slider::Appearance {
684 handle: slider::Handle {
685 color: palette.primary.base.color,
686 ..active.handle
687 },
688 ..active
689 }
690 }
691 Slider::Custom(custom) => custom.dragging(self),
692 }
693 }
694}
695
696#[derive(Clone, Default)]
698pub enum Menu {
699 #[default]
701 Default,
702 Custom(Rc<dyn menu::StyleSheet<Style = Theme>>),
704}
705
706impl menu::StyleSheet for Theme {
707 type Style = Menu;
708
709 fn appearance(&self, style: &Self::Style) -> menu::Appearance {
710 match style {
711 Menu::Default => {
712 let palette = self.extended_palette();
713
714 menu::Appearance {
715 text_color: palette.background.weak.text,
716 background: palette.background.weak.color.into(),
717 border: Border {
718 width: 1.0,
719 radius: 0.0.into(),
720 color: palette.background.strong.color,
721 },
722 selected_text_color: palette.primary.strong.text,
723 selected_background: palette.primary.strong.color.into(),
724 }
725 }
726 Menu::Custom(custom) => custom.appearance(self),
727 }
728 }
729}
730
731impl From<PickList> for Menu {
732 fn from(pick_list: PickList) -> Self {
733 match pick_list {
734 PickList::Default => Self::Default,
735 PickList::Custom(_, menu) => Self::Custom(menu),
736 }
737 }
738}
739
740#[derive(Clone, Default)]
742pub enum PickList {
743 #[default]
745 Default,
746 Custom(
748 Rc<dyn pick_list::StyleSheet<Style = Theme>>,
749 Rc<dyn menu::StyleSheet<Style = Theme>>,
750 ),
751}
752
753impl pick_list::StyleSheet for Theme {
754 type Style = PickList;
755
756 fn active(&self, style: &Self::Style) -> pick_list::Appearance {
757 match style {
758 PickList::Default => {
759 let palette = self.extended_palette();
760
761 pick_list::Appearance {
762 text_color: palette.background.weak.text,
763 background: palette.background.weak.color.into(),
764 placeholder_color: palette.background.strong.color,
765 handle_color: palette.background.weak.text,
766 border: Border {
767 radius: 2.0.into(),
768 width: 1.0,
769 color: palette.background.strong.color,
770 },
771 }
772 }
773 PickList::Custom(custom, _) => custom.active(self),
774 }
775 }
776
777 fn hovered(&self, style: &Self::Style) -> pick_list::Appearance {
778 match style {
779 PickList::Default => {
780 let palette = self.extended_palette();
781
782 pick_list::Appearance {
783 text_color: palette.background.weak.text,
784 background: palette.background.weak.color.into(),
785 placeholder_color: palette.background.strong.color,
786 handle_color: palette.background.weak.text,
787 border: Border {
788 radius: 2.0.into(),
789 width: 1.0,
790 color: palette.primary.strong.color,
791 },
792 }
793 }
794 PickList::Custom(custom, _) => custom.hovered(self),
795 }
796 }
797}
798
799#[derive(Default)]
801pub enum Radio {
802 #[default]
804 Default,
805 Custom(Box<dyn radio::StyleSheet<Style = Theme>>),
807}
808
809impl radio::StyleSheet for Theme {
810 type Style = Radio;
811
812 fn active(
813 &self,
814 style: &Self::Style,
815 is_selected: bool,
816 ) -> radio::Appearance {
817 match style {
818 Radio::Default => {
819 let palette = self.extended_palette();
820
821 radio::Appearance {
822 background: Color::TRANSPARENT.into(),
823 dot_color: palette.primary.strong.color,
824 border_width: 1.0,
825 border_color: palette.primary.strong.color,
826 text_color: None,
827 }
828 }
829 Radio::Custom(custom) => custom.active(self, is_selected),
830 }
831 }
832
833 fn hovered(
834 &self,
835 style: &Self::Style,
836 is_selected: bool,
837 ) -> radio::Appearance {
838 match style {
839 Radio::Default => {
840 let active = self.active(style, is_selected);
841 let palette = self.extended_palette();
842
843 radio::Appearance {
844 dot_color: palette.primary.strong.color,
845 background: palette.primary.weak.color.into(),
846 ..active
847 }
848 }
849 Radio::Custom(custom) => custom.hovered(self, is_selected),
850 }
851 }
852}
853
854#[derive(Default)]
856pub enum Toggler {
857 #[default]
859 Default,
860 Custom(Box<dyn toggler::StyleSheet<Style = Theme>>),
862}
863
864impl toggler::StyleSheet for Theme {
865 type Style = Toggler;
866
867 fn active(
868 &self,
869 style: &Self::Style,
870 is_active: bool,
871 ) -> toggler::Appearance {
872 match style {
873 Toggler::Default => {
874 let palette = self.extended_palette();
875
876 toggler::Appearance {
877 background: if is_active {
878 palette.primary.strong.color
879 } else {
880 palette.background.strong.color
881 },
882 background_border_width: 0.0,
883 background_border_color: Color::TRANSPARENT,
884 foreground: if is_active {
885 palette.primary.strong.text
886 } else {
887 palette.background.base.color
888 },
889 foreground_border_width: 0.0,
890 foreground_border_color: Color::TRANSPARENT,
891 }
892 }
893 Toggler::Custom(custom) => custom.active(self, is_active),
894 }
895 }
896
897 fn hovered(
898 &self,
899 style: &Self::Style,
900 is_active: bool,
901 ) -> toggler::Appearance {
902 match style {
903 Toggler::Default => {
904 let palette = self.extended_palette();
905
906 toggler::Appearance {
907 foreground: if is_active {
908 Color {
909 a: 0.5,
910 ..palette.primary.strong.text
911 }
912 } else {
913 palette.background.weak.color
914 },
915 ..self.active(style, is_active)
916 }
917 }
918 Toggler::Custom(custom) => custom.hovered(self, is_active),
919 }
920 }
921}
922
923#[derive(Default)]
925pub enum PaneGrid {
926 #[default]
928 Default,
929 Custom(Box<dyn pane_grid::StyleSheet<Style = Theme>>),
931}
932
933impl pane_grid::StyleSheet for Theme {
934 type Style = PaneGrid;
935
936 fn hovered_region(&self, style: &Self::Style) -> pane_grid::Appearance {
937 match style {
938 PaneGrid::Default => {
939 let palette = self.extended_palette();
940
941 pane_grid::Appearance {
942 background: Background::Color(Color {
943 a: 0.5,
944 ..palette.primary.base.color
945 }),
946 border: Border {
947 width: 2.0,
948 color: palette.primary.strong.color,
949 radius: 0.0.into(),
950 },
951 }
952 }
953 PaneGrid::Custom(custom) => custom.hovered_region(self),
954 }
955 }
956
957 fn picked_split(&self, style: &Self::Style) -> Option<pane_grid::Line> {
958 match style {
959 PaneGrid::Default => {
960 let palette = self.extended_palette();
961
962 Some(pane_grid::Line {
963 color: palette.primary.strong.color,
964 width: 2.0,
965 })
966 }
967 PaneGrid::Custom(custom) => custom.picked_split(self),
968 }
969 }
970
971 fn hovered_split(&self, style: &Self::Style) -> Option<pane_grid::Line> {
972 match style {
973 PaneGrid::Default => {
974 let palette = self.extended_palette();
975
976 Some(pane_grid::Line {
977 color: palette.primary.base.color,
978 width: 2.0,
979 })
980 }
981 PaneGrid::Custom(custom) => custom.hovered_split(self),
982 }
983 }
984}
985
986#[derive(Default)]
988pub enum ProgressBar {
989 #[default]
991 Primary,
992 Success,
994 Danger,
996 Custom(Box<dyn progress_bar::StyleSheet<Style = Theme>>),
998}
999
1000impl<T: Fn(&Theme) -> progress_bar::Appearance + 'static> From<T>
1001 for ProgressBar
1002{
1003 fn from(f: T) -> Self {
1004 Self::Custom(Box::new(f))
1005 }
1006}
1007
1008impl progress_bar::StyleSheet for Theme {
1009 type Style = ProgressBar;
1010
1011 fn appearance(&self, style: &Self::Style) -> progress_bar::Appearance {
1012 if let ProgressBar::Custom(custom) = style {
1013 return custom.appearance(self);
1014 }
1015
1016 let palette = self.extended_palette();
1017
1018 let from_palette = |bar: Color| progress_bar::Appearance {
1019 background: palette.background.strong.color.into(),
1020 bar: bar.into(),
1021 border_radius: 2.0.into(),
1022 };
1023
1024 match style {
1025 ProgressBar::Primary => from_palette(palette.primary.base.color),
1026 ProgressBar::Success => from_palette(palette.success.base.color),
1027 ProgressBar::Danger => from_palette(palette.danger.base.color),
1028 ProgressBar::Custom(custom) => custom.appearance(self),
1029 }
1030 }
1031}
1032
1033impl<T: Fn(&Theme) -> progress_bar::Appearance> progress_bar::StyleSheet for T {
1034 type Style = Theme;
1035
1036 fn appearance(&self, style: &Self::Style) -> progress_bar::Appearance {
1037 (self)(style)
1038 }
1039}
1040
1041#[derive(Default)]
1043pub enum QRCode {
1044 #[default]
1046 Default,
1047 Custom(Box<dyn qr_code::StyleSheet<Style = Theme>>),
1049}
1050
1051impl<T: Fn(&Theme) -> qr_code::Appearance + 'static> From<T> for QRCode {
1052 fn from(f: T) -> Self {
1053 Self::Custom(Box::new(f))
1054 }
1055}
1056
1057impl qr_code::StyleSheet for Theme {
1058 type Style = QRCode;
1059
1060 fn appearance(&self, style: &Self::Style) -> qr_code::Appearance {
1061 let palette = self.palette();
1062
1063 match style {
1064 QRCode::Default => qr_code::Appearance {
1065 cell: palette.text,
1066 background: palette.background,
1067 },
1068 QRCode::Custom(custom) => custom.appearance(self),
1069 }
1070 }
1071}
1072
1073impl<T: Fn(&Theme) -> qr_code::Appearance> qr_code::StyleSheet for T {
1074 type Style = Theme;
1075
1076 fn appearance(&self, style: &Self::Style) -> qr_code::Appearance {
1077 (self)(style)
1078 }
1079}
1080
1081#[derive(Default)]
1083pub enum Rule {
1084 #[default]
1086 Default,
1087 Custom(Box<dyn rule::StyleSheet<Style = Theme>>),
1089}
1090
1091impl<T: Fn(&Theme) -> rule::Appearance + 'static> From<T> for Rule {
1092 fn from(f: T) -> Self {
1093 Self::Custom(Box::new(f))
1094 }
1095}
1096
1097impl rule::StyleSheet for Theme {
1098 type Style = Rule;
1099
1100 fn appearance(&self, style: &Self::Style) -> rule::Appearance {
1101 let palette = self.extended_palette();
1102
1103 match style {
1104 Rule::Default => rule::Appearance {
1105 color: palette.background.strong.color,
1106 width: 1,
1107 radius: 0.0.into(),
1108 fill_mode: rule::FillMode::Full,
1109 },
1110 Rule::Custom(custom) => custom.appearance(self),
1111 }
1112 }
1113}
1114
1115impl<T: Fn(&Theme) -> rule::Appearance> rule::StyleSheet for T {
1116 type Style = Theme;
1117
1118 fn appearance(&self, style: &Self::Style) -> rule::Appearance {
1119 (self)(style)
1120 }
1121}
1122
1123#[derive(Default)]
1127pub enum Svg {
1128 #[default]
1130 Default,
1131 Custom(Box<dyn svg::StyleSheet<Style = Theme>>),
1133}
1134
1135impl Svg {
1136 pub fn custom_fn(f: fn(&Theme) -> svg::Appearance) -> Self {
1138 Self::Custom(Box::new(f))
1139 }
1140}
1141
1142impl svg::StyleSheet for Theme {
1143 type Style = Svg;
1144
1145 fn appearance(&self, style: &Self::Style) -> svg::Appearance {
1146 match style {
1147 Svg::Default => svg::Appearance::default(),
1148 Svg::Custom(custom) => custom.appearance(self),
1149 }
1150 }
1151
1152 fn hovered(&self, style: &Self::Style) -> svg::Appearance {
1153 self.appearance(style)
1154 }
1155}
1156
1157impl svg::StyleSheet for fn(&Theme) -> svg::Appearance {
1158 type Style = Theme;
1159
1160 fn appearance(&self, style: &Self::Style) -> svg::Appearance {
1161 (self)(style)
1162 }
1163
1164 fn hovered(&self, style: &Self::Style) -> svg::Appearance {
1165 self.appearance(style)
1166 }
1167}
1168
1169#[derive(Default)]
1171pub enum Scrollable {
1172 #[default]
1174 Default,
1175 Custom(Box<dyn scrollable::StyleSheet<Style = Theme>>),
1177}
1178
1179impl Scrollable {
1180 pub fn custom<T: scrollable::StyleSheet<Style = Theme> + 'static>(
1182 style: T,
1183 ) -> Self {
1184 Self::Custom(Box::new(style))
1185 }
1186}
1187
1188impl scrollable::StyleSheet for Theme {
1189 type Style = Scrollable;
1190
1191 fn active(&self, style: &Self::Style) -> scrollable::Appearance {
1192 match style {
1193 Scrollable::Default => {
1194 let palette = self.extended_palette();
1195
1196 scrollable::Appearance {
1197 container: container::Appearance::default(),
1198 scrollbar: scrollable::Scrollbar {
1199 background: Some(palette.background.weak.color.into()),
1200 border: Border::with_radius(2),
1201 scroller: scrollable::Scroller {
1202 color: palette.background.strong.color,
1203 border: Border::with_radius(2),
1204 },
1205 },
1206 gap: None,
1207 }
1208 }
1209 Scrollable::Custom(custom) => custom.active(self),
1210 }
1211 }
1212
1213 fn hovered(
1214 &self,
1215 style: &Self::Style,
1216 is_mouse_over_scrollbar: bool,
1217 ) -> scrollable::Appearance {
1218 match style {
1219 Scrollable::Default => {
1220 if is_mouse_over_scrollbar {
1221 let palette = self.extended_palette();
1222
1223 scrollable::Appearance {
1224 scrollbar: scrollable::Scrollbar {
1225 background: Some(
1226 palette.background.weak.color.into(),
1227 ),
1228 border: Border::with_radius(2),
1229 scroller: scrollable::Scroller {
1230 color: palette.primary.strong.color,
1231 border: Border::with_radius(2),
1232 },
1233 },
1234 ..self.active(style)
1235 }
1236 } else {
1237 self.active(style)
1238 }
1239 }
1240 Scrollable::Custom(custom) => {
1241 custom.hovered(self, is_mouse_over_scrollbar)
1242 }
1243 }
1244 }
1245
1246 fn dragging(&self, style: &Self::Style) -> scrollable::Appearance {
1247 match style {
1248 Scrollable::Default => self.hovered(style, true),
1249 Scrollable::Custom(custom) => custom.dragging(self),
1250 }
1251 }
1252}
1253
1254#[derive(Clone, Copy, Default)]
1256pub enum Text {
1257 #[default]
1259 Default,
1260 Color(Color),
1262}
1263
1264impl From<Color> for Text {
1265 fn from(color: Color) -> Self {
1266 Text::Color(color)
1267 }
1268}
1269
1270impl text::StyleSheet for Theme {
1271 type Style = Text;
1272
1273 fn appearance(&self, style: Self::Style) -> text::Appearance {
1274 match style {
1275 Text::Default => text::Appearance::default(),
1276 Text::Color(c) => text::Appearance { color: Some(c) },
1277 }
1278 }
1279}
1280
1281#[derive(Default)]
1283pub enum TextInput {
1284 #[default]
1286 Default,
1287 Custom(Box<dyn text_input::StyleSheet<Style = Theme>>),
1289}
1290
1291impl text_input::StyleSheet for Theme {
1292 type Style = TextInput;
1293
1294 fn active(&self, style: &Self::Style) -> text_input::Appearance {
1295 if let TextInput::Custom(custom) = style {
1296 return custom.active(self);
1297 }
1298
1299 let palette = self.extended_palette();
1300
1301 text_input::Appearance {
1302 background: palette.background.base.color.into(),
1303 border: Border {
1304 radius: 2.0.into(),
1305 width: 1.0,
1306 color: palette.background.strong.color,
1307 },
1308 icon_color: palette.background.weak.text,
1309 }
1310 }
1311
1312 fn hovered(&self, style: &Self::Style) -> text_input::Appearance {
1313 if let TextInput::Custom(custom) = style {
1314 return custom.hovered(self);
1315 }
1316
1317 let palette = self.extended_palette();
1318
1319 text_input::Appearance {
1320 background: palette.background.base.color.into(),
1321 border: Border {
1322 radius: 2.0.into(),
1323 width: 1.0,
1324 color: palette.background.base.text,
1325 },
1326 icon_color: palette.background.weak.text,
1327 }
1328 }
1329
1330 fn focused(&self, style: &Self::Style) -> text_input::Appearance {
1331 if let TextInput::Custom(custom) = style {
1332 return custom.focused(self);
1333 }
1334
1335 let palette = self.extended_palette();
1336
1337 text_input::Appearance {
1338 background: palette.background.base.color.into(),
1339 border: Border {
1340 radius: 2.0.into(),
1341 width: 1.0,
1342 color: palette.primary.strong.color,
1343 },
1344 icon_color: palette.background.weak.text,
1345 }
1346 }
1347
1348 fn placeholder_color(&self, style: &Self::Style) -> Color {
1349 if let TextInput::Custom(custom) = style {
1350 return custom.placeholder_color(self);
1351 }
1352
1353 let palette = self.extended_palette();
1354
1355 palette.background.strong.color
1356 }
1357
1358 fn value_color(&self, style: &Self::Style) -> Color {
1359 if let TextInput::Custom(custom) = style {
1360 return custom.value_color(self);
1361 }
1362
1363 let palette = self.extended_palette();
1364
1365 palette.background.base.text
1366 }
1367
1368 fn selection_color(&self, style: &Self::Style) -> Color {
1369 if let TextInput::Custom(custom) = style {
1370 return custom.selection_color(self);
1371 }
1372
1373 let palette = self.extended_palette();
1374
1375 palette.primary.weak.color
1376 }
1377
1378 fn disabled(&self, style: &Self::Style) -> text_input::Appearance {
1379 if let TextInput::Custom(custom) = style {
1380 return custom.disabled(self);
1381 }
1382
1383 let palette = self.extended_palette();
1384
1385 text_input::Appearance {
1386 background: palette.background.weak.color.into(),
1387 border: Border {
1388 radius: 2.0.into(),
1389 width: 1.0,
1390 color: palette.background.strong.color,
1391 },
1392 icon_color: palette.background.strong.color,
1393 }
1394 }
1395
1396 fn disabled_color(&self, style: &Self::Style) -> Color {
1397 if let TextInput::Custom(custom) = style {
1398 return custom.disabled_color(self);
1399 }
1400
1401 self.placeholder_color(style)
1402 }
1403}
1404
1405#[derive(Default)]
1407pub enum TextEditor {
1408 #[default]
1410 Default,
1411 Custom(Box<dyn text_editor::StyleSheet<Style = Theme>>),
1413}
1414
1415impl text_editor::StyleSheet for Theme {
1416 type Style = TextEditor;
1417
1418 fn active(&self, style: &Self::Style) -> text_editor::Appearance {
1419 if let TextEditor::Custom(custom) = style {
1420 return custom.active(self);
1421 }
1422
1423 let palette = self.extended_palette();
1424
1425 text_editor::Appearance {
1426 background: palette.background.base.color.into(),
1427 border: Border {
1428 radius: 2.0.into(),
1429 width: 1.0,
1430 color: palette.background.strong.color,
1431 },
1432 }
1433 }
1434
1435 fn hovered(&self, style: &Self::Style) -> text_editor::Appearance {
1436 if let TextEditor::Custom(custom) = style {
1437 return custom.hovered(self);
1438 }
1439
1440 let palette = self.extended_palette();
1441
1442 text_editor::Appearance {
1443 background: palette.background.base.color.into(),
1444 border: Border {
1445 radius: 2.0.into(),
1446 width: 1.0,
1447 color: palette.background.base.text,
1448 },
1449 }
1450 }
1451
1452 fn focused(&self, style: &Self::Style) -> text_editor::Appearance {
1453 if let TextEditor::Custom(custom) = style {
1454 return custom.focused(self);
1455 }
1456
1457 let palette = self.extended_palette();
1458
1459 text_editor::Appearance {
1460 background: palette.background.base.color.into(),
1461 border: Border {
1462 radius: 2.0.into(),
1463 width: 1.0,
1464 color: palette.primary.strong.color,
1465 },
1466 }
1467 }
1468
1469 fn placeholder_color(&self, style: &Self::Style) -> Color {
1470 if let TextEditor::Custom(custom) = style {
1471 return custom.placeholder_color(self);
1472 }
1473
1474 let palette = self.extended_palette();
1475
1476 palette.background.strong.color
1477 }
1478
1479 fn value_color(&self, style: &Self::Style) -> Color {
1480 if let TextEditor::Custom(custom) = style {
1481 return custom.value_color(self);
1482 }
1483
1484 let palette = self.extended_palette();
1485
1486 palette.background.base.text
1487 }
1488
1489 fn selection_color(&self, style: &Self::Style) -> Color {
1490 if let TextEditor::Custom(custom) = style {
1491 return custom.selection_color(self);
1492 }
1493
1494 let palette = self.extended_palette();
1495
1496 palette.primary.weak.color
1497 }
1498
1499 fn disabled(&self, style: &Self::Style) -> text_editor::Appearance {
1500 if let TextEditor::Custom(custom) = style {
1501 return custom.disabled(self);
1502 }
1503
1504 let palette = self.extended_palette();
1505
1506 text_editor::Appearance {
1507 background: palette.background.weak.color.into(),
1508 border: Border {
1509 radius: 2.0.into(),
1510 width: 1.0,
1511 color: palette.background.strong.color,
1512 },
1513 }
1514 }
1515
1516 fn disabled_color(&self, style: &Self::Style) -> Color {
1517 if let TextEditor::Custom(custom) = style {
1518 return custom.disabled_color(self);
1519 }
1520
1521 self.placeholder_color(style)
1522 }
1523}