From 732edc3faf0244152bf3ea7ce06925dec3bed437 Mon Sep 17 00:00:00 2001 From: Jeff Carr Date: Sat, 13 Jan 2024 22:02:12 -0600 Subject: [PATCH] work on hiding widgets When widgets are hidden, their state works exactly the same as normal, but updates are not sent to the toolkits Signed-off-by: Jeff Carr --- action.go | 68 ++++++++++ box.go | 48 +++---- button.go | 25 +--- checkbox.go | 6 +- common.go | 360 ++++++++++++++++++++++++++++++++-------------------- debug.go | 6 +- dropdown.go | 33 +++-- grid.go | 15 +-- group.go | 11 +- image.go | 9 +- label.go | 6 +- main.go | 38 ++++-- node.go | 17 ++- plugin.go | 42 ------ setText.go | 69 +++++----- slider.go | 15 +-- spinner.go | 13 +- structs.go | 71 ++++++++--- textbox.go | 16 +-- window.go | 43 +++++-- 20 files changed, 529 insertions(+), 382 deletions(-) create mode 100644 action.go diff --git a/action.go b/action.go new file mode 100644 index 0000000..516cf15 --- /dev/null +++ b/action.go @@ -0,0 +1,68 @@ +package gui + +/* + This is where the communication to the toolkit plugin happens. + + We copy the current values from the widget node of the binary tree + and send an "action" to the toolkit over a channel. + + TODO: use protobuf +*/ + +import ( + "go.wit.com/log" + "go.wit.com/gui/widget" +) + +// 2024/01/11 finally moving to type any. simplify to just 'value' +// 2023/05/09 pretty clean +// 2023/04/06 Queue() is also being used and channels are being used. +func sendAction(n *Node, atype widget.ActionType) { + if n == nil { + return + } + if n.hidden { + return + } + + var a widget.Action + a.ActionType = atype + + // These should be "stable" at this point (2024/01/13) + a.WidgetId = n.id + a.ProgName = n.progname + a.Value = n.value + a.Direction = n.direction + a.Strings = n.strings + + // These should be improved/deprecated based on the gui/widget docs + a.Expand = n.expand + + a.X = n.X + a.Y = n.Y + + a.AtW = n.AtW + a.AtH = n.AtH + + if (n.parent != nil) { + a.ParentId = n.parent.id + } + a.WidgetType = n.WidgetType + sendActionToPlugin(&a) +} + +// sends the action/event to each toolkit via a golang plugin channel +func sendActionToPlugin(a *widget.Action) { + for _, aplug := range allPlugins { + log.Log(PLUG, "Action() aplug =", aplug.name, "Action type=", a.ActionType) + if (aplug.pluginChan == nil) { + log.Info("Action() retrieving the aplug.PluginChannel()", aplug.name) + aplug.pluginChan = aplug.PluginChannel() + log.Info("Action() retrieved", aplug.pluginChan) + } + log.Info("Action() SEND to pluginChan", aplug.name, a.ActionType, a.WidgetType, a.WidgetId, a.ProgName) + aplug.pluginChan <- *a + // added during debugging. might be a good idea in general for a tactile experience + log.Sleep(.02) // this delay makes it so SetText() works on initial widget creation + } +} diff --git a/box.go b/box.go index 4a16469..03b843f 100644 --- a/box.go +++ b/box.go @@ -4,39 +4,39 @@ import ( "go.wit.com/gui/widget" ) -func (parent *Node) NewBox(name string, b bool) *Node { - newNode := parent.newNode(name, widget.Box) +func (parent *Node) NewBox(progname string, b bool) *Node { + newNode := parent.newNode(progname, widget.Box) + newNode.progname = progname - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - if b { - a.Direction = widget.Horizontal - } else { - a.Direction = widget.Vertical - } - sendAction(a) + if b { + newNode.direction = widget.Horizontal + } else { + newNode.direction = widget.Vertical } + + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } -func (parent *Node) NewHorizontalBox(name string) *Node { - newNode := parent.newNode(name, widget.Box) +func (parent *Node) NewHorizontalBox(progname string) *Node { + newNode := parent.newNode(progname, widget.Box) + newNode.progname = progname - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - a.Direction = widget.Horizontal - sendAction(a) - } + newNode.direction = widget.Horizontal + + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } -func (parent *Node) NewVerticalBox(name string) *Node { - newNode := parent.newNode(name, widget.Box) +func (parent *Node) NewVerticalBox(progname string) *Node { + newNode := parent.newNode(progname, widget.Box) + newNode.progname = progname - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - a.Direction = widget.Vertical - sendAction(a) - } + newNode.direction = widget.Vertical + + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } diff --git a/button.go b/button.go index 463a776..9ccff89 100644 --- a/button.go +++ b/button.go @@ -8,28 +8,7 @@ func (parent *Node) NewButton(name string, custom func()) *Node { newNode.value = name newNode.progname = name - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } - -// find widget by number -func (n *Node) FindId(i int) (*Node) { - if (n == nil) { - return nil - } - - if (n.id == i) { - return n - } - - for _, child := range n.children { - newN := child.FindId(i) - if (newN != nil) { - return newN - } - } - return nil -} diff --git a/checkbox.go b/checkbox.go index 9075bbd..ed28185 100644 --- a/checkbox.go +++ b/checkbox.go @@ -11,9 +11,7 @@ func (n *Node) NewCheckbox(name string) *Node { newNode.value = name newNode.progname = name - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } diff --git a/common.go b/common.go index 1d17d48..6c31758 100644 --- a/common.go +++ b/common.go @@ -11,79 +11,91 @@ import ( // functions for handling text related GUI elements func (n *Node) Show() *Node { - if ! n.hidden { - a := newAction(n, widget.Show) - sendAction(a) - } + if ! n.Ready() { return n } + if ! n.Hidden() { return n } + + n.hidden = false + n.changed = true + + // inform the toolkits + sendAction(n, widget.Show) return n } func (n *Node) Hide() *Node { - if ! n.hidden { - a := newAction(n, widget.Hide) - sendAction(a) - } + if ! n.Ready() { return n } + if n.Hidden() { return n } + + n.hidden = true + n.changed = true + // inform the toolkits + sendAction(n, widget.Hide) return n } +// enables a widget so the user can see it and work/click/etc on it +// by default, widgets are enabled when they are created func (n *Node) Enable() *Node { - if ! n.hidden { - a := newAction(n, widget.Enable) - sendAction(a) - } + if ! n.Ready() { return n } + // if n.enabled { return n } + + n.enabled = true + n.changed = true + + // inform the toolkits + sendAction(n, widget.Enable) return n } +// disables a widget so the user can see it, but can not +// interact or change it. func (n *Node) Disable() *Node { - if ! n.hidden { - a := newAction(n, widget.Disable) - sendAction(a) - } + if ! n.Ready() { return n } + // if ! n.enabled { return n } + + n.enabled = false + n.changed = true + + // inform the toolkits + sendAction(n, widget.Disable) return n } -func (n *Node) Add(str string) { - log.Log(GUI, "gui.Add() value =", str) - n.value = str - - if ! n.hidden { - a := newAction(n, widget.Add) - sendAction(a) - } -} - -func (n *Node) AddText(str string) { +// add a new text string to widgets that support +// multiple string values +// These must be unique. return false if the string already exists +func (n *Node) AddText(str string) bool { + if ! n.Ready() { return false } log.Log(CHANGE, "AddText() value =", str) n.value = str + // TODO: make sure these are unique + n.strings = append(n.strings, str) - if ! n.hidden { - a := newAction(n, widget.AddText) - sendAction(a) - } + // inform the toolkits + sendAction(n, widget.AddText) + return true } -func (n *Node) SetNext(w int, h int) { - n.NextW = w - n.NextH = h - log.Info("SetNext() w,h =", n.NextW, n.NextH) -} +// appends text to the existing text +// TODO: this is an experiement func (n *Node) AppendText(str string) { + if ! n.Ready() { return } tmp := widget.GetString(n.value) + str n.value = tmp + n.changed = true - if ! n.hidden { - a := newAction(n, widget.SetText) - sendAction(a) - } + // inform the toolkits + sendAction(n, widget.SetText) } // THESE TWO FUNCTIONS ARE TERRIBLY NAMED AND NEED TO BE FIXED // 5 seconds worth of ideas: // Value() ? // Progname() Reference() ? +// 2024/01/13 the names are starting to grow on me and make it clearer to code against // get a string from the widget func (n *Node) GetText() string { @@ -104,14 +116,177 @@ func (n *Node) GetBool() bool { } // should get the reference name used for programming and debugging -// myButton = myGroup.NewButton("hit ball", nil).SetName("HIT") -// myButton.GetName() should return "HIT" -// n = Find("HIT") should return myButton -func (n *Node) GetName() string { +func (n *Node) SetProgName(s string) { + if ! n.Ready() { return } + + if n.progname == s { + // don't do anything since nothing changed + return + } + + n.changed = true + n.progname = s + return +} + +/* + TODO: ensure these are unique and make a way to look them up + myButton = myGroup.NewButton("hit ball", nil).SetName("HIT") + myButton.GetName() should return "HIT" + n = Find("HIT") should return myButton +*/ +func (n *Node) GetProgName() string { if ! n.Ready() { return "" } return n.progname } +/* +func commonCallback(n *Node) { + // TODO: make all of this common code to all the widgets + // This might be common everywhere finally (2023/03/01) + if (n.Custom == nil) { + log.Log(CHANGE, "Not Running n.Custom(n) == nil") + } else { + log.Log(CHANGE, "Running n.Custom(n)") + n.Custom() + } +} +*/ + +func (n *Node) Margin() *Node { + if ! n.Ready() { return n } + if n.margin { return n } + + n.margin = true + n.changed = true + + // inform the toolkits + sendAction(n, widget.Margin) + return n +} + +func (n *Node) Unmargin() *Node { + if ! n.Ready() { return n } + if ! n.margin { return n } + + n.margin = false + n.changed = true + + // inform the toolkits + sendAction(n, widget.Unmargin) + return n +} + +func (n *Node) Pad() *Node { + if ! n.Ready() { return n } + if n.pad == true { return n } // nothing changed + + n.pad = true + n.changed = true + + // inform the toolkits + sendAction(n, widget.Pad) + return n +} + +func (n *Node) Unpad() *Node { + if ! n.Ready() { return n } + if n.pad == false { return n } // nothing changed + + n.pad = false + n.changed = true + + // inform the toolkits + sendAction(n, widget.Unpad) + return n +} + +func (n *Node) Expand() *Node { + if ! n.Ready() { return n } + if n.expand == true { return n } // nothing changed + + n.expand = true + n.changed = true + + // inform the toolkits + sendAction(n, widget.SetExpand) + return n +} + +func (n *Node) SetExpand(b bool) *Node { + if ! n.Ready() { return n } + if n.expand == b { return n } // nothing changed + + n.expand = b + n.changed = true + + // inform the toolkits + sendAction(n, widget.SetExpand) + return n +} + +// is the widget currently viewable? +func (n *Node) Hidden() bool { + if ! n.Ready() { return false } + return n.hidden +} + +func (n *Node) Ready() bool { + if n == nil { + log.Warn("Ready() got node == nil") + // TODO: figure out if you can identify the code trace + // to help find the root cause + return false + } + return true +} + +// +// +// DEPRECATE / REDO / SORT OUT THIS STUFF +// +// + +// This should not really do anything. as per the docs, the "Standard()" way +// should be the default way +/* +func (n *Node) Standard() *Node { + log.Warn("Standard() not implemented yet") + return n +} + +func (n *Node) SetMargin() *Node { + log.Warn("DoMargin() not implemented yet") + return n +} +*/ + +/* +func (n *Node) Window(title string) *Node { + log.Warn("Window()", n) + return n.NewWindow(title) +} +*/ + +/* +func (n *Node) Add(str string) { + log.Log(GUI, "gui.Add() value =", str) + + n.value = str + + // inform the toolkits + sendAction(n, widget.Add) +} +*/ + +/* +func (n *Node) SetNext(w int, h int) { + n.NextW = w + n.NextH = h + log.Info("SetNext() w,h =", n.NextW, n.NextH) +} +*/ + /* // string handling examples that might be helpful for normalizeInt() isAlpha := regexp.MustCompile(`^[A-Za-z]+$`).MatchString @@ -145,100 +320,3 @@ func normalizeInt(s string) string { log.Log(GUI, "normalizeInt() s =", clean) return clean } - -func commonCallback(n *Node) { - // TODO: make all of this common code to all the widgets - // This might be common everywhere finally (2023/03/01) - if (n.Custom == nil) { - log.Log(CHANGE, "Not Running n.Custom(n) == nil") - } else { - log.Log(CHANGE, "Running n.Custom(n)") - n.Custom() - } -} - -func (n *Node) Margin() *Node { - n.margin = true - if ! n.hidden { - a := newAction(n, widget.Margin) - sendAction(a) - } - return n -} - -func (n *Node) Unmargin() *Node { - n.margin = false - if ! n.hidden { - a := newAction(n, widget.Unmargin) - sendAction(a) - } - return n -} - -func (n *Node) Pad() *Node { - n.pad = true - if ! n.hidden { - a := newAction(n, widget.Pad) - sendAction(a) - } - return n -} - -func (n *Node) Unpad() *Node { - n.pad = false - if ! n.hidden { - a := newAction(n, widget.Unpad) - sendAction(a) - } - return n -} - -func (n *Node) Expand() *Node { - n.expand = true - if ! n.hidden { - a := newAction(n, widget.Pad) - a.Expand = true - sendAction(a) - } - return n -} - -// is this better? -// yes, this is better. it allows Internationalization very easily -// me.window = myGui.New2().Window("DNS and IPv6 Control Panel").Standard() -// myFunnyWindow = myGui.NewWindow("Hello").Standard().SetText("Hola") - -/* -func (n *Node) Window(title string) *Node { - log.Warn("Window()", n) - return n.NewWindow(title) -} -*/ - -func (n *Node) ProgName() string { - if ! n.Ready() { return "" } - return n.progname -} - -func (n *Node) Ready() bool { - if n == nil { - log.Warn("Ready() got node == nil") - // TODO: figure out if you can identify the code trace to help find the root cause - return false - } - return true -} - -// This should not really do anything. as per the docs, the "Standard()" way -// should be the default way -/* -func (n *Node) Standard() *Node { - log.Warn("Standard() not implemented yet") - return n -} - -func (n *Node) SetMargin() *Node { - log.Warn("DoMargin() not implemented yet") - return n -} -*/ diff --git a/debug.go b/debug.go index 35b73b7..496fa72 100644 --- a/debug.go +++ b/debug.go @@ -38,10 +38,8 @@ func (n *Node) Dump() { } Indent(b, "NODE DUMP END") - a := new(widget.Action) - a.ActionType = widget.Dump - a.WidgetId = n.id - sendAction(a) + n.changed = true + sendAction(n, widget.Dump) } func Indent(b bool, a ...interface{}) { diff --git a/dropdown.go b/dropdown.go index 03479bb3..9b65154 100644 --- a/dropdown.go +++ b/dropdown.go @@ -6,41 +6,38 @@ package gui // since it is the same. confusing names? maybe... import ( + "go.wit.com/log" "go.wit.com/gui/widget" ) // add a new entry to the dropdown name func (n *Node) AddDropdownName(name string) { + if ! n.Ready() { return } + log.Warn("AddDropdownName() deprecated") n.AddText(name) } // Set the dropdown menu to 'name' func (n *Node) SetDropdownName(name string) { + if ! n.Ready() { return } + log.Warn("SetDropdownName() deprecated") n.SetText(name) } -func (n *Node) NewDropdown(name string) *Node { - newNode := n.newNode(name, widget.Dropdown) - newNode.progname = name - newNode.value = name - - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } +func (n *Node) NewDropdown(progname string) *Node { + newNode := n.newNode(progname, widget.Dropdown) + newNode.progname = progname + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } -func (n *Node) NewCombobox(name string) *Node { - newNode := n.newNode(name, widget.Combobox) - newNode.progname = name - newNode.value = name - - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } +func (n *Node) NewCombobox(progname string) *Node { + newNode := n.newNode(progname, widget.Combobox) + newNode.progname = progname + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } diff --git a/grid.go b/grid.go index d48edcd..7f8747d 100644 --- a/grid.go +++ b/grid.go @@ -33,21 +33,20 @@ type GridOffset struct { Y int } -func (n *Node) NewGrid(name string, w int, h int) *Node { - newNode := n.newNode(name, widget.Grid) +func (n *Node) NewGrid(progname string, w int, h int) *Node { + newNode := n.newNode(progname, widget.Grid) + newNode.progname = progname newNode.W = w newNode.H = h newNode.NextW = 1 newNode.NextH = 1 - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } - // by default, always pad grids - newNode.Pad() + newNode.pad = true + + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } diff --git a/group.go b/group.go index ceab839..ada0e4a 100644 --- a/group.go +++ b/group.go @@ -13,14 +13,7 @@ func (parent *Node) NewGroup(name string) *Node { newNode.progname = name newNode.value = name - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } - - // by default, always pad groups - newNode.Pad() - - // newBox := newNode.NewBox("defaultGroupBox", false) + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } diff --git a/image.go b/image.go index bd6b972..aa7435e 100644 --- a/image.go +++ b/image.go @@ -1,6 +1,7 @@ package gui import ( + "go.wit.com/log" "go.wit.com/gui/widget" ) @@ -8,9 +9,9 @@ func (parent *Node) NewImage(name string) *Node { var newNode *Node newNode = parent.newNode(name, widget.Image) - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } + log.Warn("NewImage() not implemented. fix this") + + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } diff --git a/label.go b/label.go index 24d1ad9..39dfc57 100644 --- a/label.go +++ b/label.go @@ -9,9 +9,7 @@ func (parent *Node) NewLabel(text string) *Node { newNode.value = text newNode.progname = text - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } diff --git a/main.go b/main.go index bffbee9..4955ac9 100644 --- a/main.go +++ b/main.go @@ -19,7 +19,7 @@ func init() { log.Log(NOW, "init() has been run") me.counter = 0 - me.prefix = "wit" + // me.prefix = "wit" // Populates the top of the binary tree me.rootNode = addNode() @@ -38,30 +38,50 @@ func init() { go watchCallback() } +// lookup the widget by the id sent from the toolkit +func (n *Node) findId(i int) (*Node) { + if (n == nil) { + return nil + } + + if (n.id == i) { + return n + } + + for _, child := range n.children { + newN := child.findId(i) + if (newN != nil) { + return newN + } + } + return nil +} + func watchCallback() { - log.Info("watchCallback() START") + log.Info("guiChan() START") for { - log.Info("watchCallback() restarted select for toolkit user events") + log.Info("guiChan() restarted") select { case a := <-me.guiChan: if (a.ActionType == widget.UserQuit) { - log.Info("doUserEvent() User sent Quit()") + log.Warn("guiChan() User sent Quit()") me.rootNode.doCustom() log.Exit("wit/gui toolkit.UserQuit") break } if (a.ActionType == widget.EnableDebug) { - log.Warn("doUserEvent() Enable Debugging Window") - log.Warn("doUserEvent() TODO: not implemented") + log.Warn("guiChan() Enable Debugging Window") + log.Warn("guiChan() TODO: not implemented") // DebugWindow() break } - n := me.rootNode.FindId(a.WidgetId) + n := me.rootNode.findId(a.WidgetId) if (n == nil) { - log.Warn("watchCallback() UNKNOWN widget id =", a.WidgetId, a.ProgName) + log.Warn("guiChan() UNKNOWN widget id") + log.Warn("id =", a.WidgetId, a.ProgName) } else { - log.Info("watchCallback() FOUND widget id =", n.id, n.progname) + log.Verbose("guiChan() FOUND widget id =", n.id, n.progname) n.doUserEvent(a) } // this maybe a good idea? diff --git a/node.go b/node.go index 5ce28a2..1fd8470 100644 --- a/node.go +++ b/node.go @@ -7,14 +7,26 @@ import ( /* generic function to create a new node on the binary tree + + this is called each time you want a new widget + and it initializes basic default values + + there isn't much to see here. */ func (n *Node) newNode(title string, t widget.WidgetType) *Node { var newN *Node newN = addNode() + newN.progname = title newN.value = title newN.WidgetType = t + // set these defaults + newN.expand = true + newN.pad = true + newN.enabled = true + newN.changed = true + if n.WidgetType == widget.Grid { n.gridIncrement() } @@ -28,7 +40,7 @@ func (n *Node) newNode(title string, t widget.WidgetType) *Node { } /* - raw create function for a new node struct + raw create function for a new node struct and increments the counter */ func addNode() *Node { n := new(Node) @@ -40,10 +52,13 @@ func addNode() *Node { } func (n *Node) Parent() *Node { + if ! n.Ready() { return n } return n.parent } func (n *Node) Delete(d *Node) { + if ! n.Ready() { return } + for i, child := range n.children { log.Log(NODE, "\t", i, child.id, child.progname) if (child.id == d.id) { diff --git a/plugin.go b/plugin.go index 6623fd4..ff4d512 100644 --- a/plugin.go +++ b/plugin.go @@ -214,48 +214,6 @@ func initToolkit(name string, filename string) *aplug { return newPlug } -// 2024/01/11 finally moving to type any. simplify to just 'value' -// 2023/05/09 pretty clean -// 2023/04/06 Queue() is also being used and channels are being used. -func newAction(n *Node, atype widget.ActionType) *widget.Action { - var a widget.Action - a.ActionType = atype - if (n == nil) { - return &a - } - a.WidgetId = n.id - a.ProgName = n.progname - a.Value = n.value - - a.X = n.X - a.Y = n.Y - - a.AtW = n.AtW - a.AtH = n.AtH - - if (n.parent != nil) { - a.ParentId = n.parent.id - } - a.WidgetType = n.WidgetType - return &a -} - -// sends the action/event to each toolkit via a golang plugin channel -func sendAction(a *widget.Action) { - for _, aplug := range allPlugins { - log.Log(PLUG, "Action() aplug =", aplug.name, "Action type=", a.ActionType) - if (aplug.pluginChan == nil) { - log.Info("Action() retrieving the aplug.PluginChannel()", aplug.name) - aplug.pluginChan = aplug.PluginChannel() - log.Info("Action() retrieved", aplug.pluginChan) - } - log.Info("Action() SEND to pluginChan", aplug.name) - aplug.pluginChan <- *a - // added during debugging. might be a good idea in general for a tactile experience - log.Sleep(.02) // this delay makes it so SetText() works on initial widget creation - } -} - func (n *Node) InitEmbed(resFS embed.FS) *Node { me.resFS = resFS return n diff --git a/setText.go b/setText.go index 8b90425..3cd21fb 100644 --- a/setText.go +++ b/setText.go @@ -3,64 +3,55 @@ package gui // Common actions for widgets like 'Enable' or 'Hide' import ( + "errors" + "go.wit.com/log" "go.wit.com/gui/widget" ) func (n *Node) SetText(text string) *Node { if ! n.Ready() { return n } + + if n.GetText() == text { + // nothing changed + return n + } + n.value = text + n.changed = true log.Log(CHANGE, "SetText() value =", text) - n.value = text - - if ! n.hidden { - a := newAction(n, widget.SetText) - a.Value = n.value - sendAction(a) - } + // inform the toolkits + sendAction(n, widget.SetText) return n } -/* -func convertString(val any) string { +func (n *Node) Set(val any) { + if ! n.Ready() { return } + switch v := val.(type) { case bool: - n.B = val.(bool) + if widget.GetBool(n.value) == val.(bool) { + // nothing changed + return + } case string: - n.label = val.(string) - n.S = val.(string) + if widget.GetString(n.value) == val.(string) { + // nothing changed + return + } case int: - n.I = val.(int) + if widget.GetInt(n.value) == val.(int) { + // nothing changed + return + } default: log.Error(errors.New("Set() unknown type"), "v =", v) } -} -*/ - -func (n *Node) Set(val any) { + n.value = val + n.changed = true log.Log(CHANGE, "Set() value =", val) - n.value = val - /* - n.value = val - - switch v := val.(type) { - case bool: - n.B = val.(bool) - case string: - n.label = val.(string) - n.S = val.(string) - case int: - n.I = val.(int) - default: - log.Error(errors.New("Set() unknown type"), "v =", v) - } - */ - - if ! n.hidden { - a := newAction(n, widget.Set) - a.Value = n.value - sendAction(a) - } + // inform the toolkits + sendAction(n, widget.Set) } diff --git a/slider.go b/slider.go index fd2b207..e5314d5 100644 --- a/slider.go +++ b/slider.go @@ -5,21 +5,18 @@ import ( "go.wit.com/gui/widget" ) -func (parent *Node) NewSlider(name string, x int, y int) *Node { - newNode := parent.newNode(name, widget.Slider) +func (parent *Node) NewSlider(progname string, x int, y int) *Node { + newNode := parent.newNode(progname, widget.Slider) + newNode.progname = progname newNode.Custom = func() { - log.Log(GUI, "even newer clicker() name in NewSlider name =", name) + log.Log(GUI, "even newer clicker() name in NewSlider name =", progname) } newNode.X = x newNode.Y = y - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - a.X = x - a.Y = y - sendAction(a) - } + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } diff --git a/spinner.go b/spinner.go index 301ed61..cba9d52 100644 --- a/spinner.go +++ b/spinner.go @@ -5,19 +5,18 @@ import ( "go.wit.com/gui/widget" ) -func (parent *Node) NewSpinner(name string, x int, y int) *Node { - newNode := parent.newNode(name, widget.Spinner) +func (parent *Node) NewSpinner(progname string, x int, y int) *Node { + newNode := parent.newNode(progname, widget.Spinner) + newNode.progname = progname newNode.Custom = func() { - log.Info("default NewSpinner() change", name) + log.Info("default NewSpinner() change", progname) } newNode.X = x newNode.Y = y - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } diff --git a/structs.go b/structs.go index 079cb6f..7139cea 100644 --- a/structs.go +++ b/structs.go @@ -27,14 +27,15 @@ var me guiConfig // almost all toolkits use integers so there doesn't // seem to be a good idea to use 'type any' here as it // just makes things more complicated for no good reason -type Range struct { +type RangeMovedToWidget struct { Low int High int } -type List []string +// type List []string type guiConfig struct { + // a toolkit requirement. never allow more than one per program initOnce sync.Once // This is the master node. The Binary Tree starts here @@ -52,28 +53,69 @@ type guiConfig struct { resFS embed.FS // used to beautify logging to Stdout - depth int - prefix string +// depth int +// prefix string } -// The Node is a binary tree. This is how all GUI elements are stored -// simply the name and the size of whatever GUI element exists +/* + The Node is a binary tree. This is how all GUI elements are stored + simply the name and the size of whatever GUI element exists + + value : most widgets need 1 value. this is it. + For a window -- the title. For a button -- the name + + hidden : this means the widget is not displayed yet. In that + case, don't waste time trying to pass information to + the toolkits. This makes things efficient and fast if + the GUI does not have to display anything + + Custom() : if the user does something like click on a button, + this function will be called. (this should probably + be renamed Callback() + + progname : a short name to reference the widgets in the debugger + n.NewButton("click here to send it").SetProgName("SENT") + + parent, children : the binary tree + + pad, margin, expand : re-think these names and clarify + +*/ + type Node struct { id int // should be unique - hidden bool // Sierpinski Carpet mode. It's there, but you can't see it. - pad bool // the toolkit may use this. it's up to the toolkit - margin bool // the toolkit may use this. it's up to the toolkit - expand bool // the toolkit may use this. it's up to the toolkit + hidden bool // don't update the toolkits when it's hidden + changed bool // do we need to inform the toolkit something changed? + enabled bool // if false, then the the user can't click on it WidgetType widget.WidgetType - // the current widget value. + // most widgets need one value, this is current alue value any // this can programatically identify the widget // The name must be unique progname string // a name useful for debugging + // for widgets that a user select from a list of strings + strings []string + + // how to arrange widgets + direction widget.Orientation + + // this function is run when there are mouse or keyboard events + Custom func() + + parent *Node + children []*Node + + + // RETHINK EVERYTHING BELOW HERE + pad bool // the toolkit may use this. it's up to the toolkit + margin bool // the toolkit may use this. it's up to the toolkit + expand bool // the toolkit may use this. it's up to the toolkit + + // used for Windows in toolkits measured in pixels width int height int @@ -95,11 +137,4 @@ type Node struct { // if this widget is in a grid, this is the position of a widget AtW int AtH int - - - // this function is run when there are mouse or keyboard events - Custom func() - - parent *Node - children []*Node } diff --git a/textbox.go b/textbox.go index 16837c0..d6ae8a4 100644 --- a/textbox.go +++ b/textbox.go @@ -8,20 +8,22 @@ import ( func (parent *Node) NewTextbox(name string) *Node { newNode := parent.newNode(name, widget.Textbox) + newNode.value = name + newNode.progname = name newNode.Custom = func() { log.Log(GUI, "NewTextbox changed =", name) } - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } func (parent *Node) NewEntryLine(name string) *Node { newNode := parent.newNode(name, widget.Textbox) + newNode.value = name + newNode.progname = name newNode.X = 1 @@ -29,9 +31,7 @@ func (parent *Node) NewEntryLine(name string) *Node { log.Log(GUI, "NewTextbox changed =", name) } - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } diff --git a/window.go b/window.go index c2e2b38..4bda74e 100644 --- a/window.go +++ b/window.go @@ -18,10 +18,8 @@ func (parent *Node) NewWindow(title string) *Node { newNode.progname = title newNode.value = title - if ! newNode.hidden { - a := newAction(newNode, widget.Add) - sendAction(a) - } + // inform the toolkits + sendAction(newNode, widget.Add) return newNode } @@ -40,26 +38,51 @@ func (parent *Node) RawWindow(title string) *Node { // TODO: should do this recursively func (n *Node) UnDraw() *Node { - if ! n.hidden { - n.Hide() - } + if ! n.Ready() { return n } + n.hidden = true + n.changed = true + + // inform the toolkits + sendAction(n, widget.Delete) return n } // TODO: should do this recursively func (n *Node) Draw() *Node { - n.hidden = false + if ! n.Ready() { return n } - a := newAction(n, widget.Add) - sendAction(a) + n.hidden = false + n.changed= true + + // inform the toolkits + sendAction(n, widget.Add) return n } // if the toolkit supports a gui with pixels, it might honor this. no promises // consider this a 'recommendation' or developer 'preference' to the toolkit +/* func (n *Node) PixelSize(w, h int) *Node { n.width = w n.height = w return n } +*/ + +func (n *Node) TestDraw() { + if (n == nil) { + return + } + + // enable and + n.hidden = false + n.changed = true + log.Warn("TestDraw() sending widget.Add", n.id, n.WidgetType, n.progname) + sendAction(n, widget.Add) + + for _, child := range n.children { + child.TestDraw() + } + return +}