1use super::{ControlBase, ControlHandle};
2use crate::win32::base_helper::{check_hwnd, from_utf16, to_utf16};
3use crate::win32::window_helper as wh;
4use crate::{NwgError, RawEventHandler, unbind_raw_event_handler};
5use std::{cell::RefCell, mem, ptr, rc::Rc};
6use winapi::shared::windef::{HBITMAP, HBRUSH};
7use winapi::um::commctrl::{
8 HDF_SORTDOWN, HDF_SORTUP, HDI_FORMAT, HDITEMW, HDM_GETITEMW, HDM_SETITEMW, LVCF_FMT, LVCF_TEXT,
9 LVCF_WIDTH, LVCFMT_BITMAP_ON_RIGHT, LVCFMT_CENTER, LVCFMT_COL_HAS_IMAGES, LVCFMT_IMAGE,
10 LVCFMT_JUSTIFYMASK, LVCFMT_LEFT, LVCFMT_RIGHT, LVCOLUMNW, LVIF_IMAGE, LVIF_TEXT, LVITEMW,
11 LVM_GETHEADER, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_AUTOSIZECOLUMNS, LVS_EX_BORDERSELECT,
12 LVS_EX_FULLROWSELECT, LVS_EX_GRIDLINES, LVS_EX_HEADERDRAGDROP, LVS_EX_HEADERINALLVIEWS,
13 LVS_ICON, LVS_LIST, LVS_NOCOLUMNHEADER, LVS_REPORT, LVS_SHOWSELALWAYS, LVS_SINGLESEL,
14 LVS_SMALLICON,
15};
16use winapi::um::winuser::{WS_DISABLED, WS_TABSTOP, WS_VISIBLE};
17
18#[cfg(feature = "image-list")]
19use crate::ImageList;
20
21const NOT_BOUND: &'static str = "ListView is not yet bound to a winapi object";
22const BAD_HANDLE: &'static str = "INTERNAL ERROR: ListView handle is not HWND!";
23
24bitflags! {
25 pub struct ListViewFlags: u32 {
36 const VISIBLE = WS_VISIBLE;
37 const DISABLED = WS_DISABLED;
38 const TAB_STOP = WS_TABSTOP;
39
40 const SINGLE_SELECTION = LVS_SINGLESEL;
41
42 const ALWAYS_SHOW_SELECTION = LVS_SHOWSELALWAYS;
43
44 const NO_HEADER = LVS_NOCOLUMNHEADER;
47 }
48}
49
50bitflags! {
51 pub struct ListViewExFlags: u32 {
63 const NONE = 0;
64 const GRID = LVS_EX_GRIDLINES;
65 const BORDER_SELECT = LVS_EX_BORDERSELECT;
66 const AUTO_COLUMN_SIZE = LVS_EX_AUTOSIZECOLUMNS;
67 const FULL_ROW_SELECT = LVS_EX_FULLROWSELECT;
68 const HEADER_DRAG_DROP = LVS_EX_HEADERDRAGDROP;
69 const HEADER_IN_ALL_VIEW = LVS_EX_HEADERINALLVIEWS;
70
71 }
72}
73
74bitflags! {
75 pub struct ListViewColumnFlags: u32 {
88 const LEFT = LVCFMT_LEFT as u32;
89 const RIGHT = LVCFMT_RIGHT as u32;
90 const CENTER = LVCFMT_CENTER as u32;
91 const JUSTIFY_MASK = LVCFMT_JUSTIFYMASK as u32;
92 const IMAGE = LVCFMT_IMAGE as u32;
93 const IMAGE_RIGHT = LVCFMT_BITMAP_ON_RIGHT as u32;
94 const IMAGE_COL = LVCFMT_COL_HAS_IMAGES as u32;
95 }
96}
97
98#[derive(Copy, Clone, Debug)]
102#[repr(u8)]
103pub enum ListViewStyle {
104 Simple,
105 Detailed,
106 Icon,
107 SmallIcon,
108}
109
110impl ListViewStyle {
111 fn from_bits(bits: u32) -> ListViewStyle {
112 let bits = bits & 0b11;
113 match bits {
114 LVS_ICON => ListViewStyle::Icon,
115 LVS_REPORT => ListViewStyle::Detailed,
116 LVS_SMALLICON => ListViewStyle::SmallIcon,
117 LVS_LIST => ListViewStyle::Simple,
118 _ => unreachable!(),
119 }
120 }
121
122 fn bits(&self) -> u32 {
123 match self {
124 ListViewStyle::Simple => LVS_LIST,
125 ListViewStyle::Detailed => LVS_REPORT,
126 ListViewStyle::Icon => LVS_ICON,
127 ListViewStyle::SmallIcon => LVS_SMALLICON,
128 }
129 }
130}
131
132#[cfg(feature = "image-list")]
137#[derive(Copy, Clone, Debug)]
138pub enum ListViewImageListType {
139 Normal,
141
142 Small,
144
145 State,
147
148 GroupHeader,
150}
151
152#[cfg(feature = "image-list")]
153impl ListViewImageListType {
154 fn to_raw(&self) -> i32 {
155 use winapi::um::commctrl::{LVSIL_GROUPHEADER, LVSIL_NORMAL, LVSIL_SMALL, LVSIL_STATE};
156
157 match self {
158 Self::Normal => LVSIL_NORMAL,
159 Self::Small => LVSIL_SMALL,
160 Self::State => LVSIL_STATE,
161 Self::GroupHeader => LVSIL_GROUPHEADER,
162 }
163 }
164}
165
166#[derive(Default, Clone, Debug)]
167pub struct InsertListViewColumn {
169 pub index: Option<i32>,
171
172 pub fmt: Option<ListViewColumnFlags>,
174
175 pub width: Option<i32>,
177
178 pub text: Option<String>,
180}
181
182#[derive(Default, Clone, Debug)]
184pub struct ListViewColumn {
185 pub fmt: i32,
186 pub width: i32,
187 pub text: String,
188}
189
190#[derive(Copy, Clone, Debug)]
192pub enum ListViewColumnSortArrow {
193 Up,
194 Down,
195}
196
197#[derive(Default, Clone, Debug)]
199pub struct InsertListViewItem {
200 pub index: Option<i32>,
203
204 pub column_index: i32,
206
207 pub text: Option<String>,
209
210 #[cfg(feature = "image-list")]
213 pub image: Option<i32>,
214}
215
216#[derive(Default, Clone, Debug)]
218pub struct ListViewItem {
219 pub row_index: i32,
220 pub column_index: i32,
221 pub text: String,
222
223 pub selected: bool,
225
226 #[cfg(feature = "image-list")]
227 pub image: i32,
228}
229
230struct ListViewDoubleBuffer {
231 buffer: HBITMAP,
232 size: [i32; 2],
233 bg: HBRUSH,
234}
235
236#[derive(Default)]
277pub struct ListView {
278 pub handle: ControlHandle,
279 double_buffer: Option<Rc<RefCell<ListViewDoubleBuffer>>>,
280 handler0: Option<RawEventHandler>,
281}
282
283impl ListView {
284 pub fn builder() -> ListViewBuilder {
285 ListViewBuilder {
286 size: (300, 300),
287 position: (0, 0),
288 background_color: None,
289 double_buffer: true,
290 text_color: None,
291 focus: false,
292 flags: None,
293 ex_flags: None,
294 ex_window_flags: 0,
295 style: ListViewStyle::Simple,
296 parent: None,
297 item_count: 0,
298 }
299 }
300
301 #[cfg(feature = "image-list")]
304 pub fn set_image_list(&self, list: Option<&ImageList>, list_type: ListViewImageListType) {
305 use winapi::um::commctrl::LVM_SETIMAGELIST;
306
307 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
308
309 let list_handle = list.map(|l| l.handle).unwrap_or(ptr::null_mut());
310 wh::send_message(
311 handle,
312 LVM_SETIMAGELIST,
313 list_type.to_raw() as _,
314 list_handle as _,
315 );
316
317 self.invalidate();
318 }
319
320 #[cfg(feature = "image-list")]
323 pub fn image_list(&self, list_type: ListViewImageListType) -> Option<ImageList> {
324 use winapi::um::commctrl::LVM_GETIMAGELIST;
325
326 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
327
328 match wh::send_message(handle, LVM_GETIMAGELIST, list_type.to_raw() as _, 0) {
329 0 => None,
330 handle => Some(ImageList {
331 handle: handle as _,
332 owned: false,
333 }),
334 }
335 }
336
337 pub fn set_text_color(&self, r: u8, g: u8, b: u8) {
339 use winapi::um::commctrl::LVM_SETTEXTCOLOR;
340 use winapi::um::wingdi::RGB;
341
342 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
343
344 let color = RGB(r, g, b);
345
346 wh::send_message(handle, LVM_SETTEXTCOLOR, 0, color as _);
347
348 self.invalidate();
349 }
350
351 pub fn text_color(&self) -> [u8; 3] {
353 use winapi::um::commctrl::LVM_GETTEXTCOLOR;
354 use winapi::um::wingdi::{GetBValue, GetGValue, GetRValue};
355
356 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
357 let col = wh::send_message(handle, LVM_GETTEXTCOLOR, 0, 0) as u32;
358
359 [GetRValue(col), GetGValue(col), GetBValue(col)]
360 }
361
362 pub fn set_background_color(&self, r: u8, g: u8, b: u8) {
364 use winapi::um::commctrl::LVM_SETBKCOLOR;
365 use winapi::um::wingdi::RGB;
366
367 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
368
369 let color = RGB(r, g, b);
370
371 wh::send_message(handle, LVM_SETBKCOLOR, 0, color as _);
372
373 self.invalidate();
374 }
375
376 pub fn background_color(&self) -> [u8; 3] {
378 use winapi::um::commctrl::LVM_GETBKCOLOR;
379 use winapi::um::wingdi::{GetBValue, GetGValue, GetRValue};
380
381 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
382 let col = wh::send_message(handle, LVM_GETBKCOLOR, 0, 0) as u32;
383
384 [GetRValue(col), GetGValue(col), GetBValue(col)]
385 }
386
387 pub fn selected_column(&self) -> usize {
389 use winapi::um::commctrl::LVM_GETSELECTEDCOLUMN;
390
391 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
392 wh::send_message(handle, LVM_GETSELECTEDCOLUMN, 0, 0) as usize
393 }
394
395 pub fn set_selected_column(&self, index: usize) {
397 use winapi::um::commctrl::LVM_SETSELECTEDCOLUMN;
398
399 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
400 wh::send_message(handle, LVM_SETSELECTEDCOLUMN, index as _, 0);
401 }
402
403 pub fn selected_count(&self) -> usize {
405 use winapi::um::commctrl::LVM_GETSELECTEDCOUNT;
406
407 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
408 wh::send_message(handle, LVM_GETSELECTEDCOUNT, 0, 0) as usize
409 }
410
411 pub fn insert_column<I: Into<InsertListViewColumn>>(&self, insert: I) {
413 use winapi::um::commctrl::LVM_INSERTCOLUMNW;
414
415 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
416
417 match self.list_style() {
418 ListViewStyle::Detailed => {}
419 _ => {
420 return;
421 }
422 }
423
424 let insert = insert.into();
425
426 let mut mask = LVCF_TEXT | LVCF_WIDTH;
427
428 let text = insert.text.unwrap_or("".to_string());
429 let mut text = to_utf16(&text);
430
431 let col_width = insert.width.unwrap_or(100) as f64 * crate::win32::high_dpi::scale_factor();
432
433 if insert.fmt.is_some() {
434 mask |= LVCF_FMT;
435 }
436
437 let mut item: LVCOLUMNW = unsafe { mem::zeroed() };
438 item.mask = mask;
439 item.fmt = insert.fmt.unwrap_or(ListViewColumnFlags::empty()).bits() as _;
440 item.cx = col_width as i32;
441 item.pszText = text.as_mut_ptr();
442 item.cchTextMax = text.len() as i32;
443
444 let col_count = self.column_len() as i32;
445
446 wh::send_message(
447 handle,
448 LVM_INSERTCOLUMNW,
449 insert.index.unwrap_or(col_count) as usize,
450 (&item as *const LVCOLUMNW) as _,
451 );
452 }
453
454 pub fn has_column(&self, index: usize) -> bool {
456 use winapi::um::commctrl::LVM_GETCOLUMNW;
457
458 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
459
460 let mut col: LVCOLUMNW = unsafe { mem::zeroed() };
461
462 wh::send_message(
463 handle,
464 LVM_GETCOLUMNW,
465 index as _,
466 &mut col as *mut LVCOLUMNW as _,
467 ) != 0
468 }
469
470 pub fn column(&self, index: usize, text_buffer_size: i32) -> Option<ListViewColumn> {
473 use winapi::um::commctrl::LVM_GETCOLUMNW;
474
475 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
476
477 let mut text_buffer: Vec<u16> = Vec::with_capacity(text_buffer_size as _);
478 unsafe {
479 text_buffer.set_len(text_buffer_size as _);
480 }
481
482 let mut col: LVCOLUMNW = unsafe { mem::zeroed() };
483 col.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT;
484 col.pszText = text_buffer.as_mut_ptr();
485 col.cchTextMax = text_buffer_size;
486
487 match wh::send_message(
488 handle,
489 LVM_GETCOLUMNW,
490 index as _,
491 &mut col as *mut LVCOLUMNW as _,
492 ) == 0
493 {
494 true => None,
495 false => Some(ListViewColumn {
496 fmt: col.fmt,
497 width: col.cx,
498 text: from_utf16(&text_buffer),
499 }),
500 }
501 }
502
503 pub fn update_column<I: Into<InsertListViewColumn>>(&self, index: usize, column: I) {
505 use winapi::um::commctrl::LVM_SETCOLUMNW;
506
507 if !self.has_column(index) {
508 return;
509 }
510
511 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
512 let insert = column.into();
513
514 let use_text = insert.text.is_some();
515 let use_width = insert.width.is_some();
516 let use_fmt = insert.fmt.is_some();
517
518 let mut mask = 0;
519 if use_text {
520 mask |= LVCF_TEXT;
521 }
522 if use_width {
523 mask |= LVCF_WIDTH;
524 }
525 if use_fmt {
526 mask |= LVCF_FMT;
527 }
528
529 let text = insert.text.unwrap_or("".to_string());
530 let mut text = to_utf16(&text);
531
532 let mut item: LVCOLUMNW = unsafe { mem::zeroed() };
533 item.mask = mask;
534 item.fmt = insert.fmt.unwrap_or(ListViewColumnFlags::empty()).bits() as _;
535 item.cx = insert.width.unwrap_or(0);
536
537 if use_text {
538 item.pszText = text.as_mut_ptr();
539 item.cchTextMax = text.len() as i32;
540 }
541
542 wh::send_message(
543 handle,
544 LVM_SETCOLUMNW,
545 index as _,
546 &mut item as *mut LVCOLUMNW as _,
547 );
548 }
549
550 pub fn remove_column(&self, column_index: usize) {
552 use winapi::um::commctrl::LVM_DELETECOLUMN;
553
554 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
555 wh::send_message(handle, LVM_DELETECOLUMN, column_index as _, 0);
556 }
557
558 pub fn headers_enabled(&self) -> bool {
560 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
561 let style = wh::get_style(handle);
562 (style & LVS_REPORT == LVS_REPORT) && (style & LVS_NOCOLUMNHEADER != LVS_NOCOLUMNHEADER)
563 }
564
565 pub fn set_headers_enabled(&self, enable: bool) {
567 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
568 let style = wh::get_style(handle);
569 if style & LVS_REPORT == LVS_REPORT {
570 if !enable {
571 wh::set_style(handle, style | LVS_NOCOLUMNHEADER);
572 } else {
573 wh::set_style(handle, style & !LVS_NOCOLUMNHEADER);
574 }
575 }
576 }
577
578 pub fn column_sort_arrow(&self, column_index: usize) -> Option<ListViewColumnSortArrow> {
580 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
581
582 let headers = wh::send_message(handle, LVM_GETHEADER, 0, 0);
583 if headers == 0 {
584 return None;
585 }
586
587 let mut header: HDITEMW = unsafe { mem::zeroed() };
588 header.mask = HDI_FORMAT;
589
590 let l = &mut header as *mut HDITEMW as _;
591 wh::send_message(headers as *mut _, HDM_GETITEMW, column_index, l);
592
593 match header.fmt & (HDF_SORTUP | HDF_SORTDOWN) {
594 HDF_SORTUP => Some(ListViewColumnSortArrow::Up),
595 HDF_SORTDOWN => Some(ListViewColumnSortArrow::Down),
596 _ => None,
597 }
598 }
599
600 pub fn set_column_sort_arrow(
602 &self,
603 column_index: usize,
604 sort: Option<ListViewColumnSortArrow>,
605 ) {
606 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
607
608 let headers = wh::send_message(handle, LVM_GETHEADER, 0, 0);
609 if headers != 0 {
610 let mut header: HDITEMW = unsafe { mem::zeroed() };
611 header.mask = HDI_FORMAT;
612
613 let l = &mut header as *mut HDITEMW as _;
614 wh::send_message(headers as *mut _, HDM_GETITEMW, column_index, l);
615
616 header.fmt &= !(HDF_SORTUP | HDF_SORTDOWN);
617 match sort {
618 Some(ListViewColumnSortArrow::Up) => header.fmt |= HDF_SORTUP,
619 Some(ListViewColumnSortArrow::Down) => header.fmt |= HDF_SORTDOWN,
620 _ => {}
621 };
622
623 let l = &mut header as *mut HDITEMW as _;
624 wh::send_message(headers as *mut _, HDM_SETITEMW, column_index, l);
625 }
626 }
627
628 pub fn set_column_width(&self, column_index: usize, width: isize) {
630 use winapi::um::commctrl::LVM_SETCOLUMNWIDTH;
631
632 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
633 wh::send_message(handle, LVM_SETCOLUMNWIDTH, column_index as _, width);
634 }
635
636 pub fn column_width(&self) -> usize {
638 use winapi::um::commctrl::LVM_GETCOLUMNWIDTH;
639
640 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
641 wh::send_message(handle, LVM_GETCOLUMNWIDTH, 0, 0) as usize
642 }
643
644 pub fn select_item(&self, row_index: usize, selected: bool) {
646 use winapi::um::commctrl::{LVIF_STATE, LVIS_SELECTED, LVM_SETITEMW};
647
648 if !self.has_item(row_index, 0) {
649 return;
650 }
651
652 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
653
654 let mut item: LVITEMW = unsafe { mem::zeroed() };
655 item.iItem = row_index as _;
656 item.mask = LVIF_STATE;
657 item.state = match selected {
658 true => LVIS_SELECTED,
659 false => 0,
660 };
661 item.stateMask = LVIS_SELECTED;
662
663 wh::send_message(handle, LVM_SETITEMW, 0, &mut item as *mut LVITEMW as _);
664 }
665
666 pub fn selected_item(&self) -> Option<usize> {
669 use winapi::um::commctrl::{LVITEMINDEX, LVM_GETNEXTITEMINDEX, LVNI_SELECTED};
670
671 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
672 let mut index = None;
673
674 let mut i_data = LVITEMINDEX {
675 iItem: -1,
676 iGroup: -1,
677 };
678
679 if wh::send_message(
680 handle,
681 LVM_GETNEXTITEMINDEX,
682 &mut i_data as *mut LVITEMINDEX as _,
683 LVNI_SELECTED,
684 ) != 0
685 {
686 index = Some(i_data.iItem as usize);
687 }
688
689 index
690 }
691
692 pub fn selected_items(&self) -> Vec<usize> {
694 use winapi::um::commctrl::{LVITEMINDEX, LVM_GETNEXTITEMINDEX, LVNI_SELECTED};
695
696 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
697 let mut indices = Vec::with_capacity(self.len());
698
699 let mut i_data = LVITEMINDEX {
700 iItem: -1,
701 iGroup: -1,
702 };
703
704 while wh::send_message(
705 handle,
706 LVM_GETNEXTITEMINDEX,
707 &mut i_data as *mut LVITEMINDEX as _,
708 LVNI_SELECTED,
709 ) != 0
710 {
711 indices.push(i_data.iItem as usize);
712 }
713
714 indices
715 }
716
717 pub fn insert_item<I: Into<InsertListViewItem>>(&self, insert: I) {
719 use winapi::um::commctrl::{LVM_INSERTITEMW, LVM_SETITEMW};
720
721 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
722 let insert = insert.into();
723
724 let row_insert = insert.index.unwrap_or(i32::max_value());
725 let column_insert = insert.column_index;
726 if column_insert > 0 && !self.has_item(row_insert as _, 0) {
727 self.insert_item(InsertListViewItem {
728 index: Some(row_insert),
729 column_index: 0,
730 text: None,
731
732 #[cfg(feature = "image-list")]
733 image: None,
734 });
735 }
736
737 let mask = LVIF_TEXT | check_image_mask(&insert);
738 let image = check_image(&insert);
739 let text = insert.text.unwrap_or("".to_string());
740 let mut text = to_utf16(&text);
741
742 let mut item: LVITEMW = unsafe { mem::zeroed() };
743 item.mask = mask;
744 item.iItem = row_insert;
745 item.iImage = image;
746 item.iSubItem = column_insert;
747 item.pszText = text.as_mut_ptr();
748 item.cchTextMax = text.len() as i32;
749
750 if column_insert == 0 {
751 wh::send_message(handle, LVM_INSERTITEMW, 0, &mut item as *mut LVITEMW as _);
752 } else {
753 wh::send_message(handle, LVM_SETITEMW, 0, &mut item as *mut LVITEMW as _);
754 }
755 }
756
757 pub fn item_is_visible(&self, index: usize) -> bool {
759 use winapi::um::commctrl::LVM_ISITEMVISIBLE;
760
761 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
762 wh::send_message(handle, LVM_ISITEMVISIBLE, index as _, 0) == 1
763 }
764
765 pub fn has_item(&self, row_index: usize, column_index: usize) -> bool {
767 use winapi::um::commctrl::LVM_GETITEMW;
768
769 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
770
771 let mut item: LVITEMW = unsafe { mem::zeroed() };
772 item.iItem = row_index as _;
773 item.iSubItem = column_index as _;
774
775 wh::send_message(handle, LVM_GETITEMW, 0, &mut item as *mut LVITEMW as _) == 1
776 }
777
778 pub fn item(
781 &self,
782 row_index: usize,
783 column_index: usize,
784 text_buffer_size: usize,
785 ) -> Option<ListViewItem> {
786 use winapi::um::commctrl::{LVIF_STATE, LVIS_SELECTED, LVM_GETITEMW};
787
788 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
789
790 let mut item: LVITEMW = unsafe { mem::zeroed() };
791 item.iItem = row_index as _;
792 item.iSubItem = column_index as _;
793 item.mask = LVIF_IMAGE | LVIF_TEXT | LVIF_STATE;
794 item.stateMask = LVIS_SELECTED;
795
796 let mut text_buffer: Vec<u16> = Vec::with_capacity(text_buffer_size);
797 unsafe {
798 text_buffer.set_len(text_buffer_size);
799 }
800 item.pszText = text_buffer.as_mut_ptr();
801 item.cchTextMax = text_buffer_size as _;
802
803 let found = wh::send_message(handle, LVM_GETITEMW, 0, &mut item as *mut LVITEMW as _) == 1;
804 if !found {
805 return None;
806 }
807
808 Some(build_list_view_image(
809 row_index,
810 column_index,
811 item.state,
812 &text_buffer,
813 item.iImage,
814 ))
815 }
816
817 pub fn update_item<I: Into<InsertListViewItem>>(&self, row_index: usize, data: I) {
820 use winapi::um::commctrl::LVM_SETITEMW;
821
822 if !self.has_item(row_index, 0) {
823 return;
824 }
825
826 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
827 let insert = data.into();
828
829 let mut mask = check_image_mask(&insert);
830 if insert.text.is_some() {
831 mask |= LVIF_TEXT;
832 }
833
834 let image = check_image(&insert);
835
836 let use_text = insert.text.is_some();
837 let text = insert.text.unwrap_or("".to_string());
838 let mut text = to_utf16(&text);
839
840 let mut item: LVITEMW = unsafe { mem::zeroed() };
841 item.mask = mask;
842 item.iItem = row_index as _;
843 item.iImage = image;
844 item.iSubItem = insert.column_index as _;
845
846 if use_text {
847 item.pszText = text.as_mut_ptr();
848 item.cchTextMax = text.len() as i32;
849 }
850
851 wh::send_message(handle, LVM_SETITEMW, 0, &mut item as *mut LVITEMW as _);
852 }
853
854 pub fn remove_item(&self, row_index: usize) -> bool {
857 use winapi::um::commctrl::LVM_DELETEITEM;
858
859 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
860 wh::send_message(handle, LVM_DELETEITEM, row_index as _, 0) == 1
861 }
862
863 pub fn insert_items<I: Clone + Into<InsertListViewItem>>(&self, insert: &[I]) {
865 for i in insert.iter() {
866 self.insert_item(i.clone());
867 }
868 }
869
870 pub fn insert_items_row<I: Clone + Into<InsertListViewItem>>(
874 &self,
875 row_index: Option<i32>,
876 insert: &[I],
877 ) {
878 let mut column_index = 0;
879 let row_index = row_index.or(Some(self.len() as _));
880
881 for i in insert.iter() {
882 let mut item: InsertListViewItem = i.clone().into();
883 item.index = row_index;
884 item.column_index = column_index;
885
886 self.insert_item(item);
887
888 column_index += 1;
889 }
890 }
891
892 pub fn list_style(&self) -> ListViewStyle {
894 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
895 ListViewStyle::from_bits(wh::get_style(handle))
896 }
897
898 pub fn set_list_style(&self, style: ListViewStyle) {
900 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
901
902 let mut old_style = wh::get_style(handle);
903 old_style = old_style & !0b11;
904
905 wh::set_style(handle, old_style | style.bits());
906 }
907
908 pub fn len(&self) -> usize {
910 use winapi::um::commctrl::LVM_GETITEMCOUNT;
911 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
912 wh::send_message(handle, LVM_GETITEMCOUNT, 0, 0) as usize
913 }
914
915 pub fn column_len(&self) -> usize {
917 use winapi::um::commctrl::LVM_GETCOLUMNWIDTH;
918
919 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
920
921 let mut count = 0;
922 while wh::send_message(handle, LVM_GETCOLUMNWIDTH, count, 0) != 0 {
923 count += 1;
924 }
925
926 count
927 }
928
929 pub fn set_item_count(&self, n: u32) {
932 use winapi::um::commctrl::LVM_SETITEMCOUNT;
933
934 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
935 wh::send_message(handle, LVM_SETITEMCOUNT, n as _, 0);
936 }
937
938 pub fn set_redraw(&self, enabled: bool) {
941 use winapi::um::winuser::WM_SETREDRAW;
942
943 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
944 wh::send_message(handle, WM_SETREDRAW, enabled as _, 0);
945 }
946
947 pub fn set_icon_spacing(&self, dx: u16, dy: u16) {
951 use winapi::shared::minwindef::MAKELONG;
952 use winapi::um::commctrl::LVM_SETICONSPACING;
953
954 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
955 let spacing = MAKELONG(dx, dy);
956 wh::send_message(handle, LVM_SETICONSPACING, 0 as _, spacing as _);
957
958 self.invalidate();
959 }
960
961 pub fn invalidate(&self) {
965 use winapi::um::winuser::InvalidateRect;
966 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
967 unsafe {
968 InvalidateRect(handle, ptr::null(), 1);
969 }
970 }
971
972 pub fn clear(&self) {
974 use winapi::um::commctrl::LVM_DELETEALLITEMS;
975
976 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
977 wh::send_message(handle, LVM_DELETEALLITEMS, 0, 0);
978 }
979
980 pub fn focus(&self) -> bool {
982 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
983 wh::get_focus(handle)
984 }
985
986 pub fn set_focus(&self) {
988 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
989
990 wh::set_focus(handle);
991 }
992
993 pub fn enabled(&self) -> bool {
995 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
996 wh::get_window_enabled(handle)
997 }
998
999 pub fn set_enabled(&self, v: bool) {
1001 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1002 wh::set_window_enabled(handle, v)
1003 }
1004
1005 pub fn visible(&self) -> bool {
1008 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1009 wh::get_window_visibility(handle)
1010 }
1011
1012 pub fn set_visible(&self, v: bool) {
1014 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1015 wh::set_window_visibility(handle, v)
1016 }
1017
1018 pub fn size(&self) -> (u32, u32) {
1020 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1021 wh::get_window_size(handle)
1022 }
1023
1024 pub fn set_size(&self, x: u32, y: u32) {
1026 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1027 wh::set_window_size(handle, x, y, true)
1028 }
1029
1030 pub fn position(&self) -> (i32, i32) {
1032 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1033 wh::get_window_position(handle)
1034 }
1035
1036 pub fn set_position(&self, x: i32, y: i32) {
1038 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
1039 wh::set_window_position(handle, x, y)
1040 }
1041
1042 pub fn class_name(&self) -> &'static str {
1044 ::winapi::um::commctrl::WC_LISTVIEW
1045 }
1046
1047 pub fn flags(&self) -> u32 {
1049 WS_VISIBLE | WS_TABSTOP | LVS_SHOWSELALWAYS
1050 }
1051
1052 pub fn forced_flags(&self) -> u32 {
1054 use winapi::um::winuser::{WS_BORDER, WS_CHILD};
1055
1056 WS_CHILD | WS_BORDER | LVS_NOCOLUMNHEADER
1057 }
1058
1059 fn set_double_buffered(&mut self) {
1060 use crate::bind_raw_event_handler_inner;
1061 use winapi::um::wingdi::{CreateSolidBrush, RGB};
1062
1063 let double_buffer = ListViewDoubleBuffer {
1064 buffer: ptr::null_mut(),
1065 size: [0, 0],
1066 bg: unsafe { CreateSolidBrush(RGB(255, 255, 255)) },
1067 };
1068
1069 let rc_double_buffer = Rc::new(RefCell::new(double_buffer));
1070 let callback_double_buffer = rc_double_buffer.clone();
1071
1072 let handler = bind_raw_event_handler_inner(&self.handle, 0x020, move |hwnd, msg, _, _| {
1073 use winapi::um::wingdi::{
1074 BitBlt, CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, DeleteObject,
1075 SRCCOPY, SelectObject,
1076 };
1077 use winapi::um::winuser::{
1078 BeginPaint, EndPaint, FillRect, GetClientRect, RDW_ERASENOW, RDW_INVALIDATE,
1079 RDW_UPDATENOW, RedrawWindow, SendMessageW,
1080 };
1081 use winapi::um::winuser::{PAINTSTRUCT, WM_ERASEBKGND, WM_PAINT, WM_PRINTCLIENT};
1082
1083 match msg {
1084 WM_PAINT => unsafe {
1085 let mut double_buffer = callback_double_buffer.borrow_mut();
1086
1087 let mut r = mem::zeroed();
1088 GetClientRect(hwnd, &mut r);
1089 let client_width = r.right - r.left;
1090 let client_height = r.bottom - r.top;
1091
1092 let mut paint: PAINTSTRUCT = mem::zeroed();
1093 BeginPaint(hwnd, &mut paint);
1094
1095 if double_buffer.buffer.is_null()
1096 || double_buffer.size != [client_width, client_height]
1097 {
1098 if !double_buffer.buffer.is_null() {
1099 DeleteObject(double_buffer.buffer as _);
1100 }
1101
1102 double_buffer.size = [client_width, client_height];
1103 double_buffer.buffer =
1104 CreateCompatibleBitmap(paint.hdc, client_width, client_height);
1105 }
1106
1107 let backbuffer = double_buffer.buffer;
1108 let backbuffer_dc = CreateCompatibleDC(paint.hdc);
1109
1110 let old = SelectObject(backbuffer_dc, backbuffer as _);
1112 FillRect(backbuffer_dc, &r, double_buffer.bg as _);
1113
1114 SendMessageW(hwnd, WM_PRINTCLIENT, backbuffer_dc as _, 0);
1116 BitBlt(
1117 paint.hdc as _,
1118 0,
1119 0,
1120 client_width,
1121 client_height,
1122 backbuffer_dc,
1123 0,
1124 0,
1125 SRCCOPY,
1126 );
1127
1128 SelectObject(backbuffer_dc, old);
1130 DeleteDC(backbuffer_dc);
1131 EndPaint(hwnd, &paint);
1132
1133 let header = SendMessageW(hwnd, LVM_GETHEADER, 0, 0);
1135 if header != 0 {
1136 let mut r = mem::zeroed();
1137 GetClientRect(header as _, &mut r);
1138 RedrawWindow(
1139 header as _,
1140 ptr::null_mut(),
1141 ptr::null_mut(),
1142 RDW_ERASENOW | RDW_UPDATENOW | RDW_INVALIDATE,
1143 );
1144 }
1145
1146 Some(1)
1147 },
1148 WM_ERASEBKGND => Some(1),
1149 _ => None,
1150 }
1151 })
1152 .unwrap();
1153
1154 self.handler0 = Some(handler);
1155 self.double_buffer = Some(rc_double_buffer);
1156 }
1157}
1158
1159impl Drop for ListView {
1160 fn drop(&mut self) {
1161 use winapi::um::wingdi::DeleteObject;
1162
1163 if let Some(backbuffer) = self.double_buffer.take() {
1164 let double_buffer = backbuffer.borrow();
1165 unsafe {
1166 DeleteObject(double_buffer.buffer as _);
1167 DeleteObject(double_buffer.bg as _);
1168 }
1169 }
1170
1171 if let Some(h) = self.handler0.as_ref() {
1172 drop(unbind_raw_event_handler(h));
1173 }
1174
1175 self.handle.destroy();
1176 }
1177}
1178
1179pub struct ListViewBuilder {
1180 size: (i32, i32),
1181 position: (i32, i32),
1182 background_color: Option<[u8; 3]>,
1183 text_color: Option<[u8; 3]>,
1184 double_buffer: bool,
1185 focus: bool,
1186 flags: Option<ListViewFlags>,
1187 ex_flags: Option<ListViewExFlags>,
1188 ex_window_flags: u32,
1189 style: ListViewStyle,
1190 item_count: u32,
1191 parent: Option<ControlHandle>,
1192}
1193
1194impl ListViewBuilder {
1195 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ListViewBuilder {
1196 self.parent = Some(p.into());
1197 self
1198 }
1199
1200 pub fn flags(mut self, flags: ListViewFlags) -> ListViewBuilder {
1201 self.flags = Some(flags);
1202 self
1203 }
1204
1205 pub fn ex_flags(mut self, flags: ListViewExFlags) -> ListViewBuilder {
1206 self.ex_flags = Some(flags);
1207 self
1208 }
1209
1210 pub fn ex_window_flags(mut self, flags: u32) -> ListViewBuilder {
1211 self.ex_window_flags = flags;
1212 self
1213 }
1214
1215 pub fn size(mut self, size: (i32, i32)) -> ListViewBuilder {
1216 self.size = size;
1217 self
1218 }
1219
1220 pub fn position(mut self, position: (i32, i32)) -> ListViewBuilder {
1221 self.position = position;
1222 self
1223 }
1224
1225 pub fn double_buffer(mut self, buffer: bool) -> ListViewBuilder {
1226 self.double_buffer = buffer;
1227 self
1228 }
1229
1230 pub fn background_color(mut self, color: [u8; 3]) -> ListViewBuilder {
1231 self.background_color = Some(color);
1232 self
1233 }
1234
1235 pub fn text_color(mut self, color: [u8; 3]) -> ListViewBuilder {
1236 self.text_color = Some(color);
1237 self
1238 }
1239
1240 pub fn item_count(mut self, count: u32) -> ListViewBuilder {
1241 self.item_count = count;
1242 self
1243 }
1244
1245 pub fn list_style(mut self, style: ListViewStyle) -> ListViewBuilder {
1246 self.style = style;
1247 self
1248 }
1249
1250 pub fn focus(mut self, focus: bool) -> ListViewBuilder {
1251 self.focus = focus;
1252 self
1253 }
1254
1255 pub fn build(self, out: &mut ListView) -> Result<(), NwgError> {
1256 let mut flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
1257 flags |= self.style.bits();
1258
1259 let parent = match self.parent {
1260 Some(p) => Ok(p),
1261 None => Err(NwgError::no_parent("ListView")),
1262 }?;
1263
1264 *out = Default::default();
1265
1266 out.handle = ControlBase::build_hwnd()
1267 .class_name(out.class_name())
1268 .forced_flags(out.forced_flags())
1269 .flags(flags)
1270 .ex_flags(self.ex_window_flags)
1271 .size(self.size)
1272 .position(self.position)
1273 .text("")
1274 .parent(Some(parent))
1275 .build()?;
1276
1277 if self.double_buffer {
1278 out.set_double_buffered();
1279 }
1280
1281 if self.item_count > 0 {
1282 out.set_item_count(self.item_count);
1283 }
1284
1285 if self.focus {
1286 out.set_focus();
1287 }
1288
1289 if let Some(flags) = self.ex_flags {
1290 let flags = flags.bits();
1291 wh::send_message(
1292 out.handle.hwnd().unwrap(),
1293 LVM_SETEXTENDEDLISTVIEWSTYLE,
1294 flags as _,
1295 flags as _,
1296 );
1297 }
1298
1299 if let Some([r, g, b]) = self.background_color {
1300 out.set_background_color(r, g, b);
1301 }
1302
1303 if let Some([r, g, b]) = self.text_color {
1304 out.set_text_color(r, g, b);
1305 }
1306
1307 Ok(())
1308 }
1309}
1310
1311impl<'a> From<&'a str> for InsertListViewItem {
1312 fn from(i: &'a str) -> Self {
1313 InsertListViewItem {
1314 index: None,
1315 column_index: 0,
1316 text: Some(i.to_string()),
1317
1318 #[cfg(feature = "image-list")]
1319 image: None,
1320 }
1321 }
1322}
1323
1324impl From<String> for InsertListViewItem {
1325 fn from(i: String) -> Self {
1326 InsertListViewItem {
1327 index: None,
1328 column_index: 0,
1329 text: Some(i),
1330
1331 #[cfg(feature = "image-list")]
1332 image: None,
1333 }
1334 }
1335}
1336
1337impl<'a> From<&'a str> for InsertListViewColumn {
1338 fn from(i: &'a str) -> Self {
1339 InsertListViewColumn {
1340 index: None,
1341 fmt: None,
1342 width: Some(100),
1343 text: Some(i.to_string()),
1344 }
1345 }
1346}
1347
1348impl From<String> for InsertListViewColumn {
1349 fn from(i: String) -> Self {
1350 InsertListViewColumn {
1351 index: None,
1352 fmt: None,
1353 width: Some(100),
1354 text: Some(i),
1355 }
1356 }
1357}
1358
1359#[cfg(feature = "image-list")]
1362fn check_image_mask(i: &InsertListViewItem) -> u32 {
1363 if i.image.is_some() { LVIF_IMAGE } else { 0 }
1364}
1365
1366#[cfg(feature = "image-list")]
1367fn check_image(i: &InsertListViewItem) -> i32 {
1368 i.image.unwrap_or(0)
1369}
1370
1371#[cfg(not(feature = "image-list"))]
1372fn check_image_mask(_i: &InsertListViewItem) -> u32 {
1373 0
1374}
1375
1376#[cfg(not(feature = "image-list"))]
1377fn check_image(_i: &InsertListViewItem) -> i32 {
1378 0
1379}
1380
1381#[cfg(feature = "image-list")]
1382fn build_list_view_image(
1383 row_index: usize,
1384 column_index: usize,
1385 state: u32,
1386 text_buffer: &[u16],
1387 image: i32,
1388) -> ListViewItem {
1389 use winapi::um::commctrl::LVIS_SELECTED;
1390
1391 ListViewItem {
1392 row_index: row_index as _,
1393 column_index: column_index as _,
1394 text: from_utf16(&text_buffer),
1395 selected: state & LVIS_SELECTED == LVIS_SELECTED,
1396 image,
1397 }
1398}
1399
1400#[cfg(not(feature = "image-list"))]
1401fn build_list_view_image(
1402 row_index: usize,
1403 column_index: usize,
1404 state: u32,
1405 text_buffer: &[u16],
1406 _image: i32,
1407) -> ListViewItem {
1408 use winapi::um::commctrl::LVIS_SELECTED;
1409
1410 ListViewItem {
1411 row_index: row_index as _,
1412 column_index: column_index as _,
1413 text: from_utf16(&text_buffer),
1414 selected: state & LVIS_SELECTED == LVIS_SELECTED,
1415 }
1416}