huge refactor

This commit is contained in:
Timon Ringwald 2022-04-01 20:10:51 +02:00
parent 322b954149
commit d335211770
15 changed files with 306 additions and 72 deletions

View File

@ -1,13 +1,12 @@
package tui
import (
"git.tordarus.net/tordarus/buf2d"
"github.com/gdamore/tcell"
)
func drawBuffer(scr tcell.Screen, buf *buf2d.Buffer) {
buf.Draw(func(x, y int, cn rune) {
scr.SetContent(x, y, cn, nil, tcell.StyleDefault)
func drawBuffer(scr tcell.Screen, buf *ViewBuffer) {
buf.ForEach(func(x, y int, rn Rune) {
scr.SetContent(x, y, rn.Rn, nil, rn.Style)
})
scr.Show()
}

View File

@ -1,7 +1,5 @@
package tui
import "github.com/gdamore/tcell"
type Events interface {
OnKeyPressed(key tcell.Key)
OnKeyPressed(event *KeyEvent) (consumed bool)
}

16
go.mod Normal file
View File

@ -0,0 +1,16 @@
module git.tordarus.net/Tordarus/tui
go 1.18
require (
git.tordarus.net/Tordarus/buf2d v1.1.0
github.com/gdamore/tcell v1.4.0
)
require (
github.com/gdamore/encoding v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
github.com/mattn/go-runewidth v0.0.7 // indirect
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 // indirect
golang.org/x/text v0.3.0 // indirect
)

14
go.sum Normal file
View File

@ -0,0 +1,14 @@
git.tordarus.net/Tordarus/buf2d v1.1.0 h1:rIZjD7yeX5XK2D1h75ET5Og0u/NQF3eVonnC5aaqVkQ=
git.tordarus.net/Tordarus/buf2d v1.1.0/go.mod h1:XXPpS8nQK0gUI0ki7ftV/qlprsGCRWFVSD4ybvDuUL8=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU=
github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756 h1:9nuHUbU8dRnRRfj9KjWUVrJeoexdbeMjttk6Oh1rD10=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

60
rune.go Normal file
View File

@ -0,0 +1,60 @@
package tui
import "regexp"
type Rune struct {
Rn rune
Style Style
}
var DefaultRune = Rune{' ', StyleDefault}
// Text represents a string with rune-based styling
type Text struct {
str string
style []Style
}
// Txt returns a Text containing the given str
// using the default style
func Txt(str string) *Text {
styles := make([]Style, 0, len(str))
for range str {
styles = append(styles, StyleDefault)
}
return &Text{str: str, style: styles}
}
// Len returns the amount of runes in t
func (t *Text) Len() int {
return len(t.style)
}
// AppendString appends str with default styling to t
func (t *Text) AppendString(str string) {
t.AppendText(Txt(str))
}
// PrependString prepends str with default styling to t
func (t *Text) PrependString(str string) {
newTxt := Txt(str)
newTxt.AppendText(t)
*t = *newTxt
}
// AppendText appends txt to t
func (t *Text) AppendText(txt *Text) {
t.str += txt.str
t.style = append(t.style, txt.style...)
}
// Style applies the given style to the part of t between startIndex (inclusive) and endIndex (exclusive)
func (t *Text) Style(style Style, startIndex, endIndex int) {
for i := startIndex; i < endIndex; i++ {
t.style[i] = style
}
}
func (t *Text) StyleRegex(style Style, pattern *regexp.Regexp) {
// TODO
}

View File

@ -4,7 +4,7 @@ import (
"errors"
"fmt"
"git.tordarus.net/tordarus/buf2d"
"git.tordarus.net/Tordarus/buf2d"
"github.com/gdamore/tcell"
)
@ -38,7 +38,7 @@ func (s *Screen) eventloop() {
go s.Redraw()
case *tcell.EventKey:
go func() {
s.Root.OnKeyPressed(event.Key())
s.Root.OnKeyPressed(event)
s.Redraw()
}()
default:
@ -53,6 +53,8 @@ func (s *Screen) Start() error {
if err != nil {
return err
}
defer s.scr.Fini()
s.Redraw()
return <-s.stopCh
}
@ -67,7 +69,7 @@ func (s *Screen) StopWithError(err error) {
func (s *Screen) Redraw() {
w, h := s.scr.Size()
buf := buf2d.NewBuffer(w, h)
buf := buf2d.NewBuffer(w, h, DefaultRune)
s.Root.Draw(buf)
drawBuffer(s.scr, buf)
}

View File

@ -4,22 +4,28 @@ import (
"errors"
"fmt"
"testing"
"tui"
"tui/views"
"git.tordarus.net/Tordarus/tui"
"git.tordarus.net/Tordarus/tui/views"
"github.com/gdamore/tcell"
)
func TestScreen(t *testing.T) {
eventView := views.NewEventView()
textView := views.NewTextView("hello world")
eventView := views.NewEventView(textView)
screen, err := tui.NewScreen(eventView)
if err != nil {
t.Error(err)
return
}
eventView.KeyPressed = func(key tcell.Key) {
screen.StopWithError(errors.New(fmt.Sprintf("%#v", key)))
eventView.KeyPressed = func(event *tui.KeyEvent) (consumed bool) {
if event.Key() == tcell.KeyCtrlC {
screen.StopWithError(errors.New(fmt.Sprintf("key: %#v | rune: %s", event.Key(), string(event.Rune()))))
}
//textView.Text = event.When().String()
return true
}
err = screen.Start()

13
types.go Normal file
View File

@ -0,0 +1,13 @@
package tui
import (
"git.tordarus.net/Tordarus/buf2d"
"github.com/gdamore/tcell"
)
type ViewBuffer = buf2d.Buffer[Rune]
type KeyEvent = tcell.EventKey
type Style = tcell.Style
type Color = tcell.Color
var StyleDefault Style = tcell.StyleDefault

31
utils.go Normal file
View File

@ -0,0 +1,31 @@
package tui
import (
"strings"
)
// WriteString writes a whole string to the buffer at position (x,y)
// no word wrap is applied at all. If the string does not fit, it will be truncated
func WriteString(b *ViewBuffer, str string, style Style, x, y int) {
dx := x
for _, r := range str {
if dx >= b.Width() {
return
}
b.Set(dx, y, Rune{r, style})
dx++
}
}
// WriteMultiLineString writes a multi-line string to the buffer at position (x,y)
// no word wrap is applied at all. If a line does not fit horizontally, it will be truncated
// All lines which do not fit vertically will be truncated as well
func WriteMultiLineString(b *ViewBuffer, str string, style Style, x, y int) {
lines := strings.Split(str, "\n")
for dy, line := range lines {
if dy >= b.Height() {
return
}
WriteString(b, line, style, x, y+dy)
}
}

42
view.go
View File

@ -1,18 +1,40 @@
package tui
import (
"git.tordarus.net/tordarus/buf2d"
"github.com/gdamore/tcell"
)
// View defines the behavior of any element displayable on screen
// To define custom Views, it is recommended to add ViewTmpl
// as the promoted anonymous field for your custom View struct.
// It implements the View interface with useful default behavior
type View interface {
Events
SetForeground(color tcell.Color)
Foreground() tcell.Color
SetForeground(color Color)
Foreground() Color
SetBackground(color tcell.Color)
Background() tcell.Color
SetBackground(color Color)
Background() Color
Draw(*buf2d.Buffer)
Style() Style
Draw(*ViewBuffer)
}
// Group defines the behavior of a View which can hold multiple sub views
// To define custom Groups, it is recommended to add GroupTmpl
// as the promoted anonymous field for your custom Wrapper struct.
// It implements the Group interface with useful default behavior
type Group interface {
View
Children() []View
}
// Wrapper defines the behavior of a GroupView which can hold exactly one sub view
// To define custom Wrappers, it is recommended to add WrapperTmpl
// as the promoted anonymous field for your custom Wrapper struct.
// It implements the Wrapper interface with useful default behavior
type Wrapper interface {
Group
SetView(View)
View() View
}

View File

@ -1,6 +1,3 @@
package tui
type ViewGroup interface {
View
Children() []*View
}
// TODO GroupTmpl

View File

@ -1,22 +1,27 @@
package views
import (
"tui"
"github.com/gdamore/tcell"
"git.tordarus.net/Tordarus/tui"
)
type EventView struct {
tui.ViewTmpl
KeyPressed func(key tcell.Key)
tui.WrapperTmpl
View tui.View
KeyPressed func(event *tui.KeyEvent) (consumed bool)
}
func (v *EventView) OnKeyPressed(key tcell.Key) {
func NewEventView(view tui.View) *EventView {
return &EventView{View: view}
}
func (v *EventView) OnKeyPressed(event *tui.KeyEvent) (consumed bool) {
if v.KeyPressed != nil {
v.KeyPressed(key)
return v.KeyPressed(event)
}
return v.ViewTmpl.OnKeyPressed(event)
}
func NewEventView() *EventView {
return &EventView{}
func (v *EventView) Draw(buf *tui.ViewBuffer) {
v.View.Draw(buf)
}

View File

@ -1,9 +1,7 @@
package views
import (
"tui"
"git.tordarus.net/tordarus/buf2d"
"git.tordarus.net/Tordarus/tui"
)
type TextView struct {
@ -13,8 +11,8 @@ type TextView struct {
var _ tui.View = &TextView{}
func (v *TextView) Draw(buf *buf2d.Buffer) {
buf.WriteMultiLineString(v.Text, 0, 0)
func (v *TextView) Draw(buf *tui.ViewBuffer) {
tui.WriteMultiLineString(buf, v.Text, v.Style(), 0, 0)
}
func NewTextView(text string) *TextView {

View File

@ -1,45 +1,44 @@
package tui
import (
"git.tordarus.net/tordarus/buf2d"
"github.com/gdamore/tcell"
)
import "github.com/gdamore/tcell"
type ViewTmpl struct {
view View
foreground tcell.Color
background tcell.Color
foreground *Color
background *Color
}
var _ View = &ViewTmpl{}
func NewViewTmpl(v View) *ViewTmpl {
return &ViewTmpl{
view: v,
func (v *ViewTmpl) Draw(buf *ViewBuffer) {
buf.Fill(DefaultRune)
}
func (v *ViewTmpl) OnKeyPressed(event *KeyEvent) (consumed bool) {
return false
}
func (v *ViewTmpl) Style() Style {
return StyleDefault.Background(v.Background()).Foreground(v.Foreground())
}
func (v *ViewTmpl) Foreground() Color {
if v.foreground == nil {
return tcell.ColorDefault
}
return *v.foreground
}
func (v *ViewTmpl) Draw(buf *buf2d.Buffer) {
buf.Fill(' ')
func (v *ViewTmpl) SetForeground(color Color) {
v.foreground = &color
}
func (v *ViewTmpl) OnKeyPressed(key tcell.Key) {
func (v *ViewTmpl) Background() Color {
if v.background == nil {
return tcell.ColorDefault
}
return *v.background
}
func (v *ViewTmpl) Foreground() tcell.Color {
return v.foreground
}
func (v *ViewTmpl) SetForeground(color tcell.Color) {
v.foreground = color
}
func (v *ViewTmpl) Background() tcell.Color {
return v.background
}
func (v *ViewTmpl) SetBackground(color tcell.Color) {
v.background = color
func (v *ViewTmpl) SetBackground(color Color) {
v.background = &color
}

74
wrappertmpl.go Normal file
View File

@ -0,0 +1,74 @@
package tui
import "github.com/gdamore/tcell"
type WrapperTmpl struct {
ViewTmpl
view View
}
var _ Wrapper = &WrapperTmpl{}
func (v *WrapperTmpl) Draw(buf *ViewBuffer) {
if v.view != nil {
v.view.Draw(buf)
} else {
v.ViewTmpl.Draw(buf)
}
}
func (v *WrapperTmpl) OnKeyPressed(event *KeyEvent) (consumed bool) {
if v.view != nil {
return v.view.OnKeyPressed(event)
}
return v.ViewTmpl.OnKeyPressed(event)
}
func (v *WrapperTmpl) Style() Style {
if v.view != nil {
return v.view.Style()
}
return v.ViewTmpl.Style()
}
func (v *WrapperTmpl) Foreground() Color {
if v.view != nil {
return v.view.Foreground()
}
return v.ViewTmpl.Foreground()
}
func (v *WrapperTmpl) SetForeground(color Color) {
if v.view != nil {
v.view.SetForeground(color)
} else {
v.ViewTmpl.SetForeground(color)
}
}
func (v *WrapperTmpl) Background() Color {
if v.background == nil {
return tcell.ColorDefault
}
return *v.background
}
func (v *WrapperTmpl) SetBackground(color Color) {
if v.view != nil {
v.view.SetBackground(color)
} else {
v.ViewTmpl.SetBackground(color)
}
}
func (v *WrapperTmpl) Children() []View {
return []View{v.view}
}
func (v *WrapperTmpl) View() View {
return v.view
}
func (v *WrapperTmpl) SetView(view View) {
v.view = view
}