mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement swipe and hold gesture to turn pages rapidly
This commit is contained in:
parent
a832c170fc
commit
abe32ad8ca
@ -495,8 +495,8 @@ def onkeydown(evt):
|
|||||||
|
|
||||||
def handle_gesture(gesture):
|
def handle_gesture(gesture):
|
||||||
if gesture.type is 'swipe':
|
if gesture.type is 'swipe':
|
||||||
if not gesture.active:
|
if gesture.axis is 'vertical':
|
||||||
if gesture.axis is 'vertical':
|
pass # TODO: next/prev section
|
||||||
pass # TODO: next/prev section
|
else:
|
||||||
else:
|
if not gesture.active or gesture.is_held:
|
||||||
scroll_by_page(gesture.direction is 'right', False)
|
scroll_by_page(gesture.direction is 'right', False)
|
||||||
|
@ -4,10 +4,16 @@ from __python__ import hash_literals, bound_methods
|
|||||||
|
|
||||||
from read_book.globals import get_boss
|
from read_book.globals import get_boss
|
||||||
|
|
||||||
|
touch_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, 'page_x':v'[t.pageX]', 'page_y':v'[t.pageY]', 'viewport_x':v'[t.clientX]', 'viewport_y':v'[t.clientY]',
|
return {'identifier':t.identifier, 'id':touch_id,
|
||||||
'active':True, 'mtime':now, 'ctime':now}
|
'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}
|
||||||
|
|
||||||
def max_displacement(points):
|
def max_displacement(points):
|
||||||
ans = 0
|
ans = 0
|
||||||
@ -23,28 +29,36 @@ def max_displacement(points):
|
|||||||
def interpret_single_gesture(touch):
|
def interpret_single_gesture(touch):
|
||||||
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}
|
||||||
if max(max_x_displacement, max_y_displacement) < 25:
|
if max(max_x_displacement, max_y_displacement) < 25:
|
||||||
return {'type':'tap'}
|
ans.type = 'tap'
|
||||||
|
ans.viewport_x = touch.viewport_x[0]
|
||||||
|
ans.viewport_y = touch.viewport_y
|
||||||
|
return ans
|
||||||
if touch.viewport_y.length < 2:
|
if touch.viewport_y.length < 2:
|
||||||
return {}
|
return ans
|
||||||
delta_x = abs(touch.viewport_x[-1] - touch.viewport_x[0])
|
delta_x = abs(touch.viewport_x[-1] - touch.viewport_x[0])
|
||||||
delta_y = abs(touch.viewport_y[-1] - touch.viewport_y[0])
|
delta_y = abs(touch.viewport_y[-1] - touch.viewport_y[0])
|
||||||
if max(delta_y, delta_x) > 30 and min(delta_x, delta_y)/max(delta_y, delta_x) < 0.35:
|
if max(delta_y, delta_x) > 30 and min(delta_x, delta_y)/max(delta_y, delta_x) < 0.35:
|
||||||
ans = {'type':'swipe'}
|
ans.type = 'swipe'
|
||||||
ans.axis = 'vertical' if delta_y > delta_x else 'horizontal'
|
ans.axis = 'vertical' if delta_y > delta_x else 'horizontal'
|
||||||
pts = touch.viewport_y if ans.axis is 'vertical' else touch.viewport_x
|
ans.points = pts = touch.viewport_y if ans.axis is 'vertical' else touch.viewport_x
|
||||||
positive = pts[-1] > pts[0]
|
positive = pts[-1] > pts[0]
|
||||||
if ans.axis is 'vertical':
|
if ans.axis is 'vertical':
|
||||||
ans.direction = 'down' if positive else 'up'
|
ans.direction = 'down' if positive else 'up'
|
||||||
else:
|
else:
|
||||||
ans.direction = 'right' if positive else 'left'
|
ans.direction = 'right' if positive else 'left'
|
||||||
return ans
|
return ans
|
||||||
return {}
|
return ans
|
||||||
|
|
||||||
|
def tap_on_link(gesture):
|
||||||
|
pass
|
||||||
|
|
||||||
class TouchHandler:
|
class TouchHandler:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ongoing_touches = {}
|
self.ongoing_touches = {}
|
||||||
|
self.hold_timer = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_active_touches(self):
|
def has_active_touches(self):
|
||||||
@ -61,10 +75,34 @@ class TouchHandler:
|
|||||||
v'delete self.ongoing_touches[tid]'
|
v'delete self.ongoing_touches[tid]'
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
def start_hold_timer(self):
|
||||||
|
self.stop_hold_timer()
|
||||||
|
self.hold_timer = window.setTimeout(self.check_for_hold, 100)
|
||||||
|
|
||||||
|
def stop_hold_timer(self):
|
||||||
|
if self.hold_timer is not None:
|
||||||
|
window.clearTimeout(self.hold_timer)
|
||||||
|
self.hold_timer = None
|
||||||
|
|
||||||
|
def check_for_hold(self):
|
||||||
|
if len(self.ongoing_touches) > 0:
|
||||||
|
now = window.performance.now()
|
||||||
|
found_hold = False
|
||||||
|
for touchid in self.ongoing_touches:
|
||||||
|
touch = self.ongoing_touches[touchid]
|
||||||
|
if now - touch.mtime > 750:
|
||||||
|
touch.is_held = True
|
||||||
|
found_hold = True
|
||||||
|
if found_hold:
|
||||||
|
self.dispatch_gesture()
|
||||||
|
self.start_hold_timer()
|
||||||
|
|
||||||
def handle_touchstart(self, ev):
|
def handle_touchstart(self, ev):
|
||||||
ev.preventDefault(), ev.stopPropagation()
|
ev.preventDefault(), ev.stopPropagation()
|
||||||
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 len(self.ongoing_touches) > 0:
|
||||||
|
self.start_hold_timer()
|
||||||
|
|
||||||
def update_touch(self, t, touch):
|
def update_touch(self, t, touch):
|
||||||
t.mtime = window.performance.now()
|
t.mtime = window.performance.now()
|
||||||
@ -77,12 +115,7 @@ class TouchHandler:
|
|||||||
t = self.ongoing_touches[touch.identifier]
|
t = self.ongoing_touches[touch.identifier]
|
||||||
if t:
|
if t:
|
||||||
self.update_touch(t, touch)
|
self.update_touch(t, touch)
|
||||||
if len(self.ongoing_touches) is 1:
|
self.dispatch_gesture()
|
||||||
gesture = interpret_single_gesture(t)
|
|
||||||
if gesture?.type is 'swipe':
|
|
||||||
gesture.active = True
|
|
||||||
get_boss().handle_gesture(gesture)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_touchend(self, ev):
|
def handle_touchend(self, ev):
|
||||||
ev.preventDefault(), ev.stopPropagation()
|
ev.preventDefault(), ev.stopPropagation()
|
||||||
@ -92,15 +125,17 @@ class TouchHandler:
|
|||||||
t.active = False
|
t.active = False
|
||||||
self.update_touch(t, touch)
|
self.update_touch(t, touch)
|
||||||
if not self.has_active_touches:
|
if not self.has_active_touches:
|
||||||
touches, self.ongoing_touches = self.ongoing_touches, {}
|
self.dispatch_gesture()
|
||||||
self.interpret_gesture(touches)
|
self.ongoing_touches = {}
|
||||||
|
|
||||||
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]'
|
||||||
|
|
||||||
def interpret_gesture(self, touches):
|
|
||||||
|
def dispatch_gesture(self):
|
||||||
|
touches = self.ongoing_touches
|
||||||
num = len(touches)
|
num = len(touches)
|
||||||
gesture = {}
|
gesture = {}
|
||||||
if num is 1:
|
if num is 1:
|
||||||
@ -108,16 +143,22 @@ class TouchHandler:
|
|||||||
if not gesture?.type:
|
if not gesture?.type:
|
||||||
return
|
return
|
||||||
if gesture.type is 'tap':
|
if gesture.type is 'tap':
|
||||||
# TODO: Check for tap on link. Also convert the tap gesture into
|
if gesture.is_held:
|
||||||
# semantic gestures based on position of tap (next page/previous
|
if handled_held_taps[gesture.id]:
|
||||||
# page/show ui) as these are common to both paged and flow mode.
|
return
|
||||||
pass
|
handled_held_taps[gesture.id] = True
|
||||||
|
# TODO: send a fake click event
|
||||||
|
if not tap_on_link(gesture):
|
||||||
|
# TODO: Check for tap on link. Also convert the tap gesture into
|
||||||
|
# semantic gestures based on position of tap (next page/previous
|
||||||
|
# page/show ui) as these are common to both paged and flow mode.
|
||||||
|
pass
|
||||||
get_boss().handle_gesture(gesture)
|
get_boss().handle_gesture(gesture)
|
||||||
|
|
||||||
touch_handler = TouchHandler()
|
touch_handler = TouchHandler()
|
||||||
|
|
||||||
def create_handlers():
|
def create_handlers():
|
||||||
document.body.addEventListener('touchstart', touch_handler.handle_touchstart, True)
|
window.addEventListener('touchstart', touch_handler.handle_touchstart, True)
|
||||||
document.body.addEventListener('touchmove', touch_handler.handle_touchmove, True)
|
window.addEventListener('touchmove', touch_handler.handle_touchmove, True)
|
||||||
document.body.addEventListener('touchend', touch_handler.handle_touchend, True)
|
window.addEventListener('touchend', touch_handler.handle_touchend, True)
|
||||||
document.body.addEventListener('touchcancel', touch_handler.handle_touchcancel, True)
|
window.addEventListener('touchcancel', touch_handler.handle_touchcancel, True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user