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