Skip to main content

winio/widgets/
combo_box.rs

1use inherit_methods_macro::inherit_methods;
2use winio_elm::{Component, ComponentSender, ObservableVecEvent};
3use winio_handle::BorrowedContainer;
4use winio_primitive::{Enable, Failable, Layoutable, Point, Size, TextWidget, ToolTip, Visible};
5
6use crate::{
7    sys,
8    sys::{Error, Result},
9};
10
11/// A simple combo box.
12#[derive(Debug)]
13pub struct ComboBox {
14    widget: sys::ComboBox,
15}
16
17impl Failable for ComboBox {
18    type Error = Error;
19}
20
21#[inherit_methods(from = "self.widget")]
22impl ToolTip for ComboBox {
23    fn tooltip(&self) -> Result<String>;
24
25    fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;
26}
27
28#[inherit_methods(from = "self.widget")]
29impl TextWidget for ComboBox {
30    fn text(&self) -> Result<String>;
31
32    fn set_text(&mut self, s: impl AsRef<str>) -> Result<()>;
33}
34
35#[inherit_methods(from = "self.widget")]
36impl ComboBox {
37    /// The selection index.
38    pub fn selection(&self) -> Result<Option<usize>>;
39
40    /// Set the selection.
41    pub fn set_selection(&mut self, i: usize) -> Result<()>;
42
43    /// If the combo box is editable.
44    pub fn is_editable(&self) -> Result<bool>;
45
46    /// Set if the combo box is editable.
47    pub fn set_editable(&mut self, v: bool) -> Result<()>;
48
49    /// The length of list.
50    pub fn len(&self) -> Result<usize>;
51
52    /// If the list is empty.
53    pub fn is_empty(&self) -> Result<bool>;
54
55    /// Clear the list.
56    pub fn clear(&mut self) -> Result<()>;
57
58    /// Get the item by index.
59    pub fn get(&self, i: usize) -> Result<String>;
60
61    /// Set the item by index.
62    pub fn set(&mut self, i: usize, s: impl AsRef<str>) -> Result<()>;
63
64    /// Insert an item by index.
65    pub fn insert(&mut self, i: usize, s: impl AsRef<str>) -> Result<()>;
66
67    /// Remove the item by index.
68    pub fn remove(&mut self, i: usize) -> Result<()>;
69
70    /// Push an item to the end of the list.
71    pub fn push(&mut self, s: impl AsRef<str>) -> Result<()> {
72        let len = self.len()?;
73        self.insert(len, s)
74    }
75
76    /// Clears all items, and appends the new items one by one.
77    pub fn set_items<U: Into<String>>(&mut self, items: impl IntoIterator<Item = U>) -> Result<()> {
78        self.clear()?;
79        for it in items {
80            self.push(it.into())?;
81        }
82        Ok(())
83    }
84}
85
86#[inherit_methods(from = "self.widget")]
87impl Visible for ComboBox {
88    fn is_visible(&self) -> Result<bool>;
89
90    fn set_visible(&mut self, v: bool) -> Result<()>;
91}
92
93#[inherit_methods(from = "self.widget")]
94impl Enable for ComboBox {
95    fn is_enabled(&self) -> Result<bool>;
96
97    fn set_enabled(&mut self, v: bool) -> Result<()>;
98}
99
100#[inherit_methods(from = "self.widget")]
101impl Layoutable for ComboBox {
102    fn loc(&self) -> Result<Point>;
103
104    fn set_loc(&mut self, p: Point) -> Result<()> {
105        if !super::approx_eq_point(self.loc()?, p) {
106            self.widget.set_loc(p)?;
107        }
108        Ok(())
109    }
110
111    fn size(&self) -> Result<Size>;
112
113    fn set_size(&mut self, v: Size) -> Result<()> {
114        if !super::approx_eq_size(self.size()?, v) {
115            self.widget.set_size(v)?;
116        }
117        Ok(())
118    }
119
120    fn preferred_size(&self) -> Result<Size>;
121}
122
123/// Events of [`ComboBox`].
124#[derive(Debug)]
125#[non_exhaustive]
126pub enum ComboBoxEvent {
127    /// The selection has changed.
128    Select,
129    /// The text has been changed.
130    Change,
131}
132
133/// Messages of [`ComboBox`].
134#[derive(Debug)]
135#[non_exhaustive]
136pub enum ComboBoxMessage {
137    /// An element inserted.
138    Insert {
139        /// The insert position.
140        at: usize,
141        /// The value.
142        value: String,
143    },
144    /// An element removed.
145    Remove {
146        /// The remove position
147        at: usize,
148    },
149    /// An element of specific position is replaced.
150    Replace {
151        /// The replace position.
152        at: usize,
153        /// The new value.
154        value: String,
155    },
156    /// The vector has been cleared.
157    Clear,
158}
159
160impl ComboBoxMessage {
161    /// Retrive [`ComboBoxMessage`] from [`ObservableVecEvent`] by custom
162    /// function.
163    pub fn from_observable_vec_event_by<T>(
164        e: ObservableVecEvent<T>,
165        mut f: impl FnMut(T) -> String,
166    ) -> Self {
167        match e {
168            ObservableVecEvent::Insert { at, value } => Self::Insert {
169                at,
170                value: f(value),
171            },
172            ObservableVecEvent::Remove { at, .. } => Self::Remove { at },
173            ObservableVecEvent::Replace { at, new, .. } => Self::Replace { at, value: f(new) },
174            ObservableVecEvent::Clear => Self::Clear,
175        }
176    }
177
178    /// Retrive [`ComboBoxMessage`] from [`ObservableVecEvent`].
179    pub fn from_observable_vec_event<T: ToString>(e: ObservableVecEvent<T>) -> Self {
180        Self::from_observable_vec_event_by(e, |v| v.to_string())
181    }
182}
183
184impl Component for ComboBox {
185    type Error = Error;
186    type Event = ComboBoxEvent;
187    type Init<'a> = BorrowedContainer<'a>;
188    type Message = ComboBoxMessage;
189
190    async fn init(init: Self::Init<'_>, _sender: &ComponentSender<Self>) -> Result<Self> {
191        let widget = sys::ComboBox::new(init)?;
192        Ok(Self { widget })
193    }
194
195    async fn start(&mut self, sender: &ComponentSender<Self>) -> ! {
196        let fut_select = async {
197            loop {
198                self.widget.wait_select().await;
199                sender.output(ComboBoxEvent::Select);
200            }
201        };
202        let fut_change = async {
203            loop {
204                self.widget.wait_change().await;
205                sender.output(ComboBoxEvent::Change);
206            }
207        };
208        futures_util::future::join(fut_select, fut_change).await.0
209    }
210
211    async fn update(
212        &mut self,
213        message: Self::Message,
214        _sender: &ComponentSender<Self>,
215    ) -> Result<bool> {
216        match message {
217            ComboBoxMessage::Insert { at, value } => self.insert(at, value)?,
218            ComboBoxMessage::Remove { at } => self.remove(at)?,
219            ComboBoxMessage::Replace { at, value } => self.set(at, value)?,
220            ComboBoxMessage::Clear => self.clear()?,
221        }
222        Ok(true)
223    }
224}
225
226winio_handle::impl_as_widget!(ComboBox, widget);