gui/action.go

196 lines
5.7 KiB
Go

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 (
"errors"
"go.wit.com/log"
"go.wit.com/widget"
)
func (n *Node) SetVisable(b bool) {
n.visable = b
}
// returns true if the window is not visable to the user
// in which case events are not sent to the toolkit
// TODO: fix this so ParentVisable() works
func (n *Node) WindowVisable() bool {
if n == nil {
return false
}
if n.WidgetType == widget.Window {
return n.visable
}
if n.parent == nil {
return false
}
return n.parent.WindowVisable()
}
// returns true if the parent is not visable to the user
// in which case events are not sent to the toolkit
func (n *Node) ParentVisable() bool {
if n == nil {
return false
}
if n.parent == nil {
return false
}
return n.parent.visable
}
// copies the state of the Node into the action
// TODO: switch to protobuf here
func getNewAction(n *Node, atype widget.ActionType) *widget.Action {
var a widget.Action
a.ActionType = atype
// These should be "stable" at this point (2024/01/13)
a.WidgetId = n.id
// set state
a.State.ProgName = n.progname
a.State.Label = n.label
// a.State.Value = n.value
a.State.DefaultS = n.defaultS
a.State.CurrentS = n.currentS
a.State.NewString = n.newString
a.State.Checked = n.checked
a.State.Visable = n.visable
a.State.Hidden = n.hidden
// TODO: if visable == false here, return
a.State.Enable = n.enabled
a.State.Pad = n.pad
a.State.Expand = n.expand
a.State.Borderless = n.borderless
a.State.Direction = n.direction
for s, _ := range n.strings {
a.State.Strings = append(a.State.Strings, s)
}
a.State.Range.Low = n.X
a.State.Range.High = n.Y
a.ProgName = n.progname
// a.Value = n.value
a.Direction = n.direction
// These should be improved/deprecated based on the gui/widget docs
a.X = n.X
a.Y = n.Y
a.State.GridOffset.X = n.AtW
a.State.GridOffset.Y = n.AtH
a.State.AtW = n.AtW
a.State.AtH = n.AtH
if n.parent != nil {
a.ParentId = n.parent.id
}
a.WidgetType = n.WidgetType
return &a
}
// 2025/02/09 this will continue to suck until I switch it to protocol buffers
// 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 {
log.Error(errors.New("Sending Action on n = nil"))
return
}
if atype == widget.Disable {
// log.Log(WARN, "START got DISABLE action ", n.id, n.WidgetType, n.GetProgName())
}
n.mu.Lock()
defer n.mu.Unlock()
// log.Log(PLUG, "SENDING ACTION STRINGS n.Strings", n.strings, n.id, n.WidgetType, n.GetProgName())
if n.changed {
n.changed = false
} else {
// probably shouldn't even send this to the toolkits
// TODO: can't implement this yet
}
// this checks to see if the window is show in the toolkit. If it is not,
// then don't send any events. Unless it is a window widget, then send events
if n.WidgetType != widget.Window {
if n.WindowVisable() {
// lots of output. make this verbose?
log.Log(INFO, "Sending action to widget. Window is visable", n.id, n.WidgetType, n.GetProgName())
} else {
// lots of output. make this verbose?
log.Log(INFO, "Not sending action to widget. Window is not visable", n.id, n.WidgetType, n.GetProgName())
return
}
// set if the widget is visable based on the parent
// this has nothing to do with show() and hide()
if n.ParentVisable() {
log.Log(INFO, "Parent is visable n =", n.id, n.WidgetType, n.GetProgName())
if n.visable != true {
log.Log(INFO, "switching visable=true", n.id, n.WidgetType, n.GetProgName())
}
n.visable = true
} else {
log.Log(INFO, "Parent is not visable n =", n.id, n.WidgetType, n.GetProgName())
log.Log(INFO, "not sending action to toolkits")
if n.visable != false {
log.Log(INFO, "switching visable=false", n.id, n.WidgetType, n.GetProgName())
}
n.visable = false
return
}
}
if atype == widget.Disable {
// log.Log(WARN, "END got DISABLE action ", n.id, n.WidgetType, n.GetProgName())
}
// make a new action and populate the current node state
a := getNewAction(n, atype)
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(CHANGE, "send to toolkit =", aplug.name, "Action type=", a.ActionType, a.WidgetId)
if aplug.dead {
// log.Log(WARN, "skiping dead toolkit =", aplug.name, "Action type=", a.ActionType, a.WidgetId)
return
}
if aplug.pluginChan == nil {
log.Log(PLUG, "Action() retrieving the aplug.PluginChannel()", aplug.name)
aplug.pluginChan = aplug.PluginChannel()
log.Log(PLUG, "Action() retrieved", aplug.pluginChan)
}
log.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
// start playing with this
if a.ActionType == widget.Hide {
// log.Sleep(.001) // this delay makes it so SetText() works on initial widget creation
} else {
// 2024/12/16 still leaving this as I'm not sure binary tree access is actually
// safe (thread safe, correct locking, etc). I think it is. Things seem stable
// sync.Mutex() locks don't appear to work somehow in a way I don't understand
// it's probably due to []string use. use map[] instead
log.Sleep(.000001) // this delay makes it so SetText() works on initial widget creation
// log.Sleep(.002) // this delay makes it so SetText() works on initial widget creation
}
}
}