%PDF- %PDF-
| Direktori : /proc/thread-self/root/usr/lib/calibre/calibre/gui2/tweak_book/ |
| Current File : //proc/thread-self/root/usr/lib/calibre/calibre/gui2/tweak_book/preview.py |
#!/usr/bin/env python3
# License: GPLv3 Copyright: 2015, Kovid Goyal <kovid at kovidgoyal.net>
import json
import time
from collections import defaultdict
from functools import partial
from qt.core import (
QAction, QApplication, QByteArray, QHBoxLayout, QIcon, QLabel, QMenu, QSize,
QSizePolicy, QStackedLayout, Qt, QTimer, QToolBar, QUrl, QVBoxLayout, QWidget,
pyqtSignal
)
from qt.webengine import (
QWebEngineContextMenuData, QWebEnginePage, QWebEngineProfile, QWebEngineScript,
QWebEngineSettings, QWebEngineUrlRequestJob, QWebEngineUrlSchemeHandler,
QWebEngineView
)
from threading import Thread
from calibre import prints
from calibre.constants import (
FAKE_HOST, FAKE_PROTOCOL, __version__, is_running_from_develop, ismacos,
iswindows
)
from calibre.ebooks.oeb.base import OEB_DOCS, XHTML_MIME, serialize
from calibre.ebooks.oeb.polish.parsing import parse
from calibre.gui2 import (
NO_URL_FORMATTING, QT_HIDDEN_CLEAR_ACTION, error_dialog, is_dark_theme,
safe_open_url
)
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.file_list import OpenWithHandler
from calibre.gui2.viewer.web_view import handle_mathjax_request, send_reply
from calibre.gui2.webengine import (
Bridge, RestartingWebEngineView, create_script, from_js, insert_scripts,
secure_webengine, to_js
)
from calibre.gui2.widgets2 import HistoryLineEdit2
from calibre.utils.ipc.simple_worker import offload_worker
from polyglot.builtins import iteritems
from polyglot.queue import Empty, Queue
from polyglot.urllib import urlparse
shutdown = object()
def get_data(name):
'Get the data for name. Returns a unicode string if name is a text document/stylesheet'
if name in editors:
return editors[name].get_raw_data()
return current_container().raw_data(name)
# Parsing of html to add linenumbers {{{
def parse_html(raw):
root = parse(raw, decoder=lambda x:x.decode('utf-8'), line_numbers=True, linenumber_attribute='data-lnum')
ans = serialize(root, 'text/html')
if not isinstance(ans, bytes):
ans = ans.encode('utf-8')
return ans
class ParseItem:
__slots__ = ('name', 'length', 'fingerprint', 'parsing_done', 'parsed_data')
def __init__(self, name):
self.name = name
self.length, self.fingerprint = 0, None
self.parsed_data = None
self.parsing_done = False
def __repr__(self):
return 'ParsedItem(name={!r}, length={!r}, fingerprint={!r}, parsing_done={!r}, parsed_data_is_None={!r})'.format(
self.name, self.length, self.fingerprint, self.parsing_done, self.parsed_data is None)
class ParseWorker(Thread):
daemon = True
SLEEP_TIME = 1
def __init__(self):
Thread.__init__(self)
self.requests = Queue()
self.request_count = 0
self.parse_items = {}
self.launch_error = None
def run(self):
mod, func = 'calibre.gui2.tweak_book.preview', 'parse_html'
try:
# Connect to the worker and send a dummy job to initialize it
self.worker = offload_worker(priority='low')
self.worker(mod, func, '<p></p>')
except:
import traceback
traceback.print_exc()
self.launch_error = traceback.format_exc()
return
while True:
time.sleep(self.SLEEP_TIME)
x = self.requests.get()
requests = [x]
while True:
try:
requests.append(self.requests.get_nowait())
except Empty:
break
if shutdown in requests:
self.worker.shutdown()
break
request = sorted(requests, reverse=True)[0]
del requests
pi, data = request[1:]
try:
res = self.worker(mod, func, data)
except:
import traceback
traceback.print_exc()
else:
pi.parsing_done = True
parsed_data = res['result']
if res['tb']:
prints("Parser error:")
prints(res['tb'])
else:
pi.parsed_data = parsed_data
def add_request(self, name):
data = get_data(name)
ldata, hdata = len(data), hash(data)
pi = self.parse_items.get(name, None)
if pi is None:
self.parse_items[name] = pi = ParseItem(name)
else:
if pi.parsing_done and pi.length == ldata and pi.fingerprint == hdata:
return
pi.parsed_data = None
pi.parsing_done = False
pi.length, pi.fingerprint = ldata, hdata
self.requests.put((self.request_count, pi, data))
self.request_count += 1
def shutdown(self):
self.requests.put(shutdown)
def get_data(self, name):
return getattr(self.parse_items.get(name, None), 'parsed_data', None)
def clear(self):
self.parse_items.clear()
def is_alive(self):
return Thread.is_alive(self) or (hasattr(self, 'worker') and self.worker.is_alive())
parse_worker = ParseWorker()
# }}}
# Override network access to load data "live" from the editors {{{
class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
def __init__(self, parent=None):
QWebEngineUrlSchemeHandler.__init__(self, parent)
self.requests = defaultdict(list)
def requestStarted(self, rq):
if bytes(rq.requestMethod()) != b'GET':
rq.fail(QWebEngineUrlRequestJob.Error.RequestDenied)
return
url = rq.requestUrl()
if url.host() != FAKE_HOST or url.scheme() != FAKE_PROTOCOL:
rq.fail(QWebEngineUrlRequestJob.Error.UrlNotFound)
return
name = url.path()[1:]
try:
if name.startswith('calibre_internal-mathjax/'):
handle_mathjax_request(rq, name.partition('-')[-1])
return
c = current_container()
if not c.has_name(name):
rq.fail(QWebEngineUrlRequestJob.Error.UrlNotFound)
return
mime_type = c.mime_map.get(name, 'application/octet-stream')
if mime_type in OEB_DOCS:
mime_type = XHTML_MIME
self.requests[name].append((mime_type, rq))
QTimer.singleShot(0, self.check_for_parse)
else:
data = get_data(name)
if isinstance(data, str):
data = data.encode('utf-8')
mime_type = {
# Prevent warning in console about mimetype of fonts
'application/vnd.ms-opentype':'application/x-font-ttf',
'application/x-font-truetype':'application/x-font-ttf',
'application/font-sfnt': 'application/x-font-ttf',
}.get(mime_type, mime_type)
send_reply(rq, mime_type, data)
except Exception:
import traceback
traceback.print_exc()
rq.fail(QWebEngineUrlRequestJob.Error.RequestFailed)
def check_for_parse(self):
remove = []
for name, requests in iteritems(self.requests):
data = parse_worker.get_data(name)
if data is not None:
if not isinstance(data, bytes):
data = data.encode('utf-8')
for mime_type, rq in requests:
send_reply(rq, mime_type, data)
remove.append(name)
for name in remove:
del self.requests[name]
if self.requests:
return QTimer.singleShot(10, self.check_for_parse)
# }}}
def uniq(vals):
''' Remove all duplicates from vals, while preserving order. '''
vals = vals or ()
seen = set()
seen_add = seen.add
return tuple(x for x in vals if x not in seen and not seen_add(x))
def get_editor_settings(tprefs):
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
return {
'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),
'os': 'windows' if iswindows else ('macos' if ismacos else 'linux'),
}
def create_dark_mode_script():
dark_mode_css = P('dark_mode.css', data=True, allow_user_override=False).decode('utf-8')
return create_script('dark-mode.js', '''
(function() {
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() {
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');
style.textContent = css;
document.documentElement.appendChild(style);
apply_body_colors();
}
apply_body_colors();
document.addEventListener("DOMContentLoaded", apply_css);
})();
'''.replace('CSS', json.dumps(dark_mode_css), 1),
injection_point=QWebEngineScript.InjectionPoint.DocumentCreation)
def create_profile():
ans = getattr(create_profile, 'ans', None)
if ans is None:
ans = QWebEngineProfile(QApplication.instance())
ua = 'calibre-editor-preview ' + __version__
ans.setHttpUserAgent(ua)
if is_running_from_develop:
from calibre.utils.rapydscript import compile_editor
compile_editor()
js = P('editor.js', data=True, allow_user_override=False)
cparser = P('csscolorparser.js', data=True, allow_user_override=False)
insert_scripts(ans,
create_script('csscolorparser.js', cparser),
create_script('editor.js', js),
create_dark_mode_script(),
)
url_handler = UrlSchemeHandler(ans)
ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler)
s = ans.settings()
s.setDefaultTextEncoding('utf-8')
s.setAttribute(QWebEngineSettings.WebAttribute.FullScreenSupportEnabled, False)
s.setAttribute(QWebEngineSettings.WebAttribute.LinksIncludedInFocusChain, False)
create_profile.ans = ans
return ans
class PreviewBridge(Bridge):
request_sync = from_js(object, object, object)
request_split = from_js(object, object)
live_css_data = from_js(object)
go_to_sourceline_address = to_js()
go_to_anchor = to_js()
set_split_mode = to_js()
live_css = to_js()
class WebPage(QWebEnginePage):
def __init__(self, parent):
QWebEnginePage.__init__(self, create_profile(), parent)
secure_webengine(self, for_viewer=True)
self.bridge = PreviewBridge(self)
def javaScriptConsoleMessage(self, level, msg, linenumber, source_id):
prints(f'{source_id}:{linenumber}: {msg}')
def acceptNavigationRequest(self, url, req_type, is_main_frame):
if req_type in (QWebEnginePage.NavigationType.NavigationTypeReload, QWebEnginePage.NavigationType.NavigationTypeBackForward):
return True
if url.scheme() in (FAKE_PROTOCOL, 'data'):
return True
if url.scheme() in ('http', 'https') and req_type == QWebEnginePage.NavigationType.NavigationTypeLinkClicked:
safe_open_url(url)
prints('Blocking navigation request to:', url.toString())
return False
def go_to_anchor(self, anchor):
if anchor is TOP:
anchor = ''
self.bridge.go_to_anchor.emit(anchor or '')
def runjs(self, src, callback=None):
if callback is None:
self.runJavaScript(src, QWebEngineScript.ScriptWorldId.ApplicationWorld)
else:
self.runJavaScript(src, QWebEngineScript.ScriptWorldId.ApplicationWorld, callback)
def go_to_sourceline_address(self, sourceline_address):
if self.bridge.ready:
lnum, tags = sourceline_address
if lnum is None:
return
tags = [x.lower() for x in tags]
self.bridge.go_to_sourceline_address.emit(lnum, tags, tprefs['preview_sync_context'])
def split_mode(self, enabled):
if self.bridge.ready:
self.bridge.set_split_mode.emit(1 if enabled else 0)
class Inspector(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent=parent)
self.view_to_debug = parent
self.view = None
self.layout = QHBoxLayout(self)
self.layout.setContentsMargins(0, 0, 0, 0)
def connect_to_dock(self):
ac = actions['inspector-dock']
ac.toggled.connect(self.visibility_changed)
if ac.isChecked():
self.visibility_changed(True)
def visibility_changed(self, visible):
if visible and self.view is None:
self.view = QWebEngineView(self.view_to_debug)
self.view_to_debug.page().setDevToolsPage(self.view.page())
self.layout.addWidget(self.view)
def sizeHint(self):
return QSize(1280, 600)
class WebView(RestartingWebEngineView, OpenWithHandler):
def __init__(self, parent=None):
RestartingWebEngineView.__init__(self, parent)
self.inspector = Inspector(self)
w = self.screen().availableSize().width()
self._size_hint = QSize(int(w/3), int(w/2))
self._page = WebPage(self)
self.setPage(self._page)
self.clear()
self.setAcceptDrops(False)
self.dead_renderer_error_shown = False
self.render_process_failed.connect(self.render_process_died)
def render_process_died(self):
if self.dead_renderer_error_shown:
return
self.dead_renderer_error_shown = True
error_dialog(self, _('Render process crashed'), _(
'The Qt WebEngine Render process has crashed so Preview/Live CSS will not work.'
' You should try restarting the editor.')
, show=True)
def sizeHint(self):
return self._size_hint
def update_settings(self):
settings = get_editor_settings(tprefs)
p = self._page.profile()
ua = p.httpUserAgent().split('|')[0] + '|' + json.dumps(settings)
p.setHttpUserAgent(ua)
def refresh(self):
self.update_settings()
self.pageAction(QWebEnginePage.WebAction.ReloadAndBypassCache).trigger()
def set_url(self, qurl):
self.update_settings()
RestartingWebEngineView.setUrl(self, qurl)
def clear(self):
self.update_settings()
self.setHtml(_(
'''
<h3>Live preview</h3>
<p>Here you will see a live preview of the HTML file you are currently editing.
The preview will update automatically as you make changes.
<p style="font-size:x-small; color: gray">Note that this is a quick preview
only, it is not intended to simulate an actual e-book reader. Some
aspects of your e-book will not work, such as page breaks and page margins.
'''))
def inspect(self):
self.inspector.parent().show()
self.inspector.parent().raise_()
self.pageAction(QWebEnginePage.WebAction.InspectElement).trigger()
def contextMenuEvent(self, ev):
menu = QMenu(self)
data = self._page.contextMenuData()
url = data.linkUrl()
url = str(url.toString(NO_URL_FORMATTING)).strip()
text = data.selectedText()
if text:
ca = self.pageAction(QWebEnginePage.WebAction.Copy)
if ca.isEnabled():
menu.addAction(ca)
menu.addAction(actions['reload-preview'])
menu.addAction(QIcon(I('debug.png')), _('Inspect element'), self.inspect)
if url.partition(':')[0].lower() in {'http', 'https'}:
menu.addAction(_('Open link'), partial(safe_open_url, data.linkUrl()))
if QWebEngineContextMenuData.MediaType.MediaTypeImage <= data.mediaType() <= QWebEngineContextMenuData.MediaType.MediaTypeFile:
url = data.mediaUrl()
if url.scheme() == FAKE_PROTOCOL:
href = url.path().lstrip('/')
if href:
c = current_container()
resource_name = c.href_to_name(href)
if resource_name and c.exists(resource_name) and resource_name not in c.names_that_must_not_be_changed:
self.add_open_with_actions(menu, resource_name)
if data.mediaType() == QWebEngineContextMenuData.MediaType.MediaTypeImage:
mime = c.mime_map[resource_name]
if mime.startswith('image/'):
menu.addAction(_('Edit %s') % resource_name, partial(self.edit_image, resource_name))
menu.exec(ev.globalPos())
def open_with(self, file_name, fmt, entry):
self.parent().open_file_with.emit(file_name, fmt, entry)
def edit_image(self, resource_name):
self.parent().edit_file.emit(resource_name)
class Preview(QWidget):
sync_requested = pyqtSignal(object, object)
split_requested = pyqtSignal(object, object, object)
split_start_requested = pyqtSignal()
link_clicked = pyqtSignal(object, object)
refresh_starting = pyqtSignal()
refreshed = pyqtSignal()
live_css_data = pyqtSignal(object)
render_process_restarted = pyqtSignal()
open_file_with = pyqtSignal(object, object, object)
edit_file = pyqtSignal(object)
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.l = l = QVBoxLayout()
self.setLayout(l)
l.setContentsMargins(0, 0, 0, 0)
self.stack = QStackedLayout(l)
self.stack.setStackingMode(QStackedLayout.StackingMode.StackAll)
self.current_sync_retry_count = 0
self.view = WebView(self)
self.view._page.bridge.request_sync.connect(self.request_sync)
self.view._page.bridge.request_split.connect(self.request_split)
self.view._page.bridge.live_css_data.connect(self.live_css_data)
self.view._page.bridge.bridge_ready.connect(self.on_bridge_ready)
self.view._page.loadFinished.connect(self.load_finished)
self.view._page.loadStarted.connect(self.load_started)
self.view.render_process_restarted.connect(self.render_process_restarted)
self.pending_go_to_anchor = None
self.inspector = self.view.inspector
self.stack.addWidget(self.view)
self.cover = c = QLabel(_('Loading preview, please wait...'))
c.setWordWrap(True)
c.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
c.setStyleSheet('QLabel { background-color: palette(window); }')
c.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.stack.addWidget(self.cover)
self.stack.setCurrentIndex(self.stack.indexOf(self.cover))
self.bar = QToolBar(self)
l.addWidget(self.bar)
ac = actions['auto-reload-preview']
ac.setCheckable(True)
ac.setChecked(True)
ac.toggled.connect(self.auto_reload_toggled)
self.auto_reload_toggled(ac.isChecked())
self.bar.addAction(ac)
ac = actions['sync-preview-to-editor']
ac.setCheckable(True)
ac.setChecked(True)
ac.toggled.connect(self.sync_toggled)
self.sync_toggled(ac.isChecked())
self.bar.addAction(ac)
self.bar.addSeparator()
ac = actions['split-in-preview']
ac.setCheckable(True)
ac.setChecked(False)
ac.toggled.connect(self.split_toggled)
self.split_toggled(ac.isChecked())
self.bar.addAction(ac)
ac = actions['reload-preview']
ac.triggered.connect(self.refresh)
self.bar.addAction(ac)
actions['preview-dock'].toggled.connect(self.visibility_changed)
self.current_name = None
self.last_sync_request = None
self.refresh_timer = QTimer(self)
self.refresh_timer.timeout.connect(self.refresh)
parse_worker.start()
self.current_sync_request = None
self.search = HistoryLineEdit2(self)
self.search.setClearButtonEnabled(True)
ac = self.search.findChild(QAction, QT_HIDDEN_CLEAR_ACTION)
if ac is not None:
ac.triggered.connect(self.clear_clicked)
self.search.initialize('tweak_book_preview_search')
self.search.setPlaceholderText(_('Search in preview'))
self.search.returnPressed.connect(self.find_next)
self.bar.addSeparator()
self.bar.addWidget(self.search)
for d in ('next', 'prev'):
ac = actions['find-%s-preview' % d]
ac.triggered.connect(getattr(self, 'find_' + d))
self.bar.addAction(ac)
def clear_clicked(self):
self.view._page.findText('')
def find(self, direction):
text = str(self.search.text())
self.view._page.findText(text, (
QWebEnginePage.FindFlag.FindBackward if direction == 'prev' else QWebEnginePage.FindFlags(0)))
def find_next(self):
self.find('next')
def find_prev(self):
self.find('prev')
def go_to_anchor(self, anchor):
self.view._page.go_to_anchor(anchor)
def request_sync(self, tagname, href, lnum):
if self.current_name:
c = current_container()
if tagname == 'a' and href:
if href and href.startswith('#'):
name = self.current_name
else:
name = c.href_to_name(href, self.current_name) if href else None
if name == self.current_name:
return self.go_to_anchor(urlparse(href).fragment)
if name and c.exists(name) and c.mime_map[name] in OEB_DOCS:
return self.link_clicked.emit(name, urlparse(href).fragment or TOP)
self.sync_requested.emit(self.current_name, lnum)
def request_split(self, loc, totals):
actions['split-in-preview'].setChecked(False)
if not loc or not totals:
return error_dialog(self, _('Invalid location'),
_('Cannot split on the body tag'), show=True)
if self.current_name:
self.split_requested.emit(self.current_name, loc, totals)
@property
def bridge_ready(self):
return self.view._page.bridge.ready
def sync_to_editor(self, name, sourceline_address):
self.current_sync_request = (name, sourceline_address)
self.current_sync_retry_count = 0
QTimer.singleShot(100, self._sync_to_editor)
def _sync_to_editor(self):
if not actions['sync-preview-to-editor'].isChecked() or self.current_sync_retry_count >= 3000 or self.current_sync_request is None:
return
if self.refresh_timer.isActive() or not self.bridge_ready or self.current_sync_request[0] != self.current_name:
self.current_sync_retry_count += 1
return QTimer.singleShot(100, self._sync_to_editor)
sourceline_address = self.current_sync_request[1]
self.current_sync_request = None
self.current_sync_retry_count = 0
self.view._page.go_to_sourceline_address(sourceline_address)
def report_worker_launch_error(self):
if parse_worker.launch_error is not None:
tb, parse_worker.launch_error = parse_worker.launch_error, None
error_dialog(self, _('Failed to launch worker'), _(
'Failed to launch the worker process used for rendering the preview'), det_msg=tb, show=True)
def name_to_qurl(self, name=None):
name = name or self.current_name
qurl = QUrl()
qurl.setScheme(FAKE_PROTOCOL), qurl.setAuthority(FAKE_HOST), qurl.setPath('/' + name)
return qurl
def show(self, name):
if name != self.current_name:
self.refresh_timer.stop()
self.current_name = name
self.report_worker_launch_error()
parse_worker.add_request(name)
self.view.set_url(self.name_to_qurl())
return True
def refresh(self):
if self.current_name:
self.refresh_timer.stop()
# This will check if the current html has changed in its editor,
# and re-parse it if so
self.report_worker_launch_error()
parse_worker.add_request(self.current_name)
# Tell webkit to reload all html and associated resources
current_url = self.name_to_qurl()
self.refresh_starting.emit()
if current_url != self.view.url():
# The container was changed
self.view.set_url(current_url)
else:
self.view.refresh()
self.refreshed.emit()
def clear(self):
self.view.clear()
self.current_name = None
@property
def is_visible(self):
return actions['preview-dock'].isChecked()
@property
def live_css_is_visible(self):
try:
return actions['live-css-dock'].isChecked()
except KeyError:
return False
def start_refresh_timer(self):
if self.live_css_is_visible or (self.is_visible and actions['auto-reload-preview'].isChecked()):
self.refresh_timer.start(tprefs['preview_refresh_time'] * 1000)
def stop_refresh_timer(self):
self.refresh_timer.stop()
def auto_reload_toggled(self, checked):
if self.live_css_is_visible and not actions['auto-reload-preview'].isChecked():
actions['auto-reload-preview'].setChecked(True)
error_dialog(self, _('Cannot disable'), _(
'Auto reloading of the preview panel cannot be disabled while the'
' Live CSS panel is open.'), show=True)
actions['auto-reload-preview'].setToolTip(_(
'Auto reload preview when text changes in editor') if not checked else _(
'Disable auto reload of preview'))
def sync_toggled(self, checked):
actions['sync-preview-to-editor'].setToolTip(_(
'Disable syncing of preview position to editor position') if checked else _(
'Enable syncing of preview position to editor position'))
def visibility_changed(self, is_visible):
if is_visible:
self.refresh()
def split_toggled(self, checked):
actions['split-in-preview'].setToolTip('<p>' + (_(
'Abort file split') if checked else _(
'Split this file at a specified location.<p>After clicking this button, click'
' inside the preview panel above at the location you want the file to be split.')))
if checked:
self.split_start_requested.emit()
else:
self.view._page.split_mode(False)
def do_start_split(self):
self.view._page.split_mode(True)
def stop_split(self):
actions['split-in-preview'].setChecked(False)
def load_started(self):
self.stack.setCurrentIndex(self.stack.indexOf(self.cover))
def on_bridge_ready(self):
self.stack.setCurrentIndex(self.stack.indexOf(self.view))
def load_finished(self, ok):
self.stack.setCurrentIndex(self.stack.indexOf(self.view))
if self.pending_go_to_anchor:
self.view._page.go_to_anchor(self.pending_go_to_anchor)
self.pending_go_to_anchor = None
if actions['split-in-preview'].isChecked():
if ok:
self.do_start_split()
else:
self.stop_split()
def request_live_css_data(self, editor_name, sourceline, tags):
if self.view._page.bridge.ready:
self.view._page.bridge.live_css(editor_name, sourceline, tags)
def apply_settings(self):
s = self.view.settings()
s.setFontSize(QWebEngineSettings.FontSize.DefaultFontSize, int(tprefs['preview_base_font_size']))
s.setFontSize(QWebEngineSettings.FontSize.DefaultFixedFontSize, int(tprefs['preview_mono_font_size']))
s.setFontSize(QWebEngineSettings.FontSize.MinimumLogicalFontSize, int(tprefs['preview_minimum_font_size']))
s.setFontSize(QWebEngineSettings.FontSize.MinimumFontSize, int(tprefs['preview_minimum_font_size']))
sf, ssf, mf = tprefs['engine_preview_serif_family'], tprefs['engine_preview_sans_family'], tprefs['engine_preview_mono_family']
if sf:
s.setFontFamily(QWebEngineSettings.FontFamily.SerifFont, sf)
if ssf:
s.setFontFamily(QWebEngineSettings.FontFamily.SansSerifFont, ssf)
if mf:
s.setFontFamily(QWebEngineSettings.FontFamily.FixedFont, mf)
stdfnt = tprefs['preview_standard_font_family'] or 'serif'
stdfnt = {
'serif': QWebEngineSettings.FontFamily.SerifFont,
'sans': QWebEngineSettings.FontFamily.SansSerifFont,
'mono': QWebEngineSettings.FontFamily.FixedFont
}[stdfnt]
s.setFontFamily(QWebEngineSettings.FontFamily.StandardFont, s.fontFamily(stdfnt))