diff --git a/keymap.go b/keymap.go index 3946fce..799b7dd 100644 --- a/keymap.go +++ b/keymap.go @@ -49,24 +49,25 @@ func NewRuneMap(m map[rune]func(*tcell.EventKey) bool) KeyMap { } } -func (m KeyMap) Add(k tcell.Key, do func(*tcell.EventKey) bool) { m.Keys[k] = do } -func (m KeyMap) Remove(k tcell.Key) { - if _, ok := m.Keys[k]; ok { - delete(m.Keys, k) +func (m KeyMap) Merge(km KeyMap) { + for k, v := range km.Keys { + m.Keys[k] = v + } + for r, v := range km.Runes { + m.Runes[r] = v } } +func (m KeyMap) Add(k tcell.Key, do func(*tcell.EventKey) bool) { m.Keys[k] = do } +func (m KeyMap) Remove(k tcell.Key) { delete(m.Keys, k) } + func (m KeyMap) AddAll(all map[tcell.Key]func(*tcell.EventKey) bool) { for k, v := range all { m.Add(k, v) } } func (m KeyMap) AddRune(k rune, do func(*tcell.EventKey) bool) { m.Runes[k] = do } -func (m KeyMap) RemoveRune(k rune) { - if _, ok := m.Runes[k]; ok { - delete(m.Runes, k) - } -} +func (m KeyMap) RemoveRune(k rune) { delete(m.Runes, k) } func (m KeyMap) AddRunes(all map[rune]func(*tcell.EventKey) bool) { for k, v := range all { diff --git a/layout_flags.go b/layout_flags.go index b60b4ee..49bcb1b 100644 --- a/layout_flags.go +++ b/layout_flags.go @@ -45,6 +45,7 @@ const ( const ( LFAlignTopLeft = LayoutFlag(LFAlignHLeft | LFAlignVTop) + LFAlignCenter = LayoutFlag(LFAlignHCenter | LFAlignVCenter) ) func (f LayoutFlag) Add(fl LayoutFlag) { f |= fl } diff --git a/wdgt_absolute_layout.go b/wdgt_absolute_layout.go index 8f5cc66..390e92d 100644 --- a/wdgt_absolute_layout.go +++ b/wdgt_absolute_layout.go @@ -38,9 +38,11 @@ type AbsoluteLayout struct { defAnchor AbsoluteAnchor - active bool - visible bool - tabbable bool + active bool + visible bool + focusable bool + + keyMap KeyMap cursor int disableTab bool @@ -76,20 +78,30 @@ func (w *AbsoluteLayout) Init(id string, s tcell.Style) { w.style = s w.visible = true w.defAnchor = AnchorTL + w.keyMap = BlankKeyMap() w.wCoords = make(map[Widget]Coord) w.wAnchor = make(map[Widget]AbsoluteAnchor) w.wManualSizes = make(map[Widget]Coord) - w.tabbable = true + w.focusable = true } func (w *AbsoluteLayout) Id() string { return w.id } func (w *AbsoluteLayout) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() - // w.w = wh.Min(w.w, w.WantW()) - // w.h = wh.Min(w.h, w.WantH()) w.updateWidgetLayouts() } +func (w *AbsoluteLayout) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *AbsoluteLayout) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *AbsoluteLayout) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *AbsoluteLayout) HandleKey(ev *tcell.EventKey) bool { if !w.disableTab && ev.Key() == tcell.KeyTab { fndP := -1 @@ -101,7 +113,7 @@ func (w *AbsoluteLayout) HandleKey(ev *tcell.EventKey) bool { continue } } else { - if w.widgets[i].Focusable() && w.widgets[i].Tabbable() { + if w.widgets[i].Focusable() { w.widgets[i].SetActive(true) return true } @@ -112,7 +124,7 @@ func (w *AbsoluteLayout) HandleKey(ev *tcell.EventKey) bool { return false } for i := 0; i < fndP; i++ { - if w.widgets[i].Focusable() && w.widgets[i].Tabbable() { + if w.widgets[i].Focusable() { w.widgets[i].SetActive(true) return true } @@ -143,26 +155,25 @@ func (w *AbsoluteLayout) Draw(screen tcell.Screen) { } } -func (w *AbsoluteLayout) Active() bool { return w.active } -func (w *AbsoluteLayout) SetActive(a bool) { w.active = a } -func (w *AbsoluteLayout) Visible() bool { return w.visible } -func (w *AbsoluteLayout) SetVisible(a bool) { w.visible = a } -func (w *AbsoluteLayout) Focusable() bool { return true } -func (w *AbsoluteLayout) SetTabbable(b bool) { w.tabbable = b } -func (w *AbsoluteLayout) Tabbable() bool { return w.tabbable } -func (w *AbsoluteLayout) SetX(x int) { w.x = x } -func (w *AbsoluteLayout) SetY(y int) { w.y = y } -func (w *AbsoluteLayout) GetX() int { return w.x } -func (w *AbsoluteLayout) GetY() int { return w.y } -func (w *AbsoluteLayout) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *AbsoluteLayout) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *AbsoluteLayout) GetW() int { return w.w } -func (w *AbsoluteLayout) GetH() int { return w.h } -func (w *AbsoluteLayout) SetW(wd int) { w.w = wd } -func (w *AbsoluteLayout) SetH(h int) { w.h = h } -func (w *AbsoluteLayout) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *AbsoluteLayout) WantW() int { return w.w } -func (w *AbsoluteLayout) WantH() int { return w.h } +func (w *AbsoluteLayout) Active() bool { return w.active } +func (w *AbsoluteLayout) SetActive(a bool) { w.active = a } +func (w *AbsoluteLayout) Visible() bool { return w.visible } +func (w *AbsoluteLayout) SetVisible(a bool) { w.visible = a } +func (w *AbsoluteLayout) Focusable() bool { return w.focusable } +func (w *AbsoluteLayout) SetFocusable(b bool) { w.focusable = b } +func (w *AbsoluteLayout) SetX(x int) { w.x = x } +func (w *AbsoluteLayout) SetY(y int) { w.y = y } +func (w *AbsoluteLayout) GetX() int { return w.x } +func (w *AbsoluteLayout) GetY() int { return w.y } +func (w *AbsoluteLayout) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *AbsoluteLayout) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *AbsoluteLayout) GetW() int { return w.w } +func (w *AbsoluteLayout) GetH() int { return w.h } +func (w *AbsoluteLayout) SetW(wd int) { w.w = wd } +func (w *AbsoluteLayout) SetH(h int) { w.h = h } +func (w *AbsoluteLayout) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *AbsoluteLayout) WantW() int { return w.w } +func (w *AbsoluteLayout) WantH() int { return w.h } func (w *AbsoluteLayout) MinW() int { // Find the highest value for x in all widgets GetX() + MinW() var minW int diff --git a/wdgt_alert.go b/wdgt_alert.go index cd6e47d..186d37b 100644 --- a/wdgt_alert.go +++ b/wdgt_alert.go @@ -32,11 +32,11 @@ type Alert struct { id string style tcell.Style - x, y int - w, h int - active bool - visible bool - tabbable bool + x, y int + w, h int + active bool + visible bool + focusable bool layout *LinearLayout title string @@ -60,14 +60,17 @@ func (w *Alert) Init(id string, style tcell.Style) { w.id = id w.style = style - w.layout = NewLinearLayout(fmt.Sprintf("%s-layout", id), tcell.StyleDefault) + w.layout = NewLinearLayout(fmt.Sprintf("%s-layout", id), style) + // w.layout.SetStacked(true) w.message = NewText(fmt.Sprintf("%s-text", id), style) w.layout.Add(w.message) + w.layout.AddFlag(w.message, LFAlignCenter) - w.btnLayout = NewLinearLayout("alertbtn-layout", tcell.StyleDefault) + w.btnLayout = NewLinearLayout("alertbtn-layout", style) w.btnLayout.SetOrientation(LinLayH) w.layout.Add(w.btnLayout) + w.layout.AddFlag(w.btnLayout, LFAlignCenter) w.btnCancel = NewButton(fmt.Sprintf("%s-cancel", id), style) w.btnCancel.SetLabel("Cancel") @@ -86,22 +89,27 @@ func (w *Alert) Init(id string, style tcell.Style) { tcell.KeyUp: w.SelectNext, tcell.KeyEnter: w.Do, }) - w.tabbable = true + w.focusable = true } func (w *Alert) Id() string { return w.id } func (w *Alert) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() - if w.w > w.MinW() { - 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 w.layout.SetPos(Coord{X: 1, Y: 1}) w.layout.HandleResize(tcell.NewEventResize(w.w-2, w.h-2)) } +func (w *Alert) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Alert) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Alert) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *Alert) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false @@ -119,24 +127,23 @@ func (w *Alert) Draw(screen tcell.Screen) { w.GetPos().DrawOffset(w.layout, screen) } -func (w *Alert) Active() bool { return w.active } -func (w *Alert) SetActive(a bool) { w.active = a } -func (w *Alert) Visible() bool { return w.visible } -func (w *Alert) SetVisible(a bool) { w.visible = a } -func (w *Alert) SetX(x int) { w.x = x } -func (w *Alert) SetY(y int) { w.y = y } -func (w *Alert) GetX() int { return w.x } -func (w *Alert) GetY() int { return w.y } -func (w *Alert) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *Alert) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *Alert) SetW(x int) { w.w = x } -func (w *Alert) SetH(y int) { w.h = y } -func (w *Alert) GetW() int { return w.w } -func (w *Alert) GetH() int { return w.y } -func (w *Alert) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *Alert) Focusable() bool { return true } -func (w *Alert) SetTabbable(b bool) { w.tabbable = b } -func (w *Alert) Tabbable() bool { return w.tabbable } +func (w *Alert) Active() bool { return w.active } +func (w *Alert) SetActive(a bool) { w.active = a } +func (w *Alert) Visible() bool { return w.visible } +func (w *Alert) SetVisible(a bool) { w.visible = a } +func (w *Alert) SetX(x int) { w.x = x } +func (w *Alert) SetY(y int) { w.y = y } +func (w *Alert) GetX() int { return w.x } +func (w *Alert) GetY() int { return w.y } +func (w *Alert) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *Alert) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *Alert) SetW(x int) { w.w = x } +func (w *Alert) SetH(y int) { w.h = y } +func (w *Alert) GetW() int { return w.w } +func (w *Alert) GetH() int { return w.y } +func (w *Alert) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *Alert) Focusable() bool { return w.focusable } +func (w *Alert) SetFocusable(b bool) { w.focusable = b } func (w *Alert) WantW() int { return 4 + wh.Max(w.message.WantW(), (w.btnOk.WantW()+w.btnCancel.WantW())) } @@ -145,30 +152,40 @@ func (w *Alert) WantH() int { return 4 + w.btnOk.WantH() + w.message.WantH() } -// Borders + Buttons func (w *Alert) MinW() int { - return 5 + wh.Max(w.message.MinW(), (w.btnOk.MinW()+w.btnCancel.MinW())) + return 4 + wh.Max(w.message.MinW(), (w.btnOk.MinW()+w.btnCancel.MinW())) } -// Borders + Buttons + 2 lines for message func (w *Alert) MinH() int { - return 5 + w.message.MinH() + w.btnOk.MinH() + return 4 + w.message.MinH() + w.btnOk.MinH() } func (w *Alert) SetTitle(ttl string) { w.title = ttl } func (w *Alert) SetMessage(msg string) { w.message.SetText(msg) } -func (w *Alert) SetOkPressed(b func() bool) { w.btnOk.SetOnPressed(b) } -func (w *Alert) SetCancelPressed(b func() bool) { w.btnCancel.SetOnPressed(b) } +func (w *Alert) SetOkPressed(b func() bool) { + w.btnOk.SetVisible(b != nil) + w.btnOk.SetFocusable(b != nil) + w.btnOk.SetOnPressed(b) +} + +func (w *Alert) SetCancelPressed(b func() bool) { + w.btnCancel.SetVisible(b != nil) + w.btnCancel.SetFocusable(b != nil) + w.btnCancel.SetOnPressed(b) +} + func (w *Alert) SelectNext(ev *tcell.EventKey) bool { - if w.btnOk.Active() { + if w.btnOk.Active() && w.btnCancel.Visible() { w.btnOk.SetActive(false) w.btnCancel.SetActive(true) - } else { + return true + } else if w.btnCancel.Active() && w.btnOk.Visible() { w.btnOk.SetActive(true) w.btnCancel.SetActive(false) + return true } - return true + return false } func (w *Alert) Do(ev *tcell.EventKey) bool { diff --git a/wdgt_blank.go b/wdgt_blank.go index c95472b..a8dc3f3 100644 --- a/wdgt_blank.go +++ b/wdgt_blank.go @@ -25,8 +25,9 @@ import "github.com/gdamore/tcell" // BlankWidget is just blank. It's a good placeholder, if needed. type BlankWidget struct { - id string - x, y int + id string + x, y int + keyMap KeyMap } var _ Widget = (*BlankWidget)(nil) @@ -36,32 +37,41 @@ func NewBlankWidget(id string) *BlankWidget { return ret } -func (w *BlankWidget) Init(id string, st tcell.Style) {} +func (w *BlankWidget) Init(id string, st tcell.Style) { w.keyMap = BlankKeyMap() } func (w *BlankWidget) Id() string { return w.id } func (w *BlankWidget) HandleResize(ev *tcell.EventResize) {} -func (w *BlankWidget) HandleKey(ev *tcell.EventKey) bool { return false } -func (w *BlankWidget) HandleTime(ev *tcell.EventTime) {} -func (w *BlankWidget) Draw(screen tcell.Screen) {} +func (w *BlankWidget) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *BlankWidget) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *BlankWidget) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} +func (w *BlankWidget) HandleKey(ev *tcell.EventKey) bool { return false } +func (w *BlankWidget) HandleTime(ev *tcell.EventTime) {} +func (w *BlankWidget) Draw(screen tcell.Screen) {} -func (w *BlankWidget) Active() bool { return false } -func (w *BlankWidget) SetActive(a bool) {} -func (w *BlankWidget) Visible() bool { return true } -func (w *BlankWidget) SetVisible(v bool) {} -func (w *BlankWidget) Focusable() bool { return false } -func (w *BlankWidget) Tabbable() bool { return false } -func (w *BlankWidget) SetTabbable(t bool) {} -func (w *BlankWidget) SetX(x int) {} -func (w *BlankWidget) SetY(y int) {} -func (w *BlankWidget) GetX() int { return w.x } -func (w *BlankWidget) GetY() int { return w.y } -func (w *BlankWidget) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *BlankWidget) SetPos(pos Coord) {} -func (w *BlankWidget) SetSize(size Coord) {} -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) MinW() int { return 0 } -func (w *BlankWidget) MinH() int { return 0 } +func (w *BlankWidget) Active() bool { return false } +func (w *BlankWidget) SetActive(a bool) {} +func (w *BlankWidget) Visible() bool { return true } +func (w *BlankWidget) SetVisible(v bool) {} +func (w *BlankWidget) Focusable() bool { return false } +func (w *BlankWidget) SetFocusable(t bool) {} +func (w *BlankWidget) SetX(x int) {} +func (w *BlankWidget) SetY(y int) {} +func (w *BlankWidget) GetX() int { return w.x } +func (w *BlankWidget) GetY() int { return w.y } +func (w *BlankWidget) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *BlankWidget) SetPos(pos Coord) {} +func (w *BlankWidget) SetSize(size Coord) {} +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) MinW() int { return 0 } +func (w *BlankWidget) MinH() int { return 0 } diff --git a/wdgt_bordered.go b/wdgt_bordered.go index 886fcd0..c1aad56 100644 --- a/wdgt_bordered.go +++ b/wdgt_bordered.go @@ -36,10 +36,10 @@ type BorderedWidget struct { widget Widget border []rune - title string - active bool - visible bool - tabbable bool + title string + active bool + visible bool + focusable bool logger func(string) } @@ -57,7 +57,7 @@ func (w *BorderedWidget) Init(id string, s tcell.Style) { w.style = s w.visible = true w.border = wh.BRD_CSIMPLE - w.tabbable = true + w.focusable = true } func (w *BorderedWidget) Id() string { return w.id } @@ -68,6 +68,9 @@ func (w *BorderedWidget) HandleResize(ev *tcell.EventResize) { w.widget.HandleResize(tcell.NewEventResize(w.w-2, w.h-2)) } +func (w *BorderedWidget) SetKeyMap(km KeyMap) { w.widget.SetKeyMap(km) } +func (w *BorderedWidget) AddToKeyMap(km KeyMap) { w.widget.AddToKeyMap(km) } +func (w *BorderedWidget) RemoveFromKeyMap(km KeyMap) { w.widget.RemoveFromKeyMap(km) } func (w *BorderedWidget) HandleKey(ev *tcell.EventKey) bool { return w.HandleKey(ev) } func (w *BorderedWidget) HandleTime(ev *tcell.EventTime) { w.HandleTime(ev) } @@ -84,28 +87,27 @@ func (w *BorderedWidget) Draw(screen tcell.Screen) { w.GetPos().DrawOffset(w, screen) } -func (w *BorderedWidget) Active() bool { return w.active } -func (w *BorderedWidget) SetActive(a bool) { w.active = a } -func (w *BorderedWidget) Visible() bool { return w.visible } -func (w *BorderedWidget) SetVisible(a bool) { w.visible = a } -func (w *BorderedWidget) Focusable() bool { return true } -func (w *BorderedWidget) SetTabbable(b bool) { w.tabbable = b } -func (w *BorderedWidget) Tabbable() bool { return w.tabbable } -func (w *BorderedWidget) SetX(x int) { w.x = x } -func (w *BorderedWidget) SetY(y int) { w.y = y } -func (w *BorderedWidget) GetX() int { return w.x } -func (w *BorderedWidget) GetY() int { return w.y } -func (w *BorderedWidget) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *BorderedWidget) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *BorderedWidget) GetW() int { return w.w } -func (w *BorderedWidget) GetH() int { return w.h } -func (w *BorderedWidget) SetW(wd int) { w.w = wd } -func (w *BorderedWidget) SetH(h int) { w.h = h } -func (w *BorderedWidget) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *BorderedWidget) WantW() int { return w.w } -func (w *BorderedWidget) WantH() int { return w.h } -func (w *BorderedWidget) MinW() int { return 2 + w.widget.MinW() } -func (w *BorderedWidget) MinH() int { return 2 + w.widget.MinH() } +func (w *BorderedWidget) Active() bool { return w.active } +func (w *BorderedWidget) SetActive(a bool) { w.active = a } +func (w *BorderedWidget) Visible() bool { return w.visible } +func (w *BorderedWidget) SetVisible(a bool) { w.visible = a } +func (w *BorderedWidget) Focusable() bool { return w.focusable } +func (w *BorderedWidget) SetFocusable(b bool) { w.focusable = b } +func (w *BorderedWidget) SetX(x int) { w.x = x } +func (w *BorderedWidget) SetY(y int) { w.y = y } +func (w *BorderedWidget) GetX() int { return w.x } +func (w *BorderedWidget) GetY() int { return w.y } +func (w *BorderedWidget) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *BorderedWidget) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *BorderedWidget) GetW() int { return w.w } +func (w *BorderedWidget) GetH() int { return w.h } +func (w *BorderedWidget) SetW(wd int) { w.w = wd } +func (w *BorderedWidget) SetH(h int) { w.h = h } +func (w *BorderedWidget) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *BorderedWidget) WantW() int { return w.w } +func (w *BorderedWidget) WantH() int { return w.h } +func (w *BorderedWidget) MinW() int { return 2 + w.widget.MinW() } +func (w *BorderedWidget) MinH() int { return 2 + w.widget.MinH() } func (w *BorderedWidget) SetBorder(r []rune) { w.border = r } func (w *BorderedWidget) SetTitle(ttl string) { w.title = ttl } diff --git a/wdgt_button.go b/wdgt_button.go index 0d37c67..786ae27 100644 --- a/wdgt_button.go +++ b/wdgt_button.go @@ -36,9 +36,10 @@ type Button struct { x, y int w, h int - active bool - visible bool - tabbable bool + active bool + visible bool + focusable bool + keyMap KeyMap onPressed func() bool logger func(string, ...any) @@ -56,24 +57,31 @@ func (w *Button) 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.KeyEnter: func(ev *tcell.EventKey) bool { return w.onPressed() }, + }) w.onPressed = func() bool { return false } - w.tabbable = true + w.focusable = true } -func (w *Button) Id() string { return w.id } -func (w *Button) HandleResize(ev *tcell.EventResize) { - w.w, w.h = ev.Size() - // w.w = wh.Min(w.w, w.WantW()) - // w.h = wh.Min(w.h, w.WantH()) +func (w *Button) Id() string { return w.id } +func (w *Button) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() } + +func (w *Button) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Button) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Button) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } } func (w *Button) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false } - if ev.Key() == tcell.KeyEnter { - return w.onPressed() - } - return false + return w.keyMap.Handle(ev) } func (w *Button) HandleTime(ev *tcell.EventTime) {} @@ -81,12 +89,7 @@ func (w *Button) Draw(screen tcell.Screen) { if !w.visible { return } - dStyle := w.style - if w.active { - dStyle = w.style.Bold(true) - } else { - dStyle = w.style.Dim(true) - } + dStyle := w.style.Dim(!w.active) switch w.h { case 1: lbl := w.label @@ -123,28 +126,27 @@ func (w *Button) Draw(screen tcell.Screen) { wh.DrawText(w.x, w.y+2, fmt.Sprintf("╰%s╯", strings.Repeat("─", w.w-2)), dStyle, screen) } -func (w *Button) Active() bool { return w.active } -func (w *Button) SetActive(a bool) { w.active = a } -func (w *Button) Visible() bool { return w.visible } -func (w *Button) SetVisible(a bool) { w.visible = a } -func (w *Button) SetX(x int) { w.x = x } -func (w *Button) SetY(y int) { w.y = y } -func (w *Button) GetX() int { return w.x } -func (w *Button) GetY() int { return w.y } -func (w *Button) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -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.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 } -func (w *Button) Focusable() bool { return true } -func (w *Button) SetTabbable(b bool) { w.tabbable = b } -func (w *Button) Tabbable() bool { return w.tabbable } -func (w *Button) MinW() int { return len(w.label) + 2 } -func (w *Button) MinH() int { return 1 } +func (w *Button) Active() bool { return w.active } +func (w *Button) SetActive(a bool) { w.active = a } +func (w *Button) Visible() bool { return w.visible } +func (w *Button) SetVisible(a bool) { w.visible = a } +func (w *Button) SetX(x int) { w.x = x } +func (w *Button) SetY(y int) { w.y = y } +func (w *Button) GetX() int { return w.x } +func (w *Button) GetY() int { return w.y } +func (w *Button) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +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.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 } +func (w *Button) Focusable() bool { return w.focusable } +func (w *Button) SetFocusable(b bool) { w.focusable = b } +func (w *Button) MinW() int { return len(w.label) + 2 } +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 } diff --git a/wdgt_chat.go b/wdgt_chat.go index ad79c95..c28fd44 100644 --- a/wdgt_chat.go +++ b/wdgt_chat.go @@ -34,11 +34,11 @@ type Chat struct { id string style tcell.Style - x, y int - w, h int - active bool - visible bool - tabbable bool + x, y int + w, h int + active bool + visible bool + focusable bool title string rawLog []string @@ -66,14 +66,23 @@ func (w *Chat) Init(id string, s tcell.Style) { w.id, w.style = id, s w.visible = true w.initKeyMap() - w.tabbable = true + w.focusable = true } func (w *Chat) Id() string { return w.id } func (w *Chat) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() - // w.w = wh.Min(w.w, w.WantW()) - // w.h = wh.Min(w.h, w.WantH()) +} + +func (w *Chat) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Chat) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Chat) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } } func (w *Chat) HandleKey(ev *tcell.EventKey) bool { @@ -157,28 +166,27 @@ func (w *Chat) Draw(screen tcell.Screen) { // x += len(post) - 1 } -func (w *Chat) Active() bool { return w.active } -func (w *Chat) SetActive(a bool) { w.active = a } -func (w *Chat) Visible() bool { return w.visible } -func (w *Chat) SetVisible(a bool) { w.visible = a } -func (w *Chat) Focusable() bool { return true } -func (w *Chat) SetTabbable(b bool) { w.tabbable = b } -func (w *Chat) Tabbable() bool { return w.tabbable } -func (w *Chat) SetX(x int) { w.x = x } -func (w *Chat) SetY(y int) { w.y = y } -func (w *Chat) GetX() int { return w.x } -func (w *Chat) GetY() int { return w.y } -func (w *Chat) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *Chat) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *Chat) GetW() int { return w.w } -func (w *Chat) GetH() int { return w.h } -func (w *Chat) SetW(wd int) { w.w = wd } -func (w *Chat) SetH(h int) { w.h = h } -func (w *Chat) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *Chat) WantW() int { return wh.MaxInt } -func (w *Chat) WantH() int { return wh.MaxInt } -func (w *Chat) MinW() int { return 2 + 20 } -func (w *Chat) MinH() int { return 6 } +func (w *Chat) Active() bool { return w.active } +func (w *Chat) SetActive(a bool) { w.active = a } +func (w *Chat) Visible() bool { return w.visible } +func (w *Chat) SetVisible(a bool) { w.visible = a } +func (w *Chat) Focusable() bool { return w.focusable } +func (w *Chat) SetFocusable(b bool) { w.focusable = b } +func (w *Chat) SetX(x int) { w.x = x } +func (w *Chat) SetY(y int) { w.y = y } +func (w *Chat) GetX() int { return w.x } +func (w *Chat) GetY() int { return w.y } +func (w *Chat) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *Chat) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *Chat) GetW() int { return w.w } +func (w *Chat) GetH() int { return w.h } +func (w *Chat) SetW(wd int) { w.w = wd } +func (w *Chat) SetH(h int) { w.h = h } +func (w *Chat) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *Chat) WantW() int { return wh.MaxInt } +func (w *Chat) WantH() int { return wh.MaxInt } +func (w *Chat) MinW() int { return 2 + 20 } +func (w *Chat) MinH() int { return 6 } func (w *Chat) initKeyMap() { w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ diff --git a/wdgt_checkbox.go b/wdgt_checkbox.go index 29d6a6a..e9d02cf 100644 --- a/wdgt_checkbox.go +++ b/wdgt_checkbox.go @@ -35,16 +35,17 @@ const ( ) type Checkbox struct { - id string - label string - style tcell.Style - active bool - visible bool - tabbable bool - state int - x, y int - w, h int + id string + label string + style tcell.Style + active bool + visible bool + focusable bool + state int + x, y int + w, h int + keyMap KeyMap stateRunes []rune } @@ -61,28 +62,39 @@ func (w *Checkbox) Init(id string, style tcell.Style) { w.style = style w.visible = true w.stateRunes = []rune{'X', ' ', '-'} - w.tabbable = true + w.focusable = true + w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ + tcell.KeyEnter: func(_ *tcell.EventKey) bool { + if w.state == CHECKBOX_ON { + w.state = CHECKBOX_OFF + } else { + w.state = CHECKBOX_ON + } + return true + }, + }) } func (w *Checkbox) Id() string { return w.id } func (w *Checkbox) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() - // w.w = wh.Min(w.w, w.WantW()) - // w.h = wh.Min(w.h, w.WantH()) +} + +func (w *Checkbox) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Checkbox) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Checkbox) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } } func (w *Checkbox) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false } - if ev.Key() == tcell.KeyEnter { - if w.state == CHECKBOX_ON { - w.state = CHECKBOX_OFF - } else { - w.state = CHECKBOX_ON - } - return true - } - return false + return w.keyMap.Handle(ev) } func (w *Checkbox) HandleTime(ev *tcell.EventTime) {} func (w *Checkbox) Draw(screen tcell.Screen) { @@ -113,11 +125,10 @@ func (w *Checkbox) GetH() int { return w.y } func (w *Checkbox) WantW() int { return len(fmt.Sprintf("[%s] %s", string(w.state), w.label)) } -func (w *Checkbox) WantH() int { return 1 } -func (w *Checkbox) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *Checkbox) Focusable() bool { return true } -func (w *Checkbox) SetTabbable(b bool) { w.tabbable = b } -func (w *Checkbox) Tabbable() bool { return w.tabbable } +func (w *Checkbox) WantH() int { return 1 } +func (w *Checkbox) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *Checkbox) Focusable() bool { return w.focusable } +func (w *Checkbox) SetFocusable(b bool) { w.focusable = b } func (w *Checkbox) MinW() int { return len(fmt.Sprintf("[%s] %s", string(w.state), w.label)) } diff --git a/wdgt_cli.go b/wdgt_cli.go index bd73be2..595f6c0 100644 --- a/wdgt_cli.go +++ b/wdgt_cli.go @@ -35,11 +35,11 @@ type Cli struct { id string style tcell.Style - x, y int - w, h int - active bool - visible bool - tabbable bool + x, y int + w, h int + active bool + visible bool + focusable bool title string rawLog []string @@ -71,7 +71,7 @@ func (w *Cli) Init(id string, s tcell.Style) { w.id, w.style = id, s w.visible = true w.initKeyMap() - w.tabbable = true + w.focusable = true } func (w *Cli) Id() string { return w.id } @@ -79,6 +79,17 @@ func (w *Cli) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() } +func (w *Cli) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Cli) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Cli) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *Cli) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false @@ -167,28 +178,27 @@ func (w *Cli) Draw(screen tcell.Screen) { // x += len(post) - 1 } -func (w *Cli) Active() bool { return w.active } -func (w *Cli) SetActive(a bool) { w.active = a } -func (w *Cli) Visible() bool { return w.visible } -func (w *Cli) SetVisible(a bool) { w.visible = a } -func (w *Cli) Focusable() bool { return true } -func (w *Cli) SetTabbable(b bool) { w.tabbable = b } -func (w *Cli) Tabbable() bool { return w.tabbable } -func (w *Cli) SetX(x int) { w.x = x } -func (w *Cli) SetY(y int) { w.y = y } -func (w *Cli) GetX() int { return w.x } -func (w *Cli) GetY() int { return w.y } -func (w *Cli) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *Cli) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *Cli) GetW() int { return w.w } -func (w *Cli) GetH() int { return w.h } -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) MinW() int { return 20 } -func (w *Cli) MinH() int { return 6 } +func (w *Cli) Active() bool { return w.active } +func (w *Cli) SetActive(a bool) { w.active = a } +func (w *Cli) Visible() bool { return w.visible } +func (w *Cli) SetVisible(a bool) { w.visible = a } +func (w *Cli) Focusable() bool { return true } +func (w *Cli) SetFocusable(b bool) { w.focusable = b } +func (w *Cli) SetX(x int) { w.x = x } +func (w *Cli) SetY(y int) { w.y = y } +func (w *Cli) GetX() int { return w.x } +func (w *Cli) GetY() int { return w.y } +func (w *Cli) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *Cli) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *Cli) GetW() int { return w.w } +func (w *Cli) GetH() int { return w.h } +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) MinW() int { return 20 } +func (w *Cli) MinH() int { return 6 } func (w *Cli) initKeyMap() { w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{ diff --git a/wdgt_debug.go b/wdgt_debug.go index 70ea874..dbaa27e 100644 --- a/wdgt_debug.go +++ b/wdgt_debug.go @@ -40,9 +40,10 @@ type DebugWidget struct { drawRulers bool active bool visible bool - tabbable bool + focusable bool mTL, mBR Coord // Margins (Top-Right & Bottom Left) + keyMap KeyMap logger func(string, ...any) } @@ -60,9 +61,10 @@ func (w *DebugWidget) Init(id string, s tcell.Style) { w.id = id w.style = s w.visible = true - w.tabbable = true + w.focusable = true w.drawRulers = true w.setW, w.setH = -1, -1 + w.keyMap = BlankKeyMap() } func (w *DebugWidget) SetLogger(l func(string, ...any)) { w.logger = l } @@ -104,8 +106,24 @@ func (w *DebugWidget) HandleResize(ev *tcell.EventResize) { w.widget.HandleResize(tcell.NewEventResize(w.w-w.mTL.X-w.mBR.X, w.h-w.mTL.Y-w.mBR.Y)) } -func (w *DebugWidget) HandleKey(ev *tcell.EventKey) bool { return w.widget.HandleKey(ev) } -func (w *DebugWidget) HandleTime(ev *tcell.EventTime) { w.widget.HandleTime(ev) } +func (w *DebugWidget) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *DebugWidget) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *DebugWidget) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + +func (w *DebugWidget) HandleKey(ev *tcell.EventKey) bool { + if ok := w.keyMap.Handle(ev); ok { + return true + } + return w.widget.HandleKey(ev) +} +func (w *DebugWidget) HandleTime(ev *tcell.EventTime) { w.widget.HandleTime(ev) } func (w *DebugWidget) Draw(screen tcell.Screen) { if !w.visible { @@ -160,19 +178,18 @@ func (w *DebugWidget) SetActive(a bool) { w.active = a w.widget.SetActive(a) } -func (w *DebugWidget) Visible() bool { return w.visible } -func (w *DebugWidget) SetVisible(a bool) { w.visible = a } -func (w *DebugWidget) Focusable() bool { return true } -func (w *DebugWidget) SetTabbable(b bool) { w.tabbable = b } -func (w *DebugWidget) Tabbable() bool { return w.tabbable } -func (w *DebugWidget) SetX(x int) { w.x = x } -func (w *DebugWidget) SetY(y int) { w.y = y } -func (w *DebugWidget) GetX() int { return w.x } -func (w *DebugWidget) GetY() int { return w.y } -func (w *DebugWidget) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *DebugWidget) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *DebugWidget) GetW() int { return w.w } -func (w *DebugWidget) GetH() int { return w.h } +func (w *DebugWidget) Visible() bool { return w.visible } +func (w *DebugWidget) SetVisible(a bool) { w.visible = a } +func (w *DebugWidget) Focusable() bool { return w.focusable } +func (w *DebugWidget) SetFocusable(b bool) { w.focusable = b } +func (w *DebugWidget) SetX(x int) { w.x = x } +func (w *DebugWidget) SetY(y int) { w.y = y } +func (w *DebugWidget) GetX() int { return w.x } +func (w *DebugWidget) GetY() int { return w.y } +func (w *DebugWidget) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *DebugWidget) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *DebugWidget) GetW() int { return w.w } +func (w *DebugWidget) GetH() int { return w.h } func (w *DebugWidget) SetW(wd int) { w.setW = wd w.w = wd diff --git a/wdgt_field.go b/wdgt_field.go index e5edb0b..5166fb7 100644 --- a/wdgt_field.go +++ b/wdgt_field.go @@ -35,12 +35,12 @@ type Field struct { label string value string - cursor int - visible bool - active bool - tabbable bool - x, y int - w, h int + cursor int + visible bool + active bool + focusable bool + x, y int + w, h int filter func(*tcell.EventKey) bool onChange func(prev, curr string) @@ -71,12 +71,23 @@ func (w *Field) Init(id string, style tcell.Style) { tcell.KeyEnd: w.handleEnd, tcell.KeyCtrlU: w.clearValueBeforeCursor, }) - w.tabbable = true + w.focusable = true } func (w *Field) Id() string { return w.id } func (w *Field) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() } +func (w *Field) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Field) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Field) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *Field) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false @@ -158,12 +169,11 @@ func (w *Field) WantW() int { func (w *Field) WantH() int { return 1 } -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) + wh.Max(15, len(w.value)) } -func (w *Field) MinH() int { return 1 } +func (w *Field) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *Field) Focusable() bool { return w.focusable } +func (w *Field) SetFocusable(b bool) { w.focusable = b } +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 */ func (w *Field) handleBackspace(_ *tcell.EventKey) bool { diff --git a/wdgt_filepicker.go b/wdgt_filepicker.go index 5b759a4..6cb0a78 100644 --- a/wdgt_filepicker.go +++ b/wdgt_filepicker.go @@ -37,7 +37,6 @@ type FilePicker struct { active bool visible bool focusable bool - tabbable bool x, y int w, h int @@ -50,6 +49,8 @@ type FilePicker struct { fileList *List btnSelect, btnCancel *Button + + keyMap KeyMap } var _ Widget = (*FilePicker)(nil) @@ -72,7 +73,8 @@ func (w *FilePicker) Init(id string, style tcell.Style) { w.btnCancel = NewButton(fmt.Sprintf("%s-cancel", id), style) w.btnCancel.SetLabel("Cancel") w.layout.Add(w.btnCancel, nil, RelAncBL) - w.tabbable = true + w.focusable = true + w.keyMap = BlankKeyMap() } func (w *FilePicker) Id() string { return w.id } func (w *FilePicker) HandleResize(ev *tcell.EventResize) { @@ -82,21 +84,29 @@ func (w *FilePicker) HandleResize(ev *tcell.EventResize) { w.btnCancel.SetPos(Coord{X: w.x + 1, Y: w.y + w.h - 1}) } +func (w *FilePicker) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *FilePicker) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *FilePicker) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *FilePicker) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false } - return false + return w.keyMap.Handle(ev) } func (w *FilePicker) HandleTime(ev *tcell.EventTime) {} func (w *FilePicker) Draw(screen tcell.Screen) { if !w.visible { return } - ds := w.style - if !w.active { - ds = ds.Dim(true) - } + ds := w.style.Dim(!w.active) wh.TitledBorderFilled(w.x, w.y, w.x+w.w, w.y+w.h, w.title, wh.BRD_SIMPLE, ds, screen) // TODO: Draw the file picker wh.DrawText(w.x+1, w.y+1, "TODO: Draw Filepicker", ds, screen) @@ -104,24 +114,23 @@ func (w *FilePicker) Draw(screen tcell.Screen) { w.GetPos().DrawOffset(w.btnCancel, screen) } -func (w *FilePicker) Active() bool { return w.active } -func (w *FilePicker) SetActive(a bool) { w.active = a } -func (w *FilePicker) Visible() bool { return w.visible } -func (w *FilePicker) SetVisible(a bool) { w.visible = a } -func (w *FilePicker) SetX(x int) { w.x = x } -func (w *FilePicker) SetY(y int) { w.y = y } -func (w *FilePicker) GetX() int { return w.x } -func (w *FilePicker) GetY() int { return w.y } -func (w *FilePicker) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *FilePicker) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *FilePicker) SetW(x int) { w.w = x } -func (w *FilePicker) SetH(y int) { w.h = y } -func (w *FilePicker) GetW() int { return w.w } -func (w *FilePicker) GetH() int { return w.y } -func (w *FilePicker) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *FilePicker) Focusable() bool { return w.focusable } -func (w *FilePicker) SetTabbable(b bool) { w.tabbable = b } -func (w *FilePicker) Tabbable() bool { return w.tabbable } +func (w *FilePicker) Active() bool { return w.active } +func (w *FilePicker) SetActive(a bool) { w.active = a } +func (w *FilePicker) Visible() bool { return w.visible } +func (w *FilePicker) SetVisible(a bool) { w.visible = a } +func (w *FilePicker) SetX(x int) { w.x = x } +func (w *FilePicker) SetY(y int) { w.y = y } +func (w *FilePicker) GetX() int { return w.x } +func (w *FilePicker) GetY() int { return w.y } +func (w *FilePicker) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *FilePicker) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *FilePicker) SetW(x int) { w.w = x } +func (w *FilePicker) SetH(y int) { w.h = y } +func (w *FilePicker) GetW() int { return w.w } +func (w *FilePicker) GetH() int { return w.y } +func (w *FilePicker) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *FilePicker) Focusable() bool { return w.focusable } +func (w *FilePicker) SetFocusable(b bool) { w.focusable = b } func (w *FilePicker) WantW() int { // borders + the greater of the buttons next to each other or the list width return wh.Max((w.btnSelect.WantW()+w.btnCancel.WantW()), w.fileList.WantW()) + 2 diff --git a/wdgt_linear_layout.go b/wdgt_linear_layout.go index dd4ddfc..825f538 100644 --- a/wdgt_linear_layout.go +++ b/wdgt_linear_layout.go @@ -52,10 +52,12 @@ type LinearLayout struct { active bool visible bool - tabbable bool + focusable bool disableTab bool insetBorder bool + keyMap KeyMap + logger func(string, ...any) } @@ -78,10 +80,23 @@ func (w *LinearLayout) Init(id string, s tcell.Style) { w.id = id w.style = s w.visible = true - w.tabbable = true + w.focusable = true w.defFlags = LayoutFlag(LFAlignHCenter | LFAlignVCenter) w.layoutFlags = make(map[Widget]LayoutFlag) w.layoutWeights = make(map[Widget]int) + w.keyMap = BlankKeyMap() + w.keyMap.Add(tcell.KeyTab, func(ev *tcell.EventKey) bool { + active := w.findActive() + if active == nil && len(w.widgets) > 0 { + // No widget is active + if w.widgets[0].Focusable() { + w.widgets[0].SetActive(true) + return true + } + return false + } + return w.activateNext() + }) } func (w *LinearLayout) Id() string { return w.id } @@ -90,6 +105,17 @@ func (w *LinearLayout) HandleResize(ev *tcell.EventResize) { w.updateWidgetLayouts() } +func (w *LinearLayout) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *LinearLayout) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *LinearLayout) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *LinearLayout) HandleKey(ev *tcell.EventKey) bool { if !w.active || w.disableTab { return false @@ -100,19 +126,7 @@ func (w *LinearLayout) HandleKey(ev *tcell.EventKey) bool { return true } } - if ev.Key() == tcell.KeyTab { - if active == nil && len(w.widgets) > 0 { - // No widget is active - if w.widgets[0].Tabbable() { - w.widgets[0].SetActive(true) - return true - } - return false - } - return w.activateNext() - } - - return false + return w.keyMap.Handle(ev) } func (w *LinearLayout) GetActive() Widget { return w.findActive() } @@ -146,23 +160,22 @@ func (w *LinearLayout) SetActive(a bool) { } } } -func (w *LinearLayout) Visible() bool { return w.visible } -func (w *LinearLayout) SetVisible(a bool) { w.visible = a } -func (w *LinearLayout) Focusable() bool { return true } -func (w *LinearLayout) SetTabbable(b bool) { w.tabbable = b } -func (w *LinearLayout) Tabbable() bool { return w.tabbable } -func (w *LinearLayout) SetX(x int) { w.x = x } -func (w *LinearLayout) SetY(y int) { w.y = y } -func (w *LinearLayout) GetX() int { return w.x } -func (w *LinearLayout) GetY() int { return w.y } -func (w *LinearLayout) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *LinearLayout) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *LinearLayout) GetW() int { return w.w } -func (w *LinearLayout) GetH() int { return w.h } -func (w *LinearLayout) SetW(wd int) { w.w = wd } -func (w *LinearLayout) SetH(h int) { w.h = h } -func (w *LinearLayout) getSize() Coord { return Coord{X: w.w, Y: w.h} } -func (w *LinearLayout) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *LinearLayout) Visible() bool { return w.visible } +func (w *LinearLayout) SetVisible(a bool) { w.visible = a } +func (w *LinearLayout) Focusable() bool { return w.focusable } +func (w *LinearLayout) SetFocusable(b bool) { w.focusable = b } +func (w *LinearLayout) SetX(x int) { w.x = x } +func (w *LinearLayout) SetY(y int) { w.y = y } +func (w *LinearLayout) GetX() int { return w.x } +func (w *LinearLayout) GetY() int { return w.y } +func (w *LinearLayout) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *LinearLayout) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *LinearLayout) GetW() int { return w.w } +func (w *LinearLayout) GetH() int { return w.h } +func (w *LinearLayout) SetW(wd int) { w.w = wd } +func (w *LinearLayout) SetH(h int) { w.h = h } +func (w *LinearLayout) getSize() Coord { return Coord{X: w.w, Y: w.h} } +func (w *LinearLayout) SetSize(c Coord) { w.w, w.h = c.X, c.Y } func (w *LinearLayout) WantW() int { var wantW int for _, wd := range w.widgets { @@ -245,7 +258,7 @@ func (w *LinearLayout) findActiveOrFirst() Widget { func (w *LinearLayout) activateNext() bool { var found bool for i := range w.widgets { - if found && w.widgets[i].Tabbable() { + if found && w.widgets[i].Focusable() { w.widgets[i].SetActive(true) return true } else if w.widgets[i].Active() { @@ -443,7 +456,9 @@ func (w *LinearLayout) updateLLVWidgetPos(wd Widget) { if w.widgets[i] == wd { break } - c.Y = w.widgets[i].GetY() + w.widgets[i].GetH() + if w.widgets[i].Visible() { + c.Y = w.widgets[i].GetY() + w.widgets[i].GetH() + } } // Do we have a layout flag for this widget? @@ -484,7 +499,9 @@ func (w *LinearLayout) updateLLHWidgetPos(wd Widget) { } break } - c.X = w.widgets[i].GetX() + w.widgets[i].GetW() + if w.widgets[i].Visible() { + c.X = w.widgets[i].GetX() + w.widgets[i].GetW() + } } // Do we have a layout flag for this widget? diff --git a/wdgt_list.go b/wdgt_list.go index 088dd37..fae6df3 100644 --- a/wdgt_list.go +++ b/wdgt_list.go @@ -33,7 +33,6 @@ type List struct { active bool visible bool focusable bool - tabbable bool x, y int w, h int @@ -86,13 +85,24 @@ func (w *List) Init(id string, style tcell.Style) { return false }) w.itemsStyle = make(map[int]tcell.Style) - w.tabbable = true + w.focusable = true } func (w *List) Id() string { return w.id } func (w *List) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() } +func (w *List) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *List) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *List) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *List) HandleKey(ev *tcell.EventKey) bool { if !w.active || !w.focusable { return false @@ -122,7 +132,7 @@ func (w *List) Draw(screen tcell.Screen) { rev = true } txt := w.list[i] - if len(txt) > w.w-brdSz { + if len(txt) > w.w-brdSz && w.w-brdSz >= 0 { txt = txt[:(w.w - brdSz)] } var ok bool @@ -135,24 +145,23 @@ func (w *List) Draw(screen tcell.Screen) { } } -func (w *List) Active() bool { return w.active } -func (w *List) SetActive(a bool) { w.active = a } -func (w *List) Visible() bool { return w.visible } -func (w *List) SetVisible(a bool) { w.visible = a } -func (w *List) SetX(x int) { w.x = x } -func (w *List) SetY(y int) { w.y = y } -func (w *List) GetX() int { return w.x } -func (w *List) GetY() int { return w.y } -func (w *List) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *List) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *List) SetW(x int) { w.w = x } -func (w *List) SetH(y int) { w.h = y } -func (w *List) GetW() int { return w.w } -func (w *List) GetH() int { return w.y } -func (w *List) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *List) Focusable() bool { return w.focusable } -func (w *List) SetTabbable(b bool) { w.tabbable = b } -func (w *List) Tabbable() bool { return w.tabbable } +func (w *List) Active() bool { return w.active } +func (w *List) SetActive(a bool) { w.active = a } +func (w *List) Visible() bool { return w.visible } +func (w *List) SetVisible(a bool) { w.visible = a } +func (w *List) SetX(x int) { w.x = x } +func (w *List) SetY(y int) { w.y = y } +func (w *List) GetX() int { return w.x } +func (w *List) GetY() int { return w.y } +func (w *List) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *List) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *List) SetW(x int) { w.w = x } +func (w *List) SetH(y int) { w.h = y } +func (w *List) GetW() int { return w.w } +func (w *List) GetH() int { return w.y } +func (w *List) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *List) Focusable() bool { return w.focusable } +func (w *List) SetFocusable(b bool) { w.focusable = b } func (w *List) WantW() int { lng := wh.Longest(w.list) if len(w.border) > 0 { @@ -179,8 +188,6 @@ func (w *List) MinW() int { func (w *List) MinH() int { return 4 } -func (w *List) SetFocusable(f bool) { w.focusable = f } - func (w *List) SetCursorWrap(b bool) { w.cursorWrap = b } func (w *List) MoveUp(ev *tcell.EventKey) bool { if w.cursor > 0 { diff --git a/wdgt_menu.go b/wdgt_menu.go index 085d53c..635df59 100644 --- a/wdgt_menu.go +++ b/wdgt_menu.go @@ -29,14 +29,14 @@ import ( ) type Menu struct { - id string - label string - style tcell.Style - active bool - visible bool - tabbable bool - x, y int - w, h int + id string + label string + style tcell.Style + active bool + visible bool + focusable bool + x, y int + w, h int menuType MenuType cursor int @@ -81,18 +81,25 @@ func (w *Menu) Init(id string, style tcell.Style) { return false }, }) - w.tabbable = true + w.focusable = true } func (w *Menu) Id() string { return w.id } func (w *Menu) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() - - // w.w = wh.Min(w.w, w.WantW()) - // w.h = wh.Min(w.h, w.WantH()) - // TODO: Trickle-down HandleResize } +func (w *Menu) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Menu) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Menu) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *Menu) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false @@ -165,24 +172,23 @@ func (w *Menu) drawVMenu(screen tcell.Screen) { y++ } } -func (w *Menu) Active() bool { return w.active } -func (w *Menu) SetActive(a bool) { w.active = a } -func (w *Menu) Visible() bool { return w.visible } -func (w *Menu) SetVisible(a bool) { w.visible = a } -func (w *Menu) SetX(x int) { w.x = x } -func (w *Menu) SetY(y int) { w.y = y } -func (w *Menu) GetX() int { return w.x } -func (w *Menu) GetY() int { return w.y } -func (w *Menu) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *Menu) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *Menu) SetW(x int) { w.w = x } -func (w *Menu) SetH(y int) { w.h = y } -func (w *Menu) GetW() int { return w.w } -func (w *Menu) GetH() int { return w.y } -func (w *Menu) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *Menu) Focusable() bool { return true } -func (w *Menu) SetTabbable(b bool) { w.tabbable = b } -func (w *Menu) Tabbable() bool { return w.tabbable } +func (w *Menu) Active() bool { return w.active } +func (w *Menu) SetActive(a bool) { w.active = a } +func (w *Menu) Visible() bool { return w.visible } +func (w *Menu) SetVisible(a bool) { w.visible = a } +func (w *Menu) SetX(x int) { w.x = x } +func (w *Menu) SetY(y int) { w.y = y } +func (w *Menu) GetX() int { return w.x } +func (w *Menu) GetY() int { return w.y } +func (w *Menu) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *Menu) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *Menu) SetW(x int) { w.w = x } +func (w *Menu) SetH(y int) { w.h = y } +func (w *Menu) GetW() int { return w.w } +func (w *Menu) GetH() int { return w.y } +func (w *Menu) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *Menu) Focusable() bool { return true } +func (w *Menu) SetFocusable(b bool) { w.focusable = b } func (w *Menu) WantW() int { var maxW int @@ -241,7 +247,6 @@ func (w *Menu) MinH() int { } 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) GetItems() []*MenuItem { return w.items } func (w *Menu) GetItem(idx int) *MenuItem { diff --git a/wdgt_menu_item.go b/wdgt_menu_item.go index 3edbe9f..36fce5f 100644 --- a/wdgt_menu_item.go +++ b/wdgt_menu_item.go @@ -27,14 +27,14 @@ import ( ) type MenuItem struct { - id string - label string - style tcell.Style - active bool - visible bool - tabbable bool - x, y int - w, h int + id string + label string + style tcell.Style + active bool + visible bool + focusable bool + x, y int + w, h int menuType MenuType cursor int @@ -75,7 +75,7 @@ func (w *MenuItem) Init(id string, style tcell.Style) { for i := range w.items { w.items[i].SetActive(i == w.cursor) } - w.tabbable = true + w.focusable = true } func (w *MenuItem) Id() string { return w.id } func (w *MenuItem) HandleResize(ev *tcell.EventResize) { @@ -83,6 +83,17 @@ func (w *MenuItem) HandleResize(ev *tcell.EventResize) { // TODO: Trickle-down HandleResize } +func (w *MenuItem) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *MenuItem) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *MenuItem) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *MenuItem) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false @@ -157,10 +168,9 @@ func (w *MenuItem) WantH() int { } return ret } -func (w *MenuItem) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *MenuItem) Focusable() bool { return !w.disabled } -func (w *MenuItem) SetTabbable(b bool) { w.tabbable = b } -func (w *MenuItem) Tabbable() bool { return w.tabbable } +func (w *MenuItem) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *MenuItem) Focusable() bool { return w.focusable && !w.disabled } +func (w *MenuItem) SetFocusable(b bool) { w.focusable = b } // How much width this item wants func (w *MenuItem) Expand(e bool) { diff --git a/wdgt_prompt.go b/wdgt_prompt.go index 57e2543..d03d189 100644 --- a/wdgt_prompt.go +++ b/wdgt_prompt.go @@ -34,17 +34,19 @@ type Prompt struct { id string style tcell.Style - x, y int - w, h int - active bool - visible bool - tabbable bool + x, y int + w, h int + active bool + visible bool + focusable bool title string message *Text field *Field btnOk, btnCancel *Button onOk func(string) bool + + keyMap KeyMap } var _ Widget = (*Prompt)(nil) @@ -64,7 +66,8 @@ func (w *Prompt) Init(id string, style tcell.Style) { w.btnOk.SetLabel("Ok") w.btnCancel = NewButton(fmt.Sprintf("%s-cancel", id), style) w.btnCancel.SetLabel("Cancel") - w.tabbable = true + w.focusable = true + w.keyMap = BlankKeyMap() } func (w *Prompt) Id() string { return w.id } func (w *Prompt) HandleResize(ev *tcell.EventResize) { @@ -80,11 +83,22 @@ func (w *Prompt) HandleResize(ev *tcell.EventResize) { w.btnCancel.SetPos(Coord{X: w.x + 1, Y: w.y + w.h - 1}) } +func (w *Prompt) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Prompt) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Prompt) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *Prompt) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false } - return false + return w.keyMap.Handle(ev) } func (w *Prompt) HandleTime(ev *tcell.EventTime) {} func (w *Prompt) Draw(screen tcell.Screen) { @@ -101,24 +115,23 @@ func (w *Prompt) Draw(screen tcell.Screen) { w.GetPos().DrawOffset(w.btnCancel, screen) } -func (w *Prompt) Active() bool { return w.active } -func (w *Prompt) SetActive(a bool) { w.active = a } -func (w *Prompt) Visible() bool { return w.visible } -func (w *Prompt) SetVisible(a bool) { w.visible = a } -func (w *Prompt) SetX(x int) { w.x = x } -func (w *Prompt) SetY(y int) { w.y = y } -func (w *Prompt) GetX() int { return w.x } -func (w *Prompt) GetY() int { return w.y } -func (w *Prompt) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *Prompt) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *Prompt) SetW(x int) { w.w = x } -func (w *Prompt) SetH(y int) { w.h = y } -func (w *Prompt) GetW() int { return w.w } -func (w *Prompt) GetH() int { return w.y } -func (w *Prompt) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *Prompt) Focusable() bool { return true } -func (w *Prompt) SetTabbable(b bool) { w.tabbable = b } -func (w *Prompt) Tabbable() bool { return w.tabbable } +func (w *Prompt) Active() bool { return w.active } +func (w *Prompt) SetActive(a bool) { w.active = a } +func (w *Prompt) Visible() bool { return w.visible } +func (w *Prompt) SetVisible(a bool) { w.visible = a } +func (w *Prompt) SetX(x int) { w.x = x } +func (w *Prompt) SetY(y int) { w.y = y } +func (w *Prompt) GetX() int { return w.x } +func (w *Prompt) GetY() int { return w.y } +func (w *Prompt) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *Prompt) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *Prompt) SetW(x int) { w.w = x } +func (w *Prompt) SetH(y int) { w.h = y } +func (w *Prompt) GetW() int { return w.w } +func (w *Prompt) GetH() int { return w.y } +func (w *Prompt) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *Prompt) Focusable() bool { return w.focusable } +func (w *Prompt) SetFocusable(b bool) { w.focusable = b } func (w *Prompt) WantW() int { return w.btnOk.WantW() + w.btnCancel.WantW() + 4 } diff --git a/wdgt_relative_layout.go b/wdgt_relative_layout.go index a018b1c..8823595 100644 --- a/wdgt_relative_layout.go +++ b/wdgt_relative_layout.go @@ -28,15 +28,16 @@ import ( type RelativeLayout struct { id string style tcell.Style + x, y int + w, h int widgetRelations map[Widget][]widgetRelation widgets []Widget - active bool - visible bool - tabbable bool + active bool + visible bool + focusable bool - x, y int - w, h int + keyMap KeyMap } var _ Widget = (*RelativeLayout)(nil) @@ -74,18 +75,26 @@ func (w *RelativeLayout) Init(id string, style tcell.Style) { w.id = id w.style = style w.visible = true - w.tabbable = true + w.focusable = true + w.keyMap = BlankKeyMap() } func (w *RelativeLayout) Id() string { return w.id } func (w *RelativeLayout) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() - - // w.w = wh.Min(w.w, w.WantW()) - // w.h = wh.Min(w.h, w.WantH()) - // TODO: Trickle-down HandleResize } -func (w *RelativeLayout) HandleKey(ev *tcell.EventKey) bool { return false } + +func (w *RelativeLayout) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *RelativeLayout) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *RelativeLayout) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} +func (w *RelativeLayout) HandleKey(ev *tcell.EventKey) bool { return w.keyMap.Handle(ev) } func (w *RelativeLayout) HandleTime(ev *tcell.EventTime) {} func (w *RelativeLayout) Draw(screen tcell.Screen) { if !w.visible { @@ -105,26 +114,25 @@ func (w *RelativeLayout) Draw(screen tcell.Screen) { */ } -func (w *RelativeLayout) Active() bool { return w.active } -func (w *RelativeLayout) SetActive(a bool) { w.active = a } -func (w *RelativeLayout) Visible() bool { return w.visible } -func (w *RelativeLayout) SetVisible(a bool) { w.visible = a } -func (w *RelativeLayout) Focusable() bool { return true } -func (w *RelativeLayout) SetTabbable(b bool) { w.tabbable = b } -func (w *RelativeLayout) Tabbable() bool { return w.tabbable } -func (w *RelativeLayout) SetX(x int) { w.x = x } -func (w *RelativeLayout) SetY(y int) { w.y = y } -func (w *RelativeLayout) GetX() int { return w.x } -func (w *RelativeLayout) GetY() int { return w.y } -func (w *RelativeLayout) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *RelativeLayout) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *RelativeLayout) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *RelativeLayout) SetW(wd int) { w.w = wd } -func (w *RelativeLayout) SetH(h int) { w.h = h } -func (w *RelativeLayout) GetW() int { return w.w } -func (w *RelativeLayout) GetH() int { return w.h } -func (w *RelativeLayout) WantW() int { return 1 } -func (w *RelativeLayout) WantH() int { return 1 } +func (w *RelativeLayout) Active() bool { return w.active } +func (w *RelativeLayout) SetActive(a bool) { w.active = a } +func (w *RelativeLayout) Visible() bool { return w.visible } +func (w *RelativeLayout) SetVisible(a bool) { w.visible = a } +func (w *RelativeLayout) Focusable() bool { return w.focusable } +func (w *RelativeLayout) SetFocusable(b bool) { w.focusable = b } +func (w *RelativeLayout) SetX(x int) { w.x = x } +func (w *RelativeLayout) SetY(y int) { w.y = y } +func (w *RelativeLayout) GetX() int { return w.x } +func (w *RelativeLayout) GetY() int { return w.y } +func (w *RelativeLayout) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *RelativeLayout) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *RelativeLayout) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *RelativeLayout) SetW(wd int) { w.w = wd } +func (w *RelativeLayout) SetH(h int) { w.h = h } +func (w *RelativeLayout) GetW() int { return w.w } +func (w *RelativeLayout) GetH() int { return w.h } +func (w *RelativeLayout) WantW() int { return 1 } +func (w *RelativeLayout) WantH() int { return 1 } func (w *RelativeLayout) MinW() int { // Find the highest value for x in all widgets GetX() + MinW() var minW int diff --git a/wdgt_searcher.go b/wdgt_searcher.go index 541f473..40faa2f 100644 --- a/wdgt_searcher.go +++ b/wdgt_searcher.go @@ -33,11 +33,11 @@ type Searcher struct { id string style tcell.Style - x, y int - w, h int - active bool - visible bool - tabbable bool + x, y int + w, h int + active bool + visible bool + focusable bool title string search *Field @@ -79,7 +79,7 @@ func (w *Searcher) Init(id string, style tcell.Style) { tcell.KeyPgDn: w.handleKeyPgDn, tcell.KeyEnter: w.handleKeyEnter, }) - w.tabbable = true + w.focusable = true } func (w *Searcher) Id() string { return w.id } @@ -94,6 +94,17 @@ func (w *Searcher) HandleResize(ev *tcell.EventResize) { w.search.HandleResize(Coord{X: aW, Y: aH}.ResizeEvent()) } +func (w *Searcher) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Searcher) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Searcher) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *Searcher) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false @@ -245,16 +256,15 @@ func (w *Searcher) WantH() int { return ret } -func (w *Searcher) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *Searcher) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *Searcher) SetW(x int) { w.w = x } -func (w *Searcher) SetH(y int) { w.h = y } -func (w *Searcher) GetW() int { return w.w } -func (w *Searcher) GetH() int { return w.h } -func (w *Searcher) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *Searcher) Focusable() bool { return true } -func (w *Searcher) SetTabbable(b bool) { w.tabbable = b } -func (w *Searcher) Tabbable() bool { return w.tabbable } +func (w *Searcher) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *Searcher) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *Searcher) SetW(x int) { w.w = x } +func (w *Searcher) SetH(y int) { w.h = y } +func (w *Searcher) GetW() int { return w.w } +func (w *Searcher) GetH() int { return w.h } +func (w *Searcher) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *Searcher) Focusable() bool { return w.focusable } +func (w *Searcher) SetFocusable(b bool) { w.focusable = b } func (w *Searcher) MinW() int { return 2 + w.search.MinW() } diff --git a/wdgt_shrinkwrap.go b/wdgt_shrinkwrap.go index a018674..27ddc16 100644 --- a/wdgt_shrinkwrap.go +++ b/wdgt_shrinkwrap.go @@ -38,29 +38,32 @@ func NewShrinkWrap(w Widget) *ShrinkWrap { func (w *ShrinkWrap) Init(id string, st tcell.Style) { w.widget.Init(id, st) } func (w *ShrinkWrap) Id() string { return w.widget.Id() } func (w *ShrinkWrap) HandleResize(ev *tcell.EventResize) { w.widget.HandleResize(ev) } -func (w *ShrinkWrap) HandleKey(ev *tcell.EventKey) bool { return w.widget.HandleKey(ev) } -func (w *ShrinkWrap) HandleTime(ev *tcell.EventTime) { w.widget.HandleTime(ev) } -func (w *ShrinkWrap) Draw(screen tcell.Screen) { w.widget.Draw(screen) } -func (w *ShrinkWrap) Active() bool { return w.widget.Active() } -func (w *ShrinkWrap) SetActive(a bool) { w.widget.SetActive(a) } -func (w *ShrinkWrap) Visible() bool { return w.widget.Visible() } -func (w *ShrinkWrap) SetVisible(v bool) { w.widget.SetVisible(v) } -func (w *ShrinkWrap) Focusable() bool { return w.widget.Focusable() } -func (w *ShrinkWrap) Tabbable() bool { return w.widget.Tabbable() } -func (w *ShrinkWrap) SetTabbable(t bool) { w.widget.SetTabbable(t) } -func (w *ShrinkWrap) SetX(x int) { w.widget.SetX(x) } -func (w *ShrinkWrap) SetY(y int) { w.widget.SetY(y) } -func (w *ShrinkWrap) GetX() int { return w.widget.GetX() } -func (w *ShrinkWrap) GetY() int { return w.widget.GetY() } -func (w *ShrinkWrap) GetPos() Coord { return w.widget.GetPos() } -func (w *ShrinkWrap) SetPos(pos Coord) { w.widget.SetPos(pos) } -func (w *ShrinkWrap) SetSize(size Coord) { w.widget.SetSize(size) } -func (w *ShrinkWrap) SetW(wd int) { w.widget.SetW(wd) } -func (w *ShrinkWrap) SetH(h int) { w.widget.SetH(h) } -func (w *ShrinkWrap) GetW() int { return w.widget.GetW() } -func (w *ShrinkWrap) GetH() int { return w.widget.GetH() } -func (w *ShrinkWrap) WantW() int { return w.widget.MinW() } -func (w *ShrinkWrap) WantH() int { return w.widget.MinH() } -func (w *ShrinkWrap) MinW() int { return w.widget.MinW() } -func (w *ShrinkWrap) MinH() int { return w.widget.MinH() } +func (w *ShrinkWrap) SetKeyMap(km KeyMap) { w.widget.SetKeyMap(km) } +func (w *ShrinkWrap) AddToKeyMap(km KeyMap) { w.widget.AddToKeyMap(km) } +func (w *ShrinkWrap) RemoveFromKeyMap(km KeyMap) { w.widget.RemoveFromKeyMap(km) } +func (w *ShrinkWrap) HandleKey(ev *tcell.EventKey) bool { return w.widget.HandleKey(ev) } +func (w *ShrinkWrap) HandleTime(ev *tcell.EventTime) { w.widget.HandleTime(ev) } +func (w *ShrinkWrap) Draw(screen tcell.Screen) { w.widget.Draw(screen) } + +func (w *ShrinkWrap) Active() bool { return w.widget.Active() } +func (w *ShrinkWrap) SetActive(a bool) { w.widget.SetActive(a) } +func (w *ShrinkWrap) Visible() bool { return w.widget.Visible() } +func (w *ShrinkWrap) SetVisible(v bool) { w.widget.SetVisible(v) } +func (w *ShrinkWrap) Focusable() bool { return w.widget.Focusable() } +func (w *ShrinkWrap) SetFocusable(t bool) { w.widget.SetFocusable(t) } +func (w *ShrinkWrap) SetX(x int) { w.widget.SetX(x) } +func (w *ShrinkWrap) SetY(y int) { w.widget.SetY(y) } +func (w *ShrinkWrap) GetX() int { return w.widget.GetX() } +func (w *ShrinkWrap) GetY() int { return w.widget.GetY() } +func (w *ShrinkWrap) GetPos() Coord { return w.widget.GetPos() } +func (w *ShrinkWrap) SetPos(pos Coord) { w.widget.SetPos(pos) } +func (w *ShrinkWrap) SetSize(size Coord) { w.widget.SetSize(size) } +func (w *ShrinkWrap) SetW(wd int) { w.widget.SetW(wd) } +func (w *ShrinkWrap) SetH(h int) { w.widget.SetH(h) } +func (w *ShrinkWrap) GetW() int { return w.widget.GetW() } +func (w *ShrinkWrap) GetH() int { return w.widget.GetH() } +func (w *ShrinkWrap) WantW() int { return w.widget.MinW() } +func (w *ShrinkWrap) WantH() int { return w.widget.MinH() } +func (w *ShrinkWrap) MinW() int { return w.widget.MinW() } +func (w *ShrinkWrap) MinH() int { return w.widget.MinH() } diff --git a/wdgt_table.go b/wdgt_table.go index 112385f..d34fcf1 100644 --- a/wdgt_table.go +++ b/wdgt_table.go @@ -36,7 +36,6 @@ type Table struct { active bool visible bool focusable bool - tabbable bool header []string footer []string @@ -52,6 +51,8 @@ type Table struct { w, h int columnWidths []int + + keyMap KeyMap } var _ Widget = (*Table)(nil) @@ -82,21 +83,30 @@ func (w *Table) Init(id string, style tcell.Style) { w.style = style w.visible = true w.border = wh.BRD_CSIMPLE - w.tabbable = true + w.focusable = true + w.keyMap = BlankKeyMap() } func (w *Table) Id() string { return w.id } func (w *Table) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() +} - // w.w = wh.Min(w.w, w.WantW()) - // w.h = wh.Min(w.h, w.WantH()) +func (w *Table) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Table) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Table) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } } func (w *Table) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false } - return false + return w.keyMap.Handle(ev) } func (w *Table) HandleTime(ev *tcell.EventTime) {} func (w *Table) Draw(screen tcell.Screen) { @@ -252,13 +262,11 @@ func (w *Table) MinH() int { return datLen } -func (w *Table) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *Table) Focusable() bool { return w.focusable } -func (w *Table) SetTabbable(b bool) { w.tabbable = b } -func (w *Table) Tabbable() bool { return w.tabbable } +func (w *Table) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *Table) Focusable() bool { return w.focusable } +func (w *Table) SetFocusable(b bool) { w.focusable = b } func (w *Table) SetTitle(ttl string) { w.title = ttl } -func (w *Table) SetFocusable(f bool) { w.focusable = f } func (w *Table) SetBorder(b []rune) { w.border = b } func (w *Table) AddRow(row []string) { w.data = append(w.data, row) } diff --git a/wdgt_text.go b/wdgt_text.go index 9f743a8..ad3a0c4 100644 --- a/wdgt_text.go +++ b/wdgt_text.go @@ -31,14 +31,16 @@ import ( // Currently the text widget will only split up text on newlines // TODO: Add 'wrap' mode, which will automatically split it into lines. type Text struct { - id string - text string - message []string - style tcell.Style - x, y int - w, h int - visible bool - tabbable bool + id string + text string + message []string + style tcell.Style + x, y int + w, h int + visible bool + active bool + focusable bool + keyMap KeyMap } var _ Widget = (*Text)(nil) @@ -53,11 +55,22 @@ func (w *Text) Init(id string, style tcell.Style) { w.id = id w.style = style w.visible = true - w.tabbable = false + w.focusable = false + w.keyMap = BlankKeyMap() } -func (w *Text) Id() string { return w.id } -func (w *Text) HandleResize(ev *tcell.EventResize) { - w.w, w.h = ev.Size() + +func (w *Text) Id() string { return w.id } +func (w *Text) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() } + +func (w *Text) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *Text) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *Text) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } } func (w *Text) HandleKey(ev *tcell.EventKey) bool { return false } func (w *Text) HandleTime(ev *tcell.EventTime) {} @@ -72,28 +85,27 @@ func (w *Text) Draw(screen tcell.Screen) { } } -func (w *Text) Active() bool { return false } -func (w *Text) SetActive(a bool) {} -func (w *Text) Visible() bool { return w.visible } -func (w *Text) SetVisible(a bool) { w.visible = a } -func (w *Text) SetX(x int) { w.x = x } -func (w *Text) SetY(y int) { w.y = y } -func (w *Text) GetX() int { return w.x } -func (w *Text) GetY() int { return w.y } -func (w *Text) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *Text) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *Text) SetW(x int) { w.w = x } -func (w *Text) SetH(y int) { w.h = y } -func (w *Text) GetW() int { return w.w } -func (w *Text) GetH() int { return w.y } -func (w *Text) WantW() int { return wh.Longest(w.message) } -func (w *Text) WantH() int { return len(w.message) } -func (w *Text) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *Text) Focusable() bool { return false } -func (w *Text) SetTabbable(b bool) { w.tabbable = b } -func (w *Text) Tabbable() bool { return w.tabbable } -func (w *Text) MinW() int { return wh.Longest(w.message) } -func (w *Text) MinH() int { return len(w.message) } +func (w *Text) Active() bool { return w.active } +func (w *Text) SetActive(a bool) { w.active = a } +func (w *Text) Visible() bool { return w.visible } +func (w *Text) SetVisible(a bool) { w.visible = a } +func (w *Text) SetX(x int) { w.x = x } +func (w *Text) SetY(y int) { w.y = y } +func (w *Text) GetX() int { return w.x } +func (w *Text) GetY() int { return w.y } +func (w *Text) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *Text) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *Text) SetW(x int) { w.w = x } +func (w *Text) SetH(y int) { w.h = y } +func (w *Text) GetW() int { return w.w } +func (w *Text) GetH() int { return w.h } +func (w *Text) WantW() int { return wh.Longest(w.message) } +func (w *Text) WantH() int { return len(w.message) } +func (w *Text) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *Text) Focusable() bool { return w.focusable } +func (w *Text) SetFocusable(b bool) { w.focusable = b } +func (w *Text) MinW() int { return wh.Longest(w.message) } +func (w *Text) MinH() int { return len(w.message) } func (w *Text) SetText(txt string) { w.text = txt diff --git a/wdgt_timefield.go b/wdgt_timefield.go index 651aa67..caa0bad 100644 --- a/wdgt_timefield.go +++ b/wdgt_timefield.go @@ -31,12 +31,12 @@ import ( // TODO: Fix this, it's not drawing correctly. type TimeField struct { - id string - label string - style tcell.Style - active bool - visible bool - tabbable bool + id string + label string + style tcell.Style + active bool + visible bool + focusable bool x, y int w, h int @@ -92,7 +92,7 @@ func (w *TimeField) Init(id string, style tcell.Style) { tcell.KeyEnd: w.handleEnd, }) w.visible = true - w.tabbable = true + w.focusable = true } func (w *TimeField) Id() string { return w.id } @@ -138,6 +138,17 @@ func (w *TimeField) HandleResize(ev *tcell.EventResize) { } } +func (w *TimeField) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *TimeField) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *TimeField) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) + } +} + func (w *TimeField) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false @@ -167,26 +178,25 @@ func (w *TimeField) Draw(screen tcell.Screen) { } } -func (w *TimeField) Active() bool { return w.active } -func (w *TimeField) SetActive(a bool) { w.active = a } -func (w *TimeField) Visible() bool { return w.visible } -func (w *TimeField) SetVisible(a bool) { w.visible = a } -func (w *TimeField) SetX(x int) { w.x = x } -func (w *TimeField) SetY(y int) { w.y = y } -func (w *TimeField) GetX() int { return w.x } -func (w *TimeField) GetY() int { return w.y } -func (w *TimeField) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *TimeField) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *TimeField) SetW(x int) { w.w = x } -func (w *TimeField) SetH(y int) { w.h = y } -func (w *TimeField) GetW() int { return w.w } -func (w *TimeField) GetH() int { return w.y } -func (w *TimeField) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *TimeField) Focusable() bool { return true } -func (w *TimeField) SetTabbable(b bool) { w.tabbable = b } -func (w *TimeField) Tabbable() bool { return w.tabbable } -func (w *TimeField) WantW() int { return w.MinW() } -func (w *TimeField) WantH() int { return w.MinH() } +func (w *TimeField) Active() bool { return w.active } +func (w *TimeField) SetActive(a bool) { w.active = a } +func (w *TimeField) Visible() bool { return w.visible } +func (w *TimeField) SetVisible(a bool) { w.visible = a } +func (w *TimeField) SetX(x int) { w.x = x } +func (w *TimeField) SetY(y int) { w.y = y } +func (w *TimeField) GetX() int { return w.x } +func (w *TimeField) GetY() int { return w.y } +func (w *TimeField) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *TimeField) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *TimeField) SetW(x int) { w.w = x } +func (w *TimeField) SetH(y int) { w.h = y } +func (w *TimeField) GetW() int { return w.w } +func (w *TimeField) GetH() int { return w.y } +func (w *TimeField) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *TimeField) Focusable() bool { return w.focusable } +func (w *TimeField) SetFocusable(b bool) { w.focusable = b } +func (w *TimeField) WantW() int { return w.MinW() } +func (w *TimeField) WantH() int { return w.MinH() } func (w *TimeField) MinW() int { wdt := 0 if w.fldYear.Visible() { diff --git a/wdgt_top_menu_layout.go b/wdgt_top_menu_layout.go index 8523f26..06a7de7 100644 --- a/wdgt_top_menu_layout.go +++ b/wdgt_top_menu_layout.go @@ -39,8 +39,12 @@ type TopMenuLayout struct { menu *Menu widget Widget - active bool - visible bool + active bool + visible bool + focusable bool + + layoutFlags LayoutFlag + keyMap KeyMap logger func(string, ...any) } @@ -62,9 +66,19 @@ func (w *TopMenuLayout) Init(id string, s tcell.Style) { w.menu = NewMenu(fmt.Sprintf("%s.mainmenu", id), tcell.StyleDefault) w.menu.SetActive(false) w.menu.SetType(MenuTypeH) - w.menu.SetTabbable(false) w.widget = NewBlankWidget("blank") + w.keyMap = BlankKeyMap() + w.keyMap.Add(tcell.KeyEscape, func(ev *tcell.EventKey) bool { + if w.menu != nil { + w.menu.SetActive(!w.menu.Active()) + if w.widget != nil { + w.widget.SetActive(!w.menu.Active()) + } + return true + } + return false + }) } func (w *TopMenuLayout) Id() string { return w.id } @@ -76,8 +90,35 @@ func (w *TopMenuLayout) HandleResize(ev *tcell.EventResize) { } if w.widget != nil { - w.widget.SetPos(Coord{X: 0, Y: 1}) w.widget.HandleResize(tcell.NewEventResize(w.w, w.h-1)) + w.Log("Widget Size: %d,%d", w.widget.GetW(), w.widget.GetH()) + x, y := 0, 1 + + switch w.layoutFlags.AlignH() { + case LFAlignHRight: + x = w.w - w.widget.GetW() + case LFAlignHCenter: + x = (w.w / 2) - (w.widget.GetW() / 2) + } + + switch w.layoutFlags.AlignV() { + case LFAlignVBottom: + y = w.h - w.widget.GetH() + case LFAlignVCenter: + y = (w.h / 2) - (w.widget.GetH() / 2) + } + w.widget.SetPos(Coord{X: x, Y: y}) + } +} + +func (w *TopMenuLayout) SetKeyMap(km KeyMap) { w.keyMap = km } +func (w *TopMenuLayout) AddToKeyMap(km KeyMap) { w.keyMap.Merge(km) } +func (w *TopMenuLayout) RemoveFromKeyMap(km KeyMap) { + for k := range km.Keys { + w.keyMap.Remove(k) + } + for r := range km.Runes { + w.keyMap.RemoveRune(r) } } @@ -85,17 +126,13 @@ func (w *TopMenuLayout) HandleKey(ev *tcell.EventKey) bool { if !w.active { return false } - if ev.Key() == tcell.KeyEscape && w.menu != nil { - w.menu.SetActive(!w.menu.Active()) - if w.widget != nil { - w.widget.SetActive(!w.menu.Active()) - } + + if w.keyMap.Handle(ev) { return true } - if w.menu.Active() { + if w.menu != nil && w.menu.Active() { return w.menu.HandleKey(ev) } - // Pass the key through to the main widget if w.widget != nil { // If we're here, that means the menu isn't active, but we are. So the @@ -132,22 +169,21 @@ func (w *TopMenuLayout) SetActive(a bool) { w.widget.SetActive(a) } } -func (w *TopMenuLayout) Visible() bool { return w.visible } -func (w *TopMenuLayout) SetVisible(a bool) { w.visible = a } -func (w *TopMenuLayout) SetX(x int) { w.x = x } -func (w *TopMenuLayout) SetY(y int) { w.y = y } -func (w *TopMenuLayout) GetX() int { return w.x } -func (w *TopMenuLayout) GetY() int { return w.y } -func (w *TopMenuLayout) GetPos() Coord { return Coord{X: w.x, Y: w.y} } -func (w *TopMenuLayout) SetPos(c Coord) { w.x, w.y = c.X, c.Y } -func (w *TopMenuLayout) SetW(x int) { w.w = x } -func (w *TopMenuLayout) SetH(y int) { w.h = y } -func (w *TopMenuLayout) GetW() int { return w.w } -func (w *TopMenuLayout) GetH() int { return w.y } -func (w *TopMenuLayout) SetSize(c Coord) { w.w, w.h = c.X, c.Y } -func (w *TopMenuLayout) Focusable() bool { return true } -func (w *TopMenuLayout) SetTabbable(b bool) {} -func (w *TopMenuLayout) Tabbable() bool { return true } +func (w *TopMenuLayout) Visible() bool { return w.visible } +func (w *TopMenuLayout) SetVisible(a bool) { w.visible = a } +func (w *TopMenuLayout) SetX(x int) { w.x = x } +func (w *TopMenuLayout) SetY(y int) { w.y = y } +func (w *TopMenuLayout) GetX() int { return w.x } +func (w *TopMenuLayout) GetY() int { return w.y } +func (w *TopMenuLayout) GetPos() Coord { return Coord{X: w.x, Y: w.y} } +func (w *TopMenuLayout) SetPos(c Coord) { w.x, w.y = c.X, c.Y } +func (w *TopMenuLayout) SetW(x int) { w.w = x } +func (w *TopMenuLayout) SetH(y int) { w.h = y } +func (w *TopMenuLayout) GetW() int { return w.w } +func (w *TopMenuLayout) GetH() int { return w.y } +func (w *TopMenuLayout) SetSize(c Coord) { w.w, w.h = c.X, c.Y } +func (w *TopMenuLayout) Focusable() bool { return w.focusable } +func (w *TopMenuLayout) SetFocusable(b bool) { w.focusable = b } func (w *TopMenuLayout) WantW() int { return wh.MaxInt } func (w *TopMenuLayout) WantH() int { return wh.MaxInt } @@ -159,6 +195,24 @@ func (w *TopMenuLayout) AddMenuItems(iL ...*MenuItem) { w.menu.AddItems(iL... func (w *TopMenuLayout) RemoveMenuItems(iL ...*MenuItem) { w.menu.RemoveItems(iL...) } func (w *TopMenuLayout) SetWidget(wd Widget) { w.widget = wd } +func (w *TopMenuLayout) AddLayoutFlag(f LayoutFlag) { + if f.IsAlignH() { + w.layoutFlags.ClearAlignH() + } + if f.IsAlignV() { + w.layoutFlags.ClearAlignV() + } + w.layoutFlags.Add(f) +} + +func (w *TopMenuLayout) RemoveLayoutFlag(f LayoutFlag) { + if f.IsAlignH() { + w.layoutFlags.ClearAlignH() + } + if f.IsAlignV() { + w.layoutFlags.ClearAlignV() + } +} func (w *TopMenuLayout) SetLogger(l func(string, ...any)) { w.logger = l } func (w *TopMenuLayout) Log(txt string, args ...any) { @@ -179,11 +233,9 @@ func (w *TopMenuLayout) CreateMenuItem(id, lbl string, do func() bool, subItems func (w *TopMenuLayout) SetItemVisible(id string, v bool) bool { if mi := w.FindItem(id); mi != nil { - w.Log("%s.SetItemVisible: Found Item: %s (Set Visible: %t)", w.Id(), id, v) mi.SetVisible(v) return true } - w.Log("%s.SetItemVisible: Didn't find item with id: %s", w.Id(), id) return false } diff --git a/widget.go b/widget.go index 7a9bde9..d131c65 100644 --- a/widget.go +++ b/widget.go @@ -32,14 +32,16 @@ type Widget interface { HandleResize(*tcell.EventResize) HandleKey(*tcell.EventKey) bool HandleTime(*tcell.EventTime) + SetKeyMap(km KeyMap) + AddToKeyMap(km KeyMap) + RemoveFromKeyMap(km KeyMap) Draw(tcell.Screen) Active() bool SetActive(bool) Visible() bool SetVisible(bool) Focusable() bool - Tabbable() bool - SetTabbable(bool) + SetFocusable(bool) SetX(int) SetY(int) GetX() int