From 1474caffaa41776cb4cc4c0c5c552fae6610e2d2 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Fri, 1 Aug 2025 15:00:03 -0500 Subject: [PATCH] Menu/MenuItem work --- menu.go | 30 ++++++++++++++++++++--- menu_item.go | 68 +++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/menu.go b/menu.go index 5551176..14f8395 100644 --- a/menu.go +++ b/menu.go @@ -37,7 +37,8 @@ type Menu struct { menuType MenuType cursor int - items []Widget + items []*MenuItem + disabled []bool onPressed func() bool manualExpand bool expanded bool @@ -134,7 +135,6 @@ func (w *Menu) drawVMenu(screen tcell.Screen) { h.TitledBorderFilled(x-1, y, x+w.WantW(), y+w.WantH(), w.label, h.BRD_CSIMPLE, w.style, screen) } h.DrawText(w.x, w.y, w.label, st, screen) - y++ if w.expanded || (w.active && !w.manualExpand) { for i := range w.items { w.items[i].SetActive(w.active && w.cursor == i) @@ -190,8 +190,14 @@ func (w *Menu) SetType(tp MenuType) { w.menuType = tp } func (w *Menu) SetLabel(lbl string) { w.label = lbl } func (w *Menu) SetFocusable(f bool) {} +func (w *Menu) GetItem(idx int) *MenuItem { + if len(w.items) > idx { + return w.items[idx] + } + return nil +} func (w *Menu) SetOnPressed(p func() bool) { w.onPressed = p } -func (w *Menu) AddItems(iL ...Widget) { +func (w *Menu) AddItems(iL ...*MenuItem) { var maxW int for i := range iL { if iL[i].WantW() > maxW { @@ -202,6 +208,24 @@ func (w *Menu) AddItems(iL ...Widget) { w.SetW(maxW) } +func (w *Menu) RemoveItems(iL ...*MenuItem) { + var wrk []*MenuItem + for i := range w.items { + var skip bool + for j := range iL { + if w.items[i] == iL[j] { + skip = true + break + } + } + if skip { + continue + } + wrk = append(wrk, w.items[i]) + } + w.items = wrk +} + func (w *Menu) MoveRight(ev *tcell.EventKey) bool { if w.menuType != MenuTypeH { return false diff --git a/menu_item.go b/menu_item.go index 3a1e910..e9926b4 100644 --- a/menu_item.go +++ b/menu_item.go @@ -36,12 +36,15 @@ type MenuItem struct { w, h int menuType MenuType + cursor int items []*MenuItem onPressed func() bool manualExpand bool expanded bool disabled bool + + keyMap KeyMap } func NewMenuItem(id string, style tcell.Style) *MenuItem { @@ -54,6 +57,21 @@ func (w *MenuItem) Init(id string, style tcell.Style) { w.id = id w.style = style w.visible = true + w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ + tcell.KeyUp: w.MoveUp, + tcell.KeyDown: w.MoveDown, + tcell.KeyEnter: func(ev *tcell.EventKey) bool { + if w.onPressed != nil { + return w.onPressed() + } else if len(w.items) > w.cursor && w.items[w.cursor].HandleKey(ev) { + return true + } + return false + }, + }) + for i := range w.items { + w.items[i].SetActive(i == w.cursor) + } } func (w *MenuItem) Id() string { return w.id } func (w *MenuItem) HandleResize(ev *tcell.EventResize) {} @@ -61,12 +79,8 @@ func (w *MenuItem) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false } - if ev.Key() == tcell.KeyEnter { - if w.onPressed != nil { - return w.onPressed() - } - } - return false + // Look for a sub-item that's selected + return w.keyMap.Handle(ev) } func (w *MenuItem) HandleTime(ev *tcell.EventTime) {} func (w *MenuItem) Draw(screen tcell.Screen) { @@ -77,10 +91,15 @@ func (w *MenuItem) Draw(screen tcell.Screen) { if w.active { st = w.style.Reverse(true) } + if w.disabled { + st = st.Dim(true) + st = st.Italic(true) + } x, y := w.x, w.y wd := w.w h.DrawText(x, y, h.PadR(w.label, wd), st, screen) + y += 1 if w.expanded { x += 2 for i := range w.items { @@ -140,10 +159,47 @@ func (w *MenuItem) Expand(e bool) { } } +func (w *MenuItem) updateActive() { + for i := range w.items { + w.items[i].SetActive(i == w.cursor) + } +} + +func (w *MenuItem) MoveUp(ev *tcell.EventKey) bool { + // Look for a previous enabled item + st := w.cursor + i := (w.cursor - 1 + len(w.items)) % len(w.items) + for ; i != st; i = (i - 1 + len(w.items)) % len(w.items) { + if !w.items[i].IsDisabled() { + break + } + } + w.cursor = i + w.updateActive() + return true +} + +func (w *MenuItem) MoveDown(ev *tcell.EventKey) bool { + // Look for next enabled item + st := w.cursor + i := (st + 1) % len(w.items) + for ; i != st; i = (i + 1) % len(w.items) { + if !w.items[i].IsDisabled() { + break + } + } + w.cursor = i + w.updateActive() + return true +} func (w *MenuItem) SetLabel(lbl string) { w.label = lbl } func (w *MenuItem) SetDisabled(d bool) { w.disabled = d } +func (w *MenuItem) IsDisabled() bool { return w.disabled } func (w *MenuItem) AddItems(iL ...*MenuItem) { w.items = append(w.items, iL...) + for i := range w.items { + w.items[i].SetActive(i == w.cursor) + } } func (w *MenuItem) SetOnPressed(p func() bool) { w.onPressed = p }