LastContentFocus.kt
Each line, with the release that last changed it.
| 0.5.0 | 1 | package dev.stave.ui.nav |
| 2 | ||
| 3 | import androidx.compose.runtime.MutableState | |
| 4 | import androidx.compose.runtime.compositionLocalOf | |
| 5 | import androidx.compose.runtime.mutableStateOf | |
| 6 | import androidx.compose.ui.Modifier | |
| 7 | import androidx.compose.ui.focus.FocusRequester | |
| 8 | import androidx.compose.ui.focus.focusRequester | |
| 9 | import androidx.compose.ui.focus.onFocusChanged | |
| 10 | ||
| 11 | /** | |
| 12 | * Holds the [FocusRequester] of the most recently focused content row so | |
| 13 | * the Now Playing bar's Up gesture can restore focus to it — the user | |
| 14 | * returns to exactly where they were, not wherever Compose's spatial | |
| 15 | * resolver happens to pick. | |
| 16 | * | |
| 17 | * Screens that render content above the bar (Browse's list, Queue's | |
| 18 | * list, etc.) write their row FRs here via [Modifier.trackLastContentFocus] | |
| 19 | * on focus gain. The bar's `onUpFromBar` reads the current value and | |
| 20 | * calls `requestFocus()` on it; when the row has been disposed (scrolled | |
| 21 | * out of a LazyColumn) the call fails and the caller should fall back to | |
| 22 | * [androidx.compose.ui.focus.FocusManager.moveFocus]. | |
| 23 | * | |
| 24 | * Default value is a no-op sink so code that reads the Local without a | |
| 25 | * provider (unlikely, but harmless) doesn't null-pointer. | |
| 26 | */ | |
| 27 | val LocalLastContentFocus = | |
| 28 | compositionLocalOf<MutableState<FocusRequester?>> { mutableStateOf(null) } | |
| 29 | ||
| 30 | /** | |
| 31 | * Register [requester] as the "last focused content" when the modified | |
| 32 | * element gains focus. Pair with a real [Modifier.focusRequester] and a | |
| 33 | * focusable modifier (e.g. staveFocusable + clickable). | |
| 34 | */ | |
| 35 | fun Modifier.trackLastContentFocus( | |
| 36 | requester: FocusRequester, | |
| 37 | sink: MutableState<FocusRequester?>, | |
| 38 | ): Modifier = this | |
| 39 | .focusRequester(requester) | |
| 40 | .onFocusChanged { if (it.isFocused) sink.value = requester } |


