Compare commits

..

144 Commits

Author SHA1 Message Date
Jeff Carr 86aa5fb001 reverse in STDOUT is no longer the default 2025-09-03 01:56:01 -05:00
Jeff Carr 262426fb44 make the help menu appear when libnotify is clicked 2025-09-03 01:23:22 -05:00
Jeff Carr ec0807ce2b minor 2025-08-16 21:49:54 -05:00
Jeff Carr ce1d9457f9 syntax change 2025-06-04 06:34:06 -05:00
Jeff Carr 664ce4dfae rm old code 2025-04-30 14:41:27 -05:00
Jeff Carr 7b6af30194 text edit box kinda works sometimes 2025-04-24 19:28:33 -05:00
Jeff Carr e0c55e73d2 more standard SetView() 2025-04-22 20:50:14 -05:00
Jeff Carr 4efbfa7a1d fixing textboxes 2025-04-22 18:49:16 -05:00
Jeff Carr 9669a63c5d use isDense() everywhere 2025-03-25 13:17:00 -05:00
Jeff Carr bf250b5ac6 always make protobuf tables dense 2025-03-25 13:17:00 -05:00
Jeff Carr aaebb4c5d9 table headers identified. ready for sorting 2025-03-25 13:17:00 -05:00
Jeff Carr 36514cbb68 fix arrow up & down on scrolling stdout 2025-03-25 13:17:00 -05:00
Jeff Carr 1552eedc18 save the output window state 2025-03-25 13:17:00 -05:00
Jeff Carr 4523eda0fa remove the tmp file 2025-03-17 05:35:57 -05:00
Jeff Carr 3acf473792 quiet more startup noise 2025-03-10 23:38:43 -05:00
Jeff Carr 6c3149c0fe quiet output 2025-03-10 23:27:14 -05:00
Jeff Carr 784eac740f testing code 2025-03-05 03:00:11 -06:00
Jeff Carr a88937c508 working on delete table 2025-03-04 14:33:39 -06:00
Jeff Carr 96eac58cf5 name change libnotify 'menu' to 'icon' 2025-03-04 13:35:13 -06:00
Jeff Carr b8781b1b64 remove debugging delays 2025-03-04 04:32:22 -06:00
Jeff Carr 126b9a30a2 faster is okay now 2025-03-04 04:29:27 -06:00
Jeff Carr 994449b1c2 lock for now for gocui 2025-03-04 04:06:36 -06:00
Jeff Carr 5ebb13a454 add a frozen channel 2025-03-04 01:58:13 -06:00
Jeff Carr 6127fa1cbb try out sync.Once 2025-03-03 23:50:11 -06:00
Jeff Carr 54bbe72aa8 SIGWINCH works on background and libnotify widgets 2025-03-03 22:56:05 -06:00
Jeff Carr b373eab346 set help window offset 2025-03-03 22:21:39 -06:00
Jeff Carr e73cfaf490 by default, hide help menu 2025-03-03 18:41:34 -06:00
Jeff Carr 046e6b4d6c mouse clicks work on libnotify menu 2025-03-03 18:36:50 -06:00
Jeff Carr efb5ede4b9 libnotify menu at right height 2025-03-03 16:37:45 -06:00
Jeff Carr 5e6d7cffdf clock works again 2025-03-03 15:00:27 -06:00
Jeff Carr 76e15fa1df start looking into proper mutex locking 2025-03-03 11:59:45 -06:00
Jeff Carr 75014f4b28 define a background widget more properly 2025-03-03 08:31:21 -06:00
Jeff Carr 9ef16c1bf2 better tree init() 2025-03-03 03:45:36 -06:00
Jeff Carr da54c0f039 always write to /tmp/ also 2025-03-03 02:31:12 -06:00
Jeff Carr 6d1dfed3db set global options in init 2025-03-03 02:12:54 -06:00
Jeff Carr 65cf744a86 cleaner libnotifyUpdate() 2025-03-03 01:51:16 -06:00
Jeff Carr caf7428ba3 generic init debugging area 2025-03-03 01:24:12 -06:00
Jeff Carr 948d2af071 rm more wrong code 2025-03-03 01:19:01 -06:00
Jeff Carr ddd7709182 start rewinding all the other dumb init code 2025-03-03 01:16:54 -06:00
Jeff Carr 9912c3eb82 stdout init almost finally works 2025-03-03 01:07:27 -06:00
Jeff Carr 660d9e7e3a init is more correct now 2025-03-03 01:00:11 -06:00
Jeff Carr 0124d25c34 more work on a better Init() 2025-03-03 00:51:07 -06:00
Jeff Carr ed3789c23f jesus. I never even made a ToolkitInit() 2025-03-03 00:12:40 -06:00
Jeff Carr 6f739933b7 always update clock 2025-03-02 20:05:58 -06:00
Jeff Carr 4b79e862a7 some crippled dumb refresh code kinda makes init() work for now 2025-03-02 18:16:56 -06:00
Jeff Carr dc329ed18c can refresh manually 2025-03-02 18:04:08 -06:00
Jeff Carr 3e7287baea better, but still wrong dropdown menu sizes 2025-03-02 17:44:17 -06:00
Jeff Carr 04406b3561 support a libnotify menu 2025-03-02 17:05:17 -06:00
Jeff Carr 0638183356 start moving to standard internal TK functions 2025-03-02 13:34:09 -06:00
Jeff Carr f24c509859 align 1st window. start libnotify 2025-03-02 12:04:59 -06:00
Jeff Carr a000c06987 help menu was wrong. thanks bmath 2025-02-23 23:15:31 -06:00
Jeff Carr 3b9e5beff6 'q' doesn't work 2025-02-23 18:57:40 -06:00
Jeff Carr ad34230d67 starting the table window 2025-02-19 17:39:45 -06:00
Jeff Carr 57b6efd831 add the protobuf to the struct 2025-02-19 17:39:45 -06:00
Jeff Carr f36f9cfd0f only a few node references left 2025-02-19 17:39:45 -06:00
Jeff Carr a5800917e8 still more 2025-02-19 17:39:45 -06:00
Jeff Carr c64592f326 more and more 2025-02-19 17:39:45 -06:00
Jeff Carr 377b08eeb6 protobuf stuff 2025-02-19 17:39:45 -06:00
Jeff Carr 22e139e2e5 protobuf changes 2025-02-19 17:39:45 -06:00
Jeff Carr 6b7fafbde2 protobuf changes 2025-02-19 17:39:45 -06:00
Jeff Carr 3f09b2b6e4 refactor for protobuf 2025-02-19 17:39:45 -06:00
Jeff Carr 31324ad083 string rows 2025-02-19 17:39:45 -06:00
Jeff Carr ad299911f1 delete from the gocui binary tree 2025-02-19 17:39:45 -06:00
Jeff Carr 3d1bb9680a close kinda working 2025-02-19 17:39:45 -06:00
Jeff Carr 9cd1d582e2 window close works 2025-02-19 17:39:45 -06:00
Jeff Carr 3ca1fff755 mouse scroll in stdout 2025-02-13 22:54:57 -06:00
Jeff Carr 5a9f3565aa protect against another nil 2025-02-13 22:24:34 -06:00
Jeff Carr 1f33979af9 add Disable() and Enable() 2025-02-13 21:04:37 -06:00
Jeff Carr 1e79d31a02 subbed in tables 2025-02-13 20:10:55 -06:00
Jeff Carr 08a5b198c7 no more newaction(). finally. 2025-02-13 15:04:28 -06:00
Jeff Carr c215e3c2c4 still moving everything to initPlugin() 2025-02-13 14:22:33 -06:00
Jeff Carr c5472a42a2 finally starting to clean up the init() process 2025-02-13 14:11:52 -06:00
Jeff Carr fa9ec36dbb initOnce() 2025-02-12 17:00:32 -06:00
Jeff Carr 8c459da9f7 add some notes 2025-02-12 15:43:40 -06:00
Jeff Carr 552bdeb1e6 plugin related cleanups 2025-02-12 15:26:24 -06:00
Jeff Carr 0a60272440 working on button disable 2025-02-12 00:19:15 -06:00
Jeff Carr a575a08bcc disable me.textbox() output 2025-02-11 13:55:17 -06:00
Jeff Carr 57e5ff22dc minor 2025-02-11 13:23:50 -06:00
Jeff Carr 733c595c54 disable and enable doesn't crash 2025-02-11 13:22:23 -06:00
Jeff Carr 535646335a rm debugging output 2025-02-10 23:43:08 -06:00
Your Name 8d24366492 early debugging code 2024-01-01 12:00:00 -06:00
Jeff Carr 6df064282c attempting to force the "Running..." box in the corner 2025-02-09 17:31:32 -06:00
Jeff Carr 6ea6ffaa3d disable the whole screen while things are waiting 2025-02-09 14:35:11 -06:00
Jeff Carr 7d793c68db disable window paging code for now 2025-02-09 13:33:26 -06:00
Jeff Carr b07d8bd8f7 rename 2025-02-09 13:09:42 -06:00
Jeff Carr 42758e1459 junk 2025-02-09 13:09:22 -06:00
Jeff Carr f30489219b color cleanups 2025-02-09 13:08:00 -06:00
Jeff Carr bb2732b621 color code cleanups 2025-02-09 12:57:42 -06:00
Jeff Carr 00d1256eba mouse code is better than it's ever been 2025-02-09 12:47:49 -06:00
Jeff Carr cf073e9aae more mouse code cleanups 2025-02-09 12:43:34 -06:00
Jeff Carr 90a9f84f10 more code cleanups 2025-02-09 12:34:53 -06:00
Jeff Carr c5cada3dc9 code cleanups 2025-02-09 12:21:43 -06:00
Jeff Carr 87d31a3d94 stub in code to page large windows 2025-02-09 09:00:40 -06:00
Jeff Carr c5d9522c0b clip large windows 2025-02-09 08:28:10 -06:00
Jeff Carr 4a009f79a2 fix panic in Enable() and Disable() 2025-02-09 06:45:01 -06:00
Jeff Carr 70452bdaac buttons disabled on window open 2025-02-09 06:21:21 -06:00
Jeff Carr 36745e0492 button disable maybe works? 2025-02-09 06:14:57 -06:00
Jeff Carr 9540c01d83 cleaning up color handling for enable/disable 2025-02-09 05:17:01 -06:00
Jeff Carr 58eff2a9e2 disable enable is starting to display 2025-02-09 04:53:49 -06:00
Jeff Carr c00084bf3f better window title colors 2025-02-09 04:33:30 -06:00
Jeff Carr 9a08b37be4 better window titlebar 2025-02-09 04:19:32 -06:00
Jeff Carr 955afcb1a9 text boxes are forced 5 spaces wide 2025-02-09 04:03:48 -06:00
Jeff Carr af3fec6f20 remove old debugging code 2025-02-09 03:24:10 -06:00
Jeff Carr 5bac0308e5 window close works 2025-02-09 03:21:48 -06:00
Jeff Carr 8d8fc22745 detect an attempt to close a window 2025-02-09 03:00:10 -06:00
Jeff Carr eba5ea8cc0 leave this code as a reminder for later 2025-02-09 01:46:04 -06:00
Jeff Carr b8b8a409ea show stdout buffer when it is small 2025-02-09 01:40:57 -06:00
Jeff Carr 010bd2a33f just remove this I guess 2025-02-08 21:31:04 -06:00
Jeff Carr ba629f1892 things resized 2025-02-08 18:43:48 -06:00
Jeff Carr bff0943dc5 keep cleaning the 'msg' stdout handling code 2025-02-08 18:21:31 -06:00
Jeff Carr 2c07da350a resize window after text thing worked 2025-02-08 18:09:09 -06:00
Jeff Carr 82ed687460 mouse drag was not always right 2025-02-08 18:03:11 -06:00
Jeff Carr 5a84456c7a more usability cleanups 2025-02-08 17:19:41 -06:00
Jeff Carr f8b7c603a1 <enter> closes and saves the text from the textbox 2025-02-08 16:23:03 -06:00
Jeff Carr 419f4aef6a more work on the text entry 2025-02-08 16:15:38 -06:00
Jeff Carr 12d0e185cc text entry worked for the first time 2025-02-08 16:02:00 -06:00
Jeff Carr e80827d890 stuff 2025-02-08 15:16:41 -06:00
Jeff Carr 42eafb87c7 dropdown works again 2025-02-08 15:01:36 -06:00
Jeff Carr ea544e429e setting the size of the textbox correctly 2025-02-08 14:43:38 -06:00
Jeff Carr 58cb7f3d2d still can't remove "msg" create in mouseMove() 2025-02-08 14:24:43 -06:00
Jeff Carr 53eb14ccbd better filename 2025-02-08 14:09:14 -06:00
Jeff Carr 23dfd96a87 changing Height() to reflect more realistic values 2025-02-08 14:04:11 -06:00
Jeff Carr 5827b9ace2 buttons in dense mode line up correctly 2025-02-08 13:57:31 -06:00
Jeff Carr c4f9bac85e add config default to show stdout onscreen on start 2025-02-08 13:28:19 -06:00
Jeff Carr 90083d5bcb a clock 2025-02-08 12:50:05 -06:00
Jeff Carr 77b4bcb94b quiet refresh goroutine 2025-02-08 12:13:12 -06:00
Jeff Carr 665d2289e2 tiggers gocui on startup 2025-02-08 12:05:25 -06:00
Jeff Carr ed024aac70 textbox opens 2025-02-08 10:30:19 -06:00
Jeff Carr 6045a205fd add 'Esc' key to close dropdown menu 2025-02-08 10:20:00 -06:00
Jeff Carr 227419c1ad dropdown menu works again 2025-02-08 10:00:12 -06:00
Jeff Carr 7b06b81ed3 double click windows brings to front 2025-02-08 09:25:09 -06:00
Jeff Carr 9d1a045a1f window double click brings to forefront 2025-02-08 09:16:22 -06:00
Jeff Carr 1923f8df96 mouse dragging works correctly again 2025-02-08 09:12:35 -06:00
Jeff Carr b730ee9459 double click stdout to move to front or back 2025-02-08 08:55:26 -06:00
Jeff Carr a6c1864f43 removed old var 2025-02-08 08:47:55 -06:00
Jeff Carr 1010db44a6 mouse double click implemented too. why not? took 5 minutes 2025-02-08 08:42:41 -06:00
Jeff Carr 078a23e0e0 detect double click 2025-02-08 08:36:08 -06:00
Jeff Carr 44264df09d mouse click vs drag works 2025-02-08 08:12:39 -06:00
Jeff Carr 0aa82f5ba5 trying to delay on mouse drag 2025-02-08 08:07:03 -06:00
Jeff Carr 83b4d7142a window setTitle() 2025-02-08 07:29:42 -06:00
Jeff Carr fefa99920b global protobuf name conflict 2025-02-08 06:34:51 -06:00
Jeff Carr 8185d8bc1a macos iterm2 only seems to work with dark mode 2025-02-08 05:16:05 -06:00
Jeff Carr 481fa11211 set stdout in a better place 2025-02-07 19:11:20 -06:00
Jeff Carr a295aa420b remember stdout location on restore 2025-02-07 16:44:01 -06:00
34 changed files with 2719 additions and 1679 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
go.mod go.mod
go.sum go.sum
gocui gocui
resources/*.so

View File

@ -1,7 +1,7 @@
VERSION = $(shell git describe --tags) VERSION = $(shell git describe --tags)
BUILDTIME = $(shell date +%Y.%m.%d) BUILDTIME = $(shell date +%Y.%m.%d)
all: goimports vet gocui all: clean goimports vet gocui
@ldd gocui.so @ldd gocui.so
vet: vet:
@ -9,7 +9,7 @@ vet:
@echo this go plugin builds okay @echo this go plugin builds okay
gocui: gocui:
GO111MODULE=off go build -v -buildmode=plugin -o gocui.so \ GO111MODULE=off go build -v -x -buildmode=plugin -o gocui.so \
-ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}" -ldflags "-X main.VERSION=${VERSION} -X main.BUILDTIME=${BUILDTIME} -X gui.GUIVERSION=${VERSION}"
install: install:
@ -23,9 +23,9 @@ custom:
GO111MODULE=off go build -v -work -buildmode=blah GO111MODULE=off go build -v -work -buildmode=blah
clean: clean:
rm -f gocui gocui.so rm -f gocui *.so go.*
rm -f *.pb.go *.patch rm -f *.pb.go *.patch
go-mod-clean --purge 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
@ -49,5 +49,5 @@ redomod:
GO111MODULE= go mod tidy GO111MODULE= go mod tidy
proto: proto:
autogenpb --proto view.proto autogenpb --proto gocuiView.proto
make goimports make goimports

View File

@ -23,16 +23,16 @@ func setChecked(n *tree.Node, b bool) {
// redraw the checkbox // redraw the checkbox
func (tk *guiWidget) setCheckbox() { func (tk *guiWidget) setCheckbox() {
if tk.node.WidgetType != widget.Checkbox { if tk.WidgetType() != widget.Checkbox {
log.Log(WARN, "setCheckbox() being run on widget:", tk.node.WidgetType) log.Log(WARN, "setCheckbox() being run on widget:", tk.WidgetType())
return 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()

530
color.go
View File

@ -3,216 +3,327 @@
package main package main
// information about how terminfo works
// https://jvns.ca/blog/2024/10/01/terminal-colours/
import ( import (
"math/rand" "math/rand"
"github.com/gdamore/tcell/v2"
"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
var none gocui.Attribute = gocui.AttrNone // information about how terminfo works
var lightPurple gocui.Attribute = gocui.GetColor("#DDDDDD") // light purple // https://jvns.ca/blog/2024/10/01/terminal-colours/
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: // TODO: move all this to a protobuf
// 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: gocui.ColorWhite,
// selBg: powdererBlue,
selBg: gocui.ColorBlue,
name: "normal window",
}
var colorActiveW colorT = colorT{
frame: none,
fg: gocui.ColorWhite,
bg: gocui.ColorBlue,
selFg: gocui.ColorBlue,
selBg: none,
name: "normal window",
}
// var colorActiveW colorT = colorT{none, none, powdererBlue, none, powdererBlue, "active window"} // sets the window to blue
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 colorLabel colorT = colorT{
frame: gocui.ColorWhite,
fg: none,
bg: none,
selFg: none,
selBg: none,
name: "normal label",
}
var colorGroup colorT = colorT{
frame: none,
fg: none,
bg: none,
selFg: gocui.ColorWhite,
selBg: none,
name: "normal label",
}
var colorButton colorT = colorT{
frame: gocui.ColorGreen,
fg: none,
bg: none,
selFg: gocui.ColorGreen,
selBg: none,
name: "normal button",
}
var colorButtonDense colorT = colorT{
frame: none,
fg: none,
bg: gocui.ColorGreen,
// bg: tcell.ColorGreen,
selFg: none,
selBg: gocui.ColorWhite,
name: "normal button",
}
var colorDropdown colorT = colorT{
frame: gocui.ColorYellow,
fg: none,
bg: none,
selFg: gocui.ColorYellow,
selBg: gocui.ColorBlack,
name: "normal dropdown",
}
var colorCombobox colorT = colorT{
frame: gocui.ColorBlue,
fg: none,
bg: none,
selFg: gocui.ColorBlue,
selBg: gocui.ColorBlack,
name: "normal combobox",
}
var colorCheckbox colorT = colorT{
frame: gocui.ColorRed,
fg: none,
bg: none,
selFg: gocui.ColorRed,
selBg: gocui.ColorWhite,
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
tk.color.selFg = gocui.AttrNone
tk.color.selBg = gocui.AttrNone
} else {
tk.color.frame = gocui.AttrNone
tk.color.fg = gocui.AttrNone
tk.color.bg = gocui.AttrNone
tk.color.selFg = gocui.AttrNone
tk.color.selBg = gocui.AttrNone
} }
func (w *guiWidget) disableColor() { tk.updateColor()
if w.color != &colorDisabled {
w.defaultColor = w.color
}
w.setColor(&colorDisabled)
} }
func (w *guiWidget) enableColor() { // weird. lots of color problems for me on debian sid using the traditional Andy Herzfield 'gnome'
w.setColor(w.defaultColor) 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
} }
func (w *guiWidget) setDefaultHighlight() { tk.updateColor()
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 { func (tk *guiWidget) setColorWindowTitle() {
colors := []string{"Green", "#FFAA55", "Yellow", "Blue", "Red", "Black", "White"} if tk.color == nil {
i := rand.Intn(len(colors)) tk.color = new(colorT)
log.Log(NOW, "randColor() i =", i) }
return gocui.GetColor(colors[i]) 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
} }
func (w *guiWidget) redoColor(draw bool) { tk.updateColor()
if w == nil {
return
} }
log.Sleep(.05) func (tk *guiWidget) setColorBG() {
w.setDefaultHighlight() if tk.color == nil {
// w.setDefaultWidgetColor() tk.color = new(colorT)
w.Show() }
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
}
for _, child := range w.children { tk.updateColor()
child.redoColor(draw)
} }
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) setColorLabelTable() {
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.ColorGreen
}
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
@ -234,51 +345,28 @@ 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 (tk *guiWidget) SetColorRed() { func randColor() gocui.Attribute {
tk.color = new(colorT) colors := []string{"Green", "#FFAA55", "Yellow", "Blue", "Red", "Black", "White"}
tk.color.fg = gocui.ColorRed i := rand.Intn(len(colors))
log.Log(NOW, "randColor() i =", i)
return gocui.GetColor(colors[i])
} }
func (tk *guiWidget) setColorBlue() { var none gocui.Attribute = gocui.AttrNone
// r, g, b := var colorNone colorT = colorT{none, none, none, none, none, "debug none"}
// newgreen := gocui.NewRGBColor(tcell.ColorLightBlue.RGB())
// tk.color.fg = gocui.ColorBlue var lightPurple gocui.Attribute = gocui.GetColor("#DDDDDD") // light purple
tk.color.bg = gocui.NewRGBColor(tcell.ColorLightBlue.RGB()) 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
// weird. lots of color problems for me on debian sid using the traditional Andy Herzfield 'gnome' // Standard defined colors from gocui:
func (tk *guiWidget) setColorButtonDenseOLD() { // ColorBlack ColorRed ColorGreen ColorYellow ColorBlue ColorMagenta ColorCyan ColorWhite
/*
cellgreen := tcell.ColorLightGreen
r, g, b := cellgreen.RGB()
newgreen := gocui.NewRGBColor(r, g, b)
*/
// tk.color.fg = gocui.ColorBlue // v.BgColor = gocui.GetColor("#111111") // crazy red
if tk.color == nil { // v.BgColor = gocui.GetColor("#FF9911") // heavy red
tk.color = new(colorT) // v.SelBgColor = gocui.GetColor("#FFEE11") // blood red
}
lightGreen := gocui.GetColor("0x90EE90")
lightGreen = gocui.GetColor("0x008000")
lightGreen = gocui.NewRGBColor(0x00, 0x80, 0x00)
tk.color.frame = gocui.ColorYellow // v.BgColor = gocui.GetColor("#55AAFF") // super light grey
tk.color.fg = gocui.AttrNone // v.BgColor = gocui.GetColor("#FFC0CB") // 'w3c pink' yellow
tk.color.bg = gocui.ColorGreen
tk.color.bg = lightGreen
tk.color.bg = gocui.Attribute(tcell.ColorLightGreen)
tk.color.bg = superLightGrey
// bg: gocui.ColorGreen,
tk.color.selFg = gocui.AttrNone
tk.color.selBg = gocui.ColorGreen
// tk.color = &colorButtonDense
/*
tk.color.selFg = gocui.AttrNone
r, g, b := tcell.ColorLightGreen.RGB()
log.Info("color =", tcell.ColorLightGreen.CSS(), r, g, b)
tk.color.selBg = gocui.NewRGBColor(r, g, b)
*/
}

View File

@ -1,144 +0,0 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0
package main
// simple colors for light and dark
import (
"github.com/awesome-gocui/gocui"
)
func (tk *guiWidget) setColorWindowFrame() {
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.ColorBlack
tk.color.bg = gocui.ColorBlack
tk.color.selFg = gocui.AttrNone
tk.color.selBg = gocui.AttrNone
return
}
tk.color.frame = gocui.AttrNone
tk.color.fg = gocui.AttrNone
tk.color.bg = gocui.AttrNone
tk.color.selFg = gocui.AttrNone
tk.color.selBg = gocui.AttrNone
}
// weird. lots of color problems for me on debian sid using the traditional Andy Herzfield 'gnome'
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
return
}
tk.color.frame = gocui.ColorWhite
tk.color.fg = gocui.AttrNone
tk.color.bg = gocui.ColorBlue
tk.color.selFg = gocui.ColorBlue
tk.color.selBg = gocui.AttrNone
}
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
return
}
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
}
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
return
}
tk.color.frame = gocui.AttrNone
tk.color.fg = gocui.ColorBlack
tk.color.bg = gocui.AttrNone
tk.color.selFg = gocui.AttrNone
tk.color.selBg = gocui.AttrNone
}
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
return
}
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
}
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
return
}
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
}
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
return
}
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
}

View File

@ -12,9 +12,9 @@ import (
) )
func (w *guiWidget) dumpTree(s string) { func (w *guiWidget) dumpTree(s string) {
// log.Log(ERROR, "dump 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, "dump w.TK == nil", w.node.WidgetId, w.node.WidgetType, w.String()) log.Log(ERROR, "dump w.TK == nil", w.WidgetId(), w.WidgetType(), w.String())
return return
} }
w.dumpWidget("dumpTree() " + s) w.dumpWidget("dumpTree() " + s)
@ -25,12 +25,12 @@ func (w *guiWidget) dumpTree(s string) {
} }
func (w *guiWidget) dumpWindows(s string) { func (w *guiWidget) dumpWindows(s string) {
// log.Log(ERROR, "dump 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, "dump w.TK == nil", w.node.WidgetId, w.node.WidgetType, w.String()) log.Log(ERROR, "dump w.TK == nil", w.WidgetId(), w.WidgetType(), w.String())
return return
} }
if w.node.WidgetType == widget.Window { if w.WidgetType() == widget.Window {
s += fmt.Sprintf(" F(%d,%d)", w.force.w0, w.force.h0) s += fmt.Sprintf(" F(%d,%d)", w.force.w0, w.force.h0)
// can't set this here. doesn't work // can't set this here. doesn't work
// w.full.w0 = w.force.w0 // w.full.w0 = w.force.w0
@ -49,13 +49,13 @@ func (tk *guiWidget) dumpWidget(s string) {
var s1 string var s1 string
var pId int var pId int
// tk.verifyRect() // tk.verifyRect()
if tk.node.Parent == nil { if tk.parent == nil {
log.Logf(WARN, "showWidgetPlacement() parent == nil wId=%d cuiName=%s", tk.node.WidgetId, tk.cuiName) log.Logf(WARN, "showWidgetPlacement() parent == nil wId=%d cuiName=%s", tk.WidgetId(), tk.cuiName)
pId = 0 pId = 0
} else { } else {
pId = tk.node.Parent.WidgetId pId = tk.parent.WidgetId()
} }
s1 = fmt.Sprintf("(wId,pId)=(%4d,%4d) ", tk.node.WidgetId, pId) s1 = fmt.Sprintf("(wId,pId)=(%4d,%4d) ", tk.WidgetId(), pId)
sizeW, sizeH := tk.Size() sizeW, sizeH := tk.Size()
hide := "S" hide := "S"
if tk.Hidden() { if tk.Hidden() {
@ -69,10 +69,10 @@ func (tk *guiWidget) dumpWidget(s string) {
s1 += fmt.Sprintf(" %3s %3s %3s %3s ", "", "", "", "") s1 += fmt.Sprintf(" %3s %3s %3s %3s ", "", "", "", "")
} }
s1 += fmt.Sprintf(" full=(%3d,%3d,%3d,%3d)", tk.full.w0, tk.full.h0, tk.full.w1, tk.full.h1) s1 += fmt.Sprintf(" full=(%3d,%3d,%3d,%3d)", tk.full.w0, tk.full.h0, tk.full.w1, tk.full.h1)
if tk.node.Parent != nil { if tk.parent != nil {
if tk.node.Parent.WidgetType == widget.Grid { if tk.parent.WidgetType() == widget.Grid {
s1 += fmt.Sprintf("At(%3d,%3d)", tk.node.State.AtW, tk.node.State.AtH) s1 += fmt.Sprintf("At(%3d,%3d)", tk.GridW(), tk.GridH())
s1 += fmt.Sprintf("(%3d,%3d) ", tk.parent.widths[tk.node.State.AtW], tk.parent.heights[tk.node.State.AtH]) s1 += fmt.Sprintf("(%3d,%3d) ", tk.parent.widths[tk.GridW()], tk.parent.heights[tk.GridH()])
} else { } else {
s1 += fmt.Sprintf(" %3s %3s ", "", "") s1 += fmt.Sprintf(" %3s %3s ", "", "")
s1 += fmt.Sprintf(" %3s %3s ", "", "") s1 += fmt.Sprintf(" %3s %3s ", "", "")
@ -81,14 +81,14 @@ func (tk *guiWidget) dumpWidget(s string) {
s1 += fmt.Sprintf(" %3s %3s ", "", "") s1 += fmt.Sprintf(" %3s %3s ", "", "")
} }
var end string var end string
if tk.node.WidgetType == widget.Box { if tk.WidgetType() == widget.Box {
end = fmt.Sprintf("%-8s %-8s %s", tk.node.WidgetType, tk.cuiName, tk.node.State.Direction.String()) end = fmt.Sprintf("%-8s %-8s %s %s", tk.WidgetType(), tk.cuiName, tk.Direction().String(), tk.String())
} else { } else {
curval := tk.String() end = fmt.Sprintf("%-8s %-8s %s", tk.WidgetType(), tk.cuiName, tk.String())
if curval == "" {
curval = tk.node.GetLabel()
} }
end = fmt.Sprintf("%-8s %-8s %s", tk.node.WidgetType, tk.cuiName, tk.String()) if tk.node.InTable() {
// log.Log(GOCUI, "findParentTAble()", tk.labelN, tk.cuiName, tk.node.WidgetId)
end += " (table)"
} }
log.Log(GOCUI, s1, s, end) log.Log(GOCUI, s1, s, end)
} }

View File

@ -74,12 +74,20 @@ func (tk *guiWidget) showDropdown() {
me.dropdown.tk.Show() me.dropdown.tk.Show()
me.dropdown.active = true me.dropdown.active = true
me.dropdown.callerTK = tk me.dropdown.callerTK = tk
r := me.dropdown.tk.gocuiSize // set the 'full' size so that mouse clicks are sent here
me.dropdown.tk.full.w0 = r.w0
me.dropdown.tk.full.w1 = r.w1
me.dropdown.tk.full.h0 = r.h0
me.dropdown.tk.full.h1 = r.h1
me.dropdown.tk.dumpWidget("showDropdown()") me.dropdown.tk.dumpWidget("showDropdown()")
} }
// 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
// only need height to figure out what line in the dropdown menu the user clicked // only need height to figure out what line in the dropdown menu the user clicked
_, startH := w.Position() _, startH := w.Position()
@ -105,56 +113,3 @@ func (w *guiWidget) dropdownClicked(mouseW, mouseH int) string {
} }
return "" return ""
} }
func dropdownUnclicked(w, h int) {
var d *guiWidget
// examine everything under X & Y on the screen)
for _, tk := range findByXY(w, h) {
// tk.dumpWidget("dropdownUnclicked()")
if tk.node.WidgetType == widget.Dropdown {
d = tk
}
}
if d == nil {
// log.Log(GOCUI, fmt.Sprintf("dropdownUnclicked() there was no dropdown widget at (w=%d h=%d)", mouseW, mouseH))
return
}
// log.Log(GOCUI, "dropdownUnclicked()", d.node.Strings(), "end. now try to enable me.dropdownV")
}
func (tk *guiWidget) showTextbox() {
if me.textbox.tk == nil {
// should only happen once
me.textbox.tk = makeNewFlagWidget(me.textbox.wId)
me.textbox.tk.dumpWidget("init() textbox")
}
if me.textbox.tk == nil {
log.Log(GOCUI, "showTextbox() Is Broken")
return
}
startW, startH := tk.Position()
// log.Log(GOCUI, "showTextbox() SHOWING AT W,H=", startW, startH)
me.textbox.tk.MoveToOffset(startW+3, startH+2)
me.textbox.tk.labelN = "holy cow"
me.textbox.tk.setColorInput()
me.textbox.tk.Show()
me.textbox.active = true
me.textbox.callerTK = tk
me.textbox.tk.dumpWidget("showTextbox()")
}
// updates the text and sends an event back to the application
func (tk *guiWidget) textboxClosed() {
tk.Hide()
// get the text the user entered
newname := tk.GetText()
// change the text of the caller widget
me.textbox.callerTK.SetText(newname)
me.textbox.callerTK.node.SetCurrentS(newname)
// send an event from the plugin with the new string
me.myTree.SendUserEvent(me.textbox.callerTK.node)
}

View File

@ -4,7 +4,6 @@
package main package main
import ( import (
"fmt"
"syscall" "syscall"
"github.com/awesome-gocui/gocui" "github.com/awesome-gocui/gocui"
@ -16,6 +15,12 @@ import (
// tells 'gocui' where to send events // tells 'gocui' where to send events
func registerHandlers(g *gocui.Gui) { func registerHandlers(g *gocui.Gui) {
defer func() {
if r := recover(); r != nil {
log.Info("EVENT BINDINGS recovered in r", r)
return
}
}()
// mouse handlers // mouse handlers
g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, mouseDown) // normal left mouse down g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, mouseDown) // normal left mouse down
@ -30,6 +35,7 @@ func registerHandlers(g *gocui.Gui) {
g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, openDebuggger) // CTRL-D : open the (D)ebugger g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone, openDebuggger) // CTRL-D : open the (D)ebugger
keyForced, modForced := gocui.MustParse("ctrl+z") // setup ctrl+z 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("", 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 // regular keys
g.SetKeybinding("", 'H', gocui.ModNone, theHelp) // 'H' toggles on and off the help menu g.SetKeybinding("", 'H', gocui.ModNone, theHelp) // 'H' toggles on and off the help menu
@ -37,8 +43,13 @@ func registerHandlers(g *gocui.Gui) {
g.SetKeybinding("", 'D', gocui.ModNone, theDarkness) // 'D' toggles light/dark mode g.SetKeybinding("", 'D', gocui.ModNone, theDarkness) // 'D' toggles light/dark mode
g.SetKeybinding("", 'q', gocui.ModNone, doExit) // 'q' exit g.SetKeybinding("", 'q', gocui.ModNone, doExit) // 'q' exit
g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, tabCycleWindows) // '2' use this to test new ideas 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.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.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 // debugging
g.SetKeybinding("", '2', gocui.ModNone, theNotsure) // '2' use this to test new ideas g.SetKeybinding("", '2', gocui.ModNone, theNotsure) // '2' use this to test new ideas
@ -47,6 +58,7 @@ func registerHandlers(g *gocui.Gui) {
g.SetKeybinding("", 'L', gocui.ModNone, printWidgetTree) // 'L' list all widgets in tree view 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("", '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("", 'd', gocui.ModNone, theLetterD) // 'd' toggles on and off debugging buttons
g.SetKeybinding("", 'r', gocui.ModNone, reverseStdout) // 'r' turns scrolling of STDOUT upside down
g.SetKeybinding("", 'q', gocui.ModNone, quit) // 'q' only exits gocui. plugin stays alive (?) g.SetKeybinding("", 'q', gocui.ModNone, quit) // 'q' only exits gocui. plugin stays alive (?)
} }
@ -63,21 +75,26 @@ func theSuperMouse(g *gocui.Gui, v *gocui.View) error {
return nil return nil
} }
/*
func addDropdown() *tree.Node {
return addDropdownNew(-222)
}
*/
// use this to test code ideas // put whatever you want here and hit '2' to activate it // 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 { func theNotsure(g *gocui.Gui, v *gocui.View) error {
if me.dark { log.Info("got to theNotsure(). now what? dark =", me.dark)
me.dark = false me.refresh = true
log.Info("running VerifyParentId()")
me.treeRoot.VerifyParentId()
/*
if me.debug {
log.Info("debugging off")
me.debug = false
} else { } else {
me.dark = true log.Info("debugging on")
me.debug = true
} }
log.Info("got keypress 2. now what? dark =", me.dark) win := findWindowUnderMouse()
// findBG() if win != nil {
win.dumpWidget("found() win. redrawing window:")
win.makeWindowActive()
}
*/
return nil return nil
} }
@ -87,36 +104,20 @@ func theDarkness(g *gocui.Gui, v *gocui.View) error {
log.Info("you have seen the light") log.Info("you have seen the light")
} else { } else {
me.dark = true me.dark = true
log.Info("you have entered into darkness") log.Info("you have entered into darkness (you may need to trigger SIGWINCH)")
log.Info("or maybe open a new window. notsure. This obviously isn't finished.")
log.Info("submit patches to this and you will definitely get free cloud credits at WIT")
} }
return nil return nil
} }
func stdoutPgup(g *gocui.Gui, v *gocui.View) error {
me.stdout.pager -= 40
if me.stdout.pager < 0 {
me.stdout.pager = 0
}
tk := me.stdout.tk
tk.refreshStdout()
return nil
}
func stdoutPgdn(g *gocui.Gui, v *gocui.View) error {
me.stdout.pager += 10
tk := me.stdout.tk
tk.refreshStdout()
return nil
}
func wheelsUp(g *gocui.Gui, v *gocui.View) error { func wheelsUp(g *gocui.Gui, v *gocui.View) error {
log.Info("private wheels up") stdoutWheelsUp()
return nil return nil
} }
func wheelsDown(g *gocui.Gui, v *gocui.View) error { func wheelsDown(g *gocui.Gui, v *gocui.View) error {
log.Info("you've landed") stdoutWheelsDown()
return nil return nil
} }
@ -131,36 +132,22 @@ func tabCycleWindows(g *gocui.Gui, v *gocui.View) error {
return nil return nil
} }
tk.makeWindowActive() tk.makeWindowActive()
// w, h := g.MousePosition()
// me.downW = tk.gocuiSize.w0
// me.downH = tk.gocuiSize.h0
tk.redrawWindow(tk.gocuiSize.w0, tk.gocuiSize.h0)
setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
return nil return nil
} }
func theStdout(g *gocui.Gui, v *gocui.View) error { func doEsc(g *gocui.Gui, v *gocui.View) error {
me.stdout.pager = 0 log.Info("got escape key")
if me.stdout.outputOnTop { if me.dropdown.active {
if me.stdout.outputOffscreen { me.dropdown.tk.Hide()
me.stdout.outputOffscreen = false me.dropdown.active = false
log.Info(fmt.Sprintf("stdout moved off screen pager=%d len(%d)", me.stdout.pager, len(me.stdout.outputS))) log.Info("escaped from dropdown")
relocateStdoutOffscreen() me.baseGui.SetCurrentView(me.notify.clock.tk.cuiName)
return nil
} else {
me.stdout.outputOffscreen = true
log.Info(fmt.Sprintf("stdout moved on screen pager=%d len(%d)", me.stdout.pager, len(me.stdout.outputS)))
} }
// move the stdout window back onscreen if me.textbox.active {
me.stdout.tk.relocateStdout(me.stdout.lastW, me.stdout.lastH) me.textbox.tk.Hide()
me.stdout.outputOnTop = false me.textbox.active = false
setThingsOnTop() log.Info("escaped from textbox")
// me.baseGui.SetViewOnBottom("msg") me.baseGui.SetCurrentView(me.notify.clock.tk.cuiName)
// setBottomBG()
} else {
me.stdout.outputOnTop = true
setThingsOnTop()
// me.baseGui.SetViewOnTop("msg")
} }
return nil return nil
} }
@ -198,6 +185,19 @@ func theFind(g *gocui.Gui, v *gocui.View) error {
return nil return nil
} }
func reverseStdout(g *gocui.Gui, v *gocui.View) error {
if me.stdout.reverse {
me.stdout.reverse = false
log.Info("stdout scrolling normal")
} else {
me.stdout.reverse = true
log.Info("stdout scrolling is reversed. this is sometimes useful when you")
log.Info("only need to see a few most recent lines and have the STDOUT window")
log.Info("take up minimal realestate at the bottom of your window")
}
return nil
}
// is run whenever anyone hits 'd' (in an open space) // is run whenever anyone hits 'd' (in an open space)
func theLetterD(g *gocui.Gui, v *gocui.View) error { func theLetterD(g *gocui.Gui, v *gocui.View) error {
// widgets that don't have physical existance in // widgets that don't have physical existance in

131
eventBindingsStdout.go Normal file
View File

@ -0,0 +1,131 @@
// 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"
"go.wit.com/toolkits/tree"
)
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)
me.stdout.changed = true
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()
new1 := new(tree.ToolkitConfig)
new1.Plugin = "gocui"
new1.Name = "stdoutoffscreen"
new1.Value = "true"
me.myTree.ConfigSave(new1)
return nil
} else {
me.stdout.outputOffscreen = true
log.Info("stdout moved on screen", infos)
new1 := new(tree.ToolkitConfig)
new1.Plugin = "gocui"
new1.Name = "stdoutoffscreen"
new1.Value = "false"
me.myTree.ConfigSave(new1)
}
// move the stdout window back onscreen
me.stdout.tk.relocateStdout(me.stdout.lastW, me.stdout.lastH)
me.stdout.outputOnTop = false
setThingsOnTop()
new1 := new(tree.ToolkitConfig)
new1.Plugin = "gocui"
new1.Name = "stdoutlevel"
new1.Value = "bottom"
me.myTree.ConfigSave(new1)
} else {
me.stdout.outputOnTop = true
setThingsOnTop()
new1 := new(tree.ToolkitConfig)
new1.Plugin = "gocui"
new1.Name = "stdoutlevel"
new1.Value = "top"
me.myTree.ConfigSave(new1)
}
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 {
if me.stdout.reverse {
stdoutWheelsDown()
} else {
stdoutWheelsUp()
}
return nil
}
func stdoutArrowDown(g *gocui.Gui, v *gocui.View) error {
if me.stdout.reverse {
stdoutWheelsUp()
} else {
stdoutWheelsDown()
}
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
}
// scrolling up with the mouse wheel (or trackpad)
func stdoutWheelsUp() {
// log.Info("private wheels up")
me.stdout.pager -= 2
if me.stdout.pager < 0 {
me.stdout.pager = 0
}
me.stdout.tk.refreshStdout()
}
// scrolling down with the mouse wheel (or trackpad)
func stdoutWheelsDown() {
// 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()
}

View File

@ -1,17 +1,13 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0 // 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 package main
import ( import (
"errors" "errors"
"fmt"
"github.com/awesome-gocui/gocui" "github.com/awesome-gocui/gocui"
"go.wit.com/log" "go.wit.com/log"
) )
@ -44,6 +40,36 @@ func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit return gocui.ErrQuit
} }
func (tk *guiWidget) SetView() error {
if me.baseGui == nil {
return fmt.Errorf("me.baseGui == nil")
}
r := new(rectType)
r.w0 = tk.gocuiSize.w0
r.h0 = tk.gocuiSize.h0
r.w1 = tk.gocuiSize.w1
r.h1 = tk.gocuiSize.h1
return tk.SetViewRect(r)
}
func (tk *guiWidget) SetViewRect(r *rectType) error {
if me.baseGui == nil {
return fmt.Errorf("me.baseGui == nil")
}
var err error
tk.v, err = me.baseGui.SetView(tk.cuiName, r.w0, r.h0, r.w1, r.h1, 0)
if err != nil {
if !errors.Is(err, gocui.ErrUnknownView) {
log.Log(ERROR, "SetView() global failed on name =", tk.cuiName)
return err
}
}
return nil
}
func SetView(name string, x0, y0, x1, y1 int, overlaps byte) *gocui.View { func SetView(name string, x0, y0, x1, y1 int, overlaps byte) *gocui.View {
if me.baseGui == nil { if me.baseGui == nil {
log.Log(ERROR, "SetView() ERROR: me.baseGui == nil") log.Log(ERROR, "SetView() ERROR: me.baseGui == nil")

View File

@ -4,15 +4,15 @@
package main package main
import ( import (
"fmt"
"time"
"github.com/awesome-gocui/gocui" "github.com/awesome-gocui/gocui"
"go.wit.com/log" "go.wit.com/log"
"go.wit.com/widget"
) )
func mouseUp(g *gocui.Gui, v *gocui.View) error { func mouseUp(g *gocui.Gui, v *gocui.View) error {
w, h := g.MousePosition()
// useful to debug everything that is being clicked on // useful to debug everything that is being clicked on
/* /*
for _, tk := range findByXY(w, h) { for _, tk := range findByXY(w, h) {
@ -20,11 +20,19 @@ func mouseUp(g *gocui.Gui, v *gocui.View) error {
} }
*/ */
me.globalMouseDown = false me.mouse.mouseUp = true
me.currentDrag = nil me.mouse.currentDrag = nil
dropdownUnclicked(w, h) 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 return nil
} }
@ -32,117 +40,35 @@ func mouseUp(g *gocui.Gui, v *gocui.View) error {
// widget was underneath so you can active // widget was underneath so you can active
// the right response for the toolkit user's app // the right response for the toolkit user's app
func mouseDown(g *gocui.Gui, v *gocui.View) error { func mouseDown(g *gocui.Gui, v *gocui.View) error {
me.globalMouseDown = true 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() w, h := g.MousePosition()
me.downW = w me.mouse.downW = w
me.downH = h me.mouse.downH = h
// if the dropdown is active, never do anything else win := findWindowUnderMouse()
if me.dropdown.active { if win != nil {
log.Info("mouseDown() stopping here. dropdwon menu is in effect")
for _, tk := range findByXY(w, h) {
if (tk.node.WidgetType == widget.Flag) && (tk == me.dropdown.tk) {
// log.Info("SENDING CLICK TO Flag (dropdown)")
tk.doWidgetClick(w, h)
me.dropdown.active = false
return nil
}
}
log.Info("never found dropdown at", w, h)
return nil
}
// if the textbox is active, never do anything else
if me.textbox.active {
log.Info("mouseDown() stopping here. textbox widget is open")
for _, tk := range findByXY(w, h) {
if (tk.node.WidgetType == widget.Flag) && (tk == me.textbox.tk) {
me.textbox.active = false
tk.textboxClosed()
return nil
}
}
log.Info("never found textbox at", w, h)
return nil
}
// figre out what window this is
tk := findWindowUnderMouse()
if tk == nil {
log.Info("mouseDown() nothing to click on at", w, h)
return nil
}
tk.makeWindowActive()
// log.Info("SENDING mouseDown() to findWindowUnderMouse()")
if tk.node.WidgetType == widget.Window {
// check to see if this is a direct click on a widget
for _, tk := range tk.findByXYreal(w, h) {
// tk.dumpWidget("mouseDown()")
if tk.node.WidgetType == widget.Button {
// log.Info("SENDING CLICK TO Button")
tk.doWidgetClick(w, h)
return nil
}
if tk.node.WidgetType == widget.Checkbox {
// log.Info("SENDING CLICK TO Checkbox")
tk.doWidgetClick(w, h)
return nil
}
if tk.node.WidgetType == widget.Dropdown {
// log.Info("SENDING CLICK TO Dropdown")
tk.doWidgetClick(w, h)
return nil
}
if tk.node.WidgetType == widget.Textbox {
// log.Info("SENDING CLICK TO Textbox")
tk.doWidgetClick(w, h)
return nil
}
}
}
if tk.node.WidgetType == widget.Stdout {
// tk.dumpWidget("stdout fixme drag()" + tk.labelN)
me.currentDrag = tk
tk.dragW = w - tk.gocuiSize.w0
tk.dragH = h - tk.gocuiSize.h0
return nil
}
// tk.dumpWidget("mouse drag()" + tk.labelN)
me.currentDrag = tk
tk.dragW = w - tk.gocuiSize.w0
tk.dragH = h - tk.gocuiSize.h0
return nil
}
/*
// this needs to go
// event triggers when you push down on a mouse button
func msgDown(g *gocui.Gui, v *gocui.View) error {
w, h := g.MousePosition() w, h := g.MousePosition()
s := fmt.Sprintf("mouse(%d,%d) ", w, h)
for _, tk := range findByXY(w, h) { offW := win.full.w1 - w
tk.dumpWidget("msgDown()") offH := win.full.h1 - h
} s += fmt.Sprintf("corner(%d,%d)", offW, offH)
if (offW < 3) && (offH < 3) {
vx, vy, _, _, err := g.ViewPosition("msg") log.Info("attempting resize on ", s, win.cuiName)
if err == nil { me.mouse.resize = true
me.stdout.mouseOffsetW = w - vx // store the stdout corner for computing the drag size
me.stdout.mouseOffsetH = h - vy me.stdout.lastW = me.stdout.tk.gocuiSize.w0
} me.stdout.lastH = me.stdout.tk.gocuiSize.h0
// did the user click in the corner of the stdout window? If so, resize the window.
cornerW := w - vx
cornerH := h - vy
if (me.stdout.w-cornerW < 4) && (me.stdout.h-cornerH < 4) {
log.Info("Resize msg", cornerW, cornerH)
me.stdout.resize = true
} else { } else {
log.Info("not Resize msg", cornerW, cornerH) // log.Info("mouse down resize off", s)
me.stdout.resize = false me.mouse.resize = false
}
win.setAsDragging()
}
} }
log.Info("setting mousedown to true for msg")
// msgMouseDown = true
return nil return nil
} }
*/

View File

@ -4,152 +4,158 @@
package main package main
import ( import (
"fmt"
"github.com/awesome-gocui/gocui" "github.com/awesome-gocui/gocui"
"go.wit.com/log" "go.wit.com/log"
"go.wit.com/widget" "go.wit.com/widget"
) )
/* func (tk *guiWidget) doButtonClick() {
// this didn't work. panic() if tk.IsEnabled() {
func (tk *guiWidget) DeleteNode() { tk.dumpWidget("click()") // enable this to debug widget clicks
p := tk.parent me.myTree.SendFromUser(tk.node)
for i, child := range p.children { } else {
log.Log(GOCUI, "parent has child:", i, child.node.WidgetId, child.node.GetProgName()) log.Info("button is currently disabled by the application")
if tk == child { // tk.dumpWidget("disabled()") // enable this to debug widget clicks
log.Log(GOCUI, "Found child ==", i, child.node.WidgetId, child.node.GetProgName())
log.Log(GOCUI, "Found n ==", i, tk.node.WidgetId, tk.node.GetProgName())
p.children = append(p.children[:i], p.children[i+1:]...)
} }
} }
for i, child := range p.children {
log.Log(TREE, "parent now has child:", i, child.WidgetId, child.GetProgName())
}
}
*/
func (tk *guiWidget) doWindowClick() { // handles a mouse click
w, h := me.baseGui.MousePosition() func doMouseClick(w int, h int) {
// compare the mouse location to the 'X' indicator to close the window // Flag widgets (dropdown menus, etc) are the highest priority. ALWAYS SEND MOUSE CLICKS THERE FIRST
if tk.gocuiSize.inRect(w, h) { // handle an open dropdown menu or text entry window first
offset := w - tk.gocuiSize.w1 if me.dropdown.active || me.textbox.active {
if (offset < 2) && (offset > -2) { // can't drag or do anything when dropdown or textbox are visible
// close enough // close the window here for _, tk := range findByXY(w, h) {
tk.dumpWidget(fmt.Sprintf("Close Window(%d,%d) (off=%d)", w, h, offset)) if tk.WidgetId() == me.dropdown.wId {
tk.hideWindow() log.Info("got dropdown click", w, h, tk.cuiName)
n := tk.node tk.dropdownClicked(w, h)
tk.deleteNode()
me.myTree.SendWindowCloseEvent(n)
/*
n.DeleteNode()
tk.DeleteNode()
*/
return return
} }
if tk.window.collapsed { if tk.WidgetId() == me.textbox.wId {
tk.dumpWidget(fmt.Sprintf("collapse = false")) log.Info("got textbox click", w, h, tk.cuiName)
tk.window.collapsed = false textboxClosed()
} else {
tk.dumpWidget(fmt.Sprintf("collapse = true"))
tk.window.collapsed = true
}
} else {
tk.window.collapsed = false
// tk.dumpWidget(fmt.Sprintf("No (%d,%d)", w, h))
}
// if there is a current window, hide it
if me.currentWindow != nil {
me.currentWindow.setColor(&colorWindow)
}
// now set this window as the current window
me.currentWindow = tk
tk.redrawWindow(w, h)
setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
}
// this whole things was impossible to make but it got me where I am now
// the debugging is way way better now with it being visible in the Stdout window
// so now it's possible to redo all this and make it better
func (tk *guiWidget) doWidgetClick(w int, h int) {
switch tk.node.WidgetType {
case widget.Window:
tk.doWindowClick()
return 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: case widget.Checkbox:
if tk.node.State.Checked { if tk.Checked() {
log.Log(WARN, "checkbox is being set to false") log.Log(WARN, "checkbox is being set to false")
tk.node.State.Checked = false tk.SetChecked(false)
tk.setCheckbox() tk.setCheckbox()
} else { } else {
log.Log(WARN, "checkbox is being set to true") log.Log(WARN, "checkbox is being set to true")
tk.node.State.Checked = true tk.SetChecked(true)
tk.setCheckbox() tk.setCheckbox()
} }
me.myTree.SendUserEvent(tk.node) me.myTree.SendUserEvent(tk.node)
return
case widget.Button: case widget.Button:
me.myTree.SendFromUser(tk.node) tk.doButtonClick()
return
case widget.Combobox: case widget.Combobox:
tk.showDropdown() tk.showDropdown()
return
case widget.Dropdown: case widget.Dropdown:
tk.showDropdown() tk.showDropdown()
return
case widget.Textbox: case widget.Textbox:
tk.showTextbox() log.Log(WARN, "TODO: textbox click")
case widget.Flag: tk.prepTextbox()
tk.dropdownClicked(w, h) return
case widget.Stdout: case widget.Label:
tk.dumpWidget("stdout click()") if tk.node.InTable() {
if tk.node.State.AtH == 0 {
log.Log(NOW, "todo: sort by column here")
tk.dumpWidget("sort")
}
}
return
default: default:
tk.dumpWidget("undef click()") // TODO: enable the GUI debugger in gocui
// tk.dumpWidget("undef click()") // enable this to debug widget clicks
}
} }
} }
// sends the mouse click to a widget underneath // todo: use this?
func clickOLD(g *gocui.Gui, v *gocui.View) error { func ctrlDown(g *gocui.Gui, v *gocui.View) error {
mouseW, mouseH := me.baseGui.MousePosition() log.Info("todo: clicked with ctrlDown")
w := mouseW
h := mouseH
// Flag widgets (dropdown menus, etc) are the highest priority. ALWAYS SEND MOUSE CLICKS THERE FIRST
for _, tk := range findByXY(w, h) {
if tk.node.WidgetType == widget.Flag {
tk.doWidgetClick(w, h)
return nil return nil
} }
}
// Button widgets func doMouseDoubleClick(w int, h int) {
for _, tk := range findByXY(w, h) { me.mouse.double = false
if tk.node.WidgetType == widget.Button { // log.Printf("actually a double click (%d,%d)", w, h)
tk.doWidgetClick(w, h)
return nil 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) { for _, tk := range findByXY(w, h) {
// will show you everything found on a mouse click. great for debugging! if tk.WidgetType() == widget.Window {
// tk.dumpWidget("click()") tk.makeWindowActive()
if tk.node.WidgetType == widget.Stdout { return
// don't send clicks to the stdout debugging window
continue
}
tk.doWidgetClick(w, h)
return nil
} }
log.Log(GOCUI, "click() nothing was at:", v.Name(), mouseW, mouseH) if tk.WidgetType() == widget.Stdout {
return nil if me.stdout.outputOnTop {
/* me.stdout.outputOnTop = false
// not sure what SetCurrentView() does right now. it was here before setThingsOnTop()
// SetCurrentView dies if it's sent an non-existent view } else {
if _, err := g.SetCurrentView(v.Name()); err != nil { me.stdout.outputOnTop = true
log.Log(GOCUI, "click() END v.Name =", v.Name(), "err =", err) setThingsOnTop()
// return err // return causes gocui.MainLoop() to exit. Do we ever want that to happen here? }
return nil return
}
} }
*/
} }

136
eventMouseDrag.go Normal file
View File

@ -0,0 +1,136 @@
// 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 {
me.stdout.changed = true
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.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
me.stdout.changed = true
}
}

View File

@ -1,156 +0,0 @@
// 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"
"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) {
w, h := g.MousePosition()
me.ok = true // this tells init() it's okay to work with gocui
if me.supermouse {
for _, tk := range findByXY(w, h) {
s := fmt.Sprintf("SM (%3d,%3d)", w, h)
tk.dumpWidget(s)
}
}
// toggle off all highlight vies except for whatever is under the mouse
for _, view := range g.Views() {
view.Highlight = false
}
if v, err := g.ViewByPosition(w, h); err == nil {
v.Highlight = true
}
// 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
}
if me.globalMouseDown && (me.dropdown.active || me.textbox.active) {
log.Info("can't drag while dropdown or textbox are active", w, h)
return
}
if me.globalMouseDown {
// log.Info("msgMouseDown == true")
// plugin will segfault if you don't keep this inside a check for msgMouseDown
// don't move this code out of here
var found bool = false
if me.currentDrag != nil {
// me.currentDrag.dumpWidget(fmt.Sprintf("MM (%3d,%3d)", w, h))
me.currentDrag.moveNew()
return
}
// new function that is smarter
if tk := findWindowUnderMouse(); tk != nil {
me.currentDrag = tk
return
}
// first look for windows
for _, tk := range findByXY(w, h) {
if tk.node.WidgetType == widget.Window {
me.currentDrag = tk
return
}
}
// now look for the STDOUT window
for _, tk := range findByXY(w, h) {
if tk.node.WidgetType == widget.Flag {
me.currentDrag = tk
// tk.moveNew()
return
}
}
for _, tk := range findByXY(w, h) {
if tk.node.WidgetType == widget.Stdout {
me.currentDrag = tk
// tk.moveNew()
return
}
/*
if tk.node.WidgetType == widget.Label {
me.currentDrag = tk
// tk.moveNew()
return
}
*/
found = true
}
if !found {
log.Info(fmt.Sprintf("findByXY() empty. nothing to move at (%d,%d)", w, h))
}
}
}
// this is how the window gets dragged around
func (tk *guiWidget) moveNew() {
w, h := me.baseGui.MousePosition()
if tk.node.WidgetType == widget.Window {
tk.window.wasDragged = true
tk.redrawWindow(w-tk.dragW, h-tk.dragH) // TODO: fix these hard coded things with offsets
setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
return
}
if tk.node.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.node.WidgetType == widget.Stdout {
if me.stdout.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 {
// tk.dumpWidget(fmt.Sprintf("move(%dx%d) %s", w, h, tk.cuiName))
// log.Info("Resize false", w, h)
// me.stdout.lastW = w - me.stdout.mouseOffsetW
// me.stdout.lastH = h - me.stdout.mouseOffsetH
// tk.relocateStdout(me.stdout.lastW, me.stdout.lastH)
tk.relocateStdout(w-tk.dragW, h-tk.dragH)
}
}
// always place the help menu on top
setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
}
func createStdout(g *gocui.Gui) bool {
makeOutputWidget(g, "this is a create before a mouse click")
if me.stdout.tk != nil {
msg := fmt.Sprintf("test out gocuiEvent() %d\n", me.ecount)
// me.logStdout.v.Write([]byte(msg))
me.stdout.tk.Write([]byte(msg))
log.Log(NOW, "logStdout test out")
}
return true
}

163
find.go
View File

@ -45,25 +45,25 @@ func (r rectType) inRect(w int, h int) bool {
func (tk *guiWidget) findByXYreal(w int, h int) []*guiWidget { func (tk *guiWidget) findByXYreal(w int, h int) []*guiWidget {
var widgets []*guiWidget var widgets []*guiWidget
if !tk.Visible() { // if !tk.Visible() {
// ignore widgets that are not visible // ignore widgets that are not visible
} else { // } else {
// check the location to see if this is under (W,H) // check the location to see if this is under (W,H)
// if it is, return this widget // if it is, return this widget
// if (tk.gocuiSize.w0 <= w) && (w <= tk.gocuiSize.w1) && // if (tk.gocuiSize.w0 <= w) && (w <= tk.gocuiSize.w1) &&
// (tk.gocuiSize.h0 <= h) && (h <= tk.gocuiSize.h1) { // (tk.gocuiSize.h0 <= h) && (h <= tk.gocuiSize.h1) {
if tk.gocuiSize.inRect(w, h) { // if tk.gocuiSize.inRect(w, h) {
widgets = append(widgets, tk) // widgets = append(widgets, tk)
} else { // } else {
// if (tk.full.w0 <= w) && (w <= tk.full.w1) && // if (tk.full.w0 <= w) && (w <= tk.full.w1) &&
// (tk.full.h0 <= h) && (h <= tk.full.h1) { // (tk.full.h0 <= h) && (h <= tk.full.h1) {
if tk.full.inRect(w, h) { if tk.full.inRect(w, h) {
widgets = append(widgets, tk) widgets = append(widgets, tk)
} }
// log.Log(GOCUI, "findByXY() found", widget.node.WidgetType, w, h) // log.Log(GOCUI, "findByXY() found", widget.WidgetType(), w, h)
} // }
} // }
// tk.verifyRect() // tk.verifyRect()
// search through the children widgets in the binary tree // search through the children widgets in the binary tree
@ -84,7 +84,7 @@ func findWindows() []*guiWidget {
func (tk *guiWidget) findWindows() []*guiWidget { func (tk *guiWidget) findWindows() []*guiWidget {
var found []*guiWidget var found []*guiWidget
if tk.node.WidgetType == widget.Window { if tk.WidgetType() == widget.Window {
found = append(found, tk) found = append(found, tk)
} }
@ -94,25 +94,6 @@ func (tk *guiWidget) findWindows() []*guiWidget {
return found return found
} }
// find the BG widget.
// This widget is always in the background and covers the whole screen.
// gocui seems to not return mouse events unless there is something there
func (tk *guiWidget) findBG() *guiWidget {
if tk.node.WidgetType == widget.Stdout {
if tk.node.WidgetId != me.stdout.wId {
tk.isBG = true
return tk
}
}
for _, child := range tk.children {
if found := child.findBG(); found != nil {
return found
}
}
return nil
}
// used by gocui.TabKey to rotate through the windows // used by gocui.TabKey to rotate through the windows
func findNextWindow() *guiWidget { func findNextWindow() *guiWidget {
var found bool var found bool
@ -132,6 +113,7 @@ func findNextWindow() *guiWidget {
return me.allwin[0] return me.allwin[0]
} }
// find the window under the mouse and only the window under the mouse
func findWindowUnderMouse() *guiWidget { func findWindowUnderMouse() *guiWidget {
w, h := me.baseGui.MousePosition() w, h := me.baseGui.MousePosition()
@ -147,13 +129,6 @@ func findWindowUnderMouse() *guiWidget {
} }
} }
/*
// 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 // now check if the active window is below the mouse
for _, tk := range me.allwin { for _, tk := range me.allwin {
if tk.window.active { if tk.window.active {
@ -170,12 +145,6 @@ func findWindowUnderMouse() *guiWidget {
return a.window.order - b.window.order 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 { for _, win := range me.allwin {
if win.full.inRect(w, h) { if win.full.inRect(w, h) {
// log.Info(fmt.Sprintf("findWindowUnderMouse() found %s window (%dx%d)", win.cuiName, w, h)) // log.Info(fmt.Sprintf("findWindowUnderMouse() found %s window (%dx%d)", win.cuiName, w, h))
@ -194,68 +163,56 @@ func findWindowUnderMouse() *guiWidget {
return nil return nil
} }
// returns the "highest priority widget under the mouse func (tk *guiWidget) findParentWindow() *guiWidget {
func findUnderMouse() *guiWidget { if tk.WidgetType() == widget.Window {
w, h := me.baseGui.MousePosition() return tk
}
widgets := findByXY(w, h) if tk.parent == nil {
return nil
// search through all the widgets that were below the mouse click }
var found *guiWidget return tk.parent.findParentWindow()
for _, w := range widgets { }
// prioritize window buttons. This means if some code covers
// up the window widgets, then it will ignore everything else func (tk *guiWidget) findWidgetByName(name string) *guiWidget {
// and allow the user (hopefully) to redraw or switch windows if tk.cuiName == name {
// TODO: display the window widgets on top return tk
if w.node.WidgetType == widget.Window { }
return w 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 anything else that is interactive
for _, w := range widgets {
if w.node.WidgetType == widget.Button {
return w
}
if w.node.WidgetType == widget.Combobox {
return w
}
if w.node.WidgetType == widget.Checkbox {
return w
}
w.dumpWidget("findUnderMouse() found something unknown")
found = w
}
// maybe something else was found
return found
}
// panics. todo: fix ctrl-mouse click?
// 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 return nil
} }

89
help.go
View File

@ -1,7 +1,7 @@
// Copyright 2014 The gocui Authors. All rights reserved.
// Copyright 2017-2025 WIT.COM Inc. All rights reserved. // Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0 // 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.
@ -25,16 +25,20 @@ import (
var helpText []string = []string{"Help Menu", var helpText []string = []string{"Help Menu",
"", "",
"H: toggle (H)elp", "Tab toggle through windows",
"D: toggle light/dark mode", "'O' toggle STDOUT",
"Tab: toggle through windows", "'H' toggle this gocui menu",
"q: quit()", "'D' toggle light/dark mode",
"CTRL-z background to shell",
"CTRL-c quit()",
"", "",
"Debugging:", "Debugging:",
"O: toggle (O)output (os.STDOUT)", "'S' Supermouse mode",
"S: super mouse", "'M' list all widget positions",
"M: list all widgets positions", "'L' list all widgets in tree",
"L: list all widgets in tree", "<Pgup> scroll up the STDOUT window",
"<Pgdn> scroll down the STDOUT window",
"'r' reverse STDOUT scrolling",
} }
func hideHelp() { func hideHelp() {
@ -65,17 +69,18 @@ func showHelp() 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"))
@ -85,47 +90,19 @@ func showHelp() error {
} }
g.SetViewOnTop("help") 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
} }
// 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.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")
}
setBottomBG()
}
func setBottomBG() {
// this attempts to find the "BG" widget and set it to the background on the very very bottom
rootTK := me.treeRoot.TK.(*guiWidget)
if tk := rootTK.findBG(); tk != nil {
// log.Info("found BG. setting to bottom", tk.cuiName)
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)
}
}

471
init.go
View File

@ -9,8 +9,13 @@ package main
import ( import (
"errors" "errors"
"fmt"
"os" "os"
"runtime"
"runtime/debug" "runtime/debug"
"runtime/pprof"
"strconv"
"strings"
"time" "time"
"github.com/awesome-gocui/gocui" "github.com/awesome-gocui/gocui"
@ -22,61 +27,295 @@ import (
var VERSION string var VERSION string
var BUILDTIME string var BUILDTIME string
func queueToolkitClose() { 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()
}
// TEST TEXTBOX END
}
func toolkitClose() {
me.baseGui.Close() me.baseGui.Close()
} }
func queueSetChecked(n *tree.Node, b bool) { // a GO GUI plugin should initTree in init()
setChecked(n, b) // this should be done before the application
// starts trying to open up a channel
func init() {
me.myTree = initTree()
} }
// sets defaults and establishes communication // sets defaults and establishes communication
// to this toolkit from the wit/gui golang package // to this toolkit from the wit/gui golang package
func init() { 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("stdoutoffscreen"); err == nil {
if val == "false" {
// log.Log(NOW, "gocui: START ON SCREEN TRUE")
me.stdout.startOnscreen = 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 {
tmpFile, err := os.CreateTemp("", "gocui-*.log")
if err != nil {
fmt.Println("Error creating temp file:", err)
standardExit()
}
// defer os.Remove(tmpFile.Name())
log.Log(INFO, "stdout.disable == true. writing to", tmpFile.Name())
me.outf = tmpFile
// 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") log.Log(INFO, "Init() of awesome-gocui")
// init the config struct default values // init the config struct default values
Set(&me, "default") Set(&me, "default")
// initial app window settings
// initial stdout window settings // initial stdout window settings
me.stdout.w = 180 me.stdout.w = 180
me.stdout.h = 40 me.stdout.h = 40
me.stdout.lastW = 30 me.stdout.lastW = 4
me.stdout.lastH = 10 me.stdout.lastH = 20
if val, err := me.myTree.ConfigFind("stdoutsize"); err == nil {
parts := strings.Fields(val)
if len(parts) == 4 {
log.Info("initial stdout settings:", parts, "setting startOnscreen = true")
me.stdout.w, _ = strconv.Atoi(parts[0])
me.stdout.h, _ = strconv.Atoi(parts[1])
me.stdout.lastW, _ = strconv.Atoi(parts[2])
me.stdout.lastH, _ = strconv.Atoi(parts[3])
me.stdout.startOnscreen = true
} else {
log.Info("initial stdout settings parse error:", parts)
}
}
// just make up unique values for these // just make up unique values for these
me.dropdown.wId = -77 me.dropdown.wId = -77
me.textbox.wId = -55 me.textbox.wId = -55
me.stdout.wId = -4 me.stdout.wId = -4
me.BG.wId = -22
// Set(&me, "dense") // the clock widget id and offset
me.notify.clock.wId = -5
me.notify.clock.offsetW = 13
me.notify.clock.offsetH = 1
me.myTree = tree.New() me.notify.icon.wId = -6
me.myTree.PluginName = "gocui" me.notify.icon.offsetW = 4
me.notify.icon.offsetH = 1
me.myTree.NodeAction = newaction me.notify.help.wId = -7
me.myTree.Add = newAdd me.notify.help.offsetH = 3
me.myTree.SetTitle = newSetTitle
me.myTree.SetLabel = newSetLabel Set(&me.dropdown, "default")
me.myTree.SetText = newSetText // s := fmt.Sprintln("fake default check =", me.FakeW, "dropdown.Id", me.dropdown.Id)
me.myTree.AddText = newAddText // me.stdout.Write([]byte(s))
me.myTree.SetChecked = queueSetChecked
me.myTree.ToolkitClose = queueToolkitClose me.mouse.mouseUp = true
me.mouse.clicktime = time.Millisecond * 200
me.mouse.doubletime = time.Millisecond * 400
me.newWindowTrigger = make(chan *guiWidget, 1) me.newWindowTrigger = make(chan *guiWidget, 1)
go newWindowTrigger() go newWindowTrigger()
go refreshGocui()
log.Log(NOW, "Init() start pluginChan") log.Log(NOW, "Init() start pluginChan")
// log.Sleep(.1) // probably not needed, but in here for now under development if me.stdout.disable {
go mainGogui() log.Info("Using STDOUT")
// log.Sleep(.1) // probably not needed, but in here for now under development } 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() { func standardExit() {
log.Log(NOW, "standardExit() doing baseGui.Close()") log.Log(NOW, "standardExit() doing baseGui.Close()")
me.baseGui.Close() me.baseGui.Close()
if me.outf != nil {
log.Log(NOW, "standardExit() doing outf.Close()") log.Log(NOW, "standardExit() doing outf.Close()")
outf.Close() me.outf.Close()
os.Remove(me.outf.Name())
}
// log(true, "standardExit() setOutput(os.Stdout)") // log(true, "standardExit() setOutput(os.Stdout)")
// setOutput(os.Stdout) // setOutput(os.Stdout)
log.Log(NOW, "standardExit() send back Quit()") log.Log(NOW, "standardExit() send back Quit()")
@ -91,133 +330,135 @@ func standardClose() {
log.Log(NOW, "standardExit() doing baseGui.Close()") log.Log(NOW, "standardExit() doing baseGui.Close()")
me.baseGui.Close() me.baseGui.Close()
log.Log(NOW, "standardExit() doing outf.Close()") log.Log(NOW, "standardExit() doing outf.Close()")
outf.Close() me.outf.Close()
os.Remove(me.outf.Name())
// os.Stdin = os.Stdin // os.Stdin = os.Stdin
// os.Stdout = os.Stdout // os.Stdout = os.Stdout
// os.Stderr = os.Stderr // os.Stderr = os.Stderr
log.Log(NOW, "standardExit() send back Quit()") log.Log(NOW, "standardExit() send back Quit()")
} }
var outf *os.File
func main() { func main() {
} }
var origStdout *os.File // this hack is to wait for the application to send something
var origStderr *os.File // before trying to do anything. todo: rethink this someday
func waitOK() {
for {
if me.ok {
return
}
time.Sleep(10 * time.Millisecond)
}
}
func mainGogui() { // 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() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
log.Warn("YAHOOOO Recovered in guiMain application:", r) if me.outf == nil {
log.Warn("Recovered from panic:", r) log.Info("INIT PLUGIN recovered in r", r)
me.baseGui.Close() } else {
log.CaptureMode(nil) fmt.Fprintln(me.outf, "INIT PLUGIN recovered in r", r)
log.Warn("YAHOOOO Recovered in guiMain application:", r) }
log.Warn("Recovered from panic:", r)
me.myTree.SendToolkitPanic()
return return
} }
}() }()
var lastRefresh time.Time
var err error lastRefresh = time.Now()
me.refresh = false
outf, err = os.OpenFile("/tmp/captureMode.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) for {
if err != nil { time.Sleep(100 * time.Millisecond)
log.Info("error opening file:", err) // log.Info("refresh checking ok")
os.Exit(0) if !me.ok {
} continue
origStdout = os.Stdout
os.Stdout = outf
defer outf.Close()
log.CaptureMode(outf)
gocuiMain()
} }
// This initializes the gocui package // redraw the windows if something has changed
// it runs SetManagerFunc which passes every input if time.Since(lastRefresh) > 1000*time.Millisecond {
// event (keyboard, mouse, etc) to the function "gocuiEvent()" if me.refresh {
func gocuiMain() { log.Log(NOW, "newWindowTrigger() sending refresh to channel")
defer func() { me.newWindowTrigger <- me.treeRoot.TK.(*guiWidget)
if r := recover(); r != nil { me.refresh = false
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
} }
}() if me.stdout.changed {
g, err := gocui.NewGui(gocui.OutputNormal, true) me.stdout.changed = false
if err != nil { lastRefresh = time.Now()
return new1 := new(tree.ToolkitConfig)
} new1.Plugin = "gocui"
defer g.Close() new1.Name = "stdoutsize"
width := me.stdout.tk.gocuiSize.w1 - me.stdout.tk.gocuiSize.w0
me.baseGui = g height := me.stdout.tk.gocuiSize.h1 - me.stdout.tk.gocuiSize.h0
new1.Value = fmt.Sprintf("%d %d %d %d", width, height, me.stdout.tk.gocuiSize.w0, me.stdout.tk.gocuiSize.h0)
g.Cursor = true me.myTree.ConfigSave(new1)
g.Mouse = true // log.Log(NOW, "newWindowTrigger() gocui setting stdout size =", new1.Value)
// me.stdout.tk.dumpWidget("save")
// 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)
/*
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")
} }
} }
// 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() { func newWindowTrigger() {
log.Log(NOW, "newWindowTriggerl() START") // log.Log(NOW, "newWindowTriggerl() START")
for { for {
// log.Log(NOW, "GO plugin toolkit made a new window") // log.Log(NOW, "GO plugin toolkit made a new window")
select { select {
case tk := <-me.newWindowTrigger: case tk := <-me.newWindowTrigger:
// log.Log(NOW, "newWindowTrigger() got new window", tk.cuiName) // log.Log(NOW, "newWindowTrigger() got new window", tk.cuiName)
time.Sleep(200 * time.Millisecond) // time.Sleep(200 * time.Millisecond)
waitOK() waitOK()
redoWindows(1, -1) me.myTree.Lock()
// time.Sleep(200 * time.Millisecond)
redoWindows(me.FirstWindowW, me.FirstWindowH)
me.firstWindowOk = true
if !me.stdout.init { if !me.stdout.init {
me.stdout.init = true me.stdout.init = true
relocateStdoutOffscreen() relocateStdoutOffscreen()
} }
if me.textbox.tk == nil {
initTextbox()
}
tk.makeWindowActive() tk.makeWindowActive()
tk.redrawWindow(tk.gocuiSize.w0, tk.gocuiSize.h0) me.myTree.Unlock()
setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
// log.Log(NOW, "newWindowTrigger() after sleep")
} }
} }
} }

295
libnotify.go Normal file
View File

@ -0,0 +1,295 @@
// 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)
// print out the window list // TODO: put this in libnotify
for _, tk := range me.allwin {
log.Info("known window Window", tk.labelN, tk.window.active, tk.window.order)
}
if s == "[X]" {
log.Warn("should turn on help window here")
showHelp()
} else {
log.Warn("should turn off help window here")
hideHelp()
}
}
// 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()
r := new(rectType)
r.w0 = w
r.h0 = h
r.w1 = w + 8
r.h1 = h + 4
if err := tk.SetViewRect(r); err != nil {
log.Info("hardDrawUnderMouse() err", tk.cuiName, err)
tk.dumpWidget("hardDrawERR")
}
tk.v.Frame = false
tk.v.Clear()
tk.v.WriteString(tk.labelN + "\n" + name)
}
func hardDrawAtgocuiSize(tk *guiWidget) {
if tk.v != nil {
tk.Hide()
}
if err := tk.SetView(); err != nil {
log.Info("hardDrawAtgocuiSize() err ok widget", tk.cuiName)
tk.dumpWidget("hardDrawERR")
}
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()
tk.gocuiSize.w0 = -1
tk.gocuiSize.h0 = -1
tk.gocuiSize.w1 = w + 1
tk.gocuiSize.h1 = h + 1
if err := tk.SetView(); err != nil {
tk.dumpWidget("sigWinchBGerr()")
log.Log(ERROR, "sigWinchBG()", err)
}
log.Log(INFO, "background resized to", tk.gocuiSize)
}
// 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()
tk.gocuiSize.w0 = -1
tk.gocuiSize.h0 = -1
tk.gocuiSize.w1 = w + 1
tk.gocuiSize.h1 = h + 1
tk.SetView()
}

63
node.go Normal file
View File

@ -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
}

View File

@ -34,7 +34,7 @@ func (tk *guiWidget) Position() (int, int) {
} }
func (w *guiWidget) placeBox(startW int, startH int) { func (w *guiWidget) placeBox(startW int, startH int) {
if w.node.WidgetType != widget.Box { if w.WidgetType() != widget.Box {
return return
} }
@ -50,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
@ -77,7 +77,7 @@ func (tk *guiWidget) placeWidgets(startW int, startH int) (int, int) {
tk.startW = startW tk.startW = startW
tk.startH = startH tk.startH = startH
switch tk.node.WidgetType { switch tk.WidgetType() {
case widget.Window: case widget.Window:
tk.full.w0 = startW tk.full.w0 = startW
tk.full.h0 = startH tk.full.h0 = startH
@ -143,11 +143,20 @@ func (tk *guiWidget) placeWidgets(startW int, startH int) (int, int) {
// tk.dumpWidget(fmt.Sprintf("PlaceGroup(%d,%d)", maxW, newH)) // tk.dumpWidget(fmt.Sprintf("PlaceGroup(%d,%d)", maxW, newH))
return maxW, newH return maxW, newH
case widget.Button: case widget.Button:
if tk.isWindowDense() && tk.isInGrid() { if tk.isDense() && tk.isInGrid() {
tk.frame = false tk.frame = false
tk.color = nil // tk.color = nil
tk.defaultColor = nil // tk.defaultColor = nil
/*
if tk.IsEnabled() {
tk.setColorButtonDense() tk.setColorButtonDense()
} else {
tk.setColorDisable()
}
*/
// if tk.full.Height() > 0 {
tk.full.h1 = tk.full.h0
// }
} }
tk.gocuiSetWH(startW, startH) tk.gocuiSetWH(startW, startH)
return tk.gocuiSize.Width(), tk.gocuiSize.Height() return tk.gocuiSize.Width(), tk.gocuiSize.Height()
@ -158,8 +167,16 @@ func (tk *guiWidget) placeWidgets(startW int, startH int) (int, int) {
return 0, 0 return 0, 0
} }
func (tk *guiWidget) isDense() bool {
if tk.node.InTable() {
return true
}
return tk.isWindowDense()
}
func (tk *guiWidget) isWindowDense() bool { func (tk *guiWidget) isWindowDense() bool {
if tk.node.WidgetType == widget.Window { if tk.WidgetType() == widget.Window {
return tk.window.dense return tk.window.dense
} }
if tk.parent == nil { if tk.parent == nil {
@ -169,7 +186,7 @@ func (tk *guiWidget) isWindowDense() bool {
} }
func (tk *guiWidget) isInGrid() bool { func (tk *guiWidget) isInGrid() bool {
if tk.node.WidgetType == widget.Grid { if tk.WidgetType() == widget.Grid {
return true return true
} }
if tk.parent == nil { if tk.parent == nil {
@ -180,12 +197,10 @@ func (tk *guiWidget) isInGrid() bool {
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.node.WidgetType != widget.Grid { if w.WidgetType() != widget.Grid {
return 0, 0 return 0, 0
} }
dense := w.isWindowDense()
w.full.w0 = startW w.full.w0 = startW
w.full.h0 = startH w.full.h0 = startH
@ -194,22 +209,22 @@ func (w *guiWidget) placeGrid(startW int, startH int) (int, int) {
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 child.isDense() {
if w.heights[child.node.State.AtH] > 0 { if w.heights[child.GridH()] > 0 {
w.heights[child.node.State.AtH] = 1 w.heights[child.GridH()] = 1
} else { } else {
w.heights[child.node.State.AtH] = 0 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
@ -221,12 +236,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
} }
} }
@ -242,7 +257,7 @@ 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:")
} }
@ -311,8 +326,8 @@ func textSize(n *tree.Node) (int, int) {
*/ */
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() { if tk.Hidden() {

170
plugin.go
View File

@ -4,8 +4,7 @@
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"
@ -17,7 +16,7 @@ func newAdd(n *tree.Node) {
return return
} }
if n.TK != nil { if n.TK != nil {
log.Warn("Tree Add() sent a widget we aleady seem to have") log.Log(INFO, "Tree Add() sent a widget we aleady seem to have")
// this is done to protect the plugin being 'refreshed' with the // this is done to protect the plugin being 'refreshed' with the
// widget binary tree. TODO: find a way to keep them in sync // widget binary tree. TODO: find a way to keep them in sync
return return
@ -35,24 +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
} }
// for gocui as a GUI plugin, SetTitle & SetLabel are identical to SetText // for gocui as a GUI plugin, SetTitle & SetLabel are identical to SetText
func newSetTitle(n *tree.Node, s string) { func setTitle(n *tree.Node, s string) {
newSetText(n, s) 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
} }
// newSetText() and newAddText() are simple. They take the event sent // setText() and addText() are simple. They take the event sent
// to the GO plugin from the application and lookup the plugin structure // to the GO plugin from the application and lookup the plugin structure
// then pass that event to gocui. This is the transfer point // then pass that event to gocui. This is the transfer point
func newSetText(n *tree.Node, s string) { 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
@ -63,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
@ -76,61 +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 !me.ok {
log.Log(INFO, "newaction() START NOT OKAY", atype)
log.Log(INFO, "newaction() START NOT OKAY", atype)
log.Log(INFO, "newaction() START NOT OKAY", atype)
waitOK()
}
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:
if n.Hidden() {
// already hidden
} else {
log.Log(NOW, "attempt to hide() =", atype, n.WidgetId, n.WidgetType, n.ProgName())
w.node.State.Hidden = true
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()
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() {
@ -168,7 +116,7 @@ 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)
} }
@ -208,8 +156,92 @@ func (tk *guiWidget) GetText() string {
// return gocui.view name? // return gocui.view name?
return tk.cuiName return tk.cuiName
} }
if tk.node.State.Label != "" { if tk.GetLabel() != "" {
return tk.node.State.Label return tk.GetLabel()
} }
return "" return ""
} }
// hack. use "textbox widget" to "disable" user events
func hideDisable() {
if me.textbox.tk == nil {
initTextbox()
}
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())
}
// hack. use "textbox widget" to "disable" user events
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.textbox.tk.SetViewRect(r)
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
}
switch tk.WidgetType() {
case widget.Box:
showDisable()
return
case widget.Button:
tk.setColorDisable()
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")
}
}

62
size.go
View File

@ -9,19 +9,6 @@ import (
"go.wit.com/widget" "go.wit.com/widget"
) )
func (tk *guiWidget) Hidden() bool {
if tk.parent == nil {
return tk.node.Hidden()
}
if tk.parent.node.WidgetId == 0 {
return tk.node.Hidden()
}
if tk.parent.Hidden() {
return true
}
return tk.node.Hidden()
}
func (tk *guiWidget) Size() (int, int) { func (tk *guiWidget) Size() (int, int) {
if tk == nil { if tk == nil {
return 0, 0 return 0, 0
@ -35,7 +22,7 @@ func (tk *guiWidget) Size() (int, int) {
return 0, 0 return 0, 0
} }
switch tk.node.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
@ -76,9 +63,14 @@ func (tk *guiWidget) Size() (int, int) {
case widget.Label: case widget.Label:
return len(tk.String()) + 2, 1 return len(tk.String()) + 2, 1
case widget.Textbox: case widget.Textbox:
return len(tk.String()) + 2, 3 // TODO: compute this based on 'window dense' 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 // TODO: compute this based on 'window dense' return len(tk.String()) + 2, 3 // TODO: compute this based on 'window dense'
case widget.Button:
if tk.isDense() {
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
@ -99,11 +91,11 @@ func (w *guiWidget) sizeGrid() (int, int) {
sizeW, sizeH := child.Size() sizeW, sizeH := child.Size()
// 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
} }
} }
@ -120,7 +112,7 @@ func (w *guiWidget) sizeGrid() (int, int) {
} }
func (w *guiWidget) sizeBox() (int, int) { func (w *guiWidget) sizeBox() (int, int) {
if w.node.WidgetType != widget.Box { if w.WidgetType() != widget.Box {
return 0, 0 return 0, 0
} }
if w.Hidden() { if w.Hidden() {
@ -134,7 +126,7 @@ func (w *guiWidget) sizeBox() (int, int) {
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
@ -249,6 +241,12 @@ func (tk *guiWidget) setFullSize() bool {
tk.full.h1 = r.h1 tk.full.h1 = r.h1
changed = true changed = true
} }
if tk.WidgetType() == widget.Button {
tk.full.h1 = tk.full.h0 + 1
}
if tk.isDense() && tk.isInGrid() {
tk.full.h1 = tk.full.h0
}
if changed { if changed {
tk.dumpWidget(fmt.Sprintf("setFullSize(changed)")) tk.dumpWidget(fmt.Sprintf("setFullSize(changed)"))
} }
@ -310,6 +308,12 @@ func (tk *guiWidget) buttonFullSize() rectType {
tk.full.h0 = r.h0 tk.full.h0 = r.h0
tk.full.h1 = r.h1 tk.full.h1 = r.h1
// total hack. fix this somewhere eventually correctly
if tk.isDense() { // 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 return r
} }
@ -333,21 +337,27 @@ func (tk *guiWidget) getFullSize() rectType {
return r return r
} }
if tk.node.WidgetType == widget.Grid { if tk.WidgetType() == widget.Grid {
return tk.gridFullSize() return tk.gridFullSize()
} }
// these are 'simple' widgets // these are 'simple' widgets
// the full size is exactly what gocui uses // the full size is exactly what gocui uses
switch tk.node.WidgetType { switch tk.WidgetType() {
case widget.Label: case widget.Label:
return tk.buttonFullSize() r := tk.buttonFullSize()
r.w1 += 5
return r
case widget.Button: case widget.Button:
return tk.buttonFullSize() r := tk.buttonFullSize()
r.w1 += 5
return r
case widget.Checkbox: case widget.Checkbox:
return tk.buttonFullSize() return tk.buttonFullSize()
case widget.Dropdown: case widget.Dropdown:
return tk.buttonFullSize() r := tk.buttonFullSize()
r.w1 += 7 // TODO: fix this to be real
return r
default: default:
} }

View File

@ -4,7 +4,6 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"slices" "slices"
"strings" "strings"
@ -15,33 +14,27 @@ import (
"go.wit.com/widget" "go.wit.com/widget"
) )
func showMsg(g *gocui.Gui, v *gocui.View) error { func createStdout(g *gocui.Gui) bool {
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 = ""
}
outv := makeOutputWidget(g, l)
outv.Write([]byte("test out2"))
log.Info("test out2")
return nil
}
func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
if me.treeRoot == nil {
// keep skipping this until the binary tree is initialized
return nil
}
if me.stdout.tk == nil { 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 := new(widget.Action)
a.ProgName = "2stdout2" a.ProgName = "2stdout2"
a.WidgetType = widget.Stdout a.WidgetType = widget.Stdout
@ -52,38 +45,37 @@ func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
me.stdout.tk = initWidget(n) me.stdout.tk = initWidget(n)
tk := me.stdout.tk tk := me.stdout.tk
tk.cuiName = "msg"
tk.gocuiSize.w0 = me.stdout.lastW tk.gocuiSize.w0 = me.stdout.lastW
tk.gocuiSize.h0 = me.stdout.lastH tk.gocuiSize.h0 = me.stdout.lastH
tk.gocuiSize.w1 = tk.gocuiSize.w0 + me.stdout.w tk.gocuiSize.w1 = tk.gocuiSize.w0 + me.stdout.w
tk.gocuiSize.h1 = tk.gocuiSize.h0 + me.stdout.h 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
}
me.stdout.tk.cuiName = "msg"
me.stdout.tk.SetView()
v, err := g.View("msg") v, err := g.View("msg")
if v == nil { if v == nil {
log.Log(NOW, "makeoutputwindow() this is supposed to happen. v == nil", err) // log.Log(NOW, "makeoutputwindow() this is supposed to happen. v == nil", err)
} else { } else {
log.Log(NOW, "makeoutputwindow() msg != nil. WTF now? err =", err) log.Log(NOW, "makeoutputwindow() msg != nil. WTF now? err =", err)
return v
} }
rect := me.stdout.tk.gocuiSize v = me.stdout.tk.v
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 {
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.Clear()
v.SelBgColor = gocui.ColorCyan v.SelBgColor = gocui.ColorCyan
@ -92,7 +84,6 @@ func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
// g.SetViewOnBottom("msg") // g.SetViewOnBottom("msg")
// setBottomBG() // setBottomBG()
me.stdout.tk.v = v
me.stdout.tk.DrawAt(me.stdout.lastW, me.stdout.lastH) me.stdout.tk.DrawAt(me.stdout.lastW, me.stdout.lastH)
relocateStdoutOffscreen() relocateStdoutOffscreen()
return v return v
@ -102,14 +93,32 @@ func relocateStdoutOffscreen() {
if me.stdout.tk == nil { if me.stdout.tk == nil {
return return
} }
if !me.stdout.disable {
log.Info("Using gocui STDOUT")
log.CaptureMode(me.stdout.tk) log.CaptureMode(me.stdout.tk)
// log.Log(ERROR, "setting log.CaptureMode(tk.v) in relocateStdoutOffscreen()") }
newW := 10 newW := 10
newH := 0 - me.stdout.h - 4 newH := 0 - me.stdout.h - 4
me.stdout.tk.relocateStdout(newW, newH) me.stdout.tk.relocateStdout(newW, newH)
} }
func (tk *guiWidget) relocateStdout(w int, h int) { func (tk *guiWidget) relocateStdout(w int, h int) {
if me.stdout.w < 8 {
me.stdout.w = 8
}
if me.stdout.h < 4 {
me.stdout.h = 4
}
if w+me.stdout.w < 2 {
w = 2
}
if h+me.stdout.h < 2 {
h = 2
}
w0 := w w0 := w
h0 := h h0 := h
w1 := w + me.stdout.w w1 := w + me.stdout.w
@ -125,8 +134,7 @@ func (tk *guiWidget) relocateStdout(w int, h int) {
tk.full.h0 = h0 tk.full.h0 = h0
tk.full.h1 = h1 tk.full.h1 = h1
me.baseGui.SetView("msg", w0, h0, w1, h1, 0) tk.SetView()
// me.baseGui.SetViewOnBottom("msg")
} }
// from the gocui devs: // from the gocui devs:
@ -135,51 +143,67 @@ func (tk *guiWidget) relocateStdout(w int, h int) {
// of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must // of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must
// be called to clear the view's buffer. // 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) { 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 w.tainted = true
me.writeMutex.Lock() me.writeMutex.Lock()
defer me.writeMutex.Unlock() defer me.writeMutex.Unlock()
tk := me.stdout.tk
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
lines := strings.Split(strings.TrimSpace(string(p)), "\n")
me.stdout.outputS = append(me.stdout.outputS, lines...) me.stdout.outputS = append(me.stdout.outputS, lines...)
var cur []string tk := me.stdout.tk
// chop off the last lines in the buffer if tk == nil {
chop := len(me.stdout.outputS) - (me.stdout.h - 1) return len(p), nil
if chop < 0 {
chop = 0
} }
if len(me.stdout.outputS) > chop { if tk.v == nil {
cur = append(cur, me.stdout.outputS[chop:]...) // redo this old code
} else { v, _ := me.baseGui.View("msg")
cur = append(cur, me.stdout.outputS...) if v != nil {
tk.v = v
} }
slices.Reverse(cur) return len(p), nil
tk.v.Clear()
fmt.Fprintln(tk.v, strings.Join(cur, "\n"))
} }
tk.refreshStdout()
return len(p), nil return len(p), nil
} }
// lets the user page up and down through the stdout buffer // lets the user page up and down through the stdout buffer
func (tk *guiWidget) refreshStdout() { func (tk *guiWidget) refreshStdout() {
if len(me.stdout.outputS) < me.stdout.h+me.stdout.pager { 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))) // 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 return
} }
@ -187,7 +211,9 @@ func (tk *guiWidget) refreshStdout() {
// chop off the last lines in the buffer // chop off the last lines in the buffer
chop := len(me.stdout.outputS) - (me.stdout.pager + me.stdout.h) chop := len(me.stdout.outputS) - (me.stdout.pager + me.stdout.h)
cur = append(cur, me.stdout.outputS[chop:chop+me.stdout.h]...) cur = append(cur, me.stdout.outputS[chop:chop+me.stdout.h]...)
if me.stdout.reverse {
slices.Reverse(cur) slices.Reverse(cur)
}
tk.v.Clear() tk.v.Clear()
fmt.Fprintln(tk.v, strings.Join(cur, "\n")) fmt.Fprintln(tk.v, strings.Join(cur, "\n"))
} }

View File

@ -10,16 +10,22 @@ package main
import ( import (
"fmt" "fmt"
"os"
"reflect" "reflect"
"strconv" "strconv"
"sync" "sync"
"time"
"github.com/awesome-gocui/gocui" "github.com/awesome-gocui/gocui"
"go.wit.com/lib/protobuf/guipb"
log "go.wit.com/log" log "go.wit.com/log"
"go.wit.com/toolkits/tree" "go.wit.com/toolkits/tree"
"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. // 2025 note: doesn't seem terrible to call this 'me' anymore. notsure.
var me config var me config
@ -33,9 +39,13 @@ type config struct {
myTree *tree.TreeInfo // ? myTree *tree.TreeInfo // ?
currentWindow *guiWidget // this is the current tab or window to show currentWindow *guiWidget // this is the current tab or window to show
ok bool // if the user doesn't hit a key or move the mouse, gocui doesn't really start 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
refresh bool // redraw everything?
ctrlDown *tree.Node // shown if you click the mouse when the ctrl key is pressed ctrlDown *tree.Node // shown if you click the mouse when the ctrl key is pressed
helpLabel *gocui.View // ? helpLabel *gocui.View // ?
showHelp bool // toggle boolean for the help menu (deprecate?) showHelp bool // toggle boolean for the help menu (deprecate?)
FirstWindowW int `default:"2"` // how far over to start window #1
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 FramePadW int `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side
FramePadH int `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side FramePadH int `default:"1" dense:"0"` // When the widget has a frame, like a button, it adds 2 lines runes on each side
PadW int `default:"1" dense:"0"` // pad spacing PadW int `default:"1" dense:"0"` // pad spacing
@ -52,6 +62,7 @@ type config struct {
RawW int `default:"1"` // the raw beginning of each window (or tab) RawW int `default:"1"` // the raw beginning of each window (or tab)
RawH int `default:"5"` // the raw beginning of each window (or tab) RawH int `default:"5"` // the raw beginning of each window (or tab)
FakeW int `default:"20"` // offset for the hidden widgets FakeW int `default:"20"` // offset for the hidden widgets
DropdownId int `default:"-78"` // the widget id to use
padded bool // add space between things like buttons padded bool // add space between things like buttons
bookshelf bool // do you want things arranged in the box like a bookshelf or a stack? 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 canvas bool // if set to true, the windows are a raw canvas
@ -62,17 +73,35 @@ type config struct {
ecount int // counts how many mouse and keyboard events have occurred ecount int // counts how many mouse and keyboard events have occurred
supermouse bool // prints out every widget found while you move the mouse around supermouse bool // prints out every widget found while you move the mouse around
depth int // used for listWidgets() debugging depth int // used for listWidgets() debugging
globalMouseDown bool // yep, mouse is pressed
newWindowTrigger chan *guiWidget // work around hack to redraw windows a bit after NewWindow() newWindowTrigger chan *guiWidget // work around hack to redraw windows a bit after NewWindow()
stdout stdout // information for the STDOUT window stdout stdout // information for the STDOUT window
showDebug bool // todo: move this into config struct
dropdown dropdown // the dropdown menu dropdown dropdown // the dropdown menu
textbox dropdown // the textbox popup window textbox dropdown // the textbox popup window
BG dropdown // the background widget
notify libnotify // emulates the desktop libnotify menu
allwin []*guiWidget // for tracking which window is next allwin []*guiWidget // for tracking which window is next
dark bool // use a 'dark' color palette
mouse mouse // mouse settings
showDebug bool // todo: move this into config struct
debug bool // todo: move this into config struct
starttime time.Time // checks how long it takes on startup
winchW int // used to detect SIGWINCH
winchH int // used to detect SIGWINCH
outf *os.File // hacks for capturing stdout
}
// stuff controlling how the mouse works
type mouse struct {
down time.Time // when the mouse was pressed down
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 downW int // where the mouse was pressed down
downH int // where the mouse was pressed down downH int // where the mouse was pressed down
currentDrag *guiWidget // what widget is currently being moved around currentDrag *guiWidget // what widget is currently being moved around
dark bool // use a 'dark' color palette
} }
// settings for the stdout window // settings for the stdout window
@ -83,14 +112,15 @@ type stdout struct {
h int // the height h int // the height
outputOnTop bool // is the STDOUT window on top? outputOnTop bool // is the STDOUT window on top?
outputOffscreen bool // is the STDOUT window offscreen? 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) 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) 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 init bool // moves the window offscreen on startup
resize bool // user is resizing the window
outputS []string // the buffer of all the output outputS []string // the buffer of all the output
pager int // allows the user to page through the buffer pager int // allows the user to page through the buffer
changed bool // indicates the user has changed stdout. gocui should remember the state here
reverse bool // flip the STDOUT upside down so new STDOUT lines are at the top
} }
// settings for the dropdown window // settings for the dropdown window
@ -102,8 +132,27 @@ type dropdown struct {
h int // the height h int // the height
active bool // is the dropdown menu currently in use? active bool // is the dropdown menu currently in use?
init bool // moves the window offscreen on startup 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 wId int // the widget id to use
// dtoggle bool // is a dropdown or combobox currently active? 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
@ -117,6 +166,19 @@ 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
} }
@ -124,17 +186,16 @@ func (r *rectType) Height() int {
type window struct { type window struct {
windowFrame *guiWidget // this is the frame for a window widget windowFrame *guiWidget // this is the frame for a window widget
wasDragged bool // indicates the window was dragged. This keeps it from being rearranged wasDragged bool // indicates the window was dragged. This keeps it from being rearranged
dragW int // when dragging a window, this is the offset to the mouse position
dragH int // when dragging a window, this is the offset to the mouse position
hasTabs bool // does the window have tabs? hasTabs bool // does the window have tabs?
currentTab bool // the visible tab currentTab bool // the visible tab
selectedTab *tree.Node // for a window, this is currently selected tab selectedTab *tree.Node // for a window, this is currently selected tab
active bool // means this window is the active one active bool // means this window is the active one
isBG bool // means this is the background widget. There is only one of these
order int // what level the window is on order int // what level the window is on
resize bool // only set the title once // resize bool // only set the title once
collapsed bool // only show the window title bar collapsed bool // only show the window title bar
dense bool // true if the window is huge 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 { type colorT struct {
@ -152,10 +213,10 @@ type guiWidget struct {
parent *guiWidget // mirrors the binary node tree parent *guiWidget // mirrors the binary node tree
children []*guiWidget // mirrors the binary node tree children []*guiWidget // mirrors the binary node tree
node *tree.Node // the pointer back to the tree node *tree.Node // the pointer back to the tree
pb *guipb.Widget // the guipb Widget
wtype widget.WidgetType // used for Tables for now. todo: fix this correctly
windowFrame *guiWidget // this is the frame for a window widget windowFrame *guiWidget // this is the frame for a window widget
internal bool // indicates the widget is internal to gocui and should be treated differently internal bool // indicates the widget is internal to gocui and should be treated differently
dragW int // when dragging a window, this is the offset to the mouse position
dragH int // when dragging a window, this is the offset to the mouse position
hasTabs bool // does the window have tabs? hasTabs bool // does the window have tabs?
currentTab bool // the visible tab currentTab bool // the visible tab
window window // holds information specific only to Window widgets window window // holds information specific only to Window widgets
@ -169,6 +230,8 @@ type guiWidget struct {
force rectType // force widget within these boundries (using this to debug window dragging) force rectType // force widget within these boundries (using this to debug window dragging)
startW int // ? startW int // ?
startH int // ? startH int // ?
lastW int // used during mouse dragging
lastH int // used during mouse dragging
isFake bool // widget types like 'box' are 'false' isFake bool // widget types like 'box' are 'false'
widths map[int]int // how tall each row in the grid is widths map[int]int // how tall each row in the grid is
heights map[int]int // how wide each column in the grid is heights map[int]int // how wide each column in the grid is
@ -176,9 +239,9 @@ type guiWidget struct {
frame bool // ? frame bool // ?
selectedTab *tree.Node // for a window, this is currently selected tab selectedTab *tree.Node // for a window, this is currently selected tab
color *colorT // what color to use color *colorT // what color to use
colorLast colorT // the last color the widget had
defaultColor *colorT // the default colors // TODO: make a function for this instead defaultColor *colorT // the default colors // TODO: make a function for this instead
isBG bool // means this is the background widget. There is only one of these isTable bool // is this a table?
// resize bool // the window is currently being resized
} }
// THIS IS GO COMPILER MAGIC // THIS IS GO COMPILER MAGIC
@ -203,12 +266,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() {

93
table.go Normal file
View File

@ -0,0 +1,93 @@
// 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.wtype = widget.Grid
w.cuiName = fmt.Sprintf("%d %s", pb.Id, "TK")
w.labelN = pb.Name
w.isTable = true
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()
}

151
textbox.go Normal file
View File

@ -0,0 +1,151 @@
// 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"
"time"
"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(WARN, "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
if me.textbox.tk.v != nil {
log.Log(WARN, "WARNING textbox DeleteView()")
log.Log(WARN, "WARNING textbox DeleteView()")
log.Log(WARN, "WARNING textbox DeleteView()")
me.baseGui.DeleteView(me.textbox.tk.cuiName)
time.Sleep(time.Second)
}
if err := me.textbox.tk.SetViewRect(r); err != nil {
log.Log(WARN, "textbox SetViewRect() failed", err, "view name =", me.textbox.tk.cuiName)
return
}
// me.textbox.tk.Show() // actually makes the gocui view. TODO: redo this?
showTextbox(callertk.String())
}
func showTextbox(callers string) {
// tk := me.textbox.tk
// me.textbox.tk.dumpWidget("after sizes")
log.Log(WARN, "showTextbox() caller string =", callers)
// me.textbox.tk.Show() // actually makes the gocui view. TODO: redo this
if me.textbox.tk.v == nil {
log.Log(WARN, "textbox.tk.v == nil showTextbox() is broken")
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
me.textbox.tk.SetView()
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.baseGui.SetViewOnTop(me.textbox.tk.v.Name())
me.textbox.tk.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()
}
}

View File

@ -1,32 +0,0 @@
// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0
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 (
"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()
}

90
treeInit.go Normal file
View File

@ -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
}

View File

@ -23,6 +23,13 @@ func (tk *guiWidget) textResize() {
h += 1 h += 1
} }
// 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 // 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.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 tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH // TODO: fix this size computation
@ -43,7 +50,7 @@ func (w *guiWidget) hideWidgets() {
if w == nil { if w == nil {
return return
} }
switch w.node.WidgetType { switch w.WidgetType() {
case widget.Root: case widget.Root:
case widget.Flag: case widget.Flag:
case widget.Window: case widget.Window:

View File

@ -1,44 +0,0 @@
syntax = "proto3";
// including this in a plugin seems to keep it from loading at this point
package main;
import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp
// maybe put all the gocui specific stuff here.
message GocuiState { // `autogenpb:nomutex`
bool visible = 1;
bool internal = 2;
int64 w0 = 3;
int64 h0 = 4;
int64 w1 = 5;
int64 h1 = 6;
}
message ViewSettings { // `autogenpb:nomutex`
bool pack = 1;
int64 framesize = 5;
}
message Tree {
View parent = 1;
repeated View children = 2;
View node = 3;
}
// this is the gocui 'view' in binary tree form
message View {
int64 widgetId = 3; // `autogenpb:unique` `autogenpb:sort`
string name = 4; // `autogenpb:unique` `autogenpb:sort`
GocuiState state = 7;
}
message Views { // `autogenpb:marshal` `autogenpb:mutex`
string uuid = 1; // `autogenpb:uuid:d19c1fbb-32c2-4957-aee6-f8128a511dca`
string version = 2; // `autogenpb:version:v0.0.1`
repeated View Views = 3;
Tree tree = 4;
map<string, string> junk = 5;
ViewSettings settings = 6;
}

View File

@ -4,8 +4,6 @@
package main package main
import ( import (
"time"
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"
@ -30,17 +28,7 @@ func setFake(n *tree.Node) {
} }
} }
func waitOK() { // mostly just sets the colors of things
for {
if me.ok {
return
}
time.Sleep(10 * time.Millisecond)
}
}
// set the widget start width & height
// func (n *node) addWidget(n *tree.Node) {
func addWidget(n *tree.Node) { func addWidget(n *tree.Node) {
if !me.ok { if !me.ok {
log.Log(INFO, "addWidget() START NOT OKAY") log.Log(INFO, "addWidget() START NOT OKAY")
@ -48,38 +36,36 @@ func addWidget(n *tree.Node) {
log.Log(INFO, "addWidget() START NOT OKAY") log.Log(INFO, "addWidget() START NOT OKAY")
waitOK() waitOK()
} }
var tk *guiWidget tk := n.TK.(*guiWidget)
tk = n.TK.(*guiWidget)
log.Log(INFO, "setStartWH() w.id =", n.WidgetId, "n.name", n.String()) log.Log(INFO, "setStartWH() w.id =", n.WidgetId, "n.name", n.String())
switch n.WidgetType { switch n.WidgetType {
case widget.Root: case widget.Root:
log.Log(INFO, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.String()) log.Log(INFO, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.String())
tk.color = &colorRoot
setFake(n) setFake(n)
return return
case widget.Flag: case widget.Flag:
tk.color = &colorFlag
setFake(n) setFake(n)
return return
case widget.Window: case widget.Window:
tk.frame = false tk.frame = false
tk.labelN = tk.GetText() + " X" tk.labelN = tk.GetText() + " X"
tk.setColor(&colorWindow)
me.newWindowTrigger <- tk me.newWindowTrigger <- tk
redoWindows(0, 0) redoWindows(0, 0)
hideHelp()
showHelp()
return return
case widget.Stdout: case widget.Stdout:
tk.labelN = "moreSTDOUT" tk.labelN = "moreSTDOUT"
n.State.ProgName = "moreSTDOUT" n.State.ProgName = "moreSTDOUT"
n.State.Label = "moreSTDOUT" n.State.Label = "moreSTDOUT"
tk.isFake = true
return return
case widget.Tab: case widget.Tab:
tk.color = &colorTab
return return
case widget.Button: case widget.Button:
tk.setColorButton() tk.setColorButton()
if tk.IsEnabled() {
} else {
tk.setColorDisable()
}
return return
case widget.Checkbox: case widget.Checkbox:
tk.setColorInput() tk.setColorInput()
@ -89,20 +75,18 @@ func addWidget(n *tree.Node) {
tk.setColorInput() tk.setColorInput()
return return
case widget.Textbox: case widget.Textbox:
n.State.Label = "TEXTBOX" n.State.Label = ""
tk.labelN = " " + n.State.Label tk.labelN = " "
tk.color = &colorDropdown tk.setColorInput()
return return
case widget.Combobox: case widget.Combobox:
tk.color = &colorCombobox tk.setColorInput()
return return
case widget.Box: case widget.Box:
// tk.color = &colorBox
tk.isFake = true tk.isFake = true
setFake(n) setFake(n)
return return
case widget.Grid: case widget.Grid:
// tk.color = &colorGrid
tk.isFake = true tk.isFake = true
setFake(n) setFake(n)
return return
@ -111,7 +95,17 @@ func addWidget(n *tree.Node) {
tk.frame = false tk.frame = false
return return
case widget.Label: case widget.Label:
if tk.node.InTable() {
if tk.node.State.AtH == 0 {
// this is the table header
tk.setColorLabelTable()
} else {
// todo: highlight the whole table row
tk.setColorLabel() tk.setColorLabel()
}
} else {
tk.setColorLabel()
}
tk.frame = false tk.frame = false
return return
default: default:

View File

@ -5,8 +5,8 @@ package main
import ( import (
"strconv" "strconv"
"strings"
"github.com/awesome-gocui/gocui"
"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"
@ -17,8 +17,8 @@ func initWidget(n *tree.Node) *guiWidget {
w = new(guiWidget) w = new(guiWidget)
w.node = n w.node = n
w.cuiName = strconv.Itoa(w.node.WidgetId) + " TK" w.cuiName = strconv.Itoa(w.WidgetId()) + " TK"
// w.node.WidgetType = n.WidgetType // w.WidgetType() = n.WidgetType
w.labelN = n.State.Label w.labelN = n.State.Label
if w.labelN == "" { if w.labelN == "" {
// remove this debugging hack once things are stable and fixed // remove this debugging hack once things are stable and fixed
@ -38,11 +38,15 @@ func initWidget(n *tree.Node) *guiWidget {
p := n.Parent p := n.Parent
if p == nil { if p == nil {
log.Log(ERROR, "parent == nil", w.String(), n.WidgetId, w.node.WidgetType) log.Log(ERROR, "parent == nil", w.String(), n.WidgetId, w.WidgetType())
return w return w
} }
if p.TK == nil { if p.TK == nil {
log.Log(ERROR, "parent.TK == nil", w.String(), n.WidgetId, w.node.WidgetType) 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 return w
} }
@ -68,13 +72,34 @@ func setupCtrlDownWidget() {
func (w *guiWidget) deleteView() { func (w *guiWidget) deleteView() {
// make sure the view isn't really there // make sure the view isn't really there
// log.Log(GOCUI, "deleteView()", w.cuiName, w.node.WidgetType, w.node.WidgetId) // log.Log(GOCUI, "deleteView()", w.cuiName, w.WidgetType(), w.WidgetId())
me.baseGui.DeleteView(w.cuiName) me.baseGui.DeleteView(w.cuiName)
w.v = nil w.v = nil
} }
func (tk *guiWidget) String() string { func (tk *guiWidget) String() string {
return tk.node.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 { func (tk *guiWidget) Visible() bool {
@ -99,34 +124,3 @@ func (tk *guiWidget) SetVisible(b bool) {
tk.Hide() 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
}

View File

@ -13,11 +13,74 @@ import (
"go.wit.com/widget" "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 // display's the text of the widget in gocui
// deletes the old view if it exists and recreates it // deletes the old view if it exists and recreates it
func (tk *guiWidget) drawView() { func (tk *guiWidget) drawView() {
var err error var err error
log.Log(INFO, "drawView() START", tk.node.WidgetType, tk.String()) log.Log(INFO, "drawView() START", tk.WidgetType(), tk.String())
if me.baseGui == nil { if me.baseGui == nil {
log.Log(ERROR, "drawView() ERROR: me.baseGui == nil", tk) log.Log(ERROR, "drawView() ERROR: me.baseGui == nil", tk)
return return
@ -25,7 +88,7 @@ func (tk *guiWidget) drawView() {
if tk.cuiName == "" { if tk.cuiName == "" {
log.Log(ERROR, "drawView() tk.cuiName was not set for widget", tk) log.Log(ERROR, "drawView() tk.cuiName was not set for widget", tk)
tk.cuiName = strconv.Itoa(tk.node.WidgetId) + " TK" tk.cuiName = strconv.Itoa(tk.WidgetId()) + " TK"
} }
log.Log(INFO, "drawView() labelN =", tk.labelN) log.Log(INFO, "drawView() labelN =", tk.labelN)
@ -38,8 +101,40 @@ func (tk *guiWidget) drawView() {
c := tk.gocuiSize.w1 c := tk.gocuiSize.w1
d := tk.gocuiSize.h1 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 // this is all terrible. This sets the title. kinda
if tk.node.WidgetType == widget.Window { if tk.WidgetType() == widget.Window {
tk.textResize() tk.textResize()
tk.full.w0 = tk.force.w0 tk.full.w0 = tk.force.w0
tk.full.h0 = tk.force.h0 tk.full.h0 = tk.force.h0
@ -59,7 +154,6 @@ func (tk *guiWidget) drawView() {
b = tk.gocuiSize.h0 b = tk.gocuiSize.h0
c = tk.gocuiSize.w1 c = tk.gocuiSize.w1
d = tk.gocuiSize.h1 d = tk.gocuiSize.h1
} }
tk.v, err = me.baseGui.SetView(tk.cuiName, a, b, c, d, 0) tk.v, err = me.baseGui.SetView(tk.cuiName, a, b, c, d, 0)
@ -68,18 +162,55 @@ func (tk *guiWidget) drawView() {
log.Log(ERROR, "drawView() internal plugin error err = nil") log.Log(ERROR, "drawView() internal plugin error err = nil")
return return
} }
if !errors.Is(err, gocui.ErrUnknownView) { if !errors.Is(err, gocui.ErrUnknownView) {
tk.dumpWidget("drawView() err") tk.dumpWidget("drawView() err")
log.Log(ERROR, "drawView() internal plugin error error.IS()", err) log.Log(ERROR, "drawView() internal plugin error error.IS()", err)
return 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 // this actually sends the text to display to gocui
tk.v.Wrap = true tk.v.Wrap = true
tk.v.Frame = tk.frame tk.v.Frame = tk.frame
tk.v.Clear() tk.v.Clear()
fmt.Fprint(tk.v, tk.labelN) 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.isDense() && 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 you don't do this here, it will be black & white only
if tk.color != nil { if tk.color != nil {
tk.v.FrameColor = tk.color.frame tk.v.FrameColor = tk.color.frame
@ -88,34 +219,16 @@ func (tk *guiWidget) drawView() {
tk.v.SelFgColor = tk.color.selFg tk.v.SelFgColor = tk.color.selFg
tk.v.SelBgColor = tk.color.selBg tk.v.SelBgColor = tk.color.selBg
} }
log.Log(INFO, "drawView() END") log.Log(INFO, "drawView() END")
} }
// redraw the widget tree starting at this location
func (w *guiWidget) DrawAt(offsetW, offsetH int) { func (w *guiWidget) DrawAt(offsetW, offsetH int) {
w.setColor(&colorActiveW)
w.placeWidgets(offsetW, offsetH) // compute the sizes & places for each widget w.placeWidgets(offsetW, offsetH) // compute the sizes & places for each widget
// w.dumpWidget(fmt.Sprintf("DrawAt(%d,%d)", offsetW, offsetH)) // w.dumpWidget(fmt.Sprintf("DrawAt(%d,%d)", offsetW, offsetH))
} }
func (w *guiWidget) simpleDrawAt(offsetW, offsetH int) {
w.setColor(&colorActiveW)
w.dumpWidget("simpleDrawAt()")
}
/*
var toggle bool = true
func (w *guiWidget) toggleTree() {
if toggle {
w.drawTree(toggle)
toggle = false
} else {
w.hideWidgets()
toggle = true
}
}
*/
// display the widgets in the binary tree // display the widgets in the binary tree
func (w *guiWidget) drawTree(draw bool) { func (w *guiWidget) drawTree(draw bool) {
if w == nil { if w == nil {
@ -145,6 +258,9 @@ func (w *guiWidget) Show() {
// never show hidden widgets // never show hidden widgets
return return
} }
if me.debug {
w.dumpWidget("drawView()")
}
w.drawView() w.drawView()
} }

122
window.go
View File

@ -7,12 +7,32 @@ import (
"fmt" "fmt"
"strings" "strings"
log "go.wit.com/log"
"go.wit.com/toolkits/tree" "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) { func (tk *guiWidget) redrawWindow(w int, h int) {
if tk.node.WidgetType != widget.Window { if tk.WidgetType() != widget.Window {
return return
} }
// tk.dumpWidget(fmt.Sprintf("redrawWindow(%d,%d)", w, h)) // tk.dumpWidget(fmt.Sprintf("redrawWindow(%d,%d)", w, h))
@ -22,7 +42,8 @@ func (tk *guiWidget) redrawWindow(w int, h int) {
// pin the window to (w,h) // pin the window to (w,h)
tk.gocuiSize.w0 = w tk.gocuiSize.w0 = w
tk.gocuiSize.h0 = h tk.gocuiSize.h0 = h
tk.gocuiSize.w1 = w + len(tk.GetText()) 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.w0 = w
tk.force.w1 = w tk.force.w1 = w
tk.force.h0 = h tk.force.h0 = h
@ -34,7 +55,8 @@ func (tk *guiWidget) redrawWindow(w int, h int) {
tk.hasTabs = false tk.hasTabs = false
tk.DrawAt(w, h) tk.DrawAt(w, h)
tk.setColor(&colorActiveW) // sets the window to Green BG // tk.setColor(&colorActiveW) // sets the window to Green BG
tk.setColorWindowTitleActive()
if tk.window.collapsed { if tk.window.collapsed {
// don't show anything but the title bar // don't show anything but the title bar
@ -48,26 +70,18 @@ func (tk *guiWidget) redrawWindow(w int, h int) {
tk.full.h0 = tk.force.h0 tk.full.h0 = tk.force.h0
tk.setFullSize() tk.setFullSize()
/*
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("crap. got an err", err)
}
if tk.v != v {
log.Info("crap. got another problem v != tk.v")
}
*/
tk.Show() tk.Show()
if tk.v == nil {
log.Info("redrawWindow on tk.v == nil")
standardExit()
}
tk.v.Clear() tk.v.Clear()
fmt.Fprint(tk.v, "ZZZ"+tk.GetText()) fmt.Fprint(tk.v, "ZZZ"+tk.GetText())
tk.showWidgets() tk.showWidgets()
// RE-VERIFY THIS CAN'T BE DONE IN A BETTER WAY. However, for now, this works finally so I am leaving it alone
if tk.windowFrame == nil { if tk.windowFrame == nil {
tk.addWindowFrameTK(0 - tk.node.WidgetId) tk.addWindowFrameTK(0 - tk.WidgetId())
tk.windowFrame.node.State.Label = " ZZzzzFrame" // temporary name. blank out when ready for release tk.windowFrame.makeTK([]string{""})
tk.windowFrame.makeTK([]string{" ZZzzzFrame"})
} }
// this seems to correctly create the window frame // this seems to correctly create the window frame
@ -86,12 +100,16 @@ func (tk *guiWidget) redrawWindow(w int, h int) {
// set the window frame below the window widget, but this resizes the window widget it seems // 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) 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? // 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) 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 // so we have to redraw the widgets in the window anyway and then they will appear above he frame
tk.hideWidgets() tk.hideWidgets()
tk.showWidgets() 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
@ -100,8 +118,7 @@ func redoWindows(nextW int, nextH int) {
// tk.dumpWidget(fmt.Sprintf("redoWindowsS (%d,%d)", nextW, nextH)) // tk.dumpWidget(fmt.Sprintf("redoWindowsS (%d,%d)", nextW, nextH))
if tk.window.wasDragged { if tk.window.wasDragged {
// don't move windows around the user has dragged to a certain location // don't move windows around the user has dragged to a certain location
tk.redrawWindow(tk.gocuiSize.w0, tk.gocuiSize.h0) tk.makeWindowActive()
setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
} else { } else {
w, _ := me.baseGui.Size() w, _ := me.baseGui.Size()
if nextW > w-20 { if nextW > w-20 {
@ -140,13 +157,11 @@ func (win *guiWidget) addWindowFrame(wId int) *tree.Node {
if tk.node.Parent == nil { if tk.node.Parent == nil {
tk.node.Parent = me.treeRoot tk.node.Parent = me.treeRoot
} }
// copy the data from the action message
tk.node.State.Label = "windowFrame"
// set the name used by gocui to the id // set the name used by gocui to the id
tk.cuiName = fmt.Sprintf("%d DR", wId) tk.cuiName = fmt.Sprintf("%d DR", wId)
tk.color = &colorGroup // tk.color = &colorGroup
// add this new widget on the binary tree // add this new widget on the binary tree
tk.parent = win tk.parent = win
@ -160,29 +175,41 @@ func (win *guiWidget) addWindowFrame(wId int) *tree.Node {
return n 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() { func (tk *guiWidget) makeWindowActive() {
if !(tk.node.WidgetType == widget.Window || tk.node.WidgetType == widget.Stdout) { if !(tk.WidgetType() == widget.Window || tk.WidgetType() == widget.Stdout) {
// only allow Window or the Stdout widgets to be made active // only allow Window or the Stdout widgets to be made active
return return
} }
if tk.WidgetType() == widget.Stdout {
me.stdout.outputOnTop = true
} else {
// me.stdout.outputOnTop = false // ?
}
// disable and increment all the windows // disable and increment all the windows
for _, tk := range me.allwin { for _, tk := range me.allwin {
tk.window.order += 1 tk.window.order += 1
tk.window.active = false tk.window.active = false
tk.setColor(&colorWindow) // color for inactive windows // tk.setColor(&colorWindow) // color for inactive windows
tk.setColorWindowTitle()
} }
// set this window as the active one // set this window as the active one
tk.window.active = true tk.window.active = true
tk.window.order = 0 tk.window.order = 0
/* tk.redrawWindow(tk.gocuiSize.w0, tk.gocuiSize.h0)
// print out the window list setThingsOnTop() // sets help, Stdout, etc on the top after windows have been redrawn
for _, tk := range me.allwin {
log.Info("makeWindowActive() Window", tk.labelN, tk.window.active, tk.window.order)
}
*/
} }
func (tk *guiWidget) makeTK(ddItems []string) { func (tk *guiWidget) makeTK(ddItems []string) {
@ -193,24 +220,21 @@ func (tk *guiWidget) makeTK(ddItems []string) {
tk.gocuiSize.w1 = 120 tk.gocuiSize.w1 = 120
tk.gocuiSize.h0 = 15 tk.gocuiSize.h0 = 15
tk.gocuiSize.h1 = 18 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() 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
}