Massive refactor to use go plugins. This is neat.

update README.md
    set xterm title. make os.Exit() default on window close
    add a toolkit.Widget to the node structure
    remove 'Greeter' symbol mapping scheme
    removed the testing greeter code
    plugins:
        attempt to load plugins in a sensible order
    andlabs/ui:
        working andlabs/ui plugin (andlabs2)
   	buttons work in andlabs plugin
	TODO: re-implement non-plugin version for Windows
		mswindows doesn't support go plugins yet
    gocui:
        put the gocui console so file in the binary
        does a full init of gocui plugin
        Button() and Group() working very well with gogui
    	cleanly exit gocui
    technically you can load two toolkits at the same time
        kinda both working at the same time. esoteric
        two working plugins at the same time
        give up working on two gui's at the same time
        this is fun, but _not interesting
        wow. this actually works. NewButton() from both toolkits
    examples:
        all the examples run again
        remove early helloplugin example
        buttonplugin example cmd code
        buttonplugin runs and ldd is minimum

Signed-off-by: Jeff Carr <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2022-11-13 08:53:03 -06:00
parent 06237c36fa
commit 8221522ba8
63 changed files with 2280 additions and 1032 deletions

View File

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

View File

@ -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.
The next step is to allow this to work against go-gtk and go-qt.
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.
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 [Init](/main.go#L41)
`func LoadPlugin(name string) *plugin.Plugin`
`func Init()`
### func [LookupJcarrButton](/plugin.go#L98)
### func [LoadToolkit](/plugin.go#L50)
`func LookupJcarrButton()`
`func LoadToolkit(name string) bool`
### func [Main](/main.go#L38)
loads and initializes a toolkit (andlabs/ui, gocui, etc)
### func [Main](/main.go#L86)
`func Main(f func())`
### func [Queue](/main.go#L51)
This should not pass a function
`func Queue(f func())`
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 [RunGreet](/plugin.go#L88)
`func RunGreet()`
### 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
### 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 [Greeter](/plugin.go#L17)
`type Greeter interface { ... }`
TODO: could a protobuf work here?
### 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 Widget struct { ... }`
`type Symbol any`
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?)
## Sub Packages
* [toolkit](./toolkit)
---
Readme created from Go doc with [goreadme](https://github.com/posener/goreadme)

180
README.md
View File

@ -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
* 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
@ -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 [GetDebug](/structs.go#L25)
`func DebugTab()`
`func GetDebug() bool`
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 [DemoToolkitWindow](/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](/window-demo.go#L10)
`func DemoWindow()`
This creates a window that shows how this package works
### func [GetDebugToolkit](/structs.go#L28)
### func [GetDebugToolkit](/structs.go#L37)
`func GetDebugToolkit() bool`
### func [GolangDebugWindow](/window-golang-debug.go#L20)
`func GolangDebugWindow()`
### func [IndentPrintln](/structs.go#L199)
### func [IndentPrintln](/structs.go#L188)
`func IndentPrintln(a ...interface{})`
### func [Main](/main.go#L31)
### func [Init](/main.go#L41)
`func Init()`
### func [LoadToolkit](/plugin.go#L37)
`func LoadToolkit(name string)`
loads and initializes a toolkit (andlabs/ui, gocui, etc)
### 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()
### type [Symbol](/plugin.go#L17)
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.
`type Symbol any`
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.
## Sub Packages
```golang
package main
* [need-to-redo](./need-to-redo)
import (
"git.wit.org/wit/gui"
)
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()
}
```
Output:
```
You get a window
```
### type [Widget](/structs.go#L74)
`type Widget int`
* [toolkit](./toolkit)
---
Readme created from Go doc with [goreadme](https://github.com/posener/goreadme)

View File

@ -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")
}
}
newNode.custom = custom
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)
}
return newNode
}

View File

@ -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()
*/
return c
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 newNode
}

View File

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

View File

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

View File

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

View File

@ -2,4 +2,5 @@ run: build
./debug
build:
go build
# go build
GO111MODULE="off" go build

View File

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

View File

@ -7,6 +7,7 @@ import (
)
func main() {
gui.Init()
gui.Main(helloworld)
}

View File

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

View File

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

48
doc.go
View File

@ -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
* 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.
The next step is to allow this to work against go-gtk and go-qt.
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.
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]
*/

View File

@ -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")
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
}
} else {
if (Config.Options.DebugChange) {
log.Println("Running n.OnChanged(n)")
}
n.OnChanged(n)
aplug.AddDropdownName(&n.Widget, name)
}
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()
}
}
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) AddDropdown(name string) {
n.toolkit.AddDropdown(name)
}
func (n *Node) SetDropdown(i int) {
n.toolkit.SetDropdown(i)
}
func (n *Node) NewDropdown(text string) *Node {
newNode := n.New(text)
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
}

View File

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

View File

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

View File

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

View File

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

19
int.go
View File

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

20
label.go Normal file
View File

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

111
main.go
View File

@ -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()
}
// load the gocui plugin
PlugGocli = LoadPlugin("../../toolkit/gocui.so")
PlugGocliOk = false
}
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
}
}
// 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)
}

13
node.go
View File

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

307
plugin.go
View File

@ -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
// var gBut plugin.Symbol
var jcarrBut plugin.Symbol
var symGreeter plugin.Symbol
var greeter Greeter
var ok bool
// loads and initializes a toolkit (andlabs/ui, gocui, etc)
func LoadToolkit(name string) bool {
var newPlug aplug
var typeToolkit plugin.Symbol
var typeToolkitCast Greeter
log.Println("gui.LoadToolkit() START")
newPlug.LoadOk = false
func LoadPlugin(name string) *plugin.Plugin {
scs := spew.ConfigState{MaxDepth: 1}
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
}
}
// load module
// 1. open the so file to load the symbols
plug, err := plugin.Open(name)
log.Println("plug =")
log.Println(scs.Sdump(plug))
// 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")
allPlugins = append(allPlugins, &newPlug)
log.Println("gui.LoadToolkit() END", newPlug.name, filename)
newPlug.LoadOk = true
return true
}
func loadFuncE(p *aplug, funcName string) func() {
var newfunc func()
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())
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
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))
if !ok {
log.Println("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name)
return nil
}
return newfunc
}
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("function name =", funcName, "names didn't map correctly. Fix the plugin name =", p.name)
return nil
}
return newfunc
}
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
}
return newfunc
}
// 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
test, err = p.plug.Lookup(funcName)
if err != nil {
log.Println("DID NOT FIND: name =", test, "err =", err)
return nil
}
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 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
}
filename = "/usr/share/wit/gui/" + name
p.plug = loadfile(filename)
if (p.plug != nil) {
p.filename = filename
return
}
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
}
// 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)
}
symGreeter, err = plug.Lookup("Greeter")
log.Println("symGreater", symGreeter)
log.Println(scs.Sdump(symGreeter))
if err != nil {
log.Println(err)
os.Exit(1)
}
// 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))
if !ok {
log.Println("unexpected type from module symbol")
os.Exit(1)
}
/*
typeToolkitCast, ok = typeToolkit.(Greeter)
if !ok {
log.Println("unexpected cast of Toolkit to Greeter")
os.Exit(1)
}
*/
return plug
}
func RunGreet() {
log.Println("gui.RunGreet() START")
if (greeter == nil) {
log.Println("wit/gui gocui plugin didn't load")
return
}
PlugGocliOk = true
greeter.Greet()
}
func LookupJcarrButton() {
log.Println("lookupJcarrButton() START")
if (greeter == nil) {
log.Println("wit/gui gocui plugin didn't load")
return
}
greeter.JcarrButton()
}
func GocuiAddButton(name string) {
log.Println("GocuiAddButton() START", name)
if (greeter == nil) {
log.Println("wit/gui gocui plugin didn't load")
return
}
greeter.AddButton(name)
}

View File

@ -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
newNode := n.New(name)
newNode.Widget.Name = name
newNode.Widget.X = x
newNode.Widget.Y = y
log.Println("toolkit.NewSlider() START", name)
n.verify()
// 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)
newNode.Widget.Custom = func() {
log.Println("even newer clicker() name in NewSlider", newNode.Widget)
}
sNode.toolkit = newT
sNode.Dump()
// panic("checking Custom()")
return 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)
}
return newNode
}

View File

@ -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
newNode := n.New(name)
newNode.Widget.Name = name
newNode.Widget.X = x
newNode.Widget.Y = y
log.Println("toolkit.NewSpinner() START", name)
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()
newT.Custom = func () {
commonCallback(sNode)
newNode.Widget.Custom = func() {
log.Println("even newer clicker() name in NewSpinner", newNode.Widget)
}
return 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 newNode
}

View File

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

23
tab.go
View File

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

View File

@ -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
newNode := n.New(name)
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)
c.toolkit = newt
c.custom = n.custom
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")
}
newNode.Widget.Custom = func() {
log.Println("even newer clicker() name in NewTextBox", newNode.Widget)
}
return c
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 newNode
}

View File

@ -0,0 +1,4 @@
all: plugin
plugin:
GO111MODULE="off" go build -buildmode=plugin -o ../andlabs2.so

66
toolkit/andlabs2/box.go Normal file
View File

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

View File

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

View File

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

View File

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

92
toolkit/andlabs2/demo.go Normal file
View File

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

View File

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

56
toolkit/andlabs2/group.go Normal file
View File

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

42
toolkit/andlabs2/label.go Normal file
View File

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

56
toolkit/andlabs2/main.go Normal file
View File

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

View File

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

View File

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

View File

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

249
toolkit/andlabs2/structs.go Normal file
View File

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

147
toolkit/andlabs2/tab.go Normal file
View File

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

View File

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

View File

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

View File

@ -6,3 +6,6 @@ build:
plugin:
GO111MODULE="off" go build -buildmode=plugin -o ../gocui.so
objdump:
objdump -t ../gocui.so |less

View File

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

View File

@ -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 {
log.Panicln(err)
}
if err := newView(g); err != nil {
if err := initKeybindings(baseGui); err != nil {
log.Panicln(err)
}
addButton("hello")
addButton("world")
addButton("foo")
viewWidget = make(map[*gocui.View]*toolkit.Widget)
stringWidget = make(map[string]*toolkit.Widget)
addGroup("blank")
addButton("bar")
addButton("bar none")
addButton("bar going")
addGroup("te")
addButton("world 2")
addButton("foo 2")
if err := baseGui.MainLoop(); err != nil && !errors.Is(err, gocui.ErrQuit) {
log.Panicln(err)
}
ch = make(chan func())
}
func ToolkitMain() {
func Queue(f func()) {
log.Println("QUEUEEEEE")
f()
}
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) {

View File

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

38
toolkit/gocui/group.go Normal file
View File

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

View File

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

View File

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

28
toolkit/gocui/plugin.go Normal file
View File

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

View File

@ -11,6 +11,7 @@ import (
"strings"
"github.com/awesome-gocui/gocui"
// "git.wit.org/wit/gui/toolkit"
)
func newView(g *gocui.Gui) error {

View File

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

18
toolkit/gocui/window.go Normal file
View File

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

View File

@ -1,8 +0,0 @@
all: plugin
ldd ../hello.so
build:
GO111MODULE="off" go build
plugin:
GO111MODULE="off" go build -buildmode=plugin -o ../hello.so

View File

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

View File

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

View File

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

23
watchdog.go Normal file
View File

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

View File

@ -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
}
StandardExit(n)
}
n.toolkit = t
return n
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
}
aplug.NewWindow(&newNode.Widget)
}
// 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
}