feat: add syntax for GtkDialog's action widgets

This commit is contained in:
Gleb Smirnov 2022-02-18 19:03:41 +03:00
parent cd12c94423
commit 736681a841
No known key found for this signature in database
GPG key ID: 559DB6D1D625EFAB
3 changed files with 175 additions and 0 deletions

View file

@ -19,6 +19,7 @@
import typing as T
from .gtk_dialog import ResponseId
from .common import *
@ -80,14 +81,38 @@ class Object(AstNode):
if self.gir_class:
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):
from .gtkbuilder_child import Child
xml.start_tag("object", **{
"class": self.gir_class.glib_type_name if self.gir_class else self.tokens["class_name"],
"id": self.tokens["id"],
})
for child in self.children:
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()

View 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()

View file

@ -18,7 +18,10 @@
# SPDX-License-Identifier: LGPL-3.0-or-later
from functools import cache
from .gobject_object import Object
from .gtk_dialog import ResponseId
from .common import *
@ -28,11 +31,26 @@ class Child(AstNode):
"[",
Optional(["internal-child", UseLiteral("internal_child", True)]),
UseIdent("child_type").expected("a child type"),
Optional(ResponseId),
"]",
]),
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):
child_type = internal_child = None
if self.tokens["internal_child"]: