feat(ui): 条目管理增强 — 重命名/删除/重排序
每行右侧增加 ⋯ 按钮,弹出菜单支持重命名、删除、上移、下移。 - renameEntry / deleteEntry / moveEntry 三个 appState 方法 - rowActionMenu 弹出菜单 + renameDialog + confirmDeleteDialog - 删除自动注销快捷键,所有操作触发配置保存 - 保留顶栏 Add/Remove 全局按钮不变
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
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
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
renameBtn widget.Clickable
|
||||
deleteBtn widget.Clickable
|
||||
moveUpBtn widget.Clickable
|
||||
moveDownBtn widget.Clickable
|
||||
cancelBtn widget.Clickable
|
||||
}
|
||||
|
||||
func newRowActionMenu(idx, total int) *rowActionMenu {
|
||||
return &rowActionMenu{
|
||||
idx: idx,
|
||||
canMoveUp: idx > 0,
|
||||
canMoveDown: idx < total-1,
|
||||
}
|
||||
}
|
||||
|
||||
// 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.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.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),
|
||||
)
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user