summaryrefslogtreecommitdiff
path: root/structs.go
blob: b36bde9060de99c2fe5e06149395cd5dc5f4e162 (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
209
210
211
212
213
// 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
	globalMouseDown bool       // yep, mouse is pressed
}

// deprecate these
var (
	initialMouseX, initialMouseY, xOffset, yOffset int
	// 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
	full         rectType     // full size of children (used by widget.Window, etc)
	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()

	// _, outputH := w.Size()
	outputH := 33 // special output length for "msg" window until I figure things out

	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
}