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 <jcarr@wit.com>
This commit is contained in:
Jeff Carr 2024-01-13 22:02:12 -06:00
parent 1e2fa2dce9
commit 732edc3faf
20 changed files with 529 additions and 382 deletions

68
action.go Normal file
View File

@ -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
}
}

48
box.go
View File

@ -4,39 +4,39 @@ import (
"go.wit.com/gui/widget" "go.wit.com/gui/widget"
) )
func (parent *Node) NewBox(name string, b bool) *Node { func (parent *Node) NewBox(progname string, b bool) *Node {
newNode := parent.newNode(name, widget.Box) newNode := parent.newNode(progname, widget.Box)
newNode.progname = progname
if ! newNode.hidden { if b {
a := newAction(newNode, widget.Add) newNode.direction = widget.Horizontal
if b { } else {
a.Direction = widget.Horizontal newNode.direction = widget.Vertical
} else {
a.Direction = widget.Vertical
}
sendAction(a)
} }
// inform the toolkits
sendAction(newNode, widget.Add)
return newNode return newNode
} }
func (parent *Node) NewHorizontalBox(name string) *Node { func (parent *Node) NewHorizontalBox(progname string) *Node {
newNode := parent.newNode(name, widget.Box) newNode := parent.newNode(progname, widget.Box)
newNode.progname = progname
if ! newNode.hidden { newNode.direction = widget.Horizontal
a := newAction(newNode, widget.Add)
a.Direction = widget.Horizontal // inform the toolkits
sendAction(a) sendAction(newNode, widget.Add)
}
return newNode return newNode
} }
func (parent *Node) NewVerticalBox(name string) *Node { func (parent *Node) NewVerticalBox(progname string) *Node {
newNode := parent.newNode(name, widget.Box) newNode := parent.newNode(progname, widget.Box)
newNode.progname = progname
if ! newNode.hidden { newNode.direction = widget.Vertical
a := newAction(newNode, widget.Add)
a.Direction = widget.Vertical // inform the toolkits
sendAction(a) sendAction(newNode, widget.Add)
}
return newNode return newNode
} }

View File

@ -8,28 +8,7 @@ func (parent *Node) NewButton(name string, custom func()) *Node {
newNode.value = name newNode.value = name
newNode.progname = name newNode.progname = name
if ! newNode.hidden { // inform the toolkits
a := newAction(newNode, widget.Add) sendAction(newNode, widget.Add)
sendAction(a)
}
return newNode 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
}

View File

@ -11,9 +11,7 @@ func (n *Node) NewCheckbox(name string) *Node {
newNode.value = name newNode.value = name
newNode.progname = name newNode.progname = name
if ! newNode.hidden { // inform the toolkits
a := newAction(newNode, widget.Add) sendAction(newNode, widget.Add)
sendAction(a)
}
return newNode return newNode
} }

360
common.go
View File

@ -11,79 +11,91 @@ import (
// functions for handling text related GUI elements // functions for handling text related GUI elements
func (n *Node) Show() *Node { func (n *Node) Show() *Node {
if ! n.hidden { if ! n.Ready() { return n }
a := newAction(n, widget.Show) if ! n.Hidden() { return n }
sendAction(a)
} n.hidden = false
n.changed = true
// inform the toolkits
sendAction(n, widget.Show)
return n return n
} }
func (n *Node) Hide() *Node { func (n *Node) Hide() *Node {
if ! n.hidden { if ! n.Ready() { return n }
a := newAction(n, widget.Hide) if n.Hidden() { return n }
sendAction(a)
} n.hidden = true
n.changed = true
// inform the toolkits
sendAction(n, widget.Hide)
return n 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 { func (n *Node) Enable() *Node {
if ! n.hidden { if ! n.Ready() { return n }
a := newAction(n, widget.Enable) // if n.enabled { return n }
sendAction(a)
} n.enabled = true
n.changed = true
// inform the toolkits
sendAction(n, widget.Enable)
return n return n
} }
// disables a widget so the user can see it, but can not
// interact or change it.
func (n *Node) Disable() *Node { func (n *Node) Disable() *Node {
if ! n.hidden { if ! n.Ready() { return n }
a := newAction(n, widget.Disable) // if ! n.enabled { return n }
sendAction(a)
} n.enabled = false
n.changed = true
// inform the toolkits
sendAction(n, widget.Disable)
return n return n
} }
func (n *Node) Add(str string) {
log.Log(GUI, "gui.Add() value =", str)
n.value = str // add a new text string to widgets that support
// multiple string values
if ! n.hidden { // These must be unique. return false if the string already exists
a := newAction(n, widget.Add) func (n *Node) AddText(str string) bool {
sendAction(a) if ! n.Ready() { return false }
}
}
func (n *Node) AddText(str string) {
log.Log(CHANGE, "AddText() value =", str) log.Log(CHANGE, "AddText() value =", str)
n.value = str n.value = str
// TODO: make sure these are unique
n.strings = append(n.strings, str)
if ! n.hidden { // inform the toolkits
a := newAction(n, widget.AddText) sendAction(n, widget.AddText)
sendAction(a) 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) { func (n *Node) AppendText(str string) {
if ! n.Ready() { return }
tmp := widget.GetString(n.value) + str tmp := widget.GetString(n.value) + str
n.value = tmp n.value = tmp
n.changed = true
if ! n.hidden { // inform the toolkits
a := newAction(n, widget.SetText) sendAction(n, widget.SetText)
sendAction(a)
}
} }
// THESE TWO FUNCTIONS ARE TERRIBLY NAMED AND NEED TO BE FIXED // THESE TWO FUNCTIONS ARE TERRIBLY NAMED AND NEED TO BE FIXED
// 5 seconds worth of ideas: // 5 seconds worth of ideas:
// Value() ? // Value() ?
// Progname() Reference() ? // 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 // get a string from the widget
func (n *Node) GetText() string { func (n *Node) GetText() string {
@ -104,14 +116,177 @@ func (n *Node) GetBool() bool {
} }
// should get the reference name used for programming and debugging // should get the reference name used for programming and debugging
// myButton = myGroup.NewButton("hit ball", nil).SetName("HIT") func (n *Node) SetProgName(s string) {
// myButton.GetName() should return "HIT" if ! n.Ready() { return }
// n = Find("HIT") should return myButton
func (n *Node) GetName() string { 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 "" } if ! n.Ready() { return "" }
return n.progname 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() // string handling examples that might be helpful for normalizeInt()
isAlpha := regexp.MustCompile(`^[A-Za-z]+$`).MatchString isAlpha := regexp.MustCompile(`^[A-Za-z]+$`).MatchString
@ -145,100 +320,3 @@ func normalizeInt(s string) string {
log.Log(GUI, "normalizeInt() s =", clean) log.Log(GUI, "normalizeInt() s =", clean)
return 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
}
*/

View File

@ -38,10 +38,8 @@ func (n *Node) Dump() {
} }
Indent(b, "NODE DUMP END") Indent(b, "NODE DUMP END")
a := new(widget.Action) n.changed = true
a.ActionType = widget.Dump sendAction(n, widget.Dump)
a.WidgetId = n.id
sendAction(a)
} }
func Indent(b bool, a ...interface{}) { func Indent(b bool, a ...interface{}) {

View File

@ -6,41 +6,38 @@ package gui
// since it is the same. confusing names? maybe... // since it is the same. confusing names? maybe...
import ( import (
"go.wit.com/log"
"go.wit.com/gui/widget" "go.wit.com/gui/widget"
) )
// add a new entry to the dropdown name // add a new entry to the dropdown name
func (n *Node) AddDropdownName(name string) { func (n *Node) AddDropdownName(name string) {
if ! n.Ready() { return }
log.Warn("AddDropdownName() deprecated")
n.AddText(name) n.AddText(name)
} }
// Set the dropdown menu to 'name' // Set the dropdown menu to 'name'
func (n *Node) SetDropdownName(name string) { func (n *Node) SetDropdownName(name string) {
if ! n.Ready() { return }
log.Warn("SetDropdownName() deprecated")
n.SetText(name) n.SetText(name)
} }
func (n *Node) NewDropdown(name string) *Node { func (n *Node) NewDropdown(progname string) *Node {
newNode := n.newNode(name, widget.Dropdown) newNode := n.newNode(progname, widget.Dropdown)
newNode.progname = name newNode.progname = progname
newNode.value = name
if ! newNode.hidden {
a := newAction(newNode, widget.Add)
sendAction(a)
}
// inform the toolkits
sendAction(newNode, widget.Add)
return newNode return newNode
} }
func (n *Node) NewCombobox(name string) *Node { func (n *Node) NewCombobox(progname string) *Node {
newNode := n.newNode(name, widget.Combobox) newNode := n.newNode(progname, widget.Combobox)
newNode.progname = name newNode.progname = progname
newNode.value = name
if ! newNode.hidden {
a := newAction(newNode, widget.Add)
sendAction(a)
}
// inform the toolkits
sendAction(newNode, widget.Add)
return newNode return newNode
} }

15
grid.go
View File

@ -33,21 +33,20 @@ type GridOffset struct {
Y int Y int
} }
func (n *Node) NewGrid(name string, w int, h int) *Node { func (n *Node) NewGrid(progname string, w int, h int) *Node {
newNode := n.newNode(name, widget.Grid) newNode := n.newNode(progname, widget.Grid)
newNode.progname = progname
newNode.W = w newNode.W = w
newNode.H = h newNode.H = h
newNode.NextW = 1 newNode.NextW = 1
newNode.NextH = 1 newNode.NextH = 1
if ! newNode.hidden {
a := newAction(newNode, widget.Add)
sendAction(a)
}
// by default, always pad grids // by default, always pad grids
newNode.Pad() newNode.pad = true
// inform the toolkits
sendAction(newNode, widget.Add)
return newNode return newNode
} }

View File

@ -13,14 +13,7 @@ func (parent *Node) NewGroup(name string) *Node {
newNode.progname = name newNode.progname = name
newNode.value = name newNode.value = name
if ! newNode.hidden { // inform the toolkits
a := newAction(newNode, widget.Add) sendAction(newNode, widget.Add)
sendAction(a)
}
// by default, always pad groups
newNode.Pad()
// newBox := newNode.NewBox("defaultGroupBox", false)
return newNode return newNode
} }

View File

@ -1,6 +1,7 @@
package gui package gui
import ( import (
"go.wit.com/log"
"go.wit.com/gui/widget" "go.wit.com/gui/widget"
) )
@ -8,9 +9,9 @@ func (parent *Node) NewImage(name string) *Node {
var newNode *Node var newNode *Node
newNode = parent.newNode(name, widget.Image) newNode = parent.newNode(name, widget.Image)
if ! newNode.hidden { log.Warn("NewImage() not implemented. fix this")
a := newAction(newNode, widget.Add)
sendAction(a) // inform the toolkits
} sendAction(newNode, widget.Add)
return newNode return newNode
} }

View File

@ -9,9 +9,7 @@ func (parent *Node) NewLabel(text string) *Node {
newNode.value = text newNode.value = text
newNode.progname = text newNode.progname = text
if ! newNode.hidden { // inform the toolkits
a := newAction(newNode, widget.Add) sendAction(newNode, widget.Add)
sendAction(a)
}
return newNode return newNode
} }

38
main.go
View File

@ -19,7 +19,7 @@ func init() {
log.Log(NOW, "init() has been run") log.Log(NOW, "init() has been run")
me.counter = 0 me.counter = 0
me.prefix = "wit" // me.prefix = "wit"
// Populates the top of the binary tree // Populates the top of the binary tree
me.rootNode = addNode() me.rootNode = addNode()
@ -38,30 +38,50 @@ func init() {
go watchCallback() 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() { func watchCallback() {
log.Info("watchCallback() START") log.Info("guiChan() START")
for { for {
log.Info("watchCallback() restarted select for toolkit user events") log.Info("guiChan() restarted")
select { select {
case a := <-me.guiChan: case a := <-me.guiChan:
if (a.ActionType == widget.UserQuit) { if (a.ActionType == widget.UserQuit) {
log.Info("doUserEvent() User sent Quit()") log.Warn("guiChan() User sent Quit()")
me.rootNode.doCustom() me.rootNode.doCustom()
log.Exit("wit/gui toolkit.UserQuit") log.Exit("wit/gui toolkit.UserQuit")
break break
} }
if (a.ActionType == widget.EnableDebug) { if (a.ActionType == widget.EnableDebug) {
log.Warn("doUserEvent() Enable Debugging Window") log.Warn("guiChan() Enable Debugging Window")
log.Warn("doUserEvent() TODO: not implemented") log.Warn("guiChan() TODO: not implemented")
// DebugWindow() // DebugWindow()
break break
} }
n := me.rootNode.FindId(a.WidgetId) n := me.rootNode.findId(a.WidgetId)
if (n == nil) { 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 { } else {
log.Info("watchCallback() FOUND widget id =", n.id, n.progname) log.Verbose("guiChan() FOUND widget id =", n.id, n.progname)
n.doUserEvent(a) n.doUserEvent(a)
} }
// this maybe a good idea? // this maybe a good idea?

17
node.go
View File

@ -7,14 +7,26 @@ import (
/* /*
generic function to create a new node on the binary tree 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 { func (n *Node) newNode(title string, t widget.WidgetType) *Node {
var newN *Node var newN *Node
newN = addNode() newN = addNode()
newN.progname = title
newN.value = title newN.value = title
newN.WidgetType = t newN.WidgetType = t
// set these defaults
newN.expand = true
newN.pad = true
newN.enabled = true
newN.changed = true
if n.WidgetType == widget.Grid { if n.WidgetType == widget.Grid {
n.gridIncrement() 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 { func addNode() *Node {
n := new(Node) n := new(Node)
@ -40,10 +52,13 @@ func addNode() *Node {
} }
func (n *Node) Parent() *Node { func (n *Node) Parent() *Node {
if ! n.Ready() { return n }
return n.parent return n.parent
} }
func (n *Node) Delete(d *Node) { func (n *Node) Delete(d *Node) {
if ! n.Ready() { return }
for i, child := range n.children { for i, child := range n.children {
log.Log(NODE, "\t", i, child.id, child.progname) log.Log(NODE, "\t", i, child.id, child.progname)
if (child.id == d.id) { if (child.id == d.id) {

View File

@ -214,48 +214,6 @@ func initToolkit(name string, filename string) *aplug {
return newPlug 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 { func (n *Node) InitEmbed(resFS embed.FS) *Node {
me.resFS = resFS me.resFS = resFS
return n return n

View File

@ -3,64 +3,55 @@ package gui
// Common actions for widgets like 'Enable' or 'Hide' // Common actions for widgets like 'Enable' or 'Hide'
import ( import (
"errors"
"go.wit.com/log" "go.wit.com/log"
"go.wit.com/gui/widget" "go.wit.com/gui/widget"
) )
func (n *Node) SetText(text string) *Node { func (n *Node) SetText(text string) *Node {
if ! n.Ready() { return n } 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) log.Log(CHANGE, "SetText() value =", text)
n.value = text // inform the toolkits
sendAction(n, widget.SetText)
if ! n.hidden {
a := newAction(n, widget.SetText)
a.Value = n.value
sendAction(a)
}
return n return n
} }
/* func (n *Node) Set(val any) {
func convertString(val any) string { if ! n.Ready() { return }
switch v := val.(type) { switch v := val.(type) {
case bool: case bool:
n.B = val.(bool) if widget.GetBool(n.value) == val.(bool) {
// nothing changed
return
}
case string: case string:
n.label = val.(string) if widget.GetString(n.value) == val.(string) {
n.S = val.(string) // nothing changed
return
}
case int: case int:
n.I = val.(int) if widget.GetInt(n.value) == val.(int) {
// nothing changed
return
}
default: default:
log.Error(errors.New("Set() unknown type"), "v =", v) log.Error(errors.New("Set() unknown type"), "v =", v)
} }
}
*/
n.value = val
func (n *Node) Set(val any) { n.changed = true
log.Log(CHANGE, "Set() value =", val) log.Log(CHANGE, "Set() value =", val)
n.value = val // inform the toolkits
/* sendAction(n, widget.Set)
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)
}
} }

View File

@ -5,21 +5,18 @@ import (
"go.wit.com/gui/widget" "go.wit.com/gui/widget"
) )
func (parent *Node) NewSlider(name string, x int, y int) *Node { func (parent *Node) NewSlider(progname string, x int, y int) *Node {
newNode := parent.newNode(name, widget.Slider) newNode := parent.newNode(progname, widget.Slider)
newNode.progname = progname
newNode.Custom = func() { 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.X = x
newNode.Y = y 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 return newNode
} }

View File

@ -5,19 +5,18 @@ import (
"go.wit.com/gui/widget" "go.wit.com/gui/widget"
) )
func (parent *Node) NewSpinner(name string, x int, y int) *Node { func (parent *Node) NewSpinner(progname string, x int, y int) *Node {
newNode := parent.newNode(name, widget.Spinner) newNode := parent.newNode(progname, widget.Spinner)
newNode.progname = progname
newNode.Custom = func() { newNode.Custom = func() {
log.Info("default NewSpinner() change", name) log.Info("default NewSpinner() change", progname)
} }
newNode.X = x newNode.X = x
newNode.Y = y newNode.Y = y
if ! newNode.hidden { // inform the toolkits
a := newAction(newNode, widget.Add) sendAction(newNode, widget.Add)
sendAction(a)
}
return newNode return newNode
} }

View File

@ -27,14 +27,15 @@ var me guiConfig
// almost all toolkits use integers so there doesn't // almost all toolkits use integers so there doesn't
// seem to be a good idea to use 'type any' here as it // seem to be a good idea to use 'type any' here as it
// just makes things more complicated for no good reason // just makes things more complicated for no good reason
type Range struct { type RangeMovedToWidget struct {
Low int Low int
High int High int
} }
type List []string // type List []string
type guiConfig struct { type guiConfig struct {
// a toolkit requirement. never allow more than one per program
initOnce sync.Once initOnce sync.Once
// This is the master node. The Binary Tree starts here // This is the master node. The Binary Tree starts here
@ -52,28 +53,69 @@ type guiConfig struct {
resFS embed.FS resFS embed.FS
// used to beautify logging to Stdout // used to beautify logging to Stdout
depth int // depth int
prefix string // 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 { type Node struct {
id int // should be unique id int // should be unique
hidden bool // Sierpinski Carpet mode. It's there, but you can't see it. hidden bool // don't update the toolkits when it's hidden
pad bool // the toolkit may use this. it's up to the toolkit changed bool // do we need to inform the toolkit something changed?
margin bool // the toolkit may use this. it's up to the toolkit enabled bool // if false, then the the user can't click on it
expand bool // the toolkit may use this. it's up to the toolkit
WidgetType widget.WidgetType WidgetType widget.WidgetType
// the current widget value. // most widgets need one value, this is current alue
value any value any
// this can programatically identify the widget // this can programatically identify the widget
// The name must be unique // The name must be unique
progname string // a name useful for debugging 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 // used for Windows in toolkits measured in pixels
width int width int
height int height int
@ -95,11 +137,4 @@ type Node struct {
// if this widget is in a grid, this is the position of a widget // if this widget is in a grid, this is the position of a widget
AtW int AtW int
AtH int AtH int
// this function is run when there are mouse or keyboard events
Custom func()
parent *Node
children []*Node
} }

View File

@ -8,20 +8,22 @@ import (
func (parent *Node) NewTextbox(name string) *Node { func (parent *Node) NewTextbox(name string) *Node {
newNode := parent.newNode(name, widget.Textbox) newNode := parent.newNode(name, widget.Textbox)
newNode.value = name
newNode.progname = name
newNode.Custom = func() { newNode.Custom = func() {
log.Log(GUI, "NewTextbox changed =", name) log.Log(GUI, "NewTextbox changed =", name)
} }
if ! newNode.hidden { // inform the toolkits
a := newAction(newNode, widget.Add) sendAction(newNode, widget.Add)
sendAction(a)
}
return newNode return newNode
} }
func (parent *Node) NewEntryLine(name string) *Node { func (parent *Node) NewEntryLine(name string) *Node {
newNode := parent.newNode(name, widget.Textbox) newNode := parent.newNode(name, widget.Textbox)
newNode.value = name
newNode.progname = name
newNode.X = 1 newNode.X = 1
@ -29,9 +31,7 @@ func (parent *Node) NewEntryLine(name string) *Node {
log.Log(GUI, "NewTextbox changed =", name) log.Log(GUI, "NewTextbox changed =", name)
} }
if ! newNode.hidden { // inform the toolkits
a := newAction(newNode, widget.Add) sendAction(newNode, widget.Add)
sendAction(a)
}
return newNode return newNode
} }

View File

@ -18,10 +18,8 @@ func (parent *Node) NewWindow(title string) *Node {
newNode.progname = title newNode.progname = title
newNode.value = title newNode.value = title
if ! newNode.hidden { // inform the toolkits
a := newAction(newNode, widget.Add) sendAction(newNode, widget.Add)
sendAction(a)
}
return newNode return newNode
} }
@ -40,26 +38,51 @@ func (parent *Node) RawWindow(title string) *Node {
// TODO: should do this recursively // TODO: should do this recursively
func (n *Node) UnDraw() *Node { func (n *Node) UnDraw() *Node {
if ! n.hidden { if ! n.Ready() { return n }
n.Hide()
}
n.hidden = true n.hidden = true
n.changed = true
// inform the toolkits
sendAction(n, widget.Delete)
return n return n
} }
// TODO: should do this recursively // TODO: should do this recursively
func (n *Node) Draw() *Node { func (n *Node) Draw() *Node {
n.hidden = false if ! n.Ready() { return n }
a := newAction(n, widget.Add) n.hidden = false
sendAction(a) n.changed= true
// inform the toolkits
sendAction(n, widget.Add)
return n return n
} }
// if the toolkit supports a gui with pixels, it might honor this. no promises // if the toolkit supports a gui with pixels, it might honor this. no promises
// consider this a 'recommendation' or developer 'preference' to the toolkit // consider this a 'recommendation' or developer 'preference' to the toolkit
/*
func (n *Node) PixelSize(w, h int) *Node { func (n *Node) PixelSize(w, h int) *Node {
n.width = w n.width = w
n.height = w n.height = w
return n 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
}