Desktop app (#308)
* Implement build profiles and feature policy for offline desktop builds * Introduce unified cross-platform Settings Hub * Refactor main settings into a hierarchical page-based navigation model * Refactor library projection to use shared multiplatform logic * Refactor UI state consumption by removing intermediate screen models * Introduce AndroidSharedStateBridge to centralize state mapping and reduction logic * Refactor state management for tabs, selection, and pinning to use shared bridge logic * Refactor file type management and validation into a centralized shared module * Centralize file type resolution and improve handling of unknown types * Centralize book import logic with SharedImportPlanner * Refactor magnifier geometry logic and coordinate mapping * Properly handle orientation changes in scroll-locked PDF reader * Add screen orientation controls to EPUB and PDF readers * Implement right-to-left (RTL) pagination support and refactor reader menus * Separate right-to-left pagination settings for PDF and EPUB * Ensure PDF page data is scoped by document key for multi tab support * Implement theme-aware link styling for the epub reader * Implement jump history for back and forward navigation in the epub reader * Improve locator handling and navigation logic in paginated reader mode * Implement stable pagination navigation and location tracking * Centralize banner message management and auto-dismiss logic in MainViewModel * Implement zoom and pan state preservation for PDF pan lock mode * Enhance reader navigation UI and workspace layout management in desktop app * Refactor reader navigation sidebar and relocate search controls in desktop app * Enhance reader UI with redesigned selection menus and bottom sheet overlays * Implement custom highlight palettes and reader theme customization in desktop app * Implement cross-platform modal layer and refine reader UI styling * Improve highlight accuracy and implement metadata enrichment on book open in desktop app * Implement two-page spread layout for paginated reader on desktop * Implement persistent caching for book loading and pagination in desktop app * Implement persistent caching for book loading and pagination in desktop app * Optimize reader settings updates by separating layout and appearance changes in desktop app * Improve desktop window branding and native Windows styling * Enhance reader selection interactions and UI across EPUB and PDF viewers in desktop app * Refine selection handle positioning and interaction logic * Implement EPUB selection debug logging and improve handle targeting * Optimize desktop book loading performance and UI responsiveness * Implement anchored zoom gestures and rendering optimizations for the Desktop PDF viewer. * Implement smooth zoom preview for the PDF reader in desktop app * Optimize PDF rendering performance and responsiveness in the desktop reader * Implement conditional diagnostic logging and update desktop build configuration * Implemented hierarchical TOC, custom scrollbars, and improved desktop modal handling * Added management options for annotations and highlights in the sidebar in desktop app * Implemented `SharedStableOutlinedTextField` and updated text input fields to use `TextFieldValue` for improved cursor and selection stability. * Refined library filters and enhanced OPDS functionality in desktop app * Improved EPUB pagination measurement and implemented layout diagnostic logging for desktop app * Added PPTX support including document parsing, rendering, and indexing * Improved PPTX rendering and layout accuracy * Implemented text autofit support for PPTX rendering * Enhanced PPTX rendering with support for custom geometry, automatic numbering, table styles, and image opacity * Improved EPUB pagination accuracy and added layout telemetry in desktop app * Improved folder synchronization with metadata-only mode and hashed sidecar management in desktop app * Implemented rich text font scaling and migrated desktop ink tools to custom pointer input handling * Implemented billing account obfuscation * Implemented hierarchical folder navigation and improved library selection functionality in desktop app * Implemented platform-aware directory resolution and multi-platform native library support for desktop * Added full-screen mode for the reader workspace * Added PDF zoom indicator and interactive vertical scrollbar with page tooltips * Refactored speech bubble prefetching to use a limited radius and improved ML detector initialization and lifecycle management * Updated PDF indexing to replace existing page text and removed search result item keys * Implemented "preparing" foreground notification for TTS service * Optimized PDF rendering performance by pre-calculating page-specific annotations * Refactored desktop packaging tasks and improved distribution configuration * Optimized EPUB parser memory usage and added path traversal protection * Refactored WorkManager monitoring logic and added work pruning * Implemented comprehensive resource cleanup and memory management for WebView-based components to prevent memory leaks * Implemented bitmap size limits and scaling to prevent canvas rendering errors * Split long text paragraphs into multiple semantic blocks during HTML parsing * Implemented local ActionMode for text selection to prevent platform crashes * Refactored PPTX text layout, optimized HtmlParser block detection, and improved banner dismissal logic * Added desktop startup splash screen and deferred WebView initialization * Reorganized settings hub and added separate PDF reader defaults * Implemented embedded cover extraction and metadata support for MOBI and FB2 formats * Implemented batching for MetadataExtractionWorker and optimized EPUB metadata extraction performance. * Implemented procedurally generated book covers and replaced static placeholders * Redesigned search UI with a top bar and results overlay in desktop app * Added PDF page gap and overlay visibility options and implemented DesktopBookImporter * Refactored PDF reader UI with tabbed inspector and improved theme background handling in desktop * Implemented PDF viewport persistence for zoom and scroll positions in desktop app * Improved desktop fullscreen implementation and state restoration * Implemented desktop window state persistence * Implemented flavor-based branding and ProGuard configuration for desktop builds * Implemented precise reader positioning and improved highlight rendering logic in desktop app * Added support for user-editable book metadata * Enhanced book metadata support and integrated info/edit dialogs * Implemented embedded EPUB metadata editing * Improved highlight mapping and added custom scrollbar styling for the reader. * Reduced desktop WebView bundle size by excluding unused locales and runtime files * Added neutral pan mode as the default PDF interaction state. * Refactored library empty states and updated primary navigation tabs in desktop app * Implemented native paginated reader and unified content rendering architecture in desktop epub reader * Implemented native EPUB image rendering for desktop and improved block layout spacing with margin collapsing. * Improved pagination overflow detection in desktop * Implemented multi-block text selection with interactive handles and CFI support in desktop epub pagination
This commit is contained in:
parent
c0d0e57e79
commit
b20ade9946
247 changed files with 43321 additions and 7087 deletions
|
|
@ -63,7 +63,6 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -81,7 +80,6 @@ import androidx.compose.runtime.withFrameNanos
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.geometry.Size
|
||||
|
|
@ -101,7 +99,6 @@ import androidx.compose.ui.layout.Layout
|
|||
import androidx.compose.ui.layout.LayoutCoordinates
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.layout.positionInWindow
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalViewConfiguration
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
|
@ -116,6 +113,8 @@ import com.aryan.reader.ml.SpeechBubble
|
|||
import com.aryan.reader.pdf.data.PdfAnnotation
|
||||
import com.aryan.reader.pdf.data.PdfTextBox
|
||||
import com.aryan.reader.pdf.data.VirtualPage
|
||||
import com.aryan.reader.shared.pdf.calculatePdfVerticalPageLayoutPx
|
||||
import com.aryan.reader.shared.pdf.pdfVerticalPageGapDp
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -175,14 +174,70 @@ fun rememberVerticalPdfReaderState(): VerticalPdfReaderState {
|
|||
|
||||
private data class PdfPageLayout(
|
||||
val index: Int,
|
||||
val y: Float,
|
||||
val height: Float,
|
||||
val width: Float,
|
||||
val yPx: Int,
|
||||
val heightPx: Int,
|
||||
val widthPx: Int,
|
||||
val widthDp: Dp,
|
||||
val heightDp: Dp
|
||||
) {
|
||||
val y: Float
|
||||
get() = yPx.toFloat()
|
||||
|
||||
val height: Float
|
||||
get() = heightPx.toFloat()
|
||||
|
||||
val width: Float
|
||||
get() = widthPx.toFloat()
|
||||
}
|
||||
|
||||
private data class DividerLayout(val yPx: Int, val widthPx: Int, val heightPx: Int) {
|
||||
val y: Float
|
||||
get() = yPx.toFloat()
|
||||
|
||||
val width: Float
|
||||
get() = widthPx.toFloat()
|
||||
|
||||
val height: Float
|
||||
get() = heightPx.toFloat()
|
||||
}
|
||||
|
||||
internal data class PdfLockedOrientationResetCamera(
|
||||
val zoom: Float,
|
||||
val panX: Float,
|
||||
val panY: Float
|
||||
)
|
||||
|
||||
private data class DividerLayout(val y: Float, val width: Float, val height: Float)
|
||||
internal fun calculateLockedOrientationResetCamera(
|
||||
pageTopY: Float,
|
||||
totalDocHeight: Float,
|
||||
screenWidth: Float,
|
||||
screenHeight: Float,
|
||||
headerHeightPx: Float,
|
||||
footerHeightPx: Float,
|
||||
fitZoom: Float
|
||||
): PdfLockedOrientationResetCamera {
|
||||
val targetPanY = headerHeightPx - (pageTopY * fitZoom)
|
||||
val zoomedDocHeight = totalDocHeight * fitZoom
|
||||
val minPanY = if (zoomedDocHeight < (screenHeight - headerHeightPx - footerHeightPx)) {
|
||||
headerHeightPx
|
||||
} else {
|
||||
(screenHeight - footerHeightPx - zoomedDocHeight).coerceAtMost(headerHeightPx)
|
||||
}
|
||||
val finalPanY = targetPanY.coerceIn(minPanY, headerHeightPx)
|
||||
|
||||
val zoomedDocWidth = screenWidth * fitZoom
|
||||
val targetPanX = if (zoomedDocWidth < screenWidth) {
|
||||
(screenWidth - zoomedDocWidth) / 2f
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
|
||||
return PdfLockedOrientationResetCamera(
|
||||
zoom = fitZoom,
|
||||
panX = targetPanX,
|
||||
panY = finalPanY
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("UnusedVariable")
|
||||
@SuppressLint("UnusedBoxWithConstraintsScope", "BinaryOperationInTimber")
|
||||
|
|
@ -192,6 +247,7 @@ internal fun PdfVerticalReader(
|
|||
modifier: Modifier = Modifier,
|
||||
state: VerticalPdfReaderState,
|
||||
pdfDocument: StableHolder<ReaderDocument>,
|
||||
documentKey: String,
|
||||
activeTheme: com.aryan.reader.ReaderTheme,
|
||||
activeTextureAlpha: Float = 0.55f,
|
||||
excludeImages: Boolean = false,
|
||||
|
|
@ -230,6 +286,7 @@ internal fun PdfVerticalReader(
|
|||
selectedTool: InkType,
|
||||
richTextController: RichTextController? = null,
|
||||
textBoxes: List<PdfTextBox> = emptyList(),
|
||||
textBoxesByPage: Map<Int, List<PdfTextBox>> = emptyMap(),
|
||||
selectedTextBoxId: String? = null,
|
||||
onTextBoxChange: (PdfTextBox) -> Unit = {},
|
||||
onTextBoxSelect: (String) -> Unit = {},
|
||||
|
|
@ -245,6 +302,7 @@ internal fun PdfVerticalReader(
|
|||
stylusButtonHovering: Boolean = false,
|
||||
isHighlighterSnapEnabled: Boolean = false,
|
||||
userHighlights: List<PdfUserHighlight> = emptyList(),
|
||||
userHighlightsByPage: Map<Int, List<PdfUserHighlight>> = emptyMap(),
|
||||
onHighlightAdd: (Int, Pair<Int, Int>, String, PdfHighlightColor) -> Unit = { _,_,_,_ -> },
|
||||
onHighlightUpdate: (String, PdfHighlightColor) -> Unit = { _,_ -> },
|
||||
onHighlightDelete: (String) -> Unit = {},
|
||||
|
|
@ -258,9 +316,10 @@ internal fun PdfVerticalReader(
|
|||
onZoomAndPanChanged: ((Float, Offset) -> Unit)? = null,
|
||||
resetZoomTrigger: Long = 0L,
|
||||
isBubbleZoomModeActive: Boolean = false,
|
||||
showPageGap: Boolean = true,
|
||||
showPageNumberOverlay: Boolean = true,
|
||||
onDetectBubbles: suspend (Int, Bitmap) -> List<SpeechBubble> = { _, _ -> emptyList() }
|
||||
) {
|
||||
SideEffect { Timber.tag("PdfDrawPerf").v("LIST: PdfVerticalReader Recomposing.") }
|
||||
DisposableEffect(state) {
|
||||
onDispose {
|
||||
state.scrollToPageHandler = null
|
||||
|
|
@ -273,6 +332,13 @@ internal fun PdfVerticalReader(
|
|||
var globalEraserPosition by remember { mutableStateOf<Offset?>(null) }
|
||||
var isStylusEraserOverride by remember { mutableStateOf(false) }
|
||||
val isDarkMode = activeTheme.isDark || activeTheme.id == "reverse"
|
||||
val verticalPageBackgroundColor = remember(activeTheme) {
|
||||
when (activeTheme.id) {
|
||||
"no_theme", "system" -> Color.White
|
||||
"reverse" -> Color.Black
|
||||
else -> activeTheme.backgroundColor
|
||||
}
|
||||
}
|
||||
BoxWithConstraints(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.TopStart) {
|
||||
val imeInsets = WindowInsets.ime
|
||||
val density = LocalDensity.current
|
||||
|
|
@ -282,6 +348,12 @@ internal fun PdfVerticalReader(
|
|||
|
||||
val ratios = pageAspectRatios.item
|
||||
val bookmarkSet = bookmarks.item
|
||||
val effectiveTextBoxesByPage = remember(textBoxes, textBoxesByPage) {
|
||||
textBoxesByPage.ifEmpty { textBoxes.groupBy { it.pageIndex } }
|
||||
}
|
||||
val effectiveUserHighlightsByPage = remember(userHighlights, userHighlightsByPage) {
|
||||
userHighlightsByPage.ifEmpty { userHighlights.groupBy { it.pageIndex } }
|
||||
}
|
||||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
|
|
@ -294,56 +366,37 @@ internal fun PdfVerticalReader(
|
|||
val headerHeightPx = with(density) { headerHeight.toPx() }
|
||||
val footerHeightPx = with(density) { footerHeight.toPx() }
|
||||
|
||||
val dividerHeightDp = 8.dp
|
||||
val dividerHeightDp = pdfVerticalPageGapDp(showPageGap, 8.dp)
|
||||
val dividerHeightPx = with(density) { dividerHeightDp.toPx() }
|
||||
val dividerHeightPxInt = dividerHeightPx.roundToInt().coerceAtLeast(0)
|
||||
|
||||
var isFlinging by remember { mutableStateOf(false) }
|
||||
var isFastFlinging by remember { mutableStateOf(false) }
|
||||
var isInteracting by remember { mutableStateOf(false) }
|
||||
var isDragging by remember { mutableStateOf(false) }
|
||||
|
||||
val layoutState = remember(ratios, screenWidth, screenHeight, density) {
|
||||
val layoutState = remember(ratios, constraints.maxWidth, constraints.maxHeight, density, showPageGap, dividerHeightPxInt) {
|
||||
data class LayoutResult(val pages: List<PdfPageLayout>, val totalHeight: Float)
|
||||
|
||||
var currentY = 0.0
|
||||
val verticalLayout = calculatePdfVerticalPageLayoutPx(
|
||||
pageAspectRatios = ratios,
|
||||
viewportWidthPx = constraints.maxWidth,
|
||||
viewportHeightPx = constraints.maxHeight,
|
||||
pageGapPx = dividerHeightPxInt
|
||||
)
|
||||
|
||||
if (ratios.size == 1) {
|
||||
val ratio = ratios[0]
|
||||
val safeRatio = if (ratio <= 0f) 1f else ratio
|
||||
val pageHeight = screenWidth / safeRatio
|
||||
if (pageHeight < screenHeight) {
|
||||
currentY = ((screenHeight - pageHeight) / 2f).toDouble()
|
||||
}
|
||||
val pages = verticalLayout.pages.map { page ->
|
||||
PdfPageLayout(
|
||||
index = page.pageIndex,
|
||||
yPx = page.topPx,
|
||||
heightPx = page.heightPx,
|
||||
widthPx = page.widthPx,
|
||||
widthDp = with(density) { page.widthPx.toDp() },
|
||||
heightDp = with(density) { page.heightPx.toDp() }
|
||||
)
|
||||
}
|
||||
|
||||
val pages = ratios.mapIndexed { index, ratio ->
|
||||
val safeRatio = if (ratio <= 0f) 1f else ratio
|
||||
val pageHeightDouble = screenWidth.toDouble() / safeRatio.toDouble()
|
||||
val pageHeight = pageHeightDouble.toFloat()
|
||||
|
||||
val info = PdfPageLayout(
|
||||
index = index,
|
||||
y = currentY.toFloat(),
|
||||
height = pageHeight,
|
||||
width = screenWidth,
|
||||
widthDp = with(density) { screenWidth.toDp() },
|
||||
heightDp = with(density) { pageHeight.toDp() })
|
||||
|
||||
currentY += pageHeightDouble
|
||||
if (index < ratios.lastIndex) {
|
||||
currentY += dividerHeightPx
|
||||
}
|
||||
info
|
||||
}
|
||||
|
||||
val totalH = if (pages.isNotEmpty()) {
|
||||
val last = pages.last()
|
||||
last.y + last.height
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
|
||||
LayoutResult(pages, totalH)
|
||||
LayoutResult(pages, verticalLayout.totalHeightPx.toFloat())
|
||||
}
|
||||
|
||||
val layoutInfo = layoutState.pages
|
||||
|
|
@ -375,11 +428,17 @@ internal fun PdfVerticalReader(
|
|||
var isResizing by remember { mutableStateOf(false) }
|
||||
var previousScreenWidth by remember { mutableFloatStateOf(0f) }
|
||||
var previousScreenHeight by remember { mutableFloatStateOf(0f) }
|
||||
var lockedOrientationChangedDuringResize by remember { mutableStateOf(false) }
|
||||
val targetPageDuringResize = remember { mutableIntStateOf(-1) }
|
||||
|
||||
if (previousScreenWidth != screenWidth || previousScreenHeight != screenHeight) {
|
||||
if (previousScreenWidth > 0f) {
|
||||
val previousWasLandscape = previousScreenWidth > previousScreenHeight
|
||||
val currentIsLandscape = screenWidth > screenHeight
|
||||
isResizing = true
|
||||
if (isScrollLocked && previousWasLandscape != currentIsLandscape) {
|
||||
lockedOrientationChangedDuringResize = true
|
||||
}
|
||||
if (targetPageDuringResize.intValue == -1) {
|
||||
targetPageDuringResize.intValue = state.currentPage
|
||||
}
|
||||
|
|
@ -424,7 +483,45 @@ internal fun PdfVerticalReader(
|
|||
}
|
||||
|
||||
LaunchedEffect(layoutState.pages) {
|
||||
if (!isInitialLayout && !isScrollLocked) {
|
||||
if (!isInitialLayout && isScrollLocked && lockedOrientationChangedDuringResize) {
|
||||
val targetPageIdx = if (targetPageDuringResize.intValue != -1) {
|
||||
targetPageDuringResize.intValue
|
||||
} else {
|
||||
state.currentPage
|
||||
}
|
||||
|
||||
val newLayout = layoutState.pages
|
||||
val pageLayout = newLayout.getOrNull(targetPageIdx)
|
||||
|
||||
if (pageLayout != null) {
|
||||
val resetCamera = calculateLockedOrientationResetCamera(
|
||||
pageTopY = pageLayout.y,
|
||||
totalDocHeight = layoutState.totalHeight,
|
||||
screenWidth = screenWidth,
|
||||
screenHeight = screenHeight,
|
||||
headerHeightPx = headerHeightPx,
|
||||
footerHeightPx = footerHeightPx,
|
||||
fitZoom = fitZoom
|
||||
)
|
||||
|
||||
panXAnimatable.updateBounds(null, null)
|
||||
panYAnimatable.updateBounds(null, null)
|
||||
|
||||
coroutineScope {
|
||||
launch { zoomAnimatable.snapTo(resetCamera.zoom) }
|
||||
launch { panXAnimatable.snapTo(resetCamera.panX) }
|
||||
launch { panYAnimatable.snapTo(resetCamera.panY) }
|
||||
}
|
||||
|
||||
state.currentPage = targetPageIdx
|
||||
hasRestoredLockedState = true
|
||||
onZoomChange(resetCamera.zoom)
|
||||
onZoomAndPanChanged?.invoke(resetCamera.zoom, Offset(resetCamera.panX, resetCamera.panY))
|
||||
Timber.tag("PdfLockDiagnostic").i(
|
||||
"Orientation changed while locked; reset zoom to fit and kept page $targetPageIdx"
|
||||
)
|
||||
}
|
||||
} else if (!isInitialLayout && !isScrollLocked) {
|
||||
val targetPageIdx = if (targetPageDuringResize.intValue != -1) {
|
||||
targetPageDuringResize.intValue
|
||||
} else {
|
||||
|
|
@ -466,6 +563,7 @@ internal fun PdfVerticalReader(
|
|||
if (!isInitialLayout) {
|
||||
delay(50)
|
||||
isResizing = false
|
||||
lockedOrientationChangedDuringResize = false
|
||||
targetPageDuringResize.intValue = -1
|
||||
}
|
||||
isInitialLayout = false
|
||||
|
|
@ -1080,8 +1178,9 @@ internal fun PdfVerticalReader(
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(if (showPageGap) Color.Transparent else verticalPageBackgroundColor)
|
||||
.then(globalDrawingModifier)
|
||||
.pointerInput(isEditMode, selectedTool, isStylusOnlyMode) {
|
||||
.pointerInput(isEditMode, selectedTool, isStylusOnlyMode, isScrollLocked) {
|
||||
Timber.tag("PdfTouchDebug").v(
|
||||
"VerticalReader: TapPointerInput init. isEditMode=$isEditMode"
|
||||
)
|
||||
|
|
@ -1101,8 +1200,10 @@ internal fun PdfVerticalReader(
|
|||
onPageClick()
|
||||
}
|
||||
}, onDoubleTap = { offset ->
|
||||
Timber.tag("PdfTouchDebug").d("VerticalReader: DoubleTap detected")
|
||||
onDoubleTapToZoom(offset)
|
||||
if (!isScrollLocked) {
|
||||
Timber.tag("PdfTouchDebug").d("VerticalReader: DoubleTap detected")
|
||||
onDoubleTapToZoom(offset)
|
||||
}
|
||||
})
|
||||
}
|
||||
.pointerInput(
|
||||
|
|
@ -1428,11 +1529,16 @@ internal fun PdfVerticalReader(
|
|||
}
|
||||
|
||||
val cached = cachedVisiblePages.value
|
||||
val indicesMatch = cached.size == finalPages.size && cached.indices.all {
|
||||
cached[it].index == finalPages[it].index
|
||||
val layoutMatches = cached.size == finalPages.size && cached.indices.all {
|
||||
val cachedPage = cached[it]
|
||||
val newPage = finalPages[it]
|
||||
cachedPage.index == newPage.index &&
|
||||
cachedPage.yPx == newPage.yPx &&
|
||||
cachedPage.heightPx == newPage.heightPx &&
|
||||
cachedPage.widthPx == newPage.widthPx
|
||||
}
|
||||
|
||||
if (!indicesMatch) {
|
||||
if (!layoutMatches) {
|
||||
cachedVisiblePages.value = finalPages
|
||||
Timber.tag("PdfDrawPerf").d(
|
||||
"Vertical Visible Pages Changed: ${finalPages.map { it.index }} (Dragging: ${draggedBox != null})"
|
||||
|
|
@ -1472,20 +1578,13 @@ internal fun PdfVerticalReader(
|
|||
Layout(
|
||||
content = {
|
||||
visiblePages.forEach { page ->
|
||||
key(page.index) {
|
||||
key(documentKey, page.index) {
|
||||
val isBookmarked by remember(bookmarkSet, page.index) {
|
||||
derivedStateOf {
|
||||
bookmarkSet.any { it.pageIndex == page.index }
|
||||
}
|
||||
}
|
||||
|
||||
SideEffect {
|
||||
if (page.index == state.currentPage) {
|
||||
Timber.tag("PdfDrawPerf")
|
||||
.v("VERTICAL READER: Emitting Page ${page.index}")
|
||||
}
|
||||
}
|
||||
|
||||
val visibleScreenRectLambda = remember(page, screenWidth, screenHeight) {
|
||||
{
|
||||
val zoom = zoomAnimatable.value
|
||||
|
|
@ -1579,6 +1678,7 @@ internal fun PdfVerticalReader(
|
|||
{ text: String -> onSearchText(text) }
|
||||
}
|
||||
|
||||
val currentOnDoubleTapToZoom by rememberUpdatedState(onDoubleTapToZoom)
|
||||
val onDoubleTapLambda = remember(page, screenWidth, screenHeight) {
|
||||
{ localOffset: Offset ->
|
||||
Timber.tag("PdfZoomDebug").d(
|
||||
|
|
@ -1592,7 +1692,7 @@ internal fun PdfVerticalReader(
|
|||
val screenX = contentX * currentZ + panX
|
||||
val screenY = contentY * currentZ + panY
|
||||
Timber.tag("PdfZoomDebug").d("Mapped to Screen: ($screenX, $screenY)") // Added log
|
||||
onDoubleTapToZoom(Offset(screenX, screenY))
|
||||
currentOnDoubleTapToZoom(Offset(screenX, screenY))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1661,32 +1761,10 @@ internal fun PdfVerticalReader(
|
|||
|
||||
Box(modifier = Modifier
|
||||
.layoutId(page)
|
||||
.graphicsLayer {
|
||||
val z = zoomAnimatable.value
|
||||
val px = panXAnimatable.value
|
||||
val py = panYAnimatable.value
|
||||
|
||||
scaleX = z
|
||||
scaleY = z
|
||||
translationX = px
|
||||
translationY = page.y * (z - 1f) + py
|
||||
transformOrigin = TransformOrigin(0f, 0f)
|
||||
|
||||
if (page.index < 2 && z > 1.1f) {
|
||||
Timber.tag("PdfZoomDebug").v("Page ${page.index} Render: TransY=$translationY (PageY=${page.y}, GlobalY=${page.y + translationY})")
|
||||
}
|
||||
}
|
||||
.clipToBounds()
|
||||
.onGloballyPositioned { coordinates ->
|
||||
if (page.index == 0) {
|
||||
val pos = coordinates.positionInWindow()
|
||||
Timber.d(
|
||||
"Page 0 Box | GlobalPos: $pos | Size: ${coordinates.size} | PageY: ${page.y}"
|
||||
)
|
||||
}
|
||||
}) {
|
||||
) {
|
||||
PdfPageComposable(
|
||||
pdfDocument = pdfDocument,
|
||||
documentKey = documentKey,
|
||||
pageIndex = page.index,
|
||||
virtualPage = virtualPage,
|
||||
totalPages = totalPages,
|
||||
|
|
@ -1716,6 +1794,8 @@ internal fun PdfVerticalReader(
|
|||
isZoomEnabled = false,
|
||||
isScrolling = isDragging || (isFlinging && isFastFlinging),
|
||||
isVerticalScroll = true,
|
||||
showPageNumberOverlay = showPageNumberOverlay,
|
||||
isScrollLocked = isScrollLocked,
|
||||
visualScaleProvider = currentScaleProvider,
|
||||
onDoubleTap = onDoubleTapLambda,
|
||||
clearSelectionTrigger = selectionClearTrigger,
|
||||
|
|
@ -1734,11 +1814,11 @@ internal fun PdfVerticalReader(
|
|||
isStylusOnlyMode = isStylusOnlyMode,
|
||||
stylusButtonHovering = stylusButtonHovering,
|
||||
isAutoScrollPlaying = isAutoScrollPlaying,
|
||||
textBoxes = textBoxes.filter { it.pageIndex == page.index },
|
||||
textBoxes = effectiveTextBoxesByPage[page.index].orEmpty(),
|
||||
selectedTextBoxId = selectedTextBoxId,
|
||||
onTextBoxChange = onTextBoxChange,
|
||||
onTextBoxSelect = onTextBoxSelect,
|
||||
userHighlights = userHighlights.filter { it.pageIndex == page.index },
|
||||
userHighlights = effectiveUserHighlightsByPage[page.index].orEmpty(),
|
||||
onHighlightAdd = onHighlightAdd,
|
||||
onHighlightUpdate = onHighlightUpdate,
|
||||
onHighlightDelete = onHighlightDelete,
|
||||
|
|
@ -1852,26 +1932,17 @@ internal fun PdfVerticalReader(
|
|||
)
|
||||
}
|
||||
|
||||
if (page.index < totalPages - 1) {
|
||||
val dividerY = page.y + page.height
|
||||
if (page.index < totalPages - 1 && dividerHeightPxInt > 0) {
|
||||
val dividerYPx = page.yPx + page.heightPx
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.layoutId(
|
||||
DividerLayout(
|
||||
dividerY, page.width, dividerHeightPx
|
||||
yPx = dividerYPx,
|
||||
widthPx = page.widthPx,
|
||||
heightPx = dividerHeightPxInt
|
||||
)
|
||||
)
|
||||
.graphicsLayer {
|
||||
val z = zoomAnimatable.value
|
||||
val px = panXAnimatable.value
|
||||
val py = panYAnimatable.value
|
||||
|
||||
scaleX = z
|
||||
scaleY = z
|
||||
translationX = px
|
||||
translationY = dividerY * (z - 1f) + py
|
||||
transformOrigin = TransformOrigin(0f, 0f)
|
||||
}
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surfaceVariant
|
||||
))
|
||||
|
|
@ -1881,6 +1952,15 @@ internal fun PdfVerticalReader(
|
|||
},
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.graphicsLayer {
|
||||
val z = zoomAnimatable.value
|
||||
|
||||
scaleX = z
|
||||
scaleY = z
|
||||
translationX = panXAnimatable.value
|
||||
translationY = panYAnimatable.value
|
||||
transformOrigin = TransformOrigin(0f, 0f)
|
||||
}
|
||||
.onGloballyPositioned { _ -> }) { measurables, constraints ->
|
||||
val layoutStart = System.nanoTime()
|
||||
Timber.tag("PdfDrawPerf")
|
||||
|
|
@ -1891,19 +1971,19 @@ internal fun PdfVerticalReader(
|
|||
is PdfPageLayout -> {
|
||||
val placeable = measurable.measure(
|
||||
Constraints.fixed(
|
||||
id.width.roundToInt(), id.height.roundToInt()
|
||||
id.widthPx, id.heightPx
|
||||
)
|
||||
)
|
||||
placeable.place(0, id.y.roundToInt())
|
||||
placeable.place(0, id.yPx)
|
||||
}
|
||||
|
||||
is DividerLayout -> {
|
||||
val placeable = measurable.measure(
|
||||
Constraints.fixed(
|
||||
id.width.roundToInt(), id.height.roundToInt()
|
||||
id.widthPx, id.heightPx
|
||||
)
|
||||
)
|
||||
placeable.place(0, id.y.roundToInt())
|
||||
placeable.place(0, id.yPx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue