winio_ui_app_kit/widgets/
edit.rs1use inherit_methods_macro::inherit_methods;
2use objc2::{
3 DeclaredClass, MainThreadOnly, define_class, msg_send,
4 rc::{Allocated, Retained},
5 runtime::ProtocolObject,
6};
7use objc2_app_kit::{
8 NSControlTextEditingDelegate, NSSecureTextField, NSTextAlignment, NSTextField,
9 NSTextFieldDelegate,
10};
11use objc2_foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol};
12use winio_callback::Callback;
13use winio_handle::{AsContainer, BorrowedContainer};
14use winio_primitive::{HAlign, Point, Size};
15
16use crate::{GlobalRuntime, Result, catch, widgets::Widget};
17
18#[derive(Debug)]
19struct EditImpl {
20 handle: Widget,
21 view: Retained<NSTextField>,
22 delegate: Retained<EditDelegate>,
23}
24
25#[inherit_methods(from = "self.handle")]
26impl EditImpl {
27 pub fn new(
28 parent: impl AsContainer,
29 view: Retained<NSTextField>,
30 delegate: Retained<EditDelegate>,
31 ) -> Result<Self> {
32 catch(|| unsafe {
33 view.setBezeled(true);
34 view.setDrawsBackground(true);
35 view.setEditable(true);
36 view.setSelectable(true);
37 let del_obj = ProtocolObject::from_ref(&*delegate);
38 view.setDelegate(Some(del_obj));
39
40 let handle = Widget::from_nsview(parent, Retained::cast_unchecked(view.clone()))?;
41
42 Ok(Self {
43 handle,
44 view,
45 delegate,
46 })
47 })
48 .flatten()
49 }
50
51 pub fn is_visible(&self) -> Result<bool>;
52
53 pub fn set_visible(&mut self, v: bool) -> Result<()>;
54
55 pub fn is_enabled(&self) -> Result<bool>;
56
57 pub fn set_enabled(&mut self, v: bool) -> Result<()>;
58
59 pub fn preferred_size(&self) -> Result<Size>;
60
61 pub fn loc(&self) -> Result<Point>;
62
63 pub fn set_loc(&mut self, p: Point) -> Result<()>;
64
65 pub fn size(&self) -> Result<Size>;
66
67 pub fn set_size(&mut self, v: Size) -> Result<()>;
68
69 pub fn tooltip(&self) -> Result<String>;
70
71 pub fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;
72
73 pub fn text(&self) -> Result<String>;
74
75 pub fn set_text(&mut self, s: impl AsRef<str>) -> Result<()>;
76
77 pub fn halign(&self) -> Result<HAlign> {
78 let align = catch(|| self.view.alignment())?;
79 let align = match align {
80 NSTextAlignment::Right => HAlign::Right,
81 NSTextAlignment::Center => HAlign::Center,
82 NSTextAlignment::Justified => HAlign::Stretch,
83 _ => HAlign::Left,
84 };
85 Ok(align)
86 }
87
88 pub fn set_halign(&mut self, align: HAlign) -> Result<()> {
89 let align = match align {
90 HAlign::Left => NSTextAlignment::Left,
91 HAlign::Center => NSTextAlignment::Center,
92 HAlign::Right => NSTextAlignment::Right,
93 HAlign::Stretch => NSTextAlignment::Justified,
94 };
95 catch(|| self.view.setAlignment(align))
96 }
97
98 pub fn is_readonly(&self) -> Result<bool> {
99 catch(|| !self.view.isEditable())
100 }
101
102 pub fn set_readonly(&mut self, v: bool) -> Result<()> {
103 catch(|| self.view.setEditable(!v))
104 }
105
106 pub async fn wait_change(&self) {
107 self.delegate.ivars().changed.wait().await
108 }
109}
110
111winio_handle::impl_as_widget!(EditImpl, handle);
112
113#[derive(Debug)]
114pub struct Edit {
115 handle: EditImpl,
116 password: bool,
117}
118
119#[inherit_methods(from = "self.handle")]
120impl Edit {
121 pub fn new(parent: impl AsContainer) -> Result<Self> {
122 let parent = parent.as_container();
123 let mtm = parent.as_app_kit().mtm();
124
125 catch(|| {
126 let view = NSTextField::new(mtm);
127 let delegate = EditDelegate::new(mtm);
128 let handle = EditImpl::new(parent, view, delegate)?;
129 Ok(Self {
130 handle,
131 password: false,
132 })
133 })
134 .flatten()
135 }
136
137 fn recreate(&mut self, password: bool, mtm: MainThreadMarker) -> Result<()> {
138 let view = catch(|| unsafe {
139 if password {
140 Retained::cast_unchecked(NSSecureTextField::new(mtm))
141 } else {
142 NSTextField::new(mtm)
143 }
144 })?;
145 let parent = self.handle.handle.parent()?;
146 let mut new_handle = EditImpl::new(
147 BorrowedContainer::app_kit(&parent),
148 view,
149 self.handle.delegate.clone(),
150 )?;
151 new_handle.set_visible(self.handle.is_visible()?)?;
152 new_handle.set_enabled(self.handle.is_enabled()?)?;
153 new_handle.set_loc(self.handle.loc()?)?;
154 new_handle.set_size(self.handle.size()?)?;
155 new_handle.set_text(self.handle.text()?)?;
156 new_handle.set_halign(self.handle.halign()?)?;
157 self.handle = new_handle;
158 Ok(())
159 }
160
161 pub fn is_visible(&self) -> Result<bool>;
162
163 pub fn set_visible(&mut self, v: bool) -> Result<()>;
164
165 pub fn is_enabled(&self) -> Result<bool>;
166
167 pub fn set_enabled(&mut self, v: bool) -> Result<()>;
168
169 pub fn preferred_size(&self) -> Result<Size>;
170
171 pub fn loc(&self) -> Result<Point>;
172
173 pub fn set_loc(&mut self, p: Point) -> Result<()>;
174
175 pub fn size(&self) -> Result<Size>;
176
177 pub fn set_size(&mut self, v: Size) -> Result<()>;
178
179 pub fn tooltip(&self) -> Result<String>;
180
181 pub fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;
182
183 pub fn text(&self) -> Result<String>;
184
185 pub fn set_text(&mut self, s: impl AsRef<str>) -> Result<()>;
186
187 pub fn is_password(&self) -> Result<bool> {
188 Ok(self.password)
189 }
190
191 pub fn set_password(&mut self, v: bool) -> Result<()> {
192 if self.password != v {
193 self.recreate(v, self.handle.view.mtm())?;
194 self.password = v;
195 }
196 Ok(())
197 }
198
199 pub fn halign(&self) -> Result<HAlign>;
200
201 pub fn set_halign(&mut self, align: HAlign) -> Result<()>;
202
203 pub fn is_readonly(&self) -> Result<bool> {
204 if self.is_password()? {
205 Ok(false)
206 } else {
207 self.handle.is_readonly()
208 }
209 }
210
211 pub fn set_readonly(&mut self, v: bool) -> Result<()> {
212 if !self.is_password()? {
213 self.handle.set_readonly(v)?;
214 }
215 Ok(())
216 }
217
218 pub async fn wait_change(&self) {
219 self.handle.wait_change().await
220 }
221}
222
223winio_handle::impl_as_widget!(Edit, handle);
224
225#[derive(Debug, Default)]
226struct EditDelegateIvars {
227 changed: Callback,
228}
229
230define_class! {
231 #[unsafe(super(NSObject))]
232 #[name = "WinioEditDelegate"]
233 #[ivars = EditDelegateIvars]
234 #[thread_kind = MainThreadOnly]
235 #[derive(Debug)]
236 struct EditDelegate;
237
238 #[allow(non_snake_case)]
239 impl EditDelegate {
240 #[unsafe(method_id(init))]
241 fn init(this: Allocated<Self>) -> Option<Retained<Self>> {
242 let this = this.set_ivars(EditDelegateIvars::default());
243 unsafe { msg_send![super(this), init] }
244 }
245 }
246
247 unsafe impl NSObjectProtocol for EditDelegate {}
248
249 #[allow(non_snake_case)]
250 unsafe impl NSControlTextEditingDelegate for EditDelegate {
251 #[unsafe(method(controlTextDidChange:))]
252 fn controlTextDidChange(&self, _notification: &NSNotification) {
253 self.ivars().changed.signal::<GlobalRuntime>(());
254 }
255 }
256
257 unsafe impl NSTextFieldDelegate for EditDelegate {}
258}
259
260impl EditDelegate {
261 pub fn new(mtm: MainThreadMarker) -> Retained<Self> {
262 unsafe { msg_send![mtm.alloc::<Self>(), init] }
263 }
264}