diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/EmergencyModeActivityUi.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/EmergencyModeActivityUi.kt index 4e248895..e686fde9 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/EmergencyModeActivityUi.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/EmergencyModeActivityUi.kt @@ -1,14 +1,15 @@ package xyz.sevive.arcaeaoffline.ui.activities import android.content.Intent +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons @@ -34,7 +35,6 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -45,7 +45,8 @@ import io.github.vinceglb.filekit.path import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.helpers.context.persistUriPermissions import xyz.sevive.arcaeaoffline.ui.components.IconRow -import xyz.sevive.arcaeaoffline.ui.components.ListGroupHeader +import xyz.sevive.arcaeaoffline.ui.components.SettingsGroupHeader +import xyz.sevive.arcaeaoffline.ui.theme.spacing @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -76,7 +77,6 @@ private fun UiTopAppBar(modifier: Modifier = Modifier) { ) } -@OptIn(ExperimentalLayoutApi::class) @Composable fun EmergencyModeActivityUi( modifier: Modifier = Modifier, @@ -109,17 +109,15 @@ fun EmergencyModeActivityUi( .consumeWindowInsets(padding), contentPadding = PaddingValues( - top = padding.calculateTopPadding() + dimensionResource(R.dimen.list_padding), - bottom = padding.calculateBottomPadding() + dimensionResource(R.dimen.list_padding), - start = dimensionResource(R.dimen.list_padding), - end = dimensionResource(R.dimen.list_padding), + top = padding.calculateTopPadding() + MaterialTheme.spacing.sm, + bottom = padding.calculateBottomPadding() + MaterialTheme.spacing.sm, ), ) { item { - ListGroupHeader(stringResource(R.string.emergency_mode_output_directory_title)) + SettingsGroupHeader(stringResource(R.string.emergency_mode_output_directory_title)) } item { - Column { + Column(Modifier.padding(horizontal = MaterialTheme.spacing.pagePadding)) { IconRow { Icon(Icons.Default.Code, contentDescription = null) Text( @@ -138,7 +136,7 @@ fun EmergencyModeActivityUi( Text(stringResource(R.string.emergency_mode_output_directory_select_button)) } - Spacer(Modifier.width(dimensionResource(R.dimen.list_padding))) + Spacer(Modifier.width(MaterialTheme.spacing.sm)) CompositionLocalProvider( LocalContentColor provides @@ -163,10 +161,13 @@ fun EmergencyModeActivityUi( } item { - ListGroupHeader(stringResource(R.string.emergency_mode_ocr_title)) + SettingsGroupHeader(stringResource(R.string.emergency_mode_ocr_title)) } item { - FlowRow { + FlowRow( + Modifier.padding(horizontal = MaterialTheme.spacing.pagePadding), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), + ) { Button(onClick = { viewModel.deleteAllOcrDependencies() }) { IconRow { Icon(Icons.Default.DeleteForever, contentDescription = null) @@ -184,10 +185,13 @@ fun EmergencyModeActivityUi( } item { - ListGroupHeader(stringResource(R.string.emergency_mode_database_title)) + SettingsGroupHeader(stringResource(R.string.emergency_mode_database_title)) } item { - FlowRow { + FlowRow( + Modifier.padding(horizontal = MaterialTheme.spacing.pagePadding), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), + ) { Button( onClick = { viewModel.copyDatabase(context) }, enabled = outputDirectoryValid, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/AppIconLabelButton.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/AppIconLabelButton.kt index 56238319..bf2cad45 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/AppIconLabelButton.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/AppIconLabelButton.kt @@ -10,12 +10,12 @@ import androidx.compose.material3.Button import androidx.compose.material3.ButtonColors import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.unit.dp -import xyz.sevive.arcaeaoffline.R +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun AppIconLabelButton( @@ -34,14 +34,14 @@ internal fun AppIconLabelButton( shape = RoundedCornerShape(20.dp), ) { Row( - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.action_button_icon_text_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), verticalAlignment = Alignment.CenterVertically, ) { appIcon() Column( Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.icon_text_padding)), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.iconTextGap), ) { appLabel() diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/OcrFromShareActions.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/OcrFromShareActions.kt index 945db581..2977aee9 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/OcrFromShareActions.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/OcrFromShareActions.kt @@ -23,7 +23,6 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.launch @@ -31,6 +30,7 @@ import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.permissions.storage.SaveBitmapToGallery import xyz.sevive.arcaeaoffline.ui.common.PermissionRequiredDialog import xyz.sevive.arcaeaoffline.ui.components.IconRow +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun OcrFromShareActions(ocrFromShareViewModel: OcrFromShareViewModel) { @@ -63,7 +63,7 @@ internal fun OcrFromShareActions(ocrFromShareViewModel: OcrFromShareViewModel) { val scoreCached by ocrFromShareViewModel.scoreCached.collectAsStateWithLifecycle() Column { - Row(horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding))) { + Row(horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm)) { Button( onClick = { coroutineScope.launch { ocrFromShareViewModel.saveScore() } }, enabled = score != null && !scoreSaved && !scoreCached, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/OcrFromShareScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/OcrFromShareScreen.kt index 4021bc0d..c107cca6 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/OcrFromShareScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/activities/ocrfromshare/OcrFromShareScreen.kt @@ -25,10 +25,10 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import xyz.sevive.arcaeaoffline.R +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun OcrFromShareReturnToShareAppButton( @@ -111,12 +111,12 @@ internal fun OcrFromShareScreenContentMedium( .fillMaxSize() .consumeWindowInsets(innerPadding) .padding(innerPadding) - .padding(dimensionResource(R.dimen.page_padding)), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.page_padding)), + .padding(MaterialTheme.spacing.pagePadding), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.pagePadding), ) { Column( Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { OcrFromShareOcrDependencyStatusCard(ocrDependencyViewersUiState) @@ -127,7 +127,7 @@ internal fun OcrFromShareScreenContentMedium( LazyColumn( Modifier.weight(1f), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { item { OcrFromShareOcrResult(viewModel) @@ -170,12 +170,12 @@ fun OcrFromShareScreenCompact( .consumeWindowInsets(innerPadding), contentPadding = PaddingValues( - top = innerPadding.calculateTopPadding() + dimensionResource(R.dimen.page_padding), - bottom = innerPadding.calculateBottomPadding() + dimensionResource(R.dimen.page_padding), - start = dimensionResource(R.dimen.page_padding), - end = dimensionResource(R.dimen.page_padding), + top = innerPadding.calculateTopPadding() + MaterialTheme.spacing.pagePadding, + bottom = innerPadding.calculateBottomPadding() + MaterialTheme.spacing.pagePadding, + start = MaterialTheme.spacing.pagePadding, + end = MaterialTheme.spacing.pagePadding, ), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { item { OcrFromShareOcrDependencyStatusCard(ocrDependencyViewersUiState) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/common/PermissionRequiredDialog.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/common/PermissionRequiredDialog.kt index ef8bf961..bd4e19bb 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/common/PermissionRequiredDialog.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/common/PermissionRequiredDialog.kt @@ -17,17 +17,17 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha +import xyz.sevive.arcaeaoffline.ui.components.BaseSettingsItem import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogConfirmButton import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogDismissTextButton -import xyz.sevive.arcaeaoffline.ui.components.preferences.BasePreferencesWidget import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable fun rememberPermissionIcon(permission: String): ImageVector? = @@ -78,7 +78,7 @@ private fun PermissionWidget( val content = rememberPermissionDescription(permission) val icon = rememberPermissionIcon(permission) - BasePreferencesWidget( + BaseSettingsItem( title = { PermissionTitle(permission) }, content = { Text( @@ -110,7 +110,7 @@ fun PermissionRequiredDialog( icon = { Icon(Icons.Default.Flaky, contentDescription = null) }, title = { Text(stringResource(R.string.permission_required_dialog_title)) }, text = { - Column(verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding))) { + Column(verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm)) { Text( pluralStringResource( R.plurals.permission_required_dialog_content, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/common/datetimeeditor/DateTimeEditDialog.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/common/datetimeeditor/DateTimeEditDialog.kt index d60263bf..a41769d8 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/common/datetimeeditor/DateTimeEditDialog.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/common/datetimeeditor/DateTimeEditDialog.kt @@ -43,7 +43,6 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDateTime import kotlinx.datetime.LocalTime @@ -55,10 +54,11 @@ import kotlinx.datetime.toJavaLocalDate import kotlinx.datetime.toJavaLocalTime import kotlinx.datetime.toLocalDateTime import xyz.sevive.arcaeaoffline.R +import xyz.sevive.arcaeaoffline.ui.components.TextItem import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogConfirmButton import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogDismissTextButton -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing import java.time.format.DateTimeFormatter import java.time.format.FormatStyle import kotlin.time.Clock @@ -199,7 +199,7 @@ internal fun DateTimeEditDialog( text = { LazyColumn { item { - TextPreferencesWidget( + TextItem( onClick = { showDateEditDialog = true }, leadingIcon = Icons.Default.CalendarMonth, leadingIconTint = MaterialTheme.colorScheme.secondary, @@ -210,7 +210,7 @@ internal fun DateTimeEditDialog( } item { - TextPreferencesWidget( + TextItem( onClick = { showTimeEditDialog = true }, leadingIcon = Icons.Default.AccessTime, leadingIconTint = MaterialTheme.colorScheme.secondary, @@ -248,7 +248,7 @@ private fun DateTimeEditDialogRealDevicePreview() { ) } - Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + Column(verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm)) { Text(text = dateTime.format(LocalDateTime.Formats.ISO)) Button(onClick = { showDialog = true }) { diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaChartCard.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaChartCard.kt index 4f36f768..1df484f5 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaChartCard.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaChartCard.kt @@ -32,12 +32,10 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import kotlinx.coroutines.flow.combine import org.koin.compose.koinInject -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.constants.ArcaeaRatingClass import xyz.sevive.arcaeaoffline.core.constants.ArcaeaRatingClassDisplay import xyz.sevive.arcaeaoffline.core.database.entities.ChartInfo @@ -47,6 +45,7 @@ import xyz.sevive.arcaeaoffline.core.database.repositories.DifficultyWithSongRep import xyz.sevive.arcaeaoffline.ui.helpers.ArcaeaFormatters import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme import xyz.sevive.arcaeaoffline.ui.theme.ratingClassColor +import xyz.sevive.arcaeaoffline.ui.theme.spacing // Display data resolved as one atomic unit. Swapping the pair together is // what keeps the card consistent (title/level text and constant change in @@ -99,8 +98,8 @@ fun ArcaeaChartCard( shape = shape, ) { Row( - Modifier.padding(dimensionResource(R.dimen.card_padding)), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + Modifier.padding(MaterialTheme.spacing.cardPadding), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), verticalAlignment = Alignment.CenterVertically, ) { Image( diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaChartSelector.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaChartSelector.kt index 78ff3856..54a450f7 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaChartSelector.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaChartSelector.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State @@ -16,7 +17,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import org.koin.compose.koinInject import xyz.sevive.arcaeaoffline.R @@ -25,6 +25,7 @@ import xyz.sevive.arcaeaoffline.core.database.entities.Difficulty import xyz.sevive.arcaeaoffline.core.database.entities.Song import xyz.sevive.arcaeaoffline.core.database.repositories.DifficultyRepository import xyz.sevive.arcaeaoffline.core.database.repositories.SongRepository +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun rememberArcaeaSong( @@ -79,8 +80,8 @@ fun ArcaeaChartSelector( } Column( - Modifier.padding(dimensionResource(R.dimen.card_padding)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + Modifier.padding(MaterialTheme.spacing.cardPadding), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { ArcaeaPackAndSongSelector( song = song, @@ -89,7 +90,7 @@ fun ArcaeaChartSelector( Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.icon_text_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.iconTextGap), ) { Icon(painterResource(R.drawable.ic_rating_class), contentDescription = null) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPackAndSongQuickSearch.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPackAndSongQuickSearch.kt index c723ebbc..2627df13 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPackAndSongQuickSearch.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPackAndSongQuickSearch.kt @@ -36,6 +36,7 @@ import xyz.sevive.arcaeaoffline.core.database.entities.Pack import xyz.sevive.arcaeaoffline.core.database.entities.Song import xyz.sevive.arcaeaoffline.core.database.repositories.PackRepository import xyz.sevive.arcaeaoffline.core.database.repositories.SongRepository +import xyz.sevive.arcaeaoffline.ui.theme.spacing data class FilterResult( val packId: String, @@ -153,7 +154,7 @@ fun ArcaeaPackAndSongQuickSearch( candidates.forEachIndexed { i, candidate -> DropdownMenuItem( text = { - Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + Column(verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.xxs)) { Text(candidate.song.title, style = MaterialTheme.typography.labelLarge) Text( candidate.pack.name, @@ -168,7 +169,10 @@ fun ArcaeaPackAndSongQuickSearch( expanded = false focusManager.clearFocus() }, - contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding.plus(PaddingValues(vertical = 8.dp)), + contentPadding = + ExposedDropdownMenuDefaults.ItemContentPadding.plus( + PaddingValues(vertical = MaterialTheme.spacing.sm), + ), ) if (i != candidates.lastIndex) { diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPackAndSongSelector.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPackAndSongSelector.kt index baf05e12..b854bec3 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPackAndSongSelector.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPackAndSongSelector.kt @@ -3,6 +3,7 @@ package xyz.sevive.arcaeaoffline.ui.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State @@ -13,14 +14,13 @@ import androidx.compose.runtime.produceState import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import kotlinx.coroutines.flow.firstOrNull import org.koin.compose.koinInject -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.database.entities.Pack import xyz.sevive.arcaeaoffline.core.database.entities.Song import xyz.sevive.arcaeaoffline.core.database.repositories.PackRepository import xyz.sevive.arcaeaoffline.core.database.repositories.SongRepository +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun rememberArcaeaPacks(packRepo: PackRepository): State> = @@ -122,7 +122,7 @@ fun ArcaeaPackAndSongSelector( Column( modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { ArcaeaPackAndSongQuickSearch( onSelect = { filter -> diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPlayResultCard.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPlayResultCard.kt index d2f55c17..46144504 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPlayResultCard.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPlayResultCard.kt @@ -40,7 +40,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalResources -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString @@ -60,12 +59,12 @@ import xyz.sevive.arcaeaoffline.core.database.entities.DifficultyWithSong import xyz.sevive.arcaeaoffline.core.database.entities.PlayResult import xyz.sevive.arcaeaoffline.helpers.ArcaeaPlayResultValidatorWarning import xyz.sevive.arcaeaoffline.helpers.formatAsLocalizedDateTime -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget import xyz.sevive.arcaeaoffline.ui.helpers.ArcaeaFormatters import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme import xyz.sevive.arcaeaoffline.ui.theme.arcaeaColors import xyz.sevive.arcaeaoffline.ui.theme.extendedColorScheme import xyz.sevive.arcaeaoffline.ui.theme.playResultGradeGradientBrush +import xyz.sevive.arcaeaoffline.ui.theme.spacing import kotlin.time.Instant @Composable @@ -111,7 +110,7 @@ private fun PlayResultDetailsDialog( LazyColumn { items.entries.forEach { item { - TextPreferencesWidget(title = it.key, content = it.value) + TextItem(title = it.key, content = it.value) } } } @@ -194,7 +193,7 @@ fun ArcaeaPlayResultCard( Row(verticalAlignment = Alignment.CenterVertically) { Column( Modifier - .width(exPlusWidthDp + dimensionResource(R.dimen.card_padding)) + .width(exPlusWidthDp + MaterialTheme.spacing.cardPadding) .fillMaxHeight(), horizontalAlignment = Alignment.CenterHorizontally, ) { @@ -254,11 +253,11 @@ fun ArcaeaPlayResultCard( Column( Modifier .weight(1f) - .padding(dimensionResource(R.dimen.card_padding)), + .padding(MaterialTheme.spacing.cardPadding), ) { Text(scoreText, style = MaterialTheme.typography.titleLarge) - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Row(horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm)) { Text( pflAnnotatedString("P", playResult.pure), color = MaterialTheme.arcaeaColors.pure, @@ -455,7 +454,7 @@ private fun PlayResultCardPreview() { playResult = playResults[i], difficultyWithSong = if (i >= 1) difficulty else null, chartInfo = if (i >= 1) chartInfo else null, - modifier = Modifier.padding(bottom = 4.dp), + modifier = Modifier.padding(bottom = MaterialTheme.spacing.xs), ) } } diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPlayResultValidatorWarningDetails.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPlayResultValidatorWarningDetails.kt index 93f4ea93..d8ea025f 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPlayResultValidatorWarningDetails.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ArcaeaPlayResultValidatorWarningDetails.kt @@ -37,11 +37,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.sp -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.helpers.ArcaeaPlayResultValidatorMaxRecallOverflowWarning import xyz.sevive.arcaeaoffline.helpers.ArcaeaPlayResultValidatorPflOverflowWarning import xyz.sevive.arcaeaoffline.helpers.ArcaeaPlayResultValidatorPureMemoryFarLostNotZeroWarning @@ -49,6 +47,7 @@ import xyz.sevive.arcaeaoffline.helpers.ArcaeaPlayResultValidatorWarning import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme import xyz.sevive.arcaeaoffline.ui.theme.extendedColorScheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun ArcaeaPlayResultValidatorWarningDetailItem( @@ -64,7 +63,7 @@ private fun ArcaeaPlayResultValidatorWarningDetailItem( val context = LocalContext.current val density = LocalDensity.current - val padding = dimensionResource(R.dimen.card_padding) + val padding = MaterialTheme.spacing.cardPadding val messagePadding = remember(density) { val start = with(density) { 24.sp.toDp() + padding * 2 } @@ -87,7 +86,7 @@ private fun ArcaeaPlayResultValidatorWarningDetailItem( Modifier .minimumInteractiveComponentSize() .padding(padding), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), verticalAlignment = Alignment.CenterVertically, ) { CompositionLocalProvider( diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/BasicAlertDialogSurface.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/BasicAlertDialogSurface.kt index 4dc81780..ae2bfd82 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/BasicAlertDialogSurface.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/BasicAlertDialogSurface.kt @@ -4,11 +4,12 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties +import xyz.sevive.arcaeaoffline.ui.theme.spacing @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -27,7 +28,7 @@ fun BasicAlertDialogSurface( shape = AlertDialogDefaults.shape, color = AlertDialogDefaults.containerColor, ) { - content(PaddingValues(all = 24.dp)) + content(PaddingValues(all = MaterialTheme.spacing.dialogPadding)) } } } diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/IconRow.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/IconRow.kt index 813957b7..ef964f99 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/IconRow.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/IconRow.kt @@ -3,11 +3,11 @@ package xyz.sevive.arcaeaoffline.ui.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.RowScope +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource -import xyz.sevive.arcaeaoffline.R +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable fun IconRow( @@ -16,7 +16,7 @@ fun IconRow( ) { Row( modifier, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.icon_text_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.iconTextGap), verticalAlignment = Alignment.CenterVertically, content = content, ) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ImagePreviewDialog.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ImagePreviewDialog.kt index c3e10b26..02667d78 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ImagePreviewDialog.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ImagePreviewDialog.kt @@ -31,13 +31,12 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties import com.github.panpf.zoomimage.SketchZoomAsyncImage -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.helpers.context.getFilename +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun TopFileInfoCard( @@ -55,7 +54,7 @@ private fun TopFileInfoCard( ) { Text( if (toggle) filename else uri.toString(), - Modifier.padding(dimensionResource(R.dimen.page_padding)), + Modifier.padding(MaterialTheme.spacing.pagePadding), style = MaterialTheme.typography.labelLarge, ) } @@ -86,7 +85,7 @@ fun ImagePreviewDialogFullscreen( Box( Modifier .windowInsetsPadding(WindowInsets.navigationBars) - .padding(bottom = 28.dp) + .padding(bottom = MaterialTheme.spacing.xl) .fillMaxWidth(), ) { FilledTonalIconButton( diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/LinearProgressIndicatorWrapper.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/LinearProgressIndicatorWrapper.kt index 39a1d6d9..7f060377 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/LinearProgressIndicatorWrapper.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/LinearProgressIndicatorWrapper.kt @@ -16,8 +16,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.SpanStyle import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.withStyle -import androidx.compose.ui.unit.dp import xyz.sevive.arcaeaoffline.core.Progress +import xyz.sevive.arcaeaoffline.ui.theme.spacing import kotlin.math.roundToInt typealias PercentageFormatter = (Float) -> String @@ -69,7 +69,7 @@ fun LinearProgressIndicatorWrapper( Text(text = progressLabel, style = MaterialTheme.typography.bodyMedium) } - Spacer(modifier = Modifier.height(4.dp)) + Spacer(modifier = Modifier.height(MaterialTheme.spacing.xs)) if (isIndeterminate) { LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ListGroupHeader.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ListGroupHeader.kt index a4276da0..83c4df8d 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ListGroupHeader.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ListGroupHeader.kt @@ -1,7 +1,6 @@ package xyz.sevive.arcaeaoffline.ui.components import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalTextStyle @@ -10,37 +9,30 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontWeight -import xyz.sevive.arcaeaoffline.R - -object ListGroupHeaderDefaults { - val paddingValues - @Composable get() = - PaddingValues( - horizontal = dimensionResource(R.dimen.list_group_header_horizontal_padding), - vertical = dimensionResource(R.dimen.list_group_header_vertical_padding), - ) - val contentColor - @Composable get() = MaterialTheme.colorScheme.primary - val textStyle - @Composable get() = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Light) -} +import xyz.sevive.arcaeaoffline.ui.theme.header +import xyz.sevive.arcaeaoffline.ui.theme.spacing +/** + * Group header for items in an edge-to-edge scrolling list. + * Place it directly in the list, not inside a horizontally padded container. + * + * For headers above [BaseSettingsItem] rows, prefer [SettingsGroupHeader]. + */ @Composable fun ListGroupHeader( modifier: Modifier = Modifier, - paddingValues: PaddingValues = ListGroupHeaderDefaults.paddingValues, - contentColor: Color = ListGroupHeaderDefaults.contentColor, - textStyle: TextStyle = ListGroupHeaderDefaults.textStyle, content: @Composable () -> Unit, ) { - Box(modifier = modifier.padding(paddingValues)) { + Box( + modifier = + modifier.padding( + horizontal = MaterialTheme.spacing.lg, + vertical = MaterialTheme.spacing.sm, + ), + ) { CompositionLocalProvider( - LocalContentColor provides contentColor, - LocalTextStyle provides textStyle, + LocalContentColor provides MaterialTheme.typography.header.color, + LocalTextStyle provides MaterialTheme.typography.header, ) { content() } @@ -51,16 +43,8 @@ fun ListGroupHeader( fun ListGroupHeader( text: String, modifier: Modifier = Modifier, - paddingValues: PaddingValues = ListGroupHeaderDefaults.paddingValues, - contentColor: Color = ListGroupHeaderDefaults.contentColor, - textStyle: TextStyle = ListGroupHeaderDefaults.textStyle, ) { - ListGroupHeader( - modifier = modifier, - paddingValues = paddingValues, - contentColor = contentColor, - textStyle = textStyle, - ) { - Text(text = text) + ListGroupHeader(modifier = modifier) { + Text(text) } } diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/NullableNumberInput.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/NullableNumberInput.kt index 8b5a565a..ca08803b 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/NullableNumberInput.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/NullableNumberInput.kt @@ -32,6 +32,7 @@ import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation import androidx.compose.ui.unit.dp import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogDismissTextButtonDefaults +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun NullableNumberInputEditDialog( @@ -55,7 +56,7 @@ private fun NullableNumberInputEditDialog( AlertDialog( onDismissRequest = onDismiss, text = { - Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) { + Row(horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.xs)) { TextField( value = textFieldValue, onValueChange = { textFieldValue = it }, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/PlayRatingCalculator.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/PlayRatingCalculator.kt index 3623f5b4..9c92258a 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/PlayRatingCalculator.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/PlayRatingCalculator.kt @@ -24,16 +24,15 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.calculators.calculateClearBonus import xyz.sevive.arcaeaoffline.core.calculators.calculatePlayRating import xyz.sevive.arcaeaoffline.core.constants.ArcaeaPlayResultClearType import xyz.sevive.arcaeaoffline.ui.components.arcaea.OutlinedArcaeaScoreTextField import xyz.sevive.arcaeaoffline.ui.components.arcaea.rememberArcaeaScoreTextFieldState -import xyz.sevive.arcaeaoffline.ui.components.preferences.SwitchPreferencesWidget import xyz.sevive.arcaeaoffline.ui.helpers.ArcaeaFormatters import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable fun PlayRatingCalculator( @@ -92,7 +91,7 @@ fun PlayRatingCalculator( Column(modifier) { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { OutlinedArcaeaScoreTextField( scoreTextFieldState, @@ -110,7 +109,7 @@ fun PlayRatingCalculator( } Row( - Modifier.padding(top = 16.dp), + Modifier.padding(top = MaterialTheme.spacing.lg), verticalAlignment = Alignment.CenterVertically, ) { Icon(Icons.AutoMirrored.Filled.ArrowRight, contentDescription = null) @@ -123,7 +122,7 @@ fun PlayRatingCalculator( } if (countClearBonus) { - SwitchPreferencesWidget( + SwitchItem( value = clearType != null && clearType != ArcaeaPlayResultClearType.TRACK_LOST, onValueChange = { cleared -> clearType = if (cleared) ArcaeaPlayResultClearType.NORMAL_CLEAR else null }, title = stringResource(R.string.play_rating_cleared), diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/SelectDialog.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/SelectDialog.kt index 8a8ea7b8..a9bb75b2 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/SelectDialog.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/SelectDialog.kt @@ -14,11 +14,10 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.unit.dp -import xyz.sevive.arcaeaoffline.R +import xyz.sevive.arcaeaoffline.ui.theme.spacing /** * A simple selection dialog. @@ -38,7 +37,7 @@ fun SelectDialog( ) { BasicAlertDialogSurface(onDismissRequest = onDismiss) { if (labels.isEmpty()) { - Box(Modifier.padding(dimensionResource(R.dimen.page_padding) * 2)) { + Box(Modifier.padding(MaterialTheme.spacing.pagePadding * 2)) { Text("No options available.") } } else { @@ -60,8 +59,8 @@ fun SelectDialog( } else { Modifier }, - ).padding(horizontal = 16.dp, vertical = 8.dp), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.icon_text_padding) * 2), + ).padding(horizontal = MaterialTheme.spacing.lg, vertical = MaterialTheme.spacing.sm), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.iconTextGap * 2), verticalAlignment = Alignment.CenterVertically, ) { RadioButton( diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/SettingsItems.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/SettingsItems.kt new file mode 100644 index 00000000..b1cfa08d --- /dev/null +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/SettingsItems.kt @@ -0,0 +1,389 @@ +package xyz.sevive.arcaeaoffline.ui.components + +import androidx.annotation.IntRange +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.BugReport +import androidx.compose.material3.Checkbox +import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Slider +import androidx.compose.material3.Surface +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.minimumInteractiveComponentSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.tooling.preview.PreviewLightDark +import xyz.sevive.arcaeaoffline.helpers.DISABLED_ALPHA +import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha +import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.header +import xyz.sevive.arcaeaoffline.ui.theme.spacing + +private val HorizontalPadding + @Composable + get() = MaterialTheme.spacing.lg +private val VerticalPadding + @Composable + get() = MaterialTheme.spacing.lg + +/** + * Group header whose text aligns with [BaseSettingsItem] titles by sharing + * the same gutter. + */ +@Composable +fun SettingsGroupHeader( + text: String, + modifier: Modifier = Modifier, +) { + SettingsGroupHeader(modifier = modifier) { + Text(text) + } +} + +@Composable +fun SettingsGroupHeader( + modifier: Modifier = Modifier, + content: @Composable () -> Unit, +) { + Box( + modifier = + modifier + .fillMaxWidth() + .padding( + top = MaterialTheme.spacing.xl, + bottom = MaterialTheme.spacing.sm, + start = HorizontalPadding, + end = HorizontalPadding, + ), + contentAlignment = Alignment.CenterStart, + ) { + CompositionLocalProvider( + LocalContentColor provides MaterialTheme.typography.header.color, + LocalTextStyle provides MaterialTheme.typography.header, + ) { + content() + } + } +} + +@Composable +fun BaseSettingsItem( + title: @Composable ColumnScope.() -> Unit, + modifier: Modifier = Modifier, + onClick: (() -> Unit)? = null, + leadingSlot: (@Composable () -> Unit)? = null, + trailingSlot: (@Composable () -> Unit)? = null, + content: (@Composable ColumnScope.() -> Unit)? = null, +) { + Row( + Modifier + .clickable(onClick != null) { onClick?.invoke() } + .minimumInteractiveComponentSize() + .then(modifier), + verticalAlignment = Alignment.CenterVertically, + ) { + leadingSlot?.let { + Box(Modifier.padding(start = HorizontalPadding, end = HorizontalPadding / 2)) { it() } + } + + Column( + Modifier + .weight(1f) + .padding(horizontal = HorizontalPadding, vertical = VerticalPadding), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.xxs), + ) { + CompositionLocalProvider( + LocalTextStyle provides MaterialTheme.typography.bodyLarge, + ) { + title(this@Column) + } + content?.let { it(this@Column) } + } + + trailingSlot?.let { + Box(Modifier.padding(start = HorizontalPadding / 2, end = HorizontalPadding)) { it() } + } + } +} + +@PreviewLightDark +@Composable +private fun BaseSettingsItemPreview() { + ArcaeaOfflineTheme { + Surface { + BaseSettingsItem( + title = { Text("Title") }, + leadingSlot = { Icon(Icons.Default.BugReport, contentDescription = null) }, + trailingSlot = { Text("Trailing") }, + ) { + Text("Lorem ipsum dolor sit amet", style = MaterialTheme.typography.bodySmall) + } + } + } +} + +@Composable +fun CheckboxItem( + value: Boolean, + onValueChange: (Boolean) -> Unit, + title: @Composable ColumnScope.() -> Unit, + modifier: Modifier = Modifier, + leadingSlot: (@Composable () -> Unit)? = null, + content: (@Composable ColumnScope.() -> Unit)? = null, +) { + BaseSettingsItem( + onClick = { onValueChange(!value) }, + title = title, + modifier = modifier, + content = content, + leadingSlot = leadingSlot, + trailingSlot = { + Checkbox( + checked = value, + onCheckedChange = null, + ) + }, + ) +} + +@Composable +fun CheckboxItem( + value: Boolean, + onValueChange: (Boolean) -> Unit, + title: String, + modifier: Modifier = Modifier, + icon: ImageVector? = null, + iconTint: Color = MaterialTheme.colorScheme.primary, + description: String? = null, +) { + CheckboxItem( + value = value, + onValueChange = onValueChange, + title = { Text(title) }, + leadingSlot = icon?.let { { Icon(icon, contentDescription = null, tint = iconTint) } }, + modifier = modifier, + content = description?.let { { Text(description, Modifier.secondaryItemAlpha()) } }, + ) +} + +@Composable +fun SwitchItem( + value: Boolean, + onValueChange: (Boolean) -> Unit, + title: @Composable ColumnScope.() -> Unit, + modifier: Modifier = Modifier, + leadingSlot: (@Composable () -> Unit)? = null, + content: (@Composable ColumnScope.() -> Unit)? = null, +) { + BaseSettingsItem( + onClick = { onValueChange(!value) }, + title = title, + modifier = modifier, + content = content, + leadingSlot = leadingSlot, + trailingSlot = { + Switch( + checked = value, + onCheckedChange = null, + ) + }, + ) +} + +@Composable +fun SwitchItem( + value: Boolean, + onValueChange: (Boolean) -> Unit, + title: String, + modifier: Modifier = Modifier, + icon: ImageVector? = null, + iconTint: Color = MaterialTheme.colorScheme.primary, + description: String? = null, +) { + SwitchItem( + value = value, + onValueChange = onValueChange, + title = { Text(title) }, + leadingSlot = icon?.let { { Icon(icon, contentDescription = null, tint = iconTint) } }, + modifier = modifier, + content = description?.let { { Text(description, Modifier.secondaryItemAlpha()) } }, + ) +} + +data class SelectOption( + val value: T, + val label: String, + val description: String? = null, +) + +/** + * A radio-button list of mutually exclusive options; renders one + * [BaseSettingsItem] row per option without an outer container. + */ +@Composable +fun SelectItem( + options: List>, + selected: T, + onSelect: (T) -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier) { + options.forEach { option -> + BaseSettingsItem( + onClick = { onSelect(option.value) }, + title = { Text(option.label) }, + content = + option.description?.let { description -> + { + Text( + description, + Modifier.secondaryItemAlpha(), + style = MaterialTheme.typography.bodySmall, + ) + } + }, + leadingSlot = { + RadioButton( + selected = option.value == selected, + onClick = null, + ) + }, + ) + } + } +} + +@Composable +fun SliderItem( + value: Float, + onValueChange: (Float) -> Unit, + title: @Composable ColumnScope.() -> Unit, + valueRange: ClosedFloatingPointRange = 0f..1f, + @IntRange(from = 0) steps: Int = 0, + leadingSlot: (@Composable () -> Unit)? = null, + trailingSlot: (@Composable () -> Unit)? = null, + content: (@Composable ColumnScope.() -> Unit)? = null, +) { + BaseSettingsItem( + title = title, + leadingSlot = leadingSlot, + trailingSlot = trailingSlot, + ) { + Column( + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), + ) { + content?.invoke(this@Column) + Slider( + value = value, + onValueChange = onValueChange, + valueRange = valueRange, + steps = steps, + ) + } + } +} + +@Composable +fun SliderItem( + value: Float, + onValueChange: (Float) -> Unit, + title: String, + valueRange: ClosedFloatingPointRange = 0f..1f, + @IntRange(from = 0) steps: Int = 0, + icon: ImageVector? = null, + iconTint: Color = MaterialTheme.colorScheme.primary, + trailingSlot: (@Composable () -> Unit)? = null, + description: String? = null, +) { + SliderItem( + value = value, + onValueChange = onValueChange, + title = { Text(title) }, + valueRange = valueRange, + steps = steps, + leadingSlot = icon?.let { { Icon(icon, contentDescription = null, tint = iconTint) } }, + trailingSlot = trailingSlot, + content = description?.let { { Text(description, Modifier.secondaryItemAlpha()) } }, + ) +} + +@Composable +fun TextItem( + title: String, + modifier: Modifier = Modifier, + content: String? = null, + leadingSlot: (@Composable () -> Unit)? = null, + trailingSlot: (@Composable () -> Unit)? = null, + enabled: Boolean = true, + onClick: (() -> Unit)? = null, +) { + CompositionLocalProvider( + LocalContentColor provides + LocalContentColor.current.copy( + alpha = if (enabled) 1f else DISABLED_ALPHA, + ), + ) { + BaseSettingsItem( + title = { Text(title) }, + content = + content?.let { + { + Text( + it, + Modifier.secondaryItemAlpha(), + style = MaterialTheme.typography.bodySmall, + ) + } + }, + leadingSlot = leadingSlot, + trailingSlot = trailingSlot, + onClick = if (enabled) onClick else null, + modifier = modifier, + ) + } +} + +@Composable +fun TextItem( + title: String, + modifier: Modifier = Modifier, + content: String? = null, + leadingIcon: ImageVector? = null, + leadingIconTint: Color = MaterialTheme.colorScheme.primary, + trailingIcon: ImageVector? = null, + trailingIconTint: Color = LocalContentColor.current, + enabled: Boolean = true, + onClick: (() -> Unit)? = null, +) { + TextItem( + title = title, + content = content, + leadingSlot = + leadingIcon?.let { + { Icon(it, contentDescription = null, tint = leadingIconTint) } + }, + trailingSlot = + trailingIcon?.let { + { Icon(it, contentDescription = null, tint = trailingIconTint) } + }, + onClick = onClick, + enabled = enabled, + modifier = modifier, + ) +} diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ocr/OcrDependencyStatusChip.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ocr/OcrDependencyStatusChip.kt index c9cc8bf7..a257dcc0 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ocr/OcrDependencyStatusChip.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ocr/OcrDependencyStatusChip.kt @@ -31,7 +31,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.Preview @@ -41,6 +40,7 @@ import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.helpers.OcrDependencyStatus import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme import xyz.sevive.arcaeaoffline.ui.theme.extendedColorScheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing data class OcrDependencyStatusChipUiState( val icon: ImageVector = Icons.Default.QuestionMark, @@ -97,7 +97,7 @@ fun OcrDependencyStatusChip( modifier: Modifier = Modifier, ) { val padding by animateDpAsState( - targetValue = if (uiState.expanded) dimensionResource(R.dimen.icon_text_padding) else 0.dp, + targetValue = if (uiState.expanded) MaterialTheme.spacing.iconTextGap else 0.dp, label = "iconPadding", ) @@ -144,7 +144,7 @@ private fun OcrDependencyStatusChipSinglePreview() { Surface( Modifier .width(200.dp) - .padding(8.dp), + .padding(MaterialTheme.spacing.sm), ) { OcrDependencyStatusChip( OcrDependencyStatus.UNKNOWN, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ocr/OcrDependencyStatusViewer.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ocr/OcrDependencyStatusViewer.kt index 59fa38f9..5e0b64b6 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ocr/OcrDependencyStatusViewer.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/ocr/OcrDependencyStatusViewer.kt @@ -20,12 +20,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.tooling.preview.PreviewLightDark -import androidx.compose.ui.unit.dp import xyz.sevive.arcaeaoffline.helpers.OcrDependencyStatus import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha +import xyz.sevive.arcaeaoffline.ui.components.BaseSettingsItem import xyz.sevive.arcaeaoffline.ui.components.IconRow -import xyz.sevive.arcaeaoffline.ui.components.preferences.BasePreferencesWidget import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing object OcrDependencyStatusViewerDefaults { @Composable @@ -75,7 +75,7 @@ fun OcrDependencyStatusViewer( ) } - BasePreferencesWidget( + BaseSettingsItem( onClick = details?.let { { showDetails = true } }, title = { title() }, leadingSlot = icon, @@ -132,10 +132,10 @@ fun OcrDependencyStatusViewer( @Composable private fun OcrDependencyStatusViewerPreview() { ArcaeaOfflineTheme { - Card(Modifier.padding(8.dp)) { + Card(Modifier.padding(MaterialTheme.spacing.sm)) { Text( "Test Dependencies", - Modifier.padding(16.dp), + Modifier.padding(MaterialTheme.spacing.lg), style = MaterialTheme.typography.titleLarge, ) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/BasePreferencesWidget.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/BasePreferencesWidget.kt deleted file mode 100644 index c519567c..00000000 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/BasePreferencesWidget.kt +++ /dev/null @@ -1,89 +0,0 @@ -package xyz.sevive.arcaeaoffline.ui.components.preferences - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.padding -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.BugReport -import androidx.compose.material3.Icon -import androidx.compose.material3.LocalTextStyle -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.material3.minimumInteractiveComponentSize -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.tooling.preview.PreviewLightDark -import androidx.compose.ui.unit.dp -import xyz.sevive.arcaeaoffline.R -import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme - -private val HorizontalPadding - @Composable - get() = dimensionResource(R.dimen.pref_widget_horizontal_padding) -private val VerticalPadding - @Composable - get() = dimensionResource(R.dimen.pref_widget_vertical_padding) - -@Composable -fun BasePreferencesWidget( - title: @Composable ColumnScope.() -> Unit, - modifier: Modifier = Modifier, - onClick: (() -> Unit)? = null, - leadingSlot: (@Composable () -> Unit)? = null, - trailingSlot: (@Composable () -> Unit)? = null, - content: (@Composable ColumnScope.() -> Unit)? = null, -) { - Row( - Modifier - .clickable(onClick != null) { onClick?.invoke() } - .minimumInteractiveComponentSize() - .then(modifier), - verticalAlignment = Alignment.CenterVertically, - ) { - leadingSlot?.let { - Box(Modifier.padding(start = HorizontalPadding, end = HorizontalPadding / 2)) { it() } - } - - Column( - Modifier - .weight(1f) - .padding(horizontal = HorizontalPadding, vertical = VerticalPadding), - verticalArrangement = Arrangement.spacedBy(2.dp), - ) { - CompositionLocalProvider( - LocalTextStyle provides MaterialTheme.typography.bodyLarge, - ) { - title(this@Column) - } - content?.let { it(this@Column) } - } - - trailingSlot?.let { - Box(Modifier.padding(start = HorizontalPadding / 2, end = HorizontalPadding)) { it() } - } - } -} - -@PreviewLightDark -@Composable -private fun BasePreferencesWidgetPreview() { - ArcaeaOfflineTheme { - Surface { - BasePreferencesWidget( - title = { Text("Title") }, - leadingSlot = { Icon(Icons.Default.BugReport, contentDescription = null) }, - trailingSlot = { Text("Trailing") }, - ) { - Text("Lorem ipsum dolor sit amet", style = MaterialTheme.typography.bodySmall) - } - } - } -} diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/CheckboxPreferencesWidget.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/CheckboxPreferencesWidget.kt deleted file mode 100644 index a5f0ba3e..00000000 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/CheckboxPreferencesWidget.kt +++ /dev/null @@ -1,56 +0,0 @@ -package xyz.sevive.arcaeaoffline.ui.components.preferences - -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.material3.Checkbox -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha - -@Composable -fun CheckboxPreferencesWidget( - value: Boolean, - onValueChange: (Boolean) -> Unit, - title: @Composable ColumnScope.() -> Unit, - modifier: Modifier = Modifier, - leadingSlot: (@Composable () -> Unit)? = null, - content: (@Composable ColumnScope.() -> Unit)? = null, -) { - BasePreferencesWidget( - onClick = { onValueChange(!value) }, - title = title, - modifier = modifier, - content = content, - leadingSlot = leadingSlot, - trailingSlot = { - Checkbox( - checked = value, - onCheckedChange = null, - ) - }, - ) -} - -@Composable -fun CheckboxPreferencesWidget( - value: Boolean, - onValueChange: (Boolean) -> Unit, - title: String, - modifier: Modifier = Modifier, - icon: ImageVector? = null, - iconTint: Color = MaterialTheme.colorScheme.primary, - description: String? = null, -) { - CheckboxPreferencesWidget( - value = value, - onValueChange = onValueChange, - title = { Text(title) }, - leadingSlot = icon?.let { { Icon(icon, contentDescription = null, tint = iconTint) } }, - modifier = modifier, - content = description?.let { { Text(description, Modifier.secondaryItemAlpha()) } }, - ) -} diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/SelectPreferencesWidget.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/SelectPreferencesWidget.kt deleted file mode 100644 index fccfe46f..00000000 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/SelectPreferencesWidget.kt +++ /dev/null @@ -1,52 +0,0 @@ -package xyz.sevive.arcaeaoffline.ui.components.preferences - -import androidx.compose.foundation.layout.Column -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.RadioButton -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha - -data class SelectPreferencesOption( - val value: T, - val label: String, - val description: String? = null, -) - -/** - * A radio-button list of mutually exclusive options; renders one - * [BasePreferencesWidget] row per option without an outer container. - */ -@Composable -fun SelectPreferencesWidget( - options: List>, - selected: T, - onSelect: (T) -> Unit, - modifier: Modifier = Modifier, -) { - Column(modifier) { - options.forEach { option -> - BasePreferencesWidget( - onClick = { onSelect(option.value) }, - title = { Text(option.label) }, - content = - option.description?.let { description -> - { - Text( - description, - Modifier.secondaryItemAlpha(), - style = MaterialTheme.typography.bodySmall, - ) - } - }, - leadingSlot = { - RadioButton( - selected = option.value == selected, - onClick = null, - ) - }, - ) - } - } -} diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/SliderPreferencesWidget.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/SliderPreferencesWidget.kt deleted file mode 100644 index 0e87f2fe..00000000 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/SliderPreferencesWidget.kt +++ /dev/null @@ -1,71 +0,0 @@ -package xyz.sevive.arcaeaoffline.ui.components.preferences - -import androidx.annotation.IntRange -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Slider -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.dimensionResource -import xyz.sevive.arcaeaoffline.R -import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha - -@Composable -fun SliderPreferencesWidget( - value: Float, - onValueChange: (Float) -> Unit, - title: @Composable ColumnScope.() -> Unit, - valueRange: ClosedFloatingPointRange = 0f..1f, - @IntRange(from = 0) steps: Int = 0, - leadingSlot: (@Composable () -> Unit)? = null, - trailingSlot: (@Composable () -> Unit)? = null, - content: (@Composable ColumnScope.() -> Unit)? = null, -) { - BasePreferencesWidget( - title = title, - leadingSlot = leadingSlot, - trailingSlot = trailingSlot, - ) { - Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), - ) { - content?.invoke(this@Column) - Slider( - value = value, - onValueChange = onValueChange, - valueRange = valueRange, - steps = steps, - ) - } - } -} - -@Composable -fun SliderPreferencesWidget( - value: Float, - onValueChange: (Float) -> Unit, - title: String, - valueRange: ClosedFloatingPointRange = 0f..1f, - @IntRange(from = 0) steps: Int = 0, - icon: ImageVector? = null, - iconTint: Color = MaterialTheme.colorScheme.primary, - trailingSlot: (@Composable () -> Unit)? = null, - description: String? = null, -) { - SliderPreferencesWidget( - value = value, - onValueChange = onValueChange, - title = { Text(title) }, - valueRange = valueRange, - steps = steps, - leadingSlot = icon?.let { { Icon(icon, contentDescription = null, tint = iconTint) } }, - trailingSlot = trailingSlot, - content = description?.let { { Text(description, Modifier.secondaryItemAlpha()) } }, - ) -} diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/SwitchPreferencesWidget.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/SwitchPreferencesWidget.kt deleted file mode 100644 index 5437d825..00000000 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/SwitchPreferencesWidget.kt +++ /dev/null @@ -1,56 +0,0 @@ -package xyz.sevive.arcaeaoffline.ui.components.preferences - -import androidx.compose.foundation.layout.ColumnScope -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Switch -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha - -@Composable -fun SwitchPreferencesWidget( - value: Boolean, - onValueChange: (Boolean) -> Unit, - title: @Composable ColumnScope.() -> Unit, - modifier: Modifier = Modifier, - leadingSlot: (@Composable () -> Unit)? = null, - content: (@Composable ColumnScope.() -> Unit)? = null, -) { - BasePreferencesWidget( - onClick = { onValueChange(!value) }, - title = title, - modifier = modifier, - content = content, - leadingSlot = leadingSlot, - trailingSlot = { - Switch( - checked = value, - onCheckedChange = null, - ) - }, - ) -} - -@Composable -fun SwitchPreferencesWidget( - value: Boolean, - onValueChange: (Boolean) -> Unit, - title: String, - modifier: Modifier = Modifier, - icon: ImageVector? = null, - iconTint: Color = MaterialTheme.colorScheme.primary, - description: String? = null, -) { - SwitchPreferencesWidget( - value = value, - onValueChange = onValueChange, - title = { Text(title) }, - leadingSlot = icon?.let { { Icon(icon, contentDescription = null, tint = iconTint) } }, - modifier = modifier, - content = description?.let { { Text(description, Modifier.secondaryItemAlpha()) } }, - ) -} diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/TextPreferencesWidget.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/TextPreferencesWidget.kt deleted file mode 100644 index 490f77fc..00000000 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/preferences/TextPreferencesWidget.kt +++ /dev/null @@ -1,78 +0,0 @@ -package xyz.sevive.arcaeaoffline.ui.components.preferences - -import androidx.compose.material3.Icon -import androidx.compose.material3.LocalContentColor -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import xyz.sevive.arcaeaoffline.helpers.DISABLED_ALPHA -import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha - -@Composable -fun TextPreferencesWidget( - title: String, - modifier: Modifier = Modifier, - content: String? = null, - leadingSlot: (@Composable () -> Unit)? = null, - trailingSlot: (@Composable () -> Unit)? = null, - enabled: Boolean = true, - onClick: (() -> Unit)? = null, -) { - CompositionLocalProvider( - LocalContentColor provides - LocalContentColor.current.copy( - alpha = if (enabled) 1f else DISABLED_ALPHA, - ), - ) { - BasePreferencesWidget( - title = { Text(title) }, - content = - content?.let { - { - Text( - it, - Modifier.secondaryItemAlpha(), - style = MaterialTheme.typography.bodySmall, - ) - } - }, - leadingSlot = leadingSlot, - trailingSlot = trailingSlot, - onClick = if (enabled) onClick else null, - modifier = modifier, - ) - } -} - -@Composable -fun TextPreferencesWidget( - title: String, - modifier: Modifier = Modifier, - content: String? = null, - leadingIcon: ImageVector? = null, - leadingIconTint: Color = MaterialTheme.colorScheme.primary, - trailingIcon: ImageVector? = null, - trailingIconTint: Color = LocalContentColor.current, - enabled: Boolean = true, - onClick: (() -> Unit)? = null, -) { - TextPreferencesWidget( - title = title, - content = content, - leadingSlot = - leadingIcon?.let { - { Icon(it, contentDescription = null, tint = leadingIconTint) } - }, - trailingSlot = - trailingIcon?.let { - { Icon(it, contentDescription = null, tint = trailingIconTint) } - }, - onClick = onClick, - enabled = enabled, - modifier = modifier, - ) -} diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/resources/RemoteResourceDownloadItem.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/resources/RemoteResourceDownloadItem.kt index 5437d4a0..98d8445b 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/resources/RemoteResourceDownloadItem.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/components/resources/RemoteResourceDownloadItem.kt @@ -11,7 +11,7 @@ import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.api.ArcaeaResourcesRemoteFileInfo import xyz.sevive.arcaeaoffline.core.api.DownloadableResource import xyz.sevive.arcaeaoffline.core.api.RemoteResourcesInfoUiState -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget +import xyz.sevive.arcaeaoffline.ui.components.TextItem @Composable private fun contentFor( @@ -48,7 +48,7 @@ fun RemoteResourceDownloadItem( ) { val fileInfo = infoState.info?.get(resource) - TextPreferencesWidget( + TextItem( onClick = onDownload, // fileInfo is null only while remote info is unknown (refresh failed or not yet fetched); // downloading on an unprobed path would just 404, so wait for a successful refresh. diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/EmptyScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/EmptyScreen.kt index 9f9778ce..36a6958c 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/EmptyScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/EmptyScreen.kt @@ -13,10 +13,10 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import xyz.sevive.arcaeaoffline.R +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable fun EmptyScreen( @@ -27,7 +27,7 @@ fun EmptyScreen( Column( Modifier.align(Alignment.Center), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { Icon( Icons.Default.Inbox, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/NavEntryNavigateButton.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/NavEntryNavigateButton.kt index aed09804..2fd67d5b 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/NavEntryNavigateButton.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/NavEntryNavigateButton.kt @@ -6,7 +6,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowRight import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget +import xyz.sevive.arcaeaoffline.ui.components.TextItem @Composable fun NavEntryNavigateButton( @@ -15,7 +15,7 @@ fun NavEntryNavigateButton( icon: ImageVector? = null, onClick: () -> Unit, ) { - TextPreferencesWidget( + TextItem( title = title, content = description, leadingIcon = icon, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/DatabaseNavEntry.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/DatabaseNavEntry.kt index a8c34de1..aaa1903a 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/DatabaseNavEntry.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/DatabaseNavEntry.kt @@ -13,6 +13,7 @@ import androidx.compose.material.icons.filled.CopyAll import androidx.compose.material.icons.filled.History import androidx.compose.material.icons.filled.Star import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar @@ -20,16 +21,15 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.koin.compose.viewmodel.koinViewModel -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.constants.ArcaeaScoringMode import xyz.sevive.arcaeaoffline.ui.navigation.DatabaseSubScreen import xyz.sevive.arcaeaoffline.ui.navigation.LocalListDetailNavigationContext import xyz.sevive.arcaeaoffline.ui.navigation.MainScreen import xyz.sevive.arcaeaoffline.ui.screens.NavEntryNavigateButton +import xyz.sevive.arcaeaoffline.ui.theme.spacing @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -59,7 +59,7 @@ fun DatabaseNavEntry( statusUiState, Modifier .fillMaxWidth() - .padding(horizontal = dimensionResource(R.dimen.page_padding)), + .padding(horizontal = MaterialTheme.spacing.pagePadding), ) } diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/DatabaseStatus.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/DatabaseStatus.kt index 8ba9d8a6..27e7f43f 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/DatabaseStatus.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/DatabaseStatus.kt @@ -12,6 +12,7 @@ import androidx.compose.material.icons.filled.MusicNote import androidx.compose.material.icons.filled.Translate import androidx.compose.material3.Card import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -19,13 +20,13 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewLightDark import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.components.IconRow import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun DatabaseStatusIconRow( @@ -35,7 +36,7 @@ private fun DatabaseStatusIconRow( deletedItemCount: Int? = null, ) { Row( - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), verticalAlignment = Alignment.Bottom, ) { IconRow { @@ -92,8 +93,8 @@ fun DatabaseStatus( Card(modifier) { Column( - Modifier.padding(dimensionResource(R.dimen.card_padding)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + Modifier.padding(MaterialTheme.spacing.cardPadding), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { DatabaseStatusIconRow( icon = ImageVector.vectorResource(R.drawable.ic_database), diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultChartAction.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultChartAction.kt index 33f5a414..d971490c 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultChartAction.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultChartAction.kt @@ -12,6 +12,7 @@ import androidx.compose.material.icons.filled.TouchApp import androidx.compose.material3.Card import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.minimumInteractiveComponentSize @@ -22,7 +23,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark import xyz.sevive.arcaeaoffline.R @@ -34,6 +34,7 @@ import xyz.sevive.arcaeaoffline.ui.components.BasicAlertDialogSurface import xyz.sevive.arcaeaoffline.ui.components.IconRow import xyz.sevive.arcaeaoffline.ui.components.rememberArcaeaChartDisplay import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun SelectChartDialog( @@ -87,7 +88,7 @@ internal fun DatabaseAddPlayResultChartAction( IconRow( Modifier .minimumInteractiveComponentSize() - .padding(dimensionResource(R.dimen.card_padding)), + .padding(MaterialTheme.spacing.cardPadding), ) { Icon(Icons.Default.TouchApp, null) Text(stringResource(R.string.database_add_play_result_click_select_chart)) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultPlayResultAction.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultPlayResultAction.kt index 22ec61a6..16a2169e 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultPlayResultAction.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultPlayResultAction.kt @@ -12,6 +12,7 @@ import androidx.compose.material.icons.filled.Edit import androidx.compose.material3.Card import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.minimumInteractiveComponentSize @@ -22,7 +23,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark import xyz.sevive.arcaeaoffline.R @@ -33,6 +33,7 @@ import xyz.sevive.arcaeaoffline.ui.components.ArcaeaPlayResultCard import xyz.sevive.arcaeaoffline.ui.components.ArcaeaPlayResultEditorDialog import xyz.sevive.arcaeaoffline.ui.components.IconRow import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun DatabaseAddPlayResultPlayResultAction( @@ -68,7 +69,7 @@ internal fun DatabaseAddPlayResultPlayResultAction( IconRow( Modifier .minimumInteractiveComponentSize() - .padding(dimensionResource(R.dimen.card_padding)), + .padding(MaterialTheme.spacing.cardPadding), ) { Icon(Icons.Default.Block, null) Text(stringResource(R.string.database_add_play_result_select_chart_first)) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultScreen.kt index c13275ef..19581585 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/addplayresult/DatabaseAddPlayResultScreen.kt @@ -1,6 +1,5 @@ package xyz.sevive.arcaeaoffline.ui.screens.database.addplayresult -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets @@ -25,15 +24,15 @@ import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.koin.compose.viewmodel.koinViewModel import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.SubScreenContainer import xyz.sevive.arcaeaoffline.ui.components.IconRow -import xyz.sevive.arcaeaoffline.ui.components.ListGroupHeader +import xyz.sevive.arcaeaoffline.ui.components.SettingsGroupHeader import xyz.sevive.arcaeaoffline.ui.navigation.DatabaseSubScreen +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun BottomActionsBar( @@ -86,7 +85,7 @@ internal fun DatabaseAddPlayResultScreen( modifier = Modifier .fillMaxWidth() - .padding(dimensionResource(R.dimen.page_padding)), + .padding(MaterialTheme.spacing.pagePadding), ) }, ) { innerPadding -> @@ -97,24 +96,19 @@ internal fun DatabaseAddPlayResultScreen( contentPadding = innerPadding, ) { item { - ListGroupHeader(stringResource(R.string.database_add_play_result_select_chart_header)) + SettingsGroupHeader(stringResource(R.string.database_add_play_result_select_chart_header)) } item { DatabaseAddPlayResultChartAction( difficulty = uiState.difficulty, onDifficultyChange = { viewModel.setDifficulty(it) }, - modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.list_group_header_horizontal_padding)), + modifier = Modifier.padding(horizontal = MaterialTheme.spacing.lg), ) } item { - // Just adding a padding - Box(Modifier.padding(vertical = dimensionResource(R.dimen.list_padding))) {} - } - - item { - ListGroupHeader(stringResource(R.string.database_add_play_result_edit_play_result_header)) + SettingsGroupHeader(stringResource(R.string.database_add_play_result_edit_play_result_header)) } item { @@ -122,7 +116,7 @@ internal fun DatabaseAddPlayResultScreen( playResult = uiState.playResult, onPlayResultChange = { viewModel.setPlayResult(it) }, warnings = uiState.warnings, - modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.list_group_header_horizontal_padding)), + modifier = Modifier.padding(horizontal = MaterialTheme.spacing.lg), ) } } diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/bestlist/DatabaseBestListItem.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/bestlist/DatabaseBestListItem.kt index 4b7a0d60..77f89970 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/bestlist/DatabaseBestListItem.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/bestlist/DatabaseBestListItem.kt @@ -15,13 +15,11 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.withStyle import androidx.compose.ui.tooling.preview.Preview -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.constants.ArcaeaRatingClass import xyz.sevive.arcaeaoffline.core.database.entities.ChartInfo import xyz.sevive.arcaeaoffline.core.database.entities.DifficultyWithSong @@ -29,6 +27,7 @@ import xyz.sevive.arcaeaoffline.core.database.entities.PlayResult import xyz.sevive.arcaeaoffline.core.database.entities.PlayResultCalculated import xyz.sevive.arcaeaoffline.ui.components.ArcaeaPlayResultCard import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing import kotlin.time.Instant import kotlin.uuid.Uuid @@ -60,7 +59,7 @@ internal fun DatabaseBestListItem( Row( modifier, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), verticalAlignment = Alignment.Bottom, ) { ArcaeaPlayResultCard( @@ -78,7 +77,7 @@ internal fun DatabaseBestListItem( ) { Text(indexText) - Spacer(Modifier.height(dimensionResource(R.dimen.list_padding))) + Spacer(Modifier.height(MaterialTheme.spacing.sm)) Text("PTT", style = MaterialTheme.typography.labelSmall) Text(item.playRatingText, style = MaterialTheme.typography.labelMedium) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/bestlist/DatabaseBestListScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/bestlist/DatabaseBestListScreen.kt index 6875657e..240e1eb7 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/bestlist/DatabaseBestListScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/bestlist/DatabaseBestListScreen.kt @@ -21,6 +21,7 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Slider import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -30,15 +31,14 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.koin.compose.viewmodel.koinViewModel -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.SubScreenContainer import xyz.sevive.arcaeaoffline.ui.SubScreenTopAppBar import xyz.sevive.arcaeaoffline.ui.navigation.DatabaseSubScreen import xyz.sevive.arcaeaoffline.ui.screens.EmptyScreen +import xyz.sevive.arcaeaoffline.ui.theme.spacing import kotlin.math.round @OptIn(ExperimentalMaterial3Api::class) @@ -100,8 +100,8 @@ fun DatabaseBestListScreen(viewModel: DatabaseBestListViewModel = koinViewModel( EmptyScreen(Modifier.fillMaxSize()) } else { LazyColumn( - contentPadding = PaddingValues(all = dimensionResource(R.dimen.page_padding)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + contentPadding = PaddingValues(all = MaterialTheme.spacing.pagePadding), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { items(uiState.listItems, key = { it.index }) { DatabaseBestListItem(it, Modifier.animateItem()) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorGroupByValuesSelectDialog.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorGroupByValuesSelectDialog.kt index d0e55244..9283fe8f 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorGroupByValuesSelectDialog.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorGroupByValuesSelectDialog.kt @@ -13,8 +13,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark import xyz.sevive.arcaeaoffline.R +import xyz.sevive.arcaeaoffline.ui.components.CheckboxItem import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogConfirmButton -import xyz.sevive.arcaeaoffline.ui.components.preferences.CheckboxPreferencesWidget import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme private val groupByValueResIdMap = @@ -40,7 +40,7 @@ private fun DatabaseDeduplicatorGroupByValuesSelectDialogContent( ) { @Composable fun ValueWrapper(targetValue: GroupByValue) { - CheckboxPreferencesWidget( + CheckboxItem( value = values.contains(targetValue), onValueChange = { enabled -> onValueChange(targetValue, enabled) }, title = groupByValueTitle(targetValue), diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorGroupListItem.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorGroupListItem.kt index 103ae7d1..46b22587 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorGroupListItem.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorGroupListItem.kt @@ -22,15 +22,14 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.text.font.FontWeight -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.database.entities.PlayResult import xyz.sevive.arcaeaoffline.helpers.ArcaeaPlayResultValidator import xyz.sevive.arcaeaoffline.ui.components.ArcaeaChartCard import xyz.sevive.arcaeaoffline.ui.components.ArcaeaPlayResultCard import xyz.sevive.arcaeaoffline.ui.components.ArcaeaPlayResultEditorDialog import xyz.sevive.arcaeaoffline.ui.components.ListGroupHeader +import xyz.sevive.arcaeaoffline.ui.theme.spacing import kotlin.uuid.Uuid @Composable @@ -56,14 +55,14 @@ internal fun DatabaseDeduplicatorGroupListItem( } } - Column(verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.icon_text_padding))) { + Column(verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.iconTextGap)) { Row(verticalAlignment = Alignment.Bottom) { ListGroupHeader(Modifier.weight(1f)) { Row(verticalAlignment = Alignment.Bottom) { Text(item.index.toString(), fontWeight = FontWeight.Bold) Text( item.key, - Modifier.padding(start = dimensionResource(R.dimen.icon_text_padding)), + Modifier.padding(start = MaterialTheme.spacing.iconTextGap), style = MaterialTheme.typography.labelSmall, fontWeight = FontWeight.Light, ) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorScreen.kt index 96d535dc..bdf25c2b 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorScreen.kt @@ -24,16 +24,15 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.koin.compose.viewmodel.koinViewModel -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.SubScreenContainer import xyz.sevive.arcaeaoffline.ui.components.LoadingOverlay import xyz.sevive.arcaeaoffline.ui.navigation.DatabaseSubScreen import xyz.sevive.arcaeaoffline.ui.screens.EmptyScreen import xyz.sevive.arcaeaoffline.ui.screens.database.playresultlist.DatabasePlayResultDeleteConfirmDialog +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable fun DatabaseDeduplicatorScreen(vm: DatabaseDeduplicatorViewModel = koinViewModel()) { @@ -127,8 +126,8 @@ fun DatabaseDeduplicatorScreen(vm: DatabaseDeduplicatorViewModel = koinViewModel EmptyScreen(Modifier.fillMaxSize()) } else { LazyColumn( - contentPadding = PaddingValues(all = dimensionResource(R.dimen.page_padding)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + contentPadding = PaddingValues(all = MaterialTheme.spacing.pagePadding), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { items(uiState.listItems, key = { it.key }) { item -> DatabaseDeduplicatorGroupListItem( diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorWizardDialog.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorWizardDialog.kt index 737ce265..3847c49c 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorWizardDialog.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/deduplicator/DatabaseDeduplicatorWizardDialog.kt @@ -17,10 +17,10 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import xyz.sevive.arcaeaoffline.R +import xyz.sevive.arcaeaoffline.ui.components.TextItem import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogConfirmButton import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogConfirmButtonDefaults import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogDismissTextButton -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget import xyz.sevive.arcaeaoffline.ui.theme.extendedColorScheme @Composable @@ -69,7 +69,7 @@ internal fun DatabaseDeduplicatorWizardDialog( text = { LazyColumn { item { - TextPreferencesWidget( + TextItem( onClick = { onAutoSelect(AutoSelectMode.IDENTICAL) }, title = stringResource(R.string.database_deduplicator_auto_select_identical), content = stringResource(R.string.database_deduplicator_description_auto_select_identical), @@ -77,7 +77,7 @@ internal fun DatabaseDeduplicatorWizardDialog( } item { - TextPreferencesWidget( + TextItem( onClick = { onAutoSelect(AutoSelectMode.PROPERTIES_PRIORITY) }, title = stringResource(R.string.database_deduplicator_auto_select_properties_priority), content = stringResource(R.string.database_deduplicator_description_auto_select_properties_priority), @@ -85,7 +85,7 @@ internal fun DatabaseDeduplicatorWizardDialog( } item { - TextPreferencesWidget( + TextItem( onClick = { onAutoSelect(AutoSelectMode.R30_PRIORITY) }, title = stringResource(R.string.database_deduplicator_auto_select_r30_priority), content = stringResource(R.string.database_deduplicator_description_auto_select_r30_priority), @@ -96,7 +96,7 @@ internal fun DatabaseDeduplicatorWizardDialog( CompositionLocalProvider( LocalContentColor provides MaterialTheme.extendedColorScheme.warning, ) { - TextPreferencesWidget( + TextItem( onClick = { showAutoMergeConfirmDialog = true }, leadingIcon = Icons.Default.Merge, leadingIconTint = LocalContentColor.current, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageExport.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageExport.kt index ba887ce5..ccfb208e 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageExport.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageExport.kt @@ -10,7 +10,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import xyz.sevive.arcaeaoffline.R -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget +import xyz.sevive.arcaeaoffline.ui.components.TextItem import kotlin.time.Clock private class CreateJsonDocument : ActivityResultContracts.CreateDocument("application/json") @@ -26,7 +26,7 @@ fun DatabaseManageExport( } Column(modifier) { - TextPreferencesWidget( + TextItem( onClick = { exportPlayResultsHandler.launch( "arcaea-offline-data-exchange-${Clock.System.now().toEpochMilliseconds()}", diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageImport.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageImport.kt index 8b6d7079..c216a632 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageImport.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageImport.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.res.vectorResource import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.helpers.rememberFileChooserLauncher import xyz.sevive.arcaeaoffline.ui.components.ArcaeaAppIcon -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget +import xyz.sevive.arcaeaoffline.ui.components.TextItem @Composable private fun joinImportEntries(entries: List): String = remember(entries) { entries.joinToString(" · ") } @@ -73,21 +73,21 @@ fun DatabaseManageImport( ) Column(modifier) { - TextPreferencesWidget( + TextItem( onClick = { importPacklistLauncher.launch("*/*") }, title = stringResource(R.string.database_manage_import_packlist), content = descPacklist, leadingIcon = Icons.Default.DataObject, ) - TextPreferencesWidget( + TextItem( onClick = { importSonglistLauncher.launch("*/*") }, title = stringResource(R.string.database_manage_import_songlist), content = descSonglist, leadingIcon = Icons.Default.DataObject, ) - TextPreferencesWidget( + TextItem( onClick = { importArcaeaApkLauncher.launch("*/*") }, title = stringResource(R.string.database_manage_import_from_arcaea_apk), content = descArcaeaApk, @@ -95,28 +95,28 @@ fun DatabaseManageImport( ) if (canImportLists) { - TextPreferencesWidget( + TextItem( onClick = onImportFromInstalledArcaea, title = stringResource(R.string.database_manage_import_from_arcaea_installed), content = descArcaeaApk, leadingSlot = { ArcaeaAppIcon() }, ) } else { - TextPreferencesWidget( + TextItem( title = stringResource(R.string.arcaea_button_resource_unavailable), leadingSlot = { ArcaeaAppIcon(forceDisabled = true) }, enabled = false, ) } - TextPreferencesWidget( + TextItem( onClick = { importChartInfoDatabaseLauncher.launch("*/*") }, title = stringResource(R.string.database_manage_import_chart_info_database), content = joinImportEntries(listOf(descChartInfoEntries)), leadingIcon = ImageVector.vectorResource(R.drawable.ic_database), ) - TextPreferencesWidget( + TextItem( onClick = { importSt3Launcher.launch("*/*") }, title = stringResource(R.string.arcaea_st3), content = joinImportEntries(listOf(descPlayResultEntries)), diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageScreen.kt index 27c0856a..9db3f0ac 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/DatabaseManageScreen.kt @@ -12,7 +12,6 @@ import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Tune import androidx.compose.material.icons.filled.Upload import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -33,10 +32,10 @@ import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.constants.ArcaeaScoringMode import xyz.sevive.arcaeaoffline.ui.SubScreenContainer import xyz.sevive.arcaeaoffline.ui.components.IconRow -import xyz.sevive.arcaeaoffline.ui.components.ListGroupHeader -import xyz.sevive.arcaeaoffline.ui.components.preferences.SelectPreferencesOption -import xyz.sevive.arcaeaoffline.ui.components.preferences.SelectPreferencesWidget -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget +import xyz.sevive.arcaeaoffline.ui.components.SelectItem +import xyz.sevive.arcaeaoffline.ui.components.SelectOption +import xyz.sevive.arcaeaoffline.ui.components.SettingsGroupHeader +import xyz.sevive.arcaeaoffline.ui.components.TextItem @Composable fun DatabaseManageScreen( @@ -73,7 +72,7 @@ fun DatabaseManageScreen( ) { LazyColumn(modifier) { item { - ListGroupHeader { + SettingsGroupHeader { IconRow { Icon(Icons.Default.Tune, contentDescription = null) Text(stringResource(R.string.database_manage_scoring_mode_title)) @@ -84,15 +83,15 @@ fun DatabaseManageScreen( item { val scoringMode by viewModel.scoringMode.collectAsStateWithLifecycle() - SelectPreferencesWidget( + SelectItem( options = listOf( - SelectPreferencesOption( + SelectOption( value = ArcaeaScoringMode.B30_R10, label = stringResource(R.string.database_manage_scoring_mode_b30_r10), description = ArcaeaScoringMode.B30_R10.key.toString(), ), - SelectPreferencesOption( + SelectOption( value = ArcaeaScoringMode.B50, label = stringResource(R.string.database_manage_scoring_mode_b50), description = ArcaeaScoringMode.B50.key.toString(), @@ -104,10 +103,8 @@ fun DatabaseManageScreen( ) } - item { HorizontalDivider() } - item { - ListGroupHeader { + SettingsGroupHeader { IconRow { Icon(Icons.Default.Download, contentDescription = null) Text(stringResource(R.string.database_manage_import_title)) @@ -128,10 +125,8 @@ fun DatabaseManageScreen( ) } - item { HorizontalDivider() } - item { - ListGroupHeader { + SettingsGroupHeader { IconRow { Icon(Icons.Default.CloudDownload, contentDescription = null) Text(stringResource(R.string.database_manage_download_title)) @@ -141,7 +136,7 @@ fun DatabaseManageScreen( item { val isFetchingRemoteInfo = uiState.remoteResourcesInfoState.isFetching - TextPreferencesWidget( + TextItem( onClick = { viewModel.refreshRemoteResourcesInfo() }, enabled = !isFetchingRemoteInfo, leadingSlot = { @@ -171,10 +166,8 @@ fun DatabaseManageScreen( ) } - item { HorizontalDivider() } - item { - ListGroupHeader { + SettingsGroupHeader { IconRow { Icon(Icons.Default.Upload, contentDescription = null) Text(stringResource(R.string.database_manage_export_title)) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/ImportLogUi.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/ImportLogUi.kt index 417ce911..f672a86b 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/ImportLogUi.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/manage/ImportLogUi.kt @@ -37,12 +37,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalResources -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import kotlinx.coroutines.launch -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.helpers.context.findActivity import xyz.sevive.arcaeaoffline.helpers.formatAsLocalizedDate import xyz.sevive.arcaeaoffline.helpers.formatAsLocalizedDateTime @@ -50,6 +48,7 @@ import xyz.sevive.arcaeaoffline.helpers.formatAsLocalizedTime import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha import xyz.sevive.arcaeaoffline.ui.screens.EmptyScreen import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing import kotlin.time.Instant import kotlin.uuid.Uuid @@ -81,7 +80,7 @@ private fun ImportLogObjectUi( Row( modifier, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), verticalAlignment = Alignment.Top, ) { Column( @@ -138,7 +137,7 @@ internal fun ImportLogBottomSheet( Modifier .fillMaxSize() .consumeWindowInsets(innerPadding) - .padding(horizontal = dimensionResource(R.dimen.page_padding)), + .padding(horizontal = MaterialTheme.spacing.pagePadding), contentPadding = innerPadding, state = lazyColumnState, ) { @@ -153,7 +152,7 @@ internal fun ImportLogBottomSheet( it, Modifier .fillMaxWidth() - .padding(vertical = dimensionResource(R.dimen.list_padding)) + .padding(vertical = MaterialTheme.spacing.sm) .animateItem(), ) } diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/playresultlist/DatabasePlayResultListScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/playresultlist/DatabasePlayResultListScreen.kt index 337e60c5..7e3b4844 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/playresultlist/DatabasePlayResultListScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/playresultlist/DatabasePlayResultListScreen.kt @@ -28,7 +28,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -37,6 +36,7 @@ import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.SubScreenContainer import xyz.sevive.arcaeaoffline.ui.components.LoadingOverlay import xyz.sevive.arcaeaoffline.ui.screens.EmptyScreen +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun DatabasePlayResultListAppBarActions( @@ -145,8 +145,8 @@ fun DatabasePlayResultListScreen(viewModel: DatabasePlayResultListViewModel = ko else -> { LazyColumn( - contentPadding = PaddingValues(all = dimensionResource(R.dimen.page_padding)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + contentPadding = PaddingValues(all = MaterialTheme.spacing.pagePadding), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { items(listItems, key = { it.uuid }) { val isSelected by remember { diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/playresultlist/DatabasePlayResultListSortDialog.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/playresultlist/DatabasePlayResultListSortDialog.kt index aef09531..611471cb 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/playresultlist/DatabasePlayResultListSortDialog.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/playresultlist/DatabasePlayResultListSortDialog.kt @@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.AlertDialog import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton import androidx.compose.material3.Surface import androidx.compose.material3.Switch @@ -20,14 +21,14 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.PreviewLightDark import xyz.sevive.arcaeaoffline.R +import xyz.sevive.arcaeaoffline.ui.components.BaseSettingsItem import xyz.sevive.arcaeaoffline.ui.components.IconRow -import xyz.sevive.arcaeaoffline.ui.components.preferences.BasePreferencesWidget import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun DatabasePlayResultListSortDialogOrderSwitch( @@ -39,13 +40,13 @@ private fun DatabasePlayResultListSortDialogOrderSwitch( Modifier .clickable { onSortOrderChange(sortOrder.reverse()) } .padding( - horizontal = dimensionResource(R.dimen.pref_widget_horizontal_padding), - vertical = dimensionResource(R.dimen.pref_widget_vertical_padding), + horizontal = MaterialTheme.spacing.lg, + vertical = MaterialTheme.spacing.lg, ).then(modifier), contentAlignment = Alignment.Center, ) { Row( - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), verticalAlignment = Alignment.CenterVertically, ) { IconRow { @@ -79,7 +80,7 @@ private fun DatabasePlayResultListSortDialogContent( targetSortByValue: DatabasePlayResultListViewModel.SortByValue, modifier: Modifier = Modifier, ) { - BasePreferencesWidget( + BaseSettingsItem( title = { Text(label) }, leadingSlot = { RadioButton(selected = sortByValue == targetSortByValue, onClick = null) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/r30list/DatabaseR30ListItem.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/r30list/DatabaseR30ListItem.kt index 9ade5844..09b8dcf4 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/r30list/DatabaseR30ListItem.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/r30list/DatabaseR30ListItem.kt @@ -14,14 +14,13 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.rememberTextMeasurer import androidx.compose.ui.text.withStyle -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.components.ArcaeaPlayResultCard import xyz.sevive.arcaeaoffline.ui.helpers.ArcaeaFormatters +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun DatabaseR30ListItem( @@ -56,7 +55,7 @@ internal fun DatabaseR30ListItem( Row( modifier, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), verticalAlignment = Alignment.Bottom, ) { ArcaeaPlayResultCard( @@ -74,7 +73,7 @@ internal fun DatabaseR30ListItem( ) { Text(indexText) - Spacer(Modifier.height(dimensionResource(R.dimen.list_padding))) + Spacer(Modifier.height(MaterialTheme.spacing.sm)) Text("PTT", style = MaterialTheme.typography.labelSmall) Text(playRatingText, style = MaterialTheme.typography.labelMedium) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/r30list/DatabaseR30ListScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/r30list/DatabaseR30ListScreen.kt index ecc503ce..284bef8e 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/r30list/DatabaseR30ListScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/database/r30list/DatabaseR30ListScreen.kt @@ -30,7 +30,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.koin.compose.viewmodel.koinViewModel @@ -44,6 +43,7 @@ import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogConfirmButtonDefault import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogDismissTextButton import xyz.sevive.arcaeaoffline.ui.navigation.DatabaseSubScreen import xyz.sevive.arcaeaoffline.ui.screens.EmptyScreen +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun DatabaseR30RebuildConfirmDialog( @@ -135,8 +135,8 @@ internal fun DatabaseR30ListScreen(viewModel: DatabaseR30ListViewModel = koinVie EmptyScreen(Modifier.fillMaxSize()) } else { LazyColumn( - contentPadding = PaddingValues(all = dimensionResource(R.dimen.page_padding)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + contentPadding = PaddingValues(all = MaterialTheme.spacing.pagePadding), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { items(uiItems, key = { it.id }) { DatabaseR30ListItem(it, Modifier.animateItem()) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/dependencies/OcrDependenciesScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/dependencies/OcrDependenciesScreen.kt index 69f3eedc..d603e60c 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/dependencies/OcrDependenciesScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/dependencies/OcrDependenciesScreen.kt @@ -23,9 +23,9 @@ import xyz.sevive.arcaeaoffline.helpers.ArcaeaResourcesStateHolder import xyz.sevive.arcaeaoffline.helpers.rememberFileChooserLauncher import xyz.sevive.arcaeaoffline.ui.SubScreenContainer import xyz.sevive.arcaeaoffline.ui.components.ArcaeaAppIcon +import xyz.sevive.arcaeaoffline.ui.components.TextItem import xyz.sevive.arcaeaoffline.ui.components.ocr.OcrDependencyCrnnModelStatusViewer import xyz.sevive.arcaeaoffline.ui.components.ocr.OcrDependencyImageHashesDatabaseStatusViewer -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget import xyz.sevive.arcaeaoffline.ui.components.resources.RemoteResourceDownloadItem import xyz.sevive.arcaeaoffline.ui.navigation.OcrSubScreen @@ -65,7 +65,7 @@ fun OcrDependenciesScreen( val remoteDownloadUiState by viewModel.imageHashesDatabaseRemoteDownloadUiState.collectAsStateWithLifecycle() - TextPreferencesWidget( + TextItem( enabled = !importRunning && !remoteDownloadUiState.isWorking, onClick = { imageHashesDatabaseFileChooserLauncher.launch("*/*") }, leadingIcon = Icons.Default.FileOpen, @@ -107,7 +107,7 @@ fun OcrDependenciesScreen( R.string.arcaea_button_resource_unavailable } - TextPreferencesWidget( + TextItem( enabled = buildHashesDatabaseButtonEnabled, onClick = { viewModel.requestImageHashesDatabaseBuild() }, leadingSlot = { ArcaeaAppIcon(forceDisabled = !canBuildHashesDatabase) }, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/performance/OcrPerformanceScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/performance/OcrPerformanceScreen.kt index 39e8d2ab..5bb9944a 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/performance/OcrPerformanceScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/performance/OcrPerformanceScreen.kt @@ -41,12 +41,10 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalClipboard import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.platform.toClipEntry -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import kotlinx.coroutines.launch import org.koin.compose.viewmodel.koinViewModel @@ -57,13 +55,14 @@ import xyz.sevive.arcaeaoffline.helpers.formatAsLocalizedDate import xyz.sevive.arcaeaoffline.helpers.formatAsLocalizedTime import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha import xyz.sevive.arcaeaoffline.ui.SubScreenContainer +import xyz.sevive.arcaeaoffline.ui.components.BaseSettingsItem import xyz.sevive.arcaeaoffline.ui.components.IconRow import xyz.sevive.arcaeaoffline.ui.components.LinearProgressIndicatorWrapper -import xyz.sevive.arcaeaoffline.ui.components.ListGroupHeader -import xyz.sevive.arcaeaoffline.ui.components.preferences.BasePreferencesWidget -import xyz.sevive.arcaeaoffline.ui.components.preferences.SliderPreferencesWidget -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget +import xyz.sevive.arcaeaoffline.ui.components.SettingsGroupHeader +import xyz.sevive.arcaeaoffline.ui.components.SliderItem +import xyz.sevive.arcaeaoffline.ui.components.TextItem import xyz.sevive.arcaeaoffline.ui.navigation.OcrSubScreen +import xyz.sevive.arcaeaoffline.ui.theme.spacing import java.util.Locale @Composable @@ -89,13 +88,13 @@ fun OcrPerformanceScreen( ) { LazyColumn(modifier) { item { - ListGroupHeader(stringResource(R.string.ocr_performance_images_title)) + SettingsGroupHeader(stringResource(R.string.ocr_performance_images_title)) } item { val selectedUris = uiState.selectedImageUris val hasSelection = selectedUris.isNotEmpty() - TextPreferencesWidget( + TextItem( title = stringResource(R.string.ocr_performance_pick_images_button), content = selectedUris.takeIf { hasSelection }?.let { @@ -146,7 +145,7 @@ fun OcrPerformanceScreen( Text( stringResource(R.string.ocr_performance_image_load_failed), color = MaterialTheme.colorScheme.error, - modifier = Modifier.padding(horizontal = 16.dp), + modifier = Modifier.padding(horizontal = MaterialTheme.spacing.lg), ) } } @@ -154,7 +153,7 @@ fun OcrPerformanceScreen( item { HorizontalDivider() } item { - ListGroupHeader(stringResource(R.string.ocr_performance_concurrency_title)) + SettingsGroupHeader(stringResource(R.string.ocr_performance_concurrency_title)) } item { @@ -163,7 +162,7 @@ fun OcrPerformanceScreen( label = "benchmarkRunningState", ) { running -> if (!running) { - SliderPreferencesWidget( + SliderItem( value = uiState.parallelCount.toFloat(), onValueChange = viewModel::onParallelCountChange, icon = Icons.AutoMirrored.Default.Sort, @@ -184,7 +183,7 @@ fun OcrPerformanceScreen( }, ) } else { - BasePreferencesWidget( + BaseSettingsItem( title = { uiState.runningParallel?.let { parallel -> Text( @@ -215,7 +214,7 @@ fun OcrPerformanceScreen( Text( stringResource(R.string.ocr_performance_benchmark_error, message), color = MaterialTheme.colorScheme.error, - modifier = Modifier.padding(horizontal = 16.dp), + modifier = Modifier.padding(horizontal = MaterialTheme.spacing.lg), ) } } @@ -224,7 +223,7 @@ fun OcrPerformanceScreen( item { HorizontalDivider() } item { - ListGroupHeader(stringResource(R.string.ocr_performance_result_title)) + SettingsGroupHeader(stringResource(R.string.ocr_performance_result_title)) } item { @@ -232,7 +231,7 @@ fun OcrPerformanceScreen( parallel = uiState.resultParallel ?: uiState.parallelCount, result = result, snackbarHostState = snackbarHostState, - modifier = Modifier.padding(bottom = dimensionResource(R.dimen.list_padding)), + modifier = Modifier.padding(bottom = MaterialTheme.spacing.sm), ) } } @@ -241,7 +240,7 @@ fun OcrPerformanceScreen( item { HorizontalDivider() } item { - ListGroupHeader(stringResource(R.string.ocr_performance_history_title)) + SettingsGroupHeader(stringResource(R.string.ocr_performance_history_title)) } // Newest first, easier to compare recent runs @@ -267,10 +266,10 @@ private fun ResultCard( val coroutineScope = rememberCoroutineScope() val resources = LocalResources.current - Card(modifier = modifier.padding(horizontal = 16.dp)) { + Card(modifier = modifier.padding(horizontal = MaterialTheme.spacing.lg)) { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), - modifier = Modifier.padding(dimensionResource(R.dimen.card_padding)), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), + modifier = Modifier.padding(MaterialTheme.spacing.cardPadding), ) { Row( horizontalArrangement = Arrangement.SpaceBetween, @@ -299,7 +298,7 @@ private fun ResultCard( ) } } - Spacer(Modifier.height(dimensionResource(R.dimen.list_padding))) + Spacer(Modifier.height(MaterialTheme.spacing.sm)) KeyValueRow( label = stringResource(R.string.ocr_performance_result_median_label), value = stringResource(R.string.ocr_performance_result_median_value, result.medianPerImageMs), @@ -380,12 +379,12 @@ private fun HistoryRow( } Row( - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), verticalAlignment = Alignment.Top, modifier = modifier .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = dimensionResource(R.dimen.list_padding)), + .padding(horizontal = MaterialTheme.spacing.lg, vertical = MaterialTheme.spacing.sm), ) { Text( timestampText, @@ -394,7 +393,7 @@ private fun HistoryRow( ) Column( - verticalArrangement = Arrangement.spacedBy(2.dp), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.xxs), modifier = Modifier.weight(1f), ) { Text( diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/OcrQueueCategoryItem.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/OcrQueueCategoryItem.kt index 96d323ed..a77ae287 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/OcrQueueCategoryItem.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/OcrQueueCategoryItem.kt @@ -19,12 +19,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.tooling.preview.PreviewLightDark -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.helpers.secondaryItemAlpha -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget +import xyz.sevive.arcaeaoffline.ui.components.TextItem import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun OcrQueueCategoryItem( @@ -38,7 +37,7 @@ internal fun OcrQueueCategoryItem( ) { val countString = remember(count) { count.toString() } - TextPreferencesWidget( + TextItem( onClick = onClick, title = title, modifier = modifier, @@ -48,7 +47,7 @@ internal fun OcrQueueCategoryItem( trailingSlot = { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.icon_text_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.iconTextGap), ) { Text(countString, Modifier.secondaryItemAlpha()) tailSlot() diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/preferences/OcrQueuePreferencesBottomSheet.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/preferences/OcrQueuePreferencesBottomSheet.kt index 8c85ed3d..1e642711 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/preferences/OcrQueuePreferencesBottomSheet.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/preferences/OcrQueuePreferencesBottomSheet.kt @@ -19,9 +19,9 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.koin.compose.viewmodel.koinViewModel import xyz.sevive.arcaeaoffline.R -import xyz.sevive.arcaeaoffline.ui.components.ListGroupHeader -import xyz.sevive.arcaeaoffline.ui.components.preferences.SliderPreferencesWidget -import xyz.sevive.arcaeaoffline.ui.components.preferences.SwitchPreferencesWidget +import xyz.sevive.arcaeaoffline.ui.components.SettingsGroupHeader +import xyz.sevive.arcaeaoffline.ui.components.SliderItem +import xyz.sevive.arcaeaoffline.ui.components.SwitchItem import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme import kotlin.math.round @@ -34,11 +34,11 @@ private fun Content( ) { LazyColumn { item { - ListGroupHeader(stringResource(R.string.ocr_queue_add_image_options_title)) + SettingsGroupHeader(stringResource(R.string.ocr_queue_add_image_options_title)) } item { - SwitchPreferencesWidget( + SwitchItem( value = uiState.checkIsImage, onValueChange = { onSetCheckIsImage(it) }, title = stringResource(R.string.ocr_queue_add_image_options_check_is_image), @@ -46,7 +46,7 @@ private fun Content( } item { - SwitchPreferencesWidget( + SwitchItem( value = uiState.checkIsArcaeaImage, onValueChange = { onSetCheckIsArcaeaImage(it) }, title = stringResource(R.string.ocr_queue_add_image_options_detect_screenshot), @@ -54,11 +54,11 @@ private fun Content( } item { - ListGroupHeader(stringResource(R.string.ocr_queue_queue_options_title)) + SettingsGroupHeader(stringResource(R.string.ocr_queue_queue_options_title)) } item { - SliderPreferencesWidget( + SliderItem( value = uiState.parallelCount.toFloat(), onValueChange = { onSetParallelCount(round(it).toInt()) }, icon = Icons.AutoMirrored.Default.Sort, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/staging/OcrQueueStagingFloatingActionButton.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/staging/OcrQueueStagingFloatingActionButton.kt index 41a55ee5..d8e8de4e 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/staging/OcrQueueStagingFloatingActionButton.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/staging/OcrQueueStagingFloatingActionButton.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun OcrQueueStagingFloatingActionButton( @@ -64,7 +65,7 @@ internal fun OcrQueueStagingFloatingActionButton( private fun OcrQueueStagingFloatingActionButtonPreview() { ArcaeaOfflineTheme { Surface { - Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { + Row(horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm)) { OcrQueueStagingFloatingActionButton( onClick = {}, isVisible = true, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/staging/OcrQueueStagingSheet.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/staging/OcrQueueStagingSheet.kt index ecc2f5a1..dd225f7c 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/staging/OcrQueueStagingSheet.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/staging/OcrQueueStagingSheet.kt @@ -35,7 +35,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextAlign @@ -47,6 +46,7 @@ import xyz.sevive.arcaeaoffline.core.Progress import xyz.sevive.arcaeaoffline.ui.components.IconRow import xyz.sevive.arcaeaoffline.ui.components.LinearProgressIndicatorWrapper import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing import kotlin.time.Duration.Companion.seconds @OptIn(ExperimentalMaterial3Api::class) @@ -88,12 +88,12 @@ private fun PickerButton( Modifier .clickable(onClick = onClick) .height(IntrinsicSize.Min) - .padding(vertical = 36.dp) + .padding(vertical = MaterialTheme.spacing.xxl) .then(modifier), contentAlignment = Alignment.Center, ) { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), horizontalAlignment = Alignment.CenterHorizontally, ) { Icon( @@ -242,12 +242,12 @@ private fun BottomSheetContent( // Prevents the inner progress indicator's intrinsic width from expanding this column column(GridTrackSize.MinMax(0.dp, 1.fr)) column(GridTrackSize.MaxContent) - columnGap(24.dp) - rowGap(16.dp) + columnGap(MaterialTheme.spacing.xl) + rowGap(MaterialTheme.spacing.lg) }, Modifier - .padding(horizontal = 16.dp) - .padding(bottom = 16.dp), + .padding(horizontal = MaterialTheme.spacing.lg) + .padding(bottom = MaterialTheme.spacing.lg), ) { Text( stringResource(R.string.ocr_queue_staging_status), diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskList.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskList.kt index 95e4868e..45ee40e8 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskList.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskList.kt @@ -4,13 +4,13 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.database.entities.Difficulty import xyz.sevive.arcaeaoffline.core.database.entities.PlayResult import xyz.sevive.arcaeaoffline.ui.screens.ocr.queue.OcrQueueScreenViewModel +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun OcrQueueTaskList( @@ -22,8 +22,8 @@ internal fun OcrQueueTaskList( modifier: Modifier = Modifier, ) { LazyColumn( - modifier.padding(horizontal = dimensionResource(R.dimen.page_padding)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + modifier.padding(horizontal = MaterialTheme.spacing.pagePadding), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { items(uiItems, key = { it.dbItem.id }) { OcrQueueTaskListItem( diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskListItem.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskListItem.kt index 2acb3dc9..db228371 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskListItem.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskListItem.kt @@ -1,6 +1,7 @@ package xyz.sevive.arcaeaoffline.ui.screens.ocr.queue.tasklist import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -8,8 +9,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.database.entities.Difficulty import xyz.sevive.arcaeaoffline.core.database.entities.PlayResult import xyz.sevive.arcaeaoffline.ui.components.ArcaeaChartSelector @@ -17,6 +16,7 @@ import xyz.sevive.arcaeaoffline.ui.components.ArcaeaPlayResultEditorDialog import xyz.sevive.arcaeaoffline.ui.components.BasicAlertDialogSurface import xyz.sevive.arcaeaoffline.ui.components.ImagePreviewDialogFullscreen import xyz.sevive.arcaeaoffline.ui.screens.ocr.queue.OcrQueueScreenViewModel +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun OcrQueueTaskListItem( @@ -69,7 +69,7 @@ internal fun OcrQueueTaskListItem( OcrQueueTaskListItemResult( uiItem, - Modifier.padding(dimensionResource(R.dimen.card_padding)), + Modifier.padding(MaterialTheme.spacing.cardPadding), ) } } diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskListItemHeader.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskListItemHeader.kt index 5ed2f703..b4b91480 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskListItemHeader.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/ocr/queue/tasklist/OcrQueueTaskListItemHeader.kt @@ -43,7 +43,7 @@ import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.database.entities.OcrQueueTask import xyz.sevive.arcaeaoffline.database.entities.OcrQueueTaskStatus import xyz.sevive.arcaeaoffline.helpers.context.getFilename -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget +import xyz.sevive.arcaeaoffline.ui.components.TextItem import xyz.sevive.arcaeaoffline.ui.screens.ocr.queue.OcrQueueScreenViewModel import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme import kotlin.time.Clock @@ -60,14 +60,14 @@ private fun TaskDetailsDialog( text = { LazyColumn { item { - TextPreferencesWidget( + TextItem( title = "ID", content = dbItem.id.toString(), ) } item { - TextPreferencesWidget( + TextItem( title = "Uri", content = dbItem.fileUri.toString(), ) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/overview/OverviewPotentialCard.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/overview/OverviewPotentialCard.kt index 667d1e72..9b7c5cba 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/overview/OverviewPotentialCard.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/overview/OverviewPotentialCard.kt @@ -19,7 +19,6 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.TextStyle @@ -33,6 +32,7 @@ import xyz.sevive.arcaeaoffline.helpers.calculatePotential import xyz.sevive.arcaeaoffline.ui.helpers.ArcaeaFormatters import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme import xyz.sevive.arcaeaoffline.ui.theme.extendedColorScheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun PotentialRow( @@ -143,7 +143,7 @@ internal fun OverviewPotentialCard( PotentialRow( label = stringResource(R.string.arcaea_potential), value = ArcaeaFormatters.potentialToText(uiState.potential, mainScale), - modifier = Modifier.fillMaxWidth().padding(dimensionResource(R.dimen.page_padding)), + modifier = Modifier.fillMaxWidth().padding(MaterialTheme.spacing.pagePadding), labelTextStyle = MaterialTheme.typography.headlineSmall, valueTextStyle = MaterialTheme.typography.displayLarge, ) @@ -151,8 +151,8 @@ internal fun OverviewPotentialCard( HorizontalDivider() Column( - Modifier.padding(dimensionResource(R.dimen.page_padding)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + Modifier.padding(MaterialTheme.spacing.pagePadding), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { when (uiState.scoringMode) { ArcaeaScoringMode.B50 -> { @@ -172,7 +172,7 @@ internal fun OverviewPotentialCard( ) { Row( verticalAlignment = Alignment.Top, - horizontalArrangement = Arrangement.spacedBy(8.dp), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { Icon(painterResource(R.drawable.chart_line_alert), contentDescription = null) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/overview/OverviewScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/overview/OverviewScreen.kt index 00892bb9..2ec26cc4 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/overview/OverviewScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/overview/OverviewScreen.kt @@ -4,13 +4,13 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import org.koin.compose.viewmodel.koinViewModel -import xyz.sevive.arcaeaoffline.R +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable fun OverviewScreen( @@ -23,7 +23,7 @@ fun OverviewScreen( modifier .safeDrawingPadding() .fillMaxSize(), - contentPadding = PaddingValues(all = dimensionResource(R.dimen.page_padding)), + contentPadding = PaddingValues(all = MaterialTheme.spacing.pagePadding), ) { item { OverviewPotentialCard(uiState) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/SettingsNavEntry.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/SettingsNavEntry.kt index 885330b6..6adb262a 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/SettingsNavEntry.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/SettingsNavEntry.kt @@ -19,17 +19,17 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.data.IS_UNSTABLE_VERSION -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget +import xyz.sevive.arcaeaoffline.ui.components.TextItem import xyz.sevive.arcaeaoffline.ui.navigation.LocalListDetailNavigationContext import xyz.sevive.arcaeaoffline.ui.navigation.MainScreen import xyz.sevive.arcaeaoffline.ui.navigation.SettingsSubScreen import xyz.sevive.arcaeaoffline.ui.screens.NavEntryNavigateButton import xyz.sevive.arcaeaoffline.ui.screens.settings.unstablealert.UnstableBuildAlertCard +import xyz.sevive.arcaeaoffline.ui.theme.spacing @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -58,7 +58,7 @@ internal fun SettingsNavEntry( modifier = Modifier .fillMaxWidth() - .padding(horizontal = dimensionResource(R.dimen.page_padding)), + .padding(horizontal = MaterialTheme.spacing.pagePadding), showDetails = true, ) } @@ -86,7 +86,7 @@ internal fun SettingsNavEntry( CompositionLocalProvider( LocalContentColor provides MaterialTheme.colorScheme.error, ) { - TextPreferencesWidget( + TextItem( title = stringResource(R.string.emergency_mode_title), leadingIcon = ImageVector.vectorResource(R.drawable.ic_activity_emergency_mode), leadingIconTint = LocalContentColor.current, diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/about/SettingsAboutScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/about/SettingsAboutScreen.kt index 83617dc6..c06d8c4b 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/about/SettingsAboutScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/about/SettingsAboutScreen.kt @@ -19,7 +19,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.graphics.drawable.toBitmap @@ -28,6 +27,7 @@ import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.SubScreenContainer import xyz.sevive.arcaeaoffline.ui.navigation.SettingsSubScreen import xyz.sevive.arcaeaoffline.ui.screens.NavEntryNavigateButton +import xyz.sevive.arcaeaoffline.ui.theme.spacing class AppIconBeingClickedTooManyTimesSoEmbarrassingException : Exception() { override val message: String = "(⁄ ⁄•⁄ω⁄•⁄ ⁄)" @@ -73,7 +73,7 @@ internal fun SettingsAboutScreen( item { Column( Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), horizontalAlignment = Alignment.CenterHorizontally, ) { AppIcon( diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/general/SettingsGeneralScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/general/SettingsGeneralScreen.kt index c64611bb..cf61694a 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/general/SettingsGeneralScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/general/SettingsGeneralScreen.kt @@ -23,10 +23,10 @@ import androidx.compose.ui.text.input.KeyboardType import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.core.api.ArcaeaResourcesApiClient import xyz.sevive.arcaeaoffline.ui.SubScreenContainer +import xyz.sevive.arcaeaoffline.ui.components.SwitchItem +import xyz.sevive.arcaeaoffline.ui.components.TextItem import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogConfirmButton import xyz.sevive.arcaeaoffline.ui.components.dialogs.DialogDismissTextButton -import xyz.sevive.arcaeaoffline.ui.components.preferences.SwitchPreferencesWidget -import xyz.sevive.arcaeaoffline.ui.components.preferences.TextPreferencesWidget import xyz.sevive.arcaeaoffline.ui.navigation.SettingsSubScreen import xyz.sevive.arcaeaoffline.ui.screens.settings.SettingsViewModel @@ -110,7 +110,7 @@ internal fun SettingsGeneralScreen( ) { LazyColumn(modifier) { item { - SwitchPreferencesWidget( + SwitchItem( value = uiState.autoSendCrashReports, onValueChange = { onSetAutoSendCrashReports(it) }, icon = Icons.Default.BugReport, @@ -119,7 +119,7 @@ internal fun SettingsGeneralScreen( } item { - TextPreferencesWidget( + TextItem( onClick = { showBaseUrlEditDialog = true }, leadingIcon = Icons.Default.CloudDownload, title = stringResource(R.string.settings_general_pref_resources_api_base_url), diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/license/SettingsLicenseScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/license/SettingsLicenseScreen.kt index cd5afe1a..ed5adebf 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/license/SettingsLicenseScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/license/SettingsLicenseScreen.kt @@ -4,22 +4,22 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import com.mikepenz.markdown.compose.LazyMarkdownSuccess import com.mikepenz.markdown.m3.Markdown import com.mikepenz.markdown.model.rememberMarkdownState import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.SubScreenContainer import xyz.sevive.arcaeaoffline.ui.navigation.SettingsSubScreen +import xyz.sevive.arcaeaoffline.ui.theme.spacing private const val LICENSE_FILENAME = "gpl-3.0.md" @@ -52,7 +52,7 @@ internal fun SettingsLicenseScreen(modifier: Modifier = Modifier) { state, components, modifier, - contentPadding = PaddingValues(dimensionResource(R.dimen.page_padding)), + contentPadding = PaddingValues(MaterialTheme.spacing.pagePadding), ) }, loading = { diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/unstablealert/SettingsUnstableAlertCard.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/unstablealert/SettingsUnstableAlertCard.kt index 81b2ab3d..10baf085 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/unstablealert/SettingsUnstableAlertCard.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/unstablealert/SettingsUnstableAlertCard.kt @@ -12,17 +12,17 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.components.IconRow +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable private fun UnstableBuildAlertCardContent(showDetails: Boolean = true) { - Column(Modifier.padding(dimensionResource(R.dimen.action_button_padding))) { + Column(Modifier.padding(MaterialTheme.spacing.lg)) { IconRow { Icon(painterResource(R.drawable.ic_unstable_build), null) Text( @@ -49,9 +49,9 @@ fun UnstableBuildAlertCard( shape: Shape = CutCornerShape( topStart = 0.dp, - topEnd = dimensionResource(R.dimen.action_button_padding), + topEnd = 16.dp, bottomEnd = 0.dp, - bottomStart = dimensionResource(R.dimen.action_button_padding), + bottomStart = 16.dp, ), colors: CardColors = CardDefaults.cardColors( diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/unstablealert/SettingsUnstableAlertScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/unstablealert/SettingsUnstableAlertScreen.kt index 7ed3d56a..5b6918fd 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/unstablealert/SettingsUnstableAlertScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/settings/unstablealert/SettingsUnstableAlertScreen.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -11,7 +12,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalResources -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.mikepenz.markdown.compose.LazyMarkdownSuccess @@ -21,6 +21,7 @@ import org.koin.compose.viewmodel.koinViewModel import xyz.sevive.arcaeaoffline.R import xyz.sevive.arcaeaoffline.ui.SubScreenContainer import xyz.sevive.arcaeaoffline.ui.navigation.SettingsSubScreen +import xyz.sevive.arcaeaoffline.ui.theme.spacing @Composable internal fun SettingsUnstableAlertScreen(vm: SettingsUnstableAlertScreenViewModel = koinViewModel()) { @@ -51,7 +52,7 @@ internal fun SettingsUnstableAlertScreen(vm: SettingsUnstableAlertScreenViewMode state, components, modifier, - contentPadding = PaddingValues(dimensionResource(R.dimen.page_padding)), + contentPadding = PaddingValues(MaterialTheme.spacing.pagePadding), ) }, loading = { diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/utilities/UtilitiesCalculatorScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/utilities/UtilitiesCalculatorScreen.kt index 85951c01..4f45d8d3 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/utilities/UtilitiesCalculatorScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/utilities/UtilitiesCalculatorScreen.kt @@ -8,6 +8,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.QueueMusic import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -18,7 +19,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp @@ -37,6 +37,7 @@ import xyz.sevive.arcaeaoffline.ui.components.ArcaeaRatingClassSelector import xyz.sevive.arcaeaoffline.ui.components.PlayRatingCalculator import xyz.sevive.arcaeaoffline.ui.components.toRatingClassSelectorItems import xyz.sevive.arcaeaoffline.ui.navigation.UtilitiesSubScreen +import xyz.sevive.arcaeaoffline.ui.theme.spacing @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -86,13 +87,13 @@ fun UtilitiesCalculatorScreen( title = stringResource(UtilitiesSubScreen.Calculator.title), ) { LazyColumn( - Modifier.padding(horizontal = dimensionResource(R.dimen.page_padding)), - verticalArrangement = Arrangement.spacedBy(16.dp), + Modifier.padding(horizontal = MaterialTheme.spacing.pagePadding), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.lg), ) { item { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.page_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.pagePadding), ) { Icon(Icons.AutoMirrored.Filled.QueueMusic, contentDescription = null) @@ -105,7 +106,7 @@ fun UtilitiesCalculatorScreen( item { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.page_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.pagePadding), ) { Icon(painterResource(R.drawable.ic_rating_class), contentDescription = null) diff --git a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/utilities/UtilitiesChartRecommendScreen.kt b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/utilities/UtilitiesChartRecommendScreen.kt index 94984fcf..faa3b08e 100644 --- a/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/utilities/UtilitiesChartRecommendScreen.kt +++ b/app/src/main/java/xyz/sevive/arcaeaoffline/ui/screens/utilities/UtilitiesChartRecommendScreen.kt @@ -4,14 +4,19 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn @@ -29,6 +34,7 @@ import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard import androidx.compose.material3.ShapeDefaults +import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -42,8 +48,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.rotate +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -64,13 +71,14 @@ import xyz.sevive.arcaeaoffline.ui.components.BasicAlertDialogSurface import xyz.sevive.arcaeaoffline.ui.components.DecimalStepperTextField import xyz.sevive.arcaeaoffline.ui.components.ListGroupHeader import xyz.sevive.arcaeaoffline.ui.components.PlayRatingCalculator +import xyz.sevive.arcaeaoffline.ui.components.SwitchItem import xyz.sevive.arcaeaoffline.ui.components.arcaea.OutlinedArcaeaScoreTextField import xyz.sevive.arcaeaoffline.ui.components.arcaea.rememberArcaeaScoreTextFieldState -import xyz.sevive.arcaeaoffline.ui.components.preferences.SwitchPreferencesWidget import xyz.sevive.arcaeaoffline.ui.components.rememberDecimalStepperTextFieldState import xyz.sevive.arcaeaoffline.ui.helpers.ArcaeaFormatters import xyz.sevive.arcaeaoffline.ui.navigation.UtilitiesSubScreen import xyz.sevive.arcaeaoffline.ui.screens.EmptyScreen +import xyz.sevive.arcaeaoffline.ui.theme.spacing import kotlin.math.round private fun IntRange.average() = round((first + last) / 2.0).toInt() @@ -105,7 +113,7 @@ private fun ScoreRangeInput( Column( modifier, horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { OutlinedArcaeaScoreTextField( rangeLastTextFieldState, @@ -147,7 +155,7 @@ private fun PlayRatingCalculatorDialog( Column( Modifier.padding(contentPadding), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { Icon( Icons.Default.Calculate, @@ -157,7 +165,7 @@ private fun PlayRatingCalculatorDialog( Column( horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(2.dp), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.xxs), ) { Text(state.songTitle, style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Normal)) Text( @@ -227,7 +235,7 @@ private fun ResultsListItem( ) Row(verticalAlignment = Alignment.CenterVertically) { - Text(state.scoreText, Modifier.padding(start = 16.dp)) + Text(state.scoreText, Modifier.padding(start = MaterialTheme.spacing.lg)) Icon(Icons.AutoMirrored.Filled.ArrowRight, contentDescription = null) @@ -242,6 +250,70 @@ private fun ResultsListItem( } } +private val ParametersCardCollapsedHeight = 48.dp +private val ParametersCardTopPadding = MaterialTheme.spacing.xs +private val ParametersCardOuterVerticalPadding = MaterialTheme.spacing.sm + +/** + * Floating parameters panel. It overlays the results list, which clears its + * collapsed height via [ParametersCardCollapsedHeight]. + */ +@Composable +private fun ParametersCard( + expanded: Boolean, + onExpandedChange: (Boolean) -> Unit, + summary: @Composable () -> Unit, + modifier: Modifier = Modifier, + content: @Composable ColumnScope.() -> Unit, +) { + val arrowRotation by animateFloatAsState(if (expanded) 0f else -90f) + + Surface( + modifier = modifier, + shape = MaterialTheme.shapes.large, + color = MaterialTheme.colorScheme.surfaceContainerHigh, + // M3 elevation level 3 (dialog tier) + shadowElevation = 6.dp, + ) { + Column { + Row( + Modifier + .fillMaxWidth() + .heightIn(min = ParametersCardCollapsedHeight) + .clickable { onExpandedChange(!expanded) } + .padding(horizontal = MaterialTheme.spacing.lg, vertical = ParametersCardTopPadding), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), + verticalAlignment = Alignment.CenterVertically, + ) { + Text( + stringResource(R.string.utilities_recommend_input), + style = MaterialTheme.typography.titleMedium, + ) + + AnimatedVisibility(!expanded) { + summary() + } + + Spacer(Modifier.weight(1f)) + + Icon( + Icons.Default.ExpandMore, + contentDescription = null, + Modifier.graphicsLayer { rotationZ = arrowRotation }, + ) + } + + AnimatedVisibility(expanded) { + Column( + Modifier.padding(top = MaterialTheme.spacing.sm), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), + content = content, + ) + } + } + } +} + @Composable fun UtilitiesChartRecommendScreen( modifier: Modifier = Modifier, @@ -272,9 +344,6 @@ fun UtilitiesChartRecommendScreen( } var isInputVisible by rememberSaveable { mutableStateOf(false) } - val expandArrowRotateDegree by animateFloatAsState( - if (isInputVisible) 0f else -90f, - ) var showCalculatorDialog by rememberSaveable { mutableStateOf(false) } var calculatorDialogState by rememberSerializable { mutableStateOf(PlayRatingCalculatorDialogState()) } @@ -291,19 +360,91 @@ fun UtilitiesChartRecommendScreen( modifier = modifier, title = stringResource(UtilitiesSubScreen.Recommend.title), ) { - Column(Modifier.padding(horizontal = dimensionResource(R.dimen.page_padding))) { - Row( - Modifier.clickable { isInputVisible = !isInputVisible }, - verticalAlignment = Alignment.CenterVertically, + Box(Modifier.fillMaxSize()) { + // The card floats with top padding. The list and the top + // scrim both clear the card's bottom edge plus a lg gap. + val listTopInset = + ParametersCardCollapsedHeight + + ParametersCardOuterVerticalPadding + + MaterialTheme.spacing.lg + + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = + PaddingValues( + top = listTopInset, + bottom = MaterialTheme.spacing.sm, + ), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { - ListGroupHeader(stringResource(R.string.utilities_recommend_input)) + item { + Row(verticalAlignment = Alignment.CenterVertically) { + ListGroupHeader(stringResource(R.string.utilities_recommend_results)) + + Spacer(Modifier.weight(1f)) + + AnimatedVisibility( + visible = uiState.isLoading, + enter = fadeIn(), + exit = fadeOut(), + ) { + CircularProgressIndicator( + Modifier + .padding(end = MaterialTheme.spacing.lg) + .size(18.dp), + ) + } + } + } + + if (uiState.charts.isEmpty()) { + item { + EmptyScreen( + Modifier + .fillMaxSize() + .padding(horizontal = MaterialTheme.spacing.pagePadding), + ) + } + } else { + items(uiState.charts, { it.difficultyWithSong.songId + it.difficultyWithSong.ratingClass.name }) { item -> + val state = ResultsListItemState(item, scoreRange, targetPlayRating, uiState.clearType) + + ResultsListItem( + state = state, + onOpenCalculator = { + calculatorDialogState = PlayRatingCalculatorDialogState(item, state.score) + showCalculatorDialog = true + }, + Modifier + .padding(horizontal = MaterialTheme.spacing.pagePadding) + .animateItem(), + ) + } + } + } - AnimatedVisibility(!isInputVisible) { + Box( + Modifier + .align(Alignment.TopCenter) + .fillMaxWidth() + .height(listTopInset) + .background( + Brush.verticalGradient( + 0f to MaterialTheme.colorScheme.background, + 1f to Color.Transparent, + ), + ), + ) + + ParametersCard( + expanded = isInputVisible, + onExpandedChange = { isInputVisible = it }, + summary = { CompositionLocalProvider( LocalTextStyle provides MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.Normal), ) { Row( - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.icon_text_padding)), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.iconTextGap), verticalAlignment = Alignment.CenterVertically, ) { Text("${ArcaeaFormatters.score(scoreRange.first)} ~ ${ArcaeaFormatters.score(scoreRange.last)}") @@ -319,102 +460,64 @@ fun UtilitiesChartRecommendScreen( Text(targetPlayRating.toString(), fontWeight = FontWeight.Bold) } } + }, + modifier = + Modifier + .align(Alignment.TopCenter) + .padding( + horizontal = MaterialTheme.spacing.pagePadding, + vertical = ParametersCardOuterVerticalPadding, + ).fillMaxWidth(), + ) { + Row( + Modifier + .fillMaxWidth() + .padding(horizontal = MaterialTheme.spacing.lg), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), + verticalAlignment = Alignment.CenterVertically, + ) { + ScoreRangeInput( + scoreRange = scoreRange, + onRangeFirstChange = { viewModel.setScoreRange(it..scoreRange.last) }, + onRangeLastChange = { viewModel.setScoreRange(scoreRange.first..it) }, + Modifier.weight(1f), + ) + + Icon( + Icons.Default.Link, + contentDescription = null, + Modifier.rotate(-45f), + ) + + DecimalStepperTextField( + targetPlayRatingTextFieldState, + Modifier.weight(1f), + label = { Text(stringResource(R.string.utilities_recommend_target_play_rating)) }, + ) } - Spacer(Modifier.weight(1f)) - - Icon( - Icons.Default.ExpandMore, - contentDescription = null, - Modifier.graphicsLayer { rotationZ = expandArrowRotateDegree }, - ) - } - - AnimatedVisibility(isInputVisible) { - Column { - Row( - Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically, + Row( + Modifier.padding(horizontal = MaterialTheme.spacing.lg), + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.iconTextGap), + ) { + CompositionLocalProvider( + LocalTextStyle provides MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.Normal), ) { - ScoreRangeInput( - scoreRange = scoreRange, - onRangeFirstChange = { viewModel.setScoreRange(it..scoreRange.last) }, - onRangeLastChange = { viewModel.setScoreRange(scoreRange.first..it) }, - Modifier.weight(1f), - ) - - Icon( - Icons.Default.Link, - contentDescription = null, - Modifier.rotate(-45f), - ) - - DecimalStepperTextField( - targetPlayRatingTextFieldState, - Modifier.weight(1f), - label = { Text(stringResource(R.string.utilities_recommend_target_play_rating)) }, - ) + TextButton({ viewModel.setScoreRange(9_900_000..10_000_000) }) { Text("EX+") } + TextButton({ viewModel.setScoreRange(9_800_000..9_899_999) }) { Text("EX") } + TextButton({ viewModel.setScoreRange(9_500_000..9_799_999) }) { Text("AA") } } - - Row(horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.icon_text_padding))) { - CompositionLocalProvider( - LocalTextStyle provides MaterialTheme.typography.labelLarge.copy(fontWeight = FontWeight.Normal), - ) { - TextButton({ viewModel.setScoreRange(9_900_000..10_000_000) }) { Text("EX+") } - TextButton({ viewModel.setScoreRange(9_800_000..9_899_999) }) { Text("EX") } - TextButton({ viewModel.setScoreRange(9_500_000..9_799_999) }) { Text("AA") } - } - } - - if (uiState.scoringMode == ArcaeaScoringMode.B50) { - SwitchPreferencesWidget( - value = uiState.clearType != null && uiState.clearType != ArcaeaPlayResultClearType.TRACK_LOST, - onValueChange = { cleared -> - viewModel.setClearType(if (cleared) ArcaeaPlayResultClearType.NORMAL_CLEAR else null) - }, - title = stringResource(R.string.play_rating_cleared), - description = stringResource(R.string.play_rating_cleared_description), - ) - } - } - } - - Row(verticalAlignment = Alignment.CenterVertically) { - ListGroupHeader(stringResource(R.string.utilities_recommend_results)) - - Spacer(Modifier.weight(1f)) - - AnimatedVisibility( - visible = uiState.isLoading, - enter = fadeIn(), - exit = fadeOut(), - ) { - CircularProgressIndicator(Modifier.size(18.dp)) } - } - - LazyColumn( - contentPadding = PaddingValues(vertical = dimensionResource(R.dimen.list_padding)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.list_padding)), - ) { - if (uiState.charts.isEmpty()) { - item { - EmptyScreen(Modifier.fillMaxSize()) - } - } else { - items(uiState.charts, { it.difficultyWithSong.songId + it.difficultyWithSong.ratingClass.name }) { item -> - val state = ResultsListItemState(item, scoreRange, targetPlayRating, uiState.clearType) - ResultsListItem( - state = state, - onOpenCalculator = { - calculatorDialogState = PlayRatingCalculatorDialogState(item, state.score) - showCalculatorDialog = true - }, - Modifier.animateItem(), - ) - } + if (uiState.scoringMode == ArcaeaScoringMode.B50) { + SwitchItem( + value = uiState.clearType != null && uiState.clearType != ArcaeaPlayResultClearType.TRACK_LOST, + onValueChange = { cleared -> + viewModel.setClearType(if (cleared) ArcaeaPlayResultClearType.NORMAL_CLEAR else null) + }, + title = stringResource(R.string.play_rating_cleared), + description = stringResource(R.string.play_rating_cleared_description), + ) } } } diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml deleted file mode 100644 index c92b4cec..00000000 --- a/app/src/main/res/values/dimens.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - 16dp - 4dp - 8dp - 8dp - - 16dp - 10dp - - 16dp - 16dp - - 8dp - 16dp - diff --git a/desktopApp/src/main/kotlin/xyz/sevive/arcaeaoffline/desktop/Main.kt b/desktopApp/src/main/kotlin/xyz/sevive/arcaeaoffline/desktop/Main.kt index b011675a..84a60acf 100644 --- a/desktopApp/src/main/kotlin/xyz/sevive/arcaeaoffline/desktop/Main.kt +++ b/desktopApp/src/main/kotlin/xyz/sevive/arcaeaoffline/desktop/Main.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -27,6 +28,7 @@ import xyz.sevive.arcaeaoffline.ui.components.arcaea.OutlinedArcaeaScoreTextFiel import xyz.sevive.arcaeaoffline.ui.components.arcaea.rememberArcaeaScoreTextFieldState import xyz.sevive.arcaeaoffline.ui.components.rememberArcaeaConstantStepperTextFieldState import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing fun main() = application { @@ -49,8 +51,8 @@ fun CalculatorScreen() { val result = calculatePlayRating(score, constant) Column( - Modifier.padding(16.dp).width(400.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), + Modifier.padding(MaterialTheme.spacing.lg).width(400.dp), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.sm), ) { OutlinedArcaeaScoreTextField(scoreTextFieldState) diff --git a/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/components/DecimalStepperTextField.kt b/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/components/DecimalStepperTextField.kt index 2f18f275..b29cd69d 100644 --- a/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/components/DecimalStepperTextField.kt +++ b/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/components/DecimalStepperTextField.kt @@ -18,6 +18,7 @@ import androidx.compose.material.icons.filled.Remove import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -47,6 +48,7 @@ import com.ionspin.kotlin.bignum.decimal.BigDecimal import com.ionspin.kotlin.bignum.decimal.RoundingMode import com.ionspin.kotlin.bignum.decimal.toBigDecimal import xyz.sevive.arcaeaoffline.ui.theme.ArcaeaOfflineTheme +import xyz.sevive.arcaeaoffline.ui.theme.spacing import xyz.sevive.arcaeaoffline.ui.utils.AutoRepeatController import xyz.sevive.arcaeaoffline.ui.utils.rememberAutoRepeatController @@ -402,8 +404,8 @@ private fun DecimalStepperTextFieldPreview() { ArcaeaOfflineTheme { Surface { Column( - Modifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp), + Modifier.padding(MaterialTheme.spacing.lg), + verticalArrangement = Arrangement.spacedBy(MaterialTheme.spacing.lg), ) { DecimalStepperTextField(state) diff --git a/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/theme/Spacing.kt b/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/theme/Spacing.kt new file mode 100644 index 00000000..a8e8c2bd --- /dev/null +++ b/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/theme/Spacing.kt @@ -0,0 +1,29 @@ +package xyz.sevive.arcaeaoffline.ui.theme + +import androidx.compose.material3.MaterialTheme +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +/** + * Spacing scale on a 4dp grid, for paddings and content gaps only. + * Icon sizes, corner radii, and stroke widths are not part of this scale. + */ +object Spacing { + val xxs: Dp = 2.dp + val xs: Dp = 4.dp + val sm: Dp = 8.dp + val lg: Dp = 16.dp + val xl: Dp = 24.dp + val xxl: Dp = 32.dp + + val pagePadding: Dp = lg + val cardPadding: Dp = sm + val iconTextGap: Dp = xs + val dialogPadding: Dp = xl +} + +/** + * Placeholder for future CompositionLocal runtime-varying scale if introduced. + */ +val MaterialTheme.spacing: Spacing + get() = Spacing diff --git a/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/theme/Typography.kt b/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/theme/Typography.kt index c8262494..ae1ff1df 100644 --- a/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/theme/Typography.kt +++ b/shared/src/commonMain/kotlin/xyz/sevive/arcaeaoffline/ui/theme/Typography.kt @@ -1,6 +1,18 @@ package xyz.sevive.arcaeaoffline.ui.theme +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Typography +import androidx.compose.runtime.Composable +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight // Set of Material typography styles to start with val Typography = Typography() + +val Typography.header: TextStyle + @Composable + get() = + bodyMedium.copy( + color = MaterialTheme.colorScheme.onSurfaceVariant, + fontWeight = FontWeight.SemiBold, + )