Compare commits

..

No commits in common. "guimaster" and "v0.12.3" have entirely different histories.

25 changed files with 419 additions and 583 deletions

View File

@ -28,6 +28,20 @@ update:
git pull git pull
go get -v -t -u ./... go get -v -t -u ./...
# sync repo to the github backup
# git remote add github git@github.com:wit-go/gui.git
github:
git push origin master
git push origin devel
git push origin --tags
git push github master
git push github devel
git push github --tags
@echo
@echo check https://git.wit.org/gui/gui
@echo check https://github.com/wit-go/gui
@echo
doc: doc:
godoc -v godoc -v

View File

@ -1,66 +0,0 @@
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.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
}
}

View File

@ -1,8 +1,8 @@
package gui package gui
import ( import (
"go.wit.com/arg"
"go.wit.com/log" "go.wit.com/log"
"go.wit.com/dev/alexflint/arg"
) )
var INFO *log.LogFlag var INFO *log.LogFlag

44
box.go
View File

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

View File

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

382
common.go
View File

@ -11,121 +11,79 @@ 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.Ready() { return n } if ! n.hidden {
if ! n.Hidden() { return n } a := newAction(n, widget.Show)
sendAction(a)
n.hidden = false
n.changed = true
if n.WidgetType == widget.Window {
log.Warn("Show on a hidden window", n.progname)
log.Warn("this needs to do TestDraw()")
n.TestDraw()
return n
} }
// inform the toolkits
sendAction(n, widget.Show)
return n return n
} }
func (n *Node) Hide() *Node { func (n *Node) Hide() *Node {
if ! n.Ready() { return n } if ! n.hidden {
if n.Hidden() { return n } a := newAction(n, widget.Hide)
sendAction(a)
if n.WidgetType == widget.Window {
log.Warn("Hide on a window", n.progname)
log.Warn("this needs to do TestDestroy() ?")
n.Destroy()
n.hidden = true
n.changed = true
return nil
} }
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.Ready() { return n } if ! n.hidden {
// if n.enabled { return n } a := newAction(n, widget.Enable)
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.Ready() { return n } if ! n.hidden {
// if ! n.enabled { return n } a := newAction(n, widget.Disable)
sendAction(a)
n.enabled = false }
n.changed = true
// inform the toolkits
sendAction(n, widget.Disable)
return n return n
} }
func (n *Node) Destroy() { func (n *Node) Add(str string) {
if ! n.Ready() { return } log.Log(GUI, "gui.Add() value =", str)
// if ! n.enabled { return }
n.enabled = false n.value = str
n.changed = true
// inform the toolkits if ! n.hidden {
sendAction(n, widget.Delete) a := newAction(n, widget.Add)
return 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) log.Log(CHANGE, "AddText() value =", str)
n.value = str n.value = str
// TODO: make sure these are unique
n.strings = append(n.strings, str)
// inform the toolkits if ! n.hidden {
sendAction(n, widget.AddText) a := newAction(n, widget.AddText)
return true sendAction(a)
}
} }
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
// inform the toolkits if ! n.hidden {
sendAction(n, widget.SetText) a := newAction(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 {
@ -146,179 +104,14 @@ 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
func (n *Node) SetProgName(s string) *Node { // myButton = myGroup.NewButton("hit ball", nil).SetName("HIT")
if ! n.Ready() { return n } // myButton.GetName() should return "HIT"
// n = Find("HIT") should return myButton
if n.progname == s { func (n *Node) GetName() string {
// don't do anything since nothing changed
return n
}
n.changed = true
n.progname = s
return n
}
/*
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
log.Warn("Margin()", n.WidgetType, n.progname)
// 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
log.Warn("Pad()", n.WidgetType, n.progname)
// 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
@ -352,3 +145,100 @@ 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,8 +38,10 @@ func (n *Node) Dump() {
} }
Indent(b, "NODE DUMP END") Indent(b, "NODE DUMP END")
n.changed = true a := new(widget.Action)
sendAction(n, widget.Dump) a.ActionType = widget.Dump
a.WidgetId = n.id
sendAction(a)
} }
func Indent(b bool, a ...interface{}) { func Indent(b bool, a ...interface{}) {

View File

@ -6,38 +6,41 @@ 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(progname string) *Node { func (n *Node) NewDropdown(name string) *Node {
newNode := n.newNode(progname, widget.Dropdown) newNode := n.newNode(name, widget.Dropdown)
newNode.progname = progname newNode.progname = name
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(progname string) *Node { func (n *Node) NewCombobox(name string) *Node {
newNode := n.newNode(progname, widget.Combobox) newNode := n.newNode(name, widget.Combobox)
newNode.progname = progname newNode.progname = name
newNode.value = name
if ! newNode.hidden {
a := newAction(newNode, widget.Add)
sendAction(a)
}
// inform the toolkits
sendAction(newNode, widget.Add)
return newNode return newNode
} }

10
go.mod
View File

@ -3,12 +3,12 @@ module go.wit.com/gui/gui
go 1.21.4 go 1.21.4
require ( require (
go.wit.com/dev/alexflint/arg v1.4.5 github.com/alexflint/go-arg v1.4.3
go.wit.com/gui/widget v1.1.3 go.wit.com/gui/widget v0.0.0-20240105185907-84aafa536a93
go.wit.com/log v0.5.4 go.wit.com/log v0.3.1
) )
require ( require (
go.wit.com/dev/alexflint/scalar v1.2.1 // indirect github.com/alexflint/go-scalar v1.1.0 // indirect
go.wit.com/dev/davecgh/spew v1.1.4 // indirect go.wit.com/spew v0.0.0-20240101141411-c7b8e91573c9 // indirect
) )

32
go.sum
View File

@ -1,10 +1,22 @@
go.wit.com/dev/alexflint/arg v1.4.5 h1:asDx5f9IlfpknKjPBqqb2qndE91Pbo7ZDkWUgddfMhY= github.com/alexflint/go-arg v1.4.3 h1:9rwwEBpMXfKQKceuZfYcwuc/7YY7tWJbFsgG5cAU/uo=
go.wit.com/dev/alexflint/arg v1.4.5/go.mod h1:wnWc+c6z8kSdDKYriMf6RpM+FiXmo5RYp/t4FNi0MU0= github.com/alexflint/go-arg v1.4.3/go.mod h1:3PZ/wp/8HuqRZMUUgu7I+e1qcpUbvmS258mRXkFH4IA=
go.wit.com/dev/alexflint/scalar v1.2.1 h1:loXOcbVnd+8YeJRLey+XXidecBiedMDO00zQ26TvKNs= github.com/alexflint/go-scalar v1.1.0 h1:aaAouLLzI9TChcPXotr6gUhq+Scr8rl0P9P4PnltbhM=
go.wit.com/dev/alexflint/scalar v1.2.1/go.mod h1:+rYsfxqdI2cwA8kJ7GCMwWbNJvfvWUurOCXLiwdTtSs= github.com/alexflint/go-scalar v1.1.0/go.mod h1:LoFvNMqS1CPrMVltza4LvnGKhaSpc3oyLEBUZVhhS2o=
go.wit.com/dev/davecgh/spew v1.1.4 h1:C9hj/rjlUpdK+E6aroyLjCbS5MFcyNUOuP1ICLWdNek= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
go.wit.com/dev/davecgh/spew v1.1.4/go.mod h1:sihvWmnQ/09FWplnEmozt90CCVqBtGuPXM811tgfhFA= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
go.wit.com/gui/widget v1.1.3 h1:GvLzGSOF9tfmoh6HNbFdN+NSlBo2qeS/Ba2TnQQ1A1U= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
go.wit.com/gui/widget v1.1.3/go.mod h1:A6/FaiFQtAHTjgo7c4FrokXe6bXX1Cowo35b2Lgi31E= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
go.wit.com/log v0.5.4 h1:vijLRPTUgChb8J5tx/7Uma/lGTUxeSXosFbheAmL914= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
go.wit.com/log v0.5.4/go.mod h1:BaJBfHFqcJSJLXGQ9RHi3XVhPgsStxSMZRlaRxW4kAo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
go.wit.com/gui/widget v0.0.0-20240105185907-84aafa536a93 h1:zCzaHvXJJ/rWXmDc/v79VvM6W2lxxzJGfnW2lHCv3Ho=
go.wit.com/gui/widget v0.0.0-20240105185907-84aafa536a93/go.mod h1:A6/FaiFQtAHTjgo7c4FrokXe6bXX1Cowo35b2Lgi31E=
go.wit.com/log v0.3.1 h1:UXtgJ4dwyWL0Yv4mw6gQnlmrIQU/zz6nClCB7NGKBQs=
go.wit.com/log v0.3.1/go.mod h1:GmsggfsKrqdZdAj26fEOlcTz6qEIazbV33uyuuktvB8=
go.wit.com/spew v0.0.0-20240101141411-c7b8e91573c9 h1:UEX2EzLQPzLTfy/kUFQD7OXtvKn8wk/+jpDOkbl4ff4=
go.wit.com/spew v0.0.0-20240101141411-c7b8e91573c9/go.mod h1:qBpgJXThMMT15vym7/E4Ur9y8oOo2nP7t2RP52QHUNw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

15
grid.go
View File

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

View File

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

View File

@ -1,7 +1,6 @@
package gui package gui
import ( import (
"go.wit.com/log"
"go.wit.com/gui/widget" "go.wit.com/gui/widget"
) )
@ -9,9 +8,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)
log.Warn("NewImage() not implemented. fix this") if ! newNode.hidden {
a := newAction(newNode, widget.Add)
// inform the toolkits sendAction(a)
sendAction(newNode, widget.Add) }
return newNode return newNode
} }

View File

@ -9,7 +9,9 @@ func (parent *Node) NewLabel(text string) *Node {
newNode.value = text newNode.value = text
newNode.progname = text newNode.progname = text
// inform the toolkits if ! newNode.hidden {
sendAction(newNode, widget.Add) a := newAction(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,50 +38,30 @@ 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("guiChan() START") log.Info("watchCallback() START")
for { for {
log.Info("guiChan() restarted") log.Info("watchCallback() restarted select for toolkit user events")
select { select {
case a := <-me.guiChan: case a := <-me.guiChan:
if (a.ActionType == widget.UserQuit) { if (a.ActionType == widget.UserQuit) {
log.Warn("guiChan() User sent Quit()") log.Info("doUserEvent() 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("guiChan() Enable Debugging Window") log.Warn("doUserEvent() Enable Debugging Window")
log.Warn("guiChan() TODO: not implemented") log.Warn("doUserEvent() 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("guiChan() UNKNOWN widget id") log.Warn("watchCallback() UNKNOWN widget id =", a.WidgetId, a.ProgName)
log.Warn("id =", a.WidgetId, a.ProgName)
} else { } else {
log.Verbose("guiChan() FOUND widget id =", n.id, n.progname) log.Info("watchCallback() 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,26 +7,14 @@ 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()
} }
@ -40,7 +28,7 @@ func (n *Node) newNode(title string, t widget.WidgetType) *Node {
} }
/* /*
raw create function for a new node struct and increments the counter raw create function for a new node struct
*/ */
func addNode() *Node { func addNode() *Node {
n := new(Node) n := new(Node)
@ -52,13 +40,10 @@ 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

@ -201,6 +201,7 @@ func initToolkit(name string, filename string) *aplug {
// add it to the list of plugins // add it to the list of plugins
allPlugins = append(allPlugins, newPlug) allPlugins = append(allPlugins, newPlug)
// set the communication to the plugins // set the communication to the plugins
newPlug.pluginChan = newPlug.PluginChannel() newPlug.pluginChan = newPlug.PluginChannel()
if (newPlug.pluginChan == nil) { if (newPlug.pluginChan == nil) {
@ -213,6 +214,48 @@ 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

@ -1,14 +0,0 @@
package gui
import (
"go.wit.com/gui/widget"
)
func (parent *Node) NewSeparator(progname string) *Node {
newNode := parent.newNode(progname, widget.Separator)
newNode.progname = progname
// inform the toolkits
sendAction(newNode, widget.Add)
return newNode
}

View File

@ -3,55 +3,64 @@ 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)
// inform the toolkits n.value = text
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) { /*
if ! n.Ready() { return } func convertString(val any) string {
switch v := val.(type) { switch v := val.(type) {
case bool: case bool:
if widget.GetBool(n.value) == val.(bool) { n.B = val.(bool)
// nothing changed
return
}
case string: case string:
if widget.GetString(n.value) == val.(string) { n.label = val.(string)
// nothing changed n.S = val.(string)
return
}
case int: case int:
if widget.GetInt(n.value) == val.(int) { n.I = 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
n.changed = true func (n *Node) Set(val any) {
log.Log(CHANGE, "Set() value =", val) log.Log(CHANGE, "Set() value =", val)
// inform the toolkits n.value = val
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,18 +5,21 @@ import (
"go.wit.com/gui/widget" "go.wit.com/gui/widget"
) )
func (parent *Node) NewSlider(progname string, x int, y int) *Node { func (parent *Node) NewSlider(name string, x int, y int) *Node {
newNode := parent.newNode(progname, widget.Slider) newNode := parent.newNode(name, widget.Slider)
newNode.progname = progname
newNode.Custom = func() { newNode.Custom = func() {
log.Log(GUI, "even newer clicker() name in NewSlider name =", progname) log.Log(GUI, "even newer clicker() name in NewSlider name =", name)
} }
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,18 +5,19 @@ import (
"go.wit.com/gui/widget" "go.wit.com/gui/widget"
) )
func (parent *Node) NewSpinner(progname string, x int, y int) *Node { func (parent *Node) NewSpinner(name string, x int, y int) *Node {
newNode := parent.newNode(progname, widget.Spinner) newNode := parent.newNode(name, widget.Spinner)
newNode.progname = progname
newNode.Custom = func() { newNode.Custom = func() {
log.Info("default NewSpinner() change", progname) log.Info("default NewSpinner() change", name)
} }
newNode.X = x newNode.X = x
newNode.Y = y newNode.Y = y
// inform the toolkits if ! newNode.hidden {
sendAction(newNode, widget.Add) a := newAction(newNode, widget.Add)
sendAction(a)
}
return newNode return newNode
} }

View File

@ -27,15 +27,14 @@ 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 RangeMovedToWidget struct { type Range 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
@ -53,69 +52,28 @@ 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
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
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 // don't update the toolkits when it's hidden hidden bool // Sierpinski Carpet mode. It's there, but you can't see it.
changed bool // do we need to inform the toolkit something changed? pad 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 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
WidgetType widget.WidgetType WidgetType widget.WidgetType
// most widgets need one value, this is current alue // the current widget value.
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
@ -137,4 +95,11 @@ 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,22 +8,20 @@ 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)
} }
// inform the toolkits if ! newNode.hidden {
sendAction(newNode, widget.Add) a := newAction(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
@ -31,7 +29,9 @@ func (parent *Node) NewEntryLine(name string) *Node {
log.Log(GUI, "NewTextbox changed =", name) log.Log(GUI, "NewTextbox changed =", name)
} }
// inform the toolkits if ! newNode.hidden {
sendAction(newNode, widget.Add) a := newAction(newNode, widget.Add)
sendAction(a)
}
return newNode return newNode
} }

View File

@ -18,8 +18,10 @@ func (parent *Node) NewWindow(title string) *Node {
newNode.progname = title newNode.progname = title
newNode.value = title newNode.value = title
// inform the toolkits if ! newNode.hidden {
sendAction(newNode, widget.Add) a := newAction(newNode, widget.Add)
sendAction(a)
}
return newNode return newNode
} }
@ -38,51 +40,26 @@ 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.Ready() { return n } if ! n.hidden {
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 {
if ! n.Ready() { return n }
n.hidden = false n.hidden = false
n.changed= true
// inform the toolkits a := newAction(n, widget.Add)
sendAction(n, widget.Add) sendAction(a)
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
}