diff options
63 files changed, 2244 insertions, 996 deletions
@@ -2,10 +2,11 @@ all: README.md @echo - @echo "make cmds # will run all the cmds" - @echo "make update # full git update" + @echo "make cmds # will run all the Example demos and commands" + @echo "make update # full git update of all the dependencies" @echo - make -C cmds/helloworld + #make -C cmds/helloworld + make plugins # should update every go dependancy (?) update: @@ -33,6 +34,9 @@ cmds-textbox: github: git push origin master git push github master + @echo + @echo check https://github.com/witorg/gui + @echo doc: GO111MODULE="off" godoc -v @@ -45,6 +49,14 @@ README.md: doc.go clean: rm -f toolkit/*.so -plugins: - # GO111MODULE="off" go build -buildmode=plugin -o toolkit/test.so toolkit/gocui/*.go +plugins: plugins-gocui plugins-andlabs2 + +plugins-gocui: make -C toolkit/gocui + +plugins-andlabs2: + cd toolkit/andlabs2/ && GO111MODULE="off" go build -buildmode=plugin -o ../andlabs2.so + # make -C toolkit/andlabs2 + +objdump: + objdump -t toolkit/andlabs.so |less diff --git a/README-goreadme.md b/README-goreadme.md index 2a0470c..c48da62 100644 --- a/README-goreadme.md +++ b/README-goreadme.md @@ -1,33 +1,26 @@ # gui -Package gui implements a abstraction layer for Go visual elements in -a cross platform and library independent way. (hopefully this is will work) - -A quick overview of the features, some general design guidelines -and principles for how this package should generally work: +Package gui implements a abstraction layer for Go visual elements. Definitions: ```go -* Toolkit: the underlying library (MacOS gui, Windows gui, gtk, qt, etc) -* Node: A binary tree of all the underlying GUI toolkit elements +* Toolkit: the underlying GUI library (MacOS gui, Windows gui, gtk, qt, etc) +* Node: A binary tree of all the underlying widgets ``` Principles: ```go * Make code using this package simple to use -* When in doubt, search upward in the binary tree -* It's ok to guess. We will return something close. * Hide complexity internally here * Isolate the GUI toolkit -* Try to use [Wikipedia Graphical widget] names +* Widget names should try to match [Wikipedia Graphical widget] +* When in doubt, search upward in the binary tree +* It's ok to guess. Try to do something sensible. ``` -## Quick Start - -This section demonstrates how to quickly get started with spew. See the -sections below for further details on formatting and configuration options. +Quick Start ```go // This creates a simple hello world window @@ -42,6 +35,7 @@ var window *gui.Node // This is the beginning of the binary tree of widgets // go will sit here until the window exits func main() { + gui.Init() gui.Main(helloworld) } @@ -78,37 +72,23 @@ GO111MODULE="off" go build -v -x [./helloworld](./helloworld) ``` -## Toolkits +Toolkits -* Andlabs - [https://github.com/andlabs/ui](https://github.com/andlabs/ui) +```go +* andlabs - [https://github.com/andlabs/ui](https://github.com/andlabs/ui) * gocui - [https://github.com/awesome-gocui/gocui](https://github.com/awesome-gocui/gocui) +``` -The goal is to design something that will work with more than one. - -Right now, this abstraction is built on top of the go package 'andlabs/ui' -which does the cross platform support. -The next step is to intent is to allow this to work directly against GTK and QT. +The next step is to allow this to work against go-gtk and go-qt. -It should be able to add Fyne, WASM, native macos & windows, android and +TODO: Add Fyne, WASM, native macos & windows, android and hopefully also things like libSDL, faiface/pixel, slint -## Errors - -Since it is possible for custom Stringer/error interfaces to panic, spew -detects them and handles them internally by printing the panic information -inline with the output. Since spew is intended to provide deep pretty printing -capabilities on structures, it intentionally does not return any errors. - -## Debugging - -To dump variables with full newlines, indentation, type, and pointer -information this uses spew.Dump() - ## Bugs "The author's idea of friendly may differ to that of many other people." --- manpage quote from the excellent minimalistic window manager 'evilwm' +-- quote from the minimalistic window manager 'evilwm' ## References @@ -118,24 +98,12 @@ which might be useful [Wikipedia Graphical widget]: [https://en.wikipedia.org/wiki/Graphical_widget](https://en.wikipedia.org/wiki/Graphical_widget) [Github mirror]: [https://github.com/witorg/gui](https://github.com/witorg/gui) +[Federated git pull]: [https://github.com/forgefed/forgefed](https://github.com/forgefed/forgefed) ```go * [Wikipedia Graphical widget] * [Github mirror] -``` - -## Variables - -```golang -var PlugGocli *plugin.Plugin -``` - -```golang -var PlugGocliOk bool -``` - -```golang -var PlugHello *plugin.Plugin +* [Federated git pull] ``` ## Functions @@ -154,102 +122,80 @@ TODO: make this smarter once this uses toolkit/ Creates a window helpful for debugging this package -### func [DemoToolkitWindow](/example_window_demo_toolkit.go#L24) - -`func DemoToolkitWindow()` - -This creates a window that shows how the toolkit works -internally using it's raw unchanged code for the toolkit itself - -This is a way to test and see if the toolkit is working at all -right now it shows the andlabs/ui/DemoNumbersPage() - ### func [DemoWindow](/example_window_demo.go#L10) `func DemoWindow()` This creates a window that shows how this package works -### func [GetDebug](/structs.go#L24) +### func [GetDebug](/structs.go#L25) `func GetDebug() bool` -### func [GetDebugToolkit](/structs.go#L36) +### func [GetDebugToolkit](/structs.go#L37) `func GetDebugToolkit() bool` -### func [GocuiAddButton](/plugin.go#L108) - -`func GocuiAddButton(name string)` - ### func [GolangDebugWindow](/example_window_golang_debug.go#L12) `func GolangDebugWindow()` -### func [IndentPrintln](/structs.go#L216) +### func [IndentPrintln](/structs.go#L188) `func IndentPrintln(a ...interface{})` -### func [LoadPlugin](/plugin.go#L36) - -`func LoadPlugin(name string) *plugin.Plugin` +### func [Init](/main.go#L41) -### func [LookupJcarrButton](/plugin.go#L98) +`func Init()` -`func LookupJcarrButton()` +### func [LoadToolkit](/plugin.go#L50) -### func [Main](/main.go#L38) +`func LoadToolkit(name string) bool` -`func Main(f func())` - -### func [Queue](/main.go#L51) - -`func Queue(f func())` +loads and initializes a toolkit (andlabs/ui, gocui, etc) -Other goroutines must use this to access the GUI +### func [Main](/main.go#L86) -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.) -For example: gui.Queue(NewWindow()) - -### func [RunGreet](/plugin.go#L88) +`func Main(f func())` -`func RunGreet()` +This should not pass a function -### func [SetDebug](/structs.go#L28) +### func [SetDebug](/structs.go#L29) `func SetDebug(s bool)` -### func [SetDebugToolkit](/structs.go#L40) +### func [SetDebugToolkit](/structs.go#L41) `func SetDebugToolkit(s bool)` -### func [ShowDebugValues](/structs.go#L44) +### func [ShowDebugValues](/structs.go#L45) `func ShowDebugValues()` -### func [StandardClose](/main.go#L57) +### func [StandardClose](/main.go#L128) `func StandardClose(n *Node)` The window is destroyed but the application does not quit -### func [StandardExit](/main.go#L65) +### func [StandardExit](/main.go#L135) `func StandardExit(n *Node)` The window is destroyed but the application does not quit -## Types +### func [Watchdog](/watchdog.go#L16) + +`func Watchdog()` -### type [Greeter](/plugin.go#L17) +This program sits here. +If you exit here, the whole thing will os.Exit() -`type Greeter interface { ... }` +This goroutine can be used like a watchdog timer -TODO: could a protobuf work here? +## Types -### type [GuiConfig](/structs.go#L67) +### type [GuiConfig](/structs.go#L68) `type GuiConfig struct { ... }` @@ -259,19 +205,19 @@ TODO: could a protobuf work here? var Config GuiConfig ``` -### type [GuiOptions](/structs.go#L56) +### type [GuiDebug](/structs.go#L56) -`type GuiOptions struct { ... }` +`type GuiDebug struct { ... }` This struct can be used with go-arg -### type [Node](/structs.go#L117) +### type [Node](/structs.go#L87) `type Node struct { ... }` The Node is simply the name and the size of whatever GUI element exists -#### func [NewStandardWindow](/example_window_demo_toolkit.go#L7) +#### func [NewStandardWindow](/example_window_demo.go#L22) `func NewStandardWindow(title string) *Node` @@ -323,13 +269,13 @@ func main() { You get a window ``` -### type [Widget](/widget.go#L12) +### type [Symbol](/plugin.go#L17) + +`type Symbol any` -`type Widget struct { ... }` +## Sub Packages -what names should be used? This is not part of [[Graphical Widget]] -Event() seems like a good name. -Could a protobuf be used here? (Can functions be passed?) +* [toolkit](./toolkit) --- Readme created from Go doc with [goreadme](https://github.com/posener/goreadme) @@ -1,29 +1,22 @@ # gui -Package gui implements a abstraction layer for Go visual elements in -a cross platform and library independent way. (hopefully this is will work) - -A quick overview of the features, some general design guidelines -and principles for how this package should generally work: +Package gui implements a abstraction layer for Go visual elements. Definitions: -* Toolkit: the underlying library (MacOS gui, Windows gui, gtk, qt, etc) -* Node: A binary tree of all the underlying GUI toolkit elements +* Toolkit: the underlying GUI library (MacOS gui, Windows gui, gtk, qt, etc) +* Node: A binary tree of all the underlying widgets Principles: * Make code using this package simple to use -* When in doubt, search upward in the binary tree -* It's ok to guess. We will return something close. * Hide complexity internally here * Isolate the GUI toolkit -* Try to use [Wikipedia Graphical widget] names - -## Quick Start +* Widget names should try to match [Wikipedia Graphical widget] +* When in doubt, search upward in the binary tree +* It's ok to guess. Try to do something sensible. -This section demonstrates how to quickly get started with spew. See the -sections below for further details on formatting and configuration options. +Quick Start ```go // This creates a simple hello world window @@ -38,6 +31,7 @@ var window *gui.Node // This is the beginning of the binary tree of widgets // go will sit here until the window exits func main() { + gui.Init() gui.Main(helloworld) } @@ -74,34 +68,21 @@ GO111MODULE="off" go build -v -x [./helloworld](./helloworld) ``` -## Toolkits +Toolkits -The goal is to design something that will work with more than one. +* andlabs - [https://github.com/andlabs/ui](https://github.com/andlabs/ui) +* gocui - [https://github.com/awesome-gocui/gocui](https://github.com/awesome-gocui/gocui) -Right now, this abstraction is built on top of the go package 'andlabs/ui' -which does the cross platform support. -The next step is to intent is to allow this to work directly against GTK and QT. +The next step is to allow this to work against go-gtk and go-qt. -It should be able to add Fyne, WASM, native macos & windows, android and +TODO: Add Fyne, WASM, native macos & windows, android and hopefully also things like libSDL, faiface/pixel, slint -## Errors - -Since it is possible for custom Stringer/error interfaces to panic, spew -detects them and handles them internally by printing the panic information -inline with the output. Since spew is intended to provide deep pretty printing -capabilities on structures, it intentionally does not return any errors. - -## Debugging - -To dump variables with full newlines, indentation, type, and pointer -information this uses spew.Dump() - ## Bugs "The author's idea of friendly may differ to that of many other people." --- manpage quote from the excellent minimalistic window manager 'evilwm' +-- quote from the minimalistic window manager 'evilwm' ## References @@ -111,56 +92,37 @@ which might be useful * [Wikipedia Graphical widget](https://en.wikipedia.org/wiki/Graphical_widget) * [Github mirror](https://github.com/witorg/gui) +* [Federated git pull](https://github.com/forgefed/forgefed) ## Functions -### func [DebugTab](/window-debug.go#L26) - -`func DebugTab()` - -this function is used by the examples to add a tab -dynamically to the bugWin node -TODO: make this smarter once this uses toolkit/ - -### func [DebugWindow](/window-debug.go#L14) - -`func DebugWindow()` - -Creates a window helpful for debugging this package +### func [GetDebug](/structs.go#L25) -### func [DemoToolkitWindow](/window-demo-toolkit.go#L24) +`func GetDebug() bool` -`func DemoToolkitWindow()` +### func [GetDebugToolkit](/structs.go#L37) -This creates a window that shows how the toolkit works -internally using it's raw unchanged code for the toolkit itself - -This is a way to test and see if the toolkit is working at all -right now it shows the andlabs/ui/DemoNumbersPage() - -### func [DemoWindow](/window-demo.go#L10) - -`func DemoWindow()` +`func GetDebugToolkit() bool` -This creates a window that shows how this package works +### func [IndentPrintln](/structs.go#L188) -### func [GetDebugToolkit](/structs.go#L28) +`func IndentPrintln(a ...interface{})` -`func GetDebugToolkit() bool` +### func [Init](/main.go#L41) -### func [GolangDebugWindow](/window-golang-debug.go#L20) +`func Init()` -`func GolangDebugWindow()` +### func [LoadToolkit](/plugin.go#L37) -### func [IndentPrintln](/structs.go#L199) +`func LoadToolkit(name string)` -`func IndentPrintln(a ...interface{})` +loads and initializes a toolkit (andlabs/ui, gocui, etc) -### func [Main](/main.go#L31) +### func [Main](/main.go#L56) `func Main(f func())` -### func [Queue](/main.go#L42) +### func [Queue](/main.go#L77) `func Queue(f func())` @@ -171,21 +133,42 @@ other goroutines. This is due to the nature of how Linux, MacOS and Windows work (they all work differently. suprise. surprise.) For example: gui.Queue(NewWindow()) -### func [SetDebugToolkit](/structs.go#L24) +### func [SetDebug](/structs.go#L29) + +`func SetDebug(s bool)` + +### func [SetDebugToolkit](/structs.go#L41) `func SetDebugToolkit(s bool)` -### func [ShowDebugValues](/structs.go#L32) +### func [ShowDebugValues](/structs.go#L45) `func ShowDebugValues()` -### func [StandardClose](/window-golang-debug.go#L12) +### func [StandardClose](/main.go#L83) `func StandardClose(n *Node)` +The window is destroyed but the application does not quit + +### func [StandardExit](/main.go#L90) + +`func StandardExit(n *Node)` + +The window is destroyed but the application does not quit + +### func [Watchdog](/watchdog.go#L16) + +`func Watchdog()` + +This program sits here. +If you exit here, the whole thing will os.Exit() + +This goroutine can be used like a watchdog timer + ## Types -### type [GuiConfig](/structs.go#L56) +### type [GuiConfig](/structs.go#L68) `type GuiConfig struct { ... }` @@ -195,20 +178,18 @@ For example: gui.Queue(NewWindow()) var Config GuiConfig ``` -### type [GuiOptions](/structs.go#L44) +### type [GuiOptions](/structs.go#L56) `type GuiOptions struct { ... }` -### type [Node](/structs.go#L104) +This struct can be used with go-arg + +### type [Node](/structs.go#L87) `type Node struct { ... }` The Node is simply the name and the size of whatever GUI element exists -#### func [NewStandardWindow](/window-demo-toolkit.go#L7) - -`func NewStandardWindow(title string) *Node` - #### func [NewWindow](/window.go#L15) `func NewWindow() *Node` @@ -220,46 +201,15 @@ it can be passed via the 'andlabs/ui' queue which, because it is cross platform, must pass UI changes into the OS threads (that is my guess). -This example demonstrates how to create a NewWindow() - -Interacting with a GUI in a cross platform fashion adds some -unusual problems. To obvuscate those, andlabs/ui starts a -goroutine that interacts with the native gui toolkits -on the Linux, MacOS, Windows, etc. - -Because of this oddity, to initialize a new window, the -function is not passed any arguements and instead passes -the information via the Config type. - -```golang -package main - -import ( - "git.wit.org/wit/gui" -) +### type [Symbol](/plugin.go#L17) -func main() { - // Define the name and size - gui.Config.Title = "WIT GUI Window 1" - gui.Config.Width = 640 - gui.Config.Height = 480 - - // Create the Window - gui.NewWindow() +`type Symbol any` -} - -``` - - Output: - -``` -You get a window -``` +## Sub Packages -### type [Widget](/structs.go#L74) +* [need-to-redo](./need-to-redo) -`type Widget int` +* [toolkit](./toolkit) --- Readme created from Go doc with [goreadme](https://github.com/posener/goreadme) @@ -3,38 +3,25 @@ package gui import "log" func (n *Node) NewButton(name string, custom func()) *Node { - if (n.toolkit == nil) { - log.Println("gui.Node.NewButton() filed node.toolkit == nil") - panic("gui.Node.NewButton() filed node.toolkit == nil") - return n - } newNode := n.New(name) - newNode.toolkit = n.toolkit.NewButton(name) - - log.Println("gui.Node.NewButton()", name) - if (PlugGocliOk) { - log.Println("wit/gui gocui is loaded", PlugGocliOk) - greeter.AddButton(name) - log.Println("GOT HERE PlugGocliOk TRUE") - } else { - log.Println("GOT HERE PlugGocliOk FALSE") - } - // TODO: this is still confusing and probably wrong. This needs to communicate through a channel - newNode.toolkit.Custom = func() { - if (Config.Options.Debug) { - log.Println("gui.Newutton() Button Clicked. Running custom() from outside toolkit START") - } + newNode.Widget.Custom = func() { + log.Println("even newer clicker() name", newNode.Widget) if (custom != nil) { custom() } else { log.Println("wit/gui No callback function is defined for button name =", name) } - if (Config.Options.Debug) { - log.Println("gui.NewButton() Button Clicked. Running custom() from outside toolkit END") + } + + for _, aplug := range allPlugins { + log.Println("gui.NewButton() aplug =", aplug.name, "name =", newNode.Widget.Name) + if (aplug.NewButton == nil) { + log.Println("\tgui.NewButton() aplug.NewButton = nil", aplug.name) + continue } + aplug.NewButton(&n.Widget, &newNode.Widget) } - newNode.custom = custom return newNode } diff --git a/checkbox.go b/checkbox.go index 0284920..0aa6aeb 100644 --- a/checkbox.go +++ b/checkbox.go @@ -2,34 +2,13 @@ package gui import "log" -import toolkit "git.wit.org/wit/gui/toolkit/andlabs" - -func (n *Node) verify() { - if (n.toolkit == nil) { - log.Println("gui/wit node.Verify(): toolkit == nil", n.Name) - panic("gui/wit node.Verify(): toolkit == nil") - } -} - func (n *Node) Checked() bool { n.Dump() return n.checked } -func (n *Node) NewCheckbox(name string) *Node { - var newt *toolkit.Toolkit - var c *Node - - log.Println("toolkit.NewCheckbox() START", name) - - n.verify() - - // make a *Node with a *toolkit.Group - c = n.New(name + " part1") - newt = n.toolkit.NewCheckbox(name) - newt.Name = name - c.toolkit = newt - c.custom = n.custom +/* +This was the old code newt.Custom = func () { println("AM IN CALLBACK. SETTING NODE.checked START") if newt.Checked() { @@ -42,7 +21,30 @@ func (n *Node) NewCheckbox(name string) *Node { commonCallback(c) println("AM IN CALLBACK. SETTING NODE.checked END") } - c.Dump() +*/ + + +func (n *Node) NewCheckbox(name string) *Node { + newNode := n.New(name) + newNode.custom = n.custom + + newNode.Widget.Custom = func() { + log.Println("even newer clicker() name", newNode.Widget) + if (n.custom != nil) { + n.custom() + } else { + log.Println("wit/gui No callback function is defined for button name =", name) + } + } + + for _, aplug := range allPlugins { + log.Println("gui.NewCheckbox() aplug =", aplug.name, "name =", newNode.Widget.Name) + if (aplug.NewCheckbox == nil) { + log.Println("\tgui.NewCheckbox() aplug.NewCheckbox = nil", aplug.name) + continue + } + aplug.NewCheckbox(&n.Widget, &newNode.Widget) + } - return c + return newNode } diff --git a/cmds/buttonplugin/Makefile b/cmds/buttonplugin/Makefile index cf60369..faa8882 100644 --- a/cmds/buttonplugin/Makefile +++ b/cmds/buttonplugin/Makefile @@ -1,5 +1,14 @@ +# with andlabs plugin loaded: +# PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND +# 180006 jcarr 20 0 1918460 41688 31152 S 0.7 0.3 0:00.27 buttonplugin + +# with gocui plugin loaded: +# 180365 jcarr 20 0 1392668 24364 12596 S 2.0 0.2 0:00.09 buttonplugin +# + run: build ./buttonplugin >/tmp/buttonplugin.log 2>&1 + # ./buttonplugin build-release: go get -v -u -x . diff --git a/cmds/buttonplugin/main.go b/cmds/buttonplugin/main.go index cd4d770..c296280 100644 --- a/cmds/buttonplugin/main.go +++ b/cmds/buttonplugin/main.go @@ -2,48 +2,97 @@ package main import ( + "fmt" "log" + "time" "strconv" "git.wit.org/wit/gui" ) +var title string = "Demo Plugin Window" + func main() { + fmt.Println("\033]0;" + title + "\007") + // time.Sleep(5 * time.Second) + // var w *gui.Node // this doesn't seem to work captureSTDOUT() - gui.Main(buttonWindow) + // gui.LoadToolkit("default") + // panic("WTF gocui not happening") + // gui.LoadToolkit("gocui") + gui.Init() + + // buttonWindow() + go gui.Main(func () { + log.Println("START Main f()") + buttonWindow() + /* + log.Println("END NewWindow()") + log.Println("START NewGroup()") + g := w.NewGroup("new Group 22") + log.Println("END NewGroup()") + g.NewButton("asdjkl", func () { + log.Println("world") + }) + */ + log.Println("END Main f()") + // gui.StandardExit(nil) + }) + log.Println("Main() END") + time.Sleep(1 * time.Second) + gui.Watchdog() + gui.StandardExit(nil) } -var counter int = 10 +var counter int = 5 // This creates a window func buttonWindow() { var w, g *gui.Node - gui.Config.Title = "Demo Plugin Window" + gui.Config.Title = title gui.Config.Width = 640 gui.Config.Height = 480 w = gui.NewWindow() g = w.NewGroup("buttonGroup") + g.NewButton("NewButton()", func () { + log.Println("new foobar 2. Adding button 'foobar 3'") + name := "foobar " + strconv.Itoa(counter) + counter += 1 + g.NewButton(name, func () { + log.Println("Got all the way to main() name =", name) + }) + }) + + g.NewButton("NewGroup()", func () { + log.Println("new foobar 2. Adding button 'foobar 3'") + name := "neat " + strconv.Itoa(counter) + counter += 1 + g.NewGroup(name) + }) + g.NewButton("hello", func () { log.Println("world") }) - g.NewButton("RunGreet()", func () { - log.Println("world") - go gui.RunGreet() + g.NewButton("LoadToolkit(andlabs2)", func () { + gui.LoadToolkit("andlabs2") }) - g.NewButton("gui.LookupJcarrButton()", func () { - log.Println("gui.LookupJcarrButton()") - gui.LookupJcarrButton() + g.NewButton("LoadToolkit(gocui)", func () { + gui.LoadToolkit("gocui") }) - g.NewButton("new foobar 2", func () { - log.Println("new foobar 2. Adding button 'foobar 3'") - name := "foobar " + strconv.Itoa(counter) - counter += 1 - g.NewButton(name, nil) + g.NewButton("Init()", func () { + gui.Init() + }) + + g.NewButton("Main()", func () { + go gui.Main(func () { + w := gui.NewWindow() + w.NewGroup("buttonGroup") + }) }) } diff --git a/cmds/console-ui-helloworld/keybindings.go b/cmds/console-ui-helloworld/keybindings.go index fdac1ff..8c4623b 100644 --- a/cmds/console-ui-helloworld/keybindings.go +++ b/cmds/console-ui-helloworld/keybindings.go @@ -115,7 +115,7 @@ func initKeybindings(g *gocui.Gui) error { func(g *gocui.Gui, v *gocui.View) error { log.Println("help", v.Name()) tmp, _ := g.SetViewOnTop("help") - log.Println("help 2", tmp.Name(), "blah") + log.Println("help 2", tmp.Name()) // g.SetView("help", 2, 2, 30, 15, 0); g.SetCurrentView("help") // moveView(g, tmp, 0, -delta) diff --git a/cmds/debug/Makefile b/cmds/debug/Makefile index 25c994d..5f979cc 100644 --- a/cmds/debug/Makefile +++ b/cmds/debug/Makefile @@ -2,4 +2,5 @@ run: build ./debug build: - go build + # go build + GO111MODULE="off" go build diff --git a/cmds/debug/main.go b/cmds/debug/main.go index 5e3a350..c0df70b 100644 --- a/cmds/debug/main.go +++ b/cmds/debug/main.go @@ -17,7 +17,9 @@ import ( func main() { log.Println("Starting my Control Panel") - go gui.Main(helloworld) + gui.Init() +// go gui.Main(helloworld) + go gui.Main(gui.DebugWindow) // go gui.DemoToolkitWindow() watchGUI() @@ -36,9 +38,9 @@ func watchGUI() { gui.Config.Width = 800 gui.Config.Height = 300 gui.Config.Exit = myExit - gui.Queue(gui.DebugWindow) + // gui.DebugWindow() time.Sleep(1 * time.Second) - gui.Queue(gui.DebugTab) + // gui.DebugTab() } } } diff --git a/cmds/helloworld/main.go b/cmds/helloworld/main.go index 552ecb5..43aa08c 100644 --- a/cmds/helloworld/main.go +++ b/cmds/helloworld/main.go @@ -7,6 +7,7 @@ import ( ) func main() { + gui.Init() gui.Main(helloworld) } diff --git a/cmds/textbox/main.go b/cmds/textbox/main.go index faf8b86..b545d08 100644 --- a/cmds/textbox/main.go +++ b/cmds/textbox/main.go @@ -21,7 +21,7 @@ var args struct { Foo string Bar bool LogOptions - gui.GuiOptions + gui.GuiDebug } @@ -29,11 +29,13 @@ func main() { arg.MustParse(&args) fmt.Println(args.Foo, args.Bar, args.User) - gui.Config.Options.Debug = args.Debug - gui.Config.Options.DebugChange = args.DebugChange - gui.Config.Options.DebugDump = args.DebugDump - gui.Config.Options.DebugNode = args.DebugNode - gui.Config.Options.DebugTabs = args.DebugTabs + gui.Config.Debug.Debug = args.Debug + /* + gui.Config.Debug.Change = args.DebugChange + gui.Config.Debug.Dump = args.DebugDump + gui.Config.Debug.Node = args.DebugNode + gui.Config.Debug.Tabs = args.DebugTabs + */ /* f, err := os.OpenFile("/tmp/guilogfile", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) @@ -46,6 +48,7 @@ func main() { log.Println("This is a test log entry") */ + gui.Init() gui.Main(initGUI) } @@ -62,6 +65,8 @@ func initGUI() { addDemoTab(w, "A Simple Tab Demo") addDemoTab(w, "A Second Tab") + /* + TODO: add these back if (args.GuiDemo) { gui.DemoToolkitWindow() } @@ -69,6 +74,7 @@ func initGUI() { if (args.GuiDebug) { gui.DebugWindow() } + */ } func addDemoTab(window *gui.Node, title string) { @@ -80,9 +86,9 @@ func addDemoTab(window *gui.Node, title string) { g = newNode.NewGroup("group 1") dd := g.NewDropdown("demoCombo2") - dd.AddDropdown("more 1") - dd.AddDropdown("more 2") - dd.AddDropdown("more 3") + dd.AddDropdownName("more 1") + dd.AddDropdownName("more 2") + dd.AddDropdownName("more 3") dd.OnChanged = func(*gui.Node) { s := dd.GetText() tb.SetText("hello world " + args.User + "\n" + s) @@ -6,30 +6,16 @@ import "regexp" // functions for handling text related GUI elements -func (n *Node) NewLabel(text string) *Node { - // make new node here - newNode := n.New(text) - newNode.Dump() - - t := n.toolkit.NewLabel(text) - newNode.toolkit = t - - return newNode -} - func (n *Node) SetText(str string) bool { - if (Config.Options.DebugChange) { + if (Config.Debug.Change) { log.Println("gui.SetText() value =", str) } - if (n.toolkit == nil) { - return false - } - return n.toolkit.SetText(str) + return true } func (n *Node) GetText() string { - return n.toolkit.GetText() + return "not implemented" } /* @@ -65,3 +51,28 @@ func normalizeInt(s string) string { log.Println("normalizeInt() s =", clean) return clean } + +func commonCallback(n *Node) { + // TODO: make all of this common code to all the widgets + if (n.OnChanged == nil) { + if (Config.Debug.Change) { + log.Println("Not Running n.OnChanged(n) == nil") + } + } else { + if (Config.Debug.Change) { + log.Println("Running n.OnChanged(n)") + } + n.OnChanged(n) + } + + if (n.custom == nil) { + if (Config.Debug.Change) { + log.Println("Not Running n.custom(n) == nil") + } + } else { + if (Config.Debug.Change) { + log.Println("Running n.custom()") + } + n.custom() + } +} @@ -1,30 +1,23 @@ /* -Package gui implements a abstraction layer for Go visual elements in -a cross platform and library independent way. (hopefully this is will work) - -A quick overview of the features, some general design guidelines -and principles for how this package should generally work: +Package gui implements a abstraction layer for Go visual elements. Definitions: - * Toolkit: the underlying library (MacOS gui, Windows gui, gtk, qt, etc) - * Node: A binary tree of all the underlying GUI toolkit elements + * Toolkit: the underlying GUI library (MacOS gui, Windows gui, gtk, qt, etc) + * Node: A binary tree of all the underlying widgets Principles: * Make code using this package simple to use - * When in doubt, search upward in the binary tree - * It's ok to guess. We will return something close. * Hide complexity internally here * Isolate the GUI toolkit - * Try to use [Wikipedia Graphical widget] names + * Widget names should try to match [Wikipedia Graphical widget] + * When in doubt, search upward in the binary tree + * It's ok to guess. Try to do something sensible. Quick Start -This section demonstrates how to quickly get started with spew. See the -sections below for further details on formatting and configuration options. - // This creates a simple hello world window package main @@ -37,6 +30,7 @@ sections below for further details on formatting and configuration options. // go will sit here until the window exits func main() { + gui.Init() gui.Main(helloworld) } @@ -73,35 +67,19 @@ I didn't record the dependances needed Toolkits -* Andlabs - https://github.com/andlabs/ui -* gocui - https://github.com/awesome-gocui/gocui - -The goal is to design something that will work with more than one. + * andlabs - https://github.com/andlabs/ui + * gocui - https://github.com/awesome-gocui/gocui -Right now, this abstraction is built on top of the go package 'andlabs/ui' -which does the cross platform support. -The next step is to intent is to allow this to work directly against GTK and QT. +The next step is to allow this to work against go-gtk and go-qt. -It should be able to add Fyne, WASM, native macos & windows, android and +TODO: Add Fyne, WASM, native macos & windows, android and hopefully also things like libSDL, faiface/pixel, slint -Errors - -Since it is possible for custom Stringer/error interfaces to panic, spew -detects them and handles them internally by printing the panic information -inline with the output. Since spew is intended to provide deep pretty printing -capabilities on structures, it intentionally does not return any errors. - -Debugging - -To dump variables with full newlines, indentation, type, and pointer -information this uses spew.Dump() - Bugs "The author's idea of friendly may differ to that of many other people." --- manpage quote from the excellent minimalistic window manager 'evilwm' +-- quote from the minimalistic window manager 'evilwm' References @@ -111,9 +89,11 @@ which might be useful [Wikipedia Graphical widget]: https://en.wikipedia.org/wiki/Graphical_widget [Github mirror]: https://github.com/witorg/gui +[Federated git pull]: https://github.com/forgefed/forgefed * [Wikipedia Graphical widget] * [Github mirror] + * [Federated git pull] */ diff --git a/dropdown.go b/dropdown.go index 583ea25..f500c55 100644 --- a/dropdown.go +++ b/dropdown.go @@ -2,61 +2,30 @@ package gui import "log" -import toolkit "git.wit.org/wit/gui/toolkit/andlabs" - -func commonCallback(n *Node) { - // TODO: make all of this common code to all the widgets - if (n.OnChanged == nil) { - if (Config.Options.DebugChange) { - log.Println("Not Running n.OnChanged(n) == nil") - } - } else { - if (Config.Options.DebugChange) { - log.Println("Running n.OnChanged(n)") +func (n *Node) AddDropdownName(name string) { + for _, aplug := range allPlugins { + log.Println("gui.AddDropdownName() aplug =", aplug.name, "name =", name) + if (aplug.AddDropdownName == nil) { + log.Println("\tgui.AddDropdownName() aplug.NewDropdown = nil", aplug.name) + continue } - n.OnChanged(n) - } - - if (n.custom == nil) { - if (Config.Options.DebugChange) { - log.Println("Not Running n.custom(n) == nil") - } - } else { - if (Config.Options.DebugChange) { - log.Println("Running n.custom()") - } - n.custom() + aplug.AddDropdownName(&n.Widget, name) } } -func (n *Node) NewDropdown(name string) *Node { - var newT *toolkit.Toolkit - var sNode *Node - - if (Config.Options.Debug) { - log.Println("toolkit.NewDropdown() START", name) - } - - n.verify() - - sNode = n.New(name + " part1") - newT = n.toolkit.NewDropdown(name) - newT.Name = name - sNode.custom = n.custom - newT.Custom = func () { - commonCallback(sNode) - } - sNode.toolkit = newT - sNode.Dump() - // panic("checking Custom()") - - return sNode +func (n *Node) SetDropdown(i int) { } -func (n *Node) AddDropdown(name string) { - n.toolkit.AddDropdown(name) -} +func (n *Node) NewDropdown(text string) *Node { + newNode := n.New(text) -func (n *Node) SetDropdown(i int) { - n.toolkit.SetDropdown(i) + for _, aplug := range allPlugins { + log.Println("gui.NewDropdown() aplug =", aplug.name, "name =", newNode.Widget.Name) + if (aplug.NewDropdown == nil) { + log.Println("\tgui.NewDropdown() aplug.NewDropdown = nil", aplug.name) + continue + } + aplug.NewDropdown(&n.Widget, &newNode.Widget) + } + return newNode } diff --git a/example_window_debug.go b/example_window_debug.go index 00ccfec..0c6503e 100644 --- a/example_window_debug.go +++ b/example_window_debug.go @@ -41,9 +41,9 @@ func debugFlags(n *Node) { checkd = df.NewCheckbox("Debug") checkd.OnChanged = func(*Node) { - checkd.checked = checkd.toolkit.Checked() - Config.Options.Debug = checkd.checked - if (Config.Options.Debug) { + // checkd.checked = checkd.toolkit.Checked() + Config.Debug.Debug = true + if (Config.Debug.Debug) { log.Println("Debug turned on") } else { log.Println("Debug turned off") @@ -52,18 +52,17 @@ func debugFlags(n *Node) { checkdn = df.NewCheckbox("Debug Node") checkdn.OnChanged = func(*Node) { - checkdn.checked = checkdn.toolkit.Checked() - Config.Options.DebugNode = checkdn.checked + Config.Debug.Node = true } checkdd = df.NewCheckbox("Debug node.Dump()") checkdd.OnChanged = func(*Node) { - Config.Options.DebugDump = checkdd.toolkit.Checked() + Config.Debug.Dump = true } changeCheckbox = df.NewCheckbox("Debug Change") changeCheckbox.OnChanged = func(*Node) { - Config.Options.DebugChange = changeCheckbox.toolkit.Checked() + Config.Debug.Change = true } df.NewButton("Dump Debug Flags", func () { @@ -83,20 +82,20 @@ func (n *Node) DebugTab(title string) *Node { gog = newN.NewGroup("GOLANG") gog.NewLabel("go language") gog.NewButton("GO Language Debug", func () { - GolangDebugWindow() + // GolangDebugWindow() }) gog.NewLabel("wit/gui package") gog.NewButton("WIT/GUI Package Debug", func () { Config.Width = 640 Config.Height = 480 - Queue(DebugWindow) + // Queue(DebugWindow) }) gog.NewButton("Demo wit/gui", func () { - DemoWindow() + // DemoWindow() }) gog.NewButton("Demo toolkit andlabs/ui", func () { - DemoToolkitWindow() + // DemoToolkitWindow() }) debugFlags(newN) @@ -118,7 +117,7 @@ func (n *Node) DebugTab(title string) *Node { if (dump == true) { child.Dump() } - dd.AddDropdown(child.Name) + dd.AddDropdownName(child.Name) } dd.SetDropdown(0) diff --git a/example_window_demo.go b/example_window_demo.go index c4e0374..be07496 100644 --- a/example_window_demo.go +++ b/example_window_demo.go @@ -18,3 +18,13 @@ func DemoWindow() { log.Println("DemoWindow() END") } + +func NewStandardWindow(title string) *Node { + log.Println("NewStandardWindow() creating", title) + + Config.Title = title + Config.Width = 640 + Config.Height = 480 + Config.Exit = StandardClose + return NewWindow() +} diff --git a/example_window_demo_toolkit.go b/example_window_demo_toolkit.go deleted file mode 100644 index 0872448..0000000 --- a/example_window_demo_toolkit.go +++ /dev/null @@ -1,45 +0,0 @@ -package gui - -import "log" -// import "time" -// import toolkit "git.wit.org/wit/gui/toolkit/andlabs" - -func NewStandardWindow(title string) *Node { - log.Println("NewStandardWindow() creating", title) - - Config.Title = title - Config.Width = 640 - Config.Height = 480 - Config.Exit = StandardClose - return NewWindow() -} - -// -// This creates a window that shows how the toolkit works -// internally using it's raw unchanged code for the toolkit itself -// -// This is a way to test and see if the toolkit is working at all -// right now it shows the andlabs/ui/DemoNumbersPage() -// -func DemoToolkitWindow() { - var w *Node - - w = NewStandardWindow("Demo of the GUI Toolkit") - -// d = w.New("demo") - - w.toolkit.DemoNumbersPage() - /* - tk = w.Toolkit.DemoNumbersPage() - tk.OnChanged = func(t *toolkit.Toolkit) { - log.Println("toolkit.NewSlider() value =", t.Value()) - if (d.OnChanged != nil) { - log.Println("toolkit.Demo() running node.OnChanged") - d.OnChanged(d) - } - } - d.Toolkit = tk - */ - - log.Println("ToolkitDemoWindow() END") -} @@ -2,30 +2,41 @@ package gui import "log" -import toolkit "git.wit.org/wit/gui/toolkit/andlabs" +// import toolkit "git.wit.org/wit/gui/toolkit/andlabs" +// import newtoolkit "git.wit.org/wit/gui/toolkit" // TODO: which name is better. AddGroup or NewGroup ? // first reaction is NewGroup func (n *Node) NewGroup(name string) *Node { - var newT *toolkit.Toolkit - var gNode *Node +// var newT *toolkit.Toolkit + var newNode *Node if (GetDebug()) { log.Println("toolkit.NewGroup() START", name) } - if (n.toolkit == nil) { - log.Println("toolkit.NewGroup() toolkit == nil") - panic("toolkit should never be nil") +// if (n.toolkit == nil) { +// log.Println("toolkit.NewGroup() toolkit == nil") +// panic("toolkit should never be nil") +// } + + newNode = n.New(name) + + log.Println("gui.Node.NewGroup()", name) + for _, aplug := range allPlugins { + log.Println("gui.Node.NewGroup() toolkit plugin =", aplug.name) + if (aplug.NewGroup == nil) { + continue + } + aplug.NewGroup(&n.Widget, &newNode.Widget) } // make a *Node with a *toolkit.Group - gNode = n.New(name) - newT = n.toolkit.NewGroup(name) - gNode.toolkit = newT - gNode.Dump() + // newT = n.toolkit.NewGroup(name) + // newNode.toolkit = newT + // newNode.Dump() - return gNode + return newNode } /* @@ -4,8 +4,6 @@ import "log" import "github.com/davecgh/go-spew/spew" -import toolkit "git.wit.org/wit/gui/toolkit/andlabs" - /* Get the int from the gui toolkit because eventually this gui package should become it's own seperate go routine and never interact from the @@ -16,18 +14,14 @@ import toolkit "git.wit.org/wit/gui/toolkit/andlabs" Is it "has to go" or "should go"? Probably it makes sense to strictly inforce it. No "callback" functions. IPC only (go channels) */ func (n *Node) Int() int { - if (toolkit.DebugToolkit) { + if (Config.Debug.Toolkit) { log.Println("gui.Node.Int() for node name =", n.Name) scs := spew.ConfigState{MaxDepth: 1} scs.Dump(n) } - if (n.toolkit == nil) { - log.Println("gui.Node.Int() for toolkit struct = nil") - return 0 - } - - i := n.toolkit.Value() + // i := n.toolkit.Value() + i := 3333 return i } @@ -38,11 +32,6 @@ func (n *Node) Value() int { func (n *Node) SetValue(i int) { log.Println("gui.SetValue() START") - if (n.toolkit == nil) { - log.Println("gui.Node.SetValue() for toolkit struct = nil") - panic("SetValue failed") - } n.Dump() - n.toolkit.Dump() - n.toolkit.SetValue(i) + // n.toolkit.SetValue(i) } diff --git a/label.go b/label.go new file mode 100644 index 0000000..6542a68 --- /dev/null +++ b/label.go @@ -0,0 +1,20 @@ +package gui + +import "log" +// import "errors" +// import "regexp" + +func (n *Node) NewLabel(text string) *Node { + newNode := n.New(text) + + for _, aplug := range allPlugins { + log.Println("gui.NewLabel() aplug =", aplug.name, "name =", newNode.Widget.Name) + if (aplug.NewLabel == nil) { + log.Println("\tgui.NewLabel() aplug.NewLabel = nil", aplug.name) + continue + } + aplug.NewLabel(&n.Widget, &newNode.Widget) + } + + return newNode +} @@ -3,22 +3,29 @@ package gui import ( "log" "os" + "embed" ) -import toolkit "git.wit.org/wit/gui/toolkit/andlabs" +// Windows doesn't support plugins. How can I keep andlabs and only compile it on windows? +// https://forum.heroiclabs.com/t/setting-up-goland-to-compile-plugins-on-windows/594/5 +// import toolkit "git.wit.org/wit/gui/toolkit/andlabs" const Xaxis = 0 // stack things horizontally const Yaxis = 1 // stack things vertically +// may this plugin work when all other plugins fail +//go:embed toolkit/gocui.so +var res embed.FS + func init() { log.Println("gui.init() has been run") Config.counter = 0 Config.prefix = "wit" - // Config.Options.Debug = true - // Config.Options.DebugNode = true - // Config.Options.DebugTabs = true + // Config.Debug.Debug = true + // Config.Debug.Node = true + // Config.Debug.Tabs = true title := "guiBinaryTree" w := 640 @@ -26,45 +33,117 @@ func init() { // Populates the top of the binary tree Config.master = addNode(title, w, h) - if (Config.Options.Debug) { + if (Config.Debug.Debug) { Config.master.Dump() } +} + +func Init() { + var initBAD bool = true + + if (Config.Debug.Debug) { + log.Println("Starting gui.Init()") + } + for _, aplug := range allPlugins { + log.Println("gui.LoadToolkit() already loaded toolkit plugin =", aplug.name) + initBAD = false + } + + // the program didn't specify a plugin. Try to load one + // TODO: detect the OS & user preferences to load the best one + if (initBAD) { + if (LoadToolkit("andlabs2")) { + initBAD = false + } + } + + // andlabs2 gui failed. fall back to the terminal gui (should be compiled into the binary) + if (initBAD) { + if (LoadToolkit("gocui")) { + initBAD = false + } + } - // load the gocui plugin - PlugGocli = LoadPlugin("../../toolkit/gocui.so") - PlugGocliOk = false + // locate the shared library file + // panic("WTF Init()") + for _, aplug := range allPlugins { + log.Println("gui.Node.Init() toolkit plugin =", aplug.name) + if (aplug.InitOk) { + log.Println("gui.Node.Init() Already Ran Init()", aplug.name) + continue + } + if (aplug.Init == nil) { + log.Println("gui.Node.Main() Init == nil", aplug.name) + continue + } + aplug.InitOk = true + aplug.Init() + } + // StandardExit(nil) } +// This should not pass a function func Main(f func()) { - if (Config.Options.Debug) { + if (Config.Debug.Debug) { log.Println("Starting gui.Main() (using gtk via andlabs/ui)") } - toolkit.Main(f) + for _, aplug := range allPlugins { + log.Println("gui.Node.NewButton() toolkit plugin =", aplug.name) + if (aplug.MainOk) { + log.Println("gui.Node.Main() Already Ran Main()", aplug.name) + continue + } + if (aplug.Main == nil) { + log.Println("gui.Node.Main() Main == nil", aplug.name) + continue + } + aplug.MainOk = true + aplug.Main(f) + // f() + } + // toolkit.Main(f) } +// This should never be exposed(?) + // 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.) // For example: gui.Queue(NewWindow()) -func Queue(f func()) { +func queue(f func()) { log.Println("Sending function to gui.Main() (using gtk via andlabs/ui)") - toolkit.Queue(f) + // toolkit.Queue(f) + for _, aplug := range allPlugins { + log.Println("gui.Node.NewButton() toolkit plugin =", aplug.name) + if (aplug.Queue == nil) { + continue + } + aplug.Queue(f) + } } // The window is destroyed but the application does not quit func StandardClose(n *Node) { - if (Config.Options.Debug) { + if (Config.Debug.Debug) { log.Println("wit/gui Standard Window Close. name =", n.Name) } } - // The window is destroyed but the application does not quit func StandardExit(n *Node) { - if (Config.Options.Debug) { + if (Config.Debug.Debug) { log.Println("wit/gui Standard Window Exit. running os.Exit()") } + + log.Println("gui.Node.StandardExit() attempt to exit each toolkit plugin") + for i, aplug := range allPlugins { + log.Println("gui.Node.NewButton()", i, aplug) + if (aplug.Quit != nil) { + aplug.Quit() + } + } + os.Exit(0) } @@ -1,5 +1,7 @@ package gui +// import "git.wit.org/wit/gui/toolkit" + /* generic function to create a new node on the binary tree */ @@ -16,11 +18,16 @@ func (n *Node) New(title string) *Node { /* raw create function for a new node struct */ -func addNode(title string, w int, h int) *Node { +func addNode(title string, width int, height int) *Node { var n Node + n.Name = title - n.Width = w - n.Height = h + n.Width = width + n.Height = height + + n.Widget.Name = title + n.Widget.Width = width + n.Widget.Height = height // no longer a string // id := Config.prefix + strconv.Itoa(Config.counter) @@ -8,109 +8,234 @@ package gui import ( "log" "os" - "plugin" - "github.com/davecgh/go-spew/spew" + + "git.wit.org/wit/gui/toolkit" ) -// TODO: could a protobuf work here? -type Greeter interface { - Greet() - JcarrButton() - AddButton(string) +var err error +type Symbol any + +type aplug struct { + // Ok bool + name string + filename string + plug *plugin.Plugin + sym *plugin.Symbol + LoadOk bool + InitOk bool + MainOk bool + + Init func() + Main func(func ()) + Queue func(func ()) + Quit func() + NewWindow func(*toolkit.Widget) + NewButton func(*toolkit.Widget, *toolkit.Widget) + NewGroup func(*toolkit.Widget, *toolkit.Widget) + NewCheckbox func(*toolkit.Widget, *toolkit.Widget) + NewTab func(*toolkit.Widget, *toolkit.Widget) + NewLabel func(*toolkit.Widget, *toolkit.Widget) + NewTextbox func(*toolkit.Widget, *toolkit.Widget) + NewSlider func(*toolkit.Widget, *toolkit.Widget) + NewSpinner func(*toolkit.Widget, *toolkit.Widget) + + NewDropdown func(*toolkit.Widget, *toolkit.Widget) + AddDropdownName func(*toolkit.Widget, string) } -var PlugGocli *plugin.Plugin -var PlugGocliOk bool -var PlugHello *plugin.Plugin +var allPlugins []*aplug + +// loads and initializes a toolkit (andlabs/ui, gocui, etc) +func LoadToolkit(name string) bool { + var newPlug aplug + + log.Println("gui.LoadToolkit() START") + newPlug.LoadOk = false + + for _, aplug := range allPlugins { + log.Println("gui.LoadToolkit() already loaded toolkit plugin =", aplug.name) + if (aplug.name == name) { + log.Println("gui.LoadToolkit() SKIPPING") + return true + } + } + + // locate the shared library file + filename := name + ".so" + loadPlugin(&newPlug, filename) + if (newPlug.plug == nil) { + return false + } + // newPlug.Ok = true + newPlug.name = name + + // map all the functions + newPlug.Init = loadFuncE(&newPlug, "Init") + newPlug.Quit = loadFuncE(&newPlug, "Quit") + + // this should be laodFuncE() + newPlug.Main = loadFuncF(&newPlug, "Main") + newPlug.Queue = loadFuncF(&newPlug, "Queue") + + newPlug.NewWindow = loadFunc1(&newPlug, "NewWindow") + + newPlug.NewButton = loadFunc2(&newPlug, "NewButton") + newPlug.NewGroup = loadFunc2(&newPlug, "NewGroup") + newPlug.NewCheckbox = loadFunc2(&newPlug, "NewCheckbox") + newPlug.NewTab = loadFunc2(&newPlug, "NewTab") + newPlug.NewLabel = loadFunc2(&newPlug, "NewLabel") + newPlug.NewTextbox = loadFunc2(&newPlug, "NewTextbox") + newPlug.NewSlider = loadFunc2(&newPlug, "NewSlider") + newPlug.NewSpinner = loadFunc2(&newPlug, "NewSpinner") + + newPlug.NewDropdown = loadFunc2(&newPlug, "NewDropdown") + newPlug.AddDropdownName = loadFuncS(&newPlug, "AddDropdownName") -// var gBut plugin.Symbol -var jcarrBut plugin.Symbol -var symGreeter plugin.Symbol -var greeter Greeter -var ok bool + allPlugins = append(allPlugins, &newPlug) -var typeToolkit plugin.Symbol -var typeToolkitCast Greeter + log.Println("gui.LoadToolkit() END", newPlug.name, filename) + newPlug.LoadOk = true + return true +} -func LoadPlugin(name string) *plugin.Plugin { - scs := spew.ConfigState{MaxDepth: 1} +func loadFuncE(p *aplug, funcName string) func() { + var newfunc func() + var ok bool + var test plugin.Symbol - // load module - // 1. open the so file to load the symbols - plug, err := plugin.Open(name) - log.Println("plug =") - log.Println(scs.Sdump(plug)) + test, err = p.plug.Lookup(funcName) if err != nil { - log.Println(err) + log.Println("DID NOT FIND: name =", test, "err =", err) return nil } - // 2. look up a symbol (an exported function or variable) - // in this case, variable Greeter - typeToolkit, err = plug.Lookup("Toolkit") - log.Println("plugin.Toolkit", typeToolkit) - log.Println(scs.Sdump(typeToolkit)) - if err != nil { - log.Println(err) - os.Exit(1) + newfunc, ok = test.(func()) + if !ok { + log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name) + return nil } + return newfunc +} + +func loadFunc1(p *aplug, funcName string) func(*toolkit.Widget) { + var newfunc func(*toolkit.Widget) + var ok bool + var test plugin.Symbol - symGreeter, err = plug.Lookup("Greeter") - log.Println("symGreater", symGreeter) - log.Println(scs.Sdump(symGreeter)) + test, err = p.plug.Lookup(funcName) if err != nil { - log.Println(err) - os.Exit(1) + log.Println("DID NOT FIND: name =", test, "err =", err) + return nil } - // 3. Assert that loaded symbol is of a desired type - // in this case interface type Greeter (defined above) - // var greeter Greeter - greeter, ok = symGreeter.(Greeter) - log.Println("greeter", symGreeter) - log.Println(scs.Sdump(greeter)) + newfunc, ok = test.(func(*toolkit.Widget)) if !ok { - log.Println("unexpected type from module symbol") - os.Exit(1) + log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name) + return nil } + return newfunc +} - /* - typeToolkitCast, ok = typeToolkit.(Greeter) +func loadFuncS(p *aplug, funcName string) func(*toolkit.Widget, string) { + var newfunc func(*toolkit.Widget, string) + var ok bool + var test plugin.Symbol + + test, err = p.plug.Lookup(funcName) + if err != nil { + log.Println("DID NOT FIND: name =", test, "err =", err) + return nil + } + + newfunc, ok = test.(func(*toolkit.Widget, string)) if !ok { - log.Println("unexpected cast of Toolkit to Greeter") - os.Exit(1) + log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name) + return nil } - */ - return plug + return newfunc } -func RunGreet() { - log.Println("gui.RunGreet() START") - if (greeter == nil) { - log.Println("wit/gui gocui plugin didn't load") - return +func loadFunc2(p *aplug, funcName string) func(*toolkit.Widget, *toolkit.Widget) { + var newfunc func(*toolkit.Widget, *toolkit.Widget) + var ok bool + var test plugin.Symbol + + test, err = p.plug.Lookup(funcName) + if err != nil { + log.Println("DID NOT FIND: name =", test, "err =", err) + return nil + } + + newfunc, ok = test.(func(*toolkit.Widget, *toolkit.Widget)) + if !ok { + log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name) + return nil } - PlugGocliOk = true - greeter.Greet() + return newfunc } -func LookupJcarrButton() { - log.Println("lookupJcarrButton() START") +// This is probably dangerous and should never be done +// executing arbitrary functions will cause them to run inside the goroutine that +// the GUI toolkit itself is running in. TODO: move to channels here +func loadFuncF(p *aplug, funcName string) func(func ()) { + var newfunc func(func ()) + var ok bool + var test plugin.Symbol - if (greeter == nil) { - log.Println("wit/gui gocui plugin didn't load") - return + test, err = p.plug.Lookup(funcName) + if err != nil { + log.Println("DID NOT FIND: name =", test, "err =", err) + return nil } - greeter.JcarrButton() + + newfunc, ok = test.(func(func ())) + if !ok { + log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name) + return nil + } + return newfunc } -func GocuiAddButton(name string) { - log.Println("GocuiAddButton() START", name) +func loadPlugin(p *aplug, name string) { + var filename string + + // attempt to write out the file from the internal resource + internalName := "toolkit/" + name + soFile, err := res.ReadFile(internalName) + if (err != nil) { + log.Println(err) + } else { + err = os.WriteFile("/tmp/wit/" + name, soFile, 0644) + if (err != nil) { + log.Println(err) + } + } + + filename = "/tmp/wit/" + name + p.plug = loadfile(filename) + if (p.plug != nil) { + p.filename = filename + return + } - if (greeter == nil) { - log.Println("wit/gui gocui plugin didn't load") + filename = "/usr/share/wit/gui/" + name + p.plug = loadfile(filename) + if (p.plug != nil) { + p.filename = filename return } - greeter.AddButton(name) + return +} + +// load module +// 1. open the shared object file to load the symbols +func loadfile(filename string) *plugin.Plugin { + plug, err := plugin.Open(filename) + log.Println("plug =", plug) + if err != nil { + log.Println(err) + return nil + } + return plug } @@ -2,27 +2,24 @@ package gui import "log" -import toolkit "git.wit.org/wit/gui/toolkit/andlabs" - func (n *Node) NewSlider(name string, x int, y int) *Node { - var newT *toolkit.Toolkit - var sNode *Node - - log.Println("toolkit.NewSlider() START", name) + newNode := n.New(name) + newNode.Widget.Name = name + newNode.Widget.X = x + newNode.Widget.Y = y - n.verify() + newNode.Widget.Custom = func() { + log.Println("even newer clicker() name in NewSlider", newNode.Widget) + } - // make a *Node with a *toolkit.Group - sNode = n.New(name + " part1") - newT = n.toolkit.NewSlider(name, x, y) - newT.Name = name - sNode.custom = n.custom - newT.Custom = func () { - commonCallback(sNode) + for _, aplug := range allPlugins { + log.Println("gui.NewSlider() aplug =", aplug.name, "name =", newNode.Widget.Name) + if (aplug.NewSlider == nil) { + log.Println("\tgui.NewSlider() aplug.NewSlider = nil", aplug.name) + continue + } + aplug.NewSlider(&n.Widget, &newNode.Widget) } - sNode.toolkit = newT - sNode.Dump() - // panic("checking Custom()") - return sNode + return newNode } @@ -2,26 +2,24 @@ package gui import "log" -import toolkit "git.wit.org/wit/gui/toolkit/andlabs" - func (n *Node) NewSpinner(name string, x int, y int) *Node { - var newT *toolkit.Toolkit - var sNode *Node - - log.Println("toolkit.NewSpinner() START", name) + newNode := n.New(name) + newNode.Widget.Name = name + newNode.Widget.X = x + newNode.Widget.Y = y - n.verify() - - // make a *Node with a *toolkit.Group - sNode = n.New(name + " part1") - newT = n.toolkit.NewSpinner(name, x, y) - newT.Name = name - sNode.toolkit = newT - // sNode.Dump() + newNode.Widget.Custom = func() { + log.Println("even newer clicker() name in NewSpinner", newNode.Widget) + } - newT.Custom = func () { - commonCallback(sNode) + for _, aplug := range allPlugins { + log.Println("gui.NewSpinner() aplug =", aplug.name, "name =", newNode.Widget.Name) + if (aplug.NewSpinner == nil) { + log.Println("\tgui.NewSpinner() aplug.NewSpinner = nil", aplug.name) + continue + } + aplug.NewSpinner(&n.Widget, &newNode.Widget) } - return sNode + return newNode } @@ -5,7 +5,8 @@ import ( "reflect" ) -import toolkit "git.wit.org/wit/gui/toolkit/andlabs" +// import toolkit "git.wit.org/wit/gui/toolkit/andlabs" +import newtoolkit "git.wit.org/wit/gui/toolkit" // // All GUI Data Structures and functions that are external @@ -22,46 +23,46 @@ import toolkit "git.wit.org/wit/gui/toolkit/andlabs" var Config GuiConfig func GetDebug () bool { - return Config.Options.Debug + return Config.Debug.Debug } func SetDebug (s bool) { - Config.Options.Debug = s + Config.Debug.Debug = s // also set these - Config.Options.DebugDump = s - Config.Options.DebugNode = s - toolkit.DebugToolkit = s + Config.Debug.Dump = s + Config.Debug.Node = s + // toolkit.DebugToolkit = s } func GetDebugToolkit () bool { - return toolkit.DebugToolkit + return Config.Debug.Toolkit } func SetDebugToolkit (s bool) { - toolkit.DebugToolkit = s + Config.Debug.Toolkit = s } func ShowDebugValues() { - log.Println("\t wit/gui Debug =", Config.Options.Debug) - log.Println("\t wit/gui DebugDump =", Config.Options.DebugDump) - log.Println("\t wit/gui DebugNode =", Config.Options.DebugNode) - log.Println("\t wit/gui DebugTabs =", Config.Options.DebugTabs) - log.Println("\t wit/gui DebugPlugin =", Config.Options.DebugPlugin) - log.Println("\t wit/gui DebugChange =", Config.Options.DebugChange) - - log.Println("\t wit/gui DebugToolkit =", toolkit.DebugToolkit) + log.Println("\t wit/gui Debug =", Config.Debug.Debug) + log.Println("\t wit/gui DebugDump =", Config.Debug.Dump) + log.Println("\t wit/gui DebugNode =", Config.Debug.Node) + log.Println("\t wit/gui DebugTabs =", Config.Debug.Tabs) + log.Println("\t wit/gui DebugPlugin =", Config.Debug.Plugin) + log.Println("\t wit/gui DebugChange =", Config.Debug.Change) + log.Println("\t wit/gui DebugToolkit =", Config.Debug.Toolkit) } // This struct can be used with go-arg -type GuiOptions struct { +type GuiDebug struct { // These are global debugging settings // TODO: move to a standard logging system - Debug bool - DebugDump bool - DebugNode bool - DebugTabs bool - DebugPlugin bool - DebugChange bool `help:"debug mouse clicks and keyboard input"` + Debug bool + Dump bool + Node bool + Tabs bool + Plugin bool + Change bool `help:"debug mouse clicks and keyboard input"` + Toolkit bool `help:"debug toolkit"` } type GuiConfig struct { @@ -74,7 +75,7 @@ type GuiConfig struct { Height int Exit func(*Node) - Options GuiOptions + Debug GuiDebug // hacks depth int @@ -86,10 +87,13 @@ type GuiConfig struct { type Node struct { id int + // deprecate these and use toolkit.Widget Name string Width int Height int + Widget newtoolkit.Widget + // this function is run when there are mouse or keyboard events OnChanged func(*Node) @@ -98,13 +102,12 @@ type Node struct { children []*Node // hmm. how do you handle this when the toolkits are plugins? - toolkit *toolkit.Toolkit + // toolkit *toolkit.Toolkit // things that may not really be needed (?) custom func() checked bool text string - } func (n *Node) Parent() *Node { @@ -116,7 +119,7 @@ func (n *Node) Window() *Node { } func (n *Node) Dump() { - if ! Config.Options.DebugDump { + if ! Config.Debug.Dump { return } IndentPrintln("NODE DUMP START") @@ -141,10 +144,10 @@ func (n *Node) Dump() { IndentPrintln("OnChanged = ", n.OnChanged) } IndentPrintln("text = ", reflect.ValueOf(n.text).Kind(), n.text) - if (n.toolkit != nil) { - IndentPrintln("toolkit = ", reflect.ValueOf(n.toolkit).Kind()) - n.toolkit.Dump() - } +// if (n.toolkit != nil) { +// IndentPrintln("toolkit = ", reflect.ValueOf(n.toolkit).Kind()) +// n.toolkit.Dump() +// } // if (n.id == nil) { // // Node structs should never have a nil id. // // I probably shouldn't panic here, but this is just to check the sanity of @@ -163,7 +166,7 @@ func (n *Node) SetName(name string) { func (n *Node) Append(child *Node) { n.children = append(n.children, child) - if (Config.Options.Debug) { + if (Config.Debug.Debug) { log.Println("child node:") child.Dump() log.Println("parent node:") @@ -206,11 +209,11 @@ func (n *Node) ListChildren(dump bool) { if len(n.children) == 0 { if (n.parent == nil) { } else { - if (Config.Options.DebugNode) { + if (Config.Debug.Node) { log.Println("\t\t\tparent =",n.parent.id) } if (listChildrenParent != nil) { - if (Config.Options.DebugNode) { + if (Config.Debug.Node) { log.Println("\t\t\tlistChildrenParent =",listChildrenParent.id) } if (listChildrenParent.id != n.parent.id) { @@ -219,7 +222,7 @@ func (n *Node) ListChildren(dump bool) { } } } - if (Config.Options.DebugNode) { + if (Config.Debug.Node) { log.Println("\t\t", n.id, "has no children") } return @@ -227,7 +230,7 @@ func (n *Node) ListChildren(dump bool) { for _, child := range n.children { // log.Println("\t\t", child.id, child.Width, child.Height, child.Name) if (child.parent != nil) { - if (Config.Options.DebugNode) { + if (Config.Debug.Node) { log.Println("\t\t\tparent =",child.parent.id) } } else { @@ -237,7 +240,7 @@ func (n *Node) ListChildren(dump bool) { if (dump == true) { child.Dump() } - if (Config.Options.DebugNode) { + if (Config.Debug.Node) { if (child.children == nil) { log.Println("\t\t", child.id, "has no children") } else { @@ -7,20 +7,17 @@ import ( // This function should make a new node with the parent and // the 'tab' as a child -func (n *Node) NewTab(title string) *Node { - log.Println("gui.Node.NewTab() START name =", title) +func (n *Node) NewTab(text string) *Node { + newNode := n.New(text) - // TODO: standardize these checks somewhere - if (n.toolkit == nil) { - n.Dump() - panic("NewTab() failed. toolkit == nil") + for _, aplug := range allPlugins { + log.Println("gui.NewTab() aplug =", aplug.name, "name =", newNode.Widget.Name) + if (aplug.NewTab == nil) { + log.Println("\tgui.NewTab() aplug.NewTab = nil", aplug.name) + continue + } + aplug.NewTab(&n.Widget, &newNode.Widget) } - log.Println("Make new node") - newN := n.New(title) - log.Println("New tab to window") - t := n.toolkit.AddTab(title) - newN.toolkit = t - n.Append(newN) - return newN + return newNode } @@ -2,47 +2,21 @@ package gui import "log" -import toolkit "git.wit.org/wit/gui/toolkit/andlabs" - func (n *Node) NewTextbox(name string) *Node { - var newt *toolkit.Toolkit - var c *Node - - log.Println("toolkit.NewTextbox() START", name) - - n.verify() - - // make a new Node and a new toolbox struct - c = n.New(name) - newt = n.toolkit.NewTextbox(name) + newNode := n.New(name) - c.toolkit = newt - c.custom = n.custom + newNode.Widget.Custom = func() { + log.Println("even newer clicker() name in NewTextBox", newNode.Widget) + } - newt.Name = name - // newt.Custom = func () { - newt.OnChanged = func (*toolkit.Toolkit) { - if (Config.Options.DebugChange) { - log.Println("AM IN CALLBACK. SETTING NODE.checked START") - c.Dump() - c.toolkit.Dump() - } - c.text = c.toolkit.GetText() - if (c.OnChanged == nil) { - if (Config.Options.DebugChange) { - log.Println("this is println?") - } - } else { - if (Config.Options.DebugChange) { - log.Println("this is println? running c.OnChanged() here") - } - c.OnChanged(n) - } - if (Config.Options.DebugChange) { - log.Println("n.toolkit.GetText() =", c.text) - log.Println("AM IN CALLBACK. SETTING NODE.checked END") + for _, aplug := range allPlugins { + log.Println("gui.NewTextbox() aplug =", aplug.name, "name =", newNode.Widget.Name) + if (aplug.NewTextbox == nil) { + log.Println("\tgui.NewTextbox() aplug.NewTextbox = nil", aplug.name) + continue } + aplug.NewTextbox(&n.Widget, &newNode.Widget) } - return c + return newNode } diff --git a/toolkit/andlabs2/Makefile b/toolkit/andlabs2/Makefile new file mode 100644 index 0000000..54cf7f5 --- /dev/null +++ b/toolkit/andlabs2/Makefile @@ -0,0 +1,4 @@ +all: plugin + +plugin: + GO111MODULE="off" go build -buildmode=plugin -o ../andlabs2.so diff --git a/toolkit/andlabs2/box.go b/toolkit/andlabs2/box.go new file mode 100644 index 0000000..bb9945e --- /dev/null +++ b/toolkit/andlabs2/box.go @@ -0,0 +1,66 @@ +package main + +import "log" + +import "github.com/andlabs/ui" +import _ "github.com/andlabs/ui/winmanifest" + +// create a new box +func (t *andlabsT) GetBox() *ui.Box { + return t.uiBox +} + +// create a new box +func (t *andlabsT) NewBox() *andlabsT { + if (DebugToolkit) { + log.Println("gui.Toolbox.NewBox() START create default") + } + t.Dump() + if (t.uiGroup != nil) { + if (DebugToolkit) { + log.Println("\tgui.Toolbox.NewBox() is a Group") + } + var newTK andlabsT + + vbox := ui.NewVerticalBox() + vbox.SetPadded(padded) + t.uiGroup.SetChild(vbox) + newTK.uiBox = vbox + + return &newTK + } + if (t.uiBox != nil) { + if (DebugToolkit) { + log.Println("\tgui.Toolbox.NewBox() is a Box") + } + var newTK andlabsT + + vbox := ui.NewVerticalBox() + vbox.SetPadded(padded) + t.uiBox.Append(vbox, stretchy) + newTK.uiBox = vbox + newTK.Name = t.Name + + return &newTK + } + if (t.uiWindow != nil) { + if (DebugToolkit) { + log.Println("\tgui.Toolbox.NewBox() is a Window") + } + var newT andlabsT + + vbox := ui.NewVerticalBox() + vbox.SetPadded(padded) + t.uiWindow.SetChild(vbox) + newT.uiBox = vbox + newT.Name = t.Name + + // panic("WTF") + return &newT + } + if (DebugToolkit) { + log.Println("\tgui.Toolbox.NewBox() FAILED. Couldn't figure out where to make a box") + } + t.Dump() + return nil +} diff --git a/toolkit/andlabs2/button.go b/toolkit/andlabs2/button.go new file mode 100644 index 0000000..bd80683 --- /dev/null +++ b/toolkit/andlabs2/button.go @@ -0,0 +1,67 @@ +package main + +import "log" +// import "os" + + +import "github.com/andlabs/ui" +import _ "github.com/andlabs/ui/winmanifest" + +import "git.wit.org/wit/gui/toolkit" +func NewButton(parentW *toolkit.Widget, w *toolkit.Widget) { + var t, newt *andlabsT + var b *ui.Button + log.Println("gui.andlabs.NewButton()", w.Name) + + t = mapToolkits[parentW] + if (t == nil) { + log.Println("go.andlabs.NewButton() toolkit struct == nil. name=", parentW.Name, w.Name) + return + } + + if t.broken() { + return + } + newt = new(andlabsT) + + b = ui.NewButton(w.Name) + newt.uiButton = b + + b.OnClicked(func(*ui.Button) { + if (DebugToolkit) { + log.Println("TODO: IN TOOLKIT GOROUTINE. SHOULD LEAVE HERE VIA channels. button name =", w.Name) + log.Println("FOUND WIDGET!", w) + } + if (w.Custom != nil) { + w.Custom() + return + } + if (w.Event != nil) { + w.Event(w) + return + } + t.Dump() + newt.Dump() + if (DebugToolkit) { + log.Println("TODO: LEFT TOOLKIT GOROUTINE WITH NOTHING TO DO button name =", w.Name) + } + }) + + if (DebugToolkit) { + log.Println("gui.Toolbox.NewButton() about to append to Box parent t:", w.Name) + t.Dump() + log.Println("gui.Toolbox.NewButton() about to append to Box new t:", w.Name) + newt.Dump() + } + if (t.uiBox != nil) { + t.uiBox.Append(b, stretchy) + } else if (t.uiWindow != nil) { + t.uiWindow.SetChild(b) + } else { + log.Println("ERROR: wit/gui andlabs couldn't place this button in a box or a window") + log.Println("ERROR: wit/gui andlabs couldn't place this button in a box or a window") + return + } + + mapWidgetsToolkits(w, newt) +} diff --git a/toolkit/andlabs2/checkbox.go b/toolkit/andlabs2/checkbox.go new file mode 100644 index 0000000..b4b1524 --- /dev/null +++ b/toolkit/andlabs2/checkbox.go @@ -0,0 +1,34 @@ +package main + +import "log" + +import "github.com/andlabs/ui" +import _ "github.com/andlabs/ui/winmanifest" + +func (t andlabsT) NewCheckbox(name string) *andlabsT { + log.Println("gui.Toolkit.NewCheckbox()", name) + var newt andlabsT + + if t.broken() { + return nil + } + + c := ui.NewCheckbox(name) + newt.uiCheckbox = c + newt.uiBox = t.uiBox + t.uiBox.Append(c, stretchy) + + c.OnToggled(func(spin *ui.Checkbox) { + newt.commonChange("Checkbox") + }) + + return &newt +} + +func (t andlabsT) Checked() bool { + if t.broken() { + return false + } + + return t.uiCheckbox.Checked() +} diff --git a/toolkit/andlabs2/common.go b/toolkit/andlabs2/common.go new file mode 100644 index 0000000..fc6cbe8 --- /dev/null +++ b/toolkit/andlabs2/common.go @@ -0,0 +1,61 @@ +package main + +import "log" + +func init() { + if (DebugToolkit) { + log.Println("gui/toolkit init() Setting defaultBehavior = true") + } + setDefaultBehavior(true) +} + +func (t andlabsT) commonChange(widget string) { + s := t.String() + if (DebugToolkit) { + log.Println("gui.Toolkit.ui.OnChanged() =", s) + } + if (t.OnChanged != nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.OnChanged() trying to run toolkit.OnChanged() entered val =", s) + } + t.OnChanged(&t) + return + } + if (t.Custom != nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.OnChanged() Running toolkit.Custom()") + t.Dump() + } + t.Custom() + return + } + if (DebugToolkit) { + log.Println("gui.Toolkit.OnChanged() ENDED without finding any callback") + } +} + +// does some sanity checks on the internal structs of the binary tree +// TODO: probably this should not panic unless it's running in devel mode (?) +func (t *andlabsT) broken() bool { + if (t.uiBox == nil) { + if (t.uiWindow != nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.UiBox == nil. This is an empty window. Try to add a box") + } + t.NewBox() + return false + } + log.Println("gui.Toolkit.UiBox == nil. I can't add a widget without a place to put it") + // log.Println("probably could just make a box here?") + // corruption or something horrible? + panic("wit/gui toolkit/andlabs func broken() invalid goroutine access into this toolkit?") + panic("wit/gui toolkit/andlabs func broken() this probably should not cause the app to panic here (?)") + return true + } + if (t.uiWindow == nil) { + log.Println("gui.Toolkit.UiWindow == nil. I can't add a widget without a place to put it (IGNORING FOR NOW)") + forceDump(t) + return false + } + return false +} diff --git a/toolkit/andlabs2/demo.go b/toolkit/andlabs2/demo.go new file mode 100644 index 0000000..0781f88 --- /dev/null +++ b/toolkit/andlabs2/demo.go @@ -0,0 +1,92 @@ +package main + +import "log" + +import "github.com/andlabs/ui" +import _ "github.com/andlabs/ui/winmanifest" + +/* + This is a code example taken directly from the toolkit andlabs/ui + + This code is here to double check that the toolkit itself still works + the same way. This is intended as a sanity check. +*/ + +func BlankWindow(w *ui.Window) *ui.Box { + hbox := ui.NewHorizontalBox() + hbox.SetPadded(true) + w.SetChild(hbox) + return hbox +} + +func (t *andlabsT) DemoNumbersPage() { + var w *ui.Window + + log.Println("Starting wit/gui toolkit andlabs/ui DemoNumbersPage()") + + w = t.uiWindow + t.uiBox = makeNumbersPage() + t.uiBox.SetPadded(true) + w.SetChild(t.uiBox) + w.SetTitle("Internal demo of andlabs/ui toolkit") +} + +func makeNumbersPage() *ui.Box { + hbox := ui.NewHorizontalBox() + hbox.SetPadded(true) + + group := ui.NewGroup("Numbers") + group.SetMargined(true) + hbox.Append(group, true) + + vbox := ui.NewVerticalBox() + vbox.SetPadded(true) + group.SetChild(vbox) + + spinbox := ui.NewSpinbox(0, 100) + slider := ui.NewSlider(0, 100) + pbar := ui.NewProgressBar() + spinbox.OnChanged(func(*ui.Spinbox) { + slider.SetValue(spinbox.Value()) + pbar.SetValue(spinbox.Value()) + }) + slider.OnChanged(func(*ui.Slider) { + spinbox.SetValue(slider.Value()) + pbar.SetValue(slider.Value()) + }) + vbox.Append(spinbox, false) + vbox.Append(slider, false) + vbox.Append(pbar, false) + + ip := ui.NewProgressBar() + ip.SetValue(-1) + vbox.Append(ip, false) + + group = ui.NewGroup("Lists") + group.SetMargined(true) + hbox.Append(group, true) + + vbox = ui.NewVerticalBox() + vbox.SetPadded(true) + group.SetChild(vbox) + + cbox := ui.NewCombobox() + cbox.Append("Combobox Item 1") + cbox.Append("Combobox Item 2") + cbox.Append("Combobox Item 3") + vbox.Append(cbox, false) + + ecbox := ui.NewEditableCombobox() + ecbox.Append("Editable Item 1") + ecbox.Append("Editable Item 2") + ecbox.Append("Editable Item 3") + vbox.Append(ecbox, false) + + rb := ui.NewRadioButtons() + rb.Append("Radio Button 1") + rb.Append("Radio Button 2") + rb.Append("Radio Button 3") + vbox.Append(rb, false) + + return hbox +} diff --git a/toolkit/andlabs2/dropdown.go b/toolkit/andlabs2/dropdown.go new file mode 100644 index 0000000..1e1886a --- /dev/null +++ b/toolkit/andlabs2/dropdown.go @@ -0,0 +1,90 @@ +package main + +import "log" +// import "time" + +import "github.com/andlabs/ui" +import _ "github.com/andlabs/ui/winmanifest" + +import "git.wit.org/wit/gui/toolkit" + +func (t *andlabsT) NewDropdown(title string) *andlabsT { + // make new node here + if (DebugToolkit) { + log.Println("gui.Toolbox.NewDropdownCombobox()", title) + } + var newt andlabsT + + if t.broken() { + return nil + } + + s := ui.NewCombobox() + newt.uiCombobox = s + newt.uiBox = t.uiBox + t.uiBox.Append(s, stretchy) + + // initialize the index + newt.c = 0 + newt.val = make(map[int]string) + + s.OnSelected(func(spin *ui.Combobox) { + i := spin.Selected() + if (newt.val == nil) { + log.Println("make map didn't work") + newt.text = "error" + } + newt.text = newt.val[i] + newt.commonChange("Dropdown") + }) + + return &newt +} + +func (t *andlabsT) AddDropdownName(title string) { + t.uiCombobox.Append(title) + if (t.val == nil) { + log.Println("make map didn't work") + return + } + t.val[t.c] = title + t.c = t.c + 1 +} + +func (t andlabsT) SetDropdown(i int) { + t.uiCombobox.SetSelected(i) +} + +func NewDropdown(parentW *toolkit.Widget, w *toolkit.Widget) { + log.Println("gui.andlabs.NewDropdown()", w.Name) + + t := mapToolkits[parentW] + if (t == nil) { + log.Println("go.andlabs.NewDropdown() toolkit struct == nil. name=", parentW.Name, w.Name) + listMap() + } + newt := t.NewDropdown(w.Name) + mapWidgetsToolkits(w, newt) +} + +func AddDropdownName(w *toolkit.Widget, s string) { + log.Println("gui.andlabs.AddDropdownName()", w.Name, "add:", s) + + t := mapToolkits[w] + if (t == nil) { + log.Println("go.andlabs.AddDropdownName() toolkit struct == nil. name=", w.Name, s) + listMap() + } + t.AddDropdownName(s) +} + +func SetDropdown(w *toolkit.Widget, i int) { + log.Println("gui.andlabs.SetDropdown()", i) + + t := mapToolkits[w] + if (t == nil) { + log.Println("go.andlabs.SetDropdown() toolkit struct == nil. name=", w.Name, i) + listMap() + } + t.SetDropdown(i) +} diff --git a/toolkit/andlabs2/group.go b/toolkit/andlabs2/group.go new file mode 100644 index 0000000..39f740b --- /dev/null +++ b/toolkit/andlabs2/group.go @@ -0,0 +1,56 @@ +package main + +import "log" +import "os" + +import "github.com/andlabs/ui" +import _ "github.com/andlabs/ui/winmanifest" + +import "git.wit.org/wit/gui/toolkit" + +func NewGroup(parentW *toolkit.Widget, w *toolkit.Widget) { + log.Println("gui.andlabs.NewGroup()", w.Name) + + t := mapToolkits[parentW] + if (t == nil) { + log.Println("go.andlabs.NewGroup() toolkit struct == nil. name=", parentW.Name, w.Name) + listMap() + } + newt := t.NewGroup(w.Name) + mapWidgetsToolkits(w, newt) +} + +// make new Group here +func (t andlabsT) NewGroup(title string) *andlabsT { + var newt andlabsT + + if (DebugToolkit) { + log.Println("gui.Toolbox.NewGroup() create", title) + } + g := ui.NewGroup(title) + g.SetMargined(margin) + + if (t.uiBox != nil) { + t.uiBox.Append(g, stretchy) + } else if (t.uiWindow != nil) { + t.uiWindow.SetChild(g) + } else { + log.Println("gui.ToolboxNode.NewGroup() node.UiBox == nil. I can't add a range UI element without a place to put it") + log.Println("probably could just make a box here?") + os.Exit(0) + } + + hbox := ui.NewVerticalBox() + hbox.SetPadded(padded) + g.SetChild(hbox) + + newt.uiGroup = g + newt.uiBox = hbox + newt.uiWindow = t.uiWindow + newt.Name = title + + t.Dump() + newt.Dump() + // panic("toolkit.NewGroup") + return &newt +} diff --git a/toolkit/andlabs2/label.go b/toolkit/andlabs2/label.go new file mode 100644 index 0000000..c5a6896 --- /dev/null +++ b/toolkit/andlabs2/label.go @@ -0,0 +1,42 @@ +package main + +import "log" + +import "github.com/andlabs/ui" +import _ "github.com/andlabs/ui/winmanifest" + +import "git.wit.org/wit/gui/toolkit" + +func NewLabel(parentW *toolkit.Widget, w *toolkit.Widget) { + var t, newt *andlabsT + log.Println("gui.andlabs.NewButton()", w.Name) + + t = mapToolkits[parentW] + if (t == nil) { + log.Println("go.andlabs.NewButton() toolkit struct == nil. name=", parentW.Name, w.Name) + return + } + + if t.broken() { + return + } + newt = new(andlabsT) + + newt.uiLabel = ui.NewLabel(w.Name) + newt.uiBox = t.uiBox + + if (DebugToolkit) { + log.Println("gui.Toolbox.NewButton() about to append to Box parent t:", w.Name) + t.Dump() + log.Println("gui.Toolbox.NewButton() about to append to Box new t:", w.Name) + newt.Dump() + } + if (t.uiBox != nil) { + t.uiBox.Append(newt.uiLabel, false) + } else { + log.Println("ERROR: wit/gui andlabs couldn't place this label in a box") + return + } + + mapWidgetsToolkits(w, newt) +} diff --git a/toolkit/andlabs2/main.go b/toolkit/andlabs2/main.go new file mode 100644 index 0000000..8a28710 --- /dev/null +++ b/toolkit/andlabs2/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "log" +// "time" + + "git.wit.org/wit/gui/toolkit" + + "github.com/andlabs/ui" + // the _ means we only need this for the init() + _ "github.com/andlabs/ui/winmanifest" +) + +func Main(f func()) { + if (DebugToolkit) { + log.Println("Starting gui.Main() (using gtk via andlabs/ui)") + } + ui.Main( func() { + log.Println("Starting gui.Main() (using gtk via andlabs/ui)") + log.Println("Starting gui.Main() (using gtk via andlabs/ui)") + log.Println("Starting gui.Main() (using gtk via andlabs/ui)") + log.Println("Starting gui.Main() (using gtk via andlabs/ui)") + log.Println("Starting gui.Main() (using gtk via andlabs/ui)") + log.Println("Starting gui.Main() (using gtk via andlabs/ui)") + // time.Sleep(1 * time.Second) + // NewWindow2("helloworld2", 200, 100) + f() + }) +} + +// 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.) +// +// For example: Queue(NewWindow()) +// +func Queue(f func()) { + if (DebugToolkit) { + log.Println("Sending function to ui.QueueMain() (using gtk via andlabs/ui)") + } + ui.QueueMain(f) +} + +func Init() { + log.Println("should Init() here") + + mapWidgets = make(map[*andlabsT]*toolkit.Widget) + mapToolkits = make(map[*toolkit.Widget]*andlabsT) +} + +func Quit() { + log.Println("should Quit() here") + // myExit(nil) +} diff --git a/toolkit/andlabs2/plugin.go b/toolkit/andlabs2/plugin.go new file mode 100644 index 0000000..f78e95e --- /dev/null +++ b/toolkit/andlabs2/plugin.go @@ -0,0 +1,43 @@ +package main + +import ( + "log" + + "git.wit.org/wit/gui/toolkit" +) + +// This is a map between the widgets in wit/gui and the internal structures of gocui + +var mapWidgets map[*andlabsT]*toolkit.Widget +var mapToolkits map[*toolkit.Widget]*andlabsT + +// This lists out the know mappings +func listMap() { + log.Println("listMap() HERE") + log.Println("listMap() HERE") + log.Println("listMap() HERE mapWidgets()") + for t, w := range mapWidgets { + log.Println("andlabs =", t.Name, "widget =", w.Name) + } + log.Println("listMap() HERE mapToolkits()") + for w, t := range mapToolkits { + log.Println("andlabs =", t, "widget =", w.Name) + forceDump(t) + } +} + +func mapWidgetsToolkits(w *toolkit.Widget, t *andlabsT) { + if (mapToolkits[w] == nil) { + mapToolkits[w] = t + } else { + log.Println("WTF: mapToolkits already installed") + panic("WTF") + } + + if (mapWidgets[t] == nil) { + mapWidgets[t] = w + } else { + log.Println("WTF: mapWidgets already installed") + panic("WTF") + } +} diff --git a/toolkit/andlabs2/slider.go b/toolkit/andlabs2/slider.go new file mode 100644 index 0000000..f9ff0f3 --- /dev/null +++ b/toolkit/andlabs2/slider.go @@ -0,0 +1,48 @@ +package main + +import ( + "log" + "os" + + "git.wit.org/wit/gui/toolkit" + + "github.com/andlabs/ui" + _ "github.com/andlabs/ui/winmanifest" +) + +func (t andlabsT) NewSlider(title string, x int, y int) *andlabsT { + // make new node here + log.Println("gui.Toolkit.NewSpinbox()", x, y) + var newt andlabsT + + if (t.uiBox == nil) { + log.Println("gui.ToolkitNode.NewGroup() node.UiBox == nil. I can't add a range UI element without a place to put it") + log.Println("probably could just make a box here?") + os.Exit(0) + return nil + } + + s := ui.NewSlider(x, y) + newt.uiSlider = s + newt.uiBox = t.uiBox + t.uiBox.Append(s, stretchy) + + s.OnChanged(func(spin *ui.Slider) { + newt.commonChange("Slider") + }) + + return &newt +} + +func NewSlider(parentW *toolkit.Widget, w *toolkit.Widget) { + var newt *andlabsT + log.Println("gui.andlabs.NewTab()", w.Name) + + t := mapToolkits[parentW] + if (t == nil) { + log.Println("go.andlabs.NewTab() toolkit struct == nil. name=", parentW.Name, w.Name) + return + } + newt = t.NewSlider(w.Name, w.X, w.Y) + mapWidgetsToolkits(w, newt) +} diff --git a/toolkit/andlabs2/spinner.go b/toolkit/andlabs2/spinner.go new file mode 100644 index 0000000..6505b48 --- /dev/null +++ b/toolkit/andlabs2/spinner.go @@ -0,0 +1,30 @@ +package main + +import "log" +import "os" + +import "github.com/andlabs/ui" +import _ "github.com/andlabs/ui/winmanifest" + +func (t andlabsT) NewSpinner(title string, x int, y int) *andlabsT { + // make new node here + log.Println("gui.Toolkit.NewSpinner()", x, y) + var newt andlabsT + + if (t.uiBox == nil) { + log.Println("gui.ToolkitNode.NewSpinner() node.UiBox == nil. I can't add a range UI element without a place to put it") + os.Exit(0) + return nil + } + + s := ui.NewSpinbox(x, y) + newt.uiSpinbox = s + newt.uiBox = t.uiBox + t.uiBox.Append(s, stretchy) + + s.OnChanged(func(s *ui.Spinbox) { + newt.commonChange("Spinner") + }) + + return &newt +} diff --git a/toolkit/andlabs2/structs.go b/toolkit/andlabs2/structs.go new file mode 100644 index 0000000..2f3fa72 --- /dev/null +++ b/toolkit/andlabs2/structs.go @@ -0,0 +1,249 @@ +package main + +import "log" + +import "github.com/andlabs/ui" +import _ "github.com/andlabs/ui/winmanifest" + +import "github.com/davecgh/go-spew/spew" + +var defaultBehavior bool = true + +var bookshelf bool // do you want things arranged in the box like a bookshelf or a stack? +var canvas bool // if set to true, the windows are a raw canvas +var menubar bool // for windows +var stretchy bool // expand things like buttons to the maximum size +var padded bool // add space between things like buttons +var margin bool // add space around the frames of windows + +var DebugToolkit bool + +func setDefaultBehavior(s bool) { + defaultBehavior = s + if (defaultBehavior) { + if (DebugToolkit) { + log.Println("Setting this toolkit to use the default behavior.") + log.Println("This is the 'guessing' part as defined by the wit/gui 'Principles'. Refer to the docs.") + } + stretchy = false + padded = true + menubar = true + margin = true + canvas = false + bookshelf = true // 99% of the time, things make a vertical stack of objects + + DebugToolkit = false + } else { + log.Println("This toolkit is set to ignore the default behavior.") + } +} + +func SetDebugToolkit (s bool) { + DebugToolkit = s +} + +func GetDebugToolkit () bool { + return DebugToolkit +} + +// stores the raw toolkit internals +type andlabsT struct { + id string + + Name string + Width int + Height int + + OnChanged func(*andlabsT) + OnExit func(*andlabsT) + + Custom func() + + uiBox *ui.Box + uiBox2 *ui.Box // temporary hack while implementing tabs + uiButton *ui.Button + uiControl *ui.Control + uiCombobox *ui.Combobox + uiCheckbox *ui.Checkbox + uiEntry *ui.Entry + uiMultilineEntry *ui.MultilineEntry + uiGroup *ui.Group + uiLabel *ui.Label + uiSlider *ui.Slider + uiSpinbox *ui.Spinbox + uiTab *ui.Tab + uiText *ui.EditableCombobox + uiWindow *ui.Window + UiWindowBad *ui.Window + + // used as a counter to work around limitations of widgets like combobox + // this is probably fucked up and in many ways wrong because of unsafe goroutine threading + // but it's working for now due to the need for need for a correct interaction layer betten toolkits + c int + val map[int]string + text string +} + +func (t *andlabsT) String() string { + return t.GetText() +} + +func forceDump(t *andlabsT) { + tmp := DebugToolkit + DebugToolkit = true + t.Dump() + DebugToolkit = tmp +} + +func (t *andlabsT) GetText() string { + t.Dump() + if (DebugToolkit) { + log.Println("gui.Toolkit.Text() Enter") + scs := spew.ConfigState{MaxDepth: 1} + scs.Dump(t) + } + if (t.uiEntry != nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.Value() =", t.uiEntry.Text()) + } + return t.uiEntry.Text() + } + if (t.uiMultilineEntry != nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.Value() =", t.uiMultilineEntry.Text()) + } + text := t.uiMultilineEntry.Text() + if (DebugToolkit) { + log.Println("gui.Toolkit.Value() text =", text) + } + t.text = text + return text + } + if (t.uiCombobox != nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.GetText() =", t.text) + } + return t.text + } + return "" +} + +func (t *andlabsT) SetText(s string) bool { + if (DebugToolkit) { + log.Println("gui.Toolkit.Text() Enter") + scs := spew.ConfigState{MaxDepth: 1} + scs.Dump(t) + } + if (t.uiEntry != nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.Value() =", t.uiEntry.Text) + } + t.uiEntry.SetText(s) + return true + } + if (t.uiMultilineEntry != nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.Value() =", t.uiMultilineEntry.Text) + } + t.uiMultilineEntry.SetText(s) + return true + } + return false +} + +func sanity(t *andlabsT) bool { + if (DebugToolkit) { + log.Println("gui.Toolkit.Value() Enter") + scs := spew.ConfigState{MaxDepth: 1} + scs.Dump(t) + } + if (t.uiEntry == nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.Value() =", t.uiEntry.Text) + } + return false + } + return true +} + +func (t *andlabsT) SetValue(i int) bool { + log.Println("gui.Toolkit.SetValue() START") + if (sanity(t)) { + return false + } + t.Dump() + // panic("got to toolkit.SetValue") + return true +} + +func (t *andlabsT) Value() int { + if (DebugToolkit) { + log.Println("gui.Toolkit.Value() Enter") + scs := spew.ConfigState{MaxDepth: 1} + scs.Dump(t) + } + if (t == nil) { + log.Println("gui.Toolkit.Value() can not get value t == nil") + return 0 + } + if (t.uiSlider != nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.Value() =", t.uiSlider.Value) + } + return t.uiSlider.Value() + } + if (t.uiSpinbox != nil) { + if (DebugToolkit) { + log.Println("gui.Toolkit.Value() =", t.uiSpinbox.Value) + } + return t.uiSpinbox.Value() + } + log.Println("gui.Toolkit.Value() Could not find a ui element to get a value from") + return 0 +} + +func (t *andlabsT) Dump() { + if ! DebugToolkit { + return + } + log.Println("gui.Toolkit.Dump() Name = ", t.Name, t.Width, t.Height) + if (t.uiBox != nil) { + log.Println("gui.Toolkit.Dump() uiBox =", t.uiBox) + } + if (t.uiButton != nil) { + log.Println("gui.Toolkit.Dump() uiButton =", t.uiButton) + } + if (t.uiCombobox != nil) { + log.Println("gui.Toolkit.Dump() uiCombobox =", t.uiCombobox) + } + if (t.uiWindow != nil) { + log.Println("gui.Toolkit.Dump() uiWindow =", t.uiWindow) + } + if (t.uiTab != nil) { + log.Println("gui.Toolkit.Dump() uiTab =", t.uiTab) + } + if (t.uiGroup != nil) { + log.Println("gui.Toolkit.Dump() uiGroup =", t.uiGroup) + } + if (t.uiEntry != nil) { + log.Println("gui.Toolkit.Dump() uiEntry =", t.uiEntry) + } + if (t.uiMultilineEntry != nil) { + log.Println("gui.Toolkit.Dump() uiMultilineEntry =", t.uiMultilineEntry) + } + if (t.uiSlider != nil) { + log.Println("gui.Toolkit.Dump() uiSlider =", t.uiSlider) + } + if (t.uiCheckbox != nil) { + log.Println("gui.Toolkit.Dump() uiCheckbox =", t.uiCheckbox) + } + if (t.OnExit != nil) { + log.Println("gui.Toolkit.Dump() OnExit =", t.OnExit) + } + if (t.Custom != nil) { + log.Println("gui.Toolkit.Dump() Custom =", t.Custom) + } + log.Println("gui.Toolkit.Dump() c =", t.c) + log.Println("gui.Toolkit.Dump() val =", t.val) + log.Println("gui.Toolkit.Dump() text =", t.text) +} diff --git a/toolkit/andlabs2/tab.go b/toolkit/andlabs2/tab.go new file mode 100644 index 0000000..0556fb9 --- /dev/null +++ b/toolkit/andlabs2/tab.go @@ -0,0 +1,147 @@ +package main + +import ( + "log" + "time" + + "git.wit.org/wit/gui/toolkit" + + "github.com/andlabs/ui" + _ "github.com/andlabs/ui/winmanifest" +) + +/* + This adds a tab + + andlabs/ui is goofy in the sense that you have to determine + if the ui.Window already has a tab in it. If it does, then + you need to add this tab and not run SetChild() on the window + or instead it replaces the existing tab with the new one + + I work around this by always sending a Toolkit that is a tab + once there is one. If you send a Window here, it will replace + any existing tabs rather than adding a new one +*/ +func (t *andlabsT) newTab(name string) *andlabsT { + // var w *ui.Window + var newt *andlabsT + + log.Println("gui.toolkit.AddTab() sleep 3") + + if (t.uiWindow == nil) { + log.Println("gui.Toolkit.UiWindow == nil. I can't add a toolbar without window") + return nil + } + + if (t.uiTab == nil) { + // this means you have to make a new tab + log.Println("gui.toolkit.NewTab() GOOD. This should be the first tab:", name) + newt = newTab(t.uiWindow, name) + t.uiTab = newt.uiTab + } else { + // this means you have to append a tab + log.Println("gui.toolkit.NewTab() GOOD. This should be an additional tab:", name) + newt = t.appendTab(name) + } + + newt.Name = name + + if (DebugToolkit) { + log.Println("t:") + t.Dump() + log.Println("newt:") + newt.Dump() + } + + return newt +} + +// This sets _all_ the tabs to Margin = true +// +// TODO: do proper tab tracking (will be complicated). low priority +func tabSetMargined(tab *ui.Tab) { + c := tab.NumPages() + for i := 0; i < c; i++ { + if (DebugToolkit) { + log.Println("SetMargined", i, margin) + } + tab.SetMargined(i, margin) + } +} + +func newTab(w *ui.Window, name string) *andlabsT { + var t andlabsT + if (DebugToolkit) { + log.Println("gui.toolkit.NewTab() ADD", name) + } + + if (w == nil) { + log.Println("gui.toolkit.NewTab() node.UiWindow == nil. I can't add a tab without a window") + log.Println("gui.toolkit.NewTab() node.UiWindow == nil. I can't add a tab without a window") + log.Println("gui.toolkit.NewTab() node.UiWindow == nil. I can't add a tab without a window") + time.Sleep(1 * time.Second) + return nil + } + if (DebugToolkit) { + log.Println("gui.toolkit.AddTab() START name =", name) + } + tab := ui.NewTab() + w.SetMargined(margin) + + hbox := ui.NewHorizontalBox() // this makes everything go along the horizon + hbox.SetPadded(padded) + tab.Append(name, hbox) + tabSetMargined(tab) // TODO: run this in the right place(?) + w.SetChild(tab) + + t.uiWindow = w + t.uiTab = tab + t.uiBox = hbox + return &t +} + +func (t *andlabsT) appendTab(name string) *andlabsT { + var newT andlabsT + if (DebugToolkit) { + log.Println("gui.toolkit.NewTab() ADD", name) + } + + if (t.uiTab == nil) { + log.Println("gui.Toolkit.UiWindow == nil. I can't add a widget without a place to put it") + panic("should never have happened. wit/gui/toolkit has ui.Tab == nil") + } + if (DebugToolkit) { + log.Println("gui.toolkit.AddTab() START name =", name) + } + + var hbox *ui.Box + if (defaultBehavior) { + hbox = ui.NewHorizontalBox() + } else { + if (bookshelf) { + hbox = ui.NewHorizontalBox() + } else { + hbox = ui.NewVerticalBox() + } + } + hbox.SetPadded(padded) + t.uiTab.Append(name, hbox) + + newT.uiWindow = t.uiWindow + newT.uiTab = t.uiTab + newT.uiBox = hbox + return &newT +} + +func NewTab(parentW *toolkit.Widget, w *toolkit.Widget) { + var newt *andlabsT + log.Println("gui.andlabs.NewTab()", w.Name) + + t := mapToolkits[parentW] + if (t == nil) { + log.Println("go.andlabs.NewTab() toolkit struct == nil. name=", parentW.Name, w.Name) + return + } + newt = t.newTab(w.Name) + mapWidgetsToolkits(w, newt) +} diff --git a/toolkit/andlabs2/textbox.go b/toolkit/andlabs2/textbox.go new file mode 100644 index 0000000..c7a9390 --- /dev/null +++ b/toolkit/andlabs2/textbox.go @@ -0,0 +1,70 @@ +package main + +import "log" + +import "git.wit.org/wit/gui/toolkit" + +import "github.com/andlabs/ui" +import _ "github.com/andlabs/ui/winmanifest" + +func (t andlabsT) NewTextbox(name string) *andlabsT { + var newt andlabsT + + if (DebugToolkit) { + log.Println("gui.Toolkit.NewTextbox()", name) + } + if t.broken() { + return nil + } + + c := ui.NewNonWrappingMultilineEntry() + newt.uiMultilineEntry = c + + newt.uiBox = t.uiBox + newt.Name = name + if (defaultBehavior) { + t.uiBox.Append(c, true) + } else { + t.uiBox.Append(c, stretchy) + } + + c.OnChanged(func(spin *ui.MultilineEntry) { + newt.commonChange("Textbox") + }) + + return &newt +} + +func NewTextbox(parentW *toolkit.Widget, w *toolkit.Widget) { + var t, newt *andlabsT + log.Println("gui.andlabs.NewTextbox()", w.Name) + + t = mapToolkits[parentW] + if (t == nil) { + log.Println("go.andlabs.NewTextbox() toolkit struct == nil. name=", parentW.Name, w.Name) + return + } + + if t.broken() { + return + } + newt = new(andlabsT) + + newt.uiLabel = ui.NewLabel(w.Name) + newt.uiBox = t.uiBox + + if (DebugToolkit) { + log.Println("gui.Toolbox.NewTextbox() about to append to Box parent t:", w.Name) + t.Dump() + log.Println("gui.Toolbox.NewTextbox() about to append to Box new t:", w.Name) + newt.Dump() + } + if (t.uiBox != nil) { + t.uiBox.Append(newt.uiLabel, false) + } else { + log.Println("ERROR: wit/gui andlabs couldn't place this Textbox in a box") + return + } + + mapWidgetsToolkits(w, newt) +} diff --git a/toolkit/andlabs2/window.go b/toolkit/andlabs2/window.go new file mode 100644 index 0000000..b360cb8 --- /dev/null +++ b/toolkit/andlabs2/window.go @@ -0,0 +1,78 @@ +package main + +import ( + "log" + + "github.com/andlabs/ui" + _ "github.com/andlabs/ui/winmanifest" + + "git.wit.org/wit/gui/toolkit" +) + +func (t *andlabsT) MessageWindow(msg1 string, msg2 string) { + ui.MsgBox(t.uiWindow, msg1, msg2) +} + +func (t *andlabsT) ErrorWindow(msg1 string, msg2 string) { + ui.MsgBoxError(t.uiWindow, msg1, msg2) +} + +func NewWindow(w *toolkit.Widget) { + var t *andlabsT + + if (DebugToolkit) { + log.Println("toolkit NewWindow", w.Name, w.Width, w.Height) + } + + if (w == nil) { + log.Println("wit/gui plugin error. widget == nil") + return + } + t = new(andlabsT) + // t = NewWindow2(w.Name, w.Width, w.Height) + +// func NewWindow2(title string, x int, y int) *andlabsT { + // menubar bool is if the OS defined border on the window should be used + win := ui.NewWindow(w.Name, w.Width, w.Height, menubar) + win.SetBorderless(canvas) + win.SetMargined(margin) + win.OnClosing(func(*ui.Window) bool { + if (DebugToolkit) { + log.Println("ui.Window().OnExit() SHOULD ATTEMPT CALLBACK here") + t.Dump() + } + if (w.Custom != nil) { + w.Custom() + return true + } + if (w.Event != nil) { + w.Event(w) + return true + } + if (DebugToolkit) { + log.Println("andlabs.ui.Window().OnClosing() was not defined") + } + return false + }) + win.Show() + t.uiWindow = win + t.UiWindowBad = win // deprecate this as soon as possible + t.Name = w.Name + + mapWidgetsToolkits(w, t) + return +} + +func (t *andlabsT) SetWindowTitle(title string) { + if (DebugToolkit) { + log.Println("toolkit NewWindow", t.Name, "title", title) + } + win := t.uiWindow + if (win != nil) { + win.SetTitle(title) + } else { + if (DebugToolkit) { + log.Println("Setting the window title", title) + } + } +} diff --git a/toolkit/gocui/Makefile b/toolkit/gocui/Makefile index 27f23ed..597b2b2 100644 --- a/toolkit/gocui/Makefile +++ b/toolkit/gocui/Makefile @@ -6,3 +6,6 @@ build: plugin: GO111MODULE="off" go build -buildmode=plugin -o ../gocui.so + +objdump: + objdump -t ../gocui.so |less diff --git a/toolkit/gocui/button.go b/toolkit/gocui/button.go index 8fb6e05..5b41c48 100644 --- a/toolkit/gocui/button.go +++ b/toolkit/gocui/button.go @@ -7,42 +7,49 @@ import ( "strings" "github.com/awesome-gocui/gocui" + "git.wit.org/wit/gui/toolkit" ) -func (w *Widget) AddButton() { -// func (g greeting) AddButton() { +func NewButton(parentW *toolkit.Widget, w *toolkit.Widget) { log.Println("gui.gocui.AddButton()", w.Name) - addButton2(w.Name, w.Event) + addButton(w.Name) + // viewWidget[v] = w + stringWidget[w.Name] = w + listMap() } -func addButton2(name string, e func(*Widget) *Widget) { - addButton(name) -} - -func addButton(name string) error { +func addButton(name string) *gocui.View { t := len(name) + if (baseGui == nil) { + panic("WTF") + } v, err := baseGui.SetView(name, currentX, currentY, currentX+t+3, currentY+2, 0) if err == nil { - return err + log.Println("wit/gui internal plugin error", err) + return nil } if !errors.Is(err, gocui.ErrUnknownView) { - return err + log.Println("wit/gui internal plugin error", err) + return nil } v.Wrap = true fmt.Fprintln(v, " " + name) fmt.Fprintln(v, strings.Repeat("foo\n", 2)) - if _, err := baseGui.SetCurrentView(name); err != nil { - return err + currentView, err := baseGui.SetCurrentView(name) + if err != nil { + log.Println("wit/gui internal plugin error", err) + return nil } + log.Println("wit/gui addbutton() current view name =", currentView.Name()) views = append(views, name) curView = len(views) - 1 idxView += 1 currentY += 3 - if (groupSize < len(views)) { - groupSize = len(views) + if (groupSize < len(name)) { + groupSize = len(name) } - return nil + return currentView } diff --git a/toolkit/gocui/main.go b/toolkit/gocui/gocui.go index 314f5be..d614387 100644 --- a/toolkit/gocui/main.go +++ b/toolkit/gocui/gocui.go @@ -8,6 +8,9 @@ import ( "errors" "fmt" "log" + "os" + + "git.wit.org/wit/gui/toolkit" "github.com/awesome-gocui/gocui" ) @@ -22,61 +25,57 @@ var ( currentY = 2 groupSize = 0 baseGui *gocui.Gui + helpLabel *gocui.View + err error + ch chan(func ()) ) -var helpLabel *gocui.View - func Init() { - // setup log to write to a file -// logInit() - - g, err := gocui.NewGui(gocui.OutputNormal, true) - baseGui = g + baseGui, err = gocui.NewGui(gocui.OutputNormal, true) if err != nil { log.Panicln(err) } - defer g.Close() - g.Highlight = true - g.SelFgColor = gocui.ColorRed - g.SelFrameColor = gocui.ColorRed + baseGui.Highlight = true + baseGui.SelFgColor = gocui.ColorRed + baseGui.SelFrameColor = gocui.ColorRed - g.SetManagerFunc(layout) + baseGui.SetManagerFunc(layout) - if err := initKeybindings(g); err != nil { + if err := initKeybindings(baseGui); err != nil { log.Panicln(err) } - if err := newView(g); err != nil { - log.Panicln(err) - } - - addButton("hello") - addButton("world") - addButton("foo") - addGroup("blank") - addButton("bar") - addButton("bar none") - addButton("bar going") + viewWidget = make(map[*gocui.View]*toolkit.Widget) + stringWidget = make(map[string]*toolkit.Widget) - addGroup("te") - addButton("world 2") - addButton("foo 2") + ch = make(chan func()) +} - if err := baseGui.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) { - log.Panicln(err) - } +func Queue(f func()) { + log.Println("QUEUEEEEE") + f() } -func ToolkitMain() { +func Main(f func()) { + if (baseGui == nil) { + panic("WTF Main()") + } + defer baseGui.Close() + // log.Println("ADDDDDDDD BUTTTTTTTTTON") + // addButton("test 3") + f() if err := baseGui.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) { log.Panicln(err) } + baseGui.Close() + os.Exit(0) } func layout(g *gocui.Gui) error { var err error maxX, _ := g.Size() + helpLabel, err = g.SetView("help", maxX-32, 0, maxX-1, 11, 0) if err != nil { if !errors.Is(err, gocui.ErrUnknownView) { diff --git a/toolkit/gocui/greeter.go b/toolkit/gocui/greeter.go deleted file mode 100644 index e3094e5..0000000 --- a/toolkit/gocui/greeter.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "log" - - // "errors" - // "fmt" - // "strings" - // "github.com/awesome-gocui/gocui" -) - -type greeting string - -// stores the raw toolkit internals -type toolkit struct { - id string - Name string - - OnChanged func(toolkit) -} - -// this is exported -var Greeter greeting -var Toolkit toolkit - -// func main() { -func (g greeting) Greet() { - log.Println("Hello Universe") - Init() - // ToolkitMain() -} - -func (g greeting) JcarrButton() { - log.Println("Hello GreetButton meet Universe") - addButton("Greet foo") - addButton("Greet foo 2") -} - -func addGroup(name string) { - log.Println("addGroup()", name) - currentY = 2 - currentX += groupSize + 6 -} - -func (g greeting) AddButton(name string) { -// func (g greeting) AddButton() { - log.Println("gui.gocui.AddButton()", name) - addButton(name) -} - -/* -func addButton(name string) error { - t := len(name) - v, err := baseGui.SetView(name, currentX, currentY, currentX+t+3, currentY+2, 0) - if err == nil { - return err - } - if !errors.Is(err, gocui.ErrUnknownView) { - return err - } - - v.Wrap = true - fmt.Fprintln(v, " " + name) - fmt.Fprintln(v, strings.Repeat("foo\n", 2)) - - if _, err := baseGui.SetCurrentView(name); err != nil { - return err - } - - views = append(views, name) - curView = len(views) - 1 - idxView += 1 - currentY += 3 - if (groupSize < len(views)) { - groupSize = len(views) - } - return nil -} -*/ diff --git a/toolkit/gocui/group.go b/toolkit/gocui/group.go new file mode 100644 index 0000000..7cea346 --- /dev/null +++ b/toolkit/gocui/group.go @@ -0,0 +1,38 @@ +package main + +import ( + "log" + + "git.wit.org/wit/gui/toolkit" +) + +func NewGroup(parentW *toolkit.Widget, w *toolkit.Widget) { + if (parentW == nil) { + log.Println("wit/gui plugin error. parent widget == nil") + return + } + if (w == nil) { + log.Println("wit/gui plugin error. widget == nil") + return + } + if (w.Name == "") { + w.Name = parentW.Name + } + if (w.Name == "") { + w.Name = "nil newGroup" + } + log.Println("gui.gocui.AddGroup", w.Name) + addGroup(w.Name) + stringWidget[w.Name] = w +} + +func addGroup(name string) { + log.Println("addGroup() START name =", name) + log.Println("addGroup() START groupSize =", groupSize, "currentY =", currentY, "currentX =", currentX) + + currentY = 2 + currentX += groupSize + 5 + groupSize = 0 + + log.Println("addGroup() START, RESET Y = 3, RESET X = ", currentX) +} diff --git a/toolkit/gocui/keybindings.go b/toolkit/gocui/keybindings.go index fdac1ff..69def39 100644 --- a/toolkit/gocui/keybindings.go +++ b/toolkit/gocui/keybindings.go @@ -11,6 +11,7 @@ import ( // "strings" "github.com/awesome-gocui/gocui" + "git.wit.org/wit/gui/toolkit" ) func initKeybindings(g *gocui.Gui) error { @@ -87,6 +88,21 @@ func initKeybindings(g *gocui.Gui) error { if err := g.SetKeybinding("", gocui.KeyEnter, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { log.Println("enter", v.Name()) + var w *toolkit.Widget + w = stringWidget[v.Name()] + if (w == nil) { + log.Println("COULD NOT FIND WIDGET", v.Name()) + } else { + log.Println("FOUND WIDGET!", w) + if (w.Custom != nil) { + w.Custom() + return nil + } + if (w.Event != nil) { + w.Event(w) + return nil + } + } return nil }); err != nil { return err @@ -105,17 +121,11 @@ func initKeybindings(g *gocui.Gui) error { }); err != nil { return err } - if err := g.SetKeybinding("", 'j', gocui.ModNone, - func(g *gocui.Gui, v *gocui.View) error { - return newJ(g) - }); err != nil { - return err - } if err := g.SetKeybinding("", 'h', gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error { log.Println("help", v.Name()) tmp, _ := g.SetViewOnTop("help") - log.Println("help 2", tmp.Name(), "blah") + log.Println("help 2", tmp.Name()) // g.SetView("help", 2, 2, 30, 15, 0); g.SetCurrentView("help") // moveView(g, tmp, 0, -delta) diff --git a/toolkit/gocui/newJ.go b/toolkit/gocui/newJ.go deleted file mode 100644 index 3e04cd3..0000000 --- a/toolkit/gocui/newJ.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2014 The gocui Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package main - -import ( - "errors" - "fmt" - "log" - "strings" - - "github.com/awesome-gocui/gocui" -) - -var topX int = 2 -var bottomX int = 20 -var topY int = 2 -var bottomY int = 7 - -func newJ(g *gocui.Gui) error { - // maxX, maxY := g.Size() - name := fmt.Sprintf("jcarr %v foo ", idxView) - v, err := g.SetView(name, topX, topY, bottomX, bottomY, 0) - if err == nil { - return err - } - if !errors.Is(err, gocui.ErrUnknownView) { - return err - } - - v.Wrap = true - fmt.Fprintln(v, name) - fmt.Fprintln(v, strings.Repeat("foo\n", 2)) - // fmt.Fprintln(v, strings.Repeat(name+" ", 30)) - log.Println("newJ added a new view", v.Name()) - - if _, err := g.SetCurrentView(name); err != nil { - return err - } - - views = append(views, name) - curView = len(views) - 1 - idxView += 1 - return nil -} diff --git a/toolkit/gocui/plugin.go b/toolkit/gocui/plugin.go new file mode 100644 index 0000000..5be26f3 --- /dev/null +++ b/toolkit/gocui/plugin.go @@ -0,0 +1,28 @@ +package main + +import ( + "log" + + "git.wit.org/wit/gui/toolkit" + + "github.com/awesome-gocui/gocui" +) + +// This is a map between the widgets in wit/gui and the internal structures of gocui +var viewWidget map[*gocui.View]*toolkit.Widget +var stringWidget map[string]*toolkit.Widget + +func Quit() { + baseGui.Close() +} + +// This lists out the know mappings +func listMap() { + for v, w := range viewWidget { + log.Println("view =", v.Name, "widget name =", w.Name) + } + for s, w := range stringWidget { + log.Println("string =", s, "widget =", w) + } +} + diff --git a/toolkit/gocui/views.go b/toolkit/gocui/views.go index 6c9c65d..defe2b2 100644 --- a/toolkit/gocui/views.go +++ b/toolkit/gocui/views.go @@ -11,6 +11,7 @@ import ( "strings" "github.com/awesome-gocui/gocui" + // "git.wit.org/wit/gui/toolkit" ) func newView(g *gocui.Gui) error { diff --git a/toolkit/gocui/widget.go b/toolkit/gocui/widget.go deleted file mode 100644 index 758ac38..0000000 --- a/toolkit/gocui/widget.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -// passes information between the toolkit library (plugin) - -// All Toolkit interactions should be done via a channel or Queue() - -// This is the only thing that is passed between the toolkit plugin - -// what names should be used? This is not part of [[Graphical Widget]] -// Event() seems like a good name. -// Could a protobuf be used here? (Can functions be passed?) -type Widget struct { - i int - s string - - Name string - Width int - Height int - - Event func(*Widget) *Widget - - // Probably deprecate these - OnChanged func(*Widget) - Custom func(*Widget) - OnExit func(*Widget) -} diff --git a/toolkit/gocui/window.go b/toolkit/gocui/window.go new file mode 100644 index 0000000..b2d241d --- /dev/null +++ b/toolkit/gocui/window.go @@ -0,0 +1,18 @@ +package main + +import ( + "log" + + "git.wit.org/wit/gui/toolkit" +) + +func NewWindow(w *toolkit.Widget) { + if (w == nil) { + log.Println("wit/gui plugin error. widget == nil") + return + } + if (w.Name == "") { + w.Name = "nil newWindow" + } + log.Println("gui.gocui.AddWindow", w.Name) +} diff --git a/toolkit/hello/Makefile b/toolkit/hello/Makefile deleted file mode 100644 index 8271843..0000000 --- a/toolkit/hello/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -all: plugin - ldd ../hello.so - -build: - GO111MODULE="off" go build - -plugin: - GO111MODULE="off" go build -buildmode=plugin -o ../hello.so diff --git a/toolkit/hello/greeter.go b/toolkit/hello/greeter.go deleted file mode 100644 index 1eb3307..0000000 --- a/toolkit/hello/greeter.go +++ /dev/null @@ -1,23 +0,0 @@ -package main - -import ( -// "errors" - "fmt" -// "log" -// "strings" - -// "github.com/awesome-gocui/gocui" -) - -type greeting string - - -// func main() { -func (g greeting) Greet() { - fmt.Println("Hello Universe") - Init() - // ToolkitMain() -} - -// this is exported -var Greeter greeting diff --git a/toolkit/hello/main.go b/toolkit/hello/main.go deleted file mode 100644 index 3a72d1f..0000000 --- a/toolkit/hello/main.go +++ /dev/null @@ -1,44 +0,0 @@ -// This creates a simple hello world window -package main - -import ( - "os" - "log" - "git.wit.org/wit/gui" -) - -func Init() { - gui.Main(myGUI) -} - -// This initializes the first window -func myGUI() { - var w *gui.Node - gui.Config.Title = "Hello World golang wit/gui Window" - gui.Config.Width = 640 - gui.Config.Height = 480 - gui.Config.Exit = myExit - - w = gui.NewWindow() - addHelloWorld(w, "A Simple Tab") -} - -func addHelloWorld(window *gui.Node, title string) { - var newNode, g, tb *gui.Node - - newNode = window.NewTab(title) - - g = newNode.NewGroup("hello") - tb = g.NewTextbox("hello world box") // when debugging, this string will be used - tb.OnChanged = func(*gui.Node) { - s := tb.GetText() - log.Println("text box =", s) - } - tb.SetText("world") -} - -func myExit(n *gui.Node) { - log.Println("exit() here") - os.Exit(0) -} - diff --git a/widget.go b/toolkit/widget.go index 2d97f61..9003546 100644 --- a/widget.go +++ b/toolkit/widget.go @@ -1,4 +1,4 @@ -package gui +package toolkit // passes information between the toolkit library (plugin) @@ -16,13 +16,25 @@ type Widget struct { Name string Width int Height int + X int + Y int + Custom func() Event func(*Widget) *Widget // Probably deprecate these - OnChanged func(*Widget) - Custom func(*Widget) - OnExit func(*Widget) +// OnChanged func(*Widget) +// Custom func(*Widget) +// OnExit func(*Widget) +} + +type Blah struct { + i int + s string + + Name string + Width int + Height int } /* diff --git a/watchdog.go b/watchdog.go new file mode 100644 index 0000000..29ac21a --- /dev/null +++ b/watchdog.go @@ -0,0 +1,23 @@ +package gui + +import ( + "log" + "time" +) + +var watchtime time.Duration = 100 // in tenths of seconds + +/* + This program sits here. + If you exit here, the whole thing will os.Exit() + + This goroutine can be used like a watchdog timer +*/ +func Watchdog() { + var i = 1 + for { + log.Println("watchdog timer is alive. give me something to do.", i, "debug =", Config.Debug.Debug) + i += 1 + time.Sleep(watchtime * time.Second / 10) + } +} @@ -4,7 +4,7 @@ import ( "log" ) -import toolkit "git.wit.org/wit/gui/toolkit/andlabs" +//import toolkit "git.wit.org/wit/gui/toolkit/andlabs" // This routine creates a blank window with a Title and size (W x H) // @@ -13,33 +13,46 @@ import toolkit "git.wit.org/wit/gui/toolkit/andlabs" // cross platform, must pass UI changes into the OS threads (that is // my guess). func NewWindow() *Node { - var n *Node - var t *toolkit.Toolkit + var newNode *Node +// var t *toolkit.Toolkit title := Config.Title - w := Config.Width - h := Config.Height - // f := Config.Exit - // Windows are created off of the master node of the Binary Tree - n = Config.master.New(title) + newNode = Config.master.New(title) - n.OnChanged = Config.Exit + newNode.Widget.Name = title + newNode.Widget.Width = Config.Width + newNode.Widget.Height = Config.Height - t = toolkit.NewWindow(title, w, h) - t.Custom = func () { - if (Config.Options.Debug) { - log.Println("Got to wit/gui Window Close START user defined close()") + if (Config.Exit != nil) { + newNode.custom = func() { + Config.Exit(newNode) } - if (n.OnChanged != nil) { - if (Config.Options.Debug) { - log.Println("Got to wit/gui Window Close SKIP node.custom() == nil") - } - n.OnChanged(n) - return + } + + if (newNode.custom == nil) { + newNode.custom = func () {StandardExit(newNode)} + } + + newNode.Widget.Custom = newNode.custom + + log.Println("gui.Node.Window()", title) + + // t = toolkit.NewWindow(title, w, h) + // n.toolkit = t + + for _, aplug := range allPlugins { + log.Println("gui.Node.NewWindow() toolkit plugin =", aplug.name) + if (aplug.NewWindow == nil) { + log.Println("gui.Node.NewWindow() is nil") + continue } - StandardExit(n) + aplug.NewWindow(&newNode.Widget) } - n.toolkit = t - return n + + // TODO: this is still confusing and probably wrong. This needs to communicate through a channel + // newNode.toolkit = n.toolkit.NewButton(name) + // newNode.toolkit.Custom = newNode.Widget.Custom + + return newNode } |
