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:
parent
324666a3ec
commit
a6d18e286a
@ -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
156
app/wtf_app.go
Normal 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
132
main.go
@ -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)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user