mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Improve Library Display, add Comfortable, Cover only, List, and configurable columns
This commit is contained in:
@@ -16,6 +16,14 @@ class LibraryPreferences(private val preferenceStore: PreferenceStore) {
|
||||
return preferenceStore.getJsonObject("display_mode", DisplayMode.CompactGrid, DisplayMode.serializer())
|
||||
}
|
||||
|
||||
fun gridColumns(): Preference<Int> {
|
||||
return preferenceStore.getInt("grid_columns", 0)
|
||||
}
|
||||
|
||||
fun gridSize(): Preference<Int> {
|
||||
return preferenceStore.getInt("grid_size", 160)
|
||||
}
|
||||
|
||||
fun showAllCategory(): Preference<Boolean> {
|
||||
return preferenceStore.getBoolean("show_all_category", false)
|
||||
}
|
||||
|
||||
@@ -6,13 +6,17 @@
|
||||
|
||||
package ca.gosyer.data.library.model
|
||||
|
||||
import ca.gosyer.i18n.MR
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
@Serializable
|
||||
enum class DisplayMode {
|
||||
CompactGrid,
|
||||
ComfortableGrid,
|
||||
List;
|
||||
enum class DisplayMode(@Transient val res: StringResource) {
|
||||
CompactGrid(MR.strings.display_compact),
|
||||
ComfortableGrid(MR.strings.display_comfortable),
|
||||
CoverOnlyGrid(MR.strings.display_cover_only),
|
||||
List(MR.strings.display_list);
|
||||
|
||||
companion object {
|
||||
val values = values()
|
||||
|
||||
@@ -158,6 +158,17 @@
|
||||
|
||||
<!-- Library Settings -->
|
||||
<string name="show_all_category">Show all category</string>
|
||||
<string name="display_mode">Display mode</string>
|
||||
<string name="display_compact">Compact grid</string>
|
||||
<string name="display_comfortable">Comfortable grid</string>
|
||||
<string name="display_cover_only">Cover-only grid</string>
|
||||
<string name="display_list">List</string>
|
||||
<string name="items_per_row">Items per row</string>
|
||||
<string name="items_per_row_sub">Columns: %1$d</string>
|
||||
<string name="items_per_row_sub_adaptive">Columns: Adaptive, Size: %1$d</string>
|
||||
<string name="adaptive">Adaptive</string>
|
||||
<string name="grid_columns">Columns</string>
|
||||
<string name="grid_size">Size</string>
|
||||
|
||||
<!-- Reader Settings -->
|
||||
<string name="reader_mode">Reader Mode</string>
|
||||
|
||||
@@ -29,6 +29,8 @@ class LibraryScreen : Screen {
|
||||
categories = vm.categories.collectAsState().value,
|
||||
selectedCategoryIndex = vm.selectedCategoryIndex.collectAsState().value,
|
||||
displayMode = vm.displayMode.collectAsState().value,
|
||||
gridColumns = vm.gridColumns.collectAsState().value,
|
||||
gridSize = vm.gridSize.collectAsState().value,
|
||||
isLoading = vm.isLoading.collectAsState().value,
|
||||
error = vm.error.collectAsState().value,
|
||||
query = vm.query.collectAsState().value,
|
||||
|
||||
@@ -84,6 +84,8 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
val selectedCategoryIndex = _selectedCategoryIndex.asStateFlow()
|
||||
|
||||
val displayMode = libraryPreferences.displayMode().stateIn(scope)
|
||||
val gridColumns = libraryPreferences.gridColumns().stateIn(scope)
|
||||
val gridSize = libraryPreferences.gridSize().stateIn(scope)
|
||||
|
||||
private val _isLoading = MutableStateFlow(true)
|
||||
val isLoading = _isLoading.asStateFlow()
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.ui.library.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.ui.base.components.VerticalScrollbar
|
||||
import ca.gosyer.ui.base.components.rememberScrollbarAdapter
|
||||
import ca.gosyer.uicore.components.MangaListItem
|
||||
import ca.gosyer.uicore.components.MangaListItemImage
|
||||
import ca.gosyer.uicore.components.MangaListItemTitle
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaList(
|
||||
library: List<Manga>,
|
||||
onClickManga: (Long) -> Unit,
|
||||
onRemoveMangaClicked: (Long) -> Unit
|
||||
) {
|
||||
Box {
|
||||
val state = rememberLazyListState()
|
||||
LazyColumn(
|
||||
state = state,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
items(library) { manga ->
|
||||
LibraryMangaListItem(
|
||||
modifier = Modifier.libraryMangaModifier(
|
||||
{ onClickManga(manga.id) },
|
||||
{ onRemoveMangaClicked(manga.id) }
|
||||
),
|
||||
manga = manga,
|
||||
unread = manga.unreadCount,
|
||||
downloaded = manga.downloadCount,
|
||||
)
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
rememberScrollbarAdapter(state),
|
||||
Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LibraryMangaListItem(
|
||||
modifier: Modifier,
|
||||
manga: Manga,
|
||||
unread: Int?,
|
||||
downloaded: Int?,
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
MangaListItem(
|
||||
modifier = modifier then Modifier
|
||||
.requiredHeight(56.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
) {
|
||||
MangaListItemImage(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(MaterialTheme.shapes.medium),
|
||||
cover = cover,
|
||||
contentDescription = manga.title
|
||||
)
|
||||
MangaListItemTitle(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 16.dp),
|
||||
text = manga.title,
|
||||
)
|
||||
LibraryMangaBadges(unread, downloaded)
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ fun LibraryPager(
|
||||
pagerState: PagerState,
|
||||
categories: List<Category>,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
getLibraryForPage: @Composable (Long) -> State<List<Manga>>,
|
||||
onClickManga: (Long) -> Unit,
|
||||
onRemoveMangaClicked: (Long) -> Unit
|
||||
@@ -32,17 +34,30 @@ fun LibraryPager(
|
||||
when (displayMode) {
|
||||
DisplayMode.CompactGrid -> LibraryMangaCompactGrid(
|
||||
library = library,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
onClickManga = onClickManga,
|
||||
onRemoveMangaClicked = onRemoveMangaClicked
|
||||
)
|
||||
/*DisplayMode.ComfortableGrid -> LibraryMangaComfortableGrid(
|
||||
DisplayMode.ComfortableGrid -> LibraryMangaComfortableGrid(
|
||||
library = library,
|
||||
onClickManga = onClickManga
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
onClickManga = onClickManga,
|
||||
onRemoveMangaClicked = onRemoveMangaClicked
|
||||
)
|
||||
DisplayMode.CoverOnlyGrid -> LibraryMangaCoverOnlyGrid(
|
||||
library = library,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
onClickManga = onClickManga,
|
||||
onRemoveMangaClicked = onRemoveMangaClicked
|
||||
)
|
||||
DisplayMode.List -> LibraryMangaList(
|
||||
library = library,
|
||||
onClickManga = onClickManga
|
||||
)*/
|
||||
onClickManga = onClickManga,
|
||||
onRemoveMangaClicked = onRemoveMangaClicked
|
||||
)
|
||||
else -> Box {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ fun LibraryScreenContent(
|
||||
categories: List<Category>,
|
||||
selectedCategoryIndex: Int,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
isLoading: Boolean,
|
||||
error: String?,
|
||||
query: String,
|
||||
@@ -60,6 +62,8 @@ fun LibraryScreenContent(
|
||||
categories = categories,
|
||||
selectedCategoryIndex = selectedCategoryIndex,
|
||||
displayMode = displayMode,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
isLoading = isLoading,
|
||||
error = error,
|
||||
query = query,
|
||||
@@ -75,6 +79,8 @@ fun LibraryScreenContent(
|
||||
categories = categories,
|
||||
selectedCategoryIndex = selectedCategoryIndex,
|
||||
displayMode = displayMode,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
isLoading = isLoading,
|
||||
error = error,
|
||||
query = query,
|
||||
@@ -94,6 +100,8 @@ fun WideLibraryScreenContent(
|
||||
categories: List<Category>,
|
||||
selectedCategoryIndex: Int,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
isLoading: Boolean,
|
||||
error: String?,
|
||||
query: String,
|
||||
@@ -129,6 +137,8 @@ fun WideLibraryScreenContent(
|
||||
pagerState = pagerState,
|
||||
categories = categories,
|
||||
displayMode = displayMode,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
getLibraryForPage = getLibraryForPage,
|
||||
onClickManga = onClickManga,
|
||||
onRemoveMangaClicked = onRemoveMangaClicked
|
||||
@@ -144,6 +154,8 @@ fun ThinLibraryScreenContent(
|
||||
categories: List<Category>,
|
||||
selectedCategoryIndex: Int,
|
||||
displayMode: DisplayMode,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
isLoading: Boolean,
|
||||
error: String?,
|
||||
query: String,
|
||||
@@ -185,6 +197,8 @@ fun ThinLibraryScreenContent(
|
||||
pagerState = pagerState,
|
||||
categories = categories,
|
||||
displayMode = displayMode,
|
||||
gridColumns = gridColumns,
|
||||
gridSize = gridSize,
|
||||
getLibraryForPage = getLibraryForPage,
|
||||
onClickManga = onClickManga,
|
||||
onRemoveMangaClicked = onRemoveMangaClicked
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.ui.library.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.GridCells
|
||||
import androidx.compose.foundation.lazy.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.ui.base.components.VerticalScrollbar
|
||||
import ca.gosyer.ui.base.components.rememberScrollbarAdapter
|
||||
import ca.gosyer.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaComfortableGrid(
|
||||
library: List<Manga>,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
onClickManga: (Long) -> Unit,
|
||||
onRemoveMangaClicked: (Long) -> Unit
|
||||
) {
|
||||
Box {
|
||||
val state = rememberLazyListState()
|
||||
val cells = if (gridColumns < 1) {
|
||||
GridCells.Adaptive(gridSize.dp)
|
||||
} else {
|
||||
GridCells.Fixed(gridColumns)
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
cells = cells,
|
||||
state = state,
|
||||
modifier = Modifier.fillMaxSize().padding(4.dp)
|
||||
) {
|
||||
items(library) { manga ->
|
||||
LibraryMangaComfortableGridItem(
|
||||
modifier = Modifier.libraryMangaModifier(
|
||||
{ onClickManga(manga.id) },
|
||||
{ onRemoveMangaClicked(manga.id) }
|
||||
),
|
||||
manga = manga,
|
||||
unread = manga.unreadCount,
|
||||
downloaded = manga.downloadCount
|
||||
)
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
rememberScrollbarAdapter(state),
|
||||
Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LibraryMangaComfortableGridItem(
|
||||
modifier: Modifier,
|
||||
manga: Manga,
|
||||
unread: Int?,
|
||||
downloaded: Int?
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
.fillMaxWidth()
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
Column {
|
||||
KamelImage(
|
||||
cover,
|
||||
contentDescription = manga.title,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(3f / 4f)
|
||||
.clip(MaterialTheme.shapes.medium),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Text(
|
||||
text = manga.title,
|
||||
style = fontStyle,
|
||||
maxLines = 3,
|
||||
modifier = Modifier.padding(vertical = 4.dp, horizontal = 4.dp)
|
||||
)
|
||||
}
|
||||
LibraryMangaBadges(
|
||||
unread = unread,
|
||||
downloaded = downloaded,
|
||||
modifier = Modifier.padding(4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -47,13 +47,20 @@ expect fun Modifier.libraryMangaModifier(
|
||||
@Composable
|
||||
fun LibraryMangaCompactGrid(
|
||||
library: List<Manga>,
|
||||
onClickManga: (Long) -> Unit = {},
|
||||
onRemoveMangaClicked: (Long) -> Unit = {}
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
onClickManga: (Long) -> Unit,
|
||||
onRemoveMangaClicked: (Long) -> Unit
|
||||
) {
|
||||
Box {
|
||||
val state = rememberLazyListState()
|
||||
val cells = if (gridColumns < 1) {
|
||||
GridCells.Adaptive(gridSize.dp)
|
||||
} else {
|
||||
GridCells.Fixed(gridColumns)
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
cells = GridCells.Adaptive(160.dp),
|
||||
cells = cells,
|
||||
state = state,
|
||||
modifier = Modifier.fillMaxSize().padding(4.dp)
|
||||
) {
|
||||
@@ -96,7 +103,12 @@ private fun LibraryMangaCompactGridItem(
|
||||
.aspectRatio(3f / 4f)
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
KamelImage(cover, manga.title, contentScale = ContentScale.Crop)
|
||||
KamelImage(
|
||||
cover,
|
||||
manga.title,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxSize().then(shadowGradient))
|
||||
Text(
|
||||
text = manga.title,
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package ca.gosyer.ui.library.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.GridCells
|
||||
import androidx.compose.foundation.lazy.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.ui.base.components.VerticalScrollbar
|
||||
import ca.gosyer.ui.base.components.rememberScrollbarAdapter
|
||||
import ca.gosyer.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaCoverOnlyGrid(
|
||||
library: List<Manga>,
|
||||
gridColumns: Int,
|
||||
gridSize: Int,
|
||||
onClickManga: (Long) -> Unit,
|
||||
onRemoveMangaClicked: (Long) -> Unit
|
||||
) {
|
||||
Box {
|
||||
val state = rememberLazyListState()
|
||||
val cells = if (gridColumns < 1) {
|
||||
GridCells.Adaptive(gridSize.dp)
|
||||
} else {
|
||||
GridCells.Fixed(gridColumns)
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
cells = cells,
|
||||
state = state,
|
||||
modifier = Modifier.fillMaxSize().padding(4.dp)
|
||||
) {
|
||||
items(library) { manga ->
|
||||
LibraryMangaCoverOnlyGridItem(
|
||||
modifier = Modifier.libraryMangaModifier(
|
||||
{ onClickManga(manga.id) },
|
||||
{ onRemoveMangaClicked(manga.id) }
|
||||
),
|
||||
manga = manga,
|
||||
unread = manga.unreadCount,
|
||||
downloaded = manga.downloadCount
|
||||
)
|
||||
}
|
||||
}
|
||||
VerticalScrollbar(
|
||||
rememberScrollbarAdapter(state),
|
||||
Modifier.align(Alignment.CenterEnd)
|
||||
.fillMaxHeight()
|
||||
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LibraryMangaCoverOnlyGridItem(
|
||||
modifier: Modifier,
|
||||
manga: Manga,
|
||||
unread: Int?,
|
||||
downloaded: Int?
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
|
||||
Box(
|
||||
modifier = Modifier.padding(4.dp)
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(3f / 4f)
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
KamelImage(
|
||||
cover,
|
||||
contentDescription = manga.title,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
LibraryMangaBadges(
|
||||
unread = unread,
|
||||
downloaded = downloaded,
|
||||
modifier = Modifier.padding(4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -6,27 +6,44 @@
|
||||
|
||||
package ca.gosyer.ui.settings
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.ContentAlpha
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Slider
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
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.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.core.logging.CKLogger
|
||||
import ca.gosyer.data.library.LibraryPreferences
|
||||
import ca.gosyer.data.library.model.DisplayMode
|
||||
import ca.gosyer.data.server.interactions.CategoryInteractionHandler
|
||||
import ca.gosyer.i18n.MR
|
||||
import ca.gosyer.ui.base.components.VerticalScrollbar
|
||||
import ca.gosyer.ui.base.components.rememberScrollbarAdapter
|
||||
import ca.gosyer.ui.base.dialog.getMaterialDialogProperties
|
||||
import ca.gosyer.ui.base.navigation.Toolbar
|
||||
import ca.gosyer.ui.base.prefs.ChoicePreference
|
||||
import ca.gosyer.ui.base.prefs.PreferenceRow
|
||||
import ca.gosyer.ui.base.prefs.SwitchPreference
|
||||
import ca.gosyer.ui.categories.rememberCategoriesLauncher
|
||||
import ca.gosyer.uicore.prefs.PreferenceMutableStateFlow
|
||||
import ca.gosyer.uicore.resources.stringResource
|
||||
@@ -36,12 +53,17 @@ import ca.gosyer.uicore.vm.viewModel
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import cafe.adriel.voyager.core.screen.ScreenKey
|
||||
import cafe.adriel.voyager.core.screen.uniqueScreenKey
|
||||
import com.vanpra.composematerialdialogs.MaterialDialog
|
||||
import com.vanpra.composematerialdialogs.MaterialDialogState
|
||||
import com.vanpra.composematerialdialogs.rememberMaterialDialogState
|
||||
import com.vanpra.composematerialdialogs.title
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class SettingsLibraryScreen : Screen {
|
||||
override val key: ScreenKey = uniqueScreenKey
|
||||
@@ -52,6 +74,10 @@ class SettingsLibraryScreen : Screen {
|
||||
val categoriesLauncher = rememberCategoriesLauncher(vm::refreshCategoryCount)
|
||||
SettingsLibraryScreenContent(
|
||||
showAllCategory = vm.showAllCategory,
|
||||
displayMode = vm.displayMode,
|
||||
displayModeChoices = vm.getDisplayModeChoices(),
|
||||
gridColumns = vm.gridColumns,
|
||||
gridSize = vm.gridSize,
|
||||
categoriesSize = vm.categories.collectAsState().value,
|
||||
openCategoriesScreen = categoriesLauncher::open
|
||||
)
|
||||
@@ -65,6 +91,10 @@ class SettingsLibraryViewModel @Inject constructor(
|
||||
contextWrapper: ContextWrapper
|
||||
) : ViewModel(contextWrapper) {
|
||||
|
||||
val displayMode = libraryPreferences.displayMode().asStateFlow()
|
||||
val gridColumns = libraryPreferences.gridColumns().asStateFlow()
|
||||
val gridSize = libraryPreferences.gridSize().asStateFlow()
|
||||
|
||||
val showAllCategory = libraryPreferences.showAllCategory().asStateFlow()
|
||||
private val _categories = MutableStateFlow(0)
|
||||
val categories = _categories.asStateFlow()
|
||||
@@ -84,11 +114,19 @@ class SettingsLibraryViewModel @Inject constructor(
|
||||
.launchIn(scope)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun getDisplayModeChoices() = DisplayMode.values()
|
||||
.associateWith { stringResource(it.res) }
|
||||
|
||||
private companion object : CKLogger({})
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsLibraryScreenContent(
|
||||
displayMode: PreferenceMutableStateFlow<DisplayMode>,
|
||||
displayModeChoices: Map<DisplayMode, String>,
|
||||
gridColumns: PreferenceMutableStateFlow<Int>,
|
||||
gridSize: PreferenceMutableStateFlow<Int>,
|
||||
showAllCategory: PreferenceMutableStateFlow<Boolean>,
|
||||
categoriesSize: Int,
|
||||
openCategoriesScreen: () -> Unit
|
||||
@@ -102,10 +140,29 @@ fun SettingsLibraryScreenContent(
|
||||
val state = rememberLazyListState()
|
||||
LazyColumn(Modifier.fillMaxSize(), state) {
|
||||
item {
|
||||
ChoicePreference(
|
||||
preference = displayMode,
|
||||
choices = displayModeChoices,
|
||||
title = stringResource(MR.strings.display_mode)
|
||||
)
|
||||
}
|
||||
item {
|
||||
val displayModePref by displayMode.collectAsState()
|
||||
GridPreference(
|
||||
columnPreference = gridColumns,
|
||||
sizePreference = gridSize,
|
||||
title = stringResource(MR.strings.items_per_row),
|
||||
enabled = displayModePref != DisplayMode.List
|
||||
)
|
||||
}
|
||||
/*item {
|
||||
SwitchPreference(
|
||||
preference = showAllCategory,
|
||||
title = stringResource(MR.strings.show_all_category)
|
||||
)
|
||||
}*/
|
||||
item {
|
||||
Divider()
|
||||
}
|
||||
item {
|
||||
PreferenceRow(
|
||||
@@ -124,3 +181,114 @@ fun SettingsLibraryScreenContent(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GridPreference(
|
||||
columnPreference: PreferenceMutableStateFlow<Int>,
|
||||
sizePreference: PreferenceMutableStateFlow<Int>,
|
||||
title: String,
|
||||
enabled: Boolean
|
||||
) {
|
||||
val columnPrefValue by columnPreference.collectAsState()
|
||||
val sizePrefValue by sizePreference.collectAsState()
|
||||
val dialogState = rememberMaterialDialogState()
|
||||
PreferenceRow(
|
||||
title = title,
|
||||
subtitle = if (columnPrefValue < 1) {
|
||||
stringResource(MR.strings.items_per_row_sub_adaptive, sizePrefValue)
|
||||
} else {
|
||||
stringResource(MR.strings.items_per_row_sub, columnPrefValue)
|
||||
},
|
||||
onClick = {
|
||||
dialogState.show()
|
||||
},
|
||||
enabled = enabled
|
||||
)
|
||||
GridPrefDialog(
|
||||
state = dialogState,
|
||||
initialColumns = columnPrefValue,
|
||||
initialSize = sizePrefValue,
|
||||
title = title,
|
||||
onSelected = { columns, size ->
|
||||
columnPreference.value = columns
|
||||
sizePreference.value = size
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun GridPrefDialog(
|
||||
state: MaterialDialogState,
|
||||
initialColumns: Int,
|
||||
initialSize: Int,
|
||||
onCloseRequest: () -> Unit = {},
|
||||
onSelected: (columns: Int, size: Int) -> Unit,
|
||||
title: String
|
||||
) {
|
||||
var columns by remember(initialColumns) { mutableStateOf(initialColumns.toFloat()) }
|
||||
var size by remember(initialSize) { mutableStateOf(initialSize.toFloat()) }
|
||||
MaterialDialog(
|
||||
state,
|
||||
buttons = {
|
||||
positiveButton(stringResource(MR.strings.action_ok)) {
|
||||
val sizeInt = size.roundToInt()
|
||||
val newSize = (10 - sizeInt % 10) + sizeInt
|
||||
onSelected(columns.roundToInt(), newSize)
|
||||
}
|
||||
negativeButton(stringResource(MR.strings.action_cancel))
|
||||
},
|
||||
properties = getMaterialDialogProperties(),
|
||||
onCloseRequest = {
|
||||
state.hide()
|
||||
onCloseRequest()
|
||||
}
|
||||
) {
|
||||
title(title)
|
||||
Column(Modifier.padding(horizontal = 8.dp)) {
|
||||
Text(stringResource(MR.strings.grid_columns) + ":")
|
||||
Slider(
|
||||
value = columns,
|
||||
onValueChange = {
|
||||
columns = it
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
valueRange = 0F..10F,
|
||||
steps = 10 - 2
|
||||
)
|
||||
val columnsInt = columns.roundToInt()
|
||||
val adaptive = columnsInt < 1
|
||||
Text(
|
||||
if (adaptive) {
|
||||
stringResource(MR.strings.adaptive)
|
||||
} else {
|
||||
columnsInt.toString()
|
||||
},
|
||||
color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
|
||||
AnimatedVisibility(adaptive) {
|
||||
Column {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Text(stringResource(MR.strings.grid_size) + ":")
|
||||
Slider(
|
||||
value = size,
|
||||
onValueChange = {
|
||||
size = it
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
valueRange = 90F..300F,
|
||||
steps = 21 - 2
|
||||
)
|
||||
val sizeInt = size.roundToInt()
|
||||
val newSize = (10 - sizeInt % 10) + sizeInt
|
||||
Text(
|
||||
newSize.toString(),
|
||||
color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user