mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Refactor keyboard handling in the viewer
No longer uses deprecated APIs
This commit is contained in:
parent
2548babf59
commit
82fcdf6272
@ -5,10 +5,9 @@ from __python__ import bound_methods, hash_literals
|
||||
from select import word_at_point
|
||||
|
||||
from dom import set_css
|
||||
from keycodes import get_key
|
||||
from read_book.globals import get_boss
|
||||
from read_book.viewport import scroll_viewport
|
||||
from utils import document_height, document_width, viewport_to_document
|
||||
from utils import document_height, viewport_to_document
|
||||
|
||||
|
||||
def flow_to_scroll_fraction(frac):
|
||||
@ -127,37 +126,38 @@ def scroll_by_page(backward):
|
||||
window.scrollBy(0, -h if backward else h)
|
||||
|
||||
|
||||
def flow_onkeydown(evt):
|
||||
handled = False
|
||||
key = get_key(evt)
|
||||
if key is 'up' or key is 'down':
|
||||
handled = True
|
||||
if evt.ctrlKey:
|
||||
goto_boundary(-1 if key is 'up' else 1)
|
||||
else:
|
||||
smooth_y_scroll(key is 'up')
|
||||
elif (key is 'left' or key is 'right') and not evt.altKey:
|
||||
handled = True
|
||||
if evt.ctrlKey:
|
||||
scroll_viewport.scroll_to(0 if key is 'left' else document_width(), window.pageYOffset)
|
||||
else:
|
||||
window.scrollBy(-15 if key is 'left' else 15, 0)
|
||||
elif key is 'home' or key is 'end':
|
||||
handled = True
|
||||
get_boss().report_human_scroll()
|
||||
clear_small_scrolls()
|
||||
if evt.ctrlKey:
|
||||
get_boss().send_message('goto_doc_boundary', start=key is 'home')
|
||||
else:
|
||||
if key is 'home':
|
||||
scroll_viewport.scroll_to(window.pageXOffset, 0)
|
||||
else:
|
||||
scroll_viewport.scroll_to(window.pageXOffset, document_height())
|
||||
elif key is 'pageup' or key is 'pagedown' or key is 'space':
|
||||
handled = True
|
||||
scroll_by_page(key is 'pageup')
|
||||
if handled:
|
||||
evt.preventDefault()
|
||||
def handle_shortcut(sc_name, evt):
|
||||
if sc_name is 'down':
|
||||
smooth_y_scroll(True)
|
||||
return True
|
||||
if sc_name is 'up':
|
||||
smooth_y_scroll(True)
|
||||
return True
|
||||
if sc_name is 'start_of_file':
|
||||
goto_boundary(-1)
|
||||
return True
|
||||
if sc_name is 'end_of_file':
|
||||
goto_boundary(1)
|
||||
return True
|
||||
if sc_name is 'left':
|
||||
window.scrollBy(-15)
|
||||
return True
|
||||
if sc_name is 'right':
|
||||
window.scrollBy(-15)
|
||||
return True
|
||||
if sc_name is 'start_of_book':
|
||||
get_boss().send_message('goto_doc_boundary', start=True)
|
||||
return True
|
||||
if sc_name is 'end_of_book':
|
||||
get_boss().send_message('goto_doc_boundary', start=False)
|
||||
return True
|
||||
if sc_name is 'pageup':
|
||||
scroll_by_page(True)
|
||||
return True
|
||||
if sc_name is 'pagedown':
|
||||
scroll_by_page(False)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def layout(is_single_page):
|
||||
|
@ -8,8 +8,8 @@ from gettext import gettext as _
|
||||
from iframe_comm import IframeClient
|
||||
from read_book.cfi import at_current, scroll_to as scroll_to_cfi
|
||||
from read_book.flow_mode import (
|
||||
anchor_funcs as flow_anchor_funcs, flow_onkeydown, flow_onwheel,
|
||||
flow_to_scroll_fraction, handle_gesture as flow_handle_gesture,
|
||||
anchor_funcs as flow_anchor_funcs, flow_onwheel, flow_to_scroll_fraction,
|
||||
handle_gesture as flow_handle_gesture, handle_shortcut as flow_handle_shortcut,
|
||||
layout as flow_layout, scroll_by_page as flow_scroll_by_page
|
||||
)
|
||||
from read_book.footnotes import is_footnote_link
|
||||
@ -20,17 +20,18 @@ from read_book.globals import (
|
||||
from read_book.mathjax import apply_mathjax
|
||||
from read_book.paged_mode import (
|
||||
anchor_funcs as paged_anchor_funcs, calc_columns_per_screen,
|
||||
handle_gesture as paged_handle_gesture, jump_to_cfi as paged_jump_to_cfi,
|
||||
layout as paged_layout, onkeydown as paged_onkeydown, onwheel as paged_onwheel,
|
||||
progress_frac, reset_paged_mode_globals, scroll_by_page as paged_scroll_by_page,
|
||||
scroll_to_elem, scroll_to_fraction as paged_scroll_to_fraction,
|
||||
snap_to_selection
|
||||
handle_gesture as paged_handle_gesture, handle_shortcut as paged_handle_shortcut,
|
||||
jump_to_cfi as paged_jump_to_cfi, layout as paged_layout,
|
||||
onwheel as paged_onwheel, progress_frac, reset_paged_mode_globals,
|
||||
scroll_by_page as paged_scroll_by_page, scroll_to_elem,
|
||||
scroll_to_fraction as paged_scroll_to_fraction, snap_to_selection
|
||||
)
|
||||
from read_book.resources import finalize_resources, unserialize_html
|
||||
from read_book.settings import (
|
||||
apply_colors, apply_font_size, apply_settings, apply_stylesheet, opts,
|
||||
update_settings
|
||||
)
|
||||
from read_book.shortcuts import create_shortcut_map, shortcut_for_key_event
|
||||
from read_book.toc import update_visible_toc_anchors
|
||||
from read_book.touch import create_handlers as create_touch_handlers
|
||||
from read_book.viewport import scroll_viewport
|
||||
@ -138,7 +139,7 @@ class IframeBoss:
|
||||
if current_layout_mode() is 'flow':
|
||||
self.do_layout = flow_layout
|
||||
self.handle_wheel = flow_onwheel
|
||||
self.handle_keydown = flow_onkeydown
|
||||
self.handle_navigation_shortcut = flow_handle_shortcut
|
||||
self._handle_gesture = flow_handle_gesture
|
||||
self.to_scroll_fraction = flow_to_scroll_fraction
|
||||
self.jump_to_cfi = scroll_to_cfi
|
||||
@ -146,12 +147,13 @@ class IframeBoss:
|
||||
else:
|
||||
self.do_layout = paged_layout
|
||||
self.handle_wheel = paged_onwheel
|
||||
self.handle_keydown = paged_onkeydown
|
||||
self.handle_navigation_shortcut = paged_handle_shortcut
|
||||
self.to_scroll_fraction = paged_scroll_to_fraction
|
||||
self.jump_to_cfi = paged_jump_to_cfi
|
||||
self._handle_gesture = paged_handle_gesture
|
||||
self.anchor_funcs = paged_anchor_funcs
|
||||
update_settings(data.settings)
|
||||
self.keyboard_shortcut_map = create_shortcut_map()
|
||||
set_current_spine_item({'name':data.name, 'is_first':index is 0, 'is_last':index is spine.length - 1, 'initial_position':data.initial_position})
|
||||
self.last_cfi = None
|
||||
for name in self.blob_url_map:
|
||||
@ -343,8 +345,14 @@ class IframeBoss:
|
||||
self.handle_wheel(evt)
|
||||
|
||||
def onkeydown(self, evt):
|
||||
if current_layout_mode() is not 'flow' and evt.key is 'Tab':
|
||||
# Prevent the TAB key from shifting focus as it causes partial scrolling
|
||||
evt.preventDefault()
|
||||
if self.content_ready:
|
||||
self.handle_keydown(evt)
|
||||
sc_name = shortcut_for_key_event(evt, self.keyboard_shortcut_map)
|
||||
if sc_name:
|
||||
if self.handle_navigation_shortcut(sc_name, evt):
|
||||
evt.preventDefault()
|
||||
|
||||
def oncontextmenu(self, evt):
|
||||
if self.content_ready:
|
||||
|
@ -7,7 +7,6 @@ from elementmaker import E
|
||||
from select import word_at_point
|
||||
|
||||
from dom import set_css
|
||||
from keycodes import get_key
|
||||
from read_book.cfi import (
|
||||
at_current as cfi_at_current, at_point as cfi_at_point,
|
||||
scroll_to as cfi_scroll_to
|
||||
@ -211,15 +210,6 @@ def layout(is_single_page):
|
||||
if is_single_page:
|
||||
is_full_screen_layout = True
|
||||
|
||||
# Prevent the TAB key from shifting focus as it causes partial scrolling
|
||||
document.documentElement.addEventListener(
|
||||
'keydown',
|
||||
def (evt):
|
||||
if get_key(evt) is 'tab':
|
||||
evt.preventDefault()
|
||||
, {'passive': False}
|
||||
)
|
||||
|
||||
# Some browser engine, WebKit at least, adjust column widths to please
|
||||
# themselves, unless the container width is an exact multiple, so we check
|
||||
# for that and manually set the container widths.
|
||||
@ -485,39 +475,44 @@ def scroll_by_page(backward, by_screen):
|
||||
get_boss().report_human_scroll()
|
||||
scroll_to_xpos(pos)
|
||||
|
||||
def onkeydown(evt):
|
||||
handled = False
|
||||
key = get_key(evt)
|
||||
if key is 'up' or key is 'down':
|
||||
handled = True
|
||||
if evt.ctrlKey:
|
||||
get_boss().report_human_scroll()
|
||||
scroll_to_offset(0 if key is 'left' else document_width())
|
||||
else:
|
||||
scroll_by_page(key is 'up', True)
|
||||
elif (key is 'left' or key is 'right') and not evt.altKey:
|
||||
handled = True
|
||||
if evt.ctrlKey:
|
||||
get_boss().report_human_scroll()
|
||||
scroll_to_offset(0 if key is 'left' else document_width())
|
||||
else:
|
||||
scroll_by_page(key is 'left', False)
|
||||
elif key is 'home' or key is 'end':
|
||||
handled = True
|
||||
if evt.ctrlKey:
|
||||
get_boss().report_human_scroll()
|
||||
get_boss().send_message('goto_doc_boundary', start=key is 'home')
|
||||
else:
|
||||
if key is 'home':
|
||||
get_boss().report_human_scroll()
|
||||
scroll_to_offset(0)
|
||||
else:
|
||||
scroll_to_offset(document_width())
|
||||
elif key is 'pageup' or key is 'pagedown' or key is 'space':
|
||||
handled = True
|
||||
scroll_by_page(key is 'pageup', True)
|
||||
if handled:
|
||||
evt.preventDefault()
|
||||
|
||||
def handle_shortcut(sc_name, evt):
|
||||
if sc_name is 'up':
|
||||
scroll_by_page(True, True)
|
||||
return True
|
||||
if sc_name is 'down':
|
||||
scroll_by_page(False, True)
|
||||
return True
|
||||
if sc_name is 'start_of_file':
|
||||
get_boss().report_human_scroll()
|
||||
scroll_to_offset(0)
|
||||
return True
|
||||
if sc_name is 'end_of_file':
|
||||
get_boss().report_human_scroll()
|
||||
scroll_to_offset(document_width())
|
||||
return True
|
||||
if sc_name is 'left':
|
||||
scroll_by_page(True, False)
|
||||
return True
|
||||
if sc_name is 'right':
|
||||
scroll_by_page(False, False)
|
||||
return True
|
||||
if sc_name is 'start_of_book':
|
||||
get_boss().report_human_scroll()
|
||||
get_boss().send_message('goto_doc_boundary', start=True)
|
||||
return True
|
||||
if sc_name is 'end_of_book':
|
||||
get_boss().report_human_scroll()
|
||||
get_boss().send_message('goto_doc_boundary', start=False)
|
||||
return True
|
||||
if sc_name is 'pageup':
|
||||
scroll_by_page(True, True)
|
||||
return True
|
||||
if sc_name is 'pagedown':
|
||||
scroll_by_page(False, True)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def handle_gesture(gesture):
|
||||
if gesture.type is 'swipe':
|
||||
|
139
src/pyj/read_book/shortcuts.pyj
Normal file
139
src/pyj/read_book/shortcuts.pyj
Normal file
@ -0,0 +1,139 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
from __python__ import bound_methods, hash_literals
|
||||
|
||||
from gettext import gettext as _
|
||||
|
||||
|
||||
def parse_key_repr(sc):
|
||||
parts = sc.split('+')
|
||||
if sc.endsWith('++'):
|
||||
parts = parts[:-2]
|
||||
parts.push('+')
|
||||
key = parts[-1]
|
||||
ans = {'key': key, 'altKey': False, 'ctrlKey': False, 'metaKey': False, 'shiftKey': False}
|
||||
for modifier in parts[:-1]:
|
||||
q = modifier.toLowerCase()
|
||||
if q is 'ctrl':
|
||||
ans.ctrlKey = True
|
||||
elif q is 'alt':
|
||||
ans.altKey = True
|
||||
elif q is 'meta':
|
||||
ans.metaKey = True
|
||||
elif q is 'shift':
|
||||
ans.shiftKey = True
|
||||
return ans
|
||||
|
||||
|
||||
def desc(sc, group, short, long):
|
||||
if jstype(sc) is 'string':
|
||||
sc = v'[sc]'
|
||||
pkey = v'[]'
|
||||
for x in sc:
|
||||
pkey.push(parse_key_repr(x))
|
||||
return {'short': short, 'long': long, 'shortcuts': pkey}
|
||||
|
||||
|
||||
def serialize_keyevent(evt):
|
||||
return {
|
||||
'key': evt.key, 'altKey': evt.altKey, 'ctrlKey': evt.ctrlKey,
|
||||
'metaKey': evt.metaKey, 'shiftKey': evt.shiftKey
|
||||
}
|
||||
|
||||
|
||||
def unserialize_keyevent(sc):
|
||||
return sc
|
||||
|
||||
|
||||
def keyevent_to_index(evt):
|
||||
parts = v'[]'
|
||||
for mod in v"['altKey', 'ctrlKey', 'metaKey', 'shiftKey']":
|
||||
parts.push('y' if evt[mod] else 'n')
|
||||
return parts.join('') + evt.key
|
||||
|
||||
|
||||
SHORTCUTS = {
|
||||
'start_of_file': desc(
|
||||
v"['Ctrl+ArrowUp', 'Ctrl+ArrowLeft', 'Home']",
|
||||
'scroll',
|
||||
_('Scroll to the beginning of the current file'),
|
||||
_('When the e-book is made of of multiple individual files, scroll to the start of the current file.'),
|
||||
),
|
||||
|
||||
'start_of_book': desc(
|
||||
'Ctrl+Home',
|
||||
'scroll',
|
||||
_('Scroll to the beginning of the book'),
|
||||
),
|
||||
|
||||
'end_of_book': desc(
|
||||
'Ctrl+End',
|
||||
'scroll',
|
||||
_('Scroll to the end of the book'),
|
||||
),
|
||||
|
||||
'end_of_file': desc(
|
||||
v"['Ctrl+ArrowDown', 'Ctrl+ArrowRight', 'End']",
|
||||
'scroll',
|
||||
_('Scroll to the end of the current file'),
|
||||
_('When the e-book is made of of multiple individual files, scroll to the end of the current file.'),
|
||||
),
|
||||
|
||||
'up': desc(
|
||||
'ArrowUp',
|
||||
'scroll',
|
||||
_('Scroll backwards smoothly (by screen-fulls in paged mode)'),
|
||||
_('Scroll backwards, smoothly in flow mode and by screen fulls in paged mode'),
|
||||
),
|
||||
|
||||
'down': desc(
|
||||
'ArrowDown',
|
||||
'scroll',
|
||||
_('Scroll forwards smoothly (by screen-fulls in paged mode)'),
|
||||
_('Scroll forwards, smoothly in flow mode and by screen fulls in paged mode'),
|
||||
),
|
||||
|
||||
'left': desc(
|
||||
'ArrowLeft',
|
||||
'scroll',
|
||||
_('Scroll left'),
|
||||
_('Scroll leftwards by a little in flow mode and by a page in paged mode'),
|
||||
),
|
||||
|
||||
'right': desc(
|
||||
'ArrowRight',
|
||||
'scroll',
|
||||
_('Scroll right'),
|
||||
_('Scroll rightwards by a little in flow mode and by a page in paged mode'),
|
||||
),
|
||||
|
||||
'pageup': desc(
|
||||
'PageUp',
|
||||
'scroll',
|
||||
_('Scroll backwards by screen-fulls'),
|
||||
),
|
||||
|
||||
'pagedown': desc(
|
||||
v"[' ', 'PageDown']",
|
||||
'scroll',
|
||||
_('Scroll forwards by screen-fulls'),
|
||||
),
|
||||
|
||||
}
|
||||
|
||||
|
||||
def create_shortcut_map(custom_shortcuts):
|
||||
ans = {}
|
||||
for sc_name in Object.keys(SHORTCUTS):
|
||||
entry = SHORTCUTS[sc_name]
|
||||
shortcuts = entry.shortcuts
|
||||
if custom_shortcuts and custom_shortcuts[sc_name]:
|
||||
shortcuts = custom_shortcuts[sc_name]
|
||||
for sc in shortcuts:
|
||||
ans[keyevent_to_index(sc)] = sc_name
|
||||
return ans
|
||||
|
||||
|
||||
def shortcut_for_key_event(evt, shortcut_map):
|
||||
idx = keyevent_to_index(evt)
|
||||
return shortcut_map[idx]
|
Loading…
x
Reference in New Issue
Block a user