Restore per-row Card size and Spacing as multipliers; drop legacy spacing field

Per-row card size and spacing return to HomeRowSettings, reframed as
percent multipliers that compound with the global Display Size sliders
instead of the old absolute heightDp / spacing values. Display Presets
continue to set absolute heightDp for special cases (Favorite Episode,
Compact, etc.); the multiplier applies on top of whatever base is in
effect for that row.

HomeRowViewOptions:
- Drops the unused absolute `spacing: Int = 16` field.
- Adds `cardSizeMultiplier: Int = 100` and `spacingMultiplier: Int = 100`.
- `heightDp` stays as the per-row absolute set by Display Presets.

resolvedCardHeight() in InterfaceHelpers now layers the multiplier on
top of the existing per-row vs global resolution.

HomeRowSettings exposes Card size and Spacing sliders for each row
(range 50-150, step 5, default 100) under "General". Genre rows keep
their stripped-down options list and skip both. On HomePage, the
per-row spacingMultiplier compounds with the global Spacing slider for
that row's inter-card gap.
This commit is contained in:
Justin Visser 2026-05-26 11:56:57 +02:00
parent 3060887756
commit 1f9188abc0
4 changed files with 32 additions and 24 deletions

View file

@ -217,14 +217,17 @@ data class HomePageSettings(
const val SUPPORTED_HOME_PAGE_SETTINGS_VERSION = 1 const val SUPPORTED_HOME_PAGE_SETTINGS_VERSION = 1
/** /**
* View options for displaying a row * View options for displaying a row.
* *
* Allows for changing things like height or aspect ratio * [heightDp] of `0` means "use the global card size"; a non-zero value is treated as a per-row
* absolute override (set by Display Presets). [cardSizeMultiplier] and [spacingMultiplier] are
* percents (default 100) applied on top of the global Card size / Spacing sliders for this row.
*/ */
@Serializable @Serializable
data class HomeRowViewOptions( data class HomeRowViewOptions(
val heightDp: Int = 0, val heightDp: Int = 0,
val spacing: Int = 16, val cardSizeMultiplier: Int = 100,
val spacingMultiplier: Int = 100,
val contentScale: PrefContentScale = PrefContentScale.FILL, val contentScale: PrefContentScale = PrefContentScale.FILL,
val aspectRatio: AspectRatio = AspectRatio.TALL, val aspectRatio: AspectRatio = AspectRatio.TALL,
val imageType: ViewOptionImageType = ViewOptionImageType.PRIMARY, val imageType: ViewOptionImageType = ViewOptionImageType.PRIMARY,

View file

@ -430,7 +430,11 @@ fun HomePageContent(
.focusRequester(rowFocusRequesters[rowIndex]) .focusRequester(rowFocusRequesters[rowIndex])
.animateItem(), .animateItem(),
horizontalPadding = BASE_SPACING_DP.dp, horizontalPadding = BASE_SPACING_DP.dp,
cardSpacing = LocalInterfaceCustomization.current.spacingDp.dp, cardSpacing =
(
LocalInterfaceCustomization.current.spacingDp * viewOptions.spacingMultiplier /
100
).dp,
cardContent = { index, item, cardModifier, onClick, onLongClick -> cardContent = { index, item, cardModifier, onClick, onLongClick ->
val onFocus = val onFocus =
remember(rowIndex, index) { remember(rowIndex, index) {

View file

@ -102,7 +102,6 @@ fun HomeRowSettings(
onViewOptionsChange.invoke( onViewOptionsChange.invoke(
viewOptions.copy( viewOptions.copy(
heightDp = Cards.HEIGHT_EPISODE, heightDp = Cards.HEIGHT_EPISODE,
spacing = 20,
imageType = ViewOptionImageType.THUMB, imageType = ViewOptionImageType.THUMB,
aspectRatio = AspectRatio.WIDE, aspectRatio = AspectRatio.WIDE,
contentScale = PrefContentScale.FIT, contentScale = PrefContentScale.FIT,
@ -257,25 +256,28 @@ fun ByParentSortDialogContent(
} }
internal object Options { internal object Options {
val ViewOptionsCardHeight = val ViewOptionsCardSize =
AppSliderPreference<HomeRowViewOptions>( AppSliderPreference<HomeRowViewOptions>(
title = R.string.height, title = R.string.card_size,
defaultValue = Cards.HEIGHT_2X3_DP.toLong(), defaultValue = 100,
min = 64L, min = 50,
max = Cards.HEIGHT_2X3_DP + 64L, max = 150,
interval = 4, interval = 5,
getter = { it.heightDp.toLong() }, getter = { it.cardSizeMultiplier.toLong() },
setter = { prefs, value -> prefs.copy(heightDp = value.toInt()) }, setter = { vo, value -> vo.copy(cardSizeMultiplier = value.toInt()) },
summarizer = { it?.let { "$it%" } },
) )
val ViewOptionsSpacing = val ViewOptionsSpacing =
AppSliderPreference<HomeRowViewOptions>( AppSliderPreference<HomeRowViewOptions>(
title = R.string.spacing, title = R.string.spacing,
defaultValue = 16, defaultValue = 100,
min = 0, min = 50,
max = 32, max = 150,
interval = 2, interval = 5,
getter = { it.spacing.toLong() }, getter = { it.spacingMultiplier.toLong() },
setter = { prefs, value -> prefs.copy(spacing = value.toInt()) }, setter = { vo, value -> vo.copy(spacingMultiplier = value.toInt()) },
summarizer = { it?.let { "$it%" } },
) )
val ViewOptionsContentScale = val ViewOptionsContentScale =
@ -395,7 +397,7 @@ internal object Options {
title = R.string.general, title = R.string.general,
preferences = preferences =
listOf( listOf(
ViewOptionsCardHeight, ViewOptionsCardSize,
ViewOptionsSpacing, ViewOptionsSpacing,
ViewOptionsShowTitles, ViewOptionsShowTitles,
ViewOptionsImageType, ViewOptionsImageType,
@ -421,7 +423,7 @@ internal object Options {
title = R.string.general, title = R.string.general,
preferences = preferences =
listOf( listOf(
ViewOptionsCardHeight, ViewOptionsCardSize,
ViewOptionsSpacing, ViewOptionsSpacing,
ViewOptionsShowTitles, ViewOptionsShowTitles,
ViewOptionsImageType, ViewOptionsImageType,
@ -455,8 +457,6 @@ internal object Options {
title = R.string.general, title = R.string.general,
preferences = preferences =
listOf( listOf(
ViewOptionsCardHeight,
ViewOptionsSpacing,
ViewOptionsReset, ViewOptionsReset,
), ),
), ),

View file

@ -11,7 +11,8 @@ import com.github.damontecres.wholphin.ui.Cards
internal fun HomeRowViewOptions.resolvedCardHeight(): Dp { internal fun HomeRowViewOptions.resolvedCardHeight(): Dp {
val global = LocalInterfaceCustomization.current.cardHeightDp val global = LocalInterfaceCustomization.current.cardHeightDp
val isOverride = heightDp > 0 && heightDp != Cards.HEIGHT_2X3_DP val isOverride = heightDp > 0 && heightDp != Cards.HEIGHT_2X3_DP
return (if (isOverride) heightDp else global).dp val base = if (isOverride) heightDp else global
return (base * cardSizeMultiplier / 100).dp
} }
internal fun Int.withinBoundsOrDefault(preference: AppSliderPreference<*>): Int { internal fun Int.withinBoundsOrDefault(preference: AppSliderPreference<*>): Int {