mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
Support menus and object properties
This commit is contained in:
parent
b00401d53f
commit
7cf3c0bfb1
4 changed files with 159 additions and 15 deletions
|
@ -128,7 +128,7 @@ class AstNode:
|
||||||
class UI(AstNode):
|
class UI(AstNode):
|
||||||
""" The AST node for the entire file """
|
""" The AST node for the entire file """
|
||||||
|
|
||||||
def __init__(self, gtk_directives=[], imports=[], objects=[], templates=[]):
|
def __init__(self, gtk_directives=[], imports=[], objects=[], templates=[], menus=[]):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
assert_true(len(gtk_directives) == 1)
|
assert_true(len(gtk_directives) == 1)
|
||||||
|
|
||||||
|
@ -136,6 +136,7 @@ class UI(AstNode):
|
||||||
self.imports = imports
|
self.imports = imports
|
||||||
self.objects = objects
|
self.objects = objects
|
||||||
self.templates = templates
|
self.templates = templates
|
||||||
|
self.menus = menus
|
||||||
|
|
||||||
@validate()
|
@validate()
|
||||||
def gir(self):
|
def gir(self):
|
||||||
|
@ -156,10 +157,8 @@ class UI(AstNode):
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
xml.start_tag("interface")
|
xml.start_tag("interface")
|
||||||
self.gtk_directive.emit_xml(xml)
|
self.gtk_directive.emit_xml(xml)
|
||||||
for object in self.objects:
|
for x in [*self.templates, *self.objects, *self.menus]:
|
||||||
object.emit_xml(xml)
|
x.emit_xml(xml)
|
||||||
for template in self.templates:
|
|
||||||
template.emit_xml(xml)
|
|
||||||
xml.end_tag()
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
|
@ -288,13 +287,14 @@ class ObjectContent(AstNode):
|
||||||
|
|
||||||
class Property(AstNode):
|
class Property(AstNode):
|
||||||
child_type = "properties"
|
child_type = "properties"
|
||||||
def __init__(self, name, value=None, translatable=False, bind_source=None, bind_property=None):
|
def __init__(self, name, value=None, translatable=False, bind_source=None, bind_property=None, objects=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.name = name
|
self.name = name
|
||||||
self.value = value
|
self.value = value
|
||||||
self.translatable = translatable
|
self.translatable = translatable
|
||||||
self.bind_source = bind_source
|
self.bind_source = bind_source
|
||||||
self.bind_property = bind_property
|
self.bind_property = bind_property
|
||||||
|
self.objects = objects
|
||||||
|
|
||||||
|
|
||||||
@validate()
|
@validate()
|
||||||
|
@ -339,7 +339,11 @@ class Property(AstNode):
|
||||||
"bind-source": self.bind_source,
|
"bind-source": self.bind_source,
|
||||||
"bind-property": self.bind_property,
|
"bind-property": self.bind_property,
|
||||||
}
|
}
|
||||||
if self.value is None:
|
if self.objects is not None:
|
||||||
|
xml.start_tag("property", **props)
|
||||||
|
self.objects[0].emit_xml(xml)
|
||||||
|
xml.end_tag()
|
||||||
|
elif self.value is None:
|
||||||
xml.put_self_closing("property", **props)
|
xml.put_self_closing("property", **props)
|
||||||
else:
|
else:
|
||||||
xml.start_tag("property", **props)
|
xml.start_tag("property", **props)
|
||||||
|
@ -424,3 +428,41 @@ class StyleClass(AstNode):
|
||||||
|
|
||||||
def emit_xml(self, xml):
|
def emit_xml(self, xml):
|
||||||
xml.put_self_closing("class", name=self.name)
|
xml.put_self_closing("class", name=self.name)
|
||||||
|
|
||||||
|
|
||||||
|
class Menu(AstNode):
|
||||||
|
child_type = "menus"
|
||||||
|
|
||||||
|
def __init__(self, tag, id=None, menus=None, attributes=None):
|
||||||
|
super().__init__()
|
||||||
|
self.tag = tag
|
||||||
|
self.id = id
|
||||||
|
self.menus = menus or []
|
||||||
|
self.attributes = attributes or []
|
||||||
|
|
||||||
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
|
xml.start_tag(self.tag, id=self.id)
|
||||||
|
for attr in self.attributes:
|
||||||
|
attr.emit_xml(xml)
|
||||||
|
for menu in self.menus:
|
||||||
|
menu.emit_xml(xml)
|
||||||
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
|
class MenuAttribute(AstNode):
|
||||||
|
child_type = "attributes"
|
||||||
|
|
||||||
|
def __init__(self, name, value, translatable=False):
|
||||||
|
super().__init__()
|
||||||
|
self.name = name
|
||||||
|
self.value = value
|
||||||
|
self.translatable = translatable
|
||||||
|
|
||||||
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
|
xml.start_tag(
|
||||||
|
"attribute",
|
||||||
|
name=self.name,
|
||||||
|
translatable="yes" if self.translatable else None,
|
||||||
|
)
|
||||||
|
xml.put_text(str(self.value))
|
||||||
|
xml.end_tag()
|
||||||
|
|
|
@ -77,12 +77,20 @@ def parse(tokens) -> ast.UI:
|
||||||
UseQuoted("value"),
|
UseQuoted("value"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
object = Group(
|
||||||
|
ast.Object,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
property = Group(
|
property = Group(
|
||||||
ast.Property,
|
ast.Property,
|
||||||
Sequence(
|
Sequence(
|
||||||
UseIdent("name"),
|
UseIdent("name"),
|
||||||
Op(":"),
|
Op(":"),
|
||||||
value.expected("a value"),
|
AnyOf(
|
||||||
|
object,
|
||||||
|
value,
|
||||||
|
).expected("a value"),
|
||||||
StmtEnd().expected("`;`"),
|
StmtEnd().expected("`;`"),
|
||||||
)
|
)
|
||||||
).recover()
|
).recover()
|
||||||
|
@ -120,11 +128,6 @@ def parse(tokens) -> ast.UI:
|
||||||
)
|
)
|
||||||
).recover()
|
).recover()
|
||||||
|
|
||||||
object = Group(
|
|
||||||
ast.Object,
|
|
||||||
None
|
|
||||||
)
|
|
||||||
|
|
||||||
child = Group(
|
child = Group(
|
||||||
ast.Child,
|
ast.Child,
|
||||||
Sequence(
|
Sequence(
|
||||||
|
@ -152,6 +155,93 @@ def parse(tokens) -> ast.UI:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
menu_contents = Sequence()
|
||||||
|
|
||||||
|
menu_section = Group(
|
||||||
|
ast.Menu,
|
||||||
|
Sequence(
|
||||||
|
Keyword("section"),
|
||||||
|
UseLiteral("tag", "section"),
|
||||||
|
Optional(UseIdent("id")),
|
||||||
|
menu_contents
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
menu_submenu = Group(
|
||||||
|
ast.Menu,
|
||||||
|
Sequence(
|
||||||
|
Keyword("submenu"),
|
||||||
|
UseLiteral("tag", "submenu"),
|
||||||
|
Optional(UseIdent("id")),
|
||||||
|
menu_contents
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
menu_attribute = Group(
|
||||||
|
ast.MenuAttribute,
|
||||||
|
Sequence(
|
||||||
|
UseIdent("name"),
|
||||||
|
Op(":"),
|
||||||
|
value.expected("a value"),
|
||||||
|
StmtEnd().expected("`;`"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
menu_item = Group(
|
||||||
|
ast.Menu,
|
||||||
|
Sequence(
|
||||||
|
Keyword("item"),
|
||||||
|
UseLiteral("tag", "item"),
|
||||||
|
Optional(UseIdent("id")),
|
||||||
|
OpenBlock().expected("`{`"),
|
||||||
|
ZeroOrMore(menu_attribute),
|
||||||
|
CloseBlock().err("Could not understand statement"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
menu_item_shorthand = Group(
|
||||||
|
ast.Menu,
|
||||||
|
Sequence(
|
||||||
|
Keyword("item"),
|
||||||
|
UseLiteral("tag", "item"),
|
||||||
|
Group(
|
||||||
|
ast.MenuAttribute,
|
||||||
|
Sequence(UseLiteral("name", "label"), value),
|
||||||
|
),
|
||||||
|
Optional(Group(
|
||||||
|
ast.MenuAttribute,
|
||||||
|
Sequence(UseLiteral("name", "action"), value),
|
||||||
|
)),
|
||||||
|
Optional(Group(
|
||||||
|
ast.MenuAttribute,
|
||||||
|
Sequence(UseLiteral("name", "verb-icon-name"), value),
|
||||||
|
)),
|
||||||
|
StmtEnd().expected("`;`"),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
menu_contents.children = [
|
||||||
|
OpenBlock().expected("`{`"),
|
||||||
|
ZeroOrMore(AnyOf(
|
||||||
|
menu_section,
|
||||||
|
menu_submenu,
|
||||||
|
menu_item_shorthand,
|
||||||
|
menu_item,
|
||||||
|
menu_attribute,
|
||||||
|
)),
|
||||||
|
CloseBlock().err("Could not understand statement"),
|
||||||
|
]
|
||||||
|
|
||||||
|
menu = Group(
|
||||||
|
ast.Menu,
|
||||||
|
Sequence(
|
||||||
|
Keyword("menu"),
|
||||||
|
UseLiteral("tag", "menu"),
|
||||||
|
Optional(UseIdent("id")),
|
||||||
|
menu_contents
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
object_content = Group(
|
object_content = Group(
|
||||||
ast.ObjectContent,
|
ast.ObjectContent,
|
||||||
Sequence(
|
Sequence(
|
||||||
|
@ -171,7 +261,7 @@ def parse(tokens) -> ast.UI:
|
||||||
object.child = Sequence(
|
object.child = Sequence(
|
||||||
class_name,
|
class_name,
|
||||||
Optional(UseIdent("id")),
|
Optional(UseIdent("id")),
|
||||||
object_content.expected("block"),
|
object_content,
|
||||||
)
|
)
|
||||||
|
|
||||||
template = Group(
|
template = Group(
|
||||||
|
@ -192,6 +282,7 @@ def parse(tokens) -> ast.UI:
|
||||||
ZeroOrMore(import_statement),
|
ZeroOrMore(import_statement),
|
||||||
ZeroOrMore(AnyOf(
|
ZeroOrMore(AnyOf(
|
||||||
template,
|
template,
|
||||||
|
menu,
|
||||||
object,
|
object,
|
||||||
)),
|
)),
|
||||||
Eof().err("Failed to parse the rest of the file"),
|
Eof().err("Failed to parse the rest of the file"),
|
||||||
|
|
|
@ -26,7 +26,7 @@ from .utils import lazy_prop
|
||||||
|
|
||||||
PARSE_GIR = set([
|
PARSE_GIR = set([
|
||||||
"repository", "namespace", "class", "interface", "property", "glib:signal",
|
"repository", "namespace", "class", "interface", "property", "glib:signal",
|
||||||
"include",
|
"include", "implements",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,17 @@ class TestParser(unittest.TestCase):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
menu my_menu {
|
||||||
|
section {
|
||||||
|
item {
|
||||||
|
label: "Run";
|
||||||
|
target: "run";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
submenu {}
|
||||||
|
item _("Copy") copy-symbolic app.copy;
|
||||||
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
style "dim-label", "my-class";
|
style "dim-label", "my-class";
|
||||||
label: "Text";
|
label: "Text";
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue