commit b25f15ea7803e172204432082740d081e5f19f81 Author: Jeff Carr Date: Wed Jan 17 23:54:19 2024 -0600 the golang way. everything in it's own repo Signed-off-by: Jeff Carr diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..331617a --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +all: plugin + +plugin: + GO111MODULE="off" go build -v -x -buildmode=plugin -o ../andlabs.so + +goget: + GO111MODULE="off" go get -v -t -u + +redomod: + rm -f go.* + GO111MODULE= go mod init + GO111MODULE= go mod tidy + diff --git a/action.go b/action.go new file mode 100644 index 0000000..c089078 --- /dev/null +++ b/action.go @@ -0,0 +1,310 @@ +package main + +import ( + "errors" + + "go.wit.com/lib/widget" + "go.wit.com/log" + "go.wit.com/toolkits/tree" +) + +// this will check to make sure that the node +// is valid for making a New TK andlabs widget +// Basically, it makes sure there is a parent ID +// and that there already a widget created +func notNew(n *tree.Node) bool { + if n == nil { + log.Warn("ready() n = nil") + return true + } + if n.TK != nil { + log.Warn("ready() n.TK = nil", n.WidgetId, n.GetProgName()) + return true + } + if n.Parent == nil { + log.Warn("ready() n.Parent = nil", n.WidgetId, n.GetProgName()) + return true + } + if n.Parent.TK == nil { + log.Warn("ready() n.Parent.TK = nil", n.WidgetId, n.GetProgName()) + log.Warn("ready() n.Parent.TK = nil", n.Parent.WidgetId, n.Parent.GetProgName()) + return true + } + // this means you can add a new widgets + return false +} + +func ready(n *tree.Node) bool { + if n == nil { + log.Warn("ready() n = nil") + return false + } + if n.TK == nil { + log.Warn("ready() n.TK = nil", n.WidgetId, n.GetProgName()) + return false + } + if n.Parent == nil { + log.Warn("ready() n.Parent = nil", n.WidgetId, n.GetProgName()) + return false + } + if n.Parent.TK == nil { + log.Warn("ready() n.Parent.TK = nil", n.WidgetId, n.GetProgName()) + log.Warn("ready() n.Parent.TK = nil", n.Parent.WidgetId, n.Parent.GetProgName()) + return false + } + return true +} +func (n *node) ready() bool { + if n == nil { + return false + } + if n.tk == nil { + return false + } + return true +} + +func show(n *tree.Node, b bool) { + var tk *guiWidget + tk = n.TK.(*guiWidget) + // tk = getTK(n) + + if tk == nil { + return + } + if tk.uiControl == nil { + return + } + if b { + tk.uiControl.Show() + } else { + tk.uiControl.Hide() + } +} + +func enable(n *tree.Node, b bool) { + var tk *guiWidget + tk = n.TK.(*guiWidget) + if n == nil { + panic("WHAT? enable was passed nil. How does this even happen?") + } + if tk == nil { + return + } + if tk.uiControl == nil { + return + } + if b { + tk.uiControl.Enable() + } else { + tk.uiControl.Disable() + } +} + +func (n *node) pad(b bool) { + log.Warn("pad() on WidgetId =", n.WidgetId) + + t := n.tk + if t == nil { + log.Log(ERROR, "pad() toolkit struct == nil. for", n.WidgetId) + return + } + + switch n.WidgetType { + case widget.Window: + t.uiWindow.SetMargined(b) + t.uiWindow.SetBorderless(b) + case widget.Tab: + tabSetMargined(t.uiTab, b) + case widget.Group: + t.uiGroup.SetMargined(b) + case widget.Grid: + t.uiGrid.SetPadded(b) + case widget.Box: + t.uiBox.SetPadded(b) + default: + log.Log(ERROR, "TODO: implement pad() for", n.WidgetType, n.progname) + } +} + +func (n *node) move(newParent *node) { + p := n.parent + + switch p.WidgetType { + case widget.Group: + case widget.Tab: + // tabSetMargined(tParent.uiTab, true) + case widget.Window: + // t.uiWindow.SetBorderless(false) + case widget.Grid: + // t.uiGrid.SetPadded(true) + case widget.Box: + log.Log(INFO, "TODO: move() where =", p.ParentId) + log.Log(INFO, "TODO: move() for widget =", n.WidgetId) + + stretchy = true + if p.tk.uiBox != nil { + p.tk.uiBox.Append(n.tk.uiControl, stretchy) + } + default: + log.Log(ERROR, "TODO: need to implement move() for type =", n.WidgetType) + log.Log(ERROR, "TODO: need to implement move() for where =", p.ParentId) + log.Log(ERROR, "TODO: need to implement move() for widget =", n.WidgetId) + } +} + +func (n *node) Delete() { + p := n.parent + log.Log(NOW, "uiDelete()", n.WidgetId, "to", p.WidgetId) + + if n.WidgetType == widget.Window { + log.Warn("DESTROY uiWindow here") + log.Warn("NEED TO REMOVE n from parent.Children") + n.tk.uiWindow.Destroy() + n.tk.uiWindow = nil + for i, child := range p.children { + log.Warn("parent has child:", i, child.WidgetId, child.progname) + if n == child { + log.Warn("Found child ==", i, child.WidgetId, child.progname) + log.Warn("Found n ==", i, n.WidgetId, n.progname) + p.children = append(p.children[:i], p.children[i+1:]...) + } + // t.uiWindow.SetBorderless(false) + } + for i, child := range p.children { + log.Warn("parent now has child:", i, child.WidgetId, child.progname) + } + return + } + + switch p.WidgetType { + case widget.Group: + // tParent.uiGroup.SetMargined(true) + case widget.Tab: + // tabSetMargined(tParent.uiTab, true) + case widget.Window: + case widget.Grid: + // t.uiGrid.SetPadded(true) + case widget.Box: + log.Log(NOW, "tWidget.boxC =", p.progname) + log.Log(NOW, "is there a tParent parent? =", p.parent) + if p.tk.boxC < 1 { + log.Log(NOW, "Can not delete from Box. already empty. tWidget.boxC =", p.tk.boxC) + return + } + p.tk.uiBox.Delete(0) + p.tk.boxC -= 1 + + // this didn't work: + // tWidget.uiControl.Disable() + // sleep(.8) + // tParent.uiBox.Append(tWidget.uiControl, stretchy) + default: + log.Log(ERROR, "TODO: need to implement uiDelete() for widget =", n.WidgetId, n.WidgetType) + log.Log(ERROR, "TODO: need to implement uiDelete() for parent =", p.WidgetId, p.WidgetType) + } +} + +func rawAction(a *widget.Action) { + log.Log(INFO, "rawAction() START a.ActionType =", a.ActionType, "a.Value", a.Value) + + if a.ActionType == widget.ToolkitInit { + Init() + return + } + switch a.WidgetType { + case widget.Root: + me.treeRoot = me.myTree.AddNode(a) + log.Log(INFO, "doAction() found treeRoot") + return + } + + log.Warn("andlabs rawAction() START a.WidgetId =", a.WidgetId, "a.ParentId =", a.ParentId, a.ActionType) + switch a.WidgetType { + case widget.Flag: + log.Log(ERROR, "rawAction() RE-IMPLEMENT LOG FLAGS") + return + } + + if me.treeRoot == nil { + panic("me.treeRoot == nil") + } + + n := me.treeRoot.FindWidgetId(a.WidgetId) + + if a.ActionType == widget.Add { + me.treeRoot.ListWidgets() + // ui.QueueMain(func() { + add(a) + // }) + // TODO: remove this artificial delay + // sleep(.001) + return + } + + if a.ActionType == widget.Dump { + log.Log(NOW, "rawAction() Dump =", a.ActionType, a.WidgetType, n.State.ProgName) + // me.rootNode.listChildren(true) + return + } + + if n == nil { + log.Error(errors.New("andlabs rawAction() ERROR findWidgetId found nil"), a.ActionType, a.WidgetType) + log.Log(NOW, "rawAction() ERROR findWidgetId found nil for id =", a.WidgetId) + log.Log(NOW, "rawAction() ERROR findWidgetId found nil", a.ActionType, a.WidgetType) + log.Log(NOW, "rawAction() ERROR findWidgetId found nil for id =", a.WidgetId) + me.treeRoot.ListWidgets() + return + panic("findWidgetId found nil for id = " + string(a.WidgetId)) + } + + switch a.ActionType { + case widget.Show: + show(n, true) + // n.show(true) + case widget.Hide: + show(n, false) + //n.show(false) + case widget.Enable: + enable(n, true) + // n.enable(true) + case widget.Disable: + log.Warn("andlabs got disable for", n.WidgetId, n.State.ProgName) + enable(n, false) + // n.enable(false) + case widget.Get: + // n.setText(a) + setText(n, a) + case widget.GetText: + switch a.WidgetType { + case widget.Textbox: + a.Value = n.State.Value + } + case widget.Set: + setText(n, a) + // n.setText(a) + case widget.SetText: + setText(n, a) + // n.setText(a) + case widget.AddText: + addText(n, a) + // n.addText(a) + /* + case widget.Margin: + n.pad(true) + case widget.Unmargin: + n.pad(false) + case widget.Pad: + n.pad(true) + case widget.Unpad: + n.pad(false) + case widget.Delete: + n.Delete() + case widget.Move: + log.Log(NOW, "rawAction() attempt to move() =", a.ActionType, a.WidgetType) + */ + default: + log.Log(ERROR, "rawAction() Unknown =", a.ActionType, a.WidgetType) + } + log.Log(INFO, "rawAction() END =", a.ActionType, a.WidgetType) +} diff --git a/add.go b/add.go new file mode 100644 index 0000000..c948b88 --- /dev/null +++ b/add.go @@ -0,0 +1,72 @@ +package main + +import ( + "go.wit.com/lib/widget" + "go.wit.com/log" +) + +func add(a *widget.Action) { + log.Warn("andlabs add()", a.WidgetId, a.State.ProgName) + if a.WidgetType == widget.Root { + if me.treeRoot == nil { + me.treeRoot = me.myTree.AddNode(a) + } + return + } + // n := addNode(a) + n := me.myTree.AddNode(a) + + p := n.Parent + switch n.WidgetType { + case widget.Window: + log.Warn("SPEEDY Add window", n.WidgetId, n.GetProgName()) + newWindow(p, n) + return + case widget.Group: + log.Warn("SPEEDY Add Group", n.WidgetId, n.GetProgName()) + newGroup(p, n) + return + case widget.Grid: + newGrid(n) + return + case widget.Box: + newBox(n) + return + /* + case widget.Tab: + newTab(n) + return + */ + case widget.Label: + newLabel(p, n) + return + case widget.Button: + newButton(p, n) + return + case widget.Checkbox: + newCheckbox(p, n) + return + case widget.Spinner: + newSpinner(p, n) + return + case widget.Slider: + newSlider(p, n) + return + case widget.Dropdown: + newDropdown(p, n) + return + case widget.Combobox: + newCombobox(p, n) + return + case widget.Textbox: + newTextbox(p, n) + return + /* + case widget.Image: + newImage(p, n) + return + */ + default: + log.Log(ERROR, "add() error TODO: ", n.WidgetType, n.State.ProgName) + } +} diff --git a/addText.go b/addText.go new file mode 100644 index 0000000..e5c1483 --- /dev/null +++ b/addText.go @@ -0,0 +1,44 @@ +package main + +import ( + "go.wit.com/lib/widget" + "go.wit.com/log" + "go.wit.com/toolkits/tree" +) + +func compareStrings(n *tree.Node, ss []string) { +} + +// func (n *node) addText(a *widget.Action) { +func addText(n *tree.Node, a *widget.Action) { + var tk *guiWidget + tk = n.TK.(*guiWidget) + log.Warn("andlabs addText() START with a.Value =", a.Value) + if tk == nil { + log.Log(ERROR, "addText error. tk == nil", n.State.ProgName, n.WidgetId) + return + } + log.Warn("andlabs addText() Attempt on", n.WidgetType, "with", a.Value) + + switch n.WidgetType { + case widget.Dropdown: + for i, s := range a.State.Strings { + log.Warn("andlabs a.State.Strings =", i, s) + _, ok := n.Strings[s] + // If the key exists + if ok { + log.Warn("andlabs a.State.Strings is here", i, s) + } else { + log.Warn("andlabs is not here", i, s) + addDropdownName(n, s) + // TODO: make numbers + n.Strings[s] = 21 + } + } + case widget.Combobox: + addComboboxName(n, widget.GetString(a.Value)) + default: + log.Log(ERROR, "plugin Send() Don't know how to addText on", n.WidgetType, "yet", a.ActionType) + } + log.Log(CHANGE, "addText() END with a.Value =", a.Value) +} diff --git a/box.go b/box.go new file mode 100644 index 0000000..f2277f4 --- /dev/null +++ b/box.go @@ -0,0 +1,65 @@ +package main + +import ( + "go.wit.com/lib/widget" + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +// make new Box here +func newBox(n *tree.Node) { + if notNew(n) { + return + } + newt := new(guiWidget) + var box *ui.Box + + if n.State.Direction == widget.Horizontal { + box = ui.NewHorizontalBox() + } else { + box = ui.NewVerticalBox() + } + box.SetPadded(true) + + newt.uiBox = box + newt.uiControl = box + newt.boxC = 0 + n.TK = newt + place(n.Parent, n) +} + +/* + rawBox -- hack to arbitrarily add a box in andlabs to work + around the problem that a "group" can not have one entry in it + TODO: fix this so that a box is "added on demand" that is, + if "go.wit.com/gui/gui" sends you a 2nd thing to add to a group, + automatically add a box then. The problem with this, is the macos, windows and linux gtk + will panic on a move when an chind object is disasociated from the group + I haven't had time to try to debug this, so, instead, it's just probably better to always + add a box here. There doesn't seem to be any real issue with forcing a box to be inserted + into the toolkits that is "outside" the binary tree of widgets. This only means, that on + a destroy of the tree, this box must be checked + + even that is a probably not senario however since clicking on the close box in the toolkit + has the operating system destroy everything in the window. it may or may not be possible + to control that behavior. at this time, it's "undetermined" and the best course of action + is to detect the window is destroyed and then remove all the toolkit information + from all the nodes in the binary tree + + TODO: handle user killing/closing a window using the OS +*/ +// func (n *node) rawBox() *ui.Box { +func rawBox(n *tree.Node) *ui.Box { + var box *ui.Box + + if n.State.Direction == widget.Horizontal { + box = ui.NewHorizontalBox() + } else { + box = ui.NewVerticalBox() + } + box.SetPadded(true) + + return box +} diff --git a/button.go b/button.go new file mode 100644 index 0000000..20a6541 --- /dev/null +++ b/button.go @@ -0,0 +1,30 @@ +package main + +import ( + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +// func (p *node) newButton(n *node) { +func newButton(p *tree.Node, n *tree.Node) { + if notNew(n) { + return + } + var ptk *guiWidget + ptk = p.TK.(*guiWidget) + newt := new(guiWidget) + + b := ui.NewButton(n.GetLabel()) + newt.uiButton = b + newt.uiControl = b + newt.parent = ptk + + b.OnClicked(func(*ui.Button) { + me.myTree.DoUserEvent(n) + }) + + n.TK = newt + place(p, n) +} diff --git a/checkbox.go b/checkbox.go new file mode 100644 index 0000000..221edb4 --- /dev/null +++ b/checkbox.go @@ -0,0 +1,30 @@ +package main + +import ( + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +func newCheckbox(p *tree.Node, n *tree.Node) { + if notNew(n) { + return + } + newt := new(guiWidget) + + newt.uiCheckbox = ui.NewCheckbox(n.GetLabel()) + newt.uiControl = newt.uiCheckbox + + newt.uiCheckbox.OnToggled(func(spin *ui.Checkbox) { + n.SetValue(newt.checked()) + me.myTree.DoUserEvent(n) + }) + + n.TK = newt + place(p, n) +} + +func (t *guiWidget) checked() bool { + return t.uiCheckbox.Checked() +} diff --git a/combobox.go b/combobox.go new file mode 100644 index 0000000..a2cf4c9 --- /dev/null +++ b/combobox.go @@ -0,0 +1,79 @@ +package main + +import ( + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" + + "go.wit.com/log" + "go.wit.com/toolkits/tree" +) + +func newCombobox(p, n *tree.Node) { + if notNew(n) { + return + } + newt := new(guiWidget) + + cb := ui.NewEditableCombobox() + newt.uiEditableCombobox = cb + newt.uiControl = cb + + // initialize the index + newt.c = 0 + newt.val = make(map[int]string) + + cb.OnChanged(func(spin *ui.EditableCombobox) { + n.SetValue(spin.Text()) + log.Warn("combobox changed =" + spin.Text() + ".") + me.myTree.DoUserEvent(n) + }) + + n.TK = newt + place(p, n) + + log.Warn("add combobox entries on create:", n.State.Strings) + log.Warn("add combobox entries on create:", n.State.Strings) + log.Warn("add combobox entries on create:", n.State.Strings) + // add the initial combobox entries + for i, s := range n.State.Strings { + log.Warn("add combobox entries on create", n.GetProgName(), i, s) + addComboboxName(n, s) + } + cur := n.String() + log.Warn("add combobox: TODO: set default value on create", n.GetProgName(), cur) + setComboboxName(n, cur) +} + +func addComboboxName(n *tree.Node, s string) { + if !ready(n) { + return + } + var tk *guiWidget + tk = n.TK.(*guiWidget) + log.Log(INFO, "addComboboxName()", n.WidgetId, "add:", s) + + tk.uiEditableCombobox.Append(s) + if tk.val == nil { + log.Log(INFO, "make map didn't work") + return + } + tk.val[tk.c] = s + + // If this is the first menu added, set the dropdown to it + if tk.c == 0 { + log.Log(INFO, "THIS IS THE FIRST combobox", s) + tk.uiEditableCombobox.SetText(s) + } + tk.c = tk.c + 1 +} + +func setComboboxName(n *tree.Node, s string) bool { + if !ready(n) { + return false + } + var tk *guiWidget + tk = n.TK.(*guiWidget) + log.Log(INFO, "SetComboboxName()", n.WidgetId, ",", s) + tk.uiEditableCombobox.SetText(s) + return false +} diff --git a/common.go b/common.go new file mode 100644 index 0000000..ea981a9 --- /dev/null +++ b/common.go @@ -0,0 +1,61 @@ +package main + +import ( + "go.wit.com/lib/widget" +) + +type node struct { + parent *node + children []*node + + WidgetId int // widget ID + WidgetType widget.WidgetType + ParentId int // parent ID + + state widget.State + + // a reference name for programming and debuggign. Must be unique + progname string + + // the text used for button labesl, window titles, checkbox names, etc + label string + + // horizontal means layout widgets like books on a bookshelf + // vertical means layout widgets like books in a stack + // direction widget.Orientation + direction widget.Orientation + + // This is how the values are passed back and forth + // values from things like checkboxes & dropdown's + value any + + strings []string + + // This is used for things like a slider(0,100) + X int + Y int + + // This is for the grid size & widget position + W int + H int + AtW int + AtH int + + vals []string // dropdown menu items + + // horizontal bool `default:false` + + hasTabs bool // does the window have tabs? + currentTab bool // the visible tab + + // the internal plugin toolkit structure + // in the gtk plugin, it has gtk things like margin & border settings + // in the text console one, it has text console things like colors for menus & buttons + tk *guiWidget +} + +/* +func (n *node) doUserEvent() { + log.Log(ERROR, "doUserEvent() ERROR") +} +*/ diff --git a/debug.go b/debug.go new file mode 100644 index 0000000..f974783 --- /dev/null +++ b/debug.go @@ -0,0 +1,121 @@ +package main + +import ( + "go.wit.com/log" + // "go.wit.com/gui/widget" +) + +var defaultBehavior bool = true + +var bookshelf bool // do you want things arranged in the box like a bookshelf or a stack? +var canvas bool // if set to true, the windows are a raw canvas +var menubar bool // for windows +var stretchy bool // expand things like buttons to the maximum size +var padded bool // add space between things like buttons +var margin bool // add space around the frames of windows + +var debugToolkit bool = false +var debugChange bool = false +var debugPlugin bool = false +var debugAction bool = false +var debugFlags bool = false +var debugGrid bool = false +var debugNow bool = true +var debugError bool = true + +// This is important. This sets the defaults for the gui. Without this, there isn't correct padding, etc +func setDefaultBehavior(s bool) { + defaultBehavior = s + if defaultBehavior { + log.Log(NOW, "Setting this toolkit to use the default behavior.") + log.Log(NOW, "This is the 'guessing' part as defined by the wit/gui 'Principles'. Refer to the docs.") + stretchy = false + padded = true + menubar = true + margin = true + canvas = false + bookshelf = true // 99% of the time, things make a vertical stack of objects + } else { + log.Log(NOW, "This toolkit is set to ignore the default behavior.") + } +} + +func (t *guiWidget) Dump(b bool) { + if !b { + return + } + log.Log(NOW, "Name = ", t.Width, t.Height) + if t.uiBox != nil { + log.Log(NOW, "uiBox =", t.uiBox) + } + if t.uiButton != nil { + log.Log(NOW, "uiButton =", t.uiButton) + } + if t.uiCombobox != nil { + log.Log(NOW, "uiCombobox =", t.uiCombobox) + } + if t.uiWindow != nil { + log.Log(NOW, "uiWindow =", t.uiWindow) + } + if t.uiTab != nil { + log.Log(NOW, "uiTab =", t.uiTab) + } + if t.uiGroup != nil { + log.Log(NOW, "uiGroup =", t.uiGroup) + } + if t.uiEntry != nil { + log.Log(NOW, "uiEntry =", t.uiEntry) + } + if t.uiMultilineEntry != nil { + log.Log(NOW, "uiMultilineEntry =", t.uiMultilineEntry) + } + if t.uiSlider != nil { + log.Log(NOW, "uiSlider =", t.uiSlider) + } + if t.uiCheckbox != nil { + log.Log(NOW, "uiCheckbox =", t.uiCheckbox) + } +} + +/* +func GetDebugToolkit () bool { + return debugToolkit +} +*/ + +func (n *node) dumpWidget(b bool) { + var info, d string + + if n == nil { + log.Log(ERROR, "dumpWidget() node == nil") + return + } + info = n.WidgetType.String() + + d = string(n.WidgetId) + " " + info + " " + n.progname + + var tabs string + for i := 0; i < listChildrenDepth; i++ { + tabs = tabs + defaultPadding + } + log.Log(NOW, tabs+d) +} + +var defaultPadding string = " " +var listChildrenDepth int = 0 + +func (n *node) listChildren(dump bool) { + if n == nil { + return + } + + n.dumpWidget(dump) + if len(n.children) == 0 { + return + } + for _, child := range n.children { + listChildrenDepth += 1 + child.listChildren(dump) + listChildrenDepth -= 1 + } +} diff --git a/delete.go b/delete.go new file mode 100644 index 0000000..7d29bf2 --- /dev/null +++ b/delete.go @@ -0,0 +1,54 @@ +package main + +// if you include more than just this import +// then your plugin might be doing something un-ideal (just a guess from 2023/02/27) +import ( + "go.wit.com/lib/widget" + "go.wit.com/log" +) + +// delete the child widget from the parent +// p = parent, c = child +func (n *node) destroy() { + pId := n.parent.WidgetId + cId := n.WidgetId + log.Log(NOW, "delete()", pId, cId) + + pt := n.parent.tk + ct := n.tk + if ct == nil { + log.Log(NOW, "delete FAILED (ct = mapToolkit[c] == nil) for c", pId, cId) + // this pukes out a whole universe of shit + // listMap() + return + } + + switch n.WidgetType { + case widget.Button: + log.Log(NOW, "Should delete Button here:", n.progname) + log.Log(NOW, "Parent:") + pt.Dump(true) + log.Log(NOW, "Child:") + ct.Dump(true) + if pt.uiBox == nil { + log.Log(NOW, "Don't know how to destroy this") + } else { + log.Log(NOW, "Fuck it, destroy the whole box", n.parent.progname) + // pt.uiBox.Destroy() // You have a bug: You cannot destroy a uiControl while it still has a parent. + pt.uiBox.SetPadded(false) + pt.uiBox.Delete(4) + ct.uiButton.Disable() + // ct.uiButton.Hide() + ct.uiButton.Destroy() + } + + case widget.Window: + log.Log(NOW, "Should delete Window here:", n.progname) + default: + log.Log(NOW, "Fuckit, let's destroy a button") + if ct.uiButton != nil { + pt.uiBox.Delete(4) + ct.uiButton.Destroy() + } + } +} diff --git a/demo.go b/demo.go new file mode 100644 index 0000000..1b94056 --- /dev/null +++ b/demo.go @@ -0,0 +1,94 @@ +package main + +import ( + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" + + "go.wit.com/log" +) + +/* + This is a code example taken directly from the toolkit andlabs/ui + + This code is here to double check that the toolkit itself still works + the same way. This is intended as a sanity check. +*/ + +func BlankWindow(w *ui.Window) *ui.Box { + hbox := ui.NewHorizontalBox() + hbox.SetPadded(true) + w.SetChild(hbox) + return hbox +} + +func (t *guiWidget) DemoNumbersPage() { + var w *ui.Window + + log.Log(NOW, "Starting wit/gui toolkit andlabs/ui DemoNumbersPage()") + + w = t.uiWindow + t.uiBox = makeNumbersPage() + t.uiBox.SetPadded(true) + w.SetChild(t.uiBox) + w.SetTitle("Internal demo of andlabs/ui toolkit") +} + +func makeNumbersPage() *ui.Box { + hbox := ui.NewHorizontalBox() + hbox.SetPadded(true) + + group := ui.NewGroup("Numbers") + group.SetMargined(true) + hbox.Append(group, true) + + vbox := ui.NewVerticalBox() + vbox.SetPadded(true) + group.SetChild(vbox) + + spinbox := ui.NewSpinbox(0, 100) + slider := ui.NewSlider(0, 100) + pbar := ui.NewProgressBar() + spinbox.OnChanged(func(*ui.Spinbox) { + slider.SetValue(spinbox.Value()) + pbar.SetValue(spinbox.Value()) + }) + slider.OnChanged(func(*ui.Slider) { + spinbox.SetValue(slider.Value()) + pbar.SetValue(slider.Value()) + }) + vbox.Append(spinbox, false) + vbox.Append(slider, false) + vbox.Append(pbar, false) + + ip := ui.NewProgressBar() + ip.SetValue(-1) + vbox.Append(ip, false) + + group = ui.NewGroup("Lists") + group.SetMargined(true) + hbox.Append(group, true) + + vbox = ui.NewVerticalBox() + vbox.SetPadded(true) + group.SetChild(vbox) + + cbox := ui.NewCombobox() + cbox.Append("Combobox Item 1") + cbox.Append("Combobox Item 2") + cbox.Append("Combobox Item 3") + vbox.Append(cbox, false) + + ecbox := ui.NewEditableCombobox() + ecbox.Append("Editable Item 1") + ecbox.Append("Editable Item 2") + ecbox.Append("Editable Item 3") + vbox.Append(ecbox, false) + + rb := ui.NewRadioButtons() + rb.Append("Radio Button 1") + rb.Append("Radio Button 2") + rb.Append("Radio Button 3") + vbox.Append(rb, false) + + return hbox +} diff --git a/dropdown.go b/dropdown.go new file mode 100644 index 0000000..cda0026 --- /dev/null +++ b/dropdown.go @@ -0,0 +1,106 @@ +package main + +import ( + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" + + "go.wit.com/log" + "go.wit.com/toolkits/tree" +) + +func newDropdown(p, n *tree.Node) { + if notNew(n) { + return + } + newt := new(guiWidget) + log.Log(INFO, "gui.Toolbox.newDropdown() START", n.GetProgName()) + + cb := ui.NewCombobox() + newt.uiCombobox = cb + newt.uiControl = cb + + // initialize the index + newt.c = 0 + newt.val = make(map[int]string) + + cb.OnSelected(func(spin *ui.Combobox) { + i := spin.Selected() + if newt.val == nil { + log.Log(ERROR, "make map didn't work") + n.SetValue("map did not work. ui.Combobox error") + } else { + n.SetValue(newt.val[i]) + } + me.myTree.DoUserEvent(n) + }) + + n.TK = newt + place(p, n) + + log.Warn("add dropdown entries on create:", n.State.Strings) + log.Warn("add dropdown entries on create:", n.State.Strings) + log.Warn("add dropdown entries on create:", n.State.Strings) + if n.State.Strings == nil { + return + } + // add the initial dropdown entries + for i, s := range n.State.Strings { + log.Warn("add dropdown: add entries on create", n.GetProgName(), i, s) + addDropdownName(n, s) + } + cur := n.String() + log.Warn("add dropdown: set default value on create", n.GetProgName(), cur) + setDropdownName(n, cur) +} + +func setDropdownInt(n *tree.Node, i int) { + if !ready(n) { + return + } + var tk *guiWidget + tk = n.TK.(*guiWidget) + tk.uiCombobox.SetSelected(i) +} + +func addDropdownName(n *tree.Node, s string) { + if !ready(n) { + return + } + var tk *guiWidget + tk = n.TK.(*guiWidget) + log.Log(INFO, "addDropdownName()", n.WidgetId, "add:", s) + + tk.uiCombobox.Append(s) + if tk.val == nil { + log.Log(INFO, "make map didn't work") + return + } + tk.val[tk.c] = s + + // If this is the first menu added, set the dropdown to it + if tk.c == 0 { + log.Log(INFO, "THIS IS THE FIRST Dropdown", s) + tk.uiCombobox.SetSelected(0) + } + tk.c = tk.c + 1 +} + +func setDropdownName(n *tree.Node, s string) bool { + if !ready(n) { + return false + } + var tk *guiWidget + tk = n.TK.(*guiWidget) + log.Log(INFO, "SetDropdownName()", n.WidgetId, ",", s) + + for i, tmp := range tk.val { + if s == tmp { + n.SetValue(s) + setDropdownInt(n, i) + log.Warn("SetDropdownInt() worked", tmp, i) + return true + } + } + log.Warn("SetDropdownName() failed", s) + return false +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..66ac7bb --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module go.wit.com/gui/toolkits/andlabs + +go 1.21.4 + +require ( + go.wit.com/dev/andlabs/ui v0.0.1 + go.wit.com/gui/widget v1.1.3 + go.wit.com/log v0.5.4 +) + +require go.wit.com/dev/davecgh/spew v1.1.4 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..0a953ac --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +go.wit.com/dev/andlabs/ui v0.0.1 h1:SowOybLBu/qUOqp905EZikz5/iPa3GpmnCAPzNOYajM= +go.wit.com/dev/andlabs/ui v0.0.1/go.mod h1:mlKEEe05ZJURzjh1LtjzdGMHVbJm9a7BUaVpA9cHxsM= +go.wit.com/dev/davecgh/spew v1.1.3 h1:hqnB5qsPgC2cLZaJXqQJspQ5n/Ugry9kyL3tLk0hVzQ= +go.wit.com/dev/davecgh/spew v1.1.3/go.mod h1:sihvWmnQ/09FWplnEmozt90CCVqBtGuPXM811tgfhFA= +go.wit.com/dev/davecgh/spew v1.1.4 h1:C9hj/rjlUpdK+E6aroyLjCbS5MFcyNUOuP1ICLWdNek= +go.wit.com/dev/davecgh/spew v1.1.4/go.mod h1:sihvWmnQ/09FWplnEmozt90CCVqBtGuPXM811tgfhFA= +go.wit.com/gui/widget v1.1.3 h1:GvLzGSOF9tfmoh6HNbFdN+NSlBo2qeS/Ba2TnQQ1A1U= +go.wit.com/gui/widget v1.1.3/go.mod h1:A6/FaiFQtAHTjgo7c4FrokXe6bXX1Cowo35b2Lgi31E= +go.wit.com/log v0.5.3 h1:/zHkniOPusPEuX1R401rMny9uwSO/nSU/QOMx6qoEnE= +go.wit.com/log v0.5.3/go.mod h1:LzIzVxc2xJQxWQBtV9VbV605P4TOxmYDCl+BZF38yGE= +go.wit.com/log v0.5.4 h1:vijLRPTUgChb8J5tx/7Uma/lGTUxeSXosFbheAmL914= +go.wit.com/log v0.5.4/go.mod h1:BaJBfHFqcJSJLXGQ9RHi3XVhPgsStxSMZRlaRxW4kAo= diff --git a/grid.go b/grid.go new file mode 100644 index 0000000..603854c --- /dev/null +++ b/grid.go @@ -0,0 +1,29 @@ +package main + +import ( + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +// Grid numbering by (X,Y) +// ----------------------------- +// -- (1,1) -- (2,1) -- (3,1) -- +// -- (1,2) -- (2,1) -- (3,1) -- +// ----------------------------- +func newGrid(n *tree.Node) { + if notNew(n) { + return + } + var newt *guiWidget + newt = new(guiWidget) + + c := ui.NewGrid() + newt.uiGrid = c + newt.uiControl = c + c.SetPadded(true) + + n.TK = newt + place(n.Parent, n) +} diff --git a/group.go b/group.go new file mode 100644 index 0000000..0c2274a --- /dev/null +++ b/group.go @@ -0,0 +1,24 @@ +package main + +import ( + // "go.wit.com/gui/widget" + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +func newGroup(p, n *tree.Node) { + if notNew(n) { + return + } + newt := new(guiWidget) + + g := ui.NewGroup(n.GetLabel()) + g.SetMargined(true) + newt.uiGroup = g + newt.uiControl = g + + n.TK = newt + place(p, n) +} diff --git a/icon.go b/icon.go new file mode 100644 index 0000000..f56d1db --- /dev/null +++ b/icon.go @@ -0,0 +1,27 @@ +package main + +var rawImage = []byte{ + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, + 0x08, 0x06, 0x00, 0x00, 0x00, 0x1f, 0xf3, 0xff, 0x61, 0x00, 0x00, 0x00, + 0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, + 0x00, 0xca, 0x49, 0x44, 0x41, 0x54, 0x38, 0x11, 0xa5, 0x93, 0xb1, 0x0d, + 0xc2, 0x40, 0x0c, 0x45, 0x1d, 0xc4, 0x14, 0x0c, 0x12, 0x41, 0x0f, 0x62, + 0x12, 0x46, 0x80, 0x8a, 0x2e, 0x15, 0x30, 0x02, 0x93, 0x20, 0x68, 0x11, + 0x51, 0x06, 0x61, 0x0d, 0x88, 0x2d, 0x7f, 0xdb, 0x07, 0x87, 0x08, 0xdc, + 0x49, 0x91, 0x7d, 0xf6, 0xf7, 0xf3, 0x4f, 0xa4, 0x54, 0xbb, 0xeb, 0xf6, + 0x41, 0x05, 0x67, 0xcc, 0xb3, 0x9b, 0xfa, 0xf6, 0x17, 0x62, 0xdf, 0xcd, + 0x48, 0x00, 0x32, 0xbd, 0xa8, 0x1d, 0x72, 0xee, 0x3c, 0x47, 0x16, 0xfb, + 0x5c, 0x53, 0x8d, 0x03, 0x30, 0x14, 0x84, 0xf7, 0xd5, 0x89, 0x26, 0xc7, + 0x25, 0x10, 0x36, 0xe4, 0x05, 0xa2, 0x51, 0xbc, 0xc4, 0x1c, 0xc3, 0x1c, + 0xed, 0x30, 0x1c, 0x8f, 0x16, 0x3f, 0x02, 0x78, 0x33, 0x20, 0x06, 0x60, + 0x97, 0x70, 0xaa, 0x45, 0x7f, 0x85, 0x60, 0x5d, 0xb6, 0xf4, 0xc2, 0xc4, + 0x3e, 0x0f, 0x44, 0xcd, 0x1b, 0x20, 0x90, 0x0f, 0xed, 0x85, 0xa8, 0x55, + 0x05, 0x42, 0x43, 0xb4, 0x9e, 0xce, 0x71, 0xb3, 0xe8, 0x0e, 0xb4, 0xc4, + 0xc3, 0x39, 0x21, 0xb7, 0x73, 0xbd, 0xe4, 0x1b, 0xe4, 0x04, 0xb6, 0xaa, + 0x4f, 0x18, 0x2c, 0xee, 0x42, 0x31, 0x01, 0x84, 0xfa, 0xe0, 0xd4, 0x00, + 0xdf, 0xb6, 0x83, 0xf8, 0xea, 0xc2, 0x00, 0x10, 0xfc, 0x1a, 0x05, 0x30, + 0x74, 0x3b, 0xe0, 0xd1, 0x45, 0xb1, 0x83, 0xaa, 0xf4, 0x77, 0x7e, 0x02, + 0x87, 0x1f, 0x42, 0x7f, 0x9e, 0x2b, 0xe8, 0xdf, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, +} diff --git a/image.go b/image.go new file mode 100644 index 0000000..752ae99 --- /dev/null +++ b/image.go @@ -0,0 +1,49 @@ +package main + +import ( + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +// make new Image using andlabs/ui +func (p *node) newImage(n *node) { + newt := new(guiWidget) + var img *ui.Image + + img = ui.NewImage(16, 16) + + newt.uiImage = img + // newt.uiControl = img + + n.tk = newt + // p.place(n) +} + +/* + if (a.Name == "image") { + log(true, "NewTextbox() trying to add a new image") + i := ui.NewImage(16, 16) + img, _, err := image.Decode(bytes.NewReader(rawImage)) + if err != nil { + panic(err) + } + nr, ok := img.(*image.RGBA) + if !ok { + i2 := image.NewRGBA(img.Bounds()) + draw.Draw(i2, i2.Bounds(), img, img.Bounds().Min, draw.Src) + nr = i2 + } + i.Append(nr) + t.uiBox.Append(i, true) + + var img *ui.Image + var icon []byte + var imgA image.Image + + icon, _ = res.ReadFile("resources/ping6.working.png") + // imgA, _, err := image.Decode(bytes.NewReader(b)) + imgA, _, _ = image.Decode(icon) + img.Append(imgA) + img.Append(icon) + } +*/ diff --git a/label.go b/label.go new file mode 100644 index 0000000..bbde03d --- /dev/null +++ b/label.go @@ -0,0 +1,21 @@ +package main + +import ( + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +func newLabel(p, n *tree.Node) { + if notNew(n) { + return + } + newt := new(guiWidget) + c := ui.NewLabel(n.GetLabel()) + newt.uiLabel = c + newt.uiControl = c + + n.TK = newt + place(p, n) +} diff --git a/log.go b/log.go new file mode 100644 index 0000000..e7f4163 --- /dev/null +++ b/log.go @@ -0,0 +1,34 @@ +package main + +/* + this enables command line options from other packages like 'gui' and 'log' +*/ + +import ( + log "go.wit.com/log" +) + +var NOW *log.LogFlag +var INFO *log.LogFlag + +var SPEW *log.LogFlag +var WARN *log.LogFlag + +var ERROR *log.LogFlag +var CHANGE *log.LogFlag +var TOOLKIT *log.LogFlag + +func init() { + full := "toolkit/nocui" + short := "nocui" + + NOW = log.NewFlag("NOW", true, full, short, "temp debugging stuff") + INFO = log.NewFlag("INFO", false, full, short, "normal debugging stuff") + + WARN = log.NewFlag("WARN", true, full, short, "bad things") + SPEW = log.NewFlag("SPEW", false, full, short, "spew stuff") + + ERROR = log.NewFlag("ERROR", false, full, short, "toolkit errors") + CHANGE = log.NewFlag("ERROR", false, full, short, "show when the user does things") + TOOLKIT = log.NewFlag("ERROR", false, full, short, "andlabs specific stuff") +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..c543f6e --- /dev/null +++ b/main.go @@ -0,0 +1,70 @@ +package main + +import ( + "runtime/debug" + "sync" + + "go.wit.com/lib/widget" + "go.wit.com/log" + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + // the _ means we only need this for the init() + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +var uiMainUndef bool = true +var uiMain sync.Once +var muAction sync.Mutex + +func queueMain(currentA widget.Action) { + defer func() { + if r := recover(); r != nil { + log.Warn("YAHOOOO Recovered in queueMain() application:", r) + log.Println("Recovered from panic:", r) + log.Println("Stack trace:") + debug.PrintStack() + me.myTree.DoToolkitPanic() + } + }() + ui.QueueMain(func() { + rawAction(¤tA) + }) +} + +func guiMain() { + defer func() { + if r := recover(); r != nil { + log.Warn("YAHOOOO Recovered in guiMain application:", r) + log.Println("Recovered from panic:", r) + log.Println("Stack trace:") + debug.PrintStack() + me.myTree.DoToolkitPanic() + } + }() + ui.Main(func() { + demoUI() + }) +} + +func Init() { + log.Warn("Init() TODO: move init() to here") +} + +// This is important. This sets the defaults for the gui. Without this, there isn't correct padding, etc +func init() { + log.Log(INFO, "Init() START") + log.Log(INFO, "Init()") + // Can you pass values to a plugin init() ? Otherwise, there is no way to safely print + // log.Log(INFO, "init() Setting defaultBehavior = true") + setDefaultBehavior(true) + + me.myTree = tree.New() + me.myTree.PluginName = "andlabs" + me.myTree.ActionFromChannel = queueMain + + // TODO: this is messed up. run ui.Main() from the first add? Initialize it with an empty thing first? + // fake out the OS toolkit by making a fake window. This is probably needed for macos & windows + // actually, this probably breaks the macos build + go guiMain() +} diff --git a/place.go b/place.go new file mode 100644 index 0000000..9ca11c6 --- /dev/null +++ b/place.go @@ -0,0 +1,112 @@ +package main + +import ( + // "os" + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" + + "go.wit.com/lib/widget" + "go.wit.com/log" + + "go.wit.com/toolkits/tree" +) + +// This routine is very specific to this toolkit +// It's annoying and has to be copied to each widget when there are changes +// it could be 'simplfied' maybe or made to be more generic, but this is as far as I've gotten +// it's probably not worth working much more on this toolkit, the andlabs/ui has been great and got me here! +// but it's time to write direct GTK, QT, macos and windows toolkit plugins +// -- jcarr 2023/03/09 + +// Grid numbering examples by (X,Y) +// --------- +// -- (1) -- +// -- (2) -- +// --------- +// +// ----------------------------- +// -- (1,1) -- (1,2) -- (1,3) -- +// -- (2,1) -- (2,2) -- (2,3) -- +// ----------------------------- + +// internally for andlabs/ui +// (x&y flipped and start at zero) +// ----------------------------- +// -- (0,0) -- (1,0) -- (1,0) -- +// -- (0,1) -- (1,1) -- (1,1) -- +// ----------------------------- + +func place(p *tree.Node, n *tree.Node) bool { + log.Warn("SPEEDY newplace() 1 START", n.WidgetId, n.GetProgName(), n.GetLabel(), n.String()) + log.Warn("SPEEDY newplace() n.State.Strings =", n.State.Strings) + log.Log(INFO, "place() 1 START", n.WidgetType, n.GetProgName(), n.GetLabel()) + if !ready(n) { + log.Warn("place() 1 START not ready()") + return false + } + log.Log(INFO, "place() 1 START ready()") + var tk, ptk *guiWidget + tk = n.TK.(*guiWidget) + ptk = p.TK.(*guiWidget) + log.Warn("SPEEDY newplace() 2 START", n.WidgetId, n.GetProgName(), n.GetLabel()) + + if ptk == nil { + log.Log(ERROR, "ptk == nil", p.GetProgName(), p.ParentId, p.WidgetType, ptk) + log.Log(ERROR, "n = ", n.GetProgName(), n.ParentId, n.WidgetType, tk) + log.Warn("SPEEDY ptk == nil", n.WidgetId, n.GetProgName()) + log.Sleep(1) + panic("ptk == nil") + } + + log.Log(INFO, "place() switch", p.WidgetType) + log.Warn("SPEEDY newplace() before switch", n.WidgetId, n.GetProgName()) + switch p.WidgetType { + case widget.Grid: + tk.gridX = n.State.GridOffset.X - 1 + tk.gridY = n.State.GridOffset.Y - 1 + log.Warn("place() on Grid at gridX,gridY", tk.gridX, tk.gridY) + ptk.uiGrid.Append(tk.uiControl, + tk.gridX, tk.gridY, 1, 1, + false, ui.AlignFill, false, ui.AlignFill) + return true + case widget.Group: + if ptk.uiBox == nil { + log.Log(WARN, "place() andlabs hack group to use add a box", n.GetProgName(), n.WidgetType) + ptk.uiBox = rawBox(n) + ptk.uiGroup.SetChild(ptk.uiBox) + } + ptk.uiBox.Append(tk.uiControl, stretchy) + return true + case widget.Tab: + if ptk.uiTab == nil { + log.Log(ERROR, "ptk.uiTab == nil for n.WidgetId =", n.WidgetId, "ptk =", ptk) + panic("ptk.uiTab == nil") + } + if tk.uiControl == nil { + log.Log(ERROR, "tk.uiControl == nil for n.WidgetId =", n.WidgetId, "tk =", tk) + panic("tk.uiControl == nil") + } + log.Log(ERROR, "CHECK LOGIC ON THIS. APPENDING directly into a window without a tab") + // log.Log(ERROR, "THIS SHOULD NEVER HAPPEN ??????? trying to place() node=", n.WidgetId, n.GetProgName(), n.Text, n.WidgetType) + // log.Log(ERROR, "THIS SHOULD NEVER HAPPEN ??????? trying to place() on parent=", p.WidgetId, p.GetProgName(), p.Text, p.WidgetType) + // panic("tk.uiControl == nil") + ptk.uiTab.Append(widget.GetString(n.State.Value), tk.uiControl) + ptk.boxC += 1 + return true + case widget.Box: + log.Warn("SPEEDY Add Something to Box", n.WidgetId, n.GetProgName()) + log.Log(INFO, "place() uiBox =", ptk.uiBox) + log.Log(INFO, "place() uiControl =", tk.uiControl) + ptk.uiBox.Append(tk.uiControl, stretchy) + ptk.boxC += 1 + return true + case widget.Window: + log.Warn("SPEEDY Add Something to Window", n.WidgetId, n.GetProgName()) + ptk.uiWindow.SetChild(tk.uiControl) + return true + default: + log.Log(ERROR, "place() how? Parent =", p.WidgetId, p.WidgetType) + } + log.Warn("SPEEDY newplace() return", n.WidgetId, n.GetProgName()) + return false +} diff --git a/setText.go b/setText.go new file mode 100644 index 0000000..2e4b97b --- /dev/null +++ b/setText.go @@ -0,0 +1,76 @@ +package main + +import ( + "go.wit.com/lib/widget" + "go.wit.com/log" + "go.wit.com/toolkits/tree" +) + +// func (n *node) setText(a *widget.Action) { +func setText(n *tree.Node, a *widget.Action) { + name := widget.GetString(a.Value) + var tk *guiWidget + tk = n.TK.(*guiWidget) + + log.Log(CHANGE, "setText() START with text =", name) + if tk == nil { + log.Log(ERROR, "setText error. tk == nil", n.GetProgName(), n.WidgetId) + return + } + log.Log(CHANGE, "setText() Attempt on", n.WidgetType, "with", name) + + switch n.WidgetType { + case widget.Window: + log.Warn("setText() Attempt to set the title to", name) + tk.uiWindow.SetTitle(name) + case widget.Tab: + case widget.Group: + tk.uiGroup.SetTitle(name) + case widget.Checkbox: + tk.uiCheckbox.SetText(name) + case widget.Textbox: + if tk.uiEntry != nil { + tk.uiEntry.SetText(name) + } + if tk.uiMultilineEntry != nil { + tk.uiMultilineEntry.SetText(name) + } + case widget.Label: + tk.uiLabel.SetText(name) + case widget.Button: + tk.uiButton.SetText(name) + case widget.Slider: + log.Log(ERROR, "setText() on slider unknown", a.ActionType, "on checkbox", n.GetProgName()) + case widget.Spinner: + log.Log(ERROR, "setText() on spinner unknown", a.ActionType, "on checkbox", n.GetProgName()) + case widget.Dropdown: + var orig int + var i int = -1 + var s string + orig = tk.uiCombobox.Selected() + log.Log(CHANGE, "try to set the Dropdown to", name, "from", orig) + // try to find the string + for i, s = range tk.val { + log.Log(CHANGE, "i, s", i, s) + if name == s { + tk.uiCombobox.SetSelected(i) + log.Log(CHANGE, "setText() Dropdown worked.", name) + return + } + } + log.Log(ERROR, "setText() Dropdown did not find:", name) + // if i == -1, then there are not any things in the menu to select + if i == -1 { + return + } + // if the string was never set, then set the dropdown to the last thing added to the menu + if orig == -1 { + tk.uiCombobox.SetSelected(i) + } + case widget.Combobox: + tk.uiEditableCombobox.SetText(name) + default: + log.Log(ERROR, "plugin Send() Don't know how to setText on", n.WidgetType, "yet", a.ActionType) + } + log.Log(CHANGE, "setText() END with name =") +} diff --git a/slider.go b/slider.go new file mode 100644 index 0000000..ec82afe --- /dev/null +++ b/slider.go @@ -0,0 +1,31 @@ +package main + +import ( + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +func newSlider(p, n *tree.Node) { + if notNew(n) { + return + } + newt := new(guiWidget) + + var x, y int + x = n.State.Range.Low + y = n.State.Range.High + + s := ui.NewSlider(x, y) + newt.uiSlider = s + newt.uiControl = s + + s.OnChanged(func(spin *ui.Slider) { + n.SetValue(newt.uiSlider.Value()) + me.myTree.DoUserEvent(n) + }) + + n.TK = newt + place(p, n) +} diff --git a/spinner.go b/spinner.go new file mode 100644 index 0000000..a029edf --- /dev/null +++ b/spinner.go @@ -0,0 +1,27 @@ +package main + +import ( + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +func newSpinner(p, n *tree.Node) { + if notNew(n) { + return + } + newt := new(guiWidget) + + s := ui.NewSpinbox(n.State.Range.Low, n.State.Range.High) + newt.uiSpinbox = s + newt.uiControl = s + + s.OnChanged(func(s *ui.Spinbox) { + n.SetValue(newt.uiSpinbox.Value()) + me.myTree.DoUserEvent(n) + }) + + n.TK = newt + place(p, n) +} diff --git a/structs.go b/structs.go new file mode 100644 index 0000000..4e1c056 --- /dev/null +++ b/structs.go @@ -0,0 +1,64 @@ +package main + +import ( + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +// var andlabs map[int]*andlabsT +// var callback func(int) bool +// var callback chan toolkit.Action + +// It's probably a terrible idea to call this 'me' +var me config + +type config struct { + rootNode *tree.Node // the base of the binary tree. it should have id == 0 + treeRoot *tree.Node // the base of the binary tree. it should have id == 0 + myTree *tree.TreeInfo +} + +// stores the raw toolkit internals +type guiWidget struct { + Width int + Height int + + // tw *toolkit.Widget + parent *guiWidget + children []*guiWidget + + // used to track if a tab has a child widget yet + child bool + + uiControl ui.Control + + uiBox *ui.Box + uiButton *ui.Button + uiCombobox *ui.Combobox + uiCheckbox *ui.Checkbox + uiEntry *ui.Entry + uiGroup *ui.Group + uiLabel *ui.Label + uiSlider *ui.Slider + uiSpinbox *ui.Spinbox + uiTab *ui.Tab + uiWindow *ui.Window + uiMultilineEntry *ui.MultilineEntry + uiEditableCombobox *ui.EditableCombobox + uiImage *ui.Image + + uiGrid *ui.Grid + gridX int + gridY int + + // used as a counter to work around limitations of widgets like combobox + // this is probably fucked up and in many ways wrong because of unsafe goroutine threading + // but it's working for now due to the need for need for a correct interaction layer betten toolkits + c int + val map[int]string + + // andlabs/ui only accesses widget id numbers + boxC int // how many things on in a box or how many tabs +} diff --git a/tab.go b/tab.go new file mode 100644 index 0000000..2307e6b --- /dev/null +++ b/tab.go @@ -0,0 +1,117 @@ +package main + +import ( + "go.wit.com/lib/widget" + "go.wit.com/log" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +/* +This adds a tab + +andlabs/ui is goofy in the sense that you have to determine +if the ui.Window already has a tab in it. If it does, then +you need to add this tab and not run SetChild() on the window +or instead it replaces the existing tab with the new one + +I work around this by always sending a Toolkit that is a tab +once there is one. If you send a Window here, it will replace +any existing tabs rather than adding a new one +*/ +func (p *node) newTab(n *node) { + var newt *guiWidget + + if p == nil { + log.Log(ERROR, "newTab() p == nil. how the fuck does this happen?", n.WidgetId, n.ParentId) + } + if p.WidgetType != widget.Window { + log.Log(ERROR, "newTab() uiWindow == nil. I can't add a toolbar without window", n.WidgetId, n.ParentId) + return + } + t := p.tk + + log.Log(TOOLKIT, "newTab() START", n.WidgetId, n.ParentId) + + if t.uiTab == nil { + // this means you have to make a new tab + log.Log(TOOLKIT, "newTab() GOOD. This should be the first tab:", n.WidgetId, n.ParentId) + newt = rawTab(t.uiWindow, widget.GetString(n.value)) + t.uiTab = newt.uiTab + } else { + // this means you have to append a tab + log.Log(TOOLKIT, "newTab() GOOD. This should be an additional tab:", n.WidgetId, n.ParentId) + if n.WidgetType == widget.Tab { + // andlabs doesn't have multiple tab widgets so make a fake one? + // this makes a guiWidget internal structure with the parent values + newt = new(guiWidget) + newt.uiWindow = t.uiWindow + newt.uiTab = t.uiTab + } else { + newt = t.appendTab(widget.GetString(n.value)) + } + } + + n.tk = newt +} + +// This sets _all_ the tabs to Margin = true +// +// TODO: do proper tab tracking (will be complicated). low priority +func tabSetMargined(tab *ui.Tab, b bool) { + c := tab.NumPages() + for i := 0; i < c; i++ { + log.Log(TOOLKIT, "SetMargined", i, b) + tab.SetMargined(i, b) + } +} + +func rawTab(w *ui.Window, name string) *guiWidget { + var newt guiWidget + log.Log(TOOLKIT, "rawTab() START", name) + + if w == nil { + log.Log(ERROR, "UiWindow == nil. I can't add a tab without a window") + log.Log(ERROR, "UiWindow == nil. I can't add a tab without a window") + log.Log(ERROR, "UiWindow == nil. I can't add a tab without a window") + // sleep(1) + return nil + } + + tab := ui.NewTab() + w.SetChild(tab) + newt.uiTab = tab + newt.uiControl = tab + log.Log(TOOLKIT, "rawTab() END", name) + return &newt +} + +func (t *guiWidget) appendTab(name string) *guiWidget { + var newT guiWidget + log.Log(TOOLKIT, "appendTab() ADD", name) + + if t.uiTab == nil { + log.Log(TOOLKIT, "UiWindow == nil. I can't add a widget without a place to put it") + panic("should never have happened. wit/gui/toolkit has ui.Tab == nil") + } + log.Log(TOOLKIT, "appendTab() START name =", name) + + var hbox *ui.Box + if defaultBehavior { + hbox = ui.NewHorizontalBox() + } else { + if bookshelf { + hbox = ui.NewHorizontalBox() + } else { + hbox = ui.NewVerticalBox() + } + } + hbox.SetPadded(padded) + t.uiTab.Append(name, hbox) + + newT.uiWindow = t.uiWindow + newT.uiTab = t.uiTab + newT.uiBox = hbox + return &newT +} diff --git a/textbox.go b/textbox.go new file mode 100644 index 0000000..1e90dd3 --- /dev/null +++ b/textbox.go @@ -0,0 +1,37 @@ +package main + +import ( + "go.wit.com/toolkits/tree" + + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" +) + +func newTextbox(p, n *tree.Node) { + if notNew(n) { + return + } + newt := new(guiWidget) + + if n.State.Range.Low == 1 { + e := ui.NewEntry() + newt.uiEntry = e + newt.uiControl = e + + e.OnChanged(func(spin *ui.Entry) { + n.SetValue(spin.Text()) + me.myTree.DoUserEvent(n) + }) + } else { + e := ui.NewNonWrappingMultilineEntry() + newt.uiMultilineEntry = e + newt.uiControl = e + + e.OnChanged(func(spin *ui.MultilineEntry) { + n.SetValue(spin.Text()) + me.myTree.DoUserEvent(n) + }) + } + n.TK = newt + place(p, n) +} diff --git a/tree.go b/tree.go new file mode 100644 index 0000000..b39857c --- /dev/null +++ b/tree.go @@ -0,0 +1,31 @@ +package main + +/* + This code should be common to all gui plugins + + There are some helper functions that are probably going to be + the same everywhere. Mostly due to handling the binary tree structure + and the channel communication + + For now, it's just a symlink to the 'master' version in + ./toolkit/nocui/common.go +*/ + +import ( + "go.wit.com/lib/widget" +) + +// 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.) +// +// this sets the channel to send user events back from the plugin +func Callback(guiCallback chan widget.Action) { + me.myTree.Callback(guiCallback) +} + +func PluginChannel() chan widget.Action { + return me.myTree.PluginChannel() +} diff --git a/updateui.go b/updateui.go new file mode 100644 index 0000000..8c94163 --- /dev/null +++ b/updateui.go @@ -0,0 +1,97 @@ +package main + +import ( + "go.wit.com/dev/andlabs/ui" +) + +// Example showing how to update the UI using the QueueMain function +// especially if the update is coming from another goroutine +// +// see QueueMain in 'main.go' for detailed description + +var count int + +func demoUI() { + mainWindow := ui.NewWindow("libui Updating UI", 640, 480, true) + mainWindow.OnClosing(func(*ui.Window) bool { + ui.Quit() + return true + }) + ui.OnShouldQuit(func() bool { + mainWindow.Destroy() + return true + }) + + vbContainer := ui.NewVerticalBox() + vbContainer.SetPadded(true) + + inputGroup := ui.NewGroup("Input") + inputGroup.SetMargined(true) + + vbInput := ui.NewVerticalBox() + vbInput.SetPadded(true) + + inputForm := ui.NewForm() + inputForm.SetPadded(true) + + message := ui.NewEntry() + message.SetText("Hello World") + inputForm.Append("What message do you want to show?", message, false) + + showMessageButton := ui.NewButton("Show message") + clearMessageButton := ui.NewButton("Clear message") + + vbInput.Append(inputForm, false) + vbInput.Append(showMessageButton, false) + vbInput.Append(clearMessageButton, false) + + inputGroup.SetChild(vbInput) + + messageGroup := ui.NewGroup("Message") + messageGroup.SetMargined(true) + + vbMessage := ui.NewVerticalBox() + vbMessage.SetPadded(true) + + messageLabel := ui.NewLabel("") + + vbMessage.Append(messageLabel, false) + + messageGroup.SetChild(vbMessage) + + countGroup := ui.NewGroup("Counter") + countGroup.SetMargined(true) + + vbCounter := ui.NewVerticalBox() + vbCounter.SetPadded(true) + + countLabel := ui.NewLabel("blah") + + vbCounter.Append(countLabel, false) + countGroup.SetChild(vbCounter) + + vbContainer.Append(inputGroup, false) + vbContainer.Append(messageGroup, false) + vbContainer.Append(countGroup, false) + + mainWindow.SetChild(vbContainer) + + showMessageButton.OnClicked(func(*ui.Button) { + // Update the UI directly as it is called from the main thread + messageLabel.SetText(message.Text()) + }) + + clearMessageButton.OnClicked(func(*ui.Button) { + // Update the UI directly as it is called from the main thread + messageLabel.SetText("") + }) + + // this is messed up. + // mainWindow.Show() +} + +/* +func main() { + ui.Main(setupUI) +} +*/ diff --git a/widget.go b/widget.go new file mode 100644 index 0000000..6600fa6 --- /dev/null +++ b/widget.go @@ -0,0 +1,18 @@ +package main + +import ( + "go.wit.com/lib/widget" + "go.wit.com/toolkits/tree" +) + +func initWidget(n *tree.Node) *guiWidget { + var w *guiWidget + w = new(guiWidget) + + if n.WidgetType == widget.Root { + n.WidgetId = 0 + me.treeRoot = n + return w + } + return w +} diff --git a/window.go b/window.go new file mode 100644 index 0000000..734654e --- /dev/null +++ b/window.go @@ -0,0 +1,51 @@ +package main + +import ( + "go.wit.com/dev/andlabs/ui" + _ "go.wit.com/dev/andlabs/ui/winmanifest" + + "go.wit.com/lib/widget" + "go.wit.com/log" + "go.wit.com/toolkits/tree" +) + +func (t *guiWidget) MessageWindow(msg1 string, msg2 string) { + ui.MsgBox(t.uiWindow, msg1, msg2) +} + +func (t *guiWidget) ErrorWindow(msg1 string, msg2 string) { + ui.MsgBoxError(t.uiWindow, msg1, msg2) +} + +func newWindow(p, n *tree.Node) { + var newt *guiWidget + newt = new(guiWidget) + + // menubar bool is if the OS defined border on the window should be used + win := ui.NewWindow(n.GetProgName(), 640, 480, menubar) + win.SetBorderless(canvas) + win.SetMargined(margin) + win.OnClosing(func(*ui.Window) bool { + // show(n, false) + me.myTree.DoWindowCloseEvent(n) + return false + }) + newt.uiWindow = win + newt.uiControl = win + + n.TK = newt + place(p, n) + win.Show() + return +} + +func (n *node) SetWindowTitle(title string) { + log.Log(CHANGE, "toolkit NewWindow", widget.GetString(n.value), "title", title) + win := n.tk.uiWindow + if win == nil { + log.Log(ERROR, "Error: no window", n.WidgetId) + } else { + win.SetTitle(title) + log.Log(CHANGE, "Setting the window title", title) + } +}