Viewer: Allow right clicking on the scrollbar to easily access commonly used scrolling shortcuts

This commit is contained in:
Kovid Goyal 2020-02-19 12:34:53 +05:30
parent 9f222e809c
commit 8655121e16
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 59 additions and 15 deletions

View File

@ -13,8 +13,8 @@ from hashlib import sha256
from threading import Thread
from PyQt5.Qt import (
QApplication, QDockWidget, QEvent, QMimeData, QModelIndex, QPixmap, QScrollBar,
Qt, QToolBar, QUrl, QVBoxLayout, QWidget, pyqtSignal
QApplication, QCursor, QDockWidget, QEvent, QMenu, QMimeData, QModelIndex,
QPixmap, Qt, QToolBar, QUrl, QVBoxLayout, QWidget, pyqtSignal
)
from calibre import prints
@ -78,13 +78,6 @@ def path_key(path):
return sha256(as_bytes(path)).hexdigest()
class ScrollBar(QScrollBar):
def paintEvent(self, ev):
if self.isEnabled():
return QScrollBar.paintEvent(self, ev)
class EbookViewer(MainWindow):
msg_from_anotherinstance = pyqtSignal(object)
@ -178,6 +171,7 @@ class EbookViewer(MainWindow):
self.web_view.reset_interface.connect(self.reset_interface, type=Qt.QueuedConnection)
self.web_view.quit.connect(self.quit, type=Qt.QueuedConnection)
self.web_view.shortcuts_changed.connect(self.shortcuts_changed)
self.web_view.scrollbar_context_menu.connect(self.scrollbar_context_menu)
self.actions_toolbar.initialize(self.web_view, self.search_dock.toggleViewAction())
self.setCentralWidget(self.web_view)
self.loading_overlay = LoadingOverlay(self)
@ -193,14 +187,38 @@ class EbookViewer(MainWindow):
rmap[v].append(k)
self.actions_toolbar.set_tooltips(rmap)
def toggle_inspector(self):
visible = self.inspector_dock.toggleViewAction().isChecked()
self.inspector_dock.setVisible(not visible)
def resizeEvent(self, ev):
self.loading_overlay.resize(self.size())
return MainWindow.resizeEvent(self, ev)
def scrollbar_context_menu(self, x, y, frac):
m = QMenu(self)
amap = {}
def a(text, name):
m.addAction(text)
amap[text] = name
a(_('Scroll here'), 'here')
m.addSeparator()
a(_('Start of book'), 'start_of_book')
a(_('End of book'), 'end_of_book')
m.addSeparator()
a(_('Previous section'), 'previous_section')
a(_('Next section'), 'next_section')
m.addSeparator()
a(_('Start of current file'), 'start_of_file')
a(_('End of current file'), 'end_of_file')
q = m.exec_(QCursor.pos())
if not q:
return
q = amap[q.text()]
if q == 'here':
self.web_view.goto_frac(frac)
else:
self.web_view.trigger_shortcut(q)
# IPC {{{
def handle_commandline_arg(self, arg):
if arg:
@ -246,6 +264,10 @@ class EbookViewer(MainWindow):
# Docks (ToC, Bookmarks, Lookup, etc.) {{{
def toggle_inspector(self):
visible = self.inspector_dock.toggleViewAction().isChecked()
self.inspector_dock.setVisible(not visible)
def toggle_toc(self):
self.toc_dock.setVisible(not self.toc_dock.isVisible())

View File

@ -272,6 +272,7 @@ class ViewerBridge(Bridge):
reset_interface = from_js()
quit = from_js()
customize_toolbar = from_js()
scrollbar_context_menu = from_js(object, object, object)
create_view = to_js()
start_book_load = to_js()
@ -443,6 +444,7 @@ class WebView(RestartingWebEngineView):
reset_interface = pyqtSignal()
quit = pyqtSignal()
customize_toolbar = pyqtSignal()
scrollbar_context_menu = pyqtSignal(object, object, object)
shortcuts_changed = pyqtSignal(object)
paged_mode_changed = pyqtSignal()
standalone_misc_settings_changed = pyqtSignal(object)
@ -491,6 +493,7 @@ class WebView(RestartingWebEngineView):
self.bridge.reset_interface.connect(self.reset_interface)
self.bridge.quit.connect(self.quit)
self.bridge.customize_toolbar.connect(self.customize_toolbar)
self.bridge.scrollbar_context_menu.connect(self.scrollbar_context_menu)
self.bridge.export_shortcut_map.connect(self.set_shortcut_map)
self.shortcut_map = {}
self.bridge.report_cfi.connect(self.call_callback)

View File

@ -116,6 +116,7 @@ class IframeBoss:
'window_size': self.received_window_size,
'overlay_visibility_changed': self.on_overlay_visibility_changed,
'show_search_result': self.show_search_result,
'handle_navigation_shortcut': self.on_handle_navigation_shortcut,
}
self.comm = IframeClient(handlers)
self.last_window_ypos = 0
@ -458,6 +459,10 @@ class IframeBoss:
else:
self.send_message('handle_shortcut', name=sc_name)
def on_handle_navigation_shortcut(self, data):
self.handle_navigation_shortcut(data.name, data.key or {
'key': '', 'altKey': False, 'ctrlKey': False, 'shiftKey': False, 'metaKey': False})
def oncontextmenu(self, evt):
if self.content_ready:
evt.preventDefault()

View File

@ -7,7 +7,7 @@ from elementmaker import E
from book_list.globals import get_session_data
from book_list.theme import cached_color_to_rgba
from dom import unique_id
from read_book.globals import ui_operations
SIZE = 10
@ -31,7 +31,7 @@ class BookScrollbar:
return E.div(
id=self.container_id,
style=f'height: 100vh; background-color: #aaa; width: {SIZE}px; border-radius: 5px',
onclick=self.bar_clicked,
onclick=self.bar_clicked, oncontextmenu=self.context_menu,
E.div(
style=f'position: relative; width: 100%; height: {int(2.2*SIZE)}px; background-color: #444; border-radius: 5px',
onmousedown=self.on_bob_mousedown,
@ -41,6 +41,16 @@ class BookScrollbar:
)
)
def context_menu(self, ev):
if ui_operations.scrollbar_context_menu:
ev.preventDefault(), ev.stopPropagation()
c = self.container
bob = c.firstChild
height = c.clientHeight - bob.clientHeight
top = max(0, min(ev.clientY - bob.clientHeight, height))
frac = max(0, min(top / height, 1))
ui_operations.scrollbar_context_menu(ev.screenX, ev.screenY, frac)
def bar_clicked(self, evt):
if evt.button is 0:
c = self.container

View File

@ -442,6 +442,8 @@ class View:
self.toggle_autoscroll()
elif data.name.startsWith('switch_color_scheme:'):
self.switch_color_scheme(data.name.partition(':')[-1])
else:
self.iframe_wrapper.send_message('handle_navigation_shortcut', name=data.name)
def on_selection_change(self, data):
self.currently_showing.selected_text = data.text

View File

@ -403,6 +403,8 @@ if window is window.top:
to_python.autoscroll_state_changed(active)
ui_operations.search_result_not_found = def(sr):
to_python.search_result_not_found(sr)
ui_operations.scrollbar_context_menu = def(x, y, frac):
to_python.scrollbar_context_menu(x, y, frac)
document.body.appendChild(E.div(id='view'))
window.onerror = onerror