From 7914ad4244f0fc9e10913299d69907dcb2616b69 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 12 Aug 2019 20:27:33 +0530 Subject: [PATCH] UI for viewer font settings --- src/calibre/gui2/viewer/web_view.py | 46 ++++++++++-- src/pyj/read_book/prefs/fonts.pyj | 108 ++++++++++++++++++++++++++++ src/pyj/read_book/prefs/main.pyj | 34 ++++++--- src/pyj/session.pyj | 2 + src/pyj/viewer-main.pyj | 3 +- 5 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 src/pyj/read_book/prefs/fonts.pyj diff --git a/src/calibre/gui2/viewer/web_view.py b/src/calibre/gui2/viewer/web_view.py index eefc921adb..e002d210ed 100644 --- a/src/calibre/gui2/viewer/web_view.py +++ b/src/calibre/gui2/viewer/web_view.py @@ -9,8 +9,8 @@ import sys from itertools import count from PyQt5.Qt import ( - QApplication, QBuffer, QByteArray, QHBoxLayout, QSize, Qt, QTimer, QUrl, QWidget, - pyqtSignal + QApplication, QBuffer, QByteArray, QFontDatabase, QHBoxLayout, QSize, Qt, QTimer, + QUrl, QWidget, pyqtSignal ) from PyQt5.QtWebEngineCore import QWebEngineUrlSchemeHandler from PyQt5.QtWebEngineWidgets import ( @@ -188,6 +188,35 @@ class ViewerBridge(Bridge): get_current_cfi = to_js() +def apply_font_settings(page_or_view): + s = page_or_view.settings() + sd = vprefs['session_data'] + fs = sd.get('standalone_font_settings', {}) + if fs.get('serif_family'): + s.setFontFamily(s.SerifFont, fs.get('serif_family')) + else: + s.resetFontFamily(s.SerifFont) + if fs.get('sans_family'): + s.setFontFamily(s.SansSerifFont, fs.get('sans_family')) + else: + s.resetFontFamily(s.SansSerifFont) + if fs.get('mono_family'): + s.setFontFamily(s.FixedFont, fs.get('mono_family')) + else: + s.resetFontFamily(s.SansSerifFont) + sf = fs.get('standard_font') or 'serif' + sf = getattr(s, {'serif': 'SerifFont', 'sans': 'SansSerifFont', 'mono': 'FixedFont'}[sf]) + s.setFontFamily(s.StandardFont, s.fontFamily(sf)) + mfs = fs.get('minimum_font_size') + if mfs is None: + s.resetFontSize(s.MinimumFontSize) + else: + s.setFontSize(s.MinimumFontSize, mfs) + s.setFontSize(s.DefaultFontSize, sd.get('base_font_size')) + + return s + + class WebPage(QWebEnginePage): def __init__(self, parent): @@ -195,6 +224,7 @@ class WebPage(QWebEnginePage): QWebEnginePage.__init__(self, profile, parent) profile.setParent(self) secure_webengine(self, for_viewer=True) + apply_font_settings(self) self.bridge = ViewerBridge(self) def javaScriptConsoleMessage(self, level, msg, linenumber, source_id): @@ -309,6 +339,11 @@ class WebView(RestartingWebEngineView): if ans is not None and not sip.isdeleted(ans): return ans + def change_zoom_by(self, steps=1): + ss = vprefs['session_data'].get('zoom_step_size') or 20 + amt = (ss / 100) * steps + self._page.setZoomFactor(self._page.zoomFactor() + amt) + def render_process_died(self): if self.dead_renderer_error_shown: return @@ -339,7 +374,7 @@ class WebView(RestartingWebEngineView): return self._page.bridge def on_bridge_ready(self): - self.bridge.create_view(vprefs['session_data']) + self.bridge.create_view(vprefs['session_data'], QFontDatabase().families()) for func, args in iteritems(self.pending_bridge_ready_actions): getattr(self.bridge, func)(*args) @@ -369,9 +404,12 @@ class WebView(RestartingWebEngineView): def set_session_data(self, key, val): if key == '*' and val is None: vprefs['session_data'] = {} - else: + apply_font_settings(self._page) + elif key != '*': sd = vprefs['session_data'] sd[key] = val + if key == 'standalone_font_settings': + apply_font_settings(self._page) vprefs['session_data'] = sd def do_callback(self, func_name, callback): diff --git a/src/pyj/read_book/prefs/fonts.pyj b/src/pyj/read_book/prefs/fonts.pyj new file mode 100644 index 0000000000..3463fcb0d5 --- /dev/null +++ b/src/pyj/read_book/prefs/fonts.pyj @@ -0,0 +1,108 @@ +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2016, Kovid Goyal +from __python__ import hash_literals, bound_methods + +from gettext import gettext as _ +from dom import unique_id +from elementmaker import E +from book_list.globals import get_session_data +from read_book.globals import runtime + + +CONTAINER = unique_id('standalone-font-settings') +DEFAULT_STANDARD_FONT = 'serif' +DEFAULT_MINIMUM_FONT_SIZE = 8 +DEFAULT_ZOOM_STEP_SIZE = 20 + + +def font_select(name, settings): + ans = E.select(name=name) + ans.appendChild(E.option(_('— Choose a font —'), value='')) + current_val = settings[name] + if not current_val: + ans.lastChild.setAttribute('selected', 'selected') + for family in runtime.all_font_families: + if family: + ans.appendChild(E.option(family)) + if family is current_val: + ans.lastChild.setAttribute('selected', 'selected') + return ans + + +def standard_font(settings): + ans = E.select(name='standard_font') + ans.appendChild(E.option(_('Serif'), value='serif')) + ans.appendChild(E.option(_('Sans-serif'), value='sans')) + ans.appendChild(E.option(_('Monospace'), value='mono')) + sf = settings.standard_font or DEFAULT_STANDARD_FONT + ans.querySelector(f'[value={sf}]').setAttribute('selected', 'selected') + return ans + + +def minimum_font_size(settings): + ans = E.input(max='100', min='0', step='1', type='number', name='minimum_font_size') + if jstype(settings.minimum_font_size) is 'number': + ans.value = settings.minimum_font_size + '' + else: + ans.value = '' + DEFAULT_MINIMUM_FONT_SIZE + return ans + + +def zoom_step_size(settings): + ans = E.input(max='100', min='1', step='1', type='number', name='zoom_step_size') + if jstype(settings.zoom_step_size) is 'number': + ans.value = settings.zoom_step_size + '' + else: + ans.value = '' + DEFAULT_ZOOM_STEP_SIZE + return ans + + +def get_container(): + return document.getElementById(CONTAINER) + + +def create_fonts_panel(container): + container.appendChild(E.div(id=CONTAINER, style='margin: 1rem')) + container = container.lastChild + container.append(E.div(_('Choose fonts to use for un-styled text:'), style='margin-top: 1rem')) + sd = get_session_data() + settings = sd.get('standalone_font_settings') + + def row(label, widget): + return E.tr(E.td(label + ':\xa0', style='padding-top: 1ex'), E.td(widget, style='padding-top: 1ex')) + + container.append(E.table(style='margin-top: 1rem', + row(_('Serif family'), font_select('serif_family', settings)), + row(_('Sans-serif family'), font_select('sans_family', settings)), + row(_('Monospace family'), font_select('mono_family', settings)), + row(_('Standard font'), standard_font(settings)), + )) + + container.append(E.div(_('Zoom related settings'), style='margin-top: 1rem')) + container.append(E.table(style='margin-top: 1rem', + row(_('Zoom step size (%s)'), zoom_step_size(settings)), + row(_('Minimum font size (px)'), minimum_font_size(settings)), + )) + + +develop = create_fonts_panel + + +def commit_fonts(onchange): + sd = get_session_data() + container = get_container() + vals = {} + zss = parseInt(container.querySelector('[name=zoom_step_size]').value) + if zss is not DEFAULT_ZOOM_STEP_SIZE: + vals.zoom_step_size = zss + mfs = parseInt(container.querySelector('[name=minimum_font_size]').value) + if mfs is not DEFAULT_MINIMUM_FONT_SIZE: + vals.minimum_font_size = mfs + sf = container.querySelector('[name=standard_font]').value + if sf is not DEFAULT_STANDARD_FONT: + vals.standard_font = sf + for q in ('serif_family', 'sans_family', 'mono_family'): + val = container.querySelector(f'[name={q}]').value + if val: + vals[q] = val + sd.set('standalone_font_settings', vals) diff --git a/src/pyj/read_book/prefs/main.pyj b/src/pyj/read_book/prefs/main.pyj index dfeafb50b7..9e05754833 100644 --- a/src/pyj/read_book/prefs/main.pyj +++ b/src/pyj/read_book/prefs/main.pyj @@ -1,15 +1,20 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2016, Kovid Goyal -from __python__ import hash_literals, bound_methods +from __python__ import bound_methods, hash_literals -from gettext import gettext as _ -from dom import svgicon, ensure_id, clear from elementmaker import E +from gettext import gettext as _ + from book_list.item_list import build_list, create_item -from read_book.prefs.colors import create_colors_panel, commit_colors -from read_book.prefs.layout import create_layout_panel, commit_layout -from read_book.prefs.user_stylesheet import create_user_stylesheet_panel, commit_user_stylesheet -from read_book.prefs.head_foot import create_head_foot_panel, commit_head_foot +from dom import clear, ensure_id, svgicon +from read_book.globals import runtime +from read_book.prefs.colors import commit_colors, create_colors_panel +from read_book.prefs.fonts import commit_fonts, create_fonts_panel +from read_book.prefs.head_foot import commit_head_foot, create_head_foot_panel +from read_book.prefs.layout import commit_layout, create_layout_panel +from read_book.prefs.user_stylesheet import ( + commit_user_stylesheet, create_user_stylesheet_panel +) class Prefs: @@ -59,12 +64,23 @@ class Prefs: document.getElementById(self.title_id).textContent = _('Configure book reader') c = E.div() container.appendChild(c) - build_list(c, [ + items = [ create_item(_('Colors'), def():self.show_panel('colors');, _('Colors of the page and text')), create_item(_('Page layout'), def():self.show_panel('layout');, _('Page margins and number of pages per screen')), create_item(_('User style sheet'), def():self.show_panel('user_stylesheet');, _('Style rules for text')), create_item(_('Headers and footers'), def():self.show_panel('head_foot');, _('Customize the headers and footers')), - ]) + ] + if runtime.is_standalone_viewer: + items.push(create_item( + _('Fonts'), def():self.show_panel('fonts');, _('Font choices'))) + build_list(c, items) + + def display_fonts(self, container): + document.getElementById(self.title_id).textContent = _('Fonts') + create_fonts_panel(container) + + def close_fonts(self): + commit_fonts(self.onchange, self.container) def display_colors(self, container): document.getElementById(self.title_id).textContent = _('Colors') diff --git a/src/pyj/session.pyj b/src/pyj/session.pyj index fbaf0c79ed..6e89815888 100644 --- a/src/pyj/session.pyj +++ b/src/pyj/session.pyj @@ -40,6 +40,7 @@ defaults = { 'header': {}, 'footer': {'right': 'progress'}, 'word_actions': v'[]', + 'standalone_font_settings': {}, } is_local_setting = { @@ -55,6 +56,7 @@ is_local_setting = { 'current_color_scheme': True, 'base_font_size': True, 'controls_help_shown_count': True, + 'standalone_font_settings': True, } diff --git a/src/pyj/viewer-main.pyj b/src/pyj/viewer-main.pyj index 76c908a453..1eb69219ed 100644 --- a/src/pyj/viewer-main.pyj +++ b/src/pyj/viewer-main.pyj @@ -175,8 +175,9 @@ def create_session_data(prefs): @from_python -def create_view(prefs): +def create_view(prefs, all_font_families): nonlocal view + runtime.all_font_families = all_font_families if view is None: create_session_data(prefs) view = View(document.getElementById('view'))