A few fixes

This commit is contained in:
2025-08-18 15:49:03 -05:00
parent 061bf1b37d
commit 599554a798
7 changed files with 117 additions and 117 deletions

View File

@@ -92,7 +92,7 @@ func (ui *Ui) run() error {
case *tcell.EventKey: case *tcell.EventKey:
if ev.Key() == tcell.KeyCtrlC { if ev.Key() == tcell.KeyCtrlC {
ui.stop() ui.Exit()
return nil return nil
} }
ui.screen.HandleKey(ev) ui.screen.HandleKey(ev)
@@ -115,7 +115,7 @@ func (ui *Ui) SetScreen(scr *UiScreen) {
ui.screen.HandleResize(tcell.NewEventResize(ui.GetSize())) ui.screen.HandleResize(tcell.NewEventResize(ui.GetSize()))
} }
func (ui *Ui) stop() { ui.running = false } func (ui *Ui) Exit() { ui.running = false }
func (ui *Ui) cleanup() { ui.tScreen.Fini() } func (ui *Ui) cleanup() { ui.tScreen.Fini() }
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

View File

@@ -30,9 +30,11 @@ type UiScreen struct {
ui *Ui ui *Ui
w, h int w, h int
widget w.Widget widget w.Widget
ll *w.LinearLayout ll *w.LinearLayout
log *w.Cli log *w.Cli
alert *w.Alert
alertLayout *w.LinearLayout
widgets []w.Widget widgets []w.Widget
cursor int cursor int
@@ -46,88 +48,25 @@ func (s *UiScreen) Init(ui *Ui) {
s.ll = w.NewLinearLayout("top", ui.style) s.ll = w.NewLinearLayout("top", ui.style)
s.ll.SetLogger(s.log.Log) s.ll.SetLogger(s.log.Log)
hl := w.NewLinearLayout("dat", ui.style) tf := w.NewTimeField("time-field", ui.style)
hl.SetOrientation(w.LinLayH) tf.SetLabel("Time")
hl.SetLogger(s.log.Log) s.ll.Add(tf)
searcher := w.NewSearcher("test.searcher", ui.style)
searcher.SetLogger(s.log.Log)
searcher.SetTitle("Test Searcher")
var dat []string
for i := 0; i < 1000; i++ {
dat = append(dat, RandomString(15))
}
searcher.SetData(dat)
searcher.SetSelectFunc(func(idx int, v string) bool {
s.log.Log("Searcher Value Selected: %s", v)
return true
})
searcher.SetH(10)
hl.Add(searcher)
db := w.NewButton("test-bd-1", ui.style)
db.SetLabel("Button 1")
db.SetOnPressed(func() bool {
s.log.Log("Button 1 Pressed")
return true
})
hl.Add(db)
db = w.NewButton("test-bd-2", ui.style)
db.SetLabel("Button 2")
db.SetOnPressed(func() bool {
s.log.Log("Button 2 Pressed")
return true
})
hl.Add(db)
s.ll.Add(hl)
btnL := w.NewLinearLayout("test.btnll", ui.style)
btnL.SetTabbable(true)
btnL.SetLogger(s.log.Log)
btnL.SetOrientation(w.LinLayH)
btnCancel := w.NewButton("btn-cancel", ui.style)
btnCancel.SetLabel("Cancel")
btnCancel.SetOnPressed(func() bool {
s.log.Log("Cancel Pressed")
return true
})
btnL.Add(btnCancel)
btnL.AddFlag(btnCancel, w.LFAlignVBottom)
btnOk := w.NewButton("btn-ok", ui.style)
btnOk.SetLabel("Ok")
btnOk.SetOnPressed(func() bool {
s.log.Log("Ok Pressed")
return true
})
btnL.Add(btnOk)
btnL.AddFlag(btnOk, w.LFAlignVBottom)
s.ll.Add(btnL)
s.ll.AddFlag(btnL, w.LFAlignVBottom)
ml := w.NewTopMenuLayout("menu", ui.style) ml := w.NewTopMenuLayout("menu", ui.style)
m := ml.Menu() m := ml.Menu()
m.SetLabel("Widget Example") m.SetLabel("Widget Example")
ml.AddMenuItems( ml.AddMenuItems(
m.CreateMenuItem("File", nil, m.CreateMenuItem("File", nil,
m.CreateMenuItem("Exit", func() bool { m.CreateMenuItem("Exit", func() bool {
s.ui.stop() ui.Exit()
return true return true
}), }),
), ),
) )
ml.SetLogger(s.log.Log) ml.SetLogger(s.log.Log)
ml.SetWidget(s.ll) ml.SetWidget(s.ll)
ml.SetActive(true) ml.SetActive(true)
s.widget = ml s.widget = ml
/*
dw := w.NewDebugWidget("debug", ui.style)
dw.SetWidget(ll)
dw.SetSize(w.Coord{X: 20, Y: 30})
dw.SetActive(true)
s.widget = dw
*/
s.ll.Add(s.log) s.ll.Add(s.log)
// s.widgets = append(s.widgets, ml) // s.widgets = append(s.widgets, ml)

View File

@@ -41,6 +41,7 @@ type Alert struct {
layout *LinearLayout layout *LinearLayout
title string title string
message *Text message *Text
btnLayout *LinearLayout
btnOk, btnCancel *Button btnOk, btnCancel *Button
keyMap KeyMap keyMap KeyMap
@@ -64,18 +65,18 @@ func (w *Alert) Init(id string, style tcell.Style) {
w.message = NewText(fmt.Sprintf("%s-text", id), style) w.message = NewText(fmt.Sprintf("%s-text", id), style)
w.layout.Add(w.message) w.layout.Add(w.message)
btnLayout := NewLinearLayout("alertbtn-layout", tcell.StyleDefault) w.btnLayout = NewLinearLayout("alertbtn-layout", tcell.StyleDefault)
btnLayout.SetOrientation(LinLayH) w.btnLayout.SetOrientation(LinLayH)
w.layout.Add(btnLayout) w.layout.Add(w.btnLayout)
w.btnCancel = NewButton(fmt.Sprintf("%s-cancel", id), style) w.btnCancel = NewButton(fmt.Sprintf("%s-cancel", id), style)
w.btnCancel.SetLabel("Cancel") w.btnCancel.SetLabel("Cancel")
btnLayout.Add(w.btnCancel) w.btnLayout.Add(w.btnCancel)
w.btnOk = NewButton(fmt.Sprintf("%s-select", id), style) w.btnOk = NewButton(fmt.Sprintf("%s-select", id), style)
w.btnOk.SetLabel("Ok") w.btnOk.SetLabel("Ok")
w.btnOk.SetActive(true) w.btnOk.SetActive(true)
btnLayout.Add(w.btnOk) w.btnLayout.Add(w.btnOk)
w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{
tcell.KeyTab: w.SelectNext, tcell.KeyTab: w.SelectNext,
@@ -90,9 +91,14 @@ func (w *Alert) Init(id string, style tcell.Style) {
func (w *Alert) Id() string { return w.id } func (w *Alert) Id() string { return w.id }
func (w *Alert) HandleResize(ev *tcell.EventResize) { func (w *Alert) HandleResize(ev *tcell.EventResize) {
w.w, w.h = ev.Size() w.w, w.h = ev.Size()
// w.w = wh.Min(w.w, w.WantW()) if w.w > w.MinW() {
// w.h = wh.Min(w.h, w.WantH()) w.w = w.MinW()
}
if w.h > w.MinH() {
w.h = w.MinH()
}
// Trim space for the borders and pass on the size to the layout // Trim space for the borders and pass on the size to the layout
w.layout.SetPos(Coord{X: 1, Y: 1})
w.layout.HandleResize(tcell.NewEventResize(w.w-2, w.h-2)) w.layout.HandleResize(tcell.NewEventResize(w.w-2, w.h-2))
} }
@@ -107,13 +113,10 @@ func (w *Alert) Draw(screen tcell.Screen) {
if !w.visible { if !w.visible {
return return
} }
dS := w.style dS := w.style.Dim(!w.active)
if !w.active {
dS = dS.Dim(true)
}
wh.TitledBorderFilled(w.x, w.y, w.x+w.w, w.y+w.h, w.title, wh.BRD_SIMPLE, w.style, screen) wh.TitledBorderFilled(w.x, w.y, w.x+w.w, w.y+w.h, w.title, wh.BRD_SIMPLE, dS, screen)
w.GetPos().DrawOffset(w, screen) w.GetPos().DrawOffset(w.layout, screen)
} }
func (w *Alert) Active() bool { return w.active } func (w *Alert) Active() bool { return w.active }
@@ -144,12 +147,12 @@ func (w *Alert) WantH() int {
// Borders + Buttons // Borders + Buttons
func (w *Alert) MinW() int { func (w *Alert) MinW() int {
return 2 + wh.Max(w.message.MinW(), (w.btnOk.MinW()+w.btnCancel.MinW())) return 5 + wh.Max(w.message.MinW(), (w.btnOk.MinW()+w.btnCancel.MinW()))
} }
// Borders + Buttons + 2 lines for message // Borders + Buttons + 2 lines for message
func (w *Alert) MinH() int { func (w *Alert) MinH() int {
return 2 + w.message.MinH() + w.btnOk.MinH() return 5 + w.message.MinH() + w.btnOk.MinH()
} }
func (w *Alert) SetTitle(ttl string) { w.title = ttl } func (w *Alert) SetTitle(ttl string) { w.title = ttl }
@@ -180,6 +183,7 @@ func (w *Alert) Do(ev *tcell.EventKey) bool {
func (w *Alert) SetLogger(l func(string, ...any)) { func (w *Alert) SetLogger(l func(string, ...any)) {
w.logger = l w.logger = l
w.layout.SetLogger(l) w.layout.SetLogger(l)
w.btnLayout.SetLogger(l)
} }
func (w *Alert) Log(txt string, args ...any) { func (w *Alert) Log(txt string, args ...any) {

View File

@@ -40,6 +40,8 @@ type LinearLayout struct {
layoutFlags map[Widget]LayoutFlag layoutFlags map[Widget]LayoutFlag
layoutWeights map[Widget]int layoutWeights map[Widget]int
totalWeight int totalWeight int
// Stacked makes the layout ignore weights and effectively shrink-wrap all fields
stacked bool
active bool active bool
visible bool visible bool
@@ -81,14 +83,25 @@ func (w *LinearLayout) HandleResize(ev *tcell.EventResize) {
} }
func (w *LinearLayout) HandleKey(ev *tcell.EventKey) bool { func (w *LinearLayout) HandleKey(ev *tcell.EventKey) bool {
w.Log("LinearLayout<%s>.HandleKey", w.Id())
if !w.active {
w.Log("LinearLayout<%s>.HandleKey - NOPE. Not Active.", w.Id())
return false
}
// First, see if the active widget handles the key // First, see if the active widget handles the key
w.Log("LinearLayout<%s>.HandleKey: Checking Active Widget...", w.Id())
if len(w.widgets) > w.cursor { if len(w.widgets) > w.cursor {
w.Log("LinearLayout<%s>.HandleKey: Widget<%s>.HandleKey", w.Id(), w.widgets[w.cursor].Id())
if w.widgets[w.cursor].HandleKey(ev) { if w.widgets[w.cursor].HandleKey(ev) {
return true return true
} }
w.Log("LinearLayout<%s>.HandleKey: Widget<%s>.HandleKey Returns", w.Id(), w.widgets[w.cursor].Id())
} }
w.Log("LinearLayout<%s>.HandleKey: Checking for Tab", w.Id())
if ev.Key() == tcell.KeyTab && !w.disableTab { if ev.Key() == tcell.KeyTab && !w.disableTab {
w.Log("LinearLayout<%s>.HandleKey: Handling Tab", w.Id())
return w.handleTab() return w.handleTab()
} }
@@ -214,6 +227,18 @@ func (w *LinearLayout) Contains(n Widget) bool {
return w.IndexOf(n) >= 0 return w.IndexOf(n) >= 0
} }
func (w *LinearLayout) Replace(n, with Widget) {
idx := w.IndexOf(n)
if idx == -1 {
// 'n' isn't in layout. Bail out.
return
}
pFlags, pWeight := w.layoutFlags[n], w.layoutWeights[n]
w.Delete(n)
w.Insert(with, idx)
w.layoutFlags[with], w.layoutWeights[with] = pFlags, pWeight
}
func (w *LinearLayout) Add(n Widget) { func (w *LinearLayout) Add(n Widget) {
if w.Contains(n) { if w.Contains(n) {
// If the widget is already in the layout, move it to the end // If the widget is already in the layout, move it to the end
@@ -327,6 +352,9 @@ func (w *LinearLayout) updateWidgetLayouts() {
// We need to determine the allowed size of this widget so we can determine // We need to determine the allowed size of this widget so we can determine
// it's position // it's position
func (w *LinearLayout) updateLLVWidgetSize(wd Widget) { func (w *LinearLayout) updateLLVWidgetSize(wd Widget) {
if w.stacked {
return
}
rW := w.w rW := w.w
if wd == w.widgets[len(w.widgets)-1] { if wd == w.widgets[len(w.widgets)-1] {
wrk := float64(w.w) / float64(w.totalWeight) wrk := float64(w.w) / float64(w.totalWeight)
@@ -338,6 +366,9 @@ func (w *LinearLayout) updateLLVWidgetSize(wd Widget) {
} }
func (w *LinearLayout) updateLLHWidgetSize(wd Widget) { func (w *LinearLayout) updateLLHWidgetSize(wd Widget) {
if w.stacked {
return
}
rH := w.h rH := w.h
if wd == w.widgets[len(w.widgets)-1] { if wd == w.widgets[len(w.widgets)-1] {
wrk := float64(w.h) / float64(w.totalWeight) wrk := float64(w.h) / float64(w.totalWeight)
@@ -461,6 +492,7 @@ func (w *LinearLayout) getWeightedW(wd Widget) int {
} }
func (w *LinearLayout) handleTab() bool { func (w *LinearLayout) handleTab() bool {
w.Log("%s - Handling Tab", w.Id())
beg := w.cursor beg := w.cursor
// Find the next tabbable widget // Find the next tabbable widget
w.cursor = (w.cursor + 1) % len(w.widgets) w.cursor = (w.cursor + 1) % len(w.widgets)
@@ -471,6 +503,8 @@ func (w *LinearLayout) handleTab() bool {
return w.cursor > 0 && w.cursor != beg return w.cursor > 0 && w.cursor != beg
} }
func (w *LinearLayout) SetStacked(s bool) { w.stacked = s }
func (w *LinearLayout) SetLogger(l func(string, ...any)) { w.logger = l } func (w *LinearLayout) SetLogger(l func(string, ...any)) { w.logger = l }
func (w *LinearLayout) Log(txt string, args ...any) { func (w *LinearLayout) Log(txt string, args ...any) {
if w.logger != nil { if w.logger != nil {

View File

@@ -58,8 +58,6 @@ func (w *Text) Init(id string, style tcell.Style) {
func (w *Text) Id() string { return w.id } func (w *Text) Id() string { return w.id }
func (w *Text) HandleResize(ev *tcell.EventResize) { func (w *Text) HandleResize(ev *tcell.EventResize) {
w.w, w.h = ev.Size() w.w, w.h = ev.Size()
// w.w = wh.Min(w.w, w.WantW())
// w.h = wh.Min(w.h, w.WantH())
} }
func (w *Text) HandleKey(ev *tcell.EventKey) bool { return false } func (w *Text) HandleKey(ev *tcell.EventKey) bool { return false }
func (w *Text) HandleTime(ev *tcell.EventTime) {} func (w *Text) HandleTime(ev *tcell.EventTime) {}
@@ -69,7 +67,7 @@ func (w *Text) Draw(screen tcell.Screen) {
} }
y := w.y y := w.y
for i := range w.message { for i := range w.message {
wh.DrawText(w.x-(len(w.message[i])/2), y, w.message[i], w.style, screen) wh.DrawText(w.x+(w.w/2)-(len(w.message[i])/2), y, w.message[i], w.style, screen)
y++ y++
} }
} }

View File

@@ -40,6 +40,9 @@ type TimeField struct {
x, y int x, y int
w, h int w, h int
yr, mo, dy int
hr, mn, sc int
value time.Time value time.Time
hasDate bool hasDate bool
hasTime bool hasTime bool
@@ -75,21 +78,34 @@ func (w *TimeField) Init(id string, style tcell.Style) {
tcell.KeyHome: w.handleHome, tcell.KeyHome: w.handleHome,
tcell.KeyEnd: w.handleEnd, tcell.KeyEnd: w.handleEnd,
}) })
w.visible = true
w.tabbable = true w.tabbable = true
} }
func (w *TimeField) Id() string { return w.id } func (w *TimeField) Id() string { return w.id }
func (w *TimeField) HandleResize(ev *tcell.EventResize) { func (w *TimeField) HandleResize(ev *tcell.EventResize) {
w.w, w.h = ev.Size() w.w, w.h = ev.Size()
if w.w > w.WantW() {
// w.w = wh.Min(w.w, w.WantW()) w.w = w.WantW()
// w.h = wh.Min(w.h, w.WantH()) }
if w.h > w.WantH() {
w.h = w.WantH()
}
} }
func (w *TimeField) HandleKey(ev *tcell.EventKey) bool { func (w *TimeField) HandleKey(ev *tcell.EventKey) bool {
if !w.active { if !w.active {
return false return false
} }
switch w.cursor {
case 0: // year
case 1: // month
case 2: // day
case 3: // hour
case 4: // minute
case 5: // second
}
return false return false
} }
func (w *TimeField) HandleTime(ev *tcell.EventTime) {} func (w *TimeField) HandleTime(ev *tcell.EventTime) {}
@@ -97,31 +113,33 @@ func (w *TimeField) Draw(screen tcell.Screen) {
if !w.visible { if !w.visible {
return return
} }
ds := w.style dS := w.style.Dim(!w.active)
if !w.active {
ds = ds.Dim(true) // pos := w.GetPos()
}
x := w.x x := w.x
y := w.y
labelW := len(w.label) labelW := len(w.label)
if labelW > 0 { if labelW > 0 {
wh.DrawText(w.x, w.y, w.label+": ", ds, screen) wh.TitledBorderFilled(w.x, w.y, w.x+w.w, w.y+w.h, w.label, wh.BRD_SIMPLE, dS, screen)
x = x + labelW + 2 } else {
wh.BorderFilled(w.x, w.y, w.x+w.w, w.y+w.h, wh.BRD_SIMPLE, dS, screen)
} }
x, y = x+1, y+1
if w.hasDate { if w.hasDate {
yr, mo, dy := w.value.Year(), w.value.Month(), w.value.Day() yr, mo, dy := w.value.Year(), w.value.Month(), w.value.Day()
for idx, vl := range fmt.Sprintf("%4d%2d%2d", yr, mo, dy) { for idx, vl := range fmt.Sprintf("%4d%2d%2d", yr, mo, dy) {
if idx == 4 || idx == 7 { if idx == 4 || idx == 7 {
wh.DrawText(x, w.y, "-", ds, screen) wh.DrawText(x, y, "-", dS, screen)
x++ x++
} }
if idx == w.cursor && !w.nowBtnActive { if idx == w.cursor && !w.nowBtnActive {
if w.active { if w.active {
wh.DrawText(x, w.y, string(vl), ds.Reverse(true).Blink(true), screen) wh.DrawText(x, y, string(vl), dS.Reverse(true).Blink(true), screen)
} else { } else {
wh.DrawText(x, w.y, string(vl), ds, screen) wh.DrawText(x, y, string(vl), dS, screen)
} }
} else { } else {
wh.DrawText(x, w.y, string(vl), ds, screen) wh.DrawText(x, y, string(vl), dS, screen)
} }
x++ x++
} }
@@ -134,26 +152,27 @@ func (w *TimeField) Draw(screen tcell.Screen) {
} }
for idx, vl := range txt { for idx, vl := range txt {
if idx == 2 || idx == 5 { if idx == 2 || idx == 5 {
wh.DrawText(x, w.y, ":", ds, screen) wh.DrawText(x, y, ":", dS, screen)
x++ x++
} }
if idx+8 == w.cursor && !w.nowBtnActive { if idx+8 == w.cursor && !w.nowBtnActive {
if w.active { if w.active {
wh.DrawText(x, w.y, string(vl), ds.Reverse(true).Blink(true), screen) wh.DrawText(x, y, string(vl), dS.Reverse(true).Blink(true), screen)
} else { } else {
wh.DrawText(x, w.y, string(vl), ds, screen) wh.DrawText(x, y, string(vl), dS, screen)
} }
} else { } else {
wh.DrawText(x, w.y, string(vl), ds, screen) wh.DrawText(x, y, string(vl), dS, screen)
} }
x++ x++
} }
} }
if w.showNowBtn { if w.showNowBtn {
x, y = x+w.w-8, y+1
if w.nowBtnActive { if w.nowBtnActive {
wh.DrawText(x, w.y, "[ Now ]", ds.Reverse(true), screen) wh.DrawText(x, y, "[ Now ]", dS.Reverse(true), screen)
} else { } else {
wh.DrawText(x, w.y, "[ Now ]", ds, screen) wh.DrawText(x, y, "[ Now ]", dS, screen)
} }
} }
} }
@@ -191,9 +210,9 @@ func (w *TimeField) WantW() int {
wdt += 5 // hh:mm wdt += 5 // hh:mm
} }
} }
return wdt return wdt + 2
} }
func (w *TimeField) WantH() int { return 1 } func (w *TimeField) WantH() int { return 3 }
func (w *TimeField) MinW() int { func (w *TimeField) MinW() int {
wdt := 0 wdt := 0
if w.hasDate { if w.hasDate {
@@ -209,9 +228,9 @@ func (w *TimeField) MinW() int {
wdt += 5 // hh:mm wdt += 5 // hh:mm
} }
} }
return wdt return wdt + 2
} }
func (w *TimeField) MinH() int { return 1 } func (w *TimeField) MinH() int { return 3 }
func (w *TimeField) SetLabel(lbl string) { w.label = lbl } func (w *TimeField) SetLabel(lbl string) { w.label = lbl }
func (w *TimeField) SetTime(tm time.Time) { w.value = tm } func (w *TimeField) SetTime(tm time.Time) { w.value = tm }

View File

@@ -62,6 +62,8 @@ func (w *TopMenuLayout) Init(id string, s tcell.Style) {
w.menu.SetActive(false) w.menu.SetActive(false)
w.menu.SetType(MenuTypeH) w.menu.SetType(MenuTypeH)
w.menu.SetTabbable(false) w.menu.SetTabbable(false)
w.widget = NewBlankWidget("blank")
} }
func (w *TopMenuLayout) Id() string { return w.id } func (w *TopMenuLayout) Id() string { return w.id }
@@ -84,6 +86,9 @@ func (w *TopMenuLayout) HandleKey(ev *tcell.EventKey) bool {
} }
if ev.Key() == tcell.KeyEscape && w.menu != nil { if ev.Key() == tcell.KeyEscape && w.menu != nil {
w.menu.SetActive(!w.menu.Active()) w.menu.SetActive(!w.menu.Active())
if w.widget != nil {
w.widget.SetActive(!w.menu.Active())
}
return true return true
} }
if w.menu.Active() { if w.menu.Active() {
@@ -92,6 +97,7 @@ func (w *TopMenuLayout) HandleKey(ev *tcell.EventKey) bool {
// Pass the key through to the main widget // Pass the key through to the main widget
if w.widget != nil { if w.widget != nil {
w.Log(" Passing Key to Main Widget")
return w.widget.HandleKey(ev) return w.widget.HandleKey(ev)
} }
return false return false
@@ -106,12 +112,12 @@ func (w *TopMenuLayout) Draw(screen tcell.Screen) {
if !w.visible { if !w.visible {
return return
} }
if w.menu != nil {
w.GetPos().DrawOffset(w.menu, screen)
}
if w.widget != nil { if w.widget != nil {
w.GetPos().DrawOffset(w.widget, screen) w.GetPos().DrawOffset(w.widget, screen)
} }
if w.menu != nil {
w.GetPos().DrawOffset(w.menu, screen)
}
} }
func (w *TopMenuLayout) Active() bool { return w.active } func (w *TopMenuLayout) Active() bool { return w.active }