mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
Add support for CSS styles
This commit is contained in:
parent
75a05fe5ce
commit
bef92f2879
4 changed files with 73 additions and 2 deletions
|
@ -266,14 +266,23 @@ class Child(AstNode):
|
||||||
|
|
||||||
class ObjectContent(AstNode):
|
class ObjectContent(AstNode):
|
||||||
child_type = "object_content"
|
child_type = "object_content"
|
||||||
def __init__(self, properties=[], signals=[], children=[]):
|
def __init__(self, properties=[], signals=[], children=[], style=[]):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.properties = properties
|
self.properties = properties
|
||||||
self.signals = signals
|
self.signals = signals
|
||||||
self.children = children
|
self.children = children
|
||||||
|
self.style = style
|
||||||
|
|
||||||
|
@validate()
|
||||||
|
def only_one_style_class(self):
|
||||||
|
if len(self.style) > 1:
|
||||||
|
raise CompileError(
|
||||||
|
f"Only one style directive allowed per object, but this object contains {len(self.style)}",
|
||||||
|
start=self.style[1].group.start,
|
||||||
|
)
|
||||||
|
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
for x in [*self.properties, *self.signals, *self.children]:
|
for x in [*self.properties, *self.signals, *self.children, *self.style]:
|
||||||
x.emit_xml(xml)
|
x.emit_xml(xml)
|
||||||
|
|
||||||
|
|
||||||
|
@ -390,3 +399,28 @@ class Signal(AstNode):
|
||||||
if self.detail_name:
|
if self.detail_name:
|
||||||
name += "::" + self.detail_name
|
name += "::" + self.detail_name
|
||||||
xml.put_self_closing("signal", name=name, handler=self.handler, swapped="true" if self.swapped else None)
|
xml.put_self_closing("signal", name=name, handler=self.handler, swapped="true" if self.swapped else None)
|
||||||
|
|
||||||
|
|
||||||
|
class Style(AstNode):
|
||||||
|
child_type = "style"
|
||||||
|
|
||||||
|
def __init__(self, style_classes=None):
|
||||||
|
super().__init__()
|
||||||
|
self.style_classes = style_classes or []
|
||||||
|
|
||||||
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
|
xml.start_tag("style")
|
||||||
|
for style in self.style_classes:
|
||||||
|
style.emit_xml(xml)
|
||||||
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
|
class StyleClass(AstNode):
|
||||||
|
child_type = "style_classes"
|
||||||
|
|
||||||
|
def __init__(self, name):
|
||||||
|
super().__init__()
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
def emit_xml(self, xml):
|
||||||
|
xml.put_self_closing("class", name=self.name)
|
||||||
|
|
|
@ -319,6 +319,19 @@ class ZeroOrMore(ParseNode):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class Delimited(ParseNode):
|
||||||
|
""" ParseNode that matches its first child any number of times (including zero
|
||||||
|
times) with its second child in between and optionally at the end. """
|
||||||
|
def __init__(self, child, delimiter):
|
||||||
|
self.child = child
|
||||||
|
self.delimiter = delimiter
|
||||||
|
|
||||||
|
def _parse(self, ctx):
|
||||||
|
while self.child.parse(ctx).matched() and self.delimiter.parse(ctx).matched():
|
||||||
|
pass
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class Optional(ParseNode):
|
class Optional(ParseNode):
|
||||||
""" ParseNode that matches its child zero or one times. It cannot fail to
|
""" ParseNode that matches its child zero or one times. It cannot fail to
|
||||||
parse. """
|
parse. """
|
||||||
|
@ -362,6 +375,9 @@ class OpenParen(StaticToken):
|
||||||
class CloseParen(StaticToken):
|
class CloseParen(StaticToken):
|
||||||
token_type = TokenType.CLOSE_PAREN
|
token_type = TokenType.CLOSE_PAREN
|
||||||
|
|
||||||
|
class Comma(StaticToken):
|
||||||
|
token_type = TokenType.COMMA
|
||||||
|
|
||||||
|
|
||||||
class Op(ParseNode):
|
class Op(ParseNode):
|
||||||
""" ParseNode that matches the given operator. """
|
""" ParseNode that matches the given operator. """
|
||||||
|
|
|
@ -137,11 +137,27 @@ def parse(tokens) -> ast.UI:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
style = Group(
|
||||||
|
ast.Style,
|
||||||
|
Sequence(
|
||||||
|
Keyword("style"),
|
||||||
|
Delimited(
|
||||||
|
Group(
|
||||||
|
ast.StyleClass,
|
||||||
|
UseQuoted("name")
|
||||||
|
),
|
||||||
|
Comma(),
|
||||||
|
),
|
||||||
|
StmtEnd(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
object_content = Group(
|
object_content = Group(
|
||||||
ast.ObjectContent,
|
ast.ObjectContent,
|
||||||
Sequence(
|
Sequence(
|
||||||
OpenBlock(),
|
OpenBlock(),
|
||||||
ZeroOrMore(AnyOf(
|
ZeroOrMore(AnyOf(
|
||||||
|
style,
|
||||||
property,
|
property,
|
||||||
binding,
|
binding,
|
||||||
signal,
|
signal,
|
||||||
|
|
|
@ -45,6 +45,7 @@ class TestParser(unittest.TestCase):
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
style "dim-label", "my-class";
|
||||||
label: "Text";
|
label: "Text";
|
||||||
notify::visible => on_notify_visible();
|
notify::visible => on_notify_visible();
|
||||||
}
|
}
|
||||||
|
@ -109,3 +110,7 @@ class TestParser(unittest.TestCase):
|
||||||
self.assertEqual(signal.handler, "on_notify_visible")
|
self.assertEqual(signal.handler, "on_notify_visible")
|
||||||
self.assertEqual(signal.detail_name, "visible")
|
self.assertEqual(signal.detail_name, "visible")
|
||||||
self.assertFalse(signal.swapped)
|
self.assertFalse(signal.swapped)
|
||||||
|
self.assertEqual(len(obj.object_content.style), 1)
|
||||||
|
style = obj.object_content.style[0]
|
||||||
|
self.assertEqual(len(style.style_classes), 2)
|
||||||
|
self.assertEqual([s.name for s in style.style_classes], ["dim-label", "my-class"])
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue