mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Edit book: Allow customizing the base background/foreground and link colors for the preview window. Fixes #1889925 [[Enhancement] Editor Preview customise background colour](https://bugs.launchpad.net/calibre/+bug/1889925)
This commit is contained in:
parent
95c4381900
commit
716db6162b
@ -38,6 +38,9 @@ d['preview_standard_font_family'] = 'serif'
|
|||||||
d['preview_base_font_size'] = 18
|
d['preview_base_font_size'] = 18
|
||||||
d['preview_mono_font_size'] = 14
|
d['preview_mono_font_size'] = 14
|
||||||
d['preview_minimum_font_size'] = 8
|
d['preview_minimum_font_size'] = 8
|
||||||
|
d['preview_background'] = 'auto'
|
||||||
|
d['preview_foreground'] = 'auto'
|
||||||
|
d['preview_link_color'] = 'auto'
|
||||||
d['remove_existing_links_when_linking_sheets'] = True
|
d['remove_existing_links_when_linking_sheets'] = True
|
||||||
d['charmap_favorites'] = list(map(ord, '\xa0\u2002\u2003\u2009\xad' '‘’“”‹›«»‚„' '—–§¶†‡©®™' '→⇒•·°±−×÷¼½½¾' '…µ¢£€¿¡¨´¸ˆ˜' 'ÀÁÂÃÄÅÆÇÈÉÊË' 'ÌÍÎÏÐÑÒÓÔÕÖØ' 'ŒŠÙÚÛÜÝŸÞßàá' 'âãäåæçèéêëìí' 'îïðñòóôõöøœš' 'ùúûüýÿþªºαΩ∞')) # noqa
|
d['charmap_favorites'] = list(map(ord, '\xa0\u2002\u2003\u2009\xad' '‘’“”‹›«»‚„' '—–§¶†‡©®™' '→⇒•·°±−×÷¼½½¾' '…µ¢£€¿¡¨´¸ˆ˜' 'ÀÁÂÃÄÅÆÇÈÉÊË' 'ÌÍÎÏÐÑÒÓÔÕÖØ' 'ŒŠÙÚÛÜÝŸÞßàá' 'âãäåæçèéêëìí' 'îïðñòóôõöøœš' 'ùúûüýÿþªºαΩ∞')) # noqa
|
||||||
d['folders_for_types'] = {'style':'styles', 'image':'images', 'font':'fonts', 'audio':'audio', 'video':'video'}
|
d['folders_for_types'] = {'style':'styles', 'image':'images', 'font':'fonts', 'audio':'audio', 'video':'video'}
|
||||||
|
@ -207,6 +207,7 @@ class Boss(QObject):
|
|||||||
setup_css_parser_serialization()
|
setup_css_parser_serialization()
|
||||||
self.gui.apply_settings()
|
self.gui.apply_settings()
|
||||||
self.refresh_file_list()
|
self.refresh_file_list()
|
||||||
|
self.gui.preview.start_refresh_timer()
|
||||||
if ret == p.Accepted or p.dictionaries_changed:
|
if ret == p.Accepted or p.dictionaries_changed:
|
||||||
for ed in itervalues(editors):
|
for ed in itervalues(editors):
|
||||||
ed.apply_settings(dictionaries_changed=p.dictionaries_changed)
|
ed.apply_settings(dictionaries_changed=p.dictionaries_changed)
|
||||||
|
@ -19,7 +19,7 @@ from PyQt5.Qt import (
|
|||||||
QListWidgetItem, QIcon, QWidget, QSize, QFormLayout, Qt, QSpinBox,
|
QListWidgetItem, QIcon, QWidget, QSize, QFormLayout, Qt, QSpinBox,
|
||||||
QCheckBox, pyqtSignal, QDoubleSpinBox, QComboBox, QLabel, QFont, QApplication,
|
QCheckBox, pyqtSignal, QDoubleSpinBox, QComboBox, QLabel, QFont, QApplication,
|
||||||
QFontComboBox, QPushButton, QSizePolicy, QHBoxLayout, QGroupBox,
|
QFontComboBox, QPushButton, QSizePolicy, QHBoxLayout, QGroupBox,
|
||||||
QToolButton, QVBoxLayout, QSpacerItem, QTimer)
|
QToolButton, QVBoxLayout, QSpacerItem, QTimer, QRadioButton)
|
||||||
|
|
||||||
from calibre import prepare_string_for_xml
|
from calibre import prepare_string_for_xml
|
||||||
from calibre.utils.localization import get_lang
|
from calibre.utils.localization import get_lang
|
||||||
@ -30,6 +30,7 @@ from calibre.gui2.tweak_book.editor.themes import default_theme, all_theme_names
|
|||||||
from calibre.gui2.tweak_book.spell import ManageDictionaries
|
from calibre.gui2.tweak_book.spell import ManageDictionaries
|
||||||
from calibre.gui2.font_family_chooser import FontFamilyChooser
|
from calibre.gui2.font_family_chooser import FontFamilyChooser
|
||||||
from calibre.gui2.tweak_book.widgets import Dialog
|
from calibre.gui2.tweak_book.widgets import Dialog
|
||||||
|
from calibre.gui2.widgets2 import ColorButton
|
||||||
|
|
||||||
|
|
||||||
class BasicSettings(QWidget): # {{{
|
class BasicSettings(QWidget): # {{{
|
||||||
@ -154,7 +155,7 @@ class BasicSettings(QWidget): # {{{
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class EditorSettings(BasicSettings):
|
class EditorSettings(BasicSettings): # {{{
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
BasicSettings.__init__(self, parent)
|
BasicSettings.__init__(self, parent)
|
||||||
@ -269,9 +270,10 @@ class EditorSettings(BasicSettings):
|
|||||||
s.setter(s.widget, current_val)
|
s.setter(s.widget, current_val)
|
||||||
if d.theme_name:
|
if d.theme_name:
|
||||||
s.setter(s.widget, d.theme_name)
|
s.setter(s.widget, d.theme_name)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class IntegrationSettings(BasicSettings):
|
class IntegrationSettings(BasicSettings): # {{{
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
BasicSettings.__init__(self, parent)
|
BasicSettings.__init__(self, parent)
|
||||||
@ -293,9 +295,10 @@ class IntegrationSettings(BasicSettings):
|
|||||||
order.setToolTip(_('When auto-selecting the format to edit for a book with'
|
order.setToolTip(_('When auto-selecting the format to edit for a book with'
|
||||||
' multiple formats, this is the preference order.'))
|
' multiple formats, this is the preference order.'))
|
||||||
l.addRow(_('Preferred format order (drag and drop to change)'), order)
|
l.addRow(_('Preferred format order (drag and drop to change)'), order)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class MainWindowSettings(BasicSettings):
|
class MainWindowSettings(BasicSettings): # {{{
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
BasicSettings.__init__(self, parent)
|
BasicSettings.__init__(self, parent)
|
||||||
@ -334,9 +337,10 @@ class MainWindowSettings(BasicSettings):
|
|||||||
' multiple files with the same file name.'
|
' multiple files with the same file name.'
|
||||||
))
|
))
|
||||||
l.addRow(nd)
|
l.addRow(nd)
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class PreviewSettings(BasicSettings):
|
class PreviewSettings(BasicSettings): # {{{
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
BasicSettings.__init__(self, parent)
|
BasicSettings.__init__(self, parent)
|
||||||
@ -377,6 +381,51 @@ class PreviewSettings(BasicSettings):
|
|||||||
w = self('preview_minimum_font_size')
|
w = self('preview_minimum_font_size')
|
||||||
w.setMinimum(4), w.setMaximum(100), w.setSuffix(' px')
|
w.setMinimum(4), w.setMaximum(100), w.setSuffix(' px')
|
||||||
l.addRow(_('Mi&nimum font size:'), w)
|
l.addRow(_('Mi&nimum font size:'), w)
|
||||||
|
l.addRow(_('Background color:'), self.color_override('preview_background'))
|
||||||
|
l.addRow(_('Foreground color:'), self.color_override('preview_foreground'))
|
||||||
|
l.addRow(_('Link color:'), self.color_override('preview_link_color'))
|
||||||
|
|
||||||
|
def color_override(self, name):
|
||||||
|
w = QWidget(self)
|
||||||
|
l = QHBoxLayout(w)
|
||||||
|
|
||||||
|
def b(name, text, tt):
|
||||||
|
ans = QRadioButton(text, w)
|
||||||
|
l.addWidget(ans)
|
||||||
|
ans.setToolTip(tt)
|
||||||
|
setattr(w, name, ans)
|
||||||
|
ans.setObjectName(name)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
b('unset', _('No change'), _('Use the colors from the book styles, defaulting to black-on-white'))
|
||||||
|
b('auto', _('Theme based'), _('When using a dark theme force dark colors, otherwise same as "No change"'))
|
||||||
|
b('manual', _('Custom'), _('Choose a custom color'))
|
||||||
|
|
||||||
|
c = w.color_button = ColorButton(parent=w)
|
||||||
|
l.addWidget(c)
|
||||||
|
connect_lambda(c.clicked, w, lambda w: w.manual.setChecked(True))
|
||||||
|
|
||||||
|
def getter(w):
|
||||||
|
if w.unset.isChecked():
|
||||||
|
return 'unset'
|
||||||
|
if w.auto.isChecked():
|
||||||
|
return 'auto'
|
||||||
|
return w.color_button.color or 'auto'
|
||||||
|
|
||||||
|
def setter(w, val):
|
||||||
|
val = val or 'auto'
|
||||||
|
if val == 'unset':
|
||||||
|
w.unset.setChecked(True)
|
||||||
|
elif val == 'auto':
|
||||||
|
w.auto.setChecked(True)
|
||||||
|
else:
|
||||||
|
w.manual.setChecked(True)
|
||||||
|
w.color_button.color = val
|
||||||
|
self(name, widget=w, getter=getter, setter=setter)
|
||||||
|
l.setContentsMargins(0, 0, 0, 0)
|
||||||
|
return w
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
# ToolbarSettings {{{
|
# ToolbarSettings {{{
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ from calibre.constants import (
|
|||||||
from calibre.ebooks.oeb.base import OEB_DOCS, XHTML_MIME, serialize
|
from calibre.ebooks.oeb.base import OEB_DOCS, XHTML_MIME, serialize
|
||||||
from calibre.ebooks.oeb.polish.parsing import parse
|
from calibre.ebooks.oeb.polish.parsing import parse
|
||||||
from calibre.gui2 import NO_URL_FORMATTING, error_dialog, is_dark_theme, open_url
|
from calibre.gui2 import NO_URL_FORMATTING, error_dialog, is_dark_theme, open_url
|
||||||
from calibre.gui2.palette import dark_color, dark_text_color
|
from calibre.gui2.palette import dark_color, dark_link_color, dark_text_color
|
||||||
from calibre.gui2.tweak_book import TOP, actions, current_container, editors, tprefs
|
from calibre.gui2.tweak_book import TOP, actions, current_container, editors, tprefs
|
||||||
from calibre.gui2.tweak_book.file_list import OpenWithHandler
|
from calibre.gui2.tweak_book.file_list import OpenWithHandler
|
||||||
from calibre.gui2.viewer.web_view import handle_mathjax_request, send_reply
|
from calibre.gui2.viewer.web_view import handle_mathjax_request, send_reply
|
||||||
@ -251,30 +251,35 @@ def create_profile():
|
|||||||
create_script('csscolorparser.js', cparser),
|
create_script('csscolorparser.js', cparser),
|
||||||
create_script('editor.js', js),
|
create_script('editor.js', js),
|
||||||
create_script('dark-mode.js', '''
|
create_script('dark-mode.js', '''
|
||||||
(function() {if (%s) {
|
(function() {
|
||||||
var dark_bg = "%s", dark_fg = "%s", css = %s;
|
var settings = JSON.parse(navigator.userAgent.split('|')[1]);
|
||||||
|
var dark_css = CSS;
|
||||||
|
|
||||||
|
function apply_body_colors(event) {
|
||||||
|
if (document.documentElement) {
|
||||||
|
if (settings.bg) document.documentElement.style.backgroundColor = settings.bg;
|
||||||
|
if (settings.fg) document.documentElement.style.color = settings.fg;
|
||||||
|
}
|
||||||
|
if (document.body) {
|
||||||
|
if (settings.bg) document.body.style.backgroundColor = settings.bg;
|
||||||
|
if (settings.fg) document.body.style.color = settings.fg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function apply_css() {
|
function apply_css() {
|
||||||
|
var css = '';
|
||||||
|
if (settings.link) css += 'html > body :link, html > body :link * { color: ' + settings.link + ' !important; }';
|
||||||
|
if (settings.is_dark_theme) { css += dark_css; }
|
||||||
var style = document.createElement('style');
|
var style = document.createElement('style');
|
||||||
style.textContent = css;
|
style.textContent = css;
|
||||||
document.documentElement.appendChild(style);
|
document.documentElement.appendChild(style);
|
||||||
|
apply_body_colors();
|
||||||
}
|
}
|
||||||
|
|
||||||
function apply_dark_mode(event) {
|
apply_body_colors();
|
||||||
if (document.documentElement) {
|
|
||||||
document.documentElement.style.backgroundColor = dark_bg;
|
|
||||||
document.documentElement.style.color = dark_fg;
|
|
||||||
}
|
|
||||||
if (document.body) {
|
|
||||||
document.body.style.backgroundColor = dark_bg;
|
|
||||||
document.body.style.color = dark_fg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apply_dark_mode();
|
|
||||||
document.addEventListener("DOMContentLoaded", apply_css);
|
document.addEventListener("DOMContentLoaded", apply_css);
|
||||||
document.addEventListener("DOMContentLoaded", apply_dark_mode);
|
})();
|
||||||
} })();
|
'''.replace('CSS', json.dumps(dark_mode_css), 1),
|
||||||
''' % (
|
|
||||||
'true' if is_dark_theme() else 'false', dark_color.name(), dark_text_color.name(), json.dumps(dark_mode_css)),
|
|
||||||
injection_point=QWebEngineScript.DocumentCreation)
|
injection_point=QWebEngineScript.DocumentCreation)
|
||||||
)
|
)
|
||||||
url_handler = UrlSchemeHandler(ans)
|
url_handler = UrlSchemeHandler(ans)
|
||||||
@ -392,10 +397,37 @@ class WebView(RestartingWebEngineView, OpenWithHandler):
|
|||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
return self._size_hint
|
return self._size_hint
|
||||||
|
|
||||||
|
def update_settings(self):
|
||||||
|
dark = is_dark_theme()
|
||||||
|
|
||||||
|
def get_color(name, dark_val):
|
||||||
|
ans = tprefs[name]
|
||||||
|
if ans == 'auto' and dark:
|
||||||
|
ans = dark_val.name()
|
||||||
|
if ans in ('auto', 'unset'):
|
||||||
|
return None
|
||||||
|
return ans
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
'is_dark_theme': dark,
|
||||||
|
'bg': get_color('preview_background', dark_color),
|
||||||
|
'fg': get_color('preview_foreground', dark_text_color),
|
||||||
|
'link': get_color('preview_link_color', dark_link_color),
|
||||||
|
}
|
||||||
|
p = self._page.profile()
|
||||||
|
ua = p.httpUserAgent().split('|')[0] + '|' + json.dumps(settings)
|
||||||
|
p.setHttpUserAgent(ua)
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
|
self.update_settings()
|
||||||
self.pageAction(QWebEnginePage.ReloadAndBypassCache).trigger()
|
self.pageAction(QWebEnginePage.ReloadAndBypassCache).trigger()
|
||||||
|
|
||||||
|
def set_url(self, qurl):
|
||||||
|
self.update_settings()
|
||||||
|
RestartingWebEngineView.setUrl(self, qurl)
|
||||||
|
|
||||||
def clear(self):
|
def clear(self):
|
||||||
|
self.update_settings()
|
||||||
self.setHtml(_(
|
self.setHtml(_(
|
||||||
'''
|
'''
|
||||||
<h3>Live preview</h3>
|
<h3>Live preview</h3>
|
||||||
@ -612,7 +644,7 @@ class Preview(QWidget):
|
|||||||
self.current_name = name
|
self.current_name = name
|
||||||
self.report_worker_launch_error()
|
self.report_worker_launch_error()
|
||||||
parse_worker.add_request(name)
|
parse_worker.add_request(name)
|
||||||
self.view.setUrl(self.name_to_qurl())
|
self.view.set_url(self.name_to_qurl())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def refresh(self):
|
def refresh(self):
|
||||||
@ -627,7 +659,7 @@ class Preview(QWidget):
|
|||||||
self.refresh_starting.emit()
|
self.refresh_starting.emit()
|
||||||
if current_url != self.view.url():
|
if current_url != self.view.url():
|
||||||
# The container was changed
|
# The container was changed
|
||||||
self.view.setUrl(current_url)
|
self.view.set_url(current_url)
|
||||||
else:
|
else:
|
||||||
self.view.refresh()
|
self.view.refresh()
|
||||||
self.refreshed.emit()
|
self.refreshed.emit()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user