UI controls

This commit is contained in:
Damontecres 2026-01-25 19:01:33 -05:00
parent 15ce562957
commit efb87bd692
No known key found for this signature in database
7 changed files with 186 additions and 126 deletions

View file

@ -265,7 +265,9 @@ class MainActivity : AppCompatActivity() {
server = current.server,
startDestination =
requestedDestination
?: Destination.Home(),
// TODO undo after testing
// ?: Destination.Home(),
?: Destination.HomeSettings,
navigationManager = navigationManager,
preferences = preferences,
modifier = Modifier.fillMaxSize(),

View file

@ -2,16 +2,10 @@
package com.github.damontecres.wholphin.data.model
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.preferences.AppChoicePreference
import com.github.damontecres.wholphin.preferences.AppClickablePreference
import com.github.damontecres.wholphin.preferences.AppSliderPreference
import com.github.damontecres.wholphin.preferences.AppSwitchPreference
import com.github.damontecres.wholphin.preferences.PrefContentScale
import com.github.damontecres.wholphin.ui.AspectRatio
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.components.ViewOptionImageType
import com.github.damontecres.wholphin.ui.components.ViewOptions
import com.github.damontecres.wholphin.ui.data.SortAndDirection
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@ -160,109 +154,6 @@ data class HomeRowViewOptions(
val contentScale: PrefContentScale = PrefContentScale.FIT,
val aspectRatio: AspectRatio = AspectRatio.TALL,
val imageType: ViewOptionImageType = ViewOptionImageType.PRIMARY,
val showTitles: Boolean = true,
val showTitles: Boolean = false,
val useSeries: Boolean = true,
) {
companion object {
val ViewOptionsColumns =
AppSliderPreference<HomeRowViewOptions>(
title = R.string.height,
defaultValue = Cards.HEIGHT_2X3_DP.toLong(),
min = 64L,
max = Cards.HEIGHT_2X3_DP + 64L,
interval = 4,
getter = { it.heightDp.toLong() },
setter = { prefs, value -> prefs.copy(heightDp = value.toInt()) },
)
val ViewOptionsSpacing =
AppSliderPreference<HomeRowViewOptions>(
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<HomeRowViewOptions, PrefContentScale>(
title = R.string.global_content_scale,
defaultValue = PrefContentScale.FIT,
displayValues = R.array.content_scale,
getter = { it.contentScale },
setter = { viewOptions, value -> viewOptions.copy(contentScale = value) },
indexToValue = { PrefContentScale.forNumber(it) },
valueToIndex = { it.number },
)
val ViewOptionsAspectRatio =
AppChoicePreference<HomeRowViewOptions, AspectRatio>(
title = R.string.aspect_ratio,
defaultValue = AspectRatio.TALL,
displayValues = R.array.aspect_ratios,
getter = { it.aspectRatio },
setter = { viewOptions, value -> viewOptions.copy(aspectRatio = value) },
indexToValue = { AspectRatio.entries[it] },
valueToIndex = { it.ordinal },
)
val ViewOptionsShowTitles =
AppSwitchPreference<HomeRowViewOptions>(
title = R.string.show_titles,
defaultValue = true,
getter = { it.showTitles },
setter = { vo, value -> vo.copy(showTitles = value) },
)
val ViewOptionsUseSeries =
AppSwitchPreference<HomeRowViewOptions>(
title = R.string.go_to_series, // TODO
defaultValue = true,
getter = { it.useSeries },
setter = { vo, value -> vo.copy(useSeries = value) },
)
val ViewOptionsImageType =
AppChoicePreference<HomeRowViewOptions, ViewOptionImageType>(
title = R.string.image_type,
defaultValue = ViewOptionImageType.PRIMARY,
displayValues = R.array.image_types,
getter = { it.imageType },
setter = { viewOptions, value ->
val aspectRatio =
when (value) {
ViewOptionImageType.PRIMARY -> AspectRatio.TALL
ViewOptionImageType.THUMB -> AspectRatio.WIDE
}
viewOptions.copy(imageType = value, aspectRatio = aspectRatio)
},
indexToValue = { ViewOptionImageType.entries[it] },
valueToIndex = { it.ordinal },
)
val ViewOptionsApplyAll =
AppClickablePreference<ViewOptions>(
title = R.string.apply_all_rows,
)
val ViewOptionsReset =
AppClickablePreference<ViewOptions>(
title = R.string.reset,
)
val OPTIONS =
listOf(
ViewOptionsImageType,
ViewOptionsAspectRatio,
// TODO
// ViewOptionsShowTitles,
// ViewOptionsUseSeries,
ViewOptionsColumns,
ViewOptionsSpacing,
ViewOptionsContentScale,
ViewOptionsApplyAll,
ViewOptionsReset,
)
}
}

View file

@ -550,22 +550,13 @@ class HomeSettingsService
)
}
}
val name =
if (row.name == null && request.parentId != null) {
// If a name was not provided, use the parent's name if available
api.userLibraryApi
.getItem(itemId = request.parentId!!)
.content.name
} else {
row.name
}
GetItemsRequestHandler
.execute(api, request)
.content.items
.map { BaseItem.Companion.from(it, api, true) }
.map { BaseItem(it, true) }
.let {
HomeRowLoadingState.Success(
name ?: context.getString(R.string.collection),
row.name,
it,
row.viewOptions,
)

View file

@ -15,8 +15,18 @@ import androidx.compose.ui.unit.dp
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import androidx.tv.material3.surfaceColorAtElevation
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
import com.github.damontecres.wholphin.preferences.AppChoicePreference
import com.github.damontecres.wholphin.preferences.AppClickablePreference
import com.github.damontecres.wholphin.preferences.AppPreference
import com.github.damontecres.wholphin.preferences.AppSliderPreference
import com.github.damontecres.wholphin.preferences.AppSwitchPreference
import com.github.damontecres.wholphin.preferences.PrefContentScale
import com.github.damontecres.wholphin.ui.AspectRatio
import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.components.ViewOptionImageType
import com.github.damontecres.wholphin.ui.components.ViewOptions
import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.preferences.ComposablePreference
import com.github.damontecres.wholphin.ui.tryRequestFocus
@ -39,7 +49,7 @@ fun HomeRowSettings(
color = MaterialTheme.colorScheme.onSurface,
)
LazyColumn {
itemsIndexed(HomeRowViewOptions.OPTIONS) { index, pref ->
itemsIndexed(Options.OPTIONS) { index, pref ->
pref as AppPreference<HomeRowViewOptions, Any>
val interactionSource = remember { MutableInteractionSource() }
val value = pref.getter.invoke(viewOptions)
@ -52,9 +62,9 @@ fun HomeRowSettings(
},
interactionSource = interactionSource,
onClickPreference = { pref ->
if (pref == HomeRowViewOptions.ViewOptionsReset) {
if (pref == Options.ViewOptionsReset) {
onViewOptionsChange.invoke(defaultViewOptions)
} else if (pref == HomeRowViewOptions.ViewOptionsApplyAll) {
} else if (pref == Options.ViewOptionsApplyAll) {
onApplyApplyAll.invoke()
}
},
@ -70,3 +80,106 @@ fun HomeRowSettings(
}
}
}
internal object Options {
val ViewOptionsColumns =
AppSliderPreference<HomeRowViewOptions>(
title = R.string.height,
defaultValue = Cards.HEIGHT_2X3_DP.toLong(),
min = 64L,
max = Cards.HEIGHT_2X3_DP + 64L,
interval = 4,
getter = { it.heightDp.toLong() },
setter = { prefs, value -> prefs.copy(heightDp = value.toInt()) },
)
val ViewOptionsSpacing =
AppSliderPreference<HomeRowViewOptions>(
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<HomeRowViewOptions, PrefContentScale>(
title = R.string.global_content_scale,
defaultValue = PrefContentScale.FIT,
displayValues = R.array.content_scale,
getter = { it.contentScale },
setter = { viewOptions, value -> viewOptions.copy(contentScale = value) },
indexToValue = { PrefContentScale.forNumber(it) },
valueToIndex = { it.number },
)
val ViewOptionsAspectRatio =
AppChoicePreference<HomeRowViewOptions, AspectRatio>(
title = R.string.aspect_ratio,
defaultValue = AspectRatio.TALL,
displayValues = R.array.aspect_ratios,
getter = { it.aspectRatio },
setter = { viewOptions, value -> viewOptions.copy(aspectRatio = value) },
indexToValue = { AspectRatio.entries[it] },
valueToIndex = { it.ordinal },
)
val ViewOptionsShowTitles =
AppSwitchPreference<HomeRowViewOptions>(
title = R.string.show_titles,
defaultValue = true,
getter = { it.showTitles },
setter = { vo, value -> vo.copy(showTitles = value) },
)
val ViewOptionsUseSeries =
AppSwitchPreference<HomeRowViewOptions>(
title = R.string.go_to_series, // TODO
defaultValue = true,
getter = { it.useSeries },
setter = { vo, value -> vo.copy(useSeries = value) },
)
val ViewOptionsImageType =
AppChoicePreference<HomeRowViewOptions, ViewOptionImageType>(
title = R.string.image_type,
defaultValue = ViewOptionImageType.PRIMARY,
displayValues = R.array.image_types,
getter = { it.imageType },
setter = { viewOptions, value ->
val aspectRatio =
when (value) {
ViewOptionImageType.PRIMARY -> AspectRatio.TALL
ViewOptionImageType.THUMB -> AspectRatio.WIDE
}
viewOptions.copy(imageType = value, aspectRatio = aspectRatio)
},
indexToValue = { ViewOptionImageType.entries[it] },
valueToIndex = { it.ordinal },
)
val ViewOptionsApplyAll =
AppClickablePreference<ViewOptions>(
title = R.string.apply_all_rows,
)
val ViewOptionsReset =
AppClickablePreference<ViewOptions>(
title = R.string.reset,
)
val OPTIONS =
listOf(
ViewOptionsImageType,
ViewOptionsAspectRatio,
// TODO
// ViewOptionsShowTitles,
// ViewOptionsUseSeries,
ViewOptionsColumns,
ViewOptionsSpacing,
ViewOptionsContentScale,
ViewOptionsApplyAll,
ViewOptionsReset,
)
}

View file

@ -78,6 +78,9 @@ fun HomeSettingsPage(
listState.scrollToItem(index)
}
},
onClickResize = {
viewModel.resizeCards(it)
},
modifier = destModifier,
)
}

View file

@ -16,6 +16,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Create
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@ -48,6 +49,7 @@ enum class MoveDirection {
fun HomeSettingsRowList(
state: HomePageSettingsState,
onClick: (Int, HomeRowConfigDisplay) -> Unit,
onClickResize: (Int) -> Unit,
onClickSaveLocal: () -> Unit,
onClickAdd: () -> Unit,
onClickMove: (MoveDirection, Int) -> Unit,
@ -82,6 +84,42 @@ fun HomeSettingsRowList(
modifier = Modifier.focusRequester(firstFocus),
)
}
item {
ListItem(
selected = false,
headlineContent = {
Text(
text = "Increase card sizes",
)
},
leadingContent = {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = null,
)
},
onClick = { onClickResize.invoke(1) },
modifier = Modifier,
)
}
item {
ListItem(
selected = false,
headlineContent = {
Text(
text = "Decrease card sizes",
)
},
leadingContent = {
Icon(
imageVector = Icons.Default.Edit,
contentDescription = null,
)
},
onClick = { onClickResize.invoke(-1) },
modifier = Modifier,
)
}
item {
ListItem(
selected = false,

View file

@ -322,6 +322,28 @@ class HomeSettingsViewModel
}
homeSettingsService.currentSettings.update { HomePageResolvedSettings(state.value.rows) }
}
fun resizeCards(relative: Int) {
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
}
},
)
}
}
}
data class HomePageSettingsState(