Give gestures an id and clean up tap hold handling

This commit is contained in:
Kovid Goyal 2016-08-20 10:53:47 +05:30
parent c7163db345
commit 67f0358296

View File

@ -4,16 +4,15 @@ from __python__ import hash_literals, bound_methods
from read_book.globals import get_boss from read_book.globals import get_boss
touch_id = 0 gesture_id = 0
handled_held_taps = {}
def copy_touch(t): def copy_touch(t):
nonlocal touch_id
touch_id += 1
now = window.performance.now() now = window.performance.now()
return {'identifier':t.identifier, 'id':touch_id, return {
'identifier':t.identifier,
'page_x':v'[t.pageX]', 'page_y':v'[t.pageY]', 'viewport_x':v'[t.clientX]', 'viewport_y':v'[t.clientY]', 'page_x':v'[t.pageX]', 'page_y':v'[t.pageY]', 'viewport_x':v'[t.clientX]', 'viewport_y':v'[t.clientY]',
'active':True, 'mtime':now, 'ctime':now, 'is_held':False} 'active':True, 'mtime':now, 'ctime':now, 'is_held':False
}
def max_displacement(points): def max_displacement(points):
ans = 0 ans = 0
@ -26,10 +25,10 @@ def max_displacement(points):
ans = delta ans = delta
return ans return ans
def interpret_single_gesture(touch): def interpret_single_gesture(touch, gesture_id):
max_x_displacement = max_displacement(touch.viewport_x) max_x_displacement = max_displacement(touch.viewport_x)
max_y_displacement = max_displacement(touch.viewport_y) max_y_displacement = max_displacement(touch.viewport_y)
ans = {'active':touch.active, 'is_held':touch.is_held} ans = {'active':touch.active, 'is_held':touch.is_held, 'id':gesture_id}
if max(max_x_displacement, max_y_displacement) < 25: if max(max_x_displacement, max_y_displacement) < 25:
ans.type = 'tap' ans.type = 'tap'
ans.viewport_x = touch.viewport_x[0] ans.viewport_x = touch.viewport_x[0]
@ -84,22 +83,29 @@ class TouchHandler:
def __init__(self): def __init__(self):
self.ongoing_touches = {} self.ongoing_touches = {}
self.gesture_id = None
self.hold_timer = None self.hold_timer = None
self.handled_tap_hold = False
@property def prune_expired_touches(self):
def has_active_touches(self):
now = window.performance.now() now = window.performance.now()
expired = v'[]' expired = v'[]'
ans = False ans = False
for t in self.ongoing_touches: for tid in self.ongoing_touches:
t = self.ongoing_touches[tid]
if t.active: if t.active:
if now - t.mtime > 3000: if now - t.mtime > 3000:
expired.push(t.identifier) expired.push(t.identifier)
ans = True
break
for tid in expired: for tid in expired:
v'delete self.ongoing_touches[tid]' v'delete self.ongoing_touches[tid]'
return ans
@property
def has_active_touches(self):
for tid in self.ongoing_touches:
t = self.ongoing_touches[tid]
if t.active:
return True
return False
def start_hold_timer(self): def start_hold_timer(self):
self.stop_hold_timer() self.stop_hold_timer()
@ -116,7 +122,7 @@ class TouchHandler:
found_hold = False found_hold = False
for touchid in self.ongoing_touches: for touchid in self.ongoing_touches:
touch = self.ongoing_touches[touchid] touch = self.ongoing_touches[touchid]
if now - touch.mtime > 750: if touch.active and now - touch.mtime > 750:
touch.is_held = True touch.is_held = True
found_hold = True found_hold = True
if found_hold: if found_hold:
@ -125,8 +131,14 @@ class TouchHandler:
def handle_touchstart(self, ev): def handle_touchstart(self, ev):
ev.preventDefault(), ev.stopPropagation() ev.preventDefault(), ev.stopPropagation()
self.prune_expired_touches()
for touch in ev.changedTouches: for touch in ev.changedTouches:
self.ongoing_touches[touch.identifier] = copy_touch(touch) self.ongoing_touches[touch.identifier] = copy_touch(touch)
if self.gesture_id is None:
nonlocal gesture_id
gesture_id += 1
self.gesture_id = gesture_id
self.handled_tap_hold = False
if len(self.ongoing_touches) > 0: if len(self.ongoing_touches) > 0:
self.start_hold_timer() self.start_hold_timer()
@ -150,29 +162,33 @@ class TouchHandler:
if t: if t:
t.active = False t.active = False
self.update_touch(t, touch) self.update_touch(t, touch)
self.prune_expired_touches()
if not self.has_active_touches: if not self.has_active_touches:
self.dispatch_gesture() self.dispatch_gesture()
self.ongoing_touches = {} self.ongoing_touches = {}
self.gesture_id = None
self.handled_tap_hold = False
def handle_touchcancel(self, ev): def handle_touchcancel(self, ev):
ev.preventDefault(), ev.stopPropagation() ev.preventDefault(), ev.stopPropagation()
for touch in ev.changedTouches: for touch in ev.changedTouches:
v'delete self.ongoing_touches[touch.identifier]' v'delete self.ongoing_touches[touch.identifier]'
self.gesture_id = None
self.handled_tap_hold = False
def dispatch_gesture(self): def dispatch_gesture(self):
touches = self.ongoing_touches touches = self.ongoing_touches
num = len(touches) num = len(touches)
gesture = {} gesture = {}
if num is 1: if num is 1:
gesture = interpret_single_gesture(touches[Object.keys(touches)[0]]) gesture = interpret_single_gesture(touches[Object.keys(touches)[0]], self.gesture_id)
if not gesture?.type: if not gesture?.type:
return return
if gesture.type is 'tap': if gesture.type is 'tap':
if gesture.is_held: if gesture.is_held:
if handled_held_taps[gesture.id]: if self.handled_tap_hold:
return return
handled_held_taps[gesture.id] = True self.handled_tap_hold = True
# TODO: send a fake click event # TODO: send a fake click event
if not tap_on_link(gesture): if not tap_on_link(gesture):
# TODO: Convert the tap gesture into # TODO: Convert the tap gesture into