Add support for CSS styles

This commit is contained in:
James Westman 2021-10-23 00:59:10 -05:00
parent 75a05fe5ce
commit bef92f2879
No known key found for this signature in database
GPG key ID: CE2DBA0ADB654EA6
4 changed files with 73 additions and 2 deletions

View file

@ -266,14 +266,23 @@ class Child(AstNode):
class ObjectContent(AstNode):
child_type = "object_content"
def __init__(self, properties=[], signals=[], children=[]):
def __init__(self, properties=[], signals=[], children=[], style=[]):
super().__init__()
self.properties = properties
self.signals = signals
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):
for x in [*self.properties, *self.signals, *self.children]:
for x in [*self.properties, *self.signals, *self.children, *self.style]:
x.emit_xml(xml)
@ -390,3 +399,28 @@ class Signal(AstNode):
if self.detail_name:
name += "::" + self.detail_name
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)

View file

@ -319,6 +319,19 @@ class ZeroOrMore(ParseNode):
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):
""" ParseNode that matches its child zero or one times. It cannot fail to
parse. """
@ -362,6 +375,9 @@ class OpenParen(StaticToken):
class CloseParen(StaticToken):
token_type = TokenType.CLOSE_PAREN
class Comma(StaticToken):
token_type = TokenType.COMMA
class Op(ParseNode):
""" ParseNode that matches the given operator. """

View file

@ -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(
ast.ObjectContent,
Sequence(
OpenBlock(),
ZeroOrMore(AnyOf(
style,
property,
binding,
signal,

View file

@ -45,6 +45,7 @@ class TestParser(unittest.TestCase):
}
Label {
style "dim-label", "my-class";
label: "Text";
notify::visible => on_notify_visible();
}
@ -109,3 +110,7 @@ class TestParser(unittest.TestCase):
self.assertEqual(signal.handler, "on_notify_visible")
self.assertEqual(signal.detail_name, "visible")
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"])