package ui
import (
"os"
"sync/atomic"
"github.com/gdamore/tcell/v2"
)
var debug = os.Getenv("DEBUG") == "1"
// UI represents the user interface.
type UI struct {
screen tcell.Screen
invalid int32
exit int32
}
// New returns a new UI that runs the provided application.
func New() (*UI, error) {
var screen tcell.Screen
if debug {
screen = tcell.NewSimulationScreen("utf-8")
} else {
var err error
screen, err = tcell.NewScreen()
if err != nil {
return nil, err
}
}
if err := screen.Init(); err != nil {
return nil, err
}
screen.Clear()
screen.HideCursor()
screen.EnableMouse()
return &UI{
screen: screen,
invalid: 1,
}, nil
}
// Application represents an application.
type Application interface {
Draw()
Event(tcell.Event)
Layout(x, y, w, h int)
}
// Init initializes the application.
func (u *UI) Init(app Application) {
w, h := u.screen.Size()
app.Layout(0, 0, w, h)
}
// Tick advances the UI by one tick.
func (u *UI) Tick(app Application) bool {
var more bool
if u.screen.HasPendingEvent() {
event := u.screen.PollEvent()
if event, ok := event.(*tcell.EventResize); ok {
w, h := event.Size()
app.Layout(0, 0, w, h)
}
app.Event(event)
more = true
}
if atomic.SwapInt32(&u.invalid, 0) != 0 {
u.screen.Clear()
app.Draw()
u.screen.Show()
more = true
}
return more
}
// Close closes the UI.
func (u *UI) Close() {
u.screen.Fini()
}
// Exiting reports whether the UI is exiting.
func (u *UI) Exiting() bool {
return atomic.LoadInt32(&u.exit) == 1
}
// View returns a new view.
func (u *UI) View() *View {
return &View{
v: u.screen,
invalid: &u.invalid,
exit: &u.exit,
}
}