A lot of layout work
This commit is contained in:
46
coord.go
Normal file
46
coord.go
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright © Brian Buller <brian@bullercodeworks.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
package widgets
|
||||
|
||||
import "github.com/gdamore/tcell"
|
||||
|
||||
type Coord struct {
|
||||
X, Y int
|
||||
}
|
||||
|
||||
func (p Coord) Add(o Coord) Coord {
|
||||
return Coord{
|
||||
X: p.X + o.X,
|
||||
Y: p.Y + o.Y,
|
||||
}
|
||||
}
|
||||
|
||||
func (p Coord) ResizeEvent() *tcell.EventResize {
|
||||
return tcell.NewEventResize(p.X, p.Y)
|
||||
}
|
||||
|
||||
func (p Coord) DrawOffset(w Widget, screen tcell.Screen) {
|
||||
pr := w.GetPos()
|
||||
w.SetPos(p.Add(pr))
|
||||
w.Draw(screen)
|
||||
w.SetPos(pr)
|
||||
}
|
||||
BIN
example/example
Executable file
BIN
example/example
Executable file
Binary file not shown.
34
example/main.go
Normal file
34
example/main.go
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
Copyright © Brian Buller <brian@bullercodeworks.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
package main
|
||||
|
||||
func main() {
|
||||
app := NewUi()
|
||||
defer func() {
|
||||
maybePanic := recover()
|
||||
app.cleanup()
|
||||
if maybePanic != nil {
|
||||
panic(maybePanic)
|
||||
}
|
||||
}()
|
||||
app.run()
|
||||
}
|
||||
118
example/ui.go
Normal file
118
example/ui.go
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright © Brian Buller <brian@bullercodeworks.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
type Ui struct {
|
||||
h, w int
|
||||
|
||||
running bool
|
||||
tScreen tcell.Screen
|
||||
style tcell.Style
|
||||
|
||||
screen *UiScreen
|
||||
|
||||
cursor int
|
||||
}
|
||||
|
||||
func NewUi() *Ui {
|
||||
a := Ui{}
|
||||
err := a.init()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
return &a
|
||||
}
|
||||
|
||||
func (ui *Ui) init() error {
|
||||
var err error
|
||||
if ui.tScreen, err = tcell.NewScreen(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = ui.tScreen.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ui.style = tcell.StyleDefault
|
||||
ui.tScreen.SetStyle(ui.style)
|
||||
ui.tScreen.Clear()
|
||||
|
||||
ui.SetScreen(&UiScreen{})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ui *Ui) run() error {
|
||||
ui.running = true
|
||||
|
||||
for {
|
||||
if !ui.running {
|
||||
return nil
|
||||
}
|
||||
|
||||
ui.tScreen.Clear()
|
||||
ui.screen.Draw()
|
||||
|
||||
ui.tScreen.Show()
|
||||
|
||||
// Poll Events
|
||||
ev := ui.tScreen.PollEvent()
|
||||
switch ev := ev.(type) {
|
||||
case *tcell.EventResize:
|
||||
ui.tScreen.Sync()
|
||||
ui.w, ui.h = ev.Size()
|
||||
ui.handleResize(ev)
|
||||
|
||||
case *tcell.EventKey:
|
||||
if ev.Key() == tcell.KeyCtrlC {
|
||||
ui.stop()
|
||||
return nil
|
||||
}
|
||||
ui.screen.HandleKey(ev)
|
||||
|
||||
case *tcell.EventTime:
|
||||
ui.screen.HandleTime(ev)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *Ui) handleResize(ev *tcell.EventResize) {
|
||||
ui.w, ui.h = ev.Size()
|
||||
ui.screen.HandleResize(ev)
|
||||
}
|
||||
|
||||
func (ui *Ui) GetSize() (int, int) { return ui.tScreen.Size() }
|
||||
func (ui *Ui) SetScreen(scr *UiScreen) {
|
||||
ui.screen = scr
|
||||
ui.screen.Init(ui)
|
||||
ui.screen.HandleResize(tcell.NewEventResize(ui.GetSize()))
|
||||
}
|
||||
|
||||
func (ui *Ui) stop() { ui.running = false }
|
||||
func (ui *Ui) cleanup() { ui.tScreen.Fini() }
|
||||
129
example/ui_screen.go
Normal file
129
example/ui_screen.go
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Copyright © Brian Buller <brian@bullercodeworks.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
w "git.bullercodeworks.com/brian/tcell-widgets"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
type UiScreen struct {
|
||||
ui *Ui
|
||||
w, h int
|
||||
|
||||
widget w.Widget
|
||||
log *w.Cli
|
||||
|
||||
widgets []w.Widget
|
||||
cursor int
|
||||
}
|
||||
|
||||
func (s *UiScreen) Init(ui *Ui) {
|
||||
// Set up our log viewer
|
||||
s.ui = ui
|
||||
s.log = w.NewCli("log", ui.style)
|
||||
|
||||
ll := w.NewLinearLayout("test", ui.style)
|
||||
ll.SetTabbable(true)
|
||||
ll.SetLogger(s.log.Log)
|
||||
|
||||
testList := w.NewList("test-list", ui.style)
|
||||
for i := 0; i < 10; i++ {
|
||||
testList.Add(fmt.Sprintf("Item %d", i))
|
||||
}
|
||||
testList.SetLogger(s.log.Log)
|
||||
ll.Add(testList)
|
||||
|
||||
btnL := w.NewLinearLayout("btn-ll", ui.style)
|
||||
btnL.SetTabbable(true)
|
||||
btnL.SetLogger(s.log.Log)
|
||||
btnL.SetOrientation(w.LinLayH)
|
||||
|
||||
btnCancel := w.NewButton("btn-cancel", ui.style)
|
||||
btnCancel.SetLabel("Cancel")
|
||||
btnL.Add(btnCancel)
|
||||
btnL.AddFlag(btnCancel, w.LFAlignVBottom)
|
||||
|
||||
btnOk := w.NewButton("btn-ok", ui.style)
|
||||
btnOk.SetLabel("Ok")
|
||||
btnL.Add(btnOk)
|
||||
btnL.AddFlag(btnOk, w.LFAlignVBottom)
|
||||
ll.Add(btnL)
|
||||
ll.AddFlag(btnL, w.LFAlignVBottom)
|
||||
|
||||
dw := w.NewDebugWidget("debug", ui.style)
|
||||
dw.SetWidget(ll)
|
||||
dw.SetSize(w.Coord{X: 20, Y: 30})
|
||||
dw.SetActive(true)
|
||||
s.widget = dw
|
||||
|
||||
s.widgets = append(s.widgets, dw)
|
||||
s.widgets = append(s.widgets, s.log)
|
||||
}
|
||||
|
||||
func (s *UiScreen) HandleResize(ev *tcell.EventResize) {
|
||||
s.w, s.h = ev.Size()
|
||||
s.widget.HandleResize(ev)
|
||||
lgH := 20
|
||||
s.log.SetPos(w.Coord{X: 0, Y: s.h - lgH - 1})
|
||||
s.log.HandleResize(w.Coord{X: s.w, Y: lgH}.ResizeEvent())
|
||||
}
|
||||
|
||||
func (s *UiScreen) HandleKey(ev *tcell.EventKey) bool {
|
||||
if ev.Key() == tcell.KeyCtrlJ {
|
||||
// Ctrl+J is the keypad 'Enter'
|
||||
ev = tcell.NewEventKey(tcell.KeyEnter, 0, 0)
|
||||
}
|
||||
|
||||
if s.cursor < len(s.widgets) {
|
||||
if s.widgets[s.cursor].HandleKey(ev) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if ev.Key() == tcell.KeyTab {
|
||||
s.cursor = (s.cursor + 1) % len(s.widgets)
|
||||
if s.cursor == 0 {
|
||||
s.widget.SetActive(true)
|
||||
s.log.SetActive(false)
|
||||
} else {
|
||||
s.widget.SetActive(false)
|
||||
s.log.SetActive(true)
|
||||
}
|
||||
return true
|
||||
}
|
||||
if s.cursor == 0 {
|
||||
return s.widget.HandleKey(ev)
|
||||
} else {
|
||||
return s.log.HandleKey(ev)
|
||||
}
|
||||
}
|
||||
func (s *UiScreen) HandleTime(ev *tcell.EventTime) {}
|
||||
func (s *UiScreen) Draw() {
|
||||
s.widget.Draw(s.ui.tScreen)
|
||||
s.log.Draw(s.ui.tScreen)
|
||||
}
|
||||
func (s *UiScreen) Log(txt string, args ...any) {}
|
||||
|
||||
func (s *UiScreen) Exit() {}
|
||||
@@ -22,7 +22,6 @@ THE SOFTWARE.
|
||||
package widgets
|
||||
|
||||
import (
|
||||
wh "git.bullercodeworks.com/brian/tcell-widgets/helpers"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
@@ -86,8 +85,8 @@ func (w *AbsoluteLayout) Init(id string, s tcell.Style) {
|
||||
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.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
w.updateWidgetLayouts()
|
||||
}
|
||||
|
||||
@@ -140,16 +139,10 @@ func (w *AbsoluteLayout) Draw(screen tcell.Screen) {
|
||||
}
|
||||
p := w.GetPos()
|
||||
for _, wd := range w.widgets {
|
||||
wd.DrawOffset(p, screen)
|
||||
p.DrawOffset(wd, screen)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *AbsoluteLayout) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
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 }
|
||||
@@ -90,12 +90,9 @@ func (w *Alert) Init(id string, style tcell.Style) {
|
||||
func (w *Alert) Id() string { return w.id }
|
||||
func (w *Alert) 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.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
// 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))
|
||||
}
|
||||
|
||||
@@ -116,14 +113,7 @@ func (w *Alert) Draw(screen tcell.Screen) {
|
||||
}
|
||||
|
||||
wh.TitledBorderFilled(w.x, w.y, w.x+w.w, w.y+w.h, w.title, wh.BRD_SIMPLE, w.style, screen)
|
||||
w.layout.DrawOffset(w.GetPos(), screen)
|
||||
}
|
||||
|
||||
func (w *Alert) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
w.GetPos().DrawOffset(w, screen)
|
||||
}
|
||||
|
||||
func (w *Alert) Active() bool { return w.active }
|
||||
@@ -73,8 +73,8 @@ func (w *BorderedWidget) Id() string { return w.id }
|
||||
func (w *BorderedWidget) HandleResize(ev *tcell.EventResize) {
|
||||
// Trim space for border and pass the resize to the widget
|
||||
w.w, w.h = ev.Size()
|
||||
w.w = wh.Min(w.w, w.WantW())
|
||||
w.h = wh.Min(w.h, w.WantH())
|
||||
// w.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
|
||||
w.widget.HandleResize(tcell.NewEventResize(w.w-2, w.h-2))
|
||||
}
|
||||
@@ -97,15 +97,9 @@ func (w *BorderedWidget) Draw(screen tcell.Screen) {
|
||||
wh.BorderFilled(w.x, w.y, w.x+w.w, w.y+w.h, w.border, w.style, screen)
|
||||
}
|
||||
w.widget.SetPos(Coord{X: w.x + 1, Y: w.y + 1})
|
||||
w.widget.DrawOffset(w.GetPos(), screen)
|
||||
w.GetPos().DrawOffset(w, screen)
|
||||
}
|
||||
|
||||
func (w *BorderedWidget) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
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 }
|
||||
@@ -61,8 +61,8 @@ func (w *Button) Init(id string, style tcell.Style) {
|
||||
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())
|
||||
// w.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
}
|
||||
|
||||
func (w *Button) HandleKey(ev *tcell.EventKey) bool {
|
||||
@@ -118,12 +118,6 @@ 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) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
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 }
|
||||
@@ -72,8 +72,8 @@ func (w *Chat) Init(id string, s tcell.Style) {
|
||||
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())
|
||||
// w.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
}
|
||||
|
||||
func (w *Chat) HandleKey(ev *tcell.EventKey) bool {
|
||||
@@ -157,13 +157,6 @@ func (w *Chat) Draw(screen tcell.Screen) {
|
||||
// x += len(post) - 1
|
||||
}
|
||||
|
||||
func (w *Chat) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
|
||||
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 }
|
||||
@@ -66,8 +66,8 @@ func (w *Checkbox) Init(id string, style tcell.Style) {
|
||||
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())
|
||||
// w.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
}
|
||||
|
||||
func (w *Checkbox) HandleKey(ev *tcell.EventKey) bool {
|
||||
@@ -96,12 +96,6 @@ func (w *Checkbox) Draw(screen tcell.Screen) {
|
||||
wh.DrawText(w.x, w.y, fmt.Sprintf("[%s] %s", string(w.state), w.label), dStyle, screen)
|
||||
}
|
||||
|
||||
func (w *Checkbox) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
func (w *Checkbox) Active() bool { return w.active }
|
||||
func (w *Checkbox) SetActive(a bool) { w.active = a }
|
||||
func (w *Checkbox) Visible() bool { return w.visible }
|
||||
@@ -74,8 +74,11 @@ func (w *Cli) Init(id string, s tcell.Style) {
|
||||
w.tabbable = true
|
||||
}
|
||||
|
||||
func (w *Cli) Id() string { return w.id }
|
||||
func (w *Cli) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() }
|
||||
func (w *Cli) Id() string { return w.id }
|
||||
func (w *Cli) HandleResize(ev *tcell.EventResize) {
|
||||
w.w, w.h = ev.Size()
|
||||
}
|
||||
|
||||
func (w *Cli) HandleKey(ev *tcell.EventKey) bool {
|
||||
if !w.active {
|
||||
return false
|
||||
@@ -129,12 +132,18 @@ func (w *Cli) Draw(screen tcell.Screen) {
|
||||
|
||||
x, y := w.x+1, w.y+1+w.h-3
|
||||
for i := 0; i < w.h-2; i++ {
|
||||
if len(w.log) > i {
|
||||
line := w.log[len(w.log)-i-1]
|
||||
if len(w.log) > (i + w.logPosition) {
|
||||
idx := len(w.log) - (i + w.logPosition) - 1
|
||||
if idx < 0 {
|
||||
y--
|
||||
wh.DrawText(x, y, wh.PadR("", w.w-2), dStyle, screen)
|
||||
continue
|
||||
}
|
||||
line := w.log[idx]
|
||||
if len(line) > w.w-2 {
|
||||
line = line[:w.w-2]
|
||||
}
|
||||
wh.DrawText(x, y, wh.PadR(line, w.w), dStyle, screen)
|
||||
wh.DrawText(x, y, wh.PadR(line, w.w-2), dStyle, screen)
|
||||
y--
|
||||
}
|
||||
}
|
||||
@@ -153,17 +162,11 @@ func (w *Cli) Draw(screen tcell.Screen) {
|
||||
x += len(pre)
|
||||
wh.DrawText(x, y, cursor, dStyle.Reverse(w.active), screen)
|
||||
x += 1
|
||||
wh.DrawText(x, y, wh.PadR(post, w.w-x), dStyle, screen)
|
||||
wh.DrawText(x, y, wh.PadR(post, w.w-x-2), dStyle, screen)
|
||||
// wh.DrawText(w.x, y+1, fmt.Sprintf("Index: %d", w.historyPosition), dStyle, screen)
|
||||
// x += len(post) - 1
|
||||
}
|
||||
|
||||
func (w *Cli) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
|
||||
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 }
|
||||
@@ -259,6 +262,26 @@ func (w *Cli) initKeyMap() {
|
||||
}
|
||||
return true
|
||||
},
|
||||
tcell.KeyPgUp: func(ev *tcell.EventKey) bool {
|
||||
if w.historyPosition < len(w.log)-w.h-2 {
|
||||
w.historyPosition += (w.h - 2)
|
||||
if w.historyPosition > len(w.log) {
|
||||
w.historyPosition = len(w.log)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
tcell.KeyPgDn: func(ev *tcell.EventKey) bool {
|
||||
if w.historyPosition > 0 {
|
||||
w.historyPosition -= (w.h - 2)
|
||||
if w.historyPosition < 0 {
|
||||
w.historyPosition = 0
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
})
|
||||
}
|
||||
func (w *Cli) SetTitle(ttl string) { w.title = ttl }
|
||||
203
wdgt_debug.go
Normal file
203
wdgt_debug.go
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
Copyright © Brian Buller <brian@bullercodeworks.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
wh "git.bullercodeworks.com/brian/tcell-widgets/helpers"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// TODO: Make sure this works right... I don't think it does.
|
||||
type DebugWidget struct {
|
||||
id string
|
||||
style tcell.Style
|
||||
|
||||
x, y int
|
||||
w, h int
|
||||
setW, setH int
|
||||
widget Widget
|
||||
|
||||
drawRulers bool
|
||||
active bool
|
||||
visible bool
|
||||
tabbable bool
|
||||
mTL, mBR Coord // Margins (Top-Right & Bottom Left)
|
||||
|
||||
logger func(string, ...any)
|
||||
}
|
||||
|
||||
var _ Widget = (*DebugWidget)(nil)
|
||||
|
||||
func NewDebugWidget(id string, s tcell.Style) *DebugWidget {
|
||||
ret := &DebugWidget{}
|
||||
ret.Init(id, s)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (w *DebugWidget) Init(id string, s tcell.Style) {
|
||||
w.id = id
|
||||
w.style = s
|
||||
w.visible = true
|
||||
w.tabbable = true
|
||||
w.drawRulers = true
|
||||
w.setW, w.setH = -1, -1
|
||||
}
|
||||
|
||||
func (w *DebugWidget) SetLogger(l func(string, ...any)) { w.logger = l }
|
||||
func (w *DebugWidget) Log(txt string, args ...any) {
|
||||
if w.logger != nil {
|
||||
w.logger(txt, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *DebugWidget) Id() string { return w.id }
|
||||
func (w *DebugWidget) HandleResize(ev *tcell.EventResize) {
|
||||
// Trim space for border and pass the resize to the widget
|
||||
sw, sh := ev.Size()
|
||||
if w.setW > 0 {
|
||||
w.w = wh.Min(sw, w.WantW())
|
||||
} else {
|
||||
w.w = sw
|
||||
}
|
||||
if w.setH > 0 {
|
||||
w.h = wh.Min(sh, w.WantH())
|
||||
} else {
|
||||
w.h = sh
|
||||
}
|
||||
|
||||
nX, nY := 1, 1
|
||||
if w.w > 9 {
|
||||
nX = 2
|
||||
}
|
||||
if w.h > 9 {
|
||||
nY = 2
|
||||
}
|
||||
w.mTL = Coord{X: nX, Y: nY}
|
||||
w.mBR = Coord{X: 1, Y: 1}
|
||||
|
||||
w.widget.SetPos(w.mTL)
|
||||
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) Draw(screen tcell.Screen) {
|
||||
if !w.visible {
|
||||
return
|
||||
}
|
||||
st := w.style
|
||||
if !w.active {
|
||||
st = st.Dim(true)
|
||||
}
|
||||
|
||||
wh.Border(w.x+w.mTL.X-1, w.y+w.mTL.Y-1, w.x+w.w+w.mBR.X+1, w.y+w.h+w.mBR.Y+1, wh.BRD_CSIMPLE, st, screen)
|
||||
|
||||
if w.drawRulers {
|
||||
// X Ruler
|
||||
var lastX rune
|
||||
for i := 0; i < w.w; i++ {
|
||||
str := fmt.Sprintf("%d", i)
|
||||
var wrk rune
|
||||
if len(str) == 3 {
|
||||
wrk = rune(str[1])
|
||||
} else if len(str) == 2 {
|
||||
wrk = rune(str[0])
|
||||
}
|
||||
if wrk != lastX {
|
||||
lastX = wrk
|
||||
screen.SetContent(w.x+i+w.mTL.X, w.y, wrk, nil, st)
|
||||
}
|
||||
screen.SetContent(w.x+i+w.mTL.X, w.y+w.mTL.Y-1, rune(str[len(str)-1]), nil, st)
|
||||
}
|
||||
|
||||
// Y Ruler
|
||||
var lastY rune
|
||||
for i := 0; i < w.h; i++ {
|
||||
str := fmt.Sprintf("%d", i)
|
||||
if len(str) > 1 {
|
||||
wrk := rune(str[0])
|
||||
if wrk != lastY {
|
||||
lastY = wrk
|
||||
screen.SetContent(w.x, w.y+i+w.mTL.Y, wrk, nil, st)
|
||||
}
|
||||
}
|
||||
screen.SetContent(w.x+w.mTL.X-1, w.y+i+w.mTL.Y, rune(str[len(str)-1]), nil, st)
|
||||
}
|
||||
}
|
||||
|
||||
w.GetPos().DrawOffset(w.widget, screen)
|
||||
}
|
||||
|
||||
func (w *DebugWidget) Active() bool { return w.active }
|
||||
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) SetW(wd int) {
|
||||
w.setW = wd
|
||||
w.w = wd
|
||||
}
|
||||
|
||||
func (w *DebugWidget) SetH(h int) {
|
||||
w.setH = h
|
||||
w.h = h
|
||||
}
|
||||
|
||||
func (w *DebugWidget) SetSize(c Coord) {
|
||||
w.SetW(c.X)
|
||||
w.SetH(c.Y)
|
||||
}
|
||||
|
||||
func (w *DebugWidget) WantW() int {
|
||||
if w.setW > 0 {
|
||||
return w.setW
|
||||
}
|
||||
return w.w
|
||||
}
|
||||
|
||||
func (w *DebugWidget) WantH() int {
|
||||
if w.setH > 0 {
|
||||
return w.setH
|
||||
}
|
||||
return w.h
|
||||
}
|
||||
func (w *DebugWidget) MinW() int { return 2 + w.widget.MinW() }
|
||||
func (w *DebugWidget) MinH() int { return 2 + w.widget.MinH() }
|
||||
|
||||
func (w *DebugWidget) SetWidget(wd Widget) { w.widget = wd }
|
||||
func (w *DebugWidget) EnableRulers(b bool) { w.drawRulers = b }
|
||||
@@ -77,8 +77,8 @@ func (w *Field) Init(id string, style tcell.Style) {
|
||||
func (w *Field) Id() string { return w.id }
|
||||
func (w *Field) 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.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
}
|
||||
|
||||
func (w *Field) HandleKey(ev *tcell.EventKey) bool {
|
||||
@@ -137,12 +137,6 @@ func (w *Field) Draw(screen tcell.Screen) {
|
||||
wh.DrawText(x, w.y, post, useStyle, screen)
|
||||
}
|
||||
|
||||
func (w *Field) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
func (w *Field) Active() bool { return w.active }
|
||||
func (w *Field) SetActive(a bool) { w.active = a }
|
||||
func (w *Field) Visible() bool { return w.visible }
|
||||
@@ -100,17 +100,10 @@ func (w *FilePicker) Draw(screen tcell.Screen) {
|
||||
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)
|
||||
c := w.GetPos()
|
||||
w.btnSelect.DrawOffset(c, screen)
|
||||
w.btnCancel.DrawOffset(c, screen)
|
||||
w.GetPos().DrawOffset(w.btnSelect, screen)
|
||||
w.GetPos().DrawOffset(w.btnCancel, screen)
|
||||
}
|
||||
|
||||
func (w *FilePicker) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
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 }
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
)
|
||||
|
||||
// LinearLayout lays out all widgets added one after the other
|
||||
// It will fill as much space as you give it
|
||||
type LinearLayout struct {
|
||||
id string
|
||||
style tcell.Style
|
||||
@@ -75,45 +76,23 @@ func (w *LinearLayout) Init(id string, s tcell.Style) {
|
||||
func (w *LinearLayout) Id() string { return w.id }
|
||||
func (w *LinearLayout) 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.w = wh.Max(w.w, w.WantW())
|
||||
// w.h = wh.Max(w.h, w.WantH())
|
||||
w.updateWidgetLayouts()
|
||||
}
|
||||
|
||||
func (w *LinearLayout) HandleKey(ev *tcell.EventKey) bool {
|
||||
if !w.disableTab && ev.Key() == tcell.KeyTab {
|
||||
fndP := -1
|
||||
for i := w.cursor; i < len(w.widgets); i++ {
|
||||
if fndP == -1 {
|
||||
if w.widgets[i].Active() {
|
||||
fndP = i
|
||||
w.widgets[i].SetActive(false)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if w.widgets[i].Focusable() && w.widgets[i].Tabbable() {
|
||||
w.widgets[i].SetActive(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we're here, we hit the last widget, loop
|
||||
if fndP == -1 { // But didn't even find the active one
|
||||
return false
|
||||
}
|
||||
for i := 0; i < fndP; i++ {
|
||||
if w.widgets[i].Focusable() && w.widgets[i].Tabbable() {
|
||||
w.widgets[i].SetActive(true)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for _, wi := range w.widgets {
|
||||
if wi.HandleKey(ev) {
|
||||
// First, see if the active widget handles the key
|
||||
if len(w.widgets) > w.cursor {
|
||||
if w.widgets[w.cursor].HandleKey(ev) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if ev.Key() == tcell.KeyTab && !w.disableTab {
|
||||
return w.handleTab()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -127,21 +106,17 @@ func (w *LinearLayout) Draw(screen tcell.Screen) {
|
||||
if !w.visible {
|
||||
return
|
||||
}
|
||||
p := w.GetPos()
|
||||
pos := w.GetPos()
|
||||
for _, wd := range w.widgets {
|
||||
wd.DrawOffset(p, screen)
|
||||
pos.DrawOffset(wd, screen)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *LinearLayout) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
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 }
|
||||
@@ -216,6 +191,15 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *LinearLayout) SetOrientation(o LinearLayoutOrient) { w.orientation = o }
|
||||
func (w *LinearLayout) IndexOf(n Widget) int {
|
||||
for i := range w.widgets {
|
||||
@@ -323,39 +307,38 @@ func (w *LinearLayout) updateWidgetLayouts() {
|
||||
switch w.orientation {
|
||||
case LinLayV:
|
||||
for _, wd := range w.widgets {
|
||||
w.updateVerticalWidgetSize(wd)
|
||||
w.updateVerticalWidgetPos(wd)
|
||||
w.updateLLVWidgetSize(wd)
|
||||
w.updateLLVWidgetPos(wd)
|
||||
}
|
||||
case LinLayH:
|
||||
for _, wd := range w.widgets {
|
||||
w.updateHorizontalWidgetSize(wd)
|
||||
w.updateHorizontalWidgetPos(wd)
|
||||
w.updateLLHWidgetSize(wd)
|
||||
w.updateLLHWidgetPos(wd)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The Layout should have a static Size set at this point that we can use
|
||||
// For now we're centering all views in the Layout (on the cross-axis)
|
||||
// For now we're centering all views in the Layout (on both axes)
|
||||
//
|
||||
// We need to determine the allowed size of this widget so we can determine
|
||||
// it's position
|
||||
func (w *LinearLayout) updateVerticalWidgetSize(wd Widget) {
|
||||
func (w *LinearLayout) updateLLVWidgetSize(wd Widget) {
|
||||
wd.HandleResize((&Coord{X: w.w, Y: w.getWeightedH(wd)}).ResizeEvent())
|
||||
}
|
||||
|
||||
func (w *LinearLayout) updateHorizontalWidgetSize(wd Widget) {
|
||||
func (w *LinearLayout) updateLLHWidgetSize(wd Widget) {
|
||||
wd.HandleResize((&Coord{X: w.getWeightedW(wd), Y: w.h}).ResizeEvent())
|
||||
}
|
||||
|
||||
// The Layout should have a static Size set at this point that we can use
|
||||
// For now we're centering all views in the Layout (on the cross-axis)
|
||||
// For now we're centering all views in the Layout (on both axes)
|
||||
// TODO: Use LayoutFlags to determine alignment in each 'cell'
|
||||
//
|
||||
// The position and size of each widget before this should be correct
|
||||
// This widget should also know its size by now. We just need to
|
||||
// position it relative to the layout.
|
||||
func (w *LinearLayout) updateVerticalWidgetPos(wd Widget) {
|
||||
w.Log("Calculating Widget Pos: %s", wd.Id())
|
||||
func (w *LinearLayout) updateLLVWidgetPos(wd Widget) {
|
||||
c := Coord{}
|
||||
for i := range w.widgets {
|
||||
if w.widgets[i] == wd {
|
||||
@@ -363,17 +346,54 @@ func (w *LinearLayout) updateVerticalWidgetPos(wd Widget) {
|
||||
}
|
||||
c.Y += w.getWeightedH(w.widgets[i])
|
||||
}
|
||||
w.Log("Set %s Y = %d", wd.Id(), c.Y)
|
||||
c.X = (w.w / 2) - (wd.GetW() / 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)
|
||||
}
|
||||
|
||||
// c.Y is the top of this 'cell'
|
||||
if wd.GetH() < w.getWeightedH(wd) {
|
||||
// But we've got some extra space
|
||||
switch flgs.AlignV() {
|
||||
case LFAlignVBottom:
|
||||
c.Y += w.getWeightedH(wd) - wd.GetH()
|
||||
case LFAlignVCenter:
|
||||
c.Y += (w.getWeightedH(wd) / 2) - (wd.GetH() / 2)
|
||||
}
|
||||
}
|
||||
c.X = int((float64(w.w) / 2) - (float64(wd.GetW()) / 2))
|
||||
wd.SetPos(c)
|
||||
}
|
||||
|
||||
func (w *LinearLayout) updateHorizontalWidgetPos(wd Widget) {
|
||||
func (w *LinearLayout) updateLLHWidgetPos(wd Widget) {
|
||||
c := Coord{}
|
||||
for i := range w.widgets {
|
||||
c.X += w.getWeightedH(w.widgets[i])
|
||||
if w.widgets[i] == wd {
|
||||
break
|
||||
}
|
||||
c.X += w.getWeightedW(w.widgets[i])
|
||||
}
|
||||
c.Y = (w.h / 2) - (wd.GetH() / 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)
|
||||
}
|
||||
|
||||
// c.X is the left-most of this 'cell'
|
||||
if wd.GetW() < w.getWeightedW(wd) {
|
||||
// But we've got some extra space.
|
||||
switch flgs.AlignH() {
|
||||
case LFAlignHRight:
|
||||
c.X += w.getWeightedW(wd) - wd.GetW()
|
||||
case LFAlignHCenter:
|
||||
c.X += (w.getWeightedW(wd) / 2) - (wd.GetW() / 2)
|
||||
}
|
||||
}
|
||||
c.Y = int((float64(w.h) / 2) - (float64(wd.GetH()) / 2))
|
||||
wd.SetPos(c)
|
||||
}
|
||||
|
||||
@@ -383,38 +403,49 @@ func (w *LinearLayout) updateWidgetPos(wd Widget) {
|
||||
c := Coord{}
|
||||
switch w.orientation {
|
||||
case LinLayV:
|
||||
c.X, c.Y = w.w-(wd.GetW()/2), prevP+prevS+1
|
||||
prevP, prevS = wrk.GetY(), wrk.GetH()
|
||||
|
||||
c.X, c.Y = 0, prevP+prevS
|
||||
prevP, prevS = c.Y, wrk.GetH()
|
||||
case LinLayH:
|
||||
c.X, c.Y = prevP+prevS+1, w.h-(wd.GetH()/2)
|
||||
prevP, prevS = wrk.GetX(), wrk.GetW()
|
||||
c.X, c.Y = prevP+prevS, 0
|
||||
prevP, prevS = c.X, wrk.GetW()
|
||||
}
|
||||
wd.SetPos(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *LinearLayout) getRelPos(wd Widget) Coord {
|
||||
_ = wd
|
||||
return Coord{}
|
||||
}
|
||||
|
||||
func (w *LinearLayout) getAbsPos(wd Widget) Coord {
|
||||
rel := w.getRelPos(wd)
|
||||
return rel.Add(Coord{X: w.x, Y: w.y})
|
||||
}
|
||||
|
||||
func (w *LinearLayout) getWeightedH(wd Widget) int {
|
||||
if !w.Contains(wd) {
|
||||
return 0
|
||||
}
|
||||
return w.h * (w.layoutWeights[wd] * w.totalWeight)
|
||||
wght := w.layoutWeights[wd]
|
||||
if wght == 0 {
|
||||
wght = 1
|
||||
}
|
||||
ret := int(float64(w.h) * (float64(wght) / float64(w.totalWeight)))
|
||||
return ret
|
||||
}
|
||||
|
||||
func (w *LinearLayout) getWeightedW(wd Widget) int {
|
||||
if !w.Contains(wd) {
|
||||
return 0
|
||||
}
|
||||
return w.w * (w.layoutWeights[wd] * w.totalWeight)
|
||||
wght := w.layoutWeights[wd]
|
||||
if wght == 0 {
|
||||
wght = 1
|
||||
}
|
||||
return int(float64(w.w) * (float64(wght) / float64(w.totalWeight)))
|
||||
}
|
||||
|
||||
func (w *LinearLayout) handleTab() bool {
|
||||
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) SetLogger(l func(string, ...any)) { w.logger = l }
|
||||
@@ -47,6 +47,8 @@ type List struct {
|
||||
onSelect func(int, string) bool
|
||||
keyMap KeyMap
|
||||
vimMode bool
|
||||
|
||||
logger func(string, ...any)
|
||||
}
|
||||
|
||||
var _ Widget = (*List)(nil)
|
||||
@@ -60,6 +62,7 @@ func NewList(id string, style tcell.Style) *List {
|
||||
func (w *List) Init(id string, style tcell.Style) {
|
||||
w.id = id
|
||||
w.style = style
|
||||
w.focusable = true
|
||||
w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{
|
||||
tcell.KeyUp: w.MoveUp,
|
||||
tcell.KeyDown: w.MoveDown,
|
||||
@@ -88,9 +91,9 @@ 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()
|
||||
|
||||
w.w = wh.Min(w.w, w.WantW())
|
||||
w.h = wh.Min(w.h, w.WantH())
|
||||
// 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 {
|
||||
@@ -135,12 +138,6 @@ func (w *List) Draw(screen tcell.Screen) {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *List) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
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 }
|
||||
@@ -244,3 +241,10 @@ func (w *List) GetIdx() int { return w.cursor }
|
||||
func (w *List) ClearBorder() { w.border = []rune{} }
|
||||
func (w *List) SetOnSelect(s func(int, string) bool) { w.onSelect = s }
|
||||
func (w *List) SetVimMode(b bool) { w.vimMode = b }
|
||||
|
||||
func (w *List) SetLogger(l func(string, ...any)) { w.logger = l }
|
||||
func (w *List) Log(txt string, args ...any) {
|
||||
if w.logger != nil {
|
||||
w.logger(txt, args...)
|
||||
}
|
||||
}
|
||||
@@ -87,8 +87,8 @@ 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())
|
||||
// w.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
|
||||
// TODO: Trickle-down HandleResize
|
||||
}
|
||||
@@ -116,13 +116,6 @@ func (w *Menu) Draw(screen tcell.Screen) {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Menu) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
|
||||
func (w *Menu) drawHMenu(screen tcell.Screen) {
|
||||
st := w.style
|
||||
if w.active {
|
||||
@@ -81,8 +81,8 @@ func (w *MenuItem) Id() string { return w.id }
|
||||
func (w *MenuItem) 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.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
// TODO: Trickle-down HandleResize
|
||||
}
|
||||
|
||||
@@ -125,12 +125,6 @@ func (w *MenuItem) Draw(screen tcell.Screen) {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *MenuItem) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
func (w *MenuItem) Active() bool { return w.active }
|
||||
func (w *MenuItem) SetActive(a bool) {
|
||||
w.active = a
|
||||
@@ -70,6 +70,7 @@ func (w *Prompt) Id() string { return w.id }
|
||||
func (w *Prompt) HandleResize(ev *tcell.EventResize) {
|
||||
w.w, w.h = ev.Size()
|
||||
|
||||
// Prompt is shrink-wrapped by default
|
||||
w.w = wh.Min(w.w, w.WantW())
|
||||
w.h = wh.Min(w.h, w.WantH())
|
||||
|
||||
@@ -95,18 +96,11 @@ func (w *Prompt) Draw(screen tcell.Screen) {
|
||||
dS = dS.Dim(true)
|
||||
}
|
||||
wh.TitledBorderFilled(w.x, w.y, w.x+w.w, w.y+w.h, w.title, wh.BRD_SIMPLE, w.style, screen)
|
||||
p := w.GetPos()
|
||||
w.message.DrawOffset(p, screen)
|
||||
w.btnOk.DrawOffset(p, screen)
|
||||
w.btnCancel.DrawOffset(p, screen)
|
||||
w.GetPos().DrawOffset(w.message, screen)
|
||||
w.GetPos().DrawOffset(w.btnOk, screen)
|
||||
w.GetPos().DrawOffset(w.btnCancel, screen)
|
||||
}
|
||||
|
||||
func (w *Prompt) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
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 }
|
||||
@@ -22,7 +22,6 @@ THE SOFTWARE.
|
||||
package widgets
|
||||
|
||||
import (
|
||||
wh "git.bullercodeworks.com/brian/tcell-widgets/helpers"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
@@ -81,8 +80,8 @@ 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())
|
||||
// w.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
|
||||
// TODO: Trickle-down HandleResize
|
||||
}
|
||||
@@ -106,12 +105,6 @@ func (w *RelativeLayout) Draw(screen tcell.Screen) {
|
||||
*/
|
||||
}
|
||||
|
||||
func (w *RelativeLayout) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
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 }
|
||||
@@ -85,8 +85,8 @@ func (w *Searcher) Id() string { return w.id }
|
||||
func (w *Searcher) 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.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
// TODO: Verify this is fine:
|
||||
w.search.HandleResize(ev)
|
||||
}
|
||||
@@ -184,7 +184,7 @@ func (w *Searcher) Draw(screen tcell.Screen) {
|
||||
} else {
|
||||
wh.BorderFilled(w.x, w.y, w.x+w.w, w.y+w.h, wh.BRD_CSIMPLE, dStyle, screen)
|
||||
}
|
||||
w.search.DrawOffset(w.GetPos(), screen)
|
||||
w.GetPos().DrawOffset(w.search, screen)
|
||||
x, y := w.x+1, w.y+2
|
||||
var stIdx int
|
||||
if w.cursor > w.h/2 {
|
||||
@@ -208,12 +208,6 @@ func (w *Searcher) Draw(screen tcell.Screen) {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Searcher) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
func (w *Searcher) Active() bool { return w.active }
|
||||
func (w *Searcher) SetActive(a bool) {
|
||||
w.active = a
|
||||
@@ -88,8 +88,8 @@ 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())
|
||||
// w.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
}
|
||||
|
||||
func (w *Table) HandleKey(ev *tcell.EventKey) bool {
|
||||
@@ -147,12 +147,6 @@ func (w *Table) Draw(screen tcell.Screen) {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Table) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
func (w *Table) Active() bool { return w.active }
|
||||
func (w *Table) SetActive(a bool) { w.active = a }
|
||||
func (w *Table) Visible() bool { return w.visible }
|
||||
@@ -58,8 +58,8 @@ func (w *Text) Init(id string, style tcell.Style) {
|
||||
func (w *Text) Id() string { return w.id }
|
||||
func (w *Text) 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.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
}
|
||||
func (w *Text) HandleKey(ev *tcell.EventKey) bool { return false }
|
||||
func (w *Text) HandleTime(ev *tcell.EventTime) {}
|
||||
@@ -74,12 +74,6 @@ func (w *Text) Draw(screen tcell.Screen) {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Text) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
func (w *Text) Active() bool { return false }
|
||||
func (w *Text) SetActive(a bool) {}
|
||||
func (w *Text) Visible() bool { return w.visible }
|
||||
@@ -82,8 +82,8 @@ func (w *TimeField) Id() string { return w.id }
|
||||
func (w *TimeField) 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.w = wh.Min(w.w, w.WantW())
|
||||
// w.h = wh.Min(w.h, w.WantH())
|
||||
}
|
||||
|
||||
func (w *TimeField) HandleKey(ev *tcell.EventKey) bool {
|
||||
@@ -158,13 +158,6 @@ func (w *TimeField) Draw(screen tcell.Screen) {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *TimeField) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
|
||||
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 }
|
||||
@@ -22,6 +22,8 @@ THE SOFTWARE.
|
||||
package widgets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
wh "git.bullercodeworks.com/brian/tcell-widgets/helpers"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
@@ -39,6 +41,8 @@ type TopMenuLayout struct {
|
||||
|
||||
active bool
|
||||
visible bool
|
||||
|
||||
logger func(string, ...any)
|
||||
}
|
||||
|
||||
var _ Widget = (*TopMenuLayout)(nil)
|
||||
@@ -54,7 +58,7 @@ func (w *TopMenuLayout) Init(id string, s tcell.Style) {
|
||||
w.style = s
|
||||
w.visible = true
|
||||
|
||||
w.menu = NewMenu("mainmenu", tcell.StyleDefault)
|
||||
w.menu = NewMenu(fmt.Sprintf("%s.mainmenu", id), tcell.StyleDefault)
|
||||
w.menu.SetActive(false)
|
||||
w.menu.SetType(MenuTypeH)
|
||||
w.menu.SetTabbable(false)
|
||||
@@ -63,15 +67,14 @@ func (w *TopMenuLayout) Init(id string, s tcell.Style) {
|
||||
func (w *TopMenuLayout) Id() string { return w.id }
|
||||
func (w *TopMenuLayout) HandleResize(ev *tcell.EventResize) {
|
||||
w.w, w.h = ev.Size()
|
||||
availW, availH := w.w, w.h
|
||||
if w.menu != nil {
|
||||
w.menu.SetPos(Coord{X: 0, Y: 0})
|
||||
w.menu.SetSize(Coord{X: w.w, Y: 1})
|
||||
availH = w.h - 1
|
||||
}
|
||||
|
||||
if w.widget != nil {
|
||||
w.widget.SetPos(Coord{X: 0, Y: 1})
|
||||
w.widget.HandleResize(tcell.NewEventResize(availW-1, availH-1))
|
||||
w.widget.HandleResize(tcell.NewEventResize(w.w, w.h-1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,22 +106,14 @@ func (w *TopMenuLayout) Draw(screen tcell.Screen) {
|
||||
if !w.visible {
|
||||
return
|
||||
}
|
||||
p := w.GetPos()
|
||||
if w.menu != nil {
|
||||
w.menu.DrawOffset(p, screen)
|
||||
w.GetPos().DrawOffset(w.menu, screen)
|
||||
}
|
||||
if w.widget != nil {
|
||||
w.widget.DrawOffset(p, screen)
|
||||
w.GetPos().DrawOffset(w.widget, screen)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *TopMenuLayout) DrawOffset(c Coord, screen tcell.Screen) {
|
||||
p := w.GetPos()
|
||||
w.SetPos(p.Add(c))
|
||||
w.Draw(screen)
|
||||
w.SetPos(p)
|
||||
}
|
||||
|
||||
func (w *TopMenuLayout) Active() bool { return w.active }
|
||||
func (w *TopMenuLayout) SetActive(a bool) { w.active = a }
|
||||
func (w *TopMenuLayout) Visible() bool { return w.visible }
|
||||
@@ -146,4 +141,12 @@ func (w *TopMenuLayout) MinH() int { return 0 }
|
||||
func (w *TopMenuLayout) Menu() *Menu { return w.menu }
|
||||
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) SetWidget(wd Widget) { w.widget = wd }
|
||||
|
||||
func (w *TopMenuLayout) SetLogger(l func(string, ...any)) { w.logger = l }
|
||||
func (w *TopMenuLayout) Log(txt string, args ...any) {
|
||||
if w.logger != nil {
|
||||
w.logger(txt, args...)
|
||||
}
|
||||
}
|
||||
16
widget.go
16
widget.go
@@ -33,7 +33,6 @@ type Widget interface {
|
||||
HandleKey(*tcell.EventKey) bool
|
||||
HandleTime(*tcell.EventTime)
|
||||
Draw(tcell.Screen)
|
||||
DrawOffset(Coord, tcell.Screen)
|
||||
Active() bool
|
||||
SetActive(bool)
|
||||
Visible() bool
|
||||
@@ -73,21 +72,6 @@ func WidgetRight(w Widget) int {
|
||||
return w.GetX() + w.GetW()
|
||||
}
|
||||
|
||||
type Coord struct {
|
||||
X, Y int
|
||||
}
|
||||
|
||||
func (p *Coord) Add(o Coord) Coord {
|
||||
return Coord{
|
||||
X: p.X + o.X,
|
||||
Y: p.Y + o.Y,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Coord) ResizeEvent() *tcell.EventResize {
|
||||
return tcell.NewEventResize(p.X, p.Y)
|
||||
}
|
||||
|
||||
// To validate that a struct satisfies this interface, you can do:
|
||||
// var _ Widget - (*<struct>)(nil)
|
||||
// where <struct> is the actual struct that you're validating.
|
||||
|
||||
Reference in New Issue
Block a user