1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00

Extract most app setup out of main and into wtf_app

This commit is contained in:
Chris Cummer 2019-07-26 23:58:26 -07:00
parent 324666a3ec
commit a6d18e286a
3 changed files with 183 additions and 126 deletions

View File

@ -1,30 +1,33 @@
package wtf package app
import ( import (
"github.com/olebedev/config" "github.com/olebedev/config"
"github.com/rivo/tview" "github.com/rivo/tview"
"github.com/wtfutil/wtf/wtf"
) )
// Display is the container for the onscreen representation of a WtfApp
type Display struct { type Display struct {
Grid *tview.Grid Grid *tview.Grid
config *config.Config config *config.Config
} }
func NewDisplay(widgets []Wtfable, config *config.Config) *Display { // NewDisplay creates and returns a Display
func NewDisplay(widgets []wtf.Wtfable, config *config.Config) *Display {
display := Display{ display := Display{
Grid: tview.NewGrid(), Grid: tview.NewGrid(),
config: config, config: config,
} }
display.build(widgets) display.build(widgets)
display.Grid.SetBackgroundColor(ColorFor(config.UString("wtf.colors.background", "black"))) display.Grid.SetBackgroundColor(wtf.ColorFor(config.UString("wtf.colors.background", "black")))
return &display return &display
} }
/* -------------------- Unexported Functions -------------------- */ /* -------------------- Unexported Functions -------------------- */
func (display *Display) add(widget Wtfable) { func (display *Display) add(widget wtf.Wtfable) {
if widget.Disabled() { if widget.Disabled() {
return return
} }
@ -41,14 +44,16 @@ func (display *Display) add(widget Wtfable) {
) )
} }
func (display *Display) build(widgets []Wtfable) *tview.Grid { func (display *Display) build(widgets []wtf.Wtfable) *tview.Grid {
display.Grid.SetColumns(ToInts(display.config.UList("wtf.grid.columns"))...) cols := wtf.ToInts(display.config.UList("wtf.grid.columns"))
display.Grid.SetRows(ToInts(display.config.UList("wtf.grid.rows"))...) rows := wtf.ToInts(display.config.UList("wtf.grid.rows"))
display.Grid.SetColumns(cols...)
display.Grid.SetRows(rows...)
display.Grid.SetBorder(false) display.Grid.SetBorder(false)
for _, widget := range widgets { for _, widget := range widgets {
display.add(widget) display.add(widget)
go Schedule(widget)
} }
return display.Grid return display.Grid

156
app/wtf_app.go Normal file
View File

@ -0,0 +1,156 @@
package app
import (
"log"
"time"
"github.com/gdamore/tcell"
"github.com/olebedev/config"
"github.com/radovskyb/watcher"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/cfg"
"github.com/wtfutil/wtf/maker"
"github.com/wtfutil/wtf/utils"
"github.com/wtfutil/wtf/wtf"
)
// WtfApp is the container for a collection of widgets that are all constructed from a single
// configuration file and displayed together
type WtfApp struct {
App *tview.Application
Config *config.Config
ConfigFilePath string
Display *Display
FocusTracker wtf.FocusTracker
IsCustomConfig bool
Pages *tview.Pages
Widgets []wtf.Wtfable
}
// NewWtfApp creates and returns an instance of WtfApp
func NewWtfApp(app *tview.Application, config *config.Config, configFilePath string, isCustom bool) *WtfApp {
wtfApp := WtfApp{
App: app,
Config: config,
ConfigFilePath: configFilePath,
IsCustomConfig: isCustom,
Pages: tview.NewPages(),
}
wtfApp.Widgets = maker.MakeWidgets(wtfApp.App, wtfApp.Pages, wtfApp.Config)
wtfApp.Display = NewDisplay(wtfApp.Widgets, wtfApp.Config)
wtfApp.FocusTracker = wtf.NewFocusTracker(wtfApp.App, wtfApp.Widgets, wtfApp.Config)
wtfApp.App.SetInputCapture(wtfApp.keyboardIntercept)
wtfApp.Pages.AddPage("grid", wtfApp.Display.Grid, true, true)
wtfApp.App.SetRoot(wtfApp.Pages, true)
wtf.ValidateWidgets(wtfApp.Widgets)
wtfApp.scheduleWidgets()
return &wtfApp
}
/* -------------------- Exported Functions -------------------- */
// Start initializes the app
func (wtfApp *WtfApp) Start() {
go wtfApp.watchForConfigChanges()
}
// Stop kills all the currently-running widgets in this app
func (wtfApp *WtfApp) Stop() {
// TODO: Pretty sure we should kill their go routines that run them as well
// otherwise....?
wtfApp.disableAllWidgets()
}
/* -------------------- Unexported Functions -------------------- */
func (wtfApp *WtfApp) disableAllWidgets() {
for _, widget := range wtfApp.Widgets {
widget.Disable()
}
}
func (wtfApp *WtfApp) keyboardIntercept(event *tcell.EventKey) *tcell.EventKey {
// These keys are global keys used by the app. Widgets should not implement these keys
switch event.Key() {
case tcell.KeyCtrlR:
wtfApp.refreshAllWidgets()
return nil
case tcell.KeyTab:
wtfApp.FocusTracker.Next()
return nil
case tcell.KeyBacktab:
wtfApp.FocusTracker.Prev()
return nil
case tcell.KeyEsc:
wtfApp.FocusTracker.None()
return nil
}
// Checks to see if any widget has been assigned the pressed key as its focus key
if wtfApp.FocusTracker.FocusOn(string(event.Rune())) {
return nil
}
// If no specific widget has focus, then allow the key presses to fall through to the app
if !wtfApp.FocusTracker.IsFocused {
switch string(event.Rune()) {
case "/":
return nil
}
}
return event
}
func (wtfApp *WtfApp) refreshAllWidgets() {
for _, widget := range wtfApp.Widgets {
go widget.Refresh()
}
}
func (wtfApp *WtfApp) scheduleWidgets() {
for _, widget := range wtfApp.Widgets {
go wtf.Schedule(widget)
}
}
func (wtfApp *WtfApp) watchForConfigChanges() {
watch := watcher.New()
// Notify write events
watch.FilterOps(watcher.Write)
go func() {
for {
select {
case <-watch.Event:
wtfApp.Stop()
config := cfg.LoadWtfConfigFile(wtfApp.ConfigFilePath, wtfApp.IsCustomConfig)
newApp := NewWtfApp(wtfApp.App, config, wtfApp.ConfigFilePath, wtfApp.IsCustomConfig)
newApp.Start()
case err := <-watch.Error:
log.Fatalln(err)
case <-watch.Closed:
return
}
}
}()
// Watch config file for changes.
absPath, _ := utils.ExpandHomeDir(wtfApp.ConfigFilePath)
if err := watch.Add(absPath); err != nil {
log.Fatalln(err)
}
// Start the watching process - it'll check for changes every 100ms.
if err := watch.Start(time.Millisecond * 100); err != nil {
log.Fatalln(err)
}
}

132
main.go
View File

@ -9,23 +9,19 @@ import (
"fmt" "fmt"
"log" "log"
"os" "os"
"time"
"github.com/gdamore/tcell"
"github.com/logrusorgru/aurora" "github.com/logrusorgru/aurora"
"github.com/olebedev/config" "github.com/olebedev/config"
"github.com/pkg/profile" "github.com/pkg/profile"
"github.com/radovskyb/watcher"
"github.com/rivo/tview" "github.com/rivo/tview"
"github.com/wtfutil/wtf/app"
"github.com/wtfutil/wtf/cfg" "github.com/wtfutil/wtf/cfg"
"github.com/wtfutil/wtf/flags" "github.com/wtfutil/wtf/flags"
"github.com/wtfutil/wtf/maker"
"github.com/wtfutil/wtf/utils"
"github.com/wtfutil/wtf/wtf" "github.com/wtfutil/wtf/wtf"
) )
var focusTracker wtf.FocusTracker var tviewApp *tview.Application
var runningWidgets []wtf.Wtfable
var ( var (
commit = "dev" commit = "dev"
@ -35,52 +31,6 @@ var (
/* -------------------- Functions -------------------- */ /* -------------------- Functions -------------------- */
func disableAllWidgets(widgets []wtf.Wtfable) {
for _, widget := range widgets {
widget.Disable()
}
}
func keyboardIntercept(event *tcell.EventKey) *tcell.EventKey {
// These keys are global keys used by the app. Widgets should not implement these keys
switch event.Key() {
case tcell.KeyCtrlR:
refreshAllWidgets(runningWidgets)
return nil
case tcell.KeyTab:
focusTracker.Next()
return nil
case tcell.KeyBacktab:
focusTracker.Prev()
return nil
case tcell.KeyEsc:
focusTracker.None()
return nil
}
// This function checks to see if any widget has been assigned the pressed key as its
// focus key
if focusTracker.FocusOn(string(event.Rune())) {
return nil
}
// If no specific widget has focus, then allow the key presses to fall through to the app
if !focusTracker.IsFocused {
switch string(event.Rune()) {
case "/":
return nil
}
}
return event
}
func refreshAllWidgets(widgets []wtf.Wtfable) {
for _, widget := range widgets {
go widget.Refresh()
}
}
func setTerm(config *config.Config) { func setTerm(config *config.Config) {
term := config.UString("wtf.term", os.Getenv("TERM")) term := config.UString("wtf.term", os.Getenv("TERM"))
err := os.Setenv("TERM", term) err := os.Setenv("TERM", term)
@ -90,61 +40,19 @@ func setTerm(config *config.Config) {
} }
} }
func watchForConfigChanges(app *tview.Application, configFilePath string, isCustomConfig bool, grid *tview.Grid, pages *tview.Pages) {
watch := watcher.New()
absPath, _ := utils.ExpandHomeDir(configFilePath)
// Notify write events
watch.FilterOps(watcher.Write)
go func() {
for {
select {
case <-watch.Event:
// Disable all widgets to stop scheduler goroutines and remove widgets from memory
disableAllWidgets(runningWidgets)
config := cfg.LoadWtfConfigFile(absPath, false)
widgets := maker.MakeWidgets(app, pages, config)
runningWidgets = widgets
wtf.ValidateWidgets(widgets)
focusTracker = wtf.NewFocusTracker(app, widgets, config)
display := wtf.NewDisplay(widgets, config)
pages.AddPage("grid", display.Grid, true, true)
case err := <-watch.Error:
log.Fatalln(err)
case <-watch.Closed:
return
}
}
}()
// Watch config file for changes.
if err := watch.Add(absPath); err != nil {
log.Fatalln(err)
}
// Start the watching process - it'll check for changes every 100ms.
if err := watch.Start(time.Millisecond * 100); err != nil {
log.Fatalln(err)
}
}
/* -------------------- Main -------------------- */ /* -------------------- Main -------------------- */
func main() { func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetFlags(log.LstdFlags | log.Lshortfile)
// Manage the configuration directories and file // Manage the configuration directories and config file
cfg.Initialize() cfg.Initialize()
// Parse and handle flags // Parse and handle flags
flags := flags.NewFlags() flags := flags.NewFlags()
flags.Parse() flags.Parse()
// Load the configuration file
config := cfg.LoadWtfConfigFile(flags.ConfigFilePath(), flags.HasCustomConfig()) config := cfg.LoadWtfConfigFile(flags.ConfigFilePath(), flags.HasCustomConfig())
flags.RenderIf(version, config) flags.RenderIf(version, config)
@ -152,28 +60,16 @@ func main() {
defer profile.Start(profile.MemProfile).Stop() defer profile.Start(profile.MemProfile).Stop()
} }
setTerm(config)
app := tview.NewApplication()
pages := tview.NewPages()
widgets := maker.MakeWidgets(app, pages, config)
runningWidgets = widgets
wtf.ValidateWidgets(widgets)
focusTracker = wtf.NewFocusTracker(app, widgets, config)
display := wtf.NewDisplay(widgets, config)
pages.AddPage("grid", display.Grid, true, true)
app.SetInputCapture(keyboardIntercept)
go watchForConfigChanges(app, flags.Config, flags.HasCustomConfig(), display.Grid, pages)
wtf.Init(config.UString("wtf.openFileUtil", "open")) wtf.Init(config.UString("wtf.openFileUtil", "open"))
if err := app.SetRoot(pages, true).Run(); err != nil { setTerm(config)
// Build the application
tviewApp = tview.NewApplication()
wtfApp := app.NewWtfApp(tviewApp, config, flags.Config, flags.HasCustomConfig())
wtfApp.Start()
if err := wtfApp.App.Run(); err != nil {
fmt.Printf("\n%s %v\n", aurora.Red("ERROR"), err) fmt.Printf("\n%s %v\n", aurora.Red("ERROR"), err)
os.Exit(1) os.Exit(1)
} }