2021-10-06 08:36:28 -05:00
|
|
|
package gui
|
|
|
|
|
2021-10-06 10:43:58 -05:00
|
|
|
import (
|
2023-04-07 09:18:03 -05:00
|
|
|
"os"
|
2023-04-28 07:29:46 -05:00
|
|
|
"embed"
|
2023-03-29 23:03:04 -05:00
|
|
|
"git.wit.org/wit/gui/toolkit"
|
2021-10-06 10:43:58 -05:00
|
|
|
)
|
2021-10-06 08:36:28 -05:00
|
|
|
|
2023-04-08 15:34:36 -05:00
|
|
|
// TODO: make a fake 'plugin' channel of communication to andlabs for mswindows
|
2022-11-13 08:53:03 -06:00
|
|
|
// 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"
|
2022-10-20 06:55:42 -05:00
|
|
|
|
2022-11-06 12:59:24 -06:00
|
|
|
const Xaxis = 0 // stack things horizontally
|
|
|
|
const Yaxis = 1 // stack things vertically
|
2022-10-20 06:55:42 -05:00
|
|
|
|
|
|
|
func init() {
|
2023-03-01 11:35:36 -06:00
|
|
|
log("init() has been run")
|
2022-10-20 06:55:42 -05:00
|
|
|
|
|
|
|
Config.counter = 0
|
|
|
|
Config.prefix = "wit"
|
|
|
|
|
2022-11-06 12:59:24 -06:00
|
|
|
// Populates the top of the binary tree
|
2023-03-29 23:03:04 -05:00
|
|
|
Config.rootNode = addNode("guiBinaryTree")
|
|
|
|
Config.rootNode.WidgetType = toolkit.Root
|
2023-03-01 11:35:36 -06:00
|
|
|
|
2023-03-03 14:41:38 -06:00
|
|
|
// used to pass debugging flags to the toolkit plugins
|
2023-04-08 11:06:50 -05:00
|
|
|
Config.flag = Config.rootNode.newNode("flag", 0, nil)
|
2023-03-29 23:03:04 -05:00
|
|
|
Config.flag.WidgetType = toolkit.Flag
|
2023-04-07 21:22:51 -05:00
|
|
|
|
2023-04-26 20:56:25 -05:00
|
|
|
Config.flag = Config.rootNode.newNode("stdout", 0, nil)
|
|
|
|
Config.flag.WidgetType = toolkit.Stdout
|
|
|
|
|
2023-04-12 13:00:29 -05:00
|
|
|
Config.guiChan = make(chan toolkit.Action, 1)
|
2023-04-07 21:22:51 -05:00
|
|
|
go watchCallback()
|
2023-03-01 11:35:36 -06:00
|
|
|
}
|
|
|
|
|
2023-04-06 18:00:18 -05:00
|
|
|
func watchCallback() {
|
2023-04-08 14:31:00 -05:00
|
|
|
log(logInfo, "watchCallback() START")
|
2023-04-06 18:00:18 -05:00
|
|
|
for {
|
2023-04-08 14:31:00 -05:00
|
|
|
log(logNow, "watchCallback() restarted select for toolkit user events")
|
2023-04-06 18:00:18 -05:00
|
|
|
select {
|
|
|
|
case a := <-Config.guiChan:
|
2023-04-26 20:56:25 -05:00
|
|
|
if (a.ActionType == toolkit.UserQuit) {
|
2023-04-27 00:10:23 -05:00
|
|
|
log(logNow, "doUserEvent() User sent Quit()")
|
|
|
|
Config.rootNode.doCustom()
|
2023-04-26 20:56:25 -05:00
|
|
|
exit("wit/gui toolkit.UserQuit")
|
|
|
|
break
|
|
|
|
}
|
2023-04-27 00:10:23 -05:00
|
|
|
if (a.ActionType == toolkit.EnableDebug) {
|
|
|
|
log(logNow, "doUserEvent() Enable Debugging Window")
|
|
|
|
DebugWindow()
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
n := Config.rootNode.FindId(a.WidgetId)
|
2023-04-06 18:26:30 -05:00
|
|
|
if (n == nil) {
|
2023-04-08 14:31:00 -05:00
|
|
|
log(logError, "watchCallback() UNKNOWN widget id =", a.WidgetId, a.Name)
|
2023-04-06 18:26:30 -05:00
|
|
|
} else {
|
2023-04-08 14:31:00 -05:00
|
|
|
log(logNow, "watchCallback() FOUND widget id =", n.id, n.Name)
|
|
|
|
n.doUserEvent(a)
|
2023-04-06 18:26:30 -05:00
|
|
|
}
|
|
|
|
// this maybe a good idea?
|
|
|
|
// TODO: Throttle user events somehow
|
2023-04-28 10:30:27 -05:00
|
|
|
sleep(.01)
|
2023-04-06 18:26:30 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-06 20:25:14 -05:00
|
|
|
func (n *Node) doCustom() {
|
|
|
|
log(logNow, "doUserEvent() widget =", n.id, n.Name, n.WidgetType, n.B)
|
|
|
|
if (n.Custom == nil) {
|
|
|
|
log(debugError, "Custom() = nil. SKIPPING")
|
|
|
|
return
|
|
|
|
}
|
2023-04-08 14:31:00 -05:00
|
|
|
go n.Custom()
|
2023-04-06 20:25:14 -05:00
|
|
|
}
|
|
|
|
|
2023-04-06 18:26:30 -05:00
|
|
|
func (n *Node) doUserEvent(a toolkit.Action) {
|
|
|
|
log(logNow, "doUserEvent() node =", n.id, n.Name)
|
|
|
|
switch n.WidgetType {
|
|
|
|
case toolkit.Checkbox:
|
|
|
|
n.B = a.B
|
2023-04-06 20:25:14 -05:00
|
|
|
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.B)
|
|
|
|
n.doCustom()
|
2023-04-06 18:26:30 -05:00
|
|
|
case toolkit.Button:
|
2023-04-06 20:25:14 -05:00
|
|
|
log(logNow, "doUserEvent() node =", n.id, n.Name, "button clicked")
|
|
|
|
n.doCustom()
|
|
|
|
case toolkit.Combobox:
|
|
|
|
n.S = a.S
|
|
|
|
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.S)
|
|
|
|
n.doCustom()
|
|
|
|
case toolkit.Dropdown:
|
|
|
|
n.S = a.S
|
|
|
|
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.S)
|
|
|
|
n.doCustom()
|
|
|
|
case toolkit.Textbox:
|
|
|
|
n.S = a.S
|
|
|
|
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.S)
|
|
|
|
n.doCustom()
|
|
|
|
case toolkit.Spinner:
|
|
|
|
n.I = a.I
|
|
|
|
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.I)
|
|
|
|
n.doCustom()
|
|
|
|
case toolkit.Slider:
|
|
|
|
n.I = a.I
|
|
|
|
log(logNow, "doUserEvent() node =", n.id, n.Name, "set to:", n.I)
|
|
|
|
n.doCustom()
|
2023-04-06 19:48:24 -05:00
|
|
|
case toolkit.Window:
|
2023-04-06 20:25:14 -05:00
|
|
|
log(logNow, "doUserEvent() node =", n.id, n.Name, "window closed")
|
|
|
|
n.doCustom()
|
2023-04-06 18:26:30 -05:00
|
|
|
default:
|
|
|
|
log(logNow, "doUserEvent() type =", n.WidgetType)
|
2023-04-06 18:00:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-28 07:29:46 -05:00
|
|
|
func (n *Node) InitEmbed(resFS embed.FS) *Node {
|
|
|
|
Config.resFS = resFS
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Node) LoadToolkitEmbed(name string, b []byte) *Node {
|
|
|
|
for _, aplug := range allPlugins {
|
|
|
|
log(logInfo, "LoadToolkitEmbed() already loaded toolkit plugin =", aplug.name)
|
|
|
|
if (aplug.name == name) {
|
|
|
|
log(logError, "LoadToolkitEmbed() SKIPPING", name, "as you can't load it twice")
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.CreateTemp("", "sample." + name + ".so")
|
|
|
|
if (err != nil) {
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
defer os.Remove(f.Name())
|
|
|
|
f.Write(b)
|
|
|
|
|
|
|
|
p := initToolkit(name, f.Name())
|
|
|
|
if (p == nil) {
|
|
|
|
log(logError, "LoadToolkitEmbed() embedded go file failed", name)
|
|
|
|
}
|
|
|
|
return n
|
|
|
|
}
|
|
|
|
|
|
|
|
func (n *Node) ListToolkits() {
|
|
|
|
for _, aplug := range allPlugins {
|
|
|
|
log(logNow, "ListToolkits() already loaded toolkit plugin =", aplug.name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-12 13:00:29 -05:00
|
|
|
func (n *Node) LoadToolkit(name string) *Node {
|
2023-04-23 09:47:54 -05:00
|
|
|
log(logInfo, "LoadToolkit() START for name =", name)
|
|
|
|
plug := initPlugin(name)
|
|
|
|
if (plug == nil) {
|
2023-04-12 13:00:29 -05:00
|
|
|
return n
|
2023-04-08 15:34:36 -05:00
|
|
|
}
|
2023-04-23 09:47:54 -05:00
|
|
|
|
|
|
|
log(logInfo, "LoadToolkit() sending InitToolkit action to the plugin channel")
|
|
|
|
var a toolkit.Action
|
|
|
|
a.ActionType = toolkit.InitToolkit
|
|
|
|
plug.pluginChan <- a
|
|
|
|
sleep(.5) // temp hack until chan communication is setup
|
|
|
|
|
|
|
|
// TODO: find a new way to do this that is locking, safe and accurate
|
|
|
|
Config.rootNode.redraw(plug)
|
|
|
|
log(logInfo, "LoadToolkit() END for name =", name)
|
2023-04-12 13:00:29 -05:00
|
|
|
return n
|
2023-04-06 14:16:59 -05:00
|
|
|
}
|
|
|
|
|
2023-04-22 12:25:50 -05:00
|
|
|
func (n *Node) CloseToolkit(name string) bool {
|
|
|
|
log(logInfo, "CloseToolkit() for name =", name)
|
2023-04-23 09:47:54 -05:00
|
|
|
for _, plug := range allPlugins {
|
|
|
|
log(debugGui, "CloseToolkit() found", plug.name)
|
|
|
|
if (plug.name == name) {
|
2023-04-22 12:25:50 -05:00
|
|
|
log(debugNow, "CloseToolkit() sending close", name)
|
|
|
|
var a toolkit.Action
|
|
|
|
a.ActionType = toolkit.CloseToolkit
|
2023-04-23 09:47:54 -05:00
|
|
|
plug.pluginChan <- a
|
2023-04-22 12:25:50 -05:00
|
|
|
sleep(.5)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-04-08 15:34:36 -05:00
|
|
|
// There should only be one of these per application
|
|
|
|
// This is due to restrictions by being cross platform
|
|
|
|
// some toolkit's on some operating systems don't support more than one
|
|
|
|
// Keep things simple. Do the default expected thing whenever possible
|
|
|
|
func New() *Node {
|
2023-04-12 13:00:29 -05:00
|
|
|
return Config.rootNode
|
|
|
|
}
|
|
|
|
|
2023-04-23 11:30:54 -05:00
|
|
|
// try to load andlabs, if that doesn't work, fall back to the console
|
2023-04-12 13:00:29 -05:00
|
|
|
func (n *Node) Default() *Node {
|
2023-04-08 15:34:36 -05:00
|
|
|
// if DISPLAY isn't set, return since gtk can't load
|
|
|
|
// TODO: figure out how to check what to do in macos and mswindows
|
|
|
|
if (os.Getenv("DISPLAY") == "") {
|
2023-04-23 11:30:54 -05:00
|
|
|
if (n.LoadToolkit("gocui") == nil) {
|
|
|
|
log(logError, "New() failed to load gocui")
|
|
|
|
}
|
2023-04-12 13:00:29 -05:00
|
|
|
return n
|
2023-03-29 23:03:04 -05:00
|
|
|
}
|
2023-04-23 11:30:54 -05:00
|
|
|
if (n.LoadToolkit("andlabs") != nil) {
|
|
|
|
return n
|
2023-04-06 18:00:18 -05:00
|
|
|
}
|
2023-04-23 11:30:54 -05:00
|
|
|
n.LoadToolkit("gocui")
|
2023-04-12 13:00:29 -05:00
|
|
|
return n
|
2021-10-06 08:36:28 -05:00
|
|
|
}
|
|
|
|
|
2022-11-06 12:59:24 -06:00
|
|
|
// The window is destroyed but the application does not quit
|
2023-03-01 11:35:36 -06:00
|
|
|
func (n *Node) StandardClose() {
|
2023-02-25 14:05:25 -06:00
|
|
|
log(debugGui, "wit/gui Standard Window Close. name =", n.Name)
|
2023-03-01 11:35:36 -06:00
|
|
|
log(debugGui, "wit/gui Standard Window Close. n.Custom exit =", n.Custom)
|
2022-11-06 12:59:24 -06:00
|
|
|
}
|
|
|
|
|
2023-03-12 08:47:16 -05:00
|
|
|
// The window is destroyed and the application exits
|
|
|
|
// TODO: properly exit the plugin since Quit() doesn't do it
|
2023-03-01 11:35:36 -06:00
|
|
|
func StandardExit() {
|
|
|
|
log("wit/gui Standard Window Exit. running os.Exit()")
|
2023-04-07 21:22:51 -05:00
|
|
|
log("StandardExit() attempt to exit each toolkit plugin")
|
2023-04-23 09:47:54 -05:00
|
|
|
for i, plug := range allPlugins {
|
|
|
|
log("NewButton()", i, plug)
|
2022-11-13 08:53:03 -06:00
|
|
|
}
|
2023-03-01 11:35:36 -06:00
|
|
|
exit(0)
|
2022-11-06 12:59:24 -06:00
|
|
|
}
|