summaryrefslogtreecommitdiff
path: root/structs.go
diff options
context:
space:
mode:
Diffstat (limited to 'structs.go')
-rw-r--r--structs.go229
1 files changed, 229 insertions, 0 deletions
diff --git a/structs.go b/structs.go
new file mode 100644
index 0000000..e899f78
--- /dev/null
+++ b/structs.go
@@ -0,0 +1,229 @@
+// LICENSE: same as the go language itself
+// Copyright 2023 WIT.COM
+
+// all structures and variables are local (aka lowercase)
+// since the plugin should be isolated to access only
+// by functions() to insure everything here is run
+// inside a dedicated goroutine
+
+package main
+
+import (
+ "fmt"
+ "github.com/awesome-gocui/gocui"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+
+ "go.wit.com/log"
+)
+
+// It's probably a terrible idea to call this 'me'
+var me config
+
+var showDebug bool = true
+var showHelp bool = true
+var redoWidgets bool = true
+
+// This is the window that is currently active
+var currentWindow *node
+
+type config struct {
+ baseGui *gocui.Gui // the main gocui handle
+ rootNode *node // the base of the binary tree. it should have id == 0
+
+ ctrlDown *node // shown if you click the mouse when the ctrl key is pressed
+ currentWindow *node // this is the current tab or window to show
+ logStdout *node // where to show STDOUT
+ helpLabel *gocui.View
+ ddview *node // the gocui view to select dropdrown lists
+ ddClicked bool // the dropdown menu view was clicked
+ ddNode *node // the dropdown menu is for this widget
+
+ /*
+ // this is the channel we send user events like
+ // mouse clicks or keyboard events back to the program
+ callback chan toolkit.Action
+
+ // this is the channel we get requests to make widgets
+ pluginChan chan toolkit.Action
+ */
+
+ // When the widget has a frame, like a button, it adds 2 lines runes on each side
+ // so you need 3 char spacing in each direction to not have them overlap
+ // the amount of padding when there is a frame
+ FramePadW int `default:"1" dense:"0"`
+ FramePadH int `default:"1" dense:"0"`
+
+ PadW int `default:"1" dense:"0"`
+ PadH int `default:"1" dense:"0"`
+
+ // how far down to start Window or Tab headings
+ WindowW int `default:"8" dense:"0"`
+ WindowH int `default:"-1"`
+ TabW int `default:"5" dense:"0"`
+ TabH int `default:"1" dense:"0"`
+
+ // additional amount of space to put between window & tab widgets
+ WindowPadW int `default:"8" dense:"0"`
+ TabPadW int `default:"4" dense:"0"`
+
+ // additional amount of space to indent on a group
+ GroupPadW int `default:"6" dense:"2"`
+
+ // the raw beginning of each window (or tab)
+ RawW int `default:"1"`
+ RawH int `default:"5"`
+
+ // offset for the hidden widgets
+ FakeW int `default:"20"`
+
+ padded bool // add space between things like buttons
+ bookshelf bool // do you want things arranged in the box like a bookshelf or a stack?
+ canvas bool // if set to true, the windows are a raw canvas
+ menubar bool // for windows
+ stretchy bool // expand things like buttons to the maximum size
+ margin bool // add space around the frames of windows
+
+ // writeMutex protects locks the write process
+ writeMutex sync.Mutex
+
+ // used for listWidgets() debugging
+ depth int
+}
+
+// deprecate these
+var (
+ initialMouseX, initialMouseY, xOffset, yOffset int
+ globalMouseDown, msgMouseDown, movingMsg bool
+)
+
+// this is the gocui way
+// corner starts at in the upper left corner
+type rectType struct {
+ w0, h0, w1, h1 int // left top right bottom
+}
+
+func (r *rectType) Width() int {
+ return r.w1 - r.w0
+}
+
+func (r *rectType) Height() int {
+ return r.h1 - r.h0
+}
+
+type guiWidget struct {
+ // the gocui package variables
+ v *gocui.View // this is nil if the widget is not displayed
+ cuiName string // what gocui uses to reference the widget
+
+ // the actual text to display in the console
+ label string
+
+ // the logical size of the widget
+ // For example, 40x12 would be the center of a normal terminal
+ // size rectType
+
+ // the actual gocui display view of this widget
+ // sometimes this isn't visible like with a Box or Grid
+ gocuiSize rectType
+
+ isCurrent bool // is this the currently displayed Window or Tab?
+ isFake bool // widget types like 'box' are 'false'
+
+ // used to track the size of grids
+ widths map[int]int // how tall each row in the grid is
+ heights map[int]int // how wide each column in the grid is
+
+ tainted bool
+ frame bool
+
+ // for a window, this is currently selected tab
+ selectedTab *node
+
+ // what color to use
+ color *colorT
+}
+
+// from the gocui devs:
+// Write appends a byte slice into the view's internal buffer. Because
+// View implements the io.Writer interface, it can be passed as parameter
+// of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must
+// be called to clear the view's buffer.
+
+func (w *guiWidget) Write(p []byte) (n int, err error) {
+ w.tainted = true
+ me.writeMutex.Lock()
+ defer me.writeMutex.Unlock()
+ if me.logStdout.tk.v == nil {
+ // optionally write the output to /tmp
+ s := fmt.Sprint(string(p))
+ s = strings.TrimSuffix(s, "\n")
+ fmt.Fprintln(outf, s)
+ v, _ := me.baseGui.View("msg")
+ if v != nil {
+ // fmt.Fprintln(outf, "found msg")
+ me.logStdout.tk.v = v
+ }
+ } else {
+ // display the output in the gocui window
+ me.logStdout.tk.v.Clear()
+
+ s := fmt.Sprint(string(p))
+ s = strings.TrimSuffix(s, "\n")
+ tmp := strings.Split(s, "\n")
+ outputS = append(outputS, tmp...)
+ if len(outputS) > outputH {
+ l := len(outputS) - outputH
+ outputS = outputS[l:]
+ }
+ fmt.Fprintln(me.logStdout.tk.v, strings.Join(outputS, "\n"))
+ }
+
+ return len(p), nil
+}
+
+func Set(ptr interface{}, tag string) error {
+ if reflect.TypeOf(ptr).Kind() != reflect.Ptr {
+ log.Log(ERROR, "Set() Not a pointer", ptr, "with tag =", tag)
+ return fmt.Errorf("Not a pointer")
+ }
+
+ v := reflect.ValueOf(ptr).Elem()
+ t := v.Type()
+
+ for i := 0; i < t.NumField(); i++ {
+ defaultVal := t.Field(i).Tag.Get(tag)
+ name := t.Field(i).Name
+ // log("Set() try name =", name, "defaultVal =", defaultVal)
+ setField(v.Field(i), defaultVal, name)
+ }
+ return nil
+}
+
+func setField(field reflect.Value, defaultVal string, name string) error {
+
+ if !field.CanSet() {
+ // log("setField() Can't set value", field, defaultVal)
+ return fmt.Errorf("Can't set value\n")
+ } else {
+ log.Log(NOW, "setField() Can set value", name, defaultVal)
+ }
+
+ switch field.Kind() {
+ case reflect.Int:
+ val, _ := strconv.Atoi(defaultVal)
+ field.Set(reflect.ValueOf(int(val)).Convert(field.Type()))
+ case reflect.String:
+ field.Set(reflect.ValueOf(defaultVal).Convert(field.Type()))
+ case reflect.Bool:
+ if defaultVal == "true" {
+ field.Set(reflect.ValueOf(true))
+ } else {
+ field.Set(reflect.ValueOf(false))
+ }
+ }
+
+ return nil
+}