winio/widgets/
radio_button.rs

1use std::{
2    hint::unreachable_unchecked,
3    ops::{Deref, DerefMut},
4};
5
6use inherit_methods_macro::inherit_methods;
7use winio_elm::{Child, Component, ComponentSender};
8use winio_handle::BorrowedContainer;
9use winio_primitive::{Enable, Failable, Layoutable, Point, Size, TextWidget, ToolTip, Visible};
10
11use crate::{
12    sys,
13    sys::{Error, Result},
14};
15
16/// A simple radio box. See [`RadioButtonGroup`] for making selection groups.
17#[derive(Debug)]
18pub struct RadioButton {
19    widget: sys::RadioButton,
20}
21
22impl Failable for RadioButton {
23    type Error = Error;
24}
25
26#[inherit_methods(from = "self.widget")]
27impl ToolTip for RadioButton {
28    fn tooltip(&self) -> Result<String>;
29
30    fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;
31}
32
33#[inherit_methods(from = "self.widget")]
34impl TextWidget for RadioButton {
35    fn text(&self) -> Result<String>;
36
37    fn set_text(&mut self, s: impl AsRef<str>) -> Result<()>;
38}
39
40#[inherit_methods(from = "self.widget")]
41impl RadioButton {
42    /// If the box is checked.
43    pub fn is_checked(&self) -> Result<bool>;
44
45    /// Set the checked state.
46    pub fn set_checked(&mut self, v: bool) -> Result<()>;
47}
48
49#[inherit_methods(from = "self.widget")]
50impl Visible for RadioButton {
51    fn is_visible(&self) -> Result<bool>;
52
53    fn set_visible(&mut self, v: bool) -> Result<()>;
54}
55
56#[inherit_methods(from = "self.widget")]
57impl Enable for RadioButton {
58    fn is_enabled(&self) -> Result<bool>;
59
60    fn set_enabled(&mut self, v: bool) -> Result<()>;
61}
62
63#[inherit_methods(from = "self.widget")]
64impl Layoutable for RadioButton {
65    fn loc(&self) -> Result<Point>;
66
67    fn set_loc(&mut self, p: Point) -> Result<()>;
68
69    fn size(&self) -> Result<Size>;
70
71    fn set_size(&mut self, v: Size) -> Result<()>;
72
73    fn preferred_size(&self) -> Result<Size>;
74}
75
76/// Events of [`RadioButton`].
77#[derive(Debug)]
78#[non_exhaustive]
79pub enum RadioButtonEvent {
80    /// The check box has been clicked.
81    Click,
82}
83
84/// Messages of [`RadioButton`].
85#[derive(Debug)]
86#[non_exhaustive]
87pub enum RadioButtonMessage {}
88
89impl Component for RadioButton {
90    type Error = Error;
91    type Event = RadioButtonEvent;
92    type Init<'a> = BorrowedContainer<'a>;
93    type Message = RadioButtonMessage;
94
95    async fn init(init: Self::Init<'_>, _sender: &ComponentSender<Self>) -> Result<Self> {
96        let widget = sys::RadioButton::new(init)?;
97        Ok(Self { widget })
98    }
99
100    async fn start(&mut self, sender: &ComponentSender<Self>) -> ! {
101        loop {
102            self.widget.wait_click().await;
103            sender.output(RadioButtonEvent::Click);
104        }
105    }
106}
107
108winio_handle::impl_as_widget!(RadioButton, widget);
109
110/// A group of [`RadioButton`]. Only one of them could be checked.
111pub struct RadioButtonGroup {
112    radios: Vec<Child<RadioButton>>,
113}
114
115/// Events of [`RadioButtonGroup`].
116#[derive(Debug)]
117#[non_exhaustive]
118pub enum RadioButtonGroupEvent {
119    /// A radio button has been selected, with its index.
120    Click(usize),
121}
122
123/// Messages of [`RadioButtonGroup`].
124#[derive(Debug)]
125#[non_exhaustive]
126pub enum RadioButtonGroupMessage {
127    /// A radio button has been selected, with its index.
128    Click(usize),
129}
130
131impl Component for RadioButtonGroup {
132    type Error = Error;
133    type Event = RadioButtonGroupEvent;
134    type Init<'a> = Vec<Child<RadioButton>>;
135    type Message = RadioButtonGroupMessage;
136
137    async fn init(init: Self::Init<'_>, _sender: &ComponentSender<Self>) -> Result<Self> {
138        Ok(Self { radios: init })
139    }
140
141    async fn start(&mut self, sender: &ComponentSender<Self>) -> ! {
142        let futures = self
143            .radios
144            .iter_mut()
145            .enumerate()
146            .map(|(i, c)| {
147                c.start(
148                    sender,
149                    move |e| match e {
150                        RadioButtonEvent::Click => Some(RadioButtonGroupMessage::Click(i)),
151                    },
152                    // `RadioButton` never passes messages.
153                    || unsafe { unreachable_unchecked() },
154                )
155            })
156            .collect::<Vec<_>>();
157        futures_util::future::join_all(futures).await;
158        std::future::pending().await
159    }
160
161    async fn update(
162        &mut self,
163        message: Self::Message,
164        sender: &ComponentSender<Self>,
165    ) -> Result<bool> {
166        match message {
167            RadioButtonGroupMessage::Click(i) => {
168                for (idx, r) in self.radios.iter_mut().enumerate() {
169                    r.set_checked(idx == i)?;
170                }
171                sender.output(RadioButtonGroupEvent::Click(i));
172                Ok(false)
173            }
174        }
175    }
176}
177
178impl Deref for RadioButtonGroup {
179    type Target = Vec<Child<RadioButton>>;
180
181    fn deref(&self) -> &Self::Target {
182        &self.radios
183    }
184}
185
186impl DerefMut for RadioButtonGroup {
187    fn deref_mut(&mut self) -> &mut Self::Target {
188        &mut self.radios
189    }
190}