1use winapi::shared::{
2 windef::HBRUSH,
3 minwindef::{UINT, WPARAM, LPARAM}
4};
5use winapi::um::{
6 winuser::{WS_VISIBLE, WS_DISABLED, ES_NUMBER, ES_LEFT, ES_CENTER, ES_RIGHT, WS_TABSTOP, ES_AUTOHSCROLL},
7 wingdi::DeleteObject,
8};
9use crate::win32::window_helper as wh;
10use crate::win32::base_helper::{check_hwnd, to_utf16};
11use crate::{Font, NwgError, HTextAlign, RawEventHandler};
12use super::{ControlBase, ControlHandle};
13use std::cell::RefCell;
14use std::ops::Range;
15use std::char;
16
17const NOT_BOUND: &'static str = "TextInput is not yet bound to a winapi object";
18const BAD_HANDLE: &'static str = "INTERNAL ERROR: TextInput handle is not HWND!";
19
20
21bitflags! {
22 pub struct TextInputFlags: u32 {
33 const VISIBLE = WS_VISIBLE;
34 const DISABLED = WS_DISABLED;
35 const NUMBER = ES_NUMBER;
36 const AUTO_SCROLL = ES_AUTOHSCROLL;
37 const TAB_STOP = WS_TABSTOP;
38 }
39}
40
41#[derive(Default)]
81pub struct TextInput {
82 pub handle: ControlHandle,
83 background_brush: Option<HBRUSH>,
84 handler0: RefCell<Option<RawEventHandler>>,
85}
86
87impl TextInput {
88
89 pub fn builder<'a>() -> TextInputBuilder<'a> {
90 TextInputBuilder {
91 text: "",
92 placeholder_text: None,
93 size: (100, 25),
94 position: (0, 0),
95 flags: None,
96 ex_flags: 0,
97 limit: 0,
98 password: None,
99 align: HTextAlign::Left,
100 readonly: false,
101 focus: false,
102 font: None,
103 parent: None,
104 background_color: None,
105 }
106 }
107
108 pub fn font(&self) -> Option<Font> {
110 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
111 let font_handle = wh::get_window_font(handle);
112 if font_handle.is_null() {
113 None
114 } else {
115 Some(Font { handle: font_handle })
116 }
117 }
118
119 pub fn set_font(&self, font: Option<&Font>) {
121 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
122 unsafe { wh::set_window_font(handle, font.map(|f| f.handle), true); }
123 }
124
125 pub fn password_char(&self) -> Option<char> {
127 use winapi::um::winuser::EM_GETPASSWORDCHAR;
128
129 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
130
131 let raw_char = wh::send_message(handle, EM_GETPASSWORDCHAR as u32, 0, 0) as u32;
132 match raw_char {
133 0 => None,
134 v => char::from_u32(v)
135 }
136 }
137
138 pub fn set_password_char(&self, c: Option<char>) {
141 use winapi::um::winuser::{InvalidateRect, EM_SETPASSWORDCHAR};
142
143 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
144 wh::send_message(handle, EM_SETPASSWORDCHAR as u32, c.map(|c| c as usize).unwrap_or(0), 0);
145
146 unsafe { InvalidateRect(handle, ::std::ptr::null(), 1); }
148 }
149
150 pub fn limit(&self) -> u32 {
152 use winapi::um::winuser::EM_GETLIMITTEXT;
153
154 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
155 wh::send_message(handle, EM_GETLIMITTEXT as u32, 0, 0) as u32
156 }
157
158 pub fn set_limit(&self, limit: usize) {
161 use winapi::um::winuser::EM_SETLIMITTEXT;
162
163 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
164 wh::send_message(handle, EM_SETLIMITTEXT as u32, limit, 0);
165 }
166
167 pub fn modified(&self) -> bool {
169 use winapi::um::winuser::EM_GETMODIFY;
170
171 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
172 wh::send_message(handle, EM_GETMODIFY as u32, 0, 0) != 0
173 }
174
175 pub fn set_modified(&self, e: bool) {
177 use winapi::um::winuser::EM_SETMODIFY;
178 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
179 wh::send_message(handle, EM_SETMODIFY as u32, e as usize, 0);
180 }
181
182 pub fn undo(&self) {
184 use winapi::um::winuser::EM_UNDO;
185
186 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
187 wh::send_message(handle, EM_UNDO as u32, 0, 0);
188 }
189
190 pub fn selection(&self) -> Range<u32> {
192 use winapi::um::winuser::EM_GETSEL;
193
194 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
195
196 let mut start = 0u32;
197 let mut end = 0u32;
198 let ptr1 = &mut start as *mut u32;
199 let ptr2 = &mut end as *mut u32;
200 wh::send_message(handle, EM_GETSEL as UINT, ptr1 as WPARAM, ptr2 as LPARAM);
201
202 start..end
203 }
204
205 pub fn set_selection(&self, r: Range<u32>) {
207 use winapi::um::winuser::EM_SETSEL;
208
209 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
210 wh::send_message(handle, EM_SETSEL as u32, r.start as usize, r.end as isize);
211 }
212
213 pub fn len(&self) -> u32 {
216 use winapi::um::winuser::EM_LINELENGTH;
217 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
218 wh::send_message(handle, EM_LINELENGTH as u32, 0, 0) as u32
219 }
220
221 pub fn readonly(&self) -> bool {
224 use winapi::um::winuser::ES_READONLY;
225 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
226 wh::get_style(handle) & ES_READONLY == ES_READONLY
227 }
228
229 pub fn set_readonly(&self, r: bool) {
232 use winapi::um::winuser::EM_SETREADONLY;
233 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
234 wh::send_message(handle, EM_SETREADONLY as u32, r as WPARAM, 0);
235 }
236
237 pub fn focus(&self) -> bool {
239 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
240 unsafe { wh::get_focus(handle) }
241 }
242
243 pub fn set_focus(&self) {
245 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
246 unsafe { wh::set_focus(handle); }
247 }
248
249 pub fn enabled(&self) -> bool {
251 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
252 unsafe { wh::get_window_enabled(handle) }
253 }
254
255 pub fn set_enabled(&self, v: bool) {
257 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
258 unsafe { wh::set_window_enabled(handle, v) }
259 }
260
261 pub fn visible(&self) -> bool {
264 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
265 unsafe { wh::get_window_visibility(handle) }
266 }
267
268 pub fn set_visible(&self, v: bool) {
270 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
271 unsafe { wh::set_window_visibility(handle, v) }
272 }
273
274 pub fn size(&self) -> (u32, u32) {
276 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
277 unsafe { wh::get_window_size(handle) }
278 }
279
280 pub fn set_size(&self, x: u32, y: u32) {
282 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
283 unsafe { wh::set_window_size(handle, x, y, false) }
284 }
285
286 pub fn position(&self) -> (i32, i32) {
288 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
289 unsafe { wh::get_window_position(handle) }
290 }
291
292 pub fn set_position(&self, x: i32, y: i32) {
294 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
295 unsafe { wh::set_window_position(handle, x, y) }
296 }
297
298 pub fn text(&self) -> String {
300 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
301 unsafe { wh::get_window_text(handle) }
302 }
303
304 pub fn set_text<'a>(&self, v: &'a str) {
306 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
307 unsafe { wh::set_window_text(handle, v) }
308 }
309
310 pub fn placeholder_text<'a>(&self, text_length: usize) -> String {
315 use std::ffi::OsString;
316 use std::os::windows::ffi::OsStringExt;
317 use winapi::shared::ntdef::WCHAR;
318 use winapi::um::commctrl::EM_GETCUEBANNER;
319
320 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
321 let mut placeholder_text: Vec<WCHAR> = Vec::with_capacity(text_length);
322 unsafe {
323 placeholder_text.set_len(text_length);
324 wh::send_message(handle, EM_GETCUEBANNER, placeholder_text.as_mut_ptr() as WPARAM, placeholder_text.len() as LPARAM);
325 OsString::from_wide(&placeholder_text).into_string().unwrap_or("".to_string())
326 }
327 }
328
329 pub fn set_placeholder_text<'a>(&self, v: Option<&'a str>) {
332 use winapi::um::commctrl::EM_SETCUEBANNER;
333
334 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
335 let placeholder_text = v.unwrap_or("");
336 let text = to_utf16(placeholder_text);
337 wh::send_message(handle, EM_SETCUEBANNER, 0, text.as_ptr() as LPARAM);
338 }
339
340 pub fn class_name(&self) -> &'static str {
342 "EDIT"
343 }
344
345 pub fn flags(&self) -> u32 {
347 ::winapi::um::winuser::WS_VISIBLE
348 }
349
350 pub fn forced_flags(&self) -> u32 {
352 use winapi::um::winuser::{WS_BORDER, WS_CHILD};
353
354 WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL | WS_CHILD
355 }
356
357 fn hook_non_client_size(&mut self, bg: Option<[u8; 3]>) {
359 use crate::bind_raw_event_handler_inner;
360 use winapi::shared::windef::{HGDIOBJ, RECT, POINT};
361 use winapi::um::winuser::{WM_NCCALCSIZE, WM_NCPAINT, WM_SIZE, DT_CALCRECT, DT_LEFT, NCCALCSIZE_PARAMS, COLOR_WINDOW,};
362 use winapi::um::winuser::{SWP_NOOWNERZORDER, SWP_NOSIZE, SWP_NOMOVE, SWP_FRAMECHANGED};
363 use winapi::um::winuser::{GetDC, DrawTextW, ReleaseDC, GetClientRect, GetWindowRect, FillRect, ScreenToClient, SetWindowPos};
364 use winapi::um::wingdi::{SelectObject, CreateSolidBrush, RGB};
365 use std::{mem, ptr};
366
367 if self.handle.blank() { panic!("{}", NOT_BOUND); }
368 self.handle.hwnd().expect(BAD_HANDLE);
369
370 let brush = match bg {
371 Some(c) => {
372 let b = unsafe { CreateSolidBrush(RGB(c[0], c[1], c[2])) };
373 self.background_brush = Some(b);
374 b
375 },
376 None => COLOR_WINDOW as HBRUSH
377 };
378
379 unsafe {
380
381 let handler = bind_raw_event_handler_inner(&self.handle, 0, move |hwnd, msg, w, l| {
382 match msg {
383 WM_NCCALCSIZE => {
384 if w == 0 { return None }
385
386 let font_handle = wh::get_window_font(hwnd);
388 let mut r: RECT = mem::zeroed();
389 let dc = GetDC(hwnd);
390
391 let old = SelectObject(dc, font_handle as HGDIOBJ);
392 let calc: [u16;2] = [75, 121];
393 DrawTextW(dc, calc.as_ptr(), 2, &mut r, DT_CALCRECT | DT_LEFT);
394
395 let client_height = r.bottom;
396
397 SelectObject(dc, old);
398 ReleaseDC(hwnd, dc);
399
400 let mut client: RECT = mem::zeroed();
402 let mut window: RECT = mem::zeroed();
403 GetClientRect(hwnd, &mut client);
404 GetWindowRect(hwnd, &mut window);
405
406 let window_height = window.bottom - window.top;
407 let center = ((window_height - client_height) / 2) - 4;
408
409 let info_ptr: *mut NCCALCSIZE_PARAMS = l as *mut NCCALCSIZE_PARAMS;
411 let info = &mut *info_ptr;
412
413 info.rgrc[0].top += center;
414 info.rgrc[0].bottom -= center;
415 },
416 WM_NCPAINT => {
417 let mut window: RECT = mem::zeroed();
418 let mut client: RECT = mem::zeroed();
419 GetWindowRect(hwnd, &mut window);
420 GetClientRect(hwnd, &mut client);
421
422 let mut pt1 = POINT {x: window.left, y: window.top};
423 ScreenToClient(hwnd, &mut pt1);
424
425 let mut pt2 = POINT {x: window.right, y: window.bottom};
426 ScreenToClient(hwnd, &mut pt2);
427
428 let top = RECT {
429 left: 0,
430 top: pt1.y,
431 right: client.right,
432 bottom: client.top
433 };
434
435 let bottom = RECT {
436 left: 0,
437 top: client.bottom,
438 right: client.right,
439 bottom: pt2.y
440 };
441
442 let dc = GetDC(hwnd);
443 FillRect(dc, &top, brush);
444 FillRect(dc, &bottom, brush);
445 ReleaseDC(hwnd, dc);
446 },
447 WM_SIZE => {
448 SetWindowPos(hwnd, ptr::null_mut(), 0, 0, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED);
449 },
450 _ => {}
451 }
452
453 None
454 });
455
456 *self.handler0.borrow_mut() = Some(handler.unwrap());
457
458 }
459 }
460
461}
462
463impl Drop for TextInput {
464 fn drop(&mut self) {
465 use crate::unbind_raw_event_handler;
466
467 let handler = self.handler0.borrow();
468 if let Some(h) = handler.as_ref() {
469 drop(unbind_raw_event_handler(h));
470 }
471
472 if let Some(bg) = self.background_brush {
473 unsafe { DeleteObject(bg as _); }
474 }
475
476 self.handle.destroy();
477 }
478}
479
480pub struct TextInputBuilder<'a> {
481 text: &'a str,
482 placeholder_text: Option<&'a str>,
483 size: (i32, i32),
484 position: (i32, i32),
485 flags: Option<TextInputFlags>,
486 ex_flags: u32,
487 limit: usize,
488 password: Option<char>,
489 align: HTextAlign,
490 readonly: bool,
491 font: Option<&'a Font>,
492 parent: Option<ControlHandle>,
493 background_color: Option<[u8; 3]>,
494 focus: bool,
495}
496
497impl<'a> TextInputBuilder<'a> {
498
499 pub fn flags(mut self, flags: TextInputFlags) -> TextInputBuilder<'a> {
500 self.flags = Some(flags);
501 self
502 }
503
504 pub fn ex_flags(mut self, flags: u32) -> TextInputBuilder<'a> {
505 self.ex_flags = flags;
506 self
507 }
508
509 pub fn text(mut self, text: &'a str) -> TextInputBuilder<'a> {
510 self.text = text;
511 self
512 }
513
514 pub fn placeholder_text(mut self, placeholder_text: Option<&'a str>) -> TextInputBuilder<'a> {
515 self.placeholder_text = placeholder_text;
516 self
517 }
518
519 pub fn size(mut self, size: (i32, i32)) -> TextInputBuilder<'a> {
520 self.size = size;
521 self
522 }
523
524 pub fn position(mut self, pos: (i32, i32)) -> TextInputBuilder<'a> {
525 self.position = pos;
526 self
527 }
528
529 pub fn limit(mut self, limit: usize) -> TextInputBuilder<'a> {
530 self.limit = limit;
531 self
532 }
533
534 pub fn password(mut self, psw: Option<char>) -> TextInputBuilder<'a> {
535 self.password = psw;
536 self
537 }
538
539 pub fn align(mut self, align: HTextAlign) -> TextInputBuilder<'a> {
540 self.align = align;
541 self
542 }
543
544 pub fn readonly(mut self, read: bool) -> TextInputBuilder<'a> {
545 self.readonly = read;
546 self
547 }
548
549 pub fn font(mut self, font: Option<&'a Font>) -> TextInputBuilder<'a> {
550 self.font = font;
551 self
552 }
553
554 pub fn background_color(mut self, color: Option<[u8;3]>) -> TextInputBuilder<'a> {
555 self.background_color = color;
556 self
557 }
558
559 pub fn focus(mut self, focus: bool) -> TextInputBuilder<'a> {
560 self.focus = focus;
561 self
562 }
563
564 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> TextInputBuilder<'a> {
565 self.parent = Some(p.into());
566 self
567 }
568
569 pub fn build(self, out: &mut TextInput) -> Result<(), NwgError> {
570 let mut flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
571
572 match self.align {
573 HTextAlign::Left => flags |= ES_LEFT,
574 HTextAlign::Center => flags |= ES_CENTER,
575 HTextAlign::Right => {
576 flags |= ES_RIGHT;
577 flags &= !ES_AUTOHSCROLL;
578 },
579 }
580
581 let parent = match self.parent {
582 Some(p) => Ok(p),
583 None => Err(NwgError::no_parent("TextInput"))
584 }?;
585
586 *out = Default::default();
587
588 out.handle = ControlBase::build_hwnd()
589 .class_name(out.class_name())
590 .forced_flags(out.forced_flags())
591 .flags(flags)
592 .ex_flags(self.ex_flags)
593 .size(self.size)
594 .position(self.position)
595 .text(self.text)
596 .parent(Some(parent))
597 .build()?;
598
599 out.hook_non_client_size(self.background_color);
600
601 if self.limit > 0 {
602 out.set_limit(self.limit);
603 }
604
605 if self.password.is_some() {
606 out.set_password_char(self.password)
607 }
608
609 if self.readonly {
610 out.set_readonly(self.readonly);
611 }
612
613 if self.focus {
614 out.set_focus();
615 }
616
617 if self.font.is_some() {
618 out.set_font(self.font);
619 } else {
620 out.set_font(Font::global_default().as_ref());
621 }
622
623 if self.placeholder_text.is_some() {
624 out.set_placeholder_text(self.placeholder_text);
625 }
626
627 Ok(())
628 }
629
630}
631
632impl PartialEq for TextInput {
633 fn eq(&self, other: &Self) -> bool {
634 self.handle == other.handle
635 }
636}