Pull main aerc UI into widget
This commit is contained in:
parent
a073d7613f
commit
cab3771e17
5 changed files with 130 additions and 83 deletions
|
@ -8,31 +8,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
tb "github.com/nsf/termbox-go"
|
|
||||||
|
|
||||||
"git.sr.ht/~sircmpwn/aerc2/config"
|
"git.sr.ht/~sircmpwn/aerc2/config"
|
||||||
libui "git.sr.ht/~sircmpwn/aerc2/lib/ui"
|
libui "git.sr.ht/~sircmpwn/aerc2/lib/ui"
|
||||||
"git.sr.ht/~sircmpwn/aerc2/widgets"
|
"git.sr.ht/~sircmpwn/aerc2/widgets"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fill rune
|
|
||||||
|
|
||||||
func (f fill) Draw(ctx *libui.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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fill) OnInvalidate(callback func(d libui.Drawable)) {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fill) Invalidate() {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var logOut io.Writer
|
var logOut io.Writer
|
||||||
var logger *log.Logger
|
var logger *log.Logger
|
||||||
|
@ -49,62 +30,12 @@ func main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tabs := libui.NewTabs()
|
ui, err := libui.Initialize(conf, widgets.NewAerc(logger))
|
||||||
tabs.Add(fill('★'), "白い星")
|
|
||||||
tabs.Add(fill('☆'), "empty stars")
|
|
||||||
|
|
||||||
grid := libui.NewGrid().Rows([]libui.GridSpec{
|
|
||||||
libui.GridSpec{libui.SIZE_EXACT, 1},
|
|
||||||
libui.GridSpec{libui.SIZE_WEIGHT, 1},
|
|
||||||
libui.GridSpec{libui.SIZE_EXACT, 1},
|
|
||||||
}).Columns([]libui.GridSpec{
|
|
||||||
libui.GridSpec{libui.SIZE_EXACT, 20},
|
|
||||||
libui.GridSpec{libui.SIZE_WEIGHT, 1},
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: move sidebar into tab content, probably
|
|
||||||
grid.AddChild(libui.NewText("aerc").
|
|
||||||
Strategy(libui.TEXT_CENTER).
|
|
||||||
Color(tb.ColorBlack, tb.ColorWhite))
|
|
||||||
// sidebar placeholder:
|
|
||||||
grid.AddChild(libui.NewBordered(
|
|
||||||
fill('.'), libui.BORDER_RIGHT)).At(1, 0).Span(2, 1)
|
|
||||||
grid.AddChild(tabs.TabStrip).At(0, 1)
|
|
||||||
grid.AddChild(tabs.TabContent).At(1, 1)
|
|
||||||
|
|
||||||
statusbar := libui.NewStack()
|
|
||||||
grid.AddChild(statusbar).At(2, 1)
|
|
||||||
|
|
||||||
statusline := widgets.NewStatusLine()
|
|
||||||
statusline.Push("test status!", 6*time.Second)
|
|
||||||
statusline.Push("test error!", 3*time.Second).
|
|
||||||
Color(tb.ColorRed, tb.ColorBlack)
|
|
||||||
statusbar.Push(statusline)
|
|
||||||
|
|
||||||
exline := widgets.NewExLine(func(command string) {
|
|
||||||
statusbar.Pop()
|
|
||||||
logger.Printf("TODO: execute command: %s\n", command)
|
|
||||||
}, func() {
|
|
||||||
statusbar.Pop()
|
|
||||||
})
|
|
||||||
statusbar.Push(exline)
|
|
||||||
|
|
||||||
ui, err := libui.Initialize(conf, grid)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer ui.Close()
|
defer ui.Close()
|
||||||
|
|
||||||
// TODO: this should be a stack
|
|
||||||
ui.AddInteractive(exline)
|
|
||||||
|
|
||||||
go (func() {
|
|
||||||
for {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
tabs.Select((tabs.Selected + 1) % 2)
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
|
|
||||||
for !ui.Exit {
|
for !ui.Exit {
|
||||||
if !ui.Tick() {
|
if !ui.Tick() {
|
||||||
// ~60 FPS
|
// ~60 FPS
|
||||||
|
|
27
lib/ui/fill.go
Normal file
27
lib/ui/fill.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
tb "github.com/nsf/termbox-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Fill rune
|
||||||
|
|
||||||
|
func NewFill(f rune) Fill {
|
||||||
|
return Fill(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Fill) OnInvalidate(callback func(d Drawable)) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f Fill) Invalidate() {
|
||||||
|
// no-op
|
||||||
|
}
|
|
@ -13,3 +13,8 @@ type Simulator interface {
|
||||||
// Queues up the given input events for simulation
|
// Queues up the given input events for simulation
|
||||||
Simulate(events []tb.Event)
|
Simulate(events []tb.Event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DrawableInteractive interface {
|
||||||
|
Drawable
|
||||||
|
Interactive
|
||||||
|
}
|
||||||
|
|
19
lib/ui/ui.go
19
lib/ui/ui.go
|
@ -8,16 +8,16 @@ import (
|
||||||
|
|
||||||
type UI struct {
|
type UI struct {
|
||||||
Exit bool
|
Exit bool
|
||||||
Content Drawable
|
Content DrawableInteractive
|
||||||
ctx *Context
|
ctx *Context
|
||||||
|
|
||||||
interactive []Interactive
|
|
||||||
|
|
||||||
tbEvents chan tb.Event
|
tbEvents chan tb.Event
|
||||||
invalidations chan interface{}
|
invalidations chan interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Initialize(conf *config.AercConfig, content Drawable) (*UI, error) {
|
func Initialize(conf *config.AercConfig,
|
||||||
|
content DrawableInteractive) (*UI, error) {
|
||||||
|
|
||||||
if err := tb.Init(); err != nil {
|
if err := tb.Init(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ func (state *UI) Tick() bool {
|
||||||
case event := <-state.tbEvents:
|
case event := <-state.tbEvents:
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
case tb.EventKey:
|
case tb.EventKey:
|
||||||
|
// TODO: temporary
|
||||||
if event.Key == tb.KeyEsc {
|
if event.Key == tb.KeyEsc {
|
||||||
state.Exit = true
|
state.Exit = true
|
||||||
}
|
}
|
||||||
|
@ -60,11 +61,7 @@ func (state *UI) Tick() bool {
|
||||||
state.ctx = NewContext(event.Width, event.Height)
|
state.ctx = NewContext(event.Width, event.Height)
|
||||||
state.Content.Invalidate()
|
state.Content.Invalidate()
|
||||||
}
|
}
|
||||||
if state.interactive != nil {
|
state.Content.Event(event)
|
||||||
for _, i := range state.interactive {
|
|
||||||
i.Event(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case <-state.invalidations:
|
case <-state.invalidations:
|
||||||
state.Content.Draw(state.ctx)
|
state.Content.Draw(state.ctx)
|
||||||
tb.Flush()
|
tb.Flush()
|
||||||
|
@ -73,7 +70,3 @@ func (state *UI) Tick() bool {
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (state *UI) AddInteractive(i Interactive) {
|
|
||||||
state.interactive = append(state.interactive, i)
|
|
||||||
}
|
|
||||||
|
|
91
widgets/aerc.go
Normal file
91
widgets/aerc.go
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package widgets
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
tb "github.com/nsf/termbox-go"
|
||||||
|
|
||||||
|
libui "git.sr.ht/~sircmpwn/aerc2/lib/ui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Aerc struct {
|
||||||
|
grid *libui.Grid
|
||||||
|
tabs *libui.Tabs
|
||||||
|
statusbar *libui.Stack
|
||||||
|
statusline *StatusLine
|
||||||
|
interactive libui.Interactive
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAerc(logger *log.Logger) *Aerc {
|
||||||
|
tabs := libui.NewTabs()
|
||||||
|
tabs.Add(libui.NewFill('★'), "白い星")
|
||||||
|
tabs.Add(libui.NewFill('☆'), "empty stars")
|
||||||
|
|
||||||
|
grid := libui.NewGrid().Rows([]libui.GridSpec{
|
||||||
|
libui.GridSpec{libui.SIZE_EXACT, 1},
|
||||||
|
libui.GridSpec{libui.SIZE_WEIGHT, 1},
|
||||||
|
libui.GridSpec{libui.SIZE_EXACT, 1},
|
||||||
|
}).Columns([]libui.GridSpec{
|
||||||
|
libui.GridSpec{libui.SIZE_EXACT, 20},
|
||||||
|
libui.GridSpec{libui.SIZE_WEIGHT, 1},
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: move sidebar into tab content, probably
|
||||||
|
grid.AddChild(libui.NewText("aerc").
|
||||||
|
Strategy(libui.TEXT_CENTER).
|
||||||
|
Color(tb.ColorBlack, tb.ColorWhite))
|
||||||
|
// sidebar placeholder:
|
||||||
|
grid.AddChild(libui.NewBordered(
|
||||||
|
libui.NewFill('.'), libui.BORDER_RIGHT)).At(1, 0).Span(2, 1)
|
||||||
|
grid.AddChild(tabs.TabStrip).At(0, 1)
|
||||||
|
grid.AddChild(tabs.TabContent).At(1, 1)
|
||||||
|
|
||||||
|
statusbar := libui.NewStack()
|
||||||
|
grid.AddChild(statusbar).At(2, 1)
|
||||||
|
|
||||||
|
statusline := NewStatusLine()
|
||||||
|
statusline.Push("test status!", 6 * time.Second)
|
||||||
|
statusline.Push("test error!", 3 * time.Second).
|
||||||
|
Color(tb.ColorRed, tb.ColorBlack)
|
||||||
|
statusbar.Push(statusline)
|
||||||
|
|
||||||
|
exline := NewExLine(func(command string) {
|
||||||
|
statusbar.Pop()
|
||||||
|
logger.Printf("TODO: execute command: %s\n", command)
|
||||||
|
}, func() {
|
||||||
|
statusbar.Pop()
|
||||||
|
})
|
||||||
|
statusbar.Push(exline)
|
||||||
|
|
||||||
|
go (func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
tabs.Select((tabs.Selected + 1) % 2)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
return &Aerc{
|
||||||
|
grid: grid,
|
||||||
|
interactive: exline,
|
||||||
|
statusbar: statusbar,
|
||||||
|
statusline: statusline,
|
||||||
|
tabs: tabs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aerc *Aerc) OnInvalidate(onInvalidate func(d libui.Drawable)) {
|
||||||
|
aerc.grid.OnInvalidate(onInvalidate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aerc *Aerc) Invalidate() {
|
||||||
|
aerc.grid.Invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aerc *Aerc) Draw(ctx *libui.Context) {
|
||||||
|
aerc.grid.Draw(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (aerc *Aerc) Event(event tb.Event) bool {
|
||||||
|
return aerc.interactive.Event(event)
|
||||||
|
}
|
Loading…
Reference in a new issue