github.com/secoba/wails/v2@v2.6.4/internal/frontend/desktop/windows/winc/treeview.go (about)

     1  //go:build windows
     2  
     3  /*
     4   * Copyright (C) 2019 The Winc Authors. All Rights Reserved.
     5   */
     6  
     7  package winc
     8  
     9  import (
    10  	"errors"
    11  	"syscall"
    12  	"unsafe"
    13  
    14  	"github.com/secoba/wails/v2/internal/frontend/desktop/windows/winc/w32"
    15  )
    16  
    17  // TreeItem represents an item in a TreeView widget.
    18  type TreeItem interface {
    19  	Text() string    // Text returns the text of the item.
    20  	ImageIndex() int // ImageIndex is used only if SetImageList is called on the treeview
    21  }
    22  
    23  type treeViewItemInfo struct {
    24  	handle       w32.HTREEITEM
    25  	child2Handle map[TreeItem]w32.HTREEITEM
    26  }
    27  
    28  // StringTreeItem is helper for basic string lists.
    29  type StringTreeItem struct {
    30  	Data  string
    31  	Image int
    32  }
    33  
    34  func (s StringTreeItem) Text() string    { return s.Data }
    35  func (s StringTreeItem) ImageIndex() int { return s.Image }
    36  
    37  type TreeView struct {
    38  	ControlBase
    39  
    40  	iml         *ImageList
    41  	item2Info   map[TreeItem]*treeViewItemInfo
    42  	handle2Item map[w32.HTREEITEM]TreeItem
    43  	currItem    TreeItem
    44  
    45  	onSelectedChange EventManager
    46  	onExpand         EventManager
    47  	onCollapse       EventManager
    48  	onViewChange     EventManager
    49  }
    50  
    51  func NewTreeView(parent Controller) *TreeView {
    52  	tv := new(TreeView)
    53  
    54  	tv.InitControl("SysTreeView32", parent, 0, w32.WS_CHILD|w32.WS_VISIBLE|
    55  		w32.WS_BORDER|w32.TVS_HASBUTTONS|w32.TVS_LINESATROOT|w32.TVS_SHOWSELALWAYS|
    56  		w32.TVS_TRACKSELECT /*|w32.WS_EX_CLIENTEDGE*/)
    57  
    58  	tv.item2Info = make(map[TreeItem]*treeViewItemInfo)
    59  	tv.handle2Item = make(map[w32.HTREEITEM]TreeItem)
    60  
    61  	RegMsgHandler(tv)
    62  
    63  	tv.SetFont(DefaultFont)
    64  	tv.SetSize(200, 400)
    65  
    66  	if err := tv.SetTheme("Explorer"); err != nil {
    67  		// theme error is ignored
    68  	}
    69  	return tv
    70  }
    71  
    72  func (tv *TreeView) EnableDoubleBuffer(enable bool) {
    73  	if enable {
    74  		w32.SendMessage(tv.hwnd, w32.TVM_SETEXTENDEDSTYLE, 0, w32.TVS_EX_DOUBLEBUFFER)
    75  	} else {
    76  		w32.SendMessage(tv.hwnd, w32.TVM_SETEXTENDEDSTYLE, w32.TVS_EX_DOUBLEBUFFER, 0)
    77  	}
    78  }
    79  
    80  // SelectedItem is current selected item after OnSelectedChange event.
    81  func (tv *TreeView) SelectedItem() TreeItem {
    82  	return tv.currItem
    83  }
    84  
    85  func (tv *TreeView) SetSelectedItem(item TreeItem) bool {
    86  	var handle w32.HTREEITEM
    87  	if item != nil {
    88  		if info := tv.item2Info[item]; info == nil {
    89  			return false // invalid item
    90  		} else {
    91  			handle = info.handle
    92  		}
    93  	}
    94  
    95  	if w32.SendMessage(tv.hwnd, w32.TVM_SELECTITEM, w32.TVGN_CARET, uintptr(handle)) == 0 {
    96  		return false // set selected failed
    97  	}
    98  	tv.currItem = item
    99  	return true
   100  }
   101  
   102  func (tv *TreeView) ItemAt(x, y int) TreeItem {
   103  	hti := w32.TVHITTESTINFO{Pt: w32.POINT{int32(x), int32(y)}}
   104  	w32.SendMessage(tv.hwnd, w32.TVM_HITTEST, 0, uintptr(unsafe.Pointer(&hti)))
   105  	if item, ok := tv.handle2Item[hti.HItem]; ok {
   106  		return item
   107  	}
   108  	return nil
   109  }
   110  
   111  func (tv *TreeView) Items() (list []TreeItem) {
   112  	for item := range tv.item2Info {
   113  		list = append(list, item)
   114  	}
   115  	return list
   116  }
   117  
   118  func (tv *TreeView) InsertItem(item, parent, insertAfter TreeItem) error {
   119  	var tvins w32.TVINSERTSTRUCT
   120  	tvi := &tvins.Item
   121  
   122  	tvi.Mask = w32.TVIF_TEXT                                                     // w32.TVIF_CHILDREN | w32.TVIF_TEXT
   123  	tvi.PszText = uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(item.Text()))) // w32.LPSTR_TEXTCALLBACK
   124  	tvi.CChildren = 0                                                            // w32.I_CHILDRENCALLBACK
   125  
   126  	if parent == nil {
   127  		tvins.HParent = w32.TVI_ROOT
   128  	} else {
   129  		info := tv.item2Info[parent]
   130  		if info == nil {
   131  			return errors.New("winc: invalid parent")
   132  		}
   133  		tvins.HParent = info.handle
   134  	}
   135  
   136  	if insertAfter == nil {
   137  		tvins.HInsertAfter = w32.TVI_LAST
   138  	} else {
   139  		info := tv.item2Info[insertAfter]
   140  		if info == nil {
   141  			return errors.New("winc: invalid prev item")
   142  		}
   143  		tvins.HInsertAfter = info.handle
   144  	}
   145  
   146  	tv.applyImage(tvi, item)
   147  
   148  	hItem := w32.HTREEITEM(w32.SendMessage(tv.hwnd, w32.TVM_INSERTITEM, 0, uintptr(unsafe.Pointer(&tvins))))
   149  	if hItem == 0 {
   150  		return errors.New("winc: TVM_INSERTITEM failed")
   151  	}
   152  	tv.item2Info[item] = &treeViewItemInfo{hItem, make(map[TreeItem]w32.HTREEITEM)}
   153  	tv.handle2Item[hItem] = item
   154  	return nil
   155  }
   156  
   157  func (tv *TreeView) UpdateItem(item TreeItem) bool {
   158  	it := tv.item2Info[item]
   159  	if it == nil {
   160  		return false
   161  	}
   162  
   163  	tvi := &w32.TVITEM{
   164  		Mask:    w32.TVIF_TEXT,
   165  		HItem:   it.handle,
   166  		PszText: uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(item.Text()))),
   167  	}
   168  	tv.applyImage(tvi, item)
   169  
   170  	if w32.SendMessage(tv.hwnd, w32.TVM_SETITEM, 0, uintptr(unsafe.Pointer(tvi))) == 0 {
   171  		return false
   172  	}
   173  	return true
   174  }
   175  
   176  func (tv *TreeView) DeleteItem(item TreeItem) bool {
   177  	it := tv.item2Info[item]
   178  	if it == nil {
   179  		return false
   180  	}
   181  
   182  	if w32.SendMessage(tv.hwnd, w32.TVM_DELETEITEM, 0, uintptr(it.handle)) == 0 {
   183  		return false
   184  	}
   185  
   186  	delete(tv.item2Info, item)
   187  	delete(tv.handle2Item, it.handle)
   188  	return true
   189  }
   190  
   191  func (tv *TreeView) DeleteAllItems() bool {
   192  	if w32.SendMessage(tv.hwnd, w32.TVM_DELETEITEM, 0, 0) == 0 {
   193  		return false
   194  	}
   195  
   196  	tv.item2Info = make(map[TreeItem]*treeViewItemInfo)
   197  	tv.handle2Item = make(map[w32.HTREEITEM]TreeItem)
   198  	return true
   199  }
   200  
   201  func (tv *TreeView) Expand(item TreeItem) bool {
   202  	if w32.SendMessage(tv.hwnd, w32.TVM_EXPAND, w32.TVE_EXPAND, uintptr(tv.item2Info[item].handle)) == 0 {
   203  		return false
   204  	}
   205  	return true
   206  }
   207  
   208  func (tv *TreeView) Collapse(item TreeItem) bool {
   209  	if w32.SendMessage(tv.hwnd, w32.TVM_EXPAND, w32.TVE_COLLAPSE, uintptr(tv.item2Info[item].handle)) == 0 {
   210  		return false
   211  	}
   212  	return true
   213  }
   214  
   215  func (tv *TreeView) EnsureVisible(item TreeItem) bool {
   216  	if info := tv.item2Info[item]; info != nil {
   217  		return w32.SendMessage(tv.hwnd, w32.TVM_ENSUREVISIBLE, 0, uintptr(info.handle)) != 0
   218  	}
   219  	return false
   220  }
   221  
   222  func (tv *TreeView) SetImageList(imageList *ImageList) {
   223  	w32.SendMessage(tv.hwnd, w32.TVM_SETIMAGELIST, 0, uintptr(imageList.Handle()))
   224  	tv.iml = imageList
   225  }
   226  
   227  func (tv *TreeView) applyImage(tc *w32.TVITEM, item TreeItem) {
   228  	if tv.iml != nil {
   229  		tc.Mask |= w32.TVIF_IMAGE | w32.TVIF_SELECTEDIMAGE
   230  		tc.IImage = int32(item.ImageIndex())
   231  		tc.ISelectedImage = int32(item.ImageIndex())
   232  	}
   233  }
   234  
   235  func (tv *TreeView) OnSelectedChange() *EventManager {
   236  	return &tv.onSelectedChange
   237  }
   238  
   239  func (tv *TreeView) OnExpand() *EventManager {
   240  	return &tv.onExpand
   241  }
   242  
   243  func (tv *TreeView) OnCollapse() *EventManager {
   244  	return &tv.onCollapse
   245  }
   246  
   247  func (tv *TreeView) OnViewChange() *EventManager {
   248  	return &tv.onViewChange
   249  }
   250  
   251  // Message processer
   252  func (tv *TreeView) WndProc(msg uint32, wparam, lparam uintptr) uintptr {
   253  	switch msg {
   254  	case w32.WM_NOTIFY:
   255  		nm := (*w32.NMHDR)(unsafe.Pointer(lparam))
   256  
   257  		switch nm.Code {
   258  		case w32.TVN_ITEMEXPANDED:
   259  			nmtv := (*w32.NMTREEVIEW)(unsafe.Pointer(lparam))
   260  
   261  			switch nmtv.Action {
   262  			case w32.TVE_COLLAPSE:
   263  				tv.onCollapse.Fire(NewEvent(tv, nil))
   264  
   265  			case w32.TVE_COLLAPSERESET:
   266  
   267  			case w32.TVE_EXPAND:
   268  				tv.onExpand.Fire(NewEvent(tv, nil))
   269  
   270  			case w32.TVE_EXPANDPARTIAL:
   271  
   272  			case w32.TVE_TOGGLE:
   273  			}
   274  
   275  		case w32.TVN_SELCHANGED:
   276  			nmtv := (*w32.NMTREEVIEW)(unsafe.Pointer(lparam))
   277  			tv.currItem = tv.handle2Item[nmtv.ItemNew.HItem]
   278  			tv.onSelectedChange.Fire(NewEvent(tv, nil))
   279  
   280  		case w32.TVN_GETDISPINFO:
   281  			tv.onViewChange.Fire(NewEvent(tv, nil))
   282  		}
   283  
   284  	}
   285  	return w32.DefWindowProc(tv.hwnd, msg, wparam, lparam)
   286  }