Skip to main content

winio_ui_winui/widgets/
edit.rs

1use std::rc::Rc;
2
3use inherit_methods_macro::inherit_methods;
4use send_wrapper::SendWrapper;
5use windows::core::{HSTRING, Interface};
6use winio_callback::Callback;
7use winio_handle::{AsContainer, AsWidget, BorrowedContainer};
8use winio_primitive::{HAlign, Point, Size};
9use winui3::Microsoft::UI::Xaml::{
10    Controls::{self as MUXC, ScrollBarVisibility, ScrollViewer, TextChangedEventHandler},
11    RoutedEventHandler, TextWrapping, Visibility,
12};
13
14use crate::{GlobalRuntime, Result, Widget, widgets::Convertible};
15
16#[derive(Debug)]
17pub struct Edit {
18    on_change: SendWrapper<Rc<Callback>>,
19    handle: Widget,
20    password: bool,
21    halign: HAlign,
22}
23
24#[inherit_methods(from = "self.handle")]
25impl Edit {
26    pub fn new(parent: impl AsContainer) -> Result<Self> {
27        let text_box = MUXC::TextBox::new()?;
28        let password_box = MUXC::PasswordBox::new()?;
29        password_box.SetVisibility(Visibility::Collapsed)?;
30
31        let on_change = SendWrapper::new(Rc::new(Callback::new()));
32        {
33            let on_change = on_change.clone();
34            text_box.TextChanged(&TextChangedEventHandler::new(move |_, _| {
35                on_change.signal::<GlobalRuntime>(());
36                Ok(())
37            }))?;
38        }
39        {
40            let on_change = on_change.clone();
41            password_box.PasswordChanged(&RoutedEventHandler::new(move |_, _| {
42                on_change.signal::<GlobalRuntime>(());
43                Ok(())
44            }))?;
45        }
46
47        Ok(Self {
48            on_change,
49            handle: Widget::new(&parent, text_box.cast()?)?,
50            password: false,
51            halign: HAlign::Left,
52        })
53    }
54
55    fn recreate(&mut self, password: bool) -> Result<()> {
56        let parent = self.handle.parent()?;
57        let mut widget = if password {
58            let password_box = MUXC::PasswordBox::new()?;
59            let text_box = self.handle.as_widget().as_winui().cast::<MUXC::TextBox>()?;
60            password_box.SetPassword(&text_box.Text()?)?;
61            Widget::new(BorrowedContainer::winui(&parent), password_box.cast()?)?
62        } else {
63            let text_box = MUXC::TextBox::new()?;
64            let password_box = self
65                .handle
66                .as_widget()
67                .as_winui()
68                .cast::<MUXC::PasswordBox>()?;
69            text_box.SetText(&password_box.Password()?)?;
70            text_box.SetTextAlignment(self.halign.to_native())?;
71            Widget::new(BorrowedContainer::winui(&parent), text_box.cast()?)?
72        };
73        widget.set_visible(self.handle.is_visible()?)?;
74        widget.set_enabled(self.handle.is_enabled()?)?;
75        widget.set_loc(self.handle.loc()?)?;
76        widget.set_size(self.handle.size()?)?;
77        widget.set_tooltip(self.handle.tooltip()?)?;
78        self.handle = widget;
79        Ok(())
80    }
81
82    pub fn is_visible(&self) -> Result<bool>;
83
84    pub fn set_visible(&mut self, v: bool) -> Result<()>;
85
86    pub fn is_enabled(&self) -> Result<bool>;
87
88    pub fn set_enabled(&mut self, v: bool) -> Result<()>;
89
90    pub fn preferred_size(&self) -> Result<Size>;
91
92    pub fn loc(&self) -> Result<Point>;
93
94    pub fn set_loc(&mut self, p: Point) -> Result<()>;
95
96    pub fn size(&self) -> Result<Size>;
97
98    pub fn set_size(&mut self, v: Size) -> Result<()>;
99
100    pub fn tooltip(&self) -> Result<String>;
101
102    pub fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;
103
104    pub fn text(&self) -> Result<String> {
105        let text = if self.password {
106            self.handle
107                .as_widget()
108                .as_winui()
109                .cast::<MUXC::PasswordBox>()?
110                .Password()?
111                .to_string_lossy()
112        } else {
113            self.handle
114                .as_widget()
115                .as_winui()
116                .cast::<MUXC::TextBox>()?
117                .Text()?
118                .to_string_lossy()
119        };
120        Ok(text)
121    }
122
123    pub fn set_text(&mut self, s: impl AsRef<str>) -> Result<()> {
124        let s = HSTRING::from(s.as_ref());
125        if self.password {
126            let password_box = self
127                .handle
128                .as_widget()
129                .as_winui()
130                .cast::<MUXC::PasswordBox>()?;
131            password_box.SetPassword(&s)?;
132        } else {
133            let text_box = self.handle.as_widget().as_winui().cast::<MUXC::TextBox>()?;
134            text_box.SetText(&s)?;
135        }
136        Ok(())
137    }
138
139    pub fn is_password(&self) -> Result<bool> {
140        Ok(self.password)
141    }
142
143    pub fn set_password(&mut self, v: bool) -> Result<()> {
144        if self.password != v {
145            self.recreate(v)?;
146            self.password = v;
147        }
148        Ok(())
149    }
150
151    pub fn halign(&self) -> Result<HAlign> {
152        Ok(self.halign)
153    }
154
155    pub fn set_halign(&mut self, align: HAlign) -> Result<()> {
156        self.halign = align;
157        if let Ok(text_box) = self.handle.as_widget().as_winui().cast::<MUXC::TextBox>() {
158            text_box.SetTextAlignment(align.to_native())?;
159        }
160        Ok(())
161    }
162
163    pub fn is_readonly(&self) -> Result<bool> {
164        if self.password {
165            Ok(false)
166        } else {
167            Ok(self
168                .handle
169                .as_widget()
170                .as_winui()
171                .cast::<MUXC::TextBox>()?
172                .IsReadOnly()?)
173        }
174    }
175
176    pub fn set_readonly(&mut self, v: bool) -> Result<()> {
177        if !self.password {
178            let text_box = self.handle.as_widget().as_winui().cast::<MUXC::TextBox>()?;
179            text_box.SetIsReadOnly(v)?;
180        }
181        Ok(())
182    }
183
184    pub async fn wait_change(&self) {
185        self.on_change.wait().await
186    }
187}
188
189winio_handle::impl_as_widget!(Edit, handle);
190
191#[derive(Debug)]
192pub struct TextBox {
193    on_change: SendWrapper<Rc<Callback>>,
194    handle: Widget,
195    text_box: MUXC::TextBox,
196}
197
198#[inherit_methods(from = "self.handle")]
199impl TextBox {
200    pub fn new(parent: impl AsContainer) -> Result<Self> {
201        let text_box = MUXC::TextBox::new()?;
202        text_box.SetAcceptsReturn(true)?;
203        text_box.SetTextWrapping(TextWrapping::Wrap)?;
204        ScrollViewer::SetVerticalScrollBarVisibility2(&text_box, ScrollBarVisibility::Auto)?;
205        let on_change = SendWrapper::new(Rc::new(Callback::new()));
206        {
207            let on_change = on_change.clone();
208            text_box.TextChanged(&TextChangedEventHandler::new(move |_, _| {
209                on_change.signal::<GlobalRuntime>(());
210                Ok(())
211            }))?;
212        }
213        Ok(Self {
214            on_change,
215            handle: Widget::new(parent, text_box.cast()?)?,
216            text_box,
217        })
218    }
219
220    pub fn is_visible(&self) -> Result<bool>;
221
222    pub fn set_visible(&mut self, v: bool) -> Result<()>;
223
224    pub fn is_enabled(&self) -> Result<bool>;
225
226    pub fn set_enabled(&mut self, v: bool) -> Result<()>;
227
228    pub fn preferred_size(&self) -> Result<Size>;
229
230    pub fn min_size(&self) -> Result<Size>;
231
232    pub fn loc(&self) -> Result<Point>;
233
234    pub fn set_loc(&mut self, p: Point) -> Result<()>;
235
236    pub fn size(&self) -> Result<Size>;
237
238    pub fn set_size(&mut self, v: Size) -> Result<()>;
239
240    pub fn tooltip(&self) -> Result<String>;
241
242    pub fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;
243
244    pub fn text(&self) -> Result<String> {
245        Ok(self.text_box.Text()?.to_string_lossy().replace("\r", "\n"))
246    }
247
248    pub fn set_text(&mut self, s: impl AsRef<str>) -> Result<()> {
249        self.text_box.SetText(&HSTRING::from(s.as_ref()))?;
250        Ok(())
251    }
252
253    pub fn halign(&self) -> Result<HAlign> {
254        Ok(HAlign::from_native(self.text_box.TextAlignment()?))
255    }
256
257    pub fn set_halign(&mut self, align: HAlign) -> Result<()> {
258        self.text_box.SetTextAlignment(align.to_native())?;
259        Ok(())
260    }
261
262    pub fn is_readonly(&self) -> Result<bool> {
263        self.text_box.IsReadOnly()
264    }
265
266    pub fn set_readonly(&mut self, v: bool) -> Result<()> {
267        self.text_box.SetIsReadOnly(v)?;
268        Ok(())
269    }
270
271    pub async fn wait_change(&self) {
272        self.on_change.wait().await
273    }
274}
275
276winio_handle::impl_as_widget!(TextBox, handle);