From b25f15ea7803e172204432082740d081e5f19f81 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Wed, 17 Jan 2024 23:54:19 -0600 Subject: [PATCH] the golang way. everything in it's own repo Signed-off-by: Jeff Carr --- Makefile | 13 +++ action.go | 310 ++++++++++++++++++++++++++++++++++++++++++++++++++++ add.go | 72 ++++++++++++ addText.go | 44 ++++++++ box.go | 65 +++++++++++ button.go | 30 +++++ checkbox.go | 30 +++++ combobox.go | 79 +++++++++++++ common.go | 61 +++++++++++ debug.go | 121 ++++++++++++++++++++ delete.go | 54 +++++++++ demo.go | 94 ++++++++++++++++ dropdown.go | 106 ++++++++++++++++++ go.mod | 11 ++ go.sum | 12 ++ grid.go | 29 +++++ group.go | 24 ++++ icon.go | 27 +++++ image.go | 49 +++++++++ label.go | 21 ++++ log.go | 34 ++++++ main.go | 70 ++++++++++++ place.go | 112 +++++++++++++++++++ setText.go | 76 +++++++++++++ slider.go | 31 ++++++ spinner.go | 27 +++++ structs.go | 64 +++++++++++ tab.go | 117 ++++++++++++++++++++ textbox.go | 37 +++++++ tree.go | 31 ++++++ updateui.go | 97 ++++++++++++++++ widget.go | 18 +++ window.go | 51 +++++++++ 33 files changed, 2017 insertions(+) create mode 100644 Makefile create mode 100644 action.go create mode 100644 add.go create mode 100644 addText.go create mode 100644 box.go create mode 100644 button.go create mode 100644 checkbox.go create mode 100644 combobox.go create mode 100644 common.go create mode 100644 debug.go create mode 100644 delete.go create mode 100644 demo.go create mode 100644 dropdown.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 grid.go create mode 100644 group.go create mode 100644 icon.go create mode 100644 image.go create mode 100644 label.go create mode 100644 log.go create mode 100644 main.go create mode 100644 place.go create mode 100644 setText.go create mode 100644 slider.go create mode 100644 spinner.go create mode 100644 structs.go create mode 100644 tab.go create mode 100644 textbox.go create mode 100644 tree.go create mode 100644 updateui.go create mode 100644 widget.go create mode 100644 window.go 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) + } +}