mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Viewer: Allow right clicking on the scrollbar to easily access commonly used scrolling shortcuts
This commit is contained in:
parent
9f222e809c
commit
8655121e16
@ -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())
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user