1use crate::theme::{CosmicComponent, TRANSPARENT_COMPONENT, Theme};
7use cosmic_theme::composite::over;
8use iced::overlay::menu;
9use iced::theme::Base;
10use iced::widget::slider::{self, Rail};
11use iced::widget::{
12 button as iced_button, checkbox as iced_checkbox, combo_box, container as iced_container,
13 pane_grid, pick_list, progress_bar, radio, rule, scrollable, svg, toggler,
14};
15use iced_core::{Background, Border, Color, Shadow, Vector};
16use iced_widget::pane_grid::Highlight;
17use iced_widget::scrollable::AutoScroll;
18use iced_widget::{text_editor, text_input};
19use palette::WithAlpha;
20use std::rc::Rc;
21
22pub mod application {
23 use crate::Theme;
24 use iced_runtime::Appearance;
25
26 #[derive(Default)]
27 pub enum Application {
28 #[default]
29 Default,
30 Custom(Box<dyn Fn(&Theme) -> Appearance>),
31 }
32
33 impl Application {
34 pub fn custom<F: Fn(&Theme) -> Appearance + 'static>(f: F) -> Self {
35 Self::Custom(Box::new(f))
36 }
37 }
38
39 pub fn style(theme: &Theme) -> iced::theme::Style {
40 let cosmic = theme.cosmic();
41
42 iced::theme::Style {
43 background_color: cosmic.bg_color().into(),
44 text_color: cosmic.on_bg_color().into(),
45 icon_color: cosmic.on_bg_color().into(),
46 }
47 }
48}
49
50#[derive(Default)]
52pub enum Button {
53 Deactivated,
54 Destructive,
55 Positive,
56 #[default]
57 Primary,
58 Secondary,
59 Text,
60 Link,
61 LinkActive,
62 Transparent,
63 Card,
64 Custom(Box<dyn Fn(&Theme, iced_button::Status) -> iced_button::Style>),
65}
66
67impl iced_button::Catalog for Theme {
68 type Class<'a> = Button;
69
70 fn default<'a>() -> Self::Class<'a> {
71 Button::default()
72 }
73
74 fn style(&self, class: &Self::Class<'_>, status: iced_button::Status) -> iced_button::Style {
75 if let Button::Custom(f) = class {
76 return f(self, status);
77 }
78 let cosmic = self.cosmic();
79 let corner_radii = &cosmic.corner_radii;
80 let component = class.cosmic(self);
81
82 let mut appearance = iced_button::Style {
83 border_radius: match class {
84 Button::Link => corner_radii.radius_0.into(),
85 Button::Card => corner_radii.radius_xs.into(),
86 _ => corner_radii.radius_xl.into(),
87 },
88 border: Border {
89 radius: match class {
90 Button::Link => corner_radii.radius_0.into(),
91 Button::Card => corner_radii.radius_xs.into(),
92 _ => corner_radii.radius_xl.into(),
93 },
94 ..Default::default()
95 },
96 background: match class {
97 Button::Link | Button::Text => None,
98 Button::LinkActive => Some(Background::Color(component.divider.into())),
99 _ => Some(Background::Color(component.base.into())),
100 },
101 text_color: match class {
102 Button::Link | Button::LinkActive => component.base.into(),
103 _ => component.on.into(),
104 },
105 ..iced_button::Style::default()
106 };
107
108 match status {
109 iced_button::Status::Active => {}
110 iced_button::Status::Hovered => {
111 appearance.background = match class {
112 Button::Link => None,
113 Button::LinkActive => Some(Background::Color(component.divider.into())),
114 _ => Some(Background::Color(component.hover.into())),
115 };
116 }
117 iced_button::Status::Pressed => {
118 appearance.background = match class {
119 Button::Link => None,
120 Button::LinkActive => Some(Background::Color(component.divider.into())),
121 _ => Some(Background::Color(component.pressed.into())),
122 };
123 }
124 iced_button::Status::Disabled => {
125 if matches!(class, Button::Card) {
127 return appearance;
128 }
129 appearance.background = appearance.background.map(|background| match background {
130 Background::Color(color) => Background::Color(Color {
131 a: color.a * 0.5,
132 ..color
133 }),
134 Background::Gradient(gradient) => {
135 Background::Gradient(gradient.scale_alpha(0.5))
136 }
137 });
138 appearance.text_color = Color {
139 a: appearance.text_color.a * 0.5,
140 ..appearance.text_color
141 };
142 }
143 };
144 appearance
145 }
146}
147
148impl Button {
149 #[allow(clippy::trivially_copy_pass_by_ref)]
150 #[allow(clippy::match_same_arms)]
151 fn cosmic<'a>(&'a self, theme: &'a Theme) -> &'a CosmicComponent {
152 let cosmic = theme.cosmic();
153 match self {
154 Self::Primary => &cosmic.accent_button,
155 Self::Secondary => &theme.current_container().component,
156 Self::Positive => &cosmic.success_button,
157 Self::Destructive => &cosmic.destructive_button,
158 Self::Text => &cosmic.text_button,
159 Self::Link => &cosmic.link_button,
160 Self::LinkActive => &cosmic.link_button,
161 Self::Transparent => &TRANSPARENT_COMPONENT,
162 Self::Deactivated => &theme.current_container().component,
163 Self::Card => &theme.current_container().component,
164 Self::Custom { .. } => &TRANSPARENT_COMPONENT,
165 }
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub enum Checkbox {
174 Primary,
175 Secondary,
176 Success,
177 Danger,
178}
179
180impl Default for Checkbox {
181 fn default() -> Self {
182 Self::Primary
183 }
184}
185
186impl iced_checkbox::Catalog for Theme {
187 type Class<'a> = Checkbox;
188
189 fn default<'a>() -> Self::Class<'a> {
190 Checkbox::default()
191 }
192
193 #[allow(clippy::too_many_lines)]
194 fn style(
195 &self,
196 class: &Self::Class<'_>,
197 status: iced_checkbox::Status,
198 ) -> iced_checkbox::Style {
199 let cosmic = self.cosmic();
200
201 let corners = &cosmic.corner_radii;
202
203 let disabled = matches!(status, iced_checkbox::Status::Disabled { .. });
204 match status {
205 iced_checkbox::Status::Active { is_checked }
206 | iced_checkbox::Status::Disabled { is_checked } => {
207 let mut active = match class {
208 Checkbox::Primary => iced_checkbox::Style {
209 background: Background::Color(if is_checked {
210 cosmic.accent.base.into()
211 } else {
212 self.current_container().small_widget.into()
213 }),
214 icon_color: cosmic.accent.on.into(),
215 border: Border {
216 radius: corners.radius_xs.into(),
217 width: if is_checked { 0.0 } else { 1.0 },
218 color: if is_checked {
219 cosmic.accent.base
220 } else {
221 cosmic.palette.neutral_8
222 }
223 .into(),
224 },
225
226 text_color: None,
227 },
228 Checkbox::Secondary => iced_checkbox::Style {
229 background: Background::Color(if is_checked {
230 cosmic.background(self.transparent).component.base.into()
231 } else {
232 self.current_container().small_widget.into()
233 }),
234 icon_color: cosmic.background(self.transparent).on.into(),
235 border: Border {
236 radius: corners.radius_xs.into(),
237 width: if is_checked { 0.0 } else { 1.0 },
238 color: cosmic.palette.neutral_8.into(),
239 },
240 text_color: None,
241 },
242 Checkbox::Success => iced_checkbox::Style {
243 background: Background::Color(if is_checked {
244 cosmic.success.base.into()
245 } else {
246 self.current_container().small_widget.into()
247 }),
248 icon_color: cosmic.success.on.into(),
249 border: Border {
250 radius: corners.radius_xs.into(),
251 width: if is_checked { 0.0 } else { 1.0 },
252 color: if is_checked {
253 cosmic.success.base
254 } else {
255 cosmic.palette.neutral_8
256 }
257 .into(),
258 },
259 text_color: None,
260 },
261 Checkbox::Danger => iced_checkbox::Style {
262 background: Background::Color(if is_checked {
263 cosmic.destructive.base.into()
264 } else {
265 self.current_container().small_widget.into()
266 }),
267 icon_color: cosmic.destructive.on.into(),
268 border: Border {
269 radius: corners.radius_xs.into(),
270 width: if is_checked { 0.0 } else { 1.0 },
271 color: if is_checked {
272 cosmic.destructive.base
273 } else {
274 cosmic.palette.neutral_8
275 }
276 .into(),
277 },
278 text_color: None,
279 },
280 };
281 if disabled {
282 match &mut active.background {
283 Background::Color(color) => {
284 color.a /= 2.;
285 }
286 Background::Gradient(gradient) => {
287 *gradient = gradient.scale_alpha(0.5);
288 }
289 }
290 if let Some(c) = active.text_color.as_mut() {
291 c.a /= 2.
292 };
293 active.border.color.a /= 2.;
294 }
295 active
296 }
297 iced_checkbox::Status::Hovered { is_checked } => {
298 let cur_container = self.current_container().small_widget;
299 let hovered_bg = over(cosmic.palette.neutral_0.with_alpha(0.1), cur_container);
301 match class {
302 Checkbox::Primary => iced_checkbox::Style {
303 background: Background::Color(if is_checked {
304 cosmic.accent.hover_state_color().into()
305 } else {
306 hovered_bg.into()
307 }),
308 icon_color: cosmic.accent.on.into(),
309 border: Border {
310 radius: corners.radius_xs.into(),
311 width: if is_checked { 0.0 } else { 1.0 },
312 color: if is_checked {
313 cosmic.accent.base
314 } else {
315 cosmic.palette.neutral_8
316 }
317 .into(),
318 },
319 text_color: None,
320 },
321 Checkbox::Secondary => iced_checkbox::Style {
322 background: Background::Color(if is_checked {
323 self.current_container().component.hover.into()
324 } else {
325 hovered_bg.into()
326 }),
327 icon_color: self.current_container().on.into(),
328 border: Border {
329 radius: corners.radius_xs.into(),
330 width: if is_checked { 0.0 } else { 1.0 },
331 color: if is_checked {
332 self.current_container().base
333 } else {
334 cosmic.palette.neutral_8
335 }
336 .into(),
337 },
338 text_color: None,
339 },
340 Checkbox::Success => iced_checkbox::Style {
341 background: Background::Color(if is_checked {
342 cosmic.success.hover.into()
343 } else {
344 hovered_bg.into()
345 }),
346 icon_color: cosmic.success.on.into(),
347 border: Border {
348 radius: corners.radius_xs.into(),
349 width: if is_checked { 0.0 } else { 1.0 },
350 color: if is_checked {
351 cosmic.success.base
352 } else {
353 cosmic.palette.neutral_8
354 }
355 .into(),
356 },
357 text_color: None,
358 },
359 Checkbox::Danger => iced_checkbox::Style {
360 background: Background::Color(if is_checked {
361 cosmic.destructive.hover.into()
362 } else {
363 hovered_bg.into()
364 }),
365 icon_color: cosmic.destructive.on.into(),
366 border: Border {
367 radius: corners.radius_xs.into(),
368 width: if is_checked { 0.0 } else { 1.0 },
369 color: if is_checked {
370 cosmic.destructive.base
371 } else {
372 cosmic.palette.neutral_8
373 }
374 .into(),
375 },
376 text_color: None,
377 },
378 }
379 }
380 }
381 }
382}
383
384#[derive(Default)]
388pub enum Container<'a> {
389 WindowBackground,
390 Background,
391 Card,
392 ContextDrawer {
393 transparent: bool,
394 },
395 Custom(Box<dyn Fn(&Theme) -> iced_container::Style + 'a>),
396 Dialog(bool),
397 Dropdown,
398 HeaderBar {
399 focused: bool,
400 sharp_corners: bool,
401 transparent: bool,
402 },
403 List,
404 Primary,
405 Secondary,
406 Tooltip,
407 #[default]
408 Transparent,
409}
410
411impl<'a> Container<'a> {
412 pub fn custom<F: Fn(&Theme) -> iced_container::Style + 'a>(f: F) -> Self {
413 Self::Custom(Box::new(f))
414 }
415
416 #[must_use]
417 pub fn background(theme: &cosmic_theme::Theme, transparent: bool) -> iced_container::Style {
418 iced_container::Style {
419 icon_color: Some(Color::from(theme.background(transparent).on)),
420 text_color: Some(Color::from(theme.background(transparent).on)),
421 background: Some(iced::Background::Color(
422 theme.background(transparent).base.into(),
423 )),
424 border: Border {
425 radius: theme.corner_radii.radius_s.into(),
426 ..Default::default()
427 },
428 shadow: Shadow::default(),
429 snap: true,
430 }
431 }
432
433 #[must_use]
434 pub fn primary(theme: &cosmic_theme::Theme, transparent: bool) -> iced_container::Style {
435 iced_container::Style {
436 icon_color: Some(Color::from(theme.primary(transparent).on)),
437 text_color: Some(Color::from(theme.primary(transparent).on)),
438 background: Some(iced::Background::Color(
439 theme.primary(transparent).base.into(),
440 )),
441 border: Border {
442 radius: theme.corner_radii.radius_s.into(),
443 ..Default::default()
444 },
445 shadow: Shadow::default(),
446 snap: true,
447 }
448 }
449
450 #[must_use]
451 pub fn secondary(theme: &cosmic_theme::Theme, transparent: bool) -> iced_container::Style {
452 iced_container::Style {
453 icon_color: Some(Color::from(theme.secondary(transparent).on)),
454 text_color: Some(Color::from(theme.secondary(transparent).on)),
455 background: Some(iced::Background::Color(
456 theme.secondary(transparent).base.into(),
457 )),
458 border: Border {
459 radius: theme.corner_radii.radius_s.into(),
460 ..Default::default()
461 },
462 shadow: Shadow::default(),
463 snap: true,
464 }
465 }
466}
467
468impl<'a> From<iced_container::StyleFn<'a, Theme>> for Container<'a> {
469 fn from(value: iced_container::StyleFn<'a, Theme>) -> Self {
470 Self::custom(value)
471 }
472}
473
474impl iced_container::Catalog for Theme {
475 type Class<'a> = Container<'a>;
476
477 fn default<'a>() -> Self::Class<'a> {
478 Container::default()
479 }
480
481 fn style(&self, class: &Self::Class<'_>) -> iced_container::Style {
482 let cosmic = self.cosmic();
483
484 let window_corner_radius = cosmic.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 });
486
487 match class {
488 Container::Transparent => iced_container::Style::default(),
489
490 Container::Custom(f) => f(self),
491
492 Container::WindowBackground => iced_container::Style {
493 icon_color: Some(Color::from(cosmic.background(self.transparent).on)),
494 text_color: Some(Color::from(cosmic.background(self.transparent).on)),
495 background: Some(iced::Background::Color(
496 cosmic.background(self.transparent).base.into(),
497 )),
498 border: Border {
499 radius: [
500 cosmic.corner_radii.radius_0[0],
501 cosmic.corner_radii.radius_0[1],
502 window_corner_radius[2],
503 window_corner_radius[3],
504 ]
505 .into(),
506 ..Default::default()
507 },
508 shadow: Shadow::default(),
509 snap: true,
510 },
511
512 Container::List => {
513 let component = &self.current_container().component;
514 iced_container::Style {
515 icon_color: Some(component.on.into()),
516 text_color: Some(component.on.into()),
517 background: Some(Background::Color(component.base.into())),
518 border: iced::Border {
519 radius: cosmic.corner_radii.radius_s.into(),
520 ..Default::default()
521 },
522 shadow: Shadow::default(),
523 snap: true,
524 }
525 }
526
527 Container::HeaderBar {
528 focused,
529 sharp_corners,
530 transparent,
531 } => {
532 let (icon_color, text_color) = if *focused {
533 (
534 Color::from(cosmic.accent_text_color()),
535 Color::from(cosmic.background(self.transparent).on),
536 )
537 } else {
538 use crate::ext::ColorExt;
539 let unfocused_color =
540 Color::from(cosmic.background(self.transparent).component.on)
541 .blend_alpha(cosmic.background(self.transparent).base.into(), 0.5);
542 (unfocused_color, unfocused_color)
543 };
544
545 iced_container::Style {
546 icon_color: Some(icon_color),
547 text_color: Some(text_color),
548 background: if *transparent {
549 None
550 } else {
551 Some(iced::Background::Color(
552 cosmic.background(self.transparent).base.into(),
553 ))
554 },
555 border: Border {
556 radius: [
557 if *sharp_corners {
558 cosmic.corner_radii.radius_0[0]
559 } else {
560 window_corner_radius[0]
561 },
562 if *sharp_corners {
563 cosmic.corner_radii.radius_0[1]
564 } else {
565 window_corner_radius[1]
566 },
567 cosmic.corner_radii.radius_0[2],
568 cosmic.corner_radii.radius_0[3],
569 ]
570 .into(),
571 ..Default::default()
572 },
573 snap: true,
574 shadow: Shadow::default(),
575 }
576 }
577
578 Container::ContextDrawer { transparent } => {
579 let mut a = Container::primary(cosmic, self.transparent && *transparent);
580
581 if cosmic.is_high_contrast {
582 a.border.width = 1.;
583 a.border.color = cosmic.primary(self.transparent).divider.into();
584 }
585 a
586 }
587
588 Container::Background => Container::background(cosmic, self.transparent),
589
590 Container::Primary => Container::primary(cosmic, self.transparent),
591
592 Container::Secondary => Container::secondary(cosmic, self.transparent),
593
594 Container::Dropdown => iced_container::Style {
595 icon_color: None,
596 text_color: None,
597 background: Some(iced::Background::Color(cosmic.bg_component_color().into())),
598 border: Border {
599 color: cosmic.bg_component_divider().into(),
600 width: 1.0,
601 radius: cosmic.corner_radii.radius_s.into(),
602 },
603 shadow: Shadow::default(),
604 snap: true,
605 },
606
607 Container::Tooltip => iced_container::Style {
608 icon_color: None,
609 text_color: None,
610 background: Some(iced::Background::Color(cosmic.palette.neutral_2.into())),
611 border: Border {
612 radius: cosmic.corner_radii.radius_l.into(),
613 ..Default::default()
614 },
615 shadow: Shadow::default(),
616 snap: true,
617 },
618
619 Container::Card => {
620 let cosmic = self.cosmic();
621
622 match self.layer {
623 cosmic_theme::Layer::Background => iced_container::Style {
624 icon_color: Some(Color::from(
625 cosmic.background(self.transparent).component.on,
626 )),
627 text_color: Some(Color::from(
628 cosmic.background(self.transparent).component.on,
629 )),
630 background: Some(iced::Background::Color(
631 cosmic.background(self.transparent).component.base.into(),
632 )),
633 border: Border {
634 radius: cosmic.corner_radii.radius_s.into(),
635 ..Default::default()
636 },
637 shadow: Shadow::default(),
638 snap: true,
639 },
640 cosmic_theme::Layer::Primary => iced_container::Style {
641 icon_color: Some(Color::from(
642 cosmic.primary(self.transparent).component.on,
643 )),
644 text_color: Some(Color::from(
645 cosmic.primary(self.transparent).component.on,
646 )),
647 background: Some(iced::Background::Color(
648 cosmic.primary(self.transparent).component.base.into(),
649 )),
650 border: Border {
651 radius: cosmic.corner_radii.radius_s.into(),
652 ..Default::default()
653 },
654 shadow: Shadow::default(),
655 snap: true,
656 },
657 cosmic_theme::Layer::Secondary => iced_container::Style {
658 icon_color: Some(Color::from(
659 cosmic.secondary(self.transparent).component.on,
660 )),
661 text_color: Some(Color::from(
662 cosmic.secondary(self.transparent).component.on,
663 )),
664 background: Some(iced::Background::Color(
665 cosmic.secondary(self.transparent).component.base.into(),
666 )),
667 border: Border {
668 radius: cosmic.corner_radii.radius_s.into(),
669 ..Default::default()
670 },
671 shadow: Shadow::default(),
672 snap: true,
673 },
674 }
675 }
676
677 Container::Dialog(is_overlay) => iced_container::Style {
678 icon_color: Some(Color::from(cosmic.primary(self.transparent).on)),
679 text_color: Some(Color::from(cosmic.primary(self.transparent).on)),
680 background: Some(iced::Background::Color(
681 cosmic.primary(self.transparent && !is_overlay).base.into(),
682 )),
683 border: Border {
684 color: cosmic
685 .primary(self.transparent && !is_overlay)
686 .divider
687 .into(),
688 width: 1.0,
689 radius: cosmic.corner_radii.radius_m.into(),
690 },
691 shadow: Shadow {
692 color: cosmic.shade.into(),
693 offset: Vector::new(0.0, 4.0),
694 blur_radius: 16.0,
695 },
696 snap: true,
697 },
698 }
699 }
700}
701
702#[derive(Default)]
703pub enum Slider {
704 #[default]
705 Standard,
706 Custom {
707 active: Rc<dyn Fn(&Theme) -> slider::Style>,
708 hovered: Rc<dyn Fn(&Theme) -> slider::Style>,
709 dragging: Rc<dyn Fn(&Theme) -> slider::Style>,
710 },
711}
712
713impl slider::Catalog for Theme {
717 type Class<'a> = Slider;
718
719 fn default<'a>() -> Self::Class<'a> {
720 Slider::default()
721 }
722
723 fn style(&self, class: &Self::Class<'_>, status: slider::Status) -> slider::Style {
724 let cosmic: &cosmic_theme::Theme = self.cosmic();
725 let hc = self.theme_type.is_high_contrast();
726 let is_dark = self.theme_type.is_dark();
727
728 let mut appearance = match class {
729 Slider::Standard =>
730 {
732 let (active_track, inactive_track) = if hc {
733 (
734 cosmic.accent_text_color(),
735 if is_dark {
736 cosmic.palette.neutral_5
737 } else {
738 cosmic.palette.neutral_3
739 },
740 )
741 } else {
742 (cosmic.accent.base, cosmic.palette.neutral_6)
743 };
744 slider::Style {
745 rail: Rail {
746 backgrounds: (
747 Background::Color(active_track.into()),
748 Background::Color(inactive_track.into()),
749 ),
750 border: Border {
751 radius: cosmic.corner_radii.radius_xs.into(),
752 color: if hc && !is_dark {
753 self.current_container().component.border.into()
754 } else {
755 Color::TRANSPARENT
756 },
757 width: if hc && !is_dark { 1. } else { 0. },
758 },
759 width: 4.0,
760 },
761
762 handle: slider::Handle {
763 shape: slider::HandleShape::Rectangle {
764 height: 26,
765 width: 26,
766 border_radius: cosmic.corner_radii.radius_m.into(),
767 },
768 border_color: Color::TRANSPARENT,
769 border_width: 3.0,
770 background: Background::Color(cosmic.accent.base.into()),
771 },
772
773 breakpoint: slider::Breakpoint {
774 color: cosmic.on_bg_color().into(),
775 },
776 }
777 }
778 Slider::Custom { active, .. } => active(self),
779 };
780 match status {
781 slider::Status::Active => appearance,
782 slider::Status::Hovered => match class {
783 Slider::Standard => {
784 appearance.handle.border_color =
785 self.cosmic().palette.neutral_10.with_alpha(0.1).into();
786 appearance
787 }
788 Slider::Custom { hovered, .. } => hovered(self),
789 },
790 slider::Status::Dragged => match class {
791 Slider::Standard => {
792 appearance.handle.border_color =
793 self.cosmic().palette.neutral_10.with_alpha(0.2).into();
794 appearance
795 }
796 Slider::Custom { dragging, .. } => dragging(self),
797 },
798 }
799 }
800}
801
802impl menu::Catalog for Theme {
803 type Class<'a> = ();
804
805 fn default<'a>() -> <Self as menu::Catalog>::Class<'a> {}
806
807 fn style(&self, class: &<Self as menu::Catalog>::Class<'_>) -> menu::Style {
808 let cosmic = self.cosmic();
809
810 menu::Style {
811 text_color: cosmic.on_bg_color().into(),
812 background: Background::Color(cosmic.background(self.transparent).base.into()),
813 border: Border {
814 radius: cosmic.corner_radii.radius_m.into(),
815 ..Default::default()
816 },
817 selected_text_color: cosmic.accent_text_color().into(),
818 selected_background: Background::Color(
819 cosmic.background(self.transparent).component.hover.into(),
820 ),
821 shadow: Default::default(),
822 }
823 }
824}
825
826impl pick_list::Catalog for Theme {
827 type Class<'a> = ();
828
829 fn default<'a>() -> <Self as pick_list::Catalog>::Class<'a> {}
830
831 fn style(
832 &self,
833 class: &<Self as pick_list::Catalog>::Class<'_>,
834 status: pick_list::Status,
835 ) -> pick_list::Style {
836 let cosmic = &self.cosmic();
837 let hc = cosmic.is_high_contrast;
838 let appearance = pick_list::Style {
839 text_color: cosmic.on_bg_color().into(),
840 background: Color::TRANSPARENT.into(),
841 placeholder_color: cosmic.on_bg_color().into(),
842 border: Border {
843 radius: cosmic.corner_radii.radius_m.into(),
844 width: if hc { 1. } else { 0. },
845 color: if hc {
846 self.current_container().component.border.into()
847 } else {
848 Color::TRANSPARENT
849 },
850 },
851 handle_color: cosmic.on_bg_color().into(),
853 };
854
855 match status {
856 pick_list::Status::Active => appearance,
857 pick_list::Status::Hovered => pick_list::Style {
858 background: Background::Color(cosmic.background(self.transparent).base.into()),
859 ..appearance
860 },
861 pick_list::Status::Opened { is_hovered: _ } => appearance,
862 }
863 }
864}
865
866impl radio::Catalog for Theme {
870 type Class<'a> = ();
871
872 fn default<'a>() -> Self::Class<'a> {}
873
874 fn style(&self, class: &Self::Class<'_>, status: radio::Status) -> radio::Style {
875 let cur_container = self.current_container();
876 let theme = self.cosmic();
877
878 match status {
879 radio::Status::Active { is_selected } => radio::Style {
880 background: if is_selected {
881 Color::from(theme.accent.base).into()
882 } else {
883 Color::from(cur_container.small_widget).into()
885 },
886 dot_color: theme.accent.on.into(),
887 border_width: 1.0,
888 border_color: if is_selected {
889 Color::from(theme.accent.base)
890 } else {
891 Color::from(theme.palette.neutral_8)
892 },
893 text_color: None,
894 },
895 radio::Status::Hovered { is_selected } => {
896 let bg = if is_selected {
897 theme.accent.base
898 } else {
899 self.current_container().small_widget
900 };
901 let hovered_bg = Color::from(over(theme.palette.neutral_0.with_alpha(0.1), bg));
903 radio::Style {
904 background: hovered_bg.into(),
905 dot_color: theme.accent.on.into(),
906 border_width: 1.0,
907 border_color: if is_selected {
908 Color::from(theme.accent.base)
909 } else {
910 Color::from(theme.palette.neutral_8)
911 },
912 text_color: None,
913 }
914 }
915 }
916 }
917}
918
919impl toggler::Catalog for Theme {
923 type Class<'a> = ();
924
925 fn default<'a>() -> Self::Class<'a> {}
926
927 fn style(&self, class: &Self::Class<'_>, status: toggler::Status) -> toggler::Style {
928 let cosmic = self.cosmic();
929 const HANDLE_MARGIN: f32 = 2.0;
930 let neutral_10 = cosmic.palette.neutral_10.with_alpha(0.1);
931
932 let mut active = toggler::Style {
933 background: if matches!(
934 status,
935 toggler::Status::Active { is_toggled: true }
936 | toggler::Status::Disabled { is_toggled: true }
937 ) {
938 cosmic.accent.base.into()
939 } else if cosmic.is_dark {
940 cosmic.palette.neutral_6.into()
941 } else {
942 cosmic.palette.neutral_5.into()
943 },
944 foreground: cosmic.palette.neutral_2.into(),
945 border_radius: cosmic.radius_xl().into(),
946 handle_radius: cosmic
947 .radius_xl()
948 .map(|x| (x - HANDLE_MARGIN).max(0.0))
949 .into(),
950 handle_margin: HANDLE_MARGIN,
951 background_border_width: 0.0,
952 background_border_color: Color::TRANSPARENT,
953 foreground_border_width: 0.0,
954 foreground_border_color: Color::TRANSPARENT,
955 text_color: None,
956 padding_ratio: 0.0,
957 };
958 match status {
959 toggler::Status::Active { is_toggled } => active,
960 toggler::Status::Hovered { is_toggled } => {
961 let is_active = matches!(status, toggler::Status::Hovered { is_toggled: true });
962 toggler::Style {
963 background: if is_active {
964 over(neutral_10, cosmic.accent_color())
965 } else {
966 over(
967 neutral_10,
968 if cosmic.is_dark {
969 cosmic.palette.neutral_6
970 } else {
971 cosmic.palette.neutral_5
972 },
973 )
974 }
975 .into(),
976 ..active
977 }
978 }
979 toggler::Status::Disabled { is_toggled } => {
980 active.background = active.background.scale_alpha(0.5);
981 active.foreground = active.foreground.scale_alpha(0.5);
982 active
983 }
984 }
985 }
986}
987
988impl pane_grid::Catalog for Theme {
992 type Class<'a> = ();
993
994 fn default<'a>() -> <Self as pane_grid::Catalog>::Class<'a> {}
995
996 fn style(&self, class: &<Self as pane_grid::Catalog>::Class<'_>) -> pane_grid::Style {
997 let theme = self.cosmic();
998
999 pane_grid::Style {
1000 hovered_region: Highlight {
1001 background: Background::Color(theme.bg_color().into()),
1002 border: Border {
1003 radius: theme.corner_radii.radius_0.into(),
1004 width: 2.0,
1005 color: theme.bg_divider().into(),
1006 },
1007 },
1008 picked_split: pane_grid::Line {
1009 color: theme.accent.base.into(),
1010 width: 2.0,
1011 },
1012 hovered_split: pane_grid::Line {
1013 color: theme.accent.hover.into(),
1014 width: 2.0,
1015 },
1016 }
1017 }
1018}
1019
1020#[derive(Default)]
1024pub enum ProgressBar {
1025 #[default]
1026 Primary,
1027 Success,
1028 Danger,
1029 Custom(Box<dyn Fn(&Theme) -> progress_bar::Style>),
1030}
1031
1032impl ProgressBar {
1033 pub fn custom<F: Fn(&Theme) -> progress_bar::Style + 'static>(f: F) -> Self {
1034 Self::Custom(Box::new(f))
1035 }
1036}
1037
1038impl progress_bar::Catalog for Theme {
1039 type Class<'a> = ProgressBar;
1040
1041 fn default<'a>() -> Self::Class<'a> {
1042 ProgressBar::default()
1043 }
1044
1045 fn style(&self, class: &Self::Class<'_>) -> progress_bar::Style {
1046 let theme = self.cosmic();
1047
1048 let (active_track, inactive_track) = if theme.is_high_contrast {
1049 (
1050 theme.accent_text_color(),
1051 if theme.is_dark {
1052 theme.palette.neutral_6
1053 } else {
1054 theme.palette.neutral_4
1055 },
1056 )
1057 } else {
1058 (
1059 theme.accent.base,
1060 theme.background(self.transparent).divider,
1061 )
1062 };
1063 let border = Border {
1064 radius: theme.corner_radii.radius_xl.into(),
1065 color: if theme.is_high_contrast && !theme.is_dark {
1066 self.current_container().component.border.into()
1067 } else {
1068 Color::TRANSPARENT
1069 },
1070 width: if theme.is_high_contrast && !theme.is_dark {
1071 1.
1072 } else {
1073 0.
1074 },
1075 };
1076 match class {
1077 ProgressBar::Primary => progress_bar::Style {
1078 background: Color::from(inactive_track).into(),
1079 bar: Color::from(active_track).into(),
1080 border,
1081 },
1082 ProgressBar::Success => progress_bar::Style {
1083 background: Color::from(inactive_track).into(),
1084 bar: Color::from(theme.success.base).into(),
1085 border,
1086 },
1087 ProgressBar::Danger => progress_bar::Style {
1088 background: Color::from(inactive_track).into(),
1089 bar: Color::from(theme.destructive.base).into(),
1090 border,
1091 },
1092 ProgressBar::Custom(f) => f(self),
1093 }
1094 }
1095}
1096
1097#[derive(Default)]
1101pub enum Rule {
1102 #[default]
1103 Default,
1104 LightDivider,
1105 HeavyDivider,
1106 Custom(Box<dyn Fn(&Theme) -> rule::Style>),
1107}
1108
1109impl Rule {
1110 pub fn custom<F: Fn(&Theme) -> rule::Style + 'static>(f: F) -> Self {
1111 Self::Custom(Box::new(f))
1112 }
1113}
1114
1115impl rule::Catalog for Theme {
1116 type Class<'a> = Rule;
1117
1118 fn default<'a>() -> Self::Class<'a> {
1119 Rule::default()
1120 }
1121
1122 fn style(&self, class: &Self::Class<'_>) -> rule::Style {
1123 match class {
1124 Rule::Default => rule::Style {
1125 color: self.current_container().divider.into(),
1126 radius: 0.0.into(),
1127 fill_mode: rule::FillMode::Full,
1128 snap: true,
1129 },
1130 Rule::LightDivider => rule::Style {
1131 color: self.current_container().divider.into(),
1132 radius: 0.0.into(),
1133 fill_mode: rule::FillMode::Padded(8),
1134 snap: true,
1135 },
1136 Rule::HeavyDivider => rule::Style {
1137 color: self.current_container().divider.into(),
1138 radius: 2.0.into(),
1139 fill_mode: rule::FillMode::Full,
1140 snap: true,
1141 },
1142 Rule::Custom(f) => f(self),
1143 }
1144 }
1145}
1146
1147#[derive(Default, Clone, Copy)]
1148pub enum Scrollable {
1149 #[default]
1150 Permanent,
1151 Minimal,
1152}
1153
1154impl scrollable::Catalog for Theme {
1158 type Class<'a> = Scrollable;
1159
1160 fn default<'a>() -> Self::Class<'a> {
1161 Scrollable::default()
1162 }
1163
1164 fn style(&self, class: &Self::Class<'_>, status: scrollable::Status) -> scrollable::Style {
1165 match status {
1166 scrollable::Status::Active {
1167 is_horizontal_scrollbar_disabled,
1168 is_vertical_scrollbar_disabled,
1169 } => {
1170 let cosmic = self.cosmic();
1171 let neutral_5 = cosmic.palette.neutral_5.with_alpha(0.7);
1172 let neutral_6 = cosmic.palette.neutral_6.with_alpha(0.7);
1173 let mut a = scrollable::Style {
1174 container: iced_container::transparent(self),
1175 vertical_rail: scrollable::Rail {
1176 border: Border {
1177 radius: cosmic.corner_radii.radius_s.into(),
1178 ..Default::default()
1179 },
1180 background: None,
1181 scroller: scrollable::Scroller {
1182 background: if cosmic.is_dark {
1183 neutral_6.into()
1184 } else {
1185 neutral_5.into()
1186 },
1187 border: Border {
1188 radius: cosmic.corner_radii.radius_s.into(),
1189 ..Default::default()
1190 },
1191 },
1192 },
1193 horizontal_rail: scrollable::Rail {
1194 border: Border {
1195 radius: cosmic.corner_radii.radius_s.into(),
1196 ..Default::default()
1197 },
1198 background: None,
1199 scroller: scrollable::Scroller {
1200 background: if cosmic.is_dark {
1201 neutral_6.into()
1202 } else {
1203 neutral_5.into()
1204 },
1205 border: Border {
1206 radius: cosmic.corner_radii.radius_s.into(),
1207 ..Default::default()
1208 },
1209 },
1210 },
1211 gap: None,
1212 auto_scroll: AutoScroll {
1214 background: Color::TRANSPARENT.into(),
1215 border: Border::default(),
1216 shadow: Shadow::default(),
1217 icon: Color::TRANSPARENT.into(),
1218 },
1219 };
1220 let small_widget_container = self.current_container().small_widget.with_alpha(0.7);
1221
1222 if matches!(class, Scrollable::Permanent) {
1223 a.horizontal_rail.background =
1224 Some(Background::Color(small_widget_container.into()));
1225 a.vertical_rail.background =
1226 Some(Background::Color(small_widget_container.into()));
1227 }
1228
1229 a
1230 }
1231 scrollable::Status::Hovered { .. } | scrollable::Status::Dragged { .. } => {
1233 let cosmic = self.cosmic();
1234 let neutral_5 = cosmic.palette.neutral_5.with_alpha(0.7);
1235 let neutral_6 = cosmic.palette.neutral_6.with_alpha(0.7);
1236
1237 let mut a: scrollable::Style = scrollable::Style {
1242 container: iced_container::Style::default(),
1243 vertical_rail: scrollable::Rail {
1244 border: Border {
1245 radius: cosmic.corner_radii.radius_s.into(),
1246 ..Default::default()
1247 },
1248 background: None,
1249 scroller: scrollable::Scroller {
1250 background: if cosmic.is_dark {
1251 neutral_6.into()
1252 } else {
1253 neutral_5.into()
1254 },
1255 border: Border {
1256 radius: cosmic.corner_radii.radius_s.into(),
1257 ..Default::default()
1258 },
1259 },
1260 },
1261 horizontal_rail: scrollable::Rail {
1262 border: Border {
1263 radius: cosmic.corner_radii.radius_s.into(),
1264 ..Default::default()
1265 },
1266 background: None,
1267 scroller: scrollable::Scroller {
1268 background: if cosmic.is_dark {
1269 neutral_6.into()
1270 } else {
1271 neutral_5.into()
1272 },
1273 border: Border {
1274 radius: cosmic.corner_radii.radius_s.into(),
1275 ..Default::default()
1276 },
1277 },
1278 },
1279 gap: None,
1280 auto_scroll: AutoScroll {
1282 background: Color::TRANSPARENT.into(),
1283 border: Border::default(),
1284 shadow: Shadow::default(),
1285 icon: Color::TRANSPARENT.into(),
1286 },
1287 };
1288
1289 if matches!(class, Scrollable::Permanent) {
1290 let small_widget_container =
1291 self.current_container().small_widget.with_alpha(0.7);
1292
1293 a.horizontal_rail.background =
1294 Some(Background::Color(small_widget_container.into()));
1295 a.vertical_rail.background =
1296 Some(Background::Color(small_widget_container.into()));
1297 }
1298
1299 a
1300 }
1301 }
1302 }
1303}
1304
1305#[derive(Clone, Default)]
1306pub enum Svg {
1307 Custom(Rc<dyn Fn(&Theme) -> svg::Style>),
1309 #[default]
1311 Default,
1312}
1313
1314impl Svg {
1315 pub fn custom<F: Fn(&Theme) -> svg::Style + 'static>(f: F) -> Self {
1316 Self::Custom(Rc::new(f))
1317 }
1318}
1319
1320impl svg::Catalog for Theme {
1321 type Class<'a> = Svg;
1322
1323 fn default<'a>() -> Self::Class<'a> {
1324 Svg::default()
1325 }
1326
1327 fn style(&self, class: &Self::Class<'_>, status: svg::Status) -> svg::Style {
1328 #[allow(clippy::match_same_arms)]
1329 match class {
1330 Svg::Default => svg::Style::default(),
1331 Svg::Custom(appearance) => appearance(self),
1332 }
1333 }
1334}
1335
1336#[derive(Clone, Copy, Default)]
1340pub enum Text {
1341 Accent,
1342 #[default]
1343 Default,
1344 Color(Color),
1345 Custom(fn(&Theme) -> iced_widget::text::Style),
1347}
1348
1349impl From<Color> for Text {
1350 fn from(color: Color) -> Self {
1351 Self::Color(color)
1352 }
1353}
1354
1355impl iced_widget::text::Catalog for Theme {
1356 type Class<'a> = Text;
1357
1358 fn default<'a>() -> Self::Class<'a> {
1359 Text::default()
1360 }
1361
1362 fn style(&self, class: &Self::Class<'_>) -> iced_widget::text::Style {
1363 let selected_fill = self.cosmic().accent.base.into();
1364 let selected_text_color = Some(self.cosmic().on_accent_color().into());
1365 match class {
1366 Text::Accent => iced_widget::text::Style {
1367 color: Some(self.cosmic().accent_text_color().into()),
1368 selected_fill,
1369 selected_text_color,
1370 },
1371 Text::Default => iced_widget::text::Style {
1372 color: None,
1373 selected_fill,
1374 selected_text_color,
1375 },
1376 Text::Color(c) => iced_widget::text::Style {
1377 color: Some(*c),
1378 selected_fill,
1379 selected_text_color,
1380 },
1381 Text::Custom(f) => f(self),
1382 }
1383 }
1384}
1385
1386#[derive(Copy, Clone, Default)]
1387pub enum TextInput {
1388 #[default]
1389 Default,
1390 Search,
1391}
1392
1393impl text_input::Catalog for Theme {
1397 type Class<'a> = TextInput;
1398
1399 fn default<'a>() -> Self::Class<'a> {
1400 TextInput::default()
1401 }
1402
1403 fn style(&self, class: &Self::Class<'_>, status: text_input::Status) -> text_input::Style {
1404 let palette = self.cosmic();
1405 let bg = self.current_container().small_widget.with_alpha(0.25);
1406
1407 let neutral_9 = palette.palette.neutral_9;
1408 let value = neutral_9.into();
1409 let placeholder = neutral_9.with_alpha(0.7).into();
1410 let selection = palette.accent.base.into();
1411
1412 let mut appearance = match class {
1413 TextInput::Default => text_input::Style {
1414 background: Color::from(bg).into(),
1415 border: Border {
1416 radius: palette.corner_radii.radius_s.into(),
1417 width: 1.0,
1418 color: self.current_container().component.divider.into(),
1419 },
1420 icon: self.current_container().on.into(),
1421 placeholder,
1422 value,
1423 selection,
1424 },
1425 TextInput::Search => text_input::Style {
1426 background: Color::from(bg).into(),
1427 border: Border {
1428 radius: palette.corner_radii.radius_m.into(),
1429 ..Default::default()
1430 },
1431 icon: self.current_container().on.into(),
1432 placeholder,
1433 value,
1434 selection,
1435 },
1436 };
1437
1438 match status {
1439 text_input::Status::Active => appearance,
1440 text_input::Status::Hovered => {
1441 let bg = self.current_container().small_widget.with_alpha(0.25);
1442
1443 match class {
1444 TextInput::Default => text_input::Style {
1445 background: Color::from(bg).into(),
1446 border: Border {
1447 radius: palette.corner_radii.radius_s.into(),
1448 width: 1.0,
1449 color: self.current_container().on.into(),
1450 },
1451 icon: self.current_container().on.into(),
1452 placeholder,
1453 value,
1454 selection,
1455 },
1456 TextInput::Search => text_input::Style {
1457 background: Color::from(bg).into(),
1458 border: Border {
1459 radius: palette.corner_radii.radius_m.into(),
1460 ..Default::default()
1461 },
1462 icon: self.current_container().on.into(),
1463 placeholder,
1464 value,
1465 selection,
1466 },
1467 }
1468 }
1469 text_input::Status::Focused { is_hovered } => {
1470 let bg = self.current_container().small_widget.with_alpha(0.25);
1471
1472 match class {
1473 TextInput::Default => text_input::Style {
1474 background: Color::from(bg).into(),
1475 border: Border {
1476 radius: palette.corner_radii.radius_s.into(),
1477 width: 1.0,
1478 color: palette.accent.base.into(),
1479 },
1480 icon: self.current_container().on.into(),
1481 placeholder,
1482 value,
1483 selection,
1484 },
1485 TextInput::Search => text_input::Style {
1486 background: Color::from(bg).into(),
1487 border: Border {
1488 radius: palette.corner_radii.radius_m.into(),
1489 ..Default::default()
1490 },
1491 icon: self.current_container().on.into(),
1492 placeholder,
1493 value,
1494 selection,
1495 },
1496 }
1497 }
1498 text_input::Status::Disabled => {
1499 appearance.background = match appearance.background {
1500 Background::Color(color) => Background::Color(Color {
1501 a: color.a * 0.5,
1502 ..color
1503 }),
1504 Background::Gradient(gradient) => {
1505 Background::Gradient(gradient.scale_alpha(0.5))
1506 }
1507 };
1508 appearance.border.color.a /= 2.;
1509 appearance.icon.a /= 2.;
1510 appearance.placeholder.a /= 2.;
1511 appearance.value.a /= 2.;
1512 appearance
1513 }
1514 }
1515 }
1516}
1517
1518#[derive(Default)]
1519pub enum TextEditor<'a> {
1520 #[default]
1521 Default,
1522 Custom(text_editor::StyleFn<'a, Theme>),
1523}
1524
1525impl<'a> From<text_editor::StyleFn<'a, Theme>> for TextEditor<'a> {
1526 fn from(style: text_editor::StyleFn<'a, Theme>) -> Self {
1527 Self::Custom(style)
1528 }
1529}
1530
1531impl iced_widget::text_editor::Catalog for Theme {
1532 type Class<'a> = TextEditor<'a>;
1533
1534 fn default<'a>() -> Self::Class<'a> {
1535 TextEditor::default()
1536 }
1537
1538 fn style(
1539 &self,
1540 class: &Self::Class<'_>,
1541 status: iced_widget::text_editor::Status,
1542 ) -> iced_widget::text_editor::Style {
1543 if let TextEditor::Custom(style) = class {
1544 return style(self, status);
1545 }
1546
1547 let cosmic = self.cosmic();
1548
1549 let selection = cosmic.accent.base.into();
1550 let value = cosmic.palette.neutral_9.into();
1551 let placeholder = cosmic.palette.neutral_9.with_alpha(0.7).into();
1552 let icon: Color = cosmic.background(self.transparent).on.into();
1553 match status {
1556 iced_widget::text_editor::Status::Active
1557 | iced_widget::text_editor::Status::Hovered
1558 | iced_widget::text_editor::Status::Disabled => iced_widget::text_editor::Style {
1559 background: iced::Color::from(cosmic.bg_color()).into(),
1560 border: Border {
1561 radius: cosmic.corner_radii.radius_0.into(),
1562 width: f32::from(cosmic.space_xxxs()),
1563 color: iced::Color::from(cosmic.bg_divider()),
1564 },
1565 placeholder,
1566 value,
1567 selection,
1568 },
1569 iced_widget::text_editor::Status::Focused { is_hovered } => {
1570 iced_widget::text_editor::Style {
1571 background: iced::Color::from(cosmic.bg_color()).into(),
1572 border: Border {
1573 radius: cosmic.corner_radii.radius_0.into(),
1574 width: f32::from(cosmic.space_xxxs()),
1575 color: iced::Color::from(cosmic.accent.base),
1576 },
1577 placeholder,
1578 value,
1579 selection,
1580 }
1581 }
1582 }
1583 }
1584}
1585
1586#[cfg(feature = "markdown")]
1587impl iced_widget::markdown::Catalog for Theme {
1588 fn code_block<'a>() -> <Self as iced_container::Catalog>::Class<'a> {
1589 Container::custom(|_| iced_container::Style {
1590 background: Some(iced::color!(0x111111).into()),
1591 text_color: Some(Color::WHITE),
1592 border: iced::border::rounded(2),
1593 ..iced_container::Style::default()
1594 })
1595 }
1596}
1597
1598impl iced_widget::table::Catalog for Theme {
1599 type Class<'a> = iced_widget::table::StyleFn<'a, Self>;
1600
1601 fn default<'a>() -> Self::Class<'a> {
1602 Box::new(|theme| iced_widget::table::Style {
1603 separator_x: theme.current_container().divider.into(),
1604 separator_y: theme.current_container().divider.into(),
1605 })
1606 }
1607
1608 fn style(&self, class: &Self::Class<'_>) -> iced_widget::table::Style {
1609 class(self)
1610 }
1611}
1612
1613#[cfg(feature = "qr_code")]
1614impl iced_widget::qr_code::Catalog for Theme {
1615 type Class<'a> = iced_widget::qr_code::StyleFn<'a, Self>;
1616
1617 fn default<'a>() -> Self::Class<'a> {
1618 Box::new(|_theme| iced_widget::qr_code::Style {
1619 cell: Color::BLACK,
1620 background: Color::WHITE,
1621 })
1622 }
1623
1624 fn style(&self, class: &Self::Class<'_>) -> iced_widget::qr_code::Style {
1625 class(self)
1626 }
1627}
1628
1629impl combo_box::Catalog for Theme {}
1630
1631impl Base for Theme {
1632 fn default(preference: iced::theme::Mode) -> Self {
1633 match preference {
1634 iced::theme::Mode::Light => Theme::light(),
1635 iced::theme::Mode::Dark | iced::theme::Mode::None => Theme::dark(),
1636 }
1637 }
1638
1639 fn mode(&self) -> iced::theme::Mode {
1640 if self.theme_type.is_dark() {
1641 iced::theme::Mode::Dark
1642 } else {
1643 iced::theme::Mode::Light
1644 }
1645 }
1646
1647 fn base(&self) -> iced::theme::Style {
1648 iced::theme::Style {
1649 background_color: self.cosmic().bg_color().into(),
1650 text_color: self.cosmic().on_bg_color().into(),
1651 icon_color: self.cosmic().on_bg_color().into(),
1652 }
1653 }
1654
1655 fn palette(&self) -> Option<iced::theme::Palette> {
1656 Some(iced::theme::Palette {
1657 primary: self.cosmic().accent.base.into(),
1658 success: self.cosmic().success.base.into(),
1659 warning: self.cosmic().warning.base.into(),
1660 danger: self.cosmic().destructive.base.into(),
1661 background: iced::Color::from(self.cosmic().bg_color()),
1662 text: iced::Color::from(self.cosmic().on_bg_color()),
1663 })
1664 }
1665
1666 fn name(&self) -> &str {
1667 match &self.theme_type {
1668 crate::theme::ThemeType::Dark => "Cosmic Dark Theme",
1669 crate::theme::ThemeType::Light => "Cosmic Light Theme",
1670 crate::theme::ThemeType::HighContrastDark => "Cosmic High Contrast Dark Theme",
1671 crate::theme::ThemeType::HighContrastLight => "Cosmic High Contrast Light Theme",
1672 crate::theme::ThemeType::Custom(theme) => "Custom Cosmic Theme",
1673 crate::theme::ThemeType::System { prefer_dark, theme } => &theme.name,
1674 }
1675 }
1676}