Make termbox event loop async

This commit is contained in:
Drew DeVault 2018-01-10 22:41:15 -05:00
parent db1b2cd53f
commit 77a0f68758
5 changed files with 126 additions and 18 deletions

View File

@ -14,6 +14,11 @@ func main() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
_ui, err := ui.Initialize(conf)
if err != nil {
panic(err)
}
defer _ui.Close()
var workers []worker.Worker var workers []worker.Worker
for _, account := range conf.Accounts { for _, account := range conf.Accounts {
work, err := worker.NewWorker(account.Source) work, err := worker.NewWorker(account.Source)
@ -23,12 +28,8 @@ func main() {
go work.Run() go work.Run()
work.PostAction(types.Configure{Config: account}) work.PostAction(types.Configure{Config: account})
workers = append(workers, work) workers = append(workers, work)
_ui.AddTab(ui.NewAccountTab(&account, &work))
} }
_ui, err := ui.Initialize(conf)
if err != nil {
panic(err)
}
defer _ui.Close()
for !_ui.Exit { for !_ui.Exit {
activity := false activity := false
for _, worker := range workers { for _, worker := range workers {

41
ui/account.go Normal file
View File

@ -0,0 +1,41 @@
package ui
import (
tb "github.com/nsf/termbox-go"
"git.sr.ht/~sircmpwn/aerc2/config"
"git.sr.ht/~sircmpwn/aerc2/worker"
)
type AccountTab struct {
Config *config.AccountConfig
Worker *worker.Worker
Parent *UIState
}
func NewAccountTab(conf *config.AccountConfig, work *worker.Worker) *AccountTab {
return &AccountTab{
Config: conf,
Worker: work,
}
}
func (acc *AccountTab) Name() string {
return acc.Config.Name
}
func (acc *AccountTab) Invalid() bool {
return false
}
func (acc *AccountTab) SetParent(parent *UIState) {
acc.Parent = parent
}
func (acc *AccountTab) Render(at Geometry) {
cell := tb.Cell{
Fg: tb.ColorDefault,
Bg: tb.ColorDefault,
}
TPrintf(&at, cell, "%s", acc.Name())
}

21
ui/helpers.go Normal file
View File

@ -0,0 +1,21 @@
package ui
import (
"fmt"
tb "github.com/nsf/termbox-go"
)
func TPrintf(geo *Geometry, ref tb.Cell, format string, a ...interface{}) {
str := fmt.Sprintf(format, a...)
_geo := *geo
for _, ch := range str {
tb.SetCell(geo.Col, geo.Row, ch, ref.Fg, ref.Bg)
geo.Col++
if geo.Col == _geo.Col+geo.Width {
// TODO: Abort when out of room?
geo.Col = _geo.Col
geo.Row++
}
}
}

View File

@ -8,15 +8,21 @@ import (
func Initialize(conf *config.AercConfig) (*UIState, error) { func Initialize(conf *config.AercConfig) (*UIState, error) {
state := UIState{ state := UIState{
Config: conf,
InvalidPanes: InvalidateAll, InvalidPanes: InvalidateAll,
Tabs: make([]AercTab, len(conf.Accounts)),
tbEvents: make(chan tb.Event, 10),
} }
// TODO: Initialize each tab to a mailbox tab
if err := tb.Init(); err != nil { if err := tb.Init(); err != nil {
return nil, err return nil, err
} }
tb.SetInputMode(tb.InputEsc | tb.InputMouse) tb.SetInputMode(tb.InputEsc | tb.InputMouse)
tb.SetOutputMode(tb.Output256) tb.SetOutputMode(tb.Output256)
go (func() {
for !state.Exit {
state.tbEvents <- tb.PollEvent()
}
})()
return &state, nil return &state, nil
} }
@ -24,21 +30,50 @@ func (state *UIState) Close() {
tb.Close() tb.Close()
} }
func (state *UIState) AddTab(tab AercTab) {
state.Tabs = append(state.Tabs, tab)
}
func (state *UIState) Invalidate(what uint) { func (state *UIState) Invalidate(what uint) {
state.InvalidPanes |= what state.InvalidPanes |= what
} }
func (state *UIState) calcGeometries() {
width, height := tb.Size()
// TODO: more
state.Panes.TabView = Geometry{
Row: 0,
Col: 0,
Width: width,
Height: height,
}
}
func (state *UIState) Tick() bool { func (state *UIState) Tick() bool {
switch e := tb.PollEvent(); e.Type { select {
case event := <-state.tbEvents:
switch event.Type {
case tb.EventKey: case tb.EventKey:
if e.Key == tb.KeyEsc { if event.Key == tb.KeyEsc {
state.Exit = true state.Exit = true
} }
case tb.EventResize: case tb.EventResize:
state.Invalidate(InvalidateAll) state.Invalidate(InvalidateAll)
} }
default:
// no-op
break
}
if state.InvalidPanes != 0 { if state.InvalidPanes != 0 {
// TODO: re-render if state.InvalidPanes&InvalidateAll == InvalidateAll {
tb.Clear(tb.ColorDefault, tb.ColorDefault)
state.calcGeometries()
}
if state.InvalidPanes&InvalidateTabs != 0 {
tab := state.Tabs[state.SelectedTab]
tab.Render(state.Panes.TabView)
}
tb.Flush()
state.InvalidPanes = 0 state.InvalidPanes = 0
} }
return true return true

View File

@ -1,5 +1,11 @@
package ui package ui
import (
tb "github.com/nsf/termbox-go"
"git.sr.ht/~sircmpwn/aerc2/config"
)
const ( const (
Valid = 0 Valid = 0
InvalidateTabs = 1 << iota InvalidateTabs = 1 << iota
@ -12,19 +18,21 @@ const (
) )
type Geometry struct { type Geometry struct {
row int Row int
col int Col int
width int Width int
height int Height int
} }
type AercTab interface { type AercTab interface {
Name() string Name() string
Invalid() bool Invalid() bool
Render(at Geometry) Render(at Geometry)
SetParent(parent *UIState)
} }
type UIState struct { type UIState struct {
Config *config.AercConfig
Exit bool Exit bool
InvalidPanes uint InvalidPanes uint
@ -44,4 +52,6 @@ type UIState struct {
Index int Index int
Scroll int Scroll int
} }
tbEvents chan tb.Event
} }