summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Carr <[email protected]>2023-04-27 21:11:00 -0500
committerJeff Carr <[email protected]>2023-04-27 21:11:00 -0500
commit87b62c98a6ebd9d0e48850d1710de7f39aba41c8 (patch)
treeb5961b9d4841b20ff41ae95acac4d82459ee9d3f
parent9e285c7affa3257a46e85acde6dc64a9c781b728 (diff)
nocui: a template for porting new toolkits
Signed-off-by: Jeff Carr <[email protected]>
-rw-r--r--Makefile1
-rw-r--r--toolkit/gocui/structs.go29
-rw-r--r--toolkit/nocui/Makefile5
-rw-r--r--toolkit/nocui/README.md5
-rw-r--r--toolkit/nocui/action.go152
-rw-r--r--toolkit/nocui/common.go79
-rw-r--r--toolkit/nocui/log.go56
-rw-r--r--toolkit/nocui/main.go58
-rw-r--r--toolkit/nocui/structs.go41
9 files changed, 426 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index edac95c..2949aae 100644
--- a/Makefile
+++ b/Makefile
@@ -83,6 +83,7 @@ plugins: plugins-gocui plugins-andlabs
plugins-gocui:
GO111MODULE="off" go build -v -x -C toolkit/gocui -buildmode=plugin -o ../gocui.so
+ GO111MODULE="off" go build -v -x -C toolkit/nocui -buildmode=plugin -o ../nocui.so
plugins-andlabs:
GO111MODULE="off" go build -v -x -C toolkit/andlabs -buildmode=plugin -o ../andlabs.so
diff --git a/toolkit/gocui/structs.go b/toolkit/gocui/structs.go
index e9f89af..b2a8b20 100644
--- a/toolkit/gocui/structs.go
+++ b/toolkit/gocui/structs.go
@@ -85,11 +85,40 @@ type config struct {
writeMutex sync.Mutex
}
+// deprecate these
var (
initialMouseX, initialMouseY, xOffset, yOffset int
globalMouseDown, msgMouseDown, movingMsg bool
)
+// this is the standard binary tree structure for toolkits
+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
+
+ // the internal plugin toolkit structure
+ tk *cuiWidget
+}
+
// the gocui way
// the logical size of the widget
// corner starts at in the upper left corner
diff --git a/toolkit/nocui/Makefile b/toolkit/nocui/Makefile
new file mode 100644
index 0000000..acbf437
--- /dev/null
+++ b/toolkit/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/toolkit/nocui/README.md b/toolkit/nocui/README.md
new file mode 100644
index 0000000..018b9ce
--- /dev/null
+++ b/toolkit/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/toolkit/nocui/action.go b/toolkit/nocui/action.go
new file mode 100644
index 0000000..7b542ca
--- /dev/null
+++ b/toolkit/nocui/action.go
@@ -0,0 +1,152 @@
+package main
+
+import (
+ "git.wit.org/wit/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 action(a toolkit.Action) {
+ log(logNow, "rawAction() START a.ActionType =", a.ActionType)
+ log(logNow, "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(logNow, "rawAction() START a.WidgetId =", a.WidgetId, "a.ParentId =", a.ParentId)
+ switch a.WidgetType {
+ case toolkit.Flag:
+ // flag(&a)
+ return
+ }
+
+ n := rootNode.findWidgetId(a.WidgetId)
+
+ switch a.ActionType {
+ case toolkit.Add:
+ // QueueMain(func() {
+ // add(a)
+ // })
+ // sleep(.1)
+ 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, "rawAction() attempt to move() =", a.ActionType, a.WidgetType)
+ newParent := rootNode.findWidgetId(a.ParentId)
+ n.move(newParent)
+ default:
+ log(logError, "rawAction() Unknown =", a.ActionType, a.WidgetType)
+ }
+ log(logInfo, "rawAction() END =", a.ActionType, a.WidgetType)
+}
diff --git a/toolkit/nocui/common.go b/toolkit/nocui/common.go
new file mode 100644
index 0000000..2e0ffdc
--- /dev/null
+++ b/toolkit/nocui/common.go
@@ -0,0 +1,79 @@
+package main
+
+import (
+ "git.wit.org/wit/gui/toolkit"
+)
+
+// 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 addWidget(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
+
+ // store the internal toolkit information
+ n.tk = new(nocuiT)
+
+ if (a.WidgetType == toolkit.Root) {
+ log(logInfo, "addWidget() Root")
+ return n
+ }
+
+ if (rootNode.findWidgetId(a.WidgetId) != nil) {
+ log(logError, "addWidget() WidgetId already exists", a.WidgetId)
+ return rootNode.findWidgetId(a.WidgetId)
+ }
+
+ // add this new widget on the binary tree
+ n.parent = rootNode.findWidgetId(a.ParentId)
+ if n.parent != nil {
+ n.parent.children = append(n.parent.children, n)
+ }
+ return n
+}
+
+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
+}
diff --git a/toolkit/nocui/log.go b/toolkit/nocui/log.go
new file mode 100644
index 0000000..baf57d9
--- /dev/null
+++ b/toolkit/nocui/log.go
@@ -0,0 +1,56 @@
+package main
+
+import (
+ "io"
+// "fmt"
+// "strings"
+ witlog "git.wit.org/wit/gui/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(a ...any) {
+ witlog.Where = "wit/gocui"
+ witlog.Log(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.SetOutput(w)
+ // witlog.SetToolkitOutput(newLog)
+}
diff --git a/toolkit/nocui/main.go b/toolkit/nocui/main.go
new file mode 100644
index 0000000..7309782
--- /dev/null
+++ b/toolkit/nocui/main.go
@@ -0,0 +1,58 @@
+package main
+
+import (
+ "sync"
+ "git.wit.org/wit/gui/toolkit"
+)
+
+// this is the channel we get requests to make widgets
+var pluginChan chan toolkit.Action
+
+// the starting point of the binary tree
+var rootNode *node
+
+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()
+ action(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()
+ log(logNow, "Init() END")
+}
diff --git a/toolkit/nocui/structs.go b/toolkit/nocui/structs.go
new file mode 100644
index 0000000..ed004de
--- /dev/null
+++ b/toolkit/nocui/structs.go
@@ -0,0 +1,41 @@
+package main
+
+import "git.wit.org/wit/gui/toolkit"
+
+var callback 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
+
+ // the internal plugin toolkit structure
+ tk *nocuiT
+}
+
+// stores the raw toolkit internals
+type nocuiT struct {
+ Width int
+ Height int
+
+ c int
+ val map[int]string
+}