add style management

This commit is contained in:
2021-07-13 18:32:01 -04:00
parent 8c6c9325da
commit c2d2fe25c0
4 changed files with 98 additions and 39 deletions

View File

@@ -11,6 +11,7 @@ import (
type Drawable interface {
Draw(tcell.Screen)
SetSize(x, y, h, w int)
SetStyle(tcell.Style)
}
type Offsets struct {
@@ -34,6 +35,11 @@ const (
LayoutVerticalPercent
)
var (
StyleActive = tcell.Style(0).Foreground(tcell.ColorWhite).Background(tcell.ColorBlack)
StyleInactive = tcell.Style(0).Foreground(tcell.ColorGray).Background(tcell.ColorBlack)
)
// A Container has no visible UI of its own, but arranges sub-components on the screen.
// layoutMethod manages how subcomponents are organized. If `LayoutUnmanaged`, it just uses the offsets
// in contents to paint on the screen. Otherwise, `LayoutHorizontalEven` and `LayoutVerticalEven` will
@@ -155,6 +161,10 @@ func (c *Container) SetSize(x, y, h, w int) {
}
}
func (c *Container) SetStyle(s tcell.Style) {
// containers have no visible elements to style
}
func (c *Container) Contents() Contents {
return c.contents
}
@@ -170,13 +180,17 @@ type Box struct {
title Drawable
menuItems Drawable
contents Contents
style tcell.Style
cascade bool
}
func NewBox(title string, menuItems []string, contents Contents) *Box {
func NewBox(title string, menuItems []string, contents Contents, initialStyle tcell.Style, cascade bool) *Box {
return &Box{
title: NewPaddedText(title),
menuItems: NewPaddedText(strings.Join(menuItems, " ")),
contents: contents,
style: initialStyle,
cascade: cascade,
}
}
@@ -195,17 +209,17 @@ func (b *Box) SetSize(x, y, h, w int) {
func (b *Box) Draw(s tcell.Screen) {
for m := b.x + 1; m < b.x+b.w-1; m++ {
s.SetContent(m, b.y, tcell.RuneHLine, nil, tcell.StyleDefault)
s.SetContent(m, b.y+b.h-1, tcell.RuneHLine, nil, tcell.StyleDefault)
s.SetContent(m, b.y, tcell.RuneHLine, nil, b.style)
s.SetContent(m, b.y+b.h-1, tcell.RuneHLine, nil, b.style)
}
for m := b.y + 1; m < b.y+b.h-1; m++ {
s.SetContent(b.x, m, tcell.RuneVLine, nil, tcell.StyleDefault)
s.SetContent(b.x+b.w-1, m, tcell.RuneVLine, nil, tcell.StyleDefault)
s.SetContent(b.x, m, tcell.RuneVLine, nil, b.style)
s.SetContent(b.x+b.w-1, m, tcell.RuneVLine, nil, b.style)
}
s.SetContent(b.x, b.y, tcell.RuneULCorner, nil, tcell.StyleDefault)
s.SetContent(b.x+b.w-1, b.y, tcell.RuneURCorner, nil, tcell.StyleDefault)
s.SetContent(b.x, b.y+b.h-1, tcell.RuneLLCorner, nil, tcell.StyleDefault)
s.SetContent(b.x+b.w-1, b.y+b.h-1, tcell.RuneLRCorner, nil, tcell.StyleDefault)
s.SetContent(b.x, b.y, tcell.RuneULCorner, nil, b.style)
s.SetContent(b.x+b.w-1, b.y, tcell.RuneURCorner, nil, b.style)
s.SetContent(b.x, b.y+b.h-1, tcell.RuneLLCorner, nil, b.style)
s.SetContent(b.x+b.w-1, b.y+b.h-1, tcell.RuneLRCorner, nil, b.style)
if b.title != nil {
b.title.Draw(s)
@@ -218,6 +232,17 @@ func (b *Box) Draw(s tcell.Screen) {
}
}
func (b *Box) SetStyle(s tcell.Style) {
b.style = s
b.title.SetStyle(s)
b.menuItems.SetStyle(s)
if b.cascade {
for c := range b.contents {
b.contents[c].Container.SetStyle(s)
}
}
}
func (b *Box) Contents() Contents {
return b.contents
}
@@ -232,6 +257,7 @@ type List struct {
h, w int
selected int
listItems []ListKeyValue
style tcell.Style
}
type ListKeyValue struct {
@@ -253,14 +279,18 @@ func (l *List) SetSize(x, y, h, w int) {
func (l *List) Draw(s tcell.Screen) {
for i := range l.listItems {
for j, r := range l.listItems[i].Value {
s.SetContent(l.x+j, l.y+i, r, nil, tcell.StyleDefault)
s.SetContent(l.x+j, l.y+i, r, nil, l.style)
}
if i == l.selected {
s.SetContent(l.x+len(l.listItems[i].Value)+1, l.y+i, '<', nil, tcell.StyleDefault)
s.SetContent(l.x+len(l.listItems[i].Value)+1, l.y+i, '<', nil, l.style)
}
}
}
func (l *List) SetStyle(s tcell.Style) {
l.style = s
}
func (l *List) Selected() int {
return l.selected
}
@@ -279,9 +309,10 @@ func (l *List) ListMembers() []ListKeyValue {
// BookDetails displays an editable list of book details
type BookDetails struct {
x, y int
h, w int
book *book.Book
x, y int
h, w int
book *book.Book
style tcell.Style
}
func NewBookDetails(b *book.Book) *BookDetails {
@@ -327,17 +358,23 @@ func (l *BookDetails) Draw(s tcell.Screen) {
if i < l.h-2 {
kv := NewKeyValue(items[i].label, ": ", items[i].value)
kv.SetSize(l.x, l.y+i, 0, 0)
kv.SetStyle(l.style)
kv.Draw(s)
}
}
}
func (l *BookDetails) SetStyle(s tcell.Style) {
l.style = s
}
// PaddedText outputs strings with a space on both sides.
// Useful for generating headings, footers, etc. Used by Box.
type PaddedText struct {
x, y int
h, w int
text string
x, y int
h, w int
text string
style tcell.Style
}
func NewPaddedText(text string) *PaddedText {
@@ -348,18 +385,22 @@ func (p *PaddedText) SetSize(x, y, _, _ int) {
p.x, p.y, p.h, p.w = x, y, 1, len(p.text)+2
}
func (p *PaddedText) SetStyle(s tcell.Style) {
p.style = s
}
func (p *PaddedText) Draw(s tcell.Screen) {
if p.text == "" {
return
}
t := p.x
s.SetContent(t, p.y, ' ', nil, tcell.StyleDefault)
s.SetContent(t, p.y, ' ', nil, p.style)
t++
for _, r := range p.text {
s.SetContent(t, p.y, r, nil, tcell.StyleDefault)
s.SetContent(t, p.y, r, nil, p.style)
t++
}
s.SetContent(t, p.y, ' ', nil, tcell.StyleDefault)
s.SetContent(t, p.y, ' ', nil, p.style)
}
type KeyValue struct {
@@ -368,6 +409,7 @@ type KeyValue struct {
key string
value string
separator string
style tcell.Style
}
func NewKeyValue(key, separator, value string) *KeyValue {
@@ -382,15 +424,19 @@ func (p *KeyValue) SetSize(x, y, _, _ int) {
p.x, p.y, p.h, p.w = x, y, 1, len(p.key)+len(p.separator)+len(p.value)
}
func (p *KeyValue) SetStyle(s tcell.Style) {
p.style = s
}
func (p *KeyValue) Draw(s tcell.Screen) {
for j, r := range p.key {
s.SetContent(p.x+j, p.y, r, nil, tcell.StyleDefault)
s.SetContent(p.x+j, p.y, r, nil, p.style)
}
for j, r := range p.separator {
s.SetContent(p.x+len(p.key)+j, p.y, r, nil, tcell.StyleDefault)
s.SetContent(p.x+len(p.key)+j, p.y, r, nil, p.style)
}
for j, r := range p.value {
s.SetContent(p.x+len(p.key)+len(p.separator)+j, p.y, r, nil, tcell.StyleDefault)
s.SetContent(p.x+len(p.key)+len(p.separator)+j, p.y, r, nil, p.style)
}
}

View File

@@ -13,7 +13,7 @@ func TestContainerOneBox(t *testing.T) {
└──────────────────┘
`
m := &MockScreen{}
one := NewBox("box one", nil, Contents{})
one := NewBox("box one", nil, Contents{}, 0, false)
container := NewContainer(
Contents{{Container: one}},
LayoutHorizontalEven,
@@ -38,8 +38,8 @@ func TestContainerTwoBoxesHStack(t *testing.T) {
└────────┘└────────┘
`
m := &MockScreen{}
one := NewBox("one", nil, Contents{})
two := NewBox("two", nil, Contents{})
one := NewBox("one", nil, Contents{}, 0, false)
two := NewBox("two", nil, Contents{}, 0, false)
container := NewContainer(
Contents{{Container: one}, {Container: two}},
LayoutHorizontalEven,
@@ -64,9 +64,9 @@ func TestContainerThreeBoxesUnevenHStack(t *testing.T) {
└────────┘└────────┘└───────┘
`
m := &MockScreen{}
one := NewBox("one", nil, Contents{})
two := NewBox("two", nil, Contents{})
three := NewBox("three", nil, Contents{})
one := NewBox("one", nil, Contents{}, 0, false)
two := NewBox("two", nil, Contents{}, 0, false)
three := NewBox("three", nil, Contents{}, 0, false)
container := NewContainer(
Contents{{Container: one}, {Container: two}, {Container: three}},
LayoutHorizontalEven,
@@ -91,8 +91,8 @@ func TestContainerTwoBoxesHPercentStack(t *testing.T) {
└────────────┘└──────┘
`
m := &MockScreen{}
one := NewBox("one", nil, Contents{})
two := NewBox("two", nil, Contents{})
one := NewBox("one", nil, Contents{}, 0, false)
two := NewBox("two", nil, Contents{}, 0, false)
container := NewContainer(
Contents{
{Container: one, Offsets: Offsets{Percent: 2}},
@@ -124,8 +124,8 @@ func TestContainerTwoBoxesVStack(t *testing.T) {
└────────┘
`
m := &MockScreen{}
one := NewBox("one", nil, Contents{})
two := NewBox("two", nil, Contents{})
one := NewBox("one", nil, Contents{}, 0, false)
two := NewBox("two", nil, Contents{}, 0, false)
container := NewContainer(
Contents{{Container: one}, {Container: two}},
LayoutVerticalEven,
@@ -155,8 +155,8 @@ func TestContainerTwoBoxesPercentageVStack(t *testing.T) {
└────────┘
`
m := &MockScreen{}
one := NewBox("one", nil, Contents{})
two := NewBox("two", nil, Contents{})
one := NewBox("one", nil, Contents{}, 0, false)
two := NewBox("two", nil, Contents{}, 0, false)
container := NewContainer(
Contents{
{Container: one, Offsets: Offsets{Percent: 2}},