v1.0.47 (#279)
* Add performance and stylus debugging logs * Refactor and decouple UI models from `MainViewModel` * Refactor library state management and projection logic * Implement desktop shell using Compose Multiplatform * Implement desktop shell using Compose Multiplatform * Implement desktop shell using Compose Multiplatform * Introduce ReaderEngine and enhance EPUB reader features in windows app * Move core paginated reader logic to a Kotlin Multiplatform `shared` module and introduce experimental desktop support. * Implement PDF rendering and text extraction for desktop using Pdfium * Add `NonReaderScreens.kt` and UI dependencies * Refactor and centralize library state management and models to improve cross-platform consistency * Implement JSON persistence for desktop library and enhance library management features including shelf CRUD, tagging, and metadata editing * Implement PDF annotation system and enhanced zoom controls for the desktop viewer * Implement WebView-based EPUB rendering for desktop using CEF and embedded resources * Optimize UI state projection, navigation state handling, and main screen pager performance * Implement Bring Your Own Key (BYOK) support for AI features in OSS version * Support Gemini-based Cloud TTS with BYOK support for OSS builds * Refactor table cell image sizing in `PaginatedReader` and improve `MobiParser` native library loading and error handling. * crash fixes * Enhance navigation stability with lifecycle-aware safety checks and update `navigation-compose` to 2.9.6 * Implement dynamic bottom padding for the page info bar to account for device rounded corners * Implement bidirectional jump history navigation and replace the jump-back pill with a dedicated `PdfJumpHistoryBar` * Optimize PDF tiling performance and refine pan-and-fling gesture handling * Implement customizable toolbars with drag-and-drop reordering and placement for PDF and EPUB readers * Updated UI for customize toolbar * Refine drag-and-drop reordering and section assignment for PDF and EPUB reader controls * restructure PDF viewer UI component hierarchy to fix verifier crash * Implement separate text dimming factors for light and dark themes * Synchronize Pdfium access and improve resource lifecycle safety across Kotlin and native layers * Enhance image alignment in paginated and EPUB readers through anchor detection and style-based positioning * Centralize file type resolution logic and implement HTML sanitization during import * Introduce vertical margin customization and configurable progress bar positioning * texture support in epub reader * Enhance TTS session management, progress tracking, and diagnostic logging * Optimize library state projection and folder synchronization performance by refactoring collection lookups and refining metadata extraction logic. * Refine TTS page mapping for PDF and overhaul TTS control UI * Implement natural session completion logic in `TtsPlaybackManager` for cloud tts * Replace Snackbar with `CustomTopBanner` for notifications in `PdfViewerScreen` * Refine TTS playback continuity across PDF pages and improve state management for session transitions * Implement global texture transparency and enhance textured theme support across PDF and EPUB readers. * Update reader themes and improve texture rendering in page animations, EPUB UI, and immersive mode * Add Support Project screen * Optimize library performance via projection caching, batch database updates, and scoped folder synchronization. * Enhance folder synchronization with fallback query mechanisms and refactor annotation sidecar importing logic * Bump version to 1.0.47 (51)
This commit is contained in:
parent
f42de6b462
commit
d7a9cae9e1
126 changed files with 15287 additions and 3154 deletions
|
|
@ -21,6 +21,7 @@ import androidx.compose.foundation.gestures.awaitEachGesture
|
|||
import androidx.compose.foundation.gestures.awaitFirstDown
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -147,7 +148,7 @@ import coil.compose.AsyncImage
|
|||
import coil.imageLoader
|
||||
import coil.request.ImageRequest.Builder
|
||||
import com.aryan.reader.R
|
||||
import com.aryan.reader.ReaderTexture
|
||||
import com.aryan.reader.loadReaderTextureBitmap
|
||||
import com.aryan.reader.countWords
|
||||
import com.aryan.reader.epub.EpubBook
|
||||
import com.aryan.reader.epubreader.HighlightColor
|
||||
|
|
@ -251,6 +252,12 @@ private fun headerFontScale(level: Int): Float = when (level) {
|
|||
else -> 1.0f
|
||||
}
|
||||
|
||||
private const val WEB_VIEW_NORMAL_LINE_HEIGHT_MULTIPLIER = 1.2f
|
||||
|
||||
private fun paginationLineHeightMultiplierForWebViewSetting(multiplier: Float): Float {
|
||||
return if (abs(multiplier - 1.0f) < 0.001f) WEB_VIEW_NORMAL_LINE_HEIGHT_MULTIPLIER else multiplier
|
||||
}
|
||||
|
||||
private fun createHeaderTextStyle(
|
||||
baseStyle: TextStyle,
|
||||
level: Int,
|
||||
|
|
@ -473,6 +480,51 @@ private fun computeImageRenderSizeDp(
|
|||
return with(density) { widthPx.toDp() to heightPx.toDp() }
|
||||
}
|
||||
|
||||
private fun imageBlockContentAlignment(style: BlockStyle): Alignment {
|
||||
return when {
|
||||
style.float == "right" || style.horizontalAlign == "right" || style.horizontalAlign == "end" -> Alignment.CenterEnd
|
||||
style.float == "left" || style.horizontalAlign == "left" || style.horizontalAlign == "start" -> Alignment.CenterStart
|
||||
else -> Alignment.Center
|
||||
}
|
||||
}
|
||||
|
||||
private fun tableCellImageModifier(
|
||||
block: ImageBlock,
|
||||
density: Density,
|
||||
imageSizeMultiplier: Float
|
||||
): Modifier {
|
||||
val baseModifier = if (block.style.width.isSpecified && block.style.width > 0.dp) {
|
||||
Modifier.width(block.style.width * imageSizeMultiplier)
|
||||
} else {
|
||||
Modifier.fillMaxWidth(imageSizeMultiplier.coerceIn(0f, 1f))
|
||||
}
|
||||
|
||||
val intrinsicWidth = block.intrinsicWidth
|
||||
val intrinsicHeight = block.intrinsicHeight
|
||||
val sizedModifier = if (
|
||||
intrinsicWidth != null &&
|
||||
intrinsicHeight != null &&
|
||||
intrinsicWidth > 0f &&
|
||||
intrinsicHeight > 0f
|
||||
) {
|
||||
baseModifier.aspectRatio(intrinsicWidth / intrinsicHeight)
|
||||
} else {
|
||||
baseModifier.height(
|
||||
if (block.expectedHeight > 0) {
|
||||
with(density) { (block.expectedHeight * imageSizeMultiplier).toDp() }
|
||||
} else {
|
||||
250.dp
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return if (block.style.maxWidth.isSpecified && block.style.maxWidth > 0.dp) {
|
||||
sizedModifier.widthIn(max = block.style.maxWidth * imageSizeMultiplier)
|
||||
} else {
|
||||
sizedModifier
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WrappingContentLayout(
|
||||
block: WrappingContentBlock,
|
||||
|
|
@ -682,6 +734,7 @@ fun PaginatedReaderScreen(
|
|||
paragraphGapMultiplier: Float,
|
||||
imageSizeMultiplier: Float,
|
||||
horizontalMarginMultiplier: Float,
|
||||
verticalMarginMultiplier: Float,
|
||||
fontFamily: FontFamily,
|
||||
textAlign: ReaderTextAlign,
|
||||
ttsHighlightInfo: TtsHighlightInfo?,
|
||||
|
|
@ -702,7 +755,8 @@ fun PaginatedReaderScreen(
|
|||
onHighlightDeleted: (String) -> Unit,
|
||||
activeHighlightPalette: List<HighlightColor>,
|
||||
onUpdatePalette: (Int, HighlightColor) -> Unit,
|
||||
activeTextureId: String? = null
|
||||
activeTextureId: String? = null,
|
||||
activeTextureAlpha: Float = 0.55f
|
||||
) {
|
||||
LaunchedEffect(userHighlights) {
|
||||
Timber.d("PaginatedReaderScreen: Received ${userHighlights.size} highlights.")
|
||||
|
|
@ -713,11 +767,7 @@ fun PaginatedReaderScreen(
|
|||
|
||||
val context = LocalContext.current
|
||||
val textureBitmap = remember(activeTextureId) {
|
||||
activeTextureId?.let { id ->
|
||||
ReaderTexture.entries.find { it.id == id }?.resId?.let { resId ->
|
||||
ImageBitmap.imageResource(context.resources, resId)
|
||||
}
|
||||
}
|
||||
loadReaderTextureBitmap(context, activeTextureId)
|
||||
}
|
||||
|
||||
val textureModifier = if (textureBitmap != null) {
|
||||
|
|
@ -725,13 +775,13 @@ fun PaginatedReaderScreen(
|
|||
val brush = ShaderBrush(
|
||||
ImageShader(textureBitmap, TileMode.Repeated, TileMode.Repeated)
|
||||
)
|
||||
drawRect(brush = brush, blendMode = BlendMode.Multiply, alpha = 0.6f)
|
||||
drawRect(brush = brush, blendMode = BlendMode.SrcOver, alpha = activeTextureAlpha.coerceIn(0f, 1f))
|
||||
}
|
||||
} else Modifier
|
||||
|
||||
var isNavigatingByLink by remember { mutableStateOf(false) }
|
||||
|
||||
BoxWithConstraints(modifier = modifier.fillMaxSize().background(effectiveBg).then(textureModifier)) {
|
||||
BoxWithConstraints(modifier = modifier.fillMaxSize().background(effectiveBg)) {
|
||||
val textMeasurer = rememberTextMeasurer()
|
||||
val baseTextStyle = MaterialTheme.typography.bodyLarge
|
||||
|
||||
|
|
@ -740,6 +790,7 @@ fun PaginatedReaderScreen(
|
|||
var debouncedParagraphGapMult by remember { mutableFloatStateOf(paragraphGapMultiplier) }
|
||||
var debouncedImageSizeMult by remember { mutableFloatStateOf(imageSizeMultiplier) }
|
||||
var debouncedHorizontalMarginMult by remember { mutableFloatStateOf(horizontalMarginMultiplier) }
|
||||
var debouncedVerticalMarginMult by remember { mutableFloatStateOf(verticalMarginMultiplier) }
|
||||
var debouncedFontFamily by remember { mutableStateOf(fontFamily) }
|
||||
var debouncedTextAlign by remember { mutableStateOf(textAlign) }
|
||||
|
||||
|
|
@ -781,7 +832,7 @@ fun PaginatedReaderScreen(
|
|||
debouncedFontFamily
|
||||
) {
|
||||
val adjustedFontSize = baseTextStyle.fontSize * debouncedFontSizeMult
|
||||
val adjustedLineHeight = adjustedFontSize * debouncedLineHeightMult
|
||||
val adjustedLineHeight = adjustedFontSize * paginationLineHeightMultiplierForWebViewSetting(debouncedLineHeightMult)
|
||||
|
||||
baseTextStyle.copy(
|
||||
color = effectiveText,
|
||||
|
|
@ -810,12 +861,13 @@ fun PaginatedReaderScreen(
|
|||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(fontSizeMultiplier, lineHeightMultiplier, paragraphGapMultiplier, imageSizeMultiplier, horizontalMarginMultiplier, fontFamily, textAlign) {
|
||||
LaunchedEffect(fontSizeMultiplier, lineHeightMultiplier, paragraphGapMultiplier, imageSizeMultiplier, horizontalMarginMultiplier, verticalMarginMultiplier, fontFamily, textAlign) {
|
||||
if (fontSizeMultiplier != debouncedFontSizeMult ||
|
||||
lineHeightMultiplier != debouncedLineHeightMult ||
|
||||
paragraphGapMultiplier != debouncedParagraphGapMult ||
|
||||
imageSizeMultiplier != debouncedImageSizeMult ||
|
||||
horizontalMarginMultiplier != debouncedHorizontalMarginMult ||
|
||||
verticalMarginMultiplier != debouncedVerticalMarginMult ||
|
||||
fontFamily != debouncedFontFamily ||
|
||||
textAlign != debouncedTextAlign
|
||||
) {
|
||||
|
|
@ -836,6 +888,7 @@ fun PaginatedReaderScreen(
|
|||
debouncedParagraphGapMult = paragraphGapMultiplier
|
||||
debouncedImageSizeMult = imageSizeMultiplier
|
||||
debouncedHorizontalMarginMult = horizontalMarginMultiplier
|
||||
debouncedVerticalMarginMult = verticalMarginMultiplier
|
||||
debouncedFontFamily = fontFamily
|
||||
debouncedTextAlign = textAlign
|
||||
Timber.d("Debounce complete. Applying new format settings.")
|
||||
|
|
@ -851,8 +904,28 @@ fun PaginatedReaderScreen(
|
|||
}
|
||||
|
||||
val density = LocalDensity.current
|
||||
val horizontalPadding = 16.dp * debouncedHorizontalMarginMult
|
||||
val verticalPadding = 16.dp
|
||||
val requestedHorizontalPadding = 16.dp * debouncedHorizontalMarginMult
|
||||
val requestedVerticalPadding = 16.dp * debouncedVerticalMarginMult
|
||||
val effectiveReaderPadding =
|
||||
remember(this.constraints, density, requestedHorizontalPadding, requestedVerticalPadding) {
|
||||
val requestedHorizontalPaddingPx = with(density) { requestedHorizontalPadding.roundToPx() }
|
||||
val requestedVerticalPaddingPx = with(density) { requestedVerticalPadding.roundToPx() }
|
||||
val minReadableWidthPx = with(density) { 96.dp.roundToPx() }
|
||||
.coerceAtMost(this.constraints.maxWidth)
|
||||
val minReadableHeightPx = with(density) { 160.dp.roundToPx() }
|
||||
.coerceAtMost(this.constraints.maxHeight)
|
||||
val horizontalPaddingPx = requestedHorizontalPaddingPx.coerceAtMost(
|
||||
((this.constraints.maxWidth - minReadableWidthPx) / 2).coerceAtLeast(0)
|
||||
)
|
||||
val verticalPaddingPx = requestedVerticalPaddingPx.coerceAtMost(
|
||||
((this.constraints.maxHeight - minReadableHeightPx) / 2).coerceAtLeast(0)
|
||||
)
|
||||
with(density) {
|
||||
horizontalPaddingPx.toDp() to verticalPaddingPx.toDp()
|
||||
}
|
||||
}
|
||||
val horizontalPadding = effectiveReaderPadding.first
|
||||
val verticalPadding = effectiveReaderPadding.second
|
||||
|
||||
val textConstraints =
|
||||
remember(this.constraints, density, horizontalPadding, verticalPadding) {
|
||||
|
|
@ -860,9 +933,9 @@ fun PaginatedReaderScreen(
|
|||
val verticalPaddingPx = with(density) { verticalPadding.roundToPx() }
|
||||
val finalConstraints = this.constraints.copy(
|
||||
minWidth = 0,
|
||||
maxWidth = this.constraints.maxWidth - (2 * horizontalPaddingPx),
|
||||
maxWidth = (this.constraints.maxWidth - (2 * horizontalPaddingPx)).coerceAtLeast(1),
|
||||
minHeight = 0,
|
||||
maxHeight = this.constraints.maxHeight - (2 * verticalPaddingPx)
|
||||
maxHeight = (this.constraints.maxHeight - (2 * verticalPaddingPx)).coerceAtLeast(1)
|
||||
)
|
||||
finalConstraints
|
||||
}
|
||||
|
|
@ -950,7 +1023,8 @@ fun PaginatedReaderScreen(
|
|||
mathMLRenderer = mathMLRenderer,
|
||||
userTextAlign = userTextAlign,
|
||||
paragraphGapMultiplier = debouncedParagraphGapMult,
|
||||
imageSizeMultiplier = debouncedImageSizeMult
|
||||
imageSizeMultiplier = debouncedImageSizeMult,
|
||||
verticalMarginMultiplier = debouncedVerticalMarginMult
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -1168,7 +1242,10 @@ fun PaginatedReaderScreen(
|
|||
isDarkTheme = isDarkTheme,
|
||||
activeHighlightPalette = activeHighlightPalette,
|
||||
onUpdatePalette = onUpdatePalette,
|
||||
effectiveText = effectiveText
|
||||
effectiveText = effectiveText,
|
||||
pageTextureModifier = if (isPageTurnAnimationEnabled) Modifier else textureModifier,
|
||||
pageTextureBitmap = textureBitmap,
|
||||
pageTextureAlpha = activeTextureAlpha.coerceIn(0f, 1f)
|
||||
)
|
||||
|
||||
androidx.compose.animation.AnimatedVisibility(
|
||||
|
|
@ -1989,7 +2066,10 @@ internal fun PaginatedReaderContent(
|
|||
onHighlightDeleted: (String) -> Unit,
|
||||
activeHighlightPalette: List<HighlightColor>,
|
||||
onUpdatePalette: (Int, HighlightColor) -> Unit,
|
||||
isDarkTheme: Boolean
|
||||
isDarkTheme: Boolean,
|
||||
pageTextureModifier: Modifier = Modifier,
|
||||
pageTextureBitmap: ImageBitmap? = null,
|
||||
pageTextureAlpha: Float = 0f
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val density = LocalDensity.current
|
||||
|
|
@ -2130,7 +2210,9 @@ internal fun PaginatedReaderContent(
|
|||
pageIndex,
|
||||
effectiveBg,
|
||||
isDarkTheme,
|
||||
pageTurnTouchY
|
||||
pageTurnTouchY,
|
||||
pageTextureBitmap,
|
||||
pageTextureAlpha
|
||||
)
|
||||
} else Modifier
|
||||
|
||||
|
|
@ -2284,7 +2366,7 @@ internal fun PaginatedReaderContent(
|
|||
pendingCrossPageSelection = null
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize().then(pageModifier)) {
|
||||
Box(modifier = Modifier.fillMaxSize().background(effectiveBg).then(pageTextureModifier).then(pageModifier)) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Box(modifier = Modifier.fillMaxSize().pointerInput(Unit) {
|
||||
detectTapGestures(
|
||||
|
|
@ -2784,12 +2866,14 @@ internal fun PaginatedReaderContent(
|
|||
val markerAreaModifier =
|
||||
Modifier.width(32.dp)
|
||||
.padding(end = 8.dp)
|
||||
val itemMarkerImage = block.itemMarkerImage
|
||||
val itemMarker = block.itemMarker
|
||||
|
||||
if (block.itemMarkerImage != null) {
|
||||
if (itemMarkerImage != null) {
|
||||
val imageRequest =
|
||||
Builder(LocalContext.current).data(
|
||||
File(
|
||||
block.itemMarkerImage
|
||||
itemMarkerImage
|
||||
)
|
||||
).crossfade(true).build()
|
||||
val imageSize = with(density) {
|
||||
|
|
@ -2805,9 +2889,9 @@ internal fun PaginatedReaderContent(
|
|||
alignment = Alignment.CenterEnd,
|
||||
contentScale = ContentScale.FillHeight
|
||||
)
|
||||
} else if (block.itemMarker != null) {
|
||||
} else if (itemMarker != null) {
|
||||
Text(
|
||||
text = block.itemMarker,
|
||||
text = itemMarker,
|
||||
style = textStyle.copy(
|
||||
textAlign = TextAlign.End
|
||||
),
|
||||
|
|
@ -3017,10 +3101,12 @@ internal fun PaginatedReaderContent(
|
|||
}
|
||||
|
||||
is MathBlock -> {
|
||||
val svgContent = block.svgContent?.takeIf { it.isNotBlank() }
|
||||
Timber.d(
|
||||
"PaginatedReader: Rendering MathBlock. Alt: '${block.altText}', Has SVG: ${!block.svgContent.isNullOrBlank()}"
|
||||
"PaginatedReader: Rendering MathBlock. Alt: '${block.altText}', Has SVG: ${svgContent != null}"
|
||||
)
|
||||
if (!block.svgContent.isNullOrBlank()) {
|
||||
if (svgContent != null) {
|
||||
val nonBlankSvgContent = svgContent
|
||||
BoxWithConstraints(
|
||||
modifier = paddingModifier
|
||||
) {
|
||||
|
|
@ -3104,7 +3190,7 @@ internal fun PaginatedReaderContent(
|
|||
val imageRequest =
|
||||
Builder(LocalContext.current).data(
|
||||
SvgData(
|
||||
block.svgContent
|
||||
nonBlankSvgContent
|
||||
)
|
||||
).listener(
|
||||
onError = { _, result ->
|
||||
|
|
@ -3189,7 +3275,10 @@ internal fun PaginatedReaderContent(
|
|||
)
|
||||
}).crossfade(true).build()
|
||||
|
||||
BoxWithConstraints(modifier = paddingModifier) {
|
||||
BoxWithConstraints(
|
||||
modifier = paddingModifier,
|
||||
contentAlignment = imageBlockContentAlignment(style)
|
||||
) {
|
||||
val scaledSize = computeImageRenderSizeDp(
|
||||
block = block,
|
||||
density = density,
|
||||
|
|
@ -3357,9 +3446,10 @@ internal fun PaginatedReaderContent(
|
|||
Row(
|
||||
verticalAlignment = Alignment.Top
|
||||
) {
|
||||
if (blockInCell.itemMarker != null) {
|
||||
val itemMarker = blockInCell.itemMarker
|
||||
if (itemMarker != null) {
|
||||
Text(
|
||||
text = blockInCell.itemMarker,
|
||||
text = itemMarker,
|
||||
style = cellTextStyle,
|
||||
modifier = Modifier.padding(
|
||||
end = 4.dp
|
||||
|
|
@ -3390,40 +3480,23 @@ internal fun PaginatedReaderContent(
|
|||
}
|
||||
|
||||
is ImageBlock -> {
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
|
||||
val scaledSize = computeImageRenderSizeDp(
|
||||
AsyncImage(
|
||||
model = Builder(
|
||||
LocalContext.current
|
||||
).data(
|
||||
File(
|
||||
blockInCell.path
|
||||
)
|
||||
)
|
||||
.build(),
|
||||
contentDescription = blockInCell.altText,
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = tableCellImageModifier(
|
||||
block = blockInCell,
|
||||
density = density,
|
||||
maxWidthDp = maxWidth,
|
||||
imageSizeMultiplier = imageSizeMultiplier
|
||||
)
|
||||
val imageModifier = Modifier.then(
|
||||
if (scaledSize != null) {
|
||||
Modifier.width(scaledSize.first).height(scaledSize.second)
|
||||
} else {
|
||||
Modifier.fillMaxWidth().then(
|
||||
if (blockInCell.expectedHeight > 0) {
|
||||
Modifier.height(with(density) { (blockInCell.expectedHeight * imageSizeMultiplier).toDp() })
|
||||
} else {
|
||||
Modifier.height(250.dp)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
AsyncImage(
|
||||
model = Builder(
|
||||
LocalContext.current
|
||||
).data(
|
||||
File(
|
||||
blockInCell.path
|
||||
)
|
||||
)
|
||||
.build(),
|
||||
contentDescription = blockInCell.altText,
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = imageModifier
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
is TextContentBlock -> {
|
||||
|
|
@ -4101,10 +4174,12 @@ private fun RenderFlexChildBlock(
|
|||
val markerAreaModifier = Modifier
|
||||
.width(32.dp)
|
||||
.padding(end = 8.dp)
|
||||
val itemMarkerImage = childBlock.itemMarkerImage
|
||||
val itemMarker = childBlock.itemMarker
|
||||
|
||||
if (childBlock.itemMarkerImage != null) {
|
||||
if (itemMarkerImage != null) {
|
||||
val imageRequest =
|
||||
Builder(LocalContext.current).data(File(childBlock.itemMarkerImage))
|
||||
Builder(LocalContext.current).data(File(itemMarkerImage))
|
||||
.crossfade(true).build()
|
||||
val imageSize = with(density) { (textStyle.fontSize.value * 0.8f).sp.toDp() }
|
||||
|
||||
|
|
@ -4115,9 +4190,9 @@ private fun RenderFlexChildBlock(
|
|||
alignment = Alignment.CenterEnd,
|
||||
contentScale = ContentScale.FillHeight
|
||||
)
|
||||
} else if (childBlock.itemMarker != null) {
|
||||
} else if (itemMarker != null) {
|
||||
Text(
|
||||
text = childBlock.itemMarker,
|
||||
text = itemMarker,
|
||||
style = textStyle.copy(textAlign = TextAlign.End),
|
||||
modifier = markerAreaModifier
|
||||
)
|
||||
|
|
@ -4160,7 +4235,7 @@ private fun RenderFlexChildBlock(
|
|||
ColorFilter.colorMatrix(ColorMatrix(matrix))
|
||||
} else null
|
||||
|
||||
BoxWithConstraints {
|
||||
BoxWithConstraints(contentAlignment = imageBlockContentAlignment(style)) {
|
||||
val scaledSize = computeImageRenderSizeDp(
|
||||
block = childBlock,
|
||||
density = density,
|
||||
|
|
@ -4278,37 +4353,20 @@ private fun RenderFlexChildBlock(
|
|||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
} else if (blockInCell is ImageBlock) {
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxWidth()) {
|
||||
val scaledSize = computeImageRenderSizeDp(
|
||||
AsyncImage(
|
||||
model = Builder(LocalContext.current).data(
|
||||
File(
|
||||
blockInCell.path
|
||||
)
|
||||
).build(),
|
||||
contentDescription = blockInCell.altText,
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = tableCellImageModifier(
|
||||
block = blockInCell,
|
||||
density = density,
|
||||
maxWidthDp = maxWidth,
|
||||
imageSizeMultiplier = imageSizeMultiplier
|
||||
)
|
||||
val imageModifier = Modifier.then(
|
||||
if (scaledSize != null) {
|
||||
Modifier.width(scaledSize.first).height(scaledSize.second)
|
||||
} else {
|
||||
Modifier.fillMaxWidth().then(
|
||||
if (blockInCell.expectedHeight > 0) {
|
||||
Modifier.height(with(density) { (blockInCell.expectedHeight * imageSizeMultiplier).toDp() })
|
||||
} else {
|
||||
Modifier.height(250.dp)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
AsyncImage(
|
||||
model = Builder(LocalContext.current).data(
|
||||
File(
|
||||
blockInCell.path
|
||||
)
|
||||
).build(),
|
||||
contentDescription = blockInCell.altText,
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = imageModifier
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4332,7 +4390,9 @@ private fun Modifier.realisticBookPage(
|
|||
pageIndex: Int,
|
||||
paperColor: Color,
|
||||
isDarkTheme: Boolean,
|
||||
touchY: Float?
|
||||
touchY: Float?,
|
||||
textureBitmap: ImageBitmap? = null,
|
||||
textureAlpha: Float = 0f
|
||||
): Modifier = composed {
|
||||
|
||||
val frontPath = remember { Path() }
|
||||
|
|
@ -4360,9 +4420,19 @@ private fun Modifier.realisticBookPage(
|
|||
.drawWithContent {
|
||||
val drawStart = System.nanoTime()
|
||||
val pageOffset = (pageIndex - pagerState.currentPage) - pagerState.currentPageOffsetFraction
|
||||
fun drawPaperBackground() {
|
||||
drawRect(color = paperColor)
|
||||
if (textureBitmap != null && textureAlpha > 0f) {
|
||||
drawRect(
|
||||
brush = ShaderBrush(ImageShader(textureBitmap, TileMode.Repeated, TileMode.Repeated)),
|
||||
blendMode = BlendMode.SrcOver,
|
||||
alpha = textureAlpha
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (abs(pageOffset) < 0.001f) {
|
||||
drawRect(color = paperColor)
|
||||
drawPaperBackground()
|
||||
drawContent()
|
||||
}
|
||||
else if (pageOffset < 0f && pageOffset > -1f) {
|
||||
|
|
@ -4423,7 +4493,7 @@ private fun Modifier.realisticBookPage(
|
|||
frontPath.close()
|
||||
|
||||
clipPath(frontPath) {
|
||||
drawRect(color = paperColor)
|
||||
drawPaperBackground()
|
||||
this@drawWithContent.drawContent()
|
||||
}
|
||||
|
||||
|
|
@ -4466,6 +4536,15 @@ private fun Modifier.realisticBookPage(
|
|||
clipRect(0f, 0f, w, h) {
|
||||
clipPath(frontPath) {
|
||||
drawPath(reflectedScreenPath, color = paperColor)
|
||||
if (textureBitmap != null && textureAlpha > 0f) {
|
||||
clipPath(reflectedScreenPath) {
|
||||
drawRect(
|
||||
brush = ShaderBrush(ImageShader(textureBitmap, TileMode.Repeated, TileMode.Repeated)),
|
||||
blendMode = BlendMode.SrcOver,
|
||||
alpha = textureAlpha
|
||||
)
|
||||
}
|
||||
}
|
||||
val flapTint = if (isDarkTheme) Color.White.copy(alpha = 0.08f) else Color.Black.copy(alpha = 0.06f)
|
||||
drawPath(reflectedScreenPath, color = flapTint)
|
||||
|
||||
|
|
@ -4493,12 +4572,12 @@ private fun Modifier.realisticBookPage(
|
|||
}
|
||||
|
||||
} else {
|
||||
drawRect(color = paperColor)
|
||||
drawPaperBackground()
|
||||
drawContent()
|
||||
}
|
||||
}
|
||||
else {
|
||||
drawRect(color = paperColor)
|
||||
drawPaperBackground()
|
||||
drawContent()
|
||||
}
|
||||
|
||||
|
|
@ -4515,10 +4594,14 @@ fun Modifier.drawCssBorders(
|
|||
blockStyle: BlockStyle,
|
||||
@Suppress("unused") density: Density
|
||||
): Modifier = this.drawBehind {
|
||||
val topWidth = blockStyle.borderTop?.width?.toPx() ?: 0f
|
||||
val rightWidth = blockStyle.borderRight?.width?.toPx() ?: 0f
|
||||
val bottomWidth = blockStyle.borderBottom?.width?.toPx() ?: 0f
|
||||
val leftWidth = blockStyle.borderLeft?.width?.toPx() ?: 0f
|
||||
val borderTop = blockStyle.borderTop
|
||||
val borderRight = blockStyle.borderRight
|
||||
val borderBottom = blockStyle.borderBottom
|
||||
val borderLeft = blockStyle.borderLeft
|
||||
val topWidth = borderTop?.width?.toPx() ?: 0f
|
||||
val rightWidth = borderRight?.width?.toPx() ?: 0f
|
||||
val bottomWidth = borderBottom?.width?.toPx() ?: 0f
|
||||
val leftWidth = borderLeft?.width?.toPx() ?: 0f
|
||||
|
||||
val tlRadius = blockStyle.borderTopLeftRadius.toPx()
|
||||
val trRadius = blockStyle.borderTopRightRadius.toPx()
|
||||
|
|
@ -4550,9 +4633,9 @@ fun Modifier.drawCssBorders(
|
|||
}
|
||||
|
||||
// TOP
|
||||
if (topWidth > 0f && blockStyle.borderTop != null) {
|
||||
val color = blockStyle.borderTop.color
|
||||
val effect = getPathEffect(blockStyle.borderTop.style, topWidth)
|
||||
if (topWidth > 0f && borderTop != null) {
|
||||
val color = borderTop.color
|
||||
val effect = getPathEffect(borderTop.style, topWidth)
|
||||
val offset = topWidth / 2f
|
||||
|
||||
val startX = if (tlRadius > 0) tlRadius else 0f
|
||||
|
|
@ -4568,9 +4651,9 @@ fun Modifier.drawCssBorders(
|
|||
}
|
||||
|
||||
// BOTTOM
|
||||
if (bottomWidth > 0f && blockStyle.borderBottom != null) {
|
||||
val color = blockStyle.borderBottom.color
|
||||
val effect = getPathEffect(blockStyle.borderBottom.style, bottomWidth)
|
||||
if (bottomWidth > 0f && borderBottom != null) {
|
||||
val color = borderBottom.color
|
||||
val effect = getPathEffect(borderBottom.style, bottomWidth)
|
||||
val offset = size.height - (bottomWidth / 2f)
|
||||
|
||||
val startX = if (blRadius > 0) blRadius else 0f
|
||||
|
|
@ -4586,9 +4669,9 @@ fun Modifier.drawCssBorders(
|
|||
}
|
||||
|
||||
// LEFT
|
||||
if (leftWidth > 0f && blockStyle.borderLeft != null) {
|
||||
val color = blockStyle.borderLeft.color
|
||||
val effect = getPathEffect(blockStyle.borderLeft.style, leftWidth)
|
||||
if (leftWidth > 0f && borderLeft != null) {
|
||||
val color = borderLeft.color
|
||||
val effect = getPathEffect(borderLeft.style, leftWidth)
|
||||
val offset = leftWidth / 2f
|
||||
|
||||
val startY = if (tlRadius > 0) tlRadius else 0f
|
||||
|
|
@ -4604,9 +4687,9 @@ fun Modifier.drawCssBorders(
|
|||
}
|
||||
|
||||
// RIGHT
|
||||
if (rightWidth > 0f && blockStyle.borderRight != null) {
|
||||
val color = blockStyle.borderRight.color
|
||||
val effect = getPathEffect(blockStyle.borderRight.style, rightWidth)
|
||||
if (rightWidth > 0f && borderRight != null) {
|
||||
val color = borderRight.color
|
||||
val effect = getPathEffect(borderRight.style, rightWidth)
|
||||
val offset = size.width - (rightWidth / 2f)
|
||||
|
||||
val startY = if (trRadius > 0) trRadius else 0f
|
||||
|
|
@ -4621,9 +4704,9 @@ fun Modifier.drawCssBorders(
|
|||
)
|
||||
}
|
||||
|
||||
if (tlRadius > 0f && topWidth > 0f && leftWidth > 0f && blockStyle.borderTop != null) {
|
||||
if (tlRadius > 0f && topWidth > 0f && leftWidth > 0f && borderTop != null) {
|
||||
drawArc(
|
||||
color = blockStyle.borderTop.color,
|
||||
color = borderTop.color,
|
||||
startAngle = 180f, sweepAngle = 90f,
|
||||
useCenter = false,
|
||||
topLeft = Offset(leftWidth/2f, topWidth/2f),
|
||||
|
|
@ -4632,9 +4715,9 @@ fun Modifier.drawCssBorders(
|
|||
)
|
||||
}
|
||||
|
||||
if (trRadius > 0f && topWidth > 0f && rightWidth > 0f && blockStyle.borderTop != null) {
|
||||
if (trRadius > 0f && topWidth > 0f && rightWidth > 0f && borderTop != null) {
|
||||
drawArc(
|
||||
color = blockStyle.borderTop.color,
|
||||
color = borderTop.color,
|
||||
startAngle = 270f, sweepAngle = 90f,
|
||||
useCenter = false,
|
||||
topLeft = Offset(size.width - (trRadius * 2) + (rightWidth/2f), topWidth/2f),
|
||||
|
|
@ -4643,9 +4726,9 @@ fun Modifier.drawCssBorders(
|
|||
)
|
||||
}
|
||||
|
||||
if (brRadius > 0f && bottomWidth > 0f && rightWidth > 0f && blockStyle.borderBottom != null) {
|
||||
if (brRadius > 0f && bottomWidth > 0f && rightWidth > 0f && borderBottom != null) {
|
||||
drawArc(
|
||||
color = blockStyle.borderBottom.color,
|
||||
color = borderBottom.color,
|
||||
startAngle = 0f, sweepAngle = 90f,
|
||||
useCenter = false,
|
||||
topLeft = Offset(size.width - (brRadius * 2) + (rightWidth/2f), size.height - (brRadius * 2) + (bottomWidth/2f)),
|
||||
|
|
@ -4654,9 +4737,9 @@ fun Modifier.drawCssBorders(
|
|||
)
|
||||
}
|
||||
|
||||
if (blRadius > 0f && bottomWidth > 0f && leftWidth > 0f && blockStyle.borderBottom != null) {
|
||||
if (blRadius > 0f && bottomWidth > 0f && leftWidth > 0f && borderBottom != null) {
|
||||
drawArc(
|
||||
color = blockStyle.borderBottom.color,
|
||||
color = borderBottom.color,
|
||||
startAngle = 90f, sweepAngle = 90f,
|
||||
useCenter = false,
|
||||
topLeft = Offset(leftWidth/2f, size.height - (blRadius * 2) + (bottomWidth/2f)),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue