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, server = current.server,
startDestination = startDestination =
requestedDestination requestedDestination
?: Destination.Home(), // TODO undo after testing
// ?: Destination.Home(),
?: Destination.HomeSettings,
navigationManager = navigationManager, navigationManager = navigationManager,
preferences = preferences, preferences = preferences,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),

View file

@ -2,16 +2,10 @@
package com.github.damontecres.wholphin.data.model 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.preferences.PrefContentScale
import com.github.damontecres.wholphin.ui.AspectRatio import com.github.damontecres.wholphin.ui.AspectRatio
import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.Cards
import com.github.damontecres.wholphin.ui.components.ViewOptionImageType 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 com.github.damontecres.wholphin.ui.data.SortAndDirection
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@ -160,109 +154,6 @@ data class HomeRowViewOptions(
val contentScale: PrefContentScale = PrefContentScale.FIT, val contentScale: PrefContentScale = PrefContentScale.FIT,
val aspectRatio: AspectRatio = AspectRatio.TALL, val aspectRatio: AspectRatio = AspectRatio.TALL,
val imageType: ViewOptionImageType = ViewOptionImageType.PRIMARY, val imageType: ViewOptionImageType = ViewOptionImageType.PRIMARY,
val showTitles: Boolean = true, val showTitles: Boolean = false,
val useSeries: Boolean = true, 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 GetItemsRequestHandler
.execute(api, request) .execute(api, request)
.content.items .content.items
.map { BaseItem.Companion.from(it, api, true) } .map { BaseItem(it, true) }
.let { .let {
HomeRowLoadingState.Success( HomeRowLoadingState.Success(
name ?: context.getString(R.string.collection), row.name,
it, it,
row.viewOptions, row.viewOptions,
) )

View file

@ -15,8 +15,18 @@ import androidx.compose.ui.unit.dp
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import androidx.tv.material3.surfaceColorAtElevation import androidx.tv.material3.surfaceColorAtElevation
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions 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.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.ifElse
import com.github.damontecres.wholphin.ui.preferences.ComposablePreference import com.github.damontecres.wholphin.ui.preferences.ComposablePreference
import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.ui.tryRequestFocus
@ -39,7 +49,7 @@ fun HomeRowSettings(
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
) )
LazyColumn { LazyColumn {
itemsIndexed(HomeRowViewOptions.OPTIONS) { index, pref -> itemsIndexed(Options.OPTIONS) { index, pref ->
pref as AppPreference<HomeRowViewOptions, Any> pref as AppPreference<HomeRowViewOptions, Any>
val interactionSource = remember { MutableInteractionSource() } val interactionSource = remember { MutableInteractionSource() }
val value = pref.getter.invoke(viewOptions) val value = pref.getter.invoke(viewOptions)
@ -52,9 +62,9 @@ fun HomeRowSettings(
}, },
interactionSource = interactionSource, interactionSource = interactionSource,
onClickPreference = { pref -> onClickPreference = { pref ->
if (pref == HomeRowViewOptions.ViewOptionsReset) { if (pref == Options.ViewOptionsReset) {
onViewOptionsChange.invoke(defaultViewOptions) onViewOptionsChange.invoke(defaultViewOptions)
} else if (pref == HomeRowViewOptions.ViewOptionsApplyAll) { } else if (pref == Options.ViewOptionsApplyAll) {
onApplyApplyAll.invoke() 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) listState.scrollToItem(index)
} }
}, },
onClickResize = {
viewModel.resizeCards(it)
},
modifier = destModifier, 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.Add
import androidx.compose.material.icons.filled.Create import androidx.compose.material.icons.filled.Create
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@ -48,6 +49,7 @@ enum class MoveDirection {
fun HomeSettingsRowList( fun HomeSettingsRowList(
state: HomePageSettingsState, state: HomePageSettingsState,
onClick: (Int, HomeRowConfigDisplay) -> Unit, onClick: (Int, HomeRowConfigDisplay) -> Unit,
onClickResize: (Int) -> Unit,
onClickSaveLocal: () -> Unit, onClickSaveLocal: () -> Unit,
onClickAdd: () -> Unit, onClickAdd: () -> Unit,
onClickMove: (MoveDirection, Int) -> Unit, onClickMove: (MoveDirection, Int) -> Unit,
@ -82,6 +84,42 @@ fun HomeSettingsRowList(
modifier = Modifier.focusRequester(firstFocus), 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 { item {
ListItem( ListItem(
selected = false, selected = false,

View file

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