mirror of https://github.com/anoshenko/rui.git
272 lines
6.9 KiB
Go
272 lines
6.9 KiB
Go
|
package rui
|
||
|
|
||
|
import "strings"
|
||
|
|
||
|
const (
|
||
|
// KeyDown is the constant for "key-down-event" property tag.
|
||
|
// The "key-down-event" event is fired when a key is pressed.
|
||
|
// The main listener format: func(View, KeyEvent).
|
||
|
// The additional listener formats: func(KeyEvent), func(View), and func().
|
||
|
KeyDownEvent = "key-down-event"
|
||
|
|
||
|
// KeyPp is the constant for "key-up-event" property tag
|
||
|
// The "key-up-event" event is fired when a key is released.
|
||
|
// The main listener format: func(View, KeyEvent).
|
||
|
// The additional listener formats: func(KeyEvent), func(View), and func().
|
||
|
KeyUpEvent = "key-up-event"
|
||
|
)
|
||
|
|
||
|
type KeyEvent struct {
|
||
|
// TimeStamp is the time at which the event was created (in milliseconds).
|
||
|
// This value is time since epoch—but in reality, browsers' definitions vary.
|
||
|
TimeStamp uint64
|
||
|
|
||
|
// Key is the key value of the key represented by the event. If the value has a printed representation,
|
||
|
// this attribute's value is the same as the char property. Otherwise, it's one of the key value strings
|
||
|
// specified in Key values. If the key can't be identified, its value is the string "Unidentified".
|
||
|
Key string
|
||
|
|
||
|
// Code holds a string that identifies the physical key being pressed. The value is not affected
|
||
|
// by the current keyboard layout or modifier state, so a particular key will always return the same value.
|
||
|
Code string
|
||
|
|
||
|
// Repeat == true if a key has been depressed long enough to trigger key repetition, otherwise false.
|
||
|
Repeat bool
|
||
|
|
||
|
// CtrlKey == true if the control key was down when the event was fired. false otherwise.
|
||
|
CtrlKey bool
|
||
|
|
||
|
// ShiftKey == true if the shift key was down when the event was fired. false otherwise.
|
||
|
ShiftKey bool
|
||
|
|
||
|
// AltKey == true if the alt key was down when the event was fired. false otherwise.
|
||
|
AltKey bool
|
||
|
|
||
|
// MetaKey == true if the meta key was down when the event was fired. false otherwise.
|
||
|
MetaKey bool
|
||
|
}
|
||
|
|
||
|
func valueToKeyListeners(value interface{}) ([]func(View, KeyEvent), bool) {
|
||
|
if value == nil {
|
||
|
return nil, true
|
||
|
}
|
||
|
|
||
|
switch value := value.(type) {
|
||
|
case func(View, KeyEvent):
|
||
|
return []func(View, KeyEvent){value}, true
|
||
|
|
||
|
case func(KeyEvent):
|
||
|
fn := func(view View, event KeyEvent) {
|
||
|
value(event)
|
||
|
}
|
||
|
return []func(View, KeyEvent){fn}, true
|
||
|
|
||
|
case func(View):
|
||
|
fn := func(view View, event KeyEvent) {
|
||
|
value(view)
|
||
|
}
|
||
|
return []func(View, KeyEvent){fn}, true
|
||
|
|
||
|
case func():
|
||
|
fn := func(view View, event KeyEvent) {
|
||
|
value()
|
||
|
}
|
||
|
return []func(View, KeyEvent){fn}, true
|
||
|
|
||
|
case []func(View, KeyEvent):
|
||
|
if len(value) == 0 {
|
||
|
return nil, true
|
||
|
}
|
||
|
for _, fn := range value {
|
||
|
if fn == nil {
|
||
|
return nil, false
|
||
|
}
|
||
|
}
|
||
|
return value, true
|
||
|
|
||
|
case []func(KeyEvent):
|
||
|
count := len(value)
|
||
|
if count == 0 {
|
||
|
return nil, true
|
||
|
}
|
||
|
listeners := make([]func(View, KeyEvent), count)
|
||
|
for i, v := range value {
|
||
|
if v == nil {
|
||
|
return nil, false
|
||
|
}
|
||
|
listeners[i] = func(view View, event KeyEvent) {
|
||
|
v(event)
|
||
|
}
|
||
|
}
|
||
|
return listeners, true
|
||
|
|
||
|
case []func(View):
|
||
|
count := len(value)
|
||
|
if count == 0 {
|
||
|
return nil, true
|
||
|
}
|
||
|
listeners := make([]func(View, KeyEvent), count)
|
||
|
for i, v := range value {
|
||
|
if v == nil {
|
||
|
return nil, false
|
||
|
}
|
||
|
listeners[i] = func(view View, event KeyEvent) {
|
||
|
v(view)
|
||
|
}
|
||
|
}
|
||
|
return listeners, true
|
||
|
|
||
|
case []func():
|
||
|
count := len(value)
|
||
|
if count == 0 {
|
||
|
return nil, true
|
||
|
}
|
||
|
listeners := make([]func(View, KeyEvent), count)
|
||
|
for i, v := range value {
|
||
|
if v == nil {
|
||
|
return nil, false
|
||
|
}
|
||
|
listeners[i] = func(view View, event KeyEvent) {
|
||
|
v()
|
||
|
}
|
||
|
}
|
||
|
return listeners, true
|
||
|
|
||
|
case []interface{}:
|
||
|
count := len(value)
|
||
|
if count == 0 {
|
||
|
return nil, true
|
||
|
}
|
||
|
listeners := make([]func(View, KeyEvent), count)
|
||
|
for i, v := range value {
|
||
|
if v == nil {
|
||
|
return nil, false
|
||
|
}
|
||
|
switch v := v.(type) {
|
||
|
case func(View, KeyEvent):
|
||
|
listeners[i] = v
|
||
|
|
||
|
case func(KeyEvent):
|
||
|
listeners[i] = func(view View, event KeyEvent) {
|
||
|
v(event)
|
||
|
}
|
||
|
|
||
|
case func(View):
|
||
|
listeners[i] = func(view View, event KeyEvent) {
|
||
|
v(view)
|
||
|
}
|
||
|
|
||
|
case func():
|
||
|
listeners[i] = func(view View, event KeyEvent) {
|
||
|
v()
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
return nil, false
|
||
|
}
|
||
|
}
|
||
|
return listeners, true
|
||
|
}
|
||
|
|
||
|
return nil, false
|
||
|
}
|
||
|
|
||
|
var keyEvents = map[string]struct{ jsEvent, jsFunc string }{
|
||
|
KeyDownEvent: {jsEvent: "onkeydown", jsFunc: "keyDownEvent"},
|
||
|
KeyUpEvent: {jsEvent: "onkeyup", jsFunc: "keyUpEvent"},
|
||
|
}
|
||
|
|
||
|
func (view *viewData) setKeyListener(tag string, value interface{}) bool {
|
||
|
listeners, ok := valueToKeyListeners(value)
|
||
|
if !ok {
|
||
|
notCompatibleType(tag, value)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
if listeners == nil {
|
||
|
view.removeKeyListener(tag)
|
||
|
} else if js, ok := keyEvents[tag]; ok {
|
||
|
view.properties[tag] = listeners
|
||
|
if view.created {
|
||
|
updateProperty(view.htmlID(), js.jsEvent, js.jsFunc+"(this, event)", view.Session())
|
||
|
}
|
||
|
} else {
|
||
|
return false
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (view *viewData) removeKeyListener(tag string) {
|
||
|
delete(view.properties, tag)
|
||
|
if view.created {
|
||
|
if js, ok := keyEvents[tag]; ok {
|
||
|
updateProperty(view.htmlID(), js.jsEvent, "", view.Session())
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func getKeyListeners(view View, subviewID string, tag string) []func(View, KeyEvent) {
|
||
|
if subviewID != "" {
|
||
|
view = ViewByID(view, subviewID)
|
||
|
}
|
||
|
if view != nil {
|
||
|
if value := view.Get(tag); value != nil {
|
||
|
if result, ok := value.([]func(View, KeyEvent)); ok {
|
||
|
return result
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return []func(View, KeyEvent){}
|
||
|
}
|
||
|
|
||
|
func keyEventsHtml(view View, buffer *strings.Builder) {
|
||
|
for tag, js := range keyEvents {
|
||
|
if listeners := getKeyListeners(view, "", tag); len(listeners) > 0 {
|
||
|
buffer.WriteString(js.jsEvent + `="` + js.jsFunc + `(this, event)" `)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func handleKeyEvents(view View, tag string, data DataObject) {
|
||
|
listeners := getKeyListeners(view, "", tag)
|
||
|
if len(listeners) == 0 {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
getBool := func(tag string) bool {
|
||
|
if value, ok := data.PropertyValue(tag); ok && value == "1" {
|
||
|
return true
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
key, _ := data.PropertyValue("key")
|
||
|
code, _ := data.PropertyValue("code")
|
||
|
event := KeyEvent{
|
||
|
TimeStamp: getTimeStamp(data),
|
||
|
Key: key,
|
||
|
Code: code,
|
||
|
Repeat: getBool("repeat"),
|
||
|
CtrlKey: getBool("ctrlKey"),
|
||
|
ShiftKey: getBool("shiftKey"),
|
||
|
AltKey: getBool("altKey"),
|
||
|
MetaKey: getBool("metaKey"),
|
||
|
}
|
||
|
|
||
|
for _, listener := range listeners {
|
||
|
listener(view, event)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GetKeyDownListeners returns the "key-down-event" listener list. If there are no listeners then the empty list is returned.
|
||
|
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||
|
func GetKeyDownListeners(view View, subviewID string) []func(View, KeyEvent) {
|
||
|
return getKeyListeners(view, subviewID, KeyDownEvent)
|
||
|
}
|
||
|
|
||
|
// GetKeyUpListeners returns the "key-up-event" listener list. If there are no listeners then the empty list is returned.
|
||
|
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||
|
func GetKeyUpListeners(view View, subviewID string) []func(View, KeyEvent) {
|
||
|
return getKeyListeners(view, subviewID, KeyUpEvent)
|
||
|
}
|