too/views/
radio.rs

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}