Clean up gesture handling

All the gnarly direction detection for swipes is moved into touch.pyj
and the handlers just need to check the event type (except for flow mode
where we really want to match finger movement).
This commit is contained in:
Kovid Goyal 2023-06-11 09:30:27 +05:30
parent fdb68a1613
commit dcfc477c28
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 75 additions and 36 deletions

View File

@ -23,6 +23,7 @@ from __python__ import bound_methods, hash_literals
from dom import set_css from dom import set_css
from range_utils import wrap_range, unwrap from range_utils import wrap_range, unwrap
from read_book.cfi import scroll_to as cfi_scroll_to from read_book.cfi import scroll_to as cfi_scroll_to
from read_book.touch import GESTURE
from read_book.globals import current_spine_item, get_boss, rtl_page_progression, ltr_page_progression from read_book.globals import current_spine_item, get_boss, rtl_page_progression, ltr_page_progression
from read_book.settings import opts from read_book.settings import opts
from read_book.viewport import line_height, rem_size, scroll_viewport from read_book.viewport import line_height, rem_size, scroll_viewport
@ -536,7 +537,9 @@ def start_drag_scroll(delta):
def handle_gesture(gesture): def handle_gesture(gesture):
flick_animator.stop() flick_animator.stop()
if gesture.type is 'swipe': if gesture.type is GESTURE.flick_block_forward or gesture.type is GESTURE.flick_block_backward:
flick_animator.start(gesture)
elif gesture.type.startsWith('swipe_') and gesture.type.endsWith('_in_progress'):
if gesture.points.length > 1 and not gesture.is_held: if gesture.points.length > 1 and not gesture.is_held:
delta = gesture.points[-2] - gesture.points[-1] delta = gesture.points[-2] - gesture.points[-1]
if Math.abs(delta) >= 1: if Math.abs(delta) >= 1:
@ -556,13 +559,11 @@ def handle_gesture(gesture):
# In horizontal modes, just move by the delta. # In horizontal modes, just move by the delta.
else: else:
scroll_viewport.scroll_by(delta, 0) scroll_viewport.scroll_by(delta, 0)
if not gesture.active and not gesture.is_held: elif gesture.type is GESTURE.back_zone_tap:
flick_animator.start(gesture)
elif gesture.type is 'prev-page':
# should flip = False - previous is previous whether RTL or LTR. # should flip = False - previous is previous whether RTL or LTR.
# flipping of this command is handled higher up # flipping of this command is handled higher up
scroll_by_page(-1, False) scroll_by_page(-1, False)
elif gesture.type is 'next-page': elif gesture.type is GESTURE.forward_zone_tap:
# should flip = False - next is next whether RTL or LTR. # should flip = False - next is next whether RTL or LTR.
# flipping of this command is handled higher up # flipping of this command is handled higher up
scroll_by_page(1, False) scroll_by_page(1, False)

View File

@ -9,6 +9,7 @@ from range_utils import (
all_annots_in_selection, highlight_associated_with_selection, last_span_for_crw, all_annots_in_selection, highlight_associated_with_selection, last_span_for_crw,
reset_highlight_counter, select_crw, unwrap_all_crw, unwrap_crw, wrap_text_in_range reset_highlight_counter, select_crw, unwrap_all_crw, unwrap_crw, wrap_text_in_range
) )
from read_book.touch import GESTURE
from read_book.cfi import cfi_for_selection, range_from_cfi from read_book.cfi import cfi_for_selection, range_from_cfi
from read_book.extract import get_elements from read_book.extract import get_elements
from read_book.find import ( from read_book.find import (
@ -315,13 +316,13 @@ class IframeBoss:
self.auto_scroll_action('toggle') self.auto_scroll_action('toggle')
def handle_gesture(self, gesture): def handle_gesture(self, gesture):
if gesture.type is 'show-chrome': if gesture.type is GESTURE.control_zone_tap or gesture.type is GESTURE.two_finger_tap:
self.send_message('show_chrome') self.send_message('show_chrome')
elif gesture.type is 'two-finger-tap': elif gesture.type is GESTURE.pinch_in:
self.send_message('show_chrome') self.send_message('bump_font_size', increase=False)
elif gesture.type is 'pinch': elif gesture.type is GESTURE.pinch_out:
self.send_message('bump_font_size', increase=gesture.direction is 'out') self.send_message('bump_font_size', increase=True)
elif gesture.type is 'long-tap': elif gesture.type is GESTURE.long_tap:
self.handle_long_tap(gesture) self.handle_long_tap(gesture)
else: else:
self._handle_gesture(gesture) self._handle_gesture(gesture)

View File

@ -32,6 +32,7 @@ from read_book.cfi import (
scroll_to as cfi_scroll_to scroll_to as cfi_scroll_to
) )
from read_book.globals import current_spine_item, get_boss, rtl_page_progression from read_book.globals import current_spine_item, get_boss, rtl_page_progression
from read_book.touch import GESTURE
from read_book.settings import opts from read_book.settings import opts
from read_book.viewport import scroll_viewport, line_height, rem_size, get_unit_size_in_pixels from read_book.viewport import scroll_viewport, line_height, rem_size, get_unit_size_in_pixels
from utils import ( from utils import (
@ -808,19 +809,20 @@ def handle_shortcut(sc_name, evt):
def handle_gesture(gesture): def handle_gesture(gesture):
if gesture.type is 'swipe':
if gesture.axis is 'vertical':
if not gesture.active:
get_boss().send_message('next_section', forward=gesture.direction is 'up')
else:
if not gesture.active or gesture.is_held:
scroll_by_page(gesture.direction is 'right', True, flip_if_rtl_page_progression=True)
# Gesture progression direction is determined in the gesture code, # Gesture progression direction is determined in the gesture code,
# don't set flip_if_rtl_page_progression=True here. # don't set flip_if_rtl_page_progression=True here.
elif gesture.type is 'prev-page': if gesture.type is GESTURE.flick_block_forward:
scroll_by_page(True, opts.paged_taps_scroll_by_screen, flip_if_rtl_page_progression=False) get_boss().send_message('next_section', forward=True)
elif gesture.type is 'next-page': elif gesture.type is GESTURE.flick_block_backward:
scroll_by_page(False, opts.paged_taps_scroll_by_screen, flip_if_rtl_page_progression=False) get_boss().send_message('next_section', forward=False)
elif gesture.type is GESTURE.flick_inline_forward or gesture.type is GESTURE.swipe_inline_forward_hold:
scroll_by_page(False, True)
elif gesture.type is GESTURE.flick_inline_backward or gesture.type is GESTURE.swipe_inline_backward_hold:
scroll_by_page(True, True)
elif gesture.type is GESTURE.back_zone_tap:
scroll_by_page(True, opts.paged_taps_scroll_by_screen)
elif gesture.type is GESTURE.forward_zone_tap:
scroll_by_page(False, opts.paged_taps_scroll_by_screen)
anchor_funcs = { anchor_funcs = {

View File

@ -2,6 +2,7 @@
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net> # License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import bound_methods, hash_literals from __python__ import bound_methods, hash_literals
from gettext import gettext as _
from read_book.globals import get_boss, ltr_page_progression, ui_operations from read_book.globals import get_boss, ltr_page_progression, ui_operations
from read_book.settings import opts from read_book.settings import opts
from read_book.viewport import get_unit_size_in_pixels, scroll_viewport from read_book.viewport import get_unit_size_in_pixels, scroll_viewport
@ -11,6 +12,31 @@ TAP_THRESHOLD = 8 # pixels
SWIPE_THRESHOLD = 64 # pixels SWIPE_THRESHOLD = 64 # pixels
TAP_LINK_THRESHOLD = 5 # pixels TAP_LINK_THRESHOLD = 5 # pixels
PINCH_THRESHOLD = 20 # pixels PINCH_THRESHOLD = 20 # pixels
GESTURE_NAMES = {
'back_zone_tap': _('Tap on back zone'),
'forward_zone_tap': _('Tap on forward zone'),
'control_zone_tap': _('Tap in the controls zone'),
'long_tap': _('Long tap'),
'two_finger_tap': _('Two finger tap'),
'pinch_in': _('Pinch in'),
'pinch_out': _('Pinch out'),
'flick_inline_backward': _('Flick in writing direction, backwards'),
'flick_inline_forward': _('Flick in writing direction, forwards'),
'flick_block_backward': _('Flick perpendicular to writing direction, backwards'),
'flick_block_forward': _('Flick perpendicular to writing direction, forwards'),
'swipe_inline_backward_in_progress': _('Swipe in writing direction, backwards, in-progress'),
'swipe_inline_forward_in_progress': _('Swipe in writing direction, forwards, in-progress'),
'swipe_block_backward_in_progress': _('Swipe perpendicular to writing direction, backwards, in-progress'),
'swipe_block_forward_in_progress': _('Swipe perpendicular to writing direction, forwards, in-progress'),
'swipe_inline_backward_hold': _('Swipe in writing direction, backwards and hold'),
'swipe_inline_forward_hold': _('Swipe in writing direction, forwards and hold'),
'swipe_block_backward_hold': _('Swipe perpendicular to writing direction, backwards and hold'),
'swipe_block_forward_hold': _('Swipe perpendicular to writing direction, forwards and hold'),
}
GESTURE = {k:k for k in Object.keys(GESTURE_NAMES)}
GESTURE.tap = 'tap'
GESTURE.swipe = 'swipe'
GESTURE.pinch = 'pinch'
gesture_id = 0 gesture_id = 0
@ -50,7 +76,7 @@ def interpret_single_gesture(touch, gesture_id):
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, 'id':gesture_id, 'start_time': touch.ctime} ans = {'active':touch.active, 'is_held':touch.is_held, 'id':gesture_id, 'start_time': touch.ctime}
if max(max_x_displacement, max_y_displacement) < TAP_THRESHOLD: if max(max_x_displacement, max_y_displacement) < TAP_THRESHOLD:
ans.type = 'tap' ans.type = GESTURE.tap
ans.viewport_x = touch.viewport_x[0] ans.viewport_x = touch.viewport_x[0]
ans.viewport_y = touch.viewport_y[0] ans.viewport_y = touch.viewport_y[0]
return ans return ans
@ -60,7 +86,7 @@ def interpret_single_gesture(touch, gesture_id):
delta_y = abs(touch.viewport_y[-1] - touch.viewport_y[0]) delta_y = abs(touch.viewport_y[-1] - touch.viewport_y[0])
max_disp = max(delta_y, delta_x) max_disp = max(delta_y, delta_x)
if max_disp > SWIPE_THRESHOLD and min(delta_x, delta_y)/max_disp < 0.35: if max_disp > SWIPE_THRESHOLD and min(delta_x, delta_y)/max_disp < 0.35:
ans.type = 'swipe' ans.type = GESTURE.swipe
ans.axis = 'vertical' if delta_y > delta_x else 'horizontal' ans.axis = 'vertical' if delta_y > delta_x else 'horizontal'
ans.points = 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
ans.times = touch.mtimes ans.times = touch.mtimes
@ -82,7 +108,7 @@ def interpret_double_gesture(touch1, touch2, gesture_id):
max_y_displacement1 = max_displacement(touch1.viewport_y) max_y_displacement1 = max_displacement(touch1.viewport_y)
max_y_displacement2 = max_displacement(touch2.viewport_y) max_y_displacement2 = max_displacement(touch2.viewport_y)
if max(max_x_displacement1, max_y_displacement1) < TAP_THRESHOLD and max(max_x_displacement2, max_y_displacement2) < TAP_THRESHOLD: if max(max_x_displacement1, max_y_displacement1) < TAP_THRESHOLD and max(max_x_displacement2, max_y_displacement2) < TAP_THRESHOLD:
ans.type = 'two-finger-tap' ans.type = GESTURE.two_finger_tap
ans.viewport_x1 = touch1.viewport_x[0] ans.viewport_x1 = touch1.viewport_x[0]
ans.viewport_y1 = touch1.viewport_y[0] ans.viewport_y1 = touch1.viewport_y[0]
ans.viewport_x2 = touch2.viewport_x[0] ans.viewport_x2 = touch2.viewport_x[0]
@ -92,7 +118,7 @@ def interpret_double_gesture(touch1, touch2, gesture_id):
final_distance = Math.sqrt((touch1.viewport_x[-1] - touch2.viewport_x[-1])**2 + (touch1.viewport_y[-1] - touch2.viewport_y[-1])**2) final_distance = Math.sqrt((touch1.viewport_x[-1] - touch2.viewport_x[-1])**2 + (touch1.viewport_y[-1] - touch2.viewport_y[-1])**2)
distance = abs(final_distance - initial_distance) distance = abs(final_distance - initial_distance)
if distance > PINCH_THRESHOLD: if distance > PINCH_THRESHOLD:
ans.type = 'pinch' ans.type = GESTURE.pinch
ans.direction = 'in' if final_distance < initial_distance else 'out' ans.direction = 'in' if final_distance < initial_distance else 'out'
ans.distance = distance ans.distance = distance
return ans return ans
@ -255,42 +281,51 @@ class BookTouchHandler(TouchHandler):
TouchHandler.__init__(self) TouchHandler.__init__(self)
def handle_gesture(self, gesture): def handle_gesture(self, gesture):
gesture.from_side_margin = self.for_side_margin if gesture.type is GESTURE.tap:
if gesture.type is 'tap':
if gesture.is_held: if gesture.is_held:
if not self.for_side_margin and not self.handled_tap_hold and window.performance.now() - gesture.start_time >= HOLD_THRESHOLD: if not self.for_side_margin and not self.handled_tap_hold and window.performance.now() - gesture.start_time >= HOLD_THRESHOLD:
self.handled_tap_hold = True self.handled_tap_hold = True
gesture.type = 'long-tap' gesture.type = GESTURE.long_tap
get_boss().handle_gesture(gesture) get_boss().handle_gesture(gesture)
return return
if not gesture.active: if not gesture.active:
if self.for_side_margin or not tap_on_link(gesture): if self.for_side_margin or not tap_on_link(gesture):
inch = inch_in_pixels() inch = inch_in_pixels()
if gesture.viewport_y < min(100, scroll_viewport.height() / 4): if gesture.viewport_y < min(100, scroll_viewport.height() / 4):
gesture.type = 'show-chrome' gesture.type = GESTURE.control_zone_tap
else: else:
limit = inch limit = inch
# default, books that go left to right. # default, books that go left to right.
if ltr_page_progression() and not opts.reverse_page_turn_zones: if ltr_page_progression() and not opts.reverse_page_turn_zones:
if gesture.viewport_x < min(limit, scroll_viewport.width() / 4): if gesture.viewport_x < min(limit, scroll_viewport.width() / 4):
gesture.type = 'prev-page' gesture.type = GESTURE.back_zone_tap
else: else:
gesture.type = 'next-page' gesture.type = GESTURE.forward_zone_tap
# We swap the sizes in RTL mode, so that going to the next page is always the bigger touch region. # We swap the sizes in RTL mode, so that going to the next page is always the bigger touch region.
else: else:
# The "going back" area should not be more than limit units big, # The "going back" area should not be more than limit units big,
# even if 1/4 of the scroll viewport is more than limit units. # even if 1/4 of the scroll viewport is more than limit units.
# Checking against the larger of the width minus the limit units and 3/4 of the width will accomplish that. # Checking against the larger of the width minus the limit units and 3/4 of the width will accomplish that.
if gesture.viewport_x > max(scroll_viewport.width() - limit, scroll_viewport.width() * (3/4)): if gesture.viewport_x > max(scroll_viewport.width() - limit, scroll_viewport.width() * (3/4)):
gesture.type = 'prev-page' gesture.type = GESTURE.back_zone_tap
else: else:
gesture.type = 'next-page' gesture.type = GESTURE.forward_zone_tap
if gesture.type is 'pinch': elif gesture.type is GESTURE.pinch:
if gesture.active: if gesture.active:
return return
if gesture.type is 'two-finger-tap': gesture.type = GESTURE.pinch_in if gesture.direction is 'in' else GESTURE.pinch_out
elif gesture.type is GESTURE.two_finger_tap:
if gesture.active: if gesture.active:
return return
elif gesture.type is 'swipe':
backward_dir = 'down' if gesture.axis is 'vertical' else ('right' if ltr_page_progression() else 'left')
direction = 'backward' if gesture.direction is backward_dir else 'forward'
inline_dir = 'vertical' if scroll_viewport.vertical_writing_mode else 'horizontal'
axis = 'inline' if gesture.axis is inline_dir else 'block'
if gesture.active:
gesture.type = GESTURE[f'swipe_{axis}_{direction}' + ('_hold' if gesture.is_held else '_in_progress')]
elif not gesture.is_held:
gesture.type = GESTURE[f'flick_{axis}_{direction}']
if self.for_side_margin: if self.for_side_margin:
ui_operations.forward_gesture(gesture) ui_operations.forward_gesture(gesture)
else: else: