mirror of https://github.com/anoshenko/rui.git
427 lines
14 KiB
Go
427 lines
14 KiB
Go
package rui
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
// ClickEvent is the constant for "click-event" property tag.
|
|
// The "click-event" event occurs when the user clicks on the View.
|
|
// The main listener format:
|
|
// func(View, MouseEvent).
|
|
// The additional listener formats:
|
|
// func(MouseEvent), func(View), and func().
|
|
ClickEvent = "click-event"
|
|
|
|
// DoubleClickEvent is the constant for "double-click-event" property tag.
|
|
// The "double-click-event" event occurs when the user double clicks on the View.
|
|
// The main listener format:
|
|
// func(View, MouseEvent).
|
|
// The additional listener formats:
|
|
// func(MouseEvent), func(View), and func().
|
|
DoubleClickEvent = "double-click-event"
|
|
|
|
// MouseDown is the constant for "mouse-down" property tag.
|
|
// The "mouse-down" event is fired at a View when a pointing device button is pressed
|
|
// while the pointer is inside the view.
|
|
// The main listener format:
|
|
// func(View, MouseEvent).
|
|
// The additional listener formats:
|
|
// func(MouseEvent), func(View), and func().
|
|
MouseDown = "mouse-down"
|
|
|
|
// MouseUp is the constant for "mouse-up" property tag.
|
|
// The "mouse-up" event is fired at a View when a button on a pointing device (such as a mouse
|
|
// or trackpad) is released while the pointer is located inside it.
|
|
// "mouse-up" events are the counterpoint to "mouse-down" events.
|
|
// The main listener format:
|
|
// func(View, MouseEvent).
|
|
// The additional listener formats:
|
|
// func(MouseEvent), func(View), and func().
|
|
MouseUp = "mouse-up"
|
|
|
|
// MouseMove is the constant for "mouse-move" property tag.
|
|
// The "mouse-move" event is fired at a view when a pointing device (usually a mouse) is moved
|
|
// while the cursor's hotspot is inside it.
|
|
// The main listener format:
|
|
// func(View, MouseEvent).
|
|
// The additional listener formats:
|
|
// func(MouseEvent), func(View), and func().
|
|
MouseMove = "mouse-move"
|
|
|
|
// MouseOut is the constant for "mouse-out" property tag.
|
|
// The "mouse-out" event is fired at a View when a pointing device (usually a mouse) is used to move
|
|
// the cursor so that it is no longer contained within the view or one of its children.
|
|
// "mouse-out" is also delivered to a view if the cursor enters a child view,
|
|
// because the child view obscures the visible area of the view.
|
|
// The main listener format:
|
|
// func(View, MouseEvent).
|
|
// The additional listener formats:
|
|
// func(MouseEvent), func(View), and func().
|
|
// The additional listener formats:
|
|
// func(MouseEvent), func(View), and func().
|
|
MouseOut = "mouse-out"
|
|
|
|
// MouseOver is the constant for "mouse-over" property tag.
|
|
// The "mouse-over" event is fired at a View when a pointing device (such as a mouse or trackpad)
|
|
// is used to move the cursor onto the view or one of its child views.
|
|
// The main listener formats:
|
|
// func(View, MouseEvent).
|
|
// The additional listener formats:
|
|
// func(MouseEvent), func(View), and func().
|
|
MouseOver = "mouse-over"
|
|
|
|
// ContextMenuEvent is the constant for "context-menu-event" property tag.
|
|
// The "context-menu-event" event occurs when the user calls the context menu by the right mouse clicking.
|
|
// The main listener format:
|
|
// func(View, MouseEvent).
|
|
// The additional listener formats:
|
|
// func(MouseEvent), func(View), and func().
|
|
ContextMenuEvent = "context-menu-event"
|
|
|
|
// PrimaryMouseButton is a number of the main pressed button, usually the left button or the un-initialized state
|
|
PrimaryMouseButton = 0
|
|
// AuxiliaryMouseButton is a number of the auxiliary pressed button, usually the wheel button
|
|
// or the middle button (if present)
|
|
AuxiliaryMouseButton = 1
|
|
// SecondaryMouseButton is a number of the secondary pressed button, usually the right button
|
|
SecondaryMouseButton = 2
|
|
// MouseButton4 is a number of the fourth button, typically the Browser Back button
|
|
MouseButton4 = 3
|
|
// MouseButton5 is a number of the fifth button, typically the Browser Forward button
|
|
MouseButton5 = 4
|
|
|
|
// PrimaryMouseMask is the mask of the primary button (usually the left button)
|
|
PrimaryMouseMask = 1
|
|
// SecondaryMouseMask is the mask of the secondary button (usually the right button)
|
|
SecondaryMouseMask = 2
|
|
// AuxiliaryMouseMask is the mask of the auxiliary button (usually the mouse wheel button or middle button)
|
|
AuxiliaryMouseMask = 4
|
|
// MouseMask4 is the mask of the 4th button (typically the "Browser Back" button)
|
|
MouseMask4 = 8
|
|
//MouseMask5 is the mask of the 5th button (typically the "Browser Forward" button)
|
|
MouseMask5 = 16
|
|
)
|
|
|
|
type MouseEvent 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
|
|
|
|
// Button indicates which button was pressed on the mouse to trigger the event:
|
|
// PrimaryMouseButton (0), AuxiliaryMouseButton (1), SecondaryMouseButton (2),
|
|
// MouseButton4 (3), and MouseButton5 (4)
|
|
Button int
|
|
|
|
// Buttons indicates which buttons are pressed on the mouse (or other input device)
|
|
// when a mouse event is triggered. Each button that can be pressed is represented by a given mask:
|
|
// PrimaryMouseMask (1), SecondaryMouseMask (2), AuxiliaryMouseMask (4), MouseMask4 (8), and MouseMask5 (16)
|
|
Buttons int
|
|
|
|
// X provides the horizontal coordinate within the view's viewport.
|
|
X float64
|
|
// Y provides the vertical coordinate within the view's viewport.
|
|
Y float64
|
|
|
|
// ClientX provides the horizontal coordinate within the application's viewport at which the event occurred.
|
|
ClientX float64
|
|
// ClientY provides the vertical coordinate within the application's viewport at which the event occurred.
|
|
ClientY float64
|
|
|
|
// ScreenX provides the horizontal coordinate (offset) of the mouse pointer in global (screen) coordinates.
|
|
ScreenX float64
|
|
// ScreenY provides the vertical coordinate (offset) of the mouse pointer in global (screen) coordinates.
|
|
ScreenY float64
|
|
|
|
// 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 valueToMouseListeners(value interface{}) ([]func(View, MouseEvent), bool) {
|
|
if value == nil {
|
|
return nil, true
|
|
}
|
|
|
|
switch value := value.(type) {
|
|
case func(View, MouseEvent):
|
|
return []func(View, MouseEvent){value}, true
|
|
|
|
case func(MouseEvent):
|
|
fn := func(view View, event MouseEvent) {
|
|
value(event)
|
|
}
|
|
return []func(View, MouseEvent){fn}, true
|
|
|
|
case func(View):
|
|
fn := func(view View, event MouseEvent) {
|
|
value(view)
|
|
}
|
|
return []func(View, MouseEvent){fn}, true
|
|
|
|
case func():
|
|
fn := func(view View, event MouseEvent) {
|
|
value()
|
|
}
|
|
return []func(View, MouseEvent){fn}, true
|
|
|
|
case []func(View, MouseEvent):
|
|
if len(value) == 0 {
|
|
return nil, true
|
|
}
|
|
for _, fn := range value {
|
|
if fn == nil {
|
|
return nil, false
|
|
}
|
|
}
|
|
return value, true
|
|
|
|
case []func(MouseEvent):
|
|
count := len(value)
|
|
if count == 0 {
|
|
return nil, true
|
|
}
|
|
listeners := make([]func(View, MouseEvent), count)
|
|
for i, v := range value {
|
|
if v == nil {
|
|
return nil, false
|
|
}
|
|
listeners[i] = func(view View, event MouseEvent) {
|
|
v(event)
|
|
}
|
|
}
|
|
return listeners, true
|
|
|
|
case []func(View):
|
|
count := len(value)
|
|
if count == 0 {
|
|
return nil, true
|
|
}
|
|
listeners := make([]func(View, MouseEvent), count)
|
|
for i, v := range value {
|
|
if v == nil {
|
|
return nil, false
|
|
}
|
|
listeners[i] = func(view View, event MouseEvent) {
|
|
v(view)
|
|
}
|
|
}
|
|
return listeners, true
|
|
|
|
case []func():
|
|
count := len(value)
|
|
if count == 0 {
|
|
return nil, true
|
|
}
|
|
listeners := make([]func(View, MouseEvent), count)
|
|
for i, v := range value {
|
|
if v == nil {
|
|
return nil, false
|
|
}
|
|
listeners[i] = func(view View, event MouseEvent) {
|
|
v()
|
|
}
|
|
}
|
|
return listeners, true
|
|
|
|
case []interface{}:
|
|
count := len(value)
|
|
if count == 0 {
|
|
return nil, true
|
|
}
|
|
listeners := make([]func(View, MouseEvent), count)
|
|
for i, v := range value {
|
|
if v == nil {
|
|
return nil, false
|
|
}
|
|
switch v := v.(type) {
|
|
case func(View, MouseEvent):
|
|
listeners[i] = v
|
|
|
|
case func(MouseEvent):
|
|
listeners[i] = func(view View, event MouseEvent) {
|
|
v(event)
|
|
}
|
|
|
|
case func(View):
|
|
listeners[i] = func(view View, event MouseEvent) {
|
|
v(view)
|
|
}
|
|
|
|
case func():
|
|
listeners[i] = func(view View, event MouseEvent) {
|
|
v()
|
|
}
|
|
|
|
default:
|
|
return nil, false
|
|
}
|
|
}
|
|
return listeners, true
|
|
}
|
|
|
|
return nil, false
|
|
}
|
|
|
|
var mouseEvents = map[string]struct{ jsEvent, jsFunc string }{
|
|
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"},
|
|
}
|
|
|
|
func (view *viewData) setMouseListener(tag string, value interface{}) bool {
|
|
listeners, ok := valueToMouseListeners(value)
|
|
if !ok {
|
|
notCompatibleType(tag, value)
|
|
return false
|
|
}
|
|
|
|
if listeners == nil {
|
|
view.removeMouseListener(tag)
|
|
} else if js, ok := mouseEvents[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) removeMouseListener(tag string) {
|
|
delete(view.properties, tag)
|
|
if view.created {
|
|
if js, ok := mouseEvents[tag]; ok {
|
|
removeProperty(view.htmlID(), js.jsEvent, view.Session())
|
|
}
|
|
}
|
|
}
|
|
|
|
func getMouseListeners(view View, subviewID string, tag string) []func(View, MouseEvent) {
|
|
if subviewID != "" {
|
|
view = ViewByID(view, subviewID)
|
|
}
|
|
if view != nil {
|
|
if value := view.Get(tag); value != nil {
|
|
if result, ok := value.([]func(View, MouseEvent)); ok {
|
|
return result
|
|
}
|
|
}
|
|
}
|
|
return []func(View, MouseEvent){}
|
|
}
|
|
|
|
func mouseEventsHtml(view View, buffer *strings.Builder) {
|
|
for tag, js := range mouseEvents {
|
|
if value := view.getRaw(tag); value != nil {
|
|
if listeners, ok := value.([]func(View, MouseEvent)); ok && len(listeners) > 0 {
|
|
buffer.WriteString(js.jsEvent + `="` + js.jsFunc + `(this, event)" `)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func getTimeStamp(data DataObject) uint64 {
|
|
if value, ok := data.PropertyValue("timeStamp"); ok {
|
|
if index := strings.Index(value, "."); index > 0 {
|
|
value = value[:index]
|
|
}
|
|
if n, err := strconv.ParseUint(value, 10, 64); err == nil {
|
|
return n
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func (event *MouseEvent) init(data DataObject) {
|
|
|
|
event.TimeStamp = getTimeStamp(data)
|
|
event.Button, _ = dataIntProperty(data, "button")
|
|
event.Buttons, _ = dataIntProperty(data, "buttons")
|
|
event.X = dataFloatProperty(data, "x")
|
|
event.Y = dataFloatProperty(data, "y")
|
|
event.ClientX = dataFloatProperty(data, "clientX")
|
|
event.ClientY = dataFloatProperty(data, "clientY")
|
|
event.ScreenX = dataFloatProperty(data, "screenX")
|
|
event.ScreenY = dataFloatProperty(data, "screenY")
|
|
event.CtrlKey = dataBoolProperty(data, "ctrlKey")
|
|
event.ShiftKey = dataBoolProperty(data, "shiftKey")
|
|
event.AltKey = dataBoolProperty(data, "altKey")
|
|
event.MetaKey = dataBoolProperty(data, "metaKey")
|
|
}
|
|
|
|
func handleMouseEvents(view View, tag string, data DataObject) {
|
|
listeners := getMouseListeners(view, "", tag)
|
|
if len(listeners) == 0 {
|
|
return
|
|
}
|
|
|
|
var event MouseEvent
|
|
event.init(data)
|
|
|
|
for _, listener := range listeners {
|
|
listener(view, event)
|
|
}
|
|
}
|
|
|
|
// GetClickListeners returns the "click-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 GetClickListeners(view View, subviewID string) []func(View, MouseEvent) {
|
|
return getMouseListeners(view, subviewID, ClickEvent)
|
|
}
|
|
|
|
// GetDoubleClickListeners returns the "double-click-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 GetDoubleClickListeners(view View, subviewID string) []func(View, MouseEvent) {
|
|
return getMouseListeners(view, subviewID, DoubleClickEvent)
|
|
}
|
|
|
|
// GetContextMenuListeners returns the "context-menu" 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 GetContextMenuListeners(view View, subviewID string) []func(View, MouseEvent) {
|
|
return getMouseListeners(view, subviewID, ContextMenuEvent)
|
|
}
|
|
|
|
// GetMouseDownListeners returns the "mouse-down" 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 GetMouseDownListeners(view View, subviewID string) []func(View, MouseEvent) {
|
|
return getMouseListeners(view, subviewID, MouseDown)
|
|
}
|
|
|
|
// GetMouseUpListeners returns the "mouse-up" 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 GetMouseUpListeners(view View, subviewID string) []func(View, MouseEvent) {
|
|
return getMouseListeners(view, subviewID, MouseUp)
|
|
}
|
|
|
|
// GetMouseMoveListeners returns the "mouse-move" 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 GetMouseMoveListeners(view View, subviewID string) []func(View, MouseEvent) {
|
|
return getMouseListeners(view, subviewID, MouseMove)
|
|
}
|
|
|
|
// GetMouseOverListeners returns the "mouse-over" 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 GetMouseOverListeners(view View, subviewID string) []func(View, MouseEvent) {
|
|
return getMouseListeners(view, subviewID, MouseOver)
|
|
}
|
|
|
|
// GetMouseOutListeners returns the "mouse-out" 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 GetMouseOutListeners(view View, subviewID string) []func(View, MouseEvent) {
|
|
return getMouseListeners(view, subviewID, MouseOut)
|
|
}
|