mirror of https://github.com/anoshenko/rui.git
Improved binding for no args listeners
This commit is contained in:
parent
4b00299878
commit
f2fb948325
178
events.go
178
events.go
|
@ -67,6 +67,96 @@ func updateEventListenerHtml(view View, tag PropertyName) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type noArgListener[V View] interface {
|
||||||
|
Run(V)
|
||||||
|
rawListener() any
|
||||||
|
}
|
||||||
|
|
||||||
|
type noArgListener0[V View] struct {
|
||||||
|
fn func()
|
||||||
|
}
|
||||||
|
|
||||||
|
type noArgListenerV[V View] struct {
|
||||||
|
fn func(V)
|
||||||
|
}
|
||||||
|
|
||||||
|
type noArgListenerBinding[V View] struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNoArgListener0[V View](fn func()) noArgListener[V] {
|
||||||
|
obj := new(noArgListener0[V])
|
||||||
|
obj.fn = fn
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *noArgListener0[V]) Run(_ V) {
|
||||||
|
data.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *noArgListener0[V]) rawListener() any {
|
||||||
|
return data.fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNoArgListenerV[V View](fn func(V)) noArgListener[V] {
|
||||||
|
obj := new(noArgListenerV[V])
|
||||||
|
obj.fn = fn
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *noArgListenerV[V]) Run(view V) {
|
||||||
|
data.fn(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *noArgListenerV[V]) rawListener() any {
|
||||||
|
return data.fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNoArgListenerBinding[V View](name string) noArgListener[V] {
|
||||||
|
obj := new(noArgListenerBinding[V])
|
||||||
|
obj.name = name
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *noArgListenerBinding[V]) Run(view V) {
|
||||||
|
bind := view.binding()
|
||||||
|
if bind == nil {
|
||||||
|
ErrorLogF(`There is no a binding object for call "%s"`, data.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(bind)
|
||||||
|
method := val.MethodByName(data.name)
|
||||||
|
if !method.IsValid() {
|
||||||
|
ErrorLogF(`The "%s" method is not valid`, data.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
methodType := method.Type()
|
||||||
|
var args []reflect.Value = nil
|
||||||
|
switch methodType.NumIn() {
|
||||||
|
case 0:
|
||||||
|
args = []reflect.Value{}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
inType := methodType.In(0)
|
||||||
|
if inType == reflect.TypeOf(view) {
|
||||||
|
args = []reflect.Value{reflect.ValueOf(view)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if args != nil {
|
||||||
|
method.Call(args)
|
||||||
|
} else {
|
||||||
|
ErrorLogF(`Unsupported prototype of "%s" method`, data.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *noArgListenerBinding[V]) rawListener() any {
|
||||||
|
return data.name
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func valueToNoArgEventListeners[V any](view View, value any) ([]func(V), bool) {
|
func valueToNoArgEventListeners[V any](view View, value any) ([]func(V), bool) {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return nil, true
|
return nil, true
|
||||||
|
@ -173,20 +263,74 @@ func valueToNoArgEventListeners[V any](view View, value any) ([]func(V), bool) {
|
||||||
|
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func getNoArgEventListeners[V View](view View, subviewID []string, tag PropertyName) []func(V) {
|
func valueToNoArgEventListeners[V View](value any) ([]noArgListener[V], bool) {
|
||||||
if view = getSubview(view, subviewID); view != nil {
|
if value == nil {
|
||||||
if value := view.Get(tag); value != nil {
|
return nil, true
|
||||||
if result, ok := value.([]func(V)); ok {
|
}
|
||||||
return result
|
|
||||||
|
switch value := value.(type) {
|
||||||
|
case []noArgListener[V]:
|
||||||
|
return value, true
|
||||||
|
|
||||||
|
case noArgListener[V]:
|
||||||
|
return []noArgListener[V]{value}, true
|
||||||
|
|
||||||
|
case string:
|
||||||
|
return []noArgListener[V]{newNoArgListenerBinding[V](value)}, true
|
||||||
|
|
||||||
|
case func(V):
|
||||||
|
return []noArgListener[V]{newNoArgListenerV(value)}, true
|
||||||
|
|
||||||
|
case func():
|
||||||
|
return []noArgListener[V]{newNoArgListener0[V](value)}, true
|
||||||
|
|
||||||
|
case []func(V):
|
||||||
|
result := make([]noArgListener[V], 0, len(value))
|
||||||
|
for _, fn := range value {
|
||||||
|
if fn != nil {
|
||||||
|
result = append(result, newNoArgListenerV(fn))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, len(result) > 0
|
||||||
|
|
||||||
|
case []func():
|
||||||
|
result := make([]noArgListener[V], 0, len(value))
|
||||||
|
for _, fn := range value {
|
||||||
|
if fn != nil {
|
||||||
|
result = append(result, newNoArgListener0[V](fn))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, len(result) > 0
|
||||||
|
|
||||||
|
case []any:
|
||||||
|
result := make([]noArgListener[V], 0, len(value))
|
||||||
|
for _, v := range value {
|
||||||
|
if v != nil {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case func(V):
|
||||||
|
result = append(result, newNoArgListenerV(v))
|
||||||
|
|
||||||
|
case func():
|
||||||
|
result = append(result, newNoArgListener0[V](v))
|
||||||
|
|
||||||
|
case string:
|
||||||
|
result = append(result, newNoArgListenerBinding[V](v))
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return []func(V){}
|
return result, len(result) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func setNoArgEventListener[V View](view View, tag PropertyName, value any) []PropertyName {
|
func setNoArgEventListener[V View](view View, tag PropertyName, value any) []PropertyName {
|
||||||
if listeners, ok := valueToNoArgEventListeners[V](view, value); ok {
|
if listeners, ok := valueToNoArgEventListeners[V](value); ok {
|
||||||
if len(listeners) > 0 {
|
if len(listeners) > 0 {
|
||||||
view.setRaw(tag, listeners)
|
view.setRaw(tag, listeners)
|
||||||
} else if view.getRaw(tag) != nil {
|
} else if view.getRaw(tag) != nil {
|
||||||
|
@ -199,3 +343,23 @@ func setNoArgEventListener[V View](view View, tag PropertyName, value any) []Pro
|
||||||
notCompatibleType(tag, value)
|
notCompatibleType(tag, value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getNoArgEventListeners[V View](view View, subviewID []string, tag PropertyName) []noArgListener[V] {
|
||||||
|
if view = getSubview(view, subviewID); view != nil {
|
||||||
|
if value := view.Get(tag); value != nil {
|
||||||
|
if result, ok := value.([]noArgListener[V]); ok {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return []noArgListener[V]{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNoArgEventRawListeners[V View](view View, subviewID []string, tag PropertyName) []any {
|
||||||
|
listeners := getNoArgEventListeners[V](view, subviewID, tag)
|
||||||
|
result := make([]any, len(listeners))
|
||||||
|
for i, l := range listeners {
|
||||||
|
result[i] = l.rawListener()
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
|
@ -50,16 +50,26 @@ func focusEventsHtml(view View, buffer *strings.Builder) {
|
||||||
|
|
||||||
// GetFocusListeners returns a FocusListener list. If there are no listeners then the empty list is returned
|
// GetFocusListeners returns a FocusListener list. If there are no listeners then the empty list is returned
|
||||||
//
|
//
|
||||||
|
// Result elements can be of the following types:
|
||||||
|
// - func(rui.View),
|
||||||
|
// - func(),
|
||||||
|
// - string.
|
||||||
|
//
|
||||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||||
// If it is not specified then a value from the first argument (view) is returned.
|
// If it is not specified then a value from the first argument (view) is returned.
|
||||||
func GetFocusListeners(view View, subviewID ...string) []func(View) {
|
func GetFocusListeners(view View, subviewID ...string) []any {
|
||||||
return getNoArgEventListeners[View](view, subviewID, FocusEvent)
|
return getNoArgEventRawListeners[View](view, subviewID, FocusEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLostFocusListeners returns a LostFocusListener list. If there are no listeners then the empty list is returned
|
// GetLostFocusListeners returns a LostFocusListener list. If there are no listeners then the empty list is returned
|
||||||
//
|
//
|
||||||
|
// Result elements can be of the following types:
|
||||||
|
// - func(rui.View),
|
||||||
|
// - func(),
|
||||||
|
// - string.
|
||||||
|
//
|
||||||
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||||
// If it is not specified then a value from the first argument (view) is returned.
|
// If it is not specified then a value from the first argument (view) is returned.
|
||||||
func GetLostFocusListeners(view View, subviewID ...string) []func(View) {
|
func GetLostFocusListeners(view View, subviewID ...string) []any {
|
||||||
return getNoArgEventListeners[View](view, subviewID, LostFocusEvent)
|
return getNoArgEventRawListeners[View](view, subviewID, LostFocusEvent)
|
||||||
}
|
}
|
||||||
|
|
32
imageView.go
32
imageView.go
|
@ -300,7 +300,7 @@ func (imageView *imageViewData) handleCommand(self View, command PropertyName, d
|
||||||
switch command {
|
switch command {
|
||||||
case "imageViewError":
|
case "imageViewError":
|
||||||
for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, ErrorEvent) {
|
for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, ErrorEvent) {
|
||||||
listener(imageView)
|
listener.Run(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "imageViewLoaded":
|
case "imageViewLoaded":
|
||||||
|
@ -309,7 +309,7 @@ func (imageView *imageViewData) handleCommand(self View, command PropertyName, d
|
||||||
imageView.currentSrc, _ = data.PropertyValue("current-src")
|
imageView.currentSrc, _ = data.PropertyValue("current-src")
|
||||||
|
|
||||||
for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, LoadedEvent) {
|
for _, listener := range getNoArgEventListeners[ImageView](imageView, nil, LoadedEvent) {
|
||||||
listener(imageView)
|
listener.Run(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -370,3 +370,31 @@ func GetImageViewVerticalAlign(view View, subviewID ...string) int {
|
||||||
func GetImageViewHorizontalAlign(view View, subviewID ...string) int {
|
func GetImageViewHorizontalAlign(view View, subviewID ...string) int {
|
||||||
return enumStyledProperty(view, subviewID, ImageHorizontalAlign, LeftAlign, false)
|
return enumStyledProperty(view, subviewID, ImageHorizontalAlign, LeftAlign, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetImageViewErrorEventListeners returns the list of "error-event" event listeners.
|
||||||
|
// If there are no listeners then the empty list is returned
|
||||||
|
//
|
||||||
|
// Result elements can be of the following types:
|
||||||
|
// - func(rui.ImageView)
|
||||||
|
// - func()
|
||||||
|
// - string
|
||||||
|
//
|
||||||
|
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||||
|
// If it is not specified then a value from the first argument (view) is returned.
|
||||||
|
func GetImageViewErrorEventListeners(view View, subviewID ...string) []any {
|
||||||
|
return getNoArgEventRawListeners[View](view, subviewID, ErrorEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImageViewLoadedEventListeners returns the list of "loaded-event" event listeners.
|
||||||
|
// If there are no listeners then the empty list is returned
|
||||||
|
//
|
||||||
|
// Result elements can be of the following types:
|
||||||
|
// - func(rui.ImageView)
|
||||||
|
// - func()
|
||||||
|
// - string
|
||||||
|
//
|
||||||
|
// The second argument (subviewID) specifies the path to the child element whose value needs to be returned.
|
||||||
|
// If it is not specified then a value from the first argument (view) is returned.
|
||||||
|
func GetImageViewLoadedEventListeners(view View, subviewID ...string) []any {
|
||||||
|
return getNoArgEventRawListeners[View](view, subviewID, LoadedEvent)
|
||||||
|
}
|
||||||
|
|
160
popup.go
160
popup.go
|
@ -2,6 +2,7 @@ package rui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -281,13 +282,30 @@ type Popup interface {
|
||||||
dissmissAnimation(listener func(PropertyName)) bool
|
dissmissAnimation(listener func(PropertyName)) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type popupListener interface {
|
||||||
|
Run(Popup)
|
||||||
|
rawListener() any
|
||||||
|
}
|
||||||
|
|
||||||
|
type popupListener0 struct {
|
||||||
|
fn func()
|
||||||
|
}
|
||||||
|
|
||||||
|
type popupListener1 struct {
|
||||||
|
fn func(Popup)
|
||||||
|
}
|
||||||
|
|
||||||
|
type popupListenerBinding struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
type popupData struct {
|
type popupData struct {
|
||||||
layerView GridLayout
|
layerView GridLayout
|
||||||
popupView GridLayout
|
popupView GridLayout
|
||||||
contentView View
|
contentView View
|
||||||
buttons []PopupButton
|
buttons []PopupButton
|
||||||
cancelable bool
|
cancelable bool
|
||||||
dismissListener []func(Popup)
|
dismissListener []popupListener
|
||||||
showTransform TransformProperty
|
showTransform TransformProperty
|
||||||
showOpacity float64
|
showOpacity float64
|
||||||
showDuration float64
|
showDuration float64
|
||||||
|
@ -649,7 +667,7 @@ func (popup *popupData) init(view View, popupParams Params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case DismissEvent:
|
case DismissEvent:
|
||||||
if listeners, ok := valueToNoArgEventListeners[Popup](popup.contentView, value); ok {
|
if listeners, ok := valueToPopupEventListeners(value); ok {
|
||||||
if listeners != nil {
|
if listeners != nil {
|
||||||
popup.dismissListener = listeners
|
popup.dismissListener = listeners
|
||||||
}
|
}
|
||||||
|
@ -887,7 +905,7 @@ func (popup *popupData) onDismiss() {
|
||||||
popup.Session().callFunc("removeView", popup.layerView.htmlID())
|
popup.Session().callFunc("removeView", popup.layerView.htmlID())
|
||||||
|
|
||||||
for _, listener := range popup.dismissListener {
|
for _, listener := range popup.dismissListener {
|
||||||
listener(popup)
|
listener.Run(popup)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1014,3 +1032,139 @@ func (manager *popupManager) dismissPopup(popup Popup) {
|
||||||
listener("")
|
listener("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newPopupListener0(fn func()) popupListener {
|
||||||
|
obj := new(popupListener0)
|
||||||
|
obj.fn = fn
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *popupListener0) Run(_ Popup) {
|
||||||
|
data.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *popupListener0) rawListener() any {
|
||||||
|
return data.fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPopupListener1(fn func(Popup)) popupListener {
|
||||||
|
obj := new(popupListener1)
|
||||||
|
obj.fn = fn
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *popupListener1) Run(popup Popup) {
|
||||||
|
data.fn(popup)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *popupListener1) rawListener() any {
|
||||||
|
return data.fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPopupListenerBinding(name string) popupListener {
|
||||||
|
obj := new(popupListenerBinding)
|
||||||
|
obj.name = name
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *popupListenerBinding) Run(popup Popup) {
|
||||||
|
bind := popup.View().binding()
|
||||||
|
if bind == nil {
|
||||||
|
ErrorLogF(`There is no a binding object for call "%s"`, data.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val := reflect.ValueOf(bind)
|
||||||
|
method := val.MethodByName(data.name)
|
||||||
|
if !method.IsValid() {
|
||||||
|
ErrorLogF(`The "%s" method is not valid`, data.name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
methodType := method.Type()
|
||||||
|
var args []reflect.Value = nil
|
||||||
|
switch methodType.NumIn() {
|
||||||
|
case 0:
|
||||||
|
args = []reflect.Value{}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
inType := methodType.In(0)
|
||||||
|
if inType == reflect.TypeOf(popup) {
|
||||||
|
args = []reflect.Value{reflect.ValueOf(popup)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if args != nil {
|
||||||
|
method.Call(args)
|
||||||
|
} else {
|
||||||
|
ErrorLogF(`Unsupported prototype of "%s" method`, data.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (data *popupListenerBinding) rawListener() any {
|
||||||
|
return data.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func valueToPopupEventListeners(value any) ([]popupListener, bool) {
|
||||||
|
if value == nil {
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value := value.(type) {
|
||||||
|
case []popupListener:
|
||||||
|
return value, true
|
||||||
|
|
||||||
|
case popupListener:
|
||||||
|
return []popupListener{value}, true
|
||||||
|
|
||||||
|
case string:
|
||||||
|
return []popupListener{newPopupListenerBinding(value)}, true
|
||||||
|
|
||||||
|
case func(Popup):
|
||||||
|
return []popupListener{newPopupListener1(value)}, true
|
||||||
|
|
||||||
|
case func():
|
||||||
|
return []popupListener{newPopupListener0(value)}, true
|
||||||
|
|
||||||
|
case []func(Popup):
|
||||||
|
result := make([]popupListener, 0, len(value))
|
||||||
|
for _, fn := range value {
|
||||||
|
if fn != nil {
|
||||||
|
result = append(result, newPopupListener1(fn))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, len(result) > 0
|
||||||
|
|
||||||
|
case []func():
|
||||||
|
result := make([]popupListener, 0, len(value))
|
||||||
|
for _, fn := range value {
|
||||||
|
if fn != nil {
|
||||||
|
result = append(result, newPopupListener0(fn))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, len(result) > 0
|
||||||
|
|
||||||
|
case []any:
|
||||||
|
result := make([]popupListener, 0, len(value))
|
||||||
|
for _, v := range value {
|
||||||
|
if v != nil {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case func(Popup):
|
||||||
|
result = append(result, newPopupListener1(v))
|
||||||
|
|
||||||
|
case func():
|
||||||
|
result = append(result, newPopupListener0(v))
|
||||||
|
|
||||||
|
case string:
|
||||||
|
result = append(result, newPopupListenerBinding(v))
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, len(result) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
4
view.go
4
view.go
|
@ -1162,13 +1162,13 @@ func (view *viewData) handleCommand(self View, command PropertyName, data DataOb
|
||||||
case FocusEvent:
|
case FocusEvent:
|
||||||
view.hasFocus = true
|
view.hasFocus = true
|
||||||
for _, listener := range getNoArgEventListeners[View](view, nil, command) {
|
for _, listener := range getNoArgEventListeners[View](view, nil, command) {
|
||||||
listener(self)
|
listener.Run(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
case LostFocusEvent:
|
case LostFocusEvent:
|
||||||
view.hasFocus = false
|
view.hasFocus = false
|
||||||
for _, listener := range getNoArgEventListeners[View](view, nil, command) {
|
for _, listener := range getNoArgEventListeners[View](view, nil, command) {
|
||||||
listener(self)
|
listener.Run(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
case TransitionRunEvent, TransitionStartEvent, TransitionEndEvent, TransitionCancelEvent:
|
||||||
|
|
Loading…
Reference in New Issue