Bump webgpuviewer, fix navigation directions and long strip bounds (#3870)
* Keep previous page when jumping pages * Fix navigation directions * fix continuous jump after navigate * webgpuviewer config * Fade in loaded pages * async onPageChange * Update libs.versions.toml
This commit is contained in:
@@ -31,6 +31,7 @@ import java.text.NumberFormat
|
|||||||
internal fun ColumnScope.ReadingModePage(viewModel: ReaderSettingsViewModel) {
|
internal fun ColumnScope.ReadingModePage(viewModel: ReaderSettingsViewModel) {
|
||||||
HeadingItem(MR.strings.pref_category_for_this_series)
|
HeadingItem(MR.strings.pref_category_for_this_series)
|
||||||
val manga by viewModel.mangaFlow.collectAsState()
|
val manga by viewModel.mangaFlow.collectAsState()
|
||||||
|
val viewer by viewModel.viewerFlow.collectAsState()
|
||||||
|
|
||||||
val readingMode = remember(manga) { ReadingMode.fromPreference(manga?.readingMode?.toInt()) }
|
val readingMode = remember(manga) { ReadingMode.fromPreference(manga?.readingMode?.toInt()) }
|
||||||
SettingsChipRow(MR.strings.pref_category_reading_mode) {
|
SettingsChipRow(MR.strings.pref_category_reading_mode) {
|
||||||
@@ -43,22 +44,24 @@ internal fun ColumnScope.ReadingModePage(viewModel: ReaderSettingsViewModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val default = LocalContext.current.appGraph.readerPreferences.defaultReadingMode.get()
|
if (viewer is WebGpuViewer) {
|
||||||
val resolved = ReadingMode.fromPreference(
|
val default = LocalContext.current.appGraph.readerPreferences.defaultReadingMode.get()
|
||||||
when {
|
val resolved = ReadingMode.fromPreference(
|
||||||
readingMode == ReadingMode.DEFAULT -> default
|
when {
|
||||||
else -> manga?.readingMode?.toInt() ?: default
|
readingMode == ReadingMode.DEFAULT -> default
|
||||||
},
|
else -> manga?.readingMode?.toInt() ?: default
|
||||||
)
|
},
|
||||||
if (resolved == ReadingMode.LEFT_TO_RIGHT || resolved == ReadingMode.RIGHT_TO_LEFT) {
|
)
|
||||||
val dualPageView by viewModel.preferences.dualPageView.collectAsState()
|
if (resolved == ReadingMode.LEFT_TO_RIGHT || resolved == ReadingMode.RIGHT_TO_LEFT) {
|
||||||
SettingsChipRow(MR.strings.pref_dual_page_view) {
|
val dualPageView by viewModel.preferences.dualPageView.collectAsState()
|
||||||
ReaderPreferences.DualPageView.entries.map {
|
SettingsChipRow(MR.strings.pref_dual_page_view) {
|
||||||
FilterChip(
|
ReaderPreferences.DualPageView.entries.map {
|
||||||
selected = it == dualPageView,
|
FilterChip(
|
||||||
onClick = { viewModel.preferences.dualPageView.set(it) },
|
selected = it == dualPageView,
|
||||||
label = { Text(stringResource(it.titleRes)) },
|
onClick = { viewModel.preferences.dualPageView.set(it) },
|
||||||
)
|
label = { Text(stringResource(it.titleRes)) },
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,14 +77,10 @@ internal fun ColumnScope.ReadingModePage(viewModel: ReaderSettingsViewModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val viewer by viewModel.viewerFlow.collectAsState()
|
when (viewer) {
|
||||||
if (viewer is WebtoonViewer) {
|
is WebtoonViewer -> WebtoonViewerSettings(viewModel)
|
||||||
WebtoonViewerSettings(viewModel)
|
is WebGpuViewer -> WebGpuViewerSettings(viewModel)
|
||||||
} else {
|
else -> PagerViewerSettings(viewModel)
|
||||||
PagerViewerSettings(viewModel)
|
|
||||||
if (viewer is WebGpuViewer) {
|
|
||||||
WebGpuViewerSettings(viewModel)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,9 +263,57 @@ private fun ColumnScope.TapZonesItems(
|
|||||||
private fun ColumnScope.WebGpuViewerSettings(viewModel: ReaderSettingsViewModel) {
|
private fun ColumnScope.WebGpuViewerSettings(viewModel: ReaderSettingsViewModel) {
|
||||||
HeadingItem(MR.strings.webgpu_viewer)
|
HeadingItem(MR.strings.webgpu_viewer)
|
||||||
|
|
||||||
|
val navigationModePager by viewModel.preferences.navigationModePager.collectAsState()
|
||||||
|
val pagerNavInverted by viewModel.preferences.pagerNavInverted.collectAsState()
|
||||||
|
TapZonesItems(
|
||||||
|
selected = navigationModePager,
|
||||||
|
onSelect = viewModel.preferences.navigationModePager::set,
|
||||||
|
invertMode = pagerNavInverted,
|
||||||
|
onSelectInvertMode = viewModel.preferences.pagerNavInverted::set,
|
||||||
|
)
|
||||||
|
|
||||||
|
val imageScaleType by viewModel.preferences.imageScaleType.collectAsState()
|
||||||
|
SettingsChipRow(MR.strings.pref_image_scale_type) {
|
||||||
|
ReaderPreferences.ImageScaleTypeWebGpuViewer.forEach {
|
||||||
|
FilterChip(
|
||||||
|
selected = ReaderPreferences.ImageScaleType[imageScaleType - 1] == it,
|
||||||
|
onClick = {
|
||||||
|
viewModel.preferences.imageScaleType.set(ReaderPreferences.ImageScaleType.indexOf(it) + 1)
|
||||||
|
},
|
||||||
|
label = { Text(stringResource(it)) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val zoomStart by viewModel.preferences.zoomStart.collectAsState()
|
||||||
|
SettingsChipRow(MR.strings.pref_zoom_start) {
|
||||||
|
ReaderPreferences.ZoomStart.mapIndexed { index, it ->
|
||||||
|
FilterChip(
|
||||||
|
selected = zoomStart == index + 1,
|
||||||
|
onClick = { viewModel.preferences.zoomStart.set(index + 1) },
|
||||||
|
label = { Text(stringResource(it)) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckboxItem(
|
||||||
|
label = stringResource(MR.strings.pref_crop_borders),
|
||||||
|
pref = viewModel.preferences.cropBorders,
|
||||||
|
)
|
||||||
|
|
||||||
|
CheckboxItem(
|
||||||
|
label = stringResource(MR.strings.pref_landscape_zoom),
|
||||||
|
pref = viewModel.preferences.landscapeZoom,
|
||||||
|
)
|
||||||
|
|
||||||
|
CheckboxItem(
|
||||||
|
label = stringResource(MR.strings.pref_navigate_pan),
|
||||||
|
pref = viewModel.preferences.navigateToPan,
|
||||||
|
)
|
||||||
|
|
||||||
val transitionAnimation by viewModel.preferences.transitionAnimation.collectAsState()
|
val transitionAnimation by viewModel.preferences.transitionAnimation.collectAsState()
|
||||||
SettingsChipRow(MR.strings.pref_transition_animation) {
|
SettingsChipRow(MR.strings.pref_transition_animation) {
|
||||||
ReaderPreferences.TransitionAnimation.entries.map {
|
ReaderPreferences.TransitionAnimation.entries.forEach {
|
||||||
FilterChip(
|
FilterChip(
|
||||||
selected = it == transitionAnimation,
|
selected = it == transitionAnimation,
|
||||||
onClick = { viewModel.preferences.transitionAnimation.set(it) },
|
onClick = { viewModel.preferences.transitionAnimation.set(it) },
|
||||||
@@ -277,7 +324,7 @@ private fun ColumnScope.WebGpuViewerSettings(viewModel: ReaderSettingsViewModel)
|
|||||||
|
|
||||||
val cutoutMode by viewModel.preferences.cutoutMode.collectAsState()
|
val cutoutMode by viewModel.preferences.cutoutMode.collectAsState()
|
||||||
SettingsChipRow(MR.strings.pref_cutout_mode) {
|
SettingsChipRow(MR.strings.pref_cutout_mode) {
|
||||||
ReaderPreferences.CutoutMode.entries.map {
|
ReaderPreferences.CutoutMode.entries.forEach {
|
||||||
FilterChip(
|
FilterChip(
|
||||||
selected = it == cutoutMode,
|
selected = it == cutoutMode,
|
||||||
onClick = { viewModel.preferences.cutoutMode.set(it) },
|
onClick = { viewModel.preferences.cutoutMode.set(it) },
|
||||||
|
|||||||
@@ -298,6 +298,13 @@ class ReaderPreferences(
|
|||||||
MR.strings.scale_type_smart_fit,
|
MR.strings.scale_type_smart_fit,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val ImageScaleTypeWebGpuViewer = listOf(
|
||||||
|
MR.strings.scale_type_fit_screen,
|
||||||
|
MR.strings.scale_type_fit_width,
|
||||||
|
MR.strings.scale_type_fit_height,
|
||||||
|
MR.strings.scale_type_original_size,
|
||||||
|
)
|
||||||
|
|
||||||
val ZoomStart = listOf(
|
val ZoomStart = listOf(
|
||||||
MR.strings.zoom_start_automatic,
|
MR.strings.zoom_start_automatic,
|
||||||
MR.strings.zoom_start_left,
|
MR.strings.zoom_start_left,
|
||||||
|
|||||||
@@ -214,6 +214,21 @@ open class WebGpuViewer(
|
|||||||
@Volatile
|
@Volatile
|
||||||
var currentPage: ViewerPage? = null
|
var currentPage: ViewerPage? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What a running page turn animates away from, kept out of [evictFarthestPage]'s reach - a
|
||||||
|
* jump preloads enough pages to evict it. Replaced by the next turn's rather than cleared.
|
||||||
|
*/
|
||||||
|
@Volatile
|
||||||
|
private var pinnedFromPage: ImagePage? = null
|
||||||
|
|
||||||
|
/** True while [pinnedFromPage] is drawing [page]'s image, as itself or as a spread side. */
|
||||||
|
private fun isPinned(page: ViewerPage): Boolean {
|
||||||
|
val pinned = pinnedFromPage ?: return false
|
||||||
|
val image = page.imagePage
|
||||||
|
if (pinned === image) return true
|
||||||
|
return pinned is ImagePage.ImageSpread && (pinned.left === image || pinned.right === image)
|
||||||
|
}
|
||||||
|
|
||||||
open val preloadAhead = 3
|
open val preloadAhead = 3
|
||||||
open val preloadBehind = 2
|
open val preloadBehind = 2
|
||||||
|
|
||||||
@@ -231,12 +246,18 @@ open class WebGpuViewer(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Evicts the page farthest from reference. Must be called while holding lock.
|
* Evicts the page farthest from reference. Must be called while holding lock.
|
||||||
|
*
|
||||||
|
* Never evicts [reference], [currentPage] or what [pinnedFromPage] draws. Returns false when
|
||||||
|
* nothing was evictable, so a trim loop stops instead of spinning.
|
||||||
|
*
|
||||||
* @param reference The page to use as reference (defaults to currentPage)
|
* @param reference The page to use as reference (defaults to currentPage)
|
||||||
*/
|
*/
|
||||||
private fun evictFarthestPage(reference: ViewerPage? = null) {
|
private fun evictFarthestPage(reference: ViewerPage? = null): Boolean {
|
||||||
val current = reference ?: currentPage ?: return
|
val current = reference ?: currentPage ?: return false
|
||||||
val candidates = pageCache.values.filter { it !== current }.toMutableSet()
|
val candidates = pageCache.values
|
||||||
if (candidates.isEmpty()) return
|
.filter { it !== current && it !== currentPage && !isPinned(it) }
|
||||||
|
.toMutableSet()
|
||||||
|
if (candidates.isEmpty()) return false
|
||||||
|
|
||||||
fun findNext(page: ViewerPage): ViewerPage? = when (page) {
|
fun findNext(page: ViewerPage): ViewerPage? = when (page) {
|
||||||
is ViewerReaderPage -> {
|
is ViewerReaderPage -> {
|
||||||
@@ -305,13 +326,14 @@ open class WebGpuViewer(
|
|||||||
if (backward != null && candidates.remove(backward)) farthest = backward
|
if (backward != null && candidates.remove(backward)) farthest = backward
|
||||||
}
|
}
|
||||||
|
|
||||||
val toRemove = candidates.firstOrNull() ?: farthest ?: return
|
val toRemove = candidates.firstOrNull() ?: farthest ?: return false
|
||||||
|
|
||||||
pageCache.remove(pageKey(toRemove))
|
pageCache.remove(pageKey(toRemove))
|
||||||
decodeQueue.remove(toRemove)
|
decodeQueue.remove(toRemove)
|
||||||
toRemove.state = PageState.IDLE
|
toRemove.state = PageState.IDLE
|
||||||
(toRemove as? ViewerReaderPage)?.spreadPage?.cleanup()
|
(toRemove as? ViewerReaderPage)?.spreadPage?.cleanup()
|
||||||
toRemove.imagePage.cleanup()
|
toRemove.imagePage.cleanup()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -324,7 +346,7 @@ open class WebGpuViewer(
|
|||||||
findInCache(key) ?: ViewerReaderPage(page).also { newPage ->
|
findInCache(key) ?: ViewerReaderPage(page).also { newPage ->
|
||||||
pageCache[key] = newPage
|
pageCache[key] = newPage
|
||||||
while (pageCache.size > cacheSize) {
|
while (pageCache.size > cacheSize) {
|
||||||
evictFarthestPage(referencePage ?: newPage)
|
if (!evictFarthestPage(referencePage ?: newPage)) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,7 +362,7 @@ open class WebGpuViewer(
|
|||||||
findInCache(key) ?: ViewerTransitionPage(prevChapter, nextChapter).also { newPage ->
|
findInCache(key) ?: ViewerTransitionPage(prevChapter, nextChapter).also { newPage ->
|
||||||
pageCache[key] = newPage
|
pageCache[key] = newPage
|
||||||
while (pageCache.size > cacheSize) {
|
while (pageCache.size > cacheSize) {
|
||||||
evictFarthestPage(referencePage ?: newPage)
|
if (!evictFarthestPage(referencePage ?: newPage)) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -367,7 +389,7 @@ open class WebGpuViewer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
inner class ErrorPage internal constructor(
|
inner class ErrorPage internal constructor(
|
||||||
var message: String,
|
message: String,
|
||||||
spreadPosition: SpreadPosition = SpreadPosition.SINGLE,
|
spreadPosition: SpreadPosition = SpreadPosition.SINGLE,
|
||||||
) : ImagePage.Render(
|
) : ImagePage.Render(
|
||||||
if (spreadPosition == SpreadPosition.SINGLE) pager.state.width else pager.state.width / 2,
|
if (spreadPosition == SpreadPosition.SINGLE) pager.state.width else pager.state.width / 2,
|
||||||
@@ -379,6 +401,12 @@ open class WebGpuViewer(
|
|||||||
homeScale = 1f
|
homeScale = 1f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var message: String = message
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
override val backgroundColor: Int = readerBackgroundColor()
|
override val backgroundColor: Int = readerBackgroundColor()
|
||||||
|
|
||||||
override fun render(dst: GPUTexture, x: Float, y: Float, scale: Float) {
|
override fun render(dst: GPUTexture, x: Float, y: Float, scale: Float) {
|
||||||
@@ -403,18 +431,28 @@ open class WebGpuViewer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ProgressPage(var foregroundColor: Int = readerOnBackgroundColor()) : ImagePage.Render(
|
inner class ProgressPage(foregroundColor: Int = readerOnBackgroundColor()) : ImagePage.Render(
|
||||||
if (!isDualPageMode()) pager.state.width else pager.state.width / 2,
|
if (!isDualPageMode()) pager.state.width else pager.state.width / 2,
|
||||||
pager.state.height,
|
pager.state.height,
|
||||||
) {
|
) {
|
||||||
var progress: Float = 0f
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
minScale = 1f
|
minScale = 1f
|
||||||
maxScale = 1f
|
maxScale = 1f
|
||||||
homeScale = 1f
|
homeScale = 1f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var progress: Float = 0f
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
var foregroundColor: Int = foregroundColor
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
override val backgroundColor: Int = readerBackgroundColor()
|
override val backgroundColor: Int = readerBackgroundColor()
|
||||||
|
|
||||||
override fun render(dst: GPUTexture, x: Float, y: Float, scale: Float) {
|
override fun render(dst: GPUTexture, x: Float, y: Float, scale: Float) {
|
||||||
@@ -702,8 +740,8 @@ open class WebGpuViewer(
|
|||||||
NavigationRegion.MENU -> activity.toggleMenu()
|
NavigationRegion.MENU -> activity.toggleMenu()
|
||||||
NavigationRegion.NEXT -> if (isReversed) moveToPrevious() else moveToNext()
|
NavigationRegion.NEXT -> if (isReversed) moveToPrevious() else moveToNext()
|
||||||
NavigationRegion.PREV -> if (isReversed) moveToNext() else moveToPrevious()
|
NavigationRegion.PREV -> if (isReversed) moveToNext() else moveToPrevious()
|
||||||
NavigationRegion.RIGHT -> if (isReversed) moveLeft() else moveRight()
|
NavigationRegion.RIGHT -> moveRight()
|
||||||
NavigationRegion.LEFT -> if (isReversed) moveRight() else moveLeft()
|
NavigationRegion.LEFT -> moveLeft()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -847,7 +885,6 @@ open class WebGpuViewer(
|
|||||||
|
|
||||||
(page.imagePage as? ProgressPage)?.apply {
|
(page.imagePage as? ProgressPage)?.apply {
|
||||||
progress = value / 100f
|
progress = value / 100f
|
||||||
invalidate()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -979,7 +1016,6 @@ open class WebGpuViewer(
|
|||||||
repeat(pageCount - 1) {
|
repeat(pageCount - 1) {
|
||||||
(page.imagePage as? ProgressPage)?.apply {
|
(page.imagePage as? ProgressPage)?.apply {
|
||||||
progress = (it + 1).toFloat() / pageCount
|
progress = (it + 1).toFloat() / pageCount
|
||||||
invalidate()
|
|
||||||
}
|
}
|
||||||
val frame = dec.decodeNext()
|
val frame = dec.decodeNext()
|
||||||
val image = Image(
|
val image = Image(
|
||||||
@@ -1001,6 +1037,9 @@ open class WebGpuViewer(
|
|||||||
page.imagePage = imagePage
|
page.imagePage = imagePage
|
||||||
page.state = PageState.IDLE
|
page.state = PageState.IDLE
|
||||||
oldImagePage.cleanup()
|
oldImagePage.cleanup()
|
||||||
|
// Fade up from the placeholder's colour, if that placeholder was on screen -
|
||||||
|
// one that decoded out of view has nothing left to fade from.
|
||||||
|
if (oldImagePage.isOnScreen) imagePage.fadeIn()
|
||||||
if (page.spreadPosition == SpreadPosition.SINGLE) {
|
if (page.spreadPosition == SpreadPosition.SINGLE) {
|
||||||
(page.imagePage as? ImagePage.ImageSingle)?.let {
|
(page.imagePage as? ImagePage.ImageSingle)?.let {
|
||||||
if (!applyWideZoomIfNeeded(it)) {
|
if (!applyWideZoomIfNeeded(it)) {
|
||||||
@@ -1163,8 +1202,6 @@ open class WebGpuViewer(
|
|||||||
|
|
||||||
pager.state.apply {
|
pager.state.apply {
|
||||||
onPageChange = onPageChange@{ delta ->
|
onPageChange = onPageChange@{ delta ->
|
||||||
activity.hideMenu()
|
|
||||||
|
|
||||||
// The viewer already showed the page at fetchPage(delta).
|
// The viewer already showed the page at fetchPage(delta).
|
||||||
// We need to update currentPage to match that.
|
// We need to update currentPage to match that.
|
||||||
val current = currentPage ?: return@onPageChange
|
val current = currentPage ?: return@onPageChange
|
||||||
@@ -1176,13 +1213,23 @@ open class WebGpuViewer(
|
|||||||
page = nextPage(page, step) ?: return@onPageChange
|
page = nextPage(page, step) ?: return@onPageChange
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Synchronous, since the viewer walks getPage() from here - stale, and the next
|
||||||
|
// scroll step crosses the same boundary again.
|
||||||
currentPage = page
|
currentPage = page
|
||||||
(page as? ViewerReaderPage)?.let { activity.onPageSelected(it.page) }
|
|
||||||
preloadPages(page)
|
|
||||||
|
|
||||||
(page as? ViewerTransitionPage)?.let { ViewerTransitionPage ->
|
// The rest ran here too, on the animation thread under the viewer's scroll lock.
|
||||||
if (ViewerTransitionPage.prevChapter == null || ViewerTransitionPage.nextChapter == null) {
|
// Posted in order, so nothing is skipped or reordered - and on this viewer's own
|
||||||
activity.showMenu()
|
// MainScope, not the state's: that one dispatches inside the frame callback.
|
||||||
|
val settled = page
|
||||||
|
this@WebGpuViewer.scope.launch {
|
||||||
|
activity.hideMenu()
|
||||||
|
(settled as? ViewerReaderPage)?.let { activity.onPageSelected(it.page) }
|
||||||
|
preloadPages(settled)
|
||||||
|
|
||||||
|
(settled as? ViewerTransitionPage)?.let { transitionPage ->
|
||||||
|
if (transitionPage.prevChapter == null || transitionPage.nextChapter == null) {
|
||||||
|
activity.showMenu()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1196,12 +1243,17 @@ open class WebGpuViewer(
|
|||||||
* In dual page mode, aligns to the start of the spread containing the page.
|
* In dual page mode, aligns to the start of the spread containing the page.
|
||||||
*/
|
*/
|
||||||
override fun moveToPage(page: ReaderPage) {
|
override fun moveToPage(page: ReaderPage) {
|
||||||
|
// Pin first: resolving a target outside the cached window trims the cache.
|
||||||
|
pinnedFromPage = currentPage?.let { buildSpreadPage(it) }
|
||||||
// Get the page and align to spread anchor based on image position
|
// Get the page and align to spread anchor based on image position
|
||||||
moveToPage(getSpreadAnchor(getPage(page)))
|
moveToPage(getSpreadAnchor(getPage(page)))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun moveToPage(newPage: ViewerPage) {
|
private fun moveToPage(newPage: ViewerPage) {
|
||||||
val previousPage = currentPage
|
val previousPage = currentPage
|
||||||
|
// Before preloadPages below trims the cache - see [pinnedFromPage].
|
||||||
|
val fromSpread = previousPage?.let { buildSpreadPage(it) }
|
||||||
|
pinnedFromPage = fromSpread
|
||||||
|
|
||||||
currentPage = newPage
|
currentPage = newPage
|
||||||
(newPage as? ViewerReaderPage)?.let { activity.onPageSelected(it.page) }
|
(newPage as? ViewerReaderPage)?.let { activity.onPageSelected(it.page) }
|
||||||
@@ -1245,14 +1297,19 @@ open class WebGpuViewer(
|
|||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if (direction != 0) {
|
if (direction != 0 && fromSpread != null) {
|
||||||
pager.state.transitionFromPage = buildSpreadPage(previousPage)
|
animateTurn(direction, fromSpread)
|
||||||
pager.state.animatePageTurn(if (isReversed) direction else -direction)
|
|
||||||
} else {
|
} else {
|
||||||
pager.state.invalidate()
|
pager.state.invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** How a [moveToPage] turn is shown. [direction] is 1 forward through the pages, -1 back. */
|
||||||
|
protected open fun animateTurn(direction: Int, fromSpread: ImagePage) {
|
||||||
|
pager.state.transitionFromPage = fromSpread
|
||||||
|
pager.state.animatePageTurn(if (isReversed) direction else -direction)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves to the next page.
|
* Moves to the next page.
|
||||||
*/
|
*/
|
||||||
@@ -1277,8 +1334,7 @@ open class WebGpuViewer(
|
|||||||
val maxX = page.maxX(page.scale)
|
val maxX = page.maxX(page.scale)
|
||||||
val currentX = page.animationJob?.let { page.animationTargetX } ?: page.x
|
val currentX = page.animationJob?.let { page.animationTargetX } ?: page.x
|
||||||
|
|
||||||
val c = if (isReversed) -1 else 1
|
val x = (currentX - 1 / page.scale).coerceIn(minX, maxX)
|
||||||
val x = (currentX - c / page.scale).coerceIn(minX, maxX)
|
|
||||||
|
|
||||||
if (!currentX.closeTo(x)) {
|
if (!currentX.closeTo(x)) {
|
||||||
page.animateTo(targetX = x, targetY = page.y)
|
page.animateTo(targetX = x, targetY = page.y)
|
||||||
@@ -1286,7 +1342,7 @@ open class WebGpuViewer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateSpread(1)
|
navigateSpread(if (isReversed) -1 else 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1300,8 +1356,7 @@ open class WebGpuViewer(
|
|||||||
val maxX = page.maxX(page.scale)
|
val maxX = page.maxX(page.scale)
|
||||||
val currentX = page.animationJob?.isActive?.let { page.animationTargetX } ?: page.x
|
val currentX = page.animationJob?.isActive?.let { page.animationTargetX } ?: page.x
|
||||||
|
|
||||||
val c = if (isReversed) -1 else 1
|
val x = (currentX + 1 / page.scale).coerceIn(minX, maxX)
|
||||||
val x = (currentX + c / page.scale).coerceIn(minX, maxX)
|
|
||||||
|
|
||||||
if (!currentX.closeTo(x)) {
|
if (!currentX.closeTo(x)) {
|
||||||
page.animateTo(targetX = x, targetY = page.y)
|
page.animateTo(targetX = x, targetY = page.y)
|
||||||
@@ -1309,7 +1364,7 @@ open class WebGpuViewer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateSpread(-1)
|
navigateSpread(if (isReversed) 1 else -1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1372,7 +1427,7 @@ open class WebGpuViewer(
|
|||||||
if (!config.volumeKeysEnabled || activity.viewModel.state.value.menuVisible) {
|
if (!config.volumeKeysEnabled || activity.viewModel.state.value.menuVisible) {
|
||||||
return false
|
return false
|
||||||
} else if (isUp) {
|
} else if (isUp) {
|
||||||
if (!config.volumeKeysInverted) moveDown() else moveUp()
|
if (!config.volumeKeysInverted.xor(isReversed)) moveDown() else moveUp()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1380,7 +1435,7 @@ open class WebGpuViewer(
|
|||||||
if (!config.volumeKeysEnabled || activity.viewModel.state.value.menuVisible) {
|
if (!config.volumeKeysEnabled || activity.viewModel.state.value.menuVisible) {
|
||||||
return false
|
return false
|
||||||
} else if (isUp) {
|
} else if (isUp) {
|
||||||
if (!config.volumeKeysInverted) moveUp() else moveDown()
|
if (!config.volumeKeysInverted.xor(isReversed)) moveUp() else moveDown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-5
@@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.ui.reader.viewer.webgpu
|
package eu.kanade.tachiyomi.ui.reader.viewer.webgpu
|
||||||
|
|
||||||
import ca.mpreg.webgpuviewer.ImageViewContinuous
|
import ca.mpreg.webgpuviewer.ImageViewContinuous
|
||||||
|
import ca.mpreg.webgpuviewer.viewer.ImagePage
|
||||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||||
|
|
||||||
@@ -9,13 +10,13 @@ class WebGpuViewerContinuous(activity: ReaderActivity) :
|
|||||||
|
|
||||||
override val isContinuous: Boolean = true
|
override val isContinuous: Boolean = true
|
||||||
|
|
||||||
override val preloadAhead = 2
|
override val preloadAhead = 3
|
||||||
override val preloadBehind = 1
|
override val preloadBehind = 1
|
||||||
|
|
||||||
|
private val state get() = (pager as ImageViewContinuous).state
|
||||||
|
|
||||||
private fun scrollByHalfPage(direction: Int) {
|
private fun scrollByHalfPage(direction: Int) {
|
||||||
val state = (pager as ImageViewContinuous).state
|
state.animateScroll(direction * state.height / 2f)
|
||||||
val totalDistance = direction * state.height / 2f
|
|
||||||
state.animateScroll(totalDistance)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun moveRight() = scrollByHalfPage(1)
|
override fun moveRight() = scrollByHalfPage(1)
|
||||||
@@ -24,6 +25,11 @@ class WebGpuViewerContinuous(activity: ReaderActivity) :
|
|||||||
|
|
||||||
override fun moveToPage(page: ReaderPage) {
|
override fun moveToPage(page: ReaderPage) {
|
||||||
super.moveToPage(page)
|
super.moveToPage(page)
|
||||||
(pager as ImageViewContinuous).state.scrollY = 0f
|
// Also for a jump to the page already showing, which turns nothing to slide in.
|
||||||
|
state.resetScroll()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun animateTurn(direction: Int, fromSpread: ImagePage) {
|
||||||
|
state.animateSlideIn(direction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ swipe = "1.3.0"
|
|||||||
tapmoc = "0.4.2"
|
tapmoc = "0.4.2"
|
||||||
unifile = "08f224c8f9"
|
unifile = "08f224c8f9"
|
||||||
voyager = "2.2.21-1.10.3"
|
voyager = "2.2.21-1.10.3"
|
||||||
webgpuviewer = "38"
|
webgpuviewer = "39"
|
||||||
xmlutil = "1.0.2"
|
xmlutil = "1.0.2"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
|||||||
Reference in New Issue
Block a user