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

Reimpliments the module configuration validation

Now supports displaying errors from multiple widgets.
This commit is contained in:
Chris Cummer 2019-07-05 23:01:25 -07:00
parent c308d1b6c2
commit b07f3c5e67
65 changed files with 371 additions and 363 deletions

View File

@ -44,7 +44,7 @@ type Sigils struct {
type Common struct {
Colors
Module
Position `help:"Defines where in the grid this modules widget will be displayed."`
PositionSettings `help:"Defines where in the grid this modules widget will be displayed."`
Sigils
Enabled bool `help:"Determines whether or not this module is executed and if its data displayed onscreen." values:"true, false"`
@ -78,7 +78,7 @@ func NewCommonSettingsFromModule(name, defaultTitle string, moduleConfig *config
Type: moduleConfig.UString("type", name),
},
Position: NewPositionFromYAML(name, moduleConfig),
PositionSettings: NewPositionSettingsFromYAML(name, moduleConfig),
Enabled: moduleConfig.UBool("enabled", false),
RefreshInterval: moduleConfig.UInt("refreshInterval", 300),
@ -100,6 +100,8 @@ func NewCommonSettingsFromModule(name, defaultTitle string, moduleConfig *config
return &common
}
/* -------------------- Exported Functions -------------------- */
func (common *Common) DefaultFocusedRowColor() string {
return fmt.Sprintf("%s:%s", common.Colors.HighlightFore, common.Colors.HighlightBack)
}
@ -109,12 +111,11 @@ func (common *Common) DefaultRowColor() string {
}
func (common *Common) FocusChar() string {
focusChar := string('0' + common.focusChar)
if common.focusChar == -1 {
focusChar = ""
if common.focusChar <= -1 {
return ""
}
return focusChar
return string('0' + common.focusChar)
}
func (common *Common) RowColor(idx int) string {
@ -143,3 +144,15 @@ func (common *Common) SigilStr(len, pos int, width int) string {
return sigils
}
// Validations aggregates all the validations from all the sub-sections in Common into a
// single array of validations
func (common *Common) Validations() []Validatable {
validatables := []Validatable{}
for _, validation := range common.PositionSettings.Validations.validations {
validatables = append(validatables, validation)
}
return validatables
}

View File

@ -1,123 +0,0 @@
package cfg
import (
"fmt"
"os"
"strings"
"github.com/olebedev/config"
)
const (
positionPath = "position"
)
/* -------------------- Position Validation -------------------- */
type positionValidation struct {
err error
name string
val int
}
func (posVal *positionValidation) hasError() bool {
return posVal.err != nil
}
// String returns the Stringer representation of the positionValidation
func (posVal *positionValidation) String() string {
return fmt.Sprintf("Invalid value for %s:\t%d", posVal.name, posVal.val)
}
func newPositionValidation(name string, val int, err error) *positionValidation {
posVal := &positionValidation{
err: err,
name: name,
val: val,
}
return posVal
}
/* -------------------- Position -------------------- */
// Position represents the onscreen location of a widget
type Position struct {
Height int
Left int
Top int
Width int
}
// NewPositionFromYAML creates and returns a new instance of Position
func NewPositionFromYAML(moduleName string, moduleConfig *config.Config) Position {
var val int
var err error
validations := make(map[string]*positionValidation)
// Parse the positional data from the config data
val, err = moduleConfig.Int(positionPath + ".top")
validations["top"] = newPositionValidation("top", val, err)
val, err = moduleConfig.Int(positionPath + ".left")
validations["left"] = newPositionValidation("left", val, err)
val, err = moduleConfig.Int(positionPath + ".width")
validations["width"] = newPositionValidation("width", val, err)
val, err = moduleConfig.Int(positionPath + ".height")
validations["height"] = newPositionValidation("height", val, err)
validatePositions(moduleName, validations)
pos := Position{
Top: validations["top"].val,
Left: validations["left"].val,
Width: validations["width"].val,
Height: validations["height"].val,
}
return pos
}
/* -------------------- Unexported Functions -------------------- */
// If any of the position values have an error then we inform the user and exit the app
// Common examples of invalid position configuration are:
//
// position:
// top: 3
// width: 2
// height: 1
//
// position:
// top: 3
// # left: 2
// width: 2
// height: 1
//
// position:
// top: 3
// left: 2
// width: 2
// height: 1
//
func validatePositions(moduleName string, validations map[string]*positionValidation) {
var errStr string
for _, posVal := range validations {
if posVal.hasError() {
errStr += fmt.Sprintf(" - %s.\t\033[0;31mError\033[0m %v\n", posVal, posVal.err)
}
}
if errStr != "" {
fmt.Println()
fmt.Printf("\033[0;1mErrors in %s position configuration\033[0m\n", strings.Title(moduleName))
fmt.Println(errStr)
fmt.Println("Please check your config.yml file.")
fmt.Println()
os.Exit(1)
}
}

51
cfg/position_settings.go Normal file
View File

@ -0,0 +1,51 @@
package cfg
import (
"github.com/olebedev/config"
)
const (
positionPath = "position"
)
// PositionSettings represents the onscreen location of a widget
type PositionSettings struct {
Validations *Validations
Height int
Left int
Top int
Width int
}
// NewPositionSettingsFromYAML creates and returns a new instance of cfg.Position
func NewPositionSettingsFromYAML(moduleName string, moduleConfig *config.Config) PositionSettings {
var currVal int
var err error
validations := NewValidations()
// Parse the positional data from the config data
currVal, err = moduleConfig.Int(positionPath + ".top")
validations.append("top", newPositionValidation("top", currVal, err))
currVal, err = moduleConfig.Int(positionPath + ".left")
validations.append("left", newPositionValidation("left", currVal, err))
currVal, err = moduleConfig.Int(positionPath + ".width")
validations.append("width", newPositionValidation("width", currVal, err))
currVal, err = moduleConfig.Int(positionPath + ".height")
validations.append("height", newPositionValidation("height", currVal, err))
pos := PositionSettings{
Validations: validations,
Top: validations.valueFor("top"),
Left: validations.valueFor("left"),
Width: validations.valueFor("width"),
Height: validations.valueFor("height"),
}
return pos
}

View File

@ -0,0 +1,65 @@
package cfg
import (
"fmt"
"github.com/logrusorgru/aurora"
)
// Common examples of invalid position configuration are:
//
// position:
// top: -3
// left: 2
// width: 0
// height: 1
//
// position:
// top: 3
// width: 2
// height: 1
//
// position:
// top: 3
// # left: 2
// width: 2
// height: 1
//
// position:
// top: 3
// left: 2
// width: 2
// height: 1
//
type positionValidation struct {
err error
name string
intVal int
}
func (posVal *positionValidation) Error() error {
return posVal.err
}
func (posVal *positionValidation) HasError() bool {
return posVal.err != nil
}
func (posVal *positionValidation) IntValue() int {
return posVal.intVal
}
// String returns the Stringer representation of the positionValidation
func (posVal *positionValidation) String() string {
return fmt.Sprintf("Invalid value for %s:\t%d", aurora.Yellow(posVal.name), posVal.intVal)
}
func newPositionValidation(name string, intVal int, err error) *positionValidation {
posVal := &positionValidation{
err: err,
name: name,
intVal: intVal,
}
return posVal
}

9
cfg/validatable.go Normal file
View File

@ -0,0 +1,9 @@
package cfg
// Validatable is implemented by any value that validates a configuration setting
type Validatable interface {
Error() error
HasError() bool
String() string
IntValue() int
}

28
cfg/validations.go Normal file
View File

@ -0,0 +1,28 @@
package cfg
// Validations represent a collection of config setting validations
type Validations struct {
validations map[string]Validatable
}
// NewValidations creates and returns an instance of Validations
func NewValidations() *Validations {
vals := &Validations{
validations: make(map[string]Validatable),
}
return vals
}
func (vals *Validations) append(key string, posVal Validatable) {
vals.validations[key] = posVal
}
func (vals *Validations) valueFor(key string) int {
val := vals.validations[key]
if val != nil {
return val.IntValue()
}
return 0
}

View File

@ -52,5 +52,5 @@ func (widget *Widget) Refresh() {
/* -------------------- Unexported Functions -------------------- */
func (widget *Widget) display() {
widget.Redraw(widget.CommonSettings.Title, "Some text", false)
widget.Redraw(widget.CommonSettings().Title, "Some text", false)
}

1
go.mod
View File

@ -25,6 +25,7 @@ require (
github.com/hekmon/transmissionrpc v0.0.0-20190525133028-1d589625bacd
github.com/jessevdk/go-flags v1.4.0
github.com/kr/pretty v0.1.0 // indirect
github.com/logrusorgru/aurora v0.0.0-20190428105938-cea283e61946
github.com/mattn/go-isatty v0.0.8 // indirect
github.com/mmcdole/gofeed v1.0.0-beta2.0.20190420154928-0e68beaf6fdf
github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4

2
go.sum
View File

@ -99,6 +99,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/logrusorgru/aurora v0.0.0-20190428105938-cea283e61946 h1:z+WaKrgu3kCpcdnbK9YG+JThpOCd1nU5jO5ToVmSlR4=
github.com/logrusorgru/aurora v0.0.0-20190428105938-cea283e61946/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lucasb-eyer/go-colorful v1.0.2 h1:mCMFu6PgSozg9tDNMMK3g18oJBX7oYGrC09mS6CXfO4=
github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=

View File

@ -96,9 +96,10 @@ func watchForConfigChanges(app *tview.Application, configFilePath string, grid *
config := cfg.LoadConfigFile(absPath)
widgets := maker.MakeWidgets(app, pages, config)
wtf.ValidateWidgets(widgets)
runningWidgets = widgets
wtf.ValidateWidgets(widgets)
focusTracker = wtf.NewFocusTracker(app, widgets, config)
display := wtf.NewDisplay(widgets, config)
@ -152,9 +153,10 @@ func main() {
pages := tview.NewPages()
widgets := maker.MakeWidgets(app, pages, config)
wtf.ValidateWidgets(widgets)
runningWidgets = widgets
wtf.ValidateWidgets(widgets)
focusTracker = wtf.NewFocusTracker(app, widgets, config)
display := wtf.NewDisplay(widgets, config)

View File

@ -40,7 +40,7 @@ func (widget *Widget) Refresh() {
wtf.Now().Format(wtf.DateFormat),
)
widget.Redraw(widget.CommonSettings.Title, widget.contentFrom(todayItems), false)
widget.Redraw(widget.CommonSettings().Title, widget.contentFrom(todayItems), false)
}
/* -------------------- Unexported Functions -------------------- */

View File

@ -34,7 +34,7 @@ func (widget *Widget) Refresh() {
builds, err := widget.Client.BuildsFor()
title := fmt.Sprintf("%s - Builds", widget.CommonSettings.Title)
title := fmt.Sprintf("%s - Builds", widget.CommonSettings().Title)
var content string
wrap := false
if err != nil {

View File

@ -7,7 +7,7 @@ import (
func (widget *Widget) display(clocks []Clock, dateFormat string, timeFormat string) {
if len(clocks) == 0 {
title := fmt.Sprintf("\n%s", " no timezone data available")
widget.Redraw(widget.CommonSettings.Title, title, true)
widget.Redraw(widget.CommonSettings().Title, title, true)
return
}
@ -28,5 +28,5 @@ func (widget *Widget) display(clocks []Clock, dateFormat string, timeFormat stri
)
}
widget.Redraw(widget.CommonSettings.Title, str, false)
widget.Redraw(widget.CommonSettings().Title, str, false)
}

View File

@ -36,7 +36,7 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
func (widget *Widget) Refresh() {
result := widget.execute()
ansiTitle := tview.TranslateANSI(widget.CommonSettings.Title)
ansiTitle := tview.TranslateANSI(widget.CommonSettings().Title)
if ansiTitle == defaultTitle {
ansiTitle = tview.TranslateANSI(widget.String())
}

View File

@ -8,12 +8,12 @@ import (
func (widget *Widget) display() {
if ok == false {
widget.Redraw(widget.CommonSettings.Title, errorText, true)
widget.Redraw(widget.CommonSettings().Title, errorText, true)
return
}
summaryText := widget.summaryText(&widget.summaryList)
widget.Redraw(widget.CommonSettings.Title, summaryText, false)
widget.Redraw(widget.CommonSettings().Title, summaryText, false)
}
func (widget *Widget) summaryText(list *summaryList) string {

View File

@ -34,13 +34,13 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
func (widget *Widget) Refresh() {
positions, err := Fetch(widget.device_token)
if err != nil {
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
return
}
content := widget.contentFrom(positions)
widget.Redraw(widget.CommonSettings.Title, content, false)
widget.Redraw(widget.CommonSettings().Title, content, false)
}
/* -------------------- Unexported Functions -------------------- */

View File

@ -56,5 +56,5 @@ func (widget *Widget) display() {
str += widget.priceWidget.Result
str += widget.toplistWidget.Result
widget.Redraw(widget.CommonSettings.Title, fmt.Sprintf("\n%s", str), false)
widget.Redraw(widget.CommonSettings().Title, fmt.Sprintf("\n%s", str), false)
}

View File

@ -41,7 +41,7 @@ func (widget *Widget) Refresh() {
if monitorErr != nil {
widget.monitors = nil
widget.SetItemCount(0)
widget.Redraw(widget.CommonSettings.Title, monitorErr.Error(), true)
widget.Redraw(widget.CommonSettings().Title, monitorErr.Error(), true)
return
}
triggeredMonitors := []datadog.Monitor{}
@ -60,7 +60,7 @@ func (widget *Widget) Refresh() {
func (widget *Widget) Render() {
content := widget.contentFrom(widget.monitors)
widget.Redraw(widget.CommonSettings.Title, content, false)
widget.Redraw(widget.CommonSettings().Title, content, false)
}
func (widget *Widget) HelpText() string {

View File

@ -70,7 +70,7 @@ func (widget *Widget) Fetch(feedURLs []string) ([]*FeedItem, error) {
func (widget *Widget) Refresh() {
feedItems, err := widget.Fetch(widget.settings.feeds)
if err != nil {
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
}
widget.stories = feedItems
@ -85,7 +85,7 @@ func (widget *Widget) Render() {
return
}
title := widget.CommonSettings.Title
title := widget.CommonSettings().Title
widget.Redraw(title, widget.contentFrom(widget.stories), false)
}

View File

@ -8,11 +8,11 @@ func (widget *Widget) display() {
project := widget.currentGerritProject()
if project == nil {
widget.Redraw(widget.CommonSettings.Title, "Gerrit project data is unavailable", true)
widget.Redraw(widget.CommonSettings().Title, "Gerrit project data is unavailable", true)
return
}
title := fmt.Sprintf("%s- %s", widget.CommonSettings.Title, widget.title(project))
title := fmt.Sprintf("%s- %s", widget.CommonSettings().Title, widget.title(project))
_, _, width, _ := widget.View.GetRect()
str := widget.settings.common.SigilStr(len(widget.GerritProjects), widget.Idx, width) + "\n"

View File

@ -77,7 +77,7 @@ func (widget *Widget) Refresh() {
if err != nil {
widget.View.SetWrap(true)
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
return
}
widget.gerrit = gerrit

View File

@ -9,11 +9,11 @@ import (
func (widget *Widget) display() {
repoData := widget.currentData()
if repoData == nil {
widget.Redraw(widget.CommonSettings.Title, " Git repo data is unavailable ", false)
widget.Redraw(widget.CommonSettings().Title, " Git repo data is unavailable ", false)
return
}
title := fmt.Sprintf("%s - [green]%s[white]", widget.CommonSettings.Title, repoData.Repository)
title := fmt.Sprintf("%s - [green]%s[white]", widget.CommonSettings().Title, repoData.Repository)
_, _, width, _ := widget.View.GetRect()
str := widget.settings.common.SigilStr(len(widget.GitRepos), widget.Idx, width) + "\n"

View File

@ -8,7 +8,7 @@ import (
func (widget *Widget) display() {
repo := widget.currentGithubRepo()
title := fmt.Sprintf("%s - %s", widget.CommonSettings.Title, widget.title(repo))
title := fmt.Sprintf("%s - %s", widget.CommonSettings().Title, widget.title(repo))
if repo == nil {
widget.TextWidget.Redraw(title, " GitHub repo data is unavailable ", false)
return

View File

@ -8,11 +8,11 @@ func (widget *Widget) display() {
project := widget.currentGitlabProject()
if project == nil {
widget.Redraw(widget.CommonSettings.Title, " Gitlab project data is unavailable ", true)
widget.Redraw(widget.CommonSettings().Title, " Gitlab project data is unavailable ", true)
return
}
title := fmt.Sprintf("%s- %s", widget.CommonSettings.Title, widget.title(project))
title := fmt.Sprintf("%s- %s", widget.CommonSettings().Title, widget.title(project))
_, _, width, _ := widget.View.GetRect()
str := widget.settings.common.SigilStr(len(widget.GitlabProjects), widget.Idx, width) + "\n"

View File

@ -43,19 +43,19 @@ func (widget *Widget) Refresh() {
room, err := GetRoom(widget.settings.roomURI, widget.settings.apiToken)
if err != nil {
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
return
}
if room == nil {
widget.Redraw(widget.CommonSettings.Title, "No room", true)
widget.Redraw(widget.CommonSettings().Title, "No room", true)
return
}
messages, err := GetMessages(room.ID, widget.settings.numberOfMessages, widget.settings.apiToken)
if err != nil {
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
return
}
widget.messages = messages
@ -75,7 +75,7 @@ func (widget *Widget) display() {
return
}
title := fmt.Sprintf("%s - %s", widget.CommonSettings.Title, widget.settings.roomURI)
title := fmt.Sprintf("%s - %s", widget.CommonSettings().Title, widget.settings.roomURI)
widget.Redraw(title, widget.contentFrom(widget.messages), true)
}

View File

@ -29,7 +29,7 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
func (widget *Widget) Refresh() {
cells, _ := widget.Fetch()
widget.Redraw(widget.CommonSettings.Title, widget.contentFrom(cells), false)
widget.Redraw(widget.CommonSettings().Title, widget.contentFrom(cells), false)
}
/* -------------------- Unexported Functions -------------------- */

View File

@ -47,7 +47,7 @@ func (widget *Widget) Refresh() {
}
if err != nil {
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
return
}
var stories []Story
@ -70,7 +70,7 @@ func (widget *Widget) Render() {
return
}
title := fmt.Sprintf("%s - %s stories", widget.CommonSettings.Title, widget.settings.storyType)
title := fmt.Sprintf("%s - %s stories", widget.CommonSettings().Title, widget.settings.storyType)
widget.Redraw(title, widget.contentFrom(widget.stories), false)
}

View File

@ -48,7 +48,7 @@ func (widget *Widget) Fetch(accounts []string) ([]*Status, error) {
func (widget *Widget) Refresh() {
data, err := widget.Fetch(widget.settings.accounts)
title := widget.CommonSettings.Title
title := widget.CommonSettings().Title
title = title + widget.sinceDateForTitle()
var content string

View File

@ -52,7 +52,7 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
func (widget *Widget) Refresh() {
widget.ipinfo()
widget.Redraw(widget.CommonSettings.Title, widget.result, false)
widget.Redraw(widget.CommonSettings().Title, widget.result, false)
}
//this method reads the config and calls ipinfo for ip information

View File

@ -44,7 +44,7 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
func (widget *Widget) Refresh() {
widget.ipinfo()
widget.TextWidget.Redraw(widget.CommonSettings.Title, widget.result, false)
widget.TextWidget.Redraw(widget.CommonSettings().Title, widget.result, false)
}
//this method reads the config and calls ipinfo for ip information

View File

@ -48,7 +48,7 @@ func (widget *Widget) Refresh() {
widget.view = view
if err != nil {
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
return
}
@ -64,7 +64,7 @@ func (widget *Widget) Render() {
return
}
title := fmt.Sprintf("%s: [red]%s", widget.CommonSettings.Title, widget.view.Name)
title := fmt.Sprintf("%s: [red]%s", widget.CommonSettings().Title, widget.view.Name)
widget.Redraw(title, widget.contentFrom(widget.view), false)
}

View File

@ -43,7 +43,7 @@ func (widget *Widget) Refresh() {
if err != nil {
widget.result = nil
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
return
}
widget.result = searchResult
@ -56,7 +56,7 @@ func (widget *Widget) Render() {
return
}
str := fmt.Sprintf("%s- [green]%s[white]", widget.CommonSettings.Title, widget.settings.projects)
str := fmt.Sprintf("%s- [green]%s[white]", widget.CommonSettings().Title, widget.settings.projects)
widget.Redraw(str, widget.contentFrom(widget.result), false)
}

View File

@ -40,7 +40,7 @@ func (widget *Widget) Refresh() {
logLines := widget.tailFile()
widget.Redraw(widget.CommonSettings.Title, widget.contentFrom(logLines), false)
widget.Redraw(widget.CommonSettings().Title, widget.contentFrom(logLines), false)
}
/* -------------------- Unexported Functions -------------------- */

View File

@ -9,11 +9,11 @@ import (
func (widget *Widget) display() {
repoData := widget.currentData()
if repoData == nil {
widget.Redraw(widget.CommonSettings.Title, " Mercurial repo data is unavailable ", false)
widget.Redraw(widget.CommonSettings().Title, " Mercurial repo data is unavailable ", false)
return
}
title := fmt.Sprintf("%s - [green]%s[white]", widget.CommonSettings.Title, repoData.Repository)
title := fmt.Sprintf("%s - [green]%s[white]", widget.CommonSettings().Title, repoData.Repository)
_, _, width, _ := widget.View.GetRect()
str := widget.settings.common.SigilStr(len(widget.Data), widget.Idx, width) + "\n"

View File

@ -44,7 +44,7 @@ func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *
}
func (widget *Widget) Refresh() {
widget.Redraw(widget.CommonSettings.Title, widget.nbascore(), false)
widget.Redraw(widget.CommonSettings().Title, widget.nbascore(), false)
}
func (widget *Widget) HelpText() string {

View File

@ -39,7 +39,7 @@ func (widget *Widget) Refresh() {
}
var content string
title := fmt.Sprintf("%s - [green]%s[white]", widget.CommonSettings.Title, appName)
title := fmt.Sprintf("%s - [green]%s[white]", widget.CommonSettings().Title, appName)
wrap := false
if depErr != nil {
wrap = true

View File

@ -41,7 +41,7 @@ func (widget *Widget) Refresh() {
content = widget.contentFrom(data)
}
widget.Redraw(widget.CommonSettings.Title, content, wrap)
widget.Redraw(widget.CommonSettings().Title, content, wrap)
}
/* -------------------- Unexported Functions -------------------- */

View File

@ -58,7 +58,7 @@ func (widget *Widget) Refresh() {
content = widget.contentFrom(onCalls, incidents)
}
widget.Redraw(widget.CommonSettings.Title, content, wrap)
widget.Redraw(widget.CommonSettings().Title, content, wrap)
}
/* -------------------- Unexported Functions -------------------- */

View File

@ -36,5 +36,5 @@ func (widget *Widget) Refresh() {
content += "\n"
content += widget.Battery.String()
widget.Redraw(widget.CommonSettings.Title, content, true)
widget.Redraw(widget.CommonSettings().Title, content, true)
}

View File

@ -48,7 +48,7 @@ func (widget *Widget) Refresh() {
)
if err != nil {
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
return
}
widget.items = &items.Results
@ -64,7 +64,7 @@ func (widget *Widget) Render() {
return
}
title := fmt.Sprintf("%s - %s", widget.CommonSettings.Title, widget.settings.projectName)
title := fmt.Sprintf("%s - %s", widget.CommonSettings().Title, widget.settings.projectName)
widget.Redraw(title, widget.contentFrom(widget.items), false)
}

View File

@ -35,7 +35,7 @@ func (widget *Widget) Refresh() {
data := NewSecurityData()
data.Fetch()
widget.Redraw(widget.CommonSettings.Title, widget.contentFrom(data), false)
widget.Redraw(widget.CommonSettings().Title, widget.contentFrom(data), false)
}
/* -------------------- Unexported Functions -------------------- */

View File

@ -66,13 +66,13 @@ func (w *Widget) render() {
} else {
content = w.createOutput()
}
w.Redraw(w.CommonSettings.Title, content, true)
w.Redraw(w.CommonSettings().Title, content, true)
}
func (w *Widget) createOutput() string {
output := wtf.CenterText(fmt.Sprintf("[green]Now %v [white]\n", w.Info.Status), w.Width())
output += wtf.CenterText(fmt.Sprintf("[green]Title:[white] %v\n ", w.Info.Title), w.Width())
output += wtf.CenterText(fmt.Sprintf("[green]Artist:[white] %v\n", w.Info.Artist), w.Width())
output += wtf.CenterText(fmt.Sprintf("[green]%v:[white] %v\n", w.Info.TrackNumber, w.Info.Album), w.Width())
output := wtf.CenterText(fmt.Sprintf("[green]Now %v [white]\n", w.Info.Status), w.CommonSettings().Width)
output += wtf.CenterText(fmt.Sprintf("[green]Title:[white] %v\n ", w.Info.Title), w.CommonSettings().Width)
output += wtf.CenterText(fmt.Sprintf("[green]Artist:[white] %v\n", w.Info.Artist), w.CommonSettings().Width)
output += wtf.CenterText(fmt.Sprintf("[green]%v:[white] %v\n", w.Info.TrackNumber, w.Info.Album), w.CommonSettings().Width)
return output
}

View File

@ -158,9 +158,9 @@ func (w *Widget) refreshSpotifyInfos() error {
func (w *Widget) Refresh() {
err := w.refreshSpotifyInfos()
if err != nil {
w.Redraw(w.CommonSettings.Title, err.Error(), true)
w.Redraw(w.CommonSettings().Title, err.Error(), true)
} else {
w.Redraw(w.CommonSettings.Title, w.createOutput(), false)
w.Redraw(w.CommonSettings().Title, w.createOutput(), false)
}
}
@ -169,14 +169,14 @@ func (widget *Widget) HelpText() string {
}
func (w *Widget) createOutput() string {
output := wtf.CenterText(fmt.Sprintf("[green]Now %v [white]\n", w.Info.Status), w.Width())
output += wtf.CenterText(fmt.Sprintf("[green]Title:[white] %v\n", w.Info.Title), w.Width())
output += wtf.CenterText(fmt.Sprintf("[green]Artist:[white] %v\n", w.Info.Artists), w.Width())
output += wtf.CenterText(fmt.Sprintf("[green]Album:[white] %v\n", w.Info.Album), w.Width())
output := wtf.CenterText(fmt.Sprintf("[green]Now %v [white]\n", w.Info.Status), w.CommonSettings().Width)
output += wtf.CenterText(fmt.Sprintf("[green]Title:[white] %v\n", w.Info.Title), w.CommonSettings().Width)
output += wtf.CenterText(fmt.Sprintf("[green]Artist:[white] %v\n", w.Info.Artists), w.CommonSettings().Width)
output += wtf.CenterText(fmt.Sprintf("[green]Album:[white] %v\n", w.Info.Album), w.CommonSettings().Width)
if w.playerState.ShuffleState {
output += wtf.CenterText(fmt.Sprintf("[green]Shuffle:[white] on\n"), w.Width())
output += wtf.CenterText(fmt.Sprintf("[green]Shuffle:[white] on\n"), w.CommonSettings().Width)
} else {
output += wtf.CenterText(fmt.Sprintf("[green]Shuffle:[white] off\n"), w.Width())
output += wtf.CenterText(fmt.Sprintf("[green]Shuffle:[white] off\n"), w.CommonSettings().Width)
}
return output
}

View File

@ -28,7 +28,7 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
/* -------------------- Exported Functions -------------------- */
func (widget *Widget) Refresh() {
widget.Redraw(widget.CommonSettings.Title, widget.animation(), false)
widget.Redraw(widget.CommonSettings().Title, widget.animation(), false)
}
/* -------------------- Unexported Functions -------------------- */

View File

@ -45,7 +45,7 @@ func (widget *Widget) Refresh() {
"Build",
widget.systemInfo.BuildVersion,
)
widget.Redraw(widget.CommonSettings.Title, content, false)
widget.Redraw(widget.CommonSettings().Title, content, false)
}
func (widget *Widget) prettyDate() string {

View File

@ -31,7 +31,7 @@ func (widget *Widget) display() {
newList.SetSelectedByItem(widget.list.SelectedItem())
widget.SetList(newList)
widget.Redraw(widget.CommonSettings.Title, str, false)
widget.Redraw(widget.CommonSettings().Title, str, false)
}
func (widget *Widget) formattedItemLine(idx int, item *checklist.ChecklistItem, selectedItem *checklist.ChecklistItem, maxLen int) string {

View File

@ -32,7 +32,7 @@ func (widget *Widget) contentFrom(data []*transmissionrpc.Torrent) string {
func (widget *Widget) display() {
content := widget.contentFrom(widget.torrents)
widget.ScrollableWidget.Redraw(widget.CommonSettings.Title, content, false)
widget.ScrollableWidget.Redraw(widget.CommonSettings().Title, content, false)
}
func (widget *Widget) prettyTorrentName(name string) string {

View File

@ -64,7 +64,7 @@ func (widget *Widget) Refresh() {
torrents, err := widget.Fetch()
if err != nil {
widget.SetItemCount(0)
widget.ScrollableWidget.Redraw(widget.CommonSettings.Title, err.Error(), false)
widget.ScrollableWidget.Redraw(widget.CommonSettings().Title, err.Error(), false)
return
}

View File

@ -43,7 +43,7 @@ func (widget *Widget) Refresh() {
builds, err := BuildsFor(widget.settings.apiKey, widget.settings.pro)
if err != nil {
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
return
}
widget.builds = builds
@ -58,7 +58,7 @@ func (widget *Widget) Render() {
return
}
title := fmt.Sprintf("%s - Builds", widget.CommonSettings.Title)
title := fmt.Sprintf("%s - Builds", widget.CommonSettings().Title)
widget.Redraw(title, widget.contentFrom(widget.builds), false)
}

View File

@ -46,13 +46,13 @@ func (widget *Widget) Refresh() {
wrap := false
if err != nil {
wrap = true
title = widget.CommonSettings.Title
title = widget.CommonSettings().Title
content = err.Error()
} else {
widget.View.SetWrap(false)
title = fmt.Sprintf(
"[white]%s: [green]%s ",
widget.CommonSettings.Title,
widget.CommonSettings().Title,
widget.settings.board,
)
content = widget.contentFrom(searchResult)

View File

@ -26,6 +26,6 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
/* -------------------- Exported Functions -------------------- */
func (widget *Widget) Refresh() {
content := fmt.Sprintf("Widget %s and/or type %s does not exist", widget.Name(), widget.CommonSettings.Module.Type)
widget.Redraw(widget.CommonSettings.Title, content, true)
content := fmt.Sprintf("Widget %s and/or type %s does not exist", widget.Name(), widget.CommonSettings().Module.Type)
widget.Redraw(widget.CommonSettings().Title, content, true)
}

View File

@ -36,10 +36,10 @@ func (widget *Widget) Refresh() {
teams, err := Fetch(widget.settings.apiID, widget.settings.apiKey)
if err != nil {
widget.Redraw(widget.CommonSettings.Title, err.Error(), true)
widget.Redraw(widget.CommonSettings().Title, err.Error(), true)
} else {
widget.teams = teams
widget.Redraw(widget.CommonSettings.Title, widget.contentFrom(widget.teams), true)
widget.Redraw(widget.CommonSettings().Title, widget.contentFrom(widget.teams), true)
}
}

View File

@ -29,7 +29,7 @@ func NewWidget(app *tview.Application, settings *Settings) *Widget {
func (widget *Widget) Refresh() {
widget.prettyWeather()
widget.Redraw(widget.CommonSettings.Title, widget.result, false)
widget.Redraw(widget.CommonSettings().Title, widget.result, false)
}
//this method reads the config and calls wttr.in for pretty weather

View File

@ -24,7 +24,7 @@ func (widget *Widget) display() {
err += " Weather data is unavailable: no weather data\n"
}
title := widget.CommonSettings.Title
title := widget.CommonSettings().Title
setWrap := false
var content string

View File

@ -52,7 +52,7 @@ func (widget *Widget) Refresh() {
/* -------------------- Unexported Functions -------------------- */
func (widget *Widget) Render() {
title := fmt.Sprintf("%s (%d)", widget.CommonSettings.Title, widget.result.Count)
title := fmt.Sprintf("%s (%d)", widget.CommonSettings().Title, widget.result.Count)
widget.Redraw(title, widget.textContent(widget.result.Tickets), false)
}

View File

@ -11,18 +11,16 @@ import (
//BarGraph lets make graphs
type BarGraph struct {
enabled bool
focusable bool
key string
maxStars int
name string
starChar string
commonSettings *cfg.Common
enabled bool
focusable bool
key string
maxStars int
name string
starChar string
RefreshInt int
View *tview.TextView
settings *cfg.Common
Position
}
type Bar struct {
@ -34,22 +32,15 @@ type Bar struct {
// NewBarGraph initialize your fancy new graph
func NewBarGraph(app *tview.Application, name string, settings *cfg.Common, focusable bool) BarGraph {
widget := BarGraph{
enabled: settings.Enabled,
focusable: focusable,
maxStars: settings.Config.UInt("graphStars", 20),
name: settings.Title,
starChar: settings.Config.UString("graphIcon", "|"),
RefreshInt: settings.RefreshInterval,
settings: settings,
enabled: settings.Enabled,
focusable: focusable,
maxStars: settings.Config.UInt("graphStars", 20),
name: settings.Title,
starChar: settings.Config.UString("graphIcon", "|"),
RefreshInt: settings.RefreshInterval,
commonSettings: settings,
}
widget.Position = NewPosition(
settings.Position.Top,
settings.Position.Left,
settings.Position.Width,
settings.Position.Height,
)
widget.View = widget.addView()
widget.View.SetChangedFunc(func() {
app.Draw()
@ -60,10 +51,14 @@ func NewBarGraph(app *tview.Application, name string, settings *cfg.Common, focu
func (widget *BarGraph) BorderColor() string {
if widget.Focusable() {
return widget.settings.Colors.BorderFocusable
return widget.commonSettings.Colors.BorderFocusable
}
return widget.settings.Colors.BorderNormal
return widget.commonSettings.Colors.BorderNormal
}
func (widget *BarGraph) CommonSettings() *cfg.Common {
return widget.commonSettings
}
func (widget *BarGraph) Disable() {
@ -86,12 +81,6 @@ func (widget *BarGraph) FocusChar() string {
return ""
}
// IsPositionable returns TRUE if the widget has valid position parameters, FALSE if it has
// invalid position parameters (ie: cannot be placed onscreen)
func (widget *BarGraph) IsPositionable() bool {
return widget.Position.IsValid()
}
func (widget *BarGraph) Key() string {
return widget.key
}
@ -120,22 +109,6 @@ func (widget *BarGraph) ConfigText() string {
return ""
}
/* -------------------- Unexported Functions -------------------- */
func (widget *BarGraph) addView() *tview.TextView {
view := tview.NewTextView()
view.SetBackgroundColor(ColorFor(widget.settings.Colors.Background))
view.SetBorder(true)
view.SetBorderColor(ColorFor(widget.BorderColor()))
view.SetDynamicColors(true)
view.SetTitle(widget.Name())
view.SetTitleColor(ColorFor(widget.settings.Colors.Title))
view.SetWrap(false)
return view
}
// BuildBars will build a string of * to represent your data of [time][value]
// time should be passed as a int64
func (widget *BarGraph) BuildBars(data []Bar) {
@ -188,4 +161,18 @@ func BuildStars(data []Bar, maxStars int, starChar string) string {
return buffer.String()
}
/* -------------------- Exported Functions -------------------- */
/* -------------------- Unexported Functions -------------------- */
func (widget *BarGraph) addView() *tview.TextView {
view := tview.NewTextView()
view.SetBackgroundColor(ColorFor(widget.commonSettings.Colors.Background))
view.SetBorder(true)
view.SetBorderColor(ColorFor(widget.BorderColor()))
view.SetDynamicColors(true)
view.SetTitle(widget.Name())
view.SetTitleColor(ColorFor(widget.commonSettings.Colors.Title))
view.SetWrap(false)
return view
}

View File

@ -29,16 +29,12 @@ func (display *Display) add(widget Wtfable) {
return
}
if !widget.IsPositionable() {
return
}
display.Grid.AddItem(
widget.TextView(),
widget.Top(),
widget.Left(),
widget.Height(),
widget.Width(),
widget.CommonSettings().Top,
widget.CommonSettings().Left,
widget.CommonSettings().Height,
widget.CommonSettings().Width,
0,
0,
false,

View File

@ -3,7 +3,6 @@ package wtf
type Enabler interface {
Disabled() bool
Enabled() bool
IsPositionable() bool
Disable()
}

View File

@ -192,11 +192,14 @@ func (tracker *FocusTracker) focusables() []Wtfable {
// Sort for deterministic ordering
sort.SliceStable(focusable[:], func(i, j int) bool {
if focusable[i].Top() < focusable[j].Top() {
iTop := focusable[i].CommonSettings().Top
jTop := focusable[j].CommonSettings().Top
if iTop < jTop {
return true
}
if focusable[i].Top() == focusable[j].Top() {
return focusable[i].Left() < focusable[j].Left()
if iTop == jTop {
return focusable[i].CommonSettings().Left < focusable[j].CommonSettings().Left
}
return false
})

View File

@ -1,43 +0,0 @@
package wtf
type Position struct {
top int
left int
width int
height int
}
func NewPosition(top, left, width, height int) Position {
pos := Position{
top: top,
left: left,
width: width,
height: height,
}
return pos
}
func (pos *Position) IsValid() bool {
if pos.height < 1 || pos.left < 0 || pos.top < 0 || pos.width < 1 {
return false
}
return true
}
func (pos *Position) Height() int {
return pos.height
}
func (pos *Position) Left() int {
return pos.left
}
func (pos *Position) Top() int {
return pos.top
}
func (pos *Position) Width() int {
return pos.width
}

View File

@ -42,10 +42,10 @@ func (widget *ScrollableWidget) GetSelected() int {
func (widget *ScrollableWidget) RowColor(idx int) string {
if widget.View.HasFocus() && (idx == widget.Selected) {
return widget.CommonSettings.DefaultFocusedRowColor()
return widget.CommonSettings().DefaultFocusedRowColor()
}
return widget.CommonSettings.RowColor(idx)
return widget.CommonSettings().RowColor(idx)
}
func (widget *ScrollableWidget) Next() {

View File

@ -9,6 +9,7 @@ import (
)
type TextWidget struct {
commonSettings *cfg.Common
enabled bool
focusable bool
focusChar string
@ -17,14 +18,11 @@ type TextWidget struct {
app *tview.Application
View *tview.TextView
CommonSettings *cfg.Common
Position
}
func NewTextWidget(app *tview.Application, commonSettings *cfg.Common, focusable bool) TextWidget {
widget := TextWidget{
CommonSettings: commonSettings,
commonSettings: commonSettings,
app: app,
enabled: commonSettings.Enabled,
@ -34,13 +32,6 @@ func NewTextWidget(app *tview.Application, commonSettings *cfg.Common, focusable
refreshInterval: commonSettings.RefreshInterval,
}
widget.Position = NewPosition(
commonSettings.Position.Top,
commonSettings.Position.Left,
commonSettings.Position.Width,
commonSettings.Position.Height,
)
widget.View = widget.addView()
return widget
@ -50,10 +41,14 @@ func NewTextWidget(app *tview.Application, commonSettings *cfg.Common, focusable
func (widget *TextWidget) BorderColor() string {
if widget.Focusable() {
return widget.CommonSettings.Colors.BorderFocusable
return widget.commonSettings.Colors.BorderFocusable
}
return widget.CommonSettings.Colors.BorderNormal
return widget.commonSettings.Colors.BorderNormal
}
func (widget *TextWidget) CommonSettings() *cfg.Common {
return widget.commonSettings
}
func (widget *TextWidget) ContextualTitle(defaultStr string) string {
@ -84,12 +79,6 @@ func (widget *TextWidget) FocusChar() string {
return widget.focusChar
}
// IsPositionable returns TRUE if the widget has valid position parameters, FALSE if it has
// invalid position parameters (ie: cannot be placed onscreen)
func (widget *TextWidget) IsPositionable() bool {
return widget.Position.IsValid()
}
func (widget *TextWidget) Name() string {
return widget.name
}
@ -120,7 +109,7 @@ func (widget *TextWidget) Redraw(title, text string, wrap bool) {
}
func (widget *TextWidget) HelpText() string {
return fmt.Sprintf("\n There is no help available for widget %s", widget.CommonSettings.Module.Type)
return fmt.Sprintf("\n There is no help available for widget %s", widget.commonSettings.Module.Type)
}
func (widget *TextWidget) ConfigText() string {
@ -132,10 +121,10 @@ func (widget *TextWidget) ConfigText() string {
func (widget *TextWidget) addView() *tview.TextView {
view := tview.NewTextView()
view.SetBackgroundColor(ColorFor(widget.CommonSettings.Colors.Background))
view.SetBackgroundColor(ColorFor(widget.commonSettings.Colors.Background))
view.SetBorderColor(ColorFor(widget.BorderColor()))
view.SetTextColor(ColorFor(widget.CommonSettings.Colors.Text))
view.SetTitleColor(ColorFor(widget.CommonSettings.Colors.Title))
view.SetTextColor(ColorFor(widget.commonSettings.Colors.Text))
view.SetTitleColor(ColorFor(widget.commonSettings.Colors.Title))
view.SetBorder(true)
view.SetDynamicColors(true)

51
wtf/widget_validation.go Normal file
View File

@ -0,0 +1,51 @@
package wtf
import (
"fmt"
"os"
"github.com/logrusorgru/aurora"
)
// ValidateWidgets rolls through all the enabled widgets and looks for configuration errors.
// If it finds any it stringifies them, writes them to the console, and kills the app gracefully
func ValidateWidgets(widgets []Wtfable) {
var errStr string
hasErrors := false
for _, widget := range widgets {
var widgetErrStr string
for _, val := range widget.CommonSettings().Validations() {
if val.HasError() {
hasErrors = true
widgetErrStr += fmt.Sprintf(" - %s\t%s %v\n", val, aurora.Red("Error:"), val.Error())
}
}
if widgetErrStr != "" {
errStr += fmt.Sprintf(
"%s\n",
fmt.Sprintf(
"%s in %s configuration",
aurora.Red("Errors"),
aurora.Yellow(
fmt.Sprintf(
"%s.position",
widget.Name(),
),
),
),
)
errStr += widgetErrStr + "\n"
}
}
if hasErrors {
fmt.Println()
fmt.Printf(errStr)
os.Exit(1)
}
}

View File

@ -1,21 +0,0 @@
package wtf
import (
"fmt"
"log"
)
// Check that all the loaded widgets are valid for display
func ValidateWidgets(widgets []Wtfable) (bool, error) {
result := true
var err error
for _, widget := range widgets {
if widget.Enabled() && !widget.IsPositionable() {
errStr := fmt.Sprintf("Widget config has invalid values: %s", widget.Name())
log.Fatalln(errStr)
}
}
return result, err
}

View File

@ -1,6 +1,8 @@
package wtf
import (
"github.com/wtfutil/wtf/cfg"
"github.com/rivo/tview"
)
@ -9,16 +11,13 @@ type Wtfable interface {
Scheduler
BorderColor() string
ConfigText() string
FocusChar() string
Focusable() bool
HelpText() string
Name() string
SetFocusChar(string)
TextView() *tview.TextView
HelpText() string
ConfigText() string
Height() int
Left() int
Top() int
Width() int
CommonSettings() *cfg.Common
}