diff options
Diffstat (limited to 'nocui')
| -rw-r--r-- | nocui/Makefile | 5 | ||||
| -rw-r--r-- | nocui/README.md | 5 | ||||
| -rw-r--r-- | nocui/action.go | 153 | ||||
| -rw-r--r-- | nocui/common.go | 166 | ||||
| -rw-r--r-- | nocui/event.go | 47 | ||||
| -rw-r--r-- | nocui/log.go | 26 | ||||
| -rw-r--r-- | nocui/main.go | 55 | ||||
| -rw-r--r-- | nocui/stdin.go | 80 | ||||
| -rw-r--r-- | nocui/structs.go | 17 | ||||
| -rw-r--r-- | nocui/widget.go | 29 |
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 +} |
