Route card height and spacing through global preferences

Replaces hardcoded card heights and the per-library Spacing slider
with reads from LocalInterfaceCustomization. Home rows split the
previous single horizontalPadding arg into a fixed leading-edge
contentPadding (BASE_SPACING_DP.dp) and a slider-driven cardSpacing.
CardGrid grows a contentPadding so its leading edge stays flush
regardless of the spacing slider.

The ViewOptionsSpacing slider in ViewOptionsDialog (grid + list) and
CollectionViewOptionsDialog is removed - the global Spacing slider
covers all browse-density tuning. ViewOptions.spacing remains as a
type-driven base (16/4/2 dp for GRID/LIST/DENSE_LIST, set by the type
setter) and is multiplied by the global Spacing percent at the
list-view consumer site. The Increase / Decrease all card-size
shortcuts in HomeSettingsGlobal are also removed (superseded by the
global Card size slider).

Episode-card heights also scale with the global Card size slider via
a new InterfaceCustomization.episodeCardHeightDp helper. Direct
consumer sites (ExtrasRow, MovieDetails similar-items, PersonPage
episode row, SearchPage EPISODE_ROW results) read it. Preset-set
absolute heights (Favorite-Episode preset, etc.) stay as overrides.

Adds HomeRowViewOptions.resolvedCardHeight() to InterfaceHelpers.kt -
the composable resolver bridging per-row heightDp overrides and the
global slider. HomeRowViewOptions.heightDp default flips from
Cards.HEIGHT_2X3_DP (172) to 0, the new sentinel meaning "use the
global value".
This commit is contained in:
Justin Visser 2026-05-26 11:39:44 +02:00
parent 8fb301c2bb
commit 9fc46a80b4
25 changed files with 77 additions and 124 deletions

View file

@ -223,7 +223,7 @@ const val SUPPORTED_HOME_PAGE_SETTINGS_VERSION = 1
*/
@Serializable
data class HomeRowViewOptions(
val heightDp: Int = Cards.HEIGHT_2X3_DP,
val heightDp: Int = 0,
val spacing: Int = 16,
val contentScale: PrefContentScale = PrefContentScale.FILL,
val aspectRatio: AspectRatio = AspectRatio.TALL,

View file

@ -41,11 +41,11 @@ import com.github.damontecres.wholphin.data.model.SeerrAvailability
import com.github.damontecres.wholphin.data.model.SeerrItemType
import com.github.damontecres.wholphin.ui.AppColors
import com.github.damontecres.wholphin.ui.AspectRatios
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.enableMarquee
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import kotlinx.coroutines.delay
@Composable
@ -56,7 +56,7 @@ fun DiscoverItemCard(
modifier: Modifier = Modifier,
showOverlay: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
width: Dp = Cards.height2x3 * AspectRatios.TALL,
width: Dp = LocalInterfaceCustomization.current.cardHeightDp.dp * AspectRatios.TALL,
) {
val focused by interactionSource.collectIsFocusedAsState()
val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp)

View file

@ -4,10 +4,11 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.ExtrasItem
import com.github.damontecres.wholphin.ui.AspectRatios
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
@Composable
fun ExtrasRow(
@ -30,7 +31,7 @@ fun ExtrasRow(
onLongClick = onLongClick,
modifier = mod,
showImageOverlay = true,
imageHeight = Cards.heightEpisode,
imageHeight = LocalInterfaceCustomization.current.episodeCardHeightDp.dp,
imageWidth = Dp.Unspecified,
imageUrl = item?.imageUrl,
isFavorite = false,

View file

@ -42,6 +42,7 @@ fun <T> ItemRow(
) -> Unit,
modifier: Modifier = Modifier,
horizontalPadding: Dp = 16.dp,
cardSpacing: Dp = horizontalPadding,
showViewMore: Boolean = false,
viewMoreCardContent: @Composable (Modifier) -> Unit = {},
) {
@ -66,7 +67,7 @@ fun <T> ItemRow(
LazyRow(
state = state,
horizontalArrangement = Arrangement.spacedBy(horizontalPadding),
horizontalArrangement = Arrangement.spacedBy(cardSpacing),
contentPadding = PaddingValues(horizontal = horizontalPadding, vertical = 8.dp),
modifier =
Modifier

View file

@ -42,6 +42,7 @@ import com.github.damontecres.wholphin.ui.detail.CardGrid
import com.github.damontecres.wholphin.ui.detail.GridItemDetails
import com.github.damontecres.wholphin.ui.main.HomePageHeader
import com.github.damontecres.wholphin.ui.playback.scale
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.ui.util.ScrollToTopBringIntoViewSpec
import org.jellyfin.sdk.model.api.BaseItemKind
import org.jellyfin.sdk.model.api.CollectionType
@ -73,6 +74,7 @@ fun CollectionFolderGrid(
) {
val defaultBringIntoViewSpec = LocalBringIntoViewSpec.current
val density = LocalDensity.current
val spacingDp = LocalInterfaceCustomization.current.spacingDp
AnimatedVisibility(viewOptions.showDetails) {
HomePageHeader(
item = focusedItem,
@ -110,10 +112,10 @@ fun CollectionFolderGrid(
)
},
columns = viewOptions.columns,
spacing = viewOptions.spacing.dp,
spacing = spacingDp.dp,
bringIntoViewSpec =
remember(viewOptions) {
val spacingPx = with(density) { viewOptions.spacing.dp.toPx() }
remember(viewOptions.showDetails, spacingDp) {
val spacingPx = with(density) { spacingDp.dp.toPx() }
if (viewOptions.showDetails) {
ScrollToTopBringIntoViewSpec(spacingPx)
} else {
@ -132,7 +134,6 @@ data class PositionItem(
data class CollectionFolderGridParameters(
val columns: Int = 6,
val spacing: Dp = 16.dp,
val cardContent: @Composable (GridItemDetails<BaseItem>) -> Unit = { (item, index, onClick, onLongClick, widthPx, mod) ->
GridCard(
item = item,
@ -148,7 +149,6 @@ data class CollectionFolderGridParameters(
val POSTER =
CollectionFolderGridParameters(
columns = 6,
spacing = 16.dp,
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
GridCard(
item = item,
@ -164,7 +164,6 @@ data class CollectionFolderGridParameters(
val WIDE =
CollectionFolderGridParameters(
columns = 4,
spacing = 24.dp,
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
GridCard(
item = item,
@ -180,7 +179,6 @@ data class CollectionFolderGridParameters(
val SQUARE =
CollectionFolderGridParameters(
columns = 6,
spacing = 16.dp,
cardContent = { (item, index, onClick, onLongClick, widthPx, mod) ->
GridCard(
item = item,

View file

@ -63,6 +63,7 @@ import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.rememberInt
import com.github.damontecres.wholphin.ui.roundMinutes
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.jellyfin.sdk.model.api.BaseItemKind
@ -273,9 +274,10 @@ fun CollectionFolderContent(
onPositionChange: (Int) -> Unit,
modifier: Modifier = Modifier,
) {
val globalSpacingPercent = LocalInterfaceCustomization.current.spacingPercent
LazyColumn(
state = listState,
verticalArrangement = Arrangement.spacedBy(viewOptions.spacing.dp),
verticalArrangement = Arrangement.spacedBy((viewOptions.spacing * globalSpacingPercent / 100).dp),
modifier = modifier.focusRestorer(positionFocusRequester),
) {
itemsIndexed(items) { index, item ->

View file

@ -20,6 +20,7 @@ import com.github.damontecres.wholphin.ui.detail.CardGrid
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.RequestHandler
@ -131,7 +132,7 @@ fun ItemGrid(
showJumpButtons = false,
showLetterButtons = false,
initialPosition = destination.initialPosition,
spacing = destination.viewOptions.spacing.dp,
spacing = LocalInterfaceCustomization.current.spacingDp.dp,
cardContent = @Composable { (item, index, onClick, onLongClick, widthPx, mod) ->
GridCard(
item = item,

View file

@ -15,11 +15,11 @@ 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.Cards
import com.github.damontecres.wholphin.ui.cards.ItemRow
import com.github.damontecres.wholphin.ui.cards.SeasonCard
import com.github.damontecres.wholphin.ui.data.RowColumn
import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.util.RowLoadingState
@Composable
@ -48,7 +48,7 @@ fun LoadingRow(
onClick.invoke()
},
onLongClick = onLongClick,
imageHeight = Cards.height2x3,
imageHeight = LocalInterfaceCustomization.current.cardHeightDp.dp,
modifier =
mod
.ifElse(

View file

@ -146,16 +146,6 @@ data class ViewOptions(
getter = { it.columns.toLong() },
setter = { prefs, value -> prefs.copy(columns = value.toInt()) },
)
val ViewOptionsSpacing =
AppSliderPreference<ViewOptions>(
title = R.string.spacing,
defaultValue = 16,
min = 0,
max = 32,
interval = 2,
getter = { it.spacing.toLong() },
setter = { prefs, value -> prefs.copy(spacing = value.toInt()) },
)
val ViewOptionsContentScale =
AppChoicePreference<ViewOptions, PrefContentScale>(
@ -252,7 +242,6 @@ data class ViewOptions(
ViewOptionsBackdrop,
ViewOptionsShowTitles,
ViewOptionsColumns,
ViewOptionsSpacing,
ViewOptionsContentScale,
ViewOptionsReset,
)
@ -262,7 +251,6 @@ data class ViewOptions(
ViewOptionsTypePref,
ViewOptionsDetailHeader,
ViewOptionsBackdrop,
ViewOptionsSpacing,
ViewOptionsReset,
)
}

View file

@ -66,6 +66,7 @@ import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.ui.AppColors
import com.github.damontecres.wholphin.ui.BASE_SPACING_DP
import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.playback.isBackwardButton
@ -320,7 +321,7 @@ fun <T : CardGridItem> CardGrid(
horizontalArrangement = Arrangement.spacedBy(spacing),
verticalArrangement = Arrangement.spacedBy(spacing),
state = gridState,
contentPadding = PaddingValues(vertical = 16.dp),
contentPadding = PaddingValues(horizontal = BASE_SPACING_DP.dp, vertical = 16.dp),
modifier =
Modifier
.fillMaxSize()
@ -333,10 +334,11 @@ fun <T : CardGridItem> CardGrid(
}
}
}.onLayoutRectChanged(0, 0) {
val width = it.width
val spacingPx = with(density) { spacing.toPx() }
val horizontalPaddingPx = with(density) { BASE_SPACING_DP.dp.toPx() } * 2
val usableWidth = it.width - horizontalPaddingPx
val cardWidth =
ceil((width - (spacingPx * (columns - 1))) / columns)
ceil((usableWidth - (spacingPx * (columns - 1))) / columns)
cardWidthPx = cardWidth.toInt()
// Timber.v("cardWidthPx=%s", cardWidthPx)
},

View file

@ -58,6 +58,7 @@ import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.playback.scale
import com.github.damontecres.wholphin.ui.rememberInt
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.ui.util.StringProvider
import com.github.damontecres.wholphin.ui.util.StringStringProvider
import com.github.damontecres.wholphin.util.ApiRequestPager
@ -368,7 +369,7 @@ fun HomeRowGrid(
)
},
columns = if (viewOptions.aspectRatio.ratio > 1f) 4 else 6,
spacing = viewOptions.spacing.dp,
spacing = LocalInterfaceCustomization.current.spacingDp.dp,
bringIntoViewSpec = LocalBringIntoViewSpec.current,
)
}

View file

@ -45,7 +45,6 @@ import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.services.FavoriteWatchManager
import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.services.SeerrService
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.LocalImageUrlService
import com.github.damontecres.wholphin.ui.PreviewTvSpec
import com.github.damontecres.wholphin.ui.SlimItemFields
@ -68,6 +67,7 @@ import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.rememberPosition
import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.ui.util.ResStringProvider
import com.github.damontecres.wholphin.util.ApiRequestPager
import com.github.damontecres.wholphin.util.DataLoadingState
@ -407,7 +407,7 @@ fun PersonPageContent(
onClick.invoke()
},
onLongClick = onLongClick,
imageHeight = Cards.heightEpisode,
imageHeight = LocalInterfaceCustomization.current.episodeCardHeightDp.dp,
modifier =
mod
.ifElse(

View file

@ -18,6 +18,7 @@ import com.github.damontecres.wholphin.ui.cards.GridCard
import com.github.damontecres.wholphin.ui.data.RowColumn
import com.github.damontecres.wholphin.ui.detail.CardGrid
import com.github.damontecres.wholphin.ui.playback.scale
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.ui.util.ScrollToTopBringIntoViewSpec
import org.jellyfin.sdk.model.api.ItemSortBy
@ -44,6 +45,7 @@ fun CollectionMixedGrid(
val density = LocalDensity.current
val cardViewOptions = state.viewOptions.cardViewOptions
val spacingDp = LocalInterfaceCustomization.current.spacingDp
CardGrid(
pager = state.items,
onClickItem = { index: Int, item: BaseItem -> onClickItem.invoke(RowColumn(0, index), item) },
@ -74,10 +76,10 @@ fun CollectionMixedGrid(
)
},
columns = cardViewOptions.columns,
spacing = cardViewOptions.spacing.dp,
spacing = spacingDp.dp,
bringIntoViewSpec =
remember(cardViewOptions) {
val spacingPx = with(density) { cardViewOptions.spacing.dp.toPx() }
remember(cardViewOptions.showDetails, spacingDp) {
val spacingPx = with(density) { spacingDp.dp.toPx() }
if (cardViewOptions.showDetails) {
ScrollToTopBringIntoViewSpec(spacingPx)
} else {

View file

@ -41,7 +41,6 @@ import com.github.damontecres.wholphin.ui.components.ViewOptions.Companion.ViewO
import com.github.damontecres.wholphin.ui.components.ViewOptions.Companion.ViewOptionsImageType
import com.github.damontecres.wholphin.ui.components.ViewOptions.Companion.ViewOptionsReset
import com.github.damontecres.wholphin.ui.components.ViewOptions.Companion.ViewOptionsShowTitles
import com.github.damontecres.wholphin.ui.components.ViewOptions.Companion.ViewOptionsSpacing
import com.github.damontecres.wholphin.ui.preferences.ComposablePreference
import com.github.damontecres.wholphin.ui.tryRequestFocus
import kotlinx.serialization.Serializable
@ -159,7 +158,6 @@ data class CollectionViewOptions(
ViewOptionsDetailHeader,
ViewOptionsShowTitles,
ViewOptionsColumns,
ViewOptionsSpacing,
ViewOptionsContentScale,
ViewOptionsReset,
)

View file

@ -48,7 +48,6 @@ import com.github.damontecres.wholphin.data.model.hasPermission
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.services.SeerrUserConfig
import com.github.damontecres.wholphin.services.TrailerService
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.cards.DiscoverItemCard
import com.github.damontecres.wholphin.ui.cards.DiscoverPersonRow
import com.github.damontecres.wholphin.ui.cards.ItemRow
@ -63,6 +62,7 @@ import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.rememberInt
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.ExceptionHandler
import kotlinx.coroutines.launch
@ -416,7 +416,7 @@ fun TrailerRow(
item = item.baseItem,
onClick = { onClickTrailer.invoke(item) },
onLongClick = {},
imageHeight = Cards.height2x3,
imageHeight = LocalInterfaceCustomization.current.cardHeightDp.dp,
imageWidth = Dp.Unspecified,
showImageOverlay = false,
modifier = cardModifier,
@ -443,7 +443,7 @@ fun TrailerRow(
onLongClick = {},
modifier = cardModifier,
showImageOverlay = false,
imageHeight = Cards.height2x3,
imageHeight = LocalInterfaceCustomization.current.cardHeightDp.dp,
imageWidth = Dp.Unspecified,
)
}

View file

@ -37,7 +37,6 @@ import com.github.damontecres.wholphin.data.model.aspectRatioFloat
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.services.TrailerService
import com.github.damontecres.wholphin.ui.AspectRatios
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
import com.github.damontecres.wholphin.ui.cards.ChapterRow
import com.github.damontecres.wholphin.ui.cards.ExtrasRow
@ -62,6 +61,7 @@ import com.github.damontecres.wholphin.ui.discover.DiscoverRowData
import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.rememberInt
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.ui.util.ResStringProvider
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.DiscoverRequestType
@ -431,12 +431,14 @@ fun MovieDetailsContent(
}
state.similar.letNotEmpty { similar ->
item {
val movieCardHeight = LocalInterfaceCustomization.current.cardHeightDp.dp
val episodeCardHeight = LocalInterfaceCustomization.current.episodeCardHeightDp.dp
val imageHeight =
remember(movie.type) {
remember(movie.type, movieCardHeight, episodeCardHeight) {
if (movie.type == BaseItemKind.MOVIE) {
Cards.height2x3
movieCardHeight
} else {
Cards.heightEpisode
episodeCardHeight
}
}
ItemRow(

View file

@ -43,7 +43,6 @@ 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.Cards
import com.github.damontecres.wholphin.ui.cards.ItemRow
import com.github.damontecres.wholphin.ui.cards.ItemRowTitle
import com.github.damontecres.wholphin.ui.cards.SeasonCard
@ -52,6 +51,7 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage
import com.github.damontecres.wholphin.ui.components.SearchEditTextBox
import com.github.damontecres.wholphin.ui.components.VoiceSearchButton
import com.github.damontecres.wholphin.ui.main.SearchResult
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import kotlinx.coroutines.delay
import org.jellyfin.sdk.model.api.BaseItemKind
@ -268,7 +268,7 @@ fun SearchForResultsRow(
onClick.invoke()
},
onLongClick = onLongClick,
imageHeight = Cards.heightEpisode,
imageHeight = LocalInterfaceCustomization.current.episodeCardHeightDp.dp,
modifier = mod,
)
},

View file

@ -48,7 +48,6 @@ import com.github.damontecres.wholphin.data.model.Trailer
import com.github.damontecres.wholphin.data.model.studioNames
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.services.TrailerService
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
import com.github.damontecres.wholphin.ui.cards.ExtrasRow
import com.github.damontecres.wholphin.ui.cards.ItemRow
@ -82,6 +81,7 @@ import com.github.damontecres.wholphin.ui.discover.DiscoverRowData
import com.github.damontecres.wholphin.ui.letNotEmpty
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.rememberInt
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.ui.util.ResStringProvider
import com.github.damontecres.wholphin.util.DataLoadingState
import com.github.damontecres.wholphin.util.DiscoverRequestType
@ -527,7 +527,7 @@ fun SeriesDetailsContent(
item = item,
onClick = onClick,
onLongClick = onLongClick,
imageHeight = Cards.height2x3,
imageHeight = LocalInterfaceCustomization.current.cardHeightDp.dp,
imageWidth = Dp.Unspecified,
showImageOverlay = true,
modifier = mod,
@ -612,7 +612,7 @@ fun SeriesDetailsContent(
onLongClick = onLongClick,
modifier = mod,
showImageOverlay = true,
imageHeight = Cards.height2x3,
imageHeight = LocalInterfaceCustomization.current.cardHeightDp.dp,
imageWidth = Dp.Unspecified,
)
},

View file

@ -52,7 +52,7 @@ import com.github.damontecres.wholphin.data.model.HomeRowConfig
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
import com.github.damontecres.wholphin.data.model.QuickDetailsData
import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.BASE_SPACING_DP
import com.github.damontecres.wholphin.ui.cards.BannerCard
import com.github.damontecres.wholphin.ui.cards.BannerCardWithTitle
import com.github.damontecres.wholphin.ui.cards.GenreCard
@ -84,7 +84,9 @@ import com.github.damontecres.wholphin.ui.playback.playable
import com.github.damontecres.wholphin.ui.playback.scale
import com.github.damontecres.wholphin.ui.rememberPosition
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.ui.util.ScrollToTopBringIntoViewSpec
import com.github.damontecres.wholphin.ui.util.resolvedCardHeight
import com.github.damontecres.wholphin.util.HomeRowLoadingState
import com.github.damontecres.wholphin.util.LoadingState
import kotlinx.coroutines.delay
@ -364,7 +366,7 @@ fun HomePageContent(
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding =
PaddingValues(
bottom = Cards.height2x3,
bottom = LocalInterfaceCustomization.current.cardHeightDp.dp,
),
modifier =
Modifier
@ -427,7 +429,8 @@ fun HomePageContent(
.focusGroup()
.focusRequester(rowFocusRequesters[rowIndex])
.animateItem(),
horizontalPadding = viewOptions.spacing.dp,
horizontalPadding = BASE_SPACING_DP.dp,
cardSpacing = LocalInterfaceCustomization.current.spacingDp.dp,
cardContent = { index, item, cardModifier, onClick, onLongClick ->
val onFocus =
remember(rowIndex, index) {
@ -623,7 +626,7 @@ fun HomePageCardContent(
imageUrl = item.imageUrlOverride,
onClick = onClick,
onLongClick = onLongClick,
modifier = modifier.height(viewOptions.heightDp.dp),
modifier = modifier.height(viewOptions.resolvedCardHeight()),
)
}
@ -634,7 +637,7 @@ fun HomePageCardContent(
imageUrl = item.imageUrlOverride,
onClick = onClick,
onLongClick = onLongClick,
modifier = modifier.height(viewOptions.heightDp.dp),
modifier = modifier.height(viewOptions.resolvedCardHeight()),
)
}
@ -680,7 +683,7 @@ fun HomePageCardContent(
onClick = onClick,
onLongClick = onLongClick,
modifier = modifier,
cardHeight = viewOptions.heightDp.dp,
cardHeight = viewOptions.resolvedCardHeight(),
useSeriesForPrimary = viewOptions.useSeries,
)
} else {
@ -700,7 +703,7 @@ fun HomePageCardContent(
onLongClick = onLongClick,
modifier = modifier,
interactionSource = null,
cardHeight = viewOptions.heightDp.dp,
cardHeight = viewOptions.resolvedCardHeight(),
useSeriesForPrimary = viewOptions.useSeries,
)
}
@ -729,7 +732,7 @@ fun HomePageViewMoreCard(
onLongClick = onLongClick,
modifier = modifier,
aspectRatio = aspectRatio,
size = DpSize(height = viewOptions.heightDp.dp, width = Dp.Unspecified),
size = DpSize(height = viewOptions.resolvedCardHeight(), width = Dp.Unspecified),
showTitle = viewOptions.showTitles,
)
}

View file

@ -76,7 +76,6 @@ import com.github.damontecres.wholphin.services.NavigationManager
import com.github.damontecres.wholphin.services.SeerrService
import com.github.damontecres.wholphin.services.UserPreferencesService
import com.github.damontecres.wholphin.ui.AspectRatios
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
import com.github.damontecres.wholphin.ui.SlimItemFields
import com.github.damontecres.wholphin.ui.cards.DiscoverItemCard
@ -101,6 +100,7 @@ import com.github.damontecres.wholphin.ui.onMain
import com.github.damontecres.wholphin.ui.preferences.SwitchColors
import com.github.damontecres.wholphin.ui.rememberPosition
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.ui.util.LocalInterfaceCustomization
import com.github.damontecres.wholphin.util.ExceptionHandler
import com.github.damontecres.wholphin.util.SearchRelevance
import dagger.hilt.android.lifecycle.HiltViewModel
@ -746,7 +746,7 @@ fun SearchPage(
onClick.invoke()
},
onLongClick = onLongClick,
imageHeight = Cards.heightEpisode,
imageHeight = LocalInterfaceCustomization.current.episodeCardHeightDp.dp,
aspectRatio = AspectRatios.SQUARE,
showImageOverlay = true,
modifier = mod,
@ -770,7 +770,7 @@ fun SearchPage(
onClick.invoke()
},
onLongClick = onLongClick,
imageHeight = Cards.heightEpisode,
imageHeight = LocalInterfaceCustomization.current.episodeCardHeightDp.dp,
aspectRatio = AspectRatios.SQUARE,
showImageOverlay = true,
modifier = mod,
@ -794,7 +794,7 @@ fun SearchPage(
onClick.invoke()
},
onLongClick = onLongClick,
imageHeight = Cards.heightEpisode,
imageHeight = LocalInterfaceCustomization.current.episodeCardHeightDp.dp,
aspectRatio = AspectRatios.SQUARE,
showImageOverlay = true,
modifier = mod,
@ -1069,7 +1069,7 @@ fun LazyListScope.searchResultRow(
onClick.invoke()
},
onLongClick = onLongClick,
imageHeight = Cards.height2x3,
imageHeight = LocalInterfaceCustomization.current.cardHeightDp.dp,
showImageOverlay = true,
modifier = mod,
)

View file

@ -5,9 +5,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@ -19,7 +16,6 @@ import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.tv.material3.Icon
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R
@ -33,7 +29,6 @@ import com.github.damontecres.wholphin.ui.tryRequestFocus
fun HomeSettingsGlobal(
preferences: AppPreferences,
onPreferenceChange: (AppPreferences) -> Unit,
onClickResize: (Int) -> Unit,
onClickSave: () -> Unit,
onClickLoad: () -> Unit,
onClickLoadWeb: () -> Unit,
@ -107,35 +102,6 @@ fun HomeSettingsGlobal(
)
}
item { HorizontalDivider() }
item {
HomeSettingsListItem(
selected = false,
headlineText = stringResource(R.string.increase_all_cards_size),
leadingContent = {
Icon(
imageVector = Icons.Default.KeyboardArrowUp,
contentDescription = null,
)
},
onClick = { onClickResize.invoke(1) },
modifier = Modifier,
)
}
item {
HomeSettingsListItem(
selected = false,
headlineText = stringResource(R.string.decrease_all_cards_size),
leadingContent = {
Icon(
imageVector = Icons.Default.KeyboardArrowDown,
contentDescription = null,
)
},
onClick = { onClickResize.invoke(-1) },
modifier = Modifier,
)
}
item { HorizontalDivider() }
item {
HomeSettingsListItem(
selected = false,

View file

@ -277,7 +277,6 @@ fun HomeSettingsPage(
viewModel.preferencesDataStore.updateData { newPrefs }
}
},
onClickResize = { viewModel.resizeCards(it) },
onClickSave = {
showConfirmDialog =
ShowConfirm(R.string.overwrite_server_settings) {

View file

@ -630,30 +630,6 @@ class HomeSettingsViewModel
homeSettingsService.currentSettings.update { HomePageResolvedSettings(state.value.rows) }
}
fun resizeCards(relative: Int) {
viewModelScope.launchIO {
updateState {
val newRows =
it.rows.toMutableList().map { row ->
val vo = row.config.viewOptions
val newVo = vo.copy(heightDp = vo.heightDp + (4 * relative))
row.copy(config = row.config.updateViewOptions(newVo))
}
it.copy(
rows = newRows,
rowData =
it.rowData.toMutableList().mapIndexed { index, row ->
if (row is HomeRowLoadingState.Success) {
row.copy(viewOptions = newRows[index].config.viewOptions)
} else {
row
}
},
)
}
}
}
fun resetToDefault() =
viewModelScope.launchIO {
val userId = serverRepository.currentUser?.id ?: return@launchIO

View file

@ -37,6 +37,9 @@ data class InterfaceCustomization(
val cardHeightDp: Int
get() = Cards.HEIGHT_2X3_DP * cardSizePercent / 100
val episodeCardHeightDp: Int
get() = Cards.HEIGHT_EPISODE * cardSizePercent / 100
val spacingDp: Int
get() = BASE_SPACING_DP * spacingPercent / 100
}

View file

@ -1,7 +1,17 @@
package com.github.damontecres.wholphin.ui.util
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
import com.github.damontecres.wholphin.preferences.AppSliderPreference
@Composable
internal fun HomeRowViewOptions.resolvedCardHeight(): Dp {
val global = LocalInterfaceCustomization.current.cardHeightDp
return (if (heightDp > 0) heightDp else global).dp
}
internal fun Int.withinBoundsOrDefault(preference: AppSliderPreference<*>): Int {
val min = preference.min.toInt()
val max = preference.max.toInt()