diff --git a/app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt b/app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt new file mode 100644 index 0000000000..e881979651 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/components/SettingsItems.kt @@ -0,0 +1,128 @@ +package eu.kanade.presentation.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDownward +import androidx.compose.material.icons.filled.ArrowUpward +import androidx.compose.material.icons.rounded.CheckBox +import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank +import androidx.compose.material.icons.rounded.DisabledByDefault +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import eu.kanade.domain.manga.model.TriStateFilter + +@Composable +fun TriStateItem( + label: String, + state: TriStateFilter, + onClick: ((TriStateFilter) -> Unit)?, +) { + Row( + modifier = Modifier + .clickable( + enabled = onClick != null, + onClick = { + when (state) { + TriStateFilter.DISABLED -> onClick?.invoke(TriStateFilter.ENABLED_IS) + TriStateFilter.ENABLED_IS -> onClick?.invoke(TriStateFilter.ENABLED_NOT) + TriStateFilter.ENABLED_NOT -> onClick?.invoke(TriStateFilter.DISABLED) + } + }, + ) + .fillMaxWidth() + .padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + Icon( + imageVector = when (state) { + TriStateFilter.DISABLED -> Icons.Rounded.CheckBoxOutlineBlank + TriStateFilter.ENABLED_IS -> Icons.Rounded.CheckBox + TriStateFilter.ENABLED_NOT -> Icons.Rounded.DisabledByDefault + }, + contentDescription = null, + tint = if (state == TriStateFilter.DISABLED) { + MaterialTheme.colorScheme.onSurfaceVariant + } else { + MaterialTheme.colorScheme.primary + }, + ) + Text( + text = label, + style = MaterialTheme.typography.bodyMedium, + ) + } +} + +@Composable +fun SortItem( + label: String, + sortDescending: Boolean?, + onClick: () -> Unit, +) { + val arrowIcon = when (sortDescending) { + true -> Icons.Default.ArrowDownward + false -> Icons.Default.ArrowUpward + null -> null + } + + Row( + modifier = Modifier + .clickable(onClick = onClick) + .fillMaxWidth() + .padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + if (arrowIcon != null) { + Icon( + imageVector = arrowIcon, + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + ) + } else { + Spacer(modifier = Modifier.size(24.dp)) + } + Text( + text = label, + style = MaterialTheme.typography.bodyMedium, + ) + } +} + +@Composable +fun RadioItem( + label: String, + selected: Boolean, + onClick: () -> Unit, +) { + Row( + modifier = Modifier + .clickable(onClick = onClick) + .fillMaxWidth() + .padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(24.dp), + ) { + RadioButton( + selected = selected, + onClick = null, + ) + Text( + text = label, + style = MaterialTheme.typography.bodyMedium, + ) + } +} diff --git a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt index dd2cffe5ec..760a7aa1d7 100644 --- a/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/components/TabbedDialog.kt @@ -32,6 +32,11 @@ import androidx.compose.ui.util.fastForEachIndexed import eu.kanade.tachiyomi.R import kotlinx.coroutines.launch +object TabbedDialogPaddings { + val Horizontal = 24.dp + val Vertical = 8.dp +} + @Composable fun TabbedDialog( onDismissRequest: () -> Unit, @@ -72,9 +77,7 @@ fun TabbedDialog( } } - tabOverflowMenuContent?.let { - MoreMenu(tabOverflowMenuContent) - } + tabOverflowMenuContent?.let { MoreMenu(it) } } Divider() diff --git a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt index 66ca9e3755..e83a7856cb 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt @@ -3,26 +3,15 @@ package eu.kanade.presentation.manga import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDownward -import androidx.compose.material.icons.filled.ArrowUpward -import androidx.compose.material.icons.rounded.CheckBox -import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank -import androidx.compose.material.icons.rounded.DisabledByDefault import androidx.compose.material3.AlertDialog import androidx.compose.material3.Checkbox import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.RadioButton import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -32,12 +21,15 @@ 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.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.TriStateFilter +import eu.kanade.presentation.components.RadioItem +import eu.kanade.presentation.components.SortItem import eu.kanade.presentation.components.TabbedDialog +import eu.kanade.presentation.components.TabbedDialogPaddings +import eu.kanade.presentation.components.TriStateItem import eu.kanade.tachiyomi.R @Composable @@ -76,37 +68,41 @@ fun ChapterSettingsDialog( ) }, ) { contentPadding, page -> - when (page) { - 0 -> { - val forceDownloaded = manga?.forceDownloaded() == true - FilterPage( - contentPadding = contentPadding, - downloadFilter = if (forceDownloaded) { - TriStateFilter.ENABLED_NOT - } else { - manga?.downloadedFilter - } ?: TriStateFilter.DISABLED, - onDownloadFilterChanged = onDownloadFilterChanged.takeUnless { forceDownloaded }, - unreadFilter = manga?.unreadFilter ?: TriStateFilter.DISABLED, - onUnreadFilterChanged = onUnreadFilterChanged, - bookmarkedFilter = manga?.bookmarkedFilter ?: TriStateFilter.DISABLED, - onBookmarkedFilterChanged = onBookmarkedFilterChanged, - ) - } - 1 -> { - SortPage( - contentPadding = contentPadding, - sortingMode = manga?.sorting ?: 0, - sortDescending = manga?.sortDescending() ?: false, - onItemSelected = onSortModeChanged, - ) - } - 2 -> { - DisplayPage( - contentPadding = contentPadding, - displayMode = manga?.displayMode ?: 0, - onItemSelected = onDisplayModeChanged, - ) + Column( + modifier = Modifier + .padding(contentPadding) + .padding(vertical = TabbedDialogPaddings.Vertical) + .verticalScroll(rememberScrollState()), + ) { + when (page) { + 0 -> { + val forceDownloaded = manga?.forceDownloaded() == true + FilterPage( + downloadFilter = if (forceDownloaded) { + TriStateFilter.ENABLED_NOT + } else { + manga?.downloadedFilter + } ?: TriStateFilter.DISABLED, + onDownloadFilterChanged = onDownloadFilterChanged.takeUnless { forceDownloaded }, + unreadFilter = manga?.unreadFilter ?: TriStateFilter.DISABLED, + onUnreadFilterChanged = onUnreadFilterChanged, + bookmarkedFilter = manga?.bookmarkedFilter ?: TriStateFilter.DISABLED, + onBookmarkedFilterChanged = onBookmarkedFilterChanged, + ) + } + 1 -> { + SortPage( + sortingMode = manga?.sorting ?: 0, + sortDescending = manga?.sortDescending() ?: false, + onItemSelected = onSortModeChanged, + ) + } + 2 -> { + DisplayPage( + displayMode = manga?.displayMode ?: 0, + onItemSelected = onDisplayModeChanged, + ) + } } } } @@ -152,6 +148,7 @@ private fun SetAsDefaultDialog( TextButton( onClick = { onConfirmed(optionalChecked) + onDismissRequest() }, ) { Text(text = stringResource(android.R.string.ok)) @@ -161,8 +158,7 @@ private fun SetAsDefaultDialog( } @Composable -private fun FilterPage( - contentPadding: PaddingValues, +private fun ColumnScope.FilterPage( downloadFilter: TriStateFilter, onDownloadFilterChanged: ((TriStateFilter) -> Unit)?, unreadFilter: TriStateFilter, @@ -170,189 +166,59 @@ private fun FilterPage( bookmarkedFilter: TriStateFilter, onBookmarkedFilterChanged: (TriStateFilter) -> Unit, ) { - Column( - modifier = Modifier - .padding(vertical = VerticalPadding) - .padding(contentPadding) - .verticalScroll(rememberScrollState()), - ) { - FilterPageItem( - label = stringResource(R.string.label_downloaded), - state = downloadFilter, - onClick = onDownloadFilterChanged, - ) - FilterPageItem( - label = stringResource(R.string.action_filter_unread), - state = unreadFilter, - onClick = onUnreadFilterChanged, - ) - FilterPageItem( - label = stringResource(R.string.action_filter_bookmarked), - state = bookmarkedFilter, - onClick = onBookmarkedFilterChanged, - ) - } + TriStateItem( + label = stringResource(R.string.label_downloaded), + state = downloadFilter, + onClick = onDownloadFilterChanged, + ) + TriStateItem( + label = stringResource(R.string.action_filter_unread), + state = unreadFilter, + onClick = onUnreadFilterChanged, + ) + TriStateItem( + label = stringResource(R.string.action_filter_bookmarked), + state = bookmarkedFilter, + onClick = onBookmarkedFilterChanged, + ) } @Composable -private fun FilterPageItem( - label: String, - state: TriStateFilter, - onClick: ((TriStateFilter) -> Unit)?, -) { - Row( - modifier = Modifier - .clickable( - enabled = onClick != null, - onClick = { - when (state) { - TriStateFilter.DISABLED -> onClick?.invoke(TriStateFilter.ENABLED_IS) - TriStateFilter.ENABLED_IS -> onClick?.invoke(TriStateFilter.ENABLED_NOT) - TriStateFilter.ENABLED_NOT -> onClick?.invoke(TriStateFilter.DISABLED) - } - }, - ) - .fillMaxWidth() - .padding(horizontal = HorizontalPadding, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(24.dp), - ) { - Icon( - imageVector = when (state) { - TriStateFilter.DISABLED -> Icons.Rounded.CheckBoxOutlineBlank - TriStateFilter.ENABLED_IS -> Icons.Rounded.CheckBox - TriStateFilter.ENABLED_NOT -> Icons.Rounded.DisabledByDefault - }, - contentDescription = null, - tint = if (state == TriStateFilter.DISABLED) { - MaterialTheme.colorScheme.onSurfaceVariant - } else { - MaterialTheme.colorScheme.primary - }, - ) - Text( - text = label, - style = MaterialTheme.typography.bodyMedium, - ) - } -} - -@Composable -private fun SortPage( - contentPadding: PaddingValues, +private fun ColumnScope.SortPage( sortingMode: Long, sortDescending: Boolean, onItemSelected: (Long) -> Unit, ) { - Column( - modifier = Modifier - .padding(contentPadding) - .padding(vertical = VerticalPadding) - .verticalScroll(rememberScrollState()), - ) { - val arrowIcon = if (sortDescending) { - Icons.Default.ArrowDownward - } else { - Icons.Default.ArrowUpward - } - - SortPageItem( - label = stringResource(R.string.sort_by_source), - statusIcon = arrowIcon.takeIf { sortingMode == Manga.CHAPTER_SORTING_SOURCE }, - onClick = { onItemSelected(Manga.CHAPTER_SORTING_SOURCE) }, - ) - SortPageItem( - label = stringResource(R.string.sort_by_number), - statusIcon = arrowIcon.takeIf { sortingMode == Manga.CHAPTER_SORTING_NUMBER }, - onClick = { onItemSelected(Manga.CHAPTER_SORTING_NUMBER) }, - ) - SortPageItem( - label = stringResource(R.string.sort_by_upload_date), - statusIcon = arrowIcon.takeIf { sortingMode == Manga.CHAPTER_SORTING_UPLOAD_DATE }, - onClick = { onItemSelected(Manga.CHAPTER_SORTING_UPLOAD_DATE) }, - ) - } + SortItem( + label = stringResource(R.string.sort_by_source), + sortDescending = sortDescending.takeIf { sortingMode == Manga.CHAPTER_SORTING_SOURCE }, + onClick = { onItemSelected(Manga.CHAPTER_SORTING_SOURCE) }, + ) + SortItem( + label = stringResource(R.string.sort_by_number), + sortDescending = sortDescending.takeIf { sortingMode == Manga.CHAPTER_SORTING_NUMBER }, + onClick = { onItemSelected(Manga.CHAPTER_SORTING_NUMBER) }, + ) + SortItem( + label = stringResource(R.string.sort_by_upload_date), + sortDescending = sortDescending.takeIf { sortingMode == Manga.CHAPTER_SORTING_UPLOAD_DATE }, + onClick = { onItemSelected(Manga.CHAPTER_SORTING_UPLOAD_DATE) }, + ) } @Composable -private fun SortPageItem( - label: String, - statusIcon: ImageVector?, - onClick: () -> Unit, -) { - Row( - modifier = Modifier - .clickable(onClick = onClick) - .fillMaxWidth() - .padding(horizontal = HorizontalPadding, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(24.dp), - ) { - if (statusIcon != null) { - Icon( - imageVector = statusIcon, - contentDescription = null, - tint = MaterialTheme.colorScheme.primary, - ) - } else { - Spacer(modifier = Modifier.size(24.dp)) - } - Text( - text = label, - style = MaterialTheme.typography.bodyMedium, - ) - } -} - -@Composable -private fun DisplayPage( - contentPadding: PaddingValues, +private fun ColumnScope.DisplayPage( displayMode: Long, onItemSelected: (Long) -> Unit, ) { - Column( - modifier = Modifier - .padding(contentPadding) - .padding(vertical = VerticalPadding) - .verticalScroll(rememberScrollState()), - ) { - DisplayPageItem( - label = stringResource(R.string.show_title), - selected = displayMode == Manga.CHAPTER_DISPLAY_NAME, - onClick = { onItemSelected(Manga.CHAPTER_DISPLAY_NAME) }, - ) - DisplayPageItem( - label = stringResource(R.string.show_chapter_number), - selected = displayMode == Manga.CHAPTER_DISPLAY_NUMBER, - onClick = { onItemSelected(Manga.CHAPTER_DISPLAY_NUMBER) }, - ) - } + RadioItem( + label = stringResource(R.string.show_title), + selected = displayMode == Manga.CHAPTER_DISPLAY_NAME, + onClick = { onItemSelected(Manga.CHAPTER_DISPLAY_NAME) }, + ) + RadioItem( + label = stringResource(R.string.show_chapter_number), + selected = displayMode == Manga.CHAPTER_DISPLAY_NUMBER, + onClick = { onItemSelected(Manga.CHAPTER_DISPLAY_NUMBER) }, + ) } - -@Composable -private fun DisplayPageItem( - label: String, - selected: Boolean, - onClick: () -> Unit, -) { - Row( - modifier = Modifier - .clickable(onClick = onClick) - .fillMaxWidth() - .padding(horizontal = HorizontalPadding, vertical = 12.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(24.dp), - ) { - RadioButton( - selected = selected, - onClick = null, - ) - Text( - text = label, - style = MaterialTheme.typography.bodyMedium, - ) - } -} - -private val HorizontalPadding = 24.dp -private val VerticalPadding = 8.dp