summaryrefslogtreecommitdiff
path: root/nocui
diff options
context:
space:
mode:
Diffstat (limited to 'nocui')
-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
10 files changed, 583 insertions, 0 deletions
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
+}