Compare commits
281 Commits
Author | SHA1 | Date |
---|---|---|
|
3acf473792 | |
|
6c3149c0fe | |
|
784eac740f | |
|
a88937c508 | |
|
96eac58cf5 | |
|
b8781b1b64 | |
|
126b9a30a2 | |
|
994449b1c2 | |
|
5ebb13a454 | |
|
6127fa1cbb | |
|
54bbe72aa8 | |
|
b373eab346 | |
|
e73cfaf490 | |
|
046e6b4d6c | |
|
efb5ede4b9 | |
|
5e6d7cffdf | |
|
76e15fa1df | |
|
75014f4b28 | |
|
9ef16c1bf2 | |
|
da54c0f039 | |
|
6d1dfed3db | |
|
65cf744a86 | |
|
caf7428ba3 | |
|
948d2af071 | |
|
ddd7709182 | |
|
9912c3eb82 | |
|
660d9e7e3a | |
|
0124d25c34 | |
|
ed3789c23f | |
|
6f739933b7 | |
|
4b79e862a7 | |
|
dc329ed18c | |
|
3e7287baea | |
|
04406b3561 | |
|
0638183356 | |
|
f24c509859 | |
|
a000c06987 | |
|
3b9e5beff6 | |
|
ad34230d67 | |
|
57b6efd831 | |
|
f36f9cfd0f | |
|
a5800917e8 | |
|
c64592f326 | |
|
377b08eeb6 | |
|
22e139e2e5 | |
|
6b7fafbde2 | |
|
3f09b2b6e4 | |
|
31324ad083 | |
|
ad299911f1 | |
|
3d1bb9680a | |
|
9cd1d582e2 | |
|
3ca1fff755 | |
|
5a9f3565aa | |
|
1f33979af9 | |
|
1e79d31a02 | |
|
08a5b198c7 | |
|
c215e3c2c4 | |
|
c5472a42a2 | |
|
fa9ec36dbb | |
|
8c459da9f7 | |
|
552bdeb1e6 | |
|
0a60272440 | |
|
a575a08bcc | |
|
57e5ff22dc | |
|
733c595c54 | |
|
535646335a | |
|
8d24366492 | |
|
6df064282c | |
|
6ea6ffaa3d | |
|
7d793c68db | |
|
b07d8bd8f7 | |
|
42758e1459 | |
|
f30489219b | |
|
bb2732b621 | |
|
00d1256eba | |
|
cf073e9aae | |
|
90a9f84f10 | |
|
c5cada3dc9 | |
|
87d31a3d94 | |
|
c5d9522c0b | |
|
4a009f79a2 | |
|
70452bdaac | |
|
36745e0492 | |
|
9540c01d83 | |
|
58eff2a9e2 | |
|
c00084bf3f | |
|
9a08b37be4 | |
|
955afcb1a9 | |
|
af3fec6f20 | |
|
5bac0308e5 | |
|
8d8fc22745 | |
|
eba5ea8cc0 | |
|
b8b8a409ea | |
|
010bd2a33f | |
|
ba629f1892 | |
|
bff0943dc5 | |
|
2c07da350a | |
|
82ed687460 | |
|
5a84456c7a | |
|
f8b7c603a1 | |
|
419f4aef6a | |
|
12d0e185cc | |
|
e80827d890 | |
|
42eafb87c7 | |
|
ea544e429e | |
|
58cb7f3d2d | |
|
53eb14ccbd | |
|
23dfd96a87 | |
|
5827b9ace2 | |
|
c4f9bac85e | |
|
90083d5bcb | |
|
77b4bcb94b | |
|
665d2289e2 | |
|
ed024aac70 | |
|
6045a205fd | |
|
227419c1ad | |
|
7b06b81ed3 | |
|
9d1a045a1f | |
|
1923f8df96 | |
|
b730ee9459 | |
|
a6c1864f43 | |
|
1010db44a6 | |
|
078a23e0e0 | |
|
44264df09d | |
|
0aa82f5ba5 | |
|
83b4d7142a | |
|
fefa99920b | |
|
8185d8bc1a | |
|
481fa11211 | |
|
a295aa420b | |
|
d8353f9b1a | |
|
b6b5df6a18 | |
|
13a194dca5 | |
|
6c522a4b27 | |
|
dd5232fa6b | |
|
6ac82df949 | |
|
0b67b198bd | |
|
7813fc126d | |
|
fb3c16707d | |
|
5668e6f081 | |
|
e96cb4375c | |
|
c2f8cac4a9 | |
|
7e47ca9843 | |
|
37723c2b9a | |
|
16886945ed | |
|
3d104d5b4a | |
|
0b265d2b72 | |
|
5d1a3b2578 | |
|
e39bcafb78 | |
|
176831d0f3 | |
|
2a5734892a | |
|
f5d465901d | |
|
e134ecb6a4 | |
|
5bae8b7e41 | |
|
93e87a05c7 | |
|
bc15e6c879 | |
|
1918dcbbde | |
|
5675307497 | |
|
88f33afbb7 | |
|
9fa974f6c4 | |
|
c4095ef7aa | |
|
c136ca2b4c | |
|
87141b8d99 | |
|
6d991fef63 | |
|
9c7b139e5a | |
|
d3b25092f8 | |
|
96cb52f3ef | |
|
9c548faeda | |
|
d2c3db7b58 | |
|
d0e35bb98f | |
|
8522d4671e | |
|
88e9594b93 | |
|
3faacd6c43 | |
|
31c130045d | |
|
70a742c98a | |
|
546a4e022b | |
|
efebe00640 | |
|
07f6b7842e | |
|
83e9787e75 | |
|
85eda6aeb8 | |
|
8dd0f88e7c | |
|
c328a755c6 | |
|
3fa508f786 | |
|
d75bfa639c | |
|
f1eefc9a06 | |
|
6a0fd773f4 | |
|
ec68f448af | |
|
ae339cc587 | |
|
38a08d66fc | |
|
fb8d1d0940 | |
|
12f3d5ac5c | |
|
a81e931b9a | |
|
6237bf89c3 | |
|
8ce9ca882a | |
|
bf8cbddf1a | |
|
4e5c6f2515 | |
|
3cf873439d | |
|
652cf2b73b | |
|
1867bae62c | |
|
012273d8d3 | |
|
9d5cd2c865 | |
|
acfb80a2e7 | |
|
a10582c846 | |
|
5a28806bdf | |
|
d2c681f573 | |
|
acb0e43945 | |
|
65622d01cd | |
|
d61e03b877 | |
|
f0d403e834 | |
|
e627e4e822 | |
|
c91733e10d | |
|
6370d87fc2 | |
|
c5e6c66338 | |
|
a5b3a934d2 | |
|
517d844b3c | |
|
4a8fb6ab22 | |
|
ac9c6617e3 | |
|
d5d0262013 | |
|
3125bbb258 | |
|
a069e6c984 | |
|
d6f2fd983e | |
|
e3c874cd69 | |
|
eccec3ef1a | |
|
666d5ca52d | |
|
0355f7c2fb | |
|
f79cf89170 | |
|
5a2097d080 | |
|
9a3f9d0991 | |
|
2062060dac | |
|
7557486b13 | |
|
4dad234532 | |
|
3d5ee3f89b | |
|
2e0465e44a | |
|
49f8e1c043 | |
|
4bc9fa41b7 | |
|
96fdfd1338 | |
|
8d007ec10d | |
|
fdca4d2601 | |
|
417b3e5225 | |
|
e4f0524bdf | |
|
ebb03139bb | |
|
4695ada409 | |
|
a78cd82dcd | |
|
a8a918655a | |
|
39e851c76c | |
|
bac14a675b | |
|
b7cd6d07fc | |
|
f76960c907 | |
|
5b39848b64 | |
|
bbdf7fefbd | |
|
73de9899a8 | |
|
c348940ca1 | |
|
8a4afa760d | |
|
4aef276b64 | |
|
1d32c5da2b | |
|
9f38585892 | |
|
1a1881aa4e | |
|
75a5c7bf72 | |
|
11465e4043 | |
|
57fbbc62ed | |
|
ce11f999f9 | |
|
a16b53c289 | |
|
b5fe1eb8ff | |
|
85f3a08238 | |
|
439c6e3b01 | |
|
19370790f8 | |
|
bab555aa4c | |
|
7e2d7f72ef | |
|
39eb2e5210 | |
|
bb3802857b | |
|
c02399708e | |
|
aea18d5b65 | |
|
820f27c0a2 | |
|
15666309ee | |
|
aae13f2b57 | |
|
2461df0153 | |
|
368c25107a | |
|
2ace17294c | |
|
6d3dded68b | |
|
32619e7dad | |
|
2ea283ea74 |
|
@ -1,4 +1,7 @@
|
||||||
*.swp
|
*.swp
|
||||||
|
*.so
|
||||||
|
*.pb.go
|
||||||
|
*.patch
|
||||||
go.mod
|
go.mod
|
||||||
go.sum
|
go.sum
|
||||||
gocui
|
gocui
|
||||||
|
|
36
Makefile
36
Makefile
|
@ -1,11 +1,31 @@
|
||||||
all: plugin
|
VERSION = $(shell git describe --tags)
|
||||||
ldd ../gocui.so
|
BUILDTIME = $(shell date +%Y.%m.%d)
|
||||||
|
|
||||||
plugin:
|
all: clean goimports vet gocui
|
||||||
GO111MODULE=off go build -v -buildmode=plugin -o ../gocui.so
|
@ldd gocui.so
|
||||||
|
|
||||||
pluginreal:
|
vet:
|
||||||
go build -v -buildmode=plugin -o ~/go/lib/toolkits/gocui.so
|
@GO111MODULE=off go vet
|
||||||
|
@echo this go plugin builds okay
|
||||||
|
|
||||||
|
gocui:
|
||||||
|
GO111MODULE=off go build -v -x -buildmode=plugin -o gocui.so \
|
||||||
|
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
||||||
|
|
||||||
|
install:
|
||||||
|
go build -buildmode=plugin -o ~/go/lib/gocui-${VERSION}.so \
|
||||||
|
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
|
||||||
|
cd ~/go/lib && ln -f -s gocui-${VERSION}.so gocui.so
|
||||||
|
|
||||||
|
# for testing custom golang
|
||||||
|
custom:
|
||||||
|
# GO111MODULE=off go build -v
|
||||||
|
GO111MODULE=off go build -v -work -buildmode=blah
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f gocui *.so go.*
|
||||||
|
rm -f *.pb.go *.patch
|
||||||
|
go-mod-clean --purge
|
||||||
|
|
||||||
# Test the README.md & doc.go file
|
# Test the README.md & doc.go file
|
||||||
# this runs pkgsite, the binary that does dev.go.dev
|
# this runs pkgsite, the binary that does dev.go.dev
|
||||||
|
@ -27,3 +47,7 @@ redomod:
|
||||||
rm -f go.*
|
rm -f go.*
|
||||||
GO111MODULE= go mod init
|
GO111MODULE= go mod init
|
||||||
GO111MODULE= go mod tidy
|
GO111MODULE= go mod tidy
|
||||||
|
|
||||||
|
proto:
|
||||||
|
autogenpb --proto gocuiView.proto
|
||||||
|
make goimports
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
# a console toolkit around gocui
|
# a console toolkit around gocui
|
||||||
|
|
||||||
|
* to build, run 'make build'
|
||||||
|
* TODO: make a way to trigger plugin builds. 'package plugin' maybe?
|
||||||
|
|
||||||
[terminals that support ture color](https://github.com/termstandard/colors#truecolor-support-in-output-devices)
|
[terminals that support ture color](https://github.com/termstandard/colors#truecolor-support-in-output-devices)
|
||||||
|
[more info about color](https://jvns.ca/blog/2024/10/01/terminal-colours/)
|
||||||
|
|
94
add.go
94
add.go
|
@ -1,94 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
log "go.wit.com/log"
|
|
||||||
"go.wit.com/toolkits/tree"
|
|
||||||
"go.wit.com/widget"
|
|
||||||
)
|
|
||||||
|
|
||||||
var fakeStartWidth int = me.FakeW
|
|
||||||
var fakeStartHeight int = me.TabH + me.FramePadH
|
|
||||||
|
|
||||||
// setup fake labels for non-visible things off screen
|
|
||||||
func setFake(n *tree.Node) {
|
|
||||||
var w *guiWidget
|
|
||||||
w = n.TK.(*guiWidget)
|
|
||||||
w.isFake = true
|
|
||||||
|
|
||||||
w.gocuiSetWH(fakeStartWidth, fakeStartHeight)
|
|
||||||
|
|
||||||
fakeStartHeight += w.gocuiSize.Height()
|
|
||||||
// TODO: use the actual max hight of the terminal window
|
|
||||||
if fakeStartHeight > 24 {
|
|
||||||
fakeStartHeight = me.TabH
|
|
||||||
fakeStartWidth += me.FakeW
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the widget start width & height
|
|
||||||
// func (n *node) addWidget(n *tree.Node) {
|
|
||||||
func addWidget(n *tree.Node) {
|
|
||||||
var nw *guiWidget
|
|
||||||
nw = n.TK.(*guiWidget)
|
|
||||||
log.Log(INFO, "setStartWH() w.id =", n.WidgetId, "n.name", n.String())
|
|
||||||
switch n.WidgetType {
|
|
||||||
case widget.Root:
|
|
||||||
log.Log(INFO, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.String())
|
|
||||||
nw.color = &colorRoot
|
|
||||||
setFake(n)
|
|
||||||
return
|
|
||||||
case widget.Flag:
|
|
||||||
nw.color = &colorFlag
|
|
||||||
setFake(n)
|
|
||||||
return
|
|
||||||
case widget.Window:
|
|
||||||
nw.frame = false
|
|
||||||
// nw.color = &colorWindow
|
|
||||||
nw.setColor(&colorWindow)
|
|
||||||
wRoot := me.treeRoot.TK.(*guiWidget)
|
|
||||||
wRoot.redoWindows(0, 0)
|
|
||||||
// TODO: record the first window here?
|
|
||||||
// do initial setup of helper widgets here:
|
|
||||||
if me.dropdownV == nil {
|
|
||||||
me.dropdownV = makeDropdownView("addWidget() ddview")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
case widget.Tab:
|
|
||||||
nw.color = &colorTab
|
|
||||||
// redoWindows(0,0)
|
|
||||||
return
|
|
||||||
case widget.Button:
|
|
||||||
nw.color = &colorButton
|
|
||||||
case widget.Checkbox:
|
|
||||||
nw.color = &colorCheckbox
|
|
||||||
case widget.Dropdown:
|
|
||||||
nw.color = &colorDropdown
|
|
||||||
case widget.Combobox:
|
|
||||||
nw.color = &colorCombobox
|
|
||||||
case widget.Box:
|
|
||||||
nw.color = &colorBox
|
|
||||||
nw.isFake = true
|
|
||||||
setFake(n)
|
|
||||||
return
|
|
||||||
case widget.Grid:
|
|
||||||
nw.color = &colorGrid
|
|
||||||
nw.isFake = true
|
|
||||||
setFake(n)
|
|
||||||
return
|
|
||||||
case widget.Group:
|
|
||||||
nw.color = &colorGroup
|
|
||||||
nw.frame = false
|
|
||||||
return
|
|
||||||
case widget.Label:
|
|
||||||
nw.color = &colorLabel
|
|
||||||
nw.frame = false
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
/*
|
|
||||||
if n.IsCurrent() {
|
|
||||||
n.updateCurrent()
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
nw.showWidgetPlacement("addWidget()")
|
|
||||||
}
|
|
51
checkbox.go
51
checkbox.go
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -6,48 +9,32 @@ import (
|
||||||
"go.wit.com/widget"
|
"go.wit.com/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// this comes from the application
|
||||||
func setChecked(n *tree.Node, b bool) {
|
func setChecked(n *tree.Node, b bool) {
|
||||||
if n.WidgetType != widget.Checkbox {
|
if n.WidgetType != widget.Checkbox {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.State.Checked = b
|
||||||
var tk *guiWidget
|
var tk *guiWidget
|
||||||
tk = n.TK.(*guiWidget)
|
tk = n.TK.(*guiWidget)
|
||||||
|
|
||||||
if tk.node.State.Label == "" {
|
tk.setCheckbox()
|
||||||
tk.node.State.Label = "BLANK"
|
}
|
||||||
|
|
||||||
|
// redraw the checkbox
|
||||||
|
func (tk *guiWidget) setCheckbox() {
|
||||||
|
if tk.WidgetType() != widget.Checkbox {
|
||||||
|
log.Log(WARN, "setCheckbox() being run on widget:", tk.WidgetType())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if tk.node.State.Checked {
|
if tk.Checked() {
|
||||||
log.Log(WARN, "setCheckbox() got true", tk.node.State.Checked)
|
log.Log(WARN, "setCheckbox() got true", tk.Checked())
|
||||||
tk.labelN = "X " + tk.node.State.Label
|
tk.labelN = "X " + tk.GetLabel()
|
||||||
} else {
|
} else {
|
||||||
log.Log(WARN, "setCheckbox() got false", tk.node.State.Checked)
|
log.Log(WARN, "setCheckbox() got false", tk.Checked())
|
||||||
tk.labelN = " " + tk.node.State.Label
|
tk.labelN = "_ " + tk.GetLabel()
|
||||||
}
|
}
|
||||||
|
|
||||||
tk.Hide()
|
tk.Hide()
|
||||||
tk.Show()
|
tk.Show()
|
||||||
}
|
}
|
||||||
|
|
||||||
// redraw the checkbox
|
|
||||||
func (w *guiWidget) setCheckbox() {
|
|
||||||
if w.node.WidgetType != widget.Checkbox {
|
|
||||||
log.Log(WARN, "setCheckbox() being run on widget:", w.node.WidgetType)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if w.node.State.Label == "" {
|
|
||||||
w.node.State.Label = "BLANK"
|
|
||||||
}
|
|
||||||
if w.node.State.Checked {
|
|
||||||
log.Log(WARN, "setCheckbox() got true", w.node.State.Checked)
|
|
||||||
w.labelN = "X " + w.node.State.Label
|
|
||||||
// w.changed = true
|
|
||||||
} else {
|
|
||||||
log.Log(WARN, "setCheckbox() got false", w.node.State.Checked)
|
|
||||||
w.labelN = " " + w.node.State.Label
|
|
||||||
// w.changed = true
|
|
||||||
}
|
|
||||||
// t := len(w.labelN) + 3
|
|
||||||
// w.gocuiSize.w1 = w.gocuiSize.w0 + t
|
|
||||||
|
|
||||||
w.Hide()
|
|
||||||
w.Show()
|
|
||||||
}
|
|
||||||
|
|
229
click.go
229
click.go
|
@ -1,229 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
|
||||||
"go.wit.com/log"
|
|
||||||
"go.wit.com/widget"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (w *guiWidget) doWidgetClick() {
|
|
||||||
switch w.WidgetType {
|
|
||||||
/*
|
|
||||||
case widget.Root:
|
|
||||||
// THIS IS THE BEGINING OF THE LAYOUT
|
|
||||||
log.Log(GOCUI, "doWidgetClick()", w.String())
|
|
||||||
wRoot := me.treeRoot.TK.(*guiWidget)
|
|
||||||
wRoot.redoWindows(0, 0)
|
|
||||||
case widget.Flag:
|
|
||||||
log.Log(GOCUI, "doWidgetClick() FLAG widget name =", w.String())
|
|
||||||
log.Log(GOCUI, "doWidgetClick() if this is the dropdown menu, handle it here?")
|
|
||||||
*/
|
|
||||||
case widget.Window:
|
|
||||||
log.Log(GOCUI, "doWidgetClick() START on window", w.String())
|
|
||||||
// if the user clicked on the current window, do nothing
|
|
||||||
/* Ignore this for now and redraw the window anyway
|
|
||||||
if me.currentWindow == w {
|
|
||||||
if !w.active {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// if there is a current window, hide it
|
|
||||||
if me.currentWindow != nil {
|
|
||||||
me.currentWindow.setColor(&colorWindow)
|
|
||||||
me.currentWindow.hideWidgets()
|
|
||||||
me.currentWindow.isCurrent = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// now set this window as the current window
|
|
||||||
me.currentWindow = w
|
|
||||||
me.currentWindow.isCurrent = true
|
|
||||||
|
|
||||||
// draw the current window
|
|
||||||
log.Log(GOCUI, "doWidgetClick() set currentWindow to", w.String())
|
|
||||||
w.setColor(&colorActiveW)
|
|
||||||
w.DrawAt(3, 2)
|
|
||||||
w.placeWidgets(3, 2) // compute the sizes & places for each widget
|
|
||||||
w.active = false
|
|
||||||
w.showWidgets()
|
|
||||||
/*
|
|
||||||
hideFake()
|
|
||||||
showDebug = true
|
|
||||||
*/
|
|
||||||
case widget.Group:
|
|
||||||
if w.active {
|
|
||||||
w.active = false
|
|
||||||
w.placeWidgets(w.startW, w.startH)
|
|
||||||
w.showWidgets()
|
|
||||||
} else {
|
|
||||||
w.active = true
|
|
||||||
for _, child := range w.children {
|
|
||||||
child.hideWidgets()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// w.dumpTree("click end")
|
|
||||||
case widget.Checkbox:
|
|
||||||
if w.node.State.Checked {
|
|
||||||
log.Log(WARN, "checkbox is being set to false")
|
|
||||||
w.node.State.Checked = false
|
|
||||||
w.setCheckbox()
|
|
||||||
} else {
|
|
||||||
log.Log(WARN, "checkbox is being set to true")
|
|
||||||
w.node.State.Checked = true
|
|
||||||
w.setCheckbox()
|
|
||||||
}
|
|
||||||
me.myTree.SendUserEvent(w.node)
|
|
||||||
case widget.Grid:
|
|
||||||
newR := w.realGocuiSize()
|
|
||||||
|
|
||||||
// w,h := n.logicalSize()
|
|
||||||
// w := newR.w1 - newR.w0
|
|
||||||
// h := newR.h1 - newR.h0
|
|
||||||
|
|
||||||
w.placeGrid(newR.w0, newR.h0)
|
|
||||||
w.showWidgets()
|
|
||||||
case widget.Box:
|
|
||||||
// w.showWidgetPlacement(logNow, "drawTree()")
|
|
||||||
if w.node.State.Direction == widget.Horizontal {
|
|
||||||
log.Log(GOCUI, "BOX IS HORIZONTAL", w.String())
|
|
||||||
} else {
|
|
||||||
log.Log(GOCUI, "BOX IS VERTICAL", w.String())
|
|
||||||
}
|
|
||||||
w.placeWidgets(w.startW, w.startH)
|
|
||||||
w.toggleTree()
|
|
||||||
case widget.Button:
|
|
||||||
// doUserEvent(n)
|
|
||||||
me.myTree.SendFromUser(w.node)
|
|
||||||
case widget.Combobox:
|
|
||||||
log.Log(GOCUI, "do the combobox here")
|
|
||||||
w.showDropdown()
|
|
||||||
me.dropdownW = w
|
|
||||||
case widget.Dropdown:
|
|
||||||
log.Log(GOCUI, "do the dropdown here")
|
|
||||||
w.showDropdown()
|
|
||||||
me.dropdownW = w
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func click(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
mouseW, mouseH := me.baseGui.MousePosition()
|
|
||||||
|
|
||||||
log.Log(GOCUI, "click() START gocui name:", v.Name())
|
|
||||||
w := findUnderMouse()
|
|
||||||
|
|
||||||
// if the dropdown view is visable, process it no matter what
|
|
||||||
if me.dropdownV.Visible() {
|
|
||||||
me.dropdownV.dropdownClicked(mouseW, mouseH)
|
|
||||||
}
|
|
||||||
if w == me.dropdownV {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if w == nil {
|
|
||||||
log.Error(errors.New("click() could not find widget for view =" + v.Name()))
|
|
||||||
} else {
|
|
||||||
log.Log(GOCUI, "click() Found widget =", w.node.WidgetId, w.String(), ",", w.labelN)
|
|
||||||
w.doWidgetClick()
|
|
||||||
}
|
|
||||||
|
|
||||||
rootTK := me.treeRoot.TK.(*guiWidget)
|
|
||||||
realTK := rootTK.findWidgetByView(v)
|
|
||||||
if realTK == nil {
|
|
||||||
log.Error(errors.New("toolkit click() out of reality with gocui. v.Name() not in binary tree " + v.Name()))
|
|
||||||
log.Log(GOCUI, "click() END FAILURE ON gocui v.Name =", v.Name())
|
|
||||||
// return nil // otherwise gocui exits
|
|
||||||
}
|
|
||||||
|
|
||||||
// double check the widget view really still exists
|
|
||||||
nameTK := rootTK.findWidgetByName(v.Name())
|
|
||||||
if nameTK == nil {
|
|
||||||
log.Error(errors.New("toolkit click() out of reality with gocui. v.Name() not in binary tree " + v.Name()))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if nameTK.v == nil {
|
|
||||||
log.Log(GOCUI, "click() maybe this widget has had it's view distroyed?", nameTK.cuiName, nameTK.WidgetType)
|
|
||||||
log.Log(GOCUI, "yep. it's gone now")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetCurrentView dies if it's sent an non-existent view
|
|
||||||
if _, err := g.SetCurrentView(v.Name()); err != nil {
|
|
||||||
log.Log(GOCUI, "click() END v.Name =", v.Name(), "err =", err)
|
|
||||||
// return err // return causes gocui.MainLoop() to exit. Do we ever want that to happen here?
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Log(GOCUI, "click() END gocui name:", v.Name())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func findUnderMouse() *guiWidget {
|
|
||||||
var widgets []*guiWidget
|
|
||||||
var f func(w *guiWidget)
|
|
||||||
w, h := me.baseGui.MousePosition()
|
|
||||||
|
|
||||||
// find buttons that are below where the mouse button click
|
|
||||||
f = func(widget *guiWidget) {
|
|
||||||
// ignore widgets that are not visible
|
|
||||||
if widget.Visible() {
|
|
||||||
if (widget.gocuiSize.w0 <= w) && (w <= widget.gocuiSize.w1) &&
|
|
||||||
(widget.gocuiSize.h0 <= h) && (h <= widget.gocuiSize.h1) {
|
|
||||||
widgets = append(widgets, widget)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, child := range widget.children {
|
|
||||||
f(child)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rootW := me.treeRoot.TK.(*guiWidget)
|
|
||||||
f(rootW)
|
|
||||||
|
|
||||||
var found *guiWidget
|
|
||||||
// widgets has everything that matches
|
|
||||||
for _, w := range widgets {
|
|
||||||
// prioritize window buttons. This means if some code covers
|
|
||||||
// up the window widgets, then it will ignore everything else
|
|
||||||
// and allow the user (hopefully) to redraw or switch windows
|
|
||||||
// TODO: display the window widgets on top
|
|
||||||
if w.WidgetType == widget.Window {
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
// w.showWidgetPlacement("findUnderMouse() FOUND")
|
|
||||||
found = w
|
|
||||||
}
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the widget under the mouse click
|
|
||||||
func ctrlDown(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
var found *guiWidget
|
|
||||||
// var widgets []*node
|
|
||||||
// var f func (n *node)
|
|
||||||
found = findUnderMouse()
|
|
||||||
if me.ctrlDown == nil {
|
|
||||||
setupCtrlDownWidget()
|
|
||||||
|
|
||||||
var tk *guiWidget
|
|
||||||
tk = me.ctrlDown.TK.(*guiWidget)
|
|
||||||
tk.labelN = found.String()
|
|
||||||
tk.cuiName = "ctrlDown"
|
|
||||||
// me.ctrlDown.parent = me.rootNode
|
|
||||||
}
|
|
||||||
var tk *guiWidget
|
|
||||||
tk = me.ctrlDown.TK.(*guiWidget)
|
|
||||||
if found == nil {
|
|
||||||
found = me.treeRoot.TK.(*guiWidget)
|
|
||||||
}
|
|
||||||
tk.labelN = found.String()
|
|
||||||
newR := found.realGocuiSize()
|
|
||||||
tk.gocuiSize.w0 = newR.w0
|
|
||||||
tk.gocuiSize.h0 = newR.h0
|
|
||||||
tk.gocuiSize.w1 = newR.w1
|
|
||||||
tk.gocuiSize.h1 = newR.h1
|
|
||||||
return nil
|
|
||||||
}
|
|
466
color.go
466
color.go
|
@ -1,181 +1,309 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
"github.com/awesome-gocui/gocui"
|
||||||
|
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
//w.v.SelBgColor = gocui.ColorCyan
|
// simple colors for light and dark
|
||||||
//color.go: w.v.SelFgColor = gocui.ColorBlack
|
|
||||||
//color.go: w.v.BgColor = gocui.ColorGreen
|
|
||||||
|
|
||||||
type colorT struct {
|
// information about how terminfo works
|
||||||
frame gocui.Attribute
|
// https://jvns.ca/blog/2024/10/01/terminal-colours/
|
||||||
fg gocui.Attribute
|
|
||||||
bg gocui.Attribute
|
|
||||||
selFg gocui.Attribute
|
|
||||||
selBg gocui.Attribute
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
var none gocui.Attribute = gocui.AttrNone
|
// TODO: move all this to a protobuf
|
||||||
var lightPurple gocui.Attribute = gocui.GetColor("#DDDDDD") // light purple
|
|
||||||
var darkPurple gocui.Attribute = gocui.GetColor("#FFAA55") // Dark Purple
|
|
||||||
var heavyPurple gocui.Attribute = gocui.GetColor("#88AA55") // heavy purple
|
|
||||||
var powdererBlue gocui.Attribute = gocui.GetColor("#B0E0E6") // w3c 'powerder blue'
|
|
||||||
var superLightGrey gocui.Attribute = gocui.GetColor("#55AAFF") // super light grey
|
|
||||||
|
|
||||||
// Standard defined colors from gocui:
|
|
||||||
// ColorBlack ColorRed ColorGreen ColorYellow ColorBlue ColorMagenta ColorCyan ColorWhite
|
|
||||||
|
|
||||||
// v.BgColor = gocui.GetColor("#111111") // crazy red
|
|
||||||
// v.BgColor = gocui.GetColor("#FF9911") // heavy red
|
|
||||||
// v.SelBgColor = gocui.GetColor("#FFEE11") // blood red
|
|
||||||
|
|
||||||
// v.BgColor = gocui.GetColor("#55AAFF") // super light grey
|
|
||||||
// v.BgColor = gocui.GetColor("#FFC0CB") // 'w3c pink' yellow
|
|
||||||
|
|
||||||
// Normal Text On mouseover
|
|
||||||
//
|
|
||||||
// Widget Frame Text background Text background
|
|
||||||
var colorWindow colorT = colorT{
|
|
||||||
frame: none,
|
|
||||||
fg: gocui.ColorBlue,
|
|
||||||
bg: none,
|
|
||||||
selFg: none,
|
|
||||||
selBg: powdererBlue,
|
|
||||||
name: "normal window",
|
|
||||||
}
|
|
||||||
var colorActiveW colorT = colorT{none, none, powdererBlue, none, powdererBlue, "active window"}
|
|
||||||
|
|
||||||
var colorTab colorT = colorT{gocui.ColorBlue, gocui.ColorBlue, none, none, powdererBlue, "normal tab"}
|
|
||||||
var colorActiveT colorT = colorT{gocui.ColorBlue, none, powdererBlue, none, powdererBlue, "active tab"}
|
|
||||||
|
|
||||||
var colorLabel colorT = colorT{none, none, superLightGrey, none, superLightGrey, "normal label"}
|
|
||||||
var colorGroup colorT = colorT{none, none, superLightGrey, none, superLightGrey, "normal group"}
|
|
||||||
|
|
||||||
var colorDisabled colorT = colorT{
|
|
||||||
frame: superLightGrey,
|
|
||||||
fg: superLightGrey,
|
|
||||||
bg: superLightGrey,
|
|
||||||
selFg: gocui.ColorBlack,
|
|
||||||
selBg: gocui.ColorBlack,
|
|
||||||
name: "disabled widget",
|
|
||||||
}
|
|
||||||
|
|
||||||
var colorButton colorT = colorT{
|
|
||||||
frame: gocui.ColorGreen,
|
|
||||||
fg: none,
|
|
||||||
bg: gocui.ColorWhite,
|
|
||||||
selFg: gocui.ColorGreen,
|
|
||||||
selBg: gocui.ColorBlack,
|
|
||||||
name: "normal button",
|
|
||||||
}
|
|
||||||
|
|
||||||
var colorDropdown colorT = colorT{
|
|
||||||
frame: gocui.ColorYellow,
|
|
||||||
fg: none,
|
|
||||||
bg: gocui.ColorWhite,
|
|
||||||
selFg: gocui.ColorYellow,
|
|
||||||
selBg: gocui.ColorBlack,
|
|
||||||
name: "normal button",
|
|
||||||
}
|
|
||||||
|
|
||||||
var colorCombobox colorT = colorT{
|
|
||||||
frame: gocui.ColorBlue,
|
|
||||||
fg: none,
|
|
||||||
bg: gocui.ColorWhite,
|
|
||||||
selFg: gocui.ColorBlue,
|
|
||||||
selBg: gocui.ColorBlack,
|
|
||||||
name: "normal button",
|
|
||||||
}
|
|
||||||
|
|
||||||
var colorCheckbox colorT = colorT{
|
|
||||||
frame: gocui.ColorRed,
|
|
||||||
fg: none,
|
|
||||||
bg: gocui.ColorWhite,
|
|
||||||
selFg: gocui.ColorRed,
|
|
||||||
selBg: gocui.ColorBlack,
|
|
||||||
name: "normal checkbox",
|
|
||||||
}
|
|
||||||
|
|
||||||
// widget debugging colors. these widgets aren't displayed unless you are debugging
|
|
||||||
var colorRoot colorT = colorT{gocui.ColorRed, none, powdererBlue, none, gocui.ColorBlue, "debug root"}
|
|
||||||
var colorFlag colorT = colorT{gocui.ColorRed, none, powdererBlue, none, gocui.ColorGreen, "debug flag"}
|
|
||||||
var colorBox colorT = colorT{gocui.ColorRed, none, lightPurple, none, gocui.ColorCyan, "debug box"}
|
|
||||||
var colorGrid colorT = colorT{gocui.ColorRed, none, lightPurple, none, gocui.ColorRed, "debug grid"}
|
|
||||||
var colorNone colorT = colorT{none, none, none, none, none, "debug none"}
|
|
||||||
|
|
||||||
// actually sets the colors for the gocui element
|
|
||||||
// the user will see the colors change when this runs
|
|
||||||
// TODO: add black/white only flag for ttyS0
|
// TODO: add black/white only flag for ttyS0
|
||||||
// TODO: or fix kvm/qemu serial console & SIGWINCH.
|
// TODO: fix kvm/qemu serial console & SIGWINCH.
|
||||||
// TODO: and minicom and uboot and 5 million other things.
|
// TODO: check minicom (doesn't work)
|
||||||
// TODO: maybe enough of us could actually do that if we made it a goal.
|
// TODO: fix riscv boards
|
||||||
// TODO: start with riscv boards and fix it universally there
|
|
||||||
// TODO: so just a small little 'todo' item here
|
// DONE ON ENABLE() WIDGET
|
||||||
func (tk *guiWidget) setColor(newColor *colorT) {
|
// restores the last saved color and makes it active
|
||||||
if tk.color == newColor {
|
func (tk *guiWidget) restoreEnableColor() {
|
||||||
// nothing to do since the colors have nto changed
|
if tk.color == nil {
|
||||||
return
|
tk.color = new(colorT)
|
||||||
}
|
}
|
||||||
tk.color = newColor
|
tk.color.frame = tk.colorLast.frame
|
||||||
|
tk.color.fg = tk.colorLast.fg
|
||||||
|
tk.color.bg = tk.colorLast.bg
|
||||||
|
tk.color.selFg = tk.colorLast.selFg
|
||||||
|
tk.color.selBg = tk.colorLast.selBg
|
||||||
|
|
||||||
|
tk.activateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DONE ON DISABLE() WIDGET
|
||||||
|
// makes the button look disabled
|
||||||
|
func (tk *guiWidget) setColorDisable() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
// save the current color
|
||||||
|
tk.color.frame = superLightGrey
|
||||||
|
tk.color.fg = gocui.ColorBlack
|
||||||
|
tk.color.bg = superLightGrey
|
||||||
|
tk.color.selFg = superLightGrey
|
||||||
|
tk.color.selBg = superLightGrey
|
||||||
|
tk.activateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets the current gocui highlight colors
|
||||||
|
func (tk *guiWidget) activateColor() {
|
||||||
if tk.v == nil {
|
if tk.v == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
tk.v.FrameColor = tk.color.frame
|
||||||
|
tk.v.FgColor = tk.color.fg
|
||||||
|
tk.v.BgColor = tk.color.bg
|
||||||
|
tk.v.SelFgColor = tk.color.selFg
|
||||||
|
tk.v.SelBgColor = tk.color.selBg
|
||||||
|
}
|
||||||
|
|
||||||
|
// saves the color and makes it active
|
||||||
|
func (tk *guiWidget) updateColor() {
|
||||||
|
if tk.v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tk.color != nil {
|
||||||
|
tk.colorLast.frame = tk.color.frame
|
||||||
|
tk.colorLast.fg = tk.color.fg
|
||||||
|
tk.colorLast.bg = tk.color.bg
|
||||||
|
tk.colorLast.selFg = tk.color.selFg
|
||||||
|
tk.colorLast.selBg = tk.color.selBg
|
||||||
|
}
|
||||||
|
tk.activateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Below are all the colors. TODO: move to protobuf and save in a config file
|
||||||
|
|
||||||
|
func (tk *guiWidget) setColorWindowFrame() {
|
||||||
if tk.color == nil {
|
if tk.color == nil {
|
||||||
log.Log(NOW, "Set the node to color = nil")
|
tk.color = new(colorT)
|
||||||
tk.color = &colorNone
|
|
||||||
}
|
}
|
||||||
log.Log(NOW, "Set the node to color =", tk.color.name)
|
if me.dark { // use a dark color palette
|
||||||
tk.Show()
|
tk.color.frame = gocui.AttrNone
|
||||||
}
|
tk.color.fg = gocui.ColorBlack
|
||||||
|
tk.color.bg = gocui.ColorBlack
|
||||||
func (w *guiWidget) disableColor() {
|
tk.color.selFg = gocui.AttrNone
|
||||||
if w.color != &colorDisabled {
|
tk.color.selBg = gocui.AttrNone
|
||||||
w.defaultColor = w.color
|
} else {
|
||||||
}
|
tk.color.frame = gocui.AttrNone
|
||||||
w.setColor(&colorDisabled)
|
tk.color.fg = gocui.AttrNone
|
||||||
}
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.AttrNone
|
||||||
func (w *guiWidget) enableColor() {
|
tk.color.selBg = gocui.AttrNone
|
||||||
w.setColor(w.defaultColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *guiWidget) setDefaultHighlight() {
|
|
||||||
if w.v == nil {
|
|
||||||
log.Log(ERROR, "SetColor() failed on view == nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.v.SelBgColor = gocui.ColorGreen
|
|
||||||
w.v.SelFgColor = gocui.ColorBlack
|
|
||||||
}
|
|
||||||
|
|
||||||
func randColor() gocui.Attribute {
|
|
||||||
colors := []string{"Green", "#FFAA55", "Yellow", "Blue", "Red", "Black", "White"}
|
|
||||||
i := rand.Intn(len(colors))
|
|
||||||
log.Log(NOW, "randColor() i =", i)
|
|
||||||
return gocui.GetColor(colors[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *guiWidget) redoColor(draw bool) {
|
|
||||||
if w == nil {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Sleep(.05)
|
tk.updateColor()
|
||||||
w.setDefaultHighlight()
|
|
||||||
// w.setDefaultWidgetColor()
|
|
||||||
w.Show()
|
|
||||||
|
|
||||||
for _, child := range w.children {
|
|
||||||
child.redoColor(draw)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// weird. lots of color problems for me on debian sid using the traditional Andy Herzfield 'gnome'
|
||||||
|
func (tk *guiWidget) setColorWindowTitleActive() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
if me.dark { // use a dark color palette
|
||||||
|
tk.color.frame = gocui.AttrNone
|
||||||
|
tk.color.fg = gocui.ColorBlue
|
||||||
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.ColorWhite
|
||||||
|
tk.color.selBg = gocui.ColorBlue
|
||||||
|
} else {
|
||||||
|
tk.color.frame = gocui.ColorWhite
|
||||||
|
tk.color.fg = gocui.ColorWhite
|
||||||
|
tk.color.bg = gocui.ColorBlue
|
||||||
|
tk.color.selFg = gocui.ColorWhite
|
||||||
|
tk.color.selBg = gocui.ColorBlue
|
||||||
|
}
|
||||||
|
|
||||||
|
tk.updateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) setColorWindowTitle() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
if me.dark { // use a dark color palette
|
||||||
|
tk.color.frame = gocui.AttrNone
|
||||||
|
tk.color.fg = gocui.ColorBlue
|
||||||
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.ColorWhite
|
||||||
|
tk.color.selBg = gocui.ColorBlue
|
||||||
|
} else {
|
||||||
|
tk.color.frame = gocui.ColorWhite
|
||||||
|
tk.color.fg = gocui.ColorBlue
|
||||||
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.ColorWhite
|
||||||
|
tk.color.selBg = gocui.ColorBlue
|
||||||
|
}
|
||||||
|
|
||||||
|
tk.updateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) setColorBG() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
if me.dark {
|
||||||
|
tk.color.frame = gocui.AttrNone
|
||||||
|
tk.color.fg = gocui.ColorBlack
|
||||||
|
tk.color.bg = gocui.ColorBlack
|
||||||
|
tk.color.selFg = gocui.AttrNone
|
||||||
|
tk.color.selBg = gocui.AttrNone
|
||||||
|
} else {
|
||||||
|
tk.color.frame = gocui.ColorWhite
|
||||||
|
tk.color.fg = gocui.ColorWhite
|
||||||
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.AttrNone
|
||||||
|
tk.color.selBg = gocui.AttrNone
|
||||||
|
}
|
||||||
|
|
||||||
|
tk.updateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) setColorLabel() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
if me.dark {
|
||||||
|
tk.color.frame = gocui.AttrNone
|
||||||
|
tk.color.fg = gocui.ColorWhite
|
||||||
|
tk.color.bg = gocui.ColorBlack
|
||||||
|
tk.color.selFg = gocui.ColorWhite
|
||||||
|
tk.color.selBg = gocui.AttrNone
|
||||||
|
} else {
|
||||||
|
tk.color.frame = gocui.AttrNone
|
||||||
|
tk.color.fg = gocui.ColorBlack
|
||||||
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.AttrNone
|
||||||
|
tk.color.selBg = gocui.ColorWhite
|
||||||
|
}
|
||||||
|
|
||||||
|
tk.updateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) setColorButtonDense() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
if me.dark {
|
||||||
|
tk.color.frame = gocui.AttrNone
|
||||||
|
tk.color.fg = gocui.ColorBlue
|
||||||
|
tk.color.bg = gocui.ColorBlack
|
||||||
|
tk.color.selFg = gocui.ColorWhite
|
||||||
|
tk.color.selBg = gocui.ColorBlue
|
||||||
|
} else {
|
||||||
|
tk.color.frame = gocui.AttrNone
|
||||||
|
tk.color.fg = gocui.ColorWhite
|
||||||
|
tk.color.bg = gocui.ColorBlue
|
||||||
|
tk.color.selFg = gocui.ColorBlue
|
||||||
|
tk.color.selBg = gocui.AttrNone
|
||||||
|
}
|
||||||
|
|
||||||
|
tk.updateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) setColorNotifyIcon() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
tk.color.frame = gocui.AttrNone
|
||||||
|
tk.color.fg = gocui.ColorWhite
|
||||||
|
tk.color.bg = gocui.ColorBlue
|
||||||
|
tk.color.selFg = gocui.ColorBlue
|
||||||
|
tk.color.selBg = gocui.AttrNone
|
||||||
|
|
||||||
|
tk.updateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) setColorButton() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
if me.dark {
|
||||||
|
tk.color.frame = gocui.ColorBlack
|
||||||
|
tk.color.fg = gocui.ColorBlue
|
||||||
|
tk.color.bg = gocui.ColorBlack
|
||||||
|
tk.color.selFg = gocui.ColorWhite
|
||||||
|
tk.color.selBg = gocui.ColorBlue
|
||||||
|
} else {
|
||||||
|
tk.color.frame = gocui.ColorBlue
|
||||||
|
tk.color.fg = gocui.AttrNone
|
||||||
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.ColorWhite
|
||||||
|
tk.color.selBg = gocui.ColorBlue
|
||||||
|
}
|
||||||
|
|
||||||
|
tk.updateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) setColorInput() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
if me.dark {
|
||||||
|
tk.color.frame = gocui.ColorYellow
|
||||||
|
tk.color.fg = gocui.AttrNone
|
||||||
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.ColorYellow
|
||||||
|
tk.color.selBg = gocui.ColorBlack
|
||||||
|
} else {
|
||||||
|
tk.color.frame = gocui.ColorYellow
|
||||||
|
tk.color.fg = gocui.AttrNone
|
||||||
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.ColorYellow
|
||||||
|
tk.color.selBg = gocui.ColorBlack
|
||||||
|
}
|
||||||
|
tk.updateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) setColorModal() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
if me.dark {
|
||||||
|
tk.color.frame = gocui.ColorRed
|
||||||
|
tk.color.fg = gocui.ColorRed
|
||||||
|
tk.color.bg = gocui.ColorBlack
|
||||||
|
tk.color.selFg = gocui.ColorBlack
|
||||||
|
tk.color.selBg = gocui.AttrNone
|
||||||
|
} else {
|
||||||
|
tk.color.frame = gocui.ColorRed
|
||||||
|
tk.color.fg = gocui.AttrNone
|
||||||
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.AttrNone
|
||||||
|
tk.color.selBg = gocui.ColorWhite
|
||||||
|
}
|
||||||
|
tk.updateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
// what genius figured this out?
|
||||||
|
func (tk *guiWidget) setColorTextbox() {
|
||||||
|
if tk.color == nil {
|
||||||
|
tk.color = new(colorT)
|
||||||
|
}
|
||||||
|
if me.dark {
|
||||||
|
tk.color.frame = gocui.ColorRed
|
||||||
|
tk.color.fg = gocui.ColorRed
|
||||||
|
tk.color.bg = gocui.ColorBlack
|
||||||
|
tk.color.selFg = gocui.ColorBlack
|
||||||
|
tk.color.selBg = gocui.AttrNone
|
||||||
|
} else {
|
||||||
|
tk.color.frame = gocui.ColorRed
|
||||||
|
tk.color.fg = gocui.AttrNone
|
||||||
|
tk.color.bg = gocui.AttrNone
|
||||||
|
tk.color.selFg = gocui.AttrNone
|
||||||
|
tk.color.selBg = gocui.ColorWhite
|
||||||
|
}
|
||||||
|
tk.updateColor()
|
||||||
|
}
|
||||||
|
|
||||||
|
// just notes down here
|
||||||
|
|
||||||
// what genius figured this out?
|
// what genius figured this out?
|
||||||
// originally from github.com/dimasma0305/GoFetch
|
// originally from github.com/dimasma0305/GoFetch
|
||||||
func get_teminal_color_palette() string {
|
func get_teminal_color_palette() string {
|
||||||
|
@ -195,3 +323,29 @@ func get_teminal_color_palette() string {
|
||||||
|
|
||||||
return color1 + " " + color2 + " " + color3 + " " + color4 + " " + color5 + " " + color6 + " " + color7 + " " + color8
|
return color1 + " " + color2 + " " + color3 + " " + color4 + " " + color5 + " " + color6 + " " + color7 + " " + color8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func randColor() gocui.Attribute {
|
||||||
|
colors := []string{"Green", "#FFAA55", "Yellow", "Blue", "Red", "Black", "White"}
|
||||||
|
i := rand.Intn(len(colors))
|
||||||
|
log.Log(NOW, "randColor() i =", i)
|
||||||
|
return gocui.GetColor(colors[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
var none gocui.Attribute = gocui.AttrNone
|
||||||
|
var colorNone colorT = colorT{none, none, none, none, none, "debug none"}
|
||||||
|
|
||||||
|
var lightPurple gocui.Attribute = gocui.GetColor("#DDDDDD") // light purple
|
||||||
|
var darkPurple gocui.Attribute = gocui.GetColor("#FFAA55") // Dark Purple
|
||||||
|
var heavyPurple gocui.Attribute = gocui.GetColor("#88AA55") // heavy purple
|
||||||
|
var powdererBlue gocui.Attribute = gocui.GetColor("#B0E0E6") // w3c 'powerder blue'
|
||||||
|
var superLightGrey gocui.Attribute = gocui.GetColor("#55AAFF") // super light grey
|
||||||
|
|
||||||
|
// Standard defined colors from gocui:
|
||||||
|
// ColorBlack ColorRed ColorGreen ColorYellow ColorBlue ColorMagenta ColorCyan ColorWhite
|
||||||
|
|
||||||
|
// v.BgColor = gocui.GetColor("#111111") // crazy red
|
||||||
|
// v.BgColor = gocui.GetColor("#FF9911") // heavy red
|
||||||
|
// v.SelBgColor = gocui.GetColor("#FFEE11") // blood red
|
||||||
|
|
||||||
|
// v.BgColor = gocui.GetColor("#55AAFF") // super light grey
|
||||||
|
// v.BgColor = gocui.GetColor("#FFC0CB") // 'w3c pink' yellow
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
def color(num, text):
|
||||||
|
return f"\033[38;5;{num}m{text}\033[0m"
|
||||||
|
|
||||||
|
for i in range(300):
|
||||||
|
print(color(i, f"number {i:02}"))
|
95
debug.go
95
debug.go
|
@ -1,53 +1,102 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
"go.wit.com/widget"
|
"go.wit.com/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (w *guiWidget) dumpTree(s string) {
|
func (w *guiWidget) dumpTree(s string) {
|
||||||
// log.Log(ERROR, "dumpTree w", w.node.WidgetId, w.WidgetType, w.String())
|
// log.Log(ERROR, "dump w", w.WidgetId(), w.WidgetType, w.String())
|
||||||
if w == nil {
|
if w == nil {
|
||||||
log.Log(ERROR, "dumpTree w.TK == nil", w.node.WidgetId, w.WidgetType, w.String())
|
log.Log(ERROR, "dump w.TK == nil", w.WidgetId(), w.WidgetType(), w.String())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.showWidgetPlacement("dumpTree() " + s)
|
w.dumpWidget("dumpTree() " + s)
|
||||||
|
|
||||||
for _, child := range w.children {
|
for _, child := range w.children {
|
||||||
child.dumpTree(s)
|
child.dumpTree(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *guiWidget) showWidgetPlacement(s string) {
|
func (w *guiWidget) dumpWindows(s string) {
|
||||||
|
// log.Log(ERROR, "dump w", w.WidgetId(), w.WidgetType, w.String())
|
||||||
|
if w == nil {
|
||||||
|
log.Log(ERROR, "dump w.TK == nil", w.WidgetId(), w.WidgetType(), w.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if w.WidgetType() == widget.Window {
|
||||||
|
s += fmt.Sprintf(" F(%d,%d)", w.force.w0, w.force.h0)
|
||||||
|
// can't set this here. doesn't work
|
||||||
|
// w.full.w0 = w.force.w0
|
||||||
|
// w.full.h0 = w.force.h0
|
||||||
|
w.dumpWidget("dumpWindow() " + s)
|
||||||
|
w.windowFrame.dumpWidget("dumpFrame() " + s)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range w.children {
|
||||||
|
child.dumpWindows(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// a standard function to print out information about a widget
|
||||||
|
func (tk *guiWidget) dumpWidget(s string) {
|
||||||
var s1 string
|
var s1 string
|
||||||
var pId int
|
var pId int
|
||||||
if w.node.Parent == nil {
|
// tk.verifyRect()
|
||||||
log.Log(INFO, "showWidgetPlacement() parent == nil", w.node.WidgetId, w.cuiName)
|
if tk.parent == nil {
|
||||||
|
log.Logf(WARN, "showWidgetPlacement() parent == nil wId=%d cuiName=%s", tk.WidgetId(), tk.cuiName)
|
||||||
pId = 0
|
pId = 0
|
||||||
} else {
|
} else {
|
||||||
pId = w.node.Parent.WidgetId
|
pId = tk.parent.WidgetId()
|
||||||
}
|
}
|
||||||
s1 = fmt.Sprintf("(wId,pId)=(%2d,%2d) ", w.node.WidgetId, pId)
|
s1 = fmt.Sprintf("(wId,pId)=(%4d,%4d) ", tk.WidgetId(), pId)
|
||||||
if w.Visible() {
|
sizeW, sizeH := tk.Size()
|
||||||
sizeW, sizeH := w.Size()
|
hide := "S"
|
||||||
s1 += fmt.Sprintf("size=(%2d,%2d)", sizeW, sizeH)
|
if tk.Hidden() {
|
||||||
s1 += fmt.Sprintf("gocui=(%2d,%2d,%2d,%2d)",
|
hide = "H"
|
||||||
w.gocuiSize.w0, w.gocuiSize.h0, w.gocuiSize.w1, w.gocuiSize.h1)
|
}
|
||||||
|
s1 += fmt.Sprintf("size=(%3d,%3d)%s)", sizeW, sizeH, hide)
|
||||||
|
if tk.Visible() {
|
||||||
|
s1 += fmt.Sprintf("gocui=(%3d,%3d,%3d,%3d)",
|
||||||
|
tk.gocuiSize.w0, tk.gocuiSize.h0, tk.gocuiSize.w1, tk.gocuiSize.h1)
|
||||||
} else {
|
} else {
|
||||||
sizeW, sizeH := w.Size()
|
s1 += fmt.Sprintf(" %3s %3s %3s %3s ", "", "", "", "")
|
||||||
s1 += fmt.Sprintf("size=(%2d,%2d)", sizeW, sizeH)
|
|
||||||
s1 += fmt.Sprintf(" ")
|
|
||||||
}
|
}
|
||||||
if w.node.Parent != nil {
|
s1 += fmt.Sprintf(" full=(%3d,%3d,%3d,%3d)", tk.full.w0, tk.full.h0, tk.full.w1, tk.full.h1)
|
||||||
if w.node.Parent.WidgetType == widget.Grid {
|
if tk.parent != nil {
|
||||||
s1 += fmt.Sprintf("At(%2d,%2d) ", w.node.State.AtW, w.node.State.AtH)
|
if tk.parent.WidgetType() == widget.Grid {
|
||||||
|
s1 += fmt.Sprintf("At(%3d,%3d)", tk.GridW(), tk.GridH())
|
||||||
|
s1 += fmt.Sprintf("(%3d,%3d) ", tk.parent.widths[tk.GridW()], tk.parent.heights[tk.GridH()])
|
||||||
|
} else {
|
||||||
|
s1 += fmt.Sprintf(" %3s %3s ", "", "")
|
||||||
|
s1 += fmt.Sprintf(" %3s %3s ", "", "")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
s1 += fmt.Sprintf(" %3s %3s ", "", "")
|
||||||
}
|
}
|
||||||
tmp := "." + w.String() + ". " + w.cuiName
|
var end string
|
||||||
if w.node.WidgetType == widget.Box {
|
if tk.WidgetType() == widget.Box {
|
||||||
tmp = "." + w.node.State.Direction.String() + ". " + w.cuiName
|
end = fmt.Sprintf("%-8s %-8s %s %s", tk.WidgetType(), tk.cuiName, tk.Direction().String(), tk.String())
|
||||||
|
} else {
|
||||||
|
end = fmt.Sprintf("%-8s %-8s %s", tk.WidgetType(), tk.cuiName, tk.String())
|
||||||
}
|
}
|
||||||
log.Log(NOW, s1, s, w.node.WidgetType, ",", tmp) //
|
log.Log(GOCUI, s1, s, end)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printWidgetTree(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
me.treeRoot.ListWidgets()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printWidgetPlacements(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
w := me.treeRoot.TK.(*guiWidget)
|
||||||
|
w.dumpTree("MM")
|
||||||
|
w.dumpWindows("WW")
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
112
draw.go
112
draw.go
|
@ -1,112 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
|
||||||
log "go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
var toggle bool = true
|
|
||||||
|
|
||||||
func (w *guiWidget) DrawAt(offsetW, offsetH int) {
|
|
||||||
w.setColor(&colorActiveW)
|
|
||||||
w.placeWidgets(offsetW, offsetH) // compute the sizes & places for each widget
|
|
||||||
w.active = false
|
|
||||||
w.showWidgets()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *guiWidget) toggleTree() {
|
|
||||||
if toggle {
|
|
||||||
w.drawTree(toggle)
|
|
||||||
toggle = false
|
|
||||||
} else {
|
|
||||||
w.hideWidgets()
|
|
||||||
toggle = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// display the widgets in the binary tree
|
|
||||||
func (w *guiWidget) drawTree(draw bool) {
|
|
||||||
if w == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.showWidgetPlacement("drawTree()")
|
|
||||||
if draw {
|
|
||||||
// w.textResize()
|
|
||||||
w.Show()
|
|
||||||
} else {
|
|
||||||
w.Hide()
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, child := range w.children {
|
|
||||||
child.drawTree(draw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// display's the text of the widget in gocui
|
|
||||||
// deletes the old view if it exists and recreates it
|
|
||||||
func (w *guiWidget) drawView() {
|
|
||||||
var err error
|
|
||||||
log.Log(INFO, "drawView() START", w.WidgetType, w.String())
|
|
||||||
if me.baseGui == nil {
|
|
||||||
log.Log(ERROR, "drawView() ERROR: me.baseGui == nil", w)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.cuiName == "" {
|
|
||||||
log.Log(ERROR, "drawView() w.cuiName was not set for widget", w)
|
|
||||||
w.cuiName = strconv.Itoa(w.node.WidgetId) + " TK"
|
|
||||||
}
|
|
||||||
log.Log(INFO, "drawView() labelN =", w.labelN)
|
|
||||||
|
|
||||||
// this deletes the button from gocui
|
|
||||||
me.baseGui.DeleteView(w.cuiName)
|
|
||||||
w.v = nil
|
|
||||||
|
|
||||||
w.textResize()
|
|
||||||
a := w.gocuiSize.w0
|
|
||||||
b := w.gocuiSize.h0
|
|
||||||
c := w.gocuiSize.w1
|
|
||||||
d := w.gocuiSize.h1
|
|
||||||
|
|
||||||
w.v, err = me.baseGui.SetView(w.cuiName, a, b, c, d, 0)
|
|
||||||
if err == nil {
|
|
||||||
w.showWidgetPlacement("drawView()")
|
|
||||||
log.Log(ERROR, "drawView() internal plugin error err = nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
|
||||||
w.showWidgetPlacement("drawView()")
|
|
||||||
log.Log(ERROR, "drawView() internal plugin error error.IS()", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// this sets up the keybinding for the name of the window
|
|
||||||
// does this really need to be done? I think we probably already
|
|
||||||
// know everything about where all the widgets are so we could bypass
|
|
||||||
// the gocui package and just handle all the mouse events internally here (?)
|
|
||||||
// for now, the w.v.Name is a string "1", "2", "3", etc from the widgetId
|
|
||||||
|
|
||||||
// set the binding for this gocui view now that it has been created
|
|
||||||
// gocui handles overlaps of views so it will run on the view that is clicked on
|
|
||||||
me.baseGui.SetKeybinding(w.v.Name(), gocui.MouseLeft, gocui.ModNone, click)
|
|
||||||
|
|
||||||
// this actually sends the text to display to gocui
|
|
||||||
w.v.Wrap = true
|
|
||||||
w.v.Frame = w.frame
|
|
||||||
w.v.Clear()
|
|
||||||
fmt.Fprint(w.v, w.labelN)
|
|
||||||
|
|
||||||
// if you don't do this here, it will be black & white only
|
|
||||||
if w.color != nil {
|
|
||||||
w.v.FrameColor = w.color.frame
|
|
||||||
w.v.FgColor = w.color.fg
|
|
||||||
w.v.BgColor = w.color.bg
|
|
||||||
w.v.SelFgColor = w.color.selFg
|
|
||||||
w.v.SelBgColor = w.color.selBg
|
|
||||||
}
|
|
||||||
log.Log(INFO, "drawView() END")
|
|
||||||
}
|
|
145
dropdown.go
145
dropdown.go
|
@ -1,156 +1,115 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
// simulates a dropdown menu in gocui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
|
||||||
log "go.wit.com/log"
|
log "go.wit.com/log"
|
||||||
"go.wit.com/toolkits/tree"
|
"go.wit.com/toolkits/tree"
|
||||||
"go.wit.com/widget"
|
"go.wit.com/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeDropdownView(ddItems string) *guiWidget {
|
// create a new widget in the binary tree
|
||||||
newNode := addDropdown()
|
func makeNewFlagWidget(wId int) *guiWidget {
|
||||||
tk := newNode.TK.(*guiWidget)
|
|
||||||
tk.labelN = ddItems
|
|
||||||
tk.SetText(ddItems)
|
|
||||||
tk.gocuiSize.w0 = 100
|
|
||||||
tk.gocuiSize.w1 = 120
|
|
||||||
tk.gocuiSize.h0 = 15
|
|
||||||
tk.gocuiSize.h1 = 18
|
|
||||||
tk.v, _ = me.baseGui.SetView("ddview",
|
|
||||||
tk.gocuiSize.w0,
|
|
||||||
tk.gocuiSize.h0,
|
|
||||||
tk.gocuiSize.w1,
|
|
||||||
tk.gocuiSize.h1, 0)
|
|
||||||
if tk.v == nil {
|
|
||||||
return tk
|
|
||||||
}
|
|
||||||
tk.v.Wrap = true
|
|
||||||
tk.v.Frame = true
|
|
||||||
tk.v.Clear()
|
|
||||||
fmt.Fprint(tk.v, ddItems)
|
|
||||||
tk.Show()
|
|
||||||
return tk
|
|
||||||
}
|
|
||||||
|
|
||||||
func addDropdown() *tree.Node {
|
|
||||||
n := new(tree.Node)
|
n := new(tree.Node)
|
||||||
n.WidgetType = widget.Flag
|
n.WidgetType = widget.Flag
|
||||||
n.WidgetId = 2222
|
n.WidgetId = wId
|
||||||
n.ParentId = 0
|
n.ParentId = 0
|
||||||
|
|
||||||
// store the internal toolkit information
|
// store the internal toolkit information
|
||||||
tk := new(guiWidget)
|
tk := new(guiWidget)
|
||||||
tk.frame = true
|
tk.frame = true
|
||||||
tk.labelN = "DropBox text"
|
|
||||||
|
|
||||||
tk.node = n
|
tk.node = n
|
||||||
// copy the data from the action message
|
if tk.node.Parent == nil {
|
||||||
tk.node.State.Label = "DropBox"
|
tk.node.Parent = me.treeRoot
|
||||||
|
}
|
||||||
|
|
||||||
// set the name used by gocui to the id
|
// set the name used by gocui to the id
|
||||||
tk.cuiName = "-1 dropbox"
|
tk.cuiName = fmt.Sprintf("%d DR", wId)
|
||||||
|
|
||||||
tk.color = &colorFlag
|
tk.setColorInput()
|
||||||
|
|
||||||
// add this new widget on the binary tree
|
// add this new widget on the binary tree
|
||||||
tk.parent = me.treeRoot.TK.(*guiWidget)
|
tk.parent = me.treeRoot.TK.(*guiWidget)
|
||||||
if tk.parent == nil {
|
if tk.parent == nil {
|
||||||
panic("addDropdown() didn't get treeRoot guiWidget")
|
panic("makeNewFlagWidget() didn't get treeRoot guiWidget")
|
||||||
} else {
|
} else {
|
||||||
tk.parent.children = append(tk.parent.children, tk)
|
tk.parent.children = append(tk.parent.children, tk)
|
||||||
}
|
}
|
||||||
|
|
||||||
n.TK = tk
|
n.TK = tk
|
||||||
return n
|
return tk
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tk *guiWidget) showDropdown() {
|
func (tk *guiWidget) showDropdown() {
|
||||||
var ddItems string
|
if me.dropdown.tk == nil {
|
||||||
|
// should only happen once
|
||||||
|
me.dropdown.tk = makeNewFlagWidget(me.dropdown.wId)
|
||||||
|
me.dropdown.tk.dumpWidget("init() dropdown")
|
||||||
|
}
|
||||||
|
if me.dropdown.tk == nil {
|
||||||
|
log.Log(GOCUI, "showDropdown() Is Broken!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// todo: fix this after switching to protobuf
|
// todo: fix this after switching to protobuf
|
||||||
// var items []string
|
me.dropdown.items = []string{} // zero out whatever was there before
|
||||||
// items = tk.node.State.Strings
|
|
||||||
//for i, s := range items {
|
|
||||||
for i, s := range tk.node.Strings() {
|
for i, s := range tk.node.Strings() {
|
||||||
log.Log(GOCUI, "showDropdown()", tk.String(), i, s)
|
log.Log(GOCUI, "showDropdown()", tk.String(), i, s)
|
||||||
ddItems += s + "\n"
|
me.dropdown.items = append(me.dropdown.items, s)
|
||||||
}
|
}
|
||||||
|
log.Log(GOCUI, "new dropdown items should be set to:", me.dropdown.items)
|
||||||
|
|
||||||
log.Log(GOCUI, "new dropdown items should be set to:", ddItems)
|
|
||||||
sizeW, sizeH := tk.Size()
|
|
||||||
log.Log(GOCUI, "showDropdown() size W,H=", sizeW, sizeH)
|
|
||||||
startW, startH := tk.Position()
|
startW, startH := tk.Position()
|
||||||
log.Log(GOCUI, "showDropdown() location W,H=", startW, startH)
|
log.Log(GOCUI, "showDropdown() SHOWING AT W,H=", startW, startH)
|
||||||
me.dropdownV.MoveToOffset(startW+3, startH+2)
|
me.dropdown.tk.Hide()
|
||||||
me.dropdownV.labelN = ddItems
|
me.dropdown.tk.MoveToOffset(startW+3, startH+2)
|
||||||
me.dropdownV.Show()
|
me.dropdown.tk.labelN = strings.Join(me.dropdown.items, "\n")
|
||||||
}
|
me.dropdown.tk.Show()
|
||||||
|
me.dropdown.active = true
|
||||||
|
me.dropdown.callerTK = tk
|
||||||
|
|
||||||
func hideDDview() error {
|
r := me.dropdown.tk.gocuiSize // set the 'full' size so that mouse clicks are sent here
|
||||||
w, h := me.baseGui.MousePosition()
|
me.dropdown.tk.full.w0 = r.w0
|
||||||
log.Log(GOCUI, "hide dropdown menu() view msgMouseDown (w,h) =", w, h)
|
me.dropdown.tk.full.w1 = r.w1
|
||||||
if me.dropdownV == nil {
|
me.dropdown.tk.full.h0 = r.h0
|
||||||
return gocui.ErrUnknownView
|
me.dropdown.tk.full.h1 = r.h1
|
||||||
}
|
|
||||||
if me.dropdownV.v == nil {
|
|
||||||
return gocui.ErrUnknownView
|
|
||||||
}
|
|
||||||
me.dropdownV.SetVisible(false)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func showDDview() error {
|
me.dropdown.tk.dumpWidget("showDropdown()")
|
||||||
w, h := me.baseGui.MousePosition()
|
|
||||||
log.Log(GOCUI, "show dropdown menu() view msgMouseDown (w,h) =", w, h)
|
|
||||||
if me.dropdownV == nil {
|
|
||||||
return gocui.ErrUnknownView
|
|
||||||
}
|
|
||||||
if me.dropdownV.v == nil {
|
|
||||||
return gocui.ErrUnknownView
|
|
||||||
}
|
|
||||||
me.dropdownV.SetVisible(true)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is a drop down view active, treat it like a dialog box and close it
|
// if there is a drop down view active, treat it like a dialog box and close it
|
||||||
func (w *guiWidget) dropdownClicked(mouseW, mouseH int) string {
|
func (w *guiWidget) dropdownClicked(mouseW, mouseH int) string {
|
||||||
w.Hide()
|
w.Hide()
|
||||||
|
me.dropdown.active = false
|
||||||
|
|
||||||
startW, startH := w.Position()
|
// only need height to figure out what line in the dropdown menu the user clicked
|
||||||
log.Log(GOCUI, "dropdownClicked() start (w,h) =", startW, startH)
|
_, startH := w.Position()
|
||||||
log.Log(GOCUI, "dropdownClicked() at (w,h) =", mouseW, mouseH)
|
|
||||||
|
|
||||||
itemNumber := mouseH - startH
|
itemNumber := mouseH - startH
|
||||||
items := strings.Split(w.labelN, "\n")
|
items := me.dropdown.items
|
||||||
log.Log(GOCUI, "dropdownClicked() look for item", itemNumber, "len(items) =", len(items))
|
// log.Log(GOCUI, "dropdownClicked() look for item", itemNumber, "len(items) =", len(items))
|
||||||
if itemNumber < 1 {
|
if itemNumber < 1 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(items) >= itemNumber {
|
if len(items) >= itemNumber {
|
||||||
log.Log(GOCUI, "dropdownClicked() found", items[itemNumber-1])
|
// log.Log(GOCUI, "dropdownClicked() found", items[itemNumber-1])
|
||||||
if items[itemNumber-1] != "" {
|
if items[itemNumber-1] != "" {
|
||||||
if me.dropdownW != nil {
|
if me.dropdown.tk != nil {
|
||||||
log.Log(GOCUI, "dropdownClicked() send event for", me.dropdownW.cuiName, me.dropdownW.WidgetType)
|
// log.Log(GOCUI, "dropdownClicked() send event for", me.dropdownW.cuiName, me.dropdownW.node.WidgetType)
|
||||||
me.dropdownW.SetText(items[itemNumber-1])
|
me.dropdown.callerTK.SetText(items[itemNumber-1])
|
||||||
me.dropdownW.node.SetCurrentS(items[itemNumber-1])
|
me.dropdown.callerTK.node.SetCurrentS(items[itemNumber-1])
|
||||||
me.myTree.SendUserEvent(me.dropdownW.node)
|
me.myTree.SendUserEvent(me.dropdown.callerTK.node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return items[itemNumber-1]
|
return items[itemNumber-1]
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func dropdownUnclicked(mouseX, mouseH int) {
|
|
||||||
if me.dropdownV == nil {
|
|
||||||
log.Log(GOCUI, "mouseUp() dropdownV = nil", mouseX, mouseH)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tk := me.dropdownV
|
|
||||||
log.Log(GOCUI, "mouseUp() view msgMouseDown (check here for dropdown menu click) (w,h) =", mouseX, mouseH)
|
|
||||||
log.Log(GOCUI, "mouseUp() ddview is the thing that was clicked", mouseX, mouseH)
|
|
||||||
log.Log(GOCUI, "mouseUp() find out what the string is here", mouseX, mouseH, tk.gocuiSize.h1)
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,234 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// register how the 'gocui' will work as a GO toolkit plugin
|
||||||
|
// all applications will use these keys. they are universal.
|
||||||
|
|
||||||
|
// tells 'gocui' where to send events
|
||||||
|
func registerHandlers(g *gocui.Gui) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Info("EVENT BINDINGS recovered in r", r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// mouse handlers
|
||||||
|
g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, mouseDown) // normal left mouse down
|
||||||
|
g.SetKeybinding("", gocui.MouseLeft, gocui.ModMouseCtrl, ctrlDown) // mouse with the ctrl key held down
|
||||||
|
g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, mouseUp) // mouse button release
|
||||||
|
g.SetKeybinding("", gocui.MouseWheelUp, gocui.ModNone, wheelsUp) // mouse button release
|
||||||
|
g.SetKeybinding("", gocui.MouseWheelDown, gocui.ModNone, wheelsDown) // mouse button release
|
||||||
|
|
||||||
|
// Ctrl key handlers
|
||||||
|
g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, doExit) // CTRL-C : exits the application
|
||||||
|
g.SetKeybinding("", gocui.KeyCtrlV, gocui.ModNone, doPanic) // CTRL-V : force a panic()
|
||||||
|
g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, openDebuggger) // CTRL-D : open the (D)ebugger
|
||||||
|
keyForced, modForced := gocui.MustParse("ctrl+z") // setup ctrl+z
|
||||||
|
g.SetKeybinding("", keyForced, modForced, handle_ctrl_z) // CTRL-Z :cleverly let's you background gocui (breaks cursor mouse on return)
|
||||||
|
g.SetKeybinding("", gocui.KeyEsc, gocui.ModNone, doEsc) // escape key
|
||||||
|
|
||||||
|
// regular keys
|
||||||
|
g.SetKeybinding("", 'H', gocui.ModNone, theHelp) // 'H' toggles on and off the help menu
|
||||||
|
g.SetKeybinding("", 'O', gocui.ModNone, theStdout) // 'O' toggle the STDOUT window
|
||||||
|
g.SetKeybinding("", 'D', gocui.ModNone, theDarkness) // 'D' toggles light/dark mode
|
||||||
|
g.SetKeybinding("", 'q', gocui.ModNone, doExit) // 'q' exit
|
||||||
|
g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, tabCycleWindows) // '2' use this to test new ideas
|
||||||
|
|
||||||
|
// stdout keys
|
||||||
|
g.SetKeybinding("", gocui.KeyPgup, gocui.ModNone, stdoutPgup) // Pgup scroll up the Stdout buffer
|
||||||
|
g.SetKeybinding("", gocui.KeyPgdn, gocui.ModNone, stdoutPgdn) // Pgdn scroll down the Stdout buffer
|
||||||
|
g.SetKeybinding("", gocui.KeyHome, gocui.ModNone, stdoutHome) // Pgdn scroll down the Stdout buffer
|
||||||
|
g.SetKeybinding("", gocui.KeyArrowUp, gocui.ModNone, stdoutArrowUp) // Pgdn scroll down the Stdout buffer
|
||||||
|
g.SetKeybinding("", gocui.KeyArrowDown, gocui.ModNone, stdoutArrowDown) // Pgdn scroll down the Stdout buffer
|
||||||
|
|
||||||
|
// debugging
|
||||||
|
g.SetKeybinding("", '2', gocui.ModNone, theNotsure) // '2' use this to test new ideas
|
||||||
|
g.SetKeybinding("", 'S', gocui.ModNone, theSuperMouse) // 'S' Super Mouse mode!
|
||||||
|
g.SetKeybinding("", 'M', gocui.ModNone, printWidgetPlacements) // 'M' list all widgets with positions
|
||||||
|
g.SetKeybinding("", 'L', gocui.ModNone, printWidgetTree) // 'L' list all widgets in tree view
|
||||||
|
g.SetKeybinding("", 'f', gocui.ModNone, theFind) // 'f' shows what is under your mouse
|
||||||
|
g.SetKeybinding("", 'd', gocui.ModNone, theLetterD) // 'd' toggles on and off debugging buttons
|
||||||
|
g.SetKeybinding("", 'q', gocui.ModNone, quit) // 'q' only exits gocui. plugin stays alive (?)
|
||||||
|
}
|
||||||
|
|
||||||
|
// flips on 'super mouse' mode // this was awesome for debugging gocui. never remove this code.
|
||||||
|
// while this is turned on, it will print out every widget found under the mouse
|
||||||
|
func theSuperMouse(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
if me.supermouse {
|
||||||
|
log.Log(GOCUI, "supermouse off")
|
||||||
|
me.supermouse = false
|
||||||
|
} else {
|
||||||
|
me.supermouse = true
|
||||||
|
log.Log(GOCUI, "supermouse on")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// use this to test code ideas // put whatever you want here and hit '2' to activate it
|
||||||
|
func theNotsure(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
log.Info("got to theNotsure(). now what? dark =", me.dark)
|
||||||
|
me.refresh = true
|
||||||
|
log.Info("running VerifyParentId()")
|
||||||
|
me.treeRoot.VerifyParentId()
|
||||||
|
/*
|
||||||
|
if me.debug {
|
||||||
|
log.Info("debugging off")
|
||||||
|
me.debug = false
|
||||||
|
} else {
|
||||||
|
log.Info("debugging on")
|
||||||
|
me.debug = true
|
||||||
|
}
|
||||||
|
win := findWindowUnderMouse()
|
||||||
|
if win != nil {
|
||||||
|
win.dumpWidget("found() win. redrawing window:")
|
||||||
|
win.makeWindowActive()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func theDarkness(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
if me.dark {
|
||||||
|
me.dark = false
|
||||||
|
log.Info("you have seen the light")
|
||||||
|
} else {
|
||||||
|
me.dark = true
|
||||||
|
log.Info("you have entered into darkness")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func wheelsUp(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
// log.Info("private wheels up")
|
||||||
|
me.stdout.pager -= 2
|
||||||
|
if me.stdout.pager < 0 {
|
||||||
|
me.stdout.pager = 0
|
||||||
|
}
|
||||||
|
me.stdout.tk.refreshStdout()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func wheelsDown(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
// log.Info("you've landed")
|
||||||
|
me.stdout.pager += 2
|
||||||
|
if me.stdout.pager > len(me.stdout.outputS) {
|
||||||
|
me.stdout.pager = len(me.stdout.outputS)
|
||||||
|
}
|
||||||
|
me.stdout.tk.refreshStdout()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func tabCycleWindows(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
// log.Info("try to switch windows here")
|
||||||
|
if len(me.allwin) != len(findWindows()) {
|
||||||
|
me.allwin = findWindows()
|
||||||
|
}
|
||||||
|
tk := findNextWindow()
|
||||||
|
if tk == nil {
|
||||||
|
log.Info("findNextWindow() err. returned nil")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tk.makeWindowActive()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doEsc(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
log.Info("got escape key")
|
||||||
|
if me.dropdown.active {
|
||||||
|
me.dropdown.tk.Hide()
|
||||||
|
me.dropdown.active = false
|
||||||
|
log.Info("escaped from dropdown")
|
||||||
|
}
|
||||||
|
if me.textbox.active {
|
||||||
|
me.textbox.tk.Hide()
|
||||||
|
me.textbox.active = false
|
||||||
|
log.Info("escaped from textbox")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func theShow(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
var w *guiWidget
|
||||||
|
w = me.treeRoot.TK.(*guiWidget)
|
||||||
|
w.showWidgets()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doExit(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
standardExit()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doPanic(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
log.Log(GOCUI, "do panic() here")
|
||||||
|
standardClose()
|
||||||
|
panic("forced panic in gocui")
|
||||||
|
}
|
||||||
|
|
||||||
|
func openDebuggger(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
me.myTree.SendEnableDebugger()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func theFind(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
w, h := g.MousePosition()
|
||||||
|
for _, tk := range findByXY(w, h) {
|
||||||
|
// tk.v.BgColor = gocui.ColorGreen
|
||||||
|
tk.dumpWidget("theFind()")
|
||||||
|
// tk.verifyRect()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// is run whenever anyone hits 'd' (in an open space)
|
||||||
|
func theLetterD(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
// widgets that don't have physical existance in
|
||||||
|
// a display toolkit are hidden. In the case
|
||||||
|
// of gocui, they are set as not 'visible' and put offscreen
|
||||||
|
// or have the size set to zero
|
||||||
|
// (hopefully anyway) lots of things with the toolkit
|
||||||
|
// still don't work
|
||||||
|
|
||||||
|
fakeStartWidth = me.FakeW
|
||||||
|
fakeStartHeight = me.TabH + me.FramePadH
|
||||||
|
if me.showDebug {
|
||||||
|
showFake()
|
||||||
|
me.showDebug = false
|
||||||
|
} else {
|
||||||
|
hideFake()
|
||||||
|
me.showDebug = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func theHelp(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
if me.showHelp {
|
||||||
|
log.Info("Show the help!")
|
||||||
|
showHelp()
|
||||||
|
} else {
|
||||||
|
log.Info("Hide the help!")
|
||||||
|
hideHelp()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: find and give credit to the person that I found this patch in their forked repo
|
||||||
|
// handle ctrl+z
|
||||||
|
func handle_ctrl_z(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
gocui.Suspend()
|
||||||
|
log.Info("got ctrl+z")
|
||||||
|
syscall.Kill(syscall.Getpid(), syscall.SIGSTOP)
|
||||||
|
log.Info("got ctrl+z syscall() done")
|
||||||
|
gocui.Resume()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func theStdout(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
// me.stdout.pager = 0
|
||||||
|
infos := fmt.Sprintf("pager=%d len(%d) ", me.stdout.pager, len(me.stdout.outputS))
|
||||||
|
infos += fmt.Sprintf("last(%d,%d)", me.stdout.lastW, me.stdout.lastH)
|
||||||
|
|
||||||
|
if me.stdout.outputOnTop {
|
||||||
|
if me.stdout.outputOffscreen {
|
||||||
|
me.stdout.outputOffscreen = false
|
||||||
|
log.Info("stdout moved off screen", infos)
|
||||||
|
me.stdout.lastW = me.stdout.tk.gocuiSize.w0
|
||||||
|
me.stdout.lastH = me.stdout.tk.gocuiSize.h0
|
||||||
|
relocateStdoutOffscreen()
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
me.stdout.outputOffscreen = true
|
||||||
|
log.Info("stdout moved on screen", infos)
|
||||||
|
}
|
||||||
|
// move the stdout window back onscreen
|
||||||
|
me.stdout.tk.relocateStdout(me.stdout.lastW, me.stdout.lastH)
|
||||||
|
me.stdout.outputOnTop = false
|
||||||
|
setThingsOnTop()
|
||||||
|
} else {
|
||||||
|
me.stdout.outputOnTop = true
|
||||||
|
setThingsOnTop()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stdoutPgup(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
me.stdout.pager -= me.stdout.Height() - 2
|
||||||
|
if me.stdout.pager < 0 {
|
||||||
|
me.stdout.pager = 0
|
||||||
|
}
|
||||||
|
tk := me.stdout.tk
|
||||||
|
tk.refreshStdout()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stdoutHome(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
me.stdout.pager = 0
|
||||||
|
me.stdout.tk.refreshStdout()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stdoutArrowUp(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
me.stdout.pager += 1
|
||||||
|
me.stdout.tk.refreshStdout()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stdoutArrowDown(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
me.stdout.pager -= 1
|
||||||
|
me.stdout.tk.refreshStdout()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func stdoutPgdn(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
win := findWindowUnderMouse()
|
||||||
|
if win != nil {
|
||||||
|
if win.full.Height() > 50 {
|
||||||
|
log.Info("paging through really large window pager =", win.window.pager)
|
||||||
|
win.window.pager += 10
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
me.stdout.pager += 10
|
||||||
|
|
||||||
|
tk := me.stdout.tk
|
||||||
|
tk.refreshStdout()
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
// Copyright 2014 The gocui Authors. All rights reserved.
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by the GPL 3.0
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
@ -8,7 +7,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
"github.com/awesome-gocui/gocui"
|
||||||
|
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,29 +17,11 @@ import (
|
||||||
// complicated console handling, it sends events here in a clean way.
|
// complicated console handling, it sends events here in a clean way.
|
||||||
// This is equivalent to the linux command xev (apt install x11-utils)
|
// This is equivalent to the linux command xev (apt install x11-utils)
|
||||||
func gocuiEvent(g *gocui.Gui) error {
|
func gocuiEvent(g *gocui.Gui) error {
|
||||||
maxX, maxY := g.Size()
|
me.ecount += 1
|
||||||
mx, my := g.MousePosition()
|
|
||||||
log.Verbose("handleEvent() START", maxX, maxY, mx, my, msgMouseDown)
|
|
||||||
if _, err := g.View("msg"); msgMouseDown && err == nil {
|
|
||||||
moveMsg(g)
|
|
||||||
}
|
|
||||||
if widgetView, _ := g.View("msg"); widgetView == nil {
|
|
||||||
log.Log(NOW, "handleEvent() create output widget now", maxX, maxY, mx, my)
|
|
||||||
makeOutputWidget(g, "this is a create before a mouse click")
|
|
||||||
if me.logStdout != nil {
|
|
||||||
// setOutput(me.logStdout)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Verbose("output widget already exists", maxX, maxY, mx, my)
|
|
||||||
}
|
|
||||||
mouseMove(g)
|
mouseMove(g)
|
||||||
log.Verbose("handleEvent() END ", maxX, maxY, mx, my, msgMouseDown)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func dragOutputWindow() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// turns off the frame on the global window
|
// turns off the frame on the global window
|
||||||
func setFrame(b bool) {
|
func setFrame(b bool) {
|
||||||
// TODO: figure out what this might be useful for
|
// TODO: figure out what this might be useful for
|
||||||
|
@ -53,6 +33,8 @@ func setFrame(b bool) {
|
||||||
v.Frame = b
|
v.Frame = b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a test. exits gocui, but the application still runs
|
||||||
|
// maybe can switch toolkits?
|
||||||
func quit(g *gocui.Gui, v *gocui.View) error {
|
func quit(g *gocui.Gui, v *gocui.View) error {
|
||||||
return gocui.ErrQuit
|
return gocui.ErrQuit
|
||||||
}
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func mouseUp(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
// useful to debug everything that is being clicked on
|
||||||
|
/*
|
||||||
|
for _, tk := range findByXY(w, h) {
|
||||||
|
tk.dumpWidget("mouseUp()")
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
me.mouse.mouseUp = true
|
||||||
|
me.mouse.currentDrag = nil
|
||||||
|
|
||||||
|
if me.mouse.double && (time.Since(me.mouse.down) < me.mouse.doubletime) {
|
||||||
|
me.mouse.double = false
|
||||||
|
doMouseDoubleClick(me.mouse.downW, me.mouse.downH)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
me.mouse.double = false
|
||||||
|
|
||||||
|
if time.Since(me.mouse.down) < me.mouse.clicktime {
|
||||||
|
doMouseClick(me.mouse.downW, me.mouse.downH)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is where you have to figure out what
|
||||||
|
// widget was underneath so you can active
|
||||||
|
// the right response for the toolkit user's app
|
||||||
|
func mouseDown(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
if me.mouse.mouseUp {
|
||||||
|
if time.Since(me.mouse.down) < me.mouse.doubletime {
|
||||||
|
me.mouse.double = true
|
||||||
|
}
|
||||||
|
me.mouse.mouseUp = false
|
||||||
|
me.mouse.down = time.Now()
|
||||||
|
w, h := g.MousePosition()
|
||||||
|
me.mouse.downW = w
|
||||||
|
me.mouse.downH = h
|
||||||
|
|
||||||
|
win := findWindowUnderMouse()
|
||||||
|
if win != nil {
|
||||||
|
w, h := g.MousePosition()
|
||||||
|
s := fmt.Sprintf("mouse(%d,%d) ", w, h)
|
||||||
|
offW := win.full.w1 - w
|
||||||
|
offH := win.full.h1 - h
|
||||||
|
s += fmt.Sprintf("corner(%d,%d)", offW, offH)
|
||||||
|
if (offW < 3) && (offH < 3) {
|
||||||
|
log.Info("attempting resize on ", s, win.cuiName)
|
||||||
|
me.mouse.resize = true
|
||||||
|
// store the stdout corner for computing the drag size
|
||||||
|
me.stdout.lastW = me.stdout.tk.gocuiSize.w0
|
||||||
|
me.stdout.lastH = me.stdout.tk.gocuiSize.h0
|
||||||
|
} else {
|
||||||
|
// log.Info("mouse down resize off", s)
|
||||||
|
me.mouse.resize = false
|
||||||
|
}
|
||||||
|
win.setAsDragging()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,152 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
"go.wit.com/log"
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (tk *guiWidget) doButtonClick() {
|
||||||
|
if tk.IsEnabled() {
|
||||||
|
tk.dumpWidget("click()") // enable this to debug widget clicks
|
||||||
|
me.myTree.SendFromUser(tk.node)
|
||||||
|
} else {
|
||||||
|
log.Info("button is currently disabled by the application")
|
||||||
|
// tk.dumpWidget("disabled()") // enable this to debug widget clicks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handles a mouse click
|
||||||
|
func doMouseClick(w int, h int) {
|
||||||
|
// Flag widgets (dropdown menus, etc) are the highest priority. ALWAYS SEND MOUSE CLICKS THERE FIRST
|
||||||
|
// handle an open dropdown menu or text entry window first
|
||||||
|
if me.dropdown.active || me.textbox.active {
|
||||||
|
// can't drag or do anything when dropdown or textbox are visible
|
||||||
|
for _, tk := range findByXY(w, h) {
|
||||||
|
if tk.WidgetId() == me.dropdown.wId {
|
||||||
|
log.Info("got dropdwon click", w, h, tk.cuiName)
|
||||||
|
tk.dropdownClicked(w, h)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tk.WidgetId() == me.textbox.wId {
|
||||||
|
log.Info("got textbox click", w, h, tk.cuiName)
|
||||||
|
textboxClosed()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Info("a dropdown or textbox is active. you can't click anywhere else (otherwise hit ESC)", w, h)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
win := findWindowUnderMouse()
|
||||||
|
if win == nil {
|
||||||
|
log.Log(INFO, "click() nothing was at:", w, h)
|
||||||
|
log.Log(INFO, "click() check if", w, h, "is the libnotify icon")
|
||||||
|
if me.notify.icon.tk != nil && me.notify.icon.tk.gocuiSize.inRect(w, h) {
|
||||||
|
log.Log(GOCUI, "click() is libnotify.icon!")
|
||||||
|
if me.notify.icon.active {
|
||||||
|
log.Info("show notify.icon here")
|
||||||
|
setNotifyIconText("[X]")
|
||||||
|
me.notify.icon.active = false
|
||||||
|
} else {
|
||||||
|
log.Info("hide notify.icon here")
|
||||||
|
setNotifyIconText("[ ]")
|
||||||
|
me.notify.icon.active = true
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if me.notify.clock.tk != nil && me.notify.clock.tk.gocuiSize.inRect(w, h) {
|
||||||
|
log.Log(GOCUI, "click() is the clock!")
|
||||||
|
if me.showHelp {
|
||||||
|
log.Info("show help")
|
||||||
|
showHelp()
|
||||||
|
} else {
|
||||||
|
log.Info("hide help")
|
||||||
|
hideHelp()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !win.isWindowActive() {
|
||||||
|
win.makeWindowActive()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
// potentally the user is closing the window
|
||||||
|
if win.checkWindowClose(w, h) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// look in this window for widgets
|
||||||
|
// widgets have priority. send the click here first
|
||||||
|
for _, tk := range win.findByXYreal(w, h) {
|
||||||
|
switch tk.WidgetType() {
|
||||||
|
case widget.Checkbox:
|
||||||
|
if tk.Checked() {
|
||||||
|
log.Log(WARN, "checkbox is being set to false")
|
||||||
|
tk.SetChecked(false)
|
||||||
|
tk.setCheckbox()
|
||||||
|
} else {
|
||||||
|
log.Log(WARN, "checkbox is being set to true")
|
||||||
|
tk.SetChecked(true)
|
||||||
|
tk.setCheckbox()
|
||||||
|
}
|
||||||
|
me.myTree.SendUserEvent(tk.node)
|
||||||
|
return
|
||||||
|
case widget.Button:
|
||||||
|
tk.doButtonClick()
|
||||||
|
return
|
||||||
|
case widget.Combobox:
|
||||||
|
tk.showDropdown()
|
||||||
|
return
|
||||||
|
case widget.Dropdown:
|
||||||
|
tk.showDropdown()
|
||||||
|
return
|
||||||
|
case widget.Textbox:
|
||||||
|
tk.prepTextbox()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
// TODO: enable the GUI debugger in gocui
|
||||||
|
// tk.dumpWidget("undef click()") // enable this to debug widget clicks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: use this?
|
||||||
|
func ctrlDown(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
log.Info("todo: clicked with ctrlDown")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func doMouseDoubleClick(w int, h int) {
|
||||||
|
me.mouse.double = false
|
||||||
|
// log.Printf("actually a double click (%d,%d)", w, h)
|
||||||
|
|
||||||
|
if me.dropdown.active || me.textbox.active {
|
||||||
|
// can't drag or do anything when dropdown or textbox are visible
|
||||||
|
log.Info("can't double click. dropdown or textbox is active")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tk := range findByXY(w, h) {
|
||||||
|
if tk.WidgetType() == widget.Window {
|
||||||
|
tk.makeWindowActive()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk.WidgetType() == widget.Stdout {
|
||||||
|
if me.stdout.outputOnTop {
|
||||||
|
me.stdout.outputOnTop = false
|
||||||
|
setThingsOnTop()
|
||||||
|
} else {
|
||||||
|
me.stdout.outputOnTop = true
|
||||||
|
setThingsOnTop()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,142 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
// 2025 note by jcarr:
|
||||||
|
// this is one of the coolest things ever worked with.
|
||||||
|
// Personally, I've been working on making a gocui GO plugin
|
||||||
|
// so I can use it as a generalized console GUI toolkit.
|
||||||
|
//
|
||||||
|
// Well done everyone that has contributed to this gocui project !!!
|
||||||
|
// I am in your debt. Happy hacking & peace.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
log "go.wit.com/log"
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
// this function uses the mouse position to highlight & unhighlight things
|
||||||
|
// this is run every time the user moves the mouse over the terminal window
|
||||||
|
func mouseMove(g *gocui.Gui) {
|
||||||
|
// this runs while the user moves the mouse. this highlights text
|
||||||
|
// toggle off all highlight views except for whatever is under the mouse
|
||||||
|
// START HIGHLIGHTING
|
||||||
|
for _, view := range g.Views() {
|
||||||
|
view.Highlight = false
|
||||||
|
}
|
||||||
|
w, h := g.MousePosition()
|
||||||
|
// TODO: try to highlight entire grid rows
|
||||||
|
if v, err := g.ViewByPosition(w, h); err == nil {
|
||||||
|
// block anything from highlighting while a dialog box is open
|
||||||
|
if me.dropdown.active || me.textbox.active {
|
||||||
|
if me.dropdown.tk != nil && me.dropdown.tk.v == v {
|
||||||
|
v.Highlight = true
|
||||||
|
}
|
||||||
|
if me.textbox.tk != nil && me.textbox.tk.v == v {
|
||||||
|
v.Highlight = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
v.Highlight = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// old hack. create the 'msg' view if it does not yet exist
|
||||||
|
// TODO: put this somewhere more correct
|
||||||
|
if widgetView, _ := g.View("msg"); widgetView == nil {
|
||||||
|
if createStdout(g) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// END HIGHLIGHTING
|
||||||
|
|
||||||
|
// Super Mouse Mode. very useful for debugging in the past. also, just fun
|
||||||
|
if me.supermouse {
|
||||||
|
w, h := g.MousePosition()
|
||||||
|
for _, tk := range findByXY(w, h) {
|
||||||
|
s := fmt.Sprintf("SM (%3d,%3d)", w, h)
|
||||||
|
tk.dumpWidget(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.mouse.mouseUp {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// EVERYTHING BELOW THIS IS RELATED TO MOUSE DRAGGING
|
||||||
|
|
||||||
|
// has the mouse been pressed down long enough to start dragging?
|
||||||
|
if time.Since(me.mouse.down) < me.mouse.clicktime {
|
||||||
|
// not dragging
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.dropdown.active || me.textbox.active {
|
||||||
|
// can't drag
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// drag whatever was set to drag
|
||||||
|
if me.mouse.currentDrag != nil {
|
||||||
|
// me.mouse.currentDrag.dumpWidget(fmt.Sprintf("MM (%3d,%3d)", w, h))
|
||||||
|
me.mouse.currentDrag.moveNew()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info(fmt.Sprintf("gocui gui toolkit plugin error. nothing to drag at (%d,%d)", w, h))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) setAsDragging() {
|
||||||
|
me.mouse.currentDrag = tk
|
||||||
|
tk.lastW = tk.gocuiSize.w0
|
||||||
|
tk.lastH = tk.gocuiSize.h0
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is how the window gets dragged around
|
||||||
|
func (tk *guiWidget) moveNew() {
|
||||||
|
w, h := me.baseGui.MousePosition()
|
||||||
|
if tk.WidgetType() == widget.Window {
|
||||||
|
tk.window.wasDragged = true
|
||||||
|
|
||||||
|
// compute the new location based off how far the mouse has moved
|
||||||
|
// since the mouse button was pressed down
|
||||||
|
tk.gocuiSize.w0 = tk.lastW + w - me.mouse.downW
|
||||||
|
tk.gocuiSize.h0 = tk.lastH + h - me.mouse.downH
|
||||||
|
tk.makeWindowActive()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if tk.WidgetType() == widget.Flag {
|
||||||
|
me.baseGui.SetView(tk.cuiName, w-3, h-3, w+20, h+20, 0)
|
||||||
|
// tk.verifyRect()
|
||||||
|
s := fmt.Sprintf("move(%dx%d) %s ###", w, h, tk.cuiName)
|
||||||
|
tk.dumpWidget(s)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if tk.WidgetType() == widget.Stdout {
|
||||||
|
if me.mouse.resize {
|
||||||
|
newW := w - me.stdout.lastW
|
||||||
|
newH := h - me.stdout.lastH
|
||||||
|
me.stdout.w = newW
|
||||||
|
me.stdout.h = newH
|
||||||
|
// log.Info("Resize true", w, h, newW, newH)
|
||||||
|
// me.stdout.lastW = w - me.stdout.mouseOffsetW
|
||||||
|
// me.stdout.lastH = h - me.stdout.mouseOffsetH
|
||||||
|
tk.relocateStdout(me.stdout.lastW, me.stdout.lastH)
|
||||||
|
} else {
|
||||||
|
// compute the new location based off how far the mouse has moved
|
||||||
|
// since the mouse button was pressed down
|
||||||
|
newW := tk.lastW + w - me.mouse.downW
|
||||||
|
newH := tk.lastH + h - me.mouse.downH
|
||||||
|
tk.relocateStdout(newW, newH)
|
||||||
|
// log.Info("Resize false", w, h, newW, newH)
|
||||||
|
}
|
||||||
|
setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
|
||||||
|
}
|
||||||
|
}
|
58
fakefile.go
58
fakefile.go
|
@ -1,58 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FakeFile struct {
|
|
||||||
reader *bytes.Reader
|
|
||||||
buffer *bytes.Buffer
|
|
||||||
offset int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FakeFile) Read(p []byte) (n int, err error) {
|
|
||||||
n, err = f.reader.ReadAt(p, f.offset)
|
|
||||||
f.offset += int64(n)
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FakeFile) Write(p []byte) (n int, err error) {
|
|
||||||
n, err = f.buffer.Write(p)
|
|
||||||
f.offset += int64(n)
|
|
||||||
f.reader.Reset(f.buffer.Bytes())
|
|
||||||
return n, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FakeFile) Seek(offset int64, whence int) (int64, error) {
|
|
||||||
newOffset := f.offset
|
|
||||||
|
|
||||||
switch whence {
|
|
||||||
case io.SeekStart:
|
|
||||||
newOffset = offset
|
|
||||||
case io.SeekCurrent:
|
|
||||||
newOffset += offset
|
|
||||||
case io.SeekEnd:
|
|
||||||
newOffset = int64(f.buffer.Len()) + offset
|
|
||||||
default:
|
|
||||||
return 0, errors.New("Seek: whence not at start,current or end")
|
|
||||||
}
|
|
||||||
// never can get here right?
|
|
||||||
|
|
||||||
if newOffset < 0 {
|
|
||||||
return 0, errors.New("Seek: offset < 0")
|
|
||||||
}
|
|
||||||
|
|
||||||
f.offset = newOffset
|
|
||||||
return f.offset, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFakeFile() *FakeFile {
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
return &FakeFile{
|
|
||||||
reader: bytes.NewReader(buf.Bytes()),
|
|
||||||
buffer: buf,
|
|
||||||
offset: 0,
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,231 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
log "go.wit.com/log"
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
gocui defines the offset like this:
|
||||||
|
|
||||||
|
width -> increases to the right
|
||||||
|
---------------------------------- hieght
|
||||||
|
| H = 1 | increases
|
||||||
|
| | |
|
||||||
|
| W = 1 W = 18 | |
|
||||||
|
| | v
|
||||||
|
| H = 5 | downwards
|
||||||
|
-------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
// change over to this name
|
||||||
|
// returns all the widgets under (X,H) that are visible
|
||||||
|
func findByXY(w int, h int) []*guiWidget {
|
||||||
|
rootW := me.treeRoot.TK.(*guiWidget)
|
||||||
|
|
||||||
|
// this searches the binary tree recursively (function is right below)
|
||||||
|
return rootW.findByXYreal(w, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r rectType) inRect(w int, h int) bool {
|
||||||
|
if (r.w0 <= w) && (w <= r.w1) && (r.h0 <= h) && (h <= r.h1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// this checks a widget to see if it is under (W,H), then checks the widget's children
|
||||||
|
// anything that matches is passed back as an array of widgets
|
||||||
|
func (tk *guiWidget) findByXYreal(w int, h int) []*guiWidget {
|
||||||
|
var widgets []*guiWidget
|
||||||
|
|
||||||
|
// if !tk.Visible() {
|
||||||
|
// ignore widgets that are not visible
|
||||||
|
// } else {
|
||||||
|
|
||||||
|
// check the location to see if this is under (W,H)
|
||||||
|
// if it is, return this widget
|
||||||
|
// if (tk.gocuiSize.w0 <= w) && (w <= tk.gocuiSize.w1) &&
|
||||||
|
// (tk.gocuiSize.h0 <= h) && (h <= tk.gocuiSize.h1) {
|
||||||
|
// if tk.gocuiSize.inRect(w, h) {
|
||||||
|
// widgets = append(widgets, tk)
|
||||||
|
// } else {
|
||||||
|
// if (tk.full.w0 <= w) && (w <= tk.full.w1) &&
|
||||||
|
// (tk.full.h0 <= h) && (h <= tk.full.h1) {
|
||||||
|
if tk.full.inRect(w, h) {
|
||||||
|
widgets = append(widgets, tk)
|
||||||
|
}
|
||||||
|
// log.Log(GOCUI, "findByXY() found", widget.WidgetType(), w, h)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// tk.verifyRect()
|
||||||
|
|
||||||
|
// search through the children widgets in the binary tree
|
||||||
|
for _, child := range tk.children {
|
||||||
|
widgets = append(widgets, child.findByXYreal(w, h)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return widgets
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns all the windows from the root of the binary tree
|
||||||
|
func findWindows() []*guiWidget {
|
||||||
|
rootW := me.treeRoot.TK.(*guiWidget)
|
||||||
|
return rootW.findWindows()
|
||||||
|
}
|
||||||
|
|
||||||
|
// walk the binary tree looking for WidgetType == Window
|
||||||
|
func (tk *guiWidget) findWindows() []*guiWidget {
|
||||||
|
var found []*guiWidget
|
||||||
|
|
||||||
|
if tk.WidgetType() == widget.Window {
|
||||||
|
found = append(found, tk)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range tk.children {
|
||||||
|
found = append(found, child.findWindows()...)
|
||||||
|
}
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
|
||||||
|
// used by gocui.TabKey to rotate through the windows
|
||||||
|
func findNextWindow() *guiWidget {
|
||||||
|
var found bool
|
||||||
|
if len(me.allwin) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, tk := range me.allwin {
|
||||||
|
if tk.window.active {
|
||||||
|
found = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if found {
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// at the end, loop to the beginning
|
||||||
|
return me.allwin[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the window under the mouse and only the window under the mouse
|
||||||
|
func findWindowUnderMouse() *guiWidget {
|
||||||
|
w, h := me.baseGui.MousePosition()
|
||||||
|
|
||||||
|
if len(me.allwin) != len(findWindows()) {
|
||||||
|
me.allwin = findWindows()
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the stdout window is on top, check it first
|
||||||
|
if me.stdout.outputOnTop {
|
||||||
|
if me.stdout.tk.full.inRect(w, h) {
|
||||||
|
// log.Info(fmt.Sprintf("findWindowUnderMouse() found %s stdout on top (%dx%d)", me.stdout.tk.cuiName, w, h))
|
||||||
|
return me.stdout.tk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// print out the window list
|
||||||
|
for _, tk := range me.allwin {
|
||||||
|
log.Info("findWindowUnderMouse() print:", tk.labelN, tk.window.active, tk.window.order)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// now check if the active window is below the mouse
|
||||||
|
for _, tk := range me.allwin {
|
||||||
|
if tk.window.active {
|
||||||
|
if tk.full.inRect(w, h) {
|
||||||
|
// log.Info(fmt.Sprintf("findWindowUnderMouse() found %s active window (%dx%d)", tk.cuiName, w, h))
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// well, just find any window then
|
||||||
|
// sorting by order might work?
|
||||||
|
slices.SortFunc(me.allwin, func(a, b *guiWidget) int {
|
||||||
|
return a.window.order - b.window.order
|
||||||
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
|
// print out the window list
|
||||||
|
for _, tk := range me.allwin {
|
||||||
|
log.Info("findWindowUnderMouse() print:", tk.labelN, tk.window.active, tk.window.order)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
for _, win := range me.allwin {
|
||||||
|
if win.full.inRect(w, h) {
|
||||||
|
// log.Info(fmt.Sprintf("findWindowUnderMouse() found %s window (%dx%d)", win.cuiName, w, h))
|
||||||
|
return win
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// okay, no window. maybe the stdout is there?
|
||||||
|
if me.stdout.tk.full.inRect(w, h) {
|
||||||
|
// log.Info(fmt.Sprintf("findWindowUnderMouse() found %s stdout (%dx%d)", me.stdout.tk.cuiName, w, h))
|
||||||
|
return me.stdout.tk
|
||||||
|
}
|
||||||
|
|
||||||
|
// geez. nothing! maybe auto return stdout?
|
||||||
|
log.Info("findWindowUnderMouse() no window found at", w, h)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) findParentWindow() *guiWidget {
|
||||||
|
if tk.WidgetType() == widget.Window {
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
if tk.parent == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return tk.parent.findParentWindow()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) findWidgetByName(name string) *guiWidget {
|
||||||
|
if tk.cuiName == name {
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
for _, child := range tk.children {
|
||||||
|
found := child.findWidgetByName(name)
|
||||||
|
if found != nil {
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) findWidgetByView(v *gocui.View) *guiWidget {
|
||||||
|
if tk.v == v {
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
if tk.cuiName == v.Name() {
|
||||||
|
log.Log(NOW, "findWidget() error. names are mismatched or out of sync", tk.cuiName)
|
||||||
|
log.Log(NOW, "findWidget() or maybe the view has been deleted")
|
||||||
|
// return tk
|
||||||
|
}
|
||||||
|
for _, child := range tk.children {
|
||||||
|
found := child.findWidgetByView(v)
|
||||||
|
if found != nil {
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) findWidgetById(id int) *guiWidget {
|
||||||
|
if tk.WidgetId() == id {
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
for _, child := range tk.children {
|
||||||
|
found := child.findWidgetById(id)
|
||||||
|
if found != nil {
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
80
help.go
80
help.go
|
@ -1,4 +1,7 @@
|
||||||
// Copyright 2014 The gocui Authors. All rights reserved.
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
// Prior Copyright 2014 The gocui Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
@ -10,32 +13,47 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
"github.com/awesome-gocui/gocui"
|
||||||
|
log "go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var helpText []string = []string{"KEYBINDINGS",
|
/*
|
||||||
|
This in helpText doesn't print
|
||||||
|
|
||||||
|
"\x1b[0;32m \x1b[0m", // this was a test to see what might be
|
||||||
|
// possible with gocui. it doesn't seem to work for me
|
||||||
|
*/
|
||||||
|
|
||||||
|
var helpText []string = []string{"Help Menu",
|
||||||
"",
|
"",
|
||||||
"?: toggle help",
|
"Tab: toggle through windows",
|
||||||
"d: toggle debugging",
|
"O: toggle STDOUT",
|
||||||
"r: redraw widgets",
|
"H: toggle this gocui menu",
|
||||||
"s/h: show/hide all widgets",
|
"L: toggle light/dark mode",
|
||||||
"L: list all widgets",
|
"CTRL-c: quit()",
|
||||||
"M: list all widgets positions",
|
|
||||||
"\x1b[0;32m \x1b[0m",
|
|
||||||
"q: quit()",
|
|
||||||
"p: panic()",
|
|
||||||
"o: show Stdout",
|
|
||||||
"l: log to /tmp/witgui.log",
|
|
||||||
"Ctrl-D: Toggle Debugging",
|
|
||||||
"Ctrl-V: Toggle Verbose Debugging",
|
|
||||||
"Ctrl-C: Exit",
|
|
||||||
"",
|
"",
|
||||||
|
"Debugging:",
|
||||||
|
"S: Supermouse mode",
|
||||||
|
"M: list all widget positions",
|
||||||
|
"L: list all widgets in tree",
|
||||||
}
|
}
|
||||||
|
|
||||||
func hidehelplayout() {
|
func hideHelp() {
|
||||||
|
if me.showHelp {
|
||||||
|
log.Info("help is already down")
|
||||||
|
me.showHelp = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
me.showHelp = true
|
||||||
me.baseGui.DeleteView("help")
|
me.baseGui.DeleteView("help")
|
||||||
}
|
}
|
||||||
|
|
||||||
func helplayout() error {
|
func showHelp() error {
|
||||||
|
if !me.showHelp {
|
||||||
|
log.Info("help is already up")
|
||||||
|
me.showHelp = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
me.showHelp = false
|
||||||
g := me.baseGui
|
g := me.baseGui
|
||||||
var err error
|
var err error
|
||||||
maxX, _ := g.Size()
|
maxX, _ := g.Size()
|
||||||
|
@ -47,17 +65,18 @@ func helplayout() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
help, err := g.SetView("help", maxX-(newW+me.FramePadW), 0, maxX-1, len(helpText)+me.FramePadH, 0)
|
a := maxX - (newW + me.FramePadW)
|
||||||
|
b := me.notify.help.offsetH
|
||||||
|
c := maxX - 1
|
||||||
|
d := me.notify.help.offsetH + len(helpText) + me.FramePadH
|
||||||
|
|
||||||
|
help, err := g.SetView("help", a, b, c, d, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
if !errors.Is(err, gocui.ErrUnknownView) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
help.SelBgColor = gocui.ColorGreen
|
help.SelBgColor = gocui.ColorGreen
|
||||||
help.SelFgColor = gocui.ColorBlack
|
help.SelFgColor = gocui.ColorBlack
|
||||||
// fmt.Fprintln(help, "Enter: Click Button")
|
|
||||||
// fmt.Fprintln(help, "Tab/Space: Switch Buttons")
|
|
||||||
// fmt.Fprintln(help, "Backspace: Delete Button")
|
|
||||||
// fmt.Fprintln(help, "Arrow keys: Move Button")
|
|
||||||
|
|
||||||
fmt.Fprintln(help, strings.Join(helpText, "\n"))
|
fmt.Fprintln(help, strings.Join(helpText, "\n"))
|
||||||
|
|
||||||
|
@ -65,6 +84,21 @@ func helplayout() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
g.SetViewOnTop("help")
|
||||||
me.helpLabel = help
|
me.helpLabel = help
|
||||||
|
|
||||||
|
/*
|
||||||
|
if me.treeRoot == nil {
|
||||||
|
log.Info("gogui makeClock() error. treeRoot == nil")
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
if me.stdout.tk == nil {
|
||||||
|
makeOutputWidget(me.baseGui, "made this in showHelp()")
|
||||||
|
msg := fmt.Sprintf("test to stdout from in showHelp() %d\n", me.ecount)
|
||||||
|
me.stdout.Write([]byte(msg))
|
||||||
|
log.Log(NOW, "log.log(NOW) test")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,425 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
// Copyright 2014 The gocui Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
|
"runtime/pprof"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
"go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// sent via -ldflags
|
||||||
|
var VERSION string
|
||||||
|
var BUILDTIME string
|
||||||
|
|
||||||
|
var PLUGIN string = "gocui"
|
||||||
|
|
||||||
|
// this is called at the very initial connection
|
||||||
|
// between the app and this gocui plugin
|
||||||
|
// this is a good place to initialize gocui's default behavior
|
||||||
|
func toolkitInit() {
|
||||||
|
log.Log(INFO, "gocui toolkitInit() me.ok =", me.ok)
|
||||||
|
if me.baseGui == nil {
|
||||||
|
log.Info("gocui baseGui is still nil")
|
||||||
|
standardExit()
|
||||||
|
}
|
||||||
|
if me.treeRoot == nil {
|
||||||
|
log.Info("gocui treeRoot is still nil")
|
||||||
|
standardExit()
|
||||||
|
}
|
||||||
|
// w := me.treeRoot.TK.(*guiWidget)
|
||||||
|
// w.dumpTree("MM")
|
||||||
|
// w.dumpWindows("WW")
|
||||||
|
|
||||||
|
// SETUP HELP START
|
||||||
|
me.baseGui.Update(testRefresh)
|
||||||
|
log.Log(INFO, "gocui toolkitInit() trying showHelp() me.ok =", me.ok)
|
||||||
|
showHelp()
|
||||||
|
hideHelp()
|
||||||
|
// SETUP HELP END
|
||||||
|
|
||||||
|
// SETUP STDOUT START
|
||||||
|
if me.stdout.tk == nil {
|
||||||
|
makeOutputWidget(me.baseGui, "from setThingsOnTop()")
|
||||||
|
}
|
||||||
|
|
||||||
|
// time.Sleep(300 * time.Millisecond)
|
||||||
|
log.Log(INFO, "gocui toolkitInit() me.ok =", me.ok)
|
||||||
|
me.baseGui.Update(testRefresh)
|
||||||
|
if !me.stdout.init {
|
||||||
|
log.Log(INFO, "gocui toolkitInit() stdout.Init me.ok =", me.ok)
|
||||||
|
me.stdout.init = true
|
||||||
|
relocateStdoutOffscreen()
|
||||||
|
}
|
||||||
|
// time.Sleep(1 * time.Second)
|
||||||
|
me.stdout.outputOnTop = false
|
||||||
|
setThingsOnTop()
|
||||||
|
// SETUP STDOUT END
|
||||||
|
|
||||||
|
// SETUP BG
|
||||||
|
if me.BG.tk == nil {
|
||||||
|
me.BG.tk = makeNewInternalWidget(me.BG.wId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SETUP libnotify clock and menu
|
||||||
|
me.notify.clock.once.Do(makeNotifyClock)
|
||||||
|
me.notify.icon.once.Do(makeNotifyIcon)
|
||||||
|
|
||||||
|
// TODO: for some reason, this makes the background doesn't display
|
||||||
|
// PUT INIT DEBUG COOE HERE
|
||||||
|
var toggle bool
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
// enable this to show early debugging
|
||||||
|
// w := me.treeRoot.TK.(*guiWidget)
|
||||||
|
// w.dumpTree("MM")
|
||||||
|
// w.dumpWindows("WW")
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
if toggle {
|
||||||
|
toggle = false
|
||||||
|
// log.Info("gocui toolkitInit() put testing true stuff here")
|
||||||
|
} else {
|
||||||
|
toggle = true
|
||||||
|
// log.Info("gocui toolkitInit() put testing false stuff here")
|
||||||
|
}
|
||||||
|
setBottomBG()
|
||||||
|
}
|
||||||
|
// PUT INIT DEBUG COOE HERE END
|
||||||
|
|
||||||
|
// TEST TEXTBOX START
|
||||||
|
// time.Sleep(1 * time.Second)
|
||||||
|
log.Log(INFO, "gocui toolkitInit() me.ok =", me.ok)
|
||||||
|
me.baseGui.Update(testRefresh)
|
||||||
|
if me.textbox.tk == nil {
|
||||||
|
log.Log(INFO, "gocui toolkitInit() initTextbox me.ok =", me.ok)
|
||||||
|
initTextbox()
|
||||||
|
me.textbox.tk.prepTextbox()
|
||||||
|
}
|
||||||
|
// TEST TEXTBOX END
|
||||||
|
}
|
||||||
|
|
||||||
|
func toolkitClose() {
|
||||||
|
me.baseGui.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// a GO GUI plugin should initTree in init()
|
||||||
|
// this should be done before the application
|
||||||
|
// starts trying to open up a channel
|
||||||
|
func init() {
|
||||||
|
me.myTree = initTree()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets defaults and establishes communication
|
||||||
|
// to this toolkit from the wit/gui golang package
|
||||||
|
func initPlugin() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
fmt.Fprintf(me.outf, "PANIC: initPlugin() recovered %v\n", r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// read in defaults from config protobuf
|
||||||
|
if val, err := me.myTree.ConfigFind("stdout"); err == nil {
|
||||||
|
if val == "true" {
|
||||||
|
me.stdout.startOnscreen = true
|
||||||
|
// me.stdout.Write([]byte("starting with stdout onscreen\n"))
|
||||||
|
}
|
||||||
|
if val == "disable" {
|
||||||
|
log.Log(INFO, "gocui: attempt to COMPLETELY DISABLE STDOUT LOG")
|
||||||
|
me.stdout.disable = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if val, err := me.myTree.ConfigFind("dark"); err == nil {
|
||||||
|
if val == "true" {
|
||||||
|
me.dark = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// macos iterm2 really only works with dark mode right now
|
||||||
|
if runtime.GOOS == "macos" {
|
||||||
|
me.dark = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// todo: make this a tmp file that goes away
|
||||||
|
if !me.stdout.disable {
|
||||||
|
log.Log(INFO, "stdout.disable == true. writing to /tmp/captureMode.log")
|
||||||
|
me.outf, err = os.OpenFile("/tmp/captureMode.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("error opening file:", err)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
// todo: some early output still goes to the /tmp/ file
|
||||||
|
//time.Sleep(200 * time.Millisecond)
|
||||||
|
log.CaptureMode(me.stdout)
|
||||||
|
}
|
||||||
|
me.starttime = time.Now()
|
||||||
|
log.Log(INFO, "Init() of awesome-gocui")
|
||||||
|
|
||||||
|
// init the config struct default values
|
||||||
|
Set(&me, "default")
|
||||||
|
|
||||||
|
// initial app window settings
|
||||||
|
|
||||||
|
// initial stdout window settings
|
||||||
|
me.stdout.w = 180
|
||||||
|
me.stdout.h = 40
|
||||||
|
me.stdout.lastW = 4
|
||||||
|
me.stdout.lastH = 20
|
||||||
|
|
||||||
|
// just make up unique values for these
|
||||||
|
me.dropdown.wId = -77
|
||||||
|
me.textbox.wId = -55
|
||||||
|
me.stdout.wId = -4
|
||||||
|
me.BG.wId = -22
|
||||||
|
|
||||||
|
// the clock widget id and offset
|
||||||
|
me.notify.clock.wId = -5
|
||||||
|
me.notify.clock.offsetW = 13
|
||||||
|
me.notify.clock.offsetH = 1
|
||||||
|
|
||||||
|
me.notify.icon.wId = -6
|
||||||
|
me.notify.icon.offsetW = 4
|
||||||
|
me.notify.icon.offsetH = 1
|
||||||
|
|
||||||
|
me.notify.help.wId = -7
|
||||||
|
me.notify.help.offsetH = 3
|
||||||
|
|
||||||
|
Set(&me.dropdown, "default")
|
||||||
|
// s := fmt.Sprintln("fake default check =", me.FakeW, "dropdown.Id", me.dropdown.Id)
|
||||||
|
// me.stdout.Write([]byte(s))
|
||||||
|
|
||||||
|
me.mouse.mouseUp = true
|
||||||
|
me.mouse.clicktime = time.Millisecond * 200
|
||||||
|
me.mouse.doubletime = time.Millisecond * 400
|
||||||
|
|
||||||
|
me.newWindowTrigger = make(chan *guiWidget, 1)
|
||||||
|
go newWindowTrigger()
|
||||||
|
go refreshGocui()
|
||||||
|
|
||||||
|
log.Log(NOW, "Init() start pluginChan")
|
||||||
|
if me.stdout.disable {
|
||||||
|
log.Info("Using STDOUT")
|
||||||
|
} else {
|
||||||
|
log.Info("Using gocui STDOUT")
|
||||||
|
os.Stdout = me.outf
|
||||||
|
log.CaptureMode(me.outf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// init gocui
|
||||||
|
g, err := gocui.NewGui(gocui.OutputNormal, true)
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(-1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
me.baseGui = g
|
||||||
|
g.Cursor = true
|
||||||
|
g.Mouse = true
|
||||||
|
|
||||||
|
// this sets the function that is run on every event. For example:
|
||||||
|
// When you click the mouse, move the mouse, or press a key on the keyboard
|
||||||
|
// This is equivalent to xev or similar to cat /dev/input on linux
|
||||||
|
g.SetManagerFunc(gocuiEvent)
|
||||||
|
|
||||||
|
// register how the 'gocui' will work as a GO toolkit plugin
|
||||||
|
// all applications will use these keys. they are universal.
|
||||||
|
// registered event handlers still have the events sent to gocuiEvent() above
|
||||||
|
registerHandlers(g)
|
||||||
|
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
if me.outf != nil {
|
||||||
|
fmt.Fprintln(me.outf, "hello world", time.Since(me.starttime))
|
||||||
|
}
|
||||||
|
|
||||||
|
// coreStdout()
|
||||||
|
// createStdout(g)
|
||||||
|
// tell 'tree' that we are okay to start talking to
|
||||||
|
me.myTree.InitOK()
|
||||||
|
me.ok = true // this tells init() it's okay to work with gocui
|
||||||
|
|
||||||
|
go gocuiMain()
|
||||||
|
}
|
||||||
|
|
||||||
|
// This goroutine sits in gocui's MainLoop()
|
||||||
|
func gocuiMain() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Warn("PANIC ecovered in gocuiMain()", r)
|
||||||
|
if me.outf == nil {
|
||||||
|
debug.PrintStack()
|
||||||
|
pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
|
||||||
|
panic(os.Stdout)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(me.outf, "PANIC recovered in r = %v", r)
|
||||||
|
os.Stderr = me.outf
|
||||||
|
os.Stdout = me.outf
|
||||||
|
debug.PrintStack()
|
||||||
|
pprof.Lookup("goroutine").WriteTo(me.outf, 1)
|
||||||
|
panic(me.outf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// me.stdout.Write([]byte("begin gogui.MainLoop()\n"))
|
||||||
|
if err := me.baseGui.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
|
||||||
|
log.Log(NOW, "g.MainLoop() panic err =", err)
|
||||||
|
// normally panic here
|
||||||
|
panic("gocuiTKmainloop OOPS")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardExit() {
|
||||||
|
log.Log(NOW, "standardExit() doing baseGui.Close()")
|
||||||
|
me.baseGui.Close()
|
||||||
|
if me.outf != nil {
|
||||||
|
log.Log(NOW, "standardExit() doing outf.Close()")
|
||||||
|
me.outf.Close()
|
||||||
|
}
|
||||||
|
// log(true, "standardExit() setOutput(os.Stdout)")
|
||||||
|
// setOutput(os.Stdout)
|
||||||
|
log.Log(NOW, "standardExit() send back Quit()")
|
||||||
|
// go sendBackQuit() // don't stall here in case the
|
||||||
|
// induces a delay in case the callback channel is broken
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
log.Log(NOW, "standardExit() exit()")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardClose() {
|
||||||
|
log.Log(NOW, "standardExit() doing baseGui.Close()")
|
||||||
|
me.baseGui.Close()
|
||||||
|
log.Log(NOW, "standardExit() doing outf.Close()")
|
||||||
|
me.outf.Close()
|
||||||
|
// os.Stdin = os.Stdin
|
||||||
|
// os.Stdout = os.Stdout
|
||||||
|
// os.Stderr = os.Stderr
|
||||||
|
log.Log(NOW, "standardExit() send back Quit()")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// this hack is to wait for the application to send something
|
||||||
|
// before trying to do anything. todo: rethink this someday
|
||||||
|
func waitOK() {
|
||||||
|
for {
|
||||||
|
if me.ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this hack is to wait for the application to send something
|
||||||
|
// before trying to do anything. todo: rethink this someday
|
||||||
|
func waitFirstWindow() {
|
||||||
|
for {
|
||||||
|
if me.firstWindowOk {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// empty function. this triggers gocui to refresh the screen
|
||||||
|
func testRefresh(*gocui.Gui) error {
|
||||||
|
// log.Info("in testRefresh")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// refresh the screen 10 times a second
|
||||||
|
func refreshGocui() {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if me.outf == nil {
|
||||||
|
log.Info("INIT PLUGIN recovered in r", r)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintln(me.outf, "INIT PLUGIN recovered in r", r)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var lastRefresh time.Time
|
||||||
|
lastRefresh = time.Now()
|
||||||
|
me.refresh = false
|
||||||
|
for {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
// log.Info("refresh checking ok")
|
||||||
|
if !me.ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// redraw the windows if something has changed
|
||||||
|
if time.Since(lastRefresh) > 1000*time.Millisecond {
|
||||||
|
if me.refresh {
|
||||||
|
log.Log(NOW, "newWindowTrigger() sending refresh to channel")
|
||||||
|
me.newWindowTrigger <- me.treeRoot.TK.(*guiWidget)
|
||||||
|
me.refresh = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this code updates the clock
|
||||||
|
if time.Since(lastRefresh) > 1000*time.Millisecond {
|
||||||
|
// artificially pause clock while dragging.
|
||||||
|
// this is a reminder to make this refresh code smarter
|
||||||
|
// after the switch to protocol buffers
|
||||||
|
me.myTree.Lock()
|
||||||
|
if me.mouse.mouseUp {
|
||||||
|
// log.Info("refresh now on mouseUp")
|
||||||
|
// todo: add logic here to see if the application has changed anything
|
||||||
|
libNotifyUpdate()
|
||||||
|
lastRefresh = time.Now()
|
||||||
|
} else {
|
||||||
|
if time.Since(lastRefresh) > 3*time.Second {
|
||||||
|
libNotifyUpdate()
|
||||||
|
lastRefresh = time.Now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
me.myTree.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the widget start width & height
|
||||||
|
|
||||||
|
func newWindowTrigger() {
|
||||||
|
// log.Log(NOW, "newWindowTriggerl() START")
|
||||||
|
for {
|
||||||
|
// log.Log(NOW, "GO plugin toolkit made a new window")
|
||||||
|
select {
|
||||||
|
case tk := <-me.newWindowTrigger:
|
||||||
|
// log.Log(NOW, "newWindowTrigger() got new window", tk.cuiName)
|
||||||
|
// time.Sleep(200 * time.Millisecond)
|
||||||
|
waitOK()
|
||||||
|
me.myTree.Lock()
|
||||||
|
// time.Sleep(200 * time.Millisecond)
|
||||||
|
redoWindows(me.FirstWindowW, me.FirstWindowH)
|
||||||
|
me.firstWindowOk = true
|
||||||
|
if !me.stdout.init {
|
||||||
|
me.stdout.init = true
|
||||||
|
relocateStdoutOffscreen()
|
||||||
|
}
|
||||||
|
if me.textbox.tk == nil {
|
||||||
|
initTextbox()
|
||||||
|
me.textbox.tk.prepTextbox()
|
||||||
|
}
|
||||||
|
tk.makeWindowActive()
|
||||||
|
me.myTree.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
200
keybindings.go
200
keybindings.go
|
@ -1,200 +0,0 @@
|
||||||
// Copyright 2014 The gocui Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
|
||||||
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// handle ctrl+z
|
|
||||||
func handle_ctrl_z(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
gocui.Suspend()
|
|
||||||
log.Info("got ctrl+z")
|
|
||||||
syscall.Kill(syscall.Getpid(), syscall.SIGSTOP)
|
|
||||||
gocui.Resume()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func defaultKeybindings(g *gocui.Gui) error {
|
|
||||||
if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// setup ctrl+z
|
|
||||||
keyForced, modForced := gocui.MustParse("ctrl+z")
|
|
||||||
if err := g.SetKeybinding("", keyForced, modForced, handle_ctrl_z); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
for _, n := range []string{"but1", "but2", "help", "but3"} {
|
|
||||||
if err := g.SetKeybinding(n, gocui.MouseLeft, gocui.ModNone, showMsg); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, mouseUp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// mouseDown() runs whenever you click on an unknown view (?)
|
|
||||||
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, mouseDown); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModMouseCtrl, ctrlDown); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// if err := g.SetKeybinding(w.v.Name(), gocui.MouseLeft, gocui.ModNone, click); err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
/*
|
|
||||||
if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, globalDown); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if err := g.SetKeybinding("msg", gocui.MouseLeft, gocui.ModNone, msgDown); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
addDebugKeys(g)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func addDebugKeys(g *gocui.Gui) {
|
|
||||||
// show debugging buttons
|
|
||||||
g.SetKeybinding("", 'd', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
fakeStartWidth = me.FakeW
|
|
||||||
fakeStartHeight = me.TabH + me.FramePadH
|
|
||||||
if showDebug {
|
|
||||||
showFake()
|
|
||||||
showDebug = false
|
|
||||||
} else {
|
|
||||||
hideFake()
|
|
||||||
showDebug = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// display the help menu
|
|
||||||
g.SetKeybinding("", '?', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
if showHelp {
|
|
||||||
helplayout()
|
|
||||||
showHelp = false
|
|
||||||
if me.dropdownV == nil {
|
|
||||||
me.dropdownV = makeDropdownView("addWidget() ddview")
|
|
||||||
}
|
|
||||||
me.dropdownV.Show()
|
|
||||||
} else {
|
|
||||||
me.baseGui.DeleteView("help")
|
|
||||||
showHelp = true
|
|
||||||
me.dropdownV.Hide()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// redraw all the widgets
|
|
||||||
g.SetKeybinding("", 'r', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
var w *guiWidget
|
|
||||||
w = me.treeRoot.TK.(*guiWidget)
|
|
||||||
if redoWidgets {
|
|
||||||
wRoot := me.treeRoot.TK.(*guiWidget)
|
|
||||||
wRoot.redoWindows(0, 0)
|
|
||||||
redoWidgets = false
|
|
||||||
} else {
|
|
||||||
w.hideWidgets()
|
|
||||||
redoWidgets = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// hide all widgets
|
|
||||||
g.SetKeybinding("", 'h', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
var w *guiWidget
|
|
||||||
w = me.treeRoot.TK.(*guiWidget)
|
|
||||||
w.hideWidgets()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// show all widgets
|
|
||||||
g.SetKeybinding("", 's', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
var w *guiWidget
|
|
||||||
w = me.treeRoot.TK.(*guiWidget)
|
|
||||||
w.showWidgets()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// list all widgets
|
|
||||||
g.SetKeybinding("", 'L', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
me.treeRoot.ListWidgets()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// list all widgets with positions
|
|
||||||
g.SetKeybinding("", 'M', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
w := me.treeRoot.TK.(*guiWidget)
|
|
||||||
w.dumpTree("M")
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// redo windows
|
|
||||||
g.SetKeybinding("", 'w', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
wRoot := me.treeRoot.TK.(*guiWidget)
|
|
||||||
wRoot.redoWindows(0, 0)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// log to output window
|
|
||||||
g.SetKeybinding("", 'o', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
log.Log(ERROR, "TODO: re-implement this")
|
|
||||||
/*
|
|
||||||
if me.logStdout.Visible() {
|
|
||||||
me.logStdout.SetVisible(false)
|
|
||||||
// setOutput(os.Stdout)
|
|
||||||
} else {
|
|
||||||
me.logStdout.SetVisible(true)
|
|
||||||
// setOutput(me.logStdout.tk)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// exit
|
|
||||||
g.SetKeybinding("", 'q', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
standardExit()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
standardExit()
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
if showDebug {
|
|
||||||
me.myTree.SendEnableDebugger()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
g.SetKeybinding("", gocui.KeyCtrlV, gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// panic
|
|
||||||
g.SetKeybinding("", 'p', gocui.ModNone,
|
|
||||||
func(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
standardClose()
|
|
||||||
panic("forced panic in gocui")
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -0,0 +1,292 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
// this file implements a libnotify-like menu
|
||||||
|
// also there is SIGWINCH resizing
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
log "go.wit.com/log"
|
||||||
|
"go.wit.com/toolkits/tree"
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
// create a new widget in the binary tree
|
||||||
|
func makeNewInternalWidget(wId int) *guiWidget {
|
||||||
|
if me.treeRoot == nil {
|
||||||
|
log.Info("GOGUI Init ERROR. treeRoot == nil")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n := new(tree.Node)
|
||||||
|
n.WidgetType = widget.Flag
|
||||||
|
n.WidgetId = wId
|
||||||
|
n.ParentId = 0
|
||||||
|
|
||||||
|
// store the internal toolkit information
|
||||||
|
tk := new(guiWidget)
|
||||||
|
tk.frame = true
|
||||||
|
|
||||||
|
tk.node = n
|
||||||
|
tk.node.Parent = me.treeRoot
|
||||||
|
|
||||||
|
// set the name used by gocui to the id
|
||||||
|
tk.cuiName = fmt.Sprintf("%d DR", wId)
|
||||||
|
|
||||||
|
tk.setColorInput()
|
||||||
|
|
||||||
|
// add this new widget on the binary tree
|
||||||
|
tk.parent = me.treeRoot.TK.(*guiWidget)
|
||||||
|
if tk.parent == nil {
|
||||||
|
panic("makeNewFlagWidget() didn't get treeRoot guiWidget")
|
||||||
|
} else {
|
||||||
|
tk.parent.children = append(tk.parent.children, tk)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.TK = tk
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNotifyClock() {
|
||||||
|
if me.treeRoot == nil {
|
||||||
|
log.Info("gogui makeClock() error. treeRoot == nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
me.notify.clock.tk = makeNewInternalWidget(me.notify.clock.wId)
|
||||||
|
// me.notify.clock.tk.dumpWidget("init() clock")
|
||||||
|
me.notify.clock.tk.MoveToOffset(0, 0)
|
||||||
|
me.notify.clock.tk.labelN = time.Now().Format("15:04:05")
|
||||||
|
me.notify.clock.tk.frame = false
|
||||||
|
me.notify.clock.tk.setColorLabel()
|
||||||
|
me.notify.clock.tk.Show()
|
||||||
|
me.notify.clock.active = true
|
||||||
|
// me.notify.clock.tk.dumpWidget("notifyClock()")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeNotifyIcon() {
|
||||||
|
if me.treeRoot == nil {
|
||||||
|
log.Info("gogui makeClock() error. treeRoot == nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
me.notify.icon.tk = makeNewInternalWidget(me.notify.icon.wId)
|
||||||
|
// me.notify.icon.tk.dumpWidget("init() menu")
|
||||||
|
w, _ := me.baseGui.Size()
|
||||||
|
me.notify.icon.tk.MoveToOffset(w-5, me.notify.icon.offsetH)
|
||||||
|
me.notify.icon.tk.labelN = "[ ]"
|
||||||
|
me.notify.icon.tk.frame = false
|
||||||
|
me.notify.icon.tk.setColorNotifyIcon()
|
||||||
|
me.notify.icon.tk.Show()
|
||||||
|
me.notify.icon.active = true
|
||||||
|
// me.notify.icon.tk.dumpWidget("notifyIcon()")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func libNotifyUpdate() {
|
||||||
|
if me.baseGui == nil {
|
||||||
|
log.Info("libNotifyUpdate error baseGui == nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// refresh GOCUI
|
||||||
|
me.baseGui.Update(testRefresh)
|
||||||
|
// me.baseGui.UpdateAsync(testRefresh) // Async option. probably don't need this?
|
||||||
|
|
||||||
|
if me.notify.clock.tk == nil {
|
||||||
|
log.Info("libNotifyUpdate error clock.tk == nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for SIGWINCH. If so, move the libnotify clock
|
||||||
|
w, h := me.baseGui.Size()
|
||||||
|
if me.winchW != w || me.winchH != h {
|
||||||
|
if me.winchW == 0 && me.winchH == 0 {
|
||||||
|
// this isn't really SIGWINCH. This is the app starting
|
||||||
|
} else {
|
||||||
|
log.Printf("gocui: long live SIGWINCH! (w,h) is now (%d,%d)\n", w, h)
|
||||||
|
}
|
||||||
|
me.winchW = w
|
||||||
|
me.winchH = h
|
||||||
|
me.notify.clock.tk.MoveToOffset(w-me.notify.clock.offsetW, me.notify.clock.offsetH)
|
||||||
|
me.notify.clock.tk.Hide()
|
||||||
|
me.notify.clock.tk.Show()
|
||||||
|
|
||||||
|
sigWinchBG()
|
||||||
|
sigWinchIcon()
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the time
|
||||||
|
me.notify.clock.tk.v.Clear()
|
||||||
|
me.notify.clock.tk.labelN = time.Now().Format("15:04:05")
|
||||||
|
me.notify.clock.tk.v.WriteString(me.notify.clock.tk.labelN)
|
||||||
|
hardDrawAtgocuiSize(me.notify.clock.tk)
|
||||||
|
// hardDrawUnderMouse(me.notify.clock.tk, "clock")
|
||||||
|
// log.Info("libNotifyUpdate updated clock", me.notify.clock.tk.labelN)
|
||||||
|
|
||||||
|
if me.notify.icon.tk == nil {
|
||||||
|
log.Info("libNotifyUpdate error menu.tk == nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if me.notify.icon.tk.v == nil {
|
||||||
|
log.Info("libNotifyUpdate error menu.tk.v == nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the menu
|
||||||
|
hardDrawAtgocuiSize(me.notify.icon.tk)
|
||||||
|
me.notify.icon.tk.setColorNotifyIcon()
|
||||||
|
me.baseGui.SetViewOnTop(me.notify.icon.tk.v.Name())
|
||||||
|
me.baseGui.SetViewOnTop(me.notify.clock.tk.v.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func setNotifyIconText(s string) {
|
||||||
|
me.notify.icon.tk.v.Clear()
|
||||||
|
me.notify.icon.tk.labelN = s
|
||||||
|
me.notify.icon.tk.v.WriteString(me.notify.icon.tk.labelN)
|
||||||
|
hardDrawAtgocuiSize(me.notify.icon.tk)
|
||||||
|
me.notify.icon.tk.setColorNotifyIcon()
|
||||||
|
me.baseGui.SetViewOnTop(me.notify.icon.tk.v.Name())
|
||||||
|
log.Info("setNotifyIconText() updated menu to:", me.notify.icon.tk.labelN)
|
||||||
|
}
|
||||||
|
|
||||||
|
// in the very end of redrawing things, this will place the help and stdout on the top or botton
|
||||||
|
// depending on the state the user has chosen
|
||||||
|
func setThingsOnTop() {
|
||||||
|
if me.showHelp { // terrible variable name. FIXME
|
||||||
|
// log.Info("help does not exist")
|
||||||
|
} else {
|
||||||
|
me.baseGui.SetViewOnTop("help")
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.notify.clock.tk != nil {
|
||||||
|
me.baseGui.SetViewOnTop(me.notify.clock.tk.v.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.notify.icon.tk != nil {
|
||||||
|
if me.notify.icon.tk.v != nil {
|
||||||
|
me.baseGui.SetViewOnTop(me.notify.icon.tk.v.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.stdout.tk == nil {
|
||||||
|
makeOutputWidget(me.baseGui, "from setThingsOnTop()")
|
||||||
|
}
|
||||||
|
if me.stdout.tk == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if me.stdout.tk.v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if me.dark {
|
||||||
|
me.stdout.tk.v.FgColor = gocui.ColorWhite
|
||||||
|
me.stdout.tk.v.BgColor = gocui.ColorBlack
|
||||||
|
} else {
|
||||||
|
me.stdout.tk.v.FgColor = gocui.ColorBlack
|
||||||
|
me.stdout.tk.v.BgColor = gocui.AttrNone
|
||||||
|
}
|
||||||
|
|
||||||
|
if me.stdout.outputOnTop {
|
||||||
|
me.baseGui.SetViewOnTop("msg")
|
||||||
|
} else {
|
||||||
|
me.baseGui.SetViewOnBottom("msg")
|
||||||
|
}
|
||||||
|
if me.stdout.startOnscreen {
|
||||||
|
// log.Info("THIS TRIGGERS STDOUT") // todo: make a proper init() & move this there
|
||||||
|
me.stdout.tk.relocateStdout(me.stdout.lastW, me.stdout.lastH)
|
||||||
|
me.stdout.startOnscreen = false
|
||||||
|
}
|
||||||
|
setBottomBG()
|
||||||
|
}
|
||||||
|
|
||||||
|
// useful for debuggging
|
||||||
|
func hardDrawUnderMouse(tk *guiWidget, name string) {
|
||||||
|
if tk.v != nil {
|
||||||
|
tk.Hide()
|
||||||
|
}
|
||||||
|
w, h := me.baseGui.MousePosition()
|
||||||
|
a := w
|
||||||
|
b := h
|
||||||
|
c := w + 8
|
||||||
|
d := h + 4
|
||||||
|
var err error
|
||||||
|
tk.v, err = me.baseGui.SetView(tk.cuiName, a, b, c, d, 0)
|
||||||
|
if err == nil {
|
||||||
|
log.Info("hardDrawUnderMouse() err ok widget", tk.cuiName)
|
||||||
|
tk.dumpWidget("hard draw err")
|
||||||
|
}
|
||||||
|
tk.v.Frame = false
|
||||||
|
tk.v.Clear()
|
||||||
|
tk.v.WriteString(tk.labelN + "\n" + name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hardDrawAtgocuiSize(tk *guiWidget) {
|
||||||
|
if tk.v != nil {
|
||||||
|
tk.Hide()
|
||||||
|
}
|
||||||
|
a := tk.gocuiSize.w0
|
||||||
|
b := tk.gocuiSize.h0
|
||||||
|
c := tk.gocuiSize.w1
|
||||||
|
d := tk.gocuiSize.h1
|
||||||
|
var err error
|
||||||
|
tk.v, err = me.baseGui.SetView(tk.cuiName, a, b, c, d, 0)
|
||||||
|
if err == nil {
|
||||||
|
log.Info("hardDrawAtgocuiSize() err ok widget", tk.cuiName)
|
||||||
|
tk.dumpWidget("hard draw err")
|
||||||
|
}
|
||||||
|
tk.v.Frame = false
|
||||||
|
tk.v.Clear()
|
||||||
|
tk.v.WriteString(tk.labelN)
|
||||||
|
log.Verbose("hardDrawAtgocuiSize() err ok widget", tk.cuiName, a, b, c, d, tk.v.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func sigWinchIcon() {
|
||||||
|
w, _ := me.baseGui.Size()
|
||||||
|
me.notify.icon.tk.MoveToOffset(w-me.notify.icon.offsetW, me.notify.icon.offsetH)
|
||||||
|
me.notify.icon.tk.Hide()
|
||||||
|
me.notify.icon.tk.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func sigWinchBG() {
|
||||||
|
tk := me.BG.tk
|
||||||
|
w, h := me.baseGui.Size()
|
||||||
|
a := -1
|
||||||
|
b := -1
|
||||||
|
c := w + 1
|
||||||
|
d := h + 1
|
||||||
|
var err error
|
||||||
|
tk.v, err = me.baseGui.SetView(tk.cuiName, a, b, c, d, 0)
|
||||||
|
if err == nil {
|
||||||
|
if tk.v == nil {
|
||||||
|
tk.dumpWidget("drawView() err")
|
||||||
|
log.Log(ERROR, "drawView() internal plugin error err = nil")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Log(INFO, "background resized to", a, b, c, d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the "BG" widget and set it to the background on the very very bottom
|
||||||
|
func setBottomBG() {
|
||||||
|
if me.BG.tk == nil {
|
||||||
|
log.Info("background tk widget not initialized")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tk := me.BG.tk
|
||||||
|
// log.Info("found BG. setting to bottom", tk.cuiName)
|
||||||
|
if tk.v == nil {
|
||||||
|
sigWinchBG()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if me.dark {
|
||||||
|
tk.v.BgColor = gocui.ColorBlack
|
||||||
|
} else {
|
||||||
|
tk.v.BgColor = gocui.ColorWhite
|
||||||
|
}
|
||||||
|
tk.v.Clear()
|
||||||
|
me.baseGui.SetViewOnBottom(tk.cuiName)
|
||||||
|
w, h := me.baseGui.Size()
|
||||||
|
me.baseGui.SetView(tk.cuiName, -1, -1, w+1, h+1, 0)
|
||||||
|
}
|
7
log.go
7
log.go
|
@ -1,3 +1,6 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -8,8 +11,6 @@ import (
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var outputS []string
|
|
||||||
|
|
||||||
var NOW *log.LogFlag
|
var NOW *log.LogFlag
|
||||||
var INFO *log.LogFlag
|
var INFO *log.LogFlag
|
||||||
var GOCUI *log.LogFlag
|
var GOCUI *log.LogFlag
|
||||||
|
@ -23,7 +24,7 @@ func init() {
|
||||||
full := "go.wit.com/gui"
|
full := "go.wit.com/gui"
|
||||||
short := "gocui"
|
short := "gocui"
|
||||||
|
|
||||||
GOCUI = log.NewFlag("GOCUI", false, full, short, "gocui internals")
|
GOCUI = log.NewFlag("GOCUI", true, full, short, "gocui internals")
|
||||||
|
|
||||||
full = "go.wit.com/toolkits/gocui"
|
full = "go.wit.com/toolkits/gocui"
|
||||||
short = "gocui"
|
short = "gocui"
|
||||||
|
|
192
main.go
192
main.go
|
@ -1,192 +0,0 @@
|
||||||
// Copyright 2014 The gocui Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"runtime/debug"
|
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
|
||||||
"go.wit.com/log"
|
|
||||||
"go.wit.com/toolkits/tree"
|
|
||||||
)
|
|
||||||
|
|
||||||
func queueToolkitClose() {
|
|
||||||
me.baseGui.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func queueSetChecked(n *tree.Node, b bool) {
|
|
||||||
setChecked(n, b)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sets defaults and establishes communication
|
|
||||||
// to this toolkit from the wit/gui golang package
|
|
||||||
func init() {
|
|
||||||
log.Log(INFO, "Init() of awesome-gocui")
|
|
||||||
|
|
||||||
// init the config struct default values
|
|
||||||
Set(&me, "default")
|
|
||||||
|
|
||||||
// Set(&me, "dense")
|
|
||||||
|
|
||||||
me.myTree = tree.New()
|
|
||||||
me.myTree.PluginName = "gocui"
|
|
||||||
|
|
||||||
me.myTree.NodeAction = newaction
|
|
||||||
me.myTree.Add = newAdd
|
|
||||||
me.myTree.SetTitle = newSetTitle
|
|
||||||
me.myTree.SetLabel = newSetLabel
|
|
||||||
me.myTree.SetText = newSetText
|
|
||||||
me.myTree.AddText = newAddText
|
|
||||||
me.myTree.SetChecked = queueSetChecked
|
|
||||||
me.myTree.ToolkitClose = queueToolkitClose
|
|
||||||
|
|
||||||
log.Log(NOW, "Init() start pluginChan")
|
|
||||||
log.Sleep(.1) // probably not needed, but in here for now under development
|
|
||||||
go mainGogui()
|
|
||||||
log.Sleep(.1) // probably not needed, but in here for now under development
|
|
||||||
}
|
|
||||||
|
|
||||||
func standardExit() {
|
|
||||||
log.Log(NOW, "standardExit() doing baseGui.Close()")
|
|
||||||
me.baseGui.Close()
|
|
||||||
log.Log(NOW, "standardExit() doing outf.Close()")
|
|
||||||
outf.Close()
|
|
||||||
// log(true, "standardExit() setOutput(os.Stdout)")
|
|
||||||
// setOutput(os.Stdout)
|
|
||||||
log.Log(NOW, "standardExit() send back Quit()")
|
|
||||||
// go sendBackQuit() // don't stall here in case the
|
|
||||||
// induces a delay in case the callback channel is broken
|
|
||||||
log.Sleep(1)
|
|
||||||
log.Log(NOW, "standardExit() exit()")
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func standardClose() {
|
|
||||||
log.Log(NOW, "standardExit() doing baseGui.Close()")
|
|
||||||
me.baseGui.Close()
|
|
||||||
log.Log(NOW, "standardExit() doing outf.Close()")
|
|
||||||
outf.Close()
|
|
||||||
os.Stdin = os.Stdin
|
|
||||||
os.Stdout = os.Stdout
|
|
||||||
os.Stderr = os.Stderr
|
|
||||||
log.Log(NOW, "standardExit() send back Quit()")
|
|
||||||
}
|
|
||||||
|
|
||||||
var outf *os.File
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
}
|
|
||||||
|
|
||||||
var origStdout *os.File
|
|
||||||
var origStderr *os.File
|
|
||||||
|
|
||||||
func mainGogui() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
log.Warn("YAHOOOO Recovered in guiMain application:", r)
|
|
||||||
log.Warn("Recovered from panic:", r)
|
|
||||||
me.baseGui.Close()
|
|
||||||
me.myTree.SendToolkitPanic()
|
|
||||||
|
|
||||||
// attempts to control STDOUT
|
|
||||||
/*
|
|
||||||
// allow gocui to close if possible, then print stack
|
|
||||||
log.Sleep(1)
|
|
||||||
me.myTree.SendToolkitLoad("nocui")
|
|
||||||
log.Sleep(3)
|
|
||||||
os.Stdout = origStdout
|
|
||||||
os.Stderr = origStderr
|
|
||||||
log.Warn("Stack trace:")
|
|
||||||
debug.PrintStack()
|
|
||||||
|
|
||||||
// attempt to switch to the nocui toolkit
|
|
||||||
log.Sleep(1)
|
|
||||||
// panic("BUMMER")
|
|
||||||
*/
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// attempts to control STDOUT
|
|
||||||
/*
|
|
||||||
var err error
|
|
||||||
log.Log(INFO, "main() start Init()")
|
|
||||||
|
|
||||||
outf, err = os.OpenFile("/tmp/witgui.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err, "error opening file: %v")
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
origStdout = os.Stdout
|
|
||||||
os.Stdout = outf
|
|
||||||
defer outf.Close()
|
|
||||||
|
|
||||||
// setOutput(outf)
|
|
||||||
// log("This is a test log entry")
|
|
||||||
|
|
||||||
ferr, _ := os.OpenFile("/tmp/witgui.err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
|
|
||||||
origStderr = os.Stderr
|
|
||||||
os.Stderr = ferr
|
|
||||||
*/
|
|
||||||
gocuiMain()
|
|
||||||
}
|
|
||||||
|
|
||||||
// This initializes the gocui package
|
|
||||||
// it runs SetManagerFunc which passes every input
|
|
||||||
// event (keyboard, mouse, etc) to the function "gocuiEvent()"
|
|
||||||
func gocuiMain() {
|
|
||||||
defer func() {
|
|
||||||
if r := recover(); r != nil {
|
|
||||||
log.Warn("YAHOOOO Recovered in gocuiMain()", r)
|
|
||||||
log.Warn("Recovered from panic:", r)
|
|
||||||
me.baseGui.Close()
|
|
||||||
|
|
||||||
// allow gocui to close if possible, then print stack
|
|
||||||
log.Sleep(1)
|
|
||||||
os.Stdout = origStdout
|
|
||||||
os.Stderr = origStderr
|
|
||||||
me.myTree.SendToolkitPanic()
|
|
||||||
log.Warn("Stack trace:")
|
|
||||||
debug.PrintStack()
|
|
||||||
// panic("BUMMER 2")
|
|
||||||
|
|
||||||
// attempt to switch to the nocui toolkit
|
|
||||||
log.Sleep(1)
|
|
||||||
me.myTree.SendToolkitLoad("nocui")
|
|
||||||
log.Sleep(3)
|
|
||||||
me.myTree.SendToolkitLoad("nocui")
|
|
||||||
// panic("BUMMER")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
g, err := gocui.NewGui(gocui.OutputNormal, true)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer g.Close()
|
|
||||||
|
|
||||||
me.baseGui = g
|
|
||||||
|
|
||||||
g.Cursor = true
|
|
||||||
g.Mouse = true
|
|
||||||
|
|
||||||
// this sets the function that is run on every event. For example:
|
|
||||||
// When you click the mouse, move the mouse, or press a key on the keyboard
|
|
||||||
// This is equivalent to xev or similar to cat /dev/input on linux
|
|
||||||
g.SetManagerFunc(gocuiEvent)
|
|
||||||
|
|
||||||
if err := defaultKeybindings(g); err != nil {
|
|
||||||
// normally panic here
|
|
||||||
log.Log(NOW, "defaultKeybindings(g) panic err =", err)
|
|
||||||
panic("gocuiTKdefaultkeybindings OOPS")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := g.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
|
|
||||||
log.Log(NOW, "g.MainLoop() panic err =", err)
|
|
||||||
// normally panic here
|
|
||||||
panic("gocuiTKmainloop OOPS")
|
|
||||||
}
|
|
||||||
}
|
|
84
mouse.go
84
mouse.go
|
@ -1,84 +0,0 @@
|
||||||
// Copyright 2014 The gocui Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
|
||||||
|
|
||||||
"go.wit.com/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// this function uses the mouse position to highlight & unhighlight things
|
|
||||||
// this is run every time the user moves the mouse over the terminal window
|
|
||||||
func mouseMove(g *gocui.Gui) {
|
|
||||||
mx, my := g.MousePosition()
|
|
||||||
for _, view := range g.Views() {
|
|
||||||
view.Highlight = false
|
|
||||||
}
|
|
||||||
if v, err := g.ViewByPosition(mx, my); err == nil {
|
|
||||||
v.Highlight = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func msgDown(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
initialMouseX, initialMouseY = g.MousePosition()
|
|
||||||
log.Log(GOCUI, "msgDown() X,Y", initialMouseX, initialMouseY)
|
|
||||||
if vx, vy, _, _, err := g.ViewPosition("msg"); err == nil {
|
|
||||||
xOffset = initialMouseX - vx
|
|
||||||
yOffset = initialMouseY - vy
|
|
||||||
msgMouseDown = true
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mouseUp(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
w, h := g.MousePosition()
|
|
||||||
|
|
||||||
dropdownUnclicked(w, h)
|
|
||||||
if msgMouseDown {
|
|
||||||
msgMouseDown = false
|
|
||||||
if movingMsg {
|
|
||||||
movingMsg = false
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
g.DeleteView("msg")
|
|
||||||
}
|
|
||||||
} else if globalMouseDown {
|
|
||||||
globalMouseDown = false
|
|
||||||
g.DeleteView("globalDown")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mouseDown(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
mx, my := g.MousePosition()
|
|
||||||
if vx0, vy0, vx1, vy1, err := g.ViewPosition("msg"); err == nil {
|
|
||||||
if mx >= vx0 && mx <= vx1 && my >= vy0 && my <= vy1 {
|
|
||||||
return msgDown(g, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
globalMouseDown = true
|
|
||||||
maxX, _ := g.Size()
|
|
||||||
findUnderMouse()
|
|
||||||
msg := fmt.Sprintf("mouseDown() Mouse really down at: %d,%d", mx, my)
|
|
||||||
// dropdownClicked(mx, my)
|
|
||||||
x := mx - len(msg)/2
|
|
||||||
if x < 0 {
|
|
||||||
x = 0
|
|
||||||
} else if x+len(msg)+1 > maxX-1 {
|
|
||||||
x = maxX - 1 - len(msg) - 1
|
|
||||||
}
|
|
||||||
log.Log(GOCUI, "mouseDown() about to write out message to 'globalDown' view. msg =", msg)
|
|
||||||
if v, err := g.SetView("globalDown", x, my-1, x+len(msg)+1, my+1, 0); err != nil {
|
|
||||||
if !errors.Is(err, gocui.ErrUnknownView) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
v.WriteString(msg)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (tk *guiWidget) WidgetType() widget.WidgetType {
|
||||||
|
if tk.node == nil {
|
||||||
|
return widget.Label
|
||||||
|
}
|
||||||
|
return tk.node.WidgetType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) WidgetId() int {
|
||||||
|
return tk.node.WidgetId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) GetLabel() string {
|
||||||
|
return tk.node.GetLabel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) IsEnabled() bool {
|
||||||
|
return tk.node.IsEnabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) Checked() bool {
|
||||||
|
return tk.node.State.Checked
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) Hidden() bool {
|
||||||
|
if tk.node == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if tk.parent == nil {
|
||||||
|
return tk.node.Hidden()
|
||||||
|
}
|
||||||
|
if tk.parent.WidgetId() == 0 {
|
||||||
|
return tk.node.Hidden()
|
||||||
|
}
|
||||||
|
if tk.parent.Hidden() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return tk.node.Hidden()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) Direction() widget.Orientation {
|
||||||
|
return tk.node.State.Direction
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) GridW() int {
|
||||||
|
return tk.node.State.AtW
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) GridH() int {
|
||||||
|
return tk.node.State.AtH
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) SetChecked(b bool) {
|
||||||
|
tk.node.State.Checked = b
|
||||||
|
}
|
208
place.go
208
place.go
|
@ -1,19 +1,45 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
"go.wit.com/toolkits/tree"
|
|
||||||
"go.wit.com/widget"
|
"go.wit.com/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
gocui defines the offset like this:
|
||||||
|
|
||||||
|
width -> increases to the right
|
||||||
|
---------------------------------- hieght
|
||||||
|
| H = 1 | increases
|
||||||
|
| | |
|
||||||
|
| W = 1 W = 18 | |
|
||||||
|
| | v
|
||||||
|
| H = 5 | downwards
|
||||||
|
-------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
// moves the gocui view to the W and H offset on the screen
|
||||||
|
func (tk *guiWidget) MoveToOffset(W, H int) {
|
||||||
|
tk.gocuiSetWH(W, H)
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns where the corner of widget starts (upper left)
|
||||||
|
func (tk *guiWidget) Position() (int, int) {
|
||||||
|
return tk.gocuiSize.w0, tk.gocuiSize.h0
|
||||||
|
}
|
||||||
|
|
||||||
func (w *guiWidget) placeBox(startW int, startH int) {
|
func (w *guiWidget) placeBox(startW int, startH int) {
|
||||||
if w.WidgetType != widget.Box {
|
if w.WidgetType() != widget.Box {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// tk.dumpTree("beforebox")
|
|
||||||
|
|
||||||
|
w.full.w0 = startW
|
||||||
|
w.full.h0 = startH
|
||||||
newW := startW
|
newW := startW
|
||||||
newH := startH
|
newH := startH
|
||||||
|
|
||||||
|
@ -24,7 +50,7 @@ func (w *guiWidget) placeBox(startW int, startH int) {
|
||||||
// re-get the Size (they should not have changed, but maybe they can?)
|
// re-get the Size (they should not have changed, but maybe they can?)
|
||||||
// TODO: figure this out or report that they did
|
// TODO: figure this out or report that they did
|
||||||
sizeW, sizeH = child.Size()
|
sizeW, sizeH = child.Size()
|
||||||
if w.node.State.Direction == widget.Vertical {
|
if w.Direction() == widget.Vertical {
|
||||||
log.Log(INFO, "BOX IS VERTICAL ", w.String(), "newWH()", newW, newH, "child()", sizeW, sizeH, child.String())
|
log.Log(INFO, "BOX IS VERTICAL ", w.String(), "newWH()", newW, newH, "child()", sizeW, sizeH, child.String())
|
||||||
// expand based on the child height
|
// expand based on the child height
|
||||||
newH += sizeH
|
newH += sizeH
|
||||||
|
@ -35,7 +61,6 @@ func (w *guiWidget) placeBox(startW int, startH int) {
|
||||||
}
|
}
|
||||||
log.Log(INFO, "BOX END size(W,H) =", sizeW, sizeH, "new(W,H) =", newW, newH)
|
log.Log(INFO, "BOX END size(W,H) =", sizeW, sizeH, "new(W,H) =", newW, newH)
|
||||||
}
|
}
|
||||||
// tk.dumpTree("afterbox")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tk *guiWidget) placeWidgets(startW int, startH int) (int, int) {
|
func (tk *guiWidget) placeWidgets(startW int, startH int) (int, int) {
|
||||||
|
@ -45,37 +70,57 @@ func (tk *guiWidget) placeWidgets(startW int, startH int) (int, int) {
|
||||||
if me.treeRoot == nil {
|
if me.treeRoot == nil {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
if tk.Hidden() {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
tk.startW = startW
|
tk.startW = startW
|
||||||
tk.startH = startH
|
tk.startH = startH
|
||||||
|
|
||||||
switch tk.WidgetType {
|
switch tk.WidgetType() {
|
||||||
case widget.Window:
|
case widget.Window:
|
||||||
newW := startW
|
tk.full.w0 = startW
|
||||||
newH := startH
|
tk.full.h0 = startH
|
||||||
var maxH int = 0
|
startW += -2
|
||||||
|
startH += 3
|
||||||
for _, child := range tk.children {
|
for _, child := range tk.children {
|
||||||
child.placeWidgets(newW, newH)
|
child.placeWidgets(startW, startH)
|
||||||
sizeW, sizeH := child.Size()
|
sizeW, _ := child.Size()
|
||||||
if sizeW < 20 {
|
startW += sizeW + 4 // add the width to move the next widget over
|
||||||
sizeW = 20
|
|
||||||
}
|
|
||||||
newW += sizeW
|
|
||||||
if sizeH > maxH {
|
|
||||||
maxH = sizeH
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return newW - startW, maxH
|
return startW, startH
|
||||||
case widget.Tab:
|
case widget.Tab:
|
||||||
case widget.Grid:
|
case widget.Grid:
|
||||||
return tk.placeGrid(startW, startH)
|
// tk.dumpWidget(fmt.Sprintf("PlaceGridS(%d,%d)", startW, startH))
|
||||||
|
// if you reset the values here, grid horizontal stacking doesn't work anymore
|
||||||
|
// tk.widths = make(map[int]int) // how tall each row in the grid is
|
||||||
|
// tk.heights = make(map[int]int) // how wide each column in the grid is
|
||||||
|
newW, newH := tk.placeGrid(startW, startH)
|
||||||
|
tk.full.w0 = newW
|
||||||
|
tk.full.h0 = newH
|
||||||
|
tk.full.w1 = newW
|
||||||
|
tk.full.h1 = newH
|
||||||
|
// tk.dumpWidget(fmt.Sprintf("PlaceGridE(%d,%d)", newW, newH))
|
||||||
|
return newW, newH
|
||||||
case widget.Box:
|
case widget.Box:
|
||||||
tk.placeBox(startW, startH)
|
tk.placeBox(startW, startH)
|
||||||
|
tk.full.w0 = startW
|
||||||
|
tk.full.h0 = startH
|
||||||
|
tk.full.w1 = startW
|
||||||
|
tk.full.h1 = startH
|
||||||
|
// tk.dumpWidget(fmt.Sprintf("PlaceBox(%d,%d)", startW, startH))
|
||||||
return 0, 0
|
return 0, 0
|
||||||
|
case widget.Stdout:
|
||||||
|
tk.setStdoutWH(startW, startH)
|
||||||
|
return tk.gocuiSize.Width(), tk.gocuiSize.Height()
|
||||||
case widget.Group:
|
case widget.Group:
|
||||||
// move the group to the parent's next location
|
// move the group to the parent's next location
|
||||||
tk.gocuiSetWH(startW, startH)
|
tk.gocuiSetWH(startW, startH)
|
||||||
|
tk.full.w0 = startW
|
||||||
|
tk.full.h0 = startH
|
||||||
|
tk.full.w1 = startW
|
||||||
|
tk.full.h1 = startH
|
||||||
|
|
||||||
newW := startW + me.GroupPadW
|
newW := startW + me.GroupPadW
|
||||||
newH := startH + 1 // normal hight of the group label
|
newH := startH + 1 // normal hight of the group label
|
||||||
|
@ -94,7 +139,27 @@ func (tk *guiWidget) placeWidgets(startW int, startH int) (int, int) {
|
||||||
}
|
}
|
||||||
log.Log(INFO, "REAL HEIGHT sizeW:", sizeW, "sizeH:", sizeH)
|
log.Log(INFO, "REAL HEIGHT sizeW:", sizeW, "sizeH:", sizeH)
|
||||||
}
|
}
|
||||||
return maxW, newH - startH
|
newH = newH - startH
|
||||||
|
// tk.dumpWidget(fmt.Sprintf("PlaceGroup(%d,%d)", maxW, newH))
|
||||||
|
return maxW, newH
|
||||||
|
case widget.Button:
|
||||||
|
if tk.isWindowDense() && tk.isInGrid() {
|
||||||
|
tk.frame = false
|
||||||
|
// tk.color = nil
|
||||||
|
// tk.defaultColor = nil
|
||||||
|
/*
|
||||||
|
if tk.IsEnabled() {
|
||||||
|
tk.setColorButtonDense()
|
||||||
|
} else {
|
||||||
|
tk.setColorDisable()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// if tk.full.Height() > 0 {
|
||||||
|
tk.full.h1 = tk.full.h0
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
tk.gocuiSetWH(startW, startH)
|
||||||
|
return tk.gocuiSize.Width(), tk.gocuiSize.Height()
|
||||||
default:
|
default:
|
||||||
tk.gocuiSetWH(startW, startH)
|
tk.gocuiSetWH(startW, startH)
|
||||||
return tk.gocuiSize.Width(), tk.gocuiSize.Height()
|
return tk.gocuiSize.Width(), tk.gocuiSize.Height()
|
||||||
|
@ -102,25 +167,58 @@ func (tk *guiWidget) placeWidgets(startW int, startH int) (int, int) {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) isWindowDense() bool {
|
||||||
|
if tk.WidgetType() == widget.Window {
|
||||||
|
return tk.window.dense
|
||||||
|
}
|
||||||
|
if tk.parent == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return tk.parent.isWindowDense()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) isInGrid() bool {
|
||||||
|
if tk.WidgetType() == widget.Grid {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if tk.parent == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return tk.parent.isInGrid()
|
||||||
|
}
|
||||||
|
|
||||||
func (w *guiWidget) placeGrid(startW int, startH int) (int, int) {
|
func (w *guiWidget) placeGrid(startW int, startH int) (int, int) {
|
||||||
// w.showWidgetPlacement("grid0:")
|
// w.showWidgetPlacement("grid0:")
|
||||||
if w.WidgetType != widget.Grid {
|
if w.WidgetType() != widget.Grid {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dense := w.isWindowDense()
|
||||||
|
|
||||||
|
w.full.w0 = startW
|
||||||
|
w.full.h0 = startH
|
||||||
|
|
||||||
// first compute the max sizes of the rows and columns
|
// first compute the max sizes of the rows and columns
|
||||||
for _, child := range w.children {
|
for _, child := range w.children {
|
||||||
childW, childH := child.placeWidgets(child.startW, child.startH)
|
childW, childH := child.placeWidgets(child.startW, child.startH)
|
||||||
|
|
||||||
// set the child's realWidth, and grid offset
|
// set the child's realWidth, and grid offset
|
||||||
if w.widths[child.node.State.AtW] < childW {
|
if w.widths[child.GridW()] < childW {
|
||||||
w.widths[child.node.State.AtW] = childW
|
w.widths[child.GridW()] = childW
|
||||||
}
|
}
|
||||||
if w.heights[child.node.State.AtH] < childH {
|
if w.heights[child.GridH()] < childH {
|
||||||
w.heights[child.node.State.AtH] = childH
|
w.heights[child.GridH()] = childH
|
||||||
}
|
}
|
||||||
|
if dense {
|
||||||
|
if w.heights[child.GridH()] > 0 {
|
||||||
|
w.heights[child.GridH()] = 1
|
||||||
|
} else {
|
||||||
|
w.heights[child.GridH()] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// child.showWidgetPlacement("grid: ")
|
// child.showWidgetPlacement("grid: ")
|
||||||
log.Log(INFO, "placeGrid:", child.String(), "child()", childW, childH, "At()", child.node.State.AtW, child.node.State.AtH)
|
log.Log(INFO, "placeGrid:", child.String(), "child()", childW, childH, "At()", child.GridW(), child.GridH())
|
||||||
}
|
}
|
||||||
|
|
||||||
var maxW int = 0
|
var maxW int = 0
|
||||||
|
@ -132,12 +230,12 @@ func (w *guiWidget) placeGrid(startW int, startH int) (int, int) {
|
||||||
|
|
||||||
var totalW, totalH int
|
var totalW, totalH int
|
||||||
for i, w := range w.widths {
|
for i, w := range w.widths {
|
||||||
if i < child.node.State.AtW {
|
if i < child.GridW() {
|
||||||
totalW += w
|
totalW += w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i, h := range w.heights {
|
for i, h := range w.heights {
|
||||||
if i < child.node.State.AtH {
|
if i < child.GridH() {
|
||||||
totalH += h
|
totalH += h
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,11 +251,13 @@ func (w *guiWidget) placeGrid(startW int, startH int) (int, int) {
|
||||||
maxH = totalH
|
maxH = totalH
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Log(INFO, "placeGrid:", child.String(), "new()", newW, newH, "At()", child.node.State.AtW, child.node.State.AtH)
|
log.Log(INFO, "placeGrid:", child.String(), "new()", newW, newH, "At()", child.GridW(), child.GridH())
|
||||||
child.placeWidgets(newW, newH)
|
child.placeWidgets(newW, newH)
|
||||||
// child.showWidgetPlacement("grid2:")
|
// child.showWidgetPlacement("grid2:")
|
||||||
}
|
}
|
||||||
// w.showWidgetPlacement("grid3:")
|
// w.showWidgetPlacement("grid3:")
|
||||||
|
w.full.w1 = startW + maxW
|
||||||
|
w.full.h1 = startH + maxH
|
||||||
return maxW, maxH
|
return maxW, maxH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,9 +265,11 @@ func (w *guiWidget) placeGrid(startW int, startH int) (int, int) {
|
||||||
func (w *guiWidget) realGocuiSize() *rectType {
|
func (w *guiWidget) realGocuiSize() *rectType {
|
||||||
var f func(tk *guiWidget, r *rectType)
|
var f func(tk *guiWidget, r *rectType)
|
||||||
newR := new(rectType)
|
newR := new(rectType)
|
||||||
|
|
||||||
|
outputW, outputH := w.Size()
|
||||||
// initialize the values to opposite
|
// initialize the values to opposite
|
||||||
newR.w0 = 80
|
newR.w0 = outputW
|
||||||
newR.h0 = 24
|
newR.h0 = outputH
|
||||||
if me.baseGui != nil {
|
if me.baseGui != nil {
|
||||||
maxW, maxH := me.baseGui.Size()
|
maxW, maxH := me.baseGui.Size()
|
||||||
newR.w0 = maxW
|
newR.w0 = maxW
|
||||||
|
@ -201,6 +303,7 @@ func (w *guiWidget) realGocuiSize() *rectType {
|
||||||
return newR
|
return newR
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func textSize(n *tree.Node) (int, int) {
|
func textSize(n *tree.Node) (int, int) {
|
||||||
var tk *guiWidget
|
var tk *guiWidget
|
||||||
tk = n.TK.(*guiWidget)
|
tk = n.TK.(*guiWidget)
|
||||||
|
@ -214,35 +317,21 @@ func textSize(n *tree.Node) (int, int) {
|
||||||
}
|
}
|
||||||
return width, height
|
return width, height
|
||||||
}
|
}
|
||||||
|
|
||||||
// moves the gocui view the W and H offset on the screen
|
|
||||||
/*
|
|
||||||
gocui defines the offset like this:
|
|
||||||
|
|
||||||
width -> increases to the right
|
|
||||||
---------------------------------- hieght
|
|
||||||
| H = 1 | increases
|
|
||||||
| | |
|
|
||||||
| W = 1 W = 18 | |
|
|
||||||
| | v
|
|
||||||
| H = 5 | downwards
|
|
||||||
-------------------------------------
|
|
||||||
*/
|
*/
|
||||||
// change over to this name
|
|
||||||
func (tk *guiWidget) MoveToOffset(W, H int) {
|
|
||||||
tk.gocuiSetWH(W, H)
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns where the corner of widget starts (upper left)
|
|
||||||
func (tk *guiWidget) Position() (int, int) {
|
|
||||||
return tk.gocuiSize.w0, tk.gocuiSize.h0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tk *guiWidget) gocuiSetWH(sizeW, sizeH int) {
|
func (tk *guiWidget) gocuiSetWH(sizeW, sizeH int) {
|
||||||
w := len(widget.GetString(tk.value))
|
w := len(tk.GetLabel())
|
||||||
lines := strings.Split(widget.GetString(tk.value), "\n")
|
lines := strings.Split(tk.GetLabel(), "\n")
|
||||||
h := len(lines)
|
h := len(lines)
|
||||||
|
|
||||||
|
if tk.Hidden() {
|
||||||
|
tk.gocuiSize.w0 = 0
|
||||||
|
tk.gocuiSize.h0 = 0
|
||||||
|
tk.gocuiSize.w1 = 0
|
||||||
|
tk.gocuiSize.h1 = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// tk := n.tk
|
// tk := n.tk
|
||||||
if tk.isFake {
|
if tk.isFake {
|
||||||
tk.gocuiSize.w0 = sizeW
|
tk.gocuiSize.w0 = sizeW
|
||||||
|
@ -264,3 +353,10 @@ func (tk *guiWidget) gocuiSetWH(sizeW, sizeH int) {
|
||||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + 1
|
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) setStdoutWH(sizeW, sizeH int) {
|
||||||
|
tk.gocuiSize.w0 = sizeW
|
||||||
|
tk.gocuiSize.h0 = sizeH
|
||||||
|
tk.gocuiSize.w1 = tk.gocuiSize.w0 + me.stdout.w
|
||||||
|
tk.gocuiSize.h1 = tk.gocuiSize.h0 + me.stdout.h
|
||||||
|
}
|
||||||
|
|
199
plugin.go
199
plugin.go
|
@ -1,8 +1,10 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
// if you include more than just this import
|
"github.com/awesome-gocui/gocui"
|
||||||
// then your plugin might be doing something un-ideal (just a guess from 2023/02/27)
|
|
||||||
"go.wit.com/log"
|
"go.wit.com/log"
|
||||||
"go.wit.com/toolkits/tree"
|
"go.wit.com/toolkits/tree"
|
||||||
"go.wit.com/widget"
|
"go.wit.com/widget"
|
||||||
|
@ -32,20 +34,26 @@ func newAdd(n *tree.Node) {
|
||||||
w = n.TK.(*guiWidget)
|
w = n.TK.(*guiWidget)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// w.setColor(&colorDisabled)
|
|
||||||
w := n.TK.(*guiWidget)
|
w := n.TK.(*guiWidget)
|
||||||
w.Show()
|
w.Show()
|
||||||
|
me.refresh = true // testing code to see if refresh can work
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSetTitle(n *tree.Node, s string) {
|
// for gocui as a GUI plugin, SetTitle & SetLabel are identical to SetText
|
||||||
newSetText(n, s)
|
func setTitle(n *tree.Node, s string) {
|
||||||
|
setText(n, s)
|
||||||
|
me.refresh = true // testing code to see if refresh can work
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSetLabel(n *tree.Node, s string) {
|
func setLabel(n *tree.Node, s string) {
|
||||||
newSetText(n, s)
|
setText(n, s)
|
||||||
|
me.refresh = true // testing code to see if refresh can work
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSetText(n *tree.Node, s string) {
|
// setText() and addText() are simple. They take the event sent
|
||||||
|
// to the GO plugin from the application and lookup the plugin structure
|
||||||
|
// then pass that event to gocui. This is the transfer point
|
||||||
|
func setText(n *tree.Node, s string) {
|
||||||
if n == nil {
|
if n == nil {
|
||||||
log.Warn("Tree Error: Add() sent n == nil")
|
log.Warn("Tree Error: Add() sent n == nil")
|
||||||
return
|
return
|
||||||
|
@ -56,9 +64,10 @@ func newSetText(n *tree.Node, s string) {
|
||||||
}
|
}
|
||||||
w := n.TK.(*guiWidget)
|
w := n.TK.(*guiWidget)
|
||||||
w.SetText(s)
|
w.SetText(s)
|
||||||
|
me.refresh = true // testing code to see if refresh can work
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAddText(n *tree.Node, s string) {
|
func addText(n *tree.Node, s string) {
|
||||||
if n == nil {
|
if n == nil {
|
||||||
log.Warn("Tree Error: Add() sent n == nil")
|
log.Warn("Tree Error: Add() sent n == nil")
|
||||||
return
|
return
|
||||||
|
@ -69,51 +78,7 @@ func newAddText(n *tree.Node, s string) {
|
||||||
}
|
}
|
||||||
w := n.TK.(*guiWidget)
|
w := n.TK.(*guiWidget)
|
||||||
w.AddText(s)
|
w.AddText(s)
|
||||||
}
|
me.refresh = true // testing code to see if refresh can work
|
||||||
|
|
||||||
func newaction(n *tree.Node, atype widget.ActionType) {
|
|
||||||
log.Log(INFO, "newaction() START", atype)
|
|
||||||
if n == nil {
|
|
||||||
log.Warn("Tree Error: Add() sent n == nil")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if n.TK == nil {
|
|
||||||
log.Warn("Tree sent an action on a widget we didn't seem to have.")
|
|
||||||
// do this init here again? Probably something
|
|
||||||
// went wrong and we should reset the our while gocui.View tree
|
|
||||||
n.TK = initWidget(n)
|
|
||||||
}
|
|
||||||
w := n.TK.(*guiWidget)
|
|
||||||
switch atype {
|
|
||||||
case widget.Show:
|
|
||||||
w.Show()
|
|
||||||
case widget.Hide:
|
|
||||||
w.Hide()
|
|
||||||
case widget.Move:
|
|
||||||
log.Log(NOW, "attempt to move() =", atype, n.WidgetType, n.ProgName())
|
|
||||||
case widget.ToolkitClose:
|
|
||||||
log.Log(NOW, "attempting to close the plugin and release stdout and stderr")
|
|
||||||
standardClose()
|
|
||||||
case widget.Enable:
|
|
||||||
w.enable = true
|
|
||||||
w.enableColor()
|
|
||||||
case widget.Disable:
|
|
||||||
w.enable = false
|
|
||||||
w.disableColor()
|
|
||||||
case widget.Delete:
|
|
||||||
if w == nil {
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
w.hideWidgets()
|
|
||||||
w.deleteNode()
|
|
||||||
}
|
|
||||||
n.DeleteNode()
|
|
||||||
wRoot := me.treeRoot.TK.(*guiWidget)
|
|
||||||
wRoot.redoWindows(0, 0)
|
|
||||||
default:
|
|
||||||
log.Log(ERROR, "newaction() UNHANDLED Action Type =", atype, "WidgetType =", n.WidgetType, "Name =", n.ProgName())
|
|
||||||
}
|
|
||||||
log.Log(INFO, "newaction() END", atype, n.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *guiWidget) deleteGocuiViews() {
|
func (w *guiWidget) deleteGocuiViews() {
|
||||||
|
@ -151,45 +116,131 @@ func (w *guiWidget) AddText(text string) {
|
||||||
}
|
}
|
||||||
w.vals = append(w.vals, text)
|
w.vals = append(w.vals, text)
|
||||||
for i, s := range w.vals {
|
for i, s := range w.vals {
|
||||||
log.Log(NOW, "AddText()", w.String(), i, s)
|
log.Log(INFO, "AddText()", w.String(), i, s)
|
||||||
}
|
}
|
||||||
w.SetText(text)
|
w.SetText(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *guiWidget) SetText(text string) {
|
func (tk *guiWidget) SetText(text string) {
|
||||||
var changed bool = false
|
var changed bool = false
|
||||||
if w == nil {
|
if tk == nil {
|
||||||
log.Log(NOW, "widget is nil")
|
log.Log(NOW, "widget is nil")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if w.labelN != text {
|
if tk.labelN != text {
|
||||||
w.labelN = text
|
tk.labelN = text
|
||||||
changed = true
|
changed = true
|
||||||
}
|
}
|
||||||
|
tk.node.State.Label = text
|
||||||
if !changed {
|
if !changed {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.Visible() {
|
if tk.Visible() {
|
||||||
w.textResize()
|
tk.textResize()
|
||||||
w.Hide()
|
tk.Hide()
|
||||||
w.Show()
|
tk.Show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *guiWidget) Set(val any) {
|
func (tk *guiWidget) GetText() string {
|
||||||
if w == nil {
|
if tk == nil {
|
||||||
log.Log(WARN, "Set() w == nil. val =", val)
|
log.Log(NOW, "widget is nil")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
// deprecate this
|
||||||
|
if tk.labelN != "" {
|
||||||
|
return tk.labelN
|
||||||
|
}
|
||||||
|
if tk.node == nil {
|
||||||
|
// return gocui.view name?
|
||||||
|
return tk.cuiName
|
||||||
|
}
|
||||||
|
if tk.GetLabel() != "" {
|
||||||
|
return tk.GetLabel()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func hideDisable() {
|
||||||
|
if me.textbox.tk == nil {
|
||||||
|
initTextbox()
|
||||||
|
me.textbox.tk.prepTextbox()
|
||||||
|
}
|
||||||
|
|
||||||
|
me.textbox.tk.Hide()
|
||||||
|
me.textbox.tk.enable = false
|
||||||
|
me.textbox.tk.node.State.Enable = false
|
||||||
|
me.textbox.active = false
|
||||||
|
me.baseGui.SetCurrentView("help")
|
||||||
|
// me.baseGui.DeleteView(me.textbox.tk.cuiName)
|
||||||
|
// me.baseGui.DeleteView(me.textbox.tk.v.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func showDisable() {
|
||||||
|
if me.textbox.tk == nil {
|
||||||
|
initTextbox()
|
||||||
|
me.textbox.tk.prepTextbox()
|
||||||
|
}
|
||||||
|
r := new(rectType)
|
||||||
|
r.w0 = 2
|
||||||
|
r.h0 = 1
|
||||||
|
r.w1 = r.w0 + 24
|
||||||
|
r.h1 = r.h0 + 2
|
||||||
|
me.textbox.tk.forceSizes(r)
|
||||||
|
me.textbox.tk.Show() // actually makes the gocui view. TODO: redo this
|
||||||
|
// log.Info("textbox should be shown")
|
||||||
|
// showTextbox("Running...")
|
||||||
|
// me.textbox.tk.dumpWidget("shown?")
|
||||||
|
|
||||||
|
me.textbox.tk.setColorModal()
|
||||||
|
me.textbox.tk.v.Clear()
|
||||||
|
me.textbox.tk.v.WriteString("Running...")
|
||||||
|
|
||||||
|
me.textbox.tk.v.Editable = true
|
||||||
|
me.textbox.tk.v.Wrap = true
|
||||||
|
|
||||||
|
me.baseGui.SetView(me.textbox.tk.cuiName, r.w0, r.h0, r.w1, r.h1, 0)
|
||||||
|
me.baseGui.SetCurrentView(me.textbox.tk.v.Name())
|
||||||
|
|
||||||
|
// bind the enter key to a function so we can close the textbox
|
||||||
|
me.baseGui.SetKeybinding(me.textbox.tk.v.Name(), gocui.KeyEnter, gocui.ModNone, theCloseTheTextbox)
|
||||||
|
|
||||||
|
me.textbox.active = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) Disable() {
|
||||||
|
if tk == nil {
|
||||||
|
log.Log(NOW, "widget is nil")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Log(INFO, "Set() value =", val)
|
|
||||||
|
|
||||||
w.value = val.(string)
|
switch tk.WidgetType() {
|
||||||
if w.node.WidgetType == widget.Checkbox {
|
case widget.Box:
|
||||||
w.node.State.Checked = widget.GetBool(val)
|
showDisable()
|
||||||
w.setCheckbox()
|
return
|
||||||
}
|
case widget.Button:
|
||||||
if w.node.WidgetType == widget.Label {
|
tk.setColorDisable()
|
||||||
w.labelN = widget.GetString(val)
|
return
|
||||||
|
default:
|
||||||
|
tk.dumpWidget("fixme: disable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) Enable() {
|
||||||
|
if tk == nil {
|
||||||
|
log.Log(NOW, "widget is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tk.WidgetType() {
|
||||||
|
case widget.Box:
|
||||||
|
hideDisable()
|
||||||
|
return
|
||||||
|
case widget.Button:
|
||||||
|
tk.restoreEnableColor()
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
tk.dumpWidget("fixme: enable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
117
showStdout.go
117
showStdout.go
|
@ -1,117 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
|
||||||
|
|
||||||
"go.wit.com/log"
|
|
||||||
"go.wit.com/widget"
|
|
||||||
)
|
|
||||||
|
|
||||||
var outputW int = 180
|
|
||||||
var outputH int = 24
|
|
||||||
|
|
||||||
func moveMsg(g *gocui.Gui) {
|
|
||||||
mx, my := g.MousePosition()
|
|
||||||
if !movingMsg && (mx != initialMouseX || my != initialMouseY) {
|
|
||||||
movingMsg = true
|
|
||||||
}
|
|
||||||
g.SetView("msg", mx-xOffset, my-yOffset, mx-xOffset+outputW, my-yOffset+outputH+me.FramePadH, 0)
|
|
||||||
me.startOutputW = mx - xOffset
|
|
||||||
me.startOutputH = my - yOffset
|
|
||||||
g.SetViewOnBottom("msg")
|
|
||||||
}
|
|
||||||
|
|
||||||
func showMsg(g *gocui.Gui, v *gocui.View) error {
|
|
||||||
var l string
|
|
||||||
var err error
|
|
||||||
|
|
||||||
log.Log(NOW, "showMsg() v.name =", v.Name())
|
|
||||||
if _, err := g.SetCurrentView(v.Name()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_, cy := v.Cursor()
|
|
||||||
if l, err = v.Line(cy); err != nil {
|
|
||||||
l = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
makeOutputWidget(g, l)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
|
|
||||||
maxX, maxY := g.Size()
|
|
||||||
|
|
||||||
if me.treeRoot == nil {
|
|
||||||
// keep skipping this until the binary tree is initialized
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if me.logStdout == nil {
|
|
||||||
a := new(widget.Action)
|
|
||||||
a.ProgName = "stdout"
|
|
||||||
a.WidgetType = widget.Stdout
|
|
||||||
a.WidgetId = -3
|
|
||||||
a.ParentId = 0
|
|
||||||
// n := addNode(a)
|
|
||||||
n := me.myTree.AddNode(a)
|
|
||||||
n.TK = initWidget(n)
|
|
||||||
me.logStdout = n
|
|
||||||
|
|
||||||
var tk *guiWidget
|
|
||||||
tk = me.logStdout.TK.(*guiWidget)
|
|
||||||
// tk.gocuiSize.w0 = maxX - 32
|
|
||||||
// tk.gocuiSize.h0 = maxY / 2
|
|
||||||
tk.gocuiSize.w0 = me.startOutputW
|
|
||||||
tk.gocuiSize.h0 = me.startOutputH
|
|
||||||
tk.gocuiSize.w1 = tk.gocuiSize.w0 + outputW
|
|
||||||
tk.gocuiSize.h1 = tk.gocuiSize.h0 + outputH
|
|
||||||
}
|
|
||||||
v, err := g.View("msg")
|
|
||||||
if v == nil {
|
|
||||||
log.Log(NOW, "makeoutputwindow() this is supposed to happen. v == nil", err)
|
|
||||||
} else {
|
|
||||||
log.Log(NOW, "makeoutputwindow() msg != nil. WTF now? err =", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// help, err := g.SetView("help", maxX-32, 0, maxX-1, 13, 0)
|
|
||||||
// v, err = g.SetView("msg", 3, 3, 30, 30, 0)
|
|
||||||
|
|
||||||
if me.startOutputW == 0 {
|
|
||||||
me.startOutputW = maxX - 32
|
|
||||||
}
|
|
||||||
if me.startOutputW == 0 {
|
|
||||||
me.startOutputH = maxY / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// v, err = g.SetView("msg", maxX-32, maxY/2, maxX/2+outputW, maxY/2+outputH, 0)
|
|
||||||
v, err = g.SetView("msg", me.startOutputW, me.startOutputH, maxX/2+outputW, maxY/2+outputH, 0)
|
|
||||||
if errors.Is(err, gocui.ErrUnknownView) {
|
|
||||||
log.Log(NOW, "makeoutputwindow() this is supposed to happen?", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Log(NOW, "makeoutputwindow() create output window failed", err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if v == nil {
|
|
||||||
log.Log(NOW, "makeoutputwindow() msg == nil. WTF now? err =", err)
|
|
||||||
return nil
|
|
||||||
} else {
|
|
||||||
var tk *guiWidget
|
|
||||||
tk = me.logStdout.TK.(*guiWidget)
|
|
||||||
tk.v = v
|
|
||||||
}
|
|
||||||
|
|
||||||
v.Clear()
|
|
||||||
v.SelBgColor = gocui.ColorCyan
|
|
||||||
v.SelFgColor = gocui.ColorBlack
|
|
||||||
fmt.Fprintln(v, "figure out how to capture STDOUT to here\n"+stringFromMouseClick)
|
|
||||||
g.SetViewOnBottom("msg")
|
|
||||||
// g.SetViewOnBottom(v.Name())
|
|
||||||
return v
|
|
||||||
}
|
|
310
size.go
310
size.go
|
@ -1,6 +1,11 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"go.wit.com/widget"
|
"go.wit.com/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,16 +18,16 @@ func (tk *guiWidget) Size() (int, int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't count hidden widgets in size calculations
|
// don't count hidden widgets in size calculations
|
||||||
if tk.node.Hidden() {
|
if tk.Hidden() {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
switch tk.WidgetType {
|
switch tk.WidgetType() {
|
||||||
case widget.Window:
|
case widget.Window:
|
||||||
var maxH int = 0
|
var maxH int = 0
|
||||||
var maxW int = 0
|
var maxW int = 0
|
||||||
for _, child := range tk.children {
|
for _, child := range tk.children {
|
||||||
if tk.node.Hidden() {
|
if tk.Hidden() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sizeW, sizeH := child.Size()
|
sizeW, sizeH := child.Size()
|
||||||
|
@ -43,7 +48,7 @@ func (tk *guiWidget) Size() (int, int) {
|
||||||
maxH := tk.gocuiSize.Height()
|
maxH := tk.gocuiSize.Height()
|
||||||
|
|
||||||
for _, child := range tk.children {
|
for _, child := range tk.children {
|
||||||
if tk.node.Hidden() {
|
if tk.Hidden() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sizeW, sizeH := child.Size()
|
sizeW, sizeH := child.Size()
|
||||||
|
@ -57,8 +62,15 @@ func (tk *guiWidget) Size() (int, int) {
|
||||||
return maxW + me.GroupPadW + 3, maxH
|
return maxW + me.GroupPadW + 3, maxH
|
||||||
case widget.Label:
|
case widget.Label:
|
||||||
return len(tk.String()) + 2, 1
|
return len(tk.String()) + 2, 1
|
||||||
|
case widget.Textbox:
|
||||||
|
return len(tk.String()) + 10, 3 // TODO: compute this based on 'window dense'
|
||||||
case widget.Checkbox:
|
case widget.Checkbox:
|
||||||
return len(tk.String()) + 2, 3
|
return len(tk.String()) + 2, 3 // TODO: compute this based on 'window dense'
|
||||||
|
case widget.Button:
|
||||||
|
if tk.isWindowDense() {
|
||||||
|
return len(tk.String()) + 2, 0
|
||||||
|
}
|
||||||
|
return len(tk.String()) + 2, 3 // TODO: compute this based on 'window dense'
|
||||||
}
|
}
|
||||||
if tk.isFake {
|
if tk.isFake {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
|
@ -67,25 +79,23 @@ func (tk *guiWidget) Size() (int, int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *guiWidget) sizeGrid() (int, int) {
|
func (w *guiWidget) sizeGrid() (int, int) {
|
||||||
if w.node.Hidden() {
|
if w.Hidden() {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// first compute the max sizes of the rows and columns
|
// first compute the max sizes of the rows and columns
|
||||||
for _, child := range w.children {
|
for _, child := range w.children {
|
||||||
if w.node.Hidden() {
|
if w.Hidden() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sizeW, sizeH := child.Size()
|
sizeW, sizeH := child.Size()
|
||||||
|
|
||||||
sizeW += 2
|
|
||||||
|
|
||||||
// set the child's realWidth, and grid offset
|
// set the child's realWidth, and grid offset
|
||||||
if w.widths[child.node.State.AtW] < sizeW {
|
if w.widths[child.GridW()] < sizeW {
|
||||||
w.widths[child.node.State.AtW] = sizeW
|
w.widths[child.GridW()] = sizeW
|
||||||
}
|
}
|
||||||
if w.heights[child.node.State.AtH] < sizeH {
|
if w.heights[child.GridH()] < sizeH {
|
||||||
w.heights[child.node.State.AtH] = sizeH
|
w.heights[child.GridH()] = sizeH
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,21 +112,21 @@ func (w *guiWidget) sizeGrid() (int, int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *guiWidget) sizeBox() (int, int) {
|
func (w *guiWidget) sizeBox() (int, int) {
|
||||||
if w.WidgetType != widget.Box {
|
if w.WidgetType() != widget.Box {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
if w.node.Hidden() {
|
if w.Hidden() {
|
||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
var maxW int = 0
|
var maxW int = 0
|
||||||
var maxH int = 0
|
var maxH int = 0
|
||||||
|
|
||||||
for _, child := range w.children {
|
for _, child := range w.children {
|
||||||
if w.node.Hidden() {
|
if w.Hidden() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sizeW, sizeH := child.Size()
|
sizeW, sizeH := child.Size()
|
||||||
if child.node.State.Direction == widget.Vertical {
|
if child.Direction() == widget.Vertical {
|
||||||
maxW += sizeW
|
maxW += sizeW
|
||||||
if sizeH > maxH {
|
if sizeH > maxH {
|
||||||
maxH = sizeH
|
maxH = sizeH
|
||||||
|
@ -130,3 +140,269 @@ func (w *guiWidget) sizeBox() (int, int) {
|
||||||
}
|
}
|
||||||
return maxW + me.BoxPadW, maxH
|
return maxW + me.BoxPadW, maxH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
var wtf bool
|
||||||
|
|
||||||
|
func (tk *guiWidget) verifyRect() bool {
|
||||||
|
if !tk.Visible() {
|
||||||
|
// log.Info("verifyRect() tk is not visible", tk.cuiName)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
vw0, vh0, vw1, vh1, err := me.baseGui.ViewPosition(tk.cuiName)
|
||||||
|
if err != nil {
|
||||||
|
// log.Printf("verifyRect() gocui err=%v cuiName=%s v.Name=%s", err, tk.cuiName, tk.v.Name())
|
||||||
|
vw0, vh0, vw1, vh1, err = me.baseGui.ViewPosition(tk.v.Name())
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("verifyRect() ACTUAL FAIL gocui err=%v cuiName=%s v.Name=%s", err, tk.cuiName, tk.v.Name())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// return false
|
||||||
|
}
|
||||||
|
var ok bool = true
|
||||||
|
if vw0 != tk.full.w0 {
|
||||||
|
// log.Info("verifyRect() FIXING w0", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.w0, "w0 =", vw0)
|
||||||
|
tk.full.w0 = vw0
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
if vw1 != tk.full.w1 {
|
||||||
|
// log.Info("verifyRect() FIXING w1", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.w1, "w1 =", vw1)
|
||||||
|
tk.full.w1 = vw1
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
if vh0 != tk.full.h0 {
|
||||||
|
// log.Info("verifyRect() FIXING h0", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.h0)
|
||||||
|
tk.full.h0 = vh0
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
if vh1 != tk.full.h1 {
|
||||||
|
// log.Info("verifyRect() FIXING h1", tk.cuiName, vw0, vw1, vh0, vh1, tk.gocuiSize.h1)
|
||||||
|
tk.full.h1 = vh1
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
// log.Info("verifyRect() NEED TO FIX RECT HERE", tk.cuiName)
|
||||||
|
// tk.dumpWidget("verifyRect() FIXME")
|
||||||
|
}
|
||||||
|
// log.Printf("verifyRect() OK cuiName=%s v.Name=%s", tk.cuiName, tk.v.Name())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func (tk *guiWidget) setFullSize() bool {
|
||||||
|
r := tk.getFullSize()
|
||||||
|
|
||||||
|
if tk.Hidden() {
|
||||||
|
p := tk.parent
|
||||||
|
if p != nil {
|
||||||
|
// tk.full.w0 = p.full.w0
|
||||||
|
// tk.full.w1 = p.full.w1
|
||||||
|
// tk.full.h0 = p.full.h0
|
||||||
|
// tk.full.h1 = p.full.h1
|
||||||
|
tk.full.w0 = 0
|
||||||
|
tk.full.w1 = 0
|
||||||
|
tk.full.h0 = 0
|
||||||
|
tk.full.h1 = 0
|
||||||
|
} else {
|
||||||
|
tk.full.w0 = 0
|
||||||
|
tk.full.w1 = 0
|
||||||
|
tk.full.h0 = 0
|
||||||
|
tk.full.h1 = 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var changed bool
|
||||||
|
if tk.full.w0 != r.w0 {
|
||||||
|
tk.full.w0 = r.w0
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
// widget might be forced to a certain location
|
||||||
|
if tk.full.w0 < tk.force.w0 {
|
||||||
|
tk.gocuiSize.w0 = tk.force.w0
|
||||||
|
tk.full.w0 = tk.force.w0
|
||||||
|
changed = false
|
||||||
|
}
|
||||||
|
if tk.full.w1 != r.w1 {
|
||||||
|
tk.full.w1 = r.w1
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if tk.full.h0 != r.h0 {
|
||||||
|
tk.full.h0 = r.h0
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
// widget might be forced to a certain location
|
||||||
|
if tk.full.h0 < tk.force.h0 {
|
||||||
|
tk.gocuiSize.h0 = tk.force.h0
|
||||||
|
tk.full.h0 = tk.force.h0
|
||||||
|
changed = false
|
||||||
|
}
|
||||||
|
if tk.full.h1 != r.h1 {
|
||||||
|
tk.full.h1 = r.h1
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
if tk.WidgetType() == widget.Button {
|
||||||
|
tk.full.h1 = tk.full.h0 + 1
|
||||||
|
}
|
||||||
|
if tk.isWindowDense() && tk.isInGrid() {
|
||||||
|
tk.full.h1 = tk.full.h0
|
||||||
|
}
|
||||||
|
if changed {
|
||||||
|
tk.dumpWidget(fmt.Sprintf("setFullSize(changed)"))
|
||||||
|
}
|
||||||
|
return changed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) gridFullSize() rectType {
|
||||||
|
var r rectType
|
||||||
|
var first bool = true
|
||||||
|
for _, child := range tk.children {
|
||||||
|
cr := child.getFullSize()
|
||||||
|
if cr.Width() == 0 && cr.Height() == 0 {
|
||||||
|
// ignore widgets of zero size?
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if first {
|
||||||
|
// use the lowest width and hight from children widgets
|
||||||
|
r.w0 = cr.w0
|
||||||
|
r.h0 = cr.h0
|
||||||
|
r.w1 = cr.w1
|
||||||
|
r.h1 = cr.h1
|
||||||
|
first = false
|
||||||
|
// child.dumpWidget(fmt.Sprintf("grid(f)"))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// child.dumpWidget(fmt.Sprintf("grid()"))
|
||||||
|
// use the lowest width and hight from children widgets
|
||||||
|
if r.w0 > cr.w0 {
|
||||||
|
r.w0 = cr.w0
|
||||||
|
}
|
||||||
|
if r.h0 > cr.h0 {
|
||||||
|
r.h0 = cr.h0
|
||||||
|
}
|
||||||
|
// use the highest width and hight from children widgets
|
||||||
|
if r.w1 < cr.w1 {
|
||||||
|
r.w1 = cr.w1
|
||||||
|
}
|
||||||
|
if r.h1 < cr.h1 {
|
||||||
|
r.h1 = cr.h1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tk.full.w0 = r.w0
|
||||||
|
tk.full.w1 = r.w1
|
||||||
|
tk.full.h0 = r.h0
|
||||||
|
tk.full.h1 = r.h1
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) buttonFullSize() rectType {
|
||||||
|
var r rectType
|
||||||
|
r.w0 = tk.gocuiSize.w0
|
||||||
|
r.w1 = tk.gocuiSize.w1
|
||||||
|
r.h0 = tk.gocuiSize.h0
|
||||||
|
r.h1 = tk.gocuiSize.h1
|
||||||
|
|
||||||
|
// try setting the full values here ? is this right?
|
||||||
|
tk.full.w0 = r.w0
|
||||||
|
tk.full.w1 = r.w1
|
||||||
|
tk.full.h0 = r.h0
|
||||||
|
tk.full.h1 = r.h1
|
||||||
|
|
||||||
|
// total hack. fix this somewhere eventually correctly
|
||||||
|
if tk.isWindowDense() { // total hack. fix this somewhere eventually correctly
|
||||||
|
tk.full.h0 += 1 // total hack. fix this somewhere eventually correctly
|
||||||
|
tk.full.h1 = tk.full.h0 // total hack. fix this somewhere eventually correctly
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// this checks a widget to see if it is under (W,H), then checks the widget's children
|
||||||
|
// anything that matches is passed back as an array of widgets
|
||||||
|
func (tk *guiWidget) getFullSize() rectType {
|
||||||
|
var r rectType
|
||||||
|
|
||||||
|
if tk.Hidden() {
|
||||||
|
/*
|
||||||
|
p := tk.parent
|
||||||
|
if p != nil {
|
||||||
|
return p.full
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
var r rectType
|
||||||
|
r.w0 = 0
|
||||||
|
r.w1 = 0
|
||||||
|
r.h0 = 0
|
||||||
|
r.h1 = 0
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk.WidgetType() == widget.Grid {
|
||||||
|
return tk.gridFullSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// these are 'simple' widgets
|
||||||
|
// the full size is exactly what gocui uses
|
||||||
|
switch tk.WidgetType() {
|
||||||
|
case widget.Label:
|
||||||
|
r := tk.buttonFullSize()
|
||||||
|
r.w1 += 5
|
||||||
|
return r
|
||||||
|
case widget.Button:
|
||||||
|
r := tk.buttonFullSize()
|
||||||
|
r.w1 += 5
|
||||||
|
return r
|
||||||
|
case widget.Checkbox:
|
||||||
|
return tk.buttonFullSize()
|
||||||
|
case widget.Dropdown:
|
||||||
|
r := tk.buttonFullSize()
|
||||||
|
r.w1 += 7 // TODO: fix this to be real
|
||||||
|
return r
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk.v == nil {
|
||||||
|
r.w0 = tk.full.w0
|
||||||
|
r.w1 = tk.full.w1
|
||||||
|
r.h0 = tk.full.h0
|
||||||
|
r.h1 = tk.full.h1
|
||||||
|
} else {
|
||||||
|
r.w0 = tk.gocuiSize.w0
|
||||||
|
r.w1 = tk.gocuiSize.w1
|
||||||
|
r.h0 = tk.gocuiSize.h0
|
||||||
|
r.h1 = tk.gocuiSize.h1
|
||||||
|
}
|
||||||
|
|
||||||
|
// search through the children widgets in the binary tree
|
||||||
|
for _, child := range tk.children {
|
||||||
|
cr := child.getFullSize()
|
||||||
|
/* this didn't make things work either
|
||||||
|
if child.v == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// use the lowest width and hight from children widgets
|
||||||
|
if r.w0 > cr.w0 {
|
||||||
|
r.w0 = cr.w0
|
||||||
|
}
|
||||||
|
if r.h0 > cr.h0 {
|
||||||
|
r.h0 = cr.h0
|
||||||
|
}
|
||||||
|
// use the highest width and hight from children widgets
|
||||||
|
if r.w1 < cr.w1 {
|
||||||
|
r.w1 = cr.w1
|
||||||
|
}
|
||||||
|
if r.h1 < cr.h1 {
|
||||||
|
r.h1 = cr.h1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// try setting the full values here ? is this right?
|
||||||
|
tk.full.w0 = r.w0
|
||||||
|
tk.full.w1 = r.w1
|
||||||
|
tk.full.h0 = r.h0
|
||||||
|
tk.full.h1 = r.h1
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createStdout(g *gocui.Gui) bool {
|
||||||
|
if me.stdout.tk == nil {
|
||||||
|
makeOutputWidget(g, "this is a create before a mouse click")
|
||||||
|
// me.logStdout.v.Write([]byte(msg))
|
||||||
|
|
||||||
|
// this will show very early debugging output
|
||||||
|
// keep this code commented out but do not remove it. when it doubt, this will be the Light of Elendil
|
||||||
|
// NEVER REMOVE THIS CODE
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("test out gocuiEvent() %d\n", me.ecount)
|
||||||
|
me.stdout.tk.Write([]byte(msg))
|
||||||
|
|
||||||
|
log.Log(NOW, "logStdout test out")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func coreStdout() {
|
||||||
|
if me.stdout.tk != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a := new(widget.Action)
|
||||||
|
a.ProgName = "2stdout2"
|
||||||
|
a.WidgetType = widget.Stdout
|
||||||
|
a.WidgetId = me.stdout.wId
|
||||||
|
a.ParentId = 0
|
||||||
|
// n := addNode(a)
|
||||||
|
n := me.myTree.AddNode(a)
|
||||||
|
me.stdout.tk = initWidget(n)
|
||||||
|
|
||||||
|
tk := me.stdout.tk
|
||||||
|
tk.gocuiSize.w0 = me.stdout.lastW
|
||||||
|
tk.gocuiSize.h0 = me.stdout.lastH
|
||||||
|
tk.gocuiSize.w1 = tk.gocuiSize.w0 + me.stdout.w
|
||||||
|
tk.gocuiSize.h1 = tk.gocuiSize.h0 + me.stdout.h
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
|
||||||
|
if me.treeRoot == nil {
|
||||||
|
// keep skipping this until the binary tree is initialized
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
coreStdout()
|
||||||
|
|
||||||
|
if me.stdout.tk == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
v, err := g.View("msg")
|
||||||
|
if v == nil {
|
||||||
|
// log.Log(NOW, "makeoutputwindow() this is supposed to happen. v == nil", err)
|
||||||
|
} else {
|
||||||
|
log.Log(NOW, "makeoutputwindow() msg != nil. WTF now? err =", err)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
rect := me.stdout.tk.gocuiSize
|
||||||
|
v, err = g.SetView("msg", rect.w0, rect.h0, rect.w1, rect.h1, 0)
|
||||||
|
|
||||||
|
if errors.Is(err, gocui.ErrUnknownView) {
|
||||||
|
// log.Log(NOW, "makeoutputwindow() this is supposed to happen?", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if v == nil {
|
||||||
|
log.Log(NOW, "makeoutputwindow() BAD: v == nil && err =", err)
|
||||||
|
}
|
||||||
|
log.Log(NOW, "makeoutputwindow() create output window failed", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if v == nil {
|
||||||
|
log.Log(NOW, "makeoutputwindow() msg == nil. WTF now? err =", err)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
me.stdout.tk.v = v
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Clear()
|
||||||
|
v.SelBgColor = gocui.ColorCyan
|
||||||
|
v.SelFgColor = gocui.ColorBlack
|
||||||
|
fmt.Fprintln(v, "figure out how to capture STDOUT to here\n"+stringFromMouseClick)
|
||||||
|
// g.SetViewOnBottom("msg")
|
||||||
|
// setBottomBG()
|
||||||
|
|
||||||
|
me.stdout.tk.v = v
|
||||||
|
me.stdout.tk.DrawAt(me.stdout.lastW, me.stdout.lastH)
|
||||||
|
relocateStdoutOffscreen()
|
||||||
|
/*
|
||||||
|
if me.stdout.outputOffscreen {
|
||||||
|
me.stdout.tk.relocateStdout(me.stdout.lastW, me.stdout.lastH)
|
||||||
|
} else {
|
||||||
|
relocateStdoutOffscreen()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func relocateStdoutOffscreen() {
|
||||||
|
if me.stdout.tk == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !me.stdout.disable {
|
||||||
|
log.Info("Using gocui STDOUT")
|
||||||
|
log.CaptureMode(me.stdout.tk)
|
||||||
|
}
|
||||||
|
newW := 10
|
||||||
|
newH := 0 - me.stdout.h - 4
|
||||||
|
me.stdout.tk.relocateStdout(newW, newH)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) relocateStdout(w int, h int) {
|
||||||
|
w0 := w
|
||||||
|
h0 := h
|
||||||
|
w1 := w + me.stdout.w
|
||||||
|
h1 := h + me.stdout.h
|
||||||
|
|
||||||
|
tk.gocuiSize.w0 = w0
|
||||||
|
tk.gocuiSize.w1 = w1
|
||||||
|
tk.gocuiSize.h0 = h0
|
||||||
|
tk.gocuiSize.h1 = h1
|
||||||
|
|
||||||
|
tk.full.w0 = w0
|
||||||
|
tk.full.w1 = w1
|
||||||
|
tk.full.h0 = h0
|
||||||
|
tk.full.h1 = h1
|
||||||
|
|
||||||
|
me.baseGui.SetView("msg", w0, h0, w1, h1, 0)
|
||||||
|
// me.baseGui.SetViewOnBottom("msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 stdout) Height() int {
|
||||||
|
if w.tk == nil {
|
||||||
|
return 40
|
||||||
|
}
|
||||||
|
return w.tk.gocuiSize.Height() - 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w stdout) Write(p []byte) (n int, err error) {
|
||||||
|
me.writeMutex.Lock()
|
||||||
|
defer me.writeMutex.Unlock()
|
||||||
|
|
||||||
|
lines := strings.Split(strings.TrimSpace(string(p)), "\n")
|
||||||
|
me.stdout.outputS = append(me.stdout.outputS, lines...)
|
||||||
|
if me.outf != nil {
|
||||||
|
fmt.Fprint(me.outf, string(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *guiWidget) Write(p []byte) (n int, err error) {
|
||||||
|
lines := strings.Split(strings.TrimSpace(string(p)), "\n")
|
||||||
|
if me.outf != nil {
|
||||||
|
fmt.Fprint(me.outf, string(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
if w == nil {
|
||||||
|
me.stdout.outputS = append(me.stdout.outputS, lines...)
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
w.tainted = true
|
||||||
|
me.writeMutex.Lock()
|
||||||
|
defer me.writeMutex.Unlock()
|
||||||
|
|
||||||
|
me.stdout.outputS = append(me.stdout.outputS, lines...)
|
||||||
|
|
||||||
|
tk := me.stdout.tk
|
||||||
|
if tk == nil {
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
if tk.v == nil {
|
||||||
|
// redo this old code
|
||||||
|
v, _ := me.baseGui.View("msg")
|
||||||
|
if v != nil {
|
||||||
|
tk.v = v
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
tk.refreshStdout()
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// lets the user page up and down through the stdout buffer
|
||||||
|
func (tk *guiWidget) refreshStdout() {
|
||||||
|
if len(me.stdout.outputS) < me.stdout.h+me.stdout.pager {
|
||||||
|
// log.Info(fmt.Sprintf("buffer too small=%d len(%d)", me.stdout.pager, len(me.stdout.outputS)))
|
||||||
|
var cur []string
|
||||||
|
cur = append(cur, me.stdout.outputS...)
|
||||||
|
slices.Reverse(cur)
|
||||||
|
tk.v.Clear()
|
||||||
|
fmt.Fprintln(tk.v, strings.Join(cur, "\n"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var cur []string
|
||||||
|
// chop off the last lines in the buffer
|
||||||
|
chop := len(me.stdout.outputS) - (me.stdout.pager + me.stdout.h)
|
||||||
|
cur = append(cur, me.stdout.outputS[chop:chop+me.stdout.h]...)
|
||||||
|
slices.Reverse(cur)
|
||||||
|
tk.v.Clear()
|
||||||
|
fmt.Fprintln(tk.v, strings.Join(cur, "\n"))
|
||||||
|
}
|
392
structs.go
392
structs.go
|
@ -1,5 +1,5 @@
|
||||||
// LICENSE: same as the go language itself
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
// Copyright 2023 WIT.COM
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
// all structures and variables are local (aka lowercase)
|
// all structures and variables are local (aka lowercase)
|
||||||
// since the plugin should be isolated to access only
|
// since the plugin should be isolated to access only
|
||||||
|
@ -10,102 +10,150 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
"github.com/awesome-gocui/gocui"
|
||||||
|
|
||||||
"go.wit.com/log"
|
"go.wit.com/lib/protobuf/guipb"
|
||||||
|
log "go.wit.com/log"
|
||||||
"go.wit.com/toolkits/tree"
|
"go.wit.com/toolkits/tree"
|
||||||
"go.wit.com/widget"
|
"go.wit.com/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var initOnce sync.Once // run initPlugin() only once
|
||||||
|
|
||||||
// It's probably a terrible idea to call this 'me'
|
// 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 me config
|
||||||
|
|
||||||
var showDebug bool = true
|
// todo: move all this to a protobuf. then, redo all this mess.
|
||||||
var showHelp bool = true
|
// it got me here, but now it's time to clean it up for good
|
||||||
var redoWidgets bool = true
|
// I can't get a GO plugins that use protobuf to load yet (versioning mismatch)
|
||||||
|
|
||||||
// This is the window that is currently active
|
|
||||||
// var currentWindow *tree.Node
|
|
||||||
|
|
||||||
type config struct {
|
type config struct {
|
||||||
baseGui *gocui.Gui // the main gocui handle
|
baseGui *gocui.Gui // the main gocui handle
|
||||||
// rootNode *node // the base of the binary tree. it should have id == 0
|
treeRoot *tree.Node // the base of the binary tree. it should have id == 0
|
||||||
|
myTree *tree.TreeInfo // ?
|
||||||
treeRoot *tree.Node // the base of the binary tree. it should have id == 0
|
currentWindow *guiWidget // this is the current tab or window to show
|
||||||
myTree *tree.TreeInfo
|
ok bool // if the user doesn't hit a key or move the mouse, gocui doesn't really start
|
||||||
|
firstWindowOk bool // allows the init to wait for the first window from the application
|
||||||
ctrlDown *tree.Node // shown if you click the mouse when the ctrl key is pressed
|
refresh bool // redraw everything?
|
||||||
currentWindow *guiWidget // this is the current tab or window to show
|
ctrlDown *tree.Node // shown if you click the mouse when the ctrl key is pressed
|
||||||
logStdout *tree.Node // where to show STDOUT
|
helpLabel *gocui.View // ?
|
||||||
startOutputW int
|
showHelp bool // toggle boolean for the help menu (deprecate?)
|
||||||
startOutputH int
|
FirstWindowW int `default:"2"` // how far over to start window #1
|
||||||
helpLabel *gocui.View
|
FirstWindowH int `default:"0"` // how far down to start window #1
|
||||||
|
FramePadW int `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side
|
||||||
// this is a floating widget that we show whenever the user clicks on a
|
FramePadH int `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side
|
||||||
// dropdown menu or combobox
|
PadW int `default:"1" dense:"0"` // pad spacing
|
||||||
// the dropdown widget to select dropdrown lists
|
PadH int `default:"1" dense:"0"` // pad spacing
|
||||||
dropdownV *guiWidget
|
WindowW int `default:"8" dense:"0"` // how far down to start Window or Tab headings
|
||||||
dropdownW *guiWidget // grab the dropdown choices from this widget
|
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
|
||||||
// When the widget has a frame, like a button, it adds 2 lines runes on each side
|
TabH int `default:"1" dense:"0"` // how far down to start Window or Tab headings
|
||||||
// so you need 3 char spacing in each direction to not have them overlap
|
WindowPadW int `default:"8" dense:"0"` // additional amount of space to put between window & tab widgets
|
||||||
// the amount of padding when there is a frame
|
TabPadW int `default:"4" dense:"0"` // additional amount of space to put between window & tab widgets
|
||||||
FramePadW int `default:"1" dense:"0"`
|
GroupPadW int `default:"2" dense:"1"` // additional amount of space to indent on a group
|
||||||
FramePadH int `default:"1" dense:"0"`
|
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
|
||||||
PadW int `default:"1" dense:"0"`
|
RawW int `default:"1"` // the raw beginning of each window (or tab)
|
||||||
PadH int `default:"1" dense:"0"`
|
RawH int `default:"5"` // the raw beginning of each window (or tab)
|
||||||
|
FakeW int `default:"20"` // offset for the hidden widgets
|
||||||
// how far down to start Window or Tab headings
|
DropdownId int `default:"-78"` // the widget id to use
|
||||||
WindowW int `default:"8" dense:"0"`
|
padded bool // add space between things like buttons
|
||||||
WindowH int `default:"-1"`
|
bookshelf bool // do you want things arranged in the box like a bookshelf or a stack?
|
||||||
TabW int `default:"5" dense:"0"`
|
canvas bool // if set to true, the windows are a raw canvas
|
||||||
TabH int `default:"1" dense:"0"`
|
menubar bool // for windows
|
||||||
|
stretchy bool // expand things like buttons to the maximum size
|
||||||
// additional amount of space to put between window & tab widgets
|
margin bool // add space around the frames of windows
|
||||||
WindowPadW int `default:"8" dense:"0"`
|
writeMutex sync.Mutex // writeMutex protects writes to *guiWidget (it's global right now maybe)
|
||||||
TabPadW int `default:"4" dense:"0"`
|
ecount int // counts how many mouse and keyboard events have occurred
|
||||||
|
supermouse bool // prints out every widget found while you move the mouse around
|
||||||
// additional amount of space to indent on a group
|
depth int // used for listWidgets() debugging
|
||||||
GroupPadW int `default:"2" dense:"1"`
|
newWindowTrigger chan *guiWidget // work around hack to redraw windows a bit after NewWindow()
|
||||||
|
stdout stdout // information for the STDOUT window
|
||||||
// additional amount of space to indent on a group
|
dropdown dropdown // the dropdown menu
|
||||||
BoxPadW int `default:"2" dense:"1"`
|
textbox dropdown // the textbox popup window
|
||||||
|
BG dropdown // the background widget
|
||||||
// additional amount of space to indent on a group
|
notify libnotify // emulates the desktop libnotify menu
|
||||||
GridPadW int `default:"2" dense:"1"`
|
allwin []*guiWidget // for tracking which window is next
|
||||||
|
dark bool // use a 'dark' color palette
|
||||||
// the raw beginning of each window (or tab)
|
mouse mouse // mouse settings
|
||||||
RawW int `default:"1"`
|
showDebug bool // todo: move this into config struct
|
||||||
RawH int `default:"5"`
|
debug bool // todo: move this into config struct
|
||||||
|
starttime time.Time // checks how long it takes on startup
|
||||||
// offset for the hidden widgets
|
winchW int // used to detect SIGWINCH
|
||||||
FakeW int `default:"20"`
|
winchH int // used to detect SIGWINCH
|
||||||
|
outf *os.File // hacks for capturing stdout
|
||||||
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
|
// stuff controlling how the mouse works
|
||||||
var (
|
type mouse struct {
|
||||||
initialMouseX, initialMouseY, xOffset, yOffset int
|
down time.Time // when the mouse was pressed down
|
||||||
globalMouseDown, msgMouseDown, movingMsg bool
|
up time.Time // when the mouse was released. used to detect click vs drag
|
||||||
)
|
clicktime time.Duration // how long is too long for a mouse click vs drag
|
||||||
|
mouseUp bool // is the mouse up?
|
||||||
|
double bool // user is double clicking
|
||||||
|
doubletime time.Duration // how long is too long for double click
|
||||||
|
resize bool // mouse is resizing something
|
||||||
|
downW int // where the mouse was pressed down
|
||||||
|
downH int // where the mouse was pressed down
|
||||||
|
currentDrag *guiWidget // what widget is currently being moved around
|
||||||
|
}
|
||||||
|
|
||||||
|
// settings for the stdout window
|
||||||
|
type stdout struct {
|
||||||
|
tk *guiWidget // where to show STDOUT
|
||||||
|
wId int // the widget id
|
||||||
|
w int // the width
|
||||||
|
h int // the height
|
||||||
|
outputOnTop bool // is the STDOUT window on top?
|
||||||
|
outputOffscreen bool // is the STDOUT window offscreen?
|
||||||
|
startOnscreen bool // start the output window onscreen?
|
||||||
|
disable bool // disable the stdout window. do not change os.Stdout & os.Stderr
|
||||||
|
lastW int // the last 'w' location (used to move from offscreen to onscreen)
|
||||||
|
lastH int // the last 'h' location (used to move from offscreen to onscreen)
|
||||||
|
// mouseOffsetW int // the current 'w' offset
|
||||||
|
// mouseOffsetH int // the current 'h' offset
|
||||||
|
init bool // moves the window offscreen on startup
|
||||||
|
outputS []string // the buffer of all the output
|
||||||
|
pager int // allows the user to page through the buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// settings for the dropdown window
|
||||||
|
type dropdown struct {
|
||||||
|
tk *guiWidget // where to show STDOUT
|
||||||
|
callerTK *guiWidget // which widget called the dropdown menu
|
||||||
|
items []string // what is currently in the menu
|
||||||
|
w int // the width
|
||||||
|
h int // the height
|
||||||
|
active bool // is the dropdown menu currently in use?
|
||||||
|
init bool // moves the window offscreen on startup
|
||||||
|
// Id int `default:"-78"` // the widget id to use
|
||||||
|
wId int `default:"-78"` // the widget id to use
|
||||||
|
}
|
||||||
|
|
||||||
|
// settings for the dropdown window
|
||||||
|
type internalTK struct {
|
||||||
|
once sync.Once // for init
|
||||||
|
tk *guiWidget // where to show STDOUT
|
||||||
|
callerTK *guiWidget // which widget called the dropdown menu
|
||||||
|
wId int // the widget id to use
|
||||||
|
active bool // is the internal widget currently in use?
|
||||||
|
offsetW int // width offset
|
||||||
|
offsetH int // height offset
|
||||||
|
}
|
||||||
|
|
||||||
|
// the desktop libnotify menu
|
||||||
|
type libnotify struct {
|
||||||
|
clock internalTK // widget for the clock
|
||||||
|
icon internalTK // libnotify menu icon
|
||||||
|
window internalTK // the libnotify menu
|
||||||
|
help internalTK // the help menu
|
||||||
|
}
|
||||||
|
|
||||||
// this is the gocui way
|
// this is the gocui way
|
||||||
// corner starts at in the upper left corner
|
// corner starts at in the upper left corner
|
||||||
|
@ -118,115 +166,86 @@ func (r *rectType) Width() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rectType) Height() int {
|
func (r *rectType) Height() int {
|
||||||
|
if r.h0 == 0 && r.h1 == 0 {
|
||||||
|
// edge case. only return 0 for these
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if r.h1 == r.h0 {
|
||||||
|
// if they are equal, it's actually height = 1
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if r.h1-r.h0 < 1 {
|
||||||
|
// can't have negatives. something is wrong. return 1 for now
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
return r.h1 - r.h0
|
return r.h1 - r.h0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// settings that are window specific
|
||||||
|
type window struct {
|
||||||
|
windowFrame *guiWidget // this is the frame for a window widget
|
||||||
|
wasDragged bool // indicates the window was dragged. This keeps it from being rearranged
|
||||||
|
hasTabs bool // does the window have tabs?
|
||||||
|
currentTab bool // the visible tab
|
||||||
|
selectedTab *tree.Node // for a window, this is currently selected tab
|
||||||
|
active bool // means this window is the active one
|
||||||
|
order int // what level the window is on
|
||||||
|
// resize bool // only set the title once
|
||||||
|
collapsed bool // only show the window title bar
|
||||||
|
dense bool // true if the window is dense
|
||||||
|
large bool // true if the window is huge
|
||||||
|
pager int // allows the user to page through the window
|
||||||
|
}
|
||||||
|
|
||||||
|
type colorT struct {
|
||||||
|
frame gocui.Attribute
|
||||||
|
fg gocui.Attribute
|
||||||
|
bg gocui.Attribute
|
||||||
|
selFg gocui.Attribute
|
||||||
|
selBg gocui.Attribute
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
type guiWidget struct {
|
type guiWidget struct {
|
||||||
// the gocui package variables
|
v *gocui.View // this is nil if the widget is not displayed
|
||||||
v *gocui.View // this is nil if the widget is not displayed
|
cuiName string // what gocui uses to reference the widget (usually "TK <widgetId>"
|
||||||
cuiName string // what gocui uses to reference the widget
|
parent *guiWidget // mirrors the binary node tree
|
||||||
|
children []*guiWidget // mirrors the binary node tree
|
||||||
WidgetType widget.WidgetType
|
node *tree.Node // the pointer back to the tree
|
||||||
|
pb *guipb.Widget // the guipb Widget
|
||||||
// tw *toolkit.Widget
|
wtype widget.WidgetType // used for Tables for now. todo: fix this correctly
|
||||||
parent *guiWidget
|
windowFrame *guiWidget // this is the frame for a window widget
|
||||||
children []*guiWidget
|
internal bool // indicates the widget is internal to gocui and should be treated differently
|
||||||
|
hasTabs bool // does the window have tabs?
|
||||||
node *tree.Node
|
currentTab bool // the visible tab
|
||||||
|
window window // holds information specific only to Window widgets
|
||||||
hasTabs bool // does the window have tabs?
|
value string // ?
|
||||||
currentTab bool // the visible tab
|
checked bool // ?
|
||||||
|
labelN string // the actual text to display in the console
|
||||||
value string
|
vals []string // dropdown menu items
|
||||||
checked bool
|
enable bool // ?
|
||||||
|
gocuiSize rectType // should mirror the real display size. todo: verify these are accurate. they are not yet
|
||||||
// the actual text to display in the console
|
full rectType // full size of children (used by widget.Window, etc)
|
||||||
labelN string
|
force rectType // force widget within these boundries (using this to debug window dragging)
|
||||||
|
startW int // ?
|
||||||
vals []string // dropdown menu items
|
startH int // ?
|
||||||
|
lastW int // used during mouse dragging
|
||||||
active bool
|
lastH int // used during mouse dragging
|
||||||
|
isFake bool // widget types like 'box' are 'false'
|
||||||
enable bool
|
widths map[int]int // how tall each row in the grid is
|
||||||
defaultColor *colorT // store the color to go back to
|
heights map[int]int // how wide each column in the grid is
|
||||||
|
tainted bool // ?
|
||||||
// hidden bool
|
frame bool // ?
|
||||||
|
selectedTab *tree.Node // for a window, this is currently selected tab
|
||||||
// AtW int
|
color *colorT // what color to use
|
||||||
// AtH int
|
colorLast colorT // the last color the widget had
|
||||||
|
defaultColor *colorT // the default colors // TODO: make a function for this instead
|
||||||
// direction widget.Orientation
|
|
||||||
|
|
||||||
// progname 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
|
|
||||||
|
|
||||||
startW int
|
|
||||||
startH int
|
|
||||||
|
|
||||||
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 *tree.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()
|
|
||||||
var tk *guiWidget
|
|
||||||
tk = me.logStdout.TK.(*guiWidget)
|
|
||||||
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 {
|
func Set(ptr interface{}, tag string) error {
|
||||||
if reflect.TypeOf(ptr).Kind() != reflect.Ptr {
|
if reflect.TypeOf(ptr).Kind() != reflect.Ptr {
|
||||||
log.Log(ERROR, "Set() Not a pointer", ptr, "with tag =", tag)
|
log.Log(ERROR, "Set() Not a pointer", ptr, "with tag =", tag)
|
||||||
|
@ -246,12 +265,11 @@ func Set(ptr interface{}, tag string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setField(field reflect.Value, defaultVal string, name string) error {
|
func setField(field reflect.Value, defaultVal string, name string) error {
|
||||||
|
|
||||||
if !field.CanSet() {
|
if !field.CanSet() {
|
||||||
// log("setField() Can't set value", field, defaultVal)
|
// log("setField() Can't set value", field, defaultVal)
|
||||||
return fmt.Errorf("Can't set value\n")
|
return fmt.Errorf("Can't set value\n")
|
||||||
} else {
|
} else {
|
||||||
log.Log(NOW, "setField() Can set value", name, defaultVal)
|
// log.Log(NOW, "setField() Can set value", name, defaultVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch field.Kind() {
|
switch field.Kind() {
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"go.wit.com/lib/protobuf/guipb"
|
||||||
|
"go.wit.com/log"
|
||||||
|
"go.wit.com/toolkits/tree"
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
func initGridPB(pb *guipb.Widget) *guiWidget {
|
||||||
|
var w *guiWidget
|
||||||
|
w = new(guiWidget)
|
||||||
|
|
||||||
|
w.pb = pb
|
||||||
|
w.parent = me.treeRoot.TK.(*guiWidget)
|
||||||
|
w.wtype = widget.Window
|
||||||
|
w.cuiName = fmt.Sprintf("%d %s", pb.Id, "TK")
|
||||||
|
w.labelN = pb.Name
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
func initGridPB(pb *guipb.Widget) *guiWidget {
|
||||||
|
var w *guiWidget
|
||||||
|
w = new(guiWidget)
|
||||||
|
|
||||||
|
w.pb = pb
|
||||||
|
w.wtype = widget.Grid
|
||||||
|
w.cuiName = fmt.Sprintf("%d %s", pb.Id, "TK")
|
||||||
|
w.labelN = pb.Name
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func showTable(t *guipb.Table) {
|
||||||
|
log.Info("gocui: should show table here")
|
||||||
|
if t == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Info("gocui: table.Title", t.Title)
|
||||||
|
// log.Info("gocui: need to add window here id =", t.Window.Id, t.Window.Name)
|
||||||
|
if t.Grid == nil {
|
||||||
|
log.Info("gocui: missing grid widget. tree plugin error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
root := me.treeRoot.TK.(*guiWidget)
|
||||||
|
parent := root.findWidgetById(int(t.Parent.Id))
|
||||||
|
if parent == nil {
|
||||||
|
log.Info("gocui: show table error. parent.Id not found", t.Parent.Id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("gocui: need to add grid here id =", t.Grid.Id)
|
||||||
|
grid := initGridPB(t.Grid)
|
||||||
|
grid.parent = parent
|
||||||
|
}
|
||||||
|
|
||||||
|
func enableWidget(n *tree.Node) {
|
||||||
|
tk := n.TK.(*guiWidget)
|
||||||
|
tk.Enable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableWidget(n *tree.Node) {
|
||||||
|
tk := n.TK.(*guiWidget)
|
||||||
|
tk.Disable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func showWidget(n *tree.Node) {
|
||||||
|
tk := n.TK.(*guiWidget)
|
||||||
|
tk.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func hideWidget(n *tree.Node) {
|
||||||
|
tk := n.TK.(*guiWidget)
|
||||||
|
if n.WidgetType == widget.Window {
|
||||||
|
tk.windowFrame.Hide()
|
||||||
|
tk.hideWidgets()
|
||||||
|
}
|
||||||
|
tk.Hide()
|
||||||
|
tk.deleteWidget()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) deleteWidget() {
|
||||||
|
log.Log(INFO, "gocui deleteWidget() looking for child to delete:", tk.cuiName)
|
||||||
|
p := tk.parent
|
||||||
|
for i, child := range p.children {
|
||||||
|
if tk == child {
|
||||||
|
log.Log(INFO, "deleteWidget() found parent with child to delete:", i, child.cuiName, child.WidgetId())
|
||||||
|
p.children = slices.Delete(p.children, i, i+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tk.deleteTree()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) deleteTree() {
|
||||||
|
for _, child := range tk.children {
|
||||||
|
child.deleteTree()
|
||||||
|
}
|
||||||
|
tk.Hide()
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
// simulates a dropdown menu in gocui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
log "go.wit.com/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (tk *guiWidget) forceSizes(r *rectType) {
|
||||||
|
tk.gocuiSize.w0 = r.w0
|
||||||
|
tk.gocuiSize.w1 = r.w1
|
||||||
|
tk.gocuiSize.h0 = r.h0
|
||||||
|
tk.gocuiSize.h1 = r.h1
|
||||||
|
|
||||||
|
tk.full.w0 = r.w0
|
||||||
|
tk.full.w1 = r.w1
|
||||||
|
tk.full.h0 = r.h0
|
||||||
|
tk.full.h1 = r.h1
|
||||||
|
|
||||||
|
tk.force.w0 = r.w0
|
||||||
|
tk.force.w1 = r.w1
|
||||||
|
tk.force.h0 = r.h0
|
||||||
|
tk.force.h1 = r.h1
|
||||||
|
}
|
||||||
|
|
||||||
|
func initTextbox() {
|
||||||
|
if me.textbox.tk == nil {
|
||||||
|
// should only happen once
|
||||||
|
me.textbox.tk = makeNewFlagWidget(me.textbox.wId)
|
||||||
|
// me.textbox.tk.dumpWidget("init() textbox")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (callertk *guiWidget) prepTextbox() {
|
||||||
|
initTextbox()
|
||||||
|
if me.textbox.tk == nil {
|
||||||
|
log.Log(GOCUI, "prepTextbox() Is Broken")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r := new(rectType)
|
||||||
|
// startW, startH := tk.Position()
|
||||||
|
r.w0 = callertk.gocuiSize.w0 + 4
|
||||||
|
r.h0 = callertk.gocuiSize.h0 + 3
|
||||||
|
r.w1 = r.w0 + 24
|
||||||
|
r.h1 = r.h0 + 2
|
||||||
|
me.textbox.tk.forceSizes(r)
|
||||||
|
// me.textbox.tk.dumpWidget("after sizes")
|
||||||
|
|
||||||
|
me.textbox.callerTK = callertk
|
||||||
|
|
||||||
|
// showTextbox(callertk.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func showTextbox(callers string) {
|
||||||
|
// tk := me.textbox.tk
|
||||||
|
// me.textbox.tk.dumpWidget("after sizes")
|
||||||
|
|
||||||
|
// me.textbox.tk.Show() // actually makes the gocui view. TODO: redo this
|
||||||
|
|
||||||
|
if me.textbox.tk.v == nil {
|
||||||
|
log.Info("wtf went wrong")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
me.textbox.tk.setColorModal()
|
||||||
|
me.textbox.tk.v.Clear()
|
||||||
|
cur := strings.TrimSpace(callers)
|
||||||
|
// log.Info("setting textbox string to:", cur)
|
||||||
|
me.textbox.tk.v.WriteString(cur)
|
||||||
|
|
||||||
|
me.textbox.tk.v.Editable = true
|
||||||
|
me.textbox.tk.v.Wrap = true
|
||||||
|
|
||||||
|
r := me.textbox.tk.gocuiSize
|
||||||
|
me.baseGui.SetView(me.textbox.tk.cuiName, r.w0, r.h0, r.w1, r.h1, 0)
|
||||||
|
me.baseGui.SetCurrentView(me.textbox.tk.v.Name())
|
||||||
|
|
||||||
|
// bind the enter key to a function so we can close the textbox
|
||||||
|
me.baseGui.SetKeybinding(me.textbox.tk.v.Name(), gocui.KeyEnter, gocui.ModNone, theCloseTheTextbox)
|
||||||
|
|
||||||
|
me.textbox.active = true
|
||||||
|
|
||||||
|
// me.textbox.dumpWidget("showTextbox()")
|
||||||
|
}
|
||||||
|
|
||||||
|
func theCloseTheTextbox(g *gocui.Gui, v *gocui.View) error {
|
||||||
|
textboxClosed()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updates the text and sends an event back to the application
|
||||||
|
func textboxClosed() {
|
||||||
|
// get the text the user entered
|
||||||
|
var newtext string
|
||||||
|
if me.textbox.tk.v == nil {
|
||||||
|
newtext = ""
|
||||||
|
} else {
|
||||||
|
newtext = me.textbox.tk.v.ViewBuffer()
|
||||||
|
}
|
||||||
|
newtext = strings.TrimSpace(newtext)
|
||||||
|
me.textbox.active = false
|
||||||
|
me.textbox.tk.Hide()
|
||||||
|
// log.Info("textbox closed with text:", newtext, me.textbox.callerTK.cuiName)
|
||||||
|
|
||||||
|
if me.notify.clock.tk.v != nil {
|
||||||
|
me.baseGui.SetCurrentView("help")
|
||||||
|
} else {
|
||||||
|
me.baseGui.SetCurrentView("msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
// change the text of the caller widget
|
||||||
|
me.textbox.callerTK.SetText(newtext)
|
||||||
|
me.textbox.callerTK.node.SetCurrentS(newtext)
|
||||||
|
|
||||||
|
// send an event from the plugin with the new string
|
||||||
|
me.myTree.SendUserEvent(me.textbox.callerTK.node)
|
||||||
|
|
||||||
|
win := me.textbox.callerTK.findParentWindow()
|
||||||
|
if win != nil {
|
||||||
|
// win.dumpWidget("redraw this!!!")
|
||||||
|
tk := me.textbox.callerTK
|
||||||
|
// me.textbox.callerTK.dumpWidget("resize this!!!")
|
||||||
|
me.textbox.callerTK.Size()
|
||||||
|
me.textbox.callerTK.placeWidgets(tk.gocuiSize.w0-4, tk.gocuiSize.h0-4)
|
||||||
|
// tk.dumpWidget("resize:" + tk.String())
|
||||||
|
win.makeWindowActive()
|
||||||
|
}
|
||||||
|
}
|
24
tree.go
24
tree.go
|
@ -1,24 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is reference code for toolkit developers
|
|
||||||
*/
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go.wit.com/widget"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Other goroutines must use this to access the GUI
|
|
||||||
//
|
|
||||||
// You can not acess / process the GUI thread directly from
|
|
||||||
// other goroutines. This is due to the nature of how
|
|
||||||
// Linux, MacOS and Windows work (they all work differently. suprise. surprise.)
|
|
||||||
//
|
|
||||||
// this sets the channel to send user events back from the plugin
|
|
||||||
func Callback(guiCallback chan widget.Action) {
|
|
||||||
me.myTree.Callback(guiCallback)
|
|
||||||
}
|
|
||||||
|
|
||||||
func PluginChannel() chan widget.Action {
|
|
||||||
return me.myTree.PluginChannel()
|
|
||||||
}
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "go.wit.com/log"
|
||||||
|
"go.wit.com/toolkits/tree"
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
var fakeStartWidth int = me.FakeW
|
||||||
|
var fakeStartHeight int = me.TabH + me.FramePadH
|
||||||
|
|
||||||
|
// setup fake labels for non-visible things off screen
|
||||||
|
func setFake(n *tree.Node) {
|
||||||
|
var w *guiWidget
|
||||||
|
w = n.TK.(*guiWidget)
|
||||||
|
w.isFake = true
|
||||||
|
|
||||||
|
w.gocuiSetWH(fakeStartWidth, fakeStartHeight)
|
||||||
|
|
||||||
|
fakeStartHeight += w.gocuiSize.Height()
|
||||||
|
// TODO: use the actual max hight of the terminal window
|
||||||
|
if fakeStartHeight > 24 {
|
||||||
|
fakeStartHeight = me.TabH
|
||||||
|
fakeStartWidth += me.FakeW
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mostly just sets the colors of things
|
||||||
|
func addWidget(n *tree.Node) {
|
||||||
|
if !me.ok {
|
||||||
|
log.Log(INFO, "addWidget() START NOT OKAY")
|
||||||
|
log.Log(INFO, "addWidget() START NOT OKAY")
|
||||||
|
log.Log(INFO, "addWidget() START NOT OKAY")
|
||||||
|
waitOK()
|
||||||
|
}
|
||||||
|
tk := n.TK.(*guiWidget)
|
||||||
|
log.Log(INFO, "setStartWH() w.id =", n.WidgetId, "n.name", n.String())
|
||||||
|
switch n.WidgetType {
|
||||||
|
case widget.Root:
|
||||||
|
log.Log(INFO, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.String())
|
||||||
|
// tk.color = &colorRoot
|
||||||
|
setFake(n)
|
||||||
|
return
|
||||||
|
case widget.Flag:
|
||||||
|
// tk.color = &colorFlag
|
||||||
|
setFake(n)
|
||||||
|
return
|
||||||
|
case widget.Window:
|
||||||
|
tk.frame = false
|
||||||
|
tk.labelN = tk.GetText() + " X"
|
||||||
|
// tk.setColor(&colorWindow)
|
||||||
|
me.newWindowTrigger <- tk
|
||||||
|
redoWindows(0, 0)
|
||||||
|
return
|
||||||
|
case widget.Stdout:
|
||||||
|
tk.labelN = "moreSTDOUT"
|
||||||
|
n.State.ProgName = "moreSTDOUT"
|
||||||
|
n.State.Label = "moreSTDOUT"
|
||||||
|
tk.isFake = true
|
||||||
|
return
|
||||||
|
case widget.Tab:
|
||||||
|
// tk.color = &colorTab
|
||||||
|
return
|
||||||
|
case widget.Button:
|
||||||
|
tk.setColorButton()
|
||||||
|
if tk.IsEnabled() {
|
||||||
|
} else {
|
||||||
|
tk.setColorDisable()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case widget.Checkbox:
|
||||||
|
tk.setColorInput()
|
||||||
|
tk.labelN = "X " + n.State.Label
|
||||||
|
return
|
||||||
|
case widget.Dropdown:
|
||||||
|
tk.setColorInput()
|
||||||
|
return
|
||||||
|
case widget.Textbox:
|
||||||
|
n.State.Label = ""
|
||||||
|
tk.labelN = " "
|
||||||
|
// tk.color = &colorDropdown
|
||||||
|
tk.setColorInput()
|
||||||
|
return
|
||||||
|
case widget.Combobox:
|
||||||
|
// tk.color = &colorCombobox
|
||||||
|
tk.setColorInput()
|
||||||
|
return
|
||||||
|
case widget.Box:
|
||||||
|
// tk.color = &colorBox
|
||||||
|
tk.isFake = true
|
||||||
|
setFake(n)
|
||||||
|
return
|
||||||
|
case widget.Grid:
|
||||||
|
// tk.color = &colorGrid
|
||||||
|
tk.isFake = true
|
||||||
|
setFake(n)
|
||||||
|
return
|
||||||
|
case widget.Group:
|
||||||
|
tk.setColorLabel()
|
||||||
|
tk.frame = false
|
||||||
|
return
|
||||||
|
case widget.Label:
|
||||||
|
tk.setColorLabel()
|
||||||
|
tk.frame = false
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
/*
|
||||||
|
if n.IsCurrent() {
|
||||||
|
n.updateCurrent()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
tk.dumpWidget("addWidget()unknown")
|
||||||
|
}
|
|
@ -0,0 +1,271 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/awesome-gocui/gocui"
|
||||||
|
log "go.wit.com/log"
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
// don't draw widgets that are too far down the window
|
||||||
|
func (tk *guiWidget) doNotDraw() bool {
|
||||||
|
var check bool
|
||||||
|
switch tk.WidgetType() {
|
||||||
|
case widget.Button:
|
||||||
|
check = true
|
||||||
|
case widget.Label:
|
||||||
|
check = true
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if !check {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
win := tk.findParentWindow()
|
||||||
|
if win == nil {
|
||||||
|
// don't draw anything if you can't find the parent window
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
h := tk.gocuiSize.h0 - win.gocuiSize.h0
|
||||||
|
|
||||||
|
if h > 20 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// page widgets in the window
|
||||||
|
func (tk *guiWidget) pageWidget() *rectType {
|
||||||
|
r := new(rectType)
|
||||||
|
|
||||||
|
var check bool
|
||||||
|
switch tk.WidgetType() {
|
||||||
|
case widget.Button:
|
||||||
|
check = true
|
||||||
|
case widget.Label:
|
||||||
|
check = true
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
if !check {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
win := tk.findParentWindow()
|
||||||
|
if win == nil {
|
||||||
|
// don't draw anything if you can't find the parent window
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r.w0 = tk.gocuiSize.w0
|
||||||
|
r.h0 = tk.gocuiSize.h0
|
||||||
|
r.w1 = tk.gocuiSize.w1
|
||||||
|
r.h1 = tk.gocuiSize.h1
|
||||||
|
|
||||||
|
// r.h0 = tk.gocuiSize.h0 - win.gocuiSize.h0
|
||||||
|
|
||||||
|
if r.h0 > 20 {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// display's the text of the widget in gocui
|
||||||
|
// deletes the old view if it exists and recreates it
|
||||||
|
func (tk *guiWidget) drawView() {
|
||||||
|
var err error
|
||||||
|
log.Log(INFO, "drawView() START", tk.WidgetType(), tk.String())
|
||||||
|
if me.baseGui == nil {
|
||||||
|
log.Log(ERROR, "drawView() ERROR: me.baseGui == nil", tk)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk.cuiName == "" {
|
||||||
|
log.Log(ERROR, "drawView() tk.cuiName was not set for widget", tk)
|
||||||
|
tk.cuiName = strconv.Itoa(tk.WidgetId()) + " TK"
|
||||||
|
}
|
||||||
|
log.Log(INFO, "drawView() labelN =", tk.labelN)
|
||||||
|
|
||||||
|
// this deletes the button from gocui
|
||||||
|
me.baseGui.DeleteView(tk.cuiName)
|
||||||
|
tk.v = nil
|
||||||
|
|
||||||
|
a := tk.gocuiSize.w0
|
||||||
|
b := tk.gocuiSize.h0
|
||||||
|
c := tk.gocuiSize.w1
|
||||||
|
d := tk.gocuiSize.h1
|
||||||
|
|
||||||
|
/*
|
||||||
|
// testing code for paging large windows
|
||||||
|
if tk.doNotDraw() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk.window.pager != 0 {
|
||||||
|
if r := tk.pageWidget(); r == nil {
|
||||||
|
// if nil, draw whatever it is anyway
|
||||||
|
} else {
|
||||||
|
if r.Width() == 0 && r.Height() == 0 {
|
||||||
|
// don't draw empty stuff
|
||||||
|
return
|
||||||
|
}
|
||||||
|
a = r.w0
|
||||||
|
b = r.h0
|
||||||
|
c = r.w1
|
||||||
|
d = r.h1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk.WidgetType() == widget.Window || tk.WidgetType() == widget.Flag {
|
||||||
|
if tk.window.pager != 0 {
|
||||||
|
if tk.gocuiSize.Height() > 40 {
|
||||||
|
tk.window.large = true
|
||||||
|
tk.gocuiSize.h1 = tk.gocuiSize.h0 + 40
|
||||||
|
d = tk.gocuiSize.h1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// this is all terrible. This sets the title. kinda
|
||||||
|
if tk.WidgetType() == widget.Window {
|
||||||
|
tk.textResize()
|
||||||
|
tk.full.w0 = tk.force.w0
|
||||||
|
tk.full.h0 = tk.force.h0
|
||||||
|
|
||||||
|
// for windows, make it the full size
|
||||||
|
a = tk.full.w0
|
||||||
|
b = tk.full.h0
|
||||||
|
c = tk.full.w0 + tk.gocuiSize.Width()
|
||||||
|
d = tk.full.h0 + tk.gocuiSize.Height()
|
||||||
|
} else {
|
||||||
|
if tk.internal {
|
||||||
|
// do nothing
|
||||||
|
} else {
|
||||||
|
tk.textResize() // resize gocui frame to the widget text
|
||||||
|
}
|
||||||
|
a = tk.gocuiSize.w0
|
||||||
|
b = tk.gocuiSize.h0
|
||||||
|
c = tk.gocuiSize.w1
|
||||||
|
d = tk.gocuiSize.h1
|
||||||
|
}
|
||||||
|
|
||||||
|
tk.v, err = me.baseGui.SetView(tk.cuiName, a, b, c, d, 0)
|
||||||
|
if err == nil {
|
||||||
|
tk.dumpWidget("drawView() err")
|
||||||
|
log.Log(ERROR, "drawView() internal plugin error err = nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.Is(err, gocui.ErrUnknownView) {
|
||||||
|
tk.dumpWidget("drawView() err")
|
||||||
|
log.Log(ERROR, "drawView() internal plugin error error.IS()", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk.v == nil {
|
||||||
|
log.Info("MUTEX FAIL. tk.v == nil here in drawView()")
|
||||||
|
log.Info("MUTEX FAIL. tk.v == nil here in drawView()")
|
||||||
|
log.Info("MUTEX FAIL. tk.v == nil here in drawView()")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// this actually sends the text to display to gocui
|
||||||
|
tk.v.Wrap = true
|
||||||
|
tk.v.Frame = tk.frame
|
||||||
|
tk.v.Clear()
|
||||||
|
fmt.Fprint(tk.v, tk.labelN)
|
||||||
|
|
||||||
|
// tmp hack to disable buttons on window open
|
||||||
|
if tk.WidgetType() == widget.Button {
|
||||||
|
if tk.IsEnabled() {
|
||||||
|
} else {
|
||||||
|
tk.setColorDisable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tk.WidgetType() {
|
||||||
|
case widget.Button:
|
||||||
|
if tk.IsEnabled() {
|
||||||
|
if tk.isWindowDense() && tk.isInGrid() {
|
||||||
|
tk.setColorButtonDense()
|
||||||
|
} else {
|
||||||
|
tk.setColorButton()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tk.setColorDisable()
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk.v == nil {
|
||||||
|
log.Info("MUTEX FAIL 2. tk.v was deleted somehow tk.v == nil here in drawView()")
|
||||||
|
log.Info("MUTEX FAIL 2. tk.v == nil here in drawView()")
|
||||||
|
log.Info("MUTEX FAIL 2. tk.v == nil here in drawView()")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if you don't do this here, it will be black & white only
|
||||||
|
if tk.color != nil {
|
||||||
|
tk.v.FrameColor = tk.color.frame
|
||||||
|
tk.v.FgColor = tk.color.fg
|
||||||
|
tk.v.BgColor = tk.color.bg
|
||||||
|
tk.v.SelFgColor = tk.color.selFg
|
||||||
|
tk.v.SelBgColor = tk.color.selBg
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Log(INFO, "drawView() END")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *guiWidget) DrawAt(offsetW, offsetH int) {
|
||||||
|
// w.setColor(&colorActiveW)
|
||||||
|
w.placeWidgets(offsetW, offsetH) // compute the sizes & places for each widget
|
||||||
|
// w.dumpWidget(fmt.Sprintf("DrawAt(%d,%d)", offsetW, offsetH))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *guiWidget) simpleDrawAt(offsetW, offsetH int) {
|
||||||
|
// w.setColor(&colorActiveW)
|
||||||
|
w.dumpWidget("simpleDrawAt()")
|
||||||
|
}
|
||||||
|
|
||||||
|
// display the widgets in the binary tree
|
||||||
|
func (w *guiWidget) drawTree(draw bool) {
|
||||||
|
if w == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.dumpWidget("in drawTree()")
|
||||||
|
if draw {
|
||||||
|
// w.textResize()
|
||||||
|
w.Show()
|
||||||
|
} else {
|
||||||
|
w.Hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, child := range w.children {
|
||||||
|
child.drawTree(draw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *guiWidget) Show() {
|
||||||
|
if w.isFake {
|
||||||
|
// don't display fake widgets
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if w.Hidden() {
|
||||||
|
// recursively checks if the parent is hidden
|
||||||
|
// never show hidden widgets
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if me.debug {
|
||||||
|
w.dumpWidget("drawView()")
|
||||||
|
}
|
||||||
|
|
||||||
|
w.drawView()
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
/*
|
||||||
|
DO NOT EDIT THIS FILE
|
||||||
|
|
||||||
|
this file is the same for every GUI toolkit plugin
|
||||||
|
when you are making a new GUI toolkit plugin for
|
||||||
|
a specific toolkit, you just need to define these
|
||||||
|
functions.
|
||||||
|
|
||||||
|
for example, in the "gocui" toolkit, the functions
|
||||||
|
below are what triggers the "gocui" GO package
|
||||||
|
to draw labels, buttons, windows, etc
|
||||||
|
|
||||||
|
If you are starting out trying to make a new GUI toolkit,
|
||||||
|
all you have to do is copy this file over. Then
|
||||||
|
work on making these functions. addWidget(), setText(), etc.
|
||||||
|
|
||||||
|
That's it!
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is reference code for toolkit developers
|
||||||
|
|
||||||
|
This is how information is passed in GO back to the application
|
||||||
|
via the GO 'plugin' concept
|
||||||
|
|
||||||
|
TODO: switch this to protocol buffers
|
||||||
|
*/
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "go.wit.com/log"
|
||||||
|
"go.wit.com/toolkits/tree"
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Other goroutines must use this to access the GUI
|
||||||
|
//
|
||||||
|
// You can not acess / process the GUI thread directly from
|
||||||
|
// other goroutines. This is due to the nature of how
|
||||||
|
// Linux, MacOS and Windows work (they all work differently. suprise. surprise.)
|
||||||
|
//
|
||||||
|
// this sets the channel to send user events back from the plugin
|
||||||
|
func Callback(guiCallback chan widget.Action) {
|
||||||
|
me.myTree.Callback(guiCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PluginChannel() chan widget.Action {
|
||||||
|
initOnce.Do(initPlugin)
|
||||||
|
for {
|
||||||
|
if me.myTree != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
log.Info("me.myTree == nil")
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
}
|
||||||
|
return me.myTree.PluginChannel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func FrozenChannel() chan widget.Action {
|
||||||
|
return me.myTree.FrozenChannel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func initTree() *tree.TreeInfo {
|
||||||
|
t := tree.New()
|
||||||
|
t.PluginName = PLUGIN
|
||||||
|
t.Add = newAdd
|
||||||
|
t.SetTitle = setTitle
|
||||||
|
t.SetLabel = setLabel
|
||||||
|
t.SetText = setText
|
||||||
|
t.AddText = addText
|
||||||
|
|
||||||
|
t.Enable = enableWidget
|
||||||
|
t.Disable = disableWidget
|
||||||
|
|
||||||
|
t.Show = showWidget
|
||||||
|
t.Hide = hideWidget
|
||||||
|
|
||||||
|
t.SetChecked = setChecked
|
||||||
|
t.ToolkitInit = toolkitInit
|
||||||
|
t.ToolkitClose = toolkitClose
|
||||||
|
t.ShowTable = showTable
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.wit.com/log"
|
||||||
|
"go.wit.com/toolkits/tree"
|
||||||
|
"go.wit.com/widget"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initWidget(n *tree.Node) *guiWidget {
|
||||||
|
var w *guiWidget
|
||||||
|
w = new(guiWidget)
|
||||||
|
|
||||||
|
w.node = n
|
||||||
|
w.cuiName = strconv.Itoa(w.WidgetId()) + " TK"
|
||||||
|
// w.WidgetType() = n.WidgetType
|
||||||
|
w.labelN = n.State.Label
|
||||||
|
if w.labelN == "" {
|
||||||
|
// remove this debugging hack once things are stable and fixed
|
||||||
|
w.labelN = n.GetProgName()
|
||||||
|
}
|
||||||
|
w.frame = true
|
||||||
|
w.enable = n.State.Enable
|
||||||
|
|
||||||
|
if n.WidgetType == widget.Root {
|
||||||
|
log.Log(INFO, "setupWidget() FOUND ROOT w.id =", n.WidgetId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.WidgetType == widget.Grid {
|
||||||
|
w.widths = make(map[int]int) // how tall each row in the grid is
|
||||||
|
w.heights = make(map[int]int) // how wide each column in the grid is
|
||||||
|
}
|
||||||
|
|
||||||
|
p := n.Parent
|
||||||
|
if p == nil {
|
||||||
|
log.Log(ERROR, "parent == nil", w.String(), n.WidgetId, w.WidgetType())
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
if p.TK == nil {
|
||||||
|
if n.WidgetId == 0 {
|
||||||
|
// this is a normal init condition
|
||||||
|
} else {
|
||||||
|
log.Log(ERROR, "parent.TK == nil", w.String(), n.WidgetId, w.WidgetType())
|
||||||
|
}
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the parent and append to parent children
|
||||||
|
var ptk *guiWidget
|
||||||
|
ptk = p.TK.(*guiWidget)
|
||||||
|
w.parent = ptk
|
||||||
|
ptk.children = append(ptk.children, w)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupCtrlDownWidget() {
|
||||||
|
a := new(widget.Action)
|
||||||
|
a.ProgName = "ctrlDown"
|
||||||
|
a.WidgetType = widget.Dialog
|
||||||
|
a.WidgetId = -1
|
||||||
|
a.ParentId = 0
|
||||||
|
// n := addNode(a)
|
||||||
|
n := me.myTree.AddNode(a)
|
||||||
|
|
||||||
|
me.ctrlDown = n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *guiWidget) deleteView() {
|
||||||
|
// make sure the view isn't really there
|
||||||
|
// log.Log(GOCUI, "deleteView()", w.cuiName, w.WidgetType(), w.WidgetId())
|
||||||
|
me.baseGui.DeleteView(w.cuiName)
|
||||||
|
w.v = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) String() string {
|
||||||
|
// deprecate this?
|
||||||
|
curval := strings.TrimSpace(tk.labelN)
|
||||||
|
if curval != "" {
|
||||||
|
return curval
|
||||||
|
}
|
||||||
|
curval = strings.TrimSpace(tk.GetLabel())
|
||||||
|
if curval != "" {
|
||||||
|
return curval
|
||||||
|
}
|
||||||
|
curval = tk.GetText()
|
||||||
|
if curval != "" {
|
||||||
|
return curval
|
||||||
|
}
|
||||||
|
curval = tk.node.String()
|
||||||
|
if curval != "" {
|
||||||
|
return curval
|
||||||
|
}
|
||||||
|
curval = strings.TrimSpace(tk.node.ProgName())
|
||||||
|
if curval != "" {
|
||||||
|
return curval
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) Visible() bool {
|
||||||
|
if tk == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if tk.v == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
tk.v.Visible = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) Hide() {
|
||||||
|
tk.deleteView()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) SetVisible(b bool) {
|
||||||
|
if b {
|
||||||
|
tk.Show()
|
||||||
|
} else {
|
||||||
|
tk.Hide()
|
||||||
|
}
|
||||||
|
}
|
68
view.go
68
view.go
|
@ -1,54 +1,56 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.wit.com/log"
|
|
||||||
"go.wit.com/widget"
|
"go.wit.com/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
func splitLines(s string) []string {
|
// expands the gocuiSize rectangle to fit
|
||||||
var lines []string
|
// all the text in tk.labelN
|
||||||
sc := bufio.NewScanner(strings.NewReader(s))
|
func (tk *guiWidget) textResize() {
|
||||||
for sc.Scan() {
|
var w, h int = 0, 0
|
||||||
lines = append(lines, sc.Text())
|
|
||||||
|
for _, s := range strings.Split(tk.labelN, "\n") {
|
||||||
|
s = strings.TrimSpace(s)
|
||||||
|
// log.Log(INFO, "textResize() len =", len(s), i, s)
|
||||||
|
if w < len(s) {
|
||||||
|
w = len(s)
|
||||||
|
}
|
||||||
|
h += 1
|
||||||
}
|
}
|
||||||
return lines
|
|
||||||
|
// todo: fix all this old code
|
||||||
|
if tk.WidgetType() == widget.Textbox {
|
||||||
|
if w < 5 {
|
||||||
|
w = 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is old code. now move this somewhere smarter
|
||||||
|
tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW // TODO: move this FramePadW out of here
|
||||||
|
tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH // TODO: fix this size computation
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *guiWidget) textResize() bool {
|
// deletes every view
|
||||||
// w := n.tk
|
func (w *guiWidget) hideWindow() {
|
||||||
var width, height int = 0, 0
|
if w == nil {
|
||||||
var changed bool = false
|
return
|
||||||
|
|
||||||
for i, s := range splitLines(w.labelN) {
|
|
||||||
log.Log(INFO, "textResize() len =", len(s), i, s)
|
|
||||||
if width < len(s) {
|
|
||||||
width = len(s)
|
|
||||||
}
|
|
||||||
height += 1
|
|
||||||
}
|
}
|
||||||
if w.gocuiSize.w1 != w.gocuiSize.w0+width+me.FramePadW {
|
w.Hide()
|
||||||
w.gocuiSize.w1 = w.gocuiSize.w0 + width + me.FramePadW
|
for _, child := range w.children {
|
||||||
changed = true
|
child.hideWindow()
|
||||||
}
|
}
|
||||||
if w.gocuiSize.h1 != w.gocuiSize.h0+height+me.FramePadH {
|
|
||||||
w.gocuiSize.h1 = w.gocuiSize.h0 + height + me.FramePadH
|
|
||||||
changed = true
|
|
||||||
}
|
|
||||||
if changed {
|
|
||||||
// w.showWidgetPlacement("textResize() changed")
|
|
||||||
}
|
|
||||||
return changed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *guiWidget) hideWidgets() {
|
func (w *guiWidget) hideWidgets() {
|
||||||
if w == nil {
|
if w == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.isCurrent = false
|
switch w.WidgetType() {
|
||||||
switch w.node.WidgetType {
|
|
||||||
case widget.Root:
|
case widget.Root:
|
||||||
case widget.Flag:
|
case widget.Flag:
|
||||||
case widget.Window:
|
case widget.Window:
|
||||||
|
@ -88,7 +90,7 @@ func (w *guiWidget) hideFake() {
|
||||||
func (w *guiWidget) showFake() {
|
func (w *guiWidget) showFake() {
|
||||||
if w.isFake {
|
if w.isFake {
|
||||||
w.drawView()
|
w.drawView()
|
||||||
w.showWidgetPlacement("showFake:")
|
w.dumpWidget("in showFake()")
|
||||||
}
|
}
|
||||||
for _, child := range w.children {
|
for _, child := range w.children {
|
||||||
child.showFake()
|
child.showFake()
|
||||||
|
|
198
widget.go
198
widget.go
|
@ -1,198 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/awesome-gocui/gocui"
|
|
||||||
"go.wit.com/log"
|
|
||||||
"go.wit.com/toolkits/tree"
|
|
||||||
"go.wit.com/widget"
|
|
||||||
)
|
|
||||||
|
|
||||||
func initWidget(n *tree.Node) *guiWidget {
|
|
||||||
var w *guiWidget
|
|
||||||
w = new(guiWidget)
|
|
||||||
|
|
||||||
w.node = n
|
|
||||||
w.cuiName = strconv.Itoa(w.node.WidgetId) + " TK"
|
|
||||||
w.WidgetType = n.WidgetType
|
|
||||||
w.labelN = n.State.Label
|
|
||||||
if w.labelN == "" {
|
|
||||||
// remove this debugging hack once things are stable and fixed
|
|
||||||
w.labelN = n.GetProgName()
|
|
||||||
}
|
|
||||||
w.frame = true
|
|
||||||
w.enable = n.State.Enable
|
|
||||||
|
|
||||||
if n.WidgetType == widget.Root {
|
|
||||||
log.Log(INFO, "setupWidget() FOUND ROOT w.id =", n.WidgetId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.WidgetType == widget.Grid {
|
|
||||||
w.widths = make(map[int]int) // how tall each row in the grid is
|
|
||||||
w.heights = make(map[int]int) // how wide each column in the grid is
|
|
||||||
}
|
|
||||||
|
|
||||||
p := n.Parent
|
|
||||||
if p == nil {
|
|
||||||
log.Log(ERROR, "parent == nil", w.String(), n.WidgetId, w.WidgetType)
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
if p.TK == nil {
|
|
||||||
log.Log(ERROR, "parent.TK == nil", w.String(), n.WidgetId, w.WidgetType)
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
// set the parent and append to parent children
|
|
||||||
var ptk *guiWidget
|
|
||||||
ptk = p.TK.(*guiWidget)
|
|
||||||
w.parent = ptk
|
|
||||||
ptk.children = append(ptk.children, w)
|
|
||||||
return w
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupCtrlDownWidget() {
|
|
||||||
a := new(widget.Action)
|
|
||||||
a.ProgName = "ctrlDown"
|
|
||||||
a.WidgetType = widget.Dialog
|
|
||||||
a.WidgetId = -1
|
|
||||||
a.ParentId = 0
|
|
||||||
// n := addNode(a)
|
|
||||||
n := me.myTree.AddNode(a)
|
|
||||||
|
|
||||||
me.ctrlDown = n
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *guiWidget) deleteView() {
|
|
||||||
// make sure the view isn't really there
|
|
||||||
log.Log(GOCUI, "deleteView()", w.cuiName, w.WidgetType, w.node.WidgetId)
|
|
||||||
me.baseGui.DeleteView(w.cuiName)
|
|
||||||
w.v = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *guiWidget) IsCurrent() bool {
|
|
||||||
if w.node.WidgetType == widget.Tab {
|
|
||||||
return w.isCurrent
|
|
||||||
}
|
|
||||||
if w.node.WidgetType == widget.Window {
|
|
||||||
log.Log(GOCUI, "IsCurrent() found current window", w.cuiName, w.String())
|
|
||||||
log.Log(GOCUI, "IsCurrent() window w.isCurrent =", w.isCurrent)
|
|
||||||
return w.isCurrent
|
|
||||||
}
|
|
||||||
if w.node.WidgetType == widget.Root {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return w.parent.IsCurrent()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tk *guiWidget) String() string {
|
|
||||||
return tk.node.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tk *guiWidget) Visible() bool {
|
|
||||||
if tk == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if tk.v == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
tk.v.Visible = true
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *guiWidget) Show() {
|
|
||||||
// always should the dropdown widget
|
|
||||||
if w == me.dropdownV {
|
|
||||||
me.dropdownV.drawView()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// don't display fake widgets
|
|
||||||
if w.isFake {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this isn't in the binary tree
|
|
||||||
// it's some internal widget so always display those
|
|
||||||
if w.node == nil {
|
|
||||||
w.drawView()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// always show window titles
|
|
||||||
if w.node.WidgetType == widget.Window {
|
|
||||||
w.drawView()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if w.node.WidgetType == widget.Dropdown {
|
|
||||||
log.Log(NOW, "Show() dropdown", w.cuiName, w.String())
|
|
||||||
log.Log(NOW, "Show() dropdown n.Strings() =", w.node.Strings())
|
|
||||||
}
|
|
||||||
if w.node.WidgetType == widget.Combobox {
|
|
||||||
log.Log(NOW, "Show() dropdown", w.cuiName, w.String())
|
|
||||||
log.Log(NOW, "Show() dropdown n.Strings() =", w.node.Strings())
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the widget is not in the current displayed windo
|
|
||||||
// then ignore it
|
|
||||||
log.Log(GOCUI, "Show() widget =", w.cuiName, w.String())
|
|
||||||
log.Log(GOCUI, "Show() w.IsCurrent() returned", w.IsCurrent())
|
|
||||||
if !w.IsCurrent() {
|
|
||||||
log.Log(GOCUI, "Show() NOT drawing", w.cuiName, w.String())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Log(GOCUI, "Show() drawing", w.cuiName, w.String())
|
|
||||||
|
|
||||||
// finally, check if the widget State is hidden or not
|
|
||||||
if w.node.Hidden() {
|
|
||||||
// don't display hidden widgets
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// okay, if you made it this far, then display the widget
|
|
||||||
w.drawView()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tk *guiWidget) Hide() {
|
|
||||||
tk.deleteView()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tk *guiWidget) SetVisible(b bool) {
|
|
||||||
if b {
|
|
||||||
tk.Show()
|
|
||||||
} else {
|
|
||||||
tk.Hide()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tk *guiWidget) findWidgetByName(name string) *guiWidget {
|
|
||||||
if tk.cuiName == name {
|
|
||||||
return tk
|
|
||||||
}
|
|
||||||
for _, child := range tk.children {
|
|
||||||
found := child.findWidgetByName(name)
|
|
||||||
if found != nil {
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tk *guiWidget) findWidgetByView(v *gocui.View) *guiWidget {
|
|
||||||
if tk.v == v {
|
|
||||||
return tk
|
|
||||||
}
|
|
||||||
if tk.cuiName == v.Name() {
|
|
||||||
log.Log(NOW, "findWidget() error. names are mismatched or out of sync", tk.cuiName)
|
|
||||||
log.Log(NOW, "findWidget() or maybe the view has been deleted")
|
|
||||||
// return tk
|
|
||||||
}
|
|
||||||
for _, child := range tk.children {
|
|
||||||
found := child.findWidgetByView(v)
|
|
||||||
if found != nil {
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
270
window.go
270
window.go
|
@ -1,28 +1,264 @@
|
||||||
|
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
|
||||||
|
// Use of this source code is governed by the GPL 3.0
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "go.wit.com/log"
|
||||||
|
"go.wit.com/toolkits/tree"
|
||||||
"go.wit.com/widget"
|
"go.wit.com/widget"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (tk *guiWidget) setTitle(s string) {
|
||||||
|
if tk.WidgetType() != widget.Window {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tk.v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tk.setColorWindowTitleActive()
|
||||||
|
rect := tk.gocuiSize
|
||||||
|
rect.w1 = rect.w0 + tk.full.Width() + 1
|
||||||
|
// rect.h1 = rect.h0 + 1
|
||||||
|
me.baseGui.SetView(tk.v.Name(), rect.w0-1, rect.h0, rect.w1+1, rect.h1, 0)
|
||||||
|
tk.v.Clear()
|
||||||
|
f := " %-" + fmt.Sprintf("%d", tk.full.Width()-3) + "s %s"
|
||||||
|
tmp := tk.GetLabel()
|
||||||
|
labelN := fmt.Sprintf(f, tmp, "X")
|
||||||
|
tk.v.WriteString(labelN)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) redrawWindow(w int, h int) {
|
||||||
|
if tk.WidgetType() != widget.Window {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// tk.dumpWidget(fmt.Sprintf("redrawWindow(%d,%d)", w, h))
|
||||||
|
if tk.full.Height() > 40 {
|
||||||
|
tk.window.dense = true
|
||||||
|
}
|
||||||
|
// pin the window to (w,h)
|
||||||
|
tk.gocuiSize.w0 = w
|
||||||
|
tk.gocuiSize.h0 = h
|
||||||
|
tk.gocuiSize.w1 = w + len(tk.GetLabel())
|
||||||
|
tk.labelN = tk.GetLabel() // could set XX here also but don't have final size of window yet
|
||||||
|
tk.force.w0 = w
|
||||||
|
tk.force.w1 = w
|
||||||
|
tk.force.h0 = h
|
||||||
|
tk.force.h1 = h
|
||||||
|
|
||||||
|
tk.setFullSize() // might make the green box the right size
|
||||||
|
|
||||||
|
tk.frame = false
|
||||||
|
tk.hasTabs = false
|
||||||
|
|
||||||
|
tk.DrawAt(w, h)
|
||||||
|
// tk.setColor(&colorActiveW) // sets the window to Green BG
|
||||||
|
tk.setColorWindowTitleActive()
|
||||||
|
|
||||||
|
if tk.window.collapsed {
|
||||||
|
// don't show anything but the title bar
|
||||||
|
tk.hideWindow()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tk.placeWidgets(w, h) // compute the sizes & places for each widget
|
||||||
|
|
||||||
|
// this is a test. this should not be needed
|
||||||
|
tk.full.w0 = tk.force.w0
|
||||||
|
tk.full.h0 = tk.force.h0
|
||||||
|
|
||||||
|
tk.setFullSize()
|
||||||
|
tk.Show()
|
||||||
|
if tk.v == nil {
|
||||||
|
log.Info("redrawWindow on tk.v == nil")
|
||||||
|
standardExit()
|
||||||
|
}
|
||||||
|
tk.v.Clear()
|
||||||
|
fmt.Fprint(tk.v, "ZZZ"+tk.GetText())
|
||||||
|
tk.showWidgets()
|
||||||
|
|
||||||
|
if tk.windowFrame == nil {
|
||||||
|
tk.addWindowFrameTK(0 - tk.WidgetId())
|
||||||
|
tk.windowFrame.makeTK([]string{""})
|
||||||
|
}
|
||||||
|
|
||||||
|
// this seems to correctly create the window frame
|
||||||
|
r := tk.getFullSize()
|
||||||
|
tk.windowFrame.gocuiSize.w0 = tk.force.w0
|
||||||
|
tk.windowFrame.gocuiSize.w1 = r.w1 + 1
|
||||||
|
tk.windowFrame.gocuiSize.h0 = tk.force.h0 + 2
|
||||||
|
tk.windowFrame.gocuiSize.h1 = r.h1 + 1
|
||||||
|
tk.windowFrame.full.w0 = tk.force.w0
|
||||||
|
tk.windowFrame.full.w1 = r.w1 + 1
|
||||||
|
tk.windowFrame.full.h0 = tk.force.h0 + 2
|
||||||
|
tk.windowFrame.full.h1 = r.h1 + 1
|
||||||
|
tk.windowFrame.setColorWindowFrame()
|
||||||
|
tk.windowFrame.Hide()
|
||||||
|
tk.windowFrame.Show()
|
||||||
|
|
||||||
|
// set the window frame below the window widget, but this resizes the window widget it seems
|
||||||
|
me.baseGui.SetViewBeneath(tk.windowFrame.cuiName, tk.cuiName, 1)
|
||||||
|
// so now we have to resize the window frame, but this moves it to the top?
|
||||||
|
me.baseGui.SetView(tk.windowFrame.cuiName, tk.windowFrame.full.w0, tk.windowFrame.full.h0, tk.windowFrame.full.w1, tk.windowFrame.full.h1, 0)
|
||||||
|
|
||||||
|
// so we have to redraw the widgets in the window anyway and then they will appear above he frame
|
||||||
|
tk.hideWidgets()
|
||||||
|
tk.showWidgets()
|
||||||
|
|
||||||
|
// draw the window title
|
||||||
|
tk.setTitle(tk.GetLabel())
|
||||||
|
}
|
||||||
|
|
||||||
// re-draws the buttons for each of the windows
|
// re-draws the buttons for each of the windows
|
||||||
func (w *guiWidget) redoWindows(nextW int, nextH int) {
|
func redoWindows(nextW int, nextH int) {
|
||||||
var startW int = nextW
|
for _, tk := range findWindows() {
|
||||||
var startH int = nextH
|
// tk.dumpWidget(fmt.Sprintf("redoWindowsS (%d,%d)", nextW, nextH))
|
||||||
|
if tk.window.wasDragged {
|
||||||
for _, child := range w.children {
|
// don't move windows around the user has dragged to a certain location
|
||||||
if child.node.WidgetType != widget.Window {
|
tk.makeWindowActive()
|
||||||
continue
|
} else {
|
||||||
|
w, _ := me.baseGui.Size()
|
||||||
|
if nextW > w-20 {
|
||||||
|
nextW = 0
|
||||||
|
nextH += 4
|
||||||
|
}
|
||||||
|
// probably a new window?
|
||||||
|
tk.redrawWindow(nextW, nextH)
|
||||||
}
|
}
|
||||||
|
// tk.dumpWidget(fmt.Sprintf("redoWindowsE (%d,%d)", nextW, nextH))
|
||||||
|
|
||||||
child.frame = false
|
// increment the width for the next window
|
||||||
child.hasTabs = false
|
nextW += tk.gocuiSize.Width() + 10
|
||||||
|
// nextH += 10
|
||||||
child.gocuiSetWH(nextW, nextH)
|
|
||||||
child.Hide()
|
|
||||||
|
|
||||||
child.drawView()
|
|
||||||
sizeW := child.gocuiSize.Width()
|
|
||||||
nextW += sizeW + 4
|
|
||||||
child.redoWindows(startW+3, startH+2)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) addWindowFrameTK(wId int) {
|
||||||
|
n := tk.addWindowFrame(wId)
|
||||||
|
tk.windowFrame = n.TK.(*guiWidget)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (win *guiWidget) addWindowFrame(wId int) *tree.Node {
|
||||||
|
n := new(tree.Node)
|
||||||
|
n.WidgetType = widget.Flag
|
||||||
|
n.WidgetId = wId
|
||||||
|
n.ParentId = 0
|
||||||
|
|
||||||
|
// store the internal toolkit information
|
||||||
|
tk := new(guiWidget)
|
||||||
|
tk.frame = true
|
||||||
|
tk.labelN = "windowFrame text"
|
||||||
|
tk.internal = true
|
||||||
|
|
||||||
|
tk.node = n
|
||||||
|
if tk.node.Parent == nil {
|
||||||
|
tk.node.Parent = me.treeRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the name used by gocui to the id
|
||||||
|
tk.cuiName = fmt.Sprintf("%d DR", wId)
|
||||||
|
|
||||||
|
// tk.color = &colorGroup
|
||||||
|
|
||||||
|
// add this new widget on the binary tree
|
||||||
|
tk.parent = win
|
||||||
|
if tk.parent == nil {
|
||||||
|
panic("addDropdown() didn't get treeRoot guiWidget")
|
||||||
|
} else {
|
||||||
|
tk.parent.children = append(tk.parent.children, tk)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.TK = tk
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) isWindowActive() bool {
|
||||||
|
if !(tk.WidgetType() == widget.Window || tk.WidgetType() == widget.Stdout) {
|
||||||
|
// only allow Window or the Stdout widgets to be made active
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return tk.window.active
|
||||||
|
}
|
||||||
|
|
||||||
|
// always redraws at the corner of the gocuiSize box
|
||||||
|
func (tk *guiWidget) makeWindowActive() {
|
||||||
|
if !(tk.WidgetType() == widget.Window || tk.WidgetType() == widget.Stdout) {
|
||||||
|
// only allow Window or the Stdout widgets to be made active
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk.WidgetType() == widget.Stdout {
|
||||||
|
me.stdout.outputOnTop = true
|
||||||
|
} else {
|
||||||
|
// me.stdout.outputOnTop = false // ?
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable and increment all the windows
|
||||||
|
for _, tk := range me.allwin {
|
||||||
|
tk.window.order += 1
|
||||||
|
tk.window.active = false
|
||||||
|
// tk.setColor(&colorWindow) // color for inactive windows
|
||||||
|
tk.setColorWindowTitle()
|
||||||
|
}
|
||||||
|
|
||||||
|
// set this window as the active one
|
||||||
|
tk.window.active = true
|
||||||
|
tk.window.order = 0
|
||||||
|
|
||||||
|
tk.redrawWindow(tk.gocuiSize.w0, tk.gocuiSize.h0)
|
||||||
|
setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
|
||||||
|
|
||||||
|
/*
|
||||||
|
// print out the window list
|
||||||
|
for _, tk := range me.allwin {
|
||||||
|
log.Info("makeWindowActive() Window", tk.labelN, tk.window.active, tk.window.order)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk *guiWidget) makeTK(ddItems []string) {
|
||||||
|
items := strings.Join(ddItems, "\n")
|
||||||
|
tk.labelN = items
|
||||||
|
tk.SetText(items)
|
||||||
|
tk.gocuiSize.w0 = 100
|
||||||
|
tk.gocuiSize.w1 = 120
|
||||||
|
tk.gocuiSize.h0 = 15
|
||||||
|
tk.gocuiSize.h1 = 18
|
||||||
|
/*
|
||||||
|
var err error
|
||||||
|
tk.v, err = me.baseGui.SetView(tk.cuiName,
|
||||||
|
tk.gocuiSize.w0,
|
||||||
|
tk.gocuiSize.h0,
|
||||||
|
tk.gocuiSize.w1,
|
||||||
|
tk.gocuiSize.h1, 0)
|
||||||
|
if err != nil {
|
||||||
|
log.Info("makeTK() err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if tk.v == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tk.v.Wrap = true
|
||||||
|
tk.v.Frame = true
|
||||||
|
tk.v.Clear()
|
||||||
|
fmt.Fprint(tk.v, items)
|
||||||
|
*/
|
||||||
|
tk.Show()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (win *guiWidget) checkWindowClose(w int, h int) bool {
|
||||||
|
s := fmt.Sprintf("mouse(%d,%d) ", w, h)
|
||||||
|
offW := win.full.w1 - w
|
||||||
|
offH := h - win.full.h0
|
||||||
|
s += fmt.Sprintf("offset(%d,%d)", offW, offH)
|
||||||
|
if (offW < 2) && (offH < 2) {
|
||||||
|
log.Info("attempting close on ", s, win.cuiName)
|
||||||
|
me.myTree.SendWindowCloseEvent(win.node)
|
||||||
|
// store the stdout corner for computing the drag size
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// log.Info("not attempting close on ", s, win.cuiName)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue