Skip to content

Add an animated focus ring across the library and in-game UI#1655

Merged
utkarshdalal merged 1 commit into
utkarshdalal:masterfrom
VinceBT:feature/focus-ring-standalone
Jul 7, 2026
Merged

Add an animated focus ring across the library and in-game UI#1655
utkarshdalal merged 1 commit into
utkarshdalal:masterfrom
VinceBT:feature/focus-ring-standalone

Conversation

@VinceBT

@VinceBT VinceBT commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What

Replaces the static gradient focus borders with a rotating sweep-gradient ring that highlights the focused element when navigating with a controller or D-pad. It only animates while focused, so unfocused elements schedule no frames.

Where

  • Library grid and list cards
  • Library tab bar
  • Game page action buttons and info cards
  • In-game quick menu
  • In-game screen effects panel rows

Notes

The card ring is drawn on each card's outer box rather than the Material Card itself, since the card's own background would otherwise cover it.

Before:
https://github.com/user-attachments/assets/ea2855df-2c8d-4da5-87ad-7ff363544a4e

After:
https://github.com/user-attachments/assets/4bea6134-0470-4cea-821d-968151034a74


Summary by cubic

Adds an animated sweep‑gradient focus ring across the library and in‑game UI, replacing static borders. It runs only while focused and removes focus flicker and tab‑bar focus yanks.

  • New Features

    • Introduced Modifier.focusRing(...) in FocusRing.kt for a rotating sweep‑gradient border; configurable shape and width; animation runs only while focused.
    • Applied to library grid/list cards (ring draws on the outer container), library tab bar (aligned to the 40dp pill), game page primary/action buttons and info cards, in‑game quick menu, and screen effects rows via AccentActionRow.
  • Bug Fixes

    • Eliminated focus‑ring flicker by keeping a stable transition and gating only the draw; unfocused elements don’t animate.
    • Fixed tab‑bar issues: removed icon flicker by applying focus scale and alpha in one graphicsLayer, and prevented grid focus from pulling back while the tab bar is focused by tracking tabBarHasFocus and canceling deferred focus requests.

Written for commit 0220307. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added an accented action row component with improved clickable button layout and focus-tinted visuals.
    • Introduced a shared, animated focus ring for consistent focus highlighting.
  • UI Improvements
    • Updated library cards, tab bars, quick menu controls, and screen actions to use the unified focus ring styling.
    • Refined tab navigation so focus doesn’t jump unexpectedly between the tab bar and content.
    • Updated library and screen effect reset controls to match the new accent action row appearance.

@VinceBT VinceBT requested a review from utkarshdalal as a code owner July 1, 2026 21:22
@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a reusable focusRing modifier and AccentActionRow, then updates QuickMenu, ScreenEffectsPanel, and library UI controls to use shared focus styling. LibraryScreen also tracks tab-bar focus to suppress focus bootstrap while the tab bar is active.

Changes

Focus ring adoption across UI components

Layer / File(s) Summary
focusRing modifier implementation
app/src/main/java/app/gamenative/ui/component/FocusRing.kt
Adds the public Modifier.focusRing API and its focus-driven sweep-gradient ring rendering.
AccentActionRow composable
app/src/main/java/app/gamenative/ui/component/AccentActionRow.kt
Adds a focus-aware accent action row with icon, title, click handling, and shared focus-ring styling.
QuickMenu focus ring migration
app/src/main/java/app/gamenative/ui/component/QuickMenu.kt
QuickMenuCloseButton, QuickMenuTabButton, and QuickMenuItemRow switch from focused borders to focusRing.
ScreenEffectsPanel reset row migration
app/src/main/java/app/gamenative/ui/component/ScreenEffectsPanel.kt
Reset rows switch to AccentActionRow, and the old helper is removed.
LibraryAppScreen focus ring migration
app/src/main/java/app/gamenative/ui/screen/library/LibraryAppScreen.kt
Primary actions and InfoCard switch to focusRing; InfoCard gains an interaction source.
Library grid/list card focus ring migration
app/src/main/java/app/gamenative/ui/screen/library/components/LibraryGridCard.kt, LibraryListCard.kt
Grid and list cards adopt shared shapes and focusRing, with refactored focus-state wiring.
LibraryTabBar focus ring and layout migration
app/src/main/java/app/gamenative/ui/screen/library/components/LibraryTabBar.kt
Compact and expanded tab controls switch to focusRing; IconActionButton consolidates animation; TabItem uses fixed capsule geometry.
LibraryScreen tab-bar focus tracking
app/src/main/java/app/gamenative/ui/screen/library/LibraryScreen.kt
Adds tab-bar focus state and gates focus-bootstrap paths while the tab bar is focused.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • utkarshdalal/GameNative#797: Overlaps in app/src/main/java/app/gamenative/ui/screen/library/LibraryScreen.kt focus bootstrapping and focus-targeting behavior.

Suggested reviewers: utkarshdalal

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding an animated focus ring across library and in-game UI.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description clearly explains the change, affected areas, and includes before/after media, though the template's Type of Change and Checklist sections are missing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@phobos665

Copy link
Copy Markdown
Contributor

Thanks for this!

Couple of tidbits:

  • Could you please add previews for new components
  • Could you also do a before/after screenshot/video so it's visible to see differences?

Thanks!

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🧹 Nitpick comments (2)
app/src/main/java/app/gamenative/ui/screen/library/components/LibraryTabBar.kt (1)

554-561: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Use clickable semantics for action buttons.

Line 555 says these are actions, but selectable(selected = false) still exposes selectable-item semantics; Compose documents selectable as normally for mutually exclusive selections. Prefer clickable + focusable(interactionSource) here to preserve controller focus without toggle/selection semantics. (developer.android.com)

♻️ Suggested refactor
-            .selectable(
-                // These are actions, not toggles; feeding isFocused back as `selected` caused an
-                // extra recomposition on every focus change.
-                selected = false,
-                interactionSource = interactionSource,
-                indication = null,
-                onClick = onClick,
-            ),
+            .clickable(
+                interactionSource = interactionSource,
+                indication = null,
+                onClick = onClick,
+            )
+            .focusable(interactionSource = interactionSource),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@app/src/main/java/app/gamenative/ui/screen/library/components/LibraryTabBar.kt`
around lines 554 - 561, The action items in LibraryTabBar should not use
selectable semantics because they are not mutually exclusive selections. Update
the modifier chain on the tab bar action button to use clickable for the tap
action and keep controller focus via focusable with the same interactionSource,
replacing the current selectable(selected = false) usage in the relevant
composable.
app/src/main/java/app/gamenative/ui/screen/library/LibraryAppScreen.kt (1)

355-369: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

isFocused state in InfoCard is now dead.

isFocused is set in onFocusChanged (Line 363) but never read anywhere else in the function — the border/color branch that used to consume it was replaced by focusRing, which tracks focus via interactionSource instead. Leaving it in place triggers a recomposition of InfoCard's whole Column on every focus transition for no visual benefit.

♻️ Proposed cleanup
-    var isFocused by remember { mutableStateOf(false) }
     val interactionSource = remember { MutableInteractionSource() }
     val bringIntoViewRequester = remember { BringIntoViewRequester() }
     val scope = rememberCoroutineScope()
     val cardModifier = if (focusableForNavigation) {
         modifier
             .bringIntoViewRequester(bringIntoViewRequester)
             .onFocusChanged { state ->
-                isFocused = state.isFocused
                 if (state.isFocused) {
                     scope.launch { bringIntoViewRequester.bringIntoView() }
                 }
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/app/gamenative/ui/screen/library/LibraryAppScreen.kt`
around lines 355 - 369, The InfoCard focus state is now unused, so remove the
dead `isFocused` state and its assignment from `LibraryAppScreen`’s card
modifier setup. Keep the focus behavior driven by `interactionSource`,
`focusable`, and `focusRing`, and retain the `bringIntoViewRequester` logic only
if it is still needed for scrolling on focus.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/app/gamenative/ui/component/AccentActionRow.kt`:
- Line 68: The AccentActionRow focus ring is using the default thickness because
the focusRing call omits width, unlike QuickMenu, LibraryTabBar, and
LibraryAppScreen. Update AccentActionRow’s modifier chain to pass width = 2.dp
into focusRing so the reset row matches the rest of the UI’s focus ring styling.

In `@app/src/main/java/app/gamenative/ui/screen/library/LibraryScreen.kt`:
- Around line 501-503: The tab-bar focus guard in LibraryScreen.kt only covers
the tab-change delay, while the list population effect still calls
requestContentFocusOrDefer() and can steal focus back after the user has moved
to the tab bar. Update the state.appInfoList.size LaunchedEffect path in
LibraryScreen to apply the same tabBarHasFocus check before requesting or
deferring content focus, so slow-loading tabs do not refocus the list
unexpectedly.
- Around line 995-1006: Move the tab-bar focus tracking from the wrapper Box in
LibraryScreen to the focus group that actually owns the tab buttons in
LibraryTabBar. Update the Row with .focusGroup() in LibraryTabBar.kt to use
onFocusChanged so tabBarHasFocus reflects real tab-bar focus enter/leave events,
and keep the existing pendingGridFocusRequest/pendingCarouselFocusRequest reset
logic there.

---

Nitpick comments:
In
`@app/src/main/java/app/gamenative/ui/screen/library/components/LibraryTabBar.kt`:
- Around line 554-561: The action items in LibraryTabBar should not use
selectable semantics because they are not mutually exclusive selections. Update
the modifier chain on the tab bar action button to use clickable for the tap
action and keep controller focus via focusable with the same interactionSource,
replacing the current selectable(selected = false) usage in the relevant
composable.

In `@app/src/main/java/app/gamenative/ui/screen/library/LibraryAppScreen.kt`:
- Around line 355-369: The InfoCard focus state is now unused, so remove the
dead `isFocused` state and its assignment from `LibraryAppScreen`’s card
modifier setup. Keep the focus behavior driven by `interactionSource`,
`focusable`, and `focusRing`, and retain the `bringIntoViewRequester` logic only
if it is still needed for scrolling on focus.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ae7a5bf-b13c-4c66-bb6f-f35a3aad667d

📥 Commits

Reviewing files that changed from the base of the PR and between 33bd339 and 409d285.

📒 Files selected for processing (9)
  • app/src/main/java/app/gamenative/ui/component/AccentActionRow.kt
  • app/src/main/java/app/gamenative/ui/component/FocusRing.kt
  • app/src/main/java/app/gamenative/ui/component/QuickMenu.kt
  • app/src/main/java/app/gamenative/ui/component/ScreenEffectsPanel.kt
  • app/src/main/java/app/gamenative/ui/screen/library/LibraryAppScreen.kt
  • app/src/main/java/app/gamenative/ui/screen/library/LibraryScreen.kt
  • app/src/main/java/app/gamenative/ui/screen/library/components/LibraryGridCard.kt
  • app/src/main/java/app/gamenative/ui/screen/library/components/LibraryListCard.kt
  • app/src/main/java/app/gamenative/ui/screen/library/components/LibraryTabBar.kt

Comment thread app/src/main/java/app/gamenative/ui/component/AccentActionRow.kt Outdated
Comment thread app/src/main/java/app/gamenative/ui/screen/library/LibraryScreen.kt
Comment thread app/src/main/java/app/gamenative/ui/screen/library/LibraryScreen.kt

@cubic-dev-ai cubic-dev-ai Bot 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.

4 issues found across 9 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/ui/screen/library/components/LibraryTabBar.kt">

<violation number="1" location="app/src/main/java/app/gamenative/ui/screen/library/components/LibraryTabBar.kt:610">
P2: The expanded tab items now force a fixed 40dp height and remove vertical padding. At large accessibility font scales, the tab text's line height can exceed 40dp, and because the Box is clipped with `RoundedCornerShape(20.dp)`, the overflowing text will be visually cut off at the top and bottom. This makes tab labels unreadable for users who rely on larger text sizes.

Consider using `heightIn(min = 40.dp)` instead of `height(40.dp)` so the capsule stays 40dp tall in normal use but can grow when needed. If the sliding pill must stay aligned, its height can be derived from the measured tab row rather than hardcoded.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/ui/component/FocusRing.kt Outdated
Comment thread app/src/main/java/app/gamenative/ui/screen/library/components/LibraryTabBar.kt Outdated
Comment thread app/src/main/java/app/gamenative/ui/component/AccentActionRow.kt Outdated
Comment thread app/src/main/java/app/gamenative/ui/screen/library/LibraryScreen.kt
@VinceBT VinceBT force-pushed the feature/focus-ring-standalone branch from 409d285 to 47cf023 Compare July 1, 2026 21:40
Replaces the static gradient focus borders with a rotating sweep-gradient ring that highlights the focused element when navigating with a controller or D-pad. It only animates while focused, so unfocused elements schedule no frames.

Applied to the library grid and list cards, the tab bar, the game page action buttons and info cards, the in-game quick menu, and the screen effects panel rows. The card ring is drawn on each card's outer box rather than the Material Card itself, otherwise the card's own background would cover it.
@VinceBT VinceBT force-pushed the feature/focus-ring-standalone branch from 47cf023 to 0220307 Compare July 1, 2026 21:46
@utkarshdalal utkarshdalal merged commit db62f56 into utkarshdalal:master Jul 7, 2026
2 checks passed
@VinceBT VinceBT deleted the feature/focus-ring-standalone branch July 7, 2026 21:48
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