native_windows_gui/controls/
text_box.rs1use winapi::shared::minwindef::{WPARAM, LPARAM};
2use winapi::um::winuser::{WS_VSCROLL, WS_HSCROLL, ES_AUTOVSCROLL, ES_AUTOHSCROLL, WS_VISIBLE, WS_DISABLED, WS_TABSTOP};
3use crate::win32::window_helper as wh;
4use crate::{Font, NwgError};
5use super::{ControlBase, ControlHandle};
6use std::ops::Range;
7use newline_converter::{dos2unix, unix2dos};
8
9const NOT_BOUND: &'static str = "TextBox is not yet bound to a winapi object";
10const BAD_HANDLE: &'static str = "INTERNAL ERROR: TextBox handle is not HWND!";
11
12
13bitflags! {
14
15 pub struct TextBoxFlags: u32 {
27 const VSCROLL = WS_VSCROLL;
28 const HSCROLL = WS_HSCROLL;
29 const AUTOVSCROLL = ES_AUTOVSCROLL;
30 const AUTOHSCROLL = ES_AUTOHSCROLL;
31 const VISIBLE = WS_VISIBLE;
32 const DISABLED = WS_DISABLED;
33 const TAB_STOP = WS_TABSTOP;
34 }
35}
36
37
38#[derive(Default, PartialEq, Eq)]
78pub struct TextBox {
79 pub handle: ControlHandle
80}
81
82impl TextBox {
83
84 pub fn builder<'a>() -> TextBoxBuilder<'a> {
85 TextBoxBuilder {
86 text: "",
87 size: (100, 25),
88 position: (0, 0),
89 flags: None,
90 ex_flags: 0,
91 limit: 0,
92 readonly: false,
93 focus: false,
94 font: None,
95 parent: None
96 }
97 }
98
99 pub fn font(&self) -> Option<Font> {
101 if self.handle.blank() { panic!("{}", NOT_BOUND); }
102 let handle = self.handle.hwnd().expect(BAD_HANDLE);
103
104 let font_handle = wh::get_window_font(handle);
105 if font_handle.is_null() {
106 None
107 } else {
108 Some(Font { handle: font_handle })
109 }
110 }
111
112 pub fn set_font(&self, font: Option<&Font>) {
114 if self.handle.blank() { panic!("{}", NOT_BOUND); }
115 let handle = self.handle.hwnd().expect(BAD_HANDLE);
116 unsafe { wh::set_window_font(handle, font.map(|f| f.handle), true); }
117 }
118
119 pub fn limit(&self) -> u32 {
121 use winapi::um::winuser::EM_GETLIMITTEXT;
122
123 if self.handle.blank() { panic!("{}", NOT_BOUND); }
124 let handle = self.handle.hwnd().expect(BAD_HANDLE);
125
126 wh::send_message(handle, EM_GETLIMITTEXT as u32, 0, 0) as u32
127 }
128
129 pub fn set_limit(&self, limit: usize) {
131 use winapi::um::winuser::EM_SETLIMITTEXT;
132
133 if self.handle.blank() { panic!("{}", NOT_BOUND); }
134 let handle = self.handle.hwnd().expect(BAD_HANDLE);
135
136 wh::send_message(handle, EM_SETLIMITTEXT as u32, limit, 0);
137 }
138
139 pub fn modified(&self) -> bool {
141 use winapi::um::winuser::EM_GETMODIFY;
142
143 if self.handle.blank() { panic!("{}", NOT_BOUND); }
144 let handle = self.handle.hwnd().expect(BAD_HANDLE);
145
146 wh::send_message(handle, EM_GETMODIFY as u32, 0, 0) != 0
147 }
148
149 pub fn set_modified(&self, e: bool) {
151 use winapi::um::winuser::EM_SETMODIFY;
152 if self.handle.blank() { panic!("{}", NOT_BOUND); }
153 let handle = self.handle.hwnd().expect(BAD_HANDLE);
154
155 wh::send_message(handle, EM_SETMODIFY as u32, e as usize, 0);
156 }
157
158 pub fn undo(&self) {
160 use winapi::um::winuser::EM_UNDO;
161
162 if self.handle.blank() { panic!("{}", NOT_BOUND); }
163 let handle = self.handle.hwnd().expect(BAD_HANDLE);
164
165 wh::send_message(handle, EM_UNDO as u32, 0, 0);
166 }
167
168 pub fn selection(&self) -> Range<u32> {
170 use winapi::um::winuser::EM_GETSEL;
171
172 if self.handle.blank() { panic!("{}", NOT_BOUND); }
173 let handle = self.handle.hwnd().expect(BAD_HANDLE);
174
175 let (mut out1, mut out2) = (0u32, 0u32);
176 let (ptr1, ptr2) = (&mut out1 as *mut u32, &mut out2 as *mut u32);
177 wh::send_message(handle, EM_GETSEL as u32, ptr1 as WPARAM, ptr2 as LPARAM);
178
179 Range { start: out1 as u32, end: out2 as u32 }
180 }
181
182 pub fn set_selection(&self, r: Range<u32>) {
184 use winapi::um::winuser::EM_SETSEL;
185
186 if self.handle.blank() { panic!("{}", NOT_BOUND); }
187 let handle = self.handle.hwnd().expect(BAD_HANDLE);
188 wh::send_message(handle, EM_SETSEL as u32, r.start as usize, r.end as isize);
189 }
190
191 pub fn len(&self) -> u32 {
194 use std::convert::TryInto;
195
196 dos2unix(&self.text()).chars().count().try_into().unwrap_or_default()
197 }
198
199 pub fn linecount(&self) -> i32 {
202 use winapi::um::winuser::EM_GETLINECOUNT;
203
204 if self.handle.blank() { panic!("{}", NOT_BOUND); }
205 let handle = self.handle.hwnd().expect(BAD_HANDLE);
206 wh::send_message(handle, EM_GETLINECOUNT as u32, 0, 0) as i32
207 }
208
209 pub fn scroll(&self, v: i32) {
211 use winapi::um::winuser::EM_LINESCROLL;
212
213 if self.handle.blank() { panic!("{}", NOT_BOUND); }
214 let handle = self.handle.hwnd().expect(BAD_HANDLE);
215 wh::send_message(handle, EM_LINESCROLL as u32, 0, v as LPARAM);
216 }
217
218 pub fn scroll_lastline(&self) {
220 let lines = self.linecount();
221 self.scroll(lines * -1);
222 self.scroll(lines - 2);
223 }
224
225 pub fn readonly(&self) -> bool {
228 use winapi::um::winuser::ES_READONLY;
229
230 if self.handle.blank() { panic!("{}", NOT_BOUND); }
231 let handle = self.handle.hwnd().expect(BAD_HANDLE);
232 wh::get_style(handle) & ES_READONLY == ES_READONLY
233 }
234
235 pub fn set_readonly(&self, r: bool) {
238 use winapi::um::winuser::EM_SETREADONLY;
239
240 if self.handle.blank() { panic!("{}", NOT_BOUND); }
241 let handle = self.handle.hwnd().expect(BAD_HANDLE);
242 wh::send_message(handle, EM_SETREADONLY as u32, r as WPARAM, 0);
243 }
244
245 pub fn clear(&self) {
247 self.set_text("");
248 }
249
250 pub fn focus(&self) -> bool {
252 if self.handle.blank() { panic!("{}", NOT_BOUND); }
253 let handle = self.handle.hwnd().expect(BAD_HANDLE);
254 unsafe { wh::get_focus(handle) }
255 }
256
257 pub fn set_focus(&self) {
259 if self.handle.blank() { panic!("{}", NOT_BOUND); }
260 let handle = self.handle.hwnd().expect(BAD_HANDLE);
261 unsafe { wh::set_focus(handle); }
262 }
263
264 pub fn enabled(&self) -> bool {
266 if self.handle.blank() { panic!("{}", NOT_BOUND); }
267 let handle = self.handle.hwnd().expect(BAD_HANDLE);
268 unsafe { wh::get_window_enabled(handle) }
269 }
270
271 pub fn set_enabled(&self, v: bool) {
273 if self.handle.blank() { panic!("{}", NOT_BOUND); }
274 let handle = self.handle.hwnd().expect(BAD_HANDLE);
275 unsafe { wh::set_window_enabled(handle, v) }
276 }
277
278 pub fn visible(&self) -> bool {
281 if self.handle.blank() { panic!("{}", NOT_BOUND); }
282 let handle = self.handle.hwnd().expect(BAD_HANDLE);
283 unsafe { wh::get_window_visibility(handle) }
284 }
285
286 pub fn set_visible(&self, v: bool) {
288 if self.handle.blank() { panic!("{}", NOT_BOUND); }
289 let handle = self.handle.hwnd().expect(BAD_HANDLE);
290 unsafe { wh::set_window_visibility(handle, v) }
291 }
292
293 pub fn size(&self) -> (u32, u32) {
295 if self.handle.blank() { panic!("{}", NOT_BOUND); }
296 let handle = self.handle.hwnd().expect(BAD_HANDLE);
297 unsafe { wh::get_window_size(handle) }
298 }
299
300 pub fn set_size(&self, x: u32, y: u32) {
302 if self.handle.blank() { panic!("{}", NOT_BOUND); }
303 let handle = self.handle.hwnd().expect(BAD_HANDLE);
304 unsafe { wh::set_window_size(handle, x, y, false) }
305 }
306
307 pub fn position(&self) -> (i32, i32) {
309 if self.handle.blank() { panic!("{}", NOT_BOUND); }
310 let handle = self.handle.hwnd().expect(BAD_HANDLE);
311 unsafe { wh::get_window_position(handle) }
312 }
313
314 pub fn set_position(&self, x: i32, y: i32) {
316 if self.handle.blank() { panic!("{}", NOT_BOUND); }
317 let handle = self.handle.hwnd().expect(BAD_HANDLE);
318 unsafe { wh::set_window_position(handle, x, y) }
319 }
320
321 pub fn text(&self) -> String {
323 if self.handle.blank() { panic!("{}", NOT_BOUND); }
324 let handle = self.handle.hwnd().expect(BAD_HANDLE);
325 unsafe { wh::get_window_text(handle) }
326 }
327
328 pub fn set_text<'a>(&self, v: &'a str) {
330 if self.handle.blank() { panic!("{}", NOT_BOUND); }
331 let handle = self.handle.hwnd().expect(BAD_HANDLE);
332 unsafe { wh::set_window_text(handle, v) }
333 }
334
335 pub fn set_text_unix2dos<'a>(&self, v: &'a str) {
337 if self.handle.blank() { panic!("{}", NOT_BOUND); }
338 let handle = self.handle.hwnd().expect(BAD_HANDLE);
339 unsafe { wh::set_window_text(handle, &unix2dos(&v).to_string()) }
340 }
341
342 pub fn append<'a>(&self, v: &'a str) {
344 let text = self.text() + &unix2dos(&v).to_string();
345 self.set_text(&text);
346 self.scroll_lastline();
347 }
348
349 pub fn appendln<'a>(&self, v: &'a str) {
351 let text = self.text() + &unix2dos(&v).to_string() + "\r\n";
352 self.set_text(&text);
353 self.scroll_lastline();
354 }
355
356 pub fn class_name(&self) -> &'static str {
358 "EDIT"
359 }
360
361 pub fn flags(&self) -> u32 {
363 WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_AUTOVSCROLL | ES_AUTOHSCROLL | WS_TABSTOP
364 }
365
366 pub fn forced_flags(&self) -> u32 {
368 use winapi::um::winuser::{WS_BORDER, WS_CHILD, ES_MULTILINE, ES_WANTRETURN};
369
370 WS_BORDER | WS_CHILD | ES_MULTILINE | ES_WANTRETURN
371 }
372
373}
374
375impl Drop for TextBox {
376 fn drop(&mut self) {
377 self.handle.destroy();
378 }
379}
380pub struct TextBoxBuilder<'a> {
381 text: &'a str,
382 size: (i32, i32),
383 position: (i32, i32),
384 flags: Option<TextBoxFlags>,
385 ex_flags: u32,
386 limit: usize,
387 readonly: bool,
388 focus: bool,
389 font: Option<&'a Font>,
390 parent: Option<ControlHandle>
391}
392
393impl<'a> TextBoxBuilder<'a> {
394
395 pub fn flags(mut self, flags: TextBoxFlags) -> TextBoxBuilder<'a> {
396 self.flags = Some(flags);
397 self
398 }
399
400 pub fn ex_flags(mut self, flags: u32) -> TextBoxBuilder<'a> {
401 self.ex_flags = flags;
402 self
403 }
404
405 pub fn text(mut self, text: &'a str) -> TextBoxBuilder<'a> {
406 self.text = text;
407 self
408 }
409
410 pub fn size(mut self, size: (i32, i32)) -> TextBoxBuilder<'a> {
411 self.size = size;
412 self
413 }
414
415 pub fn position(mut self, pos: (i32, i32)) -> TextBoxBuilder<'a> {
416 self.position = pos;
417 self
418 }
419
420 pub fn limit(mut self, limit: usize) -> TextBoxBuilder<'a> {
421 self.limit = limit;
422 self
423 }
424
425 pub fn readonly(mut self, read: bool) -> TextBoxBuilder<'a> {
426 self.readonly = read;
427 self
428 }
429
430 pub fn focus(mut self, focus: bool) -> TextBoxBuilder<'a> {
431 self.focus = focus;
432 self
433 }
434
435 pub fn font(mut self, font: Option<&'a Font>) -> TextBoxBuilder<'a> {
436 self.font = font;
437 self
438 }
439
440 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> TextBoxBuilder<'a> {
441 self.parent = Some(p.into());
442 self
443 }
444
445 pub fn build(self, out: &mut TextBox) -> Result<(), NwgError> {
446 let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
447
448 let parent = match self.parent {
449 Some(p) => Ok(p),
450 None => Err(NwgError::no_parent("TextBox"))
451 }?;
452
453 *out = Default::default();
454
455 out.handle = ControlBase::build_hwnd()
456 .class_name(out.class_name())
457 .forced_flags(out.forced_flags())
458 .flags(flags)
459 .ex_flags(self.ex_flags)
460 .size(self.size)
461 .position(self.position)
462 .text(self.text)
463 .parent(Some(parent))
464 .build()?;
465
466 if self.limit > 0 {
467 out.set_limit(self.limit);
468 }
469
470 if self.readonly {
471 out.set_readonly(self.readonly);
472 }
473
474 if self.focus {
475 out.set_focus();
476 }
477
478 if self.font.is_some() {
479 out.set_font(self.font);
480 } else {
481 out.set_font(Font::global_default().as_ref());
482 }
483
484 Ok(())
485 }
486
487}