continue to add more building blocks
This commit is contained in:
parent
117c7e0e41
commit
de3f9cfadb
135
cmd/manage/interface.go
Normal file
135
cmd/manage/interface.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Drawable interface {
|
||||||
|
Draw(tcell.Screen)
|
||||||
|
SetSize(x, y, h, w int)
|
||||||
|
}
|
||||||
|
|
||||||
|
type offsets struct {
|
||||||
|
top int
|
||||||
|
bottom int
|
||||||
|
left int
|
||||||
|
right int
|
||||||
|
}
|
||||||
|
|
||||||
|
type contents []struct {
|
||||||
|
offsets offsets
|
||||||
|
container Drawable
|
||||||
|
}
|
||||||
|
|
||||||
|
type box struct {
|
||||||
|
x, y int
|
||||||
|
h, w int
|
||||||
|
title Drawable
|
||||||
|
menuItems Drawable
|
||||||
|
contents []struct {
|
||||||
|
offsets offsets
|
||||||
|
container Drawable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBox(title string, menuItems []string, contents contents) *box {
|
||||||
|
return &box{
|
||||||
|
title: NewPaddedText(title),
|
||||||
|
menuItems: NewPaddedText(strings.Join(menuItems, " ")),
|
||||||
|
contents: contents,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *box) SetSize(x, y, h, w int) {
|
||||||
|
b.x, b.y, b.h, b.w = x, y, h, w
|
||||||
|
b.title.SetSize(b.x+2, b.y, 0, 0)
|
||||||
|
b.menuItems.SetSize(b.x+2, b.y+b.h-1, 0, 0)
|
||||||
|
for c := range b.contents {
|
||||||
|
x := b.x + b.contents[c].offsets.left
|
||||||
|
y := b.y + b.contents[c].offsets.top
|
||||||
|
h := b.h - b.contents[c].offsets.bottom
|
||||||
|
w := b.w - b.contents[c].offsets.right
|
||||||
|
b.contents[c].container.SetSize(x, y, h, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *box) Draw(s tcell.Screen) {
|
||||||
|
for m := 1; m < b.w-1; m++ {
|
||||||
|
s.SetContent(m, b.y, tcell.RuneHLine, nil, tcell.StyleDefault)
|
||||||
|
s.SetContent(m, b.h-1, tcell.RuneHLine, nil, tcell.StyleDefault)
|
||||||
|
}
|
||||||
|
for m := 1; m < b.h-1; m++ {
|
||||||
|
s.SetContent(b.x, m, tcell.RuneVLine, nil, tcell.StyleDefault)
|
||||||
|
s.SetContent(b.w-1, m, tcell.RuneVLine, nil, tcell.StyleDefault)
|
||||||
|
}
|
||||||
|
s.SetContent(b.x, b.y, tcell.RuneULCorner, nil, tcell.StyleDefault)
|
||||||
|
s.SetContent(b.w-1, b.y, tcell.RuneURCorner, nil, tcell.StyleDefault)
|
||||||
|
s.SetContent(b.x, b.h-1, tcell.RuneLLCorner, nil, tcell.StyleDefault)
|
||||||
|
s.SetContent(b.w-1, b.h-1, tcell.RuneLRCorner, nil, tcell.StyleDefault)
|
||||||
|
|
||||||
|
if b.title != nil {
|
||||||
|
b.title.Draw(s)
|
||||||
|
}
|
||||||
|
if b.menuItems != nil {
|
||||||
|
b.menuItems.Draw(s)
|
||||||
|
}
|
||||||
|
for c := range b.contents {
|
||||||
|
b.contents[c].container.Draw(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type list struct {
|
||||||
|
x, y int
|
||||||
|
h, w int
|
||||||
|
selected int
|
||||||
|
listItems []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewList(listItems []string, initialSelected int) *list {
|
||||||
|
return &list{
|
||||||
|
listItems: listItems,
|
||||||
|
selected: initialSelected,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *list) SetSize(x, y, h, w int) {
|
||||||
|
l.x, l.y, l.h, l.w = x, y, h, w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *list) Draw(s tcell.Screen) {
|
||||||
|
for i := range l.listItems {
|
||||||
|
for j, r := range l.listItems[i] {
|
||||||
|
s.SetContent(l.x+j, l.y+i, r, nil, tcell.StyleDefault)
|
||||||
|
}
|
||||||
|
if i == l.selected {
|
||||||
|
s.SetContent(l.x+len(l.listItems[i])+1, l.y+i, '<', nil, tcell.StyleDefault)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type paddedText struct {
|
||||||
|
x, y int
|
||||||
|
h, w int
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPaddedText(text string) *paddedText {
|
||||||
|
return &paddedText{text: text}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) Draw(s tcell.Screen) {
|
||||||
|
t := p.x
|
||||||
|
s.SetContent(t, p.y, ' ', nil, tcell.StyleDefault)
|
||||||
|
t++
|
||||||
|
for _, r := range p.text {
|
||||||
|
s.SetContent(t, p.y, r, nil, tcell.StyleDefault)
|
||||||
|
t++
|
||||||
|
}
|
||||||
|
s.SetContent(t, p.y, ' ', nil, tcell.StyleDefault)
|
||||||
|
}
|
@ -15,7 +15,6 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
screen, err := tcell.NewScreen()
|
screen, err := tcell.NewScreen()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
@ -24,31 +23,31 @@ func main() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
l := NewList([]string{"foo", "bar", "baz"}, 0)
|
||||||
|
b := NewBox(
|
||||||
|
"library",
|
||||||
|
[]string{"(e)dit", "(q)uit"},
|
||||||
|
contents{{
|
||||||
|
offsets: offsets{top: 1, left: 2, bottom: -2, right: -2},
|
||||||
|
container: l,
|
||||||
|
}},
|
||||||
|
)
|
||||||
|
|
||||||
|
// init
|
||||||
screen.Clear()
|
screen.Clear()
|
||||||
w, h := screen.Size()
|
w, h := screen.Size()
|
||||||
l := &list{
|
b.SetSize(0, 0, h, w)
|
||||||
w: w - 4, h: h - 4,
|
b.Draw(screen)
|
||||||
x: 2, y: 1,
|
screen.Sync()
|
||||||
selected: 2,
|
|
||||||
listItems: []string{"foo", "bar", "baz"},
|
// UI loop
|
||||||
}
|
|
||||||
b := &box{
|
|
||||||
w: w, h: h,
|
|
||||||
x: 0, y: 0,
|
|
||||||
title: "library",
|
|
||||||
menuItems: []string{"(e)dit", "(q)uit"},
|
|
||||||
contents: l,
|
|
||||||
}
|
|
||||||
for {
|
for {
|
||||||
e := screen.PollEvent()
|
e := screen.PollEvent()
|
||||||
switch v := e.(type) {
|
switch v := e.(type) {
|
||||||
case *tcell.EventError:
|
case *tcell.EventError:
|
||||||
screen.Clear()
|
|
||||||
screen.Beep()
|
screen.Beep()
|
||||||
b.draw(screen)
|
case *tcell.EventKey: // input handling
|
||||||
screen.SetContent(1, 1, 'E', nil, tcell.StyleDefault)
|
|
||||||
case *tcell.EventKey:
|
|
||||||
screen.Clear()
|
|
||||||
if v.Key() == tcell.KeyUp && l.selected > 0 {
|
if v.Key() == tcell.KeyUp && l.selected > 0 {
|
||||||
l.selected = l.selected - 1
|
l.selected = l.selected - 1
|
||||||
}
|
}
|
||||||
@ -60,89 +59,18 @@ func main() {
|
|||||||
fmt.Printf("Thank you for playing Wing Commander!\n\n")
|
fmt.Printf("Thank you for playing Wing Commander!\n\n")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b.draw(screen)
|
|
||||||
case *tcell.EventResize:
|
|
||||||
screen.Clear()
|
screen.Clear()
|
||||||
|
b.Draw(screen)
|
||||||
|
case *tcell.EventResize: // screen redraw
|
||||||
w, h := screen.Size()
|
w, h := screen.Size()
|
||||||
b.w, b.h = w, h
|
b.SetSize(0, 0, h, w)
|
||||||
b.draw(screen)
|
screen.Clear()
|
||||||
|
b.Draw(screen)
|
||||||
case *tcell.EventInterrupt:
|
case *tcell.EventInterrupt:
|
||||||
case *tcell.EventMouse:
|
case *tcell.EventMouse:
|
||||||
case *tcell.EventTime:
|
case *tcell.EventTime:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
screen.Sync()
|
screen.Sync() // repaint
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type drawable interface {
|
|
||||||
draw(tcell.Screen)
|
|
||||||
}
|
|
||||||
|
|
||||||
type box struct {
|
|
||||||
x, y int
|
|
||||||
h, w int
|
|
||||||
title string
|
|
||||||
menuItems []string
|
|
||||||
contents drawable
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *box) draw(s tcell.Screen) {
|
|
||||||
for m := 1; m < b.w-1; m++ {
|
|
||||||
s.SetContent(m, b.y, tcell.RuneHLine, nil, tcell.StyleDefault)
|
|
||||||
s.SetContent(m, b.h-1, tcell.RuneHLine, nil, tcell.StyleDefault)
|
|
||||||
}
|
|
||||||
for m := 1; m < b.h-1; m++ {
|
|
||||||
s.SetContent(b.x, m, tcell.RuneVLine, nil, tcell.StyleDefault)
|
|
||||||
s.SetContent(b.w-1, m, tcell.RuneVLine, nil, tcell.StyleDefault)
|
|
||||||
}
|
|
||||||
s.SetContent(b.x, b.y, tcell.RuneULCorner, nil, tcell.StyleDefault)
|
|
||||||
s.SetContent(b.w-1, b.y, tcell.RuneURCorner, nil, tcell.StyleDefault)
|
|
||||||
s.SetContent(b.x, b.h-1, tcell.RuneLLCorner, nil, tcell.StyleDefault)
|
|
||||||
s.SetContent(b.w-1, b.h-1, tcell.RuneLRCorner, nil, tcell.StyleDefault)
|
|
||||||
|
|
||||||
if b.title != "" {
|
|
||||||
t := b.x + 2
|
|
||||||
s.SetContent(t, b.y, ' ', nil, tcell.StyleDefault)
|
|
||||||
t++
|
|
||||||
for _, r := range b.title {
|
|
||||||
s.SetContent(t, b.y, r, nil, tcell.StyleDefault)
|
|
||||||
t++
|
|
||||||
}
|
|
||||||
s.SetContent(t, b.y, ' ', nil, tcell.StyleDefault)
|
|
||||||
}
|
|
||||||
if len(b.menuItems) != 0 {
|
|
||||||
t := b.x + 2
|
|
||||||
s.SetContent(t, b.h-1, ' ', nil, tcell.StyleDefault)
|
|
||||||
t++
|
|
||||||
for i := range b.menuItems {
|
|
||||||
for _, r := range b.menuItems[i] {
|
|
||||||
s.SetContent(t, b.h-1, r, nil, tcell.StyleDefault)
|
|
||||||
t++
|
|
||||||
}
|
|
||||||
s.SetContent(t, b.h-1, ' ', nil, tcell.StyleDefault)
|
|
||||||
t++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if b.contents != nil {
|
|
||||||
b.contents.draw(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type list struct {
|
|
||||||
x, y int
|
|
||||||
h, w int
|
|
||||||
selected int
|
|
||||||
listItems []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *list) draw(s tcell.Screen) {
|
|
||||||
for i := range l.listItems {
|
|
||||||
for j, r := range l.listItems[i] {
|
|
||||||
s.SetContent(l.x+j, l.y+i, r, nil, tcell.StyleDefault)
|
|
||||||
}
|
|
||||||
if i == l.selected {
|
|
||||||
s.SetContent(l.x+len(l.listItems[i])+1, l.y+i, '<', nil, tcell.StyleDefault)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,4 +5,4 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 3306:3306
|
- 3306:3306
|
||||||
environment:
|
environment:
|
||||||
- MYSQL_ROOT_PASSWORD=KigYBNCT9IU5XyB3ehzMLFWyI
|
- MYSQL_ROOT_PASSWORD=KigYBNCT9IU5XyB3ehzMLFWyI # for dev testing only, obviously.
|
||||||
|
Loading…
Reference in New Issue
Block a user