From dcfc477c286fdb590d91055a38e4237a0a22faea Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Jun 2023 09:30:27 +0530 Subject: [PATCH] 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). --- src/pyj/read_book/flow_mode.pyj | 11 +++--- src/pyj/read_book/iframe.pyj | 13 ++++--- src/pyj/read_book/paged_mode.pyj | 24 ++++++------ src/pyj/read_book/touch.pyj | 63 +++++++++++++++++++++++++------- 4 files changed, 75 insertions(+), 36 deletions(-) diff --git a/src/pyj/read_book/flow_mode.pyj b/src/pyj/read_book/flow_mode.pyj index 1354c44b5e..ab955abbba 100644 --- a/src/pyj/read_book/flow_mode.pyj +++ b/src/pyj/read_book/flow_mode.pyj @@ -23,6 +23,7 @@ from __python__ import bound_methods, hash_literals from dom import set_css from range_utils import wrap_range, unwrap 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.settings import opts from read_book.viewport import line_height, rem_size, scroll_viewport @@ -536,7 +537,9 @@ def start_drag_scroll(delta): def handle_gesture(gesture): 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: delta = gesture.points[-2] - gesture.points[-1] if Math.abs(delta) >= 1: @@ -556,13 +559,11 @@ def handle_gesture(gesture): # In horizontal modes, just move by the delta. else: scroll_viewport.scroll_by(delta, 0) - if not gesture.active and not gesture.is_held: - flick_animator.start(gesture) - elif gesture.type is 'prev-page': + elif gesture.type is GESTURE.back_zone_tap: # should flip = False - previous is previous whether RTL or LTR. # flipping of this command is handled higher up 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. # flipping of this command is handled higher up scroll_by_page(1, False) diff --git a/src/pyj/read_book/iframe.pyj b/src/pyj/read_book/iframe.pyj index 9eaf9f5ff4..f19ba47dfb 100644 --- a/src/pyj/read_book/iframe.pyj +++ b/src/pyj/read_book/iframe.pyj @@ -9,6 +9,7 @@ from range_utils import ( 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 ) +from read_book.touch import GESTURE from read_book.cfi import cfi_for_selection, range_from_cfi from read_book.extract import get_elements from read_book.find import ( @@ -315,13 +316,13 @@ class IframeBoss: self.auto_scroll_action('toggle') 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') - elif gesture.type is 'two-finger-tap': - self.send_message('show_chrome') - elif gesture.type is 'pinch': - self.send_message('bump_font_size', increase=gesture.direction is 'out') - elif gesture.type is 'long-tap': + elif gesture.type is GESTURE.pinch_in: + self.send_message('bump_font_size', increase=False) + elif gesture.type is GESTURE.pinch_out: + self.send_message('bump_font_size', increase=True) + elif gesture.type is GESTURE.long_tap: self.handle_long_tap(gesture) else: self._handle_gesture(gesture) diff --git a/src/pyj/read_book/paged_mode.pyj b/src/pyj/read_book/paged_mode.pyj index 23ecb1ee65..42c816ded1 100644 --- a/src/pyj/read_book/paged_mode.pyj +++ b/src/pyj/read_book/paged_mode.pyj @@ -32,6 +32,7 @@ from read_book.cfi import ( scroll_to as cfi_scroll_to ) 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.viewport import scroll_viewport, line_height, rem_size, get_unit_size_in_pixels from utils import ( @@ -808,19 +809,20 @@ def handle_shortcut(sc_name, evt): 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, # don't set flip_if_rtl_page_progression=True here. - elif gesture.type is 'prev-page': - scroll_by_page(True, opts.paged_taps_scroll_by_screen, flip_if_rtl_page_progression=False) - elif gesture.type is 'next-page': - scroll_by_page(False, opts.paged_taps_scroll_by_screen, flip_if_rtl_page_progression=False) + if gesture.type is GESTURE.flick_block_forward: + get_boss().send_message('next_section', forward=True) + elif gesture.type is GESTURE.flick_block_backward: + 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 = { diff --git a/src/pyj/read_book/touch.pyj b/src/pyj/read_book/touch.pyj index be642b166e..14952656dd 100644 --- a/src/pyj/read_book/touch.pyj +++ b/src/pyj/read_book/touch.pyj @@ -2,6 +2,7 @@ # License: GPL v3 Copyright: 2016, Kovid Goyal 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.settings import opts from read_book.viewport import get_unit_size_in_pixels, scroll_viewport @@ -11,6 +12,31 @@ TAP_THRESHOLD = 8 # pixels SWIPE_THRESHOLD = 64 # pixels TAP_LINK_THRESHOLD = 5 # 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 @@ -50,7 +76,7 @@ def interpret_single_gesture(touch, gesture_id): max_y_displacement = max_displacement(touch.viewport_y) 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: - ans.type = 'tap' + ans.type = GESTURE.tap ans.viewport_x = touch.viewport_x[0] ans.viewport_y = touch.viewport_y[0] return ans @@ -60,7 +86,7 @@ def interpret_single_gesture(touch, gesture_id): delta_y = abs(touch.viewport_y[-1] - touch.viewport_y[0]) max_disp = max(delta_y, delta_x) 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.points = pts = touch.viewport_y if ans.axis is 'vertical' else touch.viewport_x 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_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: - ans.type = 'two-finger-tap' + ans.type = GESTURE.two_finger_tap ans.viewport_x1 = touch1.viewport_x[0] ans.viewport_y1 = touch1.viewport_y[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) distance = abs(final_distance - initial_distance) if distance > PINCH_THRESHOLD: - ans.type = 'pinch' + ans.type = GESTURE.pinch ans.direction = 'in' if final_distance < initial_distance else 'out' ans.distance = distance return ans @@ -255,42 +281,51 @@ class BookTouchHandler(TouchHandler): TouchHandler.__init__(self) def handle_gesture(self, gesture): - gesture.from_side_margin = self.for_side_margin - if gesture.type is 'tap': + if gesture.type is GESTURE.tap: 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: self.handled_tap_hold = True - gesture.type = 'long-tap' + gesture.type = GESTURE.long_tap get_boss().handle_gesture(gesture) return if not gesture.active: if self.for_side_margin or not tap_on_link(gesture): inch = inch_in_pixels() if gesture.viewport_y < min(100, scroll_viewport.height() / 4): - gesture.type = 'show-chrome' + gesture.type = GESTURE.control_zone_tap else: limit = inch # default, books that go left to right. if ltr_page_progression() and not opts.reverse_page_turn_zones: if gesture.viewport_x < min(limit, scroll_viewport.width() / 4): - gesture.type = 'prev-page' + gesture.type = GESTURE.back_zone_tap 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. else: # 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. # 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)): - gesture.type = 'prev-page' + gesture.type = GESTURE.back_zone_tap else: - gesture.type = 'next-page' - if gesture.type is 'pinch': + gesture.type = GESTURE.forward_zone_tap + elif gesture.type is GESTURE.pinch: if gesture.active: 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: 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: ui_operations.forward_gesture(gesture) else: