mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Start work on porting live css
This commit is contained in:
parent
8aa52dbcba
commit
4eb75032db
@ -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']
|
||||||
|
@ -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'])
|
||||||
|
@ -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(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user