mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
E-book viewer: Allow configuring the actions triggered by touch gestures. Fixes #2023367 [Feature Request: Customizable Touchscreen Behaviors in Content Server's Web Viewer](https://bugs.launchpad.net/calibre/+bug/2023367)
This commit is contained in:
parent
97ad986ace
commit
748ffc7fb3
@ -32,7 +32,7 @@ def get_action_descriptions():
|
||||
},
|
||||
'highlight_or_inspect': {
|
||||
'short': _('Highlight or inspect'),
|
||||
'long': _('Highlight the word under the tap point or')
|
||||
'long': _('Highlight the word under the tap point or view the image under the tap or select the highlight under the tap')
|
||||
},
|
||||
'decrease_font_size': {
|
||||
'short': _('Make text smaller'),
|
||||
@ -67,14 +67,14 @@ def get_action_descriptions():
|
||||
|
||||
only_flow_swipe_mode_actions = {'pan': True, 'animated_scroll': True}
|
||||
only_tap_actions = {'highlight_or_inspect': True}
|
||||
|
||||
default_actions_for_gesture = {
|
||||
'common': {
|
||||
GESTURE.back_zone_tap: 'prev_page',
|
||||
GESTURE.forward_zone_tap: 'next_page',
|
||||
GESTURE.control_zone_tap: 'show_chrome',
|
||||
GESTURE.two_finger_tap: 'show_chrome',
|
||||
GESTURE.long_tap: 'highlight_or_inspect',
|
||||
|
||||
GESTURE.two_finger_tap: 'show_chrome',
|
||||
GESTURE.pinch_in: 'decrease_font_size',
|
||||
GESTURE.pinch_out: 'increase_font_size',
|
||||
},
|
||||
@ -98,15 +98,30 @@ default_actions_for_gesture = {
|
||||
},
|
||||
}
|
||||
|
||||
def action_for_gesture(gesture, in_flow_mode):
|
||||
overrides = opts.gesture_overrides
|
||||
def current_action_for_gesture_type(overrides, gesture_type, in_flow_mode):
|
||||
mode = 'flow_mode' if in_flow_mode else 'paged_mode'
|
||||
mode_overrides = overrides[mode] or {}
|
||||
if mode_overrides[gesture.type]:
|
||||
return mode_overrides[gesture.type]
|
||||
if mode_overrides[gesture_type]:
|
||||
return mode_overrides[gesture_type]
|
||||
common_overrides = overrides.common or {}
|
||||
if common_overrides[gesture.type]:
|
||||
return common_overrides[gesture.type]
|
||||
if default_actions_for_gesture[mode][gesture.type]:
|
||||
return default_actions_for_gesture[mode][gesture.type]
|
||||
return default_actions_for_gesture.common[gesture.type] or 'none'
|
||||
if common_overrides[gesture_type]:
|
||||
return common_overrides[gesture_type]
|
||||
if default_actions_for_gesture[mode][gesture_type]:
|
||||
return default_actions_for_gesture[mode][gesture_type]
|
||||
return default_actions_for_gesture.common[gesture_type] or 'none'
|
||||
|
||||
def action_for_gesture(gesture, in_flow_mode):
|
||||
return current_action_for_gesture_type(opts.gesture_overrides, gesture.type, in_flow_mode)
|
||||
|
||||
def allowed_actions_for_tap():
|
||||
return [ac for ac in Object.keys(get_action_descriptions()) if not only_flow_swipe_mode_actions[ac]]
|
||||
|
||||
def allowed_actions_for_paged_mode_swipe():
|
||||
return [ac for ac in Object.keys(get_action_descriptions()) if not only_flow_swipe_mode_actions[ac] and not only_tap_actions[ac]]
|
||||
allowed_actions_for_two_fingers = allowed_actions_for_paged_mode_swipe
|
||||
|
||||
def allowed_actions_for_flow_mode_flick():
|
||||
return [ac for ac in Object.keys(get_action_descriptions()) if not only_tap_actions[ac]]
|
||||
|
||||
def allowed_actions_for_flow_mode_drag():
|
||||
return ['pan', 'none']
|
||||
|
@ -17,6 +17,7 @@ from read_book.prefs.layout import commit_layout, create_layout_panel
|
||||
from read_book.prefs.misc import commit_misc, create_misc_panel
|
||||
from read_book.prefs.scrolling import commit_scrolling, create_scrolling_panel
|
||||
from read_book.prefs.selection import commit_selection, create_selection_panel
|
||||
from read_book.prefs.touch import commit_touch, create_touch_panel
|
||||
from read_book.prefs.user_stylesheet import (
|
||||
commit_user_stylesheet, create_user_stylesheet_panel
|
||||
)
|
||||
@ -106,6 +107,7 @@ class Prefs:
|
||||
ci(_('Headers and footers'), 'head_foot', _('Customize the headers and footers'))
|
||||
ci(_('Scrolling behavior'), 'scrolling', _('Control how the viewer scrolls'))
|
||||
ci(_('Selection behavior'), 'selection', _('Control how the viewer selects text'))
|
||||
ci(_('Touch behavior'), 'touch', _('Customize what various touchscreen gestures do'))
|
||||
ci(_('Keyboard shortcuts'), 'keyboard', _('Customize the keyboard shortcuts'))
|
||||
if runtime.is_standalone_viewer:
|
||||
ci(_('Fonts'), 'fonts', _('Font choices'))
|
||||
@ -166,6 +168,13 @@ class Prefs:
|
||||
def close_selection(self):
|
||||
commit_selection(self.onchange, self.container)
|
||||
|
||||
def display_touch(self, container):
|
||||
self.create_panel(container, 'touch', create_touch_panel)
|
||||
|
||||
def close_touch(self):
|
||||
commit_touch(self.onchange, self.container)
|
||||
|
||||
|
||||
|
||||
def create_prefs_panel(container, close_func, on_change):
|
||||
return Prefs(container, close_func, on_change)
|
||||
|
170
src/pyj/read_book/prefs/touch.pyj
Normal file
170
src/pyj/read_book/prefs/touch.pyj
Normal file
@ -0,0 +1,170 @@
|
||||
# vim:fileencoding=utf-8
|
||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
from __python__ import bound_methods, hash_literals
|
||||
|
||||
from elementmaker import E
|
||||
|
||||
from book_list.globals import get_session_data
|
||||
from dom import unique_id
|
||||
from gettext import gettext as _
|
||||
from read_book.gestures import (
|
||||
allowed_actions_for_flow_mode_drag, allowed_actions_for_flow_mode_flick,
|
||||
allowed_actions_for_paged_mode_swipe, allowed_actions_for_tap,
|
||||
allowed_actions_for_two_fingers, current_action_for_gesture_type,
|
||||
get_action_descriptions
|
||||
)
|
||||
from read_book.prefs.utils import create_button_box
|
||||
from read_book.touch import GESTURE, GESTURE_NAMES
|
||||
|
||||
CONTAINER = unique_id('touch-settings')
|
||||
|
||||
def restore_defaults():
|
||||
apply_settings_to_ui({})
|
||||
|
||||
def get_container():
|
||||
return document.getElementById(CONTAINER)
|
||||
|
||||
|
||||
def apply_settings_to_ui(overrides):
|
||||
overrides = overrides or {}
|
||||
for group in get_container().querySelectorAll('[data-group]'):
|
||||
group_name = group.dataset.group
|
||||
in_flow_mode = group_name.indexOf('flow') >= 0
|
||||
for select in group.querySelectorAll('select'):
|
||||
allowed_actions = v'[]'
|
||||
for option in select.querySelectorAll('option'):
|
||||
allowed_actions.push(option.value)
|
||||
gesture_type = select.name
|
||||
current_action = current_action_for_gesture_type(overrides, gesture_type, in_flow_mode)
|
||||
if allowed_actions.indexOf(current_action) < 0:
|
||||
current_action = current_action_for_gesture_type({}, gesture_type, in_flow_mode)
|
||||
if not current_action or allowed_actions.indexOf(current_action) < 0:
|
||||
current_action = 'none'
|
||||
select.value = current_action
|
||||
|
||||
def get_overrides_from_ui():
|
||||
ans = {}
|
||||
for group in get_container().querySelectorAll('[data-group]'):
|
||||
group_name = group.dataset.group
|
||||
in_flow_mode = group_name.indexOf('flow') >= 0
|
||||
if group_name is 'paged_swipe':
|
||||
attr = 'paged_mode'
|
||||
elif group_name is 'flow_swipe':
|
||||
attr = 'flow_mode'
|
||||
else:
|
||||
attr = 'common'
|
||||
if not ans[attr]:
|
||||
ans[attr] = {}
|
||||
for select in group.querySelectorAll('select'):
|
||||
val = select.value
|
||||
defval = current_action_for_gesture_type({}, select.name, in_flow_mode)
|
||||
if val is not defval:
|
||||
ans[attr][select.name] = val
|
||||
for which in Object.keys(ans):
|
||||
if Object.keys(ans[which]).length is 0:
|
||||
v'delete ans[which]'
|
||||
return ans
|
||||
|
||||
|
||||
def create_touch_panel(container, apply_func, cancel_func):
|
||||
container.appendChild(E.div(id=CONTAINER, style='margin: 1rem'))
|
||||
container = container.lastChild
|
||||
sd = get_session_data()
|
||||
overrides = sd.get('gesture_overrides')
|
||||
action_descriptions = get_action_descriptions()
|
||||
in_flow_mode = False
|
||||
|
||||
def on_select_change(ev):
|
||||
select = ev.target
|
||||
ad = action_descriptions[select.value]
|
||||
span = select.parentNode.querySelector('span')
|
||||
span.textContent = ad.long
|
||||
|
||||
def make_setting(gesture_type, allowed_actions):
|
||||
ans = E.div(style='margin-top: 1ex')
|
||||
title = GESTURE_NAMES[gesture_type]
|
||||
sid = unique_id(gesture_type)
|
||||
ans.appendChild(E.h4(E.label(title, 'for'=sid)))
|
||||
select = E.select(name=gesture_type, id=sid)
|
||||
for action in allowed_actions:
|
||||
ad = action_descriptions[action]
|
||||
select.appendChild(E.option(ad.short, value=action))
|
||||
select.addEventListener('change', on_select_change)
|
||||
ans.appendChild(E.div(select, '\xa0', E.span(style='font-size: smaller; font-style: italic')))
|
||||
on_select_change({'target': select})
|
||||
return ans
|
||||
|
||||
container.appendChild(E.h2(_('Tapping')))
|
||||
container.appendChild(E.div(_(
|
||||
'There are three tap zones, depending on where on the screen you tap. When the tap is'
|
||||
' on a link, the link is followed, otherwise a configurable action based on the zone is performed.')))
|
||||
c = E.div('data-group'='tap')
|
||||
container.appendChild(c)
|
||||
aat = allowed_actions_for_tap()
|
||||
c.appendChild(make_setting(GESTURE.control_zone_tap, aat))
|
||||
c.appendChild(make_setting(GESTURE.forward_zone_tap, aat))
|
||||
c.appendChild(make_setting(GESTURE.back_zone_tap, aat))
|
||||
c.appendChild(make_setting(GESTURE.long_tap, aat))
|
||||
container.appendChild(E.hr())
|
||||
|
||||
container.appendChild(E.h2(_('Two finger gestures')))
|
||||
c = E.div('data-group'='two_finger')
|
||||
container.appendChild(c)
|
||||
aat = allowed_actions_for_two_fingers()
|
||||
c.appendChild(make_setting(GESTURE.two_finger_tap, aat))
|
||||
c.appendChild(make_setting(GESTURE.pinch_in, aat))
|
||||
c.appendChild(make_setting(GESTURE.pinch_out, aat))
|
||||
container.appendChild(E.hr())
|
||||
|
||||
|
||||
container.appendChild(E.h2(_('Swiping')))
|
||||
container.appendChild(E.div(_(
|
||||
'Swiping works differently in paged and flow mode, with different actions. For an English like language, swiping in'
|
||||
' the writing direction means swiping horizontally. For languages written vertically, it means swiping vertically.'
|
||||
' For languages written left-to-right "going forward" means swiping right-to-left, like turning a page with your finger.'
|
||||
)))
|
||||
container.appendChild(E.h3(_('Swiping in paged mode'), style='padding-top: 1ex'))
|
||||
c = E.div('data-group'='paged_swipe')
|
||||
container.appendChild(c)
|
||||
aap = allowed_actions_for_paged_mode_swipe()
|
||||
c.appendChild(make_setting(GESTURE.flick_block_forward, aap))
|
||||
c.appendChild(make_setting(GESTURE.flick_block_backward, aap))
|
||||
c.appendChild(make_setting(GESTURE.flick_inline_forward, aap))
|
||||
c.appendChild(make_setting(GESTURE.flick_inline_backward, aap))
|
||||
c.appendChild(make_setting(GESTURE.swipe_inline_forward_hold, aap))
|
||||
c.appendChild(make_setting(GESTURE.swipe_inline_backward_hold, aap))
|
||||
c.appendChild(make_setting(GESTURE.swipe_block_forward_hold, aap))
|
||||
c.appendChild(make_setting(GESTURE.swipe_block_backward_hold, aap))
|
||||
container.appendChild(E.hr())
|
||||
container.appendChild(E.h3(_('Swiping in flow mode'), style='padding-top: 1ex'))
|
||||
c = E.div('data-group'='flow_swipe')
|
||||
container.appendChild(c)
|
||||
in_flow_mode = True
|
||||
in_flow_mode
|
||||
aaf = allowed_actions_for_flow_mode_flick()
|
||||
c.appendChild(make_setting(GESTURE.flick_block_forward, aaf))
|
||||
c.appendChild(make_setting(GESTURE.flick_block_backward, aaf))
|
||||
c.appendChild(make_setting(GESTURE.flick_inline_forward, aaf))
|
||||
c.appendChild(make_setting(GESTURE.flick_inline_backward, aaf))
|
||||
aaf = allowed_actions_for_flow_mode_drag()
|
||||
c.appendChild(make_setting(GESTURE.swipe_inline_backward_in_progress, aaf))
|
||||
c.appendChild(make_setting(GESTURE.swipe_inline_forward_in_progress, aaf))
|
||||
c.appendChild(make_setting(GESTURE.swipe_block_backward_in_progress, aaf))
|
||||
c.appendChild(make_setting(GESTURE.swipe_block_forward_in_progress, aaf))
|
||||
|
||||
container.appendChild(E.hr())
|
||||
container.appendChild(create_button_box(restore_defaults, apply_func, cancel_func))
|
||||
apply_settings_to_ui(overrides)
|
||||
|
||||
|
||||
develop = create_touch_panel
|
||||
|
||||
|
||||
def commit_touch(onchange):
|
||||
sd = get_session_data()
|
||||
current_overrides = sd.get('gesture_overrides')
|
||||
overrides = get_overrides_from_ui()
|
||||
changed = overrides != current_overrides
|
||||
if changed:
|
||||
sd.set('gesture_overrides', overrides)
|
||||
onchange()
|
@ -21,18 +21,20 @@ GESTURE_NAMES = {
|
||||
'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'),
|
||||
'flick_inline_backward': _('Flick in writing direction, to go back'),
|
||||
'flick_inline_forward': _('Flick in writing direction, to go forward'),
|
||||
'flick_block_backward': _('Flick perpendicular to writing direction, to go forward'),
|
||||
'flick_block_forward': _('Flick perpendicular to writing direction, to go back'),
|
||||
|
||||
'swipe_inline_backward_in_progress': _('Drag finger in writing direction, to go back'),
|
||||
'swipe_inline_forward_in_progress': _('Drag finger in writing direction, to go forward'),
|
||||
'swipe_block_backward_in_progress': _('Drag finger perpendicular to writing direction, to go back'),
|
||||
'swipe_block_forward_in_progress': _('Drag finger perpendicular to writing direction, to go forward'),
|
||||
|
||||
'swipe_inline_backward_hold': _('Drag and hold finger in writing direction, to go back'),
|
||||
'swipe_inline_forward_hold': _('Drag and hold finger in writing direction, to go forward'),
|
||||
'swipe_block_backward_hold': _('Drag and hold finger perpendicular to writing direction, to go back'),
|
||||
'swipe_block_forward_hold': _('Drag and hold finger perpendicular to writing direction, to go forward'),
|
||||
}
|
||||
GESTURE = {k:k for k in Object.keys(GESTURE_NAMES)}
|
||||
GESTURE.tap = 'tap'
|
||||
|
Loading…
x
Reference in New Issue
Block a user