1 Star 0 Fork 0

橙子/lxnWalk

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
treeview.go 13.01 KB
一键复制 编辑 原始数据 按行查看 历史
橙子 提交于 2020-08-13 10:03 . .
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build windows
package walk
import (
"syscall"
"unsafe"
"github.com/xuchengzhi/win"
)
type treeViewItemInfo struct {
handle win.HTREEITEM
child2Handle map[TreeItem]win.HTREEITEM
}
type TreeView struct {
WidgetBase
model TreeModel
lazyPopulation bool
itemsResetEventHandlerHandle int
itemChangedEventHandlerHandle int
item2Info map[TreeItem]*treeViewItemInfo
handle2Item map[win.HTREEITEM]TreeItem
currItem TreeItem
hIml win.HIMAGELIST
usingSysIml bool
imageUintptr2Index map[uintptr]int32
filePath2IconIndex map[string]int32
expandedChangedPublisher TreeItemEventPublisher
currentItemChangedPublisher EventPublisher
itemActivatedPublisher EventPublisher
}
func NewTreeView(parent Container) (*TreeView, error) {
tv := new(TreeView)
if err := InitWidget(
tv,
parent,
"SysTreeView32",
win.WS_TABSTOP|win.WS_VISIBLE|win.TVS_HASBUTTONS|win.TVS_LINESATROOT|win.TVS_SHOWSELALWAYS|win.TVS_TRACKSELECT,
win.WS_EX_CLIENTEDGE); err != nil {
return nil, err
}
succeeded := false
defer func() {
if !succeeded {
tv.Dispose()
}
}()
if hr := win.HRESULT(tv.SendMessage(win.TVM_SETEXTENDEDSTYLE, win.TVS_EX_DOUBLEBUFFER, win.TVS_EX_DOUBLEBUFFER)); win.FAILED(hr) {
return nil, errorFromHRESULT("TVM_SETEXTENDEDSTYLE", hr)
}
if err := tv.setTheme("Explorer"); err != nil {
return nil, err
}
tv.GraphicsEffects().Add(InteractionEffect)
tv.GraphicsEffects().Add(FocusEffect)
tv.MustRegisterProperty("CurrentItem", NewReadOnlyProperty(
func() interface{} {
return tv.CurrentItem()
},
tv.CurrentItemChanged()))
tv.MustRegisterProperty("CurrentItemLevel", NewReadOnlyProperty(
func() interface{} {
level := -1
item := tv.CurrentItem()
for item != nil {
level++
item = item.Parent()
}
return level
},
tv.CurrentItemChanged()))
tv.MustRegisterProperty("HasCurrentItem", NewReadOnlyBoolProperty(
func() bool {
return tv.CurrentItem() != nil
},
tv.CurrentItemChanged()))
succeeded = true
return tv, nil
}
func (*TreeView) LayoutFlags() LayoutFlags {
return ShrinkableHorz | ShrinkableVert | GrowableHorz | GrowableVert | GreedyHorz | GreedyVert
}
func (tv *TreeView) SizeHint() Size {
return tv.dialogBaseUnitsToPixels(Size{100, 100})
}
func (tv *TreeView) Dispose() {
tv.WidgetBase.Dispose()
tv.disposeImageListAndCaches()
}
func (tv *TreeView) SetBackground(bg Brush) {
tv.WidgetBase.SetBackground(bg)
color := Color(win.GetSysColor(win.COLOR_WINDOW))
if bg != nil {
type Colorer interface {
Color() Color
}
if c, ok := bg.(Colorer); ok {
color = c.Color()
}
}
tv.SendMessage(win.TVM_SETBKCOLOR, 0, uintptr(color))
}
func (tv *TreeView) Model() TreeModel {
return tv.model
}
func (tv *TreeView) SetModel(model TreeModel) error {
if tv.model != nil {
tv.model.ItemsReset().Detach(tv.itemsResetEventHandlerHandle)
tv.model.ItemChanged().Detach(tv.itemChangedEventHandlerHandle)
tv.disposeImageListAndCaches()
}
tv.model = model
if model != nil {
tv.lazyPopulation = model.LazyPopulation()
tv.itemsResetEventHandlerHandle = model.ItemsReset().Attach(func(parent TreeItem) {
if parent == nil {
tv.resetItems()
} else if tv.item2Info[parent] != nil {
tv.SetSuspended(true)
defer tv.SetSuspended(false)
if err := tv.removeDescendants(parent); err != nil {
return
}
if err := tv.insertChildren(parent); err != nil {
return
}
}
})
tv.itemChangedEventHandlerHandle = model.ItemChanged().Attach(func(item TreeItem) {
if item == nil || tv.item2Info[item] == nil {
return
}
if err := tv.updateItem(item); err != nil {
return
}
})
}
return tv.resetItems()
}
func (tv *TreeView) CurrentItem() TreeItem {
return tv.currItem
}
func (tv *TreeView) SetCurrentItem(item TreeItem) error {
if item == tv.currItem {
return nil
}
if item != nil {
if err := tv.ensureItemAndAncestorsInserted(item); err != nil {
return err
}
}
var handle win.HTREEITEM
if item != nil {
if info := tv.item2Info[item]; info == nil {
return newError("invalid item")
} else {
handle = info.handle
}
}
if 0 == tv.SendMessage(win.TVM_SELECTITEM, win.TVGN_CARET, uintptr(handle)) {
return newError("SendMessage(TVM_SELECTITEM) failed")
}
tv.currItem = item
return nil
}
func (tv *TreeView) ItemAt(x, y int) TreeItem {
hti := win.TVHITTESTINFO{Pt: win.POINT{int32(x), int32(y)}}
tv.SendMessage(win.TVM_HITTEST, 0, uintptr(unsafe.Pointer(&hti)))
if item, ok := tv.handle2Item[hti.HItem]; ok {
return item
}
return nil
}
func (tv *TreeView) ItemHeight() int {
return int(tv.SendMessage(win.TVM_GETITEMHEIGHT, 0, 0))
}
func (tv *TreeView) SetItemHeight(height int) {
tv.SendMessage(win.TVM_SETITEMHEIGHT, uintptr(height), 0)
}
func (tv *TreeView) resetItems() error {
tv.SetSuspended(true)
defer tv.SetSuspended(false)
if err := tv.clearItems(); err != nil {
return err
}
if tv.model == nil {
return nil
}
if err := tv.insertRoots(); err != nil {
return err
}
return nil
}
func (tv *TreeView) clearItems() error {
if 0 == tv.SendMessage(win.TVM_DELETEITEM, 0, 0) {
return newError("SendMessage(TVM_DELETEITEM) failed")
}
tv.item2Info = make(map[TreeItem]*treeViewItemInfo)
tv.handle2Item = make(map[win.HTREEITEM]TreeItem)
return nil
}
func (tv *TreeView) insertRoots() error {
for i := tv.model.RootCount() - 1; i >= 0; i-- {
if _, err := tv.insertItem(i, tv.model.RootAt(i)); err != nil {
return err
}
}
return nil
}
func (tv *TreeView) ApplyDPI(dpi int) {
tv.WidgetBase.ApplyDPI(dpi)
tv.disposeImageListAndCaches()
}
func (tv *TreeView) applyImageListForImage(image interface{}) {
tv.hIml, tv.usingSysIml, _ = imageListForImage(image, tv.DPI())
tv.SendMessage(win.TVM_SETIMAGELIST, 0, uintptr(tv.hIml))
tv.imageUintptr2Index = make(map[uintptr]int32)
tv.filePath2IconIndex = make(map[string]int32)
}
func (tv *TreeView) disposeImageListAndCaches() {
if tv.hIml != 0 && !tv.usingSysIml {
win.ImageList_Destroy(tv.hIml)
}
tv.hIml = 0
tv.imageUintptr2Index = nil
tv.filePath2IconIndex = nil
}
func (tv *TreeView) setTVITEMImageInfo(tvi *win.TVITEM, item TreeItem) {
if imager, ok := item.(Imager); ok {
if tv.hIml == 0 {
tv.applyImageListForImage(imager.Image())
}
// FIXME: If not setting TVIF_SELECTEDIMAGE and tvi.ISelectedImage,
// some default icon will show up, even though we have not asked for it.
tvi.Mask |= win.TVIF_IMAGE | win.TVIF_SELECTEDIMAGE
tvi.IImage = imageIndexMaybeAdd(
imager.Image(),
tv.hIml,
tv.usingSysIml,
tv.imageUintptr2Index,
tv.filePath2IconIndex,
tv.DPI())
tvi.ISelectedImage = tvi.IImage
}
}
func (tv *TreeView) insertItem(index int, item TreeItem) (win.HTREEITEM, error) {
var tvins win.TVINSERTSTRUCT
tvi := &tvins.Item
tvi.Mask = win.TVIF_CHILDREN | win.TVIF_TEXT
tvi.PszText = win.LPSTR_TEXTCALLBACK
tvi.CChildren = win.I_CHILDRENCALLBACK
tv.setTVITEMImageInfo(tvi, item)
parent := item.Parent()
if parent == nil {
tvins.HParent = win.TVI_ROOT
} else {
info := tv.item2Info[parent]
if info == nil {
return 0, newError("invalid parent")
}
tvins.HParent = info.handle
}
tvins.HInsertAfter = win.TVI_FIRST
hItem := win.HTREEITEM(tv.SendMessage(win.TVM_INSERTITEM, 0, uintptr(unsafe.Pointer(&tvins))))
if hItem == 0 {
return 0, newError("TVM_INSERTITEM failed")
}
tv.item2Info[item] = &treeViewItemInfo{hItem, make(map[TreeItem]win.HTREEITEM)}
tv.handle2Item[hItem] = item
if !tv.lazyPopulation {
if err := tv.insertChildren(item); err != nil {
return 0, err
}
}
return hItem, nil
}
func (tv *TreeView) insertChildren(parent TreeItem) error {
info := tv.item2Info[parent]
for i := parent.ChildCount() - 1; i >= 0; i-- {
child := parent.ChildAt(i)
if handle, err := tv.insertItem(i, child); err != nil {
return err
} else {
info.child2Handle[child] = handle
}
}
return nil
}
func (tv *TreeView) updateItem(item TreeItem) error {
tvi := &win.TVITEM{
Mask: win.TVIF_TEXT,
HItem: tv.item2Info[item].handle,
PszText: win.LPSTR_TEXTCALLBACK,
}
tv.setTVITEMImageInfo(tvi, item)
if 0 == tv.SendMessage(win.TVM_SETITEM, 0, uintptr(unsafe.Pointer(tvi))) {
return newError("SendMessage(TVM_SETITEM) failed")
}
return nil
}
func (tv *TreeView) removeItem(item TreeItem) error {
if err := tv.removeDescendants(item); err != nil {
return err
}
info := tv.item2Info[item]
if info == nil {
return newError("invalid item")
}
if 0 == tv.SendMessage(win.TVM_DELETEITEM, 0, uintptr(info.handle)) {
return newError("SendMessage(TVM_DELETEITEM) failed")
}
if parentInfo := tv.item2Info[item.Parent()]; parentInfo != nil {
delete(parentInfo.child2Handle, item)
}
delete(tv.item2Info, item)
delete(tv.handle2Item, info.handle)
return nil
}
func (tv *TreeView) removeDescendants(parent TreeItem) error {
for item, _ := range tv.item2Info[parent].child2Handle {
if err := tv.removeItem(item); err != nil {
return err
}
}
return nil
}
func (tv *TreeView) ensureItemAndAncestorsInserted(item TreeItem) error {
if item == nil {
return newError("invalid item")
}
tv.SetSuspended(true)
defer tv.SetSuspended(false)
var hierarchy []TreeItem
for item != nil && tv.item2Info[item] == nil {
item = item.Parent()
if item != nil {
hierarchy = append(hierarchy, item)
} else {
return newError("invalid item")
}
}
for i := len(hierarchy) - 1; i >= 0; i-- {
if err := tv.insertChildren(hierarchy[i]); err != nil {
return err
}
}
return nil
}
func (tv *TreeView) Expanded(item TreeItem) bool {
if tv.item2Info[item] == nil {
return false
}
tvi := &win.TVITEM{
HItem: tv.item2Info[item].handle,
Mask: win.TVIF_STATE,
StateMask: win.TVIS_EXPANDED,
}
if 0 == tv.SendMessage(win.TVM_GETITEM, 0, uintptr(unsafe.Pointer(tvi))) {
newError("SendMessage(TVM_GETITEM) failed")
}
return tvi.State&win.TVIS_EXPANDED != 0
}
func (tv *TreeView) SetExpanded(item TreeItem, expanded bool) error {
if expanded {
if err := tv.ensureItemAndAncestorsInserted(item); err != nil {
return err
}
}
info := tv.item2Info[item]
if info == nil {
return newError("invalid item")
}
var action uintptr
if expanded {
action = win.TVE_EXPAND
} else {
action = win.TVE_COLLAPSE
}
if 0 == tv.SendMessage(win.TVM_EXPAND, action, uintptr(info.handle)) {
return newError("SendMessage(TVM_EXPAND) failed")
}
return nil
}
func (tv *TreeView) ExpandedChanged() *TreeItemEvent {
return tv.expandedChangedPublisher.Event()
}
func (tv *TreeView) CurrentItemChanged() *Event {
return tv.currentItemChangedPublisher.Event()
}
func (tv *TreeView) ItemActivated() *Event {
return tv.itemActivatedPublisher.Event()
}
func (tv *TreeView) WndProc(hwnd win.HWND, msg uint32, wParam, lParam uintptr) uintptr {
switch msg {
case win.WM_GETDLGCODE:
if wParam == win.VK_RETURN {
return win.DLGC_WANTALLKEYS
}
case win.WM_NOTIFY:
nmhdr := (*win.NMHDR)(unsafe.Pointer(lParam))
switch nmhdr.Code {
case win.TVN_GETDISPINFO:
nmtvdi := (*win.NMTVDISPINFO)(unsafe.Pointer(lParam))
item := tv.handle2Item[nmtvdi.Item.HItem]
if nmtvdi.Item.Mask&win.TVIF_TEXT != 0 {
var text string
rc := win.RECT{Left: int32(nmtvdi.Item.HItem)}
if 0 != tv.SendMessage(win.TVM_GETITEMRECT, 0, uintptr(unsafe.Pointer(&rc))) {
// Only retrieve text if the item is visible. Why isn't Windows doing this for us?
text = item.Text()
}
utf16 := syscall.StringToUTF16(text)
buf := (*[264]uint16)(unsafe.Pointer(nmtvdi.Item.PszText))
max := mini(len(utf16), int(nmtvdi.Item.CchTextMax))
copy((*buf)[:], utf16[:max])
(*buf)[max-1] = 0
}
if nmtvdi.Item.Mask&win.TVIF_CHILDREN != 0 {
nmtvdi.Item.CChildren = int32(item.ChildCount())
}
case win.TVN_ITEMEXPANDING:
nmtv := (*win.NMTREEVIEW)(unsafe.Pointer(lParam))
item := tv.handle2Item[nmtv.ItemNew.HItem]
if nmtv.Action == win.TVE_EXPAND && tv.lazyPopulation {
info := tv.item2Info[item]
if len(info.child2Handle) == 0 {
tv.insertChildren(item)
}
}
case win.TVN_ITEMEXPANDED:
nmtv := (*win.NMTREEVIEW)(unsafe.Pointer(lParam))
item := tv.handle2Item[nmtv.ItemNew.HItem]
switch nmtv.Action {
case win.TVE_COLLAPSE:
tv.expandedChangedPublisher.Publish(item)
case win.TVE_COLLAPSERESET:
case win.TVE_EXPAND:
tv.expandedChangedPublisher.Publish(item)
case win.TVE_EXPANDPARTIAL:
case win.TVE_TOGGLE:
}
case win.NM_DBLCLK:
tv.itemActivatedPublisher.Publish()
case win.TVN_KEYDOWN:
nmtvkd := (*win.NMTVKEYDOWN)(unsafe.Pointer(lParam))
if nmtvkd.WVKey == uint16(KeyReturn) {
tv.itemActivatedPublisher.Publish()
}
case win.TVN_SELCHANGED:
nmtv := (*win.NMTREEVIEW)(unsafe.Pointer(lParam))
tv.currItem = tv.handle2Item[nmtv.ItemNew.HItem]
tv.currentItemChangedPublisher.Publish()
}
}
return tv.WidgetBase.WndProc(hwnd, msg, wParam, lParam)
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/xuchengzhi/lxnWalk.git
git@gitee.com:xuchengzhi/lxnWalk.git
xuchengzhi
lxnWalk
lxnWalk
6b3f71bcaf88

搜索帮助

D67c1975 1850385 1daf7b77 1850385