summaryrefslogtreecommitdiff
path: root/structs.go
blob: 68c0c37cbadc83c5e5639c0aa9e06b4101f142b1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0

// 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"
	"reflect"
	"strconv"
	"strings"
	"sync"

	"github.com/awesome-gocui/gocui"

	log "go.wit.com/log"
	"go.wit.com/toolkits/tree"
)

// It's probably a terrible idea to call this 'me'
// 2025 note: doesn't seem terrible to call this 'me' anymore. notsure.
var me config

var showDebug bool = true   // todo: move this into config struct
var redoWidgets bool = true // todo: move this into config struct

// todo: move all this to a protobuf. then, redo all this mess. it got me here, but now it's time to clean it up for good
type config struct {
	baseGui       *gocui.Gui     // the main gocui handle
	treeRoot      *tree.Node     // the base of the binary tree. it should have id == 0
	myTree        *tree.TreeInfo // ?
	ctrlDown      *tree.Node     // shown if you click the mouse when the ctrl key is pressed
	currentWindow *guiWidget     // this is the current tab or window to show
	logStdout     *guiWidget     // where to show STDOUT
	startOutputW  int            // ?
	startOutputH  int            // ?
	helpLabel     *gocui.View    // ?
	dropdownV     *guiWidget     // this is a floating widget that we show whenever the user clicks on a
	dropdownW     *guiWidget     // grab the dropdown choices from this widget
	FramePadW     int            `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side
	FramePadH     int            `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side
	PadW          int            `default:"1" dense:"0"` // pad spacing
	PadH          int            `default:"1" dense:"0"` // pad spacing
	WindowW       int            `default:"8" dense:"0"` // how far down to start Window or Tab headings
	WindowH       int            `default:"-1"`          // how far down to start Window or Tab headings
	TabW          int            `default:"5" dense:"0"` // how far down to start Window or Tab headings
	TabH          int            `default:"1" dense:"0"` // how far down to start Window or Tab headings
	WindowPadW    int            `default:"8" dense:"0"` // additional amount of space to put between window & tab widgets
	TabPadW       int            `default:"4" dense:"0"` // additional amount of space to put between window & tab widgets
	GroupPadW     int            `default:"2" dense:"1"` // additional amount of space to indent on a group
	BoxPadW       int            `default:"2" dense:"1"` // additional amount of space to indent on a box
	GridPadW      int            `default:"2" dense:"1"` // additional amount of space to indent on a grid
	RawW          int            `default:"1"`           // the raw beginning of each window (or tab)
	RawH          int            `default:"5"`           // the raw beginning of each window (or tab)
	FakeW         int            `default:"20"`          // offset for the hidden widgets
	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    sync.Mutex     // writeMutex protects writes to *guiWidget (it's global right now maybe)
	fakefile      *FakeFile      // JUNK? used to attempt to write to the stdout window
	dtoggle       bool           // is a dropdown or combobox currently active?
	showHelp      bool           // toggle boolean for the help menu (deprecate?)
	ecount        int            // counts how many mouse and keyboard events have occurred
	supermouse    bool           // prints out every widget found while you move the mouse around
	depth         int            // used for listWidgets() debugging
}

// deprecate these
var (
	initialMouseX, initialMouseY, xOffset, yOffset int
	globalMouseDown, msgMouseDown                  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 {
	v            *gocui.View  // this is nil if the widget is not displayed
	cuiName      string       // what gocui uses to reference the widget (usually "TK <widgetId>"
	parent       *guiWidget   // mirrors the binary node tree
	children     []*guiWidget // mirrors the binary node tree
	node         *tree.Node   // the pointer back to the tree
	hasTabs      bool         // does the window have tabs?
	currentTab   bool         // the visible tab
	value        string       // ?
	checked      bool         // ?
	labelN       string       // the actual text to display in the console
	vals         []string     // dropdown menu items
	active       bool         // ?
	enable       bool         // ?
	defaultColor *colorT      // store the color to go back to
	gocuiSize    rectType     // should mirror the real display size. todo: verify these are accurate. they are not yet
	startW       int          // ?
	startH       int          // ?
	isCurrent    bool         // is this the currently displayed Window or Tab?
	isFake       bool         // widget types like 'box' are 'false'
	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         // ?
	selectedTab  *tree.Node   // for a window, this is currently selected tab
	color        *colorT      // what color to use
}

// 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()

	tk := me.logStdout
	if 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")
			tk.v = v
		}
	} else {
		// display the output in the gocui window
		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(tk.v, strings.Join(outputS, "\n"))
	}

	return len(p), nil
}

// THIS IS GO COMPILER MAGIC
// this sets the `default` in the structs above on init()
// this is cool code. thank the GO devs for this code and Alex Flint for explaining it to me
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
}