Skip to content

Keyboard Arrow Navigation - #180

Open
aelrased wants to merge 3 commits into
NuvioMedia:Devfrom
aelrased:keyboard-nav
Open

Keyboard Arrow Navigation#180
aelrased wants to merge 3 commits into
NuvioMedia:Devfrom
aelrased:keyboard-nav

Conversation

@aelrased

@aelrased aelrased commented Jun 29, 2026

Copy link
Copy Markdown

Summary

PR type

  • Reproducible bug fix
  • UI glitch/bug fix
  • Behavior bug/regression fix
  • Small maintenance only, with no UI or behavior change
  • Docs accuracy fix
  • Translation/localization only
  • Approved larger or directional change

Why

Desktop scope

Issue or approval

UI / behavior impact

  • No UI change
  • No behavior change
  • UI changed only to fix a documented glitch/bug
  • Behavior changed only to fix a documented bug/regression
  • UI change has explicit maintainer approval
  • Behavior change has explicit maintainer approval

Policy check

  • I have read and understood CONTRIBUTING.md.
  • This PR is small, focused, and limited to one problem.
  • This PR is scoped to the desktop app, desktop packaging, desktop documentation, or shared code required for desktop behavior.
  • This PR is not cosmetic-only.
  • Any UI change fixes a linked glitch/bug and includes visual proof, or this PR has no UI change.
  • Any behavior change fixes a linked bug/regression or has explicit approval, or this PR has no behavior change.
  • This PR does not bundle unrelated refactors, cleanups, formatting, or drive-by changes.
  • This PR does not add dependencies, architecture changes, migrations, or product-direction changes without explicit approval.
  • I listed the testing performed below.

UI polish, cosmetic-only changes, minor behavior tweaks, and unapproved product changes will be closed without review.

Scope boundaries

Testing

Screenshots / Video

Breaking changes

Linked issues

@aelrased

aelrased commented Jun 29, 2026

Copy link
Copy Markdown
Author

Summary — Keyboard Arrow Navigation

Problem

  • onPreviewKeyEvent does not work without a focused element in Compose Desktop
  • LazyRow/LazyColumn consume arrow key events before they reach the top level

Solution

Use AWT KeyEventDispatcher (same approach as installDesktopAppFullscreenShortcuts) to intercept arrow keys outside the Compose Focus system entirely.

New Files (5)

File Description
PlatformKeyboardNavigation.kt expect declaration
PlatformKeyboardNavigation.desktop.kt AWT KeyEventDispatcher → focusManager.moveFocus() + fallback FocusDirection.Next
PlatformKeyboardNavigation.android.kt no-op
PlatformKeyboardNavigation.ios.kt no-op
NuvioFocusable.kt Modifier.nuvioFocusBorder(shape) — draws a 3dp border using the theme's focusRing color around the element's actual shape

Modified Files (5)

File Change
App.kt Moved PlatformKeyboardNavigation() to App() root (works on all screens). Added nuvioFocusBorder to TabletTopPillItem and DesktopSidebarItem
NavigationBar.kt Added nuvioFocusBorder to all 3 NavItem overloads
ProfileSelectionScreen.kt Added nuvioFocusBorder to ProfileAvatarCard and AddProfileCard
ShelfComponents.kt Added nuvioFocusBorder to NuvioPosterCard
SettingsComponents.kt Added nuvioFocusBorder to SettingsSidebarItem, SettingsNavigationRow, SettingsSwitchRow

Focusable Elements

Bottom nav bar, desktop sidebar, tablet top pills, poster cards, profile avatar cards, add profile button, and settings items.

@aelrased aelrased changed the title Keyboard nav Keyboard Arrow Navigation Jun 29, 2026
Column(
modifier = Modifier
.posterCardClickable(onClick = onClick, onLongClick = onLongClick)
.nuvioFocusBorder(cardShape)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This part isn't working for the poster cards for me because .posterCardClickable is swallowing the events it needs. If you put .nuvioFocusBorder first ahead of .posterCardClickable you get the desired functionality.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for catching that! Fixed in the latest push.

What changed:

  • Moved .nuvioFocusBorder() from the inner Box to the Column modifier on SeasonPosterButton, placed before .posterCardClickable so the focus ring receives keyboard events before the click handler swallows them.
  • The episode card already had the correct order (.nuvioFocusBorder before .posterCardClickable), so no change needed there.

All other components (StreamCard, StreamsScreen filter chips, DetailActionButtons, DetailCastSection avatars) were already correct.

@aelrased

Copy link
Copy Markdown
Author

@DustinBryant Good catch! I've pushed a fix — moved .nuvioFocusBorder(cardShape) before .posterCardClickable(...) so the focus ring can receive keyboard events before the clickable modifier intercepts them. The change is on the keyboard-nav branch now.

aelrased pushed a commit to aelrased/NuvioDesktop that referenced this pull request Aug 31, 2026
- Merged upstream NuvioMedia/NuvioDesktop Dev (58+ commits)
- Reverted custom player modifications, adopted upstream Linux player
- Deleted obsolete files (LinuxDesktopIntegration, old player hosts, etc.)
- Rewrote PR NuvioMedia#180 keyboard navigation for current codebase:
  - PlatformKeyboardNavigation: AWT KeyEventDispatcher for arrow keys
  - NuvioFocusable: Modifier.nuvioFocusBorder() for visual focus rings
  - Applied focus borders to NavigationBar, ShelfComponents, ProfileSelection, Settings
  - No-op implementations for Android/iOS
@DustinBryant

Copy link
Copy Markdown

@KhooLy can we get some eyes on this?

It would address:
#267
#33
#305

This change would bring in many more users with Home Theatre PCs (HTPC). Right now we're mostly stuck with Stremio but desperately want Nuvio.

@KhooLy

KhooLy commented Sep 5, 2026

Copy link
Copy Markdown
Member

Good direction, but the global KeyEventDispatcher currently consumes every arrow key press across the app, even when focus movement isn't handled. This can interfere with text fields, sliders, scrolling and other components that need their own arrow-key behavior. Please only consume the event when appropriate, and make the focus/navigation behavior desktop-only rather than changing shared mobile focus behavior. Also please rebase onto current Dev and update the PR description/testing, since this is a behavior/UI feature rather than maintenance.

Add keyboard arrow navigation support for desktop using AWT KeyEventDispatcher,
with visual focus rings (border) on all focusable UI elements.

New files:
- PlatformKeyboardNavigation.kt (expect/actual)
- PlatformKeyboardNavigation.desktop.kt (AWT KeyEventDispatcher)
- PlatformKeyboardNavigation.android.kt / .ios.kt (no-op)
- NuvioFocusable.kt (Modifier.nuvioFocusBorder)

Modified files:
- App.kt: Register PlatformKeyboardNavigation in AppEnvironment
- NavigationBar.kt: Focus border on all NavItem variants
- ShelfComponents.kt: Focus border on NuvioPosterCard
- ProfileSelectionScreen.kt: Focus border on ProfileAvatarCard and AddProfileCard
- SettingsComponents.kt: Focus border on SettingsSidebarItem, SettingsNavigationRow, SettingsSwitchRow
@aelrased

aelrased commented Sep 7, 2026

Copy link
Copy Markdown
Author

Thanks for the review feedback - all three points have been addressed in the latest push:

  1. Smart arrow key consumption - PlatformKeyboardNavigation.desktop.kt now inspects the focused AWT component before consuming arrow keys. It only consumes when the focus is on a Compose-managed focusable (not JTextField, JTextArea, JSpinner, JSlider, JScrollPane, or Scrollable), so text fields, sliders, scroll panes and other AWT components retain their native arrow-key behavior.

  2. Desktop-only - .focusable() in NuvioFocusable.kt is now guarded with isDesktop, so mobile platforms are completely unaffected.

  3. Rebased onto current Dev - Branch has been rebased onto upstream/Dev (21aabee).

Other changes in this push:

  • Fixed modifier ordering on SeasonPosterButton - .nuvioFocusBorder() moved before .posterCardClickable() per the inline review comment, so the focus ring receives keyboard events before the click handler can swallow them.
  • Added focus rings to StreamCard, StreamsScreen filter chips, DetailActionButtons (Play / More), DetailCastSection avatars, and all episode card variants.

Build verified successfully. Ready for another review.

ahmed added 2 commits September 6, 2026 23:45
…nents

- PlatformKeyboardNavigation: skip consuming arrows when focused on
  JTextField, JTextArea, JSpinner, JSlider, JScrollPane, Scrollable
- NuvioFocusable: guard .focusable() with isDesktop (desktop-only)
- DetailActionButtons: nuvioFocusBorder on Play and More buttons
- DetailSeriesContent: nuvioFocusBorder on season chips, season posters,
  episode horizontal cards, episode list cards
- DetailCastSection: nuvioFocusBorder on cast avatar circles
- StreamCard: nuvioFocusBorder on stream link cards
- StreamsScreen: nuvioFocusBorder on provider filter chips
…Button

The focus ring was on the inner Box but posterCardClickable on the Column
was swallowing keyboard events. Move nuvioFocusBorder to the Column modifier
chain ahead of posterCardClickable so focus works correctly.
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.

3 participants