From 147a36a70920106251729915a38eafd0a2a7e20e Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Fri, 27 Sep 2024 09:50:22 +0700 Subject: [PATCH] feat(ui): Panorama cover * support panorama cover * bigger panorama cover size in GlobalSearchCardRow * setting to enable/disable * panorama cover for History items * Keep panorama cover position in Library View centered for better visual * fix: update ratio in Detail page if cover doesn't reload anew * adjust ratio to switch to panorma * panorama cover for Migration items * center panorama cover & add dominant background color * panorama cover for EditMangaInfo * Fix size on GlobalSearchCardRow * display mode for manual panorama cover * Fix LibraryFlagsTest --- .../java/eu/kanade/domain/ui/UiPreferences.kt | 2 + .../presentation/browse/BrowseSourceScreen.kt | 15 ++ .../browse/RelatedMangasScreen.kt | 14 ++ .../components/BrowseSourceComfortableGrid.kt | 5 + .../components/BrowseSourceCompactGrid.kt | 1 + .../components/BrowseSourceSimpleToolbar.kt | 15 ++ .../browse/components/BrowseSourceToolbar.kt | 17 +++ .../browse/components/GlobalSearchCardRow.kt | 23 ++- .../browse/components/MigrationItem.kt | 56 ++++++- .../RelatedMangasComfortableGrid.kt | 2 + .../history/components/HistoryItem.kt | 54 +++++-- .../library/LibrarySettingsDialog.kt | 30 +++- .../library/components/CommonMangaItem.kt | 144 +++++++++++++++--- .../components/LibraryComfortableGrid.kt | 6 + .../library/components/LibraryPager.kt | 16 ++ .../kanade/presentation/manga/MangaScreen.kt | 8 + .../manga/components/MangaCover.kt | 20 ++- .../manga/components/MangaInfoHeader.kt | 124 ++++++++++++--- .../screen/SettingsAppearanceScreen.kt | 7 + .../tachiyomi/data/coil/MangaCoverMetadata.kt | 2 +- .../tachiyomi/ui/manga/EditMangaDialog.kt | 37 ++++- .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 6 + app/src/main/res/layout/edit_manga_dialog.xml | 52 ++++--- .../library/model/LibraryDisplayMode.kt | 13 +- .../domain/library/model/LibraryFlagsTest.kt | 2 +- .../moko-resources/base/strings.xml | 3 + 26 files changed, 571 insertions(+), 103 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt index 244eb155d..fec8c0afd 100644 --- a/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt +++ b/app/src/main/java/eu/kanade/domain/ui/UiPreferences.kt @@ -64,6 +64,8 @@ class UiPreferences( fun showHomeOnRelatedMangas() = preferenceStore.getBoolean("show_home_on_related_mangas", true) fun readButtonPosition() = preferenceStore.getString("reading_button_position", FabPosition.End.toString()) + + fun usePanoramaCover() = preferenceStore.getBoolean("use_panorama_cover", false) // KMK <-- fun recommendsInOverflow() = preferenceStore.getBoolean("recommends_in_overflow", false) diff --git a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt index cf480c953..203fd2ee6 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/BrowseSourceScreen.kt @@ -182,6 +182,21 @@ fun BrowseSourceContent( // KMK <-- ) } + // KMK --> + LibraryDisplayMode.ComfortableGridPanorama -> { + BrowseSourceComfortableGrid( + mangaList = mangaList, + columns = columns, + contentPadding = contentPadding, + onMangaClick = onMangaClick, + onMangaLongClick = onMangaLongClick, + // KMK --> + selection = selection, + usePanoramaCover = true, + // KMK <-- + ) + } + // KMK <-- LibraryDisplayMode.List -> { BrowseSourceList( mangaList = mangaList, diff --git a/app/src/main/java/eu/kanade/presentation/browse/RelatedMangasScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/RelatedMangasScreen.kt index 153ccbc31..78f26f9f3 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/RelatedMangasScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/RelatedMangasScreen.kt @@ -80,6 +80,20 @@ fun RelatedMangasContent( selection = selection, ) } + LibraryDisplayMode.ComfortableGridPanorama -> { + RelatedMangasComfortableGrid( + relatedMangas = relatedMangas, + getManga = getMangaState, + columns = columns, + contentPadding = contentPadding, + onMangaClick = onMangaClick, + onMangaLongClick = onMangaLongClick, + onKeywordClick = onKeywordClick, + onKeywordLongClick = onKeywordLongClick, + selection = selection, + usePanoramaCover = true, + ) + } LibraryDisplayMode.CompactGrid, LibraryDisplayMode.CoverOnlyGrid -> { RelatedMangasCompactGrid( relatedMangas = relatedMangas, diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceComfortableGrid.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceComfortableGrid.kt index 92b6d1018..4339f3d0d 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceComfortableGrid.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceComfortableGrid.kt @@ -36,6 +36,7 @@ fun BrowseSourceComfortableGrid( onMangaLongClick: (Manga) -> Unit, // KMK --> selection: List, + usePanoramaCover: Boolean? = null, // KMK <-- ) { LazyVerticalGrid( @@ -66,6 +67,7 @@ fun BrowseSourceComfortableGrid( onLongClick = { onMangaLongClick(manga) }, // KMK --> isSelected = selection.fastAny { selected -> selected.id == manga.id }, + usePanoramaCover = usePanoramaCover, // KMK <-- ) } @@ -88,6 +90,7 @@ internal fun BrowseSourceComfortableGridItem( onLongClick: () -> Unit = onClick, // KMK --> isSelected: Boolean = false, + usePanoramaCover: Boolean?, // KMK <-- ) { MangaComfortableGridItem( @@ -101,6 +104,8 @@ internal fun BrowseSourceComfortableGridItem( ), // KMK --> isSelected = isSelected, + panoramaCover = usePanoramaCover, + fitToPanoramaCover = true, // KMK <-- coverAlpha = if (manga.favorite) CommonMangaItemDefaults.BrowseFavoriteCoverAlpha else 1f, coverBadgeStart = { diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceCompactGrid.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceCompactGrid.kt index 908ebe226..ca3964aa2 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceCompactGrid.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceCompactGrid.kt @@ -101,6 +101,7 @@ internal fun BrowseSourceCompactGridItem( ), // KMK --> isSelected = isSelected, + fitToPanoramaCover = true, // KMK <-- coverAlpha = if (manga.favorite) CommonMangaItemDefaults.BrowseFavoriteCoverAlpha else 1f, coverBadgeStart = { diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceSimpleToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceSimpleToolbar.kt index 08b265167..41a52e788 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceSimpleToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceSimpleToolbar.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.DropdownMenu @@ -18,7 +19,11 @@ import eu.kanade.tachiyomi.ui.browse.bulkSelectionButton import kotlinx.collections.immutable.persistentListOf import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.i18n.MR +import tachiyomi.i18n.kmk.KMR import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.util.collectAsState +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get @Composable fun BrowseSourceSimpleToolbar( @@ -67,6 +72,16 @@ fun BrowseSourceSimpleToolbar( selectingDisplayMode = false onDisplayModeChange(LibraryDisplayMode.ComfortableGrid) } + val usePanoramaCover by lazy { Injekt.get().usePanoramaCover() } + if (!usePanoramaCover.collectAsState().value) { + RadioMenuItem( + text = { Text(text = stringResource(KMR.strings.action_display_comfortable_grid_panorama)) }, + isChecked = displayMode == LibraryDisplayMode.ComfortableGridPanorama, + ) { + selectingDisplayMode = false + onDisplayModeChange(LibraryDisplayMode.ComfortableGridPanorama) + } + } RadioMenuItem( text = { Text(text = stringResource(MR.strings.action_display_grid)) }, isChecked = displayMode == LibraryDisplayMode.CompactGrid, diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt index 59e4b04b8..b57fc6840 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/BrowseSourceToolbar.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.components.AppBar import eu.kanade.presentation.components.AppBarActions import eu.kanade.presentation.components.AppBarTitle @@ -25,8 +26,12 @@ import exh.source.anyIs import kotlinx.collections.immutable.persistentListOf import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.i18n.MR +import tachiyomi.i18n.kmk.KMR import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.util.collectAsState import tachiyomi.source.local.LocalSource +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get @Composable fun BrowseSourceToolbar( @@ -138,6 +143,18 @@ fun BrowseSourceToolbar( selectingDisplayMode = false onDisplayModeChange(LibraryDisplayMode.ComfortableGrid) } + // KMK --> + val usePanoramaCover by lazy { Injekt.get().usePanoramaCover() } + if (!usePanoramaCover.collectAsState().value) { + RadioMenuItem( + text = { Text(text = stringResource(KMR.strings.action_display_comfortable_grid_panorama)) }, + isChecked = displayMode == LibraryDisplayMode.ComfortableGridPanorama, + ) { + selectingDisplayMode = false + onDisplayModeChange(LibraryDisplayMode.ComfortableGridPanorama) + } + } + // KMK <-- RadioMenuItem( text = { Text(text = stringResource(MR.strings.action_display_grid)) }, isChecked = displayMode == LibraryDisplayMode.CompactGrid, diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchCardRow.kt b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchCardRow.kt index 9e230fdd5..548509a39 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchCardRow.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/GlobalSearchCardRow.kt @@ -12,17 +12,24 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastAny +import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.library.components.CommonMangaItemDefaults import eu.kanade.presentation.library.components.MangaComfortableGridItem +import eu.kanade.presentation.manga.components.RatioSwitchToPanorama import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.MangaCover import tachiyomi.domain.manga.model.asMangaCover import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.util.collectAsState +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get @Composable fun GlobalSearchCardRow( @@ -68,9 +75,20 @@ internal fun MangaItem( onLongClick: () -> Unit, // KMK --> isSelected: Boolean = false, + panoramaCover: Boolean? = null, // KMK <-- ) { - Box(modifier = Modifier.width(96.dp)) { + // KMK --> + val usePanoramaCover = panoramaCover ?: Injekt.get().usePanoramaCover().collectAsState().value + val coverRatio = remember { mutableFloatStateOf(1f) } + // KMK <-- + Box( + modifier = Modifier.width( + // KMK --> + if (usePanoramaCover && coverRatio.floatValue <= RatioSwitchToPanorama) 205.dp else 96.dp, + // KMK <-- + ), + ) { MangaComfortableGridItem( title = title, titleMaxLines = 3, @@ -80,6 +98,9 @@ internal fun MangaItem( }, // KMK --> isSelected = isSelected, + coverRatio = coverRatio, + panoramaCover = usePanoramaCover, + fitToPanoramaCover = true, // KMK <-- coverAlpha = if (isFavorite) CommonMangaItemDefaults.BrowseFavoriteCoverAlpha else 1f, onClick = onClick, diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/MigrationItem.kt b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationItem.kt index 2558f1c12..b5bfae4a0 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/MigrationItem.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/MigrationItem.kt @@ -13,8 +13,11 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableFloatState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.produceState +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -25,14 +28,21 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import eu.kanade.domain.ui.UiPreferences +import eu.kanade.presentation.manga.components.CoverPlaceholderColor import eu.kanade.presentation.manga.components.MangaCover +import eu.kanade.presentation.manga.components.RatioSwitchToPanorama import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigratingManga import tachiyomi.core.common.util.lang.withIOContext import tachiyomi.domain.manga.model.Manga +import tachiyomi.domain.manga.model.asMangaCover import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.Badge import tachiyomi.presentation.core.components.BadgeGroup import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.util.collectAsState +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get @Composable fun MigrationItem( @@ -41,7 +51,15 @@ fun MigrationItem( sourcesString: String, chapterInfo: MigratingManga.ChapterInfo, onClick: () -> Unit, + // KMK --> + coverRatio: MutableFloatState = remember { mutableFloatStateOf(1f) }, + // KMK <-- ) { + // KMK --> + val usePanoramaCover by Injekt.get().usePanoramaCover().collectAsState() + val coverIsWide = coverRatio.floatValue <= RatioSwitchToPanorama + val bgColor = manga.asMangaCover().dominantCoverColors?.first?.let { Color(it) } + // KMK <-- Column( modifier .widthIn(max = 150.dp) @@ -53,13 +71,41 @@ fun MigrationItem( val context = LocalContext.current Box( Modifier.fillMaxWidth() + // KMK --> + .background(bgColor ?: CoverPlaceholderColor) + // KMK <-- .aspectRatio(MangaCover.Book.ratio), ) { - MangaCover.Book( - modifier = Modifier - .fillMaxWidth(), - data = manga, - ) + // KMK --> + if (usePanoramaCover && coverIsWide) { + MangaCover.Panorama( + modifier = Modifier + // KMK --> + .align(Alignment.Center) + // KMK <-- + .fillMaxWidth(), + data = manga, + // KMK --> + onCoverLoaded = { _, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + }, + // KMK <-- + ) + } else { + // KMK <-- + MangaCover.Book( + modifier = Modifier + .fillMaxWidth(), + data = manga, + // KMK --> + onCoverLoaded = { _, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + }, + // KMK <-- + ) + } Box( modifier = Modifier .clip(RoundedCornerShape(bottomStart = 4.dp, bottomEnd = 4.dp)) diff --git a/app/src/main/java/eu/kanade/presentation/browse/components/RelatedMangasComfortableGrid.kt b/app/src/main/java/eu/kanade/presentation/browse/components/RelatedMangasComfortableGrid.kt index 9336a7e29..5f1278a96 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/components/RelatedMangasComfortableGrid.kt +++ b/app/src/main/java/eu/kanade/presentation/browse/components/RelatedMangasComfortableGrid.kt @@ -35,6 +35,7 @@ fun RelatedMangasComfortableGrid( onKeywordClick: (String) -> Unit, onKeywordLongClick: (String) -> Unit, selection: List, + usePanoramaCover: Boolean? = null, ) { FastScrollLazyVerticalGrid( columns = columns, @@ -89,6 +90,7 @@ fun RelatedMangasComfortableGrid( onLongClick = { onMangaLongClick(manga) }, isSelected = selection.fastAny { selected -> selected.id == manga.id }, metadata = null, + usePanoramaCover = usePanoramaCover, ) } } diff --git a/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt b/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt index 21eb2bf45..c23996466 100644 --- a/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt +++ b/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt @@ -14,6 +14,9 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableFloatState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -23,7 +26,9 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.manga.components.MangaCover +import eu.kanade.presentation.manga.components.RatioSwitchToPanorama import eu.kanade.presentation.theme.TachiyomiPreviewTheme import eu.kanade.presentation.util.formatChapterNumber import eu.kanade.tachiyomi.util.lang.toTimestampString @@ -31,6 +36,9 @@ import tachiyomi.domain.history.model.HistoryWithRelations import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.util.collectAsState +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get private val HistoryItemHeight = 96.dp @@ -41,6 +49,9 @@ fun HistoryItem( onClickResume: () -> Unit, onClickDelete: () -> Unit, modifier: Modifier = Modifier, + // KMK --> + coverRatio: MutableFloatState = remember { mutableFloatStateOf(1f) }, + // KMK <-- ) { Row( modifier = modifier @@ -51,19 +62,42 @@ fun HistoryItem( ) { // KMK --> val mangaCover = history.coverData + val usePanoramaCover by Injekt.get().usePanoramaCover().collectAsState() + val coverIsWide = coverRatio.floatValue <= RatioSwitchToPanorama val bgColor = mangaCover.dominantCoverColors?.first?.let { Color(it) } val onBgColor = mangaCover.dominantCoverColors?.second - // KMK <-- - MangaCover.Book( - modifier = Modifier.fillMaxHeight(), - data = mangaCover, - onClick = onClickCover, - // KMK --> - bgColor = bgColor, - tint = onBgColor, - size = MangaCover.Size.Medium, + if (usePanoramaCover && coverIsWide) { + MangaCover.Panorama( + modifier = Modifier.fillMaxHeight(), + data = mangaCover, + onClick = onClickCover, + // KMK --> + bgColor = bgColor, + tint = onBgColor, + size = MangaCover.Size.Medium, + onCoverLoaded = { _, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + }, + // KMK <-- + ) + } else { // KMK <-- - ) + MangaCover.Book( + modifier = Modifier.fillMaxHeight(), + data = mangaCover, + onClick = onClickCover, + // KMK --> + bgColor = bgColor, + tint = onBgColor, + size = MangaCover.Size.Medium, + onCoverLoaded = { _, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + }, + // KMK <-- + ) + } Column( modifier = Modifier .weight(1f) diff --git a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt index 03e6cd155..a1a6aa9ac 100644 --- a/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/library/LibrarySettingsDialog.kt @@ -17,6 +17,7 @@ import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.res.painterResource import androidx.compose.ui.util.fastForEach import dev.icerock.moko.resources.StringResource +import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialogPaddings import eu.kanade.tachiyomi.R @@ -45,6 +46,8 @@ import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.TriStateItem import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.collectAsState +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get @Composable fun LibrarySettingsDialog( @@ -255,8 +258,11 @@ private fun ColumnScope.SortPage( private val displayModes = listOf( MR.strings.action_display_grid to LibraryDisplayMode.CompactGrid, MR.strings.action_display_comfortable_grid to LibraryDisplayMode.ComfortableGrid, - MR.strings.action_display_cover_only_grid to LibraryDisplayMode.CoverOnlyGrid, MR.strings.action_display_list to LibraryDisplayMode.List, + MR.strings.action_display_cover_only_grid to LibraryDisplayMode.CoverOnlyGrid, + // KMK --> + KMR.strings.action_display_comfortable_grid_panorama to LibraryDisplayMode.ComfortableGridPanorama, + // KMK <-- ) @Suppress("UnusedReceiverParameter") @@ -265,14 +271,22 @@ private fun ColumnScope.DisplayPage( screenModel: LibrarySettingsScreenModel, ) { val displayMode by screenModel.libraryPreferences.displayMode().collectAsState() + // KMK --> + val usePanoramaCover by lazy { Injekt.get().usePanoramaCover() } + // KMK <-- SettingsChipRow(MR.strings.action_display_mode) { - displayModes.map { (titleRes, mode) -> - FilterChip( - selected = displayMode == mode, - onClick = { screenModel.setDisplayMode(mode) }, - label = { Text(stringResource(titleRes)) }, - ) - } + displayModes + .filterNot { + it.second == LibraryDisplayMode.ComfortableGridPanorama && + usePanoramaCover.collectAsState().value + } + .map { (titleRes, mode) -> + FilterChip( + selected = displayMode == mode, + onClick = { screenModel.setDisplayMode(mode) }, + label = { Text(stringResource(titleRes)) }, + ) + } } if (displayMode != LibraryDisplayMode.List) { diff --git a/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt b/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt index e358232ba..26508890e 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/CommonMangaItem.kt @@ -25,6 +25,10 @@ import androidx.compose.material3.Text import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.MutableFloatState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -32,18 +36,24 @@ import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shadow +import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import eu.kanade.domain.ui.UiPreferences import eu.kanade.presentation.manga.components.MangaCover import eu.kanade.presentation.manga.components.MangaCoverHide +import eu.kanade.presentation.manga.components.RatioSwitchToPanorama import exh.debug.DebugToggles import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.BadgeGroup import tachiyomi.presentation.core.i18n.stringResource +import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.selectedBackground +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get import tachiyomi.domain.manga.model.MangaCover as MangaCoverModel object CommonMangaItemDefaults { @@ -82,9 +92,13 @@ fun MangaCompactGridItem( coverBadgeEnd: @Composable (RowScope.() -> Unit)? = null, // KMK --> libraryColored: Boolean = true, + coverRatio: MutableFloatState = remember { mutableFloatStateOf(1f) }, + fitToPanoramaCover: Boolean = false, // KMK <-- ) { // KMK --> + val usePanoramaCover by Injekt.get().usePanoramaCover().collectAsState() + val coverIsWide = coverRatio.floatValue <= RatioSwitchToPanorama val bgColor = coverData.dominantCoverColors?.first?.let { Color(it) }.takeIf { libraryColored } val onBgColor = coverData.dominantCoverColors?.second.takeIf { libraryColored } // KMK <-- @@ -104,22 +118,58 @@ fun MangaCompactGridItem( tint = onBgColor, ) } else { - // KMK <-- - MangaCover.Book( - modifier = Modifier + if (fitToPanoramaCover && usePanoramaCover && coverIsWide) { + MangaCover.Panorama( + modifier = Modifier + // KMK --> + // .alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha) + // KMK <-- + .fillMaxWidth(), + data = coverData, // KMK --> - // .alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha) + alpha = if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha, + bgColor = bgColor ?: (MaterialTheme.colorScheme.surface.takeIf { isSelected }), + tint = onBgColor, + onCoverLoaded = { _, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + }, // KMK <-- - .fillMaxWidth(), - data = coverData, - // KMK --> - alpha = if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha, - bgColor = bgColor ?: (MaterialTheme.colorScheme.surface.takeIf { isSelected }), - tint = onBgColor, + ) + } else { // KMK <-- - ) + MangaCover.Book( + modifier = Modifier + // KMK --> + // .alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha) + // KMK <-- + .fillMaxWidth(), + data = coverData, + // KMK --> + alpha = if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha, + bgColor = bgColor ?: (MaterialTheme.colorScheme.surface.takeIf { isSelected }), + tint = onBgColor, + onCoverLoaded = { _, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + }, + scale = if (usePanoramaCover && coverIsWide) { + ContentScale.Fit + } else { + ContentScale.Crop + }, + // KMK <-- + ) + } } }, + // KMK --> + ratio = if (fitToPanoramaCover && usePanoramaCover && coverIsWide) { + MangaCover.Panorama.ratio + } else { + MangaCover.Book.ratio + }, + // KMK <-- badgesStart = coverBadgeStart, badgesEnd = coverBadgeEnd, content = { @@ -208,14 +258,19 @@ fun MangaComfortableGridItem( isSelected: Boolean = false, titleMaxLines: Int = 2, coverAlpha: Float = 1f, - coverBadgeStart: (@Composable RowScope.() -> Unit)? = null, - coverBadgeEnd: (@Composable RowScope.() -> Unit)? = null, + coverBadgeStart: @Composable (RowScope.() -> Unit)? = null, + coverBadgeEnd: @Composable (RowScope.() -> Unit)? = null, onClickContinueReading: (() -> Unit)? = null, // KMK --> libraryColored: Boolean = true, + coverRatio: MutableFloatState = remember { mutableFloatStateOf(1f) }, + panoramaCover: Boolean?, + fitToPanoramaCover: Boolean = false, // KMK <-- ) { // KMK --> + val usePanoramaCover = panoramaCover ?: Injekt.get().usePanoramaCover().collectAsState().value + val coverIsWide = coverRatio.floatValue <= RatioSwitchToPanorama val bgColor = coverData.dominantCoverColors?.first?.let { Color(it) }.takeIf { libraryColored } val onBgColor = coverData.dominantCoverColors?.second.takeIf { libraryColored } // KMK <-- @@ -236,22 +291,58 @@ fun MangaComfortableGridItem( tint = onBgColor, ) } else { - // KMK <-- - MangaCover.Book( - modifier = Modifier + if (fitToPanoramaCover && usePanoramaCover && coverIsWide) { + MangaCover.Panorama( + modifier = Modifier + // KMK --> + // .alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha) + // KMK <-- + .fillMaxWidth(), + data = coverData, // KMK --> - // .alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha) + alpha = if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha, + bgColor = bgColor ?: (MaterialTheme.colorScheme.surface.takeIf { isSelected }), + tint = onBgColor, + onCoverLoaded = { _, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + }, // KMK <-- - .fillMaxWidth(), - data = coverData, - // KMK --> - alpha = if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha, - bgColor = bgColor ?: (MaterialTheme.colorScheme.surface.takeIf { isSelected }), - tint = onBgColor, + ) + } else { // KMK <-- - ) + MangaCover.Book( + modifier = Modifier + // KMK --> + // .alpha(if (isSelected) GridSelectedCoverAlpha else coverAlpha) + // KMK <-- + .fillMaxWidth(), + data = coverData, + // KMK --> + alpha = if (isSelected) GRID_SELECTED_COVER_ALPHA else coverAlpha, + bgColor = bgColor ?: (MaterialTheme.colorScheme.surface.takeIf { isSelected }), + tint = onBgColor, + onCoverLoaded = { _, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + }, + scale = if (usePanoramaCover && coverIsWide) { + ContentScale.Fit + } else { + ContentScale.Crop + }, + // KMK <-- + ) + } } }, + // KMK --> + ratio = if (fitToPanoramaCover && usePanoramaCover && coverIsWide) { + MangaCover.Panorama.ratio + } else { + MangaCover.Book.ratio + }, + // KMK <-- badgesStart = coverBadgeStart, badgesEnd = coverBadgeEnd, content = { @@ -285,6 +376,9 @@ fun MangaComfortableGridItem( private fun MangaGridCover( modifier: Modifier = Modifier, cover: @Composable BoxScope.() -> Unit = {}, + // KMK --> + ratio: Float = MangaCover.Book.ratio, + // KMK <-- badgesStart: (@Composable RowScope.() -> Unit)? = null, badgesEnd: (@Composable RowScope.() -> Unit)? = null, content: @Composable (BoxScope.() -> Unit)? = null, @@ -292,7 +386,7 @@ private fun MangaGridCover( Box( modifier = modifier .fillMaxWidth() - .aspectRatio(MangaCover.Book.ratio), + .aspectRatio(ratio), ) { cover() content?.invoke(this) diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt index 5b5952202..70b6afafa 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryComfortableGrid.kt @@ -21,6 +21,9 @@ internal fun LibraryComfortableGrid( onClickContinueReading: ((LibraryManga) -> Unit)?, searchQuery: String?, onGlobalSearchClicked: () -> Unit, + // KMK --> + usePanoramaCover: Boolean? = null, + // KMK <-- ) { LazyLibraryGrid( modifier = Modifier.fillMaxSize(), @@ -67,6 +70,9 @@ internal fun LibraryComfortableGrid( } else { null }, + // KMK --> + panoramaCover = usePanoramaCover, + // KMK <-- ) } } diff --git a/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt b/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt index 6487ab39f..1bd2b4f86 100644 --- a/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt +++ b/app/src/main/java/eu/kanade/presentation/library/components/LibraryPager.kt @@ -112,6 +112,22 @@ fun LibraryPager( onGlobalSearchClicked = onGlobalSearchClicked, ) } + // KMK --> + LibraryDisplayMode.ComfortableGridPanorama -> { + LibraryComfortableGrid( + items = library, + columns = columns, + contentPadding = contentPadding, + selection = selectedManga, + onClick = onClickManga, + onLongClick = onLongClickManga, + onClickContinueReading = onClickContinueReading, + searchQuery = searchQuery, + onGlobalSearchClicked = onGlobalSearchClicked, + usePanoramaCover = true, + ) + } + // KMK <-- } } } diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index 5c03ae126..4e6f0ce2b 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -36,6 +36,7 @@ import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableFloatState import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue @@ -207,6 +208,7 @@ fun MangaScreen( librarySearch: (query: String) -> Unit, onSourceClick: () -> Unit, onCoverLoaded: (MangaCover) -> Unit, + coverRatio: MutableFloatState, onPaletteScreenClick: () -> Unit, hazeState: HazeState, // KMK <-- @@ -271,6 +273,7 @@ fun MangaScreen( librarySearch = librarySearch, onSourceClick = onSourceClick, onCoverLoaded = onCoverLoaded, + coverRatio = coverRatio, onPaletteScreenClick = onPaletteScreenClick, hazeState = hazeState, // KMK <-- @@ -328,6 +331,7 @@ fun MangaScreen( librarySearch = librarySearch, onSourceClick = onSourceClick, onCoverLoaded = onCoverLoaded, + coverRatio = coverRatio, onPaletteScreenClick = onPaletteScreenClick, hazeState = hazeState, // KMK <-- @@ -402,6 +406,7 @@ private fun MangaScreenSmallImpl( librarySearch: (query: String) -> Unit, onSourceClick: () -> Unit, onCoverLoaded: (MangaCover) -> Unit, + coverRatio: MutableFloatState, onPaletteScreenClick: () -> Unit, hazeState: HazeState, // KMK <-- @@ -620,6 +625,7 @@ private fun MangaScreenSmallImpl( librarySearch = librarySearch, onSourceClick = onSourceClick, onCoverLoaded = onCoverLoaded, + coverRatio = coverRatio, // KMK <-- ) } @@ -860,6 +866,7 @@ private fun MangaScreenLargeImpl( librarySearch: (query: String) -> Unit, onSourceClick: () -> Unit, onCoverLoaded: (MangaCover) -> Unit, + coverRatio: MutableFloatState, onPaletteScreenClick: () -> Unit, hazeState: HazeState, // KMK <-- @@ -1070,6 +1077,7 @@ private fun MangaScreenLargeImpl( librarySearch = librarySearch, onSourceClick = onSourceClick, onCoverLoaded = onCoverLoaded, + coverRatio = coverRatio, // KMK <-- ) MangaActionRow( diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt index 231b188db..a0e6924c5 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp +import coil3.compose.AsyncImagePainter import coil3.compose.SubcomposeAsyncImage import eu.kanade.tachiyomi.R import tachiyomi.domain.manga.model.Manga @@ -35,6 +36,10 @@ import tachiyomi.domain.manga.model.MangaCover as DomainMangaCover enum class MangaCover(val ratio: Float) { Square(1f / 1f), Book(2f / 3f), + + // KMK --> + Panorama(3f / 2f), + // KMK <-- ; enum class Size { @@ -54,9 +59,10 @@ enum class MangaCover(val ratio: Float) { alpha: Float = 1f, bgColor: Color? = null, @ColorInt tint: Int? = null, - /** Perform action when cover loaded, specifically generating color map */ - onCoverLoaded: ((DomainMangaCover) -> Unit)? = null, + /** Perform action when cover loaded, specifically generating color map. If the cover doesn't update, it won't be called */ + onCoverLoaded: ((DomainMangaCover, result: AsyncImagePainter.State.Success) -> Unit)? = null, size: Size = Size.Normal, + scale: ContentScale = ContentScale.Crop, // KMK <-- ) { // KMK --> @@ -128,19 +134,19 @@ enum class MangaCover(val ratio: Float) { ) } }, - onSuccess = { + onSuccess = { result -> succeed = true if (onCoverLoaded != null) { when (data) { - is Manga -> onCoverLoaded(data.asMangaCover()) - is DomainMangaCover -> onCoverLoaded(data) + is Manga -> onCoverLoaded(data.asMangaCover(), result) + is DomainMangaCover -> onCoverLoaded(data, result) } } }, // KMK <-- contentDescription = contentDescription, modifier = modifierColored, - contentScale = ContentScale.Crop, + contentScale = scale, ) } } @@ -194,5 +200,7 @@ enum class MangaCoverHide(private val ratio: Float) { } } +internal val RatioSwitchToPanorama = 0.75f + internal val CoverPlaceholderColor = Color(0x1F888888) internal val CoverPlaceholderOnBgColor = Color(0x8F888888) diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt index acd1f82c8..60311386f 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaInfoHeader.kt @@ -51,6 +51,7 @@ import androidx.compose.material3.ProvideTextStyle import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.MutableFloatState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -96,6 +97,7 @@ import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.i18n.pluralStringResource import tachiyomi.presentation.core.i18n.stringResource import tachiyomi.presentation.core.util.clickableNoIndication +import tachiyomi.presentation.core.util.collectAsState import tachiyomi.presentation.core.util.secondaryItemAlpha import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -120,8 +122,12 @@ fun MangaInfoBox( librarySearch: (query: String) -> Unit, onSourceClick: () -> Unit, onCoverLoaded: (DomainMangaCover) -> Unit, + coverRatio: MutableFloatState, // KMK <-- ) { + // KMK --> + val usePanoramaCover by Injekt.get().usePanoramaCover().collectAsState() + // KMK <-- Box(modifier = modifier) { // Backdrop val backdropGradientColors = listOf( @@ -135,6 +141,12 @@ fun MangaInfoBox( .build(), contentDescription = null, contentScale = ContentScale.Crop, + // KMK --> + onSuccess = { result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + }, + // KMK <-- modifier = Modifier .matchParentSize() .drawWithContent { @@ -170,6 +182,8 @@ fun MangaInfoBox( librarySearch = librarySearch, onSourceClick = onSourceClick, onCoverLoaded = onCoverLoaded, + coverRatio = coverRatio, + usePanoramaCover = usePanoramaCover, // KMK <-- ) } else { @@ -184,6 +198,8 @@ fun MangaInfoBox( librarySearch = librarySearch, onSourceClick = onSourceClick, onCoverLoaded = onCoverLoaded, + coverRatio = coverRatio, + usePanoramaCover = usePanoramaCover, // KMK <-- ) } @@ -458,6 +474,8 @@ private fun MangaAndSourceTitlesLarge( librarySearch: (query: String) -> Unit, onSourceClick: () -> Unit, onCoverLoaded: (DomainMangaCover) -> Unit, + coverRatio: MutableFloatState, + usePanoramaCover: Boolean = false, // KMK <-- ) { Column( @@ -466,18 +484,43 @@ private fun MangaAndSourceTitlesLarge( .padding(start = 16.dp, top = appBarPadding + 16.dp, end = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { - MangaCover.Book( - modifier = Modifier.fillMaxWidth(0.65f), - data = ImageRequest.Builder(LocalContext.current) - .data(manga) - .crossfade(true) - .build(), - contentDescription = stringResource(MR.strings.manga_cover), - onClick = onCoverClick, - // KMK --> - onCoverLoaded = { mangaCover -> onCoverLoaded(mangaCover) }, + // KMK --> + if (usePanoramaCover && coverRatio.floatValue <= RatioSwitchToPanorama) { + MangaCover.Panorama( + modifier = Modifier.fillMaxWidth(0.65f), + data = ImageRequest.Builder(LocalContext.current) + .data(manga) + .crossfade(true) + .build(), + contentDescription = stringResource(MR.strings.manga_cover), + onClick = onCoverClick, + // KMK --> + onCoverLoaded = { mangaCover, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + onCoverLoaded(mangaCover) + }, + // KMK <-- + ) + } else { // KMK <-- - ) + MangaCover.Book( + modifier = Modifier.fillMaxWidth(0.65f), + data = ImageRequest.Builder(LocalContext.current) + .data(manga) + .crossfade(true) + .build(), + contentDescription = stringResource(MR.strings.manga_cover), + onClick = onCoverClick, + // KMK --> + onCoverLoaded = { mangaCover, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + onCoverLoaded(mangaCover) + }, + // KMK <-- + ) + } Spacer(modifier = Modifier.height(16.dp)) MangaContentInfo( title = manga.title, @@ -508,6 +551,8 @@ private fun MangaAndSourceTitlesSmall( librarySearch: (query: String) -> Unit, onSourceClick: () -> Unit, onCoverLoaded: (DomainMangaCover) -> Unit, + coverRatio: MutableFloatState, + usePanoramaCover: Boolean = false, // KMK <-- ) { Row( @@ -517,20 +562,51 @@ private fun MangaAndSourceTitlesSmall( horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically, ) { - MangaCover.Book( - modifier = Modifier - .sizeIn(maxWidth = 100.dp) - .align(Alignment.Top), - data = ImageRequest.Builder(LocalContext.current) - .data(manga) - .crossfade(true) - .build(), - contentDescription = stringResource(MR.strings.manga_cover), - onClick = onCoverClick, - // KMK --> - onCoverLoaded = { mangaCover -> onCoverLoaded(mangaCover) }, + // KMK --> + if (usePanoramaCover && coverRatio.floatValue <= RatioSwitchToPanorama) { + MangaCover.Panorama( + modifier = Modifier + .sizeIn(maxHeight = 100.dp) + // KMK --> + .align(Alignment.CenterVertically), + // KMK <-- + data = ImageRequest.Builder(LocalContext.current) + .data(manga) + .crossfade(true) + .build(), + contentDescription = stringResource(MR.strings.manga_cover), + onClick = onCoverClick, + // KMK --> + onCoverLoaded = { mangaCover, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + onCoverLoaded(mangaCover) + }, + // KMK <-- + ) + } else { // KMK <-- - ) + MangaCover.Book( + modifier = Modifier + .sizeIn(maxWidth = 100.dp) + // KMK --> + .align(Alignment.CenterVertically), + // KMK <-- + data = ImageRequest.Builder(LocalContext.current) + .data(manga) + .crossfade(true) + .build(), + contentDescription = stringResource(MR.strings.manga_cover), + onClick = onCoverClick, + // KMK --> + onCoverLoaded = { mangaCover, result -> + val image = result.result.image + coverRatio.floatValue = image.height.toFloat() / image.width + onCoverLoaded(mangaCover) + }, + // KMK <-- + ) + } Column( verticalArrangement = Arrangement.spacedBy(2.dp), ) { diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt index 48c388999..9e8e6da29 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsAppearanceScreen.kt @@ -272,6 +272,13 @@ object SettingsAppearanceScreen : SearchableSettings { return Preference.PreferenceGroup( stringResource(SYMR.strings.pref_category_fork), preferenceItems = persistentListOf( + // KMK --> + Preference.PreferenceItem.SwitchPreference( + pref = uiPreferences.usePanoramaCover(), + title = stringResource(KMR.strings.pref_use_panorama_cover), + subtitle = stringResource(KMR.strings.pref_use_panorama_cover_summary), + ), + // KMK <-- Preference.PreferenceItem.SwitchPreference( pref = uiPreferences.expandFilters(), title = stringResource(SYMR.strings.toggle_expand_search_filters), diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverMetadata.kt b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverMetadata.kt index 237d3bcb3..99ed9d10a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverMetadata.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/coil/MangaCoverMetadata.kt @@ -160,5 +160,5 @@ object MangaCoverMetadata { preferences.coverColors().set(mapColorCopy.map { "${it.key}|${it.value.first}|${it.value.second}" }.toSet()) } - const val SUB_SAMPLE = 4 + private const val SUB_SAMPLE = 4 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt index aaddd4ddd..8fbb04a5e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/EditMangaDialog.kt @@ -21,6 +21,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableFloatState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -37,6 +38,7 @@ import coil3.transform.RoundedCornersTransformation import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipGroup import com.google.android.material.dialog.MaterialAlertDialogBuilder +import eu.kanade.presentation.manga.components.RatioSwitchToPanorama import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.databinding.EditMangaDialogBinding import eu.kanade.tachiyomi.source.model.SManga @@ -62,6 +64,9 @@ import tachiyomi.source.local.isLocal @Composable fun EditMangaDialog( manga: Manga, + // KMK --> + coverRatio: MutableFloatState, + // KMK <-- onDismissRequest: () -> Unit, onPositiveClick: ( title: String?, @@ -145,6 +150,7 @@ fun EditMangaDialog( scope, // KMK --> colors, + coverRatio = coverRatio, // KMK <-- ) } @@ -178,9 +184,16 @@ private fun onViewCreated( scope: CoroutineScope, // KMK --> colors: EditMangaDialogColors, + coverRatio: MutableFloatState, // KMK <-- ) { - loadCover(manga, binding) + loadCover( + manga, + binding, + // KMK --> + coverRatio, + // KMK <-- + ) // KMK --> // val statusAdapter: ArrayAdapter = ArrayAdapter( @@ -344,9 +357,25 @@ private fun resetTags( } } -private fun loadCover(manga: Manga, binding: EditMangaDialogBinding) { - binding.mangaCover.load(manga) { - transformations(RoundedCornersTransformation(4.dpToPx.toFloat())) +private fun loadCover( + manga: Manga, + binding: EditMangaDialogBinding, + // KMK --> + coverRatio: MutableFloatState, + // KMK <-- +) { + // KMK --> + if (coverRatio.floatValue <= RatioSwitchToPanorama) { + binding.mangaCover.visibility = View.GONE + binding.mangaCoverPanorama.visibility = View.VISIBLE + binding.mangaCoverPanorama.load(manga) { + transformations(RoundedCornersTransformation(4.dpToPx.toFloat())) + } + } else { + // KMK <-- + binding.mangaCover.load(manga) { + transformations(RoundedCornersTransformation(4.dpToPx.toFloat())) + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index f2613c083..18a663779 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -262,6 +263,7 @@ class MangaScreen( // SY <-- // KMK --> + val coverRatio = remember { mutableFloatStateOf(1f) } val hazeState = remember { HazeState() } // KMK <-- @@ -402,6 +404,7 @@ class MangaScreen( onCoverLoaded = { if (screenModel.themeCoverBased || successState.manga.favorite) screenModel.setPaletteColor(it) }, + coverRatio = coverRatio, onPaletteScreenClick = { navigator.push(PaletteScreen(successState.seedColor?.toArgb())) }, hazeState = hazeState, // KMK <-- @@ -537,6 +540,9 @@ class MangaScreen( is MangaScreenModel.Dialog.EditMangaInfo -> { EditMangaDialog( manga = dialog.manga, + // KMK --> + coverRatio = coverRatio, + // KMK <-- onDismissRequest = screenModel::dismissDialog, onPositiveClick = screenModel::updateMangaInfo, ) diff --git a/app/src/main/res/layout/edit_manga_dialog.xml b/app/src/main/res/layout/edit_manga_dialog.xml index 877bcd4f7..353dcaaff 100644 --- a/app/src/main/res/layout/edit_manga_dialog.xml +++ b/app/src/main/res/layout/edit_manga_dialog.xml @@ -20,6 +20,17 @@ android:background="@drawable/rounded_rectangle" android:scaleType="centerCrop" android:src="@mipmap/ic_launcher" /> + + + -