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': {
|
'highlight_or_inspect': {
|
||||||
'short': _('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': {
|
'decrease_font_size': {
|
||||||
'short': _('Make text smaller'),
|
'short': _('Make text smaller'),
|
||||||
@ -67,14 +67,14 @@ def get_action_descriptions():
|
|||||||
|
|
||||||
only_flow_swipe_mode_actions = {'pan': True, 'animated_scroll': True}
|
only_flow_swipe_mode_actions = {'pan': True, 'animated_scroll': True}
|
||||||
only_tap_actions = {'highlight_or_inspect': True}
|
only_tap_actions = {'highlight_or_inspect': True}
|
||||||
|
|
||||||
default_actions_for_gesture = {
|
default_actions_for_gesture = {
|
||||||
'common': {
|
'common': {
|
||||||
GESTURE.back_zone_tap: 'prev_page',
|
GESTURE.back_zone_tap: 'prev_page',
|
||||||
GESTURE.forward_zone_tap: 'next_page',
|
GESTURE.forward_zone_tap: 'next_page',
|
||||||
GESTURE.control_zone_tap: 'show_chrome',
|
GESTURE.control_zone_tap: 'show_chrome',
|
||||||
GESTURE.two_finger_tap: 'show_chrome',
|
|
||||||
GESTURE.long_tap: 'highlight_or_inspect',
|
GESTURE.long_tap: 'highlight_or_inspect',
|
||||||
|
|
||||||
|
GESTURE.two_finger_tap: 'show_chrome',
|
||||||
GESTURE.pinch_in: 'decrease_font_size',
|
GESTURE.pinch_in: 'decrease_font_size',
|
||||||
GESTURE.pinch_out: 'increase_font_size',
|
GESTURE.pinch_out: 'increase_font_size',
|
||||||
},
|
},
|
||||||
@ -98,15 +98,30 @@ default_actions_for_gesture = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def action_for_gesture(gesture, in_flow_mode):
|
def current_action_for_gesture_type(overrides, gesture_type, in_flow_mode):
|
||||||
overrides = opts.gesture_overrides
|
|
||||||
mode = 'flow_mode' if in_flow_mode else 'paged_mode'
|
mode = 'flow_mode' if in_flow_mode else 'paged_mode'
|
||||||
mode_overrides = overrides[mode] or {}
|
mode_overrides = overrides[mode] or {}
|
||||||
if mode_overrides[gesture.type]:
|
if mode_overrides[gesture_type]:
|
||||||
return mode_overrides[gesture.type]
|
return mode_overrides[gesture_type]
|
||||||
common_overrides = overrides.common or {}
|
common_overrides = overrides.common or {}
|
||||||
if common_overrides[gesture.type]:
|
if common_overrides[gesture_type]:
|
||||||
return common_overrides[gesture.type]
|
return common_overrides[gesture_type]
|
||||||
if default_actions_for_gesture[mode][gesture.type]:
|
if default_actions_for_gesture[mode][gesture_type]:
|
||||||
return 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'
|
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.misc import commit_misc, create_misc_panel
|
||||||
from read_book.prefs.scrolling import commit_scrolling, create_scrolling_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.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 (
|
from read_book.prefs.user_stylesheet import (
|
||||||
commit_user_stylesheet, create_user_stylesheet_panel
|
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(_('Headers and footers'), 'head_foot', _('Customize the headers and footers'))
|
||||||
ci(_('Scrolling behavior'), 'scrolling', _('Control how the viewer scrolls'))
|
ci(_('Scrolling behavior'), 'scrolling', _('Control how the viewer scrolls'))
|
||||||
ci(_('Selection behavior'), 'selection', _('Control how the viewer selects text'))
|
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'))
|
ci(_('Keyboard shortcuts'), 'keyboard', _('Customize the keyboard shortcuts'))
|
||||||
if runtime.is_standalone_viewer:
|
if runtime.is_standalone_viewer:
|
||||||
ci(_('Fonts'), 'fonts', _('Font choices'))
|
ci(_('Fonts'), 'fonts', _('Font choices'))
|
||||||
@ -166,6 +168,13 @@ class Prefs:
|
|||||||
def close_selection(self):
|
def close_selection(self):
|
||||||
commit_selection(self.onchange, self.container)
|
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):
|
def create_prefs_panel(container, close_func, on_change):
|
||||||
return Prefs(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_in': _('Pinch in'),
|
||||||
'pinch_out': _('Pinch out'),
|
'pinch_out': _('Pinch out'),
|
||||||
|
|
||||||
'flick_inline_backward': _('Flick in writing direction, backwards'),
|
'flick_inline_backward': _('Flick in writing direction, to go back'),
|
||||||
'flick_inline_forward': _('Flick in writing direction, forwards'),
|
'flick_inline_forward': _('Flick in writing direction, to go forward'),
|
||||||
'flick_block_backward': _('Flick perpendicular to writing direction, backwards'),
|
'flick_block_backward': _('Flick perpendicular to writing direction, to go forward'),
|
||||||
'flick_block_forward': _('Flick perpendicular to writing direction, forwards'),
|
'flick_block_forward': _('Flick perpendicular to writing direction, to go back'),
|
||||||
'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_inline_backward_in_progress': _('Drag finger in writing direction, to go back'),
|
||||||
'swipe_block_backward_in_progress': _('Swipe perpendicular to writing direction, backwards, in-progress'),
|
'swipe_inline_forward_in_progress': _('Drag finger in writing direction, to go forward'),
|
||||||
'swipe_block_forward_in_progress': _('Swipe perpendicular to writing direction, forwards, in-progress'),
|
'swipe_block_backward_in_progress': _('Drag finger perpendicular to writing direction, to go back'),
|
||||||
'swipe_inline_backward_hold': _('Swipe in writing direction, backwards and hold'),
|
'swipe_block_forward_in_progress': _('Drag finger perpendicular to writing direction, to go forward'),
|
||||||
'swipe_inline_forward_hold': _('Swipe in writing direction, forwards and hold'),
|
|
||||||
'swipe_block_backward_hold': _('Swipe perpendicular to writing direction, backwards and hold'),
|
'swipe_inline_backward_hold': _('Drag and hold finger in writing direction, to go back'),
|
||||||
'swipe_block_forward_hold': _('Swipe perpendicular to writing direction, forwards and hold'),
|
'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 = {k:k for k in Object.keys(GESTURE_NAMES)}
|
||||||
GESTURE.tap = 'tap'
|
GESTURE.tap = 'tap'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user