Some Updates

This commit is contained in:
2025-10-02 12:12:52 -05:00
parent 998531f95d
commit 4b647d9d41
9 changed files with 150 additions and 24 deletions

View File

@@ -36,6 +36,11 @@ const (
AlignRight
)
// Return if r1 and r2 are the same, ignoring case
func RuneEqualsNC(r1, r2 rune) bool {
return unicode.ToLower(r1) == unicode.ToLower(r2)
}
func Center(txt string, width int) string {
if len(txt) >= width {
// Trim from beg & end until width

View File

@@ -58,6 +58,11 @@ func (f LayoutFlag) SetTopLeft() {
f = LFAlignHLeft | LFAlignVTop
}
func (f LayoutFlag) SetBottomCenter() {
f.ClearAll()
f = LFAlignHCenter | LFAlignVBottom
}
func (f LayoutFlag) ClearAlignH() {
f = f &^ LFAlignH
f.Add(LFAlignHCenter)

View File

@@ -28,6 +28,8 @@ type BlankWidget struct {
id string
x, y int
keyMap KeyMap
wantH, wantW int
}
var _ Widget = (*BlankWidget)(nil)
@@ -71,7 +73,10 @@ func (w *BlankWidget) SetW(wd int) {}
func (w *BlankWidget) SetH(h int) {}
func (w *BlankWidget) GetW() int { return 0 }
func (w *BlankWidget) GetH() int { return 0 }
func (w *BlankWidget) WantW() int { return 0 }
func (w *BlankWidget) WantH() int { return 0 }
func (w *BlankWidget) WantW() int { return w.wantW }
func (w *BlankWidget) WantH() int { return w.wantH }
func (w *BlankWidget) MinW() int { return 0 }
func (w *BlankWidget) MinH() int { return 0 }
func (w *BlankWidget) SetWantH(v int) { w.wantH = v }
func (w *BlankWidget) SetWantW(v int) { w.wantW = v }

View File

@@ -40,6 +40,7 @@ type Cli struct {
active bool
visible bool
focusable bool
minimized bool
title string
rawLog []string
@@ -77,6 +78,9 @@ func (w *Cli) Init(id string, s tcell.Style) {
func (w *Cli) Id() string { return w.id }
func (w *Cli) HandleResize(ev *tcell.EventResize) {
w.w, w.h = ev.Size()
if w.minimized {
w.h = 3
}
}
func (w *Cli) SetKeyMap(km KeyMap) { w.keyMap = km }
@@ -174,8 +178,6 @@ func (w *Cli) Draw(screen tcell.Screen) {
wh.DrawText(x, y, cursor, dStyle.Reverse(w.active), screen)
x += 1
wh.DrawText(x, y, wh.PadR(post, w.w-x-2), dStyle, screen)
// wh.DrawText(w.x, y+1, fmt.Sprintf("Index: %d", w.historyPosition), dStyle, screen)
// x += len(post) - 1
}
func (w *Cli) Active() bool { return w.active }
@@ -196,7 +198,13 @@ func (w *Cli) SetW(wd int) { w.w = wd }
func (w *Cli) SetH(h int) { w.h = h }
func (w *Cli) SetSize(c Coord) { w.w, w.h = c.X, c.Y }
func (w *Cli) WantW() int { return wh.MaxInt }
func (w *Cli) WantH() int { return wh.MaxInt }
func (w *Cli) WantH() int {
if w.minimized {
return 3
} else {
return wh.MaxInt
}
}
func (w *Cli) MinW() int { return 20 }
func (w *Cli) MinH() int { return 6 }
@@ -341,6 +349,9 @@ func (w *Cli) Clear() {
w.log = []string{}
}
func (w *Cli) SetMinimized(m bool) { w.minimized = m }
func (w *Cli) IsMinimized() bool { return w.minimized }
type cliCommand func(args ...string) bool
// TODO

View File

@@ -42,7 +42,7 @@ type Field struct {
x, y int
w, h int
filter func(*tcell.EventKey) bool
filter func(tcell.EventKey) bool
onChange func(prev, curr string)
keyMap KeyMap
@@ -60,9 +60,9 @@ func (w *Field) Init(id string, style tcell.Style) {
w.id = id
w.style = style
w.visible = true
w.filter = func(ev *tcell.EventKey) bool {
return wh.IsBS(*ev) ||
wh.KeyIsDisplayable(*ev)
w.filter = func(ev tcell.EventKey) bool {
return wh.IsBS(ev) ||
wh.KeyIsDisplayable(ev)
}
w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{
tcell.KeyLeft: w.handleLeft,
@@ -98,7 +98,7 @@ func (w *Field) HandleKey(ev *tcell.EventKey) bool {
if ok := w.keyMap.Handle(ev); ok {
return true
}
if w.filter != nil && !w.filter(ev) {
if w.filter != nil && !w.filter(*ev) {
return false
}
if ev.Key() == tcell.KeyRune {
@@ -238,3 +238,17 @@ func (w *Field) doOnChange(prev, curr string) {
w.onChange(prev, curr)
}
}
func (w *Field) SetFilter(f func(ev tcell.EventKey) bool) {
w.filter = func(ev tcell.EventKey) bool {
// We always want to make sure we allow backspace.
if wh.IsBS(ev) {
return true
}
// We also always want to make sure it's displayable
if !wh.KeyIsDisplayable(ev) {
return false
}
return f(ev)
}
}

View File

@@ -275,6 +275,12 @@ func (w *LinearLayout) findActiveOrFirst() Widget {
return nil
}
func (w *LinearLayout) ActivateWidget(n Widget) {
for i := range w.widgets {
w.widgets[i].SetActive(w.widgets[i] == n)
}
}
func (w *LinearLayout) ActivateNext() bool {
var found bool
for i := range w.widgets {

View File

@@ -140,6 +140,7 @@ func (w *Menu) HandleKey(ev *tcell.EventKey) bool {
} else if w.items[w.cursor].HandleKey(ev) {
// Active menuitem consumes this event
if ev.Key() == tcell.KeyEnter {
w.cursor = 0
w.SetActive(false)
}
return true
@@ -148,6 +149,21 @@ func (w *Menu) HandleKey(ev *tcell.EventKey) bool {
return true
}
// See if we can find an item that matches the key pressed
for i := range w.items {
if wh.RuneEqualsNC(ev.Rune(), w.items[i].GetHotKey()) {
if w.items[i].Active() {
if w.items[i].HandleKey(ev) {
// Reset the cursor
w.cursor = 0
w.SetActive(false)
return true
}
}
w.cursor = i
// w.items[i].SetActive(true)
return true
}
}
return false
}
@@ -178,9 +194,11 @@ func (w *Menu) drawHMenu(screen tcell.Screen) {
}
x += 2
if w.expanded || (w.active && !w.manualExpand) {
// pos := w.GetPos()
// TODO: Use DrawOffset?
for i := range w.items {
w.items[i].SetActive(w.active && w.cursor == i)
// pos.DrawOffset(w.items[i], screen)
w.items[i].SetPos(Coord{X: x, Y: y})
w.items[i].Draw(screen)
x += len(w.items[i].Label()) + 2
@@ -371,8 +389,9 @@ func (w *Menu) MoveDown(ev *tcell.EventKey) bool {
return true
}
func (w *Menu) CreateMenuItem(lbl string, do func() bool, subItems ...*MenuItem) *MenuItem {
func (w *Menu) CreateMenuItem(lbl string, do func() bool, hotKey rune, subItems ...*MenuItem) *MenuItem {
d := NewMenuItem(fmt.Sprintf("menuitem-%s", lbl), tcell.StyleDefault)
d.SetHotKey(hotKey)
d.SetLabel(lbl)
d.SetOnPressed(do)
if len(subItems) > 0 {

View File

@@ -29,6 +29,7 @@ import (
"github.com/gdamore/tcell"
)
// TODO: Sub-Menus
type MenuItem struct {
id string
label string
@@ -44,6 +45,8 @@ type MenuItem struct {
items []*MenuItem
onPressed func() bool
hotKey rune
manualExpand bool
expanded bool
disabled bool
@@ -101,6 +104,20 @@ func (w *MenuItem) HandleKey(ev *tcell.EventKey) bool {
if !w.active {
return false
}
// If the user hits the hotkey of this active item, call 'Enter' on it
if wh.RuneEqualsNC(ev.Rune(), w.hotKey) {
w.HandleKey(tcell.NewEventKey(tcell.KeyEnter, 0, 0))
}
for i := range w.items {
if wh.RuneEqualsNC(ev.Rune(), w.items[i].GetHotKey()) {
if w.items[i].Active() {
return w.items[i].HandleKey(ev)
}
w.cursor = i
w.updateActive()
return true
}
}
// Look for a sub-item that's selected
return w.keyMap.Handle(ev)
}
@@ -116,18 +133,30 @@ func (w *MenuItem) Draw(screen tcell.Screen) {
return
}
st := w.style.Dim(w.disabled).Italic(w.disabled)
// st := w.style.Reverse(w.active).Dim(w.disabled).Italic(w.disabled)
x, y := w.x, w.y
// wd := w.w
fndHotKey := w.GetHotKey() != 0
if w.expanded {
if len(w.items) > 0 {
wh.DrawText(x, y, fmt.Sprintf("╭%s╮", w.label), st, screen)
wh.DrawText(x+1, y, fmt.Sprintf("%s", w.label), st.Reverse(true), screen)
y += 1
if len(w.items) > 0 {
wh.TitledBorderFilled(w.x-1, y, w.x+w.WantW(), y+w.WantH(), fmt.Sprintf("╯%s╰", strings.Repeat(" ", len(w.label))), wh.BRD_CSIMPLE, w.style, screen)
screen.SetContent(x, y, '╭', nil, st)
x += 1
for i := range w.label {
if fndHotKey && wh.RuneEqualsNC(rune(w.label[i]), w.hotKey) {
screen.SetContent(x, y, rune(w.label[i]), nil, st.Reverse(true).Underline(true))
fndHotKey = false
} else {
screen.SetContent(x, y, rune(w.label[i]), nil, st.Reverse(true))
}
x += 1
}
screen.SetContent(x, y, '╮', nil, st)
x = w.x
y += 1
if len(w.items) == 0 {
return
}
wh.BorderFilled(w.x-1, y, w.x+w.WantW(), y+w.WantH(), wh.BRD_CSIMPLE, w.style, screen)
wh.DrawText(w.x, y, fmt.Sprintf("╯%s╰", strings.Repeat(" ", len(w.label))), w.style, screen)
x += 1
y += 1
// pos := w.GetPos()
for i := range w.items {
@@ -138,10 +167,32 @@ func (w *MenuItem) Draw(screen tcell.Screen) {
y++
}
} else {
wh.DrawText(x, y, fmt.Sprintf(" %s ", w.label), st.Reverse(true), screen)
screen.SetContent(x, y, ' ', nil, st)
x += 1
for i := range w.label {
if fndHotKey && wh.RuneEqualsNC(rune(w.label[i]), w.hotKey) {
screen.SetContent(x, y, rune(w.label[i]), nil, st.Reverse(true).Underline(true))
fndHotKey = false
} else {
screen.SetContent(x, y, rune(w.label[i]), nil, st.Reverse(true))
}
x += 1
}
screen.SetContent(x, y, ' ', nil, st)
}
} else {
wh.DrawText(x, y, fmt.Sprintf(" %s ", w.label), st, screen)
screen.SetContent(x, y, ' ', nil, st)
x += 1
for i := range w.label {
if fndHotKey && wh.RuneEqualsNC(rune(w.label[i]), w.hotKey) {
screen.SetContent(x, y, rune(w.label[i]), nil, st.Underline(true))
fndHotKey = false
} else {
screen.SetContent(x, y, rune(w.label[i]), nil, st)
}
x += 1
}
screen.SetContent(x, y, ' ', nil, st)
}
}
@@ -167,7 +218,7 @@ func (w *MenuItem) GetH() int { return w.y }
func (w *MenuItem) MinW() int { return w.w }
func (w *MenuItem) MinH() int { return w.y }
func (w *MenuItem) WantW() int {
ret := len(w.label) + 2
ret := len(w.label) + 4
if len(w.items) > 0 {
for i := range w.items {
if w.items[i].WantW() > ret {
@@ -263,3 +314,6 @@ func (w *MenuItem) FindItem(id string) *MenuItem {
}
return nil
}
func (w *MenuItem) SetHotKey(r rune) { w.hotKey = r }
func (w *MenuItem) GetHotKey() rune { return w.hotKey }

View File

@@ -220,10 +220,11 @@ func (w *TopMenuLayout) Log(txt string, args ...any) {
}
}
func (w *TopMenuLayout) CreateMenuItem(id, lbl string, do func() bool, subItems ...*MenuItem) *MenuItem {
func (w *TopMenuLayout) CreateMenuItem(id, lbl string, do func() bool, hotKey rune, subItems ...*MenuItem) *MenuItem {
d := NewMenuItem(id, tcell.StyleDefault)
d.SetLabel(lbl)
d.SetOnPressed(do)
d.SetHotKey(hotKey)
if len(subItems) > 0 {
d.AddItems(subItems...)
}
@@ -247,3 +248,9 @@ func (w *TopMenuLayout) SetItemDisabled(id string, d bool) bool {
}
func (w *TopMenuLayout) FindItem(id string) *MenuItem { return w.menu.FindItem(id) }
func (w *TopMenuLayout) CloseMenu() {
if w.menu != nil && w.menu.Active() {
w.menu.SetActive(false)
w.widget.SetActive(true)
}
}