Buffers ?!
This commit is contained in:
140
buffer.go
Normal file
140
buffer.go
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
h "git.bullercodeworks.com/brian/tcell-widgets/helpers"
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Buffer struct {
|
||||||
|
// cells [][]Cell
|
||||||
|
cells map[Coord]Cell
|
||||||
|
maxX, maxY int
|
||||||
|
minX, minY int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBuffer() Buffer {
|
||||||
|
return Buffer{cells: make(map[Coord]Cell)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) Width() int { return b.maxX - b.minX }
|
||||||
|
func (b *Buffer) Height() int { return b.maxY - b.minY }
|
||||||
|
|
||||||
|
func (b *Buffer) Draw(x, y int, screen tcell.Screen) {
|
||||||
|
for crd, cell := range b.cells {
|
||||||
|
cell.Draw(x+crd.X, y+crd.Y, screen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) SetCell(x, y int, c Cell) {
|
||||||
|
b.cells[Coord{X: x, Y: y}] = c
|
||||||
|
b.maxX, b.maxY = h.Max(b.maxX, x), h.Max(b.maxY, y)
|
||||||
|
b.minX, b.minY = h.Min(b.minX, x), h.Min(b.minY, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) SetCells(x, y int, c [][]Cell) {
|
||||||
|
for i := range c {
|
||||||
|
for j := range c[i] {
|
||||||
|
b.SetCell(x+j, y+i, c[i][j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer Helpers
|
||||||
|
func (b *Buffer) FillText(x, y int, txt string, style tcell.Style) {
|
||||||
|
for i := range txt {
|
||||||
|
b.SetCell(x+i, y, *NewCell(rune(txt[i]), style))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) Fill(x1, y1, x2, y2 int, r rune, style tcell.Style) {
|
||||||
|
if x1 > x2 {
|
||||||
|
x1, x2 = x2, x1
|
||||||
|
}
|
||||||
|
if y1 > y2 {
|
||||||
|
y1, y2 = y2, y1
|
||||||
|
}
|
||||||
|
for x := x1; x <= x2; x++ {
|
||||||
|
for y := y1; y <= y2; y++ {
|
||||||
|
b.SetCell(x, y, *NewCell(r, style))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) HRule(x1, x2, y int, rule []rune, s tcell.Style) {
|
||||||
|
b.Fill(x1, y, x2, y, rule[h.RULE_FILL], s)
|
||||||
|
b.SetCell(x1, y, *NewCell(rule[h.RULE_START], s))
|
||||||
|
b.SetCell(x2, y, *NewCell(rule[h.RULE_END], s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) VRule(x, y1, y2 int, rule []rune, s tcell.Style) {
|
||||||
|
b.Fill(x, y1, x, y2, rule[h.RULE_FILL], s)
|
||||||
|
b.SetCell(x, y1, *NewCell(rule[h.RULE_START], s))
|
||||||
|
b.SetCell(x, y2, *NewCell(rule[h.RULE_END], s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) BorderFilled(x1, y1, x2, y2 int, border []rune, s tcell.Style) {
|
||||||
|
b.Border(x1, y1, x2, y2, border, s)
|
||||||
|
b.Fill(x1+1, y1+1, x2-1, y2-1, ' ', s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) Border(x1, y1, x2, y2 int, border []rune, s tcell.Style) {
|
||||||
|
border = h.ValidateBorder(border)
|
||||||
|
b.Fill(x1+1, y1, x2-1, y1, border[h.BRD_N], s)
|
||||||
|
b.Fill(x2, y1+1, x2, y2-1, border[h.BRD_E], s)
|
||||||
|
b.Fill(x1+1, y2, x2-1, y2, border[h.BRD_S], s)
|
||||||
|
b.Fill(x1, y1+1, x1, y2-1, border[h.BRD_W], s)
|
||||||
|
b.SetCell(x1, y1, *NewCell(border[h.BRD_NW], s))
|
||||||
|
b.SetCell(x2, y1, *NewCell(border[h.BRD_NE], s))
|
||||||
|
b.SetCell(x1, y2, *NewCell(border[h.BRD_SW], s))
|
||||||
|
b.SetCell(x2, y2, *NewCell(border[h.BRD_SE], s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) TitledBorderFilled(x1, y1, x2, y2 int, ttl string, border []rune, s tcell.Style) {
|
||||||
|
b.TitledBorder(x1, y1, x2, y2, ttl, border, s)
|
||||||
|
b.Fill(x1+1, y1+1, x2-1, y2-1, ' ', s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Buffer) TitledBorder(x1, y1, x2, y2 int, ttl string, border []rune, s tcell.Style) {
|
||||||
|
if ttl == "" {
|
||||||
|
b.Border(x1, y1, x2, y2, border, s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
border = h.ValidateBorder(border)
|
||||||
|
ttlLength, maxTtlLength := len(ttl), (x2 - x1 - 2)
|
||||||
|
if ttlLength > maxTtlLength {
|
||||||
|
if maxTtlLength-3 > 0 {
|
||||||
|
ttlLength = maxTtlLength
|
||||||
|
ttl = ttl[0:maxTtlLength-3] + "..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.FillText(x1+1, y1, ttl, s)
|
||||||
|
b.Fill(x1+1+ttlLength, y1, x2-1, y1, border[h.BRD_N], s)
|
||||||
|
b.Fill(x2, y1+1, x2, y2-1, border[h.BRD_E], s)
|
||||||
|
b.Fill(x1+1, y2, x2-1, y2, border[h.BRD_S], s)
|
||||||
|
b.Fill(x1, y1+1, x1, y2-1, border[h.BRD_W], s)
|
||||||
|
b.SetCell(x1, y1, *NewCell(border[h.BRD_NW], s))
|
||||||
|
b.SetCell(x2, y1, *NewCell(border[h.BRD_NE], s))
|
||||||
|
b.SetCell(x1, y2, *NewCell(border[h.BRD_SW], s))
|
||||||
|
b.SetCell(x2, y2, *NewCell(border[h.BRD_SE], s))
|
||||||
|
}
|
||||||
41
cell.go
Normal file
41
cell.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
/*
|
||||||
|
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 Cell struct {
|
||||||
|
r rune
|
||||||
|
combc []rune
|
||||||
|
style tcell.Style
|
||||||
|
empty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCell(r rune, style tcell.Style) *Cell {
|
||||||
|
return &Cell{r: r, style: style, empty: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cell) AddCombC(combc []rune) { c.combc = combc }
|
||||||
|
|
||||||
|
func (c *Cell) Draw(x, y int, screen tcell.Screen) {
|
||||||
|
screen.SetContent(x, y, c.r, c.combc, c.style)
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ THE SOFTWARE.
|
|||||||
package widgets
|
package widgets
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
wh "git.bullercodeworks.com/brian/tcell-widgets/helpers"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ type AbsoluteLayout struct {
|
|||||||
|
|
||||||
x, y int
|
x, y int
|
||||||
w, h int
|
w, h int
|
||||||
|
bordered bool
|
||||||
widgets []Widget
|
widgets []Widget
|
||||||
wCoords map[Widget]Coord
|
wCoords map[Widget]Coord
|
||||||
wAnchor map[Widget]AbsoluteAnchor
|
wAnchor map[Widget]AbsoluteAnchor
|
||||||
@@ -151,6 +153,9 @@ func (w *AbsoluteLayout) Draw(screen tcell.Screen) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
p := w.GetPos()
|
p := w.GetPos()
|
||||||
|
if w.bordered {
|
||||||
|
wh.Border(p.X, p.Y, p.X+w.w, p.Y+w.h, wh.BRD_CSIMPLE, w.style, screen)
|
||||||
|
}
|
||||||
for _, wd := range w.widgets {
|
for _, wd := range w.widgets {
|
||||||
p.DrawOffset(wd, screen)
|
p.DrawOffset(wd, screen)
|
||||||
}
|
}
|
||||||
@@ -229,6 +234,8 @@ func (w *AbsoluteLayout) Log(txt string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *AbsoluteLayout) SetBordered(b bool) { w.bordered = b }
|
||||||
|
|
||||||
func (w *AbsoluteLayout) updateWidgetLayouts() {
|
func (w *AbsoluteLayout) updateWidgetLayouts() {
|
||||||
// In an Absolute Layout, widgets are given a definite position and anchor.
|
// In an Absolute Layout, widgets are given a definite position and anchor.
|
||||||
// The anchor is a side of the layout (see AbsoluteAnchor type)
|
// The anchor is a side of the layout (see AbsoluteAnchor type)
|
||||||
@@ -268,7 +275,9 @@ func (w *AbsoluteLayout) updateWidgetSize(wd Widget) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set a widgets position relative to the layout
|
// Set a widgets position relative to the layout
|
||||||
func (w *AbsoluteLayout) updateWidgetPos(wd Widget) { wd.SetPos(w.getRelPos(wd)) }
|
func (w *AbsoluteLayout) updateWidgetPos(wd Widget) {
|
||||||
|
wd.SetPos(w.getRelPos(wd))
|
||||||
|
}
|
||||||
|
|
||||||
// Manually set the size of a widget, the Layout won't override it
|
// Manually set the size of a widget, the Layout won't override it
|
||||||
func (w *AbsoluteLayout) SetWidgetSize(wd Widget, sz Coord) { w.wManualSizes[wd] = sz }
|
func (w *AbsoluteLayout) SetWidgetSize(wd Widget, sz Coord) { w.wManualSizes[wd] = sz }
|
||||||
@@ -288,34 +297,41 @@ func (w *AbsoluteLayout) getRelPos(wd Widget) Coord {
|
|||||||
if a, ok = w.wAnchor[wd]; !ok {
|
if a, ok = w.wAnchor[wd]; !ok {
|
||||||
a = w.defAnchor
|
a = w.defAnchor
|
||||||
}
|
}
|
||||||
|
leftX, topY, rightX, bottomY := 0, 0, w.w, w.h
|
||||||
|
if w.bordered {
|
||||||
|
leftX += 1
|
||||||
|
topY += 1
|
||||||
|
rightX -= 1
|
||||||
|
bottomY -= 1
|
||||||
|
}
|
||||||
midX, midY := (w.w / 2), (w.h / 2)
|
midX, midY := (w.w / 2), (w.h / 2)
|
||||||
switch a {
|
switch a {
|
||||||
case AnchorTL:
|
case AnchorTL:
|
||||||
return p
|
return p.Add(Coord{X: leftX, Y: topY})
|
||||||
|
|
||||||
case AnchorT:
|
case AnchorT:
|
||||||
return p.Add(Coord{X: midX - (wd.GetW() / 2), Y: 0})
|
return p.Add(Coord{X: midX - (wd.GetW() / 2), Y: topY})
|
||||||
|
|
||||||
case AnchorTR:
|
case AnchorTR:
|
||||||
return p.Add(Coord{X: w.w - wd.GetW(), Y: 0})
|
return p.Add(Coord{X: rightX - wd.GetW(), Y: topY})
|
||||||
|
|
||||||
case AnchorL:
|
case AnchorL:
|
||||||
return p.Add(Coord{X: 0, Y: midY - (wd.GetH() / 2)})
|
return p.Add(Coord{X: leftX, Y: midY - (wd.GetH() / 2)})
|
||||||
|
|
||||||
case AnchorC:
|
case AnchorC:
|
||||||
return p.Add(Coord{X: midX - (wd.GetW() / 2), Y: midY - (wd.GetH() / 2)})
|
return p.Add(Coord{X: midX - (wd.GetW() / 2), Y: midY - (wd.GetH() / 2)})
|
||||||
|
|
||||||
case AnchorR:
|
case AnchorR:
|
||||||
return p.Add(Coord{X: w.w - wd.GetW(), Y: midY - (wd.GetH() / 2)})
|
return p.Add(Coord{X: rightX - wd.GetW(), Y: midY - (wd.GetH() / 2)})
|
||||||
|
|
||||||
case AnchorBR:
|
case AnchorBR:
|
||||||
return p.Add(Coord{X: w.w - wd.GetW(), Y: w.h - wd.GetH()})
|
return p.Add(Coord{X: rightX - wd.GetW(), Y: bottomY - wd.GetH()})
|
||||||
|
|
||||||
case AnchorB:
|
case AnchorB:
|
||||||
return p.Add(Coord{X: midX - (wd.GetW() / 2), Y: w.h - wd.GetH()})
|
return p.Add(Coord{X: midX - (wd.GetW() / 2), Y: bottomY - wd.GetH()})
|
||||||
|
|
||||||
case AnchorBL:
|
case AnchorBL:
|
||||||
return p.Add(Coord{X: 0, Y: w.h - wd.GetH()})
|
return p.Add(Coord{X: leftX, Y: bottomY - wd.GetH()})
|
||||||
|
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
|
|||||||
@@ -63,12 +63,14 @@ func (w *Checkbox) Init(id string, style tcell.Style) {
|
|||||||
w.visible = true
|
w.visible = true
|
||||||
w.stateRunes = []rune{'X', ' ', '-'}
|
w.stateRunes = []rune{'X', ' ', '-'}
|
||||||
w.focusable = true
|
w.focusable = true
|
||||||
w.keyMap = NewKeyMap(map[tcell.Key]func(ev *tcell.EventKey) bool{
|
w.keyMap = KeyMap{
|
||||||
|
Keys: map[tcell.Key]func(ev *tcell.EventKey) bool{
|
||||||
tcell.KeyEnter: w.ToggleState,
|
tcell.KeyEnter: w.ToggleState,
|
||||||
})
|
},
|
||||||
w.AddToKeyMap(NewRuneMap(map[rune]func(ev *tcell.EventKey) bool{
|
Runes: map[rune]func(ev *tcell.EventKey) bool{
|
||||||
' ': w.ToggleState,
|
' ': w.ToggleState,
|
||||||
}))
|
},
|
||||||
|
}
|
||||||
w.customKeyMap = BlankKeyMap()
|
w.customKeyMap = BlankKeyMap()
|
||||||
}
|
}
|
||||||
func (w *Checkbox) Id() string { return w.id }
|
func (w *Checkbox) Id() string { return w.id }
|
||||||
|
|||||||
@@ -43,14 +43,15 @@ type FilePicker struct {
|
|||||||
wantW, wantH int
|
wantW, wantH int
|
||||||
|
|
||||||
path string
|
path string
|
||||||
wrkDir *os.File
|
|
||||||
|
|
||||||
layout *RelativeLayout
|
|
||||||
|
|
||||||
fileList *SimpleList
|
fileList *SimpleList
|
||||||
btnSelect, btnCancel *Button
|
btnSelect, btnCancel *Button
|
||||||
|
|
||||||
|
error error
|
||||||
|
|
||||||
keyMap, customKeyMap KeyMap
|
keyMap, customKeyMap KeyMap
|
||||||
|
|
||||||
|
logger func(string, ...any)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Widget = (*FilePicker)(nil)
|
var _ Widget = (*FilePicker)(nil)
|
||||||
@@ -65,24 +66,34 @@ func (w *FilePicker) Init(id string, style tcell.Style) {
|
|||||||
w.id = id
|
w.id = id
|
||||||
w.style = style
|
w.style = style
|
||||||
|
|
||||||
w.layout = NewRelativeLayout(fmt.Sprintf("%s-layout", id), style)
|
w.fileList = NewSimpleList(fmt.Sprintf("%s-files", id), style)
|
||||||
|
|
||||||
|
w.btnCancel = NewButton(fmt.Sprintf("%s-cancel", id), style)
|
||||||
|
w.btnCancel.SetLabel("Cancel")
|
||||||
|
|
||||||
w.btnSelect = NewButton(fmt.Sprintf("%s-select", id), style)
|
w.btnSelect = NewButton(fmt.Sprintf("%s-select", id), style)
|
||||||
w.btnSelect.SetLabel("Select")
|
w.btnSelect.SetLabel("Select")
|
||||||
w.layout.Add(w.btnSelect, nil, RelAncBR)
|
|
||||||
w.btnCancel = NewButton(fmt.Sprintf("%s-cancel", id), style)
|
|
||||||
w.btnCancel.SetLabel("Cancel")
|
|
||||||
w.layout.Add(w.btnCancel, nil, RelAncBL)
|
|
||||||
w.focusable = true
|
w.focusable = true
|
||||||
|
|
||||||
w.keyMap = BlankKeyMap()
|
w.keyMap = BlankKeyMap()
|
||||||
w.customKeyMap = BlankKeyMap()
|
w.customKeyMap = BlankKeyMap()
|
||||||
|
|
||||||
|
w.refreshFileList()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *FilePicker) Id() string { return w.id }
|
func (w *FilePicker) Id() string { return w.id }
|
||||||
func (w *FilePicker) HandleResize(ev *tcell.EventResize) {
|
func (w *FilePicker) HandleResize(ev *tcell.EventResize) {
|
||||||
w.w, w.h = ev.Size()
|
wd, ht := ev.Size()
|
||||||
// ww, wh := w.w-2, w.h-2 // Trim border space
|
w.SetW(wd)
|
||||||
w.btnSelect.SetPos(Coord{X: w.x + w.w - w.btnSelect.WantW(), Y: w.y + w.h - 1})
|
w.SetH(ht)
|
||||||
w.btnCancel.SetPos(Coord{X: w.x + 1, Y: w.y + w.h - 1})
|
w.fileList.SetPos(Coord{X: 0, Y: 0})
|
||||||
|
w.fileList.HandleResize(Coord{X: wd, Y: ht - 3}.ResizeEvent())
|
||||||
|
w.btnCancel.HandleResize(Coord{X: wd/2 - 1, Y: 3}.ResizeEvent())
|
||||||
|
w.btnCancel.SetPos(Coord{X: 0, Y: ht - 3})
|
||||||
|
w.btnSelect.HandleResize(Coord{X: wd/2 - 1, Y: 3}.ResizeEvent())
|
||||||
|
w.btnSelect.SetPos(Coord{X: wd/2 + 1, Y: ht - 3})
|
||||||
|
w.Log("%s: HandleResize(%d, %d)", w.id, wd, ht)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *FilePicker) SetKeyMap(km KeyMap, def bool) {
|
func (w *FilePicker) SetKeyMap(km KeyMap, def bool) {
|
||||||
@@ -110,17 +121,21 @@ func (w *FilePicker) HandleKey(ev *tcell.EventKey) bool {
|
|||||||
b2 := w.customKeyMap.Handle(ev)
|
b2 := w.customKeyMap.Handle(ev)
|
||||||
return b1 || b2
|
return b1 || b2
|
||||||
}
|
}
|
||||||
func (w *FilePicker) HandleTime(ev *tcell.EventTime) { w.layout.HandleTime(ev) }
|
|
||||||
|
func (w *FilePicker) HandleTime(ev *tcell.EventTime) {
|
||||||
|
w.fileList.HandleTime(ev)
|
||||||
|
w.btnCancel.HandleTime(ev)
|
||||||
|
w.btnSelect.HandleTime(ev)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *FilePicker) Draw(screen tcell.Screen) {
|
func (w *FilePicker) Draw(screen tcell.Screen) {
|
||||||
if !w.visible {
|
if !w.visible {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ds := w.style.Dim(!w.active)
|
p := w.GetPos()
|
||||||
wh.TitledBorderFilled(w.x, w.y, w.x+w.w, w.y+w.h, w.title, wh.BRD_SIMPLE, ds, screen)
|
p.DrawOffset(w.fileList, screen)
|
||||||
// TODO: Draw the file picker
|
p.DrawOffset(w.btnCancel, screen)
|
||||||
wh.DrawText(w.x+1, w.y+1, "TODO: Draw Filepicker", ds, screen)
|
p.DrawOffset(w.btnSelect, screen)
|
||||||
w.GetPos().DrawOffset(w.btnSelect, screen)
|
|
||||||
w.GetPos().DrawOffset(w.btnCancel, screen)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *FilePicker) Active() bool { return w.active }
|
func (w *FilePicker) Active() bool { return w.active }
|
||||||
@@ -170,8 +185,8 @@ func (w *FilePicker) SetPath(path string) error {
|
|||||||
} else if !fs.IsDir() {
|
} else if !fs.IsDir() {
|
||||||
return fmt.Errorf("path must be a directory")
|
return fmt.Errorf("path must be a directory")
|
||||||
}
|
}
|
||||||
w.wrkDir = fl
|
|
||||||
w.path = path
|
w.path = path
|
||||||
|
w.refreshFileList()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,3 +197,25 @@ func (w *FilePicker) SetOnSelect(sel func() bool) {
|
|||||||
func (w *FilePicker) SetOnCancel(cnc func() bool) {
|
func (w *FilePicker) SetOnCancel(cnc func() bool) {
|
||||||
w.btnCancel.SetOnPressed(cnc)
|
w.btnCancel.SetOnPressed(cnc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *FilePicker) refreshFileList() {
|
||||||
|
w.fileList.Clear()
|
||||||
|
if w.path == "" {
|
||||||
|
w.path = "./"
|
||||||
|
}
|
||||||
|
var entries []os.DirEntry
|
||||||
|
entries, w.error = os.ReadDir(w.path)
|
||||||
|
if w.error != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := range entries {
|
||||||
|
w.fileList.Add(entries[i].Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *FilePicker) SetLogger(l func(string, ...any)) { w.logger = l }
|
||||||
|
func (w *FilePicker) Log(txt string, args ...any) {
|
||||||
|
if w.logger != nil {
|
||||||
|
w.logger(txt, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -318,6 +318,7 @@ func (w *LinearLayout) ActivatePrev() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *LinearLayout) SetOrientation(o LinearLayoutOrient) { w.orientation = o }
|
func (w *LinearLayout) SetOrientation(o LinearLayoutOrient) { w.orientation = o }
|
||||||
|
func (w *LinearLayout) WidgetCount() int { return len(w.widgets) }
|
||||||
func (w *LinearLayout) IndexOf(n Widget) int {
|
func (w *LinearLayout) IndexOf(n Widget) int {
|
||||||
for i := range w.widgets {
|
for i := range w.widgets {
|
||||||
if w.widgets[i] == n {
|
if w.widgets[i] == n {
|
||||||
|
|||||||
@@ -400,6 +400,7 @@ func (w *Menu) MoveDown(ev *tcell.EventKey) bool {
|
|||||||
|
|
||||||
func (w *Menu) CreateMenuItem(lbl string, do func() bool, hotKey rune, subItems ...*MenuItem) *MenuItem {
|
func (w *Menu) CreateMenuItem(lbl string, do func() bool, hotKey rune, subItems ...*MenuItem) *MenuItem {
|
||||||
d := NewMenuItem(fmt.Sprintf("menuitem-%s", lbl), tcell.StyleDefault)
|
d := NewMenuItem(fmt.Sprintf("menuitem-%s", lbl), tcell.StyleDefault)
|
||||||
|
d.SetMenuType(MenuTypeV)
|
||||||
d.SetHotKey(hotKey)
|
d.SetHotKey(hotKey)
|
||||||
d.SetLabel(lbl)
|
d.SetLabel(lbl)
|
||||||
d.SetOnPressed(do)
|
d.SetOnPressed(do)
|
||||||
|
|||||||
@@ -167,14 +167,22 @@ func (w *MenuItem) Draw(screen tcell.Screen) {
|
|||||||
wh.DrawText(w.x, y, fmt.Sprintf("╯%s╰", strings.Repeat(" ", len(w.label))), w.style, screen)
|
wh.DrawText(w.x, y, fmt.Sprintf("╯%s╰", strings.Repeat(" ", len(w.label))), w.style, screen)
|
||||||
x += 1
|
x += 1
|
||||||
y += 1
|
y += 1
|
||||||
// pos := w.GetPos()
|
if len(w.items) > 0 {
|
||||||
|
if w.menuType == MenuTypeH {
|
||||||
for i := range w.items {
|
for i := range w.items {
|
||||||
// TODO: Use DrawOffset
|
|
||||||
// pos.DrawOffset(w.items[i], screen)
|
|
||||||
w.items[i].SetPos(Coord{X: x, Y: y})
|
w.items[i].SetPos(Coord{X: x, Y: y})
|
||||||
w.items[i].Draw(screen)
|
w.items[i].Draw(screen)
|
||||||
y++
|
y++
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for i := range w.items {
|
||||||
|
ix := x + w.w
|
||||||
|
w.items[i].SetPos(Coord{X: ix, Y: y})
|
||||||
|
w.items[i].Draw(screen)
|
||||||
|
y++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
screen.SetContent(x, y, ' ', nil, st)
|
screen.SetContent(x, y, ' ', nil, st)
|
||||||
x += 1
|
x += 1
|
||||||
@@ -326,3 +334,5 @@ func (w *MenuItem) FindItem(id string) *MenuItem {
|
|||||||
|
|
||||||
func (w *MenuItem) SetHotKey(r rune) { w.hotKey = r }
|
func (w *MenuItem) SetHotKey(r rune) { w.hotKey = r }
|
||||||
func (w *MenuItem) GetHotKey() rune { return w.hotKey }
|
func (w *MenuItem) GetHotKey() rune { return w.hotKey }
|
||||||
|
|
||||||
|
func (w *MenuItem) SetMenuType(t MenuType) { w.menuType = t }
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ func (w *RelativeLayout) Init(id string, style tcell.Style) {
|
|||||||
w.style = style
|
w.style = style
|
||||||
w.visible = true
|
w.visible = true
|
||||||
w.focusable = true
|
w.focusable = true
|
||||||
|
w.widgetRelations = make(map[Widget][]widgetRelation)
|
||||||
w.keyMap = BlankKeyMap()
|
w.keyMap = BlankKeyMap()
|
||||||
}
|
}
|
||||||
func (w *RelativeLayout) Id() string { return w.id }
|
func (w *RelativeLayout) Id() string { return w.id }
|
||||||
|
|||||||
@@ -72,16 +72,36 @@ func (w *SimpleList) Init(id string, style tcell.Style) {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
tcell.KeyPgDn: func(_ *tcell.EventKey) bool { return w.PageDn() },
|
||||||
|
tcell.KeyPgUp: func(_ *tcell.EventKey) bool { return w.PageUp() },
|
||||||
})
|
})
|
||||||
w.keyMap.AddRune('j', func(ev *tcell.EventKey) bool {
|
w.keyMap.AddRune('j', func(ev *tcell.EventKey) bool {
|
||||||
if w.vimMode {
|
if !w.vimMode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return w.MoveDown()
|
return w.MoveDown()
|
||||||
|
})
|
||||||
|
w.keyMap.AddRune('k', func(ev *tcell.EventKey) bool {
|
||||||
|
if !w.vimMode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return w.MoveUp()
|
||||||
|
})
|
||||||
|
w.keyMap.AddRune('b', func(ev *tcell.EventKey) bool {
|
||||||
|
if !w.vimMode {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ev.Modifiers()&tcell.ModCtrl != 0 {
|
||||||
|
return w.PageUp()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
w.keyMap.AddRune('k', func(ev *tcell.EventKey) bool {
|
w.keyMap.AddRune('f', func(ev *tcell.EventKey) bool {
|
||||||
if w.vimMode {
|
if !w.vimMode {
|
||||||
return w.MoveUp()
|
return false
|
||||||
|
}
|
||||||
|
if ev.Modifiers()&tcell.ModCtrl != 0 {
|
||||||
|
return w.PageDn()
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
@@ -89,6 +109,7 @@ func (w *SimpleList) Init(id string, style tcell.Style) {
|
|||||||
w.itemsStyle = make(map[int]tcell.Style)
|
w.itemsStyle = make(map[int]tcell.Style)
|
||||||
w.focusable = true
|
w.focusable = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *SimpleList) Id() string { return w.id }
|
func (w *SimpleList) Id() string { return w.id }
|
||||||
func (w *SimpleList) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() }
|
func (w *SimpleList) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() }
|
||||||
|
|
||||||
@@ -134,7 +155,33 @@ func (w *SimpleList) Draw(screen tcell.Screen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
x, y = x+1, y+1
|
x, y = x+1, y+1
|
||||||
for i := range w.list {
|
h := w.h - brdSz
|
||||||
|
ln := len(w.list)
|
||||||
|
st, ed := 0, ln-1
|
||||||
|
if ln == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ln > w.h-2 {
|
||||||
|
mid := h / 2
|
||||||
|
if w.cursor < mid {
|
||||||
|
// List needs to begin at 0
|
||||||
|
ed = h + 1
|
||||||
|
} else if w.cursor > ln-mid {
|
||||||
|
// List needs to begin at ln-h
|
||||||
|
st = ln - h + 1
|
||||||
|
} else {
|
||||||
|
st = w.cursor - mid
|
||||||
|
ed = st + h + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ed cannot be higher than ln-1
|
||||||
|
if st < 0 {
|
||||||
|
st = 0
|
||||||
|
}
|
||||||
|
if ed > ln-1 {
|
||||||
|
ed = ln - 1
|
||||||
|
}
|
||||||
|
for i := st; i <= ed; i++ {
|
||||||
rev := false
|
rev := false
|
||||||
if i == w.cursor {
|
if i == w.cursor {
|
||||||
rev = true
|
rev = true
|
||||||
@@ -215,7 +262,7 @@ func (w *SimpleList) MoveUp() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (w *SimpleList) MoveDown() bool {
|
func (w *SimpleList) MoveDown() bool {
|
||||||
if w.cursor < len(w.list)-1 {
|
if w.cursor <= len(w.list)-2 {
|
||||||
w.cursor++
|
w.cursor++
|
||||||
if w.onChange != nil {
|
if w.onChange != nil {
|
||||||
w.onChange(w.cursor, w.list[w.cursor])
|
w.onChange(w.cursor, w.list[w.cursor])
|
||||||
@@ -230,6 +277,28 @@ func (w *SimpleList) MoveDown() bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (w *SimpleList) PageUp() bool {
|
||||||
|
w.cursor -= w.h
|
||||||
|
if len(w.border) > 0 {
|
||||||
|
w.cursor += 2
|
||||||
|
}
|
||||||
|
if w.cursor < 0 {
|
||||||
|
w.cursor = 0
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *SimpleList) PageDn() bool {
|
||||||
|
w.cursor += w.h
|
||||||
|
if len(w.border) > 0 {
|
||||||
|
w.cursor -= 2
|
||||||
|
}
|
||||||
|
if w.cursor > len(w.list)-2 {
|
||||||
|
w.cursor = len(w.list) - 2
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
func (w *SimpleList) SetTitle(ttl string) { w.title = ttl }
|
func (w *SimpleList) SetTitle(ttl string) { w.title = ttl }
|
||||||
func (w *SimpleList) SetList(l []string) { w.list = l }
|
func (w *SimpleList) SetList(l []string) { w.list = l }
|
||||||
func (w *SimpleList) Clear() {
|
func (w *SimpleList) Clear() {
|
||||||
@@ -238,7 +307,11 @@ func (w *SimpleList) Clear() {
|
|||||||
delete(w.itemsStyle, k)
|
delete(w.itemsStyle, k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (w *SimpleList) Add(l string) { w.list = append(w.list, l) }
|
|
||||||
|
func (w *SimpleList) Add(l string) {
|
||||||
|
w.list = append(w.list, l)
|
||||||
|
}
|
||||||
|
|
||||||
func (w *SimpleList) Remove(l string) {
|
func (w *SimpleList) Remove(l string) {
|
||||||
var idx int
|
var idx int
|
||||||
var found bool
|
var found bool
|
||||||
@@ -289,4 +362,6 @@ func (w *SimpleList) Log(txt string, args ...any) {
|
|||||||
|
|
||||||
func (w *SimpleList) SetOnChange(c func(int, string) bool) { w.onChange = c }
|
func (w *SimpleList) SetOnChange(c func(int, string) bool) { w.onChange = c }
|
||||||
|
|
||||||
|
func (w *SimpleList) GetSelectedItem() string { return w.list[w.cursor] }
|
||||||
|
func (w *SimpleList) GetAllItems() []string { return w.list }
|
||||||
func (w *SimpleList) GetAllItemStyles() map[int]tcell.Style { return w.itemsStyle }
|
func (w *SimpleList) GetAllItemStyles() map[int]tcell.Style { return w.itemsStyle }
|
||||||
|
|||||||
Reference in New Issue
Block a user