Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 83 additions & 9 deletions pkg/appui/item_container.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package appui

import (
"image/color"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)

Expand All @@ -13,19 +18,31 @@ var _ fyne.DoubleTappable = (*ItemContainer)(nil)
// ItemContainer 是单个列表项,只负责显示文字和点击回调
type ItemContainer struct {
widget.BaseWidget
label *widget.Label
index int
onTapped func(index int)
onRightClicked func(index int, pos fyne.Position)
onDoubleTapped func(index int)
label *widget.Label
background *canvas.Rectangle
containerObj fyne.CanvasObject
index int
selected bool
selectionColor color.Color
transparentColor color.Color
onTapped func(index int)
onRightClicked func(index int, pos fyne.Position)
onDoubleTapped func(index int)
}

// NewItemContainer 创建新ItemContainer
func NewItemContainer(onTapped func(int), onRightClicked func(int, fyne.Position)) *ItemContainer {
label := widget.NewLabel("")
background := canvas.NewRectangle(color.Transparent)

ic := &ItemContainer{
label: widget.NewLabel(""),
onTapped: onTapped,
onRightClicked: onRightClicked,
label: label,
background: background,
containerObj: container.NewBorder(nil, nil, nil, nil, label),
selectionColor: theme.Color(theme.ColorNameSelection),
transparentColor: color.Transparent,
onTapped: onTapped,
onRightClicked: onRightClicked,
}
ic.ExtendBaseWidget(ic)
return ic
Expand All @@ -38,7 +55,11 @@ func (ic *ItemContainer) SetOnDoubleTapped(callback func(int)) {

// CreateRenderer 实现 fyne.Widget 接口
func (ic *ItemContainer) CreateRenderer() fyne.WidgetRenderer {
return widget.NewSimpleRenderer(ic.label)
return &itemContainerRenderer{
container: ic,
background: ic.background,
content: ic.containerObj,
}
}

// SetText 更新显示文本
Expand All @@ -51,6 +72,30 @@ func (ic *ItemContainer) SetIndex(i int) {
ic.index = i
}

// SetSelected 设置选中状态
func (ic *ItemContainer) SetSelected(selected bool) {
if ic.selected == selected {
return // 状态没有变化,直接返回
}

ic.selected = selected

// 使用缓存的颜色,避免重复查询主题系统
if selected {
ic.background.FillColor = ic.selectionColor
} else {
ic.background.FillColor = ic.transparentColor
}

// 只刷新背景,避免双重刷新
ic.background.Refresh()
}

// IsSelected 获取选中状态
func (ic *ItemContainer) IsSelected() bool {
return ic.selected
}

// Tapped 左键点击
func (ic *ItemContainer) Tapped(pe *fyne.PointEvent) {
if ic.onTapped != nil {
Expand All @@ -71,3 +116,32 @@ func (ic *ItemContainer) DoubleTapped(pe *fyne.PointEvent) {
ic.onDoubleTapped(ic.index)
}
}


// itemContainerRenderer 自定义渲染器
type itemContainerRenderer struct {
container *ItemContainer
background *canvas.Rectangle
content fyne.CanvasObject
}

func (r *itemContainerRenderer) Layout(size fyne.Size) {
r.background.Resize(size)
r.content.Resize(size)
}

func (r *itemContainerRenderer) MinSize() fyne.Size {
return r.content.MinSize()
}

func (r *itemContainerRenderer) Refresh() {
r.background.Refresh()
r.content.Refresh()
}

func (r *itemContainerRenderer) Objects() []fyne.CanvasObject {
return []fyne.CanvasObject{r.background, r.content}
}

func (r *itemContainerRenderer) Destroy() {}

40 changes: 37 additions & 3 deletions pkg/appui/right_clickable_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,17 @@ type RightClickableList struct {
widget.BaseWidget
list *widget.List
items []string
selectedIndex int
OnItemTapped func(index int)
OnItemRightClick func(index int, pos fyne.Position)
OnItemDoubleTapped func(index int)
}

// NewRightClickableList 创建新RightClickableList
func NewRightClickableList() *RightClickableList {
rcl := &RightClickableList{}
rcl := &RightClickableList{
selectedIndex: -1,
}
rcl.ExtendBaseWidget(rcl)
return rcl
}
Expand Down Expand Up @@ -59,8 +62,18 @@ func (rcl *RightClickableList) Build() {
},
func(i int, o fyne.CanvasObject) {
itemContainer := o.(*ItemContainer)
itemContainer.SetText(rcl.items[i])
itemContainer.SetIndex(i)
// 只在必要时更新文本和索引
if itemContainer.label.Text != rcl.items[i] {
itemContainer.SetText(rcl.items[i])
}
if itemContainer.index != i {
itemContainer.SetIndex(i)
}
// 只在选中状态真正变化时才调用SetSelected
isSelected := i == rcl.selectedIndex
if itemContainer.IsSelected() != isSelected {
itemContainer.SetSelected(isSelected)
}
},
)
}
Expand All @@ -77,10 +90,31 @@ func (rcl *RightClickableList) Refresh() {
}
}

// SetSelectedIndex 设置选中的索引
func (rcl *RightClickableList) SetSelectedIndex(index int) {
if rcl.selectedIndex == index {
return // 状态没有变化,直接返回
}

rcl.selectedIndex = index

// 立即刷新列表以更新选中状态
if rcl.list != nil {
rcl.list.Refresh()
}
}

// GetSelectedIndex 获取选中的索引
func (rcl *RightClickableList) GetSelectedIndex() int {
return rcl.selectedIndex
}

// UnselectAll 取消选中
func (rcl *RightClickableList) UnselectAll() {
rcl.selectedIndex = -1
if rcl.list != nil {
rcl.list.UnselectAll()
rcl.list.Refresh()
}
}

Expand Down
2 changes: 2 additions & 0 deletions pkg/appui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ func (ui *AppUI) setupUI() {
ui.refreshItems()
ui.rightClickableList = NewRightClickableList()
ui.rightClickableList.OnItemTapped = func(i int) {
// 立即更新选中状态,避免延迟
ui.rightClickableList.SetSelectedIndex(i)
ui.selectedIndex = i
ui.selectedName = ui.items[i]
ui.logger.Debug("left click", slog.String("item", ui.selectedName))
Expand Down