feat(webtoon): Adaptive scale for wider screen size (#1130)
* feat: Add webtoon scale type option This commit introduces a new setting for webtoon reading mode that allows users to select a scale type for webtoon images. The available scale types are: - Fit Screen - 16:9 - 20:9 - 4:3 - 3:2 This setting can be found in both the reader settings sheet and the global reader settings. * mutable * add to Reader's settings page & fix size calculation * option for long strip gap preference * fix: ensure recycler scales to default when screen ratio is less than desired * hide smart scale for long strip gap preference * Avoid zero division * using forEach * improve readability * better viewer size calculation & optimize list
This commit is contained in:
parent
9f7309fd2a
commit
a0e2d2942a
7 changed files with 121 additions and 19 deletions
|
|
@ -11,6 +11,7 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderBottomButton
|
|||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences.Companion.zoomWideImagesAllowedList
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences.WebtoonScaleType
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
|
@ -258,8 +259,10 @@ object SettingsReaderScreen : SearchableSettings {
|
|||
val rotateToFit by rotateToFitPref.collectAsState()
|
||||
|
||||
// KMK -->
|
||||
val pagedDisableZoomIn by readerPreferences.pagedDisableZoomIn().collectAsState()
|
||||
val landscapeZoom by readerPreferences.landscapeZoom().collectAsState()
|
||||
val pagedDisableZoomInPref = readerPreferences.pagedDisableZoomIn()
|
||||
val landscapeZoomPref = readerPreferences.landscapeZoom()
|
||||
val pagedDisableZoomIn by pagedDisableZoomInPref.collectAsState()
|
||||
val landscapeZoom by landscapeZoomPref.collectAsState()
|
||||
// KMK <--
|
||||
|
||||
return Preference.PreferenceGroup(
|
||||
|
|
@ -313,7 +316,7 @@ object SettingsReaderScreen : SearchableSettings {
|
|||
),
|
||||
// SY <--
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
preference = readerPreferences.landscapeZoom(),
|
||||
preference = landscapeZoomPref,
|
||||
title = stringResource(MR.strings.pref_landscape_zoom),
|
||||
// KMK -->
|
||||
enabled = imageScaleType in zoomWideImagesAllowedList,
|
||||
|
|
@ -322,17 +325,14 @@ object SettingsReaderScreen : SearchableSettings {
|
|||
// KMK -->
|
||||
Preference.PreferenceItem.ListPreference(
|
||||
preference = readerPreferences.landscapeZoomType(),
|
||||
entries = persistentListOf(
|
||||
ReaderPreferences.LandscapeZoomScaleType.FIT,
|
||||
ReaderPreferences.LandscapeZoomScaleType.DOUBLE,
|
||||
)
|
||||
entries = ReaderPreferences.LandscapeZoomScaleType.entries
|
||||
.associateWith { stringResource(it.titleRes) }
|
||||
.toImmutableMap(),
|
||||
title = stringResource(KMR.strings.pref_landscape_zoom_type),
|
||||
enabled = landscapeZoom && imageScaleType in zoomWideImagesAllowedList,
|
||||
),
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
preference = readerPreferences.pagedDisableZoomIn(),
|
||||
preference = pagedDisableZoomInPref,
|
||||
title = stringResource(KMR.strings.pref_paged_disable_zoom_in),
|
||||
),
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
|
|
@ -415,6 +415,19 @@ object SettingsReaderScreen : SearchableSettings {
|
|||
title = stringResource(MR.strings.pref_read_with_tapping_inverted),
|
||||
enabled = navMode != 5,
|
||||
),
|
||||
// KMK -->
|
||||
Preference.PreferenceItem.ListPreference(
|
||||
preference = readerPreferences.webtoonScaleType(),
|
||||
entries = WebtoonScaleType.entries
|
||||
.associateWith { stringResource(it.titleRes) }
|
||||
.toImmutableMap(),
|
||||
title = stringResource(KMR.strings.pref_webtoon_scale_type),
|
||||
),
|
||||
Preference.PreferenceItem.SwitchPreference(
|
||||
preference = readerPreferences.longStripGapSmartScale(),
|
||||
title = stringResource(KMR.strings.pref_smart_scale_long_strip_gap),
|
||||
),
|
||||
// KMK <--
|
||||
Preference.PreferenceItem.SliderPreference(
|
||||
value = webtoonSidePadding,
|
||||
valueRange = ReaderPreferences.let {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
|
||||
import eu.kanade.domain.manga.model.readerOrientation
|
||||
import eu.kanade.domain.manga.model.readingMode
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderOrientation
|
||||
|
|
@ -55,7 +54,12 @@ internal fun ReadingModePage(screenModel: ReaderSettingsScreenModel) {
|
|||
|
||||
val viewer by screenModel.viewerFlow.collectAsState()
|
||||
if (viewer is WebtoonViewer) {
|
||||
WebtoonViewerSettings(screenModel)
|
||||
WebtoonViewerSettings(
|
||||
screenModel,
|
||||
// KMK -->
|
||||
readingMode,
|
||||
// KMK <--
|
||||
)
|
||||
// SY -->
|
||||
WebtoonWithGapsViewerSettings(screenModel)
|
||||
// SY <--
|
||||
|
|
@ -125,11 +129,7 @@ private fun PagerViewerSettings(screenModel: ReaderSettingsScreenModel) {
|
|||
)
|
||||
|
||||
// KMK -->
|
||||
if (imageScaleType in listOf(
|
||||
SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE,
|
||||
SubsamplingScaleImageView.SCALE_TYPE_ORIGINAL_SIZE,
|
||||
)
|
||||
) {
|
||||
if (imageScaleType in ReaderPreferences.zoomWideImagesAllowedList) {
|
||||
// KMK <--
|
||||
CheckboxItem(
|
||||
label = stringResource(MR.strings.pref_landscape_zoom),
|
||||
|
|
@ -207,7 +207,12 @@ private fun PagerViewerSettings(screenModel: ReaderSettingsScreenModel) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun WebtoonViewerSettings(screenModel: ReaderSettingsScreenModel) {
|
||||
private fun WebtoonViewerSettings(
|
||||
screenModel: ReaderSettingsScreenModel,
|
||||
// KMK -->
|
||||
readingMode: ReadingMode,
|
||||
// KMK <--
|
||||
) {
|
||||
val numberFormat = remember { NumberFormat.getPercentInstance() }
|
||||
|
||||
HeadingItem(MR.strings.webtoon_viewer)
|
||||
|
|
@ -221,6 +226,23 @@ private fun WebtoonViewerSettings(screenModel: ReaderSettingsScreenModel) {
|
|||
onSelectInvertMode = screenModel.preferences.webtoonNavInverted()::set,
|
||||
)
|
||||
|
||||
// KMK -->
|
||||
val webtoonScaleTypePref = screenModel.preferences.webtoonScaleType()
|
||||
val webtoonScaleType by webtoonScaleTypePref.collectAsState()
|
||||
val webtoonSmartScaleLongStripGap = screenModel.preferences.longStripGapSmartScale().get()
|
||||
if (readingMode != ReadingMode.CONTINUOUS_VERTICAL || webtoonSmartScaleLongStripGap) {
|
||||
SettingsChipRow(KMR.strings.pref_webtoon_scale_type) {
|
||||
ReaderPreferences.WebtoonScaleType.entries.forEach { scaleType ->
|
||||
FilterChip(
|
||||
selected = webtoonScaleType == scaleType,
|
||||
onClick = { webtoonScaleTypePref.set(scaleType) },
|
||||
label = { Text(stringResource(scaleType.titleRes)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
val webtoonSidePadding by screenModel.preferences.webtoonSidePadding().collectAsState()
|
||||
SliderItem(
|
||||
value = webtoonSidePadding,
|
||||
|
|
|
|||
|
|
@ -63,6 +63,11 @@ class ReaderPreferences(
|
|||
|
||||
fun imageScaleType() = preferenceStore.getInt("pref_image_scale_type_key", 1)
|
||||
|
||||
// KMK -->
|
||||
fun webtoonScaleType() = preferenceStore.getEnum("pref_webtoon_scale_type_key", WebtoonScaleType.FIT)
|
||||
fun longStripGapSmartScale() = preferenceStore.getBoolean("pref_webtoon_smart_scale_long_strip_gap", false)
|
||||
// KMK <--
|
||||
|
||||
fun zoomStart() = preferenceStore.getInt("pref_zoom_start_key", 1)
|
||||
|
||||
fun readerTheme() = preferenceStore.getInt("pref_reader_theme_key", 1)
|
||||
|
|
@ -237,6 +242,17 @@ class ReaderPreferences(
|
|||
FIT(MR.strings.scale_type_fit_screen),
|
||||
DOUBLE(KMR.strings.scale_type_double),
|
||||
}
|
||||
|
||||
enum class WebtoonScaleType(
|
||||
val titleRes: StringResource,
|
||||
val ratio: Float,
|
||||
) {
|
||||
FIT(MR.strings.scale_type_fit_screen, 0f),
|
||||
R4_3(KMR.strings.scale_type_4_3, 3f / 4f),
|
||||
R3_2(KMR.strings.scale_type_3_2, 2f / 3f),
|
||||
R16_9(KMR.strings.scale_type_16_9, 9f / 16f),
|
||||
R20_9(KMR.strings.scale_type_20_9, 9f / 20f),
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
object ArchiveReaderMode {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,11 @@ class WebtoonConfig(
|
|||
private set
|
||||
|
||||
var pinchToZoomChangedListener: ((Boolean) -> Unit)? = null
|
||||
|
||||
var webtoonScaleType = readerPreferences.webtoonScaleType().get()
|
||||
private set
|
||||
|
||||
var webtoonScaleTypeChangedListener: ((ReaderPreferences.WebtoonScaleType) -> Unit)? = null
|
||||
// KMK <--
|
||||
|
||||
// SY -->
|
||||
|
|
@ -115,6 +120,12 @@ class WebtoonConfig(
|
|||
{ pinchToZoom = it },
|
||||
{ pinchToZoomChangedListener?.invoke(it) },
|
||||
)
|
||||
|
||||
readerPreferences.webtoonScaleType()
|
||||
.register(
|
||||
{ webtoonScaleType = it },
|
||||
{ webtoonScaleTypeChangedListener?.invoke(it) },
|
||||
)
|
||||
// KMK <--
|
||||
|
||||
readerPreferences.readerTheme().changes()
|
||||
|
|
|
|||
|
|
@ -180,9 +180,13 @@ class WebtoonRecyclerView @JvmOverloads constructor(
|
|||
fun onScale(scaleFactor: Float) {
|
||||
// KMK ->
|
||||
if (!detector.isQuickScaling && !pinchToZoom) return
|
||||
// KMK <--
|
||||
|
||||
currentScale *= scaleFactor
|
||||
scaleTo(currentScale * scaleFactor)
|
||||
}
|
||||
|
||||
fun scaleTo(scale: Float) {
|
||||
// KMK <--
|
||||
currentScale = scale
|
||||
currentScale = currentScale.coerceIn(
|
||||
minRate,
|
||||
MAX_SCALE_RATE,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
|
|||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.Viewer
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion
|
||||
import exh.util.nullIfZero
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
|
|
@ -40,6 +41,7 @@ class WebtoonViewer(
|
|||
private val tapByPage: Boolean = false,
|
||||
// KMK -->
|
||||
@ColorInt private val seedColor: Int? = null,
|
||||
private val readerPreferences: ReaderPreferences = Injekt.get(),
|
||||
// KMK <--
|
||||
) : Viewer {
|
||||
|
||||
|
|
@ -89,7 +91,9 @@ class WebtoonViewer(
|
|||
var currentPage: Any? = null
|
||||
|
||||
private val threshold: Int =
|
||||
Injekt.get<ReaderPreferences>()
|
||||
// KMK -->
|
||||
readerPreferences
|
||||
// KMK <--
|
||||
.readerHideThreshold()
|
||||
.get()
|
||||
.threshold
|
||||
|
|
@ -173,6 +177,32 @@ class WebtoonViewer(
|
|||
config.pinchToZoomChangedListener = {
|
||||
frame.pinchToZoom = it
|
||||
}
|
||||
|
||||
config.webtoonScaleTypeChangedListener = f@{ scaleType ->
|
||||
if (!isContinuous && !readerPreferences.longStripGapSmartScale().get()) return@f
|
||||
|
||||
recycler.post {
|
||||
if (scaleType == ReaderPreferences.WebtoonScaleType.FIT) {
|
||||
recycler.scaleTo(1f)
|
||||
return@post
|
||||
}
|
||||
|
||||
// Call `scaleTo` after the view is loaded and visible
|
||||
val currentWidth = recycler.width.takeIf { it > 0 } ?: activity.window.decorView.width.nullIfZero() ?: return@post
|
||||
val currentHeight = recycler.originalHeight.takeIf { it > 0 } ?: activity.window.decorView.height.nullIfZero() ?: return@post
|
||||
|
||||
val desiredRatio = scaleType.ratio
|
||||
val screenRatio = currentWidth.toFloat() / currentHeight
|
||||
val desiredWidth = currentHeight * desiredRatio
|
||||
val desiredScale = desiredWidth / currentWidth
|
||||
|
||||
if (screenRatio > desiredRatio) {
|
||||
recycler.scaleTo(desiredScale)
|
||||
} else {
|
||||
recycler.scaleTo(1f)
|
||||
}
|
||||
}
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
config.zoomPropertyChangedListener = {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,12 @@
|
|||
<string name="pref_paged_disable_zoom_in">Disable zoom in</string>
|
||||
<string name="pref_landscape_zoom_type">Wide images zoom mode</string>
|
||||
<string name="scale_type_double">Double zoom</string>
|
||||
<string name="pref_webtoon_scale_type">Smart scale on wide screen</string>
|
||||
<string name="pref_smart_scale_long_strip_gap">Apply Smart scale to Long strip with gaps</string>
|
||||
<string name="scale_type_16_9">16:9</string>
|
||||
<string name="scale_type_20_9">20:9</string>
|
||||
<string name="scale_type_4_3">4:3</string>
|
||||
<string name="scale_type_3_2">3:2</string>
|
||||
|
||||
<!-- Preferences -->
|
||||
<!-- Appearance section -->
|
||||
|
|
|
|||
Loading…
Reference in a new issue