Abstract ChapterSettingsDialog for reuse elsewhere

This commit is contained in:
arkon 2022-12-08 23:13:09 -05:00
parent 1009e15aa6
commit a61e2799db
2 changed files with 173 additions and 178 deletions

View file

@ -0,0 +1,125 @@
package eu.kanade.presentation.components
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.heightIn
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
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.rememberCoroutineScope
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.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import eu.kanade.tachiyomi.R
import kotlinx.coroutines.launch
@Composable
fun TabbedDialog(
onDismissRequest: () -> Unit,
tabTitles: List<String>,
tabOverflowMenuContent: (@Composable ColumnScope.(() -> Unit) -> Unit)? = null,
content: @Composable (PaddingValues, Int) -> Unit,
) {
AdaptiveSheet(
onDismissRequest = onDismissRequest,
) { contentPadding ->
val scope = rememberCoroutineScope()
val pagerState = rememberPagerState()
Column {
Row {
TabRow(
modifier = Modifier.weight(1f),
selectedTabIndex = pagerState.currentPage,
indicator = { TabIndicator(it[pagerState.currentPage]) },
divider = {},
) {
tabTitles.fastForEachIndexed { i, tab ->
val selected = pagerState.currentPage == i
Tab(
selected = selected,
onClick = { scope.launch { pagerState.animateScrollToPage(i) } },
text = {
Text(
text = tab,
color = if (selected) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
)
},
)
}
}
tabOverflowMenuContent?.let {
MoreMenu(tabOverflowMenuContent)
}
}
Divider()
val density = LocalDensity.current
var largestHeight by rememberSaveable { mutableStateOf(0f) }
HorizontalPager(
modifier = Modifier.heightIn(min = largestHeight.dp),
count = tabTitles.size,
state = pagerState,
verticalAlignment = Alignment.Top,
) { page ->
Box(
modifier = Modifier.onSizeChanged {
with(density) {
val heightDp = it.height.toDp()
if (heightDp.value > largestHeight) {
largestHeight = heightDp.value
}
}
},
) {
content(contentPadding, page)
}
}
}
}
}
@Composable
private fun MoreMenu(
content: @Composable ColumnScope.(() -> Unit) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
Box(modifier = Modifier.wrapContentSize(Alignment.TopStart)) {
IconButton(onClick = { expanded = true }) {
Icon(
imageVector = Icons.Default.MoreVert,
contentDescription = stringResource(R.string.label_more),
)
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
content { expanded = false }
}
}
}

View file

@ -2,22 +2,18 @@ package eu.kanade.presentation.manga
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.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
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.filled.MoreVert
import androidx.compose.material.icons.rounded.CheckBox
import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank
import androidx.compose.material.icons.rounded.DisabledByDefault
@ -25,41 +21,24 @@ import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Checkbox
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
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.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEachIndexed
import eu.kanade.domain.manga.model.Manga
import eu.kanade.domain.manga.model.TriStateFilter
import eu.kanade.presentation.components.AdaptiveSheet
import eu.kanade.presentation.components.Divider
import eu.kanade.presentation.components.DropdownMenu
import eu.kanade.presentation.components.HorizontalPager
import eu.kanade.presentation.components.TabIndicator
import eu.kanade.presentation.components.rememberPagerState
import eu.kanade.presentation.theme.TachiyomiTheme
import eu.kanade.presentation.util.ThemePreviews
import eu.kanade.presentation.components.TabbedDialog
import eu.kanade.tachiyomi.R
import kotlinx.coroutines.launch
@Composable
fun ChapterSettingsDialog(
@ -72,41 +51,6 @@ fun ChapterSettingsDialog(
onDisplayModeChanged: (Long) -> Unit,
onSetAsDefault: (applyToExistingManga: Boolean) -> Unit,
) {
AdaptiveSheet(
onDismissRequest = onDismissRequest,
) { contentPadding ->
ChapterSettingsDialogImpl(
manga = manga,
contentPadding = contentPadding,
onDownloadFilterChanged = onDownloadFilterChanged,
onUnreadFilterChanged = onUnreadFilterChanged,
onBookmarkedFilterChanged = onBookmarkedFilterChanged,
onSortModeChanged = onSortModeChanged,
onDisplayModeChanged = onDisplayModeChanged,
onSetAsDefault = onSetAsDefault,
)
}
}
@Composable
private fun ChapterSettingsDialogImpl(
manga: Manga? = null,
contentPadding: PaddingValues = PaddingValues(),
onDownloadFilterChanged: (TriStateFilter) -> Unit,
onUnreadFilterChanged: (TriStateFilter) -> Unit,
onBookmarkedFilterChanged: (TriStateFilter) -> Unit,
onSortModeChanged: (Long) -> Unit,
onDisplayModeChanged: (Long) -> Unit,
onSetAsDefault: (applyToExistingManga: Boolean) -> Unit,
) {
val scope = rememberCoroutineScope()
val tabTitles = listOf(
stringResource(R.string.action_filter),
stringResource(R.string.action_sort),
stringResource(R.string.action_display),
)
val pagerState = rememberPagerState()
var showSetAsDefaultDialog by rememberSaveable { mutableStateOf(false) }
if (showSetAsDefaultDialog) {
SetAsDefaultDialog(
@ -115,85 +59,54 @@ private fun ChapterSettingsDialogImpl(
)
}
Column {
Row {
TabRow(
modifier = Modifier.weight(1f),
selectedTabIndex = pagerState.currentPage,
indicator = { TabIndicator(it[pagerState.currentPage]) },
divider = {},
) {
tabTitles.fastForEachIndexed { i, s ->
val selected = pagerState.currentPage == i
Tab(
selected = selected,
onClick = { scope.launch { pagerState.animateScrollToPage(i) } },
text = {
Text(
text = s,
color = if (selected) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onSurfaceVariant
},
)
},
)
}
}
MoreMenu(onSetAsDefault = { showSetAsDefaultDialog = true })
}
Divider()
val density = LocalDensity.current
var largestHeight by rememberSaveable { mutableStateOf(0f) }
HorizontalPager(
modifier = Modifier.heightIn(min = largestHeight.dp),
count = tabTitles.size,
state = pagerState,
verticalAlignment = Alignment.Top,
) { page ->
Box(
modifier = Modifier.onSizeChanged {
with(density) {
val heightDp = it.height.toDp()
if (heightDp.value > largestHeight) {
largestHeight = heightDp.value
}
}
TabbedDialog(
onDismissRequest = onDismissRequest,
tabTitles = listOf(
stringResource(R.string.action_filter),
stringResource(R.string.action_sort),
stringResource(R.string.action_display),
),
tabOverflowMenuContent = { closeMenu ->
DropdownMenuItem(
text = { Text(stringResource(R.string.set_chapter_settings_as_default)) },
onClick = {
showSetAsDefaultDialog = true
closeMenu()
},
) {
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,
)
}
)
},
) { 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,
)
}
}
}
@ -247,33 +160,6 @@ private fun SetAsDefaultDialog(
)
}
@Composable
private fun MoreMenu(
onSetAsDefault: () -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
Box(modifier = Modifier.wrapContentSize(Alignment.TopStart)) {
IconButton(onClick = { expanded = true }) {
Icon(
imageVector = Icons.Default.MoreVert,
contentDescription = stringResource(R.string.label_more),
)
}
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.set_chapter_settings_as_default)) },
onClick = {
onSetAsDefault()
expanded = false
},
)
}
}
}
@Composable
private fun FilterPage(
contentPadding: PaddingValues,
@ -470,19 +356,3 @@ private fun DisplayPageItem(
private val HorizontalPadding = 24.dp
private val VerticalPadding = 8.dp
@ThemePreviews
@Composable
private fun ChapterSettingsDialogPreview() {
TachiyomiTheme {
Surface {
ChapterSettingsDialogImpl(
onDownloadFilterChanged = {},
onUnreadFilterChanged = {},
onBookmarkedFilterChanged = {},
onSortModeChanged = {},
onDisplayModeChanged = {},
) {}
}
}
}