Pull out settings sheet items as reusable composables

This commit is contained in:
arkon 2022-12-09 22:23:26 -05:00
parent 8937e22ce4
commit 7cbe18d325
3 changed files with 218 additions and 221 deletions

View file

@ -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,
)
}
}

View file

@ -32,6 +32,11 @@ import androidx.compose.ui.util.fastForEachIndexed
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
object TabbedDialogPaddings {
val Horizontal = 24.dp
val Vertical = 8.dp
}
@Composable @Composable
fun TabbedDialog( fun TabbedDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
@ -72,9 +77,7 @@ fun TabbedDialog(
} }
} }
tabOverflowMenuContent?.let { tabOverflowMenuContent?.let { MoreMenu(it) }
MoreMenu(tabOverflowMenuContent)
}
} }
Divider() Divider()

View file

@ -3,26 +3,15 @@ package eu.kanade.presentation.manga
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column 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.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll 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.AlertDialog
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.DropdownMenuItem 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.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -32,12 +21,15 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.TriStateFilter 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.TabbedDialog
import eu.kanade.presentation.components.TabbedDialogPaddings
import eu.kanade.presentation.components.TriStateItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
@Composable @Composable
@ -76,37 +68,41 @@ fun ChapterSettingsDialog(
) )
}, },
) { contentPadding, page -> ) { contentPadding, page ->
when (page) { Column(
0 -> { modifier = Modifier
val forceDownloaded = manga?.forceDownloaded() == true .padding(contentPadding)
FilterPage( .padding(vertical = TabbedDialogPaddings.Vertical)
contentPadding = contentPadding, .verticalScroll(rememberScrollState()),
downloadFilter = if (forceDownloaded) { ) {
TriStateFilter.ENABLED_NOT when (page) {
} else { 0 -> {
manga?.downloadedFilter val forceDownloaded = manga?.forceDownloaded() == true
} ?: TriStateFilter.DISABLED, FilterPage(
onDownloadFilterChanged = onDownloadFilterChanged.takeUnless { forceDownloaded }, downloadFilter = if (forceDownloaded) {
unreadFilter = manga?.unreadFilter ?: TriStateFilter.DISABLED, TriStateFilter.ENABLED_NOT
onUnreadFilterChanged = onUnreadFilterChanged, } else {
bookmarkedFilter = manga?.bookmarkedFilter ?: TriStateFilter.DISABLED, manga?.downloadedFilter
onBookmarkedFilterChanged = onBookmarkedFilterChanged, } ?: TriStateFilter.DISABLED,
) onDownloadFilterChanged = onDownloadFilterChanged.takeUnless { forceDownloaded },
} unreadFilter = manga?.unreadFilter ?: TriStateFilter.DISABLED,
1 -> { onUnreadFilterChanged = onUnreadFilterChanged,
SortPage( bookmarkedFilter = manga?.bookmarkedFilter ?: TriStateFilter.DISABLED,
contentPadding = contentPadding, onBookmarkedFilterChanged = onBookmarkedFilterChanged,
sortingMode = manga?.sorting ?: 0, )
sortDescending = manga?.sortDescending() ?: false, }
onItemSelected = onSortModeChanged, 1 -> {
) SortPage(
} sortingMode = manga?.sorting ?: 0,
2 -> { sortDescending = manga?.sortDescending() ?: false,
DisplayPage( onItemSelected = onSortModeChanged,
contentPadding = contentPadding, )
displayMode = manga?.displayMode ?: 0, }
onItemSelected = onDisplayModeChanged, 2 -> {
) DisplayPage(
displayMode = manga?.displayMode ?: 0,
onItemSelected = onDisplayModeChanged,
)
}
} }
} }
} }
@ -152,6 +148,7 @@ private fun SetAsDefaultDialog(
TextButton( TextButton(
onClick = { onClick = {
onConfirmed(optionalChecked) onConfirmed(optionalChecked)
onDismissRequest()
}, },
) { ) {
Text(text = stringResource(android.R.string.ok)) Text(text = stringResource(android.R.string.ok))
@ -161,8 +158,7 @@ private fun SetAsDefaultDialog(
} }
@Composable @Composable
private fun FilterPage( private fun ColumnScope.FilterPage(
contentPadding: PaddingValues,
downloadFilter: TriStateFilter, downloadFilter: TriStateFilter,
onDownloadFilterChanged: ((TriStateFilter) -> Unit)?, onDownloadFilterChanged: ((TriStateFilter) -> Unit)?,
unreadFilter: TriStateFilter, unreadFilter: TriStateFilter,
@ -170,189 +166,59 @@ private fun FilterPage(
bookmarkedFilter: TriStateFilter, bookmarkedFilter: TriStateFilter,
onBookmarkedFilterChanged: (TriStateFilter) -> Unit, onBookmarkedFilterChanged: (TriStateFilter) -> Unit,
) { ) {
Column( TriStateItem(
modifier = Modifier label = stringResource(R.string.label_downloaded),
.padding(vertical = VerticalPadding) state = downloadFilter,
.padding(contentPadding) onClick = onDownloadFilterChanged,
.verticalScroll(rememberScrollState()), )
) { TriStateItem(
FilterPageItem( label = stringResource(R.string.action_filter_unread),
label = stringResource(R.string.label_downloaded), state = unreadFilter,
state = downloadFilter, onClick = onUnreadFilterChanged,
onClick = onDownloadFilterChanged, )
) TriStateItem(
FilterPageItem( label = stringResource(R.string.action_filter_bookmarked),
label = stringResource(R.string.action_filter_unread), state = bookmarkedFilter,
state = unreadFilter, onClick = onBookmarkedFilterChanged,
onClick = onUnreadFilterChanged, )
)
FilterPageItem(
label = stringResource(R.string.action_filter_bookmarked),
state = bookmarkedFilter,
onClick = onBookmarkedFilterChanged,
)
}
} }
@Composable @Composable
private fun FilterPageItem( private fun ColumnScope.SortPage(
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,
sortingMode: Long, sortingMode: Long,
sortDescending: Boolean, sortDescending: Boolean,
onItemSelected: (Long) -> Unit, onItemSelected: (Long) -> Unit,
) { ) {
Column( SortItem(
modifier = Modifier label = stringResource(R.string.sort_by_source),
.padding(contentPadding) sortDescending = sortDescending.takeIf { sortingMode == Manga.CHAPTER_SORTING_SOURCE },
.padding(vertical = VerticalPadding) onClick = { onItemSelected(Manga.CHAPTER_SORTING_SOURCE) },
.verticalScroll(rememberScrollState()), )
) { SortItem(
val arrowIcon = if (sortDescending) { label = stringResource(R.string.sort_by_number),
Icons.Default.ArrowDownward sortDescending = sortDescending.takeIf { sortingMode == Manga.CHAPTER_SORTING_NUMBER },
} else { onClick = { onItemSelected(Manga.CHAPTER_SORTING_NUMBER) },
Icons.Default.ArrowUpward )
} SortItem(
label = stringResource(R.string.sort_by_upload_date),
SortPageItem( sortDescending = sortDescending.takeIf { sortingMode == Manga.CHAPTER_SORTING_UPLOAD_DATE },
label = stringResource(R.string.sort_by_source), onClick = { onItemSelected(Manga.CHAPTER_SORTING_UPLOAD_DATE) },
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) },
)
}
} }
@Composable @Composable
private fun SortPageItem( private fun ColumnScope.DisplayPage(
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,
displayMode: Long, displayMode: Long,
onItemSelected: (Long) -> Unit, onItemSelected: (Long) -> Unit,
) { ) {
Column( RadioItem(
modifier = Modifier label = stringResource(R.string.show_title),
.padding(contentPadding) selected = displayMode == Manga.CHAPTER_DISPLAY_NAME,
.padding(vertical = VerticalPadding) onClick = { onItemSelected(Manga.CHAPTER_DISPLAY_NAME) },
.verticalScroll(rememberScrollState()), )
) { RadioItem(
DisplayPageItem( label = stringResource(R.string.show_chapter_number),
label = stringResource(R.string.show_title), selected = displayMode == Manga.CHAPTER_DISPLAY_NUMBER,
selected = displayMode == Manga.CHAPTER_DISPLAY_NAME, onClick = { onItemSelected(Manga.CHAPTER_DISPLAY_NUMBER) },
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) },
)
}
} }
@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