From e4405a8d6b04ac8b2a417ef6864929fb7e43e36f Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Thu, 21 Aug 2025 14:25:17 -0500 Subject: [PATCH] Some Updates --- layout_flags.go | 14 +++++ wdgt_button.go | 12 ++++- wdgt_field.go | 2 +- wdgt_linear_layout.go | 121 ++++++++++++++++++++++++------------------ wdgt_list.go | 3 -- 5 files changed, 95 insertions(+), 57 deletions(-) diff --git a/layout_flags.go b/layout_flags.go index d2e49f3..b60b4ee 100644 --- a/layout_flags.go +++ b/layout_flags.go @@ -43,11 +43,20 @@ const ( LFAlignV = LayoutFlag(0x1100) ) +const ( + LFAlignTopLeft = LayoutFlag(LFAlignHLeft | LFAlignVTop) +) + func (f LayoutFlag) Add(fl LayoutFlag) { f |= fl } func (f LayoutFlag) AlignH() LayoutFlag { return f & LFAlignH } func (f LayoutFlag) AlignV() LayoutFlag { return f & LFAlignV } func (f LayoutFlag) IsAlignH() bool { return f.AlignH() > 0 } func (f LayoutFlag) IsAlignV() bool { return f.AlignV() > 0 } +func (f LayoutFlag) SetTopLeft() { + f.ClearAll() + f = LFAlignHLeft | LFAlignVTop +} + func (f LayoutFlag) ClearAlignH() { f = f &^ LFAlignH f.Add(LFAlignHCenter) @@ -57,3 +66,8 @@ func (f LayoutFlag) ClearAlignV() { f = f &^ LFAlignV f.Add(LFAlignVCenter) } + +func (f LayoutFlag) ClearAll() { + f.ClearAlignH() + f.ClearAlignV() +} diff --git a/wdgt_button.go b/wdgt_button.go index dc5a0f4..0d37c67 100644 --- a/wdgt_button.go +++ b/wdgt_button.go @@ -41,6 +41,7 @@ type Button struct { tabbable bool onPressed func() bool + logger func(string, ...any) } var _ Widget = (*Button)(nil) @@ -75,6 +76,7 @@ func (w *Button) HandleKey(ev *tcell.EventKey) bool { return false } func (w *Button) HandleTime(ev *tcell.EventTime) {} + func (w *Button) Draw(screen tcell.Screen) { if !w.visible { return @@ -96,6 +98,7 @@ func (w *Button) Draw(screen tcell.Screen) { lbl = fmt.Sprintf("[%s]", wh.Center(lbl, w.w-2)) } wh.DrawText(w.x, w.y, lbl, dStyle, screen) + return case 2: lbl := w.label if w.w < len(lbl) { @@ -133,7 +136,7 @@ func (w *Button) SetPos(c Coord) { w.x, w.y = c.X, c.Y } func (w *Button) SetW(x int) { w.w = x } func (w *Button) SetH(y int) { w.h = y } func (w *Button) GetW() int { return w.w } -func (w *Button) GetH() int { return w.y } +func (w *Button) GetH() int { return w.h } func (w *Button) WantW() int { return 4 + len(w.label) } func (w *Button) WantH() int { return 3 } func (w *Button) SetSize(c Coord) { w.w, w.h = c.X, c.Y } @@ -145,3 +148,10 @@ func (w *Button) MinH() int { return 1 } func (w *Button) SetLabel(l string) { w.label = l } func (w *Button) SetOnPressed(p func() bool) { w.onPressed = p } + +func (w *Button) SetLogger(l func(string, ...any)) { w.logger = l } +func (w *Button) Log(txt string, args ...any) { + if w.logger != nil { + w.logger(txt, args...) + } +} diff --git a/wdgt_field.go b/wdgt_field.go index 46be307..e5edb0b 100644 --- a/wdgt_field.go +++ b/wdgt_field.go @@ -162,7 +162,7 @@ func (w *Field) SetSize(c Coord) { w.w, w.h = c.X, c.Y } func (w *Field) Focusable() bool { return true } func (w *Field) SetTabbable(b bool) { w.tabbable = b } func (w *Field) Tabbable() bool { return w.tabbable } -func (w *Field) MinW() int { return len(w.label) + 15 } +func (w *Field) MinW() int { return len(w.label) + wh.Max(15, len(w.value)) } func (w *Field) MinH() int { return 1 } /* Non-Widget-Interface Functions */ diff --git a/wdgt_linear_layout.go b/wdgt_linear_layout.go index dec175e..64ad495 100644 --- a/wdgt_linear_layout.go +++ b/wdgt_linear_layout.go @@ -22,6 +22,9 @@ THE SOFTWARE. package widgets import ( + "fmt" + "time" + wh "git.bullercodeworks.com/brian/tcell-widgets/helpers" "github.com/gdamore/tcell" ) @@ -40,7 +43,11 @@ type LinearLayout struct { layoutFlags map[Widget]LayoutFlag layoutWeights map[Widget]int totalWeight int - // Stacked makes the layout ignore weights and effectively shrink-wrap all fields + defFlags LayoutFlag // The default flags applied to widgets if nothing + // is in layoutFlags + + // Stacked makes the layout ignore weights and effectively shrink-wrap all + // fields stacked bool active bool @@ -48,7 +55,6 @@ type LinearLayout struct { tabbable bool disableTab bool - cursor int logger func(string, ...any) } @@ -72,6 +78,7 @@ func (w *LinearLayout) Init(id string, s tcell.Style) { w.style = s w.visible = true w.tabbable = true + w.defFlags = LayoutFlag(LFAlignHCenter | LFAlignVCenter) w.layoutFlags = make(map[Widget]LayoutFlag) w.layoutWeights = make(map[Widget]int) } @@ -83,30 +90,32 @@ func (w *LinearLayout) HandleResize(ev *tcell.EventResize) { } 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()) + if !w.active || w.disableTab { return false } - // 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 { - w.Log("LinearLayout<%s>.HandleKey: Widget<%s>.HandleKey", w.Id(), w.widgets[w.cursor].Id()) - if w.widgets[w.cursor].HandleKey(ev) { + active := w.findActive() + if active != nil { + if active.HandleKey(ev) { + w.Log("%s.HandleKey %s <- Consumed by %s", w.Id(), ev.Name(), active.Id()) return true } - w.Log("LinearLayout<%s>.HandleKey: Widget<%s>.HandleKey Returns", w.Id(), w.widgets[w.cursor].Id()) + w.Log("%s.HandleKey Not handled by active (%s)", w.Id(), active.Id()) } - - w.Log("LinearLayout<%s>.HandleKey: Checking for Tab", w.Id()) - if ev.Key() == tcell.KeyTab && !w.disableTab { - w.Log("LinearLayout<%s>.HandleKey: Handling Tab", w.Id()) - return w.handleTab() + if ev.Key() == tcell.KeyTab { + w.Log("%s-> Activating Next (current: %s)", w.Id(), w.findActive().Id()) + ret := w.activateNext() + if ret { + w.Log("%s<- Activated: %s", w.Id(), w.findActive().Id()) + } else { + w.Log("%s<- Nothing Activated", w.Id()) + } + return ret } return false } +func (w *LinearLayout) GetActive() Widget { return w.findActive() } func (w *LinearLayout) HandleTime(ev *tcell.EventTime) { for _, wi := range w.widgets { @@ -124,11 +133,8 @@ func (w *LinearLayout) Draw(screen tcell.Screen) { } } -func (w *LinearLayout) Active() bool { return w.active } -func (w *LinearLayout) SetActive(a bool) { - w.active = a - w.updateActive() -} +func (w *LinearLayout) Active() bool { return w.active } +func (w *LinearLayout) SetActive(a bool) { w.active = a } func (w *LinearLayout) Visible() bool { return w.visible } func (w *LinearLayout) SetVisible(a bool) { w.visible = a } func (w *LinearLayout) Focusable() bool { return true } @@ -204,13 +210,34 @@ func (w *LinearLayout) MinH() int { return minH } -// Find the widget at 'cursor' and set it active. -// All others to inactive -// If this layout is not active, everything is inactive -func (w *LinearLayout) updateActive() { - for i, wd := range w.widgets { - wd.SetActive(i == w.cursor && w.active) +// Find the currently active widget, there should be only one. +func (w *LinearLayout) findActive() Widget { + for i := range w.widgets { + if w.widgets[i].Active() { + return w.widgets[i] + } } + // Didn't find one, return the first + if len(w.widgets) > 0 { + return w.widgets[0] + } + return nil +} + +func (w *LinearLayout) activateNext() bool { + var found bool + for i := range w.widgets { + if found { + w.Log("%s.activeNext Setting Next Active: %s", w.Id(), w.widgets[i].Id()) + w.widgets[i].SetActive(true) + return true + } else if w.widgets[i].Active() { + found = true + w.widgets[i].SetActive(false) + } + } + w.Log("%s.activeNext Hit End", w.Id()) + return false } func (w *LinearLayout) SetOrientation(o LinearLayoutOrient) { w.orientation = o } @@ -323,6 +350,8 @@ func (w *LinearLayout) SetWeight(wd Widget, wt int) { w.updateTotalWeight() } +func (w *LinearLayout) SetDefaultFlags(f LayoutFlag) { w.defFlags = f } + func (w *LinearLayout) updateTotalWeight() { w.totalWeight = 0 for _, v := range w.layoutWeights { @@ -363,13 +392,7 @@ func (w *LinearLayout) updateLLVWidgetSize(wd Widget) { } } } - if wd.Id() == "campaignform.begindate" { - w.Log("Setting %s Size: %d,%d", wd.Id(), rW, w.getWeightedH(wd)) - } wd.HandleResize((&Coord{X: rW, Y: w.getWeightedH(wd)}).ResizeEvent()) - if wd.Id() == "campaignform.begindate" { - w.Log(" %s Size: %d,%d", wd.Id(), wd.GetW(), wd.GetH()) - } } func (w *LinearLayout) updateLLHWidgetSize(wd Widget) { @@ -410,7 +433,7 @@ func (w *LinearLayout) updateLLVWidgetPos(wd Widget) { if w.stacked { // If we're 'stacked', set things to top/left flgs = LayoutFlag(LFAlignHLeft | LFAlignVTop) } else { - flgs = LayoutFlag(LFAlignHCenter | LFAlignVCenter) + flgs = w.defFlags } } @@ -441,19 +464,19 @@ func (w *LinearLayout) updateLLHWidgetPos(wd Widget) { } break } - c.X += w.getWeightedW(w.widgets[i]) + c.X += w.getWeightedW(w.widgets[i]) + 2 } // Do we have a layout flag for this widget? var ok bool var flgs LayoutFlag if flgs, ok = w.layoutFlags[wd]; !ok { - flgs = LayoutFlag(LFAlignHCenter | LFAlignVCenter) + flgs = w.defFlags } // c.X is the left-most of this 'cell' if wd.GetW() < w.getWeightedW(wd) { - // But we've got some extra space. + // We have extra horizontal space. switch flgs.AlignH() { case LFAlignHRight: c.X += w.getWeightedW(wd) - wd.GetW() @@ -461,10 +484,15 @@ func (w *LinearLayout) updateLLHWidgetPos(wd Widget) { c.X += (w.getWeightedW(wd) / 2) - (wd.GetW() / 2) } } + c.Y = 0 if wd.GetW() < w.h { - c.Y = int((float64(w.h) / 2) - (float64(wd.GetH()) / 2)) - } else { - c.Y = 0 + // We have extra vertical space. + switch flgs.AlignV() { + case LFAlignVBottom: + c.Y = w.h - wd.GetH() + case LFAlignVCenter: + c.Y = int((float64(w.h) / 2) - (float64(wd.GetH()) / 2)) + } } wd.SetPos(c) } @@ -507,23 +535,12 @@ func (w *LinearLayout) getWeightedW(wd Widget) int { } } -func (w *LinearLayout) handleTab() bool { - w.Log("%s - Handling Tab", w.Id()) - beg := w.cursor - // Find the next tabbable widget - w.cursor = (w.cursor + 1) % len(w.widgets) - for !w.widgets[w.cursor].Tabbable() && beg != w.cursor { - w.cursor = (w.cursor + 1) % len(w.widgets) - } - w.updateActive() - 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) Log(txt string, args ...any) { if w.logger != nil { + txt = fmt.Sprintf("%s:%s", time.Now().Format(time.TimeOnly), txt) w.logger(txt, args...) } } diff --git a/wdgt_list.go b/wdgt_list.go index 38ca253..94b758b 100644 --- a/wdgt_list.go +++ b/wdgt_list.go @@ -91,9 +91,6 @@ func (w *List) Init(id string, style tcell.Style) { func (w *List) Id() string { return w.id } func (w *List) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() - // Fill as much space as we're given - // w.w = wh.Min(w.w, w.WantW()) - // w.h = wh.Min(w.h, w.WantH()) } func (w *List) HandleKey(ev *tcell.EventKey) bool {