nocui: a template for porting new toolkits
Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
parent
dc7762fc16
commit
065b931106
1
Makefile
1
Makefile
|
@ -83,6 +83,7 @@ plugins: plugins-gocui plugins-andlabs
|
||||||
|
|
||||||
plugins-gocui:
|
plugins-gocui:
|
||||||
GO111MODULE="off" go build -v -x -C toolkit/gocui -buildmode=plugin -o ../gocui.so
|
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:
|
plugins-andlabs:
|
||||||
GO111MODULE="off" go build -v -x -C toolkit/andlabs -buildmode=plugin -o ../andlabs.so
|
GO111MODULE="off" go build -v -x -C toolkit/andlabs -buildmode=plugin -o ../andlabs.so
|
||||||
|
|
|
@ -85,11 +85,40 @@ type config struct {
|
||||||
writeMutex sync.Mutex
|
writeMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deprecate these
|
||||||
var (
|
var (
|
||||||
initialMouseX, initialMouseY, xOffset, yOffset int
|
initialMouseX, initialMouseY, xOffset, yOffset int
|
||||||
globalMouseDown, msgMouseDown, movingMsg bool
|
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 gocui way
|
||||||
// the logical size of the widget
|
// the logical size of the widget
|
||||||
// corner starts at in the upper left corner
|
// corner starts at in the upper left corner
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
all: plugin
|
||||||
|
ldd ../nocui.so
|
||||||
|
|
||||||
|
plugin:
|
||||||
|
GO111MODULE="off" go build -v -x -buildmode=plugin -o ../nocui.so
|
|
@ -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.
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue