Start work on porting live css

This commit is contained in:
Kovid Goyal 2018-07-29 21:18:12 +05:30
parent 8aa52dbcba
commit 4eb75032db
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 54 additions and 57 deletions

View File

@ -5,8 +5,6 @@ from __future__ import absolute_import, division, print_function, unicode_litera
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
import json
from PyQt5.Qt import ( from PyQt5.Qt import (
QWidget, QTimer, QStackedLayout, QLabel, QScrollArea, QVBoxLayout, QWidget, QTimer, QStackedLayout, QLabel, QScrollArea, QVBoxLayout,
QPainter, Qt, QPalette, QRect, QSize, QSizePolicy, pyqtSignal, QPainter, Qt, QPalette, QRect, QSize, QSizePolicy, pyqtSignal,
@ -382,6 +380,7 @@ class LiveCSS(QWidget):
def __init__(self, preview, parent=None): def __init__(self, preview, parent=None):
QWidget.__init__(self, parent) QWidget.__init__(self, parent)
self.preview = preview self.preview = preview
preview.live_css_data.connect(self.got_live_css_data)
self.preview_is_refreshing = False self.preview_is_refreshing = False
self.refresh_needed = False self.refresh_needed = False
preview.refresh_starting.connect(self.preview_refresh_starting) preview.refresh_starting.connect(self.preview_refresh_starting)
@ -417,8 +416,6 @@ class LiveCSS(QWidget):
def preview_refreshed(self): def preview_refreshed(self):
self.preview_is_refreshing = False self.preview_is_refreshing = False
# We must let the event loop run otherwise the webview will return
# stale data in read_data()
self.refresh_needed = True self.refresh_needed = True
self.start_update_timer() self.start_update_timer()
@ -448,53 +445,46 @@ class LiveCSS(QWidget):
if sourceline is None: if sourceline is None:
self.clear() self.clear()
else: else:
self.preview.request_live_css_data(editor_name, sourceline, tags)
def got_live_css_data(self, result):
maximum_specificities = {}
for node in result['nodes']:
is_ancestor = node['is_ancestor']
for rule in node['css']:
self.process_rule(rule, is_ancestor, maximum_specificities)
for node in result['nodes']:
for rule in node['css']:
for prop in rule['properties']:
if prop.specificity < maximum_specificities[prop.name]:
prop.is_overriden = True
self.display_received_live_css_data(result)
def display_received_live_css_data(self, data):
editor_name = data['editor_name']
sourceline = data['sourceline']
tags = data['tags']
if data is None or len(data['computed_css']) < 1:
if editor_name == self.current_name and (editor_name, sourceline, tags) == self.now_showing:
# Try again in a little while in case there was a transient
# error in the web view
self.start_update_timer()
return
if self.now_showing == (None, None, None) or self.now_showing[0] != self.current_name:
self.clear()
return
# Try to refresh the data for the currently shown tag instead
# of clearing
editor_name, sourceline, tags = self.now_showing
data = self.read_data(sourceline, tags) data = self.read_data(sourceline, tags)
if data is None or len(data['computed_css']) < 1: if data is None or len(data['computed_css']) < 1:
if editor_name == self.current_name and (editor_name, sourceline, tags) == self.now_showing: self.clear()
# Try again in a little while in case there was a transient return
# error in the web view self.now_showing = (editor_name, sourceline, tags)
self.start_update_timer() data['html_name'] = editor_name
return self.box.show_data(data)
if self.now_showing == (None, None, None) or self.now_showing[0] != self.current_name: self.refresh_needed = False
self.clear() self.stack.setCurrentIndex(1)
return
# Try to refresh the data for the currently shown tag instead
# of clearing
editor_name, sourceline, tags = self.now_showing
data = self.read_data(sourceline, tags)
if data is None or len(data['computed_css']) < 1:
self.clear()
return
self.now_showing = (editor_name, sourceline, tags)
data['html_name'] = editor_name
self.box.show_data(data)
self.refresh_needed = False
self.stack.setCurrentIndex(1)
def read_data(self, sourceline, tags):
return None # TODO: Implement this
mf = self.preview.view.page().mainFrame()
tags = [x.lower() for x in tags]
result = unicode_type(mf.evaluateJavaScript(
'window.calibre_preview_integration.live_css(%s, %s)' % (
json.dumps(sourceline), json.dumps(tags))) or '')
try:
result = json.loads(result)
except ValueError:
result = None
if result is not None:
maximum_specificities = {}
for node in result['nodes']:
is_ancestor = node['is_ancestor']
for rule in node['css']:
self.process_rule(rule, is_ancestor, maximum_specificities)
for node in result['nodes']:
for rule in node['css']:
for prop in rule['properties']:
if prop.specificity < maximum_specificities[prop.name]:
prop.is_overriden = True
return result
def process_rule(self, rule, is_ancestor, maximum_specificities): def process_rule(self, rule, is_ancestor, maximum_specificities):
selector = rule['selector'] selector = rule['selector']

View File

@ -280,13 +280,12 @@ class PreviewBridge(Bridge):
request_sync = from_js(object, object, object) request_sync = from_js(object, object, object)
request_split = from_js(object, object) request_split = from_js(object, object)
live_css_data = from_js(object)
go_to_sourceline_address = to_js() go_to_sourceline_address = to_js()
go_to_anchor = to_js() go_to_anchor = to_js()
set_split_mode = to_js() set_split_mode = to_js()
live_css = to_js()
def __init__(self, parent=None):
Bridge.__init__(self, parent)
class WebPage(QWebEnginePage): class WebPage(QWebEnginePage):
@ -423,6 +422,7 @@ class Preview(QWidget):
link_clicked = pyqtSignal(object, object) link_clicked = pyqtSignal(object, object)
refresh_starting = pyqtSignal() refresh_starting = pyqtSignal()
refreshed = pyqtSignal() refreshed = pyqtSignal()
live_css_data = pyqtSignal(object)
render_process_restarted = pyqtSignal() render_process_restarted = pyqtSignal()
def __init__(self, parent=None): def __init__(self, parent=None):
@ -433,6 +433,7 @@ class Preview(QWidget):
self.view = WebView(self) self.view = WebView(self)
self.view._page.bridge.request_sync.connect(self.request_sync) self.view._page.bridge.request_sync.connect(self.request_sync)
self.view._page.bridge.request_split.connect(self.request_split) 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.loadFinished.connect(self.load_finished) self.view._page.loadFinished.connect(self.load_finished)
self.view.render_process_restarted.connect(self.render_process_restarted) self.view.render_process_restarted.connect(self.render_process_restarted)
self.pending_go_to_anchor = None self.pending_go_to_anchor = None
@ -645,6 +646,9 @@ class Preview(QWidget):
else: else:
self.stop_split() self.stop_split()
def request_live_css_data(self, editor_name, sourceline, tags):
self.view._page.bridge.live_css(editor_name, sourceline, tags)
def apply_settings(self): def apply_settings(self):
s = self.view.settings() s = self.view.settings()
s.setFontSize(s.DefaultFontSize, tprefs['preview_base_font_size']) s.setFontSize(s.DefaultFontSize, tprefs['preview_base_font_size'])

View File

@ -336,19 +336,21 @@ def go_to_anchor(anchor):
address = get_sourceline_address(elem) address = get_sourceline_address(elem)
to_python.request_sync('', '', address) to_python.request_sync('', '', address)
def live_css(sourceline, tags): @from_python
def live_css(editor_name, sourceline, tags):
all_properties = {}
ans = {'nodes':v'[]', 'computed_css':all_properties, 'editor_name': editor_name, 'sourceline': sourceline, 'tags': tags}
target = None target = None
i = 0 i = 0
for node in document.querySelectorAll(f'[data-lnum="{sourceline}"]'): for node in document.querySelectorAll(f'[data-lnum="{sourceline}"]'):
tn = node.tagName.toLowerCase() if node.tagName else '' tn = node.tagName.toLowerCase() if node.tagName else ''
if tn is not tags[i]: if tn is not tags[i]:
return JSON.stringify(None) to_python.live_css_data(ans)
return
i += 1 i += 1
target = node target = node
if i >= tags.length: if i >= tags.length:
break break
all_properties = {}
ans = {'nodes':v'[]', 'computed_css':all_properties}
is_ancestor = False is_ancestor = False
while target and target.ownerDocument: while target and target.ownerDocument:
css = get_matched_css(target, is_ancestor, all_properties) css = get_matched_css(target, is_ancestor, all_properties)
@ -362,7 +364,8 @@ def live_css(sourceline, tags):
}) })
target = target.parentNode target = target.parentNode
is_ancestor = True is_ancestor = True
return JSON.stringify(ans) to_python.live_css_data(ans)
document.body.addEventListener('click', onclick, True) document.body.addEventListener('click', onclick, True)
document.documentElement.appendChild(E.style( document.documentElement.appendChild(E.style(