This repository has been archived on 2022-11-30. You can view files and clone it, but cannot push or open issues or pull requests.
spectator/exhibit/list_widget.go

216 lines
3.7 KiB
Go

package exhibit
import (
"image"
"sync"
)
type ListEntry interface {
String() string
Attributes() Attributes
}
type ListWidget struct {
Style Style
blockLock sync.Mutex
block Block
attributesLock sync.Mutex
attributes Attributes
rightAlign bool
border bool
listLock sync.Mutex
list [][]Cell
listBuf [][]Cell
}
func (l *ListWidget) Attributes() Attributes {
l.attributesLock.Lock()
defer l.attributesLock.Unlock()
return l.attributes
}
func (l *ListWidget) SetAttributes(a Attributes) {
l.attributesLock.Lock()
l.attributes = a
l.attributesLock.Unlock()
l.recalculateCells()
}
func (l *ListWidget) Size() image.Point {
l.blockLock.Lock()
defer l.blockLock.Unlock()
return l.block.Rect.Size()
}
func (l *ListWidget) SetSize(p image.Point) {
l.blockLock.Lock()
defer l.blockLock.Unlock()
l.block.SetSize(p)
}
func (l *ListWidget) Origin() image.Point {
l.blockLock.Lock()
defer l.blockLock.Unlock()
return l.block.Rect.Min
}
func (l *ListWidget) SetOrigin(p image.Point) {
l.blockLock.Lock()
defer l.blockLock.Unlock()
l.block.SetOrigin(p)
}
func (l *ListWidget) Render(origin image.Point) Block {
l.blockLock.Lock()
defer l.blockLock.Unlock()
b := NewBlock(0, 0, 0, 0)
b.Rect = l.block.Rect.Add(origin)
for k, v := range l.block.Cells {
k = k.Add(origin)
b.Cells[k] = v
}
return b
}
func (l *ListWidget) AddEntry(entry ListEntry) {
l.listLock.Lock()
defer l.listLock.Unlock()
if l.listBuf == nil {
l.listBuf = make([][]Cell, 1)
} else {
l.listBuf = append(l.listBuf, []Cell{})
}
index := len(l.listBuf) - 1
for _, r := range entry.String() {
cell := Cell{}
cell.Value = r
cell.Attrs = entry.Attributes()
l.listBuf[index] = append(l.listBuf[index], cell)
}
}
func (l *ListWidget) Commit() {
l.listLock.Lock()
l.list = append([][]Cell{}, l.listBuf...)
l.listLock.Unlock()
l.listBuf = nil
l.recalculateCells()
}
func (l *ListWidget) SetBorder(b bool) {
if l.border == b {
return
}
l.border = b
l.recalculateCells()
}
func (l *ListWidget) SetRightAlign(b bool) {
if l.rightAlign == b {
return
}
l.rightAlign = b
l.recalculateCells()
}
func (l *ListWidget) recalculateCells() {
l.listLock.Lock()
defer l.listLock.Unlock()
l.blockLock.Lock()
origin := l.block.Rect.Min
size := l.block.Rect.Size()
l.blockLock.Unlock()
cells := make(map[image.Point]Cell)
var i, bx, by int
if l.border {
size = size.Add(image.Point{2, 2})
bx = 1
by = 1
}
for x := 0; x < size.X; x++ {
for y := 0; y < size.Y; y++ {
c := Cell{Value: ' '}
point := image.Pt(x, y).Add(origin)
if l.border {
cell, ok := l.borderCell(image.Pt(x, y), size)
if ok {
cells[point] = cell
continue
}
}
if y < len(l.list)+by {
length := len(l.list[y-by])
if l.rightAlign {
i = (size.X - x - length - bx) * -1
} else {
i = x - bx
}
if i < length && i >= 0 {
c = l.list[y-by][i]
}
cells[point] = c
}
}
}
l.blockLock.Lock()
l.block.Cells = cells
l.blockLock.Unlock()
}
func (l *ListWidget) borderCell(p image.Point, size image.Point) (Cell, bool) {
c := Cell{}
c.Attrs = l.Attributes()
if p.X != 0 && p.X != size.X-1 && p.Y != 0 && p.Y != size.Y-1 {
return c, false
}
if p.X == 0 && p.Y == 0 {
c.Value = BorderRune(TopLeft, l.Style)
} else if p.X == size.X-1 && p.Y == 0 {
c.Value = BorderRune(TopRight, l.Style)
} else if p.X == 0 && p.Y == size.Y-1 {
c.Value = BorderRune(BottomLeft, l.Style)
} else if p.X == size.X-1 && p.Y == size.Y-1 {
c.Value = BorderRune(BottomRight, l.Style)
} else if p.X == 0 || p.X == size.X-1 {
c.Value = BorderRune(Vertical, l.Style)
} else if p.Y == 0 || p.Y == size.Y-1 {
c.Value = BorderRune(Horizontal, l.Style)
}
return c, true
}