mirror of
https://github.com/mihonapp/mihon.git
synced 2026-10-31 04:56:29 -07:00
Handle reader input and state in the activity (#4192)
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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<ColorFilter?> { 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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Region>
|
||||
|
||||
/** 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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -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<Region> = emptyList()
|
||||
}
|
||||
|
||||
@@ -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<Region> = 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,
|
||||
),
|
||||
)
|
||||
|
||||
+7
-4
@@ -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<Region> = 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,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -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<Region> = 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,
|
||||
),
|
||||
)
|
||||
|
||||
+7
-4
@@ -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<Region> = 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,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user