diff options
| author | Jeff Carr <[email protected]> | 2024-01-13 22:02:12 -0600 |
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2024-01-13 22:02:12 -0600 |
| commit | 47b15946de10a75cda026a7317a90d4857b453c8 (patch) | |
| tree | ab6a8c085226263982d3b19f2913e540707af2a1 | |
| parent | 4ef8409eeadcd4a359b7593b5ea35f9f523bfb64 (diff) | |
work on hiding widgetsv0.12.5
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 <[email protected]>
| -rw-r--r-- | action.go | 68 | ||||
| -rw-r--r-- | box.go | 48 | ||||
| -rw-r--r-- | button.go | 25 | ||||
| -rw-r--r-- | checkbox.go | 6 | ||||
| -rw-r--r-- | common.go | 296 | ||||
| -rw-r--r-- | debug.go | 6 | ||||
| -rw-r--r-- | dropdown.go | 33 | ||||
| -rw-r--r-- | grid.go | 15 | ||||
| -rw-r--r-- | group.go | 11 | ||||
| -rw-r--r-- | image.go | 9 | ||||
| -rw-r--r-- | label.go | 6 | ||||
| -rw-r--r-- | main.go | 38 | ||||
| -rw-r--r-- | node.go | 17 | ||||
| -rw-r--r-- | plugin.go | 42 | ||||
| -rw-r--r-- | setText.go | 67 | ||||
| -rw-r--r-- | slider.go | 15 | ||||
| -rw-r--r-- | spinner.go | 13 | ||||
| -rw-r--r-- | structs.go | 71 | ||||
| -rw-r--r-- | textbox.go | 16 | ||||
| -rw-r--r-- | window.go | 41 |
20 files changed, 495 insertions, 348 deletions
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 + } +} @@ -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 } @@ -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 } @@ -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) - } - return n -} + if ! n.Ready() { return n } + // if n.enabled { return n } -func (n *Node) Disable() *Node { - if ! n.hidden { - a := newAction(n, widget.Disable) - sendAction(a) - } + n.enabled = true + n.changed = true + + // inform the toolkits + sendAction(n, widget.Enable) return n } -func (n *Node) Add(str string) { - log.Log(GUI, "gui.Add() value =", str) +// disables a widget so the user can see it, but can not +// interact or change it. +func (n *Node) Disable() *Node { + if ! n.Ready() { return n } + // if ! n.enabled { return n } - n.value = str + n.enabled = false + n.changed = true - if ! n.hidden { - a := newAction(n, widget.Add) - sendAction(a) - } + // inform the toolkits + sendAction(n, widget.Disable) + return n } -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,48 +116,31 @@ 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 { - if ! n.Ready() { return "" } - return n.progname -} +func (n *Node) SetProgName(s string) { + if ! n.Ready() { return } -/* -// string handling examples that might be helpful for normalizeInt() -isAlpha := regexp.MustCompile(`^[A-Za-z]+$`).MatchString + if n.progname == s { + // don't do anything since nothing changed + return + } -for _, username := range []string{"userone", "user2", "user-three"} { - if !isAlpha(username) { - log.Log(GUI, "%q is not valid\n", username) - } + n.changed = true + n.progname = s + return } -const alpha = "abcdefghijklmnopqrstuvwxyz" - -func alphaOnly(s string) bool { - for _, char := range s { - if !strings.Contains(alpha, strings.ToLower(string(char))) { - return false - } - } - return true -} +/* + 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 normalizeInt(s string) string { - // reg, err := regexp.Compile("[^a-zA-Z0-9]+") - reg, err := regexp.Compile("[^0-9]+") - if err != nil { - log.Log(GUI, "normalizeInt() regexp.Compile() ERROR =", err) - return s - } - clean := reg.ReplaceAllString(s, "") - log.Log(GUI, "normalizeInt() s =", clean) - return clean +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) @@ -156,79 +151,102 @@ func commonCallback(n *Node) { n.Custom() } } +*/ func (n *Node) Margin() *Node { + if ! n.Ready() { return n } + if n.margin { return n } + n.margin = true - if ! n.hidden { - a := newAction(n, widget.Margin) - sendAction(a) - } + 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 - if ! n.hidden { - a := newAction(n, widget.Unmargin) - sendAction(a) - } + 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 - if ! n.hidden { - a := newAction(n, widget.Pad) - sendAction(a) - } + 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 - if ! n.hidden { - a := newAction(n, widget.Unpad) - sendAction(a) - } + 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 - if ! n.hidden { - a := newAction(n, widget.Pad) - a.Expand = true - sendAction(a) - } + n.changed = true + + // inform the toolkits + sendAction(n, widget.SetExpand) 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) SetExpand(b bool) *Node { + if ! n.Ready() { return n } + if n.expand == b { return n } // nothing changed -/* -func (n *Node) Window(title string) *Node { - log.Warn("Window()", n) - return n.NewWindow(title) + n.expand = b + n.changed = true + + // inform the toolkits + sendAction(n, widget.SetExpand) + return n } -*/ -func (n *Node) ProgName() string { - if ! n.Ready() { return "" } - return n.progname +// 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 + // 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 /* @@ -242,3 +260,63 @@ func (n *Node) SetMargin() *Node { 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 + +for _, username := range []string{"userone", "user2", "user-three"} { + if !isAlpha(username) { + log.Log(GUI, "%q is not valid\n", username) + } +} + +const alpha = "abcdefghijklmnopqrstuvwxyz" + +func alphaOnly(s string) bool { + for _, char := range s { + if !strings.Contains(alpha, strings.ToLower(string(char))) { + return false + } + } + return true +} +*/ + +func normalizeInt(s string) string { + // reg, err := regexp.Compile("[^a-zA-Z0-9]+") + reg, err := regexp.Compile("[^0-9]+") + if err != nil { + log.Log(GUI, "normalizeInt() regexp.Compile() ERROR =", err) + return s + } + clean := reg.ReplaceAllString(s, "") + log.Log(GUI, "normalizeInt() s =", clean) + return clean +} @@ -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 } @@ -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 } @@ -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 } @@ -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 } @@ -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 } @@ -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? @@ -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) { @@ -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 @@ -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 } - log.Log(CHANGE, "SetText() value =", text) + if n.GetText() == text { + // nothing changed + return n + } n.value = text + n.changed = true + log.Log(CHANGE, "SetText() 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 { - 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) - } -} -*/ - - func (n *Node) Set(val any) { - log.Log(CHANGE, "Set() value =", val) - - n.value = val - /* - n.value = val + 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) } - */ - if ! n.hidden { - a := newAction(n, widget.Set) - a.Value = n.value - sendAction(a) - } + n.value = val + n.changed = true + log.Log(CHANGE, "Set() value =", val) + + // inform the toolkits + sendAction(n, widget.Set) } @@ -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 } @@ -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 } @@ -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 } @@ -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 } @@ -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 { + if ! n.Ready() { return n } + n.hidden = false + n.changed= true - a := newAction(n, widget.Add) - sendAction(a) + // 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 +} |
