Skip to content

Fix Tab/Shift+Tab traversal in tab controls and name the tab scroll buttons - #5546

Open
Gabriel Dufresne (GabrielDuf) wants to merge 2 commits into
mainfrom
fix/tab-control-keyboard-focus
Open

Gabriel Dufresne (GabrielDuf) wants to merge 2 commits into
mainfrom
fix/tab-control-keyboard-focus

Conversation

@GabrielDuf

@GabrielDuf Gabriel Dufresne (GabrielDuf) commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Root cause

Avalonia's ItemsControl.OnGotFocus records any focused descendant as the control's TabOnceActiveElement, and TabControl copies that value onto its tab-header panel (TabNavigation=Once). As soon as something inside the selected tab's content, or a template part such as the tab-strip scroll chevrons, got focus, the header panel's single tab stop pointed at an element outside the panel:

  • Tabbing back into the tab strip jumped straight to the last-focused content control, skipping the header and everything before it ("focus jumps to Skip minor updates").
  • Shift+Tab resolved the header stop to the element that already had focus, so reverse navigation got stuck on the first content control, or on the chevron button when it was visible.

This is still the behaviour in Avalonia 12.1.0.

Fix

  • TabControlFocusGuard: an app-wide GotFocus class handler on TabControl, installed in App.Initialize next to ButtonActivationGuard. It runs after ItemsControl's handler and, unless the focused element is one of that control's own tab items, resets TabOnceActiveElement to the selected tab item. The ownership check uses IndexFromContainer instead of a type test, so a nested TabControl's headers still reset the outer one. Because it's app-wide, the About, Manage shortcuts and Operation history tab controls are fixed too.
  • Chevron names: the two scroll buttons contain only a Path, so screen readers announced Avalonia.Controls.Shapes.Path. They now get the WinUI TabView wording, Scroll tab list backward/forward, set from code-behind so the shared template stays independent of the view model. The two keys are added to all 60 language files. These are accessibility names only; nothing visible changes.

Verification

  • A headless Avalonia 12.0.4 harness driving real Tab/Shift+Tab key presses against a replica of the install-options template reproduced all three reported symptoms without the guard. With the real TabControlFocusGuard.cs linked in, traversal is symmetric in every case: chevrons hidden, chevrons visible and focusable, and a nested TabControl.
  • The build passes; dotnet format whitespace and style both exit 0; Verify-Translations.ps1 and the boundary check pass; UniGetUI.Core.LanguageEngine.Tests passes (298/298).
  • Not yet checked with NVDA or Narrator, and the three other tab dialogs haven't been exercised in the real app.

Closes #5543

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The Kurdish screen-reader names need to match the locale’s existing Sorani language variety and script.

1 open issue
What changed in this PR

Addresses #5543 by correcting tab-control keyboard traversal and naming the installation-options tab scroll buttons for screen readers.

Changes:

  • Registers an app-wide focus guard that restores the selected tab as the header’s tab stop.
  • Adds translated accessibility names for both scroll buttons.
  • Adds the two strings to all 60 language files.
File Description
src/​UniGetUI.Avalonia/​Views/​DialogPages/​InstallOptionsControl.axaml.cs Sets translated scroll-button names.
src/​UniGetUI.Avalonia/​Infrastructure/​TabControlFocusGuard.cs Corrects tab-header focus tracking.
src/​UniGetUI.Avalonia/​App.axaml.cs Installs the focus guard.
src/​Languages/​lang_zh_TW.json Adds Traditional Chinese labels.
src/​Languages/​lang_zh_CN.json Adds Simplified Chinese labels.
src/​Languages/​lang_vi.json Adds Vietnamese labels.
src/​Languages/​lang_uz.json Adds Uzbek labels.
src/​Languages/​lang_ur.json Adds Urdu labels.
src/​Languages/​lang_uk.json Adds Ukrainian labels.
src/​Languages/​lang_tr.json Adds Turkish labels.
src/​Languages/​lang_tl.json Adds Tagalog labels.
src/​Languages/​lang_th.json Adds Thai labels.
src/​Languages/​lang_ta.json Adds Tamil labels.
src/​Languages/​lang_sv.json Adds Swedish labels.
src/​Languages/​lang_sr.json Adds Serbian labels.
src/​Languages/​lang_sq.json Adds Albanian labels.
src/​Languages/​lang_sl.json Adds Slovenian labels.
src/​Languages/​lang_sk.json Adds Slovak labels.
src/​Languages/​lang_si.json Adds Sinhala labels.
src/​Languages/​lang_sa.json Adds Sanskrit labels.
src/​Languages/​lang_ru.json Adds Russian labels.
src/​Languages/​lang_ro.json Adds Romanian labels.
src/​Languages/​lang_pt_PT.json Adds European Portuguese labels.
src/​Languages/​lang_pt_BR.json Adds Brazilian Portuguese labels.
src/​Languages/​lang_pl.json Adds Polish labels.
src/​Languages/​lang_nn.json Adds Norwegian Nynorsk labels.
src/​Languages/​lang_nl.json Adds Dutch labels.
src/​Languages/​lang_nb.json Adds Norwegian Bokmål labels.
src/​Languages/​lang_mr.json Adds Marathi labels.
src/​Languages/​lang_mk.json Adds Macedonian labels.
src/​Languages/​lang_lt.json Adds Lithuanian labels.
src/​Languages/​lang_ku.json Adds Kurdish labels.
src/​Languages/​lang_ko.json Adds Korean labels.
src/​Languages/​lang_kn.json Adds Kannada labels.
src/​Languages/​lang_ka.json Adds Georgian labels.
src/​Languages/​lang_ja.json Adds Japanese labels.
src/​Languages/​lang_it.json Adds Italian labels.
src/​Languages/​lang_id.json Adds Indonesian labels.
src/​Languages/​lang_hu.json Adds Hungarian labels.
src/​Languages/​lang_hr.json Adds Croatian labels.
src/​Languages/​lang_hi.json Adds Hindi labels.
src/​Languages/​lang_he.json Adds Hebrew labels.
src/​Languages/​lang_gu.json Adds Gujarati labels.
src/​Languages/​lang_gl.json Adds Galician labels.
src/​Languages/​lang_fr.json Adds French labels.
src/​Languages/​lang_fil.json Adds Filipino labels.
src/​Languages/​lang_fi.json Adds Finnish labels.
src/​Languages/​lang_fa.json Adds Persian labels.
src/​Languages/​lang_et.json Adds Estonian labels.
src/​Languages/​lang_es.json Adds Spanish labels.
src/​Languages/​lang_es-MX.json Adds Mexican Spanish labels.
src/​Languages/​lang_eo.json Adds Esperanto labels.
src/​Languages/​lang_en.json Defines English source labels.
src/​Languages/​lang_el.json Adds Greek labels.
src/​Languages/​lang_de.json Adds German labels.
src/​Languages/​lang_da.json Adds Danish labels.
src/​Languages/​lang_cs.json Adds Czech labels.
src/​Languages/​lang_ca.json Adds Catalan labels.
src/​Languages/​lang_bn.json Adds Bengali labels.
src/​Languages/​lang_bg.json Adds Bulgarian labels.
src/​Languages/​lang_be.json Adds Belarusian labels.
src/​Languages/​lang_ar.json Adds Arabic labels.
src/​Languages/​lang_af.json Adds Afrikaans labels.

Review effort: Balanced

Comment thread src/Languages/lang_ku.json Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The application-wide focus workaround still needs real-app keyboard and screen-reader validation across the affected dialogs.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@randy-but-a-ro randy-but-a-ro Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Pull request was approved automatically: the AI review is complete and all its review threads are resolved. 🎉

Integration Details
{
	"deliveryId": "6bf873b0-cbe5-11f1-83db-79e51a2375ce",
	"headSha": "f08004a550a62254c73bbb61f087540ad5cf8296",
	"reviewer": "copilot-pull-request-reviewer[bot]"
}

@keyang556

Copy link
Copy Markdown

Maybe related to AvaloniaUI/Avalonia#22003

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Installation options has inconsistent Tab/Shift+Tab navigation, reverse focus gets stuck and some unlabel buttons

3 participants