diff --git a/blueprintcompiler/language/__init__.py b/blueprintcompiler/language/__init__.py index 5eb2b60..a7365c8 100644 --- a/blueprintcompiler/language/__init__.py +++ b/blueprintcompiler/language/__init__.py @@ -28,6 +28,7 @@ from .gtk_file_filter import ( ext_file_filter_suffixes, ) from .gtk_layout import ExtLayout +from .gtk_level_bar import ExtLevelBarOffsets from .gtk_list_item_factory import ExtListItemFactory from .gtk_menu import Menu, MenuAttribute, menu from .gtk_scale import ExtScaleMarks @@ -67,6 +68,7 @@ OBJECT_CONTENT_HOOKS.children = [ ext_file_filter_patterns, ext_file_filter_suffixes, ExtLayout, + ExtLevelBarOffsets, ExtListItemFactory, ExtScaleMarks, ExtSizeGroupWidgets, diff --git a/blueprintcompiler/language/gtk_level_bar.py b/blueprintcompiler/language/gtk_level_bar.py new file mode 100644 index 0000000..b2ed69d --- /dev/null +++ b/blueprintcompiler/language/gtk_level_bar.py @@ -0,0 +1,131 @@ +# gtk_level_bar.py +# +# Copyright 2025 Matthijs Velsink +# +# 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 . +# +# SPDX-License-Identifier: LGPL-3.0-or-later + +from .common import * +from .gobject_object import ObjectContent, validate_parent_type +from .values import StringValue + + +class ExtLevelBarOffset(AstNode): + grammar = [ + Keyword("offset"), + Match("(").expected(), + [ + UseQuoted("name"), + ",", + Optional(AnyOf(UseExact("sign", "-"), UseExact("sign", "+"))), + UseNumber("value"), + ], + Match(")").expected(), + ] + + @property + def name(self) -> str: + return self.tokens["name"] + + @property + def value(self) -> float: + return self.tokens["value"] + + @property + def document_symbol(self) -> DocumentSymbol: + return DocumentSymbol( + self.name, + SymbolKind.Field, + self.range, + self.group.tokens["offset"].range, + str(self.value), + ) + + @validate("value") + def validate_value(self): + if self.tokens["sign"] == "-": + raise CompileError( + "Offset value can't be negative", + Range.join(self.ranges["sign"], self.ranges["value"]), + ) + + @docs("offset") + def ref_docs(self): + return get_docs_section("Syntax ExtLevelBarOffsets") + + +class ExtLevelBarOffsets(AstNode): + grammar = [ + Keyword("offsets"), + Match("[").expected(), + Until(ExtLevelBarOffset, "]", ","), + ] + + @property + def offsets(self) -> T.List[ExtLevelBarOffset]: + return self.children + + @property + def document_symbol(self) -> DocumentSymbol: + return DocumentSymbol( + "offsets", + SymbolKind.Array, + self.range, + self.group.tokens["offsets"].range, + ) + + @validate("offsets") + def container_is_level_bar(self): + validate_parent_type(self, "Gtk", "LevelBar", "level bar offsets") + + @validate("offsets") + def unique_in_parent(self): + self.validate_unique_in_parent("Duplicate 'offsets' block") + + @docs("offsets") + def ref_docs(self): + return get_docs_section("Syntax ExtLevelBarOffsets") + + +@completer( + applies_in=[ObjectContent], + applies_in_subclass=("Gtk", "LevelBar"), + matches=new_statement_patterns, +) +def complete_offsets(lsp, ast_node, match_variables): + yield Completion( + "offsets", CompletionItemKind.Keyword, snippet="offsets [\n\t$0\n]" + ) + + +@completer( + applies_in=[ExtLevelBarOffsets], +) +def complete_offset(lsp, ast_node, match_variables): + yield Completion( + "offset", + CompletionItemKind.Snippet, + snippet='offset ("${1:name}", ${2:value}),', + ) + + +@decompiler("offsets") +def decompile_offsets(ctx, gir): + ctx.print("offsets [") + + +@decompiler("offset") +def decompile_offset(ctx: DecompileCtx, gir, name, value): + ctx.print(f'offset ("{name}", {value}),') diff --git a/blueprintcompiler/outputs/xml/__init__.py b/blueprintcompiler/outputs/xml/__init__.py index 15850f7..27bd38a 100644 --- a/blueprintcompiler/outputs/xml/__init__.py +++ b/blueprintcompiler/outputs/xml/__init__.py @@ -369,6 +369,12 @@ class XmlOutput(OutputFormat): xml.end_tag() xml.end_tag() + elif isinstance(extension, ExtLevelBarOffsets): + xml.start_tag("offsets") + for offset in extension.offsets: + xml.put_self_closing("offset", name=offset.name, value=offset.value) + xml.end_tag() + elif isinstance(extension, ExtScaleMarks): xml.start_tag("marks") for mark in extension.marks: diff --git a/docs/reference/extensions.rst b/docs/reference/extensions.rst index 2fd5dbb..b186c45 100644 --- a/docs/reference/extensions.rst +++ b/docs/reference/extensions.rst @@ -23,6 +23,7 @@ Properties are the main way to set values on objects, but they are limited by th | :ref:`ExtFileFilterPatterns` | :ref:`ExtFileFilterSuffixes` | :ref:`ExtLayout` + | :ref:`ExtLevelBarOffsets` | :ref:`ExtListItemFactory` | :ref:`ExtSizeGroupWidgets` | :ref:`ExtStringListStrings` @@ -214,6 +215,31 @@ The ``layout`` block describes how the widget should be positioned within its pa } +.. _Syntax ExtLevelBarOffsets: + +Gtk.LevelBar Offsets +-------------------- + +.. rst-class:: grammar-block + + ExtLevelBarOffsets = 'offsets' '[' (ExtLevelBarOffset),* ']' + ExtLevelBarOffset = 'offset' '(' `> ',' `> ')' + +Valid in `Gtk.LevelBar `_. + +The ``offsets`` block defines the offsets on a level bar. A single ``offset`` has two arguments: a CSS class name and a (non-negative) value. + +.. code-block:: blueprint + + LevelBar { + offsets [ + offset ("low-class-name", 0.3), + offset ("medium-class-name", 0.5), + offset ("high-class-name", 0.7), + ] + } + + .. _Syntax ExtListItemFactory: Gtk.BuilderListItemFactory Templates diff --git a/tests/sample_errors/level_bar_offset_value.blp b/tests/sample_errors/level_bar_offset_value.blp new file mode 100644 index 0000000..425be27 --- /dev/null +++ b/tests/sample_errors/level_bar_offset_value.blp @@ -0,0 +1,7 @@ +using Gtk 4.0; + +LevelBar { + offsets [ + offset ("negative-value", -0.3), + ] +} diff --git a/tests/sample_errors/level_bar_offset_value.err b/tests/sample_errors/level_bar_offset_value.err new file mode 100644 index 0000000..59ee32e --- /dev/null +++ b/tests/sample_errors/level_bar_offset_value.err @@ -0,0 +1 @@ +5,31,4,Offset value can't be negative \ No newline at end of file diff --git a/tests/samples/level_bar_offsets.blp b/tests/samples/level_bar_offsets.blp new file mode 100644 index 0000000..adbfcab --- /dev/null +++ b/tests/samples/level_bar_offsets.blp @@ -0,0 +1,9 @@ +using Gtk 4.0; + +LevelBar { + offsets [ + offset ("low-class-name", 0.3), + offset ("medium-class-name", 0.5), + offset ("high-class-name", 0.7), + ] +} diff --git a/tests/samples/level_bar_offsets.ui b/tests/samples/level_bar_offsets.ui new file mode 100644 index 0000000..d6b55c6 --- /dev/null +++ b/tests/samples/level_bar_offsets.ui @@ -0,0 +1,16 @@ + + + + + + + + + + + +