neft
Version:
Universal Platform
284 lines (237 loc) • 9.61 kB
text/coffeescript
'use strict'
utils = require 'src/utils'
signal = require 'src/signal'
{sin, cos} = Math
{emitSignal} = signal.Emitter
isPointInBox = (ex, ey, x, y, w, h) ->
ex >= x and ey >= y and ex < x + w and ey < y + h
module.exports = (impl) ->
PROPAGATE_UP = 1<<0
STOP_ASIDE_PROPAGATION = 1<<1
STOP_PROPAGATION = 1<<2
captureItems = do ->
checkItem = (item, ex, ey, onItem, parentX, parentY, parentScale) ->
result = 0
x = y = w = h = scale = rotation = t1 = t2 = rey = rsin = rcos = 0.0
# omit not visible children
unless item._visible
return result
# omit children with no pointer events listeners
data = item._impl
# omit children with disabled pointer
pointer = item._pointer
if pointer and not pointer._enabled
return result
# get coordinates
x = parentX + item._x * parentScale
y = parentY + item._y * parentScale
w = item._width * parentScale
h = item._height * parentScale
scale = item._scale
rotation = item._rotation
# add scale
if scale isnt 1
t1 = w * scale
t2 = h * scale
x += (w - t1) / 2
y += (h - t2) / 2
w = t1
h = t2
# add rotation
if rotation isnt 0
rsin = sin -rotation
rcos = cos -rotation
t1 = x + w/2
t2 = y + h/2
rey = rcos * (ex-t1) - rsin * (ey-t2) + t1
ey = rsin * (ex-t1) + rcos * (ey-t2) + t2
ex = rey
# omit outer point for clipped items
if item._clip and not isPointInBox(ex, ey, x, y, w, h)
return result
unless result & STOP_ASIDE_PROPAGATION
# test children
if item._children
child = item._children.topChild
while child
result |= checkItem(child, ex, ey, onItem, x, y, scale)
if result & STOP_PROPAGATION
return result
if result & STOP_ASIDE_PROPAGATION
break
child = child.belowSibling
# test content item
if item._contentItem and not (result & STOP_ASIDE_PROPAGATION)
result |= checkItem(
item._contentItem,
ex, ey,
onItem,
x - item.contentX * parentScale, y - item.contentY * parentScale,
scale
)
if result & STOP_PROPAGATION
return result
# test this child
if result & PROPAGATE_UP or isPointInBox(ex, ey, x, y, w, h)
result |= onItem(item)
return result
(item, ex, ey, onItem) ->
if item
return checkItem item, ex, ey, onItem, 0, 0, 1
return 0
itemsToRelease = []
itemsToMove = []
pressedItems = []
hoverItems = []
impl.Renderer.onReady ->
{Device} = impl.Renderer
{event} = impl.Renderer.Item.Pointer
getEventStatus = ->
if event._checkSiblings
PROPAGATE_UP
else
PROPAGATE_UP | STOP_ASIDE_PROPAGATION
# support press event
Device.onPointerPress do ->
onItem = (item) ->
{capturePointer} = item._impl
if capturePointer & CLICK
pressedItems.push item
if capturePointer & PRESS
event._ensureRelease = event._ensureMove = true
emitSignal item.pointer, 'onPress', event
if event._ensureRelease
itemsToRelease.push item
if event._ensureMove
itemsToMove.push item
if event._stopPropagation
return STOP_PROPAGATION
return getEventStatus()
PROPAGATE_UP | STOP_ASIDE_PROPAGATION
(e) ->
event._stopPropagation = false
event._checkSiblings = false
captureItems impl.window, e._x, e._y, onItem
return
# support release and click events
Device.onPointerRelease do ->
onItem = (item) ->
data = item._impl
{capturePointer} = data
if capturePointer & RELEASE
emitSignal item._pointer, 'onRelease', event
if capturePointer & PRESS
index = itemsToRelease.indexOf item
if index >= 0
itemsToRelease[index] = null
if capturePointer & CLICK
if utils.has(pressedItems, item)
emitSignal item.pointer, 'onClick', event
if capturePointer & (RELEASE | CLICK)
if event._stopPropagation
return STOP_PROPAGATION
return getEventStatus()
PROPAGATE_UP | STOP_ASIDE_PROPAGATION
(e) ->
event._stopPropagation = false
event._checkSiblings = false
captureItems impl.window, e._x, e._y, onItem
unless event._stopPropagation
for item in itemsToRelease by -1
if item
emitSignal item.pointer, 'onRelease', event
if event._stopPropagation
break
utils.clear itemsToRelease
utils.clear itemsToMove
utils.clear pressedItems
return
# support move, enter and exit events
Device.onPointerMove do ->
flag = 0
onItem = (item) ->
data = item._impl
{capturePointer} = data
if capturePointer & (ENTER | EXIT | MOVE)
data.pointerMoveFlag = flag
if capturePointer & (ENTER | EXIT) and not data.pointerHover
data.pointerHover = true
hoverItems.push item
emitSignal item.pointer, 'onEnter', event
if capturePointer & MOVE
emitSignal item._pointer, 'onMove', event
if capturePointer & (ENTER | EXIT | MOVE)
if event._stopPropagation
return STOP_PROPAGATION
return getEventStatus()
PROPAGATE_UP | STOP_ASIDE_PROPAGATION
(e) ->
event._stopPropagation = false
event._checkSiblings = false
flag = (flag % 2) + 1
captureItems impl.window, e._x, e._y, onItem
for item in itemsToMove
if event._stopPropagation
break
data = item._impl
if data.pointerMoveFlag isnt flag
emitSignal item.pointer, 'onMove', event
for item, i in hoverItems by -1
data = item._impl
if data.pointerHover and data.pointerMoveFlag isnt flag
data.pointerHover = false
data.pointerMoveFlag = 0
hoverItems.splice i, 1
emitSignal item.pointer, 'onExit', event
for item in itemsToMove
data = item._impl
if data.pointerMoveFlag isnt flag
data.pointerMoveFlag = flag
return
# support wheel event
Device.onPointerWheel do ->
onItem = (item) ->
event._stopPropagation = true
if item._impl.capturePointer & WHEEL
if (pointer = item._pointer) and not signal.isEmpty(pointer.onWheel)
emitSignal pointer, 'onWheel', event
if event._stopPropagation
return STOP_PROPAGATION
return getEventStatus()
PROPAGATE_UP | STOP_ASIDE_PROPAGATION
(e) ->
event._checkSiblings = false
captureItems impl.window, e._x, e._y, onItem
return
i = 0
# TODO: support drag events
EVENTS: EVENTS =
onPress: PRESS = 1 << i++
onRelease: RELEASE = 1 << i++
onMove: MOVE = 1 << i++
onWheel: WHEEL = 1 << i++
onClick: CLICK = 1 << i++
onEnter: ENTER = 1 << i++
onExit: EXIT = 1 << i++
# onDragStart: 1 << i++
# onDragEnd: 1 << i++
# onDragEnter: 1 << i++
# onDragExit: 1 << i++
# onDrop: 1 << i++
DATA:
pointerHover: false
pointerMoveFlag: 0
capturePointer: 0
attachItemSignal: (signal) ->
item =
data = item._impl
unless eventId = EVENTS[signal]
return
data.capturePointer |= eventId
return
setItemPointerEnabled: (val) ->
return
setItemPointerDraggable: (val) ->
return
setItemPointerDragActive: (val) ->
return