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 }