From 9079ad8c9ad57a3760eb87ccf56c15eb52d17017 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Tue, 27 Oct 2026 05:15:38 +0600 Subject: [PATCH] Handle reader input and state in the activity (#4192) --- .../reader/ReaderNavigationOverlay.kt | 6 +- .../tachiyomi/ui/reader/ReaderActivity.kt | 178 +++++++++++------- .../tachiyomi/ui/reader/ReaderViewModel.kt | 11 -- .../reader/setting/ReaderSettingsViewModel.kt | 5 +- .../tachiyomi/ui/reader/viewer/Viewer.kt | 34 +++- .../ui/reader/viewer/ViewerNavigation.kt | 74 +++++--- .../ui/reader/viewer/ViewerSettings.kt | 17 +- .../viewer/navigation/DisabledNavigation.kt | 5 +- .../viewer/navigation/EdgeNavigation.kt | 10 +- .../viewer/navigation/KindlishNavigation.kt | 11 +- .../reader/viewer/navigation/LNavigation.kt | 12 +- .../navigation/RightAndLeftNavigation.kt | 11 +- .../ui/reader/viewer/pager/PagerViewer.kt | 117 ++---------- .../ui/reader/viewer/pager/PagerViewers.kt | 14 +- .../ui/reader/viewer/webgpu/WebGpuViewer.kt | 90 +-------- .../viewer/webgpu/WebGpuViewerContinuous.kt | 3 +- .../ui/reader/viewer/webtoon/WebtoonViewer.kt | 64 +------ .../tachiyomi/util/lang/RectFExtensions.kt | 15 -- 18 files changed, 251 insertions(+), 426 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/util/lang/RectFExtensions.kt diff --git a/app/src/main/java/eu/kanade/presentation/reader/ReaderNavigationOverlay.kt b/app/src/main/java/eu/kanade/presentation/reader/ReaderNavigationOverlay.kt index a370ede2e8..285140bfa6 100644 --- a/app/src/main/java/eu/kanade/presentation/reader/ReaderNavigationOverlay.kt +++ b/app/src/main/java/eu/kanade/presentation/reader/ReaderNavigationOverlay.kt @@ -49,10 +49,10 @@ fun ReaderNavigationOverlay( Canvas(modifier = Modifier.fillMaxSize()) { regions.forEachIndexed { index, region -> - val rect = region.rectF + val rect = region.rect val topLeft = Offset(size.width * rect.left, size.height * rect.top) - val regionSize = Size(size.width * rect.width(), size.height * rect.height()) - drawRect(color = Color(region.type.color), topLeft = topLeft, size = regionSize) + val regionSize = Size(size.width * rect.width, size.height * rect.height) + drawRect(color = region.type.color, topLeft = topLeft, size = regionSize) val text = textMeasurer.measure(labels[index], textStyle) val textTopLeft = Offset( diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt index c4998e3fac..6924116bf3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt @@ -8,13 +8,12 @@ import android.content.Intent import android.net.Uri import android.os.Build import android.os.Bundle -import android.view.KeyEvent -import android.view.MotionEvent import android.view.WindowManager import android.widget.Toast import androidx.activity.enableEdgeToEdge import androidx.activity.viewModels import androidx.compose.foundation.background +import androidx.compose.foundation.focusable import androidx.compose.foundation.gestures.awaitEachGesture import androidx.compose.foundation.gestures.awaitFirstDown import androidx.compose.foundation.layout.Arrangement @@ -31,6 +30,7 @@ import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.key @@ -39,9 +39,19 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorMatrix +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEvent +import androidx.compose.ui.input.key.KeyEventType +import androidx.compose.ui.input.key.isCtrlPressed +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onPreviewKeyEvent +import androidx.compose.ui.input.key.type import androidx.compose.ui.input.pointer.PointerEventPass import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.keepScreenOn @@ -84,6 +94,8 @@ import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsViewModel import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import eu.kanade.tachiyomi.ui.reader.viewer.LocalPageColorFilter import eu.kanade.tachiyomi.ui.reader.viewer.Viewer +import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.Action +import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import eu.kanade.tachiyomi.ui.webview.WebViewActivity import eu.kanade.tachiyomi.util.system.openInBrowser import eu.kanade.tachiyomi.util.system.readerBackgroundColor @@ -184,16 +196,33 @@ class ReaderActivity : BaseActivity() { val colorOverlayEnabled by readerPreferences.colorFilter.collectAsState() val colorOverlay by readerPreferences.colorFilterValue.collectAsState() val colorOverlayMode by readerPreferences.colorFilterMode.collectAsState() + val brightness by settingsViewModel.brightness.collectAsState() + val navigation = settingsViewModel.navigation.collectAsState().value val backgroundColor = remember(general.theme) { Color(baseContext.readerBackgroundColor(general.theme)) } val colorOverlayBlendMode = remember(colorOverlayMode) { ReaderPreferences.ColorFilterMode.getOrNull(colorOverlayMode)?.second } + LaunchedEffect(brightness) { setWindowBrightness(brightness) } + + val forceNavigationOverlay = remember { settingsViewModel.takeNavigationOverlayForNewUser() } + LaunchedEffect(navigation) { + if (navigation == null) return@LaunchedEffect + val showOnStart = forceNavigationOverlay || settingsViewModel.general.value.navigationOverlayOnStart + navigationOverlayState.setNavigation(navigation, showOnStart) + } + + val focusRequester = remember { FocusRequester() } + LaunchedEffect(Unit) { focusRequester.requestFocus() } + Box( modifier = Modifier .fillMaxSize() .background(backgroundColor) + .onPreviewKeyEvent(::onKeyEvent) + .focusRequester(focusRequester) + .focusable() .pointerInput(Unit) { awaitEachGesture { awaitFirstDown(requireUnconsumed = false, pass = PointerEventPass.Initial) @@ -210,7 +239,7 @@ class ReaderActivity : BaseActivity() { } ReaderContentOverlay( - brightness = state.brightnessOverlayValue, + brightness = brightness, color = colorOverlay.takeIf { colorOverlayEnabled }, colorBlendMode = colorOverlayBlendMode, ) @@ -234,23 +263,24 @@ class ReaderActivity : BaseActivity() { AppBars(state = state) } + if (state.isLoadingAdjacentChapter) { + AlertDialog( + onDismissRequest = {}, + confirmButton = {}, + text = { + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + CircularProgressIndicator() + Text(stringResource(MR.strings.loading)) + } + }, + ) + } + val onDismissRequest = viewModel::closeDialog when (state.dialog) { - is ReaderViewModel.Dialog.Loading -> { - AlertDialog( - onDismissRequest = {}, - confirmButton = {}, - text = { - Row( - horizontalArrangement = Arrangement.spacedBy(16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - CircularProgressIndicator() - Text(stringResource(MR.strings.loading)) - } - }, - ) - } is ReaderViewModel.Dialog.Settings -> { ReaderSettingsDialog( onDismissRequest = onDismissRequest, @@ -304,17 +334,6 @@ class ReaderActivity : BaseActivity() { Notifications.ID_NEW_CHAPTERS, ) - settingsViewModel.brightness - .onEach(::setCustomBrightnessValue) - .launchIn(lifecycleScope) - val forceNavigationOverlay = settingsViewModel.takeNavigationOverlayForNewUser() - settingsViewModel.navigation - .filterNotNull() - .onEach { navigation -> - val showOnStart = forceNavigationOverlay || settingsViewModel.general.value.navigationOverlayOnStart - navigationOverlayState.setNavigation(navigation, showOnStart) - } - .launchIn(lifecycleScope) setMenuVisibility(viewModel.state.value.menuVisible) // Finish when incognito mode is disabled @@ -330,12 +349,6 @@ class ReaderActivity : BaseActivity() { .onEach(::setInitialChapterError) .launchIn(lifecycleScope) - viewModel.state - .map { it.isLoadingAdjacentChapter } - .distinctUntilChanged() - .onEach(::setProgressDialog) - .launchIn(lifecycleScope) - viewModel.state .map { it.manga } .distinctUntilChanged() @@ -464,29 +477,66 @@ class ReaderActivity : BaseActivity() { } } - override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { - if (keyCode == KeyEvent.KEYCODE_N) { - loadNextChapter() - return true - } else if (keyCode == KeyEvent.KEYCODE_P) { - loadPreviousChapter() - return true + /** + * Handles a key [event] before anything else in the reader. Movement happens on key up. + */ + private fun onKeyEvent(event: KeyEvent): Boolean { + val viewer = viewer ?: return false + val navigation = settingsViewModel.navigation.value ?: return false + val isUp = event.type == KeyEventType.KeyUp + val action = when (event.key) { + Key.VolumeDown, Key.VolumeUp -> { + val general = settingsViewModel.general.value + if (!general.volumeKeysEnabled || viewModel.state.value.menuVisible) return false + if ((event.key == Key.VolumeDown) != general.volumeKeysInverted) Action.FORWARD else Action.BACKWARD + } + Key.DirectionRight -> if (event.isCtrlPressed) { + Action.FORWARD + } else { + navigation.actionOf( + NavigationRegion.RIGHT, + ) + } + Key.DirectionLeft -> if (event.isCtrlPressed) { + Action.BACKWARD + } else { + navigation.actionOf( + NavigationRegion.LEFT, + ) + } + Key.DirectionDown, Key.PageDown -> Action.FORWARD + Key.DirectionUp, Key.PageUp -> Action.BACKWARD + Key.Menu -> Action.MENU + Key.N -> { + if (isUp) loadNextChapter() + return true + } + Key.P -> { + if (isUp) loadPreviousChapter() + return true + } + else -> return false } - return super.onKeyUp(keyCode, event) - } - - override fun dispatchKeyEvent(event: KeyEvent): Boolean { - val handled = viewer?.handleKeyEvent(event) ?: false - return handled || super.dispatchKeyEvent(event) + if (isUp) { + when (action) { + Action.MENU -> toggleMenu() + Action.FORWARD -> viewer.moveForward() + Action.BACKWARD -> viewer.moveBackward() + } + } + return true } /** - * Dispatches a generic motion event. If the viewer doesn't handle it, call the default - * implementation. + * Called from the viewer when it's tapped at [pos], given as a fraction of its size. */ - override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { - val handled = viewer?.handleGenericMotionEvent(event) ?: false - return handled || super.dispatchGenericMotionEvent(event) + fun onViewerTap(pos: Offset) { + val action = settingsViewModel.navigation.value?.getAction(pos) ?: Action.MENU + when (action) { + Action.MENU -> toggleMenu() + Action.FORWARD -> viewer?.moveForward() + Action.BACKWARD -> viewer?.moveBackward() + } } @Composable @@ -666,20 +716,6 @@ class ReaderActivity : BaseActivity() { toast(error.message) } - /** - * Called from the presenter whenever it's loading the next or previous chapter. It shows or - * dismisses a non-cancellable dialog to prevent user interaction according to the value of - * [show]. This is only used when the next/previous buttons on the toolbar are clicked; the - * other cases are handled with chapter transitions on the viewers and chapter preloading. - */ - private fun setProgressDialog(show: Boolean) { - if (show) { - viewModel.showLoadingDialog() - } else { - viewModel.closeDialog() - } - } - /** * Moves the viewer to the given page [index]. It does nothing if the viewer is null or the * page is not found. @@ -825,11 +861,11 @@ class ReaderActivity : BaseActivity() { /** * Sets the brightness of the screen. Range is [-75, 100]. - * From -75 to -1 a semi-transparent black view is overlaid with the minimum brightness. + * From -75 to -1 the minimum brightness is used, and the overlay darkens further. * From 1 to 100 it sets that value as brightness. - * 0 sets system brightness and hides the overlay. + * 0 sets system brightness. */ - private fun setCustomBrightnessValue(value: Int) { + private fun setWindowBrightness(value: Int) { // Calculate and set reader brightness. val readerBrightness = when { value > 0 -> { @@ -841,8 +877,6 @@ class ReaderActivity : BaseActivity() { else -> WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE } window.attributes = window.attributes.apply { screenBrightness = readerBrightness } - - viewModel.setBrightnessOverlayValue(value) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 099a2771a7..a719fdcc08 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.reader import android.content.Context import android.net.Uri -import androidx.annotation.IntRange import androidx.compose.runtime.Immutable import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel @@ -913,10 +912,6 @@ class ReaderViewModel( mutableState.update { it.copy(menuVisible = visible) } } - fun showLoadingDialog() { - mutableState.update { it.copy(dialog = Dialog.Loading) } - } - fun openReadingModeSelectDialog() { mutableState.update { it.copy(dialog = Dialog.ReadingModeSelect) } } @@ -937,10 +932,6 @@ class ReaderViewModel( mutableState.update { it.copy(dialog = null) } } - fun setBrightnessOverlayValue(value: Int) { - mutableState.update { it.copy(brightnessOverlayValue = value) } - } - /** * Saves the image of the selected page on the pictures directory and notifies the UI of the result. * There's also a notification to allow sharing the image somewhere else or deleting it. @@ -1105,7 +1096,6 @@ class ReaderViewModel( val currentPage: Int = -1, val dialog: Dialog? = null, val menuVisible: Boolean = false, - @IntRange(from = -100, to = 100) val brightnessOverlayValue: Int = 0, ) { val currentChapter: ReaderChapter? get() = viewerChapters?.currChapter @@ -1115,7 +1105,6 @@ class ReaderViewModel( } sealed interface Dialog { - data object Loading : Dialog data object Settings : Dialog data object ReadingModeSelect : Dialog data object OrientationModeSelect : Dialog diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsViewModel.kt index 511566581c..1765995ad6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsViewModel.kt @@ -267,13 +267,14 @@ class ReaderSettingsViewModel( } else { paged.map { it.navigation } } - val defaultNavigation: (ReaderPreferences.TappingInvertMode) -> ViewerNavigation = + val isReversed = viewerMode.readingMode == ReadingMode.RIGHT_TO_LEFT + val defaultNavigation: (ReaderPreferences.TappingInvertMode, Boolean) -> ViewerNavigation = if (viewerMode.readingMode.direction == ReadingMode.Direction.Vertical) { ::LNavigation } else { ::RightAndLeftNavigation } - settings.distinctUntilChanged().map { it.navigator(defaultNavigation) } + settings.distinctUntilChanged().map { it.navigator(isReversed, defaultNavigation) } } .stateIn(viewModelScope, SharingStarted.Eagerly, null) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/Viewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/Viewer.kt index d58a21d7e4..75df181dc9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/Viewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/Viewer.kt @@ -1,11 +1,12 @@ package eu.kanade.tachiyomi.ui.reader.viewer -import android.view.KeyEvent -import android.view.MotionEvent import androidx.compose.runtime.Composable import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.input.pointer.PointerEventPass +import androidx.compose.ui.input.pointer.PointerEventType +import androidx.compose.ui.input.pointer.pointerInput import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters @@ -36,16 +37,14 @@ interface Viewer { fun moveToPage(page: ReaderPage) /** - * Called from the containing activity when a key [event] is received. It should return true - * if the event was handled, false otherwise. + * Moves forward through the pages. */ - fun handleKeyEvent(event: KeyEvent): Boolean + fun moveForward() /** - * Called from the containing activity when a generic motion [event] is received. It should - * return true if the event was handled, false otherwise. + * Moves backward through the pages. */ - fun handleGenericMotionEvent(event: MotionEvent): Boolean + fun moveBackward() } /** @@ -53,3 +52,22 @@ interface Viewer { * theirs. Null when both are off. */ val LocalPageColorFilter = compositionLocalOf { null } + +/** + * Turns pages with the mouse wheel instead of letting the content scroll. + */ +fun Modifier.mouseWheelPaging(viewer: Viewer): Modifier = pointerInput(viewer) { + awaitPointerEventScope { + while (true) { + val event = awaitPointerEvent(PointerEventPass.Initial) + if (event.type != PointerEventType.Scroll) continue + val delta = event.changes.first().scrollDelta.y + when { + delta > 0 -> viewer.moveForward() + delta < 0 -> viewer.moveBackward() + else -> continue + } + event.changes.forEach { it.consume() } + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt index 1490a2e458..baec89c801 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerNavigation.kt @@ -1,37 +1,50 @@ package eu.kanade.tachiyomi.ui.reader.viewer -import android.graphics.Color -import android.graphics.PointF -import android.graphics.RectF +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Rect +import androidx.compose.ui.graphics.Color import dev.icerock.moko.resources.StringResource import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences -import eu.kanade.tachiyomi.util.lang.invert import tachiyomi.i18n.MR -abstract class ViewerNavigation(val invertMode: ReaderPreferences.TappingInvertMode) { +/** + * Tap zones of a viewer. [isReversed] is set for right to left books, where the right side of the + * screen goes back. + */ +abstract class ViewerNavigation( + val invertMode: ReaderPreferences.TappingInvertMode, + val isReversed: Boolean, +) { - sealed class NavigationRegion(val nameRes: StringResource, val color: Int) { - data object MENU : NavigationRegion(MR.strings.action_menu, Color.argb(0xCC, 0x95, 0x81, 0x8D)) - data object PREV : NavigationRegion(MR.strings.nav_zone_prev, Color.argb(0xCC, 0xFF, 0x77, 0x33)) - data object NEXT : NavigationRegion(MR.strings.nav_zone_next, Color.argb(0xCC, 0x84, 0xE2, 0x96)) - data object LEFT : NavigationRegion(MR.strings.nav_zone_left, Color.argb(0xCC, 0x7D, 0x11, 0x28)) - data object RIGHT : NavigationRegion(MR.strings.nav_zone_right, Color.argb(0xCC, 0xA6, 0xCF, 0xD5)) + enum class Action { MENU, FORWARD, BACKWARD } + + sealed class NavigationRegion(val nameRes: StringResource, val color: Color) { + data object MENU : NavigationRegion(MR.strings.action_menu, Color(0xCC95818D)) + data object PREV : NavigationRegion(MR.strings.nav_zone_prev, Color(0xCCFF7733)) + data object NEXT : NavigationRegion(MR.strings.nav_zone_next, Color(0xCC84E296)) + data object LEFT : NavigationRegion(MR.strings.nav_zone_left, Color(0xCC7D1128)) + data object RIGHT : NavigationRegion(MR.strings.nav_zone_right, Color(0xCCA6CFD5)) } data class Region( - val rectF: RectF, + val rect: Rect, val type: NavigationRegion, ) { fun invert(invertMode: ReaderPreferences.TappingInvertMode): Region { - if (invertMode == ReaderPreferences.TappingInvertMode.NONE) return this - return this.copy( - rectF = this.rectF.invert(invertMode), + val horizontal = invertMode.shouldInvertHorizontal + val vertical = invertMode.shouldInvertVertical + if (!horizontal && !vertical) return this + return copy( + rect = Rect( + left = if (horizontal) 1f - rect.right else rect.left, + top = if (vertical) 1f - rect.bottom else rect.top, + right = if (horizontal) 1f - rect.left else rect.right, + bottom = if (vertical) 1f - rect.top else rect.bottom, + ), ) } } - private var constantMenuRegion: RectF = RectF(0f, 0f, 1f, 0.05f) - protected abstract val regionList: List /** Returns regions with applied inversion. */ @@ -39,19 +52,24 @@ abstract class ViewerNavigation(val invertMode: ReaderPreferences.TappingInvertM return regionList.map { it.invert(invertMode) } } - fun getAction(pos: PointF): NavigationRegion { - val x = pos.x - val y = pos.y - val region = getRegions().find { it.rectF.contains(x, y) } - return when { - region != null -> region.type - constantMenuRegion.contains(x, y) -> NavigationRegion.MENU - else -> NavigationRegion.MENU - } + fun getAction(pos: Offset): Action { + val region = getRegions().find { pos in it.rect } ?: return Action.MENU + return actionOf(region.type) + } + + fun actionOf(region: NavigationRegion): Action = when (region) { + NavigationRegion.MENU -> Action.MENU + NavigationRegion.NEXT -> Action.FORWARD + NavigationRegion.PREV -> Action.BACKWARD + NavigationRegion.RIGHT -> if (isReversed) Action.BACKWARD else Action.FORWARD + NavigationRegion.LEFT -> if (isReversed) Action.FORWARD else Action.BACKWARD } override fun equals(other: Any?): Boolean = - other is ViewerNavigation && other.javaClass == javaClass && other.invertMode == invertMode + other is ViewerNavigation && + other.javaClass == javaClass && + other.invertMode == invertMode && + other.isReversed == isReversed - override fun hashCode(): Int = 31 * javaClass.hashCode() + invertMode.hashCode() + override fun hashCode(): Int = (31 * javaClass.hashCode() + invertMode.hashCode()) * 31 + isReversed.hashCode() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerSettings.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerSettings.kt index e33efc7d9e..69f622101e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerSettings.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/ViewerSettings.kt @@ -19,14 +19,17 @@ data class NavigationSettings( val inverted: ReaderPreferences.TappingInvertMode, ) { - fun navigator(default: (ReaderPreferences.TappingInvertMode) -> ViewerNavigation): ViewerNavigation { + fun navigator( + isReversed: Boolean, + default: (ReaderPreferences.TappingInvertMode, Boolean) -> ViewerNavigation, + ): ViewerNavigation { return when (mode) { - ReaderPreferences.NavigationMode.DEFAULT -> default(inverted) - ReaderPreferences.NavigationMode.L_SHAPED -> LNavigation(inverted) - ReaderPreferences.NavigationMode.KINDLISH -> KindlishNavigation(inverted) - ReaderPreferences.NavigationMode.EDGE -> EdgeNavigation(inverted) - ReaderPreferences.NavigationMode.RIGHT_AND_LEFT -> RightAndLeftNavigation(inverted) - ReaderPreferences.NavigationMode.DISABLED -> DisabledNavigation(inverted) + ReaderPreferences.NavigationMode.DEFAULT -> default(inverted, isReversed) + ReaderPreferences.NavigationMode.L_SHAPED -> LNavigation(inverted, isReversed) + ReaderPreferences.NavigationMode.KINDLISH -> KindlishNavigation(inverted, isReversed) + ReaderPreferences.NavigationMode.EDGE -> EdgeNavigation(inverted, isReversed) + ReaderPreferences.NavigationMode.RIGHT_AND_LEFT -> RightAndLeftNavigation(inverted, isReversed) + ReaderPreferences.NavigationMode.DISABLED -> DisabledNavigation(inverted, isReversed) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/DisabledNavigation.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/DisabledNavigation.kt index 0c02eb3926..de35ff532d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/DisabledNavigation.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/DisabledNavigation.kt @@ -13,7 +13,10 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation * | M | M | M | N: Next * +---+---+---+ */ -class DisabledNavigation(invertMode: TappingInvertMode) : ViewerNavigation(invertMode) { +class DisabledNavigation( + invertMode: TappingInvertMode, + isReversed: Boolean, +) : ViewerNavigation(invertMode, isReversed) { override val regionList: List = emptyList() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/EdgeNavigation.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/EdgeNavigation.kt index 943bdf0a9a..76a367092e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/EdgeNavigation.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/EdgeNavigation.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.ui.reader.viewer.navigation -import android.graphics.RectF +import androidx.compose.ui.geometry.Rect import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences.TappingInvertMode import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation @@ -14,19 +14,19 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation * | N | P | N | N: Next * +---+---+---+ */ -class EdgeNavigation(invertMode: TappingInvertMode) : ViewerNavigation(invertMode) { +class EdgeNavigation(invertMode: TappingInvertMode, isReversed: Boolean) : ViewerNavigation(invertMode, isReversed) { override val regionList: List = listOf( Region( - rectF = RectF(0f, 0f, 0.33f, 1f), + rect = Rect(0f, 0f, 0.33f, 1f), type = NavigationRegion.NEXT, ), Region( - rectF = RectF(0.33f, 0.66f, 0.66f, 1f), + rect = Rect(0.33f, 0.66f, 0.66f, 1f), type = NavigationRegion.PREV, ), Region( - rectF = RectF(0.66f, 0f, 1f, 1f), + rect = Rect(0.66f, 0f, 1f, 1f), type = NavigationRegion.NEXT, ), ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/KindlishNavigation.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/KindlishNavigation.kt index 00d6ac3349..5cadd6adb6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/KindlishNavigation.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/KindlishNavigation.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.ui.reader.viewer.navigation -import android.graphics.RectF +import androidx.compose.ui.geometry.Rect import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences.TappingInvertMode import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation @@ -14,15 +14,18 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation * | P | N | N | N: Next * +---+---+---+ */ -class KindlishNavigation(invertMode: TappingInvertMode) : ViewerNavigation(invertMode) { +class KindlishNavigation( + invertMode: TappingInvertMode, + isReversed: Boolean, +) : ViewerNavigation(invertMode, isReversed) { override val regionList: List = listOf( Region( - rectF = RectF(0.33f, 0.33f, 1f, 1f), + rect = Rect(0.33f, 0.33f, 1f, 1f), type = NavigationRegion.NEXT, ), Region( - rectF = RectF(0f, 0.33f, 0.33f, 1f), + rect = Rect(0f, 0.33f, 0.33f, 1f), type = NavigationRegion.PREV, ), ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/LNavigation.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/LNavigation.kt index 4e212afa3c..deb3da414c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/LNavigation.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/LNavigation.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.ui.reader.viewer.navigation -import android.graphics.RectF +import androidx.compose.ui.geometry.Rect import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences.TappingInvertMode import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation @@ -14,23 +14,23 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation * | N | N | N | N: Next * +---+---+---+ */ -open class LNavigation(invertMode: TappingInvertMode) : ViewerNavigation(invertMode) { +open class LNavigation(invertMode: TappingInvertMode, isReversed: Boolean) : ViewerNavigation(invertMode, isReversed) { override val regionList: List = listOf( Region( - rectF = RectF(0f, 0.33f, 0.33f, 0.66f), + rect = Rect(0f, 0.33f, 0.33f, 0.66f), type = NavigationRegion.PREV, ), Region( - rectF = RectF(0f, 0f, 1f, 0.33f), + rect = Rect(0f, 0f, 1f, 0.33f), type = NavigationRegion.PREV, ), Region( - rectF = RectF(0.66f, 0.33f, 1f, 0.66f), + rect = Rect(0.66f, 0.33f, 1f, 0.66f), type = NavigationRegion.NEXT, ), Region( - rectF = RectF(0f, 0.66f, 1f, 1f), + rect = Rect(0f, 0.66f, 1f, 1f), type = NavigationRegion.NEXT, ), ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/RightAndLeftNavigation.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/RightAndLeftNavigation.kt index 42f66038df..bcb56d6f12 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/RightAndLeftNavigation.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/navigation/RightAndLeftNavigation.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.ui.reader.viewer.navigation -import android.graphics.RectF +import androidx.compose.ui.geometry.Rect import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences.TappingInvertMode import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation @@ -14,15 +14,18 @@ import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation * | N | M | P | N: Move Left * +---+---+---+ */ -class RightAndLeftNavigation(invertMode: TappingInvertMode) : ViewerNavigation(invertMode) { +class RightAndLeftNavigation( + invertMode: TappingInvertMode, + isReversed: Boolean, +) : ViewerNavigation(invertMode, isReversed) { override val regionList: List = listOf( Region( - rectF = RectF(0f, 0f, 0.33f, 1f), + rect = Rect(0f, 0f, 0.33f, 1f), type = NavigationRegion.LEFT, ), Region( - rectF = RectF(0.66f, 0f, 1f, 1f), + rect = Rect(0.66f, 0f, 1f, 1f), type = NavigationRegion.RIGHT, ), ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt index d1f18b86a0..ae0382ed56 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt @@ -2,16 +2,14 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager import android.content.Context import android.graphics.ColorFilter -import android.graphics.PointF -import android.view.InputDevice -import android.view.KeyEvent -import android.view.MotionEvent +import android.view.ViewGroup import android.view.ViewGroup.LayoutParams import android.widget.FrameLayout import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.asAndroidColorFilter import androidx.compose.ui.viewinterop.AndroidView import androidx.core.view.children @@ -29,8 +27,8 @@ import eu.kanade.tachiyomi.ui.reader.viewer.LocalPageColorFilter import eu.kanade.tachiyomi.ui.reader.viewer.PagedSettings import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView.ZoomStartPosition import eu.kanade.tachiyomi.ui.reader.viewer.Viewer -import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import eu.kanade.tachiyomi.ui.reader.viewer.collectChanges +import eu.kanade.tachiyomi.ui.reader.viewer.mouseWheelPaging import eu.kanade.tachiyomi.ui.reader.viewer.position import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel @@ -135,6 +133,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer { pager.isVisible = false // Don't layout the pager yet pager.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) pager.isFocusable = false + pager.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS pager.offscreenPageLimit = 1 pager.id = R.id.reader_pager pager.adapter = adapter @@ -144,17 +143,11 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer { pager.getLocationOnScreen(viewPosition) val viewPositionRelativeToWindow = IntArray(2) pager.getLocationInWindow(viewPositionRelativeToWindow) - val pos = PointF( + val pos = Offset( (event.rawX - viewPosition[0] + viewPositionRelativeToWindow[0]) / pager.width, (event.rawY - viewPosition[1] + viewPositionRelativeToWindow[1]) / pager.height, ) - when (activity.settingsViewModel.navigation.value?.getAction(pos) ?: NavigationRegion.MENU) { - NavigationRegion.MENU -> activity.toggleMenu() - NavigationRegion.NEXT -> moveToNext() - NavigationRegion.PREV -> moveToPrevious() - NavigationRegion.RIGHT -> moveRight() - NavigationRegion.LEFT -> moveLeft() - } + activity.onViewerTap(pos) } pager.longTapListener = f@{ if (activity.viewModel.state.value.menuVisible || general.longTapEnabled) { @@ -212,7 +205,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer { } AndroidView( factory = { context -> DisallowInterceptBarrier(context).apply { addView(pager) } }, - modifier = modifier, + modifier = modifier.mouseWheelPaging(this), ) } @@ -367,23 +360,18 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer { } /** - * Moves to the next page. + * Whether pages run right to left, so moving forward goes left. */ - open fun moveToNext() { - moveRight() - } + protected open val isReversed = false - /** - * Moves to the previous page. - */ - open fun moveToPrevious() { - moveLeft() - } + override fun moveForward() = if (isReversed) moveLeft() else moveRight() + + override fun moveBackward() = if (isReversed) moveRight() else moveLeft() /** * Moves to the page at the right. */ - protected open fun moveRight() { + private fun moveRight() { if (pager.currentItem != adapter.count - 1) { val holder = (currentPage as? ReaderPage)?.let(::getPageHolder) if (holder != null && paged.navigateToPan && holder.canPanRight()) { @@ -397,7 +385,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer { /** * Moves to the page at the left. */ - protected open fun moveLeft() { + private fun moveLeft() { if (pager.currentItem != 0) { val holder = (currentPage as? ReaderPage)?.let(::getPageHolder) if (holder != null && paged.navigateToPan && holder.canPanLeft()) { @@ -408,20 +396,6 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer { } } - /** - * Moves to the page at the top (or previous). - */ - protected open fun moveUp() { - moveToPrevious() - } - - /** - * Moves to the page at the bottom (or next). - */ - protected open fun moveDown() { - moveToNext() - } - /** * Resets the adapter in order to recreate all the views. Used when a image configuration is * changed. @@ -433,69 +407,6 @@ abstract class PagerViewer(val activity: ReaderActivity) : Viewer { pager.setCurrentItem(currentItem, false) } - /** - * Called from the containing activity when a key [event] is received. It should return true - * if the event was handled, false otherwise. - */ - override fun handleKeyEvent(event: KeyEvent): Boolean { - val isUp = event.action == KeyEvent.ACTION_UP - val ctrlPressed = event.metaState.and(KeyEvent.META_CTRL_ON) > 0 - - when (event.keyCode) { - KeyEvent.KEYCODE_VOLUME_DOWN -> { - if (!general.volumeKeysEnabled || activity.viewModel.state.value.menuVisible) { - return false - } else if (isUp) { - if (!general.volumeKeysInverted) moveDown() else moveUp() - } - } - KeyEvent.KEYCODE_VOLUME_UP -> { - if (!general.volumeKeysEnabled || activity.viewModel.state.value.menuVisible) { - return false - } else if (isUp) { - if (!general.volumeKeysInverted) moveUp() else moveDown() - } - } - KeyEvent.KEYCODE_DPAD_RIGHT -> { - if (isUp) { - if (ctrlPressed) moveToNext() else moveRight() - } - } - KeyEvent.KEYCODE_DPAD_LEFT -> { - if (isUp) { - if (ctrlPressed) moveToPrevious() else moveLeft() - } - } - KeyEvent.KEYCODE_DPAD_DOWN -> if (isUp) moveDown() - KeyEvent.KEYCODE_DPAD_UP -> if (isUp) moveUp() - KeyEvent.KEYCODE_PAGE_DOWN -> if (isUp) moveDown() - KeyEvent.KEYCODE_PAGE_UP -> if (isUp) moveUp() - KeyEvent.KEYCODE_MENU -> if (isUp) activity.toggleMenu() - else -> return false - } - return true - } - - /** - * Called from the containing activity when a generic motion [event] is received. It should - * return true if the event was handled, false otherwise. - */ - override fun handleGenericMotionEvent(event: MotionEvent): Boolean { - if (event.source and InputDevice.SOURCE_CLASS_POINTER != 0) { - when (event.action) { - MotionEvent.ACTION_SCROLL -> { - if (event.getAxisValue(MotionEvent.AXIS_VSCROLL) < 0.0f) { - moveDown() - } else { - moveUp() - } - return true - } - } - } - return false - } - fun onPageSplit(currentPage: ReaderPage, newPage: InsertPage) { activity.runOnUiThread { // Need to insert on UI thread else images will go blank diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewers.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewers.kt index a79f858142..b97490b4f8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewers.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewers.kt @@ -25,19 +25,7 @@ class R2LPagerViewer(activity: ReaderActivity) : PagerViewer(activity) { return Pager(activity) } - /** - * Moves to the next page. On a R2L pager the next page is the one at the left. - */ - override fun moveToNext() { - moveLeft() - } - - /** - * Moves to the previous page. On a R2L pager the previous page is the one at the right. - */ - override fun moveToPrevious() { - moveRight() - } + override val isReversed = true } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewer.kt index d2d1b8108d..c7887cc243 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewer.kt @@ -1,12 +1,8 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webgpu import android.content.Context -import android.graphics.PointF import android.graphics.Rect import android.os.SystemClock -import android.view.InputDevice -import android.view.KeyEvent -import android.view.MotionEvent import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import ca.mpreg.imagedecoder.ImageDecoder @@ -46,8 +42,8 @@ import eu.kanade.tachiyomi.ui.reader.viewer.LongStripSettings import eu.kanade.tachiyomi.ui.reader.viewer.PagedSettings import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView.ZoomStartPosition import eu.kanade.tachiyomi.ui.reader.viewer.Viewer -import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import eu.kanade.tachiyomi.ui.reader.viewer.collectChanges +import eu.kanade.tachiyomi.ui.reader.viewer.mouseWheelPaging import eu.kanade.tachiyomi.ui.reader.viewer.position import eu.kanade.tachiyomi.util.system.createReaderThemeContext import eu.kanade.tachiyomi.util.system.readerBackgroundColor @@ -813,14 +809,7 @@ open class WebGpuViewer( } onTap = { offset -> - val region = activity.settingsViewModel.navigation.value?.getAction(PointF(offset.x, offset.y)) - when (region ?: NavigationRegion.MENU) { - NavigationRegion.MENU -> activity.toggleMenu() - NavigationRegion.NEXT -> moveToNext() - NavigationRegion.PREV -> moveToPrevious() - NavigationRegion.RIGHT -> moveRight() - NavigationRegion.LEFT -> moveLeft() - } + activity.onViewerTap(offset) } onLongTap = { _ -> @@ -1005,7 +994,7 @@ open class WebGpuViewer( @Composable override fun Content(modifier: Modifier) { - ImageViewer(modifier = modifier, state = viewerState) + ImageViewer(modifier = modifier.mouseWheelPaging(this), state = viewerState) } private val cropBorders: Boolean @@ -1881,14 +1870,9 @@ open class WebGpuViewer( viewerState.animatePageTurn(if (isReversed) direction else -direction) } - // moveRight/moveLeft are screen directions, so a right-to-left book's next is to the left. - fun moveToNext() { - if (isReversed) moveLeft() else moveRight() - } + override fun moveForward() = if (isReversed) moveLeft() else moveRight() - fun moveToPrevious() { - if (isReversed) moveRight() else moveLeft() - } + override fun moveBackward() = if (isReversed) moveRight() else moveLeft() protected open fun moveRight() { viewerState.getPage(0)?.let { page -> @@ -1952,68 +1936,4 @@ open class WebGpuViewer( val target = currentPage?.let { nextPage(it, direction) } ?: return moveToPage(target) } - - protected fun moveUp() { - moveToPrevious() - } - - protected fun moveDown() { - moveToNext() - } - - /** - * Called from the containing activity when a key [event] is received. It should return true - * if the event was handled, false otherwise. - */ - override fun handleKeyEvent(event: KeyEvent): Boolean { - val isUp = event.action == KeyEvent.ACTION_UP - val ctrlPressed = event.metaState.and(KeyEvent.META_CTRL_ON) > 0 - when (event.keyCode) { - KeyEvent.KEYCODE_VOLUME_DOWN -> { - if (!general.volumeKeysEnabled || activity.viewModel.state.value.menuVisible) { - return false - } else if (isUp) { - if (!general.volumeKeysInverted) moveDown() else moveUp() - } - } - - KeyEvent.KEYCODE_VOLUME_UP -> { - if (!general.volumeKeysEnabled || activity.viewModel.state.value.menuVisible) { - return false - } else if (isUp) { - if (!general.volumeKeysInverted) moveUp() else moveDown() - } - } - - KeyEvent.KEYCODE_DPAD_RIGHT -> if (isUp) if (ctrlPressed) moveToNext() else moveRight() - KeyEvent.KEYCODE_DPAD_LEFT -> if (isUp) if (ctrlPressed) moveToPrevious() else moveLeft() - KeyEvent.KEYCODE_DPAD_DOWN -> if (isUp) moveDown() - KeyEvent.KEYCODE_DPAD_UP -> if (isUp) moveUp() - KeyEvent.KEYCODE_PAGE_DOWN -> if (isUp) moveDown() - KeyEvent.KEYCODE_PAGE_UP -> if (isUp) moveUp() - KeyEvent.KEYCODE_MENU -> if (isUp) activity.toggleMenu() - else -> return false - } - return true - } - - /** - * Called from the containing activity when a generic motion [event] is received. It should - * return true if the event was handled, false otherwise. - */ - override fun handleGenericMotionEvent(event: MotionEvent): Boolean { - if (event.source and InputDevice.SOURCE_CLASS_POINTER != 0) { - when (event.action) { - MotionEvent.ACTION_SCROLL -> { - if (event.getAxisValue(MotionEvent.AXIS_VSCROLL) < 0.0f) { - moveDown() - } else { - moveUp() - } - return true - } - } - } - return false - } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewerContinuous.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewerContinuous.kt index ead83dc9b3..eb97730b86 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewerContinuous.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webgpu/WebGpuViewerContinuous.kt @@ -7,6 +7,7 @@ import ca.mpreg.webgpuviewer.viewer.ImageViewerContinuous import ca.mpreg.webgpuviewer.viewer.ImageViewerContinuousState import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.model.ReaderPage +import eu.kanade.tachiyomi.ui.reader.viewer.mouseWheelPaging import kotlinx.coroutines.launch import kotlin.math.max @@ -26,7 +27,7 @@ class WebGpuViewerContinuous(activity: ReaderActivity, val useGap: Boolean = fal @Composable override fun Content(modifier: Modifier) { - ImageViewerContinuous(modifier = modifier, state = state) + ImageViewerContinuous(modifier = modifier.mouseWheelPaging(this), state = state) } init { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index 5ed076f01e..f47158aef0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -1,15 +1,13 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon import android.graphics.ColorFilter -import android.graphics.PointF -import android.view.KeyEvent -import android.view.MotionEvent import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT import androidx.compose.runtime.Composable import androidx.compose.runtime.SideEffect import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.asAndroidColorFilter import androidx.compose.ui.viewinterop.AndroidView import androidx.core.app.ActivityCompat @@ -26,7 +24,6 @@ import eu.kanade.tachiyomi.ui.reader.viewer.LocalPageColorFilter import eu.kanade.tachiyomi.ui.reader.viewer.LongStripSettings import eu.kanade.tachiyomi.ui.reader.viewer.ReaderPageImageView import eu.kanade.tachiyomi.ui.reader.viewer.Viewer -import eu.kanade.tachiyomi.ui.reader.viewer.ViewerNavigation.NavigationRegion import eu.kanade.tachiyomi.ui.reader.viewer.collectChanges import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel @@ -139,15 +136,11 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr recycler.getLocationOnScreen(viewPosition) val viewPositionRelativeToWindow = IntArray(2) recycler.getLocationInWindow(viewPositionRelativeToWindow) - val pos = PointF( + val pos = Offset( (event.rawX - viewPosition[0] + viewPositionRelativeToWindow[0]) / recycler.width, (event.rawY - viewPosition[1] + viewPositionRelativeToWindow[1]) / recycler.originalHeight, ) - when (activity.settingsViewModel.navigation.value?.getAction(pos) ?: NavigationRegion.MENU) { - NavigationRegion.MENU -> activity.toggleMenu() - NavigationRegion.NEXT, NavigationRegion.RIGHT -> scrollDown() - NavigationRegion.PREV, NavigationRegion.LEFT -> scrollUp() - } + activity.onViewerTap(pos) } recycler.longTapListener = f@{ event -> if (activity.viewModel.state.value.menuVisible || general.longTapEnabled) { @@ -188,6 +181,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr } frame.layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT) + frame.descendantFocusability = ViewGroup.FOCUS_BLOCK_DESCENDANTS frame.addView(recycler) } @@ -322,7 +316,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr /** * Scrolls up by [scrollDistance]. */ - private fun scrollUp() { + override fun moveBackward() { if (general.usePageTransitions) { recycler.smoothScrollBy(0, -scrollDistance) } else { @@ -333,7 +327,7 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr /** * Scrolls down by [scrollDistance]. */ - private fun scrollDown() { + override fun moveForward() { if (general.usePageTransitions) { recycler.smoothScrollBy(0, scrollDistance) } else { @@ -341,52 +335,6 @@ class WebtoonViewer(val activity: ReaderActivity, val isContinuous: Boolean = tr } } - /** - * Called from the containing activity when a key [event] is received. It should return true - * if the event was handled, false otherwise. - */ - override fun handleKeyEvent(event: KeyEvent): Boolean { - val isUp = event.action == KeyEvent.ACTION_UP - - when (event.keyCode) { - KeyEvent.KEYCODE_VOLUME_DOWN -> { - if (!general.volumeKeysEnabled || activity.viewModel.state.value.menuVisible) { - return false - } else if (isUp) { - if (!general.volumeKeysInverted) scrollDown() else scrollUp() - } - } - KeyEvent.KEYCODE_VOLUME_UP -> { - if (!general.volumeKeysEnabled || activity.viewModel.state.value.menuVisible) { - return false - } else if (isUp) { - if (!general.volumeKeysInverted) scrollUp() else scrollDown() - } - } - KeyEvent.KEYCODE_MENU -> if (isUp) activity.toggleMenu() - - KeyEvent.KEYCODE_DPAD_LEFT, - KeyEvent.KEYCODE_DPAD_UP, - KeyEvent.KEYCODE_PAGE_UP, - -> if (isUp) scrollUp() - - KeyEvent.KEYCODE_DPAD_RIGHT, - KeyEvent.KEYCODE_DPAD_DOWN, - KeyEvent.KEYCODE_PAGE_DOWN, - -> if (isUp) scrollDown() - else -> return false - } - return true - } - - /** - * Called from the containing activity when a generic motion [event] is received. It should - * return true if the event was handled, false otherwise. - */ - override fun handleGenericMotionEvent(event: MotionEvent): Boolean { - return false - } - /** * Notifies adapter of changes around the current page to trigger a relayout in the recycler. * Used when an image configuration is changed. diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/RectFExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/RectFExtensions.kt deleted file mode 100644 index 2072bcd421..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/RectFExtensions.kt +++ /dev/null @@ -1,15 +0,0 @@ -package eu.kanade.tachiyomi.util.lang - -import android.graphics.RectF -import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences - -fun RectF.invert(invertMode: ReaderPreferences.TappingInvertMode): RectF { - val horizontal = invertMode.shouldInvertHorizontal - val vertical = invertMode.shouldInvertVertical - return when { - horizontal && vertical -> RectF(1f - this.right, 1f - this.bottom, 1f - this.left, 1f - this.top) - vertical -> RectF(this.left, 1f - this.bottom, this.right, 1f - this.top) - horizontal -> RectF(1f - this.right, this.top, 1f - this.left, this.bottom) - else -> this - } -}