Add basic terminal user interface

This commit is contained in:
Olli 2025-10-16 07:59:16 +02:00
commit 5956331a28
9 changed files with 878 additions and 0 deletions

114
tui/tui.go Normal file
View file

@ -0,0 +1,114 @@
// Package tui implements a terminal user interface.
package tui
import (
"fmt"
"github.com/rivo/tview"
"suruatoel.xyz/timefiles/application"
"suruatoel.xyz/timefiles/lib"
)
// TUI represents the terminal user interface.
//
// timeApp is the application to manage the time files.
//
// uiApp is the tview application.
//
// uiScreen is the main screen of the terminal application.
type TUI struct {
timeApp *application.Application
uiApp *tview.Application
uiScreen *MainScreen
}
// NewTUI creates a new terminal user interface.
func NewTUI() (*TUI, error) {
tui := TUI{}
tui.timeApp = application.NewApplication(&tui)
tui.uiApp = tview.NewApplication()
ui, uiErr := NewMainScreen(&tui)
if uiErr != nil {
return nil, uiErr
}
tui.uiScreen = ui
return &tui, nil
}
// Run loads the initial application data and runs the terminal user interface. It will wait for the UI to finish and
// for all asynchronous jobs to be completed before returning.
func (ui *TUI) Run() error {
ui.timeApp.Load()
ui.timeApp.GetTimer()
ui.timeApp.GetTimeSummary()
ui.timeApp.GetTimeTotals()
err := ui.uiApp.Run()
ui.timeApp.Wait()
return err
}
// Focus delegates the focus request of the UI to TUI.uiApp.
func (ui *TUI) Focus(p tview.Primitive) {
ui.uiApp.SetFocus(p)
}
// SetTimerStart sets the status for updating the timer.
func (ui *TUI) SetTimerStart() {
ui.uiApp.QueueUpdateDraw(func() {
ui.uiScreen.SetStatus("Updating timer")
})
}
// SetTimer updates the UI with the timer.
func (ui *TUI) SetTimer(timer *lib.Time, err error) {
ui.uiApp.QueueUpdateDraw(func() {
if err == nil {
ui.uiScreen.SetStatus("Timer updated")
} else {
ui.uiScreen.SetStatus(fmt.Sprintf("Failed to update timer: %s", err))
}
ui.uiScreen.SetTimer(timer)
})
}
// SetSummaryStart sets the status for gathering the time summary.
func (ui *TUI) SetSummaryStart() {
ui.uiApp.QueueUpdateDraw(func() {
ui.uiScreen.SetStatus("Getting summary")
})
}
// SetSummary updates the UI with the time summary.
func (ui *TUI) SetSummary(summary []*lib.Time, err error) {
ui.uiApp.QueueUpdateDraw(func() {
if err == nil {
ui.uiScreen.SetStatus("Summary calculated")
} else {
ui.uiScreen.SetStatus(fmt.Sprintf("Failed get summary: %s", err))
}
ui.uiScreen.SetSummary(summary)
})
}
// SetTotalsStart sets the status for calculating the time totals.
func (ui *TUI) SetTotalsStart() {
ui.uiApp.QueueUpdateDraw(func() {
ui.uiScreen.SetStatus("Calculated totals")
})
}
// SetTotals updates the UI with the time totals.
func (ui *TUI) SetTotals(totalTime lib.TotalTime, err error) {
ui.uiApp.QueueUpdateDraw(func() {
if err == nil {
ui.uiScreen.SetStatus("Totals calculated")
} else {
ui.uiScreen.SetStatus(fmt.Sprintf("Failed get totals: %s", err))
}
ui.uiScreen.SetTotals(totalTime)
})
}