diff options
| author | Jeff Carr <[email protected]> | 2022-10-08 23:22:47 -0500 |
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2022-10-08 23:22:47 -0500 |
| commit | 086986b6b8f55d15d18595bcbf3f76c023365b61 (patch) | |
| tree | 9f1dc4da5b2d125f7a017418d4d9912e16b71039 | |
| parent | f92a50e2e665a18e2201f1d6714025dcc39301cc (diff) | |
| parent | 45644ef9bc333f5def62d1c7f474dc96274e63fa (diff) | |
Merge branch 'master' into jcarr
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Makefile | 20 | ||||
| -rw-r--r-- | area.go | 58 | ||||
| -rw-r--r-- | box.go | 139 | ||||
| -rw-r--r-- | button.go | 108 | ||||
| -rw-r--r-- | cmds/gui-demo/Makefile | 5 | ||||
| -rw-r--r-- | cmds/gui-demo/demo-window.go | 33 | ||||
| -rw-r--r-- | cmds/gui-demo/main.go | 68 | ||||
| -rw-r--r-- | cmds/gui-example/Makefile | 5 | ||||
| -rw-r--r-- | cmds/gui-example/demo-window.go | 105 | ||||
| -rw-r--r-- | cmds/gui-example/main.go | 68 | ||||
| -rw-r--r-- | cmds/gui-example/os.go | 99 | ||||
| -rw-r--r-- | color.go | 31 | ||||
| -rw-r--r-- | debug-window.go | 193 | ||||
| -rw-r--r-- | debug.go | 113 | ||||
| -rw-r--r-- | demo-window-andlabs-ui.go | 84 | ||||
| -rw-r--r-- | demo-window.go | 92 | ||||
| -rw-r--r-- | doc.go | 69 | ||||
| -rw-r--r-- | entry.go | 112 | ||||
| -rw-r--r-- | example_test.go | 45 | ||||
| -rw-r--r-- | find.go | 97 | ||||
| -rw-r--r-- | go.mod | 10 | ||||
| -rw-r--r-- | go.sum | 10 | ||||
| -rw-r--r-- | gui.go | 31 | ||||
| -rw-r--r-- | main.go | 34 | ||||
| -rw-r--r-- | new-structs.go | 318 | ||||
| -rw-r--r-- | structs.go | 230 | ||||
| -rw-r--r-- | table.go | 30 | ||||
| -rw-r--r-- | tableCallbacks.go | 24 | ||||
| -rw-r--r-- | window.go | 206 |
30 files changed, 1815 insertions, 624 deletions
@@ -1 +1,3 @@ *.swp +cmds/gui-example/gui-example +cmds/gui-demo/gui-demo @@ -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 @@ -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) } @@ -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() } @@ -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) + } +} @@ -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 +} @@ -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 @@ -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 +} @@ -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 +} @@ -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 +) @@ -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= @@ -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 }) } @@ -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 +} @@ -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 } // @@ -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") @@ -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 } |
