From 57877b46c2e97703b5b72766b72d6fda46cd87ec Mon Sep 17 00:00:00 2001 From: Sonny Piers Date: Fri, 26 Jan 2024 18:19:01 +0100 Subject: [PATCH] add a11y rules --- blueprintcompiler/linter.py | 58 ++++++++++++++++++++++++++++++++++--- linter.blp | 27 ++++++++++++----- 2 files changed, 74 insertions(+), 11 deletions(-) diff --git a/blueprintcompiler/linter.py b/blueprintcompiler/linter.py index d53aeb7..dfd8e46 100644 --- a/blueprintcompiler/linter.py +++ b/blueprintcompiler/linter.py @@ -21,8 +21,9 @@ from blueprintcompiler.language.gobject_object import Object from blueprintcompiler.language.gtkbuilder_child import Child from blueprintcompiler.language.gobject_property import Property -from blueprintcompiler.language.values import Translated, Literal +from blueprintcompiler.language.values import Translated from .errors import CompileError, CompileWarning +from blueprintcompiler.language.gtk_a11y import ExtAccessibility def lint(ast, problems = []): for child in ast.children: @@ -31,6 +32,10 @@ def lint(ast, problems = []): properties = child.content.children[Property] type = child.class_name.gir_type.full_name + # problems are for logical errors + # suggestions are for alternative/recommended way of doing things + + # rule problem/number-of-children if (type in gir_types_no_children and len(children) > 0): range = children[0].range problem = CompileError(f'{type} cannot have children', range) @@ -40,11 +45,14 @@ def lint(ast, problems = []): problem = CompileError(f'{type} cannot have more than one child', range) problems.append(problem) + # rule suggestion/prefer-adwbin + # FIXME: Only if use Adw is in scope and no Gtk.Box properties are used if (type == 'Gtk.Box' and len(children) == 1): range = children[0].range problem = CompileWarning(f'Use Adw.Bin instead of a Gtk.Box for a single child', range) problems.append(problem) + # rule suggestion/translatable-display-string for translatable_property in translatable_properties: if type == translatable_property[0] or translatable_property[0] == None: for property in properties: @@ -55,6 +63,7 @@ def lint(ast, problems = []): problem = CompileWarning(f'Mark {type} {property.name} as translatable using _("...")', range) problems.append(problem) + # rule suggestion/no-visible-true # FIXME GTK4 only for property in properties: if (property.name == 'visible'): @@ -62,17 +71,57 @@ def lint(ast, problems = []): ident = value.value.ident if ident == 'true': range = value.range - problem = CompileWarning(f'Property {property.name} default value is already true', range) + problem = CompileWarning(f'In GTK4 widgets are visible by default', range) problems.append(problem) - + # rule problem/no-gtkswitch-state if (type == 'Gtk.Switch'): for property in properties: if (property.name == 'state'): range = property.range - problem = CompileError(f'Use the Gtk.Switch active property instead of the state property', range) + problem = CompileError(f'Use the active property instead of the state property', range) problems.append(problem) + # rule suggestion/require-a11y-label + if (type == 'Gtk.Button'): + label = None + tooltip_text = None + accessibility_label = False + + # FIXME: Check what ATs actually do + + for property in properties: + if (property.name == 'label'): + label = property.value + elif (property.name == 'tooltip-text'): + tooltip_text = property.value + + accessibility__child = child.content.children[ExtAccessibility] + if len(accessibility__child) > 0: + accessibility_properties = child.content.children[ExtAccessibility][0].properties + for accessibility_property in accessibility_properties: + if (accessibility_property.name == 'label'): + accessibility_label = True + + if (label is None and tooltip_text is None and accessibility_label is False): + problem = CompileWarning(f'{type} is missing an accessibility label', child.range) + problems.append(problem) + + # rule suggestion/require-a11y-label + elif (type == 'Gtk.Image' or type == 'Gtk.Picture'): + accessibility_label = False + + accessibility__child = child.content.children[ExtAccessibility] + if len(accessibility__child) > 0: + accessibility_properties = child.content.children[ExtAccessibility][0].properties + for accessibility_property in accessibility_properties: + if (accessibility_property.name == 'label'): + accessibility_label = True + + if (accessibility_label is False): + problem = CompileWarning(f'{type} is missing an accessibility label', child.range) + problems.append(problem) + lint(child, problems) return problems @@ -84,4 +133,5 @@ translatable_properties = [ (None, 'tooltip-text'), ('Gtk.Label', 'label'), ('Gtk.Window', 'title'), + ('Gtk.Button', 'label') ] diff --git a/linter.blp b/linter.blp index f98b92c..8eb58c2 100644 --- a/linter.blp +++ b/linter.blp @@ -1,35 +1,48 @@ using Gtk 4.0; using Adw 1; +// I don't have an accessibility label +Gtk.Image { + icon-name: "cat-symbolic"; +} +Button { + icon-name: "foobar"; +} + +// I don't display children Label { Box {} } +// I only diplay the last child Adw.StatusPage { Box {} Box {} } +// Prefer Adw.Bin if you have a single child Gtk.Box { Label {} } +// I'm a user visible string that is not marked as translatable Label { label: "foo"; } - -Switch { - state: false; -} - Button { tooltip-text: "foo"; } - Window { title: "foobar"; } +// State isn't the right property to use here +// use active instead +Switch { + state: false; +} + +// In GTK4 widgets are visible by default Box { visible: true; -} \ No newline at end of file +}