E-book viewer: Improve scrolling behavior when extending the selection using keyboard shortcuts

This commit is contained in:
Kovid Goyal 2021-04-06 09:32:25 +05:30
parent 0219746e92
commit f6e55966f5
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 60 additions and 16 deletions

View File

@ -626,6 +626,27 @@ def ensure_selection_visible():
unwrap(wrapper) unwrap(wrapper)
def ensure_selection_boundary_visible(use_end):
sel = window.getSelection()
rr = sel.getRangeAt(0)
if rr:
r = rr.getBoundingClientRect()
if r:
node = sel.focusNode if use_end else sel.anchorNode
x = scroll_viewport.rect_inline_end(r) if use_end else scroll_viewport.rect_inline_start(r)
if x < 0 or x >= window.innerWidth:
x = scroll_viewport.viewport_to_document_inline(x, doc=node.ownerDocument)
if use_end:
x -= line_height()
scroll_viewport.scroll_to_in_inline_direction(x, True)
y = scroll_viewport.rect_block_end(r) if use_end else scroll_viewport.rect_block_start(r)
if y < 0 or y >= window.innerHeight:
y = scroll_viewport.viewport_to_document_block(y, doc=node.ownerDocument)
if use_end:
y -= line_height()
scroll_viewport.scroll_to_in_block_direction(y, True)
def jump_to_cfi(cfi): def jump_to_cfi(cfi):
# Jump to the position indicated by the specified conformal fragment # Jump to the position indicated by the specified conformal fragment
# indicator. # indicator.

View File

@ -7,11 +7,9 @@ from fs_images import fix_fullscreen_svg_images
from gettext import gettext as _ from gettext import gettext as _
from iframe_comm import IframeClient from iframe_comm import IframeClient
from range_utils import ( from range_utils import (
highlight_associated_with_selection, highlight_associated_with_selection, last_span_for_crw, reset_highlight_counter,
last_span_for_crw, reset_highlight_counter, select_crw, unwrap_all_crw, select_crw, unwrap_all_crw, unwrap_crw, wrap_text_in_range
unwrap_crw, wrap_text_in_range
) )
from read_book.hints import hint_visible_links, unhint_links, apply_prefix_to_hints
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 (
@ -19,10 +17,12 @@ from read_book.find import (
) )
from read_book.flow_mode import ( from read_book.flow_mode import (
anchor_funcs as flow_anchor_funcs, auto_scroll_action as flow_auto_scroll_action, anchor_funcs as flow_anchor_funcs, auto_scroll_action as flow_auto_scroll_action,
cancel_drag_scroll as cancel_drag_scroll_flow, ensure_selection_visible, cancel_drag_scroll as cancel_drag_scroll_flow,
flow_onwheel, flow_to_scroll_fraction, handle_gesture as flow_handle_gesture, ensure_selection_boundary_visible as ensure_selection_boundary_visible_flow,
handle_shortcut as flow_handle_shortcut, jump_to_cfi as flow_jump_to_cfi, ensure_selection_visible, flow_onwheel, flow_to_scroll_fraction,
layout as flow_layout, scroll_by_page as flow_scroll_by_page, handle_gesture as flow_handle_gesture, handle_shortcut as flow_handle_shortcut,
jump_to_cfi as flow_jump_to_cfi, layout as flow_layout,
scroll_by_page as flow_scroll_by_page,
scroll_to_extend_annotation as flow_annotation_scroll, scroll_to_extend_annotation as flow_annotation_scroll,
start_drag_scroll as start_drag_scroll_flow start_drag_scroll as start_drag_scroll_flow
) )
@ -33,11 +33,13 @@ from read_book.globals import (
set_toc_anchor_map set_toc_anchor_map
) )
from read_book.highlights import highlight_style_as_css from read_book.highlights import highlight_style_as_css
from read_book.hints import apply_prefix_to_hints, hint_visible_links, unhint_links
from read_book.mathjax import apply_mathjax from read_book.mathjax import apply_mathjax
from read_book.paged_mode import ( from read_book.paged_mode import (
anchor_funcs as paged_anchor_funcs, anchor_funcs as paged_anchor_funcs,
auto_scroll_action as paged_auto_scroll_action, calc_columns_per_screen, auto_scroll_action as paged_auto_scroll_action, calc_columns_per_screen,
cancel_drag_scroll as cancel_drag_scroll_paged, current_cfi, cancel_drag_scroll as cancel_drag_scroll_paged, current_cfi,
ensure_selection_boundary_visible as ensure_selection_boundary_visible_paged,
get_columns_per_screen_data, handle_gesture as paged_handle_gesture, get_columns_per_screen_data, handle_gesture as paged_handle_gesture,
handle_shortcut as paged_handle_shortcut, jump_to_cfi as paged_jump_to_cfi, handle_shortcut as paged_handle_shortcut, jump_to_cfi as paged_jump_to_cfi,
layout as paged_layout, onwheel as paged_onwheel, page_counts, layout as paged_layout, onwheel as paged_onwheel, page_counts,
@ -65,7 +67,7 @@ from read_book.touch import (
) )
from read_book.viewport import scroll_viewport from read_book.viewport import scroll_viewport
from select import ( from select import (
move_end_of_selection, selection_extents, word_at_point, first_visible_word first_visible_word, move_end_of_selection, selection_extents, word_at_point
) )
from utils import debounce, is_ios from utils import debounce, is_ios
@ -185,18 +187,20 @@ class IframeBoss:
def modify_selection(self, data): def modify_selection(self, data):
sel = window.getSelection() sel = window.getSelection()
use_end = False
if data.granularity is 'all': if data.granularity is 'all':
r = document.createRange() r = document.createRange()
r.selectNode(document.body) r.selectNode(document.body)
sel.removeAllRanges() sel.removeAllRanges()
sel.addRange(r) sel.addRange(r)
else: else:
use_end = data.direction is 'forward' or data.direction is 'right'
try: try:
sel.modify('extend', data.direction, data.granularity) sel.modify('extend', data.direction, data.granularity)
except: except:
if data.granularity is 'paragraph': if data.granularity is 'paragraph':
sel.modify('extend', data.direction, 'line') sel.modify('extend', data.direction, 'line')
self.ensure_selection_visible() self.ensure_selection_boundary_visible(use_end)
def initialize(self, data): def initialize(self, data):
scroll_viewport.update_window_size(data.width, data.height) scroll_viewport.update_window_size(data.width, data.height)
@ -271,6 +275,7 @@ class IframeBoss:
self.auto_scroll_action = flow_auto_scroll_action self.auto_scroll_action = flow_auto_scroll_action
self.scroll_to_extend_annotation = flow_annotation_scroll self.scroll_to_extend_annotation = flow_annotation_scroll
self.ensure_selection_visible = ensure_selection_visible self.ensure_selection_visible = ensure_selection_visible
self.ensure_selection_boundary_visible = ensure_selection_boundary_visible_flow
self.start_drag_scroll = start_drag_scroll_flow self.start_drag_scroll = start_drag_scroll_flow
paged_auto_scroll_action('stop') paged_auto_scroll_action('stop')
else: else:
@ -284,6 +289,7 @@ class IframeBoss:
self.auto_scroll_action = paged_auto_scroll_action self.auto_scroll_action = paged_auto_scroll_action
self.scroll_to_extend_annotation = paged_annotation_scroll self.scroll_to_extend_annotation = paged_annotation_scroll
self.ensure_selection_visible = snap_to_selection self.ensure_selection_visible = snap_to_selection
self.ensure_selection_boundary_visible = ensure_selection_boundary_visible_paged
self.start_drag_scroll = start_drag_scroll_paged self.start_drag_scroll = start_drag_scroll_paged
flow_auto_scroll_action('stop') flow_auto_scroll_action('stop')
update_settings(data.settings) update_settings(data.settings)

View File

@ -566,6 +566,23 @@ def snap_to_selection():
# selection # selection
scroll_to_pos(pos+5) scroll_to_pos(pos+5)
def ensure_selection_boundary_visible(use_end):
sel = window.getSelection()
rr = sel.getRangeAt(0)
if rr:
r = rr.getBoundingClientRect()
if r:
cnum = column_at_current_scroll_offset()
scroll_to_column(cnum)
node = sel.focusNode if use_end else sel.anchorNode
# Columns are in the inline direction, so get the beginning of the element in the inline
x = scroll_viewport.rect_inline_end(r) if use_end else scroll_viewport.rect_inline_start(r)
if x < 0 or x >= scroll_viewport.inline_size():
pos = scroll_viewport.viewport_to_document_inline(x, doc=node.ownerDocument)
scroll_to_pos(pos+5)
def jump_to_cfi(cfi): def jump_to_cfi(cfi):
# Jump to the position indicated by the specified conformal fragment # Jump to the position indicated by the specified conformal fragment
# indicator. # indicator.

View File

@ -75,20 +75,20 @@ class ScrollViewport:
else: else:
window.scrollTo(x, y) window.scrollTo(x, y)
def scroll_to_in_inline_direction(self, pos): def scroll_to_in_inline_direction(self, pos, preserve_other):
# Lines flow vertically, so inline is vertical. # Lines flow vertically, so inline is vertical.
if self.vertical_writing_mode: if self.vertical_writing_mode:
self.scroll_to(0, pos) self.scroll_to(self.x() if preserve_other else 0, pos)
else: else:
self.scroll_to(pos, 0) self.scroll_to(pos, self.y() if preserve_other else 0)
def scroll_to_in_block_direction(self, pos): def scroll_to_in_block_direction(self, pos, preserve_other):
# In horizontal modes, the block direction is vertical. # In horizontal modes, the block direction is vertical.
if self.horizontal_writing_mode: if self.horizontal_writing_mode:
self.scroll_to(0, pos) self.scroll_to(self.x() if preserve_other else 0, pos)
# In vertical modes, the block direction is horizontal. # In vertical modes, the block direction is horizontal.
else: else:
self.scroll_to(pos, 0) self.scroll_to(pos, self.y() if preserve_other else 0)
def flow_scroll_into_view(self, elem): def flow_scroll_into_view(self, elem):
elem.scrollIntoView() elem.scrollIntoView()