mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Live CSS: Indicate which CSS properties are overriden by higher priority rules
This commit is contained in:
parent
6786df58d1
commit
dec4720fb6
@ -533,8 +533,9 @@ where the style was defined, in case you wish to make any changes to the style
|
|||||||
rules. Style rules that apply directly to the tag, as well as rules that are
|
rules. Style rules that apply directly to the tag, as well as rules that are
|
||||||
inherited from parent tags are shown.
|
inherited from parent tags are shown.
|
||||||
|
|
||||||
The panel also shows you what the finally calculated styles for the tag are, so
|
The panel also shows you what the finally calculated styles for the tag are.
|
||||||
you can see which rules were applied and which were overridden.
|
Properties in the list that are superseded by higher priority rules are shown
|
||||||
|
with a line through them.
|
||||||
|
|
||||||
You can enable the Live CSS panel via :guilabel:`View->Live CSS`.
|
You can enable the Live CSS panel via :guilabel:`View->Live CSS`.
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ __copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
|
|
||||||
|
from cssselect import parse
|
||||||
from PyQt4.Qt import (
|
from PyQt4.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,
|
||||||
@ -82,14 +83,15 @@ class Heading(QWidget): # {{{
|
|||||||
|
|
||||||
class Cell(object): # {{{
|
class Cell(object): # {{{
|
||||||
|
|
||||||
__slots__ = ('rect', 'text', 'right_align', 'color_role', 'override_color', 'swatch')
|
__slots__ = ('rect', 'text', 'right_align', 'color_role', 'override_color', 'swatch', 'is_overriden')
|
||||||
|
|
||||||
SIDE_MARGIN = 5
|
SIDE_MARGIN = 5
|
||||||
FLAGS = Qt.AlignVCenter | Qt.TextSingleLine | Qt.TextIncludeTrailingSpaces
|
FLAGS = Qt.AlignVCenter | Qt.TextSingleLine | Qt.TextIncludeTrailingSpaces
|
||||||
|
|
||||||
def __init__(self, text, rect, right_align=False, color_role=QPalette.WindowText, swatch=None):
|
def __init__(self, text, rect, right_align=False, color_role=QPalette.WindowText, swatch=None, is_overriden=False):
|
||||||
self.rect, self.text = rect, text
|
self.rect, self.text = rect, text
|
||||||
self.right_align = right_align
|
self.right_align = right_align
|
||||||
|
self.is_overriden = is_overriden
|
||||||
self.color_role = color_role
|
self.color_role = color_role
|
||||||
self.override_color = None
|
self.override_color = None
|
||||||
self.swatch = swatch
|
self.swatch = swatch
|
||||||
@ -106,6 +108,10 @@ class Cell(object): # {{{
|
|||||||
if self.swatch is not None:
|
if self.swatch is not None:
|
||||||
r = QRect(br.right() + self.SIDE_MARGIN // 2, br.top() + 2, br.height() - 4, br.height() - 4)
|
r = QRect(br.right() + self.SIDE_MARGIN // 2, br.top() + 2, br.height() - 4, br.height() - 4)
|
||||||
painter.fillRect(r, self.swatch)
|
painter.fillRect(r, self.swatch)
|
||||||
|
br.setRight(r.right())
|
||||||
|
if self.is_overriden:
|
||||||
|
painter.setPen(palette.color(QPalette.WindowText))
|
||||||
|
painter.drawLine(br.left(), br.top() + br.height() // 2, br.right(), br.top() + br.height() // 2)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class Declaration(QWidget):
|
class Declaration(QWidget):
|
||||||
@ -144,14 +150,14 @@ class Declaration(QWidget):
|
|||||||
])
|
])
|
||||||
ypos += max(br1.height(), br2.height()) + 2 * line_spacing
|
ypos += max(br1.height(), br2.height()) + 2 * line_spacing
|
||||||
|
|
||||||
for (name, value, important, color) in self.data['properties']:
|
for prop in self.data['properties']:
|
||||||
text = name + ':\xa0'
|
text = prop.name + ':\xa0'
|
||||||
br1 = bounding_rect(text)
|
br1 = bounding_rect(text)
|
||||||
vtext = value + '\xa0' + ('!' if important else '') + important
|
vtext = prop.value + '\xa0' + ('!' if prop.important else '') + prop.important
|
||||||
br2 = bounding_rect(vtext)
|
br2 = bounding_rect(vtext)
|
||||||
self.rows.append([
|
self.rows.append([
|
||||||
Cell(text, QRect(side_margin, ypos, br1.width(), br1.height()), color_role=QPalette.LinkVisited),
|
Cell(text, QRect(side_margin, ypos, br1.width(), br1.height()), color_role=QPalette.LinkVisited, is_overriden=prop.is_overriden),
|
||||||
Cell(vtext, QRect(br1.right() + side_margin, ypos, br2.width(), br2.height()), swatch=color)
|
Cell(vtext, QRect(br1.right() + side_margin, ypos, br2.width(), br2.height()), swatch=prop.color, is_overriden=prop.is_overriden)
|
||||||
])
|
])
|
||||||
ypos += max(br1.height(), br2.height()) + line_spacing
|
ypos += max(br1.height(), br2.height()) + line_spacing
|
||||||
|
|
||||||
@ -270,7 +276,7 @@ class Box(QWidget):
|
|||||||
h.toggled.connect(self.heading_toggled)
|
h.toggled.connect(self.heading_toggled)
|
||||||
self.widgets.append(h), self.layout().addWidget(h)
|
self.widgets.append(h), self.layout().addWidget(h)
|
||||||
ccss = data['computed_css']
|
ccss = data['computed_css']
|
||||||
declaration = {'properties':[[k, ccss[k][0], '', ccss[k][1]] for k in sorted(ccss)]}
|
declaration = {'properties':[Property([k, ccss[k][0], '', ccss[k][1]]) for k in sorted(ccss)]}
|
||||||
d = Declaration(None, declaration, is_first=True, parent=self)
|
d = Declaration(None, declaration, is_first=True, parent=self)
|
||||||
self.widgets.append(d), self.layout().addWidget(d)
|
self.widgets.append(d), self.layout().addWidget(d)
|
||||||
|
|
||||||
@ -288,6 +294,19 @@ class Box(QWidget):
|
|||||||
w.do_layout()
|
w.do_layout()
|
||||||
w.updateGeometry()
|
w.updateGeometry()
|
||||||
|
|
||||||
|
class Property(object):
|
||||||
|
|
||||||
|
__slots__ = 'name', 'value', 'important', 'color', 'specificity', 'is_overriden'
|
||||||
|
|
||||||
|
def __init__(self, prop, specificity=()):
|
||||||
|
self.name, self.value, self.important, self.color = prop
|
||||||
|
self.specificity = tuple(specificity)
|
||||||
|
self.is_overriden = False
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Property name=%s value=%s important=%s color=%s specificity=%s is_overriden=%s>' % (
|
||||||
|
self.name, self.value, self.important, self.color, self.specificity, self.is_overriden)
|
||||||
|
|
||||||
class LiveCSS(QWidget):
|
class LiveCSS(QWidget):
|
||||||
|
|
||||||
goto_declaration = pyqtSignal(object)
|
goto_declaration = pyqtSignal(object)
|
||||||
@ -393,17 +412,49 @@ class LiveCSS(QWidget):
|
|||||||
json.dumps(sourceline), json.dumps(tags))).toString())
|
json.dumps(sourceline), json.dumps(tags))).toString())
|
||||||
result = json.loads(result)
|
result = json.loads(result)
|
||||||
if result is not None:
|
if result is not None:
|
||||||
|
maximum_specificities = {}
|
||||||
for node in result['nodes']:
|
for node in result['nodes']:
|
||||||
for item in node['css']:
|
is_ancestor = node['is_ancestor']
|
||||||
href = item['href']
|
for rule in node['css']:
|
||||||
if hasattr(href, 'startswith') and href.startswith('file://'):
|
self.process_rule(rule, is_ancestor, maximum_specificities)
|
||||||
href = href[len('file://'):]
|
for node in result['nodes']:
|
||||||
if iswindows and href.startswith('/'):
|
for rule in node['css']:
|
||||||
href = href[1:]
|
for prop in rule['properties']:
|
||||||
if href:
|
if prop.specificity < maximum_specificities[prop.name]:
|
||||||
item['href'] = current_container().abspath_to_name(href, root=self.preview.current_root)
|
prop.is_overriden = True
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def process_rule(self, rule, is_ancestor, maximum_specificities):
|
||||||
|
selector = rule['selector']
|
||||||
|
sheet_index = rule['sheet_index']
|
||||||
|
rule_address = rule['rule_address'] or ()
|
||||||
|
if selector is not None:
|
||||||
|
try:
|
||||||
|
specificity = [0] + list(parse(selector)[0].specificity())
|
||||||
|
except (AttributeError, TypeError):
|
||||||
|
specificity = [0, 0, 0, 0]
|
||||||
|
else: # style attribute
|
||||||
|
specificity = [1, 0, 0, 0]
|
||||||
|
specificity.extend((sheet_index, tuple(rule_address)))
|
||||||
|
ancestor_specificity = 0 if is_ancestor else 1
|
||||||
|
properties = []
|
||||||
|
for prop in rule['properties']:
|
||||||
|
important = 1 if prop[-1] == 'important' else 0
|
||||||
|
p = Property(prop, [ancestor_specificity] + [important] + specificity)
|
||||||
|
properties.append(p)
|
||||||
|
if p.specificity > maximum_specificities.get(p.name, (0,0,0,0,0,0)):
|
||||||
|
maximum_specificities[p.name] = p.specificity
|
||||||
|
rule['properties'] = properties
|
||||||
|
|
||||||
|
href = rule['href']
|
||||||
|
if hasattr(href, 'startswith') and href.startswith('file://'):
|
||||||
|
href = href[len('file://'):]
|
||||||
|
if iswindows and href.startswith('/'):
|
||||||
|
href = href[1:]
|
||||||
|
if href:
|
||||||
|
rule['href'] = current_container().abspath_to_name(href, root=self.preview.current_root)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_name(self):
|
def current_name(self):
|
||||||
return self.preview.current_name
|
return self.preview.current_name
|
||||||
|
Loading…
x
Reference in New Issue
Block a user