add a11y rules

This commit is contained in:
Sonny Piers 2024-01-26 18:19:01 +01:00
parent 07ca07e08d
commit 57877b46c2
2 changed files with 74 additions and 11 deletions

View file

@ -21,8 +21,9 @@
from blueprintcompiler.language.gobject_object import Object from blueprintcompiler.language.gobject_object import Object
from blueprintcompiler.language.gtkbuilder_child import Child from blueprintcompiler.language.gtkbuilder_child import Child
from blueprintcompiler.language.gobject_property import Property 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 .errors import CompileError, CompileWarning
from blueprintcompiler.language.gtk_a11y import ExtAccessibility
def lint(ast, problems = []): def lint(ast, problems = []):
for child in ast.children: for child in ast.children:
@ -31,6 +32,10 @@ def lint(ast, problems = []):
properties = child.content.children[Property] properties = child.content.children[Property]
type = child.class_name.gir_type.full_name 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): if (type in gir_types_no_children and len(children) > 0):
range = children[0].range range = children[0].range
problem = CompileError(f'{type} cannot have children', 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) problem = CompileError(f'{type} cannot have more than one child', range)
problems.append(problem) 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): if (type == 'Gtk.Box' and len(children) == 1):
range = children[0].range range = children[0].range
problem = CompileWarning(f'Use Adw.Bin instead of a Gtk.Box for a single child', range) problem = CompileWarning(f'Use Adw.Bin instead of a Gtk.Box for a single child', range)
problems.append(problem) problems.append(problem)
# rule suggestion/translatable-display-string
for translatable_property in translatable_properties: for translatable_property in translatable_properties:
if type == translatable_property[0] or translatable_property[0] == None: if type == translatable_property[0] or translatable_property[0] == None:
for property in properties: for property in properties:
@ -55,6 +63,7 @@ def lint(ast, problems = []):
problem = CompileWarning(f'Mark {type} {property.name} as translatable using _("...")', range) problem = CompileWarning(f'Mark {type} {property.name} as translatable using _("...")', range)
problems.append(problem) problems.append(problem)
# rule suggestion/no-visible-true
# FIXME GTK4 only # FIXME GTK4 only
for property in properties: for property in properties:
if (property.name == 'visible'): if (property.name == 'visible'):
@ -62,17 +71,57 @@ def lint(ast, problems = []):
ident = value.value.ident ident = value.value.ident
if ident == 'true': if ident == 'true':
range = value.range 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) problems.append(problem)
# rule problem/no-gtkswitch-state
if (type == 'Gtk.Switch'): if (type == 'Gtk.Switch'):
for property in properties: for property in properties:
if (property.name == 'state'): if (property.name == 'state'):
range = property.range 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) 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) lint(child, problems)
return problems return problems
@ -84,4 +133,5 @@ translatable_properties = [
(None, 'tooltip-text'), (None, 'tooltip-text'),
('Gtk.Label', 'label'), ('Gtk.Label', 'label'),
('Gtk.Window', 'title'), ('Gtk.Window', 'title'),
('Gtk.Button', 'label')
] ]

View file

@ -1,35 +1,48 @@
using Gtk 4.0; using Gtk 4.0;
using Adw 1; 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 { Label {
Box {} Box {}
} }
// I only diplay the last child
Adw.StatusPage { Adw.StatusPage {
Box {} Box {}
Box {} Box {}
} }
// Prefer Adw.Bin if you have a single child
Gtk.Box { Gtk.Box {
Label {} Label {}
} }
// I'm a user visible string that is not marked as translatable
Label { Label {
label: "foo"; label: "foo";
} }
Switch {
state: false;
}
Button { Button {
tooltip-text: "foo"; tooltip-text: "foo";
} }
Window { Window {
title: "foobar"; 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 { Box {
visible: true; visible: true;
} }