nocui: a template for porting new toolkits

Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2023-04-27 21:11:00 -05:00
parent dc7762fc16
commit 065b931106
9 changed files with 426 additions and 0 deletions

View File

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

View File

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

5
toolkit/nocui/Makefile Normal file
View File

@ -0,0 +1,5 @@
all: plugin
ldd ../nocui.so
plugin:
GO111MODULE="off" go build -v -x -buildmode=plugin -o ../nocui.so

5
toolkit/nocui/README.md Normal file
View File

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

152
toolkit/nocui/action.go Normal file
View File

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

79
toolkit/nocui/common.go Normal file
View File

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

56
toolkit/nocui/log.go Normal file
View File

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

58
toolkit/nocui/main.go Normal file
View File

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

41
toolkit/nocui/structs.go Normal file
View File

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