tui/screen.go

141 lines
2.7 KiB
Go
Raw Normal View History

2021-01-10 21:52:29 +01:00
package tui
import (
"fmt"
2022-04-04 15:05:35 +02:00
"git.tordarus.net/Tordarus/adverr"
2022-04-01 20:10:51 +02:00
"git.tordarus.net/Tordarus/buf2d"
2021-01-10 21:52:29 +01:00
"github.com/gdamore/tcell"
)
type Screen struct {
scr tcell.Screen
buf *ViewBuffer
stopCh chan error
redrawCh chan struct{}
// Root is the root view which is currently shown on screen
Root View
2022-04-02 13:01:41 +02:00
// KeyPressed is called every time a key or key-combination is pressed.
KeyPressed func(event *KeyEvent) (consumed bool)
// MouseClicked is called every time a mouse button was pressed.
MouseClicked func(event *MouseEvent) (consumed bool)
2021-01-10 21:52:29 +01:00
}
func NewScreen(root View) (*Screen, error) {
scr, err := tcell.NewScreen()
if err != nil {
return nil, err
}
s := &Screen{
Root: root,
scr: scr,
stopCh: make(chan error, 1),
redrawCh: make(chan struct{}, 1),
2021-01-10 21:52:29 +01:00
}
2022-05-03 17:59:34 +02:00
s.KeyPressed = CloseOnCtrlC(s)
2021-01-10 21:52:29 +01:00
go s.eventloop()
go s.drawloop()
2021-01-10 21:52:29 +01:00
return s, nil
}
func (s *Screen) Start() error {
err := s.scr.Init()
if err != nil {
return err
}
2022-04-01 20:10:51 +02:00
defer s.scr.Fini()
s.scr.EnableMouse()
2021-01-10 21:52:29 +01:00
return <-s.stopCh
}
func (s *Screen) Stop() {
s.StopWithError(nil)
}
func (s *Screen) StopWithError(err error) {
s.stopCh <- err
}
2022-04-02 13:01:41 +02:00
func (s *Screen) onKeyPressed(event *KeyEvent) {
if s.KeyPressed == nil || !s.KeyPressed(event) {
2022-04-04 15:05:35 +02:00
go s.Root.OnKeyPressed(event)
2022-04-02 13:01:41 +02:00
}
s.Redraw()
}
func (s *Screen) onMouseClicked(event *MouseEvent) {
if s.MouseClicked == nil || !s.MouseClicked(event) {
2022-04-04 15:05:35 +02:00
go s.Root.OnMouseClicked(event)
}
2022-04-04 14:23:17 +02:00
if event.Button != MouseButtonNone {
s.Redraw()
}
}
func convertMouseEvent(original *tcell.EventMouse) *MouseEvent {
x, y := original.Position()
return &MouseEvent{
X: x, Y: y,
Button: convertMouseButton(original.Buttons()),
Modifiers: original.Modifiers(),
}
}
2021-01-10 21:52:29 +01:00
func (s *Screen) Redraw() {
s.redrawCh <- struct{}{}
}
func (s *Screen) eventloop() {
2022-04-04 15:05:35 +02:00
defer s.handlePanic("panicked while handling event")
2022-04-02 13:01:41 +02:00
for evt := s.scr.PollEvent(); evt != nil; evt = s.scr.PollEvent() {
switch event := evt.(type) {
case *tcell.EventResize:
2022-04-04 15:05:35 +02:00
s.Redraw()
case *tcell.EventKey:
2022-04-04 15:05:35 +02:00
s.onKeyPressed(event)
case *tcell.EventMouse:
2022-04-04 15:05:35 +02:00
s.onMouseClicked(convertMouseEvent(event))
default:
2022-05-04 10:11:40 +02:00
s.StopWithError(fmt.Errorf("%#v", event))
}
2022-04-02 13:01:41 +02:00
}
}
func (s *Screen) drawloop() {
2022-04-04 15:05:35 +02:00
defer s.handlePanic("panicked while redrawing")
for range s.redrawCh {
w, h := s.scr.Size()
if s.buf == nil || s.buf.Width() != w || s.buf.Height() != h {
s.buf = buf2d.NewBuffer(w, h, DefaultRune)
2022-05-04 11:53:08 +02:00
} else {
s.buf.Fill(DefaultRune)
}
2022-04-02 13:01:41 +02:00
rw, rh := s.Root.Layout()
s.Root.Draw(truncateBuffer(s.buf, rw, rh))
drawBuffer(s.scr, s.buf)
}
}
2022-04-04 15:05:35 +02:00
func (s *Screen) handlePanic(msg string) {
if err := recover(); err != nil {
2022-04-04 15:05:35 +02:00
if e, ok := err.(error); ok {
s.StopWithError(adverr.Wrap(msg, e))
} else {
s.StopWithError(adverr.Wrap(msg, fmt.Errorf("%v", err)))
}
}
2021-01-10 21:52:29 +01:00
}