Added customizable highlight color palette in epub (#6)

This introduces a user-configurable palette for text highlighting. The four default highlight colors can now be customized by the user from an expanded set of predefined colors.

Changes include:
- Added a `PaletteManagerDialog` to allow users to select and save their preferred highlight colors.
- Implemented `saveHighlightPalette` and `loadHighlightPalette` to persist the user's choices.
- The highlighting UI in both vertical and paginated readers now displays the active custom palette.
- Added new `HighlightColor` options.
- Updated JavaScript to support the expanded color set and improve highlight interaction logic.
This commit is contained in:
Aryan 2026-02-26 14:26:54 +05:30 committed by GitHub
parent 1b048c9073
commit 1f111034f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 3044 additions and 1753 deletions

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,7 @@
*
* mail: epistemereader@gmail.com
*/
// ChapterWebView.kt
package com.aryan.reader.epubreader
import android.annotation.SuppressLint
@ -27,7 +28,6 @@ import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.graphics.Rect
import timber.log.Timber
import android.webkit.JavascriptInterface
import android.webkit.WebResourceRequest
import android.webkit.WebSettings
@ -36,13 +36,14 @@ import android.webkit.WebViewClient
import android.widget.Toast
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -69,6 +70,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
@ -86,6 +88,7 @@ import com.aryan.reader.countWords
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import org.json.JSONObject
import timber.log.Timber
import java.io.BufferedReader
import java.io.InputStreamReader
@ -326,6 +329,8 @@ fun ChapterWebView(
currentTextAlign: ReaderTextAlign,
onHighlightClicked: () -> Unit,
onAutoScrollChapterEnd: () -> Unit = {},
activeHighlightPalette: List<HighlightColor>,
onUpdatePalette: (Int, HighlightColor) -> Unit
) {
Timber.d(
"RenderChapterViaWebView for '$chapterTitle', Key: $key, isDarkTheme: $isDarkTheme, initialScrollTarget: $initialScrollTarget"
@ -340,6 +345,8 @@ fun ChapterWebView(
val jsToInject = remember(context) { getJsToInject(context) }
var showPaletteManager by remember { mutableStateOf(false) }
LaunchedEffect(currentFontSize, currentLineHeight) {
localWebViewRef?.evaluateJavascript(
"javascript:if(window.getSelection) window.getSelection().removeAllRanges();",
@ -812,34 +819,29 @@ fun ChapterWebView(
Column(
modifier = Modifier.width(IntrinsicSize.Max)
) {
// 1. Color Row (Improved sizing and gaps)
Row(
modifier = Modifier
.padding(vertical = 12.dp, horizontal = 12.dp)
.fillMaxWidth(),
horizontalArrangement = Arrangement.Center, // Centered colors
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
HighlightColor.entries.forEach { colorEnum ->
activeHighlightPalette.forEachIndexed { index, colorEnum ->
Box(
modifier = Modifier
.padding(horizontal = 8.dp)
.size(24.dp)
.padding(horizontal = 6.dp)
.size(32.dp)
.background(colorEnum.color, CircleShape)
.border(1.dp, MaterialTheme.colorScheme.outline.copy(alpha = 0.5f), CircleShape)
.clickable {
.pointerInput(colorEnum) {
detectTapGestures(
onTap = {
Timber.d("Kotlin: Color clicked. Existing? ${state.isExistingHighlight}")
if (state.isExistingHighlight && state.cfi != null) {
// UPDATE EXISTING HIGHLIGHT
Timber.d("Kotlin: Requesting UPDATE via JS for CFI: ${state.cfi}")
localWebViewRef?.evaluateJavascript(
"javascript:window.HighlightBridgeHelper.updateHighlightStyle('${state.cfi}', '${colorEnum.cssClass}', '${colorEnum.id}');",
null
)
} else {
// CREATE NEW HIGHLIGHT
Timber.d("Kotlin: Requesting CREATE via JS")
localWebViewRef?.evaluateJavascript(
"javascript:window.HighlightBridgeHelper.createUserHighlight('${colorEnum.cssClass}', '${colorEnum.id}');",
null
@ -848,9 +850,20 @@ fun ChapterWebView(
state.finishActionModeCallback()
localWebViewRef?.clearFocus()
customMenuState = null
},
onLongPress = {
showPaletteManager = true
}
)
}
)
}
Spacer(modifier = Modifier.width(8.dp))
SpectrumButton(
onClick = { showPaletteManager = true },
size = 32.dp
)
}
// 2. Delete Option (Only for existing highlights)
@ -979,5 +992,17 @@ fun ChapterWebView(
}
}
}
if (showPaletteManager) {
PaletteManagerDialog(
currentPalette = activeHighlightPalette,
onDismiss = { showPaletteManager = false },
onSave = { newPalette ->
newPalette.forEachIndexed { index, color ->
onUpdatePalette(index, color)
}
showPaletteManager = false
}
)
}
}
}

View file

@ -38,6 +38,18 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
@ -65,7 +77,17 @@ enum class HighlightColor(val id: String, val color: Color, val cssClass: String
YELLOW("yellow", Color(0xFFFBC02D), "user-highlight-yellow"),
GREEN("green", Color(0xFF388E3C), "user-highlight-green"),
BLUE("blue", Color(0xFF1976D2), "user-highlight-blue"),
RED("red", Color(0xFFD32F2F), "user-highlight-red")
RED("red", Color(0xFFD32F2F), "user-highlight-red"),
PURPLE("purple", Color(0xFF7B1FA2), "user-highlight-purple"),
ORANGE("orange", Color(0xFFF57C00), "user-highlight-orange"),
CYAN("cyan", Color(0xFF0097A7), "user-highlight-cyan"),
MAGENTA("magenta", Color(0xFFC2185B), "user-highlight-magenta"),
LIME("lime", Color(0xFFAFB42B), "user-highlight-lime"),
PINK("pink", Color(0xFFE91E63), "user-highlight-pink"),
TEAL("teal", Color(0xFF00796B), "user-highlight-teal"),
INDIGO("indigo", Color(0xFF303F9F), "user-highlight-indigo"),
BLACK("black", Color(0xFF424242), "user-highlight-black"),
WHITE("white", Color(0xFFF5F5F5), "user-highlight-white");
}
data class UserHighlight(
@ -88,6 +110,24 @@ fun escapeJsString(value: String): String {
.replace("\u2029", "\\u2029")
}
fun saveHighlightPalette(context: Context, palette: List<HighlightColor>) {
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
val ids = palette.joinToString(",") { it.id }
prefs.edit { putString("highlight_palette_ids", ids) }
}
fun loadHighlightPalette(context: Context): List<HighlightColor> {
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
val savedIds = prefs.getString("highlight_palette_ids", null)
if (savedIds != null) {
val list = savedIds.split(",").mapNotNull { id ->
HighlightColor.entries.find { it.id == id }
}
if (list.size == 4) return list
}
return listOf(HighlightColor.YELLOW, HighlightColor.GREEN, HighlightColor.BLUE, HighlightColor.RED)
}
// --- Persistence Helpers ---
fun loadBookmarks(context: Context, bookTitle: String, chapters: List<EpubChapter>, bookmarksJson: String?): Set<Bookmark> {
@ -285,3 +325,110 @@ fun BookmarkButton(
}
}
}
@Composable
fun SpectrumButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
size: androidx.compose.ui.unit.Dp = 32.dp
) {
val rainbowColors = listOf(
Color.Red, Color(0xFFFF7F00), Color.Yellow, Color.Green,
Color.Blue, Color(0xFF4B0082), Color(0xFF8B00FF)
)
Box(
modifier = modifier
.size(size)
.background(
brush = Brush.sweepGradient(rainbowColors),
shape = CircleShape
)
.clickable(onClick = onClick)
)
}
@Composable
fun PaletteManagerDialog(
currentPalette: List<HighlightColor>,
onSave: (List<HighlightColor>) -> Unit,
onDismiss: () -> Unit
) {
var tempPalette by remember { mutableStateOf(currentPalette.toMutableList()) }
var selectedSlotIndex by remember { mutableIntStateOf(0) }
AlertDialog(
onDismissRequest = onDismiss,
title = { Text("Customize Palette", style = MaterialTheme.typography.titleMedium) },
text = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.fillMaxWidth()
) {
Text("Tap a slot to edit:", style = MaterialTheme.typography.bodySmall)
Row(
horizontalArrangement = Arrangement.SpaceEvenly,
modifier = Modifier.fillMaxWidth()
) {
tempPalette.forEachIndexed { index, colorEnum ->
val isSelected = index == selectedSlotIndex
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(48.dp)
.background(colorEnum.color, CircleShape)
.border(
width = if (isSelected) 3.dp else 1.dp,
color = if (isSelected) MaterialTheme.colorScheme.primary else Color.Transparent, // Thin ring if selected
shape = CircleShape
)
.clip(CircleShape)
.clickable { selectedSlotIndex = index }
) {
if (isSelected) {
Icon(
imageVector = Icons.Default.Check,
contentDescription = "Selected Slot",
tint = if (colorEnum == HighlightColor.WHITE) Color.Black else Color.White,
modifier = Modifier.size(24.dp)
)
}
}
}
}
HorizontalDivider()
// 2. Bottom Grid: Available Colors
Text("Select a color for the slot:", style = MaterialTheme.typography.bodySmall)
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 40.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.height(200.dp)
) {
items(HighlightColor.entries) { colorOption ->
Box(
modifier = Modifier
.size(36.dp)
.background(colorOption.color, CircleShape)
.border(1.dp, MaterialTheme.colorScheme.outline.copy(alpha = 0.2f), CircleShape)
.clickable {
val newList = tempPalette.toMutableList()
newList[selectedSlotIndex] = colorOption
tempPalette = newList
}
)
}
}
}
},
confirmButton = {
TextButton(onClick = { onSave(tempPalette) }) { Text("Save") }
},
dismissButton = {
TextButton(onClick = onDismiss) { Text("Cancel") }
}
)
}

View file

@ -283,6 +283,19 @@ fun EpubReaderHost(
}
}
var currentHighlightPalette by remember {
mutableStateOf(loadHighlightPalette(context))
}
val onUpdateHighlightPalette: (Int, HighlightColor) -> Unit = { index, newColor ->
val newList = currentHighlightPalette.toMutableList()
if (index in newList.indices) {
newList[index] = newColor
currentHighlightPalette = newList
saveHighlightPalette(context, newList)
}
}
LaunchedEffect(userHighlights.size, userHighlights.toList()) {
saveHighlightsToPrefs(context, epubBook.title, userHighlights)
}
@ -1541,6 +1554,8 @@ fun EpubReaderHost(
initialCfi = cfiToLoad,
initialFragmentId = fragmentToLoad.also { },
userHighlights = userHighlights.filter { it.chapterIndex == targetChapterIndex },
activeHighlightPalette = currentHighlightPalette,
onUpdatePalette = onUpdateHighlightPalette,
onHighlightCreated = { cfi, text, colorId ->
Timber.d("Vertical Mode (Source): Creating Highlight. CFI: $cfi")
Timber.d("Vertical Mode (Source): Text Snippet: '${text.take(50)}...'")
@ -2090,6 +2105,8 @@ fun EpubReaderHost(
lineHeightMultiplier = currentLineHeight,
fontFamily = activeFontFamily,
textAlign = currentTextAlign,
activeHighlightPalette = currentHighlightPalette,
onUpdatePalette = onUpdateHighlightPalette,
ttsHighlightInfo = TtsHighlightInfo(
text = ttsState.currentText ?: "",
cfi = ttsState.sourceCfi ?: "",

View file

@ -17,6 +17,7 @@
*
* mail: epistemereader@gmail.com
*/
// PaginatedReader.kt
package com.aryan.reader.paginatedreader
import android.annotation.SuppressLint
@ -51,6 +52,9 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.shape.CircleShape
@ -143,7 +147,9 @@ import com.aryan.reader.R
import com.aryan.reader.countWords
import com.aryan.reader.epub.EpubBook
import com.aryan.reader.epubreader.HighlightColor
import com.aryan.reader.epubreader.PaletteManagerDialog
import com.aryan.reader.epubreader.ReaderTextAlign
import com.aryan.reader.epubreader.SpectrumButton
import com.aryan.reader.epubreader.TtsHighlightInfo
import com.aryan.reader.epubreader.UserHighlight
import com.aryan.reader.paginatedreader.data.BookCacheDatabase
@ -488,7 +494,9 @@ fun PaginatedReaderScreen(
onWordSelectedForAiDefinition: (String) -> Unit,
userHighlights: List<UserHighlight>,
onHighlightCreated: (String, String, String) -> Unit,
onHighlightDeleted: (String) -> Unit
onHighlightDeleted: (String) -> Unit,
activeHighlightPalette: List<HighlightColor>,
onUpdatePalette: (Int, HighlightColor) -> Unit
) {
LaunchedEffect(userHighlights) {
Timber.d("PaginatedReaderScreen: Received ${userHighlights.size} highlights.")
@ -757,7 +765,9 @@ fun PaginatedReaderScreen(
userHighlights = userHighlights,
onHighlightCreated = onHighlightCreated,
onHighlightDeleted = onHighlightDeleted,
isDarkTheme = isDarkTheme
isDarkTheme = isDarkTheme,
activeHighlightPalette = activeHighlightPalette,
onUpdatePalette = onUpdatePalette
)
}
}
@ -1079,7 +1089,7 @@ private fun TextWithEmphasis(
activeSelection: PaginatedSelection?,
@Suppress("unused") onSelectionChange: (PaginatedSelection?) -> Unit,
onHighlightClick: (UserHighlight, Rect) -> Unit,
isDarkTheme: Boolean,
@Suppress("unused") isDarkTheme: Boolean,
onRegisterLayout: ((TextLayoutResult, LayoutCoordinates) -> Unit)? = null
) {
var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
@ -1107,36 +1117,20 @@ private fun TextWithEmphasis(
range.first, range.last + 1
)
if (isDarkTheme) {
Timber.d(
"DRAWING: Highlight '${highlight.text.take(10)}' on Block ${block.cfi} at range $range"
)
}
if (isDarkTheme) {
drawPath(
path,
highlight.color.color.copy(alpha = 0.6f),
highlight.color.color.copy(alpha = 0.4f),
blendMode = BlendMode.SrcOver
)
} else {
drawPath(
path,
highlight.color.color.copy(alpha = 0.5f),
blendMode = BlendMode.Multiply
)
}
if (highlight.cfi == pressedHighlightCfi) {
drawPath(
path,
Color.White.copy(alpha = 0.3f),
Color.Black.copy(alpha = 0.1f),
blendMode = BlendMode.SrcOver
)
}
} catch (_: Exception) {
// Ignore layout errors during drawing
}
} catch (_: Exception) { }
}
}
}
@ -1174,8 +1168,7 @@ private fun TextWithEmphasis(
val lineLeft = layout.getLineLeft(lineIndex)
val lineRight = layout.getLineRight(lineIndex)
if (offset.x < minOf(lineLeft, lineRight) - 50 || offset.x > maxOf(
lineLeft,
lineRight
lineLeft, lineRight
) + 50
) {
return null
@ -1316,6 +1309,8 @@ internal fun PaginatedReaderContent(
userHighlights: List<UserHighlight>,
onHighlightCreated: (String, String, String) -> Unit,
onHighlightDeleted: (String) -> Unit,
activeHighlightPalette: List<HighlightColor>,
onUpdatePalette: (Int, HighlightColor) -> Unit,
isDarkTheme: Boolean
) {
val coroutineScope = rememberCoroutineScope()
@ -1330,6 +1325,8 @@ internal fun PaginatedReaderContent(
val blockLayoutMap = remember {
androidx.compose.runtime.mutableStateMapOf<String, Triple<TextLayoutResult, LayoutCoordinates, Int>>()
}
var showColorPickerDialog by remember { mutableStateOf<Int?>(null) }
var showPaletteManager by remember { mutableStateOf(false) }
if (showExternalLinkDialog != null) {
val urlToShow = showExternalLinkDialog!!
@ -1433,8 +1430,7 @@ internal fun PaginatedReaderContent(
LocalTextToolbar provides textToolbar, LocalClipboard provides dictionaryClipboard
) {
HorizontalPager(
state = pagerState,
modifier = Modifier.fillMaxSize()
state = pagerState, modifier = Modifier.fillMaxSize()
) { pageIndex ->
var pageContent by remember { mutableStateOf<Page?>(null) }
var currentChapterPath by remember { mutableStateOf<String?>(null) }
@ -1524,7 +1520,9 @@ internal fun PaginatedReaderContent(
if (block.style.backgroundColor.isSpecified) {
Modifier.background(
block.style.backgroundColor,
shape = if (block.style.borderRadius > 0.dp) RoundedCornerShape(block.style.borderRadius) else androidx.compose.ui.graphics.RectangleShape
shape = if (block.style.borderRadius > 0.dp) RoundedCornerShape(
block.style.borderRadius
) else androidx.compose.ui.graphics.RectangleShape
)
} else {
Modifier
@ -1535,7 +1533,9 @@ internal fun PaginatedReaderContent(
BorderStroke(
border.width, border.color
),
shape = if (block.style.borderRadius > 0.dp) RoundedCornerShape(block.style.borderRadius) else androidx.compose.ui.graphics.RectangleShape
shape = if (block.style.borderRadius > 0.dp) RoundedCornerShape(
block.style.borderRadius
) else androidx.compose.ui.graphics.RectangleShape
)
} ?: Modifier)
@ -2419,8 +2419,7 @@ internal fun PaginatedReaderContent(
File(
blockInCell.path
)
)
.build(),
).build(),
contentDescription = blockInCell.altText,
contentScale = ContentScale.Fit,
modifier = Modifier.fillMaxWidth()
@ -2492,10 +2491,9 @@ internal fun PaginatedReaderContent(
val selectionRect = state.rect
Timber.d("Menu: Selection Rect: $selectionRect")
// 1. Attempt Geometric Strategy (Hit Testing)
var geometricSuccess = false
val candidates = blockLayoutMap.filter { (_, triple) ->
val (_, coords, _) = triple // Deconstruct Triple
val (_, coords, _) = triple
if (!coords.isAttached) return@filter false
val pos = coords.positionInWindow()
val size = coords.size.toSize()
@ -2694,7 +2692,9 @@ internal fun PaginatedReaderContent(
}
}
state.onHide()
}, onDelete = null, isProUser = isProUser, isOss = isOss
}, onDelete = null, isProUser = isProUser, isOss = isOss,
activeHighlightPalette = activeHighlightPalette,
onOpenPaletteManager = { showPaletteManager = true }
)
}
}
@ -2726,11 +2726,41 @@ internal fun PaginatedReaderContent(
else sel.baseCfi
onHighlightCreated(finalCfi, sel.text, color.id)
activeSelection = null
}, onDelete = null, isProUser = isProUser, isOss = isOss
}, onDelete = null, isProUser = isProUser, isOss = isOss,
activeHighlightPalette = activeHighlightPalette,
onOpenPaletteManager = { showPaletteManager = true }
)
}
}
if (showColorPickerDialog != null) {
AlertDialog(
onDismissRequest = { showColorPickerDialog = null },
title = { Text("Select Color") },
text = {
LazyVerticalGrid(
columns = GridCells.Adaptive(minSize = 48.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(HighlightColor.entries) { colorOption ->
Box(
modifier = Modifier
.size(48.dp)
.background(colorOption.color, CircleShape)
.border(1.dp, MaterialTheme.colorScheme.outline, CircleShape)
.clickable {
onUpdatePalette(showColorPickerDialog!!, colorOption)
showColorPickerDialog = null
}
)
}
}
},
confirmButton = { TextButton(onClick = { showColorPickerDialog = null }) { Text("Close") } }
)
}
// Edit Menu (Delete)
if (activeHighlightForMenu != null) {
val (highlight, rect) = activeHighlightForMenu!!
@ -2744,25 +2774,46 @@ internal fun PaginatedReaderContent(
val clip = ClipData.newPlainText("Copied Text", highlight.text)
clipboardManager.setPrimaryClip(clip)
activeHighlightForMenu = null
}, onSelectAll = null, onDictionary = {
},
onSelectAll = null,
onDictionary = {
if (isProUser || countWords(highlight.text) <= 1) {
onWordSelectedForAiDefinition(highlight.text)
} else {
onShowDictionaryUpsellDialog()
}
activeHighlightForMenu = null
}, onHighlight = { color ->
},
onHighlight = { color ->
Timber.d("Menu: Updating highlight color to ${color.id}")
onHighlightDeleted(highlight.cfi)
onHighlightCreated(highlight.cfi, highlight.text, color.id)
activeHighlightForMenu = null
}, onDelete = {
},
onDelete = {
onHighlightDeleted(highlight.cfi)
activeHighlightForMenu = null
}, isProUser = isProUser, isOss = isOss
},
isProUser = isProUser,
isOss = isOss,
activeHighlightPalette = activeHighlightPalette,
onOpenPaletteManager = { showPaletteManager = true }
)
}
}
if (showPaletteManager) {
PaletteManagerDialog(
currentPalette = activeHighlightPalette,
onDismiss = { showPaletteManager = false },
onSave = { newPalette ->
newPalette.forEachIndexed { index, color ->
onUpdatePalette(index, color)
}
showPaletteManager = false
}
)
}
} else {
Timber.w("Book has no pages to display.")
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
@ -2811,9 +2862,11 @@ private fun PaginatedTextSelectionMenu(
onSelectAll: (() -> Unit)?,
onDictionary: () -> Unit,
onHighlight: ((HighlightColor) -> Unit)?,
@Suppress("SameParameterValue") onDelete: (() -> Unit)?,
onDelete: (() -> Unit)?,
@Suppress("unused") isProUser: Boolean,
isOss: Boolean
isOss: Boolean,
activeHighlightPalette: List<HighlightColor> = emptyList(),
onOpenPaletteManager: (() -> Unit)? = null
) {
Surface(
shape = RoundedCornerShape(12.dp),
@ -2822,7 +2875,7 @@ private fun PaginatedTextSelectionMenu(
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant)
) {
Column(modifier = Modifier.width(IntrinsicSize.Max)) {
// 1. Colors Row (Only show if highlighting is supported)
// 1. Colors Row
if (onHighlight != null) {
Row(
modifier = Modifier
@ -2831,18 +2884,22 @@ private fun PaginatedTextSelectionMenu(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
HighlightColor.entries.forEach { colorEnum ->
activeHighlightPalette.forEach { colorEnum ->
Box(
modifier = Modifier
.padding(horizontal = 8.dp)
.size(24.dp)
.padding(horizontal = 6.dp)
.size(32.dp)
.background(colorEnum.color, CircleShape)
.border(
1.dp, MaterialTheme.colorScheme.outline.copy(
alpha = 0.5f
), CircleShape
.clickable { onHighlight(colorEnum) }
)
}
if (onOpenPaletteManager != null) {
Spacer(modifier = Modifier.width(8.dp))
SpectrumButton(
onClick = onOpenPaletteManager,
size = 32.dp
)
.clickable { onHighlight(colorEnum) })
}
}
HorizontalDivider()
@ -3137,9 +3194,11 @@ private fun RenderFlexChildBlock(
is TableBlock -> {
Column(modifier = Modifier.fillMaxWidth()) {
childBlock.rows.forEach { tableRow ->
Row(Modifier
Row(
Modifier
.fillMaxWidth()
.height(IntrinsicSize.Min)) {
.height(IntrinsicSize.Min)
) {
val hasFixedWidths =
tableRow.any { it.style.blockStyle.width != Dp.Unspecified }