Convert the main subtitle grid to TableView (screen reader accessibility, #13015) - #13017
Merged
Conversation
TableView rows are ListBoxItems, so keyboard focus moves to the current row and UI Automation exposes it - with the DataGrid, focus stayed on the grid control itself, which made the grid unusable with a screen reader (issue #13015). Rows also get an accessible name ("number: text"). The DataGrid-specific machinery is replaced by a reusable TableViewExtras component (Logic/TableViewExtras.cs) so future TableView conversions can share it: - SeTableViewColumn + TableViewColumnManager: bindable column IsVisible (TableViewColumn has none) implemented by adding/removing columns in order, plus Tag/MinWidth for width persistence and auto-fit. - TableViewDragSelect: drag-to-select with accelerating edge auto-scroll. - Scroll helpers (center row / ensure fully visible) via ScrollViewer offset instead of the DataGrid reflection hack. - Row hit-testing, page size, header/scrollbar hit checks, FocusRow. Selection now goes through the ListBox SelectionModel with batch updates, which replaces both the ItemsSource detach hack for large selections (#11529) and the incremental drag-select bookkeeping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The empty trailing column existed to keep the DataGrid's overlay scrollbar off the outermost text column (issue #12351). With TableView the scrollbar comes from a regular ScrollViewer, so setting AllowAutoHide=false keeps it at full width in its own reserved layout space instead of the thin expand-on-hover overlay - no gutter column (or its stray header line) needed, and the bar is an easier drag target. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Alternating rows: the nth-child style never applied to virtualized TableViewRow containers; tint per container from ContainerPrepared/ ContainerIndexChanged instead, which also survives insert/remove/recycle. Selection and hover still win because the row theme's pseudo-class styles set the template Border's background directly. - Column headers: a bit more breathing room - 2px more above the text and 1px more below. - Grid lines: drawn as per-cell borders they read stronger than the old DataGrid's gridline pass, so the body separators now use a fainter brush (0.22 opacity vs 0.5); the header keeps the stronger line. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The alternating row background was applied correctly (verified with a headless Avalonia harness) but invisible: TableView's theme paints SystemControlBackgroundChromeMediumLowBrush (~#2B2B2B in dark mode) as the control background, and the default alternating tint is #2D2D2D - a two shade difference. The DataGrid had no background of its own, so rows sat on SE's darker panel background and the same tint contrasted fine. Transparent restores that backdrop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Cell backgrounds: TableViewColumn.HorizontalContentAlignment defaults to Left and is copied onto each cell, so a template's colored background Border shrink-wrapped the text instead of filling the cell like the DataGrid did. SeTableViewColumn now defaults to Stretch (verified with the headless harness: the border fills the cell in both directions). - Header lines: with grid lines set to None the header's always-on right/bottom borders are the only separators in the grid, and at the full 0.5-opacity border brush they read much stronger than the old DataGrid header - use the faint grid-line brush for them too. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
niksedk
marked this pull request as ready for review
July 31, 2026 09:17
This was referenced Jul 31, 2026
potplayer-fanpack
pushed a commit
to potplayer-fanpack/subtitleedit
that referenced
this pull request
Jul 31, 2026
Batch 1 of the TableView sweep (after the main grid in SubtitleEdit#13017): every DataGrid without sorting becomes a TableView built from the shared TableViewExtras component, so keyboard focus lands on rows and screen readers can follow the current line in these dialogs too (issue SubtitleEdit#13015). Converted: BinaryEdit, Bookmarks, ErrorList, FindText, Compare, ExportCustomTextFormat, ExportImageBased, ImportCsvXlsxCustomColumns, RestoreAutoBackup, BatchErrorList, FixNames, MergeContinuationLines, MergeTwoSubtitles, SortBy, FindDoubleLines, FindDoubleWords, AutoTranslate, CopyPasteTranslate, ShotChanges (both windows), ActorVoiceMapping, ReviewSpeech, ReviewSpeechHistory, VideoOcr. Notable mappings: - Former Auto-width columns get fixed pixel widths (TableView has no content sizing); the main text column stays the star column. - DataGridCheckboxMultiSelect: extended selection is native ListBox behavior; its Space-toggles-checkbox piece is now TableViewExtras.AddSpaceToggle, used by BinaryEdit, FixNames and MergeContinuationLines. The helper class stays for the remaining DataGrids. - BinaryEdit's mode-dependent zone column uses TableViewColumnManager. - VideoOcr's editable text column becomes an in-cell two-way TextBox (TableView has no cell editing); focusing it selects its row. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 tasks
pull Bot
pushed a commit
to A-Archives-and-Forks/subtitleedit
that referenced
this pull request
Jul 31, 2026
Batch 2 of the TableView sweep (SubtitleEdit#13017, SubtitleEdit#13018): every remaining DataGrid with CanUserSortColumns=true but no deliberate SortMemberPath sorting. Rows take keyboard focus, so the screen-reader fix (SubtitleEdit#13015) now covers these dialogs too. Sorting was decided per grid. In the DataGrid only plain text columns ever sorted (a view-level sort); TableViewHeaderSorter reorders the backing collection, so it is wired only where the list order is presentation-only and nothing consumes collection order: - Wired (12 grids): FontCollector, FindRule, ManualChosenEncoding, PickFontName, PickLayerFilter, PickRuleProfile, PickOnlineSubtitle, and the track lists in PickMatroskaTrack/PickMp4Track/PickTsTrack/ PickVobSubLanguage - with typed keys (numeric stream ids, nullable comparers) and header-double-click guards where double-tap accepted the dialog. - Dropped everywhere else: subtitle/fix previews in timeline order, ASSA/SSA style and attachment lists (order is written to the file), MultipleReplace exports, batch queues, join/split file lists, and embedded-track lists (order is the output track order). Each carries a code comment with the reason. Other notable mappings: - DataGridCheckboxMultiSelect replaced by native extended selection + TableViewExtras.AddSpaceToggle (AiReview, ApplyDurationLimits, ConvertActors, FixNetflixErrors incl. its canToggle semantics, PickLayerFilter, RemoveTextForHearingImpaired; RemoveUnicodeCharacters hand-rolls it to coexist with its in-cell ReplaceWith TextBox editor). - Home/End first/last-row navigation hoisted into TableViewExtras.AttachHomeEndNavigation (sync + Assa/Ssa windows). - AssaStyles' DataGridCollectionView category filter replaced by a plain rebuilt ObservableCollection view. - IsFocused checks on grids became IsKeyboardFocusWithin (focus lands on row containers now) in CutVideo/PickMatroskaTrack/PickVobSubLanguage. - Sync-point lists show a column header now (TableView cannot hide headers). Remaining DataGrids: Shortcuts, BatchConvert and FixCommonErrors (real SortMemberPath sorting) - batch 3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #13015.
Why
The Avalonia DataGrid keeps keyboard focus on the grid control itself — rows/cells never become the focused UI Automation element, and its automation peers implement no UIA patterns at all, so screen readers cannot tell which line is current. Upstream has deprecated the DataGrid (bug-fix-only), so this follows the TableView pilot path instead (#12704, #13001):
TableViewRowis aListBoxItem, so keyboard focus genuinely moves to the current row, UIA focus follows automatically, and selection is exposed viaISelectionItemProvider— no accessibility-specific code needed. Rows additionally get an accessible name ("number: text") so announcements are meaningful.What
TableViewwith the same columns, templates, context menu, drag & drop, width persistence and auto-fit.src/UI/Logic/TableViewExtras.csso the DataGrid-era machinery has a shared TableView home for future conversions:SeTableViewColumn+TableViewColumnManager— bindable columnIsVisible(TableView has none) implemented by inserting/removing columns in order;Tag/MinWidthfor width persistence + auto-fit.TableViewDragSelect— drag-to-select with accelerating edge auto-scroll.ScrollViewer.Offset, replacing theProcessVerticalScrollreflection hack.FocusRow(focuses the selected row's container for UIA).SelectionModelwith batch updates — replaces both the ItemsSource detach hack for large selections (SE V5.0.0-rc3 hangs when Modify Selection results in a large number of lines #11529) and the incremental drag-select bookkeeping.SelectionMode.AlwaysSelectedreplaces the "re-select last removed item" workaround.Behavior differences vs the DataGrid
DataGridScrollBarBehavior(shift+click jump on the trough is not carried over).Testing so far
Builds clean (0 errors, warning count unchanged vs main). Runtime smoke testing still to do — hence draft. Main things to verify by hand:
🤖 Generated with Claude Code