mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 23:51:21 +02:00
Preload grid card images & other minor optimizations (#1328)
## Description Preload ~6 rows worth of images on library grid pages. So depending on device CPU & memory, most cards do not show a placeholder image when scrolling unless you press and hold up/down to scroll as fast as possible. There's a couple other minor optimizations on the grid code as well, but nothing huge. ### Dev notes This PR makes the `cacheWindow` for pre-rendering cards applicable to their images. Previously, images would not be fetched until the card is laid out within the window. This was to get the width/height to pass to the server request. This PR instead calculates the card widths when the grid itself is laid out and then passes this width into the cards' composables. ### Related issues Related to #1246 ### Testing Tested with emulator, shield, fire stick 4k max ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
b94faceb2f
commit
d538b8d4f9
14 changed files with 167 additions and 125 deletions
|
|
@ -38,8 +38,8 @@ fun CoilConfig(
|
|||
}
|
||||
},
|
||||
okHttpClient = okHttpClient,
|
||||
debugLogging = false,
|
||||
enableCache = true,
|
||||
debugLogging = debugLogging,
|
||||
enableCache = enableCache,
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
|
@ -70,6 +71,8 @@ fun EpisodeCard(
|
|||
val aspectRatio = item?.aspectRatio?.coerceAtLeast(AspectRatios.MIN) ?: AspectRatios.MIN
|
||||
val width = imageHeight * aspectRatio
|
||||
val height = imageWidth * (1f / aspectRatio)
|
||||
val density = LocalDensity.current
|
||||
val imageWidthPx = remember(imageWidth) { with(density) { imageWidth.roundToPx() } }
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(spaceBetween),
|
||||
modifier = modifier.size(width, height),
|
||||
|
|
@ -102,6 +105,7 @@ fun EpisodeCard(
|
|||
watchedPercent = dto?.userData?.playedPercentage,
|
||||
numberOfVersions = dto?.mediaSourceCount ?: 0,
|
||||
useFallbackText = false,
|
||||
fillWidth = imageWidthPx,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxSize(),
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ fun GridCard(
|
|||
imageContentScale: ContentScale = ContentScale.Fit,
|
||||
imageType: ViewOptionImageType = ViewOptionImageType.PRIMARY,
|
||||
showTitle: Boolean = true,
|
||||
fillWidth: Int? = null,
|
||||
fillHeight: Int? = null,
|
||||
) {
|
||||
val dto = item?.data
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
|
|
@ -95,6 +97,8 @@ fun GridCard(
|
|||
numberOfVersions = dto?.mediaSourceCount ?: 0,
|
||||
useFallbackText = false,
|
||||
contentScale = imageContentScale,
|
||||
fillWidth = fillWidth,
|
||||
fillHeight = fillHeight,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
|
|||
|
|
@ -24,11 +24,9 @@ import androidx.compose.ui.graphics.BlendMode
|
|||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onLayoutRectChanged
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
|
|
@ -63,20 +61,19 @@ fun ItemCardImage(
|
|||
imageType: ImageType = ImageType.PRIMARY,
|
||||
useFallbackText: Boolean = true,
|
||||
contentScale: ContentScale = ContentScale.Fit,
|
||||
fillWidth: Int? = null,
|
||||
fillHeight: Int? = null,
|
||||
) {
|
||||
val imageUrlService = LocalImageUrlService.current
|
||||
var size by remember { mutableStateOf(IntSize.Zero) }
|
||||
val imageUrl =
|
||||
remember(size, item) {
|
||||
if (size != IntSize.Zero && item != null) {
|
||||
remember(item) {
|
||||
item?.let {
|
||||
imageUrlService.getItemImageUrl(
|
||||
item,
|
||||
imageType,
|
||||
fillWidth = size.width,
|
||||
fillHeight = size.height,
|
||||
fillWidth = fillWidth,
|
||||
fillHeight = fillHeight,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
ItemCardImage(
|
||||
|
|
@ -88,13 +85,7 @@ fun ItemCardImage(
|
|||
unwatchedCount = unwatchedCount,
|
||||
watchedPercent = watchedPercent,
|
||||
numberOfVersions = numberOfVersions,
|
||||
modifier =
|
||||
modifier.onLayoutRectChanged(
|
||||
throttleMillis = 100,
|
||||
debounceMillis = 25,
|
||||
) {
|
||||
size = IntSize(width = it.width, height = it.height)
|
||||
},
|
||||
modifier = modifier,
|
||||
useFallbackText = useFallbackText,
|
||||
contentScale = contentScale,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog
|
|||
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo
|
||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||
import com.github.damontecres.wholphin.ui.detail.CardGrid
|
||||
import com.github.damontecres.wholphin.ui.detail.GridItemDetails
|
||||
import com.github.damontecres.wholphin.ui.detail.ItemViewModel
|
||||
import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
|
||||
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
||||
|
|
@ -1060,7 +1061,7 @@ fun CollectionFolderGridContent(
|
|||
position = newPosition
|
||||
positionCallback?.invoke(columns, newPosition)
|
||||
},
|
||||
cardContent = { item, onClick, onLongClick, mod ->
|
||||
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
GridCard(
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
|
|
@ -1069,6 +1070,7 @@ fun CollectionFolderGridContent(
|
|||
imageAspectRatio = viewOptions.aspectRatio.ratio,
|
||||
imageType = viewOptions.imageType,
|
||||
showTitle = viewOptions.showTitles,
|
||||
fillWidth = widthPx,
|
||||
modifier = mod,
|
||||
)
|
||||
},
|
||||
|
|
@ -1109,17 +1111,13 @@ data class PositionItem(
|
|||
data class CollectionFolderGridParameters(
|
||||
val columns: Int = 6,
|
||||
val spacing: Dp = 16.dp,
|
||||
val cardContent: @Composable (
|
||||
item: BaseItem?,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
mod: Modifier,
|
||||
) -> Unit = { item, onClick, onLongClick, mod ->
|
||||
val cardContent: @Composable (GridItemDetails<BaseItem>) -> Unit = { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
GridCard(
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
imageContentScale = ContentScale.FillBounds,
|
||||
fillWidth = widthPx,
|
||||
modifier = mod,
|
||||
)
|
||||
},
|
||||
|
|
@ -1129,13 +1127,14 @@ data class CollectionFolderGridParameters(
|
|||
CollectionFolderGridParameters(
|
||||
columns = 6,
|
||||
spacing = 16.dp,
|
||||
cardContent = { item, onClick, onLongClick, mod ->
|
||||
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
GridCard(
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
imageContentScale = ContentScale.FillBounds,
|
||||
imageAspectRatio = AspectRatios.TALL,
|
||||
fillWidth = widthPx,
|
||||
modifier = mod,
|
||||
)
|
||||
},
|
||||
|
|
@ -1144,13 +1143,14 @@ data class CollectionFolderGridParameters(
|
|||
CollectionFolderGridParameters(
|
||||
columns = 4,
|
||||
spacing = 24.dp,
|
||||
cardContent = { item, onClick, onLongClick, mod ->
|
||||
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
GridCard(
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
imageContentScale = ContentScale.Crop,
|
||||
imageAspectRatio = AspectRatios.WIDE,
|
||||
fillWidth = widthPx,
|
||||
modifier = mod,
|
||||
)
|
||||
},
|
||||
|
|
@ -1159,13 +1159,14 @@ data class CollectionFolderGridParameters(
|
|||
CollectionFolderGridParameters(
|
||||
columns = 6,
|
||||
spacing = 16.dp,
|
||||
cardContent = { item, onClick, onLongClick, mod ->
|
||||
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
GridCard(
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
imageContentScale = ContentScale.FillBounds,
|
||||
imageAspectRatio = AspectRatios.SQUARE,
|
||||
fillWidth = widthPx,
|
||||
modifier = mod,
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -309,7 +309,7 @@ fun GenreCardGrid(
|
|||
},
|
||||
columns = columns,
|
||||
spacing = spacing,
|
||||
cardContent = { item: Genre?, onClick: () -> Unit, onLongClick: () -> Unit, mod: Modifier ->
|
||||
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
GenreCard(
|
||||
genre = item,
|
||||
onClick = onClick,
|
||||
|
|
|
|||
|
|
@ -133,11 +133,12 @@ fun ItemGrid(
|
|||
showJumpButtons = false,
|
||||
showLetterButtons = false,
|
||||
spacing = 24.dp,
|
||||
cardContent = @Composable { item, onClick, onLongClick, mod ->
|
||||
cardContent = @Composable { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
GridCard(
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
fillWidth = widthPx,
|
||||
modifier = mod,
|
||||
imageAspectRatio = AspectRatios.WIDE, // TODO
|
||||
)
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ fun StudioCardGrid(
|
|||
},
|
||||
columns = columns,
|
||||
spacing = spacing,
|
||||
cardContent = { item: Studio?, onClick: () -> Unit, onLongClick: () -> Unit, mod: Modifier ->
|
||||
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
StudioCard(
|
||||
studio = item,
|
||||
onClick = onClick,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -34,6 +35,7 @@ import androidx.compose.runtime.mutableIntStateOf
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
|
|
@ -51,7 +53,8 @@ import androidx.compose.ui.input.key.KeyEventType
|
|||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.layout.onLayoutRectChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
|
|
@ -62,10 +65,8 @@ import androidx.tv.material3.LocalContentColor
|
|||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.ui.AppColors
|
||||
import com.github.damontecres.wholphin.ui.FontAwesome
|
||||
import com.github.damontecres.wholphin.ui.cards.GridCard
|
||||
import com.github.damontecres.wholphin.ui.ifElse
|
||||
import com.github.damontecres.wholphin.ui.playback.isBackwardButton
|
||||
import com.github.damontecres.wholphin.ui.playback.isForwardButton
|
||||
|
|
@ -76,6 +77,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import kotlin.math.ceil
|
||||
|
||||
private const val DEBUG = false
|
||||
|
||||
|
|
@ -85,6 +87,15 @@ interface CardGridItem {
|
|||
val sortName: String
|
||||
}
|
||||
|
||||
data class GridItemDetails<T : CardGridItem>(
|
||||
val item: T?,
|
||||
val index: Int,
|
||||
val onClick: () -> Unit,
|
||||
val onLongClick: () -> Unit,
|
||||
val widthPx: Int,
|
||||
val mod: Modifier,
|
||||
)
|
||||
|
||||
/**
|
||||
* Shows a vertical grid of [CardGridItem]s
|
||||
*/
|
||||
|
|
@ -102,31 +113,21 @@ fun <T : CardGridItem> CardGrid(
|
|||
modifier: Modifier = Modifier,
|
||||
initialPosition: Int = 0,
|
||||
positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
|
||||
cardContent: @Composable (
|
||||
item: T?,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
mod: Modifier,
|
||||
) -> Unit = { item, onClick, onLongClick, mod ->
|
||||
GridCard(
|
||||
item = item as BaseItem?,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
imageContentScale = ContentScale.FillBounds,
|
||||
modifier = mod,
|
||||
)
|
||||
},
|
||||
cardContent: @Composable (GridItemDetails<T>) -> Unit,
|
||||
columns: Int = 6,
|
||||
spacing: Dp = 16.dp,
|
||||
bringIntoViewSpec: BringIntoViewSpec = LocalBringIntoViewSpec.current,
|
||||
) {
|
||||
val startPosition = initialPosition.coerceIn(0, (pager.size - 1).coerceAtLeast(0))
|
||||
val startPosition =
|
||||
remember(initialPosition, pager.size) {
|
||||
initialPosition.coerceIn(0, (pager.size - 1).coerceAtLeast(0))
|
||||
}
|
||||
|
||||
val fractionCacheWindow = LazyLayoutCacheWindow(aheadFraction = 1f, behindFraction = 0.5f)
|
||||
var focusedIndex by rememberSaveable { mutableIntStateOf(initialPosition) }
|
||||
val currentFocusedIndex by rememberUpdatedState(focusedIndex)
|
||||
val gridState =
|
||||
rememberLazyGridState(
|
||||
cacheWindow = fractionCacheWindow,
|
||||
cacheWindow = LazyLayoutCacheWindow(aheadFraction = 2f, behindFraction = 0.5f),
|
||||
initialFirstVisibleItemIndex = focusedIndex,
|
||||
)
|
||||
val scope = rememberCoroutineScope()
|
||||
|
|
@ -135,13 +136,16 @@ fun <T : CardGridItem> CardGrid(
|
|||
var previouslyFocusedIndex by rememberSaveable { mutableIntStateOf(0) }
|
||||
|
||||
var alphabetFocus by remember { mutableStateOf(false) }
|
||||
val focusOn = { index: Int ->
|
||||
if (DEBUG) Timber.v("focusOn: focusedIndex=$focusedIndex, index=$index")
|
||||
if (index != focusedIndex) {
|
||||
val focusOn =
|
||||
remember {
|
||||
{ index: Int ->
|
||||
if (DEBUG) Timber.v("focusOn: focusedIndex=$currentFocusedIndex, index=$index")
|
||||
if (index != currentFocusedIndex) {
|
||||
previouslyFocusedIndex = focusedIndex
|
||||
}
|
||||
focusedIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for a recomposition to focus
|
||||
val alphabetFocusRequester = remember { FocusRequester() }
|
||||
|
|
@ -180,7 +184,9 @@ fun <T : CardGridItem> CardGrid(
|
|||
}
|
||||
}
|
||||
|
||||
val jump = { jump: Int ->
|
||||
val jump =
|
||||
remember {
|
||||
{ jump: Int ->
|
||||
scope.launch(ExceptionHandler()) {
|
||||
val newPosition =
|
||||
(gridState.firstVisibleItemIndex + jump).coerceIn(0..<pager.size)
|
||||
|
|
@ -189,9 +195,12 @@ fun <T : CardGridItem> CardGrid(
|
|||
gridState.scrollToItem(newPosition, 0)
|
||||
}
|
||||
}
|
||||
val jumpToTop = {
|
||||
}
|
||||
val jumpToTop =
|
||||
remember {
|
||||
{
|
||||
scope.launch(ExceptionHandler()) {
|
||||
if (focusedIndex < (columns * 6)) {
|
||||
if (currentFocusedIndex < (columns * 6)) {
|
||||
// If close, animate the scroll
|
||||
gridState.animateScrollToItem(0, 0)
|
||||
} else {
|
||||
|
|
@ -201,6 +210,7 @@ fun <T : CardGridItem> CardGrid(
|
|||
zeroFocus.tryRequestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val jumpToLetter: (Char) -> Unit =
|
||||
remember {
|
||||
|
|
@ -298,6 +308,9 @@ fun <T : CardGridItem> CardGrid(
|
|||
modifier = Modifier.align(Alignment.CenterVertically),
|
||||
)
|
||||
}
|
||||
val density = LocalDensity.current
|
||||
var cardWidthPx by rememberSaveable { mutableIntStateOf(0) }
|
||||
|
||||
Box(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
|
|
@ -314,20 +327,26 @@ fun <T : CardGridItem> CardGrid(
|
|||
.focusGroup()
|
||||
.focusRestorer(firstFocus)
|
||||
.focusProperties {
|
||||
onExit = {
|
||||
// Leaving the grid, so "forget" the position
|
||||
// focusedIndex = -1
|
||||
}
|
||||
onEnter = {
|
||||
if (focusedIndex < 0 && gridState.firstVisibleItemIndex <= startPosition) {
|
||||
focusedIndex = startPosition
|
||||
}
|
||||
}
|
||||
}.onLayoutRectChanged(0, 0) {
|
||||
val width = it.width
|
||||
val spacingPx = with(density) { spacing.toPx() }
|
||||
val cardWidth =
|
||||
ceil((width - (spacingPx * (columns - 1))) / columns)
|
||||
cardWidthPx = cardWidth.toInt()
|
||||
Timber.v("cardWidthPx=%s", cardWidthPx)
|
||||
},
|
||||
) {
|
||||
items(pager.size) { index ->
|
||||
val item = pager[index]
|
||||
val details =
|
||||
remember(index, item) {
|
||||
val mod =
|
||||
if ((index == focusedIndex) or (focusedIndex < 0 && index == 0)) {
|
||||
if ((index == currentFocusedIndex) or (currentFocusedIndex < 0 && index == 0)) {
|
||||
if (DEBUG) Timber.d("Adding firstFocus to focusedIndex $index")
|
||||
Modifier
|
||||
.focusRequester(firstFocus)
|
||||
|
|
@ -336,19 +355,27 @@ fun <T : CardGridItem> CardGrid(
|
|||
} else {
|
||||
Modifier
|
||||
}
|
||||
val item = pager[index]
|
||||
cardContent(
|
||||
item,
|
||||
{
|
||||
GridItemDetails(
|
||||
item = item,
|
||||
index = index,
|
||||
onClick = {
|
||||
if (item != null) {
|
||||
focusedIndex = index
|
||||
onClickItem.invoke(index, item)
|
||||
}
|
||||
},
|
||||
{ if (item != null) onLongClickItem.invoke(index, item) },
|
||||
onLongClick = {
|
||||
if (item != null) {
|
||||
onLongClickItem.invoke(index, item)
|
||||
}
|
||||
},
|
||||
widthPx = cardWidthPx,
|
||||
mod =
|
||||
mod
|
||||
.ifElse(index == 0, Modifier.focusRequester(zeroFocus))
|
||||
.onFocusChanged { focusState ->
|
||||
.ifElse(
|
||||
index == 0,
|
||||
Modifier.focusRequester(zeroFocus),
|
||||
).onFocusChanged { focusState ->
|
||||
if (DEBUG) {
|
||||
Timber.v(
|
||||
"$index isFocused=${focusState.isFocused}",
|
||||
|
|
@ -358,14 +385,12 @@ fun <T : CardGridItem> CardGrid(
|
|||
// Focused, so set that up
|
||||
focusOn(index)
|
||||
positionCallback?.invoke(columns, index)
|
||||
} else if (focusedIndex == index) {
|
||||
// savedFocusedIndex = index
|
||||
// // Was focused on this, so mark unfocused
|
||||
// focusedIndex = -1
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
cardContent.invoke(details)
|
||||
}
|
||||
}
|
||||
if (pager.isEmpty()) {
|
||||
// focusedIndex = -1
|
||||
|
|
@ -429,11 +454,21 @@ fun <T : CardGridItem> CardGrid(
|
|||
// Add end padding to push away from edge
|
||||
letterClicked = jumpToLetter,
|
||||
)
|
||||
} else {
|
||||
// Spacer ensures card sizes do not change if the alphabet buttons are not shown
|
||||
Spacer(
|
||||
Modifier
|
||||
.padding(start = 16.dp)
|
||||
.width(letterHorizontalPadding * 2 + letterButtonSize),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val letterHorizontalPadding = 2.dp
|
||||
private val letterButtonSize = 14.dp
|
||||
|
||||
@Composable
|
||||
fun JumpButtons(
|
||||
jump1: Int,
|
||||
|
|
@ -500,7 +535,7 @@ fun AlphabetButtons(
|
|||
var alphabetPickerFocused by remember { mutableStateOf(false) }
|
||||
|
||||
LazyColumn(
|
||||
contentPadding = PaddingValues(vertical = 1.1.dp, horizontal = 2.dp),
|
||||
contentPadding = PaddingValues(vertical = 1.1.dp, horizontal = letterHorizontalPadding),
|
||||
verticalArrangement = Arrangement.spacedBy(1.1.dp),
|
||||
state = listState,
|
||||
modifier =
|
||||
|
|
@ -534,14 +569,14 @@ fun AlphabetButtons(
|
|||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(14.dp)
|
||||
.size(letterButtonSize)
|
||||
.clip(CircleShape)
|
||||
.alpha(itemAlpha),
|
||||
) {
|
||||
Button(
|
||||
modifier =
|
||||
Modifier
|
||||
.size(14.dp)
|
||||
.size(letterButtonSize)
|
||||
.focusRequester(focusRequesters[index]),
|
||||
contentPadding = PaddingValues(0.dp), // No padding to maximize text space
|
||||
interactionSource = interactionSource,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import androidx.compose.ui.focus.focusRequester
|
|||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -781,6 +782,9 @@ fun PlaylistItem(
|
|||
isQueued: Boolean = false,
|
||||
) {
|
||||
val focused by interactionSource.collectIsFocusedAsState()
|
||||
val imageWidth = 160.dp
|
||||
val density = LocalDensity.current
|
||||
val imageWidthPx = remember(imageWidth) { with(density) { imageWidth.roundToPx() } }
|
||||
ListItem(
|
||||
selected = false,
|
||||
onClick = onClick,
|
||||
|
|
@ -843,8 +847,9 @@ fun PlaylistItem(
|
|||
unwatchedCount = item?.data?.userData?.unplayedItemCount ?: -1,
|
||||
watchedPercent = 0.0,
|
||||
numberOfVersions = item?.data?.mediaSourceCount ?: 0,
|
||||
modifier = Modifier.width(160.dp),
|
||||
modifier = Modifier.width(imageWidth),
|
||||
useFallbackText = false,
|
||||
fillWidth = imageWidthPx,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ fun CollectionMixedGrid(
|
|||
positionCallback = { _, newPosition ->
|
||||
onFocusPosition.invoke(RowColumn(0, newPosition))
|
||||
},
|
||||
cardContent = { item, onClick, onLongClick, mod ->
|
||||
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
GridCard(
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
|
|
@ -69,6 +69,7 @@ fun CollectionMixedGrid(
|
|||
imageAspectRatio = cardViewOptions.aspectRatio.ratio,
|
||||
imageType = cardViewOptions.imageType,
|
||||
showTitle = cardViewOptions.showTitles,
|
||||
fillWidth = widthPx,
|
||||
modifier = mod,
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ fun DiscoverPersonPage(
|
|||
showJumpButtons = false,
|
||||
showLetterButtons = false,
|
||||
spacing = 16.dp,
|
||||
cardContent = @Composable { item, onClick, onLongClick, mod ->
|
||||
cardContent = @Composable { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
DiscoverItemCard(
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ fun DiscoverRequestGrid(
|
|||
gridFocusRequester = gridFocusRequester,
|
||||
showJumpButtons = false,
|
||||
showLetterButtons = false,
|
||||
cardContent = { item, onClick, onLongClick, mod ->
|
||||
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
DiscoverItemCard(
|
||||
item = item,
|
||||
onClick = onClick,
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ fun SeerrRequestsPage(
|
|||
showJumpButtons = false,
|
||||
showLetterButtons = false,
|
||||
spacing = 16.dp,
|
||||
cardContent = @Composable { item, onClick, onLongClick, mod ->
|
||||
cardContent = @Composable { (item, index, onClick, onLongClick, widthPx, mod) ->
|
||||
DiscoverItemCard(
|
||||
item = item?.item,
|
||||
onClick = onClick,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue