summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore8
-rw-r--r--Makefile115
-rw-r--r--andlabs/Makefile7
-rw-r--r--andlabs/action.go265
-rw-r--r--andlabs/add.go157
-rw-r--r--andlabs/box.go29
-rw-r--r--andlabs/button.go31
-rw-r--r--andlabs/checkbox.go27
-rw-r--r--andlabs/combobox.go41
l---------andlabs/common.go1
-rw-r--r--andlabs/debug.go168
-rw-r--r--andlabs/delete.go51
-rw-r--r--andlabs/demo.go90
-rw-r--r--andlabs/dropdown.go79
-rw-r--r--andlabs/grid.go25
-rw-r--r--andlabs/group.go22
-rw-r--r--andlabs/icon.go27
-rw-r--r--andlabs/image.go50
-rw-r--r--andlabs/label.go18
-rw-r--r--andlabs/log.go24
-rw-r--r--andlabs/main.go57
-rw-r--r--andlabs/setText.go128
-rw-r--r--andlabs/slider.go22
-rw-r--r--andlabs/spinner.go22
-rw-r--r--andlabs/structs.go58
-rw-r--r--andlabs/tab.go116
-rw-r--r--andlabs/textbox.go32
-rw-r--r--andlabs/updateui.go97
-rw-r--r--andlabs/widget.go29
-rw-r--r--andlabs/window.go46
-rw-r--r--go.mod20
-rw-r--r--go.sum24
-rw-r--r--gocui/Makefile15
-rw-r--r--gocui/add.go78
-rw-r--r--gocui/checkbox.go33
-rw-r--r--gocui/click.go356
-rw-r--r--gocui/color.go117
l---------gocui/common.go1
-rw-r--r--gocui/debug.go73
-rw-r--r--gocui/fakefile.go58
-rw-r--r--gocui/gocui.go100
-rw-r--r--gocui/help.go71
-rw-r--r--gocui/keybindings.go175
-rw-r--r--gocui/log.go55
-rw-r--r--gocui/main.go101
-rw-r--r--gocui/mouse.go149
-rw-r--r--gocui/place.go186
-rw-r--r--gocui/plugin.go126
-rw-r--r--gocui/showStdout.go97
-rw-r--r--gocui/structs.go224
-rw-r--r--gocui/tab.go109
-rw-r--r--gocui/view.go232
-rw-r--r--gocui/widget.go147
-rw-r--r--nocui/Makefile5
-rw-r--r--nocui/README.md5
-rw-r--r--nocui/action.go153
-rw-r--r--nocui/common.go166
-rw-r--r--nocui/event.go47
-rw-r--r--nocui/log.go26
-rw-r--r--nocui/main.go55
-rw-r--r--nocui/stdin.go80
-rw-r--r--nocui/structs.go17
-rw-r--r--nocui/widget.go29
63 files changed, 4972 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6c5452f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+*.swp
+
+# ignore compiled plugins
+*.so
+
+# temporary files when building debian packages
+/*.deb
+/files
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..532b3ca
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,115 @@
+.PHONY: nocui gocui andlabs
+
+all:
+ # reset
+ @echo
+ @echo "make examples # will run all the Example demos and commands"
+ @echo "make update # full git update of all the dependencies"
+ @echo
+ @echo This Requires working IPv6
+ @echo
+ifeq ($(GO111MODULE),)
+ @echo
+ @echo If you are compiling this here, you probably want to set GO111MODULE
+ @echo
+ @echo Setting GO111MODULE means that the version you are compiling has plugins
+ @echo that get compiled against this current running version of the code
+ @echo Otherwise, the GO language plugins can complain about being compiled against
+ @echo mis-matched versions
+ @echo
+ @echo export GO111MODULE=off
+ @echo
+ sleep 3
+endif
+ make nocui gocui andlabs
+
+nocui:
+ go build -C nocui -v -buildmode=plugin -o ../nocui.so
+
+gocui:
+ go build -C gocui -v -buildmode=plugin -o ../gocui.so
+
+andlabs:
+ go build -C andlabs -v -buildmode=plugin -o ../andlabs.so
+
+something:
+ifeq (,$(wildcard go.mod))
+ go mod init gui
+ go mod tidy
+endif
+ make clean
+ make plugins
+
+build-dep:
+ apt install -f libgtk-3-dev
+
+# should update every go dependancy (?)
+update:
+ git pull
+ go get -v -t -u ./...
+
+deb:
+ cd debian && make
+ dpkg-deb -c go-wit-gui*.deb
+ -wit mirrors
+
+examples: \
+ all \
+ examples-helloworld \
+ examples-buttons \
+ examples-console-ui-helloworld
+
+# this is the most basic one. This syntax should always work
+examples-helloworld:
+ make -C examples/helloworld
+
+examples-buttons:
+ make -C examples/buttons
+
+examples-console-ui-helloworld:
+ make -C examples/console-ui-helloworld
+
+# sync repo to the github backup
+# git remote add github [email protected]:witorg/gui.git
+# git remote add github2 [email protected]:wit-go/gui.git
+github:
+ git push origin master
+ git push origin devel
+ git push origin --tags
+ git push github master
+ git push github devel
+ git push github --tags
+ @echo
+ @echo check https://github.com/wit-go/gui
+ @echo
+
+doc:
+ godoc -v
+
+goget:
+ go get -v -t -u
+ make -C toolkit/gocui goget
+ make -C toolkit/andlabs goget
+
+clean:
+ rm -f *.so
+ # cd debian && make clean
+
+plugins: plugins-gocui plugins-andlabs
+
+plugins-gocui:
+ go build -C toolkit/gocui -v -buildmode=plugin -o ../gocui.so
+ go build -C toolkit/nocui -v -buildmode=plugin -o ../nocui.so
+
+plugins-andlabs:
+ go build -C toolkit/andlabs -v -buildmode=plugin -o ../andlabs.so
+
+objdump:
+ objdump -t toolkit/andlabs.so |less
+
+log:
+ reset
+ tail -f /tmp/witgui.* /tmp/guilogfile
+
+submit-to-docs:
+ GOPROXY=https://proxy.golang.org GO111MODULE=on go get go.wit.com/[email protected]
diff --git a/andlabs/Makefile b/andlabs/Makefile
new file mode 100644
index 0000000..b839c33
--- /dev/null
+++ b/andlabs/Makefile
@@ -0,0 +1,7 @@
+all: plugin
+
+plugin:
+ GO111MODULE="off" go build -v -x -buildmode=plugin -o ../andlabs.so
+
+goget:
+ GO111MODULE="off" go get -v -t -u
diff --git a/andlabs/action.go b/andlabs/action.go
new file mode 100644
index 0000000..82acd58
--- /dev/null
+++ b/andlabs/action.go
@@ -0,0 +1,265 @@
+package main
+
+import (
+ "strconv"
+ "github.com/andlabs/ui"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func (n *node) show(b bool) {
+ if n.tk == nil {
+ return
+ }
+ if n.tk.uiControl == nil {
+ return
+ }
+ if (b) {
+ n.tk.uiControl.Show()
+ } else {
+ n.tk.uiControl.Hide()
+ }
+}
+
+func (n *node) enable(b bool) {
+ if n == nil {
+ panic("WHAT? enable was passed nil. How does this even happen?")
+ }
+ if n.tk == nil {
+ return
+ }
+ if n.tk.uiControl == nil {
+ return
+ }
+ if (b) {
+ n.tk.uiControl.Enable()
+ } else {
+ n.tk.uiControl.Disable()
+ }
+}
+
+func (n *node) pad(at toolkit.ActionType) {
+ log(logInfo, "pad() on WidgetId =", n.WidgetId)
+
+ t := n.tk
+ if (t == nil) {
+ log(logError, "pad() toolkit struct == nil. for", n.WidgetId)
+ return
+ }
+
+ switch n.WidgetType {
+ case toolkit.Group:
+ switch at {
+ case toolkit.Margin:
+ t.uiGroup.SetMargined(true)
+ case toolkit.Unmargin:
+ t.uiGroup.SetMargined(false)
+ case toolkit.Pad:
+ t.uiGroup.SetMargined(true)
+ case toolkit.Unpad:
+ t.uiGroup.SetMargined(false)
+ }
+ case toolkit.Tab:
+ switch at {
+ case toolkit.Margin:
+ tabSetMargined(t.uiTab, true)
+ case toolkit.Unmargin:
+ tabSetMargined(t.uiTab, false)
+ case toolkit.Pad:
+ tabSetMargined(t.uiTab, true)
+ case toolkit.Unpad:
+ tabSetMargined(t.uiTab, false)
+ }
+ case toolkit.Window:
+ switch at {
+ case toolkit.Margin:
+ t.uiWindow.SetMargined(true)
+ case toolkit.Unmargin:
+ t.uiWindow.SetMargined(false)
+ case toolkit.Pad:
+ t.uiWindow.SetBorderless(false)
+ case toolkit.Unpad:
+ t.uiWindow.SetBorderless(true)
+ }
+ case toolkit.Grid:
+ switch at {
+ case toolkit.Margin:
+ t.uiGrid.SetPadded(true)
+ case toolkit.Unmargin:
+ t.uiGrid.SetPadded(false)
+ case toolkit.Pad:
+ t.uiGrid.SetPadded(true)
+ case toolkit.Unpad:
+ t.uiGrid.SetPadded(false)
+ }
+ case toolkit.Box:
+ switch at {
+ case toolkit.Margin:
+ t.uiBox.SetPadded(true)
+ case toolkit.Unmargin:
+ t.uiBox.SetPadded(false)
+ case toolkit.Pad:
+ t.uiBox.SetPadded(true)
+ case toolkit.Unpad:
+ t.uiBox.SetPadded(false)
+ }
+ case toolkit.Textbox:
+ log(debugError, "TODO: implement ActionType =", at)
+ default:
+ log(debugError, "TODO: implement pad() for", at)
+ }
+}
+
+func (n *node) move(newParent *node) {
+ p := n.parent
+
+ switch p.WidgetType {
+ case toolkit.Group:
+ case toolkit.Tab:
+ // tabSetMargined(tParent.uiTab, true)
+ case toolkit.Window:
+ // t.uiWindow.SetBorderless(false)
+ case toolkit.Grid:
+ // t.uiGrid.SetPadded(true)
+ case toolkit.Box:
+ log(logInfo, "TODO: move() where =", p.ParentId)
+ log(logInfo, "TODO: move() for widget =", n.WidgetId)
+
+ stretchy = true
+ if (p.tk.uiBox != nil) {
+ p.tk.uiBox.Append(n.tk.uiControl, stretchy)
+ }
+ // log(debugNow, "is there a tParent parent? =", tParent.parent)
+ // tParent.uiBox.Delete(0)
+
+ // this didn't work:
+ // tWidget.uiControl.Disable()
+ // sleep(.8)
+ default:
+ log(logError, "TODO: need to implement move() for type =", n.WidgetType)
+ log(logError, "TODO: need to implement move() for where =", p.ParentId)
+ log(logError, "TODO: need to implement move() for widget =", n.WidgetId)
+ }
+}
+
+func (n *node) Delete() {
+ p := n.parent
+ log(debugNow, "uiDelete()", n.WidgetId, "to", p.WidgetId)
+
+ switch p.WidgetType {
+ case toolkit.Group:
+ // tParent.uiGroup.SetMargined(true)
+ case toolkit.Tab:
+ // tabSetMargined(tParent.uiTab, true)
+ case toolkit.Window:
+ // t.uiWindow.SetBorderless(false)
+ case toolkit.Grid:
+ // t.uiGrid.SetPadded(true)
+ case toolkit.Box:
+ log(debugNow, "tWidget.boxC =", p.Name)
+ log(debugNow, "is there a tParent parent? =", p.parent)
+ if (p.tk.boxC < 1) {
+ log(debugNow, "Can not delete from Box. already empty. tWidget.boxC =", p.tk.boxC)
+ return
+ }
+ p.tk.uiBox.Delete(0)
+ p.tk.boxC -= 1
+
+ // this didn't work:
+ // tWidget.uiControl.Disable()
+ // sleep(.8)
+ // tParent.uiBox.Append(tWidget.uiControl, stretchy)
+ default:
+ log(debugError, "TODO: need to implement uiDelete() for widget =", n.WidgetId, n.WidgetType)
+ log(debugError, "TODO: need to implement uiDelete() for parent =", p.WidgetId, p.WidgetType)
+ }
+}
+
+func rawAction(a *toolkit.Action) {
+ log(logInfo, "rawAction() START a.ActionType =", a.ActionType)
+ log(logInfo, "rawAction() START a.S =", a.S)
+
+ if (a.ActionType == toolkit.InitToolkit) {
+ // TODO: make sure to only do this once
+ // go uiMain.Do(func() {
+ // ui.Main(demoUI)
+ // go catchActionChannel()
+ // })
+ // try doing this on toolkit load in init()
+ return
+ }
+
+ log(logInfo, "rawAction() START a.WidgetId =", a.WidgetId, "a.ParentId =", a.ParentId)
+ switch a.WidgetType {
+ case toolkit.Flag:
+ flag(a)
+ return
+ }
+
+ n := me.rootNode.findWidgetId(a.WidgetId)
+
+ if (a.ActionType == toolkit.Add) {
+ ui.QueueMain(func() {
+ add(a)
+ })
+ // TODO: remove this artificial delay
+ // sleep(.001)
+ return
+ }
+
+ if (a.ActionType == toolkit.Dump) {
+ log(debugNow, "rawAction() Dump =", a.ActionType, a.WidgetType, n.Name)
+ me.rootNode.listChildren(true)
+ return
+ }
+
+ if (n == nil) {
+ me.rootNode.listChildren(true)
+ log(true, "rawAction() ERROR findWidgetId found nil", a.ActionType, a.WidgetType)
+ log(true, "rawAction() ERROR findWidgetId found nil for id =", a.WidgetId)
+ log(true, "rawAction() ERROR findWidgetId found nil", a.ActionType, a.WidgetType)
+ log(true, "rawAction() ERROR findWidgetId found nil for id =", a.WidgetId)
+ return
+ panic("findWidgetId found nil for id = " + strconv.Itoa(a.WidgetId))
+ }
+
+ switch a.ActionType {
+ case toolkit.Show:
+ n.show(true)
+ case toolkit.Hide:
+ n.show(false)
+ case toolkit.Enable:
+ n.enable(true)
+ case toolkit.Disable:
+ n.enable(false)
+ case toolkit.Get:
+ n.setText(a)
+ case toolkit.GetText:
+ switch a.WidgetType {
+ case toolkit.Textbox:
+ a.S = n.S
+ }
+ case toolkit.Set:
+ n.setText(a)
+ case toolkit.SetText:
+ n.setText(a)
+ case toolkit.AddText:
+ n.setText(a)
+ case toolkit.Margin:
+ n.pad(toolkit.Unmargin)
+ case toolkit.Unmargin:
+ n.pad(toolkit.Margin)
+ case toolkit.Pad:
+ n.pad(toolkit.Pad)
+ case toolkit.Unpad:
+ n.pad(toolkit.Unpad)
+ case toolkit.Delete:
+ n.Delete()
+ case toolkit.Move:
+ log(debugNow, "rawAction() attempt to move() =", a.ActionType, a.WidgetType)
+ newParent := me.rootNode.findWidgetId(a.ParentId)
+ n.move(newParent)
+ default:
+ log(debugError, "rawAction() Unknown =", a.ActionType, a.WidgetType)
+ }
+ log(logInfo, "rawAction() END =", a.ActionType, a.WidgetType)
+}
diff --git a/andlabs/add.go b/andlabs/add.go
new file mode 100644
index 0000000..d78101f
--- /dev/null
+++ b/andlabs/add.go
@@ -0,0 +1,157 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func actionDump(b bool, a *toolkit.Action) {
+ log(b, "actionDump() Widget.Type =", a.ActionType)
+ log(b, "actionDump() Widget.S =", a.S)
+ log(b, "actionDump() Widget.I =", a.I)
+ log(b, "actionDump() WidgetId =", a.WidgetId)
+ log(b, "actionDump() ParentId =", a.ParentId)
+}
+
+func add(a *toolkit.Action) {
+ if (a.WidgetType == toolkit.Root) {
+ me.rootNode = addNode(a)
+ return
+ }
+ n := addNode(a)
+
+ p := n.parent
+ switch n.WidgetType {
+ case toolkit.Window:
+ newWindow(n)
+ return
+ case toolkit.Tab:
+ p.newTab(n)
+ return
+ case toolkit.Label:
+ p.newLabel(n)
+ return
+ case toolkit.Button:
+ p.newButton(n)
+ return
+ case toolkit.Grid:
+ p.newGrid(n)
+ return
+ case toolkit.Checkbox:
+ p.newCheckbox(n)
+ return
+ case toolkit.Spinner:
+ p.newSpinner(n)
+ return
+ case toolkit.Slider:
+ p.newSlider(n)
+ return
+ case toolkit.Dropdown:
+ p.newDropdown(n)
+ return
+ case toolkit.Combobox:
+ p.newCombobox(n)
+ return
+ case toolkit.Textbox:
+ p.newTextbox(n)
+ return
+ case toolkit.Group:
+ p.newGroup(n)
+ return
+ case toolkit.Box:
+ p.newBox(n)
+ return
+ case toolkit.Image:
+ p.newImage(n)
+ return
+ default:
+ log(debugError, "add() error TODO: ", n.WidgetType, n.Name)
+ }
+}
+
+// This routine is very specific to this toolkit
+// It's annoying and has to be copied to each widget when there are changes
+// it could be 'simplfied' maybe or made to be more generic, but this is as far as I've gotten
+// it's probably not worth working much more on this toolkit, the andlabs/ui has been great and got me here!
+// but it's time to write direct GTK, QT, macos and windows toolkit plugins
+// -- jcarr 2023/03/09
+
+// Grid numbering examples by (X,Y)
+// ---------
+// -- (1) --
+// -- (2) --
+// ---------
+//
+// -----------------------------
+// -- (1,1) -- (1,2) -- (1,3) --
+// -- (2,1) -- (2,2) -- (2,3) --
+// -----------------------------
+
+// internally for andlabs/ui
+// (x&y flipped and start at zero)
+// -----------------------------
+// -- (0,0) -- (1,0) -- (1,0) --
+// -- (0,1) -- (1,1) -- (1,1) --
+// -----------------------------
+func (p *node) place(n *node) bool {
+ log(logInfo, "place() START", n.WidgetType, n.Name)
+
+ if (p.tk == nil) {
+ log(logError, "p.tk == nil", p.Name, p.ParentId, p.WidgetType, p.tk)
+ log(logError, "n = ", n.Name, n.ParentId, n.WidgetType, n.tk)
+ panic("p.tk == nil")
+ }
+
+ log(logInfo, "place() switch", p.WidgetType)
+ switch p.WidgetType {
+ case toolkit.Grid:
+ log(logInfo, "place() Grid try at Parent X,Y =", n.X, n.Y)
+ n.tk.gridX = n.AtW - 1
+ n.tk.gridY = n.AtH - 1
+ log(logInfo, "place() Grid try at gridX,gridY", n.tk.gridX, n.tk.gridY)
+ // at the very end, subtract 1 from X & Y since andlabs/ui starts counting at zero
+ p.tk.uiGrid.Append(n.tk.uiControl,
+ n.tk.gridX, n.tk.gridY, 1, 1,
+ false, ui.AlignFill, false, ui.AlignFill)
+ return true
+ case toolkit.Group:
+ if (p.tk.uiBox == nil) {
+ p.tk.uiGroup.SetChild(n.tk.uiControl)
+ log(logInfo, "place() hack Group to use this as the box?", n.Name, n.WidgetType)
+ p.tk.uiBox = n.tk.uiBox
+ } else {
+ p.tk.uiBox.Append(n.tk.uiControl, stretchy)
+ }
+ return true
+ case toolkit.Tab:
+ if (p.tk.uiTab == nil) {
+ log(logError, "p.tk.uiTab == nil for n.WidgetId =", n.WidgetId, "p.tk =", p.tk)
+ panic("p.tk.uiTab == nil")
+ }
+ if (n.tk.uiControl == nil) {
+ log(logError, "n.tk.uiControl == nil for n.WidgetId =", n.WidgetId, "n.tk =", n.tk)
+ panic("n.tk.uiControl == nil")
+ }
+ log(logError, "CHECK LOGIC ON THIS. APPENDING directly into a window without a tab")
+ // log(logError, "THIS SHOULD NEVER HAPPEN ??????? trying to place() node=", n.WidgetId, n.Name, n.Text, n.WidgetType)
+ // log(logError, "THIS SHOULD NEVER HAPPEN ??????? trying to place() on parent=", p.WidgetId, p.Name, p.Text, p.WidgetType)
+ // panic("n.tk.uiControl == nil")
+ p.tk.uiTab.Append(n.Text, n.tk.uiControl)
+ p.tk.boxC += 1
+ return true
+ case toolkit.Box:
+ log(logInfo, "place() uiBox =", p.tk.uiBox)
+ log(logInfo, "place() uiControl =", n.tk.uiControl)
+ p.tk.uiBox.Append(n.tk.uiControl, stretchy)
+ p.tk.boxC += 1
+ return true
+ case toolkit.Window:
+ p.tk.uiWindow.SetChild(n.tk.uiControl)
+ return true
+ default:
+ log(debugError, "place() how? Parent =", p.WidgetId, p.WidgetType)
+ }
+ return false
+}
diff --git a/andlabs/box.go b/andlabs/box.go
new file mode 100644
index 0000000..e33b7a1
--- /dev/null
+++ b/andlabs/box.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+// make new Box here
+func (p *node) newBox(n *node) {
+ log(debugToolkit, "newBox()", n.Name)
+
+ newt := new(guiWidget)
+ var box *ui.Box
+
+ log(debugToolkit, "rawBox() create", n.Name)
+
+ if (n.B) {
+ box = ui.NewHorizontalBox()
+ } else {
+ box = ui.NewVerticalBox()
+ }
+ box.SetPadded(padded)
+
+ newt.uiBox = box
+ newt.uiControl = box
+ newt.boxC = 0
+ n.tk = newt
+ p.place(n)
+}
diff --git a/andlabs/button.go b/andlabs/button.go
new file mode 100644
index 0000000..1dbad5c
--- /dev/null
+++ b/andlabs/button.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+func (p *node) newButton(n *node) {
+ log(debugToolkit, "newButton() START", n.Name)
+
+ t := p.tk
+ if (t == nil) {
+ log(debugToolkit, "newButton() toolkit struct == nil. name=", n.Name)
+ return
+ }
+
+ newt := new(guiWidget)
+
+ b := ui.NewButton(n.Text)
+ newt.uiButton = b
+ newt.uiControl = b
+ newt.parent = t
+
+ b.OnClicked(func(*ui.Button) {
+ n.doUserEvent()
+ })
+
+ n.tk = newt
+ p.place(n)
+ log(debugToolkit, "newButton() END", n.Name)
+}
diff --git a/andlabs/checkbox.go b/andlabs/checkbox.go
new file mode 100644
index 0000000..4c37fd6
--- /dev/null
+++ b/andlabs/checkbox.go
@@ -0,0 +1,27 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+func (p *node) newCheckbox(n *node) {
+ newt := new(guiWidget)
+ log(debugToolkit, "newCheckbox()", n.Name, n.WidgetType)
+
+ newt.uiCheckbox = ui.NewCheckbox(n.Text)
+ newt.uiControl = newt.uiCheckbox
+
+ newt.uiCheckbox.OnToggled(func(spin *ui.Checkbox) {
+ n.B = newt.checked()
+ log(debugChange, "val =", n.B)
+ n.doUserEvent()
+ })
+
+ n.tk = newt
+ p.place(n)
+}
+
+func (t *guiWidget) checked() bool {
+ return t.uiCheckbox.Checked()
+}
diff --git a/andlabs/combobox.go b/andlabs/combobox.go
new file mode 100644
index 0000000..283a29d
--- /dev/null
+++ b/andlabs/combobox.go
@@ -0,0 +1,41 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+func (p *node) newCombobox(n *node) {
+ newt := new(guiWidget)
+ log(debugToolkit, "newCombobox() START", n.Name)
+
+ cb := ui.NewEditableCombobox()
+ newt.uiEditableCombobox = cb
+ newt.uiControl = cb
+
+ // initialize the index
+ newt.c = 0
+ newt.val = make(map[int]string)
+
+ cb.OnChanged(func(spin *ui.EditableCombobox) {
+ n.S = spin.Text()
+ n.doUserEvent()
+ })
+
+ n.tk = newt
+ p.place(n)
+}
+
+func (t *guiWidget) AddComboboxName(title string) {
+ t.uiEditableCombobox.Append(title)
+ if (t.val == nil) {
+ log(debugToolkit, "make map didn't work")
+ return
+ }
+ t.val[t.c] = title
+
+ // If this is the first menu added, set the dropdown to it
+ // if (t.c == 0) {
+ // }
+ t.c = t.c + 1
+}
diff --git a/andlabs/common.go b/andlabs/common.go
new file mode 120000
index 0000000..35417a1
--- /dev/null
+++ b/andlabs/common.go
@@ -0,0 +1 @@
+../nocui/common.go \ No newline at end of file
diff --git a/andlabs/debug.go b/andlabs/debug.go
new file mode 100644
index 0000000..c2ab2a2
--- /dev/null
+++ b/andlabs/debug.go
@@ -0,0 +1,168 @@
+package main
+
+import (
+ "strconv"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+var defaultBehavior bool = true
+
+var bookshelf bool // do you want things arranged in the box like a bookshelf or a stack?
+var canvas bool // if set to true, the windows are a raw canvas
+var menubar bool // for windows
+var stretchy bool // expand things like buttons to the maximum size
+var padded bool // add space between things like buttons
+var margin bool // add space around the frames of windows
+
+var debugToolkit bool = false
+var debugChange bool = false
+var debugPlugin bool = false
+var debugAction bool = false
+var debugFlags bool = false
+var debugGrid bool = false
+var debugNow bool = true
+var debugError bool = true
+
+// This is important. This sets the defaults for the gui. Without this, there isn't correct padding, etc
+func setDefaultBehavior(s bool) {
+ defaultBehavior = s
+ if (defaultBehavior) {
+ log(debugToolkit, "Setting this toolkit to use the default behavior.")
+ log(debugToolkit, "This is the 'guessing' part as defined by the wit/gui 'Principles'. Refer to the docs.")
+ stretchy = false
+ padded = true
+ menubar = true
+ margin = true
+ canvas = false
+ bookshelf = true // 99% of the time, things make a vertical stack of objects
+ } else {
+ log(debugToolkit, "This toolkit is set to ignore the default behavior.")
+ }
+}
+
+func ShowDebug () {
+ log(true, "debugToolkit =", debugToolkit)
+ log(true, "debugChange =", debugChange)
+ log(true, "debugAction =", debugPlugin)
+ log(true, "debugFlags =", debugFlags)
+ log(true, "debugNow =", debugNow)
+ log(true, "debugError =", debugError)
+}
+
+func (t *guiWidget) Dump(b bool) {
+ if ! b {
+ return
+ }
+ log(b, "Name = ", t.Width, t.Height)
+ if (t.uiBox != nil) {
+ log(b, "uiBox =", t.uiBox)
+ }
+ if (t.uiButton != nil) {
+ log(b, "uiButton =", t.uiButton)
+ }
+ if (t.uiCombobox != nil) {
+ log(b, "uiCombobox =", t.uiCombobox)
+ }
+ if (t.uiWindow != nil) {
+ log(b, "uiWindow =", t.uiWindow)
+ }
+ if (t.uiTab != nil) {
+ log(b, "uiTab =", t.uiTab)
+ }
+ if (t.uiGroup != nil) {
+ log(b, "uiGroup =", t.uiGroup)
+ }
+ if (t.uiEntry != nil) {
+ log(b, "uiEntry =", t.uiEntry)
+ }
+ if (t.uiMultilineEntry != nil) {
+ log(b, "uiMultilineEntry =", t.uiMultilineEntry)
+ }
+ if (t.uiSlider != nil) {
+ log(b, "uiSlider =", t.uiSlider)
+ }
+ if (t.uiCheckbox != nil) {
+ log(b, "uiCheckbox =", t.uiCheckbox)
+ }
+}
+
+/*
+func GetDebugToolkit () bool {
+ return debugToolkit
+}
+*/
+
+func flag(a *toolkit.Action) {
+ // should set the checkbox to this value
+ switch a.S {
+ case "Quiet":
+ logInfo = a.B
+ logVerbose = a.B
+ logWarn = a.B
+ logError = a.B
+ case "Error":
+ logError = a.B
+ case "Info":
+ logInfo = a.B
+ case "Verbose":
+ logInfo = a.B
+ logVerbose = a.B
+ logWarn = a.B
+ logError = a.B
+ debugToolkit = a.B
+ debugChange = a.B
+ debugPlugin = a.B
+ debugFlags = a.B
+ case "Toolkit":
+ debugToolkit = a.B
+ case "Change":
+ debugChange = a.B
+ case "Plugin":
+ debugPlugin = a.B
+ case "Flags":
+ debugFlags = a.B
+ case "Now":
+ debugNow = a.B
+ case "Show":
+ ShowDebug()
+ default:
+ log(debugError, "Can't set unknown flag", a.S)
+ }
+}
+
+func (n *node) dumpWidget(b bool) {
+ var info, d string
+
+ if (n == nil) {
+ log(debugError, "dumpWidget() node == nil")
+ return
+ }
+ info = n.WidgetType.String()
+
+ d = strconv.Itoa(n.WidgetId) + " " + info + " " + n.Name
+
+ var tabs string
+ for i := 0; i < listChildrenDepth; i++ {
+ tabs = tabs + defaultPadding
+ }
+ log(b, tabs + d)
+}
+
+var defaultPadding string = " "
+var listChildrenDepth int = 0
+
+func (n *node) listChildren(dump bool) {
+ if (n == nil) {
+ return
+ }
+
+ n.dumpWidget(dump)
+ if len(n.children) == 0 {
+ return
+ }
+ for _, child := range n.children {
+ listChildrenDepth += 1
+ child.listChildren(dump)
+ listChildrenDepth -= 1
+ }
+}
diff --git a/andlabs/delete.go b/andlabs/delete.go
new file mode 100644
index 0000000..591d75e
--- /dev/null
+++ b/andlabs/delete.go
@@ -0,0 +1,51 @@
+package main
+
+// if you include more than just this import
+// then your plugin might be doing something un-ideal (just a guess from 2023/02/27)
+import "go.wit.com/gui/gui/toolkit"
+
+// delete the child widget from the parent
+// p = parent, c = child
+func (n *node) destroy() {
+ pId := n.parent.WidgetId
+ cId := n.WidgetId
+ log(logNow, "delete()", pId, cId)
+
+ pt := n.parent.tk
+ ct := n.tk
+ if (ct == nil) {
+ log(true, "delete FAILED (ct = mapToolkit[c] == nil) for c", pId, cId)
+ // this pukes out a whole universe of shit
+ // listMap()
+ return
+ }
+
+ switch n.WidgetType {
+ case toolkit.Button:
+ log(true, "Should delete Button here:", n.Name)
+ log(true, "Parent:")
+ pt.Dump(true)
+ log(true, "Child:")
+ ct.Dump(true)
+ if (pt.uiBox == nil) {
+ log(true, "Don't know how to destroy this")
+ } else {
+ log(true, "Fuck it, destroy the whole box", n.parent.Name)
+ // pt.uiBox.Destroy() // You have a bug: You cannot destroy a uiControl while it still has a parent.
+ pt.uiBox.SetPadded(false)
+ pt.uiBox.Delete(4)
+ ct.uiButton.Disable()
+ // ct.uiButton.Hide()
+ ct.uiButton.Destroy()
+ }
+
+ case toolkit.Window:
+ log(true, "Should delete Window here:", n.Name)
+ default:
+ log(true, "Fuckit, let's destroy a button")
+ if (ct.uiButton != nil) {
+ pt.uiBox.Delete(4)
+ ct.uiButton.Destroy()
+ }
+ }
+}
diff --git a/andlabs/demo.go b/andlabs/demo.go
new file mode 100644
index 0000000..c3cd418
--- /dev/null
+++ b/andlabs/demo.go
@@ -0,0 +1,90 @@
+package main
+
+import "github.com/andlabs/ui"
+import _ "github.com/andlabs/ui/winmanifest"
+
+/*
+ This is a code example taken directly from the toolkit andlabs/ui
+
+ This code is here to double check that the toolkit itself still works
+ the same way. This is intended as a sanity check.
+*/
+
+func BlankWindow(w *ui.Window) *ui.Box {
+ hbox := ui.NewHorizontalBox()
+ hbox.SetPadded(true)
+ w.SetChild(hbox)
+ return hbox
+}
+
+func (t *guiWidget) DemoNumbersPage() {
+ var w *ui.Window
+
+ log(debugToolkit, "Starting wit/gui toolkit andlabs/ui DemoNumbersPage()")
+
+ w = t.uiWindow
+ t.uiBox = makeNumbersPage()
+ t.uiBox.SetPadded(true)
+ w.SetChild(t.uiBox)
+ w.SetTitle("Internal demo of andlabs/ui toolkit")
+}
+
+func makeNumbersPage() *ui.Box {
+ hbox := ui.NewHorizontalBox()
+ hbox.SetPadded(true)
+
+ group := ui.NewGroup("Numbers")
+ group.SetMargined(true)
+ hbox.Append(group, true)
+
+ vbox := ui.NewVerticalBox()
+ vbox.SetPadded(true)
+ group.SetChild(vbox)
+
+ spinbox := ui.NewSpinbox(0, 100)
+ slider := ui.NewSlider(0, 100)
+ pbar := ui.NewProgressBar()
+ spinbox.OnChanged(func(*ui.Spinbox) {
+ slider.SetValue(spinbox.Value())
+ pbar.SetValue(spinbox.Value())
+ })
+ slider.OnChanged(func(*ui.Slider) {
+ spinbox.SetValue(slider.Value())
+ pbar.SetValue(slider.Value())
+ })
+ vbox.Append(spinbox, false)
+ vbox.Append(slider, false)
+ vbox.Append(pbar, false)
+
+ ip := ui.NewProgressBar()
+ ip.SetValue(-1)
+ vbox.Append(ip, false)
+
+ group = ui.NewGroup("Lists")
+ group.SetMargined(true)
+ hbox.Append(group, true)
+
+ vbox = ui.NewVerticalBox()
+ vbox.SetPadded(true)
+ group.SetChild(vbox)
+
+ cbox := ui.NewCombobox()
+ cbox.Append("Combobox Item 1")
+ cbox.Append("Combobox Item 2")
+ cbox.Append("Combobox Item 3")
+ vbox.Append(cbox, false)
+
+ ecbox := ui.NewEditableCombobox()
+ ecbox.Append("Editable Item 1")
+ ecbox.Append("Editable Item 2")
+ ecbox.Append("Editable Item 3")
+ vbox.Append(ecbox, false)
+
+ rb := ui.NewRadioButtons()
+ rb.Append("Radio Button 1")
+ rb.Append("Radio Button 2")
+ rb.Append("Radio Button 3")
+ vbox.Append(rb, false)
+
+ return hbox
+}
diff --git a/andlabs/dropdown.go b/andlabs/dropdown.go
new file mode 100644
index 0000000..89cbf1a
--- /dev/null
+++ b/andlabs/dropdown.go
@@ -0,0 +1,79 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func (p *node) newDropdown(n *node) {
+ newt := new(guiWidget)
+ log(debugToolkit, "gui.Toolbox.newDropdown() START", n.Name)
+
+ cb := ui.NewCombobox()
+ newt.uiCombobox = cb
+ newt.uiControl = cb
+
+ // initialize the index
+ newt.c = 0
+ newt.val = make(map[int]string)
+
+ cb.OnSelected(func(spin *ui.Combobox) {
+ i := spin.Selected()
+ if (newt.val == nil) {
+ log(logError, "make map didn't work")
+ n.S = "map did not work. ui.Combobox error"
+ } else {
+ n.S = newt.val[i]
+ }
+ n.doUserEvent()
+ })
+
+ n.tk = newt
+ p.place(n)
+}
+
+func (t *guiWidget) addDropdownName(title string) {
+ t.uiCombobox.Append(title)
+ if (t.val == nil) {
+ log(debugToolkit, "make map didn't work")
+ return
+ }
+ t.val[t.c] = title
+
+ // If this is the first menu added, set the dropdown to it
+ if (t.c == 0) {
+ log(debugChange, "THIS IS THE FIRST Dropdown", title)
+ t.uiCombobox.SetSelected(0)
+ }
+ t.c = t.c + 1
+}
+
+func (t *guiWidget) SetDropdown(i int) {
+ t.uiCombobox.SetSelected(i)
+}
+
+func (n *node) AddDropdownName(s string) {
+ log(logInfo, "AddDropdownName()", n.WidgetId, "add:", s)
+
+ t := n.tk
+ if (t == nil) {
+ log(logInfo, "AddDropdownName() toolkit struct == nil. name=", n.Name, s)
+ return
+ }
+ t.addDropdownName(s)
+}
+
+func (n *node) SetDropdownName(a *toolkit.Action, s string) {
+ log(logInfo, "SetDropdown()", n.WidgetId, ",", s)
+
+ t := n.tk
+ if (t == nil) {
+ log(debugError, "SetDropdown() FAILED mapToolkits[w] == nil. name=", n.WidgetId, s)
+ return
+ }
+ t.SetDropdown(1)
+ // TODO: send back to wit/gui goroutine with the chan
+ n.S = s
+}
diff --git a/andlabs/grid.go b/andlabs/grid.go
new file mode 100644
index 0000000..6c47d1b
--- /dev/null
+++ b/andlabs/grid.go
@@ -0,0 +1,25 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+// Grid numbering by (X,Y)
+// -----------------------------
+// -- (1,1) -- (2,1) -- (3,1) --
+// -- (1,2) -- (2,1) -- (3,1) --
+// -----------------------------
+func (p *node) newGrid(n *node) {
+ var newt *guiWidget
+ log(debugToolkit, "newGrid()", n.WidgetId, "to", n.ParentId)
+
+ newt = new(guiWidget)
+
+ c := ui.NewGrid()
+ newt.uiGrid = c
+ newt.uiControl = c
+
+ n.tk = newt
+ p.place(n)
+}
diff --git a/andlabs/group.go b/andlabs/group.go
new file mode 100644
index 0000000..b7c450e
--- /dev/null
+++ b/andlabs/group.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+func (p *node) newGroup(n *node) {
+ log(debugToolkit, "NewGroup()", n.Name)
+
+ newt := new(guiWidget)
+
+ log(debugToolkit, "NewGroup() create", n.Name)
+
+ g := ui.NewGroup(n.Name)
+ g.SetMargined(margin)
+ newt.uiGroup = g
+ newt.uiControl = g
+
+ n.tk = newt
+ p.place(n)
+}
diff --git a/andlabs/icon.go b/andlabs/icon.go
new file mode 100644
index 0000000..00c25f6
--- /dev/null
+++ b/andlabs/icon.go
@@ -0,0 +1,27 @@
+package main
+
+var rawImage = []byte{
+ 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
+ 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10,
+ 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00,
+ 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
+ 0x00, 0xca, 0x49, 0x44, 0x41, 0x54, 0x38, 0x11, 0xa5, 0x93, 0xb1, 0x0d,
+ 0xc2, 0x40, 0x0c, 0x45, 0x1d, 0xc4, 0x14, 0x0c, 0x12, 0x41, 0x0f, 0x62,
+ 0x12, 0x46, 0x80, 0x8a, 0x2e, 0x15, 0x30, 0x02, 0x93, 0x20, 0x68, 0x11,
+ 0x51, 0x06, 0x61, 0x0d, 0x88, 0x2d, 0x7f, 0xdb, 0x07, 0x87, 0x08, 0xdc,
+ 0x49, 0x91, 0x7d, 0xf6, 0xf7, 0xf3, 0x4f, 0xa4, 0x54, 0xbb, 0xeb, 0xf6,
+ 0x41, 0x05, 0x67, 0xcc, 0xb3, 0x9b, 0xfa, 0xf6, 0x17, 0x62, 0xdf, 0xcd,
+ 0x48, 0x00, 0x32, 0xbd, 0xa8, 0x1d, 0x72, 0xee, 0x3c, 0x47, 0x16, 0xfb,
+ 0x5c, 0x53, 0x8d, 0x03, 0x30, 0x14, 0x84, 0xf7, 0xd5, 0x89, 0x26, 0xc7,
+ 0x25, 0x10, 0x36, 0xe4, 0x05, 0xa2, 0x51, 0xbc, 0xc4, 0x1c, 0xc3, 0x1c,
+ 0xed, 0x30, 0x1c, 0x8f, 0x16, 0x3f, 0x02, 0x78, 0x33, 0x20, 0x06, 0x60,
+ 0x97, 0x70, 0xaa, 0x45, 0x7f, 0x85, 0x60, 0x5d, 0xb6, 0xf4, 0xc2, 0xc4,
+ 0x3e, 0x0f, 0x44, 0xcd, 0x1b, 0x20, 0x90, 0x0f, 0xed, 0x85, 0xa8, 0x55,
+ 0x05, 0x42, 0x43, 0xb4, 0x9e, 0xce, 0x71, 0xb3, 0xe8, 0x0e, 0xb4, 0xc4,
+ 0xc3, 0x39, 0x21, 0xb7, 0x73, 0xbd, 0xe4, 0x1b, 0xe4, 0x04, 0xb6, 0xaa,
+ 0x4f, 0x18, 0x2c, 0xee, 0x42, 0x31, 0x01, 0x84, 0xfa, 0xe0, 0xd4, 0x00,
+ 0xdf, 0xb6, 0x83, 0xf8, 0xea, 0xc2, 0x00, 0x10, 0xfc, 0x1a, 0x05, 0x30,
+ 0x74, 0x3b, 0xe0, 0xd1, 0x45, 0xb1, 0x83, 0xaa, 0xf4, 0x77, 0x7e, 0x02,
+ 0x87, 0x1f, 0x42, 0x7f, 0x9e, 0x2b, 0xe8, 0xdf, 0x00, 0x00, 0x00, 0x00,
+ 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
+}
diff --git a/andlabs/image.go b/andlabs/image.go
new file mode 100644
index 0000000..bab1a9e
--- /dev/null
+++ b/andlabs/image.go
@@ -0,0 +1,50 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+// make new Image using andlabs/ui
+func (p *node) newImage(n *node) {
+ newt := new(guiWidget)
+ var img *ui.Image
+
+ log(debugToolkit, "rawImage() create", n.Name)
+
+ img = ui.NewImage(16, 16)
+
+ newt.uiImage = img
+ // newt.uiControl = img
+
+ n.tk = newt
+ p.place(n)
+}
+/*
+ if (a.Name == "image") {
+ log(true, "NewTextbox() trying to add a new image")
+ i := ui.NewImage(16, 16)
+ img, _, err := image.Decode(bytes.NewReader(rawImage))
+ if err != nil {
+ panic(err)
+ }
+ nr, ok := img.(*image.RGBA)
+ if !ok {
+ i2 := image.NewRGBA(img.Bounds())
+ draw.Draw(i2, i2.Bounds(), img, img.Bounds().Min, draw.Src)
+ nr = i2
+ }
+ i.Append(nr)
+ t.uiBox.Append(i, true)
+
+ var img *ui.Image
+ var icon []byte
+ var imgA image.Image
+
+ icon, _ = res.ReadFile("resources/ping6.working.png")
+ // imgA, _, err := image.Decode(bytes.NewReader(b))
+ imgA, _, _ = image.Decode(icon)
+ img.Append(imgA)
+ img.Append(icon)
+ }
+*/
diff --git a/andlabs/label.go b/andlabs/label.go
new file mode 100644
index 0000000..3f06546
--- /dev/null
+++ b/andlabs/label.go
@@ -0,0 +1,18 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+func (p *node) newLabel(n *node) {
+ log(logInfo, "NewLabel()", n.Name)
+
+ newt := new(guiWidget)
+ c := ui.NewLabel(n.Name)
+ newt.uiLabel = c
+ newt.uiControl = c
+
+ n.tk = newt
+ p.place(n)
+}
diff --git a/andlabs/log.go b/andlabs/log.go
new file mode 100644
index 0000000..5f5b81b
--- /dev/null
+++ b/andlabs/log.go
@@ -0,0 +1,24 @@
+package main
+
+import (
+ witlog "go.wit.com/log"
+)
+
+// various debugging flags
+var logNow bool = true // useful for active development
+var logError bool = true
+var logWarn bool = true
+var logInfo bool = false
+var logVerbose bool = false
+
+func log(b bool, a ...any) {
+ witlog.Log(b, a...)
+}
+
+func sleep(a ...any) {
+ witlog.Sleep(a...)
+}
+
+func exit(a ...any) {
+ witlog.Exit(a...)
+}
diff --git a/andlabs/main.go b/andlabs/main.go
new file mode 100644
index 0000000..c1fc7ac
--- /dev/null
+++ b/andlabs/main.go
@@ -0,0 +1,57 @@
+package main
+
+import (
+ "sync"
+ "go.wit.com/gui/gui/toolkit"
+
+ "github.com/andlabs/ui"
+ // the _ means we only need this for the init()
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+var uiMainUndef bool = true
+var uiMain sync.Once
+var muAction sync.Mutex
+
+func catchActionChannel() {
+ log(logInfo, "catchActionChannel() START")
+ for {
+ log(logInfo, "catchActionChannel() for loop")
+ select {
+ case a := <-pluginChan:
+ log(logInfo, "catchActionChannel() SELECT widget id =", a.WidgetId, a.Name)
+ log(logInfo, "catchActionChannel() STUFF", a.WidgetId, a.ActionType, a.WidgetType)
+ muAction.Lock()
+ // TODO ui.QueueMain(f)
+ // TODO ui.QueueMain( func() {rawAction(a)} )
+ ui.QueueMain( func() {rawAction(&a)} )
+ // rawAction(a)
+ muAction.Unlock()
+ log(logInfo, "catchActionChannel() STUFF END", a.WidgetId, a.ActionType, a.WidgetType)
+ }
+ }
+}
+
+// This is important. This sets the defaults for the gui. Without this, there isn't correct padding, etc
+func init() {
+ log(logNow, "Init() START")
+ log(debugToolkit, "Init()")
+ // Can you pass values to a plugin init() ? Otherwise, there is no way to safely print
+ // log(debugToolkit, "init() Setting defaultBehavior = true")
+ setDefaultBehavior(true)
+
+
+ // TODO: this is messed up. run ui.Main() from the first add? Initialize it with an empty thing first?
+ // fake out the OS toolkit by making a fake window. This is probably needed for macos & windows
+ // actually, this probably breaks the macos build
+ go ui.Main(func() {
+ demoUI()
+ })
+
+ // andlabs = make(map[int]*andlabsT)
+ pluginChan = make(chan toolkit.Action, 1)
+
+ log(logNow, "Init() start channel reciever")
+ go catchActionChannel()
+ log(logNow, "Init() END")
+}
diff --git a/andlabs/setText.go b/andlabs/setText.go
new file mode 100644
index 0000000..7452982
--- /dev/null
+++ b/andlabs/setText.go
@@ -0,0 +1,128 @@
+package main
+
+import (
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func (n *node) setText(a *toolkit.Action) {
+ log(debugChange, "setText() START with a.S =", a.S)
+ t := n.tk
+ if (t == nil) {
+ log(debugError, "setText error. tk == nil", n.Name, n.WidgetId)
+ actionDump(debugError, a)
+ return
+ }
+ log(debugChange, "setText() Attempt on", n.WidgetType, "with", a.S)
+
+ switch n.WidgetType {
+ case toolkit.Window:
+ t.uiWindow.SetTitle(a.S)
+ case toolkit.Tab:
+ case toolkit.Group:
+ t.uiGroup.SetTitle(a.S)
+ case toolkit.Checkbox:
+ switch a.ActionType {
+ case toolkit.SetText:
+ t.uiCheckbox.SetText(a.S)
+ case toolkit.Get:
+ n.B = t.uiCheckbox.Checked()
+ case toolkit.Set:
+ // TODO: commented out while working on chan
+ t.uiCheckbox.SetChecked(a.B)
+ default:
+ log(debugError, "setText() unknown", a.ActionType, "on checkbox", n.Name)
+ }
+ case toolkit.Textbox:
+ switch a.ActionType {
+ case toolkit.Set:
+ if (t.uiEntry != nil) {
+ t.uiEntry.SetText(a.S)
+ }
+ if (t.uiMultilineEntry != nil) {
+ t.uiMultilineEntry.SetText(a.S)
+ }
+ case toolkit.SetText:
+ if (t.uiEntry != nil) {
+ t.uiEntry.SetText(a.S)
+ }
+ if (t.uiMultilineEntry != nil) {
+ t.uiMultilineEntry.SetText(a.S)
+ }
+ default:
+ log(debugError, "setText() unknown", a.ActionType, "on checkbox", n.Name)
+ }
+ case toolkit.Label:
+ t.uiLabel.SetText(a.S)
+ case toolkit.Button:
+ t.uiButton.SetText(a.S)
+ case toolkit.Slider:
+ switch a.ActionType {
+ case toolkit.Get:
+ n.I = t.uiSlider.Value()
+ case toolkit.Set:
+ t.uiSlider.SetValue(a.I)
+ default:
+ log(debugError, "setText() unknown", a.ActionType, "on checkbox", n.Name)
+ }
+ case toolkit.Spinner:
+ switch a.ActionType {
+ case toolkit.Get:
+ n.I = t.uiSpinbox.Value()
+ case toolkit.Set:
+ t.uiSpinbox.SetValue(a.I)
+ default:
+ log(debugError, "setText() unknown", a.ActionType, "on checkbox", n.Name)
+ }
+ case toolkit.Dropdown:
+ switch a.ActionType {
+ case toolkit.AddText:
+ n.AddDropdownName(a.S)
+ case toolkit.Set:
+ var orig int
+ var i int = -1
+ var s string
+ orig = t.uiCombobox.Selected()
+ log(debugChange, "try to set the Dropdown to", a.S, "from", orig)
+ // try to find the string
+ for i, s = range t.val {
+ log(debugChange, "i, s", i, s)
+ if (a.S == s) {
+ t.uiCombobox.SetSelected(i)
+ log(debugChange, "setText() Dropdown worked.", n.S)
+ return
+ }
+ }
+ log(debugError, "setText() Dropdown did not find:", a.S)
+ // if i == -1, then there are not any things in the menu to select
+ if (i == -1) {
+ return
+ }
+ // if the string was never set, then set the dropdown to the last thing added to the menu
+ if (orig == -1) {
+ t.uiCombobox.SetSelected(i)
+ }
+ case toolkit.Get:
+ // t.S = t.s
+ case toolkit.GetText:
+ // t.S = t.s
+ default:
+ log(debugError, "setText() unknown", a.ActionType, "on checkbox", n.Name)
+ }
+ case toolkit.Combobox:
+ switch a.ActionType {
+ case toolkit.AddText:
+ t.AddComboboxName(a.S)
+ case toolkit.Set:
+ t.uiEditableCombobox.SetText(a.S)
+ n.S = a.S
+ case toolkit.SetText:
+ t.uiEditableCombobox.SetText(a.S)
+ n.S = a.S
+ default:
+ log(debugError, "setText() unknown", a.ActionType, "on checkbox", n.Name)
+ }
+ default:
+ log(debugError, "plugin Send() Don't know how to setText on", n.WidgetType, "yet", a.ActionType)
+ }
+ log(debugChange, "setText() END with a.S =", a.S)
+}
diff --git a/andlabs/slider.go b/andlabs/slider.go
new file mode 100644
index 0000000..5098943
--- /dev/null
+++ b/andlabs/slider.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+func (p *node) newSlider(n *node) {
+ newt := new(guiWidget)
+
+ s := ui.NewSlider(n.X, n.Y)
+ newt.uiSlider = s
+ newt.uiControl = s
+
+ s.OnChanged(func(spin *ui.Slider) {
+ n.I = newt.uiSlider.Value()
+ n.doUserEvent()
+ })
+
+ n.tk = newt
+ p.place(n)
+}
diff --git a/andlabs/spinner.go b/andlabs/spinner.go
new file mode 100644
index 0000000..bab7a29
--- /dev/null
+++ b/andlabs/spinner.go
@@ -0,0 +1,22 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+func (p *node) newSpinner(n *node) {
+ newt := new(guiWidget)
+
+ s := ui.NewSpinbox(n.X, n.Y)
+ newt.uiSpinbox = s
+ newt.uiControl = s
+
+ s.OnChanged(func(s *ui.Spinbox) {
+ n.I = newt.uiSpinbox.Value()
+ n.doUserEvent()
+ })
+
+ n.tk = newt
+ p.place(n)
+}
diff --git a/andlabs/structs.go b/andlabs/structs.go
new file mode 100644
index 0000000..81c4669
--- /dev/null
+++ b/andlabs/structs.go
@@ -0,0 +1,58 @@
+package main
+
+import "github.com/andlabs/ui"
+import _ "github.com/andlabs/ui/winmanifest"
+
+// var andlabs map[int]*andlabsT
+// var callback func(int) bool
+// var callback chan toolkit.Action
+
+// It's probably a terrible idea to call this 'me'
+var me config
+
+type config struct {
+ rootNode *node // the base of the binary tree. it should have id == 0
+}
+
+// stores the raw toolkit internals
+type guiWidget struct {
+ Width int
+ Height int
+
+ // tw *toolkit.Widget
+ parent *guiWidget
+ children []*guiWidget
+
+ // used to track if a tab has a child widget yet
+ child bool
+
+ uiControl ui.Control
+
+ uiBox *ui.Box
+ uiButton *ui.Button
+ uiCombobox *ui.Combobox
+ uiCheckbox *ui.Checkbox
+ uiEntry *ui.Entry
+ uiGroup *ui.Group
+ uiLabel *ui.Label
+ uiSlider *ui.Slider
+ uiSpinbox *ui.Spinbox
+ uiTab *ui.Tab
+ uiWindow *ui.Window
+ uiMultilineEntry *ui.MultilineEntry
+ uiEditableCombobox *ui.EditableCombobox
+ uiImage *ui.Image
+
+ uiGrid *ui.Grid
+ gridX int
+ gridY int
+
+ // used as a counter to work around limitations of widgets like combobox
+ // this is probably fucked up and in many ways wrong because of unsafe goroutine threading
+ // but it's working for now due to the need for need for a correct interaction layer betten toolkits
+ c int
+ val map[int]string
+
+ // andlabs/ui only accesses widget id numbers
+ boxC int // how many things on in a box or how many tabs
+}
diff --git a/andlabs/tab.go b/andlabs/tab.go
new file mode 100644
index 0000000..d075a51
--- /dev/null
+++ b/andlabs/tab.go
@@ -0,0 +1,116 @@
+package main
+
+import (
+ "go.wit.com/gui/gui/toolkit"
+
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+/*
+ This adds a tab
+
+ andlabs/ui is goofy in the sense that you have to determine
+ if the ui.Window already has a tab in it. If it does, then
+ you need to add this tab and not run SetChild() on the window
+ or instead it replaces the existing tab with the new one
+
+ I work around this by always sending a Toolkit that is a tab
+ once there is one. If you send a Window here, it will replace
+ any existing tabs rather than adding a new one
+*/
+func (p *node) newTab(n *node) {
+ var newt *guiWidget
+
+ if (p == nil) {
+ log(debugError, "newTab() p == nil. how the fuck does this happen?", n.WidgetId, n.ParentId)
+ }
+ if (p.WidgetType != toolkit.Window) {
+ log(debugError, "newTab() uiWindow == nil. I can't add a toolbar without window", n.WidgetId, n.ParentId)
+ return
+ }
+ t := p.tk
+
+ log(debugToolkit, "newTab() START", n.WidgetId, n.ParentId)
+
+ if (t.uiTab == nil) {
+ // this means you have to make a new tab
+ log(debugToolkit, "newTab() GOOD. This should be the first tab:", n.WidgetId, n.ParentId)
+ newt = rawTab(t.uiWindow, n.Text)
+ t.uiTab = newt.uiTab
+ } else {
+ // this means you have to append a tab
+ log(debugToolkit, "newTab() GOOD. This should be an additional tab:", n.WidgetId, n.ParentId)
+ if (n.WidgetType == toolkit.Tab) {
+ // andlabs doesn't have multiple tab widgets so make a fake one?
+ // this makes a guiWidget internal structure with the parent values
+ newt = new(guiWidget)
+ newt.uiWindow = t.uiWindow
+ newt.uiTab = t.uiTab
+ } else {
+ newt = t.appendTab(n.Text)
+ }
+ }
+
+ n.tk = newt
+}
+
+// This sets _all_ the tabs to Margin = true
+//
+// TODO: do proper tab tracking (will be complicated). low priority
+func tabSetMargined(tab *ui.Tab, b bool) {
+ c := tab.NumPages()
+ for i := 0; i < c; i++ {
+ log(debugToolkit, "SetMargined", i, b)
+ tab.SetMargined(i, b)
+ }
+}
+
+func rawTab(w *ui.Window, name string) *guiWidget {
+ var newt guiWidget
+ log(debugToolkit, "rawTab() START", name)
+
+ if (w == nil) {
+ log(debugError, "UiWindow == nil. I can't add a tab without a window")
+ log(debugError, "UiWindow == nil. I can't add a tab without a window")
+ log(debugError, "UiWindow == nil. I can't add a tab without a window")
+ // sleep(1)
+ return nil
+ }
+
+ tab := ui.NewTab()
+ w.SetChild(tab)
+ newt.uiTab = tab
+ newt.uiControl = tab
+ log(debugToolkit, "rawTab() END", name)
+ return &newt
+}
+
+func (t *guiWidget) appendTab(name string) *guiWidget {
+ var newT guiWidget
+ log(debugToolkit, "appendTab() ADD", name)
+
+ if (t.uiTab == nil) {
+ log(debugToolkit, "UiWindow == nil. I can't add a widget without a place to put it")
+ panic("should never have happened. wit/gui/toolkit has ui.Tab == nil")
+ }
+ log(debugToolkit, "appendTab() START name =", name)
+
+ var hbox *ui.Box
+ if (defaultBehavior) {
+ hbox = ui.NewHorizontalBox()
+ } else {
+ if (bookshelf) {
+ hbox = ui.NewHorizontalBox()
+ } else {
+ hbox = ui.NewVerticalBox()
+ }
+ }
+ hbox.SetPadded(padded)
+ t.uiTab.Append(name, hbox)
+
+ newT.uiWindow = t.uiWindow
+ newT.uiTab = t.uiTab
+ newT.uiBox = hbox
+ return &newT
+}
diff --git a/andlabs/textbox.go b/andlabs/textbox.go
new file mode 100644
index 0000000..7cb5d63
--- /dev/null
+++ b/andlabs/textbox.go
@@ -0,0 +1,32 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+func (p *node) newTextbox(n *node) {
+ newt := new(guiWidget)
+
+ if (n.X == 1) {
+ e := ui.NewEntry()
+ newt.uiEntry = e
+ newt.uiControl = e
+
+ e.OnChanged(func(spin *ui.Entry) {
+ n.S = spin.Text()
+ n.doUserEvent()
+ })
+ } else {
+ e := ui.NewNonWrappingMultilineEntry()
+ newt.uiMultilineEntry = e
+ newt.uiControl = e
+
+ e.OnChanged(func(spin *ui.MultilineEntry) {
+ n.S = spin.Text()
+ n.doUserEvent()
+ })
+ }
+ n.tk = newt
+ p.place(n)
+}
diff --git a/andlabs/updateui.go b/andlabs/updateui.go
new file mode 100644
index 0000000..c43e15f
--- /dev/null
+++ b/andlabs/updateui.go
@@ -0,0 +1,97 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+)
+
+// Example showing how to update the UI using the QueueMain function
+// especially if the update is coming from another goroutine
+//
+// see QueueMain in 'main.go' for detailed description
+
+var count int
+
+func demoUI() {
+ mainWindow := ui.NewWindow("libui Updating UI", 640, 480, true)
+ mainWindow.OnClosing(func(*ui.Window) bool {
+ ui.Quit()
+ return true
+ })
+ ui.OnShouldQuit(func() bool {
+ mainWindow.Destroy()
+ return true
+ })
+
+ vbContainer := ui.NewVerticalBox()
+ vbContainer.SetPadded(true)
+
+ inputGroup := ui.NewGroup("Input")
+ inputGroup.SetMargined(true)
+
+ vbInput := ui.NewVerticalBox()
+ vbInput.SetPadded(true)
+
+ inputForm := ui.NewForm()
+ inputForm.SetPadded(true)
+
+ message := ui.NewEntry()
+ message.SetText("Hello World")
+ inputForm.Append("What message do you want to show?", message, false)
+
+ showMessageButton := ui.NewButton("Show message")
+ clearMessageButton := ui.NewButton("Clear message")
+
+ vbInput.Append(inputForm, false)
+ vbInput.Append(showMessageButton, false)
+ vbInput.Append(clearMessageButton, false)
+
+ inputGroup.SetChild(vbInput)
+
+ messageGroup := ui.NewGroup("Message")
+ messageGroup.SetMargined(true)
+
+ vbMessage := ui.NewVerticalBox()
+ vbMessage.SetPadded(true)
+
+ messageLabel := ui.NewLabel("")
+
+ vbMessage.Append(messageLabel, false)
+
+ messageGroup.SetChild(vbMessage)
+
+ countGroup := ui.NewGroup("Counter")
+ countGroup.SetMargined(true)
+
+ vbCounter := ui.NewVerticalBox()
+ vbCounter.SetPadded(true)
+
+ countLabel := ui.NewLabel("blah")
+
+ vbCounter.Append(countLabel, false)
+ countGroup.SetChild(vbCounter)
+
+ vbContainer.Append(inputGroup, false)
+ vbContainer.Append(messageGroup, false)
+ vbContainer.Append(countGroup, false)
+
+ mainWindow.SetChild(vbContainer)
+
+ showMessageButton.OnClicked(func(*ui.Button) {
+ // Update the UI directly as it is called from the main thread
+ messageLabel.SetText(message.Text())
+ })
+
+ clearMessageButton.OnClicked(func(*ui.Button) {
+ // Update the UI directly as it is called from the main thread
+ messageLabel.SetText("")
+ })
+
+ // this is messed up.
+ // mainWindow.Show()
+}
+
+/*
+func main() {
+ ui.Main(setupUI)
+}
+*/
diff --git a/andlabs/widget.go b/andlabs/widget.go
new file mode 100644
index 0000000..cf05a9e
--- /dev/null
+++ b/andlabs/widget.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ "go.wit.com/gui/gui/toolkit"
+)
+
+// this is specific to the nocui toolkit
+func initWidget(n *node) *guiWidget {
+ var w *guiWidget
+ w = new(guiWidget)
+ // Set(w, "default")
+
+ if n.WidgetType == toolkit.Root {
+ log(logInfo, "setupWidget() FOUND ROOT w.id =", n.WidgetId)
+ n.WidgetId = 0
+ me.rootNode = n
+ return w
+ }
+
+ if (n.WidgetType == toolkit.Box) {
+ if (n.B) {
+ n.horizontal = true
+ } else {
+ n.horizontal = false
+ }
+ }
+
+ return w
+}
diff --git a/andlabs/window.go b/andlabs/window.go
new file mode 100644
index 0000000..f51536b
--- /dev/null
+++ b/andlabs/window.go
@@ -0,0 +1,46 @@
+package main
+
+import (
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+func (t *guiWidget) MessageWindow(msg1 string, msg2 string) {
+ ui.MsgBox(t.uiWindow, msg1, msg2)
+}
+
+func (t *guiWidget) ErrorWindow(msg1 string, msg2 string) {
+ ui.MsgBoxError(t.uiWindow, msg1, msg2)
+}
+
+func newWindow(n *node) {
+ var newt *guiWidget
+
+ newt = new(guiWidget)
+
+ // menubar bool is if the OS defined border on the window should be used
+ win := ui.NewWindow(n.Name, n.X, n.Y, menubar)
+ win.SetBorderless(canvas)
+ win.SetMargined(margin)
+ win.OnClosing(func(*ui.Window) bool {
+ n.doUserEvent()
+ return true
+ })
+ newt.uiWindow = win
+ newt.uiControl = win
+
+ n.tk = newt
+ win.Show()
+ return
+}
+
+func (n *node) SetWindowTitle(title string) {
+ log(debugToolkit, "toolkit NewWindow", n.Text, "title", title)
+ win := n.tk.uiWindow
+ if (win == nil) {
+ log(debugError, "Error: no window", n.WidgetId)
+ } else {
+ win.SetTitle(title)
+ log(debugToolkit, "Setting the window title", title)
+ }
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..ef8e734
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,20 @@
+module go.wit.com/toolkits
+
+go 1.21.4
+
+require (
+ github.com/andlabs/ui v0.0.0-20200610043537-70a69d6ae31e
+ github.com/awesome-gocui/gocui v1.1.0
+ go.wit.com/gui v0.9.2
+)
+
+require (
+ github.com/gdamore/encoding v1.0.0 // indirect
+ github.com/gdamore/tcell/v2 v2.4.0 // indirect
+ github.com/lucasb-eyer/go-colorful v1.0.3 // indirect
+ github.com/mattn/go-runewidth v0.0.10 // indirect
+ github.com/rivo/uniseg v0.1.0 // indirect
+ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
+ golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
+ golang.org/x/text v0.3.3 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..936d163
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,24 @@
+github.com/andlabs/ui v0.0.0-20200610043537-70a69d6ae31e h1:wSQCJiig/QkoUnpvelSPbLiZNWvh2yMqQTQvIQqSUkU=
+github.com/andlabs/ui v0.0.0-20200610043537-70a69d6ae31e/go.mod h1:5G2EjwzgZUPnnReoKvPWVneT8APYbyKkihDVAHUi0II=
+github.com/awesome-gocui/gocui v1.1.0 h1:db2j7yFEoHZjpQFeE2xqiatS8bm1lO3THeLwE6MzOII=
+github.com/awesome-gocui/gocui v1.1.0/go.mod h1:M2BXkrp7PR97CKnPRT7Rk0+rtswChPtksw/vRAESGpg=
+github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
+github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
+github.com/gdamore/tcell/v2 v2.4.0 h1:W6dxJEmaxYvhICFoTY3WrLLEXsQ11SaFnKGVEXW57KM=
+github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
+github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
+github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
+github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
+github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
+github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
+github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+go.wit.com/gui v0.9.2 h1:QHMYdwpV6MzKwmFUMGevKUDn2a6GAqHN2Ltx8V3HufI=
+go.wit.com/gui v0.9.2/go.mod h1:asRXEYKmdjhtg1yiBi5A8YEY2YG4lWPS0gvNz4NXGDE=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
+golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/gocui/Makefile b/gocui/Makefile
new file mode 100644
index 0000000..4f2a6ee
--- /dev/null
+++ b/gocui/Makefile
@@ -0,0 +1,15 @@
+all: plugin
+ ldd ../gocui.so
+
+goget:
+ go get -v -t -u
+
+plugin:
+ go build -v -x -buildmode=plugin -o ../gocui.so
+
+objdump:
+ objdump -t ../gocui.so |less
+
+log:
+ reset
+ tail -f /tmp/witgui.* /tmp/guilogfile
diff --git a/gocui/add.go b/gocui/add.go
new file mode 100644
index 0000000..8e87f86
--- /dev/null
+++ b/gocui/add.go
@@ -0,0 +1,78 @@
+package main
+
+import (
+ "go.wit.com/gui/gui/toolkit"
+)
+
+var fakeStartWidth int = me.FakeW
+var fakeStartHeight int = me.TabH + me.FramePadH
+// setup fake labels for non-visible things off screen
+func (n *node) setFake() {
+ w := n.tk
+ w.isFake = true
+
+ n.gocuiSetWH(fakeStartWidth, fakeStartHeight)
+
+ fakeStartHeight += w.gocuiSize.Height()
+ // TODO: use the actual max hight of the terminal window
+ if (fakeStartHeight > 24) {
+ fakeStartHeight = me.TabH
+ fakeStartWidth += me.FakeW
+ }
+ if (logInfo) {
+ n.showView()
+ }
+}
+
+// set the widget start width & height
+func (n *node) addWidget() {
+ nw := n.tk
+ log(logInfo, "setStartWH() w.id =", n.WidgetId, "n.name", n.Name)
+ switch n.WidgetType {
+ case toolkit.Root:
+ log(logInfo, "setStartWH() rootNode w.id =", n.WidgetId, "w.name", n.Name)
+ nw.color = &colorRoot
+ n.setFake()
+ return
+ case toolkit.Flag:
+ nw.color = &colorFlag
+ n.setFake()
+ return
+ case toolkit.Window:
+ nw.frame = false
+ nw.color = &colorWindow
+ // redoWindows(0,0)
+ return
+ case toolkit.Tab:
+ nw.color = &colorTab
+ // redoWindows(0,0)
+ return
+ case toolkit.Button:
+ nw.color = &colorButton
+ case toolkit.Box:
+ nw.color = &colorBox
+ nw.isFake = true
+ n.setFake()
+ return
+ case toolkit.Grid:
+ nw.color = &colorGrid
+ nw.isFake = true
+ n.setFake()
+ return
+ case toolkit.Group:
+ nw.color = &colorGroup
+ nw.frame = false
+ return
+ case toolkit.Label:
+ nw.color = &colorLabel
+ nw.frame = false
+ return
+ default:
+ /*
+ if n.IsCurrent() {
+ n.updateCurrent()
+ }
+ */
+ }
+ n.showWidgetPlacement(logInfo, "addWidget()")
+}
diff --git a/gocui/checkbox.go b/gocui/checkbox.go
new file mode 100644
index 0000000..d8c6f4b
--- /dev/null
+++ b/gocui/checkbox.go
@@ -0,0 +1,33 @@
+package main
+
+import (
+// "github.com/awesome-gocui/gocui"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func (n *node) setCheckbox(b bool) {
+ w := n.tk
+ if (n.WidgetType != toolkit.Checkbox) {
+ return
+ }
+ if (b) {
+ n.B = b
+ n.Text = "X " + n.Name
+ } else {
+ n.B = b
+ n.Text = " " + n.Name
+ }
+ t := len(n.Text) + 1
+ w.gocuiSize.w1 = w.gocuiSize.w0 + t
+
+// w.realWidth = w.gocuiSize.Width() + me.PadW
+// w.realHeight = w.gocuiSize.Height() + me.PadH
+
+// if w.frame {
+// w.realWidth += me.FramePadW
+// w.realHeight += me.FramePadH
+// }
+
+ n.deleteView()
+ n.showView()
+}
diff --git a/gocui/click.go b/gocui/click.go
new file mode 100644
index 0000000..45823e3
--- /dev/null
+++ b/gocui/click.go
@@ -0,0 +1,356 @@
+package main
+
+import (
+ "fmt"
+ "github.com/awesome-gocui/gocui"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+// set isCurrent = false everywhere
+func unsetCurrent(n *node) {
+ w := n.tk
+ w.isCurrent = false
+
+ if n.WidgetType == toolkit.Tab {
+ // n.tk.color = &colorTab
+ // n.setColor()
+ }
+
+ for _, child := range n.children {
+ unsetCurrent(child)
+ }
+}
+
+// when adding a new widget, this will update the display
+// of the current widgets if that widget is supposed
+// to be in current display
+func (n *node) updateCurrent() {
+ log(true, "updateCurrent()", n.Name)
+ if n.WidgetType == toolkit.Tab {
+ if n.IsCurrent() {
+ // n.tk.color = &colorActiveT
+ n.setColor(&colorActiveT)
+ n.hideView()
+ n.showView()
+ setCurrentTab(n)
+ } else {
+ // n.tk.color = &colorTab
+ // n.setColor()
+ }
+ return
+ }
+ if n.WidgetType == toolkit.Window {
+ if n.IsCurrent() {
+ // setCurrentWindow(n)
+ }
+ return
+ }
+ if n.WidgetType == toolkit.Root {
+ return
+ }
+ n.parent.updateCurrent()
+}
+
+// shows the widgets in a window
+func setCurrentWindow(n *node) {
+ if n.IsCurrent() {
+ return
+ }
+ w := n.tk
+ if n.WidgetType != toolkit.Window {
+ return
+ }
+ unsetCurrent(me.rootNode)
+
+ if n.hasTabs {
+ // set isCurrent = true on the first tab
+ for _, child := range n.children {
+ child.tk.isCurrent = true
+ break
+ }
+ } else {
+ w.isCurrent = true
+ }
+}
+
+// shows the widgets in a tab
+func setCurrentTab(n *node) {
+ w := n.tk
+ if n.WidgetType != toolkit.Tab {
+ return
+ }
+ unsetCurrent(me.rootNode)
+ w.isCurrent = true
+ p := n.parent.tk
+ p.isCurrent = true
+ log(true, "setCurrent()", n.Name)
+}
+
+func (n *node) doWidgetClick() {
+ switch n.WidgetType {
+ case toolkit.Root:
+ // THIS IS THE BEGINING OF THE LAYOUT
+ log(true, "doWidgetClick()", n.Name)
+ redoWindows(0,0)
+ case toolkit.Flag:
+ log(true, "doWidgetClick() FLAG widget name =", n.Name)
+ log(true, "doWidgetClick() if this is the dropdown menu, handle it here?")
+ case toolkit.Window:
+ if (me.currentWindow == n) {
+ return
+ }
+ if (me.currentWindow != nil) {
+ unsetCurrent(me.currentWindow)
+ me.currentWindow.setColor(&colorWindow)
+ me.currentWindow.hideWidgets()
+ }
+ n.hideWidgets()
+ me.currentWindow = n
+ // setCurrentWindow(n) // probably delete this
+ n.setColor(&colorActiveW)
+ n.redoTabs(me.TabW, me.TabH)
+ for _, child := range n.children {
+ if (child.currentTab == true) {
+ log(true, "FOUND CURRENT TAB", child.Name)
+ setCurrentTab(child)
+ child.placeWidgets(me.RawW, me.RawH)
+ child.showWidgets()
+ return
+ }
+ }
+ /* FIXME: redo this
+ if ! n.hasTabs {
+ }
+ */
+ case toolkit.Tab:
+ if (n.IsCurrent()) {
+ return // do nothing if you reclick on the already selected tab
+ }
+ // find the window and disable the active tab
+ p := n.parent
+ if (p != nil) {
+ p.hideWidgets()
+ p.redoTabs(me.TabW, me.TabH)
+ unsetCurrent(p)
+ for _, child := range p.children {
+ if child.WidgetType == toolkit.Tab {
+ child.setColor(&colorTab)
+ n.currentTab = false
+ }
+ }
+ }
+ n.currentTab = true
+ n.setColor(&colorActiveT)
+ setCurrentTab(n)
+ n.placeWidgets(me.RawW, me.RawH)
+ n.showWidgets()
+ case toolkit.Group:
+ // n.placeWidgets(p.tk.startH, newH)
+ n.toggleTree()
+ case toolkit.Checkbox:
+ if (n.B) {
+ n.setCheckbox(false)
+ } else {
+ n.setCheckbox(true)
+ }
+ n.doUserEvent()
+ case toolkit.Grid:
+ newR := n.realGocuiSize()
+
+ // w,h := n.logicalSize()
+ // w := newR.w1 - newR.w0
+ // h := newR.h1 - newR.h0
+
+ n.placeGrid(newR.w0, newR.h0)
+ n.showWidgets()
+ case toolkit.Box:
+ // w.showWidgetPlacement(logNow, "drawTree()")
+ if (n.horizontal) {
+ log(true, "BOX IS HORIZONTAL", n.Name)
+ } else {
+ log(true, "BOX IS VERTICAL", n.Name)
+ }
+ // n.placeWidgets()
+ n.toggleTree()
+ case toolkit.Button:
+ n.doUserEvent()
+ case toolkit.Dropdown:
+ log(true, "do the dropdown here")
+ if (me.ddview == nil) {
+ me.ddview = addDropdown()
+ tk := me.ddview.tk
+ tk.gocuiSize.w0 = 20
+ tk.gocuiSize.w1 = 40
+ tk.gocuiSize.h0 = 10
+ tk.gocuiSize.h1 = 25
+ tk.v, _ = me.baseGui.SetView("ddview",
+ tk.gocuiSize.w0,
+ tk.gocuiSize.h0,
+ tk.gocuiSize.w1,
+ tk.gocuiSize.h1, 0)
+ if (tk.v == nil) {
+ return
+ }
+ tk.v.Wrap = true
+ tk.v.Frame = true
+ tk.v.Clear()
+ fmt.Fprint(tk.v, "example.com\nwit.com")
+ me.ddview.SetVisible(true)
+ return
+ }
+ log(true, "doWidgetClick() visible =", me.ddview.Visible())
+ if (me.ddview.Visible()) {
+ me.ddview.SetVisible(false)
+ me.baseGui.DeleteView("ddview")
+ me.ddview.tk.v = nil
+ } else {
+ var dnsList string
+ for i, s := range n.vals {
+ log(logNow, "AddText()", n.Name, i, s)
+ dnsList += s + "\n"
+ }
+ me.ddNode = n
+ log(logNow, "new dns list should be set to:", dnsList)
+ me.ddview.Text = dnsList
+ me.ddview.SetText(dnsList)
+ me.ddview.SetVisible(true)
+ }
+ for i, s := range n.vals {
+ log(logNow, "AddText()", n.Name, i, s)
+ }
+ default:
+ }
+}
+
+var toggle bool = true
+func (n *node) toggleTree() {
+ if (toggle) {
+ n.drawTree(toggle)
+ toggle = false
+ } else {
+ n.hideWidgets()
+ toggle = true
+ }
+}
+
+
+// display the widgets in the binary tree
+func (n *node) drawTree(draw bool) {
+ w := n.tk
+ if (w == nil) {
+ return
+ }
+ n.showWidgetPlacement(logNow, "drawTree()")
+ if (draw) {
+ // w.textResize()
+ n.showView()
+ } else {
+ n.deleteView()
+ }
+
+ for _, child := range n.children {
+ child.drawTree(draw)
+ }
+}
+
+func click(g *gocui.Gui, v *gocui.View) error {
+ // var l string
+ // var err error
+
+ log(logVerbose, "click() START", v.Name())
+ // n := me.rootNode.findWidgetName(v.Name())
+ n := findUnderMouse()
+ if (n != nil) {
+ log(logNow, "click() Found widget =", n.WidgetId, n.Name, ",", n.Text)
+ if (n.Name == "DropBox") {
+ log(logNow, "click() this is the dropdown menu. set a flag here what did I click? where is the mouse?")
+ log(logNow, "click() set a global dropdown clicked flag=true here")
+ me.ddClicked = true
+ }
+ n.doWidgetClick()
+ } else {
+ log(logNow, "click() could not find node name =", v.Name())
+ }
+
+ if _, err := g.SetCurrentView(v.Name()); err != nil {
+ log(logNow, "click() END err =", err)
+ return err
+ }
+
+ log(logVerbose, "click() END")
+ return nil
+}
+
+func findUnderMouse() *node {
+ var found *node
+ var widgets []*node
+ var f func (n *node)
+ w, h := me.baseGui.MousePosition()
+
+ // find buttons that are below where the mouse button click
+ f = func(n *node) {
+ widget := n.tk
+ // ignore widgets that are not visible
+ if n.Visible() {
+ if ((widget.gocuiSize.w0 <= w) && (w <= widget.gocuiSize.w1) &&
+ (widget.gocuiSize.h0 <= h) && (h <= widget.gocuiSize.h1)) {
+ widgets = append(widgets, n)
+ found = n
+ }
+ }
+ if (n == me.ddview) {
+ log(true, "findUnderMouse() found ddview")
+ if n.Visible() {
+ log(true, "findUnderMouse() and ddview is visable. hide it here. TODO: find highlighted row")
+ found = n
+ // find the actual value here and set the dropdown widget
+ me.baseGui.DeleteView("ddview")
+ } else {
+ log(true, "findUnderMouse() I was lying, actually it's not found")
+ }
+ }
+
+ for _, child := range n.children {
+ f(child)
+ }
+ }
+ f(me.rootNode)
+ // widgets has everything that matches
+ // TODO: pop up menu with a list of them
+ for _, n := range widgets {
+ //log(logNow, "ctrlDown() FOUND widget", widget.id, widget.name)
+ n.showWidgetPlacement(logNow, "findUnderMouse() FOUND")
+ }
+ return found
+}
+
+// find the widget under the mouse click
+func ctrlDown(g *gocui.Gui, v *gocui.View) error {
+ var found *node
+ // var widgets []*node
+ // var f func (n *node)
+ found = findUnderMouse()
+ if (me.ctrlDown == nil) {
+ setupCtrlDownWidget()
+ me.ctrlDown.Text = found.Name
+ me.ctrlDown.tk.cuiName = "ctrlDown"
+ // me.ctrlDown.parent = me.rootNode
+ }
+ cd := me.ctrlDown.tk
+ if (found == nil) {
+ found = me.rootNode
+ }
+ me.ctrlDown.Text = found.Name
+ newR := found.realGocuiSize()
+ cd.gocuiSize.w0 = newR.w0
+ cd.gocuiSize.h0 = newR.h0
+ cd.gocuiSize.w1 = newR.w1
+ cd.gocuiSize.h1 = newR.h1
+ if me.ctrlDown.Visible() {
+ me.ctrlDown.hideView()
+ } else {
+ me.ctrlDown.showView()
+ }
+ me.ctrlDown.showWidgetPlacement(logNow, "ctrlDown:")
+ return nil
+}
diff --git a/gocui/color.go b/gocui/color.go
new file mode 100644
index 0000000..c665bb6
--- /dev/null
+++ b/gocui/color.go
@@ -0,0 +1,117 @@
+package main
+
+import (
+ "math/rand"
+ "github.com/awesome-gocui/gocui"
+)
+
+//w.v.SelBgColor = gocui.ColorCyan
+//color.go: w.v.SelFgColor = gocui.ColorBlack
+//color.go: w.v.BgColor = gocui.ColorGreen
+
+type colorT struct {
+ frame gocui.Attribute
+ fg gocui.Attribute
+ bg gocui.Attribute
+ selFg gocui.Attribute
+ selBg gocui.Attribute
+ name string
+}
+
+var none gocui.Attribute = gocui.AttrNone
+var lightPurple gocui.Attribute = gocui.GetColor("#DDDDDD") // light purple
+var darkPurple gocui.Attribute = gocui.GetColor("#FFAA55") // Dark Purple
+var heavyPurple gocui.Attribute = gocui.GetColor("#88AA55") // heavy purple
+var powdererBlue gocui.Attribute = gocui.GetColor("#B0E0E6") // w3c 'powerder blue'
+var superLightGrey gocui.Attribute = gocui.GetColor("#55AAFF") // super light grey
+
+// Standard defined colors from gocui:
+// ColorBlack ColorRed ColorGreen ColorYellow ColorBlue ColorMagenta ColorCyan ColorWhite
+
+// v.BgColor = gocui.GetColor("#111111") // crazy red
+// v.BgColor = gocui.GetColor("#FF9911") // heavy red
+// v.SelBgColor = gocui.GetColor("#FFEE11") // blood red
+
+// v.BgColor = gocui.GetColor("#55AAFF") // super light grey
+// v.BgColor = gocui.GetColor("#FFC0CB") // 'w3c pink' yellow
+
+// Normal Text On mouseover
+// Widget Frame Text background Text background
+var colorWindow colorT = colorT{ none , gocui.ColorBlue, none , none , powdererBlue , "normal window"}
+var colorActiveW colorT = colorT{ none , none , powdererBlue , none , powdererBlue , "active window"}
+
+var colorTab colorT = colorT{gocui.ColorBlue, gocui.ColorBlue, none , none , powdererBlue , "normal tab"}
+var colorActiveT colorT = colorT{gocui.ColorBlue, none , powdererBlue , none , powdererBlue , "active tab"}
+
+var colorButton colorT = colorT{gocui.ColorGreen, none , gocui.ColorWhite, gocui.ColorGreen, gocui.ColorBlack, "normal button"}
+var colorLabel colorT = colorT{ none , none , superLightGrey , none , superLightGrey , "normal label"}
+var colorGroup colorT = colorT{ none , none , superLightGrey , none , superLightGrey , "normal group"}
+
+// 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: or fix kvm/qemu serial console & SIGWINCH.
+// TODO: and minicom and uboot and 5 million other things.
+// TODO: maybe enough of us could actually do that if we made it a goal.
+// TODO: start with riscv boards and fix it universally there
+// TODO: so just a small little 'todo' item here
+func (n *node) setColor(newColor *colorT) {
+ tk := n.tk
+ if (tk.color == newColor) {
+ // nothing to do since the colors have nto changed
+ return
+ }
+ tk.color = newColor
+ if (tk.v == nil) {
+ return
+ }
+ if (tk.color == nil) {
+ log(true, "Set the node to color = nil")
+ tk.color = &colorNone
+ }
+ log(true, "Set the node to color =", tk.color.name)
+ n.recreateView()
+}
+
+func (n *node) setDefaultWidgetColor() {
+ n.showView()
+}
+
+func (n *node) setDefaultHighlight() {
+ w := n.tk
+ if (w.v == nil) {
+ log(logError, "SetColor() failed on view == nil")
+ return
+ }
+ w.v.SelBgColor = gocui.ColorGreen
+ w.v.SelFgColor = gocui.ColorBlack
+}
+
+func randColor() gocui.Attribute {
+ colors := []string{"Green", "#FFAA55", "Yellow", "Blue", "Red", "Black", "White"}
+ i := rand.Intn(len(colors))
+ log(true, "randColor() i =", i)
+ return gocui.GetColor(colors[i])
+}
+
+func (n *node) redoColor(draw bool) {
+ w := n.tk
+ if (w == nil) {
+ return
+ }
+
+ sleep(.05)
+ n.setDefaultHighlight()
+ n.setDefaultWidgetColor()
+
+ for _, child := range n.children {
+ child.redoColor(draw)
+ }
+}
diff --git a/gocui/common.go b/gocui/common.go
new file mode 120000
index 0000000..35417a1
--- /dev/null
+++ b/gocui/common.go
@@ -0,0 +1 @@
+../nocui/common.go \ No newline at end of file
diff --git a/gocui/debug.go b/gocui/debug.go
new file mode 100644
index 0000000..db94e37
--- /dev/null
+++ b/gocui/debug.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+ "fmt"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func (n *node) dumpTree(draw bool) {
+ w := n.tk
+ if (w == nil) {
+ return
+ }
+ n.showWidgetPlacement(logNow, "dumpTree()")
+
+ for _, child := range n.children {
+ child.dumpTree(draw)
+ }
+}
+
+func (n *node) showWidgetPlacement(b bool, s string) {
+ if (n == nil) {
+ log(logError, "WTF w == nil")
+ return
+ }
+ w := n.tk
+
+ var s1 string
+ var pId int
+ if (n.parent == nil) {
+ log(logVerbose, "showWidgetPlacement() parent == nil", n.WidgetId, w.cuiName)
+ pId = 0
+ } else {
+ pId = n.parent.WidgetId
+ }
+ s1 = fmt.Sprintf("(wId,pId)=(%2d,%2d) ", n.WidgetId, pId)
+ if n.Visible() {
+ s1 += fmt.Sprintf("gocui=(%2d,%2d)(%2d,%2d,%2d,%2d)",
+ w.gocuiSize.Width(), w.gocuiSize.Height(),
+ w.gocuiSize.w0, w.gocuiSize.h0, w.gocuiSize.w1, w.gocuiSize.h1)
+ } else {
+ s1 += fmt.Sprintf(" ")
+ }
+ if (n.parent != nil) {
+ if (n.parent.WidgetType == toolkit.Grid) {
+ s1 += fmt.Sprintf("At(%2d,%2d) ", n.AtW, n.AtH)
+ }
+ }
+ tmp := "." + n.Name + "."
+ log(b, s1, s, n.WidgetType, ",", tmp) // , "text=", w.text)
+}
+
+func (n *node) dumpWidget(pad string) {
+ log(true, "node:", pad, n.WidgetId, "At(", n.AtW, n.AtH, ") ,", n.WidgetType, ", n.Name =", n.Name, ", n.Text =", n.Text)
+}
+
+func (n *node) listWidgets() {
+ if (n == nil) {
+ return
+ }
+
+ var pad string
+ for i := 0; i < me.depth; i++ {
+ pad = pad + " "
+ }
+ n.dumpWidget(pad)
+
+ for _, child := range n.children {
+ me.depth += 1
+ child.listWidgets()
+ me.depth -= 1
+ }
+ return
+}
diff --git a/gocui/fakefile.go b/gocui/fakefile.go
new file mode 100644
index 0000000..c6b8877
--- /dev/null
+++ b/gocui/fakefile.go
@@ -0,0 +1,58 @@
+package main
+
+import (
+ "bytes"
+ "io"
+ "errors"
+)
+
+type FakeFile struct {
+ reader *bytes.Reader
+ buffer *bytes.Buffer
+ offset int64
+}
+
+func (f *FakeFile) Read(p []byte) (n int, err error) {
+ n, err = f.reader.ReadAt(p, f.offset)
+ f.offset += int64(n)
+ return n, err
+}
+
+func (f *FakeFile) Write(p []byte) (n int, err error) {
+ n, err = f.buffer.Write(p)
+ f.offset += int64(n)
+ f.reader.Reset(f.buffer.Bytes())
+ return n, err
+}
+
+func (f *FakeFile) Seek(offset int64, whence int) (int64, error) {
+ newOffset := f.offset
+
+ switch whence {
+ case io.SeekStart:
+ newOffset = offset
+ case io.SeekCurrent:
+ newOffset += offset
+ case io.SeekEnd:
+ newOffset = int64(f.buffer.Len()) + offset
+ default:
+ return 0, errors.New("Seek: whence not at start,current or end")
+ }
+ // never can get here right?
+
+ if newOffset < 0 {
+ return 0, errors.New("Seek: offset < 0")
+ }
+
+ f.offset = newOffset
+ return f.offset, nil
+}
+
+func NewFakeFile() *FakeFile {
+ buf := &bytes.Buffer{}
+ return &FakeFile{
+ reader: bytes.NewReader(buf.Bytes()),
+ buffer: buf,
+ offset: 0,
+ }
+}
diff --git a/gocui/gocui.go b/gocui/gocui.go
new file mode 100644
index 0000000..d2877d7
--- /dev/null
+++ b/gocui/gocui.go
@@ -0,0 +1,100 @@
+// Copyright 2014 The gocui Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "errors"
+ "github.com/awesome-gocui/gocui"
+)
+
+// This initializes the gocui package
+// it runs SetManagerFunc which passes every input
+// event (keyboard, mouse, etc) to the function "gocuiEvent()"
+func gocuiMain() {
+ g, err := gocui.NewGui(gocui.OutputNormal, true)
+ if err != nil {
+ panic(err)
+ }
+ defer g.Close()
+
+ me.baseGui = g
+
+ g.Cursor = true
+ g.Mouse = true
+
+ // this sets the function that is run on every event. For example:
+ // When you click the mouse, move the mouse, or press a key on the keyboard
+ // This is equivalent to xev or similar to cat /dev/input on linux
+ g.SetManagerFunc(gocuiEvent)
+
+ if err := defaultKeybindings(g); err != nil {
+ panic(err)
+ }
+
+ if err := g.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
+ panic(err)
+ }
+}
+
+// Thanks to the gocui developers -- your package kicks ass
+// This function is called on every event. It is a callback function from the gocui package
+// which has an excellent implementation. While gocui handles things like text highlighting
+// and the layout of the text areas -- also things like handling SIGWINCH and lots of really
+// complicated console handling, it sends events here in a clean way.
+// This is equivalent to the linux command xev (apt install x11-utils)
+func gocuiEvent(g *gocui.Gui) error {
+ maxX, maxY := g.Size()
+ mx, my := g.MousePosition()
+ log(logVerbose, "handleEvent() START", maxX, maxY, mx, my, msgMouseDown)
+ if _, err := g.View("msg"); msgMouseDown && err == nil {
+ moveMsg(g)
+ }
+ if widgetView, _ := g.View("msg"); widgetView == nil {
+ log(logNow, "handleEvent() create output widget now", maxX, maxY, mx, my)
+ makeOutputWidget(g, "this is a create before a mouse click")
+ if (me.logStdout != nil) {
+ // setOutput(me.logStdout)
+ }
+ } else {
+ log(logInfo, "output widget already exists", maxX, maxY, mx, my)
+ }
+ mouseMove(g)
+ log(logVerbose, "handleEvent() END ", maxX, maxY, mx, my, msgMouseDown)
+ return nil
+}
+
+func dragOutputWindow() {
+}
+
+// turns off the frame on the global window
+func setFrame(b bool) {
+ // TODO: figure out what this might be useful for
+ // what is this do? I made it just 2 lines for now. Is this useful for something?
+ v := SetView("global", 5, 10, 5, 10, 0) // x0, x1, y1, y2, overlap
+ if (v == nil) {
+ log(logError, "setFrame() global failed")
+ }
+ v.Frame = b
+}
+
+func quit(g *gocui.Gui, v *gocui.View) error {
+ return gocui.ErrQuit
+}
+
+func SetView(name string, x0, y0, x1, y1 int, overlaps byte) *gocui.View {
+ if (me.baseGui == nil) {
+ log(logError, "SetView() ERROR: me.baseGui == nil")
+ return nil
+ }
+
+ v, err := me.baseGui.SetView(name, x0, y0, x1, y1, overlaps)
+ if err != nil {
+ if !errors.Is(err, gocui.ErrUnknownView) {
+ log(logError, "SetView() global failed on name =", name)
+ }
+ return nil
+ }
+ return v
+}
diff --git a/gocui/help.go b/gocui/help.go
new file mode 100644
index 0000000..82d5cc5
--- /dev/null
+++ b/gocui/help.go
@@ -0,0 +1,71 @@
+// Copyright 2014 The gocui Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/awesome-gocui/gocui"
+)
+
+var helpText []string = []string{"KEYBINDINGS",
+ "",
+ "?: toggle help",
+ "d: toggle debugging",
+ "r: redraw widgets",
+ "s/h: show/hide all widgets",
+ "L: list all widgets",
+ "M: list all widgets positions",
+ "q: quit()",
+ "p: panic()",
+ "o: show Stdout",
+ "l: log to /tmp/witgui.log",
+ "Ctrl-D: Toggle Debugging",
+ "Ctrl-V: Toggle Verbose Debugging",
+ "Ctrl-C: Exit",
+ "",
+}
+
+func hidehelplayout() {
+ me.baseGui.DeleteView("help")
+ // n.deleteView()
+ // child.hideFake()
+}
+
+func helplayout() error {
+ g := me.baseGui
+ var err error
+ maxX, _ := g.Size()
+
+ var newW int = 8
+ for _, s := range(helpText) {
+ if newW < len(s) {
+ newW = len(s)
+ }
+ }
+
+ help, err := g.SetView("help", maxX-(newW + me.FramePadW), 0, maxX-1, len(helpText) + me.FramePadH, 0)
+ if err != nil {
+ if !errors.Is(err, gocui.ErrUnknownView) {
+ return err
+ }
+ help.SelBgColor = gocui.ColorGreen
+ 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"))
+
+ if _, err := g.SetCurrentView("help"); err != nil {
+ return err
+ }
+ }
+ me.helpLabel = help
+ return nil
+}
diff --git a/gocui/keybindings.go b/gocui/keybindings.go
new file mode 100644
index 0000000..2741800
--- /dev/null
+++ b/gocui/keybindings.go
@@ -0,0 +1,175 @@
+// Copyright 2014 The gocui Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "os"
+ "github.com/awesome-gocui/gocui"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func defaultKeybindings(g *gocui.Gui) error {
+ if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
+ return err
+ }
+ for _, n := range []string{"but1", "but2", "help", "but3"} {
+ if err := g.SetKeybinding(n, gocui.MouseLeft, gocui.ModNone, showMsg); err != nil {
+ return err
+ }
+ }
+ if err := g.SetKeybinding("", gocui.MouseRelease, gocui.ModNone, mouseUp); err != nil {
+ return err
+ }
+ // mouseDown() runs whenever you click on an unknown view (?)
+ if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, mouseDown); err != nil {
+ return err
+ }
+ if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModMouseCtrl, ctrlDown); err != nil {
+ return err
+ }
+// if err := g.SetKeybinding(w.v.Name(), gocui.MouseLeft, gocui.ModNone, click); err != nil {
+// return err
+// }
+ /*
+ if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, globalDown); err != nil {
+ return err
+ }
+ */
+ if err := g.SetKeybinding("msg", gocui.MouseLeft, gocui.ModNone, msgDown); err != nil {
+ return err
+ }
+ addDebugKeys(g)
+ return nil
+}
+
+func addDebugKeys(g *gocui.Gui) {
+ // show debugging buttons
+ g.SetKeybinding("", 'd', gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ fakeStartWidth = me.FakeW
+ fakeStartHeight = me.TabH + me.FramePadH
+ if (showDebug) {
+ me.rootNode.showFake()
+ showDebug = false
+ } else {
+ me.rootNode.hideFake()
+ showDebug = true
+ }
+ return nil
+ })
+
+ // display the help menu
+ g.SetKeybinding("", '?', gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ if (showHelp) {
+ helplayout()
+ showHelp = false
+ } else {
+ me.baseGui.DeleteView("help")
+ showHelp = true
+ }
+ return nil
+ })
+
+ // redraw all the widgets
+ g.SetKeybinding("", 'r', gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ if (redoWidgets) {
+ redoWindows(0,0)
+ redoWidgets = false
+ } else {
+ me.rootNode.hideWidgets()
+ redoWidgets = true
+ }
+ return nil
+ })
+
+ // hide all widgets
+ g.SetKeybinding("", 'h', gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ me.rootNode.hideWidgets()
+ return nil
+ })
+
+ // show all widgets
+ g.SetKeybinding("", 's', gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ me.rootNode.showWidgets()
+ return nil
+ })
+
+ // list all widgets
+ g.SetKeybinding("", 'L', gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ me.rootNode.listWidgets()
+ return nil
+ })
+
+ // list all widgets with positions
+ g.SetKeybinding("", 'M', gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ me.rootNode.dumpTree(true)
+ return nil
+ })
+
+ // log to output window
+ g.SetKeybinding("", 'o', gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ if me.logStdout.Visible() {
+ me.logStdout.SetVisible(false)
+ setOutput(os.Stdout)
+ } else {
+ me.logStdout.SetVisible(true)
+ setOutput(me.logStdout.tk)
+ }
+ return nil
+ })
+
+ // exit
+ g.SetKeybinding("", 'q', gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ standardExit()
+ return nil
+ })
+ g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ standardExit()
+ return nil
+ })
+ g.SetKeybinding("", gocui.KeyCtrlD, gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ if (showDebug) {
+ var a toolkit.Action
+ a.B = true
+ a.ActionType = toolkit.EnableDebug
+ callback <- a
+ logInfo = true
+ logVerbose = true
+ } else {
+ logInfo = false
+ logVerbose = false
+ }
+ return nil
+ })
+ g.SetKeybinding("", gocui.KeyCtrlV, gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ if (logVerbose) {
+ logInfo = false
+ logVerbose = false
+ } else {
+ logInfo = true
+ logVerbose = true
+ }
+ return nil
+ })
+
+ // panic
+ g.SetKeybinding("", 'p', gocui.ModNone,
+ func(g *gocui.Gui, v *gocui.View) error {
+ standardExit()
+ panic("forced panic in gocui")
+ return nil
+ })
+}
diff --git a/gocui/log.go b/gocui/log.go
new file mode 100644
index 0000000..33dc558
--- /dev/null
+++ b/gocui/log.go
@@ -0,0 +1,55 @@
+package main
+
+import (
+ "io"
+// "fmt"
+// "strings"
+ witlog "go.wit.com/log"
+)
+
+// various debugging flags
+var logNow bool = true // useful for active development
+var logError bool = true
+var logWarn bool = false
+var logInfo bool = false
+var logVerbose bool = false
+
+var outputS []string
+
+func log(b bool, a ...any) {
+ witlog.Log(b, a...)
+}
+
+func sleep(a ...any) {
+ witlog.Sleep(a...)
+}
+
+func exit(a ...any) {
+ witlog.Exit(a...)
+}
+
+/*
+func newLog(a ...any) {
+ s := fmt.Sprint(a...)
+ tmp := strings.Split(s, "\n")
+ outputS = append(outputS, tmp...)
+ if (len(outputS) > 50) {
+ outputS = outputS[10:]
+ }
+ if (me.baseGui != nil) {
+ v, _ := me.baseGui.View("msg")
+ if (v != nil) {
+ v.Clear()
+ fmt.Fprintln(v, strings.Join(outputS, "\n"))
+ }
+ }
+}
+*/
+
+func setOutput(w io.Writer) {
+ if (w == nil) {
+ return
+ }
+ witlog.SetTmp()
+ // witlog.SetToolkitOutput(newLog)
+}
diff --git a/gocui/main.go b/gocui/main.go
new file mode 100644
index 0000000..2b0e06d
--- /dev/null
+++ b/gocui/main.go
@@ -0,0 +1,101 @@
+// Copyright 2014 The gocui Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "os"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+// sets defaults and establishes communication
+// to this toolkit from the wit/gui golang package
+func init() {
+ log(logInfo, "Init() of awesome-gocui")
+
+ // init the config struct default values
+ Set(&me, "default")
+
+ pluginChan = make(chan toolkit.Action)
+
+ log(logNow, "Init() start pluginChan")
+ go catchActionChannel()
+ sleep(.1) // probably not needed, but in here for now under development
+ go main()
+ sleep(.1) // probably not needed, but in here for now under development
+}
+
+/*
+recieves requests from the program to do things like:
+* add new widgets
+* change the text of a label
+* etc..
+*/
+func catchActionChannel() {
+ log(logInfo, "catchActionChannel() START")
+ for {
+ log(logInfo, "catchActionChannel() infinite for() loop restarted select on channel")
+ select {
+ case a := <-pluginChan:
+ if (me.baseGui == nil) {
+ // something went wrong initializing the gocui
+ log(logError,"ERROR: console did not initialize")
+ continue
+ }
+ log(logInfo, "catchActionChannel()", a.WidgetId, a.ActionType, a.WidgetType, a.Name)
+ action(&a)
+ }
+ }
+}
+
+func Exit() {
+ // TODO: what should actually happen here?
+ log(true, "Exit() here. doing standardExit()")
+ standardExit()
+}
+
+func standardExit() {
+ log(true, "standardExit() doing baseGui.Close()")
+ me.baseGui.Close()
+ log(true, "standardExit() doing outf.Close()")
+ outf.Close()
+ // log(true, "standardExit() setOutput(os.Stdout)")
+ // setOutput(os.Stdout)
+ log(true, "standardExit() send back Quit()")
+ go sendBackQuit() // don't stall here in case the
+ // induces a delay in case the callback channel is broken
+ sleep(1)
+ log(true, "standardExit() exit()")
+ exit()
+}
+func sendBackQuit() {
+ // send 'Quit' back to the program (?)
+ var a toolkit.Action
+ a.ActionType = toolkit.UserQuit
+ callback <- a
+}
+
+var outf *os.File
+
+func main() {
+ var err error
+ log(logInfo, "main() start Init()")
+
+ outf, err = os.OpenFile("/tmp/witgui.log", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
+ if err != nil {
+ exit("error opening file: %v", err)
+ }
+ os.Stdout = outf
+ defer outf.Close()
+
+ // setOutput(outf)
+ // log("This is a test log entry")
+
+ ferr, _ := os.OpenFile("/tmp/witgui.err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
+ os.Stderr = ferr
+ gocuiMain()
+
+ log(true, "MouseMain() closed")
+ standardExit()
+}
diff --git a/gocui/mouse.go b/gocui/mouse.go
new file mode 100644
index 0000000..50c2834
--- /dev/null
+++ b/gocui/mouse.go
@@ -0,0 +1,149 @@
+// Copyright 2014 The gocui Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "errors"
+ "fmt"
+ "github.com/awesome-gocui/gocui"
+)
+
+// this function uses the mouse position to highlight & unhighlight things
+// this is run every time the user moves the mouse over the terminal window
+func mouseMove(g *gocui.Gui) {
+ mx, my := g.MousePosition()
+ for _, view := range g.Views() {
+ view.Highlight = false
+ }
+ if v, err := g.ViewByPosition(mx, my); err == nil {
+ v.Highlight = true
+ }
+}
+
+func msgDown(g *gocui.Gui, v *gocui.View) error {
+ initialMouseX, initialMouseY = g.MousePosition()
+ log(true, "msgDown() X,Y", initialMouseX, initialMouseY)
+ if vx, vy, _, _, err := g.ViewPosition("msg"); err == nil {
+ xOffset = initialMouseX - vx
+ yOffset = initialMouseY - vy
+ msgMouseDown = true
+ }
+ return nil
+}
+
+func hideDDview() error {
+ w, h := me.baseGui.MousePosition()
+ log(true, "hide dropdown menu() view msgMouseDown (w,h) =", w, h)
+ if (me.ddview == nil) {
+ return gocui.ErrUnknownView
+ }
+ if (me.ddview.tk.v == nil) {
+ return gocui.ErrUnknownView
+ }
+ me.ddview.SetVisible(false)
+ return nil
+}
+
+func showDDview() error {
+ w, h := me.baseGui.MousePosition()
+ log(true, "show dropdown menu() view msgMouseDown (w,h) =", w, h)
+ if (me.ddview == nil) {
+ return gocui.ErrUnknownView
+ }
+ if (me.ddview.tk.v == nil) {
+ return gocui.ErrUnknownView
+ }
+ me.ddview.SetVisible(true)
+ return nil
+}
+
+func mouseUp(g *gocui.Gui, v *gocui.View) error {
+ w, h := g.MousePosition()
+ log(true, "mouseUp() view msgMouseDown (check here for dropdown menu click) (w,h) =", w, h)
+ if (me.ddClicked) {
+ me.ddClicked = false
+ log(true, "mouseUp() ddview is the thing that was clicked", w, h)
+ log(true, "mouseUp() find out what the string is here", w, h, me.ddview.tk.gocuiSize.h1)
+
+ var newZone string = ""
+ if (me.ddNode != nil) {
+ value := h - me.ddview.tk.gocuiSize.h0 - 1
+ log(true, "mouseUp() me.ddview.tk.gocuiSize.h1 =", me.ddview.tk.gocuiSize.h1)
+ log(true, "mouseUp() me.ddNode.vals =", me.ddNode.vals)
+ valsLen := len(me.ddNode.vals)
+ log(true, "mouseUp() value =", value, "valsLen =", valsLen)
+ log(true, "mouseUp() me.ddNode.vals =", me.ddNode.vals)
+ if ((value >= 0) && (value < valsLen)) {
+ newZone = me.ddNode.vals[value]
+ log(true, "mouseUp() value =", value, "newZone =", newZone)
+ }
+ }
+ hideDDview()
+ if (newZone != "") {
+ if (me.ddNode != nil) {
+ me.ddNode.SetText(newZone)
+ me.ddNode.S = newZone
+ me.ddNode.doUserEvent()
+ }
+ }
+ return nil
+ }
+ /*
+ // if there is a drop down view active, treat it like a dialog box and close it
+ if (hideDDview() == nil) {
+ return nil
+ }
+ */
+ if msgMouseDown {
+ msgMouseDown = false
+ if movingMsg {
+ movingMsg = false
+ return nil
+ } else {
+ g.DeleteView("msg")
+ }
+ } else if globalMouseDown {
+ globalMouseDown = false
+ g.DeleteView("globalDown")
+ }
+ return nil
+}
+
+func mouseDown(g *gocui.Gui, v *gocui.View) error {
+ mx, my := g.MousePosition()
+ if vx0, vy0, vx1, vy1, err := g.ViewPosition("msg"); err == nil {
+ if mx >= vx0 && mx <= vx1 && my >= vy0 && my <= vy1 {
+ return msgDown(g, v)
+ }
+ }
+ globalMouseDown = true
+ maxX, _ := g.Size()
+ test := findUnderMouse()
+ msg := fmt.Sprintf("Mouse really down at: %d,%d", mx, my) + "foobar"
+ if (test == me.ddview) {
+ if (me.ddview.Visible()) {
+ log(true, "hide DDview() Mouse really down at:", mx, my)
+ hideDDview()
+ } else {
+ log(true, "show DDview() Mouse really down at:", mx, my)
+ showDDview()
+ }
+ return nil
+ }
+ x := mx - len(msg)/2
+ if x < 0 {
+ x = 0
+ } else if x+len(msg)+1 > maxX-1 {
+ x = maxX - 1 - len(msg) - 1
+ }
+ log(true, "mouseDown() about to write out message to 'globalDown' view. msg =", msg)
+ if v, err := g.SetView("globalDown", x, my-1, x+len(msg)+1, my+1, 0); err != nil {
+ if !errors.Is(err, gocui.ErrUnknownView) {
+ return err
+ }
+ v.WriteString(msg)
+ }
+ return nil
+}
diff --git a/gocui/place.go b/gocui/place.go
new file mode 100644
index 0000000..1ed46bd
--- /dev/null
+++ b/gocui/place.go
@@ -0,0 +1,186 @@
+package main
+
+import (
+ "strings"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func (n *node) placeBox(startW int, startH int) {
+ if (n.WidgetType != toolkit.Box) {
+ return
+ }
+ n.showWidgetPlacement(logNow, "boxS()")
+
+ newW := startW
+ newH := startH
+ for _, child := range n.children {
+ child.placeWidgets(newW, newH)
+ // n.showWidgetPlacement(logNow, "boxS()")
+ newR := child.realGocuiSize()
+ w := newR.w1 - newR.w0
+ h := newR.h1 - newR.h0
+ if (n.horizontal) {
+ log(logNow, "BOX IS HORIZONTAL", n.Name, "newWH()", newW, newH, "child()", w, h, child.Name)
+ // expand based on the child width
+ newW += w
+ } else {
+ log(logNow, "BOX IS VERTICAL ", n.Name, "newWH()", newW, newH, "child()", w, h, child.Name)
+ // expand based on the child height
+ newH += h
+ }
+ }
+
+ // just compute this every time?
+ // newR := n.realGocuiSize()
+
+ n.showWidgetPlacement(logNow, "boxE()")
+}
+
+func (n *node) placeWidgets(startW int, startH int) {
+ if (n == nil) {
+ return
+ }
+ if (me.rootNode == nil) {
+ return
+ }
+
+ switch n.WidgetType {
+ case toolkit.Window:
+ for _, child := range n.children {
+ child.placeWidgets(me.RawW, me.RawH)
+ return
+ }
+ case toolkit.Tab:
+ for _, child := range n.children {
+ child.placeWidgets(me.RawW, me.RawH)
+ return
+ }
+ case toolkit.Grid:
+ n.placeGrid(startW, startH)
+ case toolkit.Box:
+ n.placeBox(startW, startH)
+ case toolkit.Group:
+ // move the group to the parent's next location
+ n.gocuiSetWH(startW, startH)
+ n.showWidgetPlacement(logNow, "group()")
+
+ newW := startW + me.GroupPadW
+ newH := startH + 3 // normal hight of the group label
+ // now move all the children aka: run place() on them
+ for _, child := range n.children {
+ child.placeWidgets(newW, newH)
+ newR := child.realGocuiSize()
+ // w := newR.w1 - newR.w0
+ h := newR.h1 - newR.h0
+
+ // increment straight down
+ newH += h
+ }
+ default:
+ n.gocuiSetWH(startW, startH)
+ // n.moveTo(startW, startH)
+ }
+}
+
+func (n *node) placeGrid(startW int, startH int) {
+ w := n.tk
+ n.showWidgetPlacement(logInfo, "grid0:")
+ if (n.WidgetType != toolkit.Grid) {
+ return
+ }
+
+ // first compute the max sizes of the rows and columns
+ for _, child := range n.children {
+ newR := child.realGocuiSize()
+ childW := newR.w1 - newR.w0
+ childH := newR.h1 - newR.h0
+
+ // set the child's realWidth, and grid offset
+ if (w.widths[child.AtW] < childW) {
+ w.widths[child.AtW] = childW
+ }
+ if (w.heights[child.AtH] < childH) {
+ w.heights[child.AtH] = childH
+ }
+ // child.showWidgetPlacement(logInfo, "grid: ")
+ log(logVerbose, "placeGrid:", child.Name, "child()", childW, childH, "At()", child.AtW, child.AtH)
+ }
+
+ // find the width and height offset of the grid for AtW,AtH
+ for _, child := range n.children {
+ child.showWidgetPlacement(logInfo, "grid1:")
+
+ var totalW, totalH int
+ for i, w := range w.widths {
+ if (i < child.AtW) {
+ totalW += w
+ }
+ }
+ for i, h := range w.heights {
+ if (i < child.AtH) {
+ totalH += h
+ }
+ }
+
+ // the new corner to move the child to
+ newW := startW + totalW
+ newH := startH + totalH
+
+ log(logVerbose, "placeGrid:", child.Name, "new()", newW, newH, "At()", child.AtW, child.AtH)
+ child.placeWidgets(newW, newH)
+ child.showWidgetPlacement(logInfo, "grid2:")
+ }
+ n.showWidgetPlacement(logInfo, "grid3:")
+}
+
+// computes the real, actual size of all the gocli objects in a widget
+func (n *node) realGocuiSize() *rectType {
+ var f func (n *node, r *rectType)
+ newR := new(rectType)
+ // initialize the values to opposite
+ newR.w0 = 80
+ newR.h0 = 24
+ if me.baseGui != nil {
+ maxW, maxH := me.baseGui.Size()
+ newR.w0 = maxW
+ newR.h0 = maxH
+ }
+ newR.w1 = 0
+ newR.h1 = 0
+
+ // expand the rectangle to the biggest thing displayed
+ f = func(n *node, r *rectType) {
+ newR := n.tk.gocuiSize
+ if ! n.tk.isFake {
+ if r.w0 > newR.w0 {
+ r.w0 = newR.w0
+ }
+ if r.h0 > newR.h0 {
+ r.h0 = newR.h0
+ }
+ if r.w1 < newR.w1 {
+ r.w1 = newR.w1
+ }
+ if r.h1 < newR.h1 {
+ r.h1 = newR.h1
+ }
+ }
+ for _, child := range n.children {
+ f(child, r)
+ }
+ }
+ f(n, newR)
+ return newR
+}
+
+func (n *node) textSize() (int, int) {
+ var width, height int
+
+ for _, s := range strings.Split(n.Text, "\n") {
+ if (width < len(s)) {
+ width = len(s)
+ }
+ height += 1
+ }
+ return width, height
+}
diff --git a/gocui/plugin.go b/gocui/plugin.go
new file mode 100644
index 0000000..381880c
--- /dev/null
+++ b/gocui/plugin.go
@@ -0,0 +1,126 @@
+package main
+
+import (
+ // if you include more than just this import
+ // then your plugin might be doing something un-ideal (just a guess from 2023/02/27)
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func action(a *toolkit.Action) {
+ log(logInfo, "action() START", a.WidgetId, a.ActionType, a.WidgetType, a.Name)
+ n := me.rootNode.findWidgetId(a.WidgetId)
+ var w *guiWidget
+ if (n != nil) {
+ w = n.tk
+ }
+ switch a.ActionType {
+ case toolkit.Add:
+ if (w == nil) {
+ n := addNode(a)
+ // w = n.tk
+ n.addWidget()
+ } else {
+ // this is done to protect the plugin being 'refreshed' with the
+ // widget binary tree. TODO: find a way to keep them in sync
+ log(logError, "action() Add ignored for already defined widget",
+ a.WidgetId, a.ActionType, a.WidgetType, a.Name)
+ }
+ case toolkit.Show:
+ if (a.B) {
+ n.showView()
+ } else {
+ n.hideWidgets()
+ }
+ case toolkit.Set:
+ if a.WidgetType == toolkit.Flag {
+ log(logNow, "TODO: set flag here", a.ActionType, a.WidgetType, a.Name)
+ log(logNow, "TODO: n.WidgetType =", n.WidgetType, "n.Name =", a.Name)
+ } else {
+ if (a.A == nil) {
+ log(logError, "TODO: Set here. a == nil", a.ActionType, "WidgetType =", a.WidgetType, "Name =", a.Name)
+ } else {
+ n.Set(a.A)
+ }
+ }
+ case toolkit.SetText:
+ n.SetText(a.S)
+ case toolkit.AddText:
+ n.AddText(a.S)
+ case toolkit.Move:
+ log(logNow, "attempt to move() =", a.ActionType, a.WidgetType, a.Name)
+ case toolkit.CloseToolkit:
+ log(logNow, "attempting to close the plugin and release stdout and stderr")
+ standardExit()
+ case toolkit.Enable:
+ if n.Visible() {
+ // widget was already shown
+ } else {
+ log(logInfo, "Setting Visable to true", a.Name)
+ n.SetVisible(true)
+ }
+ case toolkit.Disable:
+ if n.Visible() {
+ log(logInfo, "Setting Visable to false", a.Name)
+ n.SetVisible(false)
+ } else {
+ // widget was already hidden
+ }
+ default:
+ log(logError, "action() ActionType =", a.ActionType, "WidgetType =", a.WidgetType, "Name =", a.Name)
+ }
+ log(logInfo, "action() END")
+}
+
+func (n *node) AddText(text string) {
+ if (n == nil) {
+ log(logNow, "widget is nil")
+ return
+ }
+ n.vals = append(n.vals, text)
+ for i, s := range n.vals {
+ log(logNow, "AddText()", n.Name, i, s)
+ }
+ n.SetText(text)
+}
+
+func (n *node) SetText(text string) {
+ var changed bool = false
+ if (n == nil) {
+ log(logNow, "widget is nil")
+ return
+ }
+ if (n.Text != text) {
+ n.Text = text
+ changed = true
+ }
+ if (n.S != text) {
+ n.S = text
+ changed = true
+ }
+ if (! changed) {
+ return
+ }
+
+ if (n.Visible()) {
+ n.textResize()
+ n.deleteView()
+ n.showView()
+ }
+}
+
+func (n *node) Set(val any) {
+ // w := n.tk
+ log(logInfo, "Set() value =", val)
+
+ switch v := val.(type) {
+ case bool:
+ n.B = val.(bool)
+ n.setCheckbox(val.(bool))
+ case string:
+ n.SetText(val.(string))
+ case int:
+ n.I = val.(int)
+ default:
+ log(logError, "Set() unknown type =", val, v)
+ }
+}
diff --git a/gocui/showStdout.go b/gocui/showStdout.go
new file mode 100644
index 0000000..70f7cb7
--- /dev/null
+++ b/gocui/showStdout.go
@@ -0,0 +1,97 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/awesome-gocui/gocui"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+var outputW int = 180
+var outputH int = 24
+
+func moveMsg(g *gocui.Gui) {
+ mx, my := g.MousePosition()
+ if !movingMsg && (mx != initialMouseX || my != initialMouseY) {
+ movingMsg = true
+ }
+ g.SetView("msg", mx-xOffset, my-yOffset, mx-xOffset+outputW, my-yOffset+outputH + me.FramePadH, 0)
+ g.SetViewOnBottom("msg")
+}
+
+func showMsg(g *gocui.Gui, v *gocui.View) error {
+ var l string
+ var err error
+
+ log(true, "showMsg() v.name =", v.Name())
+ if _, err := g.SetCurrentView(v.Name()); err != nil {
+ return err
+ }
+
+ _, cy := v.Cursor()
+ if l, err = v.Line(cy); err != nil {
+ l = ""
+ }
+
+ makeOutputWidget(g, l)
+ return nil
+}
+
+func makeOutputWidget(g *gocui.Gui, stringFromMouseClick string) *gocui.View {
+ maxX, maxY := g.Size()
+
+ if (me.rootNode == nil) {
+ // keep skipping this until the binary tree is initialized
+ return nil
+ }
+
+ if (me.logStdout == nil) {
+ a := new(toolkit.Action)
+ a.Name = "stdout"
+ a.Text = "stdout"
+ a.WidgetType = toolkit.Stdout
+ a.WidgetId = -3
+ a.ParentId = 0
+ n := addNode(a)
+ me.logStdout = n
+ me.logStdout.tk.gocuiSize.w0 = maxX - 32
+ me.logStdout.tk.gocuiSize.h0 = maxY/2
+ me.logStdout.tk.gocuiSize.w1 = me.logStdout.tk.gocuiSize.w0 + outputW
+ me.logStdout.tk.gocuiSize.h1 = me.logStdout.tk.gocuiSize.h0 + outputH
+ }
+ v, err := g.View("msg")
+ if (v == nil) {
+ log(true, "makeoutputwindow() this is supposed to happen. v == nil", err)
+ } else {
+ log(true, "makeoutputwindow() msg != nil. WTF now? err =", err)
+ }
+
+ // help, err := g.SetView("help", maxX-32, 0, maxX-1, 13, 0)
+ // v, err = g.SetView("msg", 3, 3, 30, 30, 0)
+
+ v, err = g.SetView("msg", maxX-32, maxY/2, maxX/2+outputW, maxY/2+outputH, 0)
+ if errors.Is(err, gocui.ErrUnknownView) {
+ log(true, "makeoutputwindow() this is supposed to happen?", err)
+ }
+
+ if (err != nil) {
+ log(true, "makeoutputwindow() create output window failed", err)
+ return nil
+ }
+
+ if (v == nil) {
+ log(true, "makeoutputwindow() msg == nil. WTF now? err =", err)
+ return nil
+ } else {
+ me.logStdout.tk.v = v
+ }
+
+ v.Clear()
+ v.SelBgColor = gocui.ColorCyan
+ v.SelFgColor = gocui.ColorBlack
+ fmt.Fprintln(v, "figure out how to capture STDOUT to here\n" + stringFromMouseClick)
+ g.SetViewOnBottom("msg")
+ // g.SetViewOnBottom(v.Name())
+ return v
+}
diff --git a/gocui/structs.go b/gocui/structs.go
new file mode 100644
index 0000000..d1fe381
--- /dev/null
+++ b/gocui/structs.go
@@ -0,0 +1,224 @@
+// LICENSE: same as the go language itself
+// Copyright 2023 WIT.COM
+
+// all structures and variables are local (aka lowercase)
+// since the plugin should be isolated to access only
+// by functions() to insure everything here is run
+// inside a dedicated goroutine
+
+package main
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "sync"
+ "strings"
+ "github.com/awesome-gocui/gocui"
+)
+
+// It's probably a terrible idea to call this 'me'
+var me config
+
+var showDebug bool = true
+var showHelp bool = true
+var redoWidgets bool = true
+
+// This is the window that is currently active
+var currentWindow *node
+
+type config struct {
+ baseGui *gocui.Gui // the main gocui handle
+ rootNode *node // the base of the binary tree. it should have id == 0
+
+ ctrlDown *node // shown if you click the mouse when the ctrl key is pressed
+ currentWindow *node // this is the current tab or window to show
+ logStdout *node // where to show STDOUT
+ helpLabel *gocui.View
+ ddview *node // the gocui view to select dropdrown lists
+ ddClicked bool // the dropdown menu view was clicked
+ ddNode *node // the dropdown menu is for this widget
+
+ /*
+ // this is the channel we send user events like
+ // mouse clicks or keyboard events back to the program
+ callback chan toolkit.Action
+
+ // this is the channel we get requests to make widgets
+ pluginChan chan toolkit.Action
+ */
+
+ // When the widget has a frame, like a button, it adds 2 lines runes on each side
+ // so you need 3 char spacing in each direction to not have them overlap
+ // the amount of padding when there is a frame
+ FramePadW int `default:"1" dense:"0"`
+ FramePadH int `default:"1" dense:"0"`
+
+ PadW int `default:"1" dense:"0"`
+ PadH int `default:"1" dense:"0"`
+
+ // how far down to start Window or Tab headings
+ WindowW int `default:"8" dense:"0"`
+ WindowH int `default:"-1"`
+ TabW int `default:"5" dense:"0"`
+ TabH int `default:"1" dense:"0"`
+
+ // additional amount of space to put between window & tab widgets
+ WindowPadW int `default:"8" dense:"0"`
+ TabPadW int `default:"4" dense:"0"`
+
+ // additional amount of space to indent on a group
+ GroupPadW int `default:"6" dense:"2"`
+
+ // the raw beginning of each window (or tab)
+ RawW int `default:"1"`
+ RawH int `default:"5"`
+
+ // offset for the hidden widgets
+ FakeW int `default:"20"`
+
+ padded bool // add space between things like buttons
+ bookshelf bool // do you want things arranged in the box like a bookshelf or a stack?
+ canvas bool // if set to true, the windows are a raw canvas
+ menubar bool // for windows
+ stretchy bool // expand things like buttons to the maximum size
+ margin bool // add space around the frames of windows
+
+ // writeMutex protects locks the write process
+ writeMutex sync.Mutex
+
+ // used for listWidgets() debugging
+ depth int
+}
+
+// deprecate these
+var (
+ initialMouseX, initialMouseY, xOffset, yOffset int
+ globalMouseDown, msgMouseDown, movingMsg bool
+)
+
+// this is the gocui way
+// corner starts at in the upper left corner
+type rectType struct {
+ w0, h0, w1, h1 int // left top right bottom
+}
+
+func (r *rectType) Width() int {
+ return r.w1 - r.w0
+}
+
+func (r *rectType) Height() int {
+ return r.h1 - r.h0
+}
+
+type guiWidget struct {
+ // the gocui package variables
+ v *gocui.View // this is nil if the widget is not displayed
+ cuiName string // what gocui uses to reference the widget
+
+ // the logical size of the widget
+ // For example, 40x12 would be the center of a normal terminal
+ // size rectType
+
+ // the actual gocui display view of this widget
+ // sometimes this isn't visible like with a Box or Grid
+ gocuiSize rectType
+
+ isCurrent bool // is this the currently displayed Window or Tab?
+ isFake bool // widget types like 'box' are 'false'
+
+ // used to track the size of grids
+ widths map[int]int // how tall each row in the grid is
+ heights map[int]int // how wide each column in the grid is
+
+ tainted bool
+ frame bool
+
+ // for a window, this is currently selected tab
+ selectedTab *node
+
+ // what color to use
+ color *colorT
+}
+
+// from the gocui devs:
+// Write appends a byte slice into the view's internal buffer. Because
+// View implements the io.Writer interface, it can be passed as parameter
+// of functions like fmt.Fprintf, fmt.Fprintln, io.Copy, etc. Clear must
+// be called to clear the view's buffer.
+
+func (w *guiWidget) Write(p []byte) (n int, err error) {
+ w.tainted = true
+ me.writeMutex.Lock()
+ defer me.writeMutex.Unlock()
+ if (me.logStdout.tk.v == nil) {
+ // optionally write the output to /tmp
+ s := fmt.Sprint(string(p))
+ s = strings.TrimSuffix(s, "\n")
+ fmt.Fprintln(outf, s)
+ v, _ := me.baseGui.View("msg")
+ if (v != nil) {
+ // fmt.Fprintln(outf, "found msg")
+ me.logStdout.tk.v = v
+ }
+ } else {
+ // display the output in the gocui window
+ me.logStdout.tk.v.Clear()
+
+ s := fmt.Sprint(string(p))
+ s = strings.TrimSuffix(s, "\n")
+ tmp := strings.Split(s, "\n")
+ outputS = append(outputS, tmp...)
+ if (len(outputS) > outputH) {
+ l := len(outputS) - outputH
+ outputS = outputS[l:]
+ }
+ fmt.Fprintln(me.logStdout.tk.v, strings.Join(outputS, "\n"))
+ }
+
+ return len(p), nil
+}
+
+func Set(ptr interface{}, tag string) error {
+ if reflect.TypeOf(ptr).Kind() != reflect.Ptr {
+ log(logError, "Set() Not a pointer", ptr, "with tag =", tag)
+ return fmt.Errorf("Not a pointer")
+ }
+
+ v := reflect.ValueOf(ptr).Elem()
+ t := v.Type()
+
+ for i := 0; i < t.NumField(); i++ {
+ defaultVal := t.Field(i).Tag.Get(tag)
+ name := t.Field(i).Name
+ // log("Set() try name =", name, "defaultVal =", defaultVal)
+ setField(v.Field(i), defaultVal, name)
+ }
+ return nil
+}
+
+func setField(field reflect.Value, defaultVal string, name string) error {
+
+ if !field.CanSet() {
+ // log("setField() Can't set value", field, defaultVal)
+ return fmt.Errorf("Can't set value\n")
+ } else {
+ log(true, "setField() Can set value", name, defaultVal)
+ }
+
+ switch field.Kind() {
+ case reflect.Int:
+ val, _ := strconv.Atoi(defaultVal)
+ field.Set(reflect.ValueOf(int(val)).Convert(field.Type()))
+ case reflect.String:
+ field.Set(reflect.ValueOf(defaultVal).Convert(field.Type()))
+ case reflect.Bool:
+ if defaultVal == "true" {
+ field.Set(reflect.ValueOf(true))
+ } else {
+ field.Set(reflect.ValueOf(false))
+ }
+ }
+
+ return nil
+}
diff --git a/gocui/tab.go b/gocui/tab.go
new file mode 100644
index 0000000..2f909ae
--- /dev/null
+++ b/gocui/tab.go
@@ -0,0 +1,109 @@
+package main
+
+// implements widgets 'Window' and 'Tab'
+
+import (
+ "strings"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func (w *guiWidget) Width() int {
+ if w.frame {
+ return w.gocuiSize.w1 - w.gocuiSize.w0
+ }
+ return w.gocuiSize.w1 - w.gocuiSize.w0 - 1
+}
+
+func (w *guiWidget) Height() int {
+ if w.frame {
+ return w.gocuiSize.h1 - w.gocuiSize.h0
+ }
+ return w.gocuiSize.h1 - w.gocuiSize.h0 - 1
+}
+
+func (n *node) gocuiSetWH(sizeW, sizeH int) {
+ w := len(n.Text)
+ lines := strings.Split(n.Text, "\n")
+ h := len(lines)
+
+ tk := n.tk
+ if tk.isFake {
+ tk.gocuiSize.w0 = sizeW
+ tk.gocuiSize.h0 = sizeH
+ tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
+ tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
+ return
+ }
+
+ if tk.frame {
+ tk.gocuiSize.w0 = sizeW
+ tk.gocuiSize.h0 = sizeH
+ tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
+ tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
+ } else {
+ tk.gocuiSize.w0 = sizeW - 1
+ tk.gocuiSize.h0 = sizeH - 1
+ tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + 1
+ tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + 1
+ }
+}
+
+func redoWindows(nextW int, nextH int) {
+ for _, n := range me.rootNode.children {
+ if n.WidgetType != toolkit.Window {
+ continue
+ }
+ w := n.tk
+ var tabs bool
+ for _, child := range n.children {
+ if (child.WidgetType == toolkit.Tab) {
+ tabs = true
+ }
+ }
+ if (tabs) {
+ // window is tabs. Don't show it as a standard button
+ w.frame = false
+ n.hasTabs = true
+ } else {
+ w.frame = false
+ n.hasTabs = false
+ }
+
+ n.gocuiSetWH(nextW, nextH)
+ n.deleteView()
+ n.showView()
+
+ sizeW := w.Width() + me.WindowPadW
+ sizeH := w.Height()
+ nextW += sizeW
+ log(logNow, "redoWindows() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, n.Name)
+
+ if n.hasTabs {
+ n.redoTabs(me.TabW, me.TabH)
+ }
+ }
+}
+
+func (p *node) redoTabs(nextW int, nextH int) {
+ for _, n := range p.children {
+ if n.WidgetType != toolkit.Tab {
+ continue
+ }
+ w := n.tk
+ w.frame = true
+
+ n.gocuiSetWH(nextW, nextH)
+ n.deleteView()
+ // setCurrentTab(n)
+ // if (len(w.cuiName) < 4) {
+ // w.cuiName = "abcd"
+ // }
+
+ n.showView()
+
+ sizeW := w.Width() + me.TabPadW
+ sizeH := w.Height()
+ log(logNow, "redoTabs() start nextW,H =", nextW, nextH, "gocuiSize.W,H =", sizeW, sizeH, n.Name)
+ nextW += sizeW
+ }
+}
diff --git a/gocui/view.go b/gocui/view.go
new file mode 100644
index 0000000..08117fc
--- /dev/null
+++ b/gocui/view.go
@@ -0,0 +1,232 @@
+package main
+
+import (
+ "fmt"
+ "errors"
+ "strconv"
+ "bufio"
+ "strings"
+
+ "github.com/awesome-gocui/gocui"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func splitLines(s string) []string {
+ var lines []string
+ sc := bufio.NewScanner(strings.NewReader(s))
+ for sc.Scan() {
+ lines = append(lines, sc.Text())
+ }
+ return lines
+}
+
+func (n *node) textResize() bool {
+ w := n.tk
+ var width, height int = 0, 0
+ var changed bool = false
+
+ for i, s := range splitLines(n.Text) {
+ log(logInfo, "textResize() len =", len(s), i, s)
+ if (width < len(s)) {
+ width = len(s)
+ }
+ height += 1
+ }
+ if (w.gocuiSize.w1 != w.gocuiSize.w0 + width + me.FramePadW) {
+ w.gocuiSize.w1 = w.gocuiSize.w0 + width + me.FramePadW
+ changed = true
+ }
+ if (w.gocuiSize.h1 != w.gocuiSize.h0 + height + me.FramePadH) {
+ w.gocuiSize.h1 = w.gocuiSize.h0 + height + me.FramePadH
+ changed = true
+ }
+ if (changed) {
+ n.showWidgetPlacement(logNow, "textResize() changed")
+ }
+ return changed
+}
+
+func (n *node) hideView() {
+ n.SetVisible(false)
+}
+
+// display's the text of the widget in gocui
+// will create a new gocui view if there isn't one or if it has been moved
+func (n *node) showView() {
+ var err error
+ w := n.tk
+
+ if (w.cuiName == "") {
+ log(logError, "showView() w.cuiName was not set for widget", w)
+ w.cuiName = strconv.Itoa(n.WidgetId)
+ }
+
+ // if the gocui element doesn't exist, create it
+ if (w.v == nil) {
+ n.recreateView()
+ }
+ x0, y0, x1, y1, err := me.baseGui.ViewPosition(w.cuiName)
+ log(logInfo, "showView() w.v already defined for widget", n.Name, err)
+
+ // n.smartGocuiSize()
+ changed := n.textResize()
+
+ if (changed) {
+ log(logNow, "showView() textResize() changed. Should recreateView here wId =", w.cuiName)
+ } else {
+ log(logNow, "showView() Clear() and Fprint() here wId =", w.cuiName)
+ w.v.Clear()
+ fmt.Fprint(w.v, n.Text)
+ n.SetVisible(false)
+ n.SetVisible(true)
+ return
+ }
+
+ // if the gocui element has changed where it is supposed to be on the screen
+ // recreate it
+ if (x0 != w.gocuiSize.w0) {
+ n.recreateView()
+ return
+ }
+ if (y0 != w.gocuiSize.h0) {
+ log(logError, "showView() start hight mismatch id=", w.cuiName, "gocui h vs computed h =", w.gocuiSize.h0, y0)
+ n.recreateView()
+ return
+ }
+ if (x1 != w.gocuiSize.w1) {
+ log(logError, "showView() too wide", w.cuiName, "w,w", w.gocuiSize.w1, x1)
+ n.recreateView()
+ return
+ }
+ if (y1 != w.gocuiSize.h1) {
+ log(logError, "showView() too high", w.cuiName, "h,h", w.gocuiSize.h1, y1)
+ n.recreateView()
+ return
+ }
+
+ n.SetVisible(true)
+}
+
+// create or recreate the gocui widget visible
+// deletes the old view if it exists and recreates it
+func (n *node) recreateView() {
+ var err error
+ w := n.tk
+ log(logError, "recreateView() START", n.WidgetType, n.Name)
+ if (me.baseGui == nil) {
+ log(logError, "recreateView() ERROR: me.baseGui == nil", w)
+ return
+ }
+
+ // this deletes the button from gocui
+ me.baseGui.DeleteView(w.cuiName)
+ w.v = nil
+
+ if (n.Name == "CLOUDFLARE_EMAIL") {
+ n.showWidgetPlacement(logNow, "n.Name=" + n.Name + " n.Text=" + n.Text + " " + w.cuiName)
+ n.dumpWidget("jwc")
+ n.textResize()
+ n.showWidgetPlacement(logNow, "n.Name=" + n.Name + " n.Text=" + n.Text + " " + w.cuiName)
+ }
+
+ a := w.gocuiSize.w0
+ b := w.gocuiSize.h0
+ c := w.gocuiSize.w1
+ d := w.gocuiSize.h1
+
+ w.v, err = me.baseGui.SetView(w.cuiName, a, b, c, d, 0)
+ if err == nil {
+ n.showWidgetPlacement(logError, "recreateView()")
+ log(logError, "recreateView() internal plugin error err = nil")
+ return
+ }
+ if !errors.Is(err, gocui.ErrUnknownView) {
+ n.showWidgetPlacement(logError, "recreateView()")
+ log(logError, "recreateView() internal plugin error error.IS()", err)
+ return
+ }
+
+ // this sets up the keybinding for the name of the window
+ // does this really need to be done? I think we probably already
+ // know everything about where all the widgets are so we could bypass
+ // the gocui package and just handle all the mouse events internally here (?)
+ // for now, the w.v.Name is a string "1", "2", "3", etc from the widgetId
+
+ // set the binding for this gocui view now that it has been created
+ // gocui handles overlaps of views so it will run on the view that is clicked on
+ me.baseGui.SetKeybinding(w.v.Name(), gocui.MouseLeft, gocui.ModNone, click)
+
+ // this actually sends the text to display to gocui
+ w.v.Wrap = true
+ w.v.Frame = w.frame
+ w.v.Clear()
+ fmt.Fprint(w.v, n.Text)
+ // n.showWidgetPlacement(logNow, "n.Name=" + n.Name + " n.Text=" + n.Text + " " + w.cuiName)
+ // n.dumpWidget("jwc 2")
+
+ // if you don't do this here, it will be black & white only
+ if (w.color != nil) {
+ w.v.FrameColor = w.color.frame
+ w.v.FgColor = w.color.fg
+ w.v.BgColor = w.color.bg
+ w.v.SelFgColor = w.color.selFg
+ w.v.SelBgColor = w.color.selBg
+ }
+ if (n.Name == "CLOUDFLARE_EMAIL") {
+ n.showWidgetPlacement(logNow, "n.Name=" + n.Name + " n.Text=" + n.Text + " " + w.cuiName)
+ n.dumpTree(true)
+ }
+ log(logError, "recreateView() END")
+}
+
+func (n *node) hideWidgets() {
+ w := n.tk
+ w.isCurrent = false
+ switch n.WidgetType {
+ case toolkit.Root:
+ case toolkit.Flag:
+ case toolkit.Window:
+ case toolkit.Box:
+ case toolkit.Grid:
+ default:
+ n.hideView()
+ }
+ for _, child := range n.children {
+ child.hideWidgets()
+ }
+}
+
+func (n *node) hideFake() {
+ w := n.tk
+ if (w.isFake) {
+ n.hideView()
+ }
+ for _, child := range n.children {
+ child.hideFake()
+ }
+}
+
+func (n *node) showFake() {
+ w := n.tk
+ if (w.isFake) {
+ n.setFake()
+ n.showWidgetPlacement(logNow, "showFake:")
+ n.showView()
+ }
+ for _, child := range n.children {
+ child.showFake()
+ }
+}
+
+func (n *node) showWidgets() {
+ w := n.tk
+ if (w.isFake) {
+ // don't display by default
+ } else {
+ n.showWidgetPlacement(logInfo, "current:")
+ n.showView()
+ }
+ for _, child := range n.children {
+ child.showWidgets()
+ }
+}
diff --git a/gocui/widget.go b/gocui/widget.go
new file mode 100644
index 0000000..87856e4
--- /dev/null
+++ b/gocui/widget.go
@@ -0,0 +1,147 @@
+package main
+
+import (
+ "strconv"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func initWidget(n *node) *guiWidget {
+ var w *guiWidget
+ w = new(guiWidget)
+ // Set(w, "default")
+
+ w.frame = true
+
+ // set the name used by gocui to the id
+ w.cuiName = strconv.Itoa(n.WidgetId)
+
+ if n.WidgetType == toolkit.Root {
+ log(logInfo, "setupWidget() FOUND ROOT w.id =", n.WidgetId)
+ n.WidgetId = 0
+ me.rootNode = n
+ return w
+ }
+
+ if (n.WidgetType == toolkit.Box) {
+ if (n.B) {
+ n.horizontal = true
+ } else {
+ n.horizontal = false
+ }
+ }
+
+ if (n.WidgetType == toolkit.Grid) {
+ w.widths = make(map[int]int) // how tall each row in the grid is
+ w.heights = make(map[int]int) // how wide each column in the grid is
+ }
+
+ return w
+}
+
+func setupCtrlDownWidget() {
+ a := new(toolkit.Action)
+ a.Name = "ctrlDown"
+ a.WidgetType = toolkit.Dialog
+ a.WidgetId = -1
+ a.ParentId = 0
+ n := addNode(a)
+
+ me.ctrlDown = n
+}
+
+func (n *node) deleteView() {
+ w := n.tk
+ if (w.v != nil) {
+ w.v.Visible = false
+ return
+ }
+ // make sure the view isn't really there
+ me.baseGui.DeleteView(w.cuiName)
+ w.v = nil
+}
+
+// searches the binary tree for a WidgetId
+func (n *node) findWidgetName(name string) *node {
+ if (n == nil) {
+ return nil
+ }
+
+ if n.tk.cuiName == name {
+ return n
+ }
+
+ for _, child := range n.children {
+ newN := child.findWidgetName(name)
+ if (newN != nil) {
+ return newN
+ }
+ }
+ return nil
+}
+
+func (n *node) IsCurrent() bool {
+ w := n.tk
+ if (n.WidgetType == toolkit.Tab) {
+ return w.isCurrent
+ }
+ if (n.WidgetType == toolkit.Window) {
+ return w.isCurrent
+ }
+ if (n.WidgetType == toolkit.Root) {
+ return false
+ }
+ return n.parent.IsCurrent()
+}
+
+func (n *node) Visible() bool {
+ if (n == nil) {
+ return false
+ }
+ if (n.tk == nil) {
+ return false
+ }
+ if (n.tk.v == nil) {
+ return false
+ }
+ return n.tk.v.Visible
+}
+
+func (n *node) SetVisible(b bool) {
+ if (n == nil) {
+ return
+ }
+ if (n.tk == nil) {
+ return
+ }
+ if (n.tk.v == nil) {
+ return
+ }
+ n.tk.v.Visible = b
+}
+
+func addDropdown() *node {
+ n := new(node)
+ n.WidgetType = toolkit.Flag
+ n.WidgetId = -2
+ n.ParentId = 0
+
+ // copy the data from the action message
+ n.Name = "DropBox"
+ n.Text = "DropBox text"
+
+ // store the internal toolkit information
+ n.tk = new(guiWidget)
+ n.tk.frame = true
+
+ // set the name used by gocui to the id
+ n.tk.cuiName = "-1 dropbox"
+
+ n.tk.color = &colorFlag
+
+ // add this new widget on the binary tree
+ n.parent = me.rootNode
+ if n.parent != nil {
+ n.parent.children = append(n.parent.children, n)
+ }
+ return n
+}
diff --git a/nocui/Makefile b/nocui/Makefile
new file mode 100644
index 0000000..acbf437
--- /dev/null
+++ b/nocui/Makefile
@@ -0,0 +1,5 @@
+all: plugin
+ ldd ../nocui.so
+
+plugin:
+ GO111MODULE="off" go build -v -x -buildmode=plugin -o ../nocui.so
diff --git a/nocui/README.md b/nocui/README.md
new file mode 100644
index 0000000..018b9ce
--- /dev/null
+++ b/nocui/README.md
@@ -0,0 +1,5 @@
+# nogui
+
+Package gui implements a abstraction layer for Go visual elements.
+
+This is a sample plugin. It's a skeleton intended to be used when making a new toolkit plugin.
diff --git a/nocui/action.go b/nocui/action.go
new file mode 100644
index 0000000..86645cd
--- /dev/null
+++ b/nocui/action.go
@@ -0,0 +1,153 @@
+package main
+
+import (
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func (n *node) show(b bool) {
+}
+
+func (n *node) enable(b bool) {
+}
+
+func (n *node) pad(at toolkit.ActionType) {
+ switch n.WidgetType {
+ case toolkit.Group:
+ switch at {
+ case toolkit.Margin:
+ // SetMargined(true)
+ case toolkit.Unmargin:
+ // SetMargined(false)
+ case toolkit.Pad:
+ // SetMargined(true)
+ case toolkit.Unpad:
+ // SetMargined(false)
+ }
+ case toolkit.Tab:
+ case toolkit.Window:
+ case toolkit.Grid:
+ case toolkit.Box:
+ case toolkit.Textbox:
+ log(logError, "TODO: implement ActionType =", at)
+ default:
+ log(logError, "TODO: implement pad() for", at)
+ }
+}
+
+func (n *node) move(newParent *node) {
+ p := n.parent
+
+ switch p.WidgetType {
+ case toolkit.Group:
+ case toolkit.Tab:
+ // tabSetMargined(tParent.uiTab, true)
+ case toolkit.Window:
+ // t.uiWindow.SetBorderless(false)
+ case toolkit.Grid:
+ // t.uiGrid.SetPadded(true)
+ case toolkit.Box:
+ log(logInfo, "TODO: move() where =", p.ParentId)
+ log(logInfo, "TODO: move() for widget =", n.WidgetId)
+ default:
+ log(logError, "TODO: need to implement move() for type =", n.WidgetType)
+ log(logError, "TODO: need to implement move() for where =", p.ParentId)
+ log(logError, "TODO: need to implement move() for widget =", n.WidgetId)
+ }
+}
+
+func (n *node) Delete() {
+ p := n.parent
+ log(logNow, "uiDelete()", n.WidgetId, "to", p.WidgetId)
+
+ switch p.WidgetType {
+ case toolkit.Group:
+ // tParent.uiGroup.SetMargined(true)
+ case toolkit.Tab:
+ // tabSetMargined(tParent.uiTab, true)
+ case toolkit.Window:
+ // t.uiWindow.SetBorderless(false)
+ case toolkit.Grid:
+ // t.uiGrid.SetPadded(true)
+ case toolkit.Box:
+ log(logNow, "tWidget.boxC =", p.Name)
+ log(logNow, "is there a tParent parent? =", p.parent)
+ // this didn't work:
+ // tWidget.uiControl.Disable()
+ // sleep(.8)
+ // tParent.uiBox.Append(tWidget.uiControl, stretchy)
+ default:
+ log(logError, "TODO: need to implement uiDelete() for widget =", n.WidgetId, n.WidgetType)
+ log(logError, "TODO: need to implement uiDelete() for parent =", p.WidgetId, p.WidgetType)
+ }
+}
+
+func doAction(a *toolkit.Action) {
+ log(logNow, "doAction() START a.ActionType =", a.ActionType)
+ log(logNow, "doAction() START a.S =", a.S)
+
+ if (a.ActionType == toolkit.InitToolkit) {
+ // TODO: make sure to only do this once
+ // go uiMain.Do(func() {
+ // ui.Main(demoUI)
+ // go catchActionChannel()
+ // })
+ // try doing this on toolkit load in init()
+ return
+ }
+
+ log(logNow, "doAction() START a.WidgetId =", a.WidgetId, "a.ParentId =", a.ParentId)
+ switch a.WidgetType {
+ case toolkit.Root:
+ me.rootNode = addNode(a)
+ log(logNow, "doAction() found rootNode")
+ return
+ case toolkit.Flag:
+ // flag(&a)
+ return
+ }
+
+ n := me.rootNode.findWidgetId(a.WidgetId)
+
+ switch a.ActionType {
+ case toolkit.Add:
+ addNode(a)
+ case toolkit.Show:
+ n.show(true)
+ case toolkit.Hide:
+ n.show(false)
+ case toolkit.Enable:
+ n.enable(true)
+ case toolkit.Disable:
+ n.enable(false)
+ case toolkit.Get:
+ // n.setText(a.S)
+ case toolkit.GetText:
+ switch a.WidgetType {
+ case toolkit.Textbox:
+ a.S = n.S
+ }
+ case toolkit.Set:
+ // n.setText(a.S)
+ case toolkit.SetText:
+ // n.setText(a.S)
+ case toolkit.AddText:
+ // n.setText(a.S)
+ case toolkit.Margin:
+ n.pad(toolkit.Unmargin)
+ case toolkit.Unmargin:
+ n.pad(toolkit.Margin)
+ case toolkit.Pad:
+ n.pad(toolkit.Pad)
+ case toolkit.Unpad:
+ n.pad(toolkit.Unpad)
+ case toolkit.Delete:
+ n.Delete()
+ case toolkit.Move:
+ log(logNow, "doAction() attempt to move() =", a.ActionType, a.WidgetType)
+ newParent := me.rootNode.findWidgetId(a.ParentId)
+ n.move(newParent)
+ default:
+ log(logError, "doAction() Unknown =", a.ActionType, a.WidgetType)
+ }
+ log(logInfo, "doAction() END =", a.ActionType, a.WidgetType)
+}
diff --git a/nocui/common.go b/nocui/common.go
new file mode 100644
index 0000000..a8d9684
--- /dev/null
+++ b/nocui/common.go
@@ -0,0 +1,166 @@
+package main
+
+/*
+ These code should be common to all gui plugins
+
+ There are some helper functions that are probably going to be
+ the same everywhere. Mostly due to handling the binary tree structure
+ and the channel communication
+
+ For now, it's just a symlink to the 'master' version in
+ ./toolkit/nocui/common.go
+*/
+
+import (
+ "go.wit.com/gui/gui/toolkit"
+)
+
+// this is the channel we send user events like
+// mouse clicks or keyboard events back to the program
+var callback chan toolkit.Action
+
+// this is the channel we get requests to make widgets
+var pluginChan chan toolkit.Action
+
+type node struct {
+ parent *node
+ children []*node
+
+ WidgetId int // widget ID
+ WidgetType toolkit.WidgetType
+ ParentId int // parent ID
+
+ Name string
+ Text string
+
+ // This is how the values are passed back and forth
+ // values from things like checkboxes & dropdown's
+ B bool
+ I int
+ S string
+
+ A any // switch to this or deprecate this? pros/cons?
+
+ // This is used for things like a slider(0,100)
+ X int
+ Y int
+
+ // This is for the grid size & widget position
+ W int
+ H int
+ AtW int
+ AtH int
+
+ vals []string // dropdown menu items
+
+ // horizontal=true means layout widgets like books on a bookshelf
+ // horizontal=false means layout widgets like books in a stack
+ horizontal bool `default:false`
+
+ hasTabs bool // does the window have tabs?
+ currentTab bool // the visible tab
+
+ // the internal plugin toolkit structure
+ // in the gtk plugin, it has gtk things like margin & border settings
+ // in the text console one, it has text console things like colors for menus & buttons
+ tk *guiWidget
+}
+
+// searches the binary tree for a WidgetId
+func (n *node) findWidgetId(id int) *node {
+ if (n == nil) {
+ return nil
+ }
+
+ if n.WidgetId == id {
+ return n
+ }
+
+ for _, child := range n.children {
+ newN := child.findWidgetId(id)
+ if (newN != nil) {
+ return newN
+ }
+ }
+ return nil
+}
+
+func (n *node) doUserEvent() {
+ if (callback == nil) {
+ log(logError, "doUserEvent() callback == nil", n.WidgetId)
+ return
+ }
+ var a toolkit.Action
+ a.WidgetId = n.WidgetId
+ a.Name = n.Name
+ a.Text = n.Text
+ a.S = n.S
+ a.I = n.I
+ a.B = n.B
+ a.ActionType = toolkit.User
+ log(logInfo, "doUserEvent() START: send a user event to the callback channel")
+ callback <- a
+ log(logInfo, "doUserEvent() END: sent a user event to the callback channel")
+ return
+}
+
+func addNode(a *toolkit.Action) *node {
+ n := new(node)
+ n.WidgetType = a.WidgetType
+ n.WidgetId = a.WidgetId
+ n.ParentId = a.ParentId
+
+ // copy the data from the action message
+ n.Name = a.Name
+ n.Text = a.Text
+ n.I = a.I
+ n.S = a.S
+ n.B = a.B
+
+ n.X = a.X
+ n.Y = a.Y
+
+ n.W = a.W
+ n.H = a.H
+ n.AtW = a.AtW
+ n.AtH = a.AtH
+
+ // store the internal toolkit information
+ n.tk = initWidget(n)
+ // n.tk = new(guiWidget)
+
+ if (a.WidgetType == toolkit.Root) {
+ log(logInfo, "addNode() Root")
+ return n
+ }
+
+ if (me.rootNode.findWidgetId(a.WidgetId) != nil) {
+ log(logError, "addNode() WidgetId already exists", a.WidgetId)
+ return me.rootNode.findWidgetId(a.WidgetId)
+ }
+
+ // add this new widget on the binary tree
+ n.parent = me.rootNode.findWidgetId(a.ParentId)
+ if n.parent != nil {
+ n.parent.children = append(n.parent.children, n)
+ //w := n.tk
+ //w.parent = n.parent.tk
+ //w.parent.children = append(w.parent.children, w)
+ }
+ return n
+}
+
+// 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 toolkit.Action) {
+ callback = guiCallback
+}
+
+func PluginChannel() chan toolkit.Action {
+ return pluginChan
+}
diff --git a/nocui/event.go b/nocui/event.go
new file mode 100644
index 0000000..17d262a
--- /dev/null
+++ b/nocui/event.go
@@ -0,0 +1,47 @@
+package main
+
+import (
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func (n *node) doWidgetClick() {
+ switch n.WidgetType {
+ case toolkit.Root:
+ // THIS IS THE BEGINING OF THE LAYOUT
+ // rootNode.nextW = 0
+ // rootNode.nextH = 0
+ // rootNode.redoTabs(true)
+ case toolkit.Flag:
+ // me.rootNode.redoColor(true)
+ // rootNode.dumpTree(true)
+ case toolkit.Window:
+ // setCurrentWindow(w)
+ n.doUserEvent()
+ case toolkit.Tab:
+ // setCurrentTab(w)
+ case toolkit.Group:
+ // n.placeWidgets()
+ // n.toggleTree()
+ case toolkit.Checkbox:
+ if (n.B) {
+ // n.setCheckbox(false)
+ } else {
+ // n.setCheckbox(true)
+ }
+ n.doUserEvent()
+ case toolkit.Grid:
+ // rootNode.hideWidgets()
+ // n.placeGrid()
+ // n.showWidgets()
+ case toolkit.Box:
+ // n.showWidgetPlacement(logNow, "drawTree()")
+ if (n.B) {
+ log(true, "BOX IS HORIZONTAL", n.Name)
+ } else {
+ log(true, "BOX IS VERTICAL", n.Name)
+ }
+ case toolkit.Button:
+ n.doUserEvent()
+ default:
+ }
+}
diff --git a/nocui/log.go b/nocui/log.go
new file mode 100644
index 0000000..0dc05ca
--- /dev/null
+++ b/nocui/log.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+ witlog "go.wit.com/log"
+)
+
+// various debugging flags
+var logNow bool = true // useful for active development
+var logError bool = true
+var logWarn bool = false
+var logInfo bool = false
+var logVerbose bool = false
+
+var outputS []string
+
+func log(b bool, a ...any) {
+ witlog.Log(b, a...)
+}
+
+func sleep(a ...any) {
+ witlog.Sleep(a...)
+}
+
+func exit(a ...any) {
+ witlog.Exit(a...)
+}
diff --git a/nocui/main.go b/nocui/main.go
new file mode 100644
index 0000000..15f2954
--- /dev/null
+++ b/nocui/main.go
@@ -0,0 +1,55 @@
+package main
+
+import (
+ "sync"
+ "go.wit.com/gui/gui/toolkit"
+)
+
+var muAction sync.Mutex
+
+func catchActionChannel() {
+ log(logNow, "catchActionChannel() START")
+ for {
+ log(logNow, "catchActionChannel() for loop")
+ select {
+ case a := <-pluginChan:
+ log(logNow, "catchActionChannel() SELECT widget id =", a.WidgetId, a.Name)
+ log(logNow, "catchActionChannel() STUFF", a.WidgetId, a.ActionType, a.WidgetType)
+ muAction.Lock()
+ doAction(&a)
+ muAction.Unlock()
+ log(logNow, "catchActionChannel() STUFF END", a.WidgetId, a.ActionType, a.WidgetType)
+ }
+ }
+}
+
+/*
+// 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 toolkit.Action) {
+ callback = guiCallback
+}
+
+func PluginChannel() chan toolkit.Action {
+ return pluginChan
+}
+*/
+
+// This is important. This sets the defaults for the gui. Without this, there isn't correct padding, etc
+func init() {
+ log(logNow, "Init() START")
+ log(logInfo, "Init()")
+
+ // andlabs = make(map[int]*andlabsT)
+ pluginChan = make(chan toolkit.Action, 1)
+
+ log(logNow, "Init() start channel reciever")
+ go catchActionChannel()
+ go simpleStdin()
+ log(logNow, "Init() END")
+}
diff --git a/nocui/stdin.go b/nocui/stdin.go
new file mode 100644
index 0000000..f1c6537
--- /dev/null
+++ b/nocui/stdin.go
@@ -0,0 +1,80 @@
+package main
+
+import (
+ "os"
+ "fmt"
+ "bufio"
+ "strings"
+ "strconv"
+
+ "go.wit.com/gui/gui/toolkit"
+)
+
+func simpleStdin() {
+ scanner := bufio.NewScanner(os.Stdin)
+ for scanner.Scan() {
+ s := scanner.Text()
+ s = strings.TrimSuffix(s, "\n")
+ switch s {
+ case "l":
+ log(true, "list widgets")
+ me.rootNode.listWidgets()
+ case "b":
+ log(true, "show buttons")
+ me.rootNode.showButtons()
+ case "d":
+ var a toolkit.Action
+ a.ActionType = toolkit.EnableDebug
+ callback <- a
+ case "":
+ fmt.Println("")
+ fmt.Println("Enter:")
+ fmt.Println("'l': list all widgets")
+ fmt.Println("'b': for buttons")
+ fmt.Println("'d': enable debugging")
+ default:
+ i, _ := strconv.Atoi(s)
+ log(true, "got input:", i)
+ n := me.rootNode.findWidgetId(i)
+ if (n != nil) {
+ n.dumpWidget("found node")
+ n.doUserEvent()
+ }
+ }
+ }
+}
+
+func (n *node) showButtons() {
+ if n.WidgetType == toolkit.Button {
+ n.dumpWidget("Button:")
+ }
+
+ for _, child := range n.children {
+ child.showButtons()
+ }
+}
+
+func (n *node) dumpWidget(pad string) {
+ log(true, "node:", pad, n.WidgetId, ",", n.WidgetType, ",", n.Name)
+}
+
+var depth int = 0
+
+func (n *node) listWidgets() {
+ if (n == nil) {
+ return
+ }
+
+ var pad string
+ for i := 0; i < depth; i++ {
+ pad = pad + " "
+ }
+ n.dumpWidget(pad)
+
+ for _, child := range n.children {
+ depth += 1
+ child.listWidgets()
+ depth -= 1
+ }
+ return
+}
diff --git a/nocui/structs.go b/nocui/structs.go
new file mode 100644
index 0000000..888bcb5
--- /dev/null
+++ b/nocui/structs.go
@@ -0,0 +1,17 @@
+package main
+
+// stores the raw toolkit internals
+type guiWidget struct {
+ Width int
+ Height int
+
+ c int
+ val map[int]string
+}
+
+// It's probably a terrible idea to call this 'me'
+var me config
+
+type config struct {
+ rootNode *node // the base of the binary tree. it should have id == 0
+}
diff --git a/nocui/widget.go b/nocui/widget.go
new file mode 100644
index 0000000..cf05a9e
--- /dev/null
+++ b/nocui/widget.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ "go.wit.com/gui/gui/toolkit"
+)
+
+// this is specific to the nocui toolkit
+func initWidget(n *node) *guiWidget {
+ var w *guiWidget
+ w = new(guiWidget)
+ // Set(w, "default")
+
+ if n.WidgetType == toolkit.Root {
+ log(logInfo, "setupWidget() FOUND ROOT w.id =", n.WidgetId)
+ n.WidgetId = 0
+ me.rootNode = n
+ return w
+ }
+
+ if (n.WidgetType == toolkit.Box) {
+ if (n.B) {
+ n.horizontal = true
+ } else {
+ n.horizontal = false
+ }
+ }
+
+ return w
+}