1use crate::{
2 renderer::Rgba,
3 view::{Adhoc, Palette, Response, StyleKind},
4 Str,
5};
6
7use super::label::{label, LabelStyle};
8
9pub type RadioClass = fn(&Palette, bool) -> RadioStyle;
10
11#[derive(Debug)]
12pub struct RadioStyle {
13 pub selected: Option<&'static str>,
14 pub unselected: Option<&'static str>,
15
16 pub text_color: Rgba,
17
18 pub background: Rgba,
19 pub selected_background: Rgba,
20
21 pub hovered_text: Option<Rgba>,
22 pub hovered_background: Option<Rgba>,
23}
24
25impl RadioStyle {
26 pub fn default(palette: &Palette, _selected: bool) -> Self {
27 Self {
28 selected: None,
29 unselected: None,
30 text_color: palette.foreground,
31 background: palette.surface,
32 selected_background: palette.primary,
33 hovered_text: None,
34 hovered_background: None,
35 }
36 }
37
38 pub fn hovered(palette: &Palette, selected: bool) -> Self {
39 Self {
40 hovered_text: Some(palette.surface),
41 hovered_background: Some(palette.secondary),
42 ..Self::default(palette, selected)
43 }
44 }
45}
46
47pub struct Radio<'a, V> {
48 value: V,
49 existing: &'a mut V,
50 label: Str,
51 class: StyleKind<RadioClass, RadioStyle>,
52}
53
54impl<'v, V> Adhoc<'v> for Radio<'v, V>
55where
56 V: PartialEq,
57{
58 type Output = Response<bool>;
59
60 fn show(self, ui: &crate::view::Ui) -> Self::Output {
61 let resp = ui
62 .mouse_area(|ui| {
63 let style = match self.class {
64 StyleKind::Deferred(style) => {
65 (style)(&ui.palette(), self.value == *self.existing)
66 }
67 StyleKind::Direct(style) => style,
68 };
69
70 let hovered = ui.is_hovered();
71 let fill = match (hovered, self.value == *self.existing) {
72 (false, true) => style.selected_background,
73 (false, false) => style.background,
74 (true, true) => style
75 .hovered_background
76 .unwrap_or(style.selected_background),
77 (true, false) => style.hovered_background.unwrap_or(style.background),
78 };
79
80 let foreground = if hovered {
81 style.hovered_text.unwrap_or(style.text_color)
82 } else {
83 style.text_color
84 };
85
86 ui.background(fill, |ui| {
87 ui.show(label(self.label).style(LabelStyle { foreground }))
88 });
89 })
90 .flatten_left();
91
92 if resp.clicked() {
93 *self.existing = self.value;
94 }
95 resp.map(|c| c.clicked())
96 }
97}
98
99pub fn radio<V>(value: V, existing: &mut V, label: impl Into<Str>) -> Radio<'_, V>
100where
101 V: PartialEq,
102{
103 Radio {
104 value,
105 existing,
106 label: label.into(),
107 class: StyleKind::deferred(RadioStyle::default),
108 }
109}