Restores TestHomeRowSamples.kt (sample-data + sanity tests that
survived an earlier soft-reset on disk but never got staged). Updates
the RecentlyAdded sample to use the new cardSizeMultiplier and
spacingMultiplier fields.
Adds InterfaceHelpersTest.kt covering:
- Int.withinBoundsOrDefault (in-range, below-min, above-max, boundary
equality) for both CardSize and Spacing preferences.
- resolveCardHeight (newly extracted pure helper underneath the
composable resolvedCardHeight): sentinel 0, legacy 172, explicit
per-row override, multiplier path.
- InterfaceCustomization.cardHeightDp / spacingDp math.
- migrateLegacyRowSpacing: absent / default / non-default / snapping /
clamping / does-not-overwrite-existing-multiplier.
Adds migrateLegacyRowSpacing in HomeSettingsService and wires it into
decode(): when a row's raw JSON still carries the legacy "spacing"
field (from before this PR deleted the absolute spacing data field),
translate the value into the new spacingMultiplier so users keep the
same visual gap after upgrade.
Extracts resolveCardHeight as a pure helper from the @Composable
resolvedCardHeight so the resolution math is testable without the
Compose runtime.
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.
HomeRowViewOptions is serialized to the user's Jellyfin server.
Existing users have rows with heightDp == 172 (the pre-PR default)
persisted upstream. Without this guard, resolvedCardHeight() would
treat 172 as a per-row override and the Card size slider would do
nothing on the home page until the user explicitly reset their rows.
Treat the legacy 172 value as "no override" so users coming from older
builds see the slider take effect immediately on home rows.
HEIGHT_2X3_DP is now the base value the global multiplier scales
from; no preset sets heightDp to exactly 172. HEIGHT_EPISODE (128)
and other preset values remain valid per-row overrides because they
are not equal to 172.
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".
Consolidates UI sizing into three percentage sliders surfaced through a
new Settings -> Interface -> Display size page: UI scale (50-150%),
Card size (50-150%), Spacing (25-175%). Each persists immediately via
DataStore; the rest of the app reflects them through
LocalInterfaceCustomization and a root-level LocalDensity override in
MainContent.
The page mirrors HomeSettingsPage's two-column shell: a fixed-width
settings panel on the left, HomePageContent rendering live preview rows
on the right. The page snapshots its own density on entry so the slider
rail stays fixed while UI scale changes the rest of the app.
Includes a one-shot AppUpgradeHandler migration block for installs at or
before 0.6.4-31-g0 that resets the three new percent fields to their
defaults.