Skip to content

OCR window: index-mapped scrollbar for the subtitle grid - #13756

Merged
niksedk merged 2 commits into
mainfrom
claude/ocr-window-scrollbar-68ee51
Aug 17, 2026
Merged

OCR window: index-mapped scrollbar for the subtitle grid#13756
niksedk merged 2 commits into
mainfrom
claude/ocr-window-scrollbar-68ee51

Conversation

@niksedk

@niksedk niksedk commented Aug 17, 2026

Copy link
Copy Markdown
Member

The OCR subtitle grid had the same jumping scrollbar thumb as the main subtitle grid (#13579): Avalonia's VirtualizingStackPanel estimates the total pixel extent as realized rows + remaining × average realized height, so with variable-height rows the extent — and the native thumb — shifts on every wheel tick. The OCR grid is the worse case: every row holds a subtitle bitmap, so heights vary far more than the main grid's one-vs-two text lines.

Change

MakeSubtitleView now wraps the grid in TableViewIndexScrollBar, the same control the main grid already uses. It hides the native pixel-mapped vertical bar and docks a standalone one mapped to row indices:

  • Value = index of the first visible row (+ the fraction scrolled into it)
  • Maximum = row count − fully visible rows

Wheel, keyboard and the OCR loop's own ScrollIntoView still move the pixel offset; ScrollChanged re-derives the thumb. Trough press-and-hold (#12894) and shift+click jump come along with the control.

Verification

New test OcrWindow_UsesIndexMappedScrollBar asserts, on a 500-item OCR window:

  • the native vertical bar is hidden and the index bar is the visible one
  • Maximum is in row units (400–499), while the pixel extent is many times larger
  • setting Value = 250 places row 250 at the viewport top
  • scrolling the view down never moves the thumb backwards

779 Ocr/TableView UI tests pass; build clean.

Not included

The main grid also gets TableViewScrollAnchor.Attach (#13619) to hold the view steady when a row changes height. The OCR grid's rows do change height as text lands during OCR, but the OCR loop already scrolls to the current line itself, so an anchor could fight it — left for a separate look.

🤖 Generated with Claude Code

niksedk and others added 2 commits August 17, 2026 09:24
The OCR grid had the same jumping thumb as the main subtitle grid (#13579):
Avalonia's VirtualizingStackPanel estimates the pixel extent from the average
realized row height, which shifts on every scroll when rows differ in height -
and here every row holds a subtitle bitmap, so they differ a lot.

Wrap the grid in TableViewIndexScrollBar, the same control the main grid uses:
it hides the native pixel-mapped vertical bar and docks a standalone one mapped
to row indices, so the thumb tracks the first visible row instead of a moving
estimate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ctrl+plus/minus re-measures every row, and the ScrollViewer keeps its pixel
offset across that - so zooming moved the list to a different row: in a 500 line
grid, zooming in five notches dropped the view from row 250 to row 172, and
zooming out far enough pinned it to the end of the list.

The index-mapped bar knows which row was at the viewport top, so add
PreserveTopRow(): capture the row before the change and put it back once the new
heights are measured. Repeated calls while one is pending keep the first row, so
holding the zoom key down does not drift.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@niksedk

niksedk commented Aug 17, 2026

Copy link
Copy Markdown
Member Author

Follow-up: checked how the image zoom (Ctrl+plus/minus) interacts with the index bar.

The bar itself is fine — a headless probe on 500 rows shows Maximum, ViewportSize and Value all resync when the rows are re-measured, and Value keeps tracking the actual first visible row.

But the view jumped rows on zoom, because the ScrollViewer preserves its pixel offset across the re-measure (pre-existing, the native bar did the same):

at row 250:      value=250.00 max=495 first=250  offsetY=14000 extent=28000 rowH=56
zoom in x5:      value=172.84 max=497 first=172  offsetY=14000 extent=40500 rowH=81   <- 78 rows back
zoom out x10:    value=492.00 max=492 first=491  offsetY=19165 extent=19500 rowH=39   <- pinned to the end

Since the bar now speaks row indices, this is cheap to fix: TableViewIndexScrollBar.PreserveTopRow() captures the top row before the change and re-places it once the new heights are measured (repeated calls while one is pending keep the first row, so holding the zoom key doesn't drift). MakeSubtitleView calls it from the existing ImageMaxWidth handler. Same probe after the fix:

zoom in x5:      value=250.00 max=496 first=250  offsetY=20250 extent=40500 rowH=81
zoom out x10:    value=400.00 max=492 first=400  offsetY=15600 extent=19500 rowH=39

New test OcrWindow_ZoomKeepsTheRowAtTheTop zooms in five notches and back out ten, asserting the row heights really change and row 250 stays at the top; it fails without the fix. 780 Ocr/TableView tests pass.

@niksedk
niksedk merged commit e8959a8 into main Aug 17, 2026
1 check passed
@niksedk
niksedk deleted the claude/ocr-window-scrollbar-68ee51 branch August 17, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant