aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--go.mod1
-rw-r--r--lib/ui/borders.go16
-rw-r--r--lib/ui/context.go86
-rw-r--r--lib/ui/fill.go4
-rw-r--r--lib/ui/interactive.go6
-rw-r--r--lib/ui/stack.go9
-rw-r--r--lib/ui/tab.go19
-rw-r--r--lib/ui/text.go18
-rw-r--r--lib/ui/ui.go48
-rw-r--r--widgets/aerc.go24
-rw-r--r--widgets/exline.go49
-rw-r--r--widgets/status.go30
12 files changed, 151 insertions, 159 deletions
diff --git a/go.mod b/go.mod
index cc52cd8..c41cca6 100644
--- a/go.mod
+++ b/go.mod
@@ -6,4 +6,5 @@ require (
"github.com/mattn/go-isatty" v0.0.3
"github.com/mattn/go-runewidth" v0.0.2
"github.com/nsf/termbox-go" v0.0.0-20180129072728-88b7b944be8b
+ "github.com/gdamore/tcell" v1.0.0
)
diff --git a/lib/ui/borders.go b/lib/ui/borders.go
index 08071ad..38b35fd 100644
--- a/lib/ui/borders.go
+++ b/lib/ui/borders.go
@@ -1,7 +1,7 @@
package ui
import (
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
)
const (
@@ -45,27 +45,23 @@ func (bordered *Bordered) Draw(ctx *Context) {
y := 0
width := ctx.Width()
height := ctx.Height()
- cell := tb.Cell{
- Ch: ' ',
- Fg: tb.ColorBlack,
- Bg: tb.ColorWhite,
- }
+ style := tcell.StyleDefault.Background(tcell.ColorWhite).Foreground(tcell.ColorBlack)
if bordered.borders&BORDER_LEFT != 0 {
- ctx.Fill(0, 0, 1, ctx.Height(), cell)
+ ctx.Fill(0, 0, 1, ctx.Height(), ' ', style)
x += 1
width -= 1
}
if bordered.borders&BORDER_TOP != 0 {
- ctx.Fill(0, 0, ctx.Width(), 1, cell)
+ ctx.Fill(0, 0, ctx.Width(), 1, ' ', style)
y += 1
height -= 1
}
if bordered.borders&BORDER_RIGHT != 0 {
- ctx.Fill(ctx.Width()-1, 0, 1, ctx.Height(), cell)
+ ctx.Fill(ctx.Width()-1, 0, 1, ctx.Height(), ' ', style)
width -= 1
}
if bordered.borders&BORDER_BOTTOM != 0 {
- ctx.Fill(0, ctx.Height()-1, ctx.Width(), 1, cell)
+ ctx.Fill(0, ctx.Height()-1, ctx.Width(), 1, ' ', style)
height -= 1
}
subctx := ctx.Subcontext(x, y, width, height)
diff --git a/lib/ui/context.go b/lib/ui/context.go
index ca3f452..8031689 100644
--- a/lib/ui/context.go
+++ b/lib/ui/context.go
@@ -4,73 +4,77 @@ import (
"fmt"
"github.com/mattn/go-runewidth"
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
+ "github.com/gdamore/tcell/views"
)
// A context allows you to draw in a sub-region of the terminal
type Context struct {
- x int
- y int
- width int
- height int
+ viewport *views.ViewPort
}
func (ctx *Context) X() int {
- return ctx.x
+ x, _, _, _ := ctx.viewport.GetPhysical()
+ return x
}
func (ctx *Context) Y() int {
- return ctx.y
+ _, y, _, _ := ctx.viewport.GetPhysical()
+ return y
}
func (ctx *Context) Width() int {
- return ctx.width
+ width, _ := ctx.viewport.Size()
+ return width
}
func (ctx *Context) Height() int {
- return ctx.height
+ _, height := ctx.viewport.Size()
+ return height
}
-func NewContext(width, height int) *Context {
- return &Context{0, 0, width, height}
+func NewContext(width, height int, screen tcell.Screen) *Context {
+ vp := views.NewViewPort(screen, 0, 0, width, height)
+ return &Context{vp}
}
func (ctx *Context) Subcontext(x, y, width, height int) *Context {
- if x+width > ctx.width || y+height > ctx.height {
- panic(fmt.Errorf("Attempted to create context larger than parent"))
+ vp_width, vp_height := ctx.viewport.Size()
+ if (x < 0 || y < 0) {
+ panic(fmt.Errorf("Attempted to create context with negative offset"))
}
- return &Context{
- x: ctx.x + x,
- y: ctx.y + y,
- width: width,
- height: height,
+ if (x + width > vp_width || y + height > vp_height) {
+ panic(fmt.Errorf("Attempted to create context larger than parent"))
}
+ vp := views.NewViewPort(ctx.viewport, x, y, width, height)
+ return &Context{vp}
}
-func (ctx *Context) SetCell(x, y int, ch rune, fg, bg tb.Attribute) {
- if x >= ctx.width || y >= ctx.height {
+func (ctx *Context) SetCell(x, y int, ch rune, style tcell.Style) {
+ width, height := ctx.viewport.Size()
+ if x >= width || y >= height {
panic(fmt.Errorf("Attempted to draw outside of context"))
}
- tb.SetCell(ctx.x+x, ctx.y+y, ch, fg, bg)
+ crunes := []rune{}
+ ctx.viewport.SetContent(x, y, ch, crunes, style)
}
-func (ctx *Context) Printf(x, y int, ref tb.Cell,
+func (ctx *Context) Printf(x, y int, style tcell.Style,
format string, a ...interface{}) int {
+ width, height := ctx.viewport.Size()
- if x >= ctx.width || y >= ctx.height {
+ if x >= width || y >= height {
panic(fmt.Errorf("Attempted to draw outside of context"))
}
str := fmt.Sprintf(format, a...)
- x += ctx.x
- y += ctx.y
old_x := x
newline := func() bool {
x = old_x
y++
- return y < ctx.height
+ return y < height
}
for _, ch := range str {
if str == " こんにちは " {
@@ -84,9 +88,10 @@ func (ctx *Context) Printf(x, y int, ref tb.Cell,
case '\r':
x = old_x
default:
- tb.SetCell(x, y, ch, ref.Fg, ref.Bg)
+ crunes := []rune{}
+ ctx.viewport.SetContent(x, y, ch, crunes, style)
x += runewidth.RuneWidth(ch)
- if x == old_x+ctx.width {
+ if x == old_x + width {
if !newline() {
return runewidth.StringWidth(str)
}
@@ -97,13 +102,20 @@ func (ctx *Context) Printf(x, y int, ref tb.Cell,
return runewidth.StringWidth(str)
}
-func (ctx *Context) Fill(x, y, width, height int, ref tb.Cell) {
- _x := x
- _y := y
- for ; y < _y+height && y < ctx.height; y++ {
- for ; x < _x+width && x < ctx.width; x++ {
- ctx.SetCell(x, y, ref.Ch, ref.Fg, ref.Bg)
- }
- x = _x
- }
+//func (ctx *Context) Screen() tcell.Screen {
+// return ctx.screen
+//}
+
+func (ctx *Context) Fill(x, y, width, height int, rune rune, style tcell.Style) {
+ vp := views.NewViewPort(ctx.viewport, x, y, width, height)
+ vp.Fill(rune, style)
+}
+
+func (ctx *Context) SetCursor(x, y int) {
+ // FIXME: Cursor needs to be set on tcell.Screen, or layout has to
+ // provide a CellModel
+ // cv := views.NewCellView()
+ // cv.Init()
+ // cv.SetView(ctx.viewport)
+ // cv.SetCursor(x, y)
}
diff --git a/lib/ui/fill.go b/lib/ui/fill.go
index 3c6f0a5..4d36478 100644
--- a/lib/ui/fill.go
+++ b/lib/ui/fill.go
@@ -1,7 +1,7 @@
package ui
import (
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
)
type Fill rune
@@ -13,7 +13,7 @@ func NewFill(f rune) Fill {
func (f Fill) Draw(ctx *Context) {
for x := 0; x < ctx.Width(); x += 1 {
for y := 0; y < ctx.Height(); y += 1 {
- ctx.SetCell(x, y, rune(f), tb.ColorDefault, tb.ColorDefault)
+ ctx.SetCell(x, y, rune(f), tcell.StyleDefault)
}
}
}
diff --git a/lib/ui/interactive.go b/lib/ui/interactive.go
index efab828..2d4f099 100644
--- a/lib/ui/interactive.go
+++ b/lib/ui/interactive.go
@@ -1,17 +1,17 @@
package ui
import (
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
)
type Interactive interface {
// Returns true if the event was handled by this component
- Event(event tb.Event) bool
+ Event(event tcell.Event) bool
}
type Simulator interface {
// Queues up the given input events for simulation
- Simulate(events []tb.Event)
+ Simulate(events []tcell.Event)
}
type DrawableInteractive interface {
diff --git a/lib/ui/stack.go b/lib/ui/stack.go
index 9f81db8..3c66f5a 100644
--- a/lib/ui/stack.go
+++ b/lib/ui/stack.go
@@ -3,7 +3,7 @@ package ui
import (
"fmt"
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
)
type Stack struct {
@@ -29,12 +29,7 @@ func (stack *Stack) Draw(ctx *Context) {
if len(stack.children) > 0 {
stack.Peek().Draw(ctx)
} else {
- cell := tb.Cell{
- Fg: tb.ColorDefault,
- Bg: tb.ColorDefault,
- Ch: ' ',
- }
- ctx.Fill(0, 0, ctx.Width(), ctx.Height(), cell)
+ ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
}
}
diff --git a/lib/ui/tab.go b/lib/ui/tab.go
index e6a8aa5..d0635c7 100644
--- a/lib/ui/tab.go
+++ b/lib/ui/tab.go
@@ -1,7 +1,7 @@
package ui
import (
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
)
type Tabs struct {
@@ -72,21 +72,14 @@ func (tabs *Tabs) Select(index int) {
func (strip *TabStrip) Draw(ctx *Context) {
x := 0
for i, tab := range strip.Tabs {
- cell := tb.Cell{
- Fg: tb.ColorBlack,
- Bg: tb.ColorWhite,
- }
+ style := tcell.StyleDefault.Background(tcell.ColorWhite).Foreground(tcell.ColorBlack)
if strip.Selected == i {
- cell.Fg = tb.ColorDefault
- cell.Bg = tb.ColorDefault
+ style = style.Reverse(true)
}
- x += ctx.Printf(x, 0, cell, " %s ", tab.Name)
- }
- cell := tb.Cell{
- Fg: tb.ColorBlack,
- Bg: tb.ColorWhite,
+ x += ctx.Printf(x, 0, style, " %s ", tab.Name)
}
- ctx.Fill(x, 0, ctx.Width()-x, 1, cell)
+ style := tcell.StyleDefault.Background(tcell.ColorWhite).Foreground(tcell.ColorBlack)
+ ctx.Fill(x, 0, ctx.Width()-x, 1, ' ', style)
}
func (strip *TabStrip) Invalidate() {
diff --git a/lib/ui/text.go b/lib/ui/text.go
index 6164837..e2e218c 100644
--- a/lib/ui/text.go
+++ b/lib/ui/text.go
@@ -2,7 +2,7 @@ package ui
import (
"github.com/mattn/go-runewidth"
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
)
const (
@@ -14,8 +14,8 @@ const (
type Text struct {
text string
strategy uint
- fg tb.Attribute
- bg tb.Attribute
+ fg tcell.Color
+ bg tcell.Color
onInvalidate func(d Drawable)
}
@@ -35,7 +35,7 @@ func (t *Text) Strategy(strategy uint) *Text {
return t
}
-func (t *Text) Color(fg tb.Attribute, bg tb.Attribute) *Text {
+func (t *Text) Color(fg tcell.Color, bg tcell.Color) *Text {
t.fg = fg
t.bg = bg
t.Invalidate()
@@ -44,11 +44,6 @@ func (t *Text) Color(fg tb.Attribute, bg tb.Attribute) *Text {
func (t *Text) Draw(ctx *Context) {
size := runewidth.StringWidth(t.text)
- cell := tb.Cell{
- Ch: ' ',
- Fg: t.fg,
- Bg: t.bg,
- }
x := 0
if t.strategy == TEXT_CENTER {
x = (ctx.Width() - size) / 2
@@ -56,8 +51,9 @@ func (t *Text) Draw(ctx *Context) {
if t.strategy == TEXT_RIGHT {
x = ctx.Width() - size
}
- ctx.Fill(0, 0, ctx.Width(), ctx.Height(), cell)
- ctx.Printf(x, 0, cell, "%s", t.text)
+ style := tcell.StyleDefault.Background(t.bg).Foreground(t.fg)
+ ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', style)
+ ctx.Printf(x, 0, style, t.text)
}
func (t *Text) OnInvalidate(onInvalidate func(d Drawable)) {
diff --git a/lib/ui/ui.go b/lib/ui/ui.go
index e9b4e9b..d3eacf2 100644
--- a/lib/ui/ui.go
+++ b/lib/ui/ui.go
@@ -1,7 +1,7 @@
package ui
import (
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
"git.sr.ht/~sircmpwn/aerc2/config"
)
@@ -10,30 +10,41 @@ type UI struct {
Exit bool
Content DrawableInteractive
ctx *Context
+ screen tcell.Screen
- tbEvents chan tb.Event
+ tcEvents chan tcell.Event
invalidations chan interface{}
}
func Initialize(conf *config.AercConfig,
content DrawableInteractive) (*UI, error) {
- if err := tb.Init(); err != nil {
+ screen, err := tcell.NewScreen()
+ if err != nil {
return nil, err
}
- width, height := tb.Size()
+
+ if err = screen.Init(); err != nil {
+ return nil, err
+ }
+
+ screen.Clear()
+ screen.HideCursor()
+
+ width, height := screen.Size()
+
state := UI{
Content: content,
- ctx: NewContext(width, height),
+ ctx: NewContext(width, height, screen),
+ screen: screen,
- tbEvents: make(chan tb.Event, 10),
+ tcEvents: make(chan tcell.Event, 10),
invalidations: make(chan interface{}),
}
- tb.SetInputMode(tb.InputEsc | tb.InputMouse)
- tb.SetOutputMode(tb.Output256)
+ //tb.SetOutputMode(tb.Output256)
go (func() {
for !state.Exit {
- state.tbEvents <- tb.PollEvent()
+ state.tcEvents <- screen.PollEvent()
}
})()
go (func() { state.invalidations <- nil })()
@@ -44,27 +55,28 @@ func Initialize(conf *config.AercConfig,
}
func (state *UI) Close() {
- tb.Close()
+ state.screen.Fini()
}
func (state *UI) Tick() bool {
select {
- case event := <-state.tbEvents:
- switch event.Type {
- case tb.EventKey:
+ case event := <-state.tcEvents:
+ switch event := event.(type) {
+ case *tcell.EventKey:
// TODO: temporary
- if event.Key == tb.KeyEsc {
+ if event.Key() == tcell.KeyEsc {
state.Exit = true
}
- case tb.EventResize:
- tb.Clear(tb.ColorDefault, tb.ColorDefault)
- state.ctx = NewContext(event.Width, event.Height)
+ case *tcell.EventResize:
+ state.screen.Clear()
+ width, height := event.Size()
+ state.ctx = NewContext(width, height, state.screen)
state.Content.Invalidate()
}
state.Content.Event(event)
case <-state.invalidations:
state.Content.Draw(state.ctx)
- tb.Flush()
+ state.screen.Show()
default:
return false
}
diff --git a/widgets/aerc.go b/widgets/aerc.go
index 5563275..19ddfdd 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -5,7 +5,7 @@ import (
"log"
"time"
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
libui "git.sr.ht/~sircmpwn/aerc2/lib/ui"
)
@@ -35,7 +35,7 @@ func NewAerc(logger *log.Logger) *Aerc {
// TODO: move sidebar into tab content, probably
grid.AddChild(libui.NewText("aerc").
Strategy(libui.TEXT_CENTER).
- Color(tb.ColorBlack, tb.ColorWhite))
+ Color(tcell.ColorBlack, tcell.ColorWhite))
// sidebar placeholder:
grid.AddChild(libui.NewBordered(
libui.NewFill('.'), libui.BORDER_RIGHT)).At(1, 0).Span(2, 1)
@@ -75,10 +75,10 @@ func (aerc *Aerc) Draw(ctx *libui.Context) {
aerc.grid.Draw(ctx)
}
-func (aerc *Aerc) Event(event tb.Event) bool {
- switch event.Type {
- case tb.EventKey:
- if event.Ch == ':' {
+func (aerc *Aerc) Event(event tcell.Event) bool {
+ switch event := event.(type) {
+ case *tcell.EventKey:
+ if event.Rune() == ':' {
exline := NewExLine(func(command string) {
aerc.statusline.Push(fmt.Sprintf("TODO: execute %s", command),
3 * time.Second)
@@ -92,12 +92,10 @@ func (aerc *Aerc) Event(event tb.Event) bool {
aerc.statusbar.Push(exline)
return true
}
- fallthrough
- default:
- if aerc.interactive != nil {
- return aerc.interactive.Event(event)
- } else {
- return false
- }
+ }
+ if aerc.interactive != nil {
+ return aerc.interactive.Event(event)
+ } else {
+ return false
}
}
diff --git a/widgets/exline.go b/widgets/exline.go
index 0522371..de652ba 100644
--- a/widgets/exline.go
+++ b/widgets/exline.go
@@ -2,7 +2,7 @@ package widgets
import (
"github.com/mattn/go-runewidth"
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
"git.sr.ht/~sircmpwn/aerc2/lib/ui"
)
@@ -40,15 +40,10 @@ func (ex *ExLine) Invalidate() {
}
func (ex *ExLine) Draw(ctx *ui.Context) {
- cell := tb.Cell{
- Fg: tb.ColorDefault,
- Bg: tb.ColorDefault,
- Ch: ' ',
- }
- ctx.Fill(0, 0, ctx.Width(), ctx.Height(), cell)
- ctx.Printf(0, 0, cell, ":%s", string(ex.command))
+ ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', tcell.StyleDefault)
+ ctx.Printf(0, 0, tcell.StyleDefault, ":%s", string(ex.command))
cells := runewidth.StringWidth(string(ex.command[:ex.index]))
- tb.SetCursor(ctx.X()+cells+1, ctx.Y())
+ ctx.SetCursor(cells + 1, 0)
}
func (ex *ExLine) insert(ch rune) {
@@ -93,43 +88,41 @@ func (ex *ExLine) backspace() {
}
}
-func (ex *ExLine) Event(event tb.Event) bool {
- switch event.Type {
- case tb.EventKey:
- switch event.Key {
- case tb.KeySpace:
- ex.insert(' ')
- case tb.KeyBackspace, tb.KeyBackspace2:
+func (ex *ExLine) Event(event tcell.Event) bool {
+ switch event := event.(type) {
+ case *tcell.EventKey:
+ switch event.Key() {
+ case tcell.KeyBackspace, tcell.KeyBackspace2:
ex.backspace()
- case tb.KeyCtrlD, tb.KeyDelete:
+ case tcell.KeyCtrlD, tcell.KeyDelete:
ex.deleteChar()
- case tb.KeyCtrlB, tb.KeyArrowLeft:
+ case tcell.KeyCtrlB, tcell.KeyLeft:
if ex.index > 0 {
ex.index--
ex.Invalidate()
}
- case tb.KeyCtrlF, tb.KeyArrowRight:
+ case tcell.KeyCtrlF, tcell.KeyRight:
if ex.index < len(ex.command) {
ex.index++
ex.Invalidate()
}
- case tb.KeyCtrlA, tb.KeyHome:
+ case tcell.KeyCtrlA, tcell.KeyHome:
ex.index = 0
ex.Invalidate()
- case tb.KeyCtrlE, tb.KeyEnd:
+ case tcell.KeyCtrlE, tcell.KeyEnd:
ex.index = len(ex.command)
ex.Invalidate()
- case tb.KeyCtrlW:
+ case tcell.KeyCtrlW:
ex.deleteWord()
- case tb.KeyEnter:
- tb.HideCursor()
+ case tcell.KeyEnter:
+ //ex.ctx.Screen().HideCursor()
ex.commit(string(ex.command))
- case tb.KeyEsc, tb.KeyCtrlC:
- tb.HideCursor()
+ case tcell.KeyEsc, tcell.KeyCtrlC:
+ //ex.ctx.Screen().HideCursor()
ex.cancel()
default:
- if event.Ch != 0 {
- ex.insert(event.Ch)
+ if event.Rune() != 0 {
+ ex.insert(event.Rune())
}
}
}
diff --git a/widgets/status.go b/widgets/status.go
index bb87d33..3b4dbcc 100644
--- a/widgets/status.go
+++ b/widgets/status.go
@@ -3,7 +3,7 @@ package widgets
import (
"time"
- tb "github.com/nsf/termbox-go"
+ "github.com/gdamore/tcell"
"git.sr.ht/~sircmpwn/aerc2/lib/ui"
)
@@ -16,16 +16,16 @@ type StatusLine struct {
}
type StatusMessage struct {
- bg tb.Attribute
- fg tb.Attribute
+ bg tcell.Color
+ fg tcell.Color
message string
}
func NewStatusLine() *StatusLine {
return &StatusLine{
fallback: StatusMessage{
- bg: tb.ColorWhite,
- fg: tb.ColorBlack,
+ bg: tcell.ColorWhite,
+ fg: tcell.ColorBlack,
message: "Idle",
},
}
@@ -46,19 +46,15 @@ func (status *StatusLine) Draw(ctx *ui.Context) {
if len(status.stack) != 0 {
line = status.stack[len(status.stack)-1]
}
- cell := tb.Cell{
- Fg: line.fg,
- Bg: line.bg,
- Ch: ' ',
- }
- ctx.Fill(0, 0, ctx.Width(), ctx.Height(), cell)
- ctx.Printf(0, 0, cell, "%s", line.message)
+ style := tcell.StyleDefault.Background(line.bg).Foreground(line.fg)
+ ctx.Fill(0, 0, ctx.Width(), ctx.Height(), ' ', style)
+ ctx.Printf(0, 0, style, "%s", line.message)
}
func (status *StatusLine) Set(text string) *StatusMessage {
status.fallback = StatusMessage{
- bg: tb.ColorWhite,
- fg: tb.ColorBlack,
+ bg: tcell.ColorWhite,
+ fg: tcell.ColorBlack,
message: text,
}
status.Invalidate()
@@ -67,8 +63,8 @@ func (status *StatusLine) Set(text string) *StatusMessage {
func (status *StatusLine) Push(text string, expiry time.Duration) *StatusMessage {
msg := &StatusMessage{
- bg: tb.ColorWhite,
- fg: tb.ColorBlack,
+ bg: tcell.ColorWhite,
+ fg: tcell.ColorBlack,
message: text,
}
status.stack = append(status.stack, msg)
@@ -85,7 +81,7 @@ func (status *StatusLine) Push(text string, expiry time.Duration) *StatusMessage
return msg
}
-func (msg *StatusMessage) Color(bg tb.Attribute, fg tb.Attribute) {
+func (msg *StatusMessage) Color(bg tcell.Color, fg tcell.Color) {
msg.bg = bg
msg.fg = fg
}