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 0cc7531..7c42c6c 100644 --- a/area.go +++ b/area.go @@ -107,10 +107,10 @@ func (ah GuiArea) KeyEvent(a *ui.Area, ke *ui.AreaKeyEvent) (handled bool) { return false } -func ShowTextBox(box *GuiBox, newText *ui.AttributedString, custom func(*GuiButton), name string) { +func (b *GuiBox) ShowTextBox(newText *ui.AttributedString, custom func(*GuiButton), name string) { log.Println("ShowTextBox() START") - gw := box.Window + gw := b.Window if (gw == nil) { log.Println("ShowTextBox() ERROR gw = nil") return @@ -127,10 +127,10 @@ func ShowTextBox(box *GuiBox, newText *ui.AttributedString, custom func(*GuiButt */ // TODO: allow padded & axis here - box.UiBox.SetPadded(true) + b.UiBox.SetPadded(true) // add(gw.BoxMap["MAINBOX"], newbox) - makeGenericArea(box, newText, custom) - box.UiBox.Append(box.Window.Area.UiArea, true) + makeGenericArea(b, newText, custom) + b.UiBox.Append(b.Window.Area.UiArea, true) } diff --git a/box.go b/box.go index 08d4918..7a61f55 100644 --- a/box.go +++ b/box.go @@ -1,6 +1,7 @@ package gui import "log" +import "os" // import "reflect" import "github.com/andlabs/ui" @@ -34,6 +35,7 @@ func add(box *GuiBox, newbox *GuiBox) { newbox.Window.BoxMap["MAINBOX"] = newbox log.Println("gui.add() END") + panic("gui.add() MAINBOX gui.add() END") return } else { log.Println("\tgui.add() ERROR DONT KNOW HOW TO ADD TO A RAW WINDOW YET") @@ -41,33 +43,77 @@ func add(box *GuiBox, newbox *GuiBox) { } log.Println("\tgui.add() ERROR DON'T KNOW HOW TO add to Window as MAINBOX DONE") log.Println("gui.add() END") + panic("gui.add() 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") + if (box.node == nil) { + box.Dump() + panic("gui.add() ERROR box.node == nil") } + if (newbox.UiBox == nil) { - log.Println("\tgui.add() ERROR newbox.UiBox == nil") - panic("crap") + panic("gui.add() ERROR newbox.UiBox == nil") + } + + if (box.UiBox == nil) { + box.Dump() + // panic("gui.add() ERROR box.UiBox == nil") + return + // TODO: fix this whole add() function // Oct 9 } - // log.Println("\tgui.add() newbox.UiBox == ", newbox.UiBox.GetParent()) - // spew.Dump(newbox.UiBox) box.UiBox.Append(newbox.UiBox, false) + box.Dump() + panic("gui.add()") // 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") +func (n *Node) AddBox(axis int, name string) *Node { + newBox := new(GuiBox) + newBox.Window = n.window + newBox.Name = name + + if (n.box == nil) { + n.box = newBox + } + + // make a new box & a new node + newNode := n.makeNode(name, 111, 100 + Config.counter) + newNode.box = newBox + Config.counter += 1 + + var uiBox *ui.Box + if (axis == Xaxis) { + uiBox = ui.NewHorizontalBox() + } else { + uiBox = ui.NewVerticalBox() + } + uiBox.SetPadded(true) + newBox.UiBox = uiBox + newNode.uiBox = uiBox + + n.Append(newNode) + // add(n.box, newBox) + return newNode +} + +func (b *GuiBox) NewBox(axis int, name string) *GuiBox { + log.Println("gui.NewBox() START") + n := b.FindNode() + if (n == nil) { + log.Println("gui.NewBox() SERIOUS ERROR. CAN NOT FIND NODE") + os.Exit(0) + } else { + log.Println("gui.NewBox() node =", n.Name) + } var newbox *GuiBox newbox = new(GuiBox) - newbox.Window = box.Window + newbox.Window = b.Window newbox.Name = name var uiBox *ui.Box @@ -78,13 +124,18 @@ func NewBox(box *GuiBox, axis int, name string) *GuiBox { } uiBox.SetPadded(true) newbox.UiBox = uiBox - add(box, newbox) + add(b, newbox) + // panic("gui.NewBox") return newbox } func HardBox(gw *GuiWindow, axis int, name string) *GuiBox { log.Println("HardBox() START axis =", axis) + if (gw.node == nil) { + gw.Dump() + panic("gui.HardBox() gw.node == nil") + } // add a Vertical Seperator if there is already a box // Is this right? box := gw.BoxMap["MAINBOX"] @@ -120,6 +171,12 @@ func HardBox(gw *GuiWindow, axis int, name string) *GuiBox { func HorizontalBreak(box *GuiBox) { log.Println("VerticalSeparator added to box =", box.Name) tmp := ui.NewHorizontalSeparator() + if (box == nil) { + return + } + if (box.UiBox == nil) { + return + } box.UiBox.Append(tmp, false) } @@ -128,3 +185,40 @@ func VerticalBreak(box *GuiBox) { tmp := ui.NewVerticalSeparator() box.UiBox.Append(tmp, false) } + +func (n *Node) AddComboBox(title string, s ...string) *Node { + box := n.uiBox + if (box == nil) { + return n + } + + ecbox := ui.NewEditableCombobox() + + for id, name := range s { + log.Println("Adding Combobox Entry:", id, name) + ecbox.Append(name) + } + + ecbox.OnChanged(func(*ui.EditableCombobox) { + test := ecbox.Text() + log.Println("node.Name = '" + n.Name + "' text for '" + title + "' is now: '" + test + "'") + }) + + box.Append(ecbox, false) + + newNode := n.AddNode(title) + newNode.uiText = ecbox + return newNode +} + +func (n *Node) OnChanged(f func()) { + f() +} + +func (n *Node) GetText() string { + if (n.uiText == nil) { + return "" + } + ecbox := n.uiText + return ecbox.Text() +} diff --git a/button.go b/button.go index 24dcd49..8ad0a3b 100644 --- a/button.go +++ b/button.go @@ -1,10 +1,12 @@ package gui import "log" +import "reflect" import "github.com/andlabs/ui" import _ "github.com/andlabs/ui/winmanifest" // import "github.com/davecgh/go-spew/spew" + // This is the default mouse click handler // Every mouse click that hasn't been assigned to // something specific will fall into this routine @@ -50,6 +52,60 @@ func guiButtonClick(button *GuiButton) { } } +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 + } + button := ui.NewButton(name) + log.Println("reflect.TypeOF(uiBox) =", reflect.TypeOf(n.uiBox)) + log.Println("reflect.TypeOF(uiButton) =", reflect.TypeOf(button)) + n.uiBox.Append(button, false) + n.uiButton = button + + newNode := n.makeNode(name, 888, 888 + Config.counter) + newNode.uiButton = button + newNode.custom = custom + + button.OnClicked(func(*ui.Button) { + log.Println("gui.AppendButton() Button Clicked. Running custom()") + custom(newNode) + }) + // panic("AppendButton") + // time.Sleep(3 * time.Second) + return newNode +} + +func (n *Node) CreateButton(custom func(*GuiButton), name string, values interface {}) *Node { + newNode := n.AddBox(Xaxis, "test CreateButton") + box := newNode.FindBox() + if (box == nil) { + panic("node.CreateButton().FindBox() == nil") + } + newUiB := ui.NewButton(name) + newUiB.OnClicked(defaultButtonClick) + + var newB *GuiButton + newB = new(GuiButton) + newB.B = newUiB + if (box.UiBox == 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?") + } else { + // uibox := box.UiBox + // uibox.Append(newUiB, true) + } + newB.Box = box + newB.Custom = custom + newB.Values = values + + Data.AllButtons = append(Data.AllButtons, newB) + + box.Append(newB.B, false) + return newNode +} + func CreateButton(box *GuiBox, custom func(*GuiButton), name string, values interface {}) *GuiButton { newUiB := ui.NewButton(name) newUiB.OnClicked(defaultButtonClick) @@ -68,7 +124,7 @@ func CreateButton(box *GuiBox, custom func(*GuiButton), name string, values inte Data.AllButtons = append(Data.AllButtons, newB) - box.UiBox.Append(newB.B, false) + box.Append(newB.B, false) return newB } @@ -110,6 +166,6 @@ func CreateColorButton(box *GuiBox, custom func(*GuiButton), name string, values Data.MouseClick(&newCB) } }) - box.UiBox.Append(newCB.CB, false) + box.Append(newCB.CB, false) return &newCB } 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..638cffc --- /dev/null +++ b/cmds/gui-example/Makefile @@ -0,0 +1,6 @@ +run: build + ./gui-example + +build: + GO111MODULE="off" go -v get . + GO111MODULE="off" go build diff --git a/cmds/gui-example/demo-window.go b/cmds/gui-example/demo-window.go new file mode 100644 index 0000000..993900d --- /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/cmds/helloworld/main.go b/cmds/helloworld/main.go new file mode 100644 index 0000000..1cb1bbd --- /dev/null +++ b/cmds/helloworld/main.go @@ -0,0 +1,27 @@ +// This creates a simple hello world window +package main + +import ( + "git.wit.org/wit/gui" +) + +func main() { + gui.Main(initGUI) +} + +// This initializes the first window +func initGUI() { + gui.Config.Title = "Hello World golang wit/gui Window" + gui.Config.Width = 640 + gui.Config.Height = 480 + node1 := gui.NewWindow() + addDemoTab(node1, "A Simple Tab Demo") + addDemoTab(node1, "A Second Tab") +} + +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") +} diff --git a/debug-window.go b/debug-window.go new file mode 100644 index 0000000..19f276d --- /dev/null +++ b/debug-window.go @@ -0,0 +1,355 @@ +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) + + ///////////////////////////////////////////////////// + vbox := addGroup(hbox, "range Data.WindowMap") + cbox := ui.NewCombobox() + + for name, _ := range Data.WindowMap { + if (Config.Debug) { + log.Println("range Data.WindowMap() name =", name) + } + addName(cbox, name) + } + cbox.SetSelected(0) + + vbox.Append(cbox, false) + + cbox.OnSelected(func(*ui.Combobox) { + x := cbox.Selected() + log.Println("x =", x) + log.Println("names[x] =", names[x]) + dumpBox(names[x]) + }) + + ///////////////////////////////////////////////////// + vbox = addGroup(hbox, "Debug Window") + + b1 := addButton(vbox, "dumpBox(window)") + b1.OnClicked(func(*ui.Button) { + x := cbox.Selected() + log.Println("x =", x) + log.Println("names[x] =", names[x]) + dumpBox(names[x]) + }) + + b2 := addButton(vbox, "SetMargined(tab)") + b2.OnClicked(func(*ui.Button) { + x := cbox.Selected() + log.Println("x =", x) + log.Println("FindWindow; names[x] =", names[x]) + gw := FindWindow(names[x]) + if gw == nil { + return + } + if gw.UiTab == nil { + return + } + if gw.TabNumber == nil { + return + } + scs := spew.ConfigState{MaxDepth: 1} + scs.Dump(gw) + log.Println("gui.DumpBoxes()\tWindow.UiTab =", gw.UiTab) + log.Println("gui.DumpBoxes()\tWindow.TabNumber =", *gw.TabNumber) + gw.UiTab.SetMargined(*gw.TabNumber, true) + }) + + b3 := addButton(vbox, "Hide(tab)") + b3.OnClicked(func(*ui.Button) { + x := cbox.Selected() + log.Println("x =", x) + log.Println("FindWindow; names[x] =", names[x]) + gw := FindWindow(names[x]) + if gw == nil { + return + } + if gw.UiTab == nil { + return + } + gw.UiTab.Hide() + }) + + b4 := addButton(vbox, "Show(tab)") + b4.OnClicked(func(*ui.Button) { + x := cbox.Selected() + log.Println("x =", x) + log.Println("FindWindow; names[x] =", names[x]) + gw := FindWindow(names[x]) + if gw == nil { + return + } + if gw.UiTab == nil { + return + } + gw.UiTab.Show() + }) + + b5 := addButton(vbox, "Delete(tab)") + b5.OnClicked(func(*ui.Button) { + x := cbox.Selected() + log.Println("x =", x) + log.Println("FindWindow; names[x] =", names[x]) + gw := FindWindow(names[x]) + if gw == nil { + return + } + if gw.UiTab == nil { + return + } + if gw.TabNumber == nil { + return + } + gw.UiTab.Delete(*gw.TabNumber) + }) + + ///////////////////////////////////////////////////// + vbox = addGroup(hbox, "Global Debug") + + dump3 := addButton(vbox, "Dump Windows") + dump3.OnClicked(func(*ui.Button) { + DumpWindows() + }) + + dump2 := addButton(vbox, "Dump Boxes") + dump2.OnClicked(func(*ui.Button) { + DumpBoxes() + }) + + dump1 := addButton(vbox, "Dump MAP") + dump1.OnClicked(func(*ui.Button) { + DumpMap() + }) + + ///////////////////////////////////////////////////// + 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, false) + + 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, true) + + vbox := ui.NewVerticalBox() + vbox.SetPadded(true) + group.SetChild(vbox) + + return vbox +} + +func dumpBox(s string) { + var name string + var window *GuiWindow + + for name, window = range Data.WindowMap { + if name != s { + continue + } + log.Println("gui.DumpBoxes() MAP: ", name) + if window.TabNumber == nil { + log.Println("gui.DumpBoxes() \tWindows.TabNumber = nil") + } else { + log.Println("gui.DumpBoxes() \tWindows.TabNumber =", *window.TabNumber) + } + 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) + log.Println("gui.DumpBoxes()\tWindow.UiTab =", window.UiTab) + log.Println("gui.dumpBox() BoxMap START") + for name, abox := range window.BoxMap { + log.Printf("gui.DumpBoxes() \tBOX mapname=%-12s abox.Name=%-12s", name, abox.Name) + abox.Dump() + if name == "MAINBOX" { + if Config.Debug { + scs := spew.ConfigState{MaxDepth: 1} + scs.Dump(abox.UiBox) + } + } + } + log.Println("gui.dumpBox() BoxMap END") + if window.UiTab != nil { + pages := window.UiTab.NumPages() + log.Println("gui.DumpBoxes()\tWindow.UiTab.NumPages() =", pages) + tabSetMargined(window.UiTab) + if Config.Debug { + scs := spew.ConfigState{MaxDepth: 2} + scs.Dump(window.UiTab) + } + } + } +} + +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, false) + 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 ec60df2..0b0baa4 100644 --- a/debug.go +++ b/debug.go @@ -1,26 +1,27 @@ 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) { + if Config.Debug { DumpBoxes() } count = 0 @@ -30,10 +31,26 @@ func WatchGUI() { } } +func DumpWindows() { + for name, _ := range Data.WindowMap { + log.Println("gui.DumpWindows() window =", name) + } +} + +func DumpMap() { + for name, window := range Data.WindowMap { + log.Println("gui.DumpBoxes() MAP: ", name) + log.Println("gui.DumpBoxes() BOXES:", name) + for name, abox := range window.BoxMap { + log.Printf("gui.DumpBoxes() \tBOX mapname=%-12s abox.Name=%-12s", name, abox.Name) + } + } +} + func DumpBoxes() { for name, window := range Data.WindowMap { log.Println("gui.DumpBoxes() MAP: ", name) - if (window.TabNumber == nil) { + if window.TabNumber == nil { log.Println("gui.DumpBoxes() \tWindows.TabNumber = nil") } else { log.Println("gui.DumpBoxes() \tWindows.TabNumber =", *window.TabNumber) @@ -41,16 +58,17 @@ func DumpBoxes() { 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) + log.Println("gui.DumpBoxes()\tWindow.UiTab =", window.UiTab) for name, abox := range window.BoxMap { log.Printf("gui.DumpBoxes() \tBOX mapname=%-12s abox.Name=%-12s", name, abox.Name) - if (name == "MAINBOX") { - if (Config.Debug) { + if name == "MAINBOX" { + if Config.Debug { scs := spew.ConfigState{MaxDepth: 1} scs.Dump(abox.UiBox) } } } - if (window.UiTab != nil) { + if window.UiTab != nil { // log.Println("gui.DumpBoxes()\tWindow.UiTab type =", reflect.TypeOf(window.UiTab)) // log.Println("gui.DumpBoxes()\tWindow.UiTab =", window.UiTab) pages := window.UiTab.NumPages() @@ -61,20 +79,20 @@ func DumpBoxes() { // tmp := spew.NewDefaultConfig() // tmp.MaxDepth = 2 // tmp.Dump(window.UiTab) - if (Config.Debug) { + if Config.Debug { scs := spew.ConfigState{MaxDepth: 2} scs.Dump(window.UiTab) } } } /* - for i, window := range Data.Windows { - if (window.TabNumber == nil) { - log.Println("gui.DumpBoxes() Data.Windows", i, "Name =", window.Name, "TabNumber = nil") - } else { - log.Println("gui.DumpBoxes() Data.Windows", i, "Name =", window.Name, "TabNumber =", *window.TabNumber) + for i, window := range Data.Windows { + if (window.TabNumber == nil) { + log.Println("gui.DumpBoxes() Data.Windows", i, "Name =", window.Name, "TabNumber = nil") + } else { + log.Println("gui.DumpBoxes() Data.Windows", i, "Name =", window.Name, "TabNumber =", *window.TabNumber) + } } - } */ } @@ -86,10 +104,57 @@ 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 1 second, then try to add new tabs") time.Sleep(1 * time.Second) } + +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 DebugDataNodeChildren() { + if Data.NodeMap == nil { + log.Println("DebugDataNodeChildren() NodeMap == nil") + return + } + log.Println("DebugDataNodeChildren():") + for name, node := range Data.NodeMap { + log.Println("\tNode name =", node.Width, node.Height, name) + if (node.children == nil) { + log.Println("\t\tNo children") + break + } + log.Println("\t\tHas children:", node.children) + } +} +*/ + +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 { + log.Println("\tgui.Data.ListChildren() node =", node.id, node.Width, node.Height, name) + if (dump == true) { + 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..16566c4 --- /dev/null +++ b/demo-window.go @@ -0,0 +1,28 @@ +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, true) + + vbox := ui.NewVerticalBox() + vbox.SetPadded(true) + group.SetChild(vbox) + + newNode := n.AddNode(title) + newNode.uiBox = vbox + return newNode +} diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..35c6a8f --- /dev/null +++ b/doc.go @@ -0,0 +1,81 @@ +/* +Package gui implements a abstraction layer for Go visual elements in +a cross platform way. + +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. + + // This creates a simple hello world window + package main + + import ( + "git.wit.org/wit/gui" + ) + + func main() { + gui.Main(initGUI) + } + + // This initializes the first window + func initGUI() { + gui.Config.Title = "Hello World golang wit/gui Window" + gui.Config.Width = 640 + gui.Config.Height = 480 + node1 := gui.NewWindow() + addDemoTab(node1, "A Simple Tab Demo") + addDemoTab(node1, "A Second Tab") + } + + 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 + +Toolkit Usage + +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, etc. + +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() + +*/ +package gui diff --git a/entry.go b/entry.go index db5013a..c10b7d0 100644 --- a/entry.go +++ b/entry.go @@ -10,29 +10,49 @@ 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) + box.Append(ui.NewLabel(text), false) } -func GetText(box *GuiBox, name string) string { - if (box == nil) { - log.Println("gui.GetText() ERROR box == nil") - return "" - } - if (box.Window.EntryMap == nil) { +func (n *Node) NewLabel(text string) *Node { + // make new node here + // n.Append(ui.NewLabel(text), false) + newNode := makeNode(n, text, 333, 334) + newNode.Dump() + // panic("node.NewLabel()") + + n.Append(newNode) + return newNode +} + +func (b *GuiBox) GetText(name string) string { + if (b.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) { + spew.Dump(b.Window.EntryMap) + if (b.Window.EntryMap[name] == nil) { log.Println("gui.GetText() ERROR box.Window.EntryMap[", name, "] == nil ") return "" } - e := box.Window.EntryMap[name] + e := b.Window.EntryMap[name] log.Println("gui.GetText() box.Window.EntryMap[", name, "] = ", e.UiEntry.Text()) log.Println("gui.GetText() END") return e.UiEntry.Text() } +func (n *Node) SetText(value string) error { + log.Println("gui.SetText() value =", value) + if (n.uiText != nil) { + n.uiText.SetText(value) + return nil + } + if (n.uiButton != nil) { + n.uiButton.SetText(value) + return nil + } + return nil +} + func SetText(box *GuiBox, name string, value string) error { if (box == nil) { return fmt.Errorf("gui.SetText() ERROR box == nil") @@ -42,7 +62,7 @@ func SetText(box *GuiBox, name string, value string) error { } spew.Dump(box.Window.EntryMap) if (box.Window.EntryMap[name] == nil) { - return fmt.Errorf("gui.SetText() ERROR box.Window.EntryMap[", name, "] == nil ") + return fmt.Errorf("gui.SetText() ERROR box.Window.EntryMap[" + name + "] == nil ") } e := box.Window.EntryMap[name] log.Println("gui.SetText() box.Window.EntryMap[", name, "] = ", e.UiEntry.Text()) 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 + * + * 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..e046161 --- /dev/null +++ b/find.go @@ -0,0 +1,156 @@ +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 (n *Node) FindBox() *GuiBox { + if (n.box != nil) { + return n.box + } + if (n.parent != nil) { + p := n.parent + return p.box + } + return n.box +} + +func (n *Node) FindWindowBox() *GuiBox { + if (n.box == nil) { + panic("SERIOUS ERROR n.box == nil in FindWindowBox()") + } + return n.box +} + +func (w *GuiWindow) FindNode() *Node { + return w.node +} + +func (b *GuiBox) FindNode() *Node { + log.Println("gui.FindNode() on GuiBox") + if b.node != nil { + return b.node + } + Data.ListChildren(true) + b.Dump() + log.Println("gui.FindNode() on GuiBox is nil") + os.Exit(-1) + return nil +} + +func FindWindow(s string) *GuiWindow { + for name, window := range Data.WindowMap { + if name == s { + return window + } + } + log.Printf("COULD NOT FIND WINDOW " + s) + return nil +} + +func FindBox(s string) *GuiBox { + for name, window := range Data.WindowMap { + if name != s { + continue + } + for name, abox := range window.BoxMap { + log.Printf("gui.DumpBoxes() \tBOX mapname=%-12s abox.Name=%-12s", name, abox.Name) + return abox + } + log.Println("gui.FindBox() NEED TO INIT WINDOW name =", name) + } + log.Println("gui.FindBox() COULD NOT FIND BOX", s) + return nil +} + +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..c72bbca --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module git.wit.org/wit/gui + +go 1.17 + +require ( + github.com/andlabs/ui v0.0.0-20200610043537-70a69d6ae31e + github.com/davecgh/go-spew v1.1.1 + golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..27ff5d2 --- /dev/null +++ b/go.sum @@ -0,0 +1,8 @@ +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-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/gui.go b/gui.go index 3bdd32e..4174133 100644 --- a/gui.go +++ b/gui.go @@ -1,11 +1,14 @@ 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" // the _ means we only need this for the init() + _ "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 @@ -13,13 +16,21 @@ const Yaxis = 1 // box that is vertical func init() { log.Println("gui.init() has been run") - Data.buttonMap = make(map[*ui.Button]*GuiButton) - Data.WindowMap = make(map[string]*GuiWindow) + Data.buttonMap = make(map[*ui.Button]*GuiButton) + Data.WindowMap = make(map[string]*GuiWindow) + Data.NodeMap = make(map[string]*Node) + + Data.NodeSlice = make([]*Node, 0) + + Config.counter = 0 + Config.prefix = "wit" + Config.DebugNode = false + Config.DebugTabs = false } 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..4f9989e --- /dev/null +++ b/new-structs.go @@ -0,0 +1,271 @@ +package gui + +import ( + "log" + "fmt" +// "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 + + parent *Node + children []*Node + + window *GuiWindow + box *GuiBox + custom func(*Node) + + uiControl *ui.Control + uiButton *ui.Button + uiWindow *ui.Window + uiTab *ui.Tab + uiBox *ui.Box + uiText *ui.EditableCombobox +} + +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) + + 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) + if (n.id == "") { + panic("gui.Node.Dump() id == nil") + } +} + + +func (n *Node) SetBox(box *GuiBox) { + n.box = box +} + +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 + +func indentPrintln(depth int, format string, a ...interface{}) { + var tabs string + for i := 0; i < depth; i++ { + tabs = tabs + "\t" + } + + // newFormat := tabs + strconv.Itoa(depth) + " " + format + newFormat := tabs + format + log.Println(newFormat, a) +} + +func (n *Node) ListChildren(dump bool) { + indentPrintln(listChildrenDepth, "\t", 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, b *GuiBox) *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("wit/gui/ AddTabNode() Something went wrong tab == nil") + // TODO: try to find the tab or window and make them if need be + return newNode + } + newNode.uiTab.Append(title, b.UiBox) + + return newNode +} + +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.box == nil { + parent.Dump() + panic("gui.AddTab() ERROR box == nil") + } + if parent.uiTab == nil { + inittab := ui.NewTab() // no, not that 'inittab' + parent.uiWindow.SetChild(inittab) + parent.uiWindow.SetMargined(true) + parent.uiTab = inittab + + // parent.Dump() + // panic("gui.AddTab() ERROR uiTab == nil") + } + + 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 + // panic("gui.AddTab() after makeNode()") + tabSetMargined(newNode.uiTab) + return newNode +} diff --git a/structs.go b/structs.go index 514febe..c15f4b7 100644 --- a/structs.go +++ b/structs.go @@ -1,37 +1,53 @@ 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 - Exit func(*GuiWindow) + Title string + Width int + Height int + 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(*GuiButton) // A map of all the entry boxes - AllEntries []*GuiEntry - WindowMap map[string]*GuiWindow + AllEntries []*GuiEntry + WindowMap map[string]*GuiWindow - // Windows []*GuiWindow + // Store access to everything via binary tree's + NodeMap map[string]*Node + NodeArray []*Node + NodeSlice []*Node // A map of all buttons everywhere on all // windows, all tabs, across all goroutines @@ -39,8 +55,14 @@ type GuiData struct { // // This has to work this way because of how // andlabs/ui & andlabs/libui work - AllButtons []*GuiButton - buttonMap map[*ui.Button]*GuiButton + AllButtons []*GuiButton + buttonMap map[*ui.Button]*GuiButton +} + +type GuiTab struct { + Name string // field for human readable name + Number int // the andlabs/ui tab index + Window *GuiWindow // the parent Window } // @@ -62,34 +84,97 @@ type GuiData struct { // can destroy and replace it with something else // type GuiWindow struct { - Name string // field for human readable name - Width int - Height int - Axis int // does it add items to the X or Y axis - TabNumber *int // the andlabs/ui tab index + Name string // field for human readable name + Width int + Height int + Axis int // does it add items to the X or Y axis + TabNumber *int // the andlabs/ui tab index // the callback function to make the window contents // MakeWindow func(*GuiBox) *GuiBox // the components of the window - BoxMap map[string]*GuiBox - EntryMap map[string]*GuiEntry - Area *GuiArea + BoxMap map[string]*GuiBox + EntryMap map[string]*GuiEntry + Area *GuiArea + + node *Node // andlabs/ui abstraction mapping - UiWindow *ui.Window - UiTab *ui.Tab // if this != nil, the window is 'tabbed' + UiWindow *ui.Window + UiTab *ui.Tab // if this != nil, the window is 'tabbed' +} + +func (w *GuiWindow) Dump() { + log.Println("gui.GuiWindow.Dump() Name = ", w.Name) + log.Println("gui.GuiWindow.Dump() node = ", w.node) + log.Println("gui.GuiWindow.Dump() Width = ", w.Width) + log.Println("gui.GuiWindow.Dump() Height = ", w.Height) } // 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 + Name string // field for human readable name + Axis int // does it add items to the X or Y axis + Window *GuiWindow // the parent Window + + node *Node // andlabs/ui abstraction mapping - UiBox *ui.Box + UiBox *ui.Box +} + +func (b *GuiBox) Dump() { + log.Println("gui.GuiBox.Dump() Name = ", b.Name) + log.Println("gui.GuiBox.Dump() Axis = ", b.Axis) + log.Println("gui.GuiBox.Dump() GuiWindow = ", b.Window) + log.Println("gui.GuiBox.Dump() node = ", b.node) + log.Println("gui.GuiBox.Dump() UiBox = ", b.UiBox) +} + +func (b *GuiBox) SetTitle(title string) { + log.Println("DID IT!", title) + if b.Window == nil { + return + } + if b.Window.UiWindow == nil { + return + } + b.Window.UiWindow.SetTitle(title) + return +} + +func (w *GuiWindow) SetNode(n *Node) { + if (w.node != nil) { + w.Dump() + panic("gui.SetNode() Error not nil") + } + w.node = n + if (w.node == nil) { + w.Dump() + panic("gui.SetNode() node == nil") + } +} + +func (b *GuiBox) SetNode(n *Node) { + if (b.node != nil) { + b.Dump() + panic("gui.SetNode() Error not nil") + } + b.node = n + if (b.node == nil) { + b.Dump() + panic("gui.SetNode() node == nil") + } +} + +func (b *GuiBox) Append(child ui.Control, x bool) { + if b.UiBox == nil { + panic("GuiBox.Append() can't work. UiBox == nil") + return + } + b.UiBox.Append(child, x) } // Note: every mouse click is handled @@ -97,32 +182,32 @@ type GuiBox struct { // 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 + 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 {} - Color color.RGBA + Custom func(*GuiButton) + Values interface{} + Color color.RGBA // andlabs/ui abstraction mapping - B *ui.Button - FB *ui.FontButton - CB *ui.ColorButton + B *ui.Button + FB *ui.FontButton + CB *ui.ColorButton } // 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 + B *GuiButton + Box *GuiBox // andlabs/ui abstraction mapping - UiEntry *ui.Entry + UiEntry *ui.Entry } // @@ -130,20 +215,21 @@ type GuiEntry struct { // AREA STRUCTURES START // AREA STRUCTURES START // -type GuiArea struct{ - Button *GuiButton // what button handles mouse events - Box *GuiBox +type GuiArea struct { + Button *GuiButton // what button handles mouse events + Box *GuiBox - 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 @@ -161,18 +247,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 + Box *GuiBox - lastRow int - lastColumn int + lastRow int + lastColumn int } // @@ -191,44 +277,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 + Button *GuiButton } 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 87e69b2..92ae871 100644 --- a/table.go +++ b/table.go @@ -98,12 +98,12 @@ func InitColumns(mh *TableData, parts []TableColumnData) { } func AddTableTab(gw *GuiWindow, name string, rowcount int, parts []TableColumnData) *TableData { - box := InitWindow(gw, name, Yaxis) - - return AddTableBox(box, name, rowcount, parts) + node := NewWindow() + b := node.box + return b.AddTableBox(name, rowcount, parts) } -func AddTableBox(box *GuiBox, name string, rowcount int, parts []TableColumnData) *TableData { +func (b *GuiBox) AddTableBox(name string, rowcount int, parts []TableColumnData) *TableData { mh := new(TableData) mh.RowCount = rowcount @@ -142,9 +142,9 @@ func AddTableBox(box *GuiBox, name string, rowcount int, parts []TableColumnData // is this needed? // gw.BoxMap[name] = box - mh.Box = box + mh.Box = b - box.UiBox.Append(table, true) + b.UiBox.Append(table, true) return mh } diff --git a/tableCallbacks.go b/tableCallbacks.go index 966f173..6eefd8d 100644 --- a/tableCallbacks.go +++ b/tableCallbacks.go @@ -30,7 +30,8 @@ func (mh *TableData) ColumnTypes(m *ui.TableModel) []ui.TableValue { } // 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) diff --git a/window.go b/window.go index 400a3f5..da7cff6 100644 --- a/window.go +++ b/window.go @@ -1,50 +1,13 @@ package gui -import "log" -import "time" -// import "regexp" +import ( + "log" + "fmt" + "strconv" -import "github.com/andlabs/ui" -import _ "github.com/andlabs/ui/winmanifest" - -func StartNewWindow(bg bool, name string, axis int, callback func(*GuiBox) *GuiBox) { - log.Println("StartNewWindow() Create a new window") - - if (bg) { - log.Println("StartNewWindow() START NEW GOROUTINE for ui.Main()") - go ui.Main(func() { - log.Println("gui.StartNewWindow() inside ui.Main() in NEW goroutine") - - // InitWindow must be called from within ui.Main() - box := InitWindow(nil, name, axis) - box = callback(box) - window := box.Window - log.Println("StartNewWindow() box =", box) - - runWindow(window.UiWindow) - }) - time.Sleep(500 * 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()") - - // InitWindow must be called from within ui.Main() - box := InitWindow(nil, name, axis) - box = callback(box) - window := box.Window - log.Println("StartNewWindow() box =", box) - - runWindow(window.UiWindow) - }) - } -} - -// This creates the raw andlabs/ui Window -func runWindow(uiWindow *ui.Window) { - log.Println("runWindow() START ui.Window.Show()") - uiWindow.Show() -} + "github.com/andlabs/ui" + _ "github.com/andlabs/ui/winmanifest" +) func MessageWindow(gw *GuiWindow, msg1 string, msg2 string) { ui.MsgBox(gw.UiWindow, msg1, msg2) @@ -54,91 +17,17 @@ func ErrorWindow(gw *GuiWindow, msg1 string, msg2 string) { ui.MsgBoxError(gw.UiWindow, msg1, msg2) } -// -// This creates a new 'window' (which is just a tab in the window) -// This is this way because on Linux you can have more than one -// actual window but that does not appear to work on the MacOS or Windows -// -func InitWindow(gw *GuiWindow, name string, axis int) *GuiBox { - window := Data.WindowMap[name] - if (window != nil) { - box := window.BoxMap["MAINBOX"] - log.Println("gui.InitWindow() tab already exists name =", name) - ErrorWindow(box.Window, "Create Window Error", "Window " + name + " already exists") - return nil - } - - log.Println("InitGuiWindow() START") - var newGuiWindow GuiWindow - newGuiWindow.Height = Config.Height - newGuiWindow.Width = Config.Width - newGuiWindow.Axis = axis - newGuiWindow.Name = name - - // This is the first window. One must create it here - if (gw == nil) { - log.Println("initWindow() ADDING ui.NewWindow()") - newGuiWindow.UiWindow = ui.NewWindow(name, int(newGuiWindow.Width), int(newGuiWindow.Height), true) - newGuiWindow.UiWindow.SetBorderless(false) - - newGuiWindow.UiWindow.OnClosing(func(*ui.Window) bool { - log.Println("initTabWindow() OnClosing() THIS WINDOW IS CLOSING newGuiWindow=", newGuiWindow) - // newGuiWindow.UiWindow.Destroy() - if (Config.Exit == nil) { - ui.Quit() - } else { - // allow a custom exit function - Config.Exit(&newGuiWindow) - } - return true - }) - - newGuiWindow.UiTab = ui.NewTab() - newGuiWindow.UiWindow.SetChild(newGuiWindow.UiTab) - newGuiWindow.UiWindow.SetMargined(true) - tmp := 0 - newGuiWindow.TabNumber = &tmp - } else { - newGuiWindow.UiWindow = gw.UiWindow - newGuiWindow.UiTab = gw.UiTab - } - - - newGuiWindow.BoxMap = make(map[string]*GuiBox) - newGuiWindow.EntryMap = make(map[string]*GuiEntry) - // Data.Windows = append(Data.Windows, &newGuiWindow) - - if (newGuiWindow.UiTab == nil) { - tabnum := 0 - newGuiWindow.TabNumber = &tabnum - } else { - tabnum := newGuiWindow.UiTab.NumPages() - newGuiWindow.TabNumber = &tabnum - } - - Data.WindowMap[newGuiWindow.Name] = &newGuiWindow - - var box *GuiBox - if (axis == Xaxis) { - box = HardBox(&newGuiWindow, Xaxis, name) - } else { - box = HardBox(&newGuiWindow, Yaxis, name) - } - log.Println("InitGuiWindow() END *GuiWindow =", &newGuiWindow) - return box -} - func DeleteWindow(name string) { log.Println("gui.DeleteWindow() START name =", name) window := Data.WindowMap[name] - if (window == nil) { + if window == nil { log.Println("gui.DeleteWindow() NO WINDOW WITH name =", name) return } log.Println("gui.DumpBoxes() MAP: ", name) log.Println("gui.DumpBoxes()\tWindow.name =", window.Name) - if (window.TabNumber == nil) { + if window.TabNumber == nil { log.Println("gui.DumpBoxes() \tWindows.TabNumber = nil") } tab := *window.TabNumber @@ -156,11 +45,11 @@ func DeleteWindow(name string) { // renumber tabs here for name, window := range Data.WindowMap { log.Println("gui.DumpBoxes() MAP: ", name) - if (window.TabNumber == nil) { + if window.TabNumber == nil { log.Println("gui.DumpBoxes() \tWindows.TabNumber = nil") } else { log.Println("gui.DumpBoxes() \tWindows.TabNumber =", *window.TabNumber) - if (tab < *window.TabNumber) { + if tab < *window.TabNumber { log.Println("gui.DumpBoxes() \tSubtracting 1 from TabNumber") *window.TabNumber -= 1 log.Println("gui.DumpBoxes() \tWindows.TabNumber is now =", *window.TabNumber) @@ -168,3 +57,159 @@ func DeleteWindow(name string) { } } } + +func makeNode(parent *Node, 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 + + // panic("gui.makeNode() START") + if (parent == nil) { + if (Data.NodeMap[title] != nil) { + log.Println("Duplicate window name =", title) + // TODO: just change the 'title' to something unique + panic(fmt.Sprintf("Duplicate window name = %s\n", title)) + return nil + } + // panic("gui.makeNode() before NodeMap()") + Data.NodeMap[title] = &node + Data.NodeArray = append(Data.NodeArray, &node) + Data.NodeSlice = append(Data.NodeSlice, &node) + // panic("gui.makeNode() after NodeMap()") + return &node + } else { + // panic("gui.makeNode() before Append()") + parent.Append(&node) + // panic("gui.makeNode() after Append()") + } + node.parent = parent + return &node +} + +func (parent *Node) makeNode(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 + + 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, false) + w.SetBorderless(false) + f := Config.Exit + w.OnClosing(func(*ui.Window) bool { + if (Config.Debug) { + log.Println("ui.Window().OnClosing()") + } + if (f != nil) { + f(n) + } + return true + }) + w.SetMargined(true) + w.Show() + n.uiWindow = w + // w.node = &node + return +} + +func mapWindow(parent *Node, window *ui.Window, title string, x int, y int) *Node { + log.Println("gui.WindowMap START title =", title) + if Data.WindowMap[title] != nil { + log.Println("Data.WindowMap[title] already exists title =", title) + title = title + Config.prefix + strconv.Itoa(Config.counter) + Config.counter += 1 + } + if Data.WindowMap[title] != nil { + log.Println("Data.WindowMap[title] already exists title =", title) + panic("Data.WindowMap[newGuiWindow.Name] already exists") + return nil + } + + var newGuiWindow GuiWindow + newGuiWindow.Width = x + newGuiWindow.Height = y + newGuiWindow.Name = title + newGuiWindow.UiWindow = window + + newGuiWindow.BoxMap = make(map[string]*GuiBox) + newGuiWindow.EntryMap = make(map[string]*GuiEntry) + + Data.WindowMap[newGuiWindow.Name] = &newGuiWindow + + var box GuiBox + box.Window = &newGuiWindow + box.Name = title + + node := makeNode(parent, title, x, y) + node.box = &box + node.uiWindow = window + box.node = node + + newGuiWindow.BoxMap["jcarrInitTest"] = &box + + return node +} + +// 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). +func NewWindow() *Node { + title := Config.Title + w := Config.Width + h := Config.Height + + var n *Node + n = mapWindow(nil, nil, title, w, h) + box := n.box + log.Println("gui.NewWindow() title = box.Name =", box.Name) + + n.uiNewWindow(box.Name, 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 + }) + + box.Window.UiWindow = window + if(n.uiWindow == nil) { + panic("node.uiWindow == nil. This should never happen") + } + return n +}