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.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
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.Icons
import androidx.compose.material.icons.filled.ArrowDownward import androidx.compose.material.icons.filled.ArrowDownward
import androidx.compose.material.icons.filled.ArrowUpward 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.CheckBox
import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank
import androidx.compose.material.icons.rounded.DisabledByDefault 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.Checkbox
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton 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.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf 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.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.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.res.stringResource
import androidx.compose.ui.unit.dp 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.Manga
import eu.kanade.domain.manga.model.TriStateFilter import eu.kanade.domain.manga.model.TriStateFilter
import eu.kanade.presentation.components.AdaptiveSheet import eu.kanade.presentation.components.TabbedDialog
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.tachiyomi.R import eu.kanade.tachiyomi.R
import kotlinx.coroutines.launch
@Composable @Composable
fun ChapterSettingsDialog( fun ChapterSettingsDialog(
@ -72,41 +51,6 @@ fun ChapterSettingsDialog(
onDisplayModeChanged: (Long) -> Unit, onDisplayModeChanged: (Long) -> Unit,
onSetAsDefault: (applyToExistingManga: Boolean) -> 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) } var showSetAsDefaultDialog by rememberSaveable { mutableStateOf(false) }
if (showSetAsDefaultDialog) { if (showSetAsDefaultDialog) {
SetAsDefaultDialog( SetAsDefaultDialog(
@ -115,56 +59,23 @@ private fun ChapterSettingsDialogImpl(
) )
} }
Column { TabbedDialog(
Row { onDismissRequest = onDismissRequest,
TabRow( tabTitles = listOf(
modifier = Modifier.weight(1f), stringResource(R.string.action_filter),
selectedTabIndex = pagerState.currentPage, stringResource(R.string.action_sort),
indicator = { TabIndicator(it[pagerState.currentPage]) }, stringResource(R.string.action_display),
divider = {}, ),
) { tabOverflowMenuContent = { closeMenu ->
tabTitles.fastForEachIndexed { i, s -> DropdownMenuItem(
val selected = pagerState.currentPage == i text = { Text(stringResource(R.string.set_chapter_settings_as_default)) },
Tab( onClick = {
selected = selected, showSetAsDefaultDialog = true
onClick = { scope.launch { pagerState.animateScrollToPage(i) } }, closeMenu()
text = {
Text(
text = s,
color = if (selected) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.onSurfaceVariant
}, },
) )
}, },
) ) { contentPadding, page ->
}
}
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
}
}
},
) {
when (page) { when (page) {
0 -> { 0 -> {
val forceDownloaded = manga?.forceDownloaded() == true val forceDownloaded = manga?.forceDownloaded() == true
@ -182,13 +93,16 @@ private fun ChapterSettingsDialogImpl(
onBookmarkedFilterChanged = onBookmarkedFilterChanged, onBookmarkedFilterChanged = onBookmarkedFilterChanged,
) )
} }
1 -> SortPage( 1 -> {
SortPage(
contentPadding = contentPadding, contentPadding = contentPadding,
sortingMode = manga?.sorting ?: 0, sortingMode = manga?.sorting ?: 0,
sortDescending = manga?.sortDescending() ?: false, sortDescending = manga?.sortDescending() ?: false,
onItemSelected = onSortModeChanged, onItemSelected = onSortModeChanged,
) )
2 -> DisplayPage( }
2 -> {
DisplayPage(
contentPadding = contentPadding, contentPadding = contentPadding,
displayMode = manga?.displayMode ?: 0, displayMode = manga?.displayMode ?: 0,
onItemSelected = onDisplayModeChanged, onItemSelected = onDisplayModeChanged,
@ -196,7 +110,6 @@ private fun ChapterSettingsDialogImpl(
} }
} }
} }
}
} }
@Composable @Composable
@ -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 @Composable
private fun FilterPage( private fun FilterPage(
contentPadding: PaddingValues, contentPadding: PaddingValues,
@ -470,19 +356,3 @@ private fun DisplayPageItem(
private val HorizontalPadding = 24.dp private val HorizontalPadding = 24.dp
private val VerticalPadding = 8.dp private val VerticalPadding = 8.dp
@ThemePreviews
@Composable
private fun ChapterSettingsDialogPreview() {
TachiyomiTheme {
Surface {
ChapterSettingsDialogImpl(
onDownloadFilterChanged = {},
onUnreadFilterChanged = {},
onBookmarkedFilterChanged = {},
onSortModeChanged = {},
onDisplayModeChanged = {},
) {}
}
}
}