1use super::{ControlBase, ControlHandle};
2use crate::win32::base_helper::{check_hwnd, from_utf16, to_utf16};
3use crate::win32::window_helper as wh;
4use crate::{Font, NwgError};
5use std::cell::{Ref, RefCell, RefMut};
6use std::fmt::Display;
7use std::mem;
8use std::ops::Range;
9use winapi::shared::minwindef::{LPARAM, WPARAM};
10use winapi::shared::windef::HWND;
11use winapi::um::winuser::{LBS_MULTIPLESEL, LBS_NOSEL, WS_DISABLED, WS_TABSTOP, WS_VISIBLE};
12
13const NOT_BOUND: &'static str = "ListBox is not yet bound to a winapi object";
14const BAD_HANDLE: &'static str = "INTERNAL ERROR: ListBox handle is not HWND!";
15
16bitflags! {
17 pub struct ListBoxFlags: u32 {
28 const NONE = 0;
29 const VISIBLE = WS_VISIBLE;
30 const DISABLED = WS_DISABLED;
31 const MULTI_SELECT = LBS_MULTIPLESEL;
32 const NO_SELECT = LBS_NOSEL;
33 const TAB_STOP = WS_TABSTOP;
34 }
35}
36
37#[derive(Default)]
77pub struct ListBox<D: Display + Default> {
78 pub handle: ControlHandle,
79 collection: RefCell<Vec<D>>,
80}
81
82impl<D: Display + Default> ListBox<D> {
83 pub fn builder<'a>() -> ListBoxBuilder<'a, D> {
84 ListBoxBuilder {
85 size: (100, 300),
86 position: (0, 0),
87 enabled: true,
88 focus: false,
89 flags: None,
90 ex_flags: 0,
91 font: None,
92 collection: None,
93 selected_index: None,
94 multi_selection: Vec::new(),
95 parent: None,
96 }
97 }
98
99 pub fn push(&self, item: D) {
101 use winapi::um::winuser::LB_ADDSTRING;
102
103 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
104 let display = format!("{}", item);
105 let display_os = to_utf16(&display);
106
107 wh::send_message(handle, LB_ADDSTRING, 0, display_os.as_ptr() as LPARAM);
108
109 self.collection.borrow_mut().push(item);
110 }
111
112 pub fn insert(&self, index: usize, item: D) {
117 use winapi::um::winuser::LB_INSERTSTRING;
118
119 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
120 let display = format!("{}", item);
121 let display_os = to_utf16(&display);
122
123 let mut col = self.collection.borrow_mut();
124 if index == std::usize::MAX {
125 col.push(item);
126 } else {
127 col.insert(index, item);
128 }
129
130 wh::send_message(
131 handle,
132 LB_INSERTSTRING,
133 index,
134 display_os.as_ptr() as LPARAM,
135 );
136 }
137
138 pub fn remove(&self, index: usize) -> D {
141 use winapi::um::winuser::LB_DELETESTRING;
142
143 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
144 wh::send_message(handle, LB_DELETESTRING, index as WPARAM, 0);
145
146 let mut col_ref = self.collection.borrow_mut();
147 col_ref.remove(index)
148 }
149
150 pub fn selection(&self) -> Option<usize> {
153 use winapi::um::winuser::{LB_ERR, LB_GETCURSEL};
154
155 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
156 let index = wh::send_message(handle, LB_GETCURSEL, 0, 0);
157
158 if index == LB_ERR {
159 None
160 } else {
161 Some(index as usize)
162 }
163 }
164
165 pub fn multi_selection_len(&self) -> usize {
168 use winapi::um::winuser::{LB_ERR, LB_GETSELCOUNT};
169
170 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
171 match wh::send_message(handle, LB_GETSELCOUNT, 0, 0) {
172 LB_ERR => 0,
173 value => value as usize,
174 }
175 }
176
177 pub fn multi_selection(&self) -> Vec<usize> {
180 use winapi::um::winuser::{LB_ERR, LB_GETSELCOUNT, LB_GETSELITEMS};
181
182 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
183 let select_count = match wh::send_message(handle, LB_GETSELCOUNT, 0, 0) {
184 LB_ERR => usize::max_value(),
185 value => value as usize,
186 };
187
188 if select_count == usize::max_value() || usize::max_value() == 0 {
189 return Vec::new();
190 }
191
192 let mut indices_buffer: Vec<u32> = Vec::with_capacity(select_count);
193 unsafe { indices_buffer.set_len(select_count) };
194
195 wh::send_message(
196 handle,
197 LB_GETSELITEMS,
198 select_count as WPARAM,
199 indices_buffer.as_mut_ptr() as LPARAM,
200 );
201
202 indices_buffer.into_iter().map(|i| i as usize).collect()
203 }
204
205 pub fn selection_string(&self) -> Option<String> {
208 use winapi::shared::ntdef::WCHAR;
209 use winapi::um::winuser::{LB_ERR, LB_GETCURSEL, LB_GETTEXT, LB_GETTEXTLEN};
210
211 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
212 let index = wh::send_message(handle, LB_GETCURSEL, 0, 0);
213
214 if index == LB_ERR {
215 None
216 } else {
217 let index = index as usize;
218 let length = (wh::send_message(handle, LB_GETTEXTLEN, index, 0) as usize) + 1; let mut buffer: Vec<WCHAR> = Vec::with_capacity(length);
220 unsafe {
221 buffer.set_len(length);
222 }
223 wh::send_message(handle, LB_GETTEXT, index, buffer.as_ptr() as LPARAM);
224
225 Some(from_utf16(&buffer))
226 }
227 }
228
229 pub fn set_selection(&self, index: Option<usize>) {
233 use winapi::um::winuser::LB_SETCURSEL;
234
235 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
236 let index = index.unwrap_or(-1isize as usize);
237 wh::send_message(handle, LB_SETCURSEL, index, 0);
238 }
239
240 pub fn multi_add_selection(&self, index: usize) {
242 use winapi::um::winuser::LB_SETSEL;
243 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
244 wh::send_message(handle, LB_SETSEL, 1, index as LPARAM);
245 }
246
247 pub fn multi_remove_selection(&self, index: usize) {
249 use winapi::um::winuser::LB_SETSEL;
250 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
251 wh::send_message(handle, LB_SETSEL, 0, index as LPARAM);
252 }
253
254 pub fn unselect_all(&self) {
256 use winapi::um::winuser::LB_SETSEL;
257 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
258 wh::send_message(handle, LB_SETSEL, 0, -1);
259 }
260
261 pub fn select_all(&self) {
263 use winapi::um::winuser::LB_SETSEL;
264 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
265 wh::send_message(handle, LB_SETSEL, 1, -1);
266 }
267
268 pub fn multi_select_range(&self, range: Range<usize>) {
270 use winapi::um::winuser::LB_SELITEMRANGEEX;
271
272 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
273 let start = range.start as WPARAM;
274 let end = range.end as LPARAM;
275 wh::send_message(handle, LB_SELITEMRANGEEX, start, end);
276 }
277
278 pub fn multi_unselect_range(&self, range: Range<usize>) {
280 use winapi::um::winuser::LB_SELITEMRANGEEX;
281
282 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
283 let start = range.start as LPARAM;
284 let end = range.end as WPARAM;
285 wh::send_message(handle, LB_SELITEMRANGEEX, end, start);
286 }
287
288 pub fn set_selection_string(&self, value: &str) -> Option<usize> {
292 use winapi::um::winuser::{LB_ERR, LB_SELECTSTRING};
293
294 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
295 let os_string = to_utf16(value);
296
297 let index = wh::send_message(handle, LB_SELECTSTRING, 0, os_string.as_ptr() as LPARAM);
298 if index == LB_ERR {
299 None
300 } else {
301 Some(index as usize)
302 }
303 }
304
305 pub fn selected(&self, index: usize) -> bool {
308 use winapi::um::winuser::LB_GETSEL;
309
310 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
311 wh::send_message(handle, LB_GETSEL, index as WPARAM, 0) > 0
312 }
313
314 pub fn sync(&self) {
317 use winapi::um::winuser::{LB_ADDSTRING, LB_INITSTORAGE};
318
319 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
320
321 self.clear_inner(handle);
322
323 let item_count = self.collection.borrow().len();
324 wh::send_message(
325 handle,
326 LB_INITSTORAGE,
327 item_count as WPARAM,
328 (10 * item_count) as LPARAM,
329 );
330
331 for item in self.collection.borrow().iter() {
332 let display = format!("{}", item);
333 let display_os = to_utf16(&display);
334
335 wh::send_message(handle, LB_ADDSTRING, 0, display_os.as_ptr() as LPARAM);
336 }
337 }
338
339 pub fn set_collection(&self, mut col: Vec<D>) -> Vec<D> {
341 use winapi::um::winuser::LB_ADDSTRING;
342
343 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
344
345 self.clear_inner(handle);
346
347 for item in col.iter() {
348 let display = format!("{}", item);
349 let display_os = to_utf16(&display);
350
351 wh::send_message(handle, LB_ADDSTRING, 0, display_os.as_ptr() as LPARAM);
352 }
353
354 let mut col_ref = self.collection.borrow_mut();
355 mem::swap::<Vec<D>>(&mut col_ref, &mut col);
356
357 col
358 }
359
360 pub fn clear(&self) {
362 self.set_collection(Vec::new());
363 }
364
365 pub fn len(&self) -> usize {
367 use winapi::um::winuser::LB_GETCOUNT;
368 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
369 wh::send_message(handle, LB_GETCOUNT, 0, 0) as usize
370 }
371
372 pub fn font(&self) -> Option<Font> {
378 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
379
380 let font_handle = wh::get_window_font(handle);
381 if font_handle.is_null() {
382 None
383 } else {
384 Some(Font {
385 handle: font_handle,
386 })
387 }
388 }
389
390 pub fn set_font(&self, font: Option<&Font>) {
392 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
393
394 wh::set_window_font(handle, font.map(|f| f.handle), true);
395 }
396
397 pub fn focus(&self) -> bool {
399 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
400 wh::get_focus(handle)
401 }
402
403 pub fn set_focus(&self) {
405 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
406
407 wh::set_focus(handle);
408 }
409
410 pub fn enabled(&self) -> bool {
412 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
413 wh::get_window_enabled(handle)
414 }
415
416 pub fn set_enabled(&self, v: bool) {
418 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
419 wh::set_window_enabled(handle, v)
420 }
421
422 pub fn visible(&self) -> bool {
425 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
426 wh::get_window_visibility(handle)
427 }
428
429 pub fn set_visible(&self, v: bool) {
431 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
432 wh::set_window_visibility(handle, v)
433 }
434
435 pub fn size(&self) -> (u32, u32) {
437 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
438 wh::get_window_size(handle)
439 }
440
441 pub fn set_size(&self, x: u32, y: u32) {
443 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
444 wh::set_window_size(handle, x, y, false)
445 }
446
447 pub fn position(&self) -> (i32, i32) {
449 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
450 wh::get_window_position(handle)
451 }
452
453 pub fn set_position(&self, x: i32, y: i32) {
455 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
456 wh::set_window_position(handle, x, y)
457 }
458
459 pub fn collection(&self) -> Ref<'_, Vec<D>> {
463 self.collection.borrow()
464 }
465
466 pub fn collection_mut(&self) -> RefMut<'_, Vec<D>> {
470 self.collection.borrow_mut()
471 }
472
473 pub fn class_name(&self) -> &'static str {
475 "ListBox"
476 }
477
478 pub fn flags(&self) -> u32 {
480 WS_VISIBLE | WS_TABSTOP
481 }
482
483 pub fn forced_flags(&self) -> u32 {
485 use winapi::um::winuser::{LBS_HASSTRINGS, LBS_NOTIFY, WS_BORDER, WS_CHILD, WS_VSCROLL};
486
487 LBS_HASSTRINGS | LBS_NOTIFY | WS_BORDER | WS_CHILD | WS_VSCROLL
488 }
489
490 fn clear_inner(&self, handle: HWND) {
492 use winapi::um::winuser::LB_RESETCONTENT;
493 wh::send_message(handle, LB_RESETCONTENT, 0, 0);
494 }
495}
496
497impl<D: Display + Default> Drop for ListBox<D> {
498 fn drop(&mut self) {
499 self.handle.destroy();
500 }
501}
502
503pub struct ListBoxBuilder<'a, D: Display + Default> {
504 size: (i32, i32),
505 position: (i32, i32),
506 enabled: bool,
507 focus: bool,
508 flags: Option<ListBoxFlags>,
509 ex_flags: u32,
510 font: Option<&'a Font>,
511 collection: Option<Vec<D>>,
512 selected_index: Option<usize>,
513 multi_selection: Vec<usize>,
514 parent: Option<ControlHandle>,
515}
516
517impl<'a, D: Display + Default> ListBoxBuilder<'a, D> {
518 pub fn flags(mut self, flags: ListBoxFlags) -> ListBoxBuilder<'a, D> {
519 self.flags = Some(flags);
520 self
521 }
522
523 pub fn ex_flags(mut self, flags: u32) -> ListBoxBuilder<'a, D> {
524 self.ex_flags = flags;
525 self
526 }
527
528 pub fn size(mut self, size: (i32, i32)) -> ListBoxBuilder<'a, D> {
529 self.size = size;
530 self
531 }
532
533 pub fn position(mut self, pos: (i32, i32)) -> ListBoxBuilder<'a, D> {
534 self.position = pos;
535 self
536 }
537
538 pub fn font(mut self, font: Option<&'a Font>) -> ListBoxBuilder<'a, D> {
539 self.font = font;
540 self
541 }
542
543 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ListBoxBuilder<'a, D> {
544 self.parent = Some(p.into());
545 self
546 }
547
548 pub fn collection(mut self, collection: Vec<D>) -> ListBoxBuilder<'a, D> {
549 self.collection = Some(collection);
550 self
551 }
552
553 pub fn selected_index(mut self, index: Option<usize>) -> ListBoxBuilder<'a, D> {
554 self.selected_index = index;
555 self
556 }
557
558 pub fn multi_selection(mut self, select: Vec<usize>) -> ListBoxBuilder<'a, D> {
559 self.multi_selection = select;
560 self
561 }
562
563 pub fn enabled(mut self, enabled: bool) -> ListBoxBuilder<'a, D> {
564 self.enabled = enabled;
565 self
566 }
567
568 pub fn focus(mut self, focus: bool) -> ListBoxBuilder<'a, D> {
569 self.focus = focus;
570 self
571 }
572
573 pub fn build(self, out: &mut ListBox<D>) -> Result<(), NwgError> {
574 let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
575
576 let parent = match self.parent {
577 Some(p) => Ok(p),
578 None => Err(NwgError::no_parent("ListBox")),
579 }?;
580
581 *out = Default::default();
582
583 out.handle = ControlBase::build_hwnd()
584 .class_name(out.class_name())
585 .forced_flags(out.forced_flags())
586 .flags(flags)
587 .ex_flags(self.ex_flags)
588 .size(self.size)
589 .position(self.position)
590 .parent(Some(parent))
591 .build()?;
592
593 if self.font.is_some() {
594 out.set_font(self.font);
595 } else {
596 out.set_font(Font::global_default().as_ref());
597 }
598
599 if let Some(col) = self.collection {
600 out.set_collection(col);
601 }
602
603 if flags & LBS_MULTIPLESEL == LBS_MULTIPLESEL {
604 for i in self.multi_selection {
605 out.multi_add_selection(i);
606 }
607 } else {
608 out.set_selection(self.selected_index);
609 }
610
611 if self.focus {
612 out.set_focus();
613 }
614
615 if !self.enabled {
616 out.set_enabled(self.enabled);
617 }
618
619 Ok(())
620 }
621}
622
623impl<D: Display + Default> PartialEq for ListBox<D> {
624 fn eq(&self, other: &Self) -> bool {
625 self.handle == other.handle
626 }
627}