1use super::{ControlBase, ControlHandle};
6use crate::win32::base_helper::{check_hwnd, from_utf16, to_utf16};
7use crate::win32::window_helper as wh;
8use crate::{Font, NwgError};
9use std::{mem, ptr};
10use winapi::shared::minwindef::{LPARAM, WPARAM};
11use winapi::um::commctrl::{HTREEITEM, TVIS_EXPANDED, TVIS_SELECTED, TVITEMW, TVS_SHOWSELALWAYS};
12use winapi::um::winuser::{WS_DISABLED, WS_TABSTOP, WS_VISIBLE};
13
14#[cfg(feature = "image-list")]
15use winapi::um::commctrl::HIMAGELIST;
16
17#[cfg(feature = "image-list")]
18use crate::ImageList;
19
20const NOT_BOUND: &'static str = "TreeView is not yet bound to a winapi object";
21const BAD_HANDLE: &'static str = "INTERNAL ERROR: TreeView handle is not HWND!";
22
23bitflags! {
24 pub struct TreeViewFlags: u32 {
32 const VISIBLE = WS_VISIBLE;
33 const DISABLED = WS_DISABLED;
34 const TAB_STOP = WS_TABSTOP;
35 const ALWAYS_SHOW_SELECTION = TVS_SHOWSELALWAYS;
36 }
37}
38
39bitflags! {
40 pub struct TreeItemState: u32 {
48 const SELECTED = TVIS_SELECTED;
49 const EXPANDED = TVIS_EXPANDED;
50 }
51}
52
53#[derive(Copy, Clone, Debug)]
55pub enum TreeInsert {
56 First,
58
59 Last,
61
62 Root,
64
65 Sort,
67
68 After(HTREEITEM),
70}
71
72#[derive(Copy, Clone, Debug)]
74#[repr(u8)]
75pub enum ExpandState {
76 Collapse,
77 CollapseReset,
78 Expand,
79 ExpandPartial,
80 Toggle,
81}
82
83#[derive(Copy, Clone, Debug)]
85pub enum TreeItemAction {
86 Unknown,
88
89 Expand(ExpandState),
91
92 State {
94 old: TreeItemState,
95 new: TreeItemState,
96 },
97}
98
99#[derive(Debug)]
101pub struct TreeItem {
102 pub handle: HTREEITEM,
103}
104
105impl TreeItem {
106 pub fn is_null(&self) -> bool {
108 self.handle.is_null()
109 }
110}
111
112#[derive(Default, PartialEq, Eq)]
146pub struct TreeView {
147 pub handle: ControlHandle,
148}
149
150impl TreeView {
151 pub fn builder<'a>() -> TreeViewBuilder<'a> {
152 TreeViewBuilder {
153 size: (100, 200),
154 position: (0, 0),
155 enabled: true,
156 focus: false,
157 flags: None,
158 ex_flags: 0,
159 font: None,
160 parent: None,
161
162 #[cfg(feature = "image-list")]
163 image_list: None,
164 }
165 }
166
167 #[cfg(feature = "image-list")]
169 pub fn set_image_list(&self, list: Option<&ImageList>) {
170 use winapi::um::commctrl::{TVM_SETIMAGELIST, TVSIL_NORMAL};
171
172 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
173 let list_handle = list.map(|l| l.handle).unwrap_or(ptr::null_mut());
174
175 wh::send_message(handle, TVM_SETIMAGELIST, TVSIL_NORMAL, list_handle as _);
176 }
177
178 #[cfg(feature = "image-list")]
181 pub fn image_list(&self) -> Option<ImageList> {
182 use winapi::um::commctrl::{TVM_GETIMAGELIST, TVSIL_NORMAL};
183
184 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
185 let handle = wh::send_message(handle, TVM_GETIMAGELIST, TVSIL_NORMAL, 0) as HIMAGELIST;
186 if handle.is_null() {
187 None
188 } else {
189 Some(ImageList {
190 handle,
191 owned: false,
192 })
193 }
194 }
195
196 #[cfg(feature = "image-list")]
200 pub fn set_item_image(&self, item: &TreeItem, index: i32, on_select: bool) {
201 use winapi::um::commctrl::{TVIF_IMAGE, TVIF_SELECTEDIMAGE, TVM_SETITEMW};
202
203 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
204 let mut tree_item = blank_item();
205 tree_item.hItem = item.handle;
206
207 tree_item.mask = match on_select {
208 true => TVIF_SELECTEDIMAGE,
209 false => TVIF_IMAGE,
210 };
211
212 match on_select {
213 true => {
214 tree_item.iSelectedImage = index;
215 }
216 false => {
217 tree_item.iImage = index;
218 }
219 }
220
221 wh::send_message(
222 handle,
223 TVM_SETITEMW,
224 0,
225 &mut tree_item as *mut TVITEMW as LPARAM,
226 );
227 }
228
229 #[cfg(feature = "image-list")]
233 pub fn item_image(&self, item: &TreeItem, on_select: bool) -> i32 {
234 use winapi::um::commctrl::{TVIF_IMAGE, TVIF_SELECTEDIMAGE, TVM_GETITEMW};
235
236 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
237 let mut tree_item = blank_item();
238 tree_item.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
239 tree_item.hItem = item.handle;
240
241 match wh::send_message(
242 handle,
243 TVM_GETITEMW,
244 0,
245 &mut tree_item as *mut TVITEMW as LPARAM,
246 ) {
247 0 => 0,
248 _ => match on_select {
249 true => tree_item.iSelectedImage,
250 false => tree_item.iImage,
251 },
252 }
253 }
254
255 pub fn set_text_color(&self, r: u8, g: u8, b: u8) {
257 use winapi::um::commctrl::TVM_SETTEXTCOLOR;
258 use winapi::um::wingdi::RGB;
259
260 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
261 let color = RGB(r, g, b);
262
263 wh::send_message(handle, TVM_SETTEXTCOLOR, 0, color as _);
264
265 self.invalidate();
266 }
267
268 pub fn text_color(&self) -> [u8; 3] {
270 use winapi::um::commctrl::TVM_GETTEXTCOLOR;
271 use winapi::um::wingdi::{GetBValue, GetGValue, GetRValue};
272
273 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
274
275 let col = wh::send_message(handle, TVM_GETTEXTCOLOR, 0, 0) as u32;
276
277 [GetRValue(col), GetGValue(col), GetBValue(col)]
278 }
279
280 pub fn indent(&self) -> u32 {
282 use winapi::um::commctrl::TVM_GETINDENT;
283
284 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
285 wh::send_message(handle, TVM_GETINDENT, 0, 0) as u32
286 }
287
288 pub fn set_indent(&self, indent: u32) {
290 use winapi::um::commctrl::TVM_SETINDENT;
291
292 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
293 wh::send_message(handle, TVM_SETINDENT, indent as _, 0);
294 }
295
296 pub fn root(&self) -> Option<TreeItem> {
299 use winapi::um::commctrl::TVGN_ROOT;
300 next_treeview_item(&self.handle, TVGN_ROOT, ptr::null_mut())
301 }
302
303 pub fn first_child(&self, item: &TreeItem) -> Option<TreeItem> {
306 use winapi::um::commctrl::TVGN_CHILD;
307 next_treeview_item(&self.handle, TVGN_CHILD, item.handle)
308 }
309
310 pub fn next_sibling(&self, item: &TreeItem) -> Option<TreeItem> {
312 use winapi::um::commctrl::TVGN_NEXT;
313 next_treeview_item(&self.handle, TVGN_NEXT, item.handle)
314 }
315
316 pub fn previous_sibling(&self, item: &TreeItem) -> Option<TreeItem> {
318 use winapi::um::commctrl::TVGN_PREVIOUS;
319 next_treeview_item(&self.handle, TVGN_PREVIOUS, item.handle)
320 }
321
322 pub fn parent(&self, item: &TreeItem) -> Option<TreeItem> {
324 use winapi::um::commctrl::TVGN_PARENT;
325 next_treeview_item(&self.handle, TVGN_PARENT, item.handle)
326 }
327
328 pub fn selected_item(&self) -> Option<TreeItem> {
331 use winapi::um::commctrl::{TVGN_NEXTSELECTED, TVM_GETNEXTITEM};
332
333 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
334 let tree_handle =
335 wh::send_message(handle, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, 0) as HTREEITEM;
336 if tree_handle.is_null() {
337 None
338 } else {
339 Some(TreeItem {
340 handle: tree_handle,
341 })
342 }
343 }
344
345 pub fn selected_items(&self) -> Vec<TreeItem> {
348 use winapi::um::commctrl::{TVGN_NEXTSELECTED, TVM_GETNEXTITEM};
349
350 let mut items = Vec::new();
351
352 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
353 let mut last_handle = wh::send_message(handle, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, 0);
354 while last_handle != 0 {
355 items.push(TreeItem {
356 handle: last_handle as _,
357 });
358 last_handle =
359 wh::send_message(handle, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, last_handle as _);
360 }
361
362 items
363 }
364
365 pub fn selected_item_count(&self) -> usize {
367 use winapi::um::commctrl::{TVGN_NEXTSELECTED, TVM_GETNEXTITEM};
368
369 let mut count = 0;
370 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
371
372 let mut last_handle = wh::send_message(handle, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, 0);
373 while last_handle != 0 {
374 count += 1;
375 last_handle =
376 wh::send_message(handle, TVM_GETNEXTITEM, TVGN_NEXTSELECTED, last_handle as _);
377 }
378
379 count
380 }
381
382 pub fn insert_item<'a>(
384 &self,
385 new: &'a str,
386 parent: Option<&TreeItem>,
387 position: TreeInsert,
388 ) -> TreeItem {
389 use winapi::um::commctrl::TVINSERTSTRUCTW_u;
390 use winapi::um::commctrl::{
391 TVI_FIRST, TVI_LAST, TVI_ROOT, TVI_SORT, TVIF_TEXT, TVINSERTSTRUCTW, TVM_INSERTITEMW,
392 };
393 use winapi::um::winnt::LPWSTR;
394
395 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
396
397 let insert = match position {
398 TreeInsert::First => TVI_FIRST,
399 TreeInsert::Last => TVI_LAST,
400 TreeInsert::Root => TVI_ROOT,
401 TreeInsert::Sort => TVI_SORT,
402 TreeInsert::After(i) => i,
403 };
404
405 let text = to_utf16(new);
406
407 let item = {
408 let mut item: TVINSERTSTRUCTW_u = unsafe { mem::zeroed() };
409 let i = unsafe { item.item_mut() };
410 i.mask = TVIF_TEXT;
411 i.pszText = text.as_ptr() as LPWSTR;
412 item
413 };
414
415 let new_item = TVINSERTSTRUCTW {
416 hParent: parent.map(|p| p.handle).unwrap_or(ptr::null_mut()),
417 hInsertAfter: insert,
418 u: item,
419 };
420
421 let ptr = &new_item as *const TVINSERTSTRUCTW;
422 let handle = wh::send_message(handle, TVM_INSERTITEMW, 0, ptr as LPARAM) as HTREEITEM;
423
424 self.invalidate();
425
426 TreeItem { handle }
427 }
428
429 pub fn insert_item_with_param<'a>(
431 &self,
432 new: &'a str,
433 parent: Option<&TreeItem>,
434 position: TreeInsert,
435 data: isize,
436 ) -> TreeItem {
437 use winapi::um::commctrl::TVINSERTSTRUCTW_u;
438 use winapi::um::commctrl::{
439 TVI_FIRST, TVI_LAST, TVI_ROOT, TVI_SORT, TVIF_PARAM, TVIF_TEXT, TVINSERTSTRUCTW,
440 TVM_INSERTITEMW,
441 };
442 use winapi::um::winnt::LPWSTR;
443
444 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
445
446 let insert = match position {
447 TreeInsert::First => TVI_FIRST,
448 TreeInsert::Last => TVI_LAST,
449 TreeInsert::Root => TVI_ROOT,
450 TreeInsert::Sort => TVI_SORT,
451 TreeInsert::After(i) => i,
452 };
453
454 let text = to_utf16(new);
455
456 let item = {
457 let mut item: TVINSERTSTRUCTW_u = unsafe { mem::zeroed() };
458 let i = unsafe { item.item_mut() };
459 i.mask = TVIF_TEXT | TVIF_PARAM;
460 i.pszText = text.as_ptr() as LPWSTR;
461 i.lParam = data;
462 item
463 };
464
465 let new_item = TVINSERTSTRUCTW {
466 hParent: parent.map(|p| p.handle).unwrap_or(ptr::null_mut()),
467 hInsertAfter: insert,
468 u: item,
469 };
470
471 let ptr = &new_item as *const TVINSERTSTRUCTW;
472 let handle = wh::send_message(handle, TVM_INSERTITEMW, 0, ptr as LPARAM) as HTREEITEM;
473
474 self.invalidate();
475
476 TreeItem { handle }
477 }
478
479 pub fn remove_item(&self, item: &TreeItem) {
481 use winapi::um::commctrl::TVM_DELETEITEM;
482
483 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
484 wh::send_message(handle, TVM_DELETEITEM, 0, item.handle as LPARAM);
485 }
486
487 pub fn select_item(&self, item: &TreeItem) {
489 use winapi::um::commctrl::{TVIF_STATE, TVM_SETITEMW};
490
491 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
492 let mut tree_item = blank_item();
493 tree_item.mask = TVIF_STATE;
494 tree_item.hItem = item.handle;
495 tree_item.state = TVIS_SELECTED;
496 tree_item.stateMask = TVIS_SELECTED;
497
498 wh::send_message(
499 handle,
500 TVM_SETITEMW,
501 0,
502 &mut tree_item as *mut TVITEMW as LPARAM,
503 );
504 }
505
506 pub fn unselect_item(&self, item: &TreeItem) {
508 use winapi::um::commctrl::{TVIF_STATE, TVM_SETITEMW};
509
510 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
511 let mut tree_item = blank_item();
512 tree_item.mask = TVIF_STATE;
513 tree_item.hItem = item.handle;
514 tree_item.state = 0;
515 tree_item.stateMask = TVIS_SELECTED;
516
517 wh::send_message(
518 handle,
519 TVM_SETITEMW,
520 0,
521 &mut tree_item as *mut TVITEMW as LPARAM,
522 );
523 }
524
525 #[cfg(feature = "tree-view-iterator")]
527 pub fn iter<'a>(&'a self) -> crate::TreeViewIterator<'a> {
528 check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
529 crate::TreeViewIterator::new(self, ptr::null_mut())
530 }
531
532 #[cfg(feature = "tree-view-iterator")]
534 pub fn iter_item<'a>(&'a self, item: &TreeItem) -> crate::TreeViewIterator<'a> {
535 check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
536 crate::TreeViewIterator::new(self, item.handle)
537 }
538
539 pub fn item_text(&self, tree_item: &TreeItem) -> Option<String> {
542 use winapi::um::commctrl::{TVIF_HANDLE, TVIF_TEXT, TVM_GETITEMW};
543 const BUFFER_MAX: usize = 260;
544
545 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
546
547 let mut text_buffer = Vec::with_capacity(BUFFER_MAX);
548 unsafe {
549 text_buffer.set_len(BUFFER_MAX);
550 }
551
552 let mut item: TVITEMW = blank_item();
553 item.mask = TVIF_TEXT | TVIF_HANDLE;
554 item.hItem = tree_item.handle;
555 item.pszText = text_buffer.as_mut_ptr();
556 item.cchTextMax = BUFFER_MAX as _;
557
558 let result = wh::send_message(handle, TVM_GETITEMW, 0, &mut item as *mut TVITEMW as LPARAM);
559 if result == 0 {
560 return None;
561 }
562
563 Some(from_utf16(&text_buffer))
564 }
565
566 pub fn set_item_text(&self, tree_item: &TreeItem, new_text: &str) {
568 use winapi::um::commctrl::{TVIF_TEXT, TVM_SETITEMW};
569 use winapi::um::winnt::LPWSTR;
570
571 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
572 let text = to_utf16(new_text);
573
574 let mut item: TVITEMW = blank_item();
575 item.mask = TVIF_TEXT;
576 item.hItem = tree_item.handle;
577 item.pszText = text.as_ptr() as LPWSTR;
578
579 wh::send_message(handle, TVM_SETITEMW, 0, &mut item as *mut TVITEMW as LPARAM);
580 }
581
582 pub fn item_param(&self, tree_item: &TreeItem) -> Option<isize> {
584 use winapi::um::commctrl::{TVIF_HANDLE, TVIF_PARAM, TVM_GETITEMW};
585
586 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
587
588 let mut item: TVITEMW = blank_item();
589 item.mask = TVIF_HANDLE | TVIF_PARAM;
590 item.hItem = tree_item.handle;
591
592 let result = wh::send_message(handle, TVM_GETITEMW, 0, &mut item as *mut TVITEMW as LPARAM);
593 if result == 0 {
594 return None;
595 }
596
597 Some(item.lParam)
598 }
599
600 pub fn item_has_children(&self, tree_item: &TreeItem) -> Option<bool> {
602 use winapi::um::commctrl::{TVIF_CHILDREN, TVIF_HANDLE, TVM_GETITEMW};
603
604 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
605
606 let mut item: TVITEMW = blank_item();
607 item.hItem = tree_item.handle;
608 item.mask = TVIF_CHILDREN | TVIF_HANDLE;
609
610 let result = wh::send_message(handle, TVM_GETITEMW, 0, &mut item as *mut TVITEMW as LPARAM);
611 if result == 0 {
612 return None;
613 }
614
615 Some(item.cChildren != 0)
616 }
617
618 pub fn item_state(&self, tree_item: &TreeItem) -> Option<TreeItemState> {
620 use winapi::um::commctrl::{TVIF_HANDLE, TVIF_STATE, TVM_GETITEMW};
621
622 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
623
624 let mut item: TVITEMW = unsafe { mem::zeroed() };
625 item.hItem = tree_item.handle;
626 item.mask = TVIF_STATE | TVIF_HANDLE;
627 item.stateMask = 0xFF;
628
629 let result = wh::send_message(handle, TVM_GETITEMW, 0, &mut item as *mut TVITEMW as LPARAM);
630 if result == 0 {
631 return None;
632 }
633
634 Some(TreeItemState::from_bits_truncate(item.state))
635 }
636
637 pub fn set_expand_state(&self, item: &TreeItem, state: ExpandState) {
639 use winapi::um::commctrl::{
640 TVE_COLLAPSE, TVE_COLLAPSERESET, TVE_EXPAND, TVE_EXPANDPARTIAL, TVE_TOGGLE, TVM_EXPAND,
641 };
642
643 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
644
645 let state = match state {
646 ExpandState::Collapse => TVE_COLLAPSE,
647 ExpandState::CollapseReset => TVE_COLLAPSE | TVE_COLLAPSERESET,
648 ExpandState::Expand => TVE_EXPAND,
649 ExpandState::ExpandPartial => TVE_EXPANDPARTIAL,
650 ExpandState::Toggle => TVE_TOGGLE,
651 };
652
653 wh::send_message(handle, TVM_EXPAND, state as WPARAM, item.handle as LPARAM);
654 }
655
656 pub fn ensure_visible(&self, item: &TreeItem) {
658 use winapi::um::commctrl::TVM_ENSUREVISIBLE;
659
660 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
661 wh::send_message(handle, TVM_ENSUREVISIBLE, 0, item.handle as LPARAM);
662 }
663
664 pub fn clear(&self) {
666 use winapi::um::commctrl::{TVI_ROOT, TVM_DELETEITEM};
667
668 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
669 wh::send_message(handle, TVM_DELETEITEM, 0, TVI_ROOT as LPARAM);
670 }
671
672 pub fn len(&self) -> usize {
674 use winapi::um::commctrl::TVM_GETCOUNT;
675
676 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
677 wh::send_message(handle, TVM_GETCOUNT, 0, 0) as usize
678 }
679
680 pub fn visible_len(&self) -> usize {
682 use winapi::um::commctrl::TVM_GETVISIBLECOUNT;
683
684 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
685 wh::send_message(handle, TVM_GETVISIBLECOUNT, 0, 0) as usize
686 }
687
688 pub fn invalidate(&self) {
694 use winapi::um::winuser::InvalidateRect;
695
696 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
697 unsafe {
698 InvalidateRect(handle, ptr::null(), 1);
699 }
700 }
701
702 pub fn font(&self) -> Option<Font> {
704 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
705
706 let font_handle = wh::get_window_font(handle);
707 if font_handle.is_null() {
708 None
709 } else {
710 Some(Font {
711 handle: font_handle,
712 })
713 }
714 }
715
716 pub fn set_font(&self, font: Option<&Font>) {
718 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
719
720 wh::set_window_font(handle, font.map(|f| f.handle), true);
721 }
722
723 pub fn focus(&self) -> bool {
725 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
726 wh::get_focus(handle)
727 }
728
729 pub fn set_focus(&self) {
731 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
732
733 wh::set_focus(handle);
734 }
735
736 pub fn enabled(&self) -> bool {
738 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
739 wh::get_window_enabled(handle)
740 }
741
742 pub fn set_enabled(&self, v: bool) {
744 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
745 wh::set_window_enabled(handle, v)
746 }
747
748 pub fn visible(&self) -> bool {
751 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
752 wh::get_window_visibility(handle)
753 }
754
755 pub fn set_visible(&self, v: bool) {
757 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
758 wh::set_window_visibility(handle, v)
759 }
760
761 pub fn size(&self) -> (u32, u32) {
763 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
764 wh::get_window_size(handle)
765 }
766
767 pub fn set_size(&self, x: u32, y: u32) {
769 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
770 wh::set_window_size(handle, x, y, false)
771 }
772
773 pub fn position(&self) -> (i32, i32) {
775 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
776 wh::get_window_position(handle)
777 }
778
779 pub fn set_position(&self, x: i32, y: i32) {
781 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
782 wh::set_window_position(handle, x, y)
783 }
784
785 pub fn class_name(&self) -> &'static str {
787 winapi::um::commctrl::WC_TREEVIEW
788 }
789
790 pub fn flags(&self) -> u32 {
792 use winapi::um::commctrl::{TVS_EDITLABELS, TVS_HASBUTTONS, TVS_HASLINES, TVS_LINESATROOT};
793
794 WS_VISIBLE
795 | TVS_HASBUTTONS
796 | TVS_LINESATROOT
797 | TVS_HASLINES
798 | WS_TABSTOP
799 | TVS_SHOWSELALWAYS
800 | TVS_EDITLABELS
801 }
802
803 pub fn forced_flags(&self) -> u32 {
805 use winapi::um::commctrl::TVS_NOTOOLTIPS;
806 use winapi::um::winuser::{WS_BORDER, WS_CHILD};
807
808 WS_CHILD | WS_BORDER | TVS_NOTOOLTIPS
809 }
810
811 pub fn edit_label(&self, item: &TreeItem) -> Option<ControlHandle> {
815 use winapi::shared::windef::HWND;
816 use winapi::um::commctrl::TVM_EDITLABELW;
817 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
818
819 let result = wh::send_message(
820 handle,
821 TVM_EDITLABELW,
822 0,
823 item.handle as HTREEITEM as LPARAM,
824 );
825
826 if result == 0 {
827 return None;
828 }
829 Some(ControlHandle::Hwnd(result as HWND))
830 }
831
832 pub fn end_edit_label_now(&self, f_cancel: bool) -> bool {
837 use winapi::um::commctrl::TVM_ENDEDITLABELNOW;
838 let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
839
840 wh::send_message(handle, TVM_ENDEDITLABELNOW, f_cancel as WPARAM, 0) != 0
841 }
842}
843
844impl Drop for TreeView {
845 fn drop(&mut self) {
846 self.handle.destroy();
847 }
848}
849
850pub struct TreeViewBuilder<'a> {
852 size: (i32, i32),
853 position: (i32, i32),
854 enabled: bool,
855 focus: bool,
856 flags: Option<TreeViewFlags>,
857 ex_flags: u32,
858 font: Option<&'a Font>,
859 parent: Option<ControlHandle>,
860
861 #[cfg(feature = "image-list")]
862 image_list: Option<&'a ImageList>,
863}
864
865impl<'a> TreeViewBuilder<'a> {
866 pub fn flags(mut self, flags: TreeViewFlags) -> TreeViewBuilder<'a> {
867 self.flags = Some(flags);
868 self
869 }
870
871 pub fn ex_flags(mut self, flags: u32) -> TreeViewBuilder<'a> {
872 self.ex_flags = flags;
873 self
874 }
875
876 pub fn size(mut self, size: (i32, i32)) -> TreeViewBuilder<'a> {
877 self.size = size;
878 self
879 }
880
881 pub fn position(mut self, pos: (i32, i32)) -> TreeViewBuilder<'a> {
882 self.position = pos;
883 self
884 }
885
886 pub fn enabled(mut self, e: bool) -> TreeViewBuilder<'a> {
887 self.enabled = e;
888 self
889 }
890
891 pub fn focus(mut self, focus: bool) -> TreeViewBuilder<'a> {
892 self.focus = focus;
893 self
894 }
895
896 pub fn font(mut self, font: Option<&'a Font>) -> TreeViewBuilder<'a> {
897 self.font = font;
898 self
899 }
900
901 pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> TreeViewBuilder<'a> {
902 self.parent = Some(p.into());
903 self
904 }
905
906 #[cfg(feature = "image-list")]
907 pub fn image_list(mut self, list: Option<&'a ImageList>) -> TreeViewBuilder<'a> {
908 self.image_list = list;
909 self
910 }
911
912 pub fn build(self, out: &mut TreeView) -> Result<(), NwgError> {
913 let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
914
915 let parent = match self.parent {
916 Some(p) => Ok(p),
917 None => Err(NwgError::no_parent("TreeView")),
918 }?;
919
920 *out = Default::default();
921
922 out.handle = ControlBase::build_hwnd()
923 .class_name(out.class_name())
924 .forced_flags(out.forced_flags())
925 .flags(flags)
926 .ex_flags(self.ex_flags)
927 .size(self.size)
928 .position(self.position)
929 .parent(Some(parent))
930 .build()?;
931
932 if self.font.is_some() {
933 out.set_font(self.font);
934 } else {
935 out.set_font(Font::global_default().as_ref());
936 }
937
938 builder_set_image_list(&self, out);
939
940 if self.focus {
941 out.set_focus();
942 }
943
944 out.set_enabled(self.enabled);
945
946 Ok(())
947 }
948}
949
950impl PartialEq for TreeItem {
951 fn eq(&self, other: &Self) -> bool {
952 self.handle == other.handle
953 }
954}
955
956impl Eq for TreeItem {}
957
958fn next_treeview_item(handle: &ControlHandle, action: usize, item: HTREEITEM) -> Option<TreeItem> {
959 use winapi::um::commctrl::TVM_GETNEXTITEM;
960
961 if handle.blank() {
962 panic!("{}", NOT_BOUND);
963 }
964 let handle = handle.hwnd().expect(BAD_HANDLE);
965
966 let handle = wh::send_message(handle, TVM_GETNEXTITEM, action as _, item as _) as HTREEITEM;
967 if handle.is_null() {
968 None
969 } else {
970 Some(TreeItem { handle })
971 }
972}
973
974#[cfg(feature = "image-list")]
975fn builder_set_image_list(builder: &TreeViewBuilder, out: &TreeView) {
976 if builder.image_list.is_some() {
977 out.set_image_list(builder.image_list);
978 }
979}
980
981#[cfg(not(feature = "image-list"))]
982fn builder_set_image_list(_builder: &TreeViewBuilder, _out: &TreeView) {}
983
984fn blank_item() -> TVITEMW {
985 TVITEMW {
986 mask: 0,
987 hItem: ptr::null_mut(),
988 state: 0,
989 stateMask: 0,
990 pszText: ptr::null_mut(),
991 cchTextMax: 0,
992 iImage: 0,
993 iSelectedImage: 0,
994 cChildren: 0,
995 lParam: 0,
996 }
997}