Skip to content

Commit 8ab9538

Browse files
fix Accessibility issues on the settings page for screen reader users. (#4667)
* Fix navigation bar accessibility for screen readers Added AutomationProperties.SetName to the CustomNavViewItem control to ensure screen readers correctly announce the localized names for navigation options like 'Software Updates' and 'Installed Packages'. * Fix comprehensive screen reader accessibility issues This commit resolves several UX issues to significantly improve the keyboard and screen reader navigation flow: - Added AutomationProperties.Name to the loading screen grid, operation list resizers, and the ExpandCollapseOpList button. - Disabled IsTabStop on the custom TitleBar so it doesn't get focused unnecessarily. - Fixed accessibility for view mode and filter options by assigning them grouping roles. - Added descriptive labels to the ToggleFiltersButton and MainToolbarButtonDropdown ('More actions'). - Bound AutomationProperties.Name to the dynamic text of MainToolbarButton so it correctly reads 'Install selected packages', etc. - Added AutomationProperties.Name to the BackButton on the package preferences dialog. - Added AutomationProperties.Name to the search mode radio buttons. - Removed 'ghost' tab stops following the Help button by explicitly setting IsTabStop='False' and OverflowButtonVisibility='Collapsed' on the CommandBar and surrounding layout containers. - Fixed CheckboxHeader by setting IsTabStop='False' and AccessibilityView='Raw'. - Improved package list item accessibility by building a custom AutomationPeer for PackageItemContainer that implements IToggleProvider and acts natively as a CheckBox role, allowing direct selection and status announcement without an extra tab stop. - Added AutomationProperties.Name and ToolTip labels ('User profile') to the dynamic GitHub login/cloud backup avatar UserControl in the main title bar. * Hide FiltersResizer from screen readers Set IsTabStop='False' and AccessibilityView='Raw' on the FiltersResizer to completely remove it from the keyboard navigation flow and accessibility tree, as it is purely visual and does not need to be focused by screen reader users. * Fix accessibility for Settings toggles and hyperlinks This commit improves the Settings screen accessibility: - Fixed an issue where toggle switches (e.g., 'Install prerelease versions') only announced their state ('Enabled'/'Disabled') by properly binding their AutomationProperties.Name to the setting's descriptive text across CheckboxCard, CheckboxButtonCard, and SecureCheckboxCard. - Fixed an issue where hyperlinks (e.g., 'Become a translator') announced strictly as 'link' without text by properly exposing the internal text of TranslatedTextBlock user controls to UI Automation. * Enforce SettingsCard accessibility groupings on load This commit adds a Loaded event handler to the custom SettingsCard widgets (ButtonCard, CheckboxButtonCard, CheckboxCard, SecureCheckboxCard) to strictly enforce their AutomationProperties.Name and LocalizedControlType='grouping'. This prevents the underlying Windows Community Toolkit logic from overriding the grouping name with the internal button's face text upon rendering. * Fix whitespace formatting This commit resolves trailing whitespace formatting issues reported by CI. * Fix description labels on main SettingsPageButtons This commit applies AutomationProperties.Name and AutomationProperties.HelpText to the SettingsPageButton custom widget and enforces them on the Loaded event, ensuring that screen readers properly announce the button text alongside its descriptive subtext.
1 parent ff915f0 commit 8ab9538

7 files changed

Lines changed: 114 additions & 27 deletions

File tree

src/UniGetUI/Controls/SettingsWidgets/ButtonCard.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,16 @@ public string ButtonText
1616
set => _button.Content = CoreTools.Translate(value);
1717
}
1818

19+
private string _text = "";
1920
public string Text
2021
{
21-
set => Header = CoreTools.Translate(value);
22+
set
23+
{
24+
_text = CoreTools.Translate(value);
25+
Header = _text;
26+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _text);
27+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping");
28+
}
2229
}
2330

2431
public new event EventHandler<EventArgs>? Click;
@@ -31,6 +38,12 @@ public ButtonCard()
3138
Click?.Invoke(this, EventArgs.Empty);
3239
};
3340
Content = _button;
41+
42+
Loaded += (s, e) =>
43+
{
44+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _text);
45+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping");
46+
};
3447
}
3548
}
3649
}

src/UniGetUI/Controls/SettingsWidgets/CheckboxButtonCard.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ public bool Checked
4343

4444
public string CheckboxText
4545
{
46-
set => _textblock.Text = CoreTools.Translate(value);
46+
set
47+
{
48+
_textblock.Text = CoreTools.Translate(value);
49+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text);
50+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping");
51+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(_checkbox, _textblock.Text);
52+
}
4753
}
4854

4955
public string ButtonText
@@ -93,6 +99,12 @@ public CheckboxButtonCard()
9399
};
94100

95101
Button.Click += (s, e) => Click?.Invoke(s, e);
102+
103+
Loaded += (s, e) =>
104+
{
105+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text);
106+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping");
107+
};
96108
}
97109
}
98110
}

src/UniGetUI/Controls/SettingsWidgets/CheckboxCard.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@ public bool Checked
3939

4040
public string Text
4141
{
42-
set => _textblock.Text = CoreTools.Translate(value);
42+
set
43+
{
44+
_textblock.Text = CoreTools.Translate(value);
45+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text);
46+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping");
47+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(_checkbox, _textblock.Text);
48+
}
4349
}
4450

4551
public string WarningText
@@ -95,6 +101,12 @@ public CheckboxCard()
95101

96102
_checkbox.HorizontalAlignment = HorizontalAlignment.Stretch;
97103
_checkbox.Toggled += _checkbox_Toggled;
104+
105+
Loaded += (s, e) =>
106+
{
107+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text);
108+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping");
109+
};
98110
}
99111

100112
protected virtual void _checkbox_Toggled(object sender, RoutedEventArgs e)

src/UniGetUI/Controls/SettingsWidgets/SecureCheckboxCard.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@ public bool Checked
5353

5454
public string Text
5555
{
56-
set => _textblock.Text = CoreTools.Translate(value);
56+
set
57+
{
58+
_textblock.Text = CoreTools.Translate(value);
59+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text);
60+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping");
61+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(_checkbox, _textblock.Text);
62+
}
5763
}
5864

5965
public string WarningText
@@ -112,6 +118,12 @@ public SecureCheckboxCard()
112118

113119
_checkbox.HorizontalAlignment = HorizontalAlignment.Stretch;
114120
_checkbox.Toggled += (s, e) => _ = _checkbox_Toggled();
121+
122+
Loaded += (s, e) =>
123+
{
124+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textblock.Text);
125+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetLocalizedControlType(this, "grouping");
126+
};
115127
}
116128

117129
protected virtual async Task _checkbox_Toggled()

src/UniGetUI/Controls/SettingsWidgets/SettingsPageButton.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,26 @@ namespace UniGetUI.Interface.Widgets
1010
{
1111
public partial class SettingsPageButton : SettingsCard
1212
{
13+
private string _text = "";
1314
public string Text
1415
{
15-
set => Header = CoreTools.Translate(value);
16+
set
17+
{
18+
_text = CoreTools.Translate(value);
19+
Header = _text;
20+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _text);
21+
}
1622
}
1723

24+
private string _underText = "";
1825
public string UnderText
1926
{
20-
set => Description = CoreTools.Translate(value);
27+
set
28+
{
29+
_underText = CoreTools.Translate(value);
30+
Description = _underText;
31+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetHelpText(this, _underText);
32+
}
2133
}
2234

2335
public IconType Icon
@@ -30,6 +42,14 @@ public SettingsPageButton()
3042
CornerRadius = new CornerRadius(8);
3143
HorizontalAlignment = HorizontalAlignment.Stretch;
3244
IsClickEnabled = true;
45+
46+
Loaded += (s, e) =>
47+
{
48+
if (!string.IsNullOrEmpty(_text))
49+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _text);
50+
if (!string.IsNullOrEmpty(_underText))
51+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetHelpText(this, _underText);
52+
};
3353
}
3454
}
3555
}

src/UniGetUI/Controls/TranslatedTextBlock.xaml.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ public TextWrapping WrappingMode
4343
public TranslatedTextBlock()
4444
{
4545
InitializeComponent();
46+
Loaded += (s, e) =>
47+
{
48+
if (Parent is Microsoft.UI.Xaml.Controls.Primitives.ButtonBase parentBtn && string.IsNullOrEmpty(Microsoft.UI.Xaml.Automation.AutomationProperties.GetName(parentBtn)))
49+
{
50+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(parentBtn, _textBlock.Text);
51+
}
52+
};
4653
}
4754

4855
public void ApplyText(string? text)
@@ -51,7 +58,13 @@ public void ApplyText(string? text)
5158
{
5259
if (text is not null)
5360
__text = CoreTools.Translate(text);
54-
_textBlock?.Text = __prefix + __text + __suffix;
61+
_textBlock.Text = __prefix + __text + __suffix;
62+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(this, _textBlock.Text);
63+
64+
if (IsLoaded && Parent is Microsoft.UI.Xaml.Controls.Primitives.ButtonBase parentBtn)
65+
{
66+
Microsoft.UI.Xaml.Automation.AutomationProperties.SetName(parentBtn, _textBlock.Text);
67+
}
5568
}
5669
catch (Exception ex)
5770
{

src/UniGetUI/Pages/SettingsPages/GeneralPages/General.xaml

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
<HyperlinkButton
4242
Padding="0"
4343
NavigateUri="https://github.com/Devolutions/UniGetUI/wiki#translating-wingetui"
44+
AutomationProperties.Name="Is your language missing or incomplete? Become a translator"
4445
>
4546
<widgets:TranslatedTextBlock Text="Become a translator" />
4647
</HyperlinkButton>
@@ -112,27 +113,31 @@
112113
Text="Reset UniGetUI"
113114
/>
114115

115-
<widgets:TranslatedTextBlock
116-
Margin="4,32,4,8"
117-
FontWeight="SemiBold"
118-
Text="Related settings"
119-
/>
116+
<StackPanel AutomationProperties.Name="Related settings" AutomationProperties.LocalizedControlType="grouping">
117+
<widgets:TranslatedTextBlock
118+
Margin="4,32,4,8"
119+
FontWeight="SemiBold"
120+
Text="Related settings"
121+
/>
120122

121-
<controls:SettingsCard
122-
Click="InterfaceSettingsButton_Click"
123-
CornerRadius="8"
124-
IsClickEnabled="True"
125-
>
126-
<controls:SettingsCard.Header>
127-
<widgets:TranslatedTextBlock Text="User interface preferences" />
128-
</controls:SettingsCard.Header>
129-
<controls:SettingsCard.Description>
130-
<widgets:TranslatedTextBlock Text="Application theme, startup page, package icons, clear successful installs automatically" />
131-
</controls:SettingsCard.Description>
132-
<controls:SettingsCard.HeaderIcon>
133-
<widgets:LocalIcon Icon="Interactive" />
134-
</controls:SettingsCard.HeaderIcon>
135-
</controls:SettingsCard>
123+
<controls:SettingsCard
124+
Click="InterfaceSettingsButton_Click"
125+
CornerRadius="8"
126+
IsClickEnabled="True"
127+
AutomationProperties.Name="User interface preferences"
128+
AutomationProperties.HelpText="Application theme, startup page, package icons, clear successful installs automatically"
129+
>
130+
<controls:SettingsCard.Header>
131+
<widgets:TranslatedTextBlock Text="User interface preferences" />
132+
</controls:SettingsCard.Header>
133+
<controls:SettingsCard.Description>
134+
<widgets:TranslatedTextBlock Text="Application theme, startup page, package icons, clear successful installs automatically" />
135+
</controls:SettingsCard.Description>
136+
<controls:SettingsCard.HeaderIcon>
137+
<widgets:LocalIcon Icon="Interactive" />
138+
</controls:SettingsCard.HeaderIcon>
139+
</controls:SettingsCard>
140+
</StackPanel>
136141
</StackPanel>
137142
</ScrollViewer>
138143
</Page>

0 commit comments

Comments
 (0)