Files
winauth-go/internal/ui/row_menu.go
T
iceking2nd 089efa088d feat(ui): Battle.Net 序列号/恢复码展示 + 热键复制确认
- dialog_bnet_info.go: 新增 Battle.Net 信息对话框,显示序列号
  和恢复码,支持一键复制恢复码
- row_menu: 新增 rowActionBnetInfo 动作,仅对 Battle.Net 条目可见
- 热键行为确认:autoType 已自动复制到剪贴板 + 键入前台窗口
- i18n: action_bnet_info / dialog_bnet_info_title / bnet_info_intro
  / hint_bnet_restore_code_secret 三语翻译
2026-06-12 11:22:26 +08:00

128 lines
4.0 KiB
Go

package ui
import (
"gioui.org/layout"
"gioui.org/unit"
"gioui.org/widget"
"gioui.org/widget/material"
"git.wxccs.org/iceking2nd/winauth-go/internal/i18n"
)
// rowAction is what the per-row ⋯ popup returns to the caller.
type rowAction int
const (
rowActionNone rowAction = iota
rowActionRename
rowActionDelete
rowActionMoveUp
rowActionMoveDown
rowActionChangeIcon
rowActionBnetInfo
)
// rowActionMenu is the small popup opened by the ⋯ button on each entry
// row. It carries the target entry index so the caller knows which row
// the chosen action applies to. moveUp/moveDown are disabled (rendered
// but inert) when the index is at a boundary.
type rowActionMenu struct {
idx int
canMoveUp bool
canMoveDown bool
isBnet bool
renameBtn widget.Clickable
deleteBtn widget.Clickable
moveUpBtn widget.Clickable
moveDownBtn widget.Clickable
changeIconBtn widget.Clickable
bnetInfoBtn widget.Clickable
cancelBtn widget.Clickable
}
func newRowActionMenu(idx, total int, vendor string) *rowActionMenu {
return &rowActionMenu{
idx: idx,
canMoveUp: idx > 0,
canMoveDown: idx < total-1,
isBnet: vendor == "battlenet",
}
}
// Pick reports the user's choice and whether the menu should close.
func (m *rowActionMenu) Pick(gtx layout.Context) (rowAction, bool) {
switch {
case m.renameBtn.Clicked(gtx):
return rowActionRename, true
case m.deleteBtn.Clicked(gtx):
return rowActionDelete, true
case m.moveUpBtn.Clicked(gtx):
if !m.canMoveUp {
return rowActionNone, false
}
return rowActionMoveUp, true
case m.moveDownBtn.Clicked(gtx):
if !m.canMoveDown {
return rowActionNone, false
}
return rowActionMoveDown, true
case m.changeIconBtn.Clicked(gtx):
return rowActionChangeIcon, true
case m.cancelBtn.Clicked(gtx):
return rowActionNone, true
}
return rowActionNone, false
}
func (m *rowActionMenu) Layout(gtx layout.Context, th *material.Theme) layout.Dimensions {
fillBackground(gtx, activePalette.ScrimBg)
row := func(btn *widget.Clickable, label string, enabled bool) layout.FlexChild {
return layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return layout.Inset{Top: unit.Dp(4), Bottom: unit.Dp(4)}.Layout(gtx,
func(gtx layout.Context) layout.Dimensions {
gtx.Constraints.Min.X = gtx.Constraints.Max.X
b := material.Button(th, btn, label)
if !enabled {
// Visually dim the disabled state; Pick also
// refuses to dispatch the action so clicks
// are inert beyond the colour change.
b.Background = activePalette.RingBg
b.Color = activePalette.MutedFg
}
return b.Layout(gtx)
})
})
}
return layout.Center.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
gtx.Constraints.Max.X = gtx.Dp(260)
return widget.Border{
Color: activePalette.DialogBorder,
CornerRadius: unit.Dp(4),
Width: unit.Dp(1),
}.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
fillBackground(gtx, activePalette.DialogBg)
return layout.UniformInset(unit.Dp(16)).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Axis: layout.Vertical}.Layout(gtx,
layout.Rigid(material.H6(th, i18n.T("dialog_row_actions_title")).Layout),
layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout),
row(&m.renameBtn, i18n.T("action_rename"), true),
row(&m.deleteBtn, i18n.T("action_delete"), true),
row(&m.changeIconBtn, i18n.T("action_change_icon"), true),
row(&m.bnetInfoBtn, i18n.T("action_bnet_info"), m.isBnet),
row(&m.moveUpBtn, i18n.T("action_move_up"), m.canMoveUp),
row(&m.moveDownBtn, i18n.T("action_move_down"), m.canMoveDown),
layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout),
layout.Rigid(func(gtx layout.Context) layout.Dimensions {
return layout.Flex{Axis: layout.Horizontal, Spacing: layout.SpaceStart}.Layout(gtx,
layout.Rigid(material.Button(th, &m.cancelBtn, i18n.T("btn_cancel")).Layout),
)
}),
)
})
})
})
}