mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement a vertical toolbar
This commit is contained in:
parent
a82a2724ce
commit
78d048c809
@ -15,6 +15,15 @@ def get_main_window_for(widget):
|
|||||||
p = p.parent()
|
p = p.parent()
|
||||||
|
|
||||||
|
|
||||||
|
def index_to_key_sequence(idx):
|
||||||
|
mods = []
|
||||||
|
for i, x in enumerate(('ALT', 'CTRL', 'META', 'SHIFT')):
|
||||||
|
if idx[i] == 'y':
|
||||||
|
mods.append(x.capitalize())
|
||||||
|
mods.append(idx[4:])
|
||||||
|
return QKeySequence('+'.join(mods))
|
||||||
|
|
||||||
|
|
||||||
def key_to_text(key):
|
def key_to_text(key):
|
||||||
return QKeySequence(key).toString(QKeySequence.PortableText).lower()
|
return QKeySequence(key).toString(QKeySequence.PortableText).lower()
|
||||||
|
|
||||||
|
130
src/calibre/gui2/viewer/toolbars.py
Normal file
130
src/calibre/gui2/viewer/toolbars.py
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#!/usr/bin/env python2
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
from collections import defaultdict
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
|
from PyQt5.Qt import QAction, QIcon, QKeySequence, QMenu, Qt, QToolBar, pyqtSignal
|
||||||
|
from PyQt5.QtWebEngineWidgets import QWebEnginePage
|
||||||
|
|
||||||
|
from calibre.gui2 import elided_text
|
||||||
|
from calibre.gui2.viewer.shortcuts import index_to_key_sequence
|
||||||
|
from calibre.gui2.viewer.web_view import get_session_pref, set_book_path
|
||||||
|
from polyglot.builtins import iteritems
|
||||||
|
|
||||||
|
|
||||||
|
class VerticalToolBar(QToolBar):
|
||||||
|
|
||||||
|
action_triggered = pyqtSignal(object)
|
||||||
|
open_book_at_path = pyqtSignal(object)
|
||||||
|
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
QToolBar.__init__(self, parent)
|
||||||
|
self.setObjectName('vertical_toolbar')
|
||||||
|
self.setAllowedAreas(Qt.LeftToolBarArea | Qt.RightToolBarArea)
|
||||||
|
self.setToolButtonStyle(Qt.ToolButtonIconOnly)
|
||||||
|
self.setOrientation(Qt.Vertical)
|
||||||
|
|
||||||
|
def initialize(self, web_view):
|
||||||
|
self.action_triggered.connect(web_view.trigger_shortcut)
|
||||||
|
page = web_view.page()
|
||||||
|
web_view.shortcuts_changed.connect(self.set_tooltips)
|
||||||
|
web_view.paged_mode_changed.connect(self.update_mode_action)
|
||||||
|
self.shortcut_actions = {}
|
||||||
|
|
||||||
|
self.back_action = page.action(QWebEnginePage.Back)
|
||||||
|
self.back_action.setIcon(QIcon(I('back.png')))
|
||||||
|
self.back_action.setText(_('Back'))
|
||||||
|
self.addAction(self.back_action)
|
||||||
|
self.forward_action = page.action(QWebEnginePage.Forward)
|
||||||
|
self.forward_action.setIcon(QIcon(I('forward.png')))
|
||||||
|
self.forward_action.setText(_('Forward'))
|
||||||
|
self.addAction(self.forward_action)
|
||||||
|
self.addSeparator()
|
||||||
|
|
||||||
|
def shortcut_action(icon, text, sc):
|
||||||
|
a = QAction(QIcon(I(icon)), text, self)
|
||||||
|
self.addAction(a)
|
||||||
|
connect_lambda(a.triggered, self, lambda self: self.action_triggered.emit(sc))
|
||||||
|
self.shortcut_actions[sc] = a
|
||||||
|
return a
|
||||||
|
|
||||||
|
self.open_action = a = QAction(QIcon(I('document_open.png')), _('Open e-book'), self)
|
||||||
|
self.open_menu = m = QMenu(self)
|
||||||
|
a.setMenu(m)
|
||||||
|
m.aboutToShow.connect(self.populate_open_menu)
|
||||||
|
connect_lambda(a.triggered, self, lambda self: self.open_book_at_path.emit(None))
|
||||||
|
self.addAction(a)
|
||||||
|
self.copy_action = a = page.action(QWebEnginePage.Copy)
|
||||||
|
a.setIcon(QIcon(I('edit-copy.png'))), a.setText(_('Copy to clipboard'))
|
||||||
|
self.addAction(a)
|
||||||
|
self.increase_font_size_action = shortcut_action('font_size_larger.png', _('Increase font size'), 'increase_font_size')
|
||||||
|
self.decrease_font_size_action = shortcut_action('font_size_smaller.png', _('Decrease font size'), 'decrease_font_size')
|
||||||
|
self.fullscreen_action = shortcut_action('page.png', _('Toggle full screen'), 'toggle_full_screen')
|
||||||
|
self.addSeparator()
|
||||||
|
|
||||||
|
self.next_action = shortcut_action('next.png', _('Next page'), 'next')
|
||||||
|
self.previous_action = shortcut_action('previous.png', _('Previous page'), 'previous')
|
||||||
|
self.addSeparator()
|
||||||
|
|
||||||
|
self.toc_action = shortcut_action('toc.png', _('Table of Contents'), 'toggle_toc')
|
||||||
|
self.bookmarks_action = shortcut_action('bookmarks.png', _('Bookmarks'), 'toggle_bookmarks')
|
||||||
|
self.lookup_action = shortcut_action('search.png', _('Lookup words'), 'toggle_lookup')
|
||||||
|
self.chrome_action = shortcut_action('tweaks.png', _('Show viewer controls'), 'show_chrome')
|
||||||
|
self.addSeparator()
|
||||||
|
|
||||||
|
self.mode_action = a = shortcut_action('scroll.png', _('Toggle paged mode'), 'toggle_paged_mode')
|
||||||
|
a.setCheckable(True)
|
||||||
|
self.print_action = shortcut_action('print.png', _('Print book'), 'print')
|
||||||
|
self.preferences_action = shortcut_action('config.png', _('Preferences'), 'preferences')
|
||||||
|
self.metadata_action = shortcut_action('metadata.png', _('Show book metadata'), 'metadata')
|
||||||
|
self.update_mode_action()
|
||||||
|
self.addSeparator()
|
||||||
|
|
||||||
|
def update_mode_action(self):
|
||||||
|
mode = get_session_pref('read_mode', default='paged', group=None)
|
||||||
|
a = self.mode_action
|
||||||
|
if mode == 'paged':
|
||||||
|
a.setChecked(False)
|
||||||
|
a.setToolTip(_('Switch to flow mode — where the text is not broken into pages'))
|
||||||
|
else:
|
||||||
|
a.setChecked(True)
|
||||||
|
a.setToolTip(_('Switch to paged mode — where the text is broken into pages'))
|
||||||
|
|
||||||
|
def set_tooltips(self, smap):
|
||||||
|
rmap = defaultdict(list)
|
||||||
|
for k, v in iteritems(smap):
|
||||||
|
rmap[v].append(k)
|
||||||
|
for sc, a in iteritems(self.shortcut_actions):
|
||||||
|
if a.isCheckable():
|
||||||
|
continue
|
||||||
|
x = rmap.get(sc)
|
||||||
|
if x is not None:
|
||||||
|
|
||||||
|
def as_text(idx):
|
||||||
|
return index_to_key_sequence(idx).toString(QKeySequence.NativeText)
|
||||||
|
|
||||||
|
keys = sorted(filter(None, map(as_text, x)))
|
||||||
|
if keys:
|
||||||
|
a.setToolTip('{} [{}]'.format(a.text(), ', '.join(keys)))
|
||||||
|
|
||||||
|
def populate_open_menu(self):
|
||||||
|
m = self.open_menu
|
||||||
|
m.clear()
|
||||||
|
recent = get_session_pref('standalone_recently_opened', group=None, default=())
|
||||||
|
if recent:
|
||||||
|
for entry in recent:
|
||||||
|
try:
|
||||||
|
path = os.path.abspath(entry['pathtoebook'])
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if path == os.path.abspath(set_book_path.pathtoebook):
|
||||||
|
continue
|
||||||
|
m.addAction('{}\t {}'.format(
|
||||||
|
elided_text(entry['title'], pos='right', width=250),
|
||||||
|
elided_text(os.path.basename(path), width=250))).triggered.connect(partial(
|
||||||
|
self.open_book_at_path.emit, path))
|
@ -32,6 +32,7 @@ from calibre.gui2.viewer.convert_book import prepare_book, update_book
|
|||||||
from calibre.gui2.viewer.lookup import Lookup
|
from calibre.gui2.viewer.lookup import Lookup
|
||||||
from calibre.gui2.viewer.overlay import LoadingOverlay
|
from calibre.gui2.viewer.overlay import LoadingOverlay
|
||||||
from calibre.gui2.viewer.toc import TOC, TOCSearch, TOCView
|
from calibre.gui2.viewer.toc import TOC, TOCSearch, TOCView
|
||||||
|
from calibre.gui2.viewer.toolbars import VerticalToolBar
|
||||||
from calibre.gui2.viewer.web_view import (
|
from calibre.gui2.viewer.web_view import (
|
||||||
WebView, get_path_for_name, get_session_pref, set_book_path, viewer_config_dir,
|
WebView, get_path_for_name, get_session_pref, set_book_path, viewer_config_dir,
|
||||||
vprefs
|
vprefs
|
||||||
@ -98,6 +99,10 @@ class EbookViewer(MainWindow):
|
|||||||
self.setWindowTitle(self.base_window_title)
|
self.setWindowTitle(self.base_window_title)
|
||||||
self.in_full_screen_mode = None
|
self.in_full_screen_mode = None
|
||||||
self.image_popup = ImagePopup(self)
|
self.image_popup = ImagePopup(self)
|
||||||
|
self.vertical_toolbar = vt = VerticalToolBar(self)
|
||||||
|
vt.open_book_at_path.connect(self.ask_for_open)
|
||||||
|
self.addToolBar(Qt.LeftToolBarArea, vt)
|
||||||
|
# vt.setVisible(False)
|
||||||
try:
|
try:
|
||||||
os.makedirs(annotations_dir)
|
os.makedirs(annotations_dir)
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
@ -156,6 +161,7 @@ class EbookViewer(MainWindow):
|
|||||||
self.web_view.show_loading_message.connect(self.show_loading_message)
|
self.web_view.show_loading_message.connect(self.show_loading_message)
|
||||||
self.web_view.show_error.connect(self.show_error)
|
self.web_view.show_error.connect(self.show_error)
|
||||||
self.web_view.print_book.connect(self.print_book, type=Qt.QueuedConnection)
|
self.web_view.print_book.connect(self.print_book, type=Qt.QueuedConnection)
|
||||||
|
self.vertical_toolbar.initialize(self.web_view)
|
||||||
self.setCentralWidget(self.web_view)
|
self.setCentralWidget(self.web_view)
|
||||||
self.loading_overlay = LoadingOverlay(self)
|
self.loading_overlay = LoadingOverlay(self)
|
||||||
self.restore_state()
|
self.restore_state()
|
||||||
|
@ -265,6 +265,7 @@ class ViewerBridge(Bridge):
|
|||||||
show_home_page = to_js()
|
show_home_page = to_js()
|
||||||
background_image_changed = to_js()
|
background_image_changed = to_js()
|
||||||
goto_frac = to_js()
|
goto_frac = to_js()
|
||||||
|
trigger_shortcut = to_js()
|
||||||
|
|
||||||
|
|
||||||
def apply_font_settings(page_or_view):
|
def apply_font_settings(page_or_view):
|
||||||
@ -399,6 +400,8 @@ class WebView(RestartingWebEngineView):
|
|||||||
show_loading_message = pyqtSignal(object)
|
show_loading_message = pyqtSignal(object)
|
||||||
show_error = pyqtSignal(object, object, object)
|
show_error = pyqtSignal(object, object, object)
|
||||||
print_book = pyqtSignal()
|
print_book = pyqtSignal()
|
||||||
|
shortcuts_changed = pyqtSignal(object)
|
||||||
|
paged_mode_changed = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
self._host_widget = None
|
self._host_widget = None
|
||||||
@ -447,6 +450,7 @@ class WebView(RestartingWebEngineView):
|
|||||||
|
|
||||||
def set_shortcut_map(self, smap):
|
def set_shortcut_map(self, smap):
|
||||||
self.shortcut_map = smap
|
self.shortcut_map = smap
|
||||||
|
self.shortcuts_changed.emit(smap)
|
||||||
|
|
||||||
def url_changed(self, url):
|
def url_changed(self, url):
|
||||||
if url.hasFragment():
|
if url.hasFragment():
|
||||||
@ -527,12 +531,15 @@ class WebView(RestartingWebEngineView):
|
|||||||
if key == '*' and val is None:
|
if key == '*' and val is None:
|
||||||
vprefs['session_data'] = {}
|
vprefs['session_data'] = {}
|
||||||
apply_font_settings(self._page)
|
apply_font_settings(self._page)
|
||||||
|
self.paged_mode_changed.emit()
|
||||||
elif key != '*':
|
elif key != '*':
|
||||||
sd = vprefs['session_data']
|
sd = vprefs['session_data']
|
||||||
sd[key] = val
|
sd[key] = val
|
||||||
vprefs['session_data'] = sd
|
vprefs['session_data'] = sd
|
||||||
if key in ('standalone_font_settings', 'base_font_size'):
|
if key in ('standalone_font_settings', 'base_font_size'):
|
||||||
apply_font_settings(self._page)
|
apply_font_settings(self._page)
|
||||||
|
elif key == 'read_mode':
|
||||||
|
self.paged_mode_changed.emit()
|
||||||
|
|
||||||
def set_local_storage(self, key, val):
|
def set_local_storage(self, key, val):
|
||||||
if key == '*' and val is None:
|
if key == '*' and val is None:
|
||||||
@ -573,3 +580,6 @@ class WebView(RestartingWebEngineView):
|
|||||||
|
|
||||||
def clear_history(self):
|
def clear_history(self):
|
||||||
self._page.history().clear()
|
self._page.history().clear()
|
||||||
|
|
||||||
|
def trigger_shortcut(self, which):
|
||||||
|
self.execute_when_ready('trigger_shortcut', which)
|
||||||
|
@ -245,6 +245,7 @@ def shortcuts_definition():
|
|||||||
'ui',
|
'ui',
|
||||||
_('Show the viewer controls'),
|
_('Show the viewer controls'),
|
||||||
),
|
),
|
||||||
|
|
||||||
}
|
}
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
@ -288,6 +289,13 @@ def add_standalone_viewer_shortcuts():
|
|||||||
_('Quit the viewer'),
|
_('Quit the viewer'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sc['print'] = desc(
|
||||||
|
"Ctrl+P",
|
||||||
|
'ui',
|
||||||
|
_('Print book to PDF'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_shortcut_map(custom_shortcuts):
|
def create_shortcut_map(custom_shortcuts):
|
||||||
ans = {}
|
ans = {}
|
||||||
scd = shortcuts_definition()
|
scd = shortcuts_definition()
|
||||||
|
@ -339,6 +339,8 @@ class View:
|
|||||||
ui_operations.toggle_inspector()
|
ui_operations.toggle_inspector()
|
||||||
elif data.name is 'toggle_lookup':
|
elif data.name is 'toggle_lookup':
|
||||||
ui_operations.toggle_lookup()
|
ui_operations.toggle_lookup()
|
||||||
|
elif data.name is 'toggle_full_screen':
|
||||||
|
ui_operations.toggle_full_screen()
|
||||||
elif data.name is 'toggle_paged_mode':
|
elif data.name is 'toggle_paged_mode':
|
||||||
self.toggle_paged_mode()
|
self.toggle_paged_mode()
|
||||||
elif data.name is 'quit':
|
elif data.name is 'quit':
|
||||||
@ -367,6 +369,19 @@ class View:
|
|||||||
self.on_next_section({'forward': False})
|
self.on_next_section({'forward': False})
|
||||||
elif data.name is 'open_book':
|
elif data.name is 'open_book':
|
||||||
self.overlay.open_book()
|
self.overlay.open_book()
|
||||||
|
elif data.name is 'next':
|
||||||
|
self.iframe_wrapper.send_message(
|
||||||
|
'next_screen', backwards=False, all_pages_on_screen=get_session_data().get('paged_margin_clicks_scroll_by_screen'))
|
||||||
|
elif data.name is 'previous':
|
||||||
|
self.iframe_wrapper.send_message(
|
||||||
|
'next_screen', backwards=True, all_pages_on_screen=get_session_data().get('paged_margin_clicks_scroll_by_screen'))
|
||||||
|
elif data.name is 'print':
|
||||||
|
ui_operations.print_book()
|
||||||
|
elif data.name is 'preferences':
|
||||||
|
self.overlay.show_prefs()
|
||||||
|
elif data.name is 'metadata':
|
||||||
|
self.overlay.show_metadata()
|
||||||
|
|
||||||
|
|
||||||
def on_selection_change(self, data):
|
def on_selection_change(self, data):
|
||||||
self.currently_showing.selected_text = data.text
|
self.currently_showing.selected_text = data.text
|
||||||
|
@ -269,6 +269,12 @@ def background_image_changed(img_id):
|
|||||||
img.src = READER_BACKGROUND_URL + '?' + Date().getTime()
|
img.src = READER_BACKGROUND_URL + '?' + Date().getTime()
|
||||||
|
|
||||||
|
|
||||||
|
@from_python
|
||||||
|
def trigger_shortcut(which):
|
||||||
|
if view:
|
||||||
|
view.on_handle_shortcut({'name': which})
|
||||||
|
|
||||||
|
|
||||||
def onerror(msg, script_url, line_number, column_number, error_object):
|
def onerror(msg, script_url, line_number, column_number, error_object):
|
||||||
if not error_object:
|
if not error_object:
|
||||||
# cross domain error
|
# cross domain error
|
||||||
|
Loading…
x
Reference in New Issue
Block a user