Skip to content

Commit 8501b4b

Browse files
ashuntuFeichtmeier
authored andcommitted
feat(a11y)!: Add focus border to focusable Yaru widgets (#1045)
* feat(a11y): Add YaruFocusBorder to YaruIconButton * feat(a11y): Add YaruFocusBorder to YaruBanner * feat(a11y): Add YaruFocusBorder to YaruCarousel nav buttons * feat(a11y): Add YaruFocusBorder to YaruChoiceChipBar chips * feat(a11y): Add YaruFocusBorder to YaruDateTimeEntry buttons * feat(a11y): Add YaruFocusBorder to YaruSelectableContainer * feat(a11y): Add YaruFocusBorder to toggleable widgets, tiles, and buttons * feat(a11y): Add YaruFocusBorder to YaruOptionButton * feat(a11y): Add YaruFocusBorder to YaruSplitButton * feat(a11y): Add YaruFocusBorder to YaruMasterTile This removes the need for a specific focus decoration as added previously * test: Update tests and goldens * fix(a11y): Add YaruFocusBorder to example app widgets * fix(a11y): Add focus border toggle to YaruTheme This makes it so the focus borders can be turned on/off globally, or at a per-widget basis. The downside of this is that widgets won't have focus borders by default when not a descendent of a YaruTheme * test: Update golden images * fix: Remove unnecessary import
1 parent 0b7dd1f commit 8501b4b

File tree

52 files changed

+584
-447
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+584
-447
lines changed

example/lib/code_snippet_button.dart

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@ class CodeSnippedButton extends StatelessWidget {
1515

1616
@override
1717
Widget build(BuildContext context) {
18-
return FloatingActionButton(
19-
onPressed: () => showDialog(
20-
barrierDismissible: true,
21-
context: context,
22-
builder: (context) => _CodeDialog(snippetUrl: snippetUrl),
18+
return YaruFocusBorder.primary(
19+
child: FloatingActionButton(
20+
onPressed: () => showDialog(
21+
barrierDismissible: true,
22+
context: context,
23+
builder: (context) => _CodeDialog(snippetUrl: snippetUrl),
24+
),
25+
tooltip: 'Example snippet',
26+
foregroundColor: Theme.of(context).colorScheme.onSurface,
27+
backgroundColor: PopupMenuTheme.of(context).color,
28+
shape: PopupMenuTheme.of(context).shape,
29+
child: const Icon(YaruIcons.code),
2330
),
24-
tooltip: 'Example snippet',
25-
foregroundColor: Theme.of(context).colorScheme.onSurface,
26-
backgroundColor: PopupMenuTheme.of(context).color,
27-
shape: PopupMenuTheme.of(context).shape,
28-
child: const Icon(YaruIcons.code),
2931
);
3032
}
3133
}

example/lib/example_dark_light_toggle_button.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class ExampleDarkLightToggleButton extends StatelessWidget with WatchItMixin {
1111
Widget build(BuildContext context) {
1212
final themeMode = watchPropertyValue((ExampleModel m) => m.themeMode);
1313

14-
return IconButton(
14+
return YaruIconButton(
1515
tooltip: 'ThemeMode (System, Light, Dark)',
1616
onPressed: () => di<ExampleModel>().setThemeMode(switch (themeMode) {
1717
ThemeMode.system => ThemeMode.light,

example/lib/example_high_contrast_button.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class ExampleHighContrastButton extends StatelessWidget with WatchItMixin {
1313
watchPropertyValue((ExampleModel m) => m.forceHighContrast) ||
1414
Theme.of(context).colorScheme.isHighContrast == true;
1515

16-
return IconButton(
16+
return YaruIconButton(
1717
tooltip: 'Force HighContrast mode',
1818
onPressed: di<ExampleModel>().toggleForceHighContrast,
1919
icon: Icon(highContrast ? YaruIcons.eye_filled : YaruIcons.eye),

example/lib/example_theme_button.dart

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,33 @@ class ExampleYaruVariantPicker extends StatelessWidget with WatchItMixin {
1212
final model = di<ExampleModel>();
1313
final yaruVariant = watchPropertyValue((ExampleModel m) => m.yaruVariant);
1414

15-
return PopupMenuButton<Color>(
16-
tooltip: 'Pick a YaruVariant',
17-
padding: EdgeInsets.zero,
18-
icon: Icon(YaruIcons.color_select, color: Theme.of(context).primaryColor),
19-
itemBuilder: (context) {
20-
return [
21-
for (final variant in YaruVariant.values) // skip flavors
22-
PopupMenuItem(
23-
onTap: () => model.setYaruVariant(variant),
24-
child: Row(
25-
children: [
26-
ColorDisk(
27-
color: variant.color,
28-
selected: variant == yaruVariant,
29-
),
30-
Text(variant.name),
31-
],
15+
return YaruFocusBorder.primary(
16+
borderRadius: BorderRadius.circular(100),
17+
child: PopupMenuButton<Color>(
18+
tooltip: 'Pick a YaruVariant',
19+
padding: EdgeInsets.zero,
20+
icon: Icon(
21+
YaruIcons.color_select,
22+
color: Theme.of(context).primaryColor,
23+
),
24+
itemBuilder: (context) {
25+
return [
26+
for (final variant in YaruVariant.values) // skip flavors
27+
PopupMenuItem(
28+
onTap: () => model.setYaruVariant(variant),
29+
child: Row(
30+
children: [
31+
ColorDisk(
32+
color: variant.color,
33+
selected: variant == yaruVariant,
34+
),
35+
Text(variant.name),
36+
],
37+
),
3238
),
33-
),
34-
];
35-
},
39+
];
40+
},
41+
),
3642
);
3743
}
3844
}

example/lib/pages/full_color_icons_page.dart

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,20 @@ class _FullColorIconsPageState extends State<FullColorIconsPage>
6262
.lastOrNull
6363
?.replaceAll(_urlSuffix, '') ??
6464
'',
65-
child: InkWell(
66-
borderRadius: BorderRadius.circular(
67-
kYaruButtonRadius,
68-
),
69-
onTap: () => kIsWeb
70-
? html.window.open(icon, '')
71-
: launchUrl(Uri.parse(icon)),
72-
child: Image.network(
73-
icon,
74-
filterQuality: FilterQuality.medium,
75-
errorBuilder: (context, error, stackTrace) =>
76-
const Icon(YaruIcons.question, size: 35),
65+
child: YaruFocusBorder.primary(
66+
child: InkWell(
67+
borderRadius: BorderRadius.circular(
68+
kYaruButtonRadius,
69+
),
70+
onTap: () => kIsWeb
71+
? html.window.open(icon, '')
72+
: launchUrl(Uri.parse(icon)),
73+
child: Image.network(
74+
icon,
75+
filterQuality: FilterQuality.medium,
76+
errorBuilder: (context, error, stackTrace) =>
77+
const Icon(YaruIcons.question, size: 35),
78+
),
7779
),
7880
),
7981
);

example/lib/pages/icons_page/common/clickable_icon.dart

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ class ClickableIcon extends StatelessWidget {
1818
Widget build(BuildContext context) {
1919
return AspectRatio(
2020
aspectRatio: 1,
21-
child: InkWell(
22-
borderRadius: BorderRadius.circular(kYaruButtonRadius),
23-
onTap: () => showDialog(
24-
context: context,
25-
builder: (context) => IconDialog(iconItem: iconItem),
21+
child: YaruFocusBorder.primary(
22+
child: InkWell(
23+
borderRadius: BorderRadius.circular(kYaruButtonRadius),
24+
onTap: () => showDialog(
25+
context: context,
26+
builder: (context) => IconDialog(iconItem: iconItem),
27+
),
28+
child: Center(child: iconItem.iconBuilder(context, iconSize)),
2629
),
27-
child: Center(child: iconItem.iconBuilder(context, iconSize)),
2830
),
2931
);
3032
}

example/lib/pages/info_page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class _InfoPageState extends State<InfoPage> {
2121
padding: const EdgeInsets.all(kYaruPagePadding),
2222
child: Row(
2323
children: [
24-
IconButton(
24+
YaruIconButton(
2525
tooltip: 'Custom icons and colors are possible',
2626
isSelected: _idea,
2727
onPressed: () => setState(() => _idea = !_idea),

example/lib/pages/theme_page/src/home/home_page.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@ class MaterialThemeHomePageState extends State<MaterialThemeHomePage> {
6565
appBar: AppBar(
6666
backgroundColor: Colors.transparent,
6767
leading: Center(
68-
child: IconButton(
68+
child: YaruIconButton(
6969
onPressed: () => themePageScaffoldKey.currentState?.openDrawer(),
7070
icon: const Icon(YaruIcons.menu),
7171
),
7272
),
7373
title: const Text(''),
7474
actions: [
75-
IconButton(
75+
YaruIconButton(
7676
onPressed: () => showSnack(context),
7777
icon: const Icon(YaruIcons.plus),
7878
),

lib/src/settings/inherited_theme.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ class YaruThemeData with Diagnosticable {
302302
this.useMaterial3,
303303
this.visualDensity,
304304
this.statusShapes,
305+
this.focusBorders = true,
305306
});
306307

307308
/// Specifies the theme variant.
@@ -327,6 +328,9 @@ class YaruThemeData with Diagnosticable {
327328

328329
final bool? statusShapes;
329330

331+
/// Whether to draw focus borders.
332+
final bool? focusBorders;
333+
330334
/// The light theme of [variant] (or [yaruLight] if not available) merged with
331335
/// the `YaruThemeData` overrides.
332336
ThemeData? get theme => (variant?.theme ?? yaruLight).overrideWith(this);
@@ -356,6 +360,7 @@ class YaruThemeData with Diagnosticable {
356360
useMaterial3: useMaterial3 ?? this.useMaterial3,
357361
visualDensity: visualDensity ?? this.visualDensity,
358362
statusShapes: statusShapes ?? this.statusShapes,
363+
focusBorders: focusBorders ?? this.focusBorders,
359364
);
360365
}
361366

@@ -371,6 +376,8 @@ class YaruThemeData with Diagnosticable {
371376
);
372377
properties.add(DiagnosticsProperty('useMaterial3', useMaterial3));
373378
properties.add(DiagnosticsProperty('visualDensity', visualDensity));
379+
properties.add(DiagnosticsProperty<bool>('statusShapes', statusShapes));
380+
properties.add(DiagnosticsProperty<bool>('focusBorders', focusBorders));
374381
}
375382

376383
@override
@@ -385,7 +392,8 @@ class YaruThemeData with Diagnosticable {
385392
other.pageTransitionsTheme == pageTransitionsTheme &&
386393
other.useMaterial3 == useMaterial3 &&
387394
other.visualDensity == visualDensity &&
388-
other.statusShapes == statusShapes;
395+
other.statusShapes == statusShapes &&
396+
other.focusBorders == focusBorders;
389397
}
390398

391399
@override
@@ -399,6 +407,7 @@ class YaruThemeData with Diagnosticable {
399407
useMaterial3,
400408
visualDensity,
401409
statusShapes,
410+
focusBorders,
402411
);
403412
}
404413
}

0 commit comments

Comments
 (0)