mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
feat: add syntax for GtkDialog
's action widgets
This commit is contained in:
parent
cd12c94423
commit
736681a841
3 changed files with 175 additions and 0 deletions
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
|
|
||||||
import typing as T
|
import typing as T
|
||||||
|
from .gtk_dialog import ResponseId
|
||||||
from .common import *
|
from .common import *
|
||||||
|
|
||||||
|
|
||||||
|
@ -80,14 +81,38 @@ class Object(AstNode):
|
||||||
if self.gir_class:
|
if self.gir_class:
|
||||||
return self.gir_class.doc
|
return self.gir_class.doc
|
||||||
|
|
||||||
|
@property
|
||||||
|
def action_widgets(self) -> T.List[ResponseId]:
|
||||||
|
"""Get list of `GtkDialog`'s action widgets.
|
||||||
|
|
||||||
|
Empty if object is not `GtkDialog`.
|
||||||
|
"""
|
||||||
|
from .gtkbuilder_child import Child
|
||||||
|
|
||||||
|
return [
|
||||||
|
child.response_id
|
||||||
|
for child in self.children[ObjectContent][0].children[Child]
|
||||||
|
if child.response_id
|
||||||
|
]
|
||||||
|
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
|
from .gtkbuilder_child import Child
|
||||||
|
|
||||||
xml.start_tag("object", **{
|
xml.start_tag("object", **{
|
||||||
"class": self.gir_class.glib_type_name if self.gir_class else self.tokens["class_name"],
|
"class": self.gir_class.glib_type_name if self.gir_class else self.tokens["class_name"],
|
||||||
"id": self.tokens["id"],
|
"id": self.tokens["id"],
|
||||||
})
|
})
|
||||||
for child in self.children:
|
for child in self.children:
|
||||||
child.emit_xml(xml)
|
child.emit_xml(xml)
|
||||||
|
|
||||||
|
# List action widgets
|
||||||
|
action_widgets = self.action_widgets
|
||||||
|
if action_widgets:
|
||||||
|
xml.start_tag("action-widgets")
|
||||||
|
for action_widget in action_widgets:
|
||||||
|
action_widget.emit_action_widget(xml)
|
||||||
|
xml.end_tag()
|
||||||
|
|
||||||
xml.end_tag()
|
xml.end_tag()
|
||||||
|
|
||||||
|
|
||||||
|
|
132
blueprintcompiler/language/gtk_dialog.py
Normal file
132
blueprintcompiler/language/gtk_dialog.py
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
# gtk_dialog.py
|
||||||
|
#
|
||||||
|
# Copyright 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>
|
||||||
|
#
|
||||||
|
# This file is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU Lesser General Public License as
|
||||||
|
# published by the Free Software Foundation; either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This file is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
|
||||||
|
from .common import *
|
||||||
|
|
||||||
|
|
||||||
|
class ResponseId(AstNode):
|
||||||
|
"""Response ID of GtkDialog's action widget."""
|
||||||
|
|
||||||
|
grammar = [
|
||||||
|
UseIdent("response"),
|
||||||
|
"=",
|
||||||
|
AnyOf(
|
||||||
|
UseIdent("response_id"),
|
||||||
|
UseNumber("response_id")
|
||||||
|
),
|
||||||
|
Optional([
|
||||||
|
Keyword("default"), UseLiteral("is_default", True)
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
@validate()
|
||||||
|
def child_type_is_action(self) -> None:
|
||||||
|
"""Check that child type is "action"."""
|
||||||
|
child_type = self.parent.tokens["child_type"]
|
||||||
|
if child_type != "action":
|
||||||
|
raise CompileError(f"Only action widget can have response ID")
|
||||||
|
|
||||||
|
@validate()
|
||||||
|
def parent_is_dialog(self) -> None:
|
||||||
|
"""Chech that parent widget is `GtkDialog`."""
|
||||||
|
from .gobject_object import validate_parent_type
|
||||||
|
|
||||||
|
validate_parent_type(self, "Gtk", "Dialog", "action widgets")
|
||||||
|
|
||||||
|
@validate()
|
||||||
|
def widget_have_id(self) -> None:
|
||||||
|
"""Check that action widget have ID."""
|
||||||
|
from .gobject_object import Object
|
||||||
|
|
||||||
|
_object = self.parent.children[Object][0]
|
||||||
|
if _object.tokens["id"] is None:
|
||||||
|
raise CompileError(f"Action widget must have ID")
|
||||||
|
|
||||||
|
@validate("response_id")
|
||||||
|
def correct_response_type(self) -> None:
|
||||||
|
"""Validate response type.
|
||||||
|
|
||||||
|
Response type might be GtkResponseType member
|
||||||
|
or positive number.
|
||||||
|
"""
|
||||||
|
gir = self.root.gir
|
||||||
|
response = self.tokens["response_id"]
|
||||||
|
|
||||||
|
if isinstance(response, int):
|
||||||
|
if response < 0:
|
||||||
|
raise CompileError(
|
||||||
|
"Numeric response type can't be negative")
|
||||||
|
elif isinstance(response, float):
|
||||||
|
raise CompileError(
|
||||||
|
"Response type must be GtkResponseType member or integer,"
|
||||||
|
" not float"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
responses = gir.get_type("ResponseType", "Gtk").members.keys()
|
||||||
|
if response not in responses:
|
||||||
|
raise CompileError(
|
||||||
|
f"Response type \"{response}\" doesn't exist")
|
||||||
|
|
||||||
|
@validate("default")
|
||||||
|
def no_multiple_default(self) -> None:
|
||||||
|
"""Only one action widget in dialog can be default."""
|
||||||
|
from .gtkbuilder_child import Child
|
||||||
|
from .gobject_object import Object
|
||||||
|
|
||||||
|
if not self.tokens["is_default"]:
|
||||||
|
return
|
||||||
|
|
||||||
|
action_widgets = self.parent_by_type(Object).action_widgets
|
||||||
|
for widget in action_widgets:
|
||||||
|
if widget == self:
|
||||||
|
break
|
||||||
|
if widget.tokens["is_default"]:
|
||||||
|
raise CompileError("Default response is already set")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def widget_id(self) -> str:
|
||||||
|
"""Get action widget ID."""
|
||||||
|
from .gobject_object import Object
|
||||||
|
|
||||||
|
_object: Object = self.parent.children[Object][0]
|
||||||
|
return _object.tokens["id"]
|
||||||
|
|
||||||
|
def emit_xml(self, xml: XmlEmitter) -> None:
|
||||||
|
"""Emit nothing.
|
||||||
|
|
||||||
|
Response ID don't have to emit any XML in place,
|
||||||
|
but have to emit action-widget tag in separate
|
||||||
|
place (see `ResponseId.emit_action_widget`)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def emit_action_widget(self, xml: XmlEmitter) -> None:
|
||||||
|
"""Emit action-widget XML.
|
||||||
|
|
||||||
|
Must be called while <action-widgets> tag is open.
|
||||||
|
|
||||||
|
For more details see `GtkDialog` docs.
|
||||||
|
"""
|
||||||
|
xml.start_tag(
|
||||||
|
"action-widget",
|
||||||
|
response=self.tokens["response_id"],
|
||||||
|
default=self.tokens["is_default"]
|
||||||
|
)
|
||||||
|
xml.put_text(self.widget_id)
|
||||||
|
xml.end_tag()
|
|
@ -18,7 +18,10 @@
|
||||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
|
||||||
|
|
||||||
|
from functools import cache
|
||||||
|
|
||||||
from .gobject_object import Object
|
from .gobject_object import Object
|
||||||
|
from .gtk_dialog import ResponseId
|
||||||
from .common import *
|
from .common import *
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,11 +31,26 @@ class Child(AstNode):
|
||||||
"[",
|
"[",
|
||||||
Optional(["internal-child", UseLiteral("internal_child", True)]),
|
Optional(["internal-child", UseLiteral("internal_child", True)]),
|
||||||
UseIdent("child_type").expected("a child type"),
|
UseIdent("child_type").expected("a child type"),
|
||||||
|
Optional(ResponseId),
|
||||||
"]",
|
"]",
|
||||||
]),
|
]),
|
||||||
Object,
|
Object,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@property
|
||||||
|
@cache
|
||||||
|
def response_id(self) -> T.Optional[ResponseId]:
|
||||||
|
"""Get action widget's response ID.
|
||||||
|
|
||||||
|
If child is not action widget, returns `None`.
|
||||||
|
"""
|
||||||
|
response_ids = self.children[ResponseId]
|
||||||
|
|
||||||
|
if response_ids:
|
||||||
|
return response_ids[0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def emit_xml(self, xml: XmlEmitter):
|
def emit_xml(self, xml: XmlEmitter):
|
||||||
child_type = internal_child = None
|
child_type = internal_child = None
|
||||||
if self.tokens["internal_child"]:
|
if self.tokens["internal_child"]:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue