mirror of https://github.com/anoshenko/rui.git
				
				
				
			
		
			
				
	
	
		
			263 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			263 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Go
		
	
	
	
| package rui
 | |
| 
 | |
| import (
 | |
| 	"reflect"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| var eventJsFunc = map[PropertyName]struct{ jsEvent, jsFunc string }{
 | |
| 	FocusEvent:              {jsEvent: "onfocus", jsFunc: "focusEvent"},
 | |
| 	LostFocusEvent:          {jsEvent: "onblur", jsFunc: "blurEvent"},
 | |
| 	KeyDownEvent:            {jsEvent: "onkeydown", jsFunc: "keyDownEvent"},
 | |
| 	KeyUpEvent:              {jsEvent: "onkeyup", jsFunc: "keyUpEvent"},
 | |
| 	ClickEvent:              {jsEvent: "onclick", jsFunc: "clickEvent"},
 | |
| 	DoubleClickEvent:        {jsEvent: "ondblclick", jsFunc: "doubleClickEvent"},
 | |
| 	MouseDown:               {jsEvent: "onmousedown", jsFunc: "mouseDownEvent"},
 | |
| 	MouseUp:                 {jsEvent: "onmouseup", jsFunc: "mouseUpEvent"},
 | |
| 	MouseMove:               {jsEvent: "onmousemove", jsFunc: "mouseMoveEvent"},
 | |
| 	MouseOut:                {jsEvent: "onmouseout", jsFunc: "mouseOutEvent"},
 | |
| 	MouseOver:               {jsEvent: "onmouseover", jsFunc: "mouseOverEvent"},
 | |
| 	ContextMenuEvent:        {jsEvent: "oncontextmenu", jsFunc: "contextMenuEvent"},
 | |
| 	PointerDown:             {jsEvent: "onpointerdown", jsFunc: "pointerDownEvent"},
 | |
| 	PointerUp:               {jsEvent: "onpointerup", jsFunc: "pointerUpEvent"},
 | |
| 	PointerMove:             {jsEvent: "onpointermove", jsFunc: "pointerMoveEvent"},
 | |
| 	PointerCancel:           {jsEvent: "onpointercancel", jsFunc: "pointerCancelEvent"},
 | |
| 	PointerOut:              {jsEvent: "onpointerout", jsFunc: "pointerOutEvent"},
 | |
| 	PointerOver:             {jsEvent: "onpointerover", jsFunc: "pointerOverEvent"},
 | |
| 	TouchStart:              {jsEvent: "ontouchstart", jsFunc: "touchStartEvent"},
 | |
| 	TouchEnd:                {jsEvent: "ontouchend", jsFunc: "touchEndEvent"},
 | |
| 	TouchMove:               {jsEvent: "ontouchmove", jsFunc: "touchMoveEvent"},
 | |
| 	TouchCancel:             {jsEvent: "ontouchcancel", jsFunc: "touchCancelEvent"},
 | |
| 	TransitionRunEvent:      {jsEvent: "ontransitionrun", jsFunc: "transitionRunEvent"},
 | |
| 	TransitionStartEvent:    {jsEvent: "ontransitionstart", jsFunc: "transitionStartEvent"},
 | |
| 	TransitionEndEvent:      {jsEvent: "ontransitionend", jsFunc: "transitionEndEvent"},
 | |
| 	TransitionCancelEvent:   {jsEvent: "ontransitioncancel", jsFunc: "transitionCancelEvent"},
 | |
| 	AnimationStartEvent:     {jsEvent: "onanimationstart", jsFunc: "animationStartEvent"},
 | |
| 	AnimationEndEvent:       {jsEvent: "onanimationend", jsFunc: "animationEndEvent"},
 | |
| 	AnimationIterationEvent: {jsEvent: "onanimationiteration", jsFunc: "animationIterationEvent"},
 | |
| 	AnimationCancelEvent:    {jsEvent: "onanimationcancel", jsFunc: "animationCancelEvent"},
 | |
| 	DragEndEvent:            {jsEvent: "ondragend", jsFunc: "dragEndEvent"},
 | |
| 	DragEnterEvent:          {jsEvent: "ondragenter", jsFunc: "dragEnterEvent"},
 | |
| 	DragLeaveEvent:          {jsEvent: "ondragleave", jsFunc: "dragLeaveEvent"},
 | |
| }
 | |
| 
 | |
| func viewEventsHtml[T any](view View, events []PropertyName, buffer *strings.Builder) {
 | |
| 	for _, tag := range events {
 | |
| 		if js, ok := eventJsFunc[tag]; ok {
 | |
| 			if value := getOneArgEventListeners[View, T](view, nil, tag); len(value) > 0 {
 | |
| 				buffer.WriteString(js.jsEvent)
 | |
| 				buffer.WriteString(`="`)
 | |
| 				buffer.WriteString(js.jsFunc)
 | |
| 				buffer.WriteString(`(this, event)" `)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func updateEventListenerHtml(view View, tag PropertyName) {
 | |
| 	if js, ok := eventJsFunc[tag]; ok {
 | |
| 		value := view.getRaw(tag)
 | |
| 		session := view.Session()
 | |
| 		htmlID := view.htmlID()
 | |
| 		if value == nil {
 | |
| 			session.removeProperty(view.htmlID(), js.jsEvent)
 | |
| 		} else {
 | |
| 			session.updateProperty(htmlID, js.jsEvent, js.jsFunc+"(this, event)")
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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:
 | |
| 		if equalType(methodType.In(0), 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 equalType(inType reflect.Type, argType reflect.Type) bool {
 | |
| 	return inType == argType || (inType.Kind() == reflect.Interface && argType.Implements(inType))
 | |
| }
 | |
| 
 | |
| func (data *noArgListenerBinding[V]) rawListener() any {
 | |
| 	return data.name
 | |
| }
 | |
| 
 | |
| func valueToNoArgEventListeners[V View](value any) ([]noArgListener[V], bool) {
 | |
| 	if value == nil {
 | |
| 		return nil, true
 | |
| 	}
 | |
| 
 | |
| 	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 result, len(result) > 0
 | |
| 	}
 | |
| 
 | |
| 	return nil, false
 | |
| }
 | |
| 
 | |
| func setNoArgEventListener[V View](view View, tag PropertyName, value any) []PropertyName {
 | |
| 	if listeners, ok := valueToNoArgEventListeners[V](value); ok {
 | |
| 		return setArrayPropertyValue(view, tag, listeners)
 | |
| 	}
 | |
| 	notCompatibleType(tag, value)
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func getNoArgEventListeners[V View](view View, subviewID []string, tag PropertyName) []noArgListener[V] {
 | |
| 	if view = getSubview(view, subviewID); view != nil {
 | |
| 		if value := view.getRaw(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
 | |
| }
 | |
| 
 | |
| func getNoArgBinding[V View](listeners []noArgListener[V]) string {
 | |
| 	for _, listener := range listeners {
 | |
| 		raw := listener.rawListener()
 | |
| 		if text, ok := raw.(string); ok && text != "" {
 | |
| 			return text
 | |
| 		}
 | |
| 	}
 | |
| 	return ""
 | |
| }
 |