summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Carr <[email protected]>2022-10-08 23:22:47 -0500
committerJeff Carr <[email protected]>2022-10-08 23:22:47 -0500
commit086986b6b8f55d15d18595bcbf3f76c023365b61 (patch)
tree9f1dc4da5b2d125f7a017418d4d9912e16b71039
parentf92a50e2e665a18e2201f1d6714025dcc39301cc (diff)
parent45644ef9bc333f5def62d1c7f474dc96274e63fa (diff)
Merge branch 'master' into jcarr
-rw-r--r--.gitignore2
-rw-r--r--Makefile20
-rw-r--r--area.go58
-rw-r--r--box.go139
-rw-r--r--button.go108
-rw-r--r--cmds/gui-demo/Makefile5
-rw-r--r--cmds/gui-demo/demo-window.go33
-rw-r--r--cmds/gui-demo/main.go68
-rw-r--r--cmds/gui-example/Makefile5
-rw-r--r--cmds/gui-example/demo-window.go105
-rw-r--r--cmds/gui-example/main.go68
-rw-r--r--cmds/gui-example/os.go99
-rw-r--r--color.go31
-rw-r--r--debug-window.go193
-rw-r--r--debug.go113
-rw-r--r--demo-window-andlabs-ui.go84
-rw-r--r--demo-window.go92
-rw-r--r--doc.go69
-rw-r--r--entry.go112
-rw-r--r--example_test.go45
-rw-r--r--find.go97
-rw-r--r--go.mod10
-rw-r--r--go.sum10
-rw-r--r--gui.go31
-rw-r--r--main.go34
-rw-r--r--new-structs.go318
-rw-r--r--structs.go230
-rw-r--r--table.go30
-rw-r--r--tableCallbacks.go24
-rw-r--r--window.go206
30 files changed, 1815 insertions, 624 deletions
diff --git a/.gitignore b/.gitignore
index 1377554..1445c95 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
*.swp
+cmds/gui-example/gui-example
+cmds/gui-demo/gui-demo
diff --git a/Makefile b/Makefile
index 2b7dd82..7225bce 100644
--- a/Makefile
+++ b/Makefile
@@ -1,19 +1,13 @@
-# simple sortcut to push all git changes
-push:
- git checkout devel
- git pull
- git add --all
- -git commit -a -s
- git push
+all:
+ @echo
+ @echo "make examples # will run all the examples"
+ @echo "make update # full git update"
+ @echo
# should update every go dependancy (?)
update:
git pull
go get -v -t -u ./...
-merge-devel:
- git checkout master
- git pull origin master
- git merge devel
- git push origin master
- git checkout devel
+examples:
+ make -C gui-example
diff --git a/area.go b/area.go
index 3c41f62..c4e0cae 100644
--- a/area.go
+++ b/area.go
@@ -7,28 +7,14 @@ import _ "github.com/andlabs/ui/winmanifest"
import "github.com/davecgh/go-spew/spew"
-func makeGenericArea(gb *GuiBox, newText *ui.AttributedString, custom func(*GuiButton)) {
- // make this button just to get the default font (but don't display the button)
- // There should be another way to do this (?)
- var newB *GuiButton
- newB = CreateFontButton(gb, "AREA")
- newB.Box = gb
- newB.Custom = custom
-
- gw := gb.Window
- // initialize the GuiArea{}
- gw.Area = new(GuiArea)
- gw.Area.Button = newB
- gw.Area.Box = gb
- gw.Area.UiAttrstr = newText
- gw.Area.UiArea = ui.NewArea(gw.Area)
-
- if (Config.Debug) {
- spew.Dump(gw.Area.UiArea)
- log.Println("DEBUGGING", Config.Debug)
- } else {
- log.Println("NOT DEBUGGING AREA mhAH.Button =", gw.Area.Button)
- }
+// make this button just to get the default font (but don't display the button)
+// There should be another way to do this (?)
+func (n *Node) makeGenericArea(newText *ui.AttributedString, custom func(*Node)) {
+ newNode := n.CreateFontButton("AREA")
+ newNode.custom = custom
+ area := new(GuiArea)
+ newNode.uiArea = ui.NewArea(area)
+ newNode.uiAttrstr = newText
}
func AreaAppendText(newText *ui.AttributedString, what string, attrs ...ui.Attribute) {
@@ -52,7 +38,7 @@ func appendWithAttributes(newText *ui.AttributedString, what string, attrs ...ui
func (ah GuiArea) Draw(a *ui.Area, p *ui.AreaDrawParams) {
tl := ui.DrawNewTextLayout(&ui.DrawTextLayoutParams{
String: ah.UiAttrstr,
- DefaultFont: ah.Button.FB.Font(),
+ DefaultFont: ah.N.uiFontButton.Font(),
Width: p.AreaWidth,
Align: ui.DrawTextAlign(1),
})
@@ -61,6 +47,7 @@ func (ah GuiArea) Draw(a *ui.Area, p *ui.AreaDrawParams) {
}
func (ah GuiArea) MouseEvent(a *ui.Area, me *ui.AreaMouseEvent) {
+ /*
if (Config.Debug) {
log.Println("GOT MouseEvent() ah.Button =", ah.Button)
spew.Dump(me)
@@ -80,6 +67,7 @@ func (ah GuiArea) MouseEvent(a *ui.Area, me *ui.AreaMouseEvent) {
Data.MouseClick(ah.Button)
}
}
+ */
}
func (ah GuiArea) MouseCrossed(a *ui.Area, left bool) {
@@ -107,28 +95,14 @@ func (ah GuiArea) KeyEvent(a *ui.Area, ke *ui.AreaKeyEvent) (handled bool) {
return false
}
-func ShowTextBox(gw *GuiWindow, newText *ui.AttributedString, custom func(*GuiButton), name string) *GuiBox {
+func (n *Node) ShowTextBox(newText *ui.AttributedString, custom func(*Node), name string) {
log.Println("ShowTextBox() START")
- if (gw == nil) {
- log.Println("ShowTextBox() ERROR gw = nil")
- return nil
- }
- log.Println("ShowTextBox() START gw =", gw)
-
- var newbox *GuiBox
- newbox = new(GuiBox)
- newbox.Window = gw
- newbox.Name = name
- hbox := ui.NewVerticalBox()
- newbox.UiBox = hbox
// TODO: allow padded & axis here
- hbox.SetPadded(true)
-
- add(gw.BoxMap["MAINBOX"], newbox)
+ n.uiBox.SetPadded(true)
- makeGenericArea(newbox, newText, custom)
- newbox.UiBox.Append(newbox.Window.Area.UiArea, true)
+ // add(gw.BoxMap["MAINBOX"], newbox)
- return newbox
+ n.makeGenericArea(newText, custom)
+ n.uiBox.Append(n.area.UiArea, true)
}
diff --git a/box.go b/box.go
index f19e22a..67c0c05 100644
--- a/box.go
+++ b/box.go
@@ -1,6 +1,7 @@
package gui
import "log"
+// import "os"
// import "reflect"
import "github.com/andlabs/ui"
@@ -8,123 +9,45 @@ import _ "github.com/andlabs/ui/winmanifest"
// import "github.com/davecgh/go-spew/spew"
-// add(nil, newbox, "") // use this when the Window is created. Always called 'MAINBOX'
-// add(gw.BoxMap["MAINBOX"], newbox, name) // use this to add a box off the main box
-// add(gw.BoxMap["BUTTONBOX"], newbox, name) // use this to add something to the box called 'BUTTONBOX'
-// add(box, newbox, name) // add 'newbox' to 'box' and call it 'name'
-func add(box *GuiBox, newbox *GuiBox) {
- log.Println("gui.add() START box =", box)
- log.Println("gui.add() START newbox =", newbox)
+func (n *Node) AddComboBox(title string, s ...string) *Node {
+ newNode := n.AddNode(title)
+ box := n.uiBox
if (box == nil) {
- log.Println("\tgui.add() add to Window as MAINBOX")
- if (newbox.Window.UiTab != nil) {
- // create a new tab here
- // add the box to it as MAINBOX
- log.Println("\tgui.add() add to Window as a UiTab")
- // TODO: allow passing where to append
- // newbox.Window.UiTab.InsertAt(newbox.Name, 0, newbox.UiBox)
- newbox.Window.UiTab.Append(newbox.Name, newbox.UiBox)
- newbox.Window.UiTab.SetMargined(0, true)
-
- // TODO: figure out how to make a new Tab/Window/Box here
- // window := InitGuiWindow(Data.Config, newbox.Name, gw.MakeWindow, gw.UiWindow, gw.UiTab)
- // window.UiTab.Delete(0)
- // window.MakeWindow(window)
- // newbox.Window = window
-
- newbox.Window.BoxMap["MAINBOX"] = newbox
- log.Println("gui.add() END")
- return
- } else {
- log.Println("\tgui.add() ERROR DONT KNOW HOW TO ADD TO A RAW WINDOW YET")
- // add this to the window
- }
- log.Println("\tgui.add() ERROR DON'T KNOW HOW TO add to Window as MAINBOX DONE")
- log.Println("gui.add() END")
- return
- }
- log.Println("\tgui.add() adding", newbox.Name, "to", box.Name)
- // copy the box settings over
- newbox.Window = box.Window
- if (box.UiBox == nil) {
- log.Println("\tgui.add() ERROR box.UiBox == nil")
- panic("crap")
+ return n
}
- if (newbox.UiBox == nil) {
- log.Println("\tgui.add() ERROR newbox.UiBox == nil")
- panic("crap")
- }
- // log.Println("\tgui.add() newbox.UiBox == ", newbox.UiBox.GetParent())
- // spew.Dump(newbox.UiBox)
- box.UiBox.Append(newbox.UiBox, false)
-
- // add the newbox to the Window.BoxMap[]
- box.Window.BoxMap[newbox.Name] = newbox
- log.Println("gui.add() END")
-}
-func NewBox(box *GuiBox, axis int, name string) *GuiBox {
- log.Println("VerticalBox START")
- var newbox *GuiBox
- newbox = new(GuiBox)
- newbox.Window = box.Window
- newbox.Name = name
+ ecbox := ui.NewEditableCombobox()
+ newNode.uiText = ecbox
+ // newNode.Dump()
+ // log.Println("ecbox", ecbox)
- var uiBox *ui.Box
- if (axis == Xaxis) {
- uiBox = ui.NewHorizontalBox()
- } else {
- uiBox = ui.NewVerticalBox()
+ for id, name := range s {
+ log.Println("Adding Combobox Entry:", id, name)
+ ecbox.Append(name)
}
- uiBox.SetPadded(true)
- newbox.UiBox = uiBox
- add(box, newbox)
- return newbox
-}
-
-func HardBox(gw *GuiWindow, axis int, name string) *GuiBox {
- log.Println("HardBox() START axis =", axis)
- // add a Vertical Seperator if there is already a box
- // Is this right?
- box := gw.BoxMap["MAINBOX"]
- if (box != nil) {
- if (axis == Xaxis) {
- VerticalBreak(box)
+ ecbox.OnChanged(func(*ui.EditableCombobox) {
+ test := ecbox.Text()
+ log.Println("node.Name = '" + newNode.Name + "' text for '" + title + "' is now: '" + test + "'")
+ if (newNode.OnChanged == nil) {
+ log.Println("Not doing custom OnChanged since OnChanged == nil")
+ newNode.Dump()
} else {
- HorizontalBreak(box)
+ newNode.OnChanged()
}
- }
-
- // make the new vbox
- var uiBox *ui.Box
- if (axis == Xaxis) {
- uiBox = ui.NewHorizontalBox()
- } else {
- uiBox = ui.NewVerticalBox()
- }
- uiBox.SetPadded(true)
-
- // Init a new GuiBox
- newbox := new(GuiBox)
- newbox.Window = gw
- newbox.UiBox = uiBox
- newbox.Name = name
-
- add(gw.BoxMap["MAINBOX"], newbox)
+ })
- log.Println("HardBoxk END")
- return newbox
+ box.Append(ecbox, Config.Stretchy)
+ return newNode
}
-func HorizontalBreak(box *GuiBox) {
- log.Println("VerticalSeparator added to box =", box.Name)
- tmp := ui.NewHorizontalSeparator()
- box.UiBox.Append(tmp, false)
-}
-
-func VerticalBreak(box *GuiBox) {
- log.Println("VerticalSeparator added to box =", box.Name)
- tmp := ui.NewVerticalSeparator()
- box.UiBox.Append(tmp, false)
+func (n *Node) GetText() string {
+ if (n.uiMultilineEntry != nil) {
+ return n.uiMultilineEntry.Text()
+ }
+ if (n.uiText == nil) {
+ return ""
+ }
+ ecbox := n.uiText
+ return ecbox.Text()
}
diff --git a/button.go b/button.go
index bf36f23..755d9ab 100644
--- a/button.go
+++ b/button.go
@@ -1,10 +1,15 @@
package gui
import "log"
+import "reflect"
+// import "image/color"
import "github.com/andlabs/ui"
import _ "github.com/andlabs/ui/winmanifest"
// import "github.com/davecgh/go-spew/spew"
+
+// TODO: bring this generic mouse click function back
+//
// This is the default mouse click handler
// Every mouse click that hasn't been assigned to
// something specific will fall into this routine
@@ -14,77 +19,64 @@ import _ "github.com/andlabs/ui/winmanifest"
// This routine MUST be here as this is how the andlabs/ui works
// This is the raw routine passed to every button in andlabs libui / ui
//
-// There is a []GuiButton which has all the buttons. We search
-// for the button and then call the function below
//
-func defaultButtonClick(button *ui.Button) {
- log.Println("gui.defaultButtonClick() LOOK FOR BUTTON button =", button)
- for key, foo := range Data.AllButtons {
- if (Config.Debug) {
- log.Println("gui.defaultButtonClick() Data.AllButtons =", key, foo)
- // spew.Dump(foo)
- }
- if Data.AllButtons[key].B == button {
- log.Println("\tgui.defaultButtonClick() BUTTON MATCHED")
- guiButtonClick(Data.AllButtons[key])
- return
- }
+
+func (n *Node) AddButton(name string, custom func(*Node)) *Node {
+ if (n.uiBox == nil) {
+ log.Println("gui.Node.AppendButton() filed node.UiBox == nil")
+ return n
}
- log.Println("\tgui.defaultButtonClick() ERROR: BUTTON NOT FOUND")
+ button := ui.NewButton(name)
if (Config.Debug) {
- panic("gui.defaultButtonClick() ERROR: UNMAPPED ui.Button")
+ log.Println("reflect.TypeOF(uiBox) =", reflect.TypeOf(n.uiBox))
+ log.Println("reflect.TypeOF(uiButton) =", reflect.TypeOf(button))
}
-}
+ // true == expand, false == make normal size button
+ n.uiBox.Append(button, Config.Stretchy)
+ n.uiButton = button
-func guiButtonClick(button *GuiButton) {
- log.Println("\tgui.guiButtonClick() button.Name =", button.Name)
- if button.Custom != nil {
- log.Println("\tgui.guiButtonClick() DOING CUSTOM FUNCTION")
- button.Custom(button)
- return
- }
- if (Data.MouseClick != nil) {
- Data.MouseClick(button)
- } else {
- log.Println("\tgui.guiButtonClick() IGNORING BUTTON. MouseClick() is nil")
- }
-}
+ newNode := n.makeNode(name, 888, 888 + Config.counter)
+ newNode.uiButton = button
+ newNode.custom = custom
-func CreateButton(box *GuiBox, custom func(*GuiButton), name string, values interface {}) *GuiButton {
- newUiB := ui.NewButton(name)
- newUiB.OnClicked(defaultButtonClick)
+ button.OnClicked(func(*ui.Button) {
+ log.Println("gui.AppendButton() Button Clicked. Running custom()")
+ custom(newNode)
+ })
+ return newNode
+}
- var newB *GuiButton
- newB = new(GuiButton)
- newB.B = newUiB
- if (box.Window == nil) {
- log.Println("CreateButton() box.Window == nil")
- // ErrorWindow(box.Window, "Login Failed", msg) // can't even do this
- panic("maybe print an error and return nil? or make a fake button?")
- }
- newB.Box = box
- newB.Custom = custom
- newB.Values = values
+func (n *Node) CreateFontButton(action string) *Node {
+ n.uiFontButton = ui.NewFontButton()
- Data.AllButtons = append(Data.AllButtons, newB)
+ n.uiFontButton.OnChanged(func (*ui.FontButton) {
+ log.Println("FontButton.OnChanged() START")
+ n.Dump()
+ })
+ n.uiBox.Append(n.uiFontButton, Config.Stretchy)
- box.UiBox.Append(newB.B, false)
- return newB
+ // TODO: implement Grid
+ n.uiGrid = ui.NewGrid()
+ return n
}
-func CreateFontButton(box *GuiBox, action string) *GuiButton {
+func (n *Node) CreateColorButton(custom func(*Node), name string, values interface {}) *Node {
// create a 'fake' button entry for the mouse clicks
- var newGB GuiButton
- newGB.Name = "FONT"
- newGB.FB = ui.NewFontButton()
- newGB.Box = box
- Data.AllButtons = append(Data.AllButtons, &newGB)
+ n.uiColorButton = ui.NewColorButton()
+ n.custom = custom
+ n.values = values
- newGB.FB.OnChanged(func (*ui.FontButton) {
- log.Println("FontButton.OnChanged() START mouseClick(&newBM)", newGB)
- if (Data.MouseClick != nil) {
- Data.MouseClick(&newGB)
+ n.uiColorButton.OnChanged(func (*ui.ColorButton) {
+ log.Println("ColorButton.OnChanged() START Color Button Click")
+ rgba := n.Color
+ r, g, b, a := rgba.R, rgba.G, rgba.B, rgba.A
+ log.Println("ColorButton.OnChanged() Color() =", r, g, b, a)
+ if (n.custom != nil) {
+ n.custom(n)
+ } else if (Data.MouseClick != nil) {
+ Data.MouseClick(n)
}
})
- return &newGB
+ n.uiBox.Append(n.uiColorButton, Config.Stretchy)
+ return n
}
diff --git a/cmds/gui-demo/Makefile b/cmds/gui-demo/Makefile
new file mode 100644
index 0000000..41fdd10
--- /dev/null
+++ b/cmds/gui-demo/Makefile
@@ -0,0 +1,5 @@
+run: build
+ ./gui-demo
+
+build:
+ go build
diff --git a/cmds/gui-demo/demo-window.go b/cmds/gui-demo/demo-window.go
new file mode 100644
index 0000000..fd8e8f4
--- /dev/null
+++ b/cmds/gui-demo/demo-window.go
@@ -0,0 +1,33 @@
+package main
+
+import "log"
+
+import "git.wit.org/wit/gui"
+
+func demoClick (n *gui.Node) {
+ log.Println("demoClick() Dumping node:")
+ n.Dump()
+}
+
+func addDemoTab(n *gui.Node, title string) {
+ newNode := n.AddTab(title, nil)
+ if (gui.Config.Debug) {
+ newNode.Dump()
+ }
+ newNode.ListChildren(false)
+
+ groupNode1 := newNode.AddGroup("group 1")
+ groupNode1.AddComboBox("demoCombo1", "foo", "bar", "stuff")
+ groupNode1.AddComboBox("demoCombo3", "foo 3", "bar", "stuff")
+
+ groupNode1.Dump()
+
+ butNode1 := groupNode1.AddButton("button1", demoClick)
+ butNode1.Dump()
+
+ butNode2 := groupNode1.AddButton("button2", demoClick)
+ butNode2.Dump()
+
+ groupNode2 := newNode.AddGroup("group 2")
+ groupNode2.AddComboBox("demoCombo2", "more 1", "more 2", "more 3")
+}
diff --git a/cmds/gui-demo/main.go b/cmds/gui-demo/main.go
new file mode 100644
index 0000000..8eae878
--- /dev/null
+++ b/cmds/gui-demo/main.go
@@ -0,0 +1,68 @@
+package main
+
+import (
+ "log"
+ "os"
+ "time"
+
+ "git.wit.org/wit/gui"
+)
+
+// This initializes the first window
+//
+// Then starts a goroutine to demonstrate how to
+// inject things into the GUI
+func main() {
+ log.Println("Starting my Control Panel")
+
+ go gui.Main(initGUI)
+
+ watchGUI()
+}
+
+// This initializes the first window
+func initGUI() {
+ gui.Config.Title = "WIT GUI Window Demo 1"
+ gui.Config.Width = 640
+ gui.Config.Height = 480
+ gui.Config.Exit = myExit
+ node1 := gui.NewWindow()
+ addDemoTab(node1, "A Simple Tab Demo")
+
+ gui.Config.Title = "WIT GUI Window Demo 2"
+ gui.Config.Width = 640
+ gui.Config.Height = 240
+ gui.Config.Exit = myExit
+ node2 := gui.NewWindow()
+ node2.DemoAndlabsUiTab("A Simple andlabs/ui Tab Demo")
+}
+
+// This demonstrates how to properly interact with the GUI
+// You can not involke the GUI from external goroutines in most cases.
+func watchGUI() {
+ var i = 1
+ for {
+ log.Println("Waiting", i, "seconds")
+ i += 1
+ time.Sleep(1 * time.Second)
+ if i == 4 {
+ log.Println("Opening a Debug Window via the gui.Queue()")
+ gui.Config.Width = 800
+ gui.Config.Height = 300
+ gui.Config.Exit = myDebugExit
+ gui.Queue(gui.DebugWindow)
+ }
+ }
+}
+
+func myExit(n *gui.Node) {
+ log.Println()
+ log.Println("Entered myExit() on node.Name =", n.Name)
+ log.Println()
+ os.Exit(0)
+}
+
+func myDebugExit(n *gui.Node) {
+ log.Println("Entered myDebugExit() on node.Name =", n.Name)
+ log.Println("Don't actually os.Exit()")
+}
diff --git a/cmds/gui-example/Makefile b/cmds/gui-example/Makefile
new file mode 100644
index 0000000..8718fd1
--- /dev/null
+++ b/cmds/gui-example/Makefile
@@ -0,0 +1,5 @@
+run: build
+ ./gui-example
+
+build:
+ go build
diff --git a/cmds/gui-example/demo-window.go b/cmds/gui-example/demo-window.go
new file mode 100644
index 0000000..f01c1a9
--- /dev/null
+++ b/cmds/gui-example/demo-window.go
@@ -0,0 +1,105 @@
+package main
+
+import "log"
+import "reflect"
+
+import "git.wit.org/wit/gui"
+
+import "github.com/davecgh/go-spew/spew"
+
+func demoClick (n *gui.Node) {
+ log.Println("demoClick() Dumping node:")
+ n.Dump()
+}
+
+var username = "jcarr"
+var hostname = "fire"
+
+func newClick (n *gui.Node) {
+ var tmp []string
+ junk := "ssh -v " + username + "@" + hostname
+ log.Println("junk = " , junk)
+ xterm(junk)
+ log.Println("tmp = " , reflect.ValueOf(tmp).Kind())
+ // spew.Dump(tmp)
+}
+
+func addDemoTab(n *gui.Node, title string) {
+ newNode := n.AddTab(title, nil)
+ if (gui.Config.Debug) {
+ newNode.Dump()
+ }
+ newNode.ListChildren(false)
+
+ groupNode1 := newNode.AddGroup("group 1")
+ cbNode := groupNode1.AddComboBox("username", "root", "jcarr", "hugo")
+ cbNode.OnChanged = func () {
+ username = cbNode.GetText()
+ }
+ groupNode1.AddComboBox("demoCombo3", "foo 3", "bar", "stuff")
+
+ groupNode1.Dump()
+
+ butNode1 := groupNode1.AddButton("button1", demoClick)
+ butNode1.Dump()
+
+ butNode2 := groupNode1.AddButton("button2", newClick)
+ butNode2.Dump()
+
+ groupNode2 := newNode.AddGroup("group 2")
+ groupNode2.AddComboBox("demoCombo2", "more 1", "more 2", "more 3")
+
+ gNode := newNode.AddGroup("domU")
+ makeSSHbutton(gNode, "hugo@www", "www.wit.org")
+ makeSSHbutton(gNode, "check.lab", "check.lab.wit.org")
+ makeSSHbutton(gNode, "gobuild.lab", "gobuild.lab.wit.org")
+ makeSSHbutton(gNode, "gobuild2.lab", "gobuild2.lab.wit.org")
+
+/////////////////////////////// Column DNS ////////////////////////////////
+ gNode = newNode.AddGroup("dns")
+ makeSSHbutton(gNode, "bind.wit.org", "bind.wit.org")
+ makeSSHbutton(gNode, "ns1.wit.com", "ns1.wit.com")
+ makeSSHbutton(gNode, "ns2.wit.com", "ns2.wit.com")
+ makeSSHbutton(gNode, "coredns", "coredns.lab.wit.org")
+
+/////////////////////////////// PHYS 530 //////////////////////////////////
+ gNode = newNode.AddGroup("phys 530")
+ // makeXtermButton(gNode, "openwrt", "SUBDOMAIN", "ssh -4 -v root@openwrt")
+ gNode.AddButton("openwrt", func (*gui.Node) {
+ stuff := "ssh -4 -v root@openwrt"
+ xterm(stuff)
+ })
+ makeSSHbutton (gNode, "mirrors", "mirrors.wit.org")
+ makeSSHbutton (gNode, "node004", "node004.lab.wit.org")
+ makeSSHbutton (gNode, "lenovo-z70", "lenovo-z70.lab.wit.org")
+
+/////////////////////////////// PHYS 522 //////////////////////////////////
+ gNode = newNode.AddGroup("phys 522")
+ // makeXtermButton(gNode, "openwrt2", "SUBDOMAIN", "ssh -4 -v root@openwrt2")
+ gNode.AddButton("openwrt2", func (*gui.Node) {
+ stuff := "ssh -4 -v root@openwrt2"
+ xterm(stuff)
+ })
+ makeSSHbutton (gNode, "fire.lab", "fire.lab.wit.org")
+ makeSSHbutton (gNode, "predator", "predator.lab.wit.org")
+
+/////////////////////////////// FLOAT /////////////////////////////////////
+ gNode = newNode.AddGroup("float")
+ makeSSHbutton(gNode, "root@asus-n501vw", "asus-n501vw.lab.wit.org")
+}
+
+func makeSSHbutton (n *gui.Node, name string, hostname string) {
+ bNode := n.AddButton(name, func (*gui.Node) {
+ var tmp []string
+ if (username == "") {
+ username = "root"
+ }
+ junk := "ssh -v " + username + "@" + hostname
+ log.Println("junk = " , junk)
+ log.Println("username = '" + username + "'")
+ xterm(junk)
+ log.Println("tmp = " , reflect.ValueOf(tmp).Kind())
+ spew.Dump(tmp)
+ })
+ bNode.Dump()
+}
diff --git a/cmds/gui-example/main.go b/cmds/gui-example/main.go
new file mode 100644
index 0000000..8eae878
--- /dev/null
+++ b/cmds/gui-example/main.go
@@ -0,0 +1,68 @@
+package main
+
+import (
+ "log"
+ "os"
+ "time"
+
+ "git.wit.org/wit/gui"
+)
+
+// This initializes the first window
+//
+// Then starts a goroutine to demonstrate how to
+// inject things into the GUI
+func main() {
+ log.Println("Starting my Control Panel")
+
+ go gui.Main(initGUI)
+
+ watchGUI()
+}
+
+// This initializes the first window
+func initGUI() {
+ gui.Config.Title = "WIT GUI Window Demo 1"
+ gui.Config.Width = 640
+ gui.Config.Height = 480
+ gui.Config.Exit = myExit
+ node1 := gui.NewWindow()
+ addDemoTab(node1, "A Simple Tab Demo")
+
+ gui.Config.Title = "WIT GUI Window Demo 2"
+ gui.Config.Width = 640
+ gui.Config.Height = 240
+ gui.Config.Exit = myExit
+ node2 := gui.NewWindow()
+ node2.DemoAndlabsUiTab("A Simple andlabs/ui Tab Demo")
+}
+
+// This demonstrates how to properly interact with the GUI
+// You can not involke the GUI from external goroutines in most cases.
+func watchGUI() {
+ var i = 1
+ for {
+ log.Println("Waiting", i, "seconds")
+ i += 1
+ time.Sleep(1 * time.Second)
+ if i == 4 {
+ log.Println("Opening a Debug Window via the gui.Queue()")
+ gui.Config.Width = 800
+ gui.Config.Height = 300
+ gui.Config.Exit = myDebugExit
+ gui.Queue(gui.DebugWindow)
+ }
+ }
+}
+
+func myExit(n *gui.Node) {
+ log.Println()
+ log.Println("Entered myExit() on node.Name =", n.Name)
+ log.Println()
+ os.Exit(0)
+}
+
+func myDebugExit(n *gui.Node) {
+ log.Println("Entered myDebugExit() on node.Name =", n.Name)
+ log.Println("Don't actually os.Exit()")
+}
diff --git a/cmds/gui-example/os.go b/cmds/gui-example/os.go
new file mode 100644
index 0000000..ce5db8d
--- /dev/null
+++ b/cmds/gui-example/os.go
@@ -0,0 +1,99 @@
+package main
+
+import "log"
+import "strings"
+import "os"
+import "os/exec"
+import "io/ioutil"
+import "errors"
+// import "bufio"
+
+// import "github.com/davecgh/go-spew/spew"
+
+/*
+import "time"
+import "runtime"
+import "runtime/debug"
+import "runtime/pprof"
+
+import "git.wit.org/wit/gui"
+import "git.wit.org/wit/shell"
+import "github.com/gobuffalo/packr"
+*/
+
+func runSimpleCommand(s string) {
+ cmd := strings.TrimSpace(s) // this is like 'chomp' in perl
+ cmd = strings.TrimSuffix(cmd, "\n") // this is like 'chomp' in perl
+ cmdArgs := strings.Fields(cmd)
+ runLinuxCommand(cmdArgs)
+}
+
+var geom string = "120x30+500+500"
+
+func xterm(cmd string) {
+ var tmp []string
+ var argsXterm = []string{"nohup", "xterm", "-geometry", geom}
+ tmp = append(argsXterm, "-hold", "-e", cmd)
+ log.Println("xterm cmd=", cmd)
+ go runCommand(tmp)
+}
+
+func runCommand(cmdArgs []string) {
+ log.Println("runCommand() START", cmdArgs)
+ process := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
+ // process := exec.Command("xterm", "-e", "ping localhost")
+ log.Println("runCommand() process.Start()")
+ process.Start()
+ log.Println("runCommand() process.Wait()")
+ err := process.Wait()
+ lookupError(err)
+ log.Println("runCommand() NEED TO CHECK THE TIME HERE TO SEE IF THIS WORKED")
+ log.Println("runCommand() OTHERWISE INFORM THE USER")
+ log.Println("runCommand() END", cmdArgs)
+}
+
+func lookupError(err error) {
+ var (
+ ee *exec.ExitError
+ pe *os.PathError
+ )
+
+ if errors.As(err, &ee) {
+ log.Println("ran, but non-zero exit code =", ee.ExitCode()) // ran, but non-zero exit code
+ } else if errors.As(err, &pe) {
+ log.Printf("os.PathError = %v", pe) // "no such file ...", "permission denied" etc.
+ } else if err != nil {
+ log.Printf("something really bad happened general err = %v", err) // something really bad happened!
+ if exitError, ok := err.(*exec.ExitError); ok {
+ log.Printf("exitError.ExitCode() is %d\n", exitError.ExitCode())
+ }
+ } else {
+ log.Println("success! // ran without error (exit code zero)")
+ }
+}
+
+func runLinuxCommand(cmdArgs []string) (string, error) {
+ process := exec.Command(cmdArgs[0], cmdArgs[1:len(cmdArgs)]...)
+
+ process.Stdin = os.Stdin
+ process.Stderr = os.Stderr
+
+ stdOut, err := process.StdoutPipe()
+ if err != nil {
+ return "", err
+ }
+
+ if err := process.Start(); err != nil {
+ return "", err
+ }
+
+ bytes, err := ioutil.ReadAll(stdOut)
+ if err != nil {
+ return "", err
+ }
+ err = process.Wait()
+ lookupError(err)
+
+ log.Println(string(bytes))
+ return string(bytes), err
+}
diff --git a/color.go b/color.go
new file mode 100644
index 0000000..cf4a362
--- /dev/null
+++ b/color.go
@@ -0,0 +1,31 @@
+package gui
+
+//
+// convert between 'standard' golang Color and andlabs/ui Color
+//
+
+// import "log"
+// import "fmt"
+import "image/color"
+
+import "github.com/andlabs/ui"
+import _ "github.com/andlabs/ui/winmanifest"
+
+func libuiColorToGOlangColor(rgba color.RGBA) ui.TableColor {
+ /* a hack to see if colors work differently on macos or windows
+ if (rgba.R == 72) {
+ log.Println("SETTING COLOR TO NIL")
+ log.Println("SETTING COLOR TO NIL")
+ log.Println("SETTING COLOR TO NIL")
+ return ui.TableColor{}
+ }
+ */
+ return ui.TableColor{float64(rgba.R) / 256, float64(rgba.G) / 256, float64(rgba.B) / 256, float64(rgba.A) / 256}
+}
+
+/*
+func golangColorGOlibuiColorTo (ui.TableColor) (rgba color.RGBA) {
+ color.RGBA{float64(, 100, 200, 100}
+ return ui.TableColor{float64(rgba.R) / 256, float64(rgba.G) / 256, float64(rgba.B) / 256, float64(rgba.A) / 256}
+}
+*/
diff --git a/debug-window.go b/debug-window.go
new file mode 100644
index 0000000..72272ec
--- /dev/null
+++ b/debug-window.go
@@ -0,0 +1,193 @@
+package gui
+
+import (
+ "log"
+ // "fmt"
+ "strconv"
+
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+// "github.com/davecgh/go-spew/spew"
+)
+
+var names = make([]string, 100)
+var nodeNames = make([]string, 100)
+
+func DebugWindow() {
+ Config.Title = "DebugWindow()"
+ node := NewWindow()
+ node.DebugTab("WIT GUI Debug Tab")
+}
+
+// TODO: remove this crap
+// What does this actually do?
+// It populates the nodeNames in a map. No, not a map, an array.
+// What is the difference again? (other than one being in order and a predefined length)
+func addNodeName(c *ui.Combobox, s string, id string) {
+ c.Append(s)
+ nodeNames[y] = id
+ y = y + 1
+}
+
+func makeWindowDebug() *ui.Box {
+ hbox := ui.NewHorizontalBox()
+ hbox.SetPadded(true)
+
+ /////////////////////////////////////////////////////
+ nodeBox := addGroup(hbox, "Windows:")
+ nodeCombo := ui.NewCombobox()
+
+ for name, node := range Data.NodeMap {
+ if (Config.Debug) {
+ log.Println("range Data.NodeMap() name =", name)
+ }
+ tmp := node.id + " (" + name + ")"
+ addNodeName(nodeCombo, tmp, node.id)
+ }
+ nodeCombo.SetSelected(0)
+
+ nodeBox.Append(nodeCombo, Config.Stretchy)
+
+ nodeCombo.OnSelected(func(*ui.Combobox) {
+ y := nodeCombo.Selected()
+ log.Println("y =", y)
+ log.Println("nodeNames[y] =", nodeNames[y])
+ node := Data.findId(nodeNames[y])
+ if (node != nil) {
+ node.Dump()
+ }
+ })
+
+ /////////////////////////////////////////////////////
+ vbox := addGroup(hbox, "Node Debug")
+
+ n1 := addButton(vbox, "Data.DumpNodeMap()")
+ n1.OnClicked(func(*ui.Button) {
+ Data.DumpNodeMap()
+ })
+
+ n1 = addButton(vbox, "Data.ListChildren(false)")
+ n1.OnClicked(func(*ui.Button) {
+ Data.ListChildren(false)
+ })
+
+ n1 = addButton(vbox, "Data.ListChildren(true)")
+ n1.OnClicked(func(*ui.Button) {
+ Data.ListChildren(true)
+ })
+
+ n1 = addButton(vbox, "Node.Dump()")
+ n1.OnClicked(func(*ui.Button) {
+ y := nodeCombo.Selected()
+ log.Println("y =", y)
+ log.Println("nodeNames[y] =", nodeNames[y])
+ node := Data.findId(nodeNames[y])
+ if (node != nil) {
+ node.Dump()
+ }
+ })
+
+ n1 = addButton(vbox, "Node.ListChildren(false)")
+ n1.OnClicked(func(*ui.Button) {
+ y := nodeCombo.Selected()
+ log.Println("y =", y)
+ log.Println("nodeNames[y] =", nodeNames[y])
+ node := Data.findId(nodeNames[y])
+ if (node != nil) {
+ node.ListChildren(false)
+ }
+ })
+
+ n1 = addButton(vbox, "Node.ListChildren(true)")
+ n1.OnClicked(func(*ui.Button) {
+ y := nodeCombo.Selected()
+ log.Println("y =", y)
+ log.Println("nodeNames[y] =", nodeNames[y])
+ node := Data.findId(nodeNames[y])
+ if (node != nil) {
+ node.ListChildren(true)
+ }
+ })
+
+ n1 = addButton(vbox, "Node.AddDebugTab")
+ n1.OnClicked(func(*ui.Button) {
+ y := nodeCombo.Selected()
+ log.Println("y =", y)
+ log.Println("nodeNames[y] =", nodeNames[y])
+ node := Data.findId(nodeNames[y])
+ if (node != nil) {
+ node.DebugTab("added this DebugTab")
+ }
+ })
+
+ n1 = addButton(vbox, "Node.DemoAndlabsUiTab")
+ n1.OnClicked(func(*ui.Button) {
+ y := nodeCombo.Selected()
+ log.Println("y =", y)
+ log.Println("nodeNames[y] =", nodeNames[y])
+ node := Data.findId(nodeNames[y])
+ if (node != nil) {
+ node.DemoAndlabsUiTab("ran gui.AddDemoAndlabsUiTab() " + strconv.Itoa(Config.counter))
+ }
+ })
+
+ return hbox
+}
+
+// TODO: remove this crap
+var x int = 0
+var y int = 0
+
+// TODO: remove this crap
+func addName(c *ui.Combobox, s string) {
+ c.Append(s)
+ names[x] = s
+ x = x + 1
+}
+
+func addGroup(b *ui.Box, name string) *ui.Box {
+ group := ui.NewGroup(name)
+ group.SetMargined(true)
+ b.Append(group, Config.Stretchy)
+
+ vbox := ui.NewVerticalBox()
+ vbox.SetPadded(true)
+ group.SetChild(vbox)
+
+ return vbox
+}
+
+func addButton(box *ui.Box, name string) *ui.Button {
+ button := ui.NewButton(name)
+
+ button.OnClicked(func(*ui.Button) {
+ log.Println("Should do something here")
+ })
+
+ box.Append(button, Config.Stretchy)
+ return button
+}
+
+func (n *Node) DebugTab(title string) {
+ newNode := n.AddTab(title, makeWindowDebug())
+ if (Config.DebugNode) {
+ newNode.Dump()
+ }
+ tabSetMargined(newNode.uiTab)
+}
+
+// This sets _all_ the tabs to Margin = true
+//
+// TODO: do proper tab tracking (will be complicated). low priority
+func tabSetMargined(tab *ui.Tab) {
+ if (Config.DebugTabs) {
+ log.Println("tabSetMargined() IGNORE THIS")
+ }
+ c := tab.NumPages()
+ for i := 0; i < c; i++ {
+ if (Config.DebugTabs) {
+ log.Println("tabSetMargined() i =", i)
+ }
+ tab.SetMargined(i, true)
+ }
+}
diff --git a/debug.go b/debug.go
index 3369be6..c895e01 100644
--- a/debug.go
+++ b/debug.go
@@ -1,27 +1,28 @@
package gui
-import "log"
-import "time"
-import "fmt"
-import "reflect"
+import (
+ "fmt"
+ "log"
+ "time"
-// import "github.com/andlabs/ui"
-// import _ "github.com/andlabs/ui/winmanifest"
-import "github.com/davecgh/go-spew/spew"
-// import pb "git.wit.com/wit/witProtobuf"
+ // "github.com/davecgh/go-spew/spew"
+)
+// WatchGUI() opens a goroutine
//
-// this watches the GUI primarily to process protobuf's
-// this is pointless or wrong but I use it for debugging
-//
+// From that goroutine, it dumps out debugging information every 4 seconds
+/*
+ TODO: add configuration triggers on what to dump out
+ TODO: allow this to be sent to /var/log, syslogd, systemd's journalctl, etc
+*/
func WatchGUI() {
count := 0
for {
- if (count > 20) {
+ if count > 20 {
log.Println("Sleep() in watchGUI()")
- if (Config.Debug) {
- DumpBoxes()
+ if Config.Debug {
+ // DumpBoxes()
}
count = 0
}
@@ -30,46 +31,6 @@ func WatchGUI() {
}
}
-func DumpBoxes() {
- for name, window := range Data.WindowMap {
- log.Println("gui.DumpBoxes()", name)
- log.Println("gui.DumpBoxes()\tWindow.name =", window.Name)
- log.Println("gui.DumpBoxes()\tWindow.UiWindow type =", reflect.TypeOf(window.UiWindow))
- log.Println("gui.DumpBoxes()\tWindow.UiWindow =", window.UiWindow)
- if (window.UiTab != nil) {
- log.Println("gui.DumpBoxes()\tWindow.UiTab type =", reflect.TypeOf(window.UiTab))
- log.Println("gui.DumpBoxes()\tWindow.UiTab =", window.UiTab)
- log.Println("gui.DumpBoxes()\tWindow.UiTab.NumPages() =", window.UiTab.NumPages())
- // tmp := spew.NewDefaultConfig()
- // tmp.MaxDepth = 2
- // tmp.Dump(window.UiTab)
- if (Config.Debug) {
- scs := spew.ConfigState{MaxDepth: 2}
- scs.Dump(window.UiTab)
- }
- }
- }
- for i, window := range Data.Windows {
- log.Println("gui.DumpBoxes() Data.Windows", i, "Name =", window.Name)
- for name, abox := range window.BoxMap {
- log.Printf("gui.DumpBoxes() \tBOX mapname=%-12s abox.Name=%-12s", name, abox.Name)
- /*
- if (name == "DEBUG") {
- log.Println("\t\twatchGUI() BOX abox =", reflect.TypeOf(abox))
- win := abox.Window
- log.Println("\t\twatchGUI() BOX win =", reflect.TypeOf(win))
- area := win.Area
- log.Println("\t\twatchGUI() BOX area =", reflect.TypeOf(area), area.UiArea)
- // spew.Dump(area.UiArea)
- // area.UiArea.Show()
- // time.Sleep(2000 * time.Millisecond)
- // os.Exit(0)
- }
- */
- }
- }
-}
-
func addTableTab() {
var parts []TableColumnData
@@ -78,22 +39,40 @@ func addTableTab() {
var b TableColumnData
b.CellType = foo
- b.Heading = fmt.Sprintf("heading%d", key)
+ b.Heading = fmt.Sprintf("heading%d", key)
parts = append(parts, b)
}
- log.Println("Sleep for 2 seconds, then try to add new tabs")
- time.Sleep(1 * 1000 * 1000 * 1000)
- // AddTableTab(Data.Window1.T, 1, "test seven", 7, parts, nil)
+ log.Println("Sleep for 1 second, then try to add new tabs")
+ time.Sleep(1 * time.Second)
}
-/*
-func runTestHide(b *GuiButton) {
- log.Println("runTestHide START")
- Data.Window1.Box1.Hide()
- Data.Window1.Box2.Hide()
- // time.Sleep(2000 * time.Millisecond)
- Data.State = "HIDE"
- log.Println("runTestHide END")
+func (dn *GuiData) DumpNodeMap() {
+ log.Println("DebugDataNodeMap():")
+ for name, node := range dn.NodeMap {
+ log.Println("\tNode =", node.id, node.Width, node.Height, name)
+ if (node.children == nil) {
+ log.Println("\t\tNo children")
+ } else {
+ log.Println("\t\tHas children:", node.children)
+ }
+ // node.SetName("yahoo")
+ // log.Println("\tData.NodeMap node =", node)
+ }
+}
+
+func (dn *GuiData) ListChildren(dump bool) {
+ if Data.NodeMap == nil {
+ log.Println("gui.Data.ListChildren() Data.NodeMap == nil")
+ return
+ }
+ log.Println("gui.Data.ListChildren() Data.NodeMap:")
+ for name, node := range Data.NodeMap {
+ listChildrenDepth = 0
+ if (dump == true) {
+ log.Println("tgui.Data.ListChildren() node =", node.id, node.Width, node.Height, name)
+ node.Dump()
+ }
+ node.ListChildren(dump)
+ }
}
-*/
diff --git a/demo-window-andlabs-ui.go b/demo-window-andlabs-ui.go
new file mode 100644
index 0000000..e11ffe9
--- /dev/null
+++ b/demo-window-andlabs-ui.go
@@ -0,0 +1,84 @@
+package gui
+
+import "log"
+import "github.com/andlabs/ui"
+import _ "github.com/andlabs/ui/winmanifest"
+
+// This will create a tab in a window using direct
+// calls to andlabs/ui. This can be used to bypass
+// the obvuscation added in this package if it is desired
+// or needed.
+func (n *Node) DemoAndlabsUiTab(title string) {
+ newNode := n.AddTab(title, makeAndlabsUiTab())
+ if (Config.DebugNode) {
+ newNode.Dump()
+ }
+ tabSetMargined(newNode.uiTab)
+}
+
+func makeAndlabsUiTab() *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(47, 100)
+ slider := ui.NewSlider(21, 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)
+
+ ecbox.OnChanged(func(*ui.EditableCombobox) {
+ log.Println("test")
+ test := ecbox.Text()
+ log.Println("test=", test)
+ })
+
+ rb := ui.NewRadioButtons()
+ rb.Append("Radio Button 1")
+ rb.Append("Radio Button 2")
+ rb.Append("Radio Button 3")
+ vbox.Append(rb, false)
+
+ return hbox
+}
diff --git a/demo-window.go b/demo-window.go
new file mode 100644
index 0000000..6cbb5fd
--- /dev/null
+++ b/demo-window.go
@@ -0,0 +1,92 @@
+package gui
+
+import "log"
+import "github.com/andlabs/ui"
+import _ "github.com/andlabs/ui/winmanifest"
+
+var mybox *ui.Box
+
+func (n *Node) AddGroup(title string) *Node {
+ if (n == nil) {
+ return nil
+ }
+ hbox := n.uiBox
+ if (hbox == nil) {
+ return n
+ }
+ group := ui.NewGroup(title)
+ group.SetMargined(true)
+ hbox.Append(group, Config.Stretchy)
+
+ vbox := ui.NewVerticalBox()
+ vbox.SetPadded(true)
+ group.SetChild(vbox)
+
+ newNode := n.AddNode(title)
+ newNode.uiBox = vbox
+ return newNode
+}
+
+func (n *Node) MakeBasicControlsPage(title string) *Node {
+ if (n == nil) {
+ return nil
+ }
+ origbox := n.uiBox
+ if (origbox == nil) {
+ return n
+ }
+
+ vbox := ui.NewVerticalBox()
+ vbox.SetPadded(true)
+
+ hbox := ui.NewHorizontalBox()
+ hbox.SetPadded(true)
+ vbox.Append(hbox, false)
+
+ hbox.Append(ui.NewButton("Button"), false)
+ hbox.Append(ui.NewCheckbox("Checkbox"), false)
+
+ vbox.Append(ui.NewLabel("This is a label. Right now, labels can only span one line."), false)
+
+ vbox.Append(ui.NewHorizontalSeparator(), false)
+
+ group := ui.NewGroup("Entries")
+ group.SetMargined(true)
+ vbox.Append(group, true)
+
+ group.SetChild(ui.NewNonWrappingMultilineEntry())
+
+ entryForm := ui.NewForm()
+ entryForm.SetPadded(true)
+ group.SetChild(entryForm)
+
+ entryForm.Append("Entry", ui.NewEntry(), false)
+ entryForm.Append("Password Entry", ui.NewPasswordEntry(), false)
+ entryForm.Append("Search Entry", ui.NewSearchEntry(), false)
+ entryForm.Append("Multiline Entry", ui.NewMultilineEntry(), true)
+ entryForm.Append("Multiline Entry No Wrap", ui.NewNonWrappingMultilineEntry(), true)
+
+ origbox.Append(vbox, false)
+ newNode := n.AddNode(title)
+ newNode.uiBox = vbox
+ return newNode
+}
+
+func (n *Node) MakeGroupEdit(title string) *Node {
+ n.Dump()
+
+ group := ui.NewGroup(title)
+ group.SetMargined(true)
+ n.uiBox.Append(group, Config.Stretchy)
+
+ entrybox := ui.NewNonWrappingMultilineEntry()
+
+ group.SetChild(entrybox)
+
+ log.Println("entrybox =", entrybox)
+ n.uiMultilineEntry = entrybox
+ newNode := n.AddNode(title)
+ newNode.uiMultilineEntry = entrybox
+ newNode.uiGroup = group
+ return newNode
+}
diff --git a/doc.go b/doc.go
new file mode 100644
index 0000000..3f8bc64
--- /dev/null
+++ b/doc.go
@@ -0,0 +1,69 @@
+/*
+Package wit/gui implements a abstraction layer for Go visual elements in
+a cross platform way. Right now, this abstraction is built on top of
+the GUI toolkit 'andlabs/ui' which does the cross platform support.
+
+A quick overview of the features, some general design guidelines
+and principles for how this package should generally work:
+
+ * GUI elements are stored in a tree of nodes
+ * When in doubt, it's ok to guess. We will return something close.
+ * It tries to make your code simple
+
+Quick Start
+
+This section demonstrates how to quickly get started with spew. See the
+sections below for further details on formatting and configuration options.
+
+To dump a variable with full newlines, indentation, type, and pointer
+information use Dump, Fdump, or Sdump:
+
+ package main
+
+ import (
+ "git.wit.org/wit/gui"
+ )
+
+ func main() {
+ gui.Main(initGUI)
+ }
+
+ // This initializes the first window
+ func initGUI() {
+ gui.Config.Title = "WIT GUI Window 1"
+ gui.Config.Width = 640
+ gui.Config.Height = 480
+ node1 := gui.NewWindow()
+ addDemoTab(node1, "A Simple Tab Demo")
+ }
+
+ func addDemoTab(n *gui.Node, title string) {
+ newNode := n.AddTab(title, nil)
+
+ groupNode1 := newNode.AddGroup("group 1")
+ groupNode1.AddComboBox("demoCombo2", "more 1", "more 2", "more 3")
+ }
+
+Configuration Options
+
+Configuration of the GUI is handled by fields in the ConfigType type. For
+convenience, all of the top-level functions use a global state available
+via the gui.Config global.
+
+The following configuration options are available:
+ * Width
+ When creating a new window, this is the width
+
+ * Height
+ When creating a new window, this is the height
+
+ * Debug
+ When 'true' log more output
+
+GUI Usage
+
+Errors
+
+Not sure about errors yet. To early to document them. This is a work in progress.
+*/
+package gui
diff --git a/entry.go b/entry.go
index 280ef5f..524609f 100644
--- a/entry.go
+++ b/entry.go
@@ -1,103 +1,49 @@
package gui
import "log"
-import "fmt"
+import "errors"
+// import "fmt"
import "github.com/andlabs/ui"
+// import ui "git.wit.org/interesting/andlabs-ui"
import _ "github.com/andlabs/ui/winmanifest"
-import "github.com/davecgh/go-spew/spew"
+// import "github.com/davecgh/go-spew/spew"
// functions for handling text entry boxes
-func NewLabel(box *GuiBox, text string) {
- box.UiBox.Append(ui.NewLabel(text), false)
-}
+func (n *Node) NewLabel(text string) *Node {
+ // make new node here
+newNode := n.makeNode(text, 333, 334)
+ newNode.Dump()
-func GetText(box *GuiBox, name string) string {
- if (box == nil) {
- log.Println("gui.GetText() ERROR box == nil")
- return ""
- }
- if (box.Window.EntryMap == nil) {
- log.Println("gui.GetText() ERROR b.Box.Window.EntryMap == nil")
- return ""
- }
- spew.Dump(box.Window.EntryMap)
- if (box.Window.EntryMap[name] == nil) {
- log.Println("gui.GetText() ERROR box.Window.EntryMap[", name, "] == nil ")
- return ""
- }
- e := box.Window.EntryMap[name]
- log.Println("gui.GetText() box.Window.EntryMap[", name, "] = ", e.UiEntry.Text())
- log.Println("gui.GetText() END")
- return e.UiEntry.Text()
+ n.Append(newNode)
+ return newNode
}
-func SetText(box *GuiBox, name string, value string) error {
- if (box == nil) {
- return fmt.Errorf("gui.SetText() ERROR box == nil")
+func (n *Node) SetText(value string) error {
+ log.Println("gui.SetText() value =", value)
+ if (n.uiText != nil) {
+ n.uiText.SetText(value)
+ return nil
}
- if (box.Window.EntryMap == nil) {
- return fmt.Errorf("gui.SetText() ERROR b.Box.Window.EntryMap == nil")
+ if (n.uiButton != nil) {
+ n.uiButton.SetText(value)
+ return nil
}
- spew.Dump(box.Window.EntryMap)
- if (box.Window.EntryMap[name] == nil) {
- return fmt.Errorf("gui.SetText() ERROR box.Window.EntryMap[", name, "] == nil ")
+ if (n.uiMultilineEntry != nil) {
+ n.uiMultilineEntry.SetText(value)
+ return nil
}
- e := box.Window.EntryMap[name]
- log.Println("gui.SetText() box.Window.EntryMap[", name, "] = ", e.UiEntry.Text())
- e.UiEntry.SetText(value)
- log.Println("gui.SetText() box.Window.EntryMap[", name, "] = ", e.UiEntry.Text())
- log.Println("gui.SetText() END")
- return nil
-}
-
-// makeEntryBox(box, "hostname:", "blah.foo.org") {
-func MakeEntryVbox(box *GuiBox, a string, startValue string, edit bool, action string) *GuiEntry {
- // Start 'Nickname' vertical box
- vboxN := ui.NewVerticalBox()
- vboxN.SetPadded(true)
- vboxN.Append(ui.NewLabel(a), false)
-
- e := defaultMakeEntry(startValue, edit, action)
-
- vboxN.Append(e.UiEntry, false)
- box.UiBox.Append(vboxN, false)
- // End 'Nickname' vertical box
-
- return e
+ n.Dump()
+ return errors.New("couldn't find something to set the text to")
}
-func MakeEntryHbox(box *GuiBox, a string, startValue string, edit bool, action string) *GuiEntry {
- // Start 'Nickname' vertical box
- hboxN := ui.NewHorizontalBox()
- hboxN.SetPadded(true)
- hboxN.Append(ui.NewLabel(a), false)
-
- e := defaultMakeEntry(startValue, edit, action)
- hboxN.Append(e.UiEntry, false)
-
- box.UiBox.Append(hboxN, false)
- // End 'Nickname' vertical box
-
- return e
-}
-
-func AddEntry(box *GuiBox, name string) *GuiEntry {
- var ge *GuiEntry
- ge = new(GuiEntry)
-
- ue := ui.NewEntry()
- ue.SetReadOnly(false)
- ue.OnChanged(func(*ui.Entry) {
- log.Println("gui.AddEntry() OK. ue.Text() =", ue.Text())
- })
- box.UiBox.Append(ue, false)
-
- ge.UiEntry = ue
- box.Window.EntryMap[name] = ge
-
- return ge
+func (n *Node) SetMargined(x bool) {
+ if (n.uiGroup != nil) {
+ n.uiGroup.SetMargined(x)
+ return
+ }
+ log.Println("Couldn't find something that has a Margin setting")
}
func defaultEntryChange(e *ui.Entry) {
diff --git a/example_test.go b/example_test.go
new file mode 100644
index 0000000..f65cad1
--- /dev/null
+++ b/example_test.go
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013-2016 Dave Collins <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package gui_test
+
+import (
+ "git.wit.org/wit/gui"
+)
+
+// This example demonstrates how to create a NewWindow()
+//
+// Interacting with a GUI in a cross platform fashion adds some
+// unusual problems. To obvuscate those, andlabs/ui starts a
+// goroutine that interacts with the native gui toolkits
+// on the Linux, MacOS, Windows, etc.
+//
+// Because of this oddity, to initialize a new window, the
+// function is not passed any arguements and instead passes
+// the information via the Config type.
+//
+func ExampleNewWindow() {
+ // 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
+}
diff --git a/find.go b/find.go
new file mode 100644
index 0000000..15f2714
--- /dev/null
+++ b/find.go
@@ -0,0 +1,97 @@
+package gui
+
+import (
+ "log"
+ "os"
+
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+// "github.com/davecgh/go-spew/spew"
+)
+
+func (n *Node) FindTab() *ui.Tab {
+ return n.uiTab
+}
+
+func (n *Node) FindControl() *ui.Control {
+ return n.uiControl
+}
+
+func FindNode(name string) *Node {
+ if Data.NodeMap == nil {
+ log.Println("gui.FindNode() gui.Data.NodeMap == nil")
+ return nil
+ }
+ log.Println("gui.FindNode() searching Data.NodeMap:")
+ for id, node := range Data.NodeMap {
+ log.Println("\tData.NodeMap name =", node.Width, node.Height, id)
+ node.Dump()
+ if (name == node.Name) {
+ return node
+ }
+ newNode := findByName(node, name)
+ if (newNode != nil) {
+ return newNode
+ }
+ log.Println("gui.FindNode() could not find node name =", name)
+ os.Exit(-1)
+ }
+ log.Println("gui.FindNode() could not find node name =", name)
+ return nil
+}
+
+func (dn *GuiData) findId(id string) *Node {
+ if Data.NodeMap == nil {
+ log.Println("gui.Data.findId() map == nil")
+ return nil
+ }
+ // log.Println("Dumping Data.NodeMap:")
+ for name, node := range Data.NodeMap {
+ // log.Println("\tData.NodeMap name =", node.id, node.Width, node.Height, name)
+ if (id == node.id) {
+ log.Println("\tgui.Data.findId() found node =", node.id, node.Width, node.Height, name)
+ return node
+ }
+ // TODO: fix // Oct 9
+ // node.findId(id)
+ }
+ return nil
+}
+
+func findByIdDFS(node *Node, id string) *Node {
+ log.Println("findByIdDFS()", id, node)
+ node.Dump()
+ if node.id == id {
+ log.Println("Found node id =", id, node)
+ return node
+ }
+
+ if len(node.children) > 0 {
+ for _, child := range node.children {
+ newNode := findByIdDFS(child, id)
+ if (newNode != nil) {
+ return newNode
+ }
+ }
+ }
+ return nil
+}
+
+func findByName(node *Node, name string) *Node {
+ log.Println("findByName()", name, node)
+ node.Dump()
+ if node.Name == name {
+ log.Println("findByName() Found node name =", name, node)
+ return node
+ }
+
+ if len(node.children) > 0 {
+ for _, child := range node.children {
+ newNode := findByName(child, name)
+ if (newNode != nil) {
+ return newNode
+ }
+ }
+ }
+ return nil
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..18a5b64
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,10 @@
+module git.wit.org/wit/gui
+
+go 1.19
+
+require (
+ git.wit.org/interesting/andlabs-ui v0.0.0-20200610043537-70a69d6ae31e // indirect
+ github.com/andlabs/ui v0.0.0-20200610043537-70a69d6ae31e // indirect
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..dd2a245
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,10 @@
+git.wit.org/interesting/andlabs-ui v0.0.0-20200610043537-70a69d6ae31e h1:CTg83RH/Poy/HCBbBkRFIqKsdBSsHkLeED6XbMmzZzk=
+git.wit.org/interesting/andlabs-ui v0.0.0-20200610043537-70a69d6ae31e/go.mod h1:UuaKXIGj4crFE8XDWljgHTyKi8j4pSd9Vvn+zeHNjkQ=
+github.com/andlabs/ui v0.0.0-20200610043537-70a69d6ae31e h1:wSQCJiig/QkoUnpvelSPbLiZNWvh2yMqQTQvIQqSUkU=
+github.com/andlabs/ui v0.0.0-20200610043537-70a69d6ae31e/go.mod h1:5G2EjwzgZUPnnReoKvPWVneT8APYbyKkihDVAHUi0II=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
+golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
diff --git a/gui.go b/gui.go
index fca054e..ac75f1e 100644
--- a/gui.go
+++ b/gui.go
@@ -1,21 +1,34 @@
package gui
-import "log"
-// import "time"
-import "regexp"
+import (
+ "github.com/andlabs/ui" // import "time"
+ "log"
+ "regexp"
-import "github.com/andlabs/ui"
-import _ "github.com/andlabs/ui/winmanifest"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+// the _ means we only need this for the init()
const Xaxis = 0 // box that is horizontal
const Yaxis = 1 // box that is vertical
-func GuiInit() {
- Data.buttonMap = make(map[*ui.Button]*GuiButton)
- Data.WindowMap = make(map[string]*GuiWindow)
+func init() {
+ log.Println("gui.init() REMOVE THIS init()")
+
+ Data.NodeMap = make(map[string]*Node)
+ Data.NodeSlice = make([]*Node, 0)
+ Config.counter = 0
+ Config.prefix = "wit"
+ Config.DebugNode = false
+ Config.DebugTabs = false
+ Config.Stretchy = true
+}
+
+func GuiInit() {
ui.OnShouldQuit(func() bool {
- ui.Quit()
+ ui.Quit()
return true
})
}
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..70e0e65
--- /dev/null
+++ b/main.go
@@ -0,0 +1,34 @@
+package gui
+
+import (
+ "log"
+
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+func Main(f func()) {
+ log.Println("Starting gui.Main() (using gtk via andlabs/ui)")
+ ui.Main(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: gui.Queue(addNewTabForColorSelection())
+func Queue(f func()) {
+ log.Println("Sending function to gui.Main() (using gtk via andlabs/ui)")
+ ui.QueueMain(f)
+}
+
+/*
+func ExampleWindow() {
+ log.Println("START gui.ExampleWindow()")
+
+ Config.Title = "ExampleWindow"
+ node := NewWindow()
+ node.AddDebugTab("jcarr Debug")
+}
+*/
diff --git a/new-structs.go b/new-structs.go
new file mode 100644
index 0000000..f806696
--- /dev/null
+++ b/new-structs.go
@@ -0,0 +1,318 @@
+package gui
+
+import (
+ "log"
+ "fmt"
+ "image/color"
+// "reflect"
+
+ // "github.com/davecgh/go-spew/spew"
+
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
+
+type Element int
+
+// https://ieftimov.com/post/golang-datastructures-trees/
+const (
+ Unknown Element = iota
+ Window
+ Tab
+ Box
+ Label
+ Combo
+)
+
+func (s Element) String() string {
+ switch s {
+ case Window:
+ return "window"
+ case Tab:
+ return "tab"
+ case Box:
+ return "box"
+ case Label:
+ return "label"
+ case Combo:
+ return "combo"
+ }
+ return "unknown"
+}
+
+type Node struct {
+ id string
+ Name string
+ Width int
+ Height int
+ OnChanged func ()
+ Color color.RGBA
+
+ parent *Node
+ children []*Node
+
+ area *GuiArea
+ custom func(*Node)
+ values interface {}
+
+ uiControl *ui.Control
+ uiButton *ui.Button
+ uiFontButton *ui.FontButton
+ uiColorButton *ui.ColorButton
+ uiWindow *ui.Window
+ uiAttrstr *ui.AttributedString
+ uiTab *ui.Tab
+ uiBox *ui.Box
+ uiArea *ui.Area
+ uiText *ui.EditableCombobox
+ uiMultilineEntry *ui.MultilineEntry
+ uiGroup *ui.Group
+ uiGrid *ui.Grid
+}
+
+func (n *Node) Parent() *Node {
+ return n.parent
+}
+
+func (n *Node) Window() *Node {
+ return n.parent
+}
+
+func (n *Node) Dump() {
+ log.Println("gui.Node.Dump() id = ", n.id)
+ log.Println("gui.Node.Dump() Name = ", n.Name)
+ log.Println("gui.Node.Dump() Width = ", n.Width)
+ log.Println("gui.Node.Dump() Height = ", n.Height)
+ log.Println("gui.Node.Dump() OnChanged = ", n.OnChanged)
+
+ if (n.parent == nil) {
+ log.Println("gui.Node.Dump() parent = nil")
+ } else {
+ log.Println("gui.Node.Dump() parent = ", n.parent.id)
+ }
+ log.Println("gui.Node.Dump() children = ", n.children)
+
+ // log.Println("gui.Node.Dump() window = ", n.window)
+ // log.Println("gui.Node.Dump() box = ", n.box)
+
+ log.Println("gui.Node.Dump() uiWindow = ", n.uiWindow)
+ log.Println("gui.Node.Dump() uiTab = ", n.uiTab)
+ log.Println("gui.Node.Dump() uiBox = ", n.uiBox)
+ log.Println("gui.Node.Dump() uiControl = ", n.uiControl)
+ log.Println("gui.Node.Dump() uiButton = ", n.uiButton)
+ log.Println("gui.Node.Dump() uiText = ", n.uiText)
+ if (n.id == "") {
+ log.Println("THIS SHOULD NOT HAPPEN: gui.Node.Dump() id == nil")
+ }
+}
+
+func (n *Node) SetName(name string) {
+ // n.uiType.SetName(name)
+ if (n.uiWindow != nil) {
+ log.Println("node is a window. setting title =", name)
+ n.uiWindow.SetTitle(name)
+ return
+ }
+ log.Println("*ui.Control =", n.uiControl)
+ return
+}
+
+func (n *Node) Append(child *Node) {
+ // if (n.UiBox == nil) {
+ // return
+ // }
+ n.children = append(n.children, child)
+ if (Config.Debug) {
+ log.Println("child node:")
+ child.Dump()
+ log.Println("parent node:")
+ n.Dump()
+ }
+ // time.Sleep(3 * time.Second)
+}
+
+func (n *Node) List() {
+ findByIdDFS(n, "test")
+}
+
+var listChildrenParent *Node
+var listChildrenDepth int = 0
+
+// var indent string = "\t"
+var indent string = " "
+
+func indentPrintln(depth int, format string, a ...interface{}) {
+ var space string
+ for i := 0; i < depth; i++ {
+ space = space + indent
+ }
+
+ // newFormat := tabs + strconv.Itoa(depth) + " " + format
+ newFormat := space + format
+ log.Println(newFormat, a)
+}
+
+func (n *Node) ListChildren(dump bool) {
+ indentPrintln(listChildrenDepth, "", n.id, n.Width, n.Height, n.Name)
+
+ if (dump == true) {
+ n.Dump()
+ }
+ if len(n.children) == 0 {
+ if (n.parent == nil) {
+ } else {
+ if (Config.DebugNode) {
+ log.Println("\t\t\tparent =",n.parent.id)
+ }
+ if (listChildrenParent != nil) {
+ if (Config.DebugNode) {
+ log.Println("\t\t\tlistChildrenParent =",listChildrenParent.id)
+ }
+ if (listChildrenParent.id != n.parent.id) {
+ log.Println("parent.child does not match child.parent")
+ panic("parent.child does not match child.parent")
+ }
+ }
+ }
+ if (Config.DebugNode) {
+ log.Println("\t\t", n.id, "has no children")
+ }
+ return
+ }
+ for _, child := range n.children {
+ // log.Println("\t\t", child.id, child.Width, child.Height, child.Name)
+ if (child.parent != nil) {
+ if (Config.DebugNode) {
+ log.Println("\t\t\tparent =",child.parent.id)
+ }
+ } else {
+ log.Println("\t\t\tno parent")
+ panic("no parent")
+ }
+ if (dump == true) {
+ child.Dump()
+ }
+ if (Config.DebugNode) {
+ if (child.children == nil) {
+ log.Println("\t\t", child.id, "has no children")
+ } else {
+ log.Println("\t\t\tHas children:", child.children)
+ }
+ }
+ listChildrenParent = n
+ listChildrenDepth += 1
+ child.ListChildren(dump)
+ listChildrenDepth -= 1
+ }
+ return
+}
+
+// The parent Node needs to be the raw Window
+// The 'stuff' Node needs to be the contents of the tab
+//
+// This function should make a new node with the parent and
+// the 'stuff' Node as a child
+func (n *Node) AddTabNode(title string) *Node {
+ var newNode *Node
+ parent := n
+
+ newNode = parent.makeNode(title, 444, 400 + Config.counter)
+ newNode.uiTab = parent.uiTab
+ // newNode.box = b
+
+ if (Config.DebugNode) {
+ fmt.Println("")
+ log.Println("parent:")
+ parent.Dump()
+
+ fmt.Println("")
+ log.Println("newNode:")
+ newNode.Dump()
+ }
+
+ if (newNode.uiTab != nil) {
+ log.Println("ERROR: wit/gui/ AddTabNode() Something went wrong tab == nil")
+ // TODO: try to find the tab or window and make them if need be
+ // newNode.uiTab.Append(title, b.UiBox)
+ }
+
+ return newNode
+}
+
+func (n *Node) AddHorizontalBreak() *Node {
+ log.Println("AddHorizontalBreak added to node =", n.Name)
+ if (n.uiBox != nil) {
+ tmp := ui.NewHorizontalSeparator()
+ n.uiBox.Append(tmp, Config.Stretchy)
+ } else {
+ n.Dump()
+ return nil
+ }
+ return n
+}
+
+func (n *Node) AddVerticalBreak() *Node {
+ log.Println("AddVerticalBreak added to node =", n.Name)
+ if (n.uiBox != nil) {
+ tmp := ui.NewVerticalSeparator()
+ n.uiBox.Append(tmp, Config.Stretchy)
+ } else {
+ n.Dump()
+ return nil
+ }
+ return n
+}
+
+func (n *Node) AddHorizontalBox(title string) *Node {
+ hbox := ui.NewHorizontalBox()
+ hbox.SetPadded(true)
+ if (n.uiBox != nil) {
+ log.Println("add new hbox to uiBox =", n.uiBox)
+ n.uiBox.Append(hbox, Config.Stretchy)
+ newNode := n.makeNode(title, 333, 333 + Config.counter)
+ newNode.parent = n
+ newNode.uiBox = hbox
+ // newNode.uiControl = hbox
+ return newNode
+ }
+ if (n.uiTab != nil) {
+ log.Println("add new hbox to uiTab =", n.uiTab)
+ n.uiTab.Append(title, hbox)
+ newNode := n.makeNode(title, 333, 333 + Config.counter)
+ newNode.parent = n
+ newNode.uiBox = hbox
+ // newNode.uiControl = hbox
+ return newNode
+ }
+ return n
+}
+func (n *Node) AddTab(title string, uiC *ui.Box) *Node {
+ parent := n
+ log.Println("gui.Node.AddTab() START name =", title)
+ if parent.uiWindow == nil {
+ parent.Dump()
+ log.Println("gui.Node.AddTab() ERROR ui.Window == nil")
+ return nil
+ }
+ if parent.uiTab == nil {
+ inittab := ui.NewTab() // no, not that 'inittab'
+ parent.uiWindow.SetChild(inittab)
+ parent.uiWindow.SetMargined(true)
+ parent.uiTab = inittab
+ }
+ tab := parent.uiTab
+ parent.uiWindow.SetMargined(true)
+
+ if (uiC == nil) {
+ hbox := ui.NewHorizontalBox()
+ hbox.SetPadded(true)
+ uiC = hbox
+ }
+ tab.Append(title, uiC)
+
+ newNode := parent.makeNode(title, 555, 600 + Config.counter)
+ newNode.uiTab = tab
+ newNode.uiBox = uiC
+ tabSetMargined(newNode.uiTab)
+ return newNode
+}
diff --git a/structs.go b/structs.go
index aae0b7f..79b3fbf 100644
--- a/structs.go
+++ b/structs.go
@@ -1,153 +1,85 @@
package gui
-import "image/color"
-import "golang.org/x/image/font"
+import (
+ "image/color"
+// "log"
-import "github.com/andlabs/ui"
-import _ "github.com/andlabs/ui/winmanifest"
+ "github.com/andlabs/ui"
+ "golang.org/x/image/font"
+
+ _ "github.com/andlabs/ui/winmanifest"
+)
//
// All GUI Data Structures and functions that are external
// If you need cross platform support, these might only
// be the safe way to interact with the GUI
//
-var Data GuiData
-var Config GuiConfig
+var Data GuiData
+var Config GuiConfig
type GuiConfig struct {
- Width int
- Height int
- Debug bool
- DebugTable bool
+ Title string
+ Width int
+ Height int
+ Stretchy bool
+ Menu bool
+ Exit func(*Node)
+
+ Debug bool
+ DebugNode bool
+ DebugTabs bool
+ DebugTable bool
+ DebugWindow bool
+
+ depth int
+ counter int // used to make unique ID's
+ prefix string
}
type GuiData struct {
- // a fallback default function to handle mouse events
+ // a fallback default function to handle mouse events
// if nothing else is defined to handle them
- MouseClick func(*GuiButton)
+ MouseClick func(*Node)
// A map of all the entry boxes
- AllEntries []*GuiEntry
- Windows []*GuiWindow
- WindowMap map[string]*GuiWindow
-
- // A map of all buttons everywhere on all
- // windows, all tabs, across all goroutines
- // This is "GLOBAL"
- //
- // This has to work this way because of how
- // andlabs/ui & andlabs/libui work
- AllButtons []*GuiButton
- buttonMap map[*ui.Button]*GuiButton
-}
-
-//
-// stores information on the 'window'
-//
-// This merges the concept of andlabs/ui *Window and *Tab
-//
-// More than one Window is not supported in a cross platform
-// sense & may never be. On Windows and MacOS, you have to have
-// 'tabs'. Even under Linux, more than one Window is currently
-// unstable
-//
-// This code will make a 'GuiWindow' regardless of if it is
-// a stand alone window (which is more or less working on Linux)
-// or a 'tab' inside a window (which is all that works on MacOS
-// and MSWindows.
-//
-// This struct keeps track of what is in the window so you
-// can destroy and replace it with something else
-//
-type GuiWindow struct {
- Name string // field for human readable name
- Width int
- Height int
-
- // the callback function to make the window contents
- MakeWindow func(*GuiWindow) *GuiBox
-
- // the components of the window
- BoxMap map[string]*GuiBox
- EntryMap map[string]*GuiEntry
- Area *GuiArea
-
- // andlabs/ui abstraction mapping
- UiWindow *ui.Window
- UiTab *ui.Tab // if this != nil, the window is 'tabbed'
-}
+ AllEntries []*GuiEntry
-// GuiBox is any type of ui.Hbox or ui.Vbox
-// There can be lots of these for each GuiWindow
-type GuiBox struct {
- Name string // field for human readable name
- Axis int // does it add items to the X or Y axis
- Window *GuiWindow // the parent Window
-
- // andlabs/ui abstraction mapping
- UiBox *ui.Box
-}
-
-// Note: every mouse click is handled
-// as a 'Button' regardless of where
-// the user clicks it. You could probably
-// call this 'GuiMouseClick'
-type GuiButton struct {
- Name string // field for human readable name
- Box *GuiBox // what box the button click was in
-
- // a callback function for the main application
- Custom func (*GuiButton)
- Values interface {}
-
- // andlabs/ui abstraction mapping
- B *ui.Button
- FB *ui.FontButton
+ // Store access to everything via binary tree's
+ NodeMap map[string]*Node
+ NodeArray []*Node
+ NodeSlice []*Node
}
// text entry fields
type GuiEntry struct {
- Name string // field for human readable name
- Edit bool
- Last string // the last value
- Normalize func (string) string // function to 'normalize' the data
+ Name string // field for human readable name
+ Edit bool
+ Last string // the last value
+ Normalize func(string) string // function to 'normalize' the data
- B *GuiButton
- Box *GuiBox
+ N *Node
// andlabs/ui abstraction mapping
- UiEntry *ui.Entry
+ UiEntry *ui.Entry
}
-//
-// AREA STRUCTURES START
-// AREA STRUCTURES START
-// AREA STRUCTURES START
-//
-type GuiArea struct{
- Button *GuiButton // what button handles mouse events
- Box *GuiBox
+type GuiArea struct {
+ N *Node // what node to pass mouse events
- UiAttrstr *ui.AttributedString
- UiArea *ui.Area
+ UiAttrstr *ui.AttributedString
+ UiArea *ui.Area
}
type FontString struct {
- S string
- Size int
- F font.Face
- W font.Weight
+ S string
+ Size int
+ F font.Face
+ W font.Weight
}
-//
-// AREA STRUCTURES END
-// AREA STRUCTURES END
-// AREA STRUCTURES END
-//
//
// TABLE DATA STRUCTURES START
-// TABLE DATA STRUCTURES START
-// TABLE DATA STRUCTURES START
//
//
@@ -155,18 +87,18 @@ type FontString struct {
// to the GUI. This is the "authoritative" data.
//
type TableData struct {
- RowCount int // This is the number of 'rows' which really means data elements not what the human sees
- RowWidth int // This is how wide each row is
- Rows []RowData // This is all the table data by row
- generatedColumnTypes []ui.TableValue // generate this dynamically
+ RowCount int // This is the number of 'rows' which really means data elements not what the human sees
+ RowWidth int // This is how wide each row is
+ Rows []RowData // This is all the table data by row
+ generatedColumnTypes []ui.TableValue // generate this dynamically
- Cells [20]CellData
- Human [20]HumanMap
+ Cells [20]CellData
+ Human [20]HumanMap
- Box *GuiBox
+ n *Node
- lastRow int
- lastColumn int
+ lastRow int
+ lastColumn int
}
//
@@ -185,44 +117,44 @@ type TableData struct {
// TODO: re-add images and the progress bar (works in andlabs/ui)
//
type HumanCellData struct {
- Name string // what kind of row is this?
- Text string
- TextID int
- Color color.RGBA
- ColorID int
- Button *GuiButton
+ Name string // what kind of row is this?
+ Text string
+ TextID int
+ Color color.RGBA
+ ColorID int
+ N *Node
}
type HumanMap struct {
- Name string // what kind of row is this?
- TextID int
- ColorID int
+ Name string // what kind of row is this?
+ TextID int
+ ColorID int
}
type TableColumnData struct {
- Index int
- CellType string
- Heading string
- Color string
+ Index int
+ CellType string
+ Heading string
+ Color string
}
type CellData struct {
- Index int
- HumanID int
- Name string // what type of cell is this?
+ Index int
+ HumanID int
+ Name string // what type of cell is this?
}
// hmm. will this stand the test of time?
type RowData struct {
- Name string // what kind of row is this?
- Status string // status of the row?
-/*
- // TODO: These may or may not be implementable
- // depending on if it's possible to detect the bgcolor or what row is selected
- click func() // what function to call if the user clicks on it
- doubleclick func() // what function to call if the user double clicks on it
-*/
- HumanData [20]HumanCellData
+ Name string // what kind of row is this?
+ Status string // status of the row?
+ /*
+ // TODO: These may or may not be implementable
+ // depending on if it's possible to detect the bgcolor or what row is selected
+ click func() // what function to call if the user clicks on it
+ doubleclick func() // what function to call if the user double clicks on it
+ */
+ HumanData [20]HumanCellData
}
//
diff --git a/table.go b/table.go
index b4e73c1..378cab2 100644
--- a/table.go
+++ b/table.go
@@ -97,7 +97,12 @@ func InitColumns(mh *TableData, parts []TableColumnData) {
}
}
-func AddTableTab(gw *GuiWindow, name string, rowcount int, parts []TableColumnData) *TableData {
+func AddTableTab(name string, rowcount int, parts []TableColumnData) *TableData {
+ n := NewWindow()
+ return n.AddTableBox(name, rowcount, parts)
+}
+
+func (n *Node) AddTableBox(name string, rowcount int, parts []TableColumnData) *TableData {
mh := new(TableData)
mh.RowCount = rowcount
@@ -134,25 +139,12 @@ func AddTableTab(gw *GuiWindow, name string, rowcount int, parts []TableColumnDa
}
}
- var gb *GuiBox
- gb = new(GuiBox)
-
- vbox := ui.NewVerticalBox()
- vbox.SetPadded(true)
- gb.UiBox = vbox
- gb.Window = gw
- gw.BoxMap[name] = gb
- mh.Box = gb
-
- vbox.Append(table, true)
- gw.UiTab.Append(name, vbox)
-
- vbox.Append(ui.NewVerticalSeparator(), false)
-
- hbox := ui.NewHorizontalBox()
- hbox.SetPadded(true)
+ // is this needed?
+ // gw.BoxMap[name] = box
+ // mh.Box = b
+ mh.n = n
- vbox.Append(hbox, false)
+ n.uiBox.Append(table, true)
return mh
}
diff --git a/tableCallbacks.go b/tableCallbacks.go
index 954a832..f294a9b 100644
--- a/tableCallbacks.go
+++ b/tableCallbacks.go
@@ -29,20 +29,9 @@ func (mh *TableData) ColumnTypes(m *ui.TableModel) []ui.TableValue {
return mh.generatedColumnTypes
}
-func libuiColorToGOlangColor(rgba color.RGBA) ui.TableColor {
- /* a hack to see if colors work differently on macos or windows
- if (rgba.R == 72) {
- log.Println("SETTING COLOR TO NIL")
- log.Println("SETTING COLOR TO NIL")
- log.Println("SETTING COLOR TO NIL")
- return ui.TableColor{}
- }
- */
- return ui.TableColor{float64(rgba.R) / 256, float64(rgba.G) / 256, float64(rgba.B) / 256, float64(rgba.A) / 256}
-}
-
// TODO: Figure out why this is being called 1000 times a second (10 times for each row & column)
-// Nevermind this TODO. Who gives a shit. This is a really smart way to treat the OS toolkits
+//
+// Nevermind that TODO. Who gives a shit. This is a really smart way to treat the OS toolkits
func (mh *TableData) CellValue(m *ui.TableModel, row, column int) ui.TableValue {
if (Config.DebugTable) {
log.Println("CellValue() row, column =", row, column)
@@ -106,9 +95,12 @@ func defaultSetCellValue(mh *TableData, row int, column int) {
humanID := mh.Cells[column].HumanID
log.Println("defaultSetCellValue() FOUND THE TABLE BUTTON ", row, humanID)
- button := mh.Rows[row].HumanData[humanID].Button
- if (button != nil) {
- guiButtonClick(button)
+ n := mh.Rows[row].HumanData[humanID].N
+ if (n != nil) {
+ // TODO: fixme. removed on Oct 31 2021
+ if (n.OnChanged != nil) {
+ n.OnChanged()
+ }
return
}
log.Println("defaultSetCellValue() ERROR: UNKNOWN BUTTON IN TABLE")
diff --git a/window.go b/window.go
index 0100778..50f7073 100644
--- a/window.go
+++ b/window.go
@@ -1,82 +1,168 @@
package gui
-import "log"
-import "time"
-// import "regexp"
+import (
+ "log"
+// "fmt"
+ "strconv"
-import "github.com/andlabs/ui"
-import _ "github.com/andlabs/ui/winmanifest"
+ "github.com/andlabs/ui"
+ _ "github.com/andlabs/ui/winmanifest"
+)
-func InitGuiWindow(action string, gw *GuiWindow) *GuiWindow {
- log.Println("InitGuiWindow() START")
- var newGuiWindow GuiWindow
- newGuiWindow.Width = Config.Width
- newGuiWindow.Height = Config.Height
- newGuiWindow.MakeWindow = gw.MakeWindow
- newGuiWindow.UiWindow = gw.UiWindow
- newGuiWindow.UiTab = gw.UiTab
- newGuiWindow.BoxMap = make(map[string]*GuiBox)
- newGuiWindow.EntryMap = make(map[string]*GuiEntry)
- newGuiWindow.EntryMap["test"] = nil
- Data.Windows = append(Data.Windows, &newGuiWindow)
-
- if (Data.buttonMap == nil) {
- GuiInit()
+func findUiWindow() *ui.Window {
+ for _, node := range Data.NodeMap {
+ if (node.uiWindow != nil) {
+ return node.uiWindow
+ }
}
- log.Println("InitGuiWindow() END *GuiWindow =", &newGuiWindow)
- return &newGuiWindow
+ return nil
+}
+
+func MessageWindow(msg1 string, msg2 string) (*Node) {
+ uiW := findUiWindow()
+ ui.MsgBox(uiW, msg1, msg2)
+ // TODO: make new node
+ return nil
}
+func ErrorWindow(msg1 string, msg2 string) (*Node) {
+ uiW := findUiWindow()
+ ui.MsgBoxError(uiW, msg1, msg2)
+ return nil
+}
+
+func initNode(title string, x int, y int) *Node {
+ var node Node
+ node.Name = title
+ node.Width = x
+ node.Height = y
+
+ id := Config.prefix + strconv.Itoa(Config.counter)
+ Config.counter += 1
+ node.id = id
-func StartNewWindow(bg bool, action string, callback func(*GuiWindow) *GuiBox) {
- log.Println("StartNewWindow() Create a new window")
- var junk GuiWindow
- junk.MakeWindow = callback
-// junk.Action = action
- window := InitGuiWindow(action, &junk)
- if (bg) {
- log.Println("StartNewWindow() START NEW GOROUTINE for ui.Main()")
- go ui.Main(func() {
- log.Println("gui.StartNewWindow() inside ui.Main()")
- go InitTabWindow(window)
- })
- time.Sleep(2000 * time.Millisecond) // this might make it more stable on windows?
- } else {
- log.Println("StartNewWindow() WAITING for ui.Main()")
- ui.Main(func() {
- log.Println("gui.StartNewWindow() inside ui.Main()")
- InitTabWindow(window)
- })
+ if (Data.NodeMap[title] != nil) {
+ log.Println("ERROR: Duplicate window name =", title)
+ // TODO: just change the 'title' to something unique
+ return Data.NodeMap[title]
}
+ Data.NodeMap[title] = &node
+ Data.NodeArray = append(Data.NodeArray, &node)
+ Data.NodeSlice = append(Data.NodeSlice, &node)
+ return &node
+ // parent.Append(&node)
+ //node.parent = parent
+ return &node
}
-func InitTabWindow(gw *GuiWindow) {
- log.Println("InitTabWindow() START. THIS WINDOW IS NOT YET SHOWN")
+func (parent *Node) makeNode(title string, x int, y int) *Node {
+ var node Node
+ node.Name = title
+ node.Width = x
+ node.Height = y
- gw.UiWindow = ui.NewWindow("InitTabWindow()", int(gw.Width), int(gw.Height), true)
- gw.UiWindow.SetBorderless(false)
+ id := Config.prefix + strconv.Itoa(Config.counter)
+ Config.counter += 1
+ node.id = id
- gw.UiWindow.OnClosing(func(*ui.Window) bool {
- log.Println("InitTabWindow() OnClosing() THIS WINDOW IS CLOSING gw=", gw)
- ui.Quit()
+ parent.Append(&node)
+ node.parent = parent
+ return &node
+}
+
+func (n *Node) AddNode(title string) *Node {
+ var node Node
+ node.Name = title
+ node.Width = n.Width
+ node.Height = n.Height
+
+ id := Config.prefix + strconv.Itoa(Config.counter)
+ Config.counter += 1
+ node.id = id
+
+ n.Append(&node)
+ node.parent = n
+ return &node
+}
+
+func (n *Node) uiNewWindow(title string, x int, y int) {
+ w := ui.NewWindow(title, x, y, Config.Menu)
+ w.SetBorderless(false)
+ f := Config.Exit
+ w.OnClosing(func(*ui.Window) bool {
+ log.Println("RUNNING the ui.Window().OnClosing() function")
+ if (f != nil) {
+ f(n)
+ } else {
+ n.Dump()
+ log.Println("gui.uiWindow().OnClosing() NOT SURE WHAT TO DO HERE")
+ // TODO: always do this here? // by default delete the node?
+ name := n.Name
+ delete(Data.NodeMap, name)
+ }
return true
})
+ w.SetMargined(true)
+ w.Show()
+ n.uiWindow = w
+ // w.node = &node
+ return
+}
- gw.UiTab = ui.NewTab()
- gw.UiWindow.SetChild(gw.UiTab)
- gw.UiWindow.SetMargined(true)
+/*
+func mapWindow(parent *Node, window *ui.Window, title string, x int, y int) *Node {
+ log.Println("gui.WindowMap START title =", title)
+ node := makeNode(parent, title, x, y)
+ node.uiWindow = window
- box := gw.MakeWindow(gw)
- log.Println("InitTabWindow() END box =", box)
- log.Println("InitTabWindow() END gw =", gw)
- gw.UiWindow.Show()
+ return node
}
+*/
-func MessageWindow(gw *GuiWindow, msg1 string, msg2 string) {
- ui.MsgBox(gw.UiWindow, msg1, msg2)
-}
+// This routine creates a blank window with a Title and size (W x H)
+//
+// This routine can not have any arguements due to the nature of how
+// 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).
+//
+// There is probably some way to pass arguements here that I'm can't think of right now
+//
+func NewWindow() *Node {
+ title := Config.Title
+ w := Config.Width
+ h := Config.Height
+
+ if (Data.NodeMap[title] != nil) {
+ log.Println("Duplicate window name =", title)
+ Data.NodeMap[title].Dump()
+ Data.NodeMap[title].ListChildren(false)
+ uiW := Data.NodeMap[title].uiWindow
+ if (uiW != nil) {
+ uiW.Show()
+ }
+ log.Println("PROBABLY BAD ERROR: check here to see if window is really alive")
+ return Data.NodeMap[title]
+ }
-func ErrorWindow(gw *GuiWindow, msg1 string, msg2 string) {
- ui.MsgBoxError(gw.UiWindow, msg1, msg2)
+ var n *Node
+ n = initNode(title, w, h)
+ n.uiNewWindow(title, w, h)
+ window := n.uiWindow
+
+ f := Config.Exit
+ ui.OnShouldQuit(func() bool {
+ log.Println("createWindow().Destroy() on node.Name =", n.Name)
+ if (f != nil) {
+ f(n)
+ }
+ return true
+ })
+
+ n.uiWindow = window
+ if(n.uiWindow == nil) {
+ log.Println("ERROR: node.uiWindow == nil. This should never happen")
+ }
+ return n
}