From a0978903bb5bba5c4159dd4dba29840c80542b54 Mon Sep 17 00:00:00 2001 From: YaMiN Date: Sat, 29 Apr 2023 03:07:02 +0330 Subject: [PATCH] ui overhaul/refactors --- .gitignore | 1 + app/build.gradle | 4 +- .../io/github/yamin8000/dooz/content/About.kt | 10 + .../yamin8000/dooz/content/MainNavigation.kt | 2 +- .../yamin8000/dooz/content/MainTopAppBar.kt | 10 +- .../dooz/content/game/GameContent.kt | 227 ++++++++---------- .../dooz/content/game/PlayersInfoContent.kt | 49 ++-- .../content/settings/SettingsComposables.kt | 174 ++++++++++++++ .../content/settings/content/GameSettings.kt | 150 ++++++------ .../settings/content/GeneralSettings.kt | 26 +- .../settings/content/PlayerSettings.kt | 43 ++-- .../dooz/content/settings/content/Settings.kt | 8 +- .../io/github/yamin8000/dooz/ui/Shapes.kt | 69 ++++-- .../dooz/ui/composables/Composables.kt | 129 ++-------- .../yamin8000/dooz/ui/composables/Texts.kt | 11 + .../github/yamin8000/dooz/ui/theme/Theme.kt | 20 +- .../github/yamin8000/dooz/util/Constants.kt | 2 +- app/src/main/res/values-fa/strings.xml | 2 +- app/src/main/res/values/strings.xml | 5 +- 19 files changed, 529 insertions(+), 413 deletions(-) create mode 100644 app/src/main/java/io/github/yamin8000/dooz/content/settings/SettingsComposables.kt diff --git a/.gitignore b/.gitignore index a023cdc..6d3f8e9 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ local.properties /app/src/irMarket/java/io/github/yamin8000/dooz/ad/AdConstants.kt /app/free/ /app/irMarket/ +/app/src/main/java/io/github/yamin8000/dooz/Previews.kt diff --git a/app/build.gradle b/app/build.gradle index 9f07a89..28e753e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -32,8 +32,8 @@ android { applicationId "io.github.yamin8000.dooz" minSdk 24 targetSdk 33 - versionCode 7 - versionName "1.0.6" + versionCode 8 + versionName "1.0.8" vectorDrawables { useSupportLibrary true } diff --git a/app/src/main/java/io/github/yamin8000/dooz/content/About.kt b/app/src/main/java/io/github/yamin8000/dooz/content/About.kt index 4ca3d2b..05c5153 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/content/About.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/content/About.kt @@ -62,6 +62,7 @@ fun AboutContent(onBackClick: () -> Unit) { val uriHandler = LocalUriHandler.current val sourceUri = stringResource(R.string.github_source) val licenseUri = stringResource(R.string.license_link) + val developerUri = stringResource(R.string.developer_uri) Ripple( onClick = { uriHandler.openUri(licenseUri) }, content = { @@ -101,6 +102,15 @@ fun AboutContent(onBackClick: () -> Unit) { ) } ) + Ripple( + onClick = { uriHandler.openUri(developerUri) }, + content = { + Text( + text = developerUri, + textDecoration = TextDecoration.Underline + ) + } + ) } } } diff --git a/app/src/main/java/io/github/yamin8000/dooz/content/MainNavigation.kt b/app/src/main/java/io/github/yamin8000/dooz/content/MainNavigation.kt index e62e37e..3fa68fb 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/content/MainNavigation.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/content/MainNavigation.kt @@ -48,9 +48,9 @@ fun MainNavigation( ) { val context = LocalContext.current var theme by remember { mutableStateOf(ThemeSetting.System) } + val dataStore = DataStoreHelper(context.settings) LaunchedEffect(Unit) { - val dataStore = DataStoreHelper(context.settings) theme = ThemeSetting.valueOf( dataStore.getString(Constants.theme) ?: ThemeSetting.System.name ) diff --git a/app/src/main/java/io/github/yamin8000/dooz/content/MainTopAppBar.kt b/app/src/main/java/io/github/yamin8000/dooz/content/MainTopAppBar.kt index 30588a6..9ba1c67 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/content/MainTopAppBar.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/content/MainTopAppBar.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.github.yamin8000.dooz.R @@ -50,11 +51,18 @@ fun MainTopAppBar( val appName = stringResource(R.string.app_name) CenterAlignedTopAppBar( scrollBehavior = scrollBehavior, - title = { PersianText(text = appName, fontSize = 20.sp) }, navigationIcon = { AnimatedAppIcon() }, actions = { SettingsIcon(onSettingsIconClick) AboutIcon(onAboutIconClick) + }, + title = { + PersianText( + text = appName, + fontSize = 16.sp, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) } ) } diff --git a/app/src/main/java/io/github/yamin8000/dooz/content/game/GameContent.kt b/app/src/main/java/io/github/yamin8000/dooz/content/game/GameContent.kt index f9134e3..6a73d1f 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/content/game/GameContent.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/content/game/GameContent.kt @@ -21,7 +21,6 @@ package io.github.yamin8000.dooz.content.game import android.content.pm.ActivityInfo -import android.content.res.Configuration import androidx.compose.animation.* import androidx.compose.animation.core.tween import androidx.compose.foundation.* @@ -32,7 +31,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.itemsIndexed import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.twotone.Gamepad +import androidx.compose.material.icons.twotone.Games import androidx.compose.material.icons.twotone.Undo import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.* @@ -46,19 +45,17 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.github.yamin8000.dooz.R import io.github.yamin8000.dooz.content.MainTopAppBar import io.github.yamin8000.dooz.model.* -import io.github.yamin8000.dooz.ui.XShape -import io.github.yamin8000.dooz.ui.composables.ButtonWithIcon -import io.github.yamin8000.dooz.ui.composables.InfoCard import io.github.yamin8000.dooz.ui.composables.LockScreenOrientation import io.github.yamin8000.dooz.ui.composables.PersianText -import io.github.yamin8000.dooz.ui.theme.PreviewTheme +import io.github.yamin8000.dooz.ui.composables.SingleLinePersianText +import io.github.yamin8000.dooz.ui.composables.isFontScaleNormal @OptIn(ExperimentalAnimationApi::class, ExperimentalMaterial3Api::class) @Composable @@ -70,10 +67,25 @@ fun GameContent( val gameState = rememberHomeState() val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior() + val undoRepeats = remember { + if (gameState.gamePlayersType.value == GamePlayersType.PvC) 2 else 1 + } Scaffold( - modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { MainTopAppBar(scrollBehavior, onNavigateToSettings, onNavigateToAbout) }, + modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), + floatingActionButton = { + ExtendedFloatingActionButton( + text = { SingleLinePersianText(stringResource(R.string.new_game)) }, + onClick = { gameState.newGame() }, + icon = { + Icon( + imageVector = Icons.TwoTone.Games, + contentDescription = null + ) + } + ) + }, content = { contentPadding -> Surface( modifier = Modifier @@ -89,28 +101,40 @@ fun GameContent( .verticalScroll(rememberScrollState()) ) { Row( + modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp) ) { - ButtonWithIcon( + AnimatedVisibility( + visible = gameState.isGameStarted.value, + enter = slideInHorizontally( + initialOffsetX = { -it }, + animationSpec = tween(300) + ) + ) { + GameInfoCard( + modifier = Modifier.weight(1f), + playersType = gameState.gamePlayersType.value, + aiDifficulty = gameState.aiDifficulty.value, + winnerName = gameState.winner.value?.name, + isGameDrew = gameState.isGameDrew.value + ) + } + + FilledIconButton( onClick = { gameState.newGame() }, - enabled = !gameState.isRollingDices.value, - content = { PersianText(stringResource(R.string.start_game)) }, - icon = { + content = { Icon( - imageVector = Icons.TwoTone.Gamepad, - contentDescription = stringResource(R.string.start_game) + imageVector = Icons.TwoTone.Games, + contentDescription = null ) } ) - val undoRepeats = - if (gameState.gamePlayersType.value == GamePlayersType.PvC) 2 - else 1 - ButtonWithIcon( + + FilledIconButton( onClick = { repeat(undoRepeats) { gameState.undo() } }, enabled = gameState.isGameStarted.value && gameState.lastPlayedCells.value.isNotEmpty(), - content = { PersianText(stringResource(R.string.undo)) }, - icon = { + content = { Icon( imageVector = Icons.TwoTone.Undo, stringResource(R.string.undo) @@ -119,21 +143,6 @@ fun GameContent( ) } - AnimatedVisibility( - visible = gameState.isGameStarted.value, - enter = slideInHorizontally( - initialOffsetX = { -it }, - animationSpec = tween(300) - ) - ) { - GameInfoCard( - gameState.winner.value?.name, - gameState.isGameDrew.value, - gameState.gamePlayersType.value, - gameState.aiDifficulty.value, - ) - } - AnimatedVisibility( visible = gameState.isGameStarted.value, enter = slideInHorizontally( @@ -142,9 +151,9 @@ fun GameContent( ) ) { PlayerCards( - gameState.firstPlayerPolicy.value, - gameState.players.value, - gameState.currentPlayer.value + firstPlayerPolicy = gameState.firstPlayerPolicy.value, + players = gameState.players.value, + currentPlayer = gameState.currentPlayer.value ) } @@ -163,6 +172,7 @@ fun GameContent( onItemClick = { gameState.playCell(it) } ) } + Spacer(modifier = Modifier.height(64.dp)) } } } @@ -170,50 +180,69 @@ fun GameContent( } @Composable -private fun GameInfoCard( +fun GameResult( winnerName: String?, - isGameDrew: Boolean = true, - playersType: GamePlayersType = GamePlayersType.PvP, - aiDifficulty: AiDifficulty = AiDifficulty.Easy, + isGameDrew: Boolean ) { - InfoCard( - modifier = Modifier.fillMaxWidth(), - columnModifier = Modifier.fillMaxWidth(), - header = { + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + if (isFontScaleNormal()) { PersianText( - text = stringResource(R.string.game_info), - fontSize = 18.sp, - color = MaterialTheme.colorScheme.primary + text = stringResource(R.string.game_result), + color = MaterialTheme.colorScheme.primary, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) - }, + } + if (isGameDrew) + SingleLinePersianText(stringResource(R.string.game_is_drew)) + if (winnerName != null) + SingleLinePersianText(stringResource(R.string.x_is_winner, winnerName)) + if (!isGameDrew && winnerName == null) + SingleLinePersianText(stringResource(R.string.undefined)) + } +} + +@Composable +private fun GameInfoCard( + modifier: Modifier = Modifier, + playersType: GamePlayersType = GamePlayersType.PvP, + aiDifficulty: AiDifficulty = AiDifficulty.Easy, + winnerName: String?, + isGameDrew: Boolean +) { + Card( + modifier = modifier, content = { - PersianText(stringResource(playersType.persianNameStringResource)) + Column( + modifier = Modifier.padding(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + content = { + PersianText( + text = stringResource(R.string.game_info), + fontSize = 16.sp, + color = MaterialTheme.colorScheme.primary + ) + SingleLinePersianText(stringResource(playersType.persianNameStringResource)) - if (playersType == GamePlayersType.PvC) { - PersianText( - stringResource( - R.string.ai_difficulty_var, - stringResource(aiDifficulty.persianNameStringResource) + if (playersType == GamePlayersType.PvC) { + SingleLinePersianText( + stringResource( + R.string.ai_difficulty_var, + stringResource(aiDifficulty.persianNameStringResource) + ) + ) + } + GameResult( + winnerName = winnerName, + isGameDrew = isGameDrew ) - ) - } - }, - footer = { - Row( - horizontalArrangement = Arrangement.spacedBy(4.dp) - ) { - PersianText( - text = stringResource(R.string.game_result), - color = MaterialTheme.colorScheme.primary - ) - if (isGameDrew) - PersianText(stringResource(R.string.game_is_drew)) - if (winnerName != null) - PersianText(stringResource(R.string.x_is_winner, winnerName)) - if (!isGameDrew && winnerName == null) - PersianText(stringResource(R.string.undefined)) - } - }) + } + ) + } + ) } @Composable @@ -306,52 +335,4 @@ fun DoozItem( } } } -} - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true) -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true) -@Composable -private fun Preview() = PreviewTheme { GameContent({}, {}) } - -//@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true) -//@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true) -@Composable -private fun DoozItemPreview() { - PreviewTheme { - DoozItem( - shape = XShape, - clickable = true, - itemSize = 40.dp, - doozCellOwner = Player("Player"), - itemBackgroundColor = MaterialTheme.colorScheme.primary, - itemContentColor = MaterialTheme.colorScheme.onPrimary, - onClick = {} - ) - } -} - -//@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true) -//@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true) -@Composable -private fun GameBoardPreview() { - PreviewTheme { - Surface { - GameBoard( - gameSize = 3, - gameCells = buildList { - for (i in 1..3) { - val row = mutableListOf() - for (j in 1..3) - row.add(DoozCell(i, j)) - add(row) - } - }, - winnerCells = buildList { }, - isGameFinished = false, - currentPlayerType = null, - shapeProvider = { XShape }, - onItemClick = {} - ) - } - } } \ No newline at end of file diff --git a/app/src/main/java/io/github/yamin8000/dooz/content/game/PlayersInfoContent.kt b/app/src/main/java/io/github/yamin8000/dooz/content/game/PlayersInfoContent.kt index 382457a..da3694c 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/content/game/PlayersInfoContent.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/content/game/PlayersInfoContent.kt @@ -20,30 +20,28 @@ package io.github.yamin8000.dooz.content.game -import android.content.res.Configuration import androidx.compose.animation.* -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.material.ContentAlpha import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import io.github.yamin8000.dooz.R import io.github.yamin8000.dooz.game.FirstPlayerPolicy import io.github.yamin8000.dooz.model.Player import io.github.yamin8000.dooz.ui.ShapePreview import io.github.yamin8000.dooz.ui.composables.PersianText -import io.github.yamin8000.dooz.ui.theme.PreviewTheme +import io.github.yamin8000.dooz.ui.composables.isFontScaleNormal import io.github.yamin8000.dooz.ui.toShape @Composable @@ -83,42 +81,39 @@ internal fun PlayerCard( firstPlayerPolicy: FirstPlayerPolicy, isCurrentPlayer: Boolean = true ) { - val iconTint = - if (isCurrentPlayer) MaterialTheme.colorScheme.secondary - else MaterialTheme.colorScheme.secondary.copy(alpha = 0.5f) - - val borderColor = - if (isCurrentPlayer) MaterialTheme.colorScheme.outline - else MaterialTheme.colorScheme.outlineVariant + val alpha = if (isCurrentPlayer) ContentAlpha.high else ContentAlpha.disabled OutlinedCard( - border = BorderStroke(1.dp, borderColor), - modifier = modifier + modifier = modifier.alpha(alpha) ) { Row( modifier = Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - if (firstPlayerPolicy == FirstPlayerPolicy.DiceRolling) { + if (firstPlayerPolicy == FirstPlayerPolicy.DiceRolling && isFontScaleNormal()) { AnimatedContent( targetState = player.diceIndex, transitionSpec = { slideInVertically { it } + fadeIn() with slideOutVertically { -it } + fadeOut() }, - content = { PlayerDice(iconTint = iconTint, diceIndex = it) } + content = { PlayerDice( diceIndex = it) } ) } - player.shape?.toShape()?.let { shape -> ShapePreview(shape, 30.dp, iconTint) } - PersianText(text = player.name, modifier = Modifier.weight(2f)) + player.shape?.toShape()?.let { shape -> ShapePreview(shape, 30.dp) } + PersianText( + text = player.name, + modifier = Modifier.weight(2f), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) } } } @Composable private fun PlayerDice( - iconTint: Color, diceIndex: Int = 1 ) { val icon = when (diceIndex) { @@ -133,19 +128,5 @@ private fun PlayerDice( Icon( painter = painterResource(icon), contentDescription = stringResource(R.string.player_turn), - tint = iconTint ) -} - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true) -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true) -@Composable -private fun PlayerCardsPreview() { - PreviewTheme { - PlayerCards( - firstPlayerPolicy = FirstPlayerPolicy.DiceRolling, - players = listOf(Player("Yamin"), Player("Amir")), - currentPlayer = null - ) - } } \ No newline at end of file diff --git a/app/src/main/java/io/github/yamin8000/dooz/content/settings/SettingsComposables.kt b/app/src/main/java/io/github/yamin8000/dooz/content/settings/SettingsComposables.kt new file mode 100644 index 0000000..740713c --- /dev/null +++ b/app/src/main/java/io/github/yamin8000/dooz/content/settings/SettingsComposables.kt @@ -0,0 +1,174 @@ +/* + * Dooz/Dooz.app.main + * SettingsComposables.kt Copyrighted by Yamin Siahmargooei at 2023/4/29 + * SettingsComposables.kt Last modified at 2023/4/29 + * This file is part of Dooz/Dooz.app.main. + * Copyright (C) 2023 Yamin Siahmargooei + * + * Dooz/Dooz.app.main is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dooz/Dooz.app.main is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dooz. If not, see . + */ + +package io.github.yamin8000.dooz.content.settings + +import android.content.Context +import androidx.compose.foundation.LocalIndication +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +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.RowScope +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.twotone.ArrowDropDownCircle +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Card +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.github.yamin8000.dooz.ui.DefaultCornerShape +import io.github.yamin8000.dooz.ui.composables.PersianText +import io.github.yamin8000.dooz.ui.composables.SingleLinePersianText + +@Composable +internal fun SettingsItemCard( + modifier: Modifier = Modifier, + title: String, + content: @Composable ColumnScope.() -> Unit +) { + Column( + verticalArrangement = Arrangement.spacedBy(4.dp), + horizontalAlignment = Alignment.Start, + ) { + PersianText( + text = title, + fontSize = 18.sp, + color = MaterialTheme.colorScheme.primary + ) + Card( + modifier = modifier, + shape = DefaultCornerShape + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + content = { content() } + ) + } + } +} + +@Composable +internal fun SettingsItem( + modifier: Modifier = Modifier, + onClick: () -> Unit, + content: @Composable RowScope.() -> Unit +) { + val interactionSource = remember { MutableInteractionSource() } + Box( + modifier = modifier.clickable( + interactionSource = interactionSource, + indication = LocalIndication.current, + onClick = onClick, + ), + content = { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(vertical = 16.dp), + content = content + ) + Icon( + imageVector = Icons.TwoTone.ArrowDropDownCircle, + contentDescription = "" + ) + } + } + ) +} + +@Composable +internal fun SettingsSelectorDialog( + title: String, + icon: (@Composable () -> Unit)? = null, + displayProvider: (T, Context) -> String = { item, _ -> item.toString() }, + options: List, + currentItem: T, + onDismiss: () -> Unit, + onOptionChanged: (T) -> Unit +) { + val context = LocalContext.current + AlertDialog( + onDismissRequest = onDismiss, + confirmButton = {/*ignored*/ }, + icon = icon, + title = { SingleLinePersianText(title) }, + text = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.SpaceBetween, + modifier = Modifier + .padding(16.dp) + .selectableGroup() + .fillMaxWidth() + ) { + options.forEach { item -> + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(2.dp, Alignment.Start), + modifier = Modifier + .fillMaxWidth() + .selectable( + selected = (item == currentItem), + role = Role.RadioButton, + onClick = { + onOptionChanged(item) + onDismiss() + } + ) + ) { + RadioButton( + selected = (item == currentItem), + onClick = null, + modifier = Modifier.padding(start = 8.dp) + ) + PersianText( + text = displayProvider(item, context), + modifier = Modifier.padding(vertical = 16.dp) + ) + } + } + } + } + ) +} diff --git a/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/GameSettings.kt b/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/GameSettings.kt index dec5ed2..6e7c245 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/GameSettings.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/GameSettings.kt @@ -23,28 +23,30 @@ package io.github.yamin8000.dooz.content.settings.content import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.selection.selectable -import androidx.compose.foundation.selection.selectableGroup import androidx.compose.material.icons.Icons import androidx.compose.material.icons.twotone.Add import androidx.compose.material.icons.twotone.Remove -import androidx.compose.material3.* +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +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.stringResource -import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import io.github.yamin8000.dooz.R +import io.github.yamin8000.dooz.content.settings.SettingsItem +import io.github.yamin8000.dooz.content.settings.SettingsItemCard +import io.github.yamin8000.dooz.content.settings.SettingsSelectorDialog import io.github.yamin8000.dooz.game.FirstPlayerPolicy import io.github.yamin8000.dooz.model.AiDifficulty import io.github.yamin8000.dooz.model.GamePlayersType -import io.github.yamin8000.dooz.ui.composables.InfoCard -import io.github.yamin8000.dooz.ui.composables.PersianText -import io.github.yamin8000.dooz.ui.composables.RadioGroup +import io.github.yamin8000.dooz.ui.composables.SingleLinePersianText import io.github.yamin8000.dooz.util.Constants @Composable @@ -52,48 +54,25 @@ internal fun AiDifficultyCard( aiDifficulty: AiDifficulty, onDifficultyChanged: (AiDifficulty) -> Unit ) { - InfoCard( - modifier = Modifier.fillMaxWidth(), - columnModifier = Modifier.fillMaxWidth(), - header = { - PersianText( - text = stringResource(R.string.ai_difficulty), - fontSize = 18.sp, - color = MaterialTheme.colorScheme.primary - ) - }, + val title = stringResource(R.string.ai_difficulty) + var isDialogVisible by remember { mutableStateOf(false) } + SettingsItemCard( + title = title, content = { - Row( - modifier = Modifier - .selectableGroup() - .fillMaxWidth() - .padding(horizontal = 16.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Constants.difficulties.forEach { difficulty -> - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier - .selectable( - selected = (difficulty == aiDifficulty), - onClick = { onDifficultyChanged(difficulty) }, - role = Role.RadioButton - ) - ) { - PersianText( - text = stringResource(difficulty.persianNameStringResource), - modifier = Modifier.padding(vertical = 16.dp, horizontal = 8.dp) - ) - RadioButton( - selected = (difficulty == aiDifficulty), - onClick = null, - modifier = Modifier.padding(horizontal = 8.dp) - ) - } - } + if (isDialogVisible) { + SettingsSelectorDialog( + title = title, + options = Constants.difficulties, + currentItem = aiDifficulty, + onDismiss = { isDialogVisible = false }, + onOptionChanged = onDifficultyChanged, + displayProvider = { item, context -> context.getString(item.persianNameStringResource) } + ) } + SettingsItem( + onClick = { isDialogVisible = true }, + content = { SingleLinePersianText(stringResource(aiDifficulty.persianNameStringResource)) } + ) } ) } @@ -105,29 +84,43 @@ internal fun GeneralGameSettings( firstPlayerPolicy: FirstPlayerPolicy, onFirstPlayerPolicyChange: (FirstPlayerPolicy) -> Unit ) { - val resources = LocalContext.current.resources - InfoCard( - modifier = Modifier.fillMaxWidth(), - columnModifier = Modifier.fillMaxWidth(), - header = { - PersianText( - text = stringResource(R.string.game_general_settings), - fontSize = 18.sp, - color = MaterialTheme.colorScheme.primary - ) - }, + var playerTypeDialogVisibility by remember { mutableStateOf(false) } + var firstPlayerPolicyVisibility by remember { mutableStateOf(false) } + val title = stringResource(R.string.game_general_settings) + + SettingsItemCard( + title = title, content = { - RadioGroup( - options = GamePlayersType.values().toList(), - currentOption = gamePlayersType, - onOptionChange = onPlayerTypeChange, - optionStringProvider = { resources.getString(it.persianNameStringResource) } + if (playerTypeDialogVisibility) { + SettingsSelectorDialog( + title = title, + options = GamePlayersType.values().toList(), + currentItem = gamePlayersType, + onDismiss = { playerTypeDialogVisibility = false }, + onOptionChanged = onPlayerTypeChange, + displayProvider = { item, context -> context.getString(item.persianNameStringResource) } + ) + } + + if (firstPlayerPolicyVisibility) { + SettingsSelectorDialog( + title = title, + options = FirstPlayerPolicy.values().toList(), + currentItem = firstPlayerPolicy, + onDismiss = { firstPlayerPolicyVisibility = false }, + onOptionChanged = onFirstPlayerPolicyChange, + displayProvider = { item, context -> context.getString(item.persianNameStringResource) } + ) + } + + SettingsItem( + onClick = { playerTypeDialogVisibility = true }, + content = { SingleLinePersianText(stringResource(gamePlayersType.persianNameStringResource)) } ) - RadioGroup( - options = FirstPlayerPolicy.values().toList(), - currentOption = firstPlayerPolicy, - onOptionChange = onFirstPlayerPolicyChange, - optionStringProvider = { resources.getString(it.persianNameStringResource) } + + SettingsItem( + onClick = { firstPlayerPolicyVisibility = true }, + content = { SingleLinePersianText(stringResource(firstPlayerPolicy.persianNameStringResource)) } ) } ) @@ -139,20 +132,13 @@ internal fun GameSizeChanger( onGameSizeIncrease: () -> Unit, onGameSizeDecrease: () -> Unit ) { - InfoCard( - modifier = Modifier.fillMaxWidth(), - columnModifier = Modifier.fillMaxWidth(), - header = { - PersianText( - text = stringResource(R.string.game_board_size), - fontSize = 18.sp, - color = MaterialTheme.colorScheme.primary - ) - }, + SettingsItemCard( + title = stringResource(R.string.game_board_size), content = { Row( + modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(16.dp) + horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally) ) { IconButton(onClick = onGameSizeDecrease) { Icon( diff --git a/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/GeneralSettings.kt b/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/GeneralSettings.kt index 59a60c3..447d61e 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/GeneralSettings.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/GeneralSettings.kt @@ -21,7 +21,11 @@ package io.github.yamin8000.dooz.content.settings.content import android.os.Build -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.selection.selectableGroup import androidx.compose.material.icons.Icons @@ -29,17 +33,25 @@ import androidx.compose.material.icons.twotone.DisplaySettings import androidx.compose.material3.AlertDialog import androidx.compose.material3.Icon import androidx.compose.material3.RadioButton -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +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.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import io.github.yamin8000.dooz.R +import io.github.yamin8000.dooz.content.settings.SettingsItem +import io.github.yamin8000.dooz.content.settings.SettingsItemCard import io.github.yamin8000.dooz.content.settings.ThemeSetting -import io.github.yamin8000.dooz.ui.composables.* +import io.github.yamin8000.dooz.ui.composables.InfoCard +import io.github.yamin8000.dooz.ui.composables.PersianText +import io.github.yamin8000.dooz.ui.composables.SingleLinePersianText +import io.github.yamin8000.dooz.ui.composables.SwitchWithText @Composable internal fun ThemeChangerCard( @@ -66,10 +78,7 @@ internal fun ThemeChangerCard( imageVector = Icons.TwoTone.DisplaySettings, contentDescription = stringResource(R.string.theme) ) - PersianText( - text = stringResource(currentTheme.persianNameStringResource), - modifier = Modifier.padding() - ) + SingleLinePersianText(stringResource(currentTheme.persianNameStringResource)) } ) if (currentTheme == ThemeSetting.System && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) @@ -139,7 +148,6 @@ internal fun EffectsCard( ) { InfoCard( modifier = Modifier.fillMaxWidth(), - columnModifier = Modifier.fillMaxWidth(), header = stringResource(R.string.effects), content = { SwitchWithText( diff --git a/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/PlayerSettings.kt b/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/PlayerSettings.kt index dd7b366..9be0ac4 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/PlayerSettings.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/PlayerSettings.kt @@ -34,12 +34,14 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.github.yamin8000.dooz.R import io.github.yamin8000.dooz.ui.ClickableShapes import io.github.yamin8000.dooz.ui.composables.InfoCard import io.github.yamin8000.dooz.ui.composables.PersianText +import io.github.yamin8000.dooz.ui.composables.SingleLinePersianText import io.github.yamin8000.dooz.ui.shapes import io.github.yamin8000.dooz.ui.toName import io.github.yamin8000.dooz.ui.toShape @@ -59,40 +61,49 @@ internal fun PlayerCustomization( ) { InfoCard( modifier = Modifier.fillMaxWidth(), - columnModifier = Modifier.fillMaxWidth(), header = { PersianText( text = stringResource(R.string.player_names), fontSize = 16.sp, - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.primary, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) }, content = { PlayerNamesCustomizer( - firstPlayerName, - onFirstPlayerNameChange, - secondPlayerName, - onSecondPlayerNameChange + firstPlayerName = firstPlayerName, + onFirstPlayerNameChange = onFirstPlayerNameChange, + secondPlayerName = secondPlayerName, + onSecondPlayerNameChange = onSecondPlayerNameChange ) PersianText( text = stringResource(R.string.player_shapes), fontSize = 16.sp, modifier = Modifier.padding(top = 16.dp), - color = MaterialTheme.colorScheme.primary + color = MaterialTheme.colorScheme.primary, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) PlayerShapesCustomizer( - firstPlayerShape, - onFirstPlayerShapeChange, - secondPlayerShape, - onSecondPlayerShapeChange + firstPlayerShape = firstPlayerShape, + onFirstPlayerShapeChange = onFirstPlayerShapeChange, + secondPlayerShape = secondPlayerShape, + onSecondPlayerShapeChange = onSecondPlayerShapeChange ) Button( + onClick = { onSave() }, modifier = Modifier .fillMaxWidth(.5f) .padding(top = 16.dp), - onClick = { onSave() }) { - PersianText(text = stringResource(R.string.save)) - } + content = { + PersianText( + text = stringResource(R.string.save), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + ) } ) } @@ -107,13 +118,13 @@ internal fun PlayerShapesCustomizer( ClickableShapes( shapes = shapes, lastSelectedShape = firstPlayerShape.toShape(), - header = { PersianText(stringResource(R.string.first_player_shape)) }, + header = { SingleLinePersianText(stringResource(R.string.first_player_shape)) }, onShapeSelected = { onFirstPlayerShapeChange(it.toName() ?: Constants.Shapes.ringShape) } ) ClickableShapes( shapes = shapes, lastSelectedShape = secondPlayerShape.toShape(), - header = { PersianText(stringResource(R.string.second_player_shape)) }, + header = { SingleLinePersianText(stringResource(R.string.second_player_shape)) }, onShapeSelected = { onSecondPlayerShapeChange(it.toName() ?: Constants.Shapes.xShape) } ) } diff --git a/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/Settings.kt b/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/Settings.kt index f84525e..1e81cc6 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/Settings.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/content/settings/content/Settings.kt @@ -108,15 +108,15 @@ fun SettingsContent( firstPlayerPolicy = state.firstPlayerPolicy, onFirstPlayerPolicyChange = { state.firstPlayerPolicy = it } ) + AiDifficultyCard( + aiDifficulty = state.aiDifficulty, + onDifficultyChanged = { state.aiDifficulty = it } + ) GameSizeChanger( gameSize = state.gameSize, onGameSizeIncrease = { state.gameSize++ }, onGameSizeDecrease = { state.gameSize-- } ) - AiDifficultyCard( - aiDifficulty = state.aiDifficulty, - onDifficultyChanged = { state.aiDifficulty = it } - ) } stringResource(R.string.players) -> { diff --git a/app/src/main/java/io/github/yamin8000/dooz/ui/Shapes.kt b/app/src/main/java/io/github/yamin8000/dooz/ui/Shapes.kt index e400c90..765ee59 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/ui/Shapes.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/ui/Shapes.kt @@ -25,13 +25,14 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.GenericShape import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ContentAlpha import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable @@ -39,10 +40,16 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.geometry.Rect import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.* +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Outline +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.graphics.PathOperation +import androidx.compose.ui.graphics.RectangleShape +import androidx.compose.ui.graphics.Shape import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp @@ -124,44 +131,58 @@ fun ClickableShapes( shapes: List, lastSelectedShape: Shape? = null, size: Dp = 30.dp, - backgroundColor: Color = MaterialTheme.colorScheme.secondary, header: (@Composable () -> Unit) = {}, onShapeSelected: (Shape) -> Unit ) { + val disabled = ContentAlpha.disabled + val high = ContentAlpha.high + val selectedIndex = remember { mutableStateOf(-1) } - val colors = remember { mutableStateOf(listOf()) } - colors.value = buildList { + val alphas = remember { mutableStateOf(listOf()) } + alphas.value = buildList { shapes.forEach { if (it == lastSelectedShape) - add(backgroundColor.copy(alpha = 0.5f)) - else add(backgroundColor) + add(disabled) + else add(high) } } - LazyRow( + Row( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.spacedBy(4.dp) ) { - itemsIndexed(shapes) { index, shape -> - ClickableShape( - shape = shape, - backgroundColor = colors.value[index], - size = size + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(32.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + header() + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterHorizontally) ) { - selectedIndex.value = index - if (selectedIndex.value != -1 && selectedIndex.value == index) { - onShapeSelected(shapes[index]) - colors.value = buildList { - for (i in shapes.indices) { - if (i == selectedIndex.value) - add(backgroundColor.copy(alpha = 0.5f)) - else add(backgroundColor) + shapes.forEachIndexed { index, shape -> + ClickableShape( + shape = shape, + modifier = Modifier.alpha(alphas.value[index]), + size = size + ) { + selectedIndex.value = index + if (selectedIndex.value != -1 && selectedIndex.value == index) { + onShapeSelected(shapes[index]) + alphas.value = buildList { + for (i in shapes.indices) { + if (i == selectedIndex.value) + add(disabled) + else add(high) + } + } } } } } } - item { header() } } } diff --git a/app/src/main/java/io/github/yamin8000/dooz/ui/composables/Composables.kt b/app/src/main/java/io/github/yamin8000/dooz/ui/composables/Composables.kt index fcdba9a..ca23d57 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/ui/composables/Composables.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/ui/composables/Composables.kt @@ -23,7 +23,6 @@ package io.github.yamin8000.dooz.ui.composables import android.app.Activity import android.content.Context import android.content.ContextWrapper -import android.content.res.Configuration import androidx.compose.animation.core.* import androidx.compose.foundation.* import androidx.compose.foundation.interaction.MutableInteractionSource @@ -35,7 +34,6 @@ import androidx.compose.foundation.selection.selectable import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.twotone.ArrowBack -import androidx.compose.material.icons.twotone.ArrowDropDownCircle import androidx.compose.material.ripple.rememberRipple import androidx.compose.material3.* import androidx.compose.runtime.* @@ -48,112 +46,16 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.github.yamin8000.dooz.R -import io.github.yamin8000.dooz.ui.DefaultCornerShape -import io.github.yamin8000.dooz.ui.theme.PreviewTheme - -//@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true) -@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true) -@Composable -private fun RadioGroupPreview() { - PreviewTheme { - Card { - RadioGroup( - columns = GridCells.Fixed(3), - options = listOf("On", "Off", "System Default"), - currentOption = "On", - onOptionChange = {}, - optionStringProvider = { it } - ) - } - } -} - -//@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true) -//@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO, showBackground = true) -@Composable -private fun SwitchWithTextPreview() { - PreviewTheme { - SwitchWithText( - caption = "Hello", - checked = true, - onCheckedChange = {} - ) - } -} - -@Composable -fun SettingsItemCard( - modifier: Modifier = Modifier, - columnModifier: Modifier = Modifier, - title: String, - content: @Composable ColumnScope.() -> Unit -) { - Column( - verticalArrangement = Arrangement.spacedBy(4.dp), - horizontalAlignment = Alignment.Start, - ) { - PersianText( - text = title, - fontSize = 18.sp, - color = MaterialTheme.colorScheme.primary - ) - Card( - modifier = modifier, - shape = DefaultCornerShape - ) { - Column( - modifier = columnModifier.padding(16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - horizontalAlignment = Alignment.CenterHorizontally, - content = { content() } - ) - } - } -} - -@Composable -fun SettingsItem( - modifier: Modifier = Modifier, - onClick: () -> Unit, - content: @Composable RowScope.() -> Unit -) { - val interactionSource = remember { MutableInteractionSource() } - Box( - modifier = modifier.clickable( - interactionSource = interactionSource, - indication = LocalIndication.current, - onClick = onClick, - ), - content = { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start), - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(vertical = 16.dp), - content = content - ) - Icon( - imageVector = Icons.TwoTone.ArrowDropDownCircle, - contentDescription = "" - ) - } - } - ) -} @OptIn(ExperimentalFoundationApi::class) @Composable @@ -186,7 +88,6 @@ fun ButtonWithIcon( border: BorderStroke? = null, contentPadding: PaddingValues = ButtonDefaults.ContentPadding, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, - space: Dp = 8.dp, icon: @Composable () -> Unit, content: @Composable RowScope.() -> Unit, ) { @@ -201,9 +102,15 @@ fun ButtonWithIcon( contentPadding = contentPadding, interactionSource = interactionSource, content = { - content() - Spacer(modifier = Modifier.width(space)) - icon() + Row( + modifier = modifier.fillMaxSize(), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp, Alignment.CenterHorizontally) + ) { + if (isFontScaleNormal()) + icon() + content() + } } ) } @@ -259,7 +166,7 @@ fun SwitchWithText( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { - PersianText(caption) + PersianText(text = caption) Switch( checked = checked, onCheckedChange = null @@ -353,7 +260,6 @@ fun ClickableIcon( @Composable fun InfoCard( modifier: Modifier = Modifier, - columnModifier: Modifier = Modifier, contentPadding: Dp = 8.dp, elementVerticalSpacing: Dp = 8.dp, horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, @@ -370,7 +276,6 @@ fun InfoCard( ) }, modifier = modifier, - columnModifier = columnModifier, contentPadding = contentPadding, elementVerticalSpacing = elementVerticalSpacing, horizontalAlignment = horizontalAlignment, @@ -383,7 +288,6 @@ fun InfoCard( @Composable fun InfoCard( modifier: Modifier = Modifier, - columnModifier: Modifier = Modifier, contentPadding: Dp = 8.dp, elementVerticalSpacing: Dp = 8.dp, horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, @@ -395,9 +299,11 @@ fun InfoCard( modifier = modifier, ) { Column( - modifier = columnModifier.padding(contentPadding), horizontalAlignment = horizontalAlignment, - verticalArrangement = Arrangement.spacedBy(elementVerticalSpacing) + verticalArrangement = Arrangement.spacedBy(elementVerticalSpacing), + modifier = Modifier + .padding(contentPadding) + .fillMaxWidth() ) { header() content() @@ -503,4 +409,7 @@ fun ScaffoldWithTitle( ) } ) -} \ No newline at end of file +} + +@Composable +fun isFontScaleNormal() = LocalDensity.current.fontScale <= 1.0f \ No newline at end of file diff --git a/app/src/main/java/io/github/yamin8000/dooz/ui/composables/Texts.kt b/app/src/main/java/io/github/yamin8000/dooz/ui/composables/Texts.kt index 4ab0a1a..5c3539b 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/ui/composables/Texts.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/ui/composables/Texts.kt @@ -143,4 +143,15 @@ fun PersianText( style = localStyle ) } +} + +@Composable +fun SingleLinePersianText( + text: String +) { + PersianText( + text = text, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) } \ No newline at end of file diff --git a/app/src/main/java/io/github/yamin8000/dooz/ui/theme/Theme.kt b/app/src/main/java/io/github/yamin8000/dooz/ui/theme/Theme.kt index 1d7bf39..63480c5 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/ui/theme/Theme.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/ui/theme/Theme.kt @@ -23,12 +23,20 @@ package io.github.yamin8000.dooz.ui.theme import android.app.Activity import android.os.Build import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.* +import androidx.compose.material3.ColorScheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.SideEffect import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalView +import androidx.compose.ui.unit.Density import androidx.core.view.WindowCompat private val LightColors = lightColorScheme( @@ -99,15 +107,20 @@ fun DoozTheme( isDynamicColor: Boolean, content: @Composable () -> Unit ) { - val isDynamicColorReadyDevice = isDynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S + //CompositionLocalProvider(LocalDensity provides Density(LocalDensity.current.density, 1f)) {} + + val isDynamicColorReadyDevice = + isDynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S val colors = when { isDynamicColorReadyDevice && isDarkTheme -> { dynamicDarkColorScheme(LocalContext.current).injectBrandColors(DarkColors) } + isDynamicColorReadyDevice && !isDarkTheme -> { dynamicLightColorScheme(LocalContext.current).injectBrandColors(LightColors) } + isDarkTheme -> DarkColors else -> LightColors } @@ -117,7 +130,8 @@ fun DoozTheme( SideEffect { activity.window.statusBarColor = colors.surface.toArgb() activity.window.navigationBarColor = colors.surface.toArgb() - val wic = WindowCompat.getInsetsController(activity.window, activity.window.decorView) + val wic = + WindowCompat.getInsetsController(activity.window, activity.window.decorView) wic.isAppearanceLightStatusBars = !isDarkTheme wic.isAppearanceLightNavigationBars = !isDarkTheme } diff --git a/app/src/main/java/io/github/yamin8000/dooz/util/Constants.kt b/app/src/main/java/io/github/yamin8000/dooz/util/Constants.kt index 5c1b013..631d47c 100644 --- a/app/src/main/java/io/github/yamin8000/dooz/util/Constants.kt +++ b/app/src/main/java/io/github/yamin8000/dooz/util/Constants.kt @@ -51,7 +51,7 @@ object Constants { val difficulties = listOf(AiDifficulty.Easy, AiDifficulty.Medium, AiDifficulty.Hard) - val aiPlayDelayRange = 250L..750L + val aiPlayDelayRange = 350L..750L val diceRange = 1..6 diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index eecaa77..d1b017e 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -29,7 +29,7 @@ اندازه زمین بازی نام بازیکن ها ذخیره - شروع بازی + بازی جدید نوبت بازیکن بازی مساوی شد اسم بازیکن اول diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 984796e..72a298a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,7 +28,7 @@ Game\'s board size Players\' names Save - Start game + New game Player\'s turn Game\'s drew First player\'s name @@ -59,7 +59,7 @@ Effects On Android 12+ your app theme colors are based on your home screen background Sounds - Haptic feedback (vibrations) + Vibrations Undo Data saved Players\' names cannot be empty @@ -74,4 +74,5 @@ Easy Medium Hard + https://yamins.ir \ No newline at end of file