mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Add support for letter jump buttons
This commit is contained in:
parent
560e7cc545
commit
fe1d63793d
3 changed files with 129 additions and 31 deletions
|
|
@ -27,6 +27,7 @@ val DefaultItemFields =
|
|||
ItemFields.CHILD_COUNT,
|
||||
ItemFields.OVERVIEW,
|
||||
ItemFields.TRICKPLAY,
|
||||
ItemFields.SORT_NAME,
|
||||
)
|
||||
|
||||
@Preview(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.github.damontecres.dolphin.ui.detail
|
|||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.focusGroup
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -10,11 +12,15 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
|
|
@ -37,6 +43,7 @@ import androidx.compose.ui.input.key.type
|
|||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.LocalContentColor
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.dolphin.R
|
||||
|
|
@ -63,6 +70,7 @@ fun CardGrid(
|
|||
letterPosition: suspend (Char) -> Int,
|
||||
gridFocusRequester: FocusRequester,
|
||||
showJumpButtons: Boolean,
|
||||
showLetterButtons: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
initialPosition: Int = 0,
|
||||
positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
|
||||
|
|
@ -87,12 +95,13 @@ fun CardGrid(
|
|||
}
|
||||
|
||||
// Wait for a recomposition to focus
|
||||
// LaunchedEffect(alphabetFocus) {
|
||||
// if (alphabetFocus) {
|
||||
// firstFocus.tryRequestFocus()
|
||||
// }
|
||||
// alphabetFocus = false
|
||||
// }
|
||||
val alphabetFocusRequester = remember { FocusRequester() }
|
||||
LaunchedEffect(alphabetFocus) {
|
||||
if (alphabetFocus) {
|
||||
alphabetFocusRequester.tryRequestFocus()
|
||||
}
|
||||
alphabetFocus = false
|
||||
}
|
||||
|
||||
val useBackToJump = true // uiConfig.preferences.interfacePreferences.scrollTopOnBack
|
||||
val showFooter = true // uiConfig.preferences.interfacePreferences.showPositionFooter
|
||||
|
|
@ -215,7 +224,7 @@ fun CardGrid(
|
|||
.focusProperties {
|
||||
onExit = {
|
||||
// Leaving the grid, so "forget" the position
|
||||
focusedIndex = -1
|
||||
// focusedIndex = -1
|
||||
}
|
||||
onEnter = {
|
||||
if (focusedIndex < 0 && gridState.firstVisibleItemIndex <= startPosition) {
|
||||
|
|
@ -231,6 +240,7 @@ fun CardGrid(
|
|||
Modifier
|
||||
.focusRequester(firstFocus)
|
||||
.focusRequester(gridFocusRequester)
|
||||
.focusRequester(alphabetFocusRequester)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
|
|
@ -299,18 +309,38 @@ fun CardGrid(
|
|||
}
|
||||
}
|
||||
// Letters
|
||||
if (pager.isNotEmpty() && false) {
|
||||
// TODO
|
||||
val currentLetter =
|
||||
remember(focusedIndex) {
|
||||
pager
|
||||
.getOrNull(focusedIndex)
|
||||
?.data
|
||||
?.sortName
|
||||
?.first()
|
||||
?.uppercaseChar()
|
||||
?.let {
|
||||
if (it >= '0' && it <= '9') {
|
||||
'#'
|
||||
} else if (it >= 'A' && it <= 'Z') {
|
||||
it
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
?: LETTERS[0]
|
||||
}
|
||||
if (showLetterButtons && pager.isNotEmpty()) {
|
||||
AlphabetButtons(
|
||||
currentLetter = currentLetter,
|
||||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
letterClicked = { letter ->
|
||||
scope.launch(ExceptionHandler()) {
|
||||
val jumpPosition = letterPosition.invoke(letter)
|
||||
Timber.d("Alphabet jump to $jumpPosition")
|
||||
if (jumpPosition >= 0) {
|
||||
gridState.scrollToItem(jumpPosition)
|
||||
focusOn(jumpPosition)
|
||||
alphabetFocus = true
|
||||
// firstFocus.tryRequestFocus()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -353,26 +383,68 @@ fun JumpButton(
|
|||
}
|
||||
}
|
||||
|
||||
private const val LETTERS = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
@Composable
|
||||
fun AlphabetButtons(
|
||||
currentLetter: Char,
|
||||
letterClicked: (Char) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
// LazyColumn(modifier = modifier) {
|
||||
// items(
|
||||
// AlphabetSearchUtils.LETTERS.length,
|
||||
// key = { AlphabetSearchUtils.LETTERS[it] },
|
||||
// ) { index ->
|
||||
// Button(
|
||||
// modifier =
|
||||
// Modifier.size(24.dp),
|
||||
// contentPadding = PaddingValues(2.dp),
|
||||
// onClick = {
|
||||
// letterClicked.invoke(AlphabetSearchUtils.LETTERS[index])
|
||||
// },
|
||||
// ) {
|
||||
// Text(text = AlphabetSearchUtils.LETTERS[index].toString())
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
val scope = rememberCoroutineScope()
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
val index = LETTERS.indexOf(currentLetter)
|
||||
LaunchedEffect(currentLetter) {
|
||||
scope.launch(ExceptionHandler()) {
|
||||
val firstVisibleItemIndex = listState.firstVisibleItemIndex
|
||||
val lastVisibleItemIndex =
|
||||
listState.layoutInfo.visibleItemsInfo
|
||||
.lastOrNull()
|
||||
?.index ?: -1
|
||||
if (index < firstVisibleItemIndex || index > lastVisibleItemIndex) {
|
||||
listState.animateScrollToItem(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
val focusRequesters = remember { List(LETTERS.length) { FocusRequester() } }
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
modifier =
|
||||
modifier.focusProperties {
|
||||
onEnter = {
|
||||
focusRequesters[index.coerceIn(0, LETTERS.length - 1)].tryRequestFocus()
|
||||
}
|
||||
},
|
||||
) {
|
||||
items(
|
||||
LETTERS.length,
|
||||
key = { LETTERS[it] },
|
||||
) { index ->
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
Button(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(24.dp)
|
||||
.focusRequester(focusRequesters[index]),
|
||||
contentPadding = PaddingValues(2.dp),
|
||||
interactionSource = interactionSource,
|
||||
onClick = {
|
||||
letterClicked.invoke(LETTERS[index])
|
||||
},
|
||||
) {
|
||||
val color =
|
||||
if (!focused && LETTERS[index] == currentLetter) {
|
||||
MaterialTheme.colorScheme.tertiary
|
||||
} else {
|
||||
LocalContentColor.current
|
||||
}
|
||||
Text(
|
||||
text = LETTERS[index].toString(),
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,6 +128,28 @@ class CollectionFolderViewModel
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun positionOfLetter(letter: Char): Int? =
|
||||
item.value?.let { item ->
|
||||
val includeItemTypes =
|
||||
when (item.data.collectionType) {
|
||||
CollectionType.MOVIES -> listOf(BaseItemKind.MOVIE)
|
||||
CollectionType.TVSHOWS -> listOf(BaseItemKind.SERIES)
|
||||
CollectionType.HOMEVIDEOS -> listOf(BaseItemKind.VIDEO)
|
||||
|
||||
else -> listOf()
|
||||
}
|
||||
val request =
|
||||
GetItemsRequest(
|
||||
parentId = item.id,
|
||||
includeItemTypes = includeItemTypes,
|
||||
nameLessThan = letter.toString(),
|
||||
limit = 0,
|
||||
enableTotalRecordCount = true,
|
||||
)
|
||||
val result by GetItemsRequestHandler.execute(api, request)
|
||||
result.totalRecordCount
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -172,6 +194,7 @@ fun CollectionFolderDetails(
|
|||
},
|
||||
showTitle = showTitle,
|
||||
positionCallback = positionCallback,
|
||||
letterPosition = { viewModel.positionOfLetter(it) ?: -1 },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -187,6 +210,7 @@ fun CollectionDetails(
|
|||
sortAndDirection: SortAndDirection,
|
||||
onClickItem: (BaseItem) -> Unit,
|
||||
onSortChange: (SortAndDirection) -> Unit,
|
||||
letterPosition: suspend (Char) -> Int,
|
||||
modifier: Modifier = Modifier,
|
||||
showTitle: Boolean = true,
|
||||
positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
|
||||
|
|
@ -233,9 +257,10 @@ fun CollectionDetails(
|
|||
pager = pager,
|
||||
onClickItem = onClickItem,
|
||||
longClicker = {},
|
||||
letterPosition = { 0 },
|
||||
letterPosition = letterPosition,
|
||||
gridFocusRequester = gridFocusRequester,
|
||||
showJumpButtons = false, // TODO add preference
|
||||
showLetterButtons = sortAndDirection.sort == ItemSortBy.SORT_NAME,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
initialPosition = 0,
|
||||
positionCallback = { columns, position ->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue