Parent directory

application.go

3585 bytes
  1package tui
  2
  3import (
  4	"fmt"
  5	"math"
  6	"strings"
  7
  8	"github.com/gdamore/tcell/v2"
  9	"github.com/rivo/tview"
 10
 11	"git.theedgeofrage.com/TheEdgeOfRage/rpn/rpn"
 12)
 13
 14const calculatorPage = "calculator"
 15
 16const helpPage = "help"
 17
 18type Calculator interface {
 19	Eval(input string) (rpn.Event, error)
 20	Values() []float64
 21}
 22
 23type Application struct {
 24	application *tview.Application
 25	calculator  Calculator
 26	pages       *tview.Pages
 27	layout      *tview.Flex
 28	stack       *tview.TextView
 29	status      *tview.TextView
 30	input       *tview.InputField
 31}
 32
 33func New(calculator Calculator) *Application {
 34	stack := tview.NewTextView().SetWrap(false)
 35	stack.SetBorder(true).SetTitle("Stack")
 36
 37	app := &Application{
 38		application: tview.NewApplication(),
 39		calculator:  calculator,
 40		stack:       stack,
 41		status: tview.NewTextView().
 42			SetWrap(false),
 43		input: tview.NewInputField().
 44			SetLabel("> ").
 45			SetFieldBackgroundColor(tcell.ColorBlack).
 46			SetFieldTextColor(tcell.ColorWhite),
 47	}
 48
 49	app.input.SetDoneFunc(app.submit)
 50
 51	app.layout = tview.NewFlex().
 52		SetDirection(tview.FlexRow).
 53		AddItem(nil, 0, 1, false).
 54		AddItem(app.stack, 0, 1, false).
 55		AddItem(app.status, 0, 0, false).
 56		AddItem(app.input, 1, 0, true)
 57
 58	app.pages = tview.NewPages().AddPage(calculatorPage, app.layout, true, true)
 59	app.application.
 60		SetRoot(app.pages, true).
 61		SetFocus(app.input).
 62		SetInputCapture(app.captureInput).
 63		SetBeforeDrawFunc(app.resizeStack)
 64	app.renderStack()
 65
 66	return app
 67}
 68
 69func (a *Application) Run() error {
 70	return a.application.Run()
 71}
 72
 73func (a *Application) captureInput(event *tcell.EventKey) *tcell.EventKey {
 74	if event.Key() == tcell.KeyCtrlD {
 75		a.application.Stop()
 76		return nil
 77	}
 78	return event
 79}
 80
 81func (a *Application) submit(key tcell.Key) {
 82	if key != tcell.KeyEnter {
 83		return
 84	}
 85
 86	input := strings.TrimSpace(a.input.GetText())
 87	a.input.SetText("")
 88	if input == "" {
 89		return
 90	}
 91
 92	event, err := a.calculator.Eval(input)
 93	a.renderStack()
 94	if err != nil {
 95		a.status.SetText(err.Error())
 96		return
 97	}
 98
 99	a.status.SetText("")
100	switch event {
101	case rpn.EventHelp:
102		a.showHelp()
103	case rpn.EventExit:
104		a.application.Stop()
105	}
106}
107
108func (a *Application) renderStack() {
109	values := a.calculator.Values()
110	lines := make([]string, len(values))
111	for i, value := range values {
112		lines[i] = formatValue(value)
113	}
114	a.stack.SetText(strings.Join(lines, "\n")).ScrollToEnd()
115}
116
117func (a *Application) resizeStack(screen tcell.Screen) bool {
118	_, height := screen.Size()
119	statusHeight := 0
120	if a.status.GetText(true) != "" {
121		statusHeight = 1
122	}
123	a.layout.ResizeItem(a.status, statusHeight, 0)
124
125	stackHeight := len(a.calculator.Values()) + 2
126	maxStackHeight := height - statusHeight - 1
127	if stackHeight > maxStackHeight {
128		stackHeight = maxStackHeight
129	}
130	if stackHeight < 0 {
131		stackHeight = 0
132	}
133	a.layout.ResizeItem(a.stack, stackHeight, 0)
134	return false
135}
136
137func (a *Application) showHelp() {
138	help := tview.NewModal().
139		SetText(rpn.HelpText).
140		SetBackgroundColor(tcell.ColorBlack).
141		SetTextColor(tcell.ColorWhite).
142		SetButtonBackgroundColor(tcell.ColorBlack).
143		SetButtonTextColor(tcell.ColorWhite).
144		SetButtonActivatedStyle(tcell.StyleDefault.Background(tcell.ColorBlack).Foreground(tcell.ColorWhite)).
145		AddButtons([]string{"Close"}).
146		SetDoneFunc(func(buttonIndex int, buttonLabel string) {
147			a.pages.RemovePage(helpPage)
148			a.application.SetFocus(a.input)
149		})
150	a.pages.AddPage(helpPage, help, true, true)
151	a.application.SetFocus(help)
152}
153
154func formatValue(value float64) string {
155	if value == math.Trunc(value) {
156		return fmt.Sprintf("%d", int64(value))
157	}
158	return fmt.Sprintf("%f", value)
159}