freya_components/
radio_item.rs

1use freya_core::prelude::*;
2use torin::prelude::*;
3
4use crate::{
5    get_theme,
6    theming::component_themes::{
7        RadioItemTheme,
8        RadioItemThemePartial,
9    },
10};
11
12/// Radio component.
13///
14/// # Example
15///
16/// ```rust
17/// # use std::collections::HashSet;
18/// # use freya::prelude::*;
19/// fn app() -> impl IntoElement {
20///     let mut checked = use_state(|| false);
21///
22///     rect()
23///         .child(
24///             Tile::new()
25///                 .on_select(move |_| checked.toggle())
26///                 .child(RadioItem::new().selected(checked()))
27///                 .leading("Click to check"),
28///         )
29///         .child(
30///             Tile::new()
31///                 .on_select(move |_| checked.toggle())
32///                 .child(RadioItem::new().selected(!checked()))
33///                 .child("Click to check"),
34///         )
35/// }
36///
37/// # use freya_testing::prelude::*;
38/// # launch_doc(|| {
39/// #   rect()
40///         .spacing(8.).center().expanded().child(app())
41/// # }, (250., 250.).into(), "./images/gallery_radio.png");
42/// ```
43///
44/// # Preview
45/// ![Radio Preview][radio]
46#[cfg_attr(feature = "docs",
47    doc = embed_doc_image::embed_image!("radio", "images/gallery_radio.png")
48)]
49#[derive(Clone, PartialEq)]
50pub struct RadioItem {
51    pub(crate) theme: Option<RadioItemThemePartial>,
52    key: DiffKey,
53    selected: bool,
54    size: f32,
55}
56
57impl Default for RadioItem {
58    fn default() -> Self {
59        Self::new()
60    }
61}
62
63impl RadioItem {
64    pub fn new() -> Self {
65        Self {
66            selected: false,
67            theme: None,
68            key: DiffKey::None,
69            size: 20.,
70        }
71    }
72
73    pub fn selected(mut self, selected: bool) -> Self {
74        self.selected = selected;
75        self
76    }
77
78    pub fn theme(mut self, theme: RadioItemThemePartial) -> Self {
79        self.theme = Some(theme);
80        self
81    }
82
83    pub fn key(mut self, key: impl Into<DiffKey>) -> Self {
84        self.key = key.into();
85        self
86    }
87
88    pub fn size(mut self, size: impl Into<f32>) -> Self {
89        self.size = size.into();
90        self
91    }
92}
93
94impl Render for RadioItem {
95    fn render(&self) -> impl IntoElement {
96        let focus = use_focus();
97        let focus_status = use_focus_status(focus);
98        let RadioItemTheme {
99            unselected_fill,
100            selected_fill,
101            border_fill,
102        } = get_theme!(&self.theme, radio);
103
104        let fill = if self.selected {
105            selected_fill
106        } else {
107            unselected_fill
108        };
109
110        let border = Border::new()
111            .fill(fill)
112            .width(2.)
113            .alignment(BorderAlignment::Inner);
114
115        let focused_border = (focus_status() == FocusStatus::Keyboard).then(|| {
116            Border::new()
117                .fill(border_fill)
118                .width((self.size * 0.15).ceil())
119                .alignment(BorderAlignment::Outer)
120        });
121
122        rect()
123            .a11y_id(focus.a11y_id())
124            .a11y_focusable(Focusable::Enabled)
125            .width(Size::px(self.size))
126            .height(Size::px(self.size))
127            .border(border)
128            .border(focused_border)
129            .padding(Gaps::new_all(4.0))
130            .main_align(Alignment::center())
131            .cross_align(Alignment::center())
132            .corner_radius(CornerRadius::new_all(99.))
133            .on_key_down(move |e: Event<KeyboardEventData>| {
134                if !Focus::is_pressed(&e) {
135                    e.stop_propagation();
136                }
137            })
138            .maybe_child(self.selected.then(|| {
139                rect()
140                    .width(Size::px((self.size * 0.55).floor()))
141                    .height(Size::px((self.size * 0.55).floor()))
142                    .background(fill)
143                    .corner_radius(CornerRadius::new_all(99.))
144            }))
145    }
146
147    fn render_key(&self) -> DiffKey {
148        self.key.clone().or(self.default_key())
149    }
150}
151
152pub fn radio() -> RadioItem {
153    RadioItem::new()
154}