Added adjustable eraser thickness and improved tool selection logic for highlighter (#95)

This commit is contained in:
Aryan 2026-03-19 15:48:03 +05:30 committed by GitHub
parent 982efb063d
commit eaf0d4af00
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 201 additions and 131 deletions

View file

@ -67,6 +67,7 @@ fun AnnotationDock(
canUndo: Boolean, canUndo: Boolean,
canRedo: Boolean, canRedo: Boolean,
lastPenTool: InkType, lastPenTool: InkType,
lastHighlighterTool: InkType = InkType.HIGHLIGHTER,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isSticky: Boolean = false, isSticky: Boolean = false,
isMinimized: Boolean, isMinimized: Boolean,
@ -180,7 +181,15 @@ fun AnnotationDock(
description = "Pen", description = "Pen",
size = buttonSize, size = buttonSize,
iconSize = iconSize, iconSize = iconSize,
onClick = { if(!isMinimized) onToolClick(lastPenTool) } onClick = {
if(!isMinimized) {
if (selectedTool != InkType.PEN && selectedTool != InkType.FOUNTAIN_PEN && selectedTool != InkType.PENCIL) {
onToolClick(lastPenTool)
} else {
onToolClick(selectedTool)
}
}
}
) )
// Highlighter // Highlighter
@ -195,7 +204,7 @@ fun AnnotationDock(
onClick = { onClick = {
if (!isMinimized) { if (!isMinimized) {
if (selectedTool != InkType.HIGHLIGHTER && selectedTool != InkType.HIGHLIGHTER_ROUND) { if (selectedTool != InkType.HIGHLIGHTER && selectedTool != InkType.HIGHLIGHTER_ROUND) {
onToolClick(InkType.HIGHLIGHTER) onToolClick(lastHighlighterTool)
} else { } else {
onToolClick(selectedTool) onToolClick(selectedTool)
} }

View file

@ -438,6 +438,7 @@ internal fun PdfPageComposable(
onHighlightUpdate: (String, PdfHighlightColor) -> Unit = { _,_ -> }, onHighlightUpdate: (String, PdfHighlightColor) -> Unit = { _,_ -> },
onHighlightDelete: (String) -> Unit = {}, onHighlightDelete: (String) -> Unit = {},
onTts: (Int, Int) -> Unit = { _, _ -> }, onTts: (Int, Int) -> Unit = { _, _ -> },
activeToolThickness: Float = 0f
) { ) {
val pdfDocumentItem = pdfDocument.item val pdfDocumentItem = pdfDocument.item
var bitmapState by remember { mutableStateOf(PdfThumbnailCache.get(pageIndex)) } var bitmapState by remember { mutableStateOf(PdfThumbnailCache.get(pageIndex)) }
@ -3775,6 +3776,7 @@ internal fun PdfPageComposable(
isEditMode = isEditMode, isEditMode = isEditMode,
selectedTool = selectedTool, selectedTool = selectedTool,
eraserPosition = eraserPosition, eraserPosition = eraserPosition,
activeToolThickness = activeToolThickness,
richTextController = richTextController, richTextController = richTextController,
textBoxes = textBoxes, textBoxes = textBoxes,
selectedTextBoxId = selectedTextBoxId, selectedTextBoxId = selectedTextBoxId,
@ -4516,6 +4518,8 @@ private fun PdfPageRenderer(
onHighlightUpdate: (String, PdfHighlightColor) -> Unit, onHighlightUpdate: (String, PdfHighlightColor) -> Unit,
onHighlightDelete: (String) -> Unit, onHighlightDelete: (String) -> Unit,
onTts: (Int, Int) -> Unit, onTts: (Int, Int) -> Unit,
activeToolThickness: Float
) { ) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
Box( Box(
@ -4704,7 +4708,11 @@ private fun PdfPageRenderer(
if (isEditMode && selectedTool == InkType.ERASER && eraserPosition != null) { if (isEditMode && selectedTool == InkType.ERASER && eraserPosition != null) {
Canvas(modifier = Modifier.fillMaxSize()) { Canvas(modifier = Modifier.fillMaxSize()) {
val radiusPx = 8.dp.toPx() val radiusPx = if (activeToolThickness > 0f && staticData.targetWidth > 0) {
activeToolThickness * staticData.targetWidth * scale // Calculate dynamic size based on tool settings scale
} else {
8.dp.toPx()
}
drawCircle( drawCircle(
color = Color.White.copy(alpha = 0.3f), color = Color.White.copy(alpha = 0.3f),

View file

@ -231,6 +231,7 @@ internal fun PdfVerticalReader(
onHighlightUpdate: (String, PdfHighlightColor) -> Unit = { _,_ -> }, onHighlightUpdate: (String, PdfHighlightColor) -> Unit = { _,_ -> },
onHighlightDelete: (String) -> Unit = {}, onHighlightDelete: (String) -> Unit = {},
onTts: (Int, Int) -> Unit = { _, _ -> }, onTts: (Int, Int) -> Unit = { _, _ -> },
activeToolThickness: Float = 0f
) { ) {
SideEffect { Timber.tag("PdfDrawPerf").v("LIST: PdfVerticalReader Recomposing.") } SideEffect { Timber.tag("PdfDrawPerf").v("LIST: PdfVerticalReader Recomposing.") }
var globalEraserPosition by remember { mutableStateOf<Offset?>(null) } var globalEraserPosition by remember { mutableStateOf<Offset?>(null) }
@ -1512,6 +1513,7 @@ internal fun PdfVerticalReader(
onHighlightUpdate = onHighlightUpdate, onHighlightUpdate = onHighlightUpdate,
onHighlightDelete = onHighlightDelete, onHighlightDelete = onHighlightDelete,
onTts = onTts, onTts = onTts,
activeToolThickness = activeToolThickness,
onTextBoxDragStart = { box, localTopLeft, touchOffset -> onTextBoxDragStart = { box, localTopLeft, touchOffset ->
val currentZoom = zoomAnimatable.value val currentZoom = zoomAnimatable.value
val panX = panXAnimatable.value val panX = panXAnimatable.value
@ -1911,7 +1913,11 @@ internal fun PdfVerticalReader(
if (isEditMode && selectedTool == InkType.ERASER && globalEraserPosition != null) { if (isEditMode && selectedTool == InkType.ERASER && globalEraserPosition != null) {
Canvas(modifier = Modifier.fillMaxSize()) { Canvas(modifier = Modifier.fillMaxSize()) {
val pos = globalEraserPosition!! val pos = globalEraserPosition!!
val radiusPx = 8.dp.toPx() val radiusPx = if (activeToolThickness > 0f) {
activeToolThickness * screenWidth * zoomAnimatable.value
} else {
8.dp.toPx()
}
drawCircle(color = Color.White.copy(alpha = 0.3f), radius = radiusPx, center = pos) drawCircle(color = Color.White.copy(alpha = 0.3f), radius = radiusPx, center = pos)

View file

@ -1374,8 +1374,9 @@ fun PdfViewerScreen(
val selectedTool = toolSettings.getActiveTool() val selectedTool = toolSettings.getActiveTool()
val lastPenTool = toolSettings.getLastPenTool() val lastPenTool = toolSettings.getLastPenTool()
val lastHighlighterTool = toolSettings.getLastHighlighterTool()
val dockPenColor = toolSettings.getToolColor(lastPenTool) val dockPenColor = toolSettings.getToolColor(lastPenTool)
val dockHighlighterColor = toolSettings.getToolColor(InkType.HIGHLIGHTER) val dockHighlighterColor = toolSettings.getToolColor(lastHighlighterTool)
val activeToolColor = toolSettings.getToolColor(selectedTool) val activeToolColor = toolSettings.getToolColor(selectedTool)
val activeToolThickness = toolSettings.getToolThickness(selectedTool) val activeToolThickness = toolSettings.getToolThickness(selectedTool)
@ -2714,7 +2715,7 @@ fun PdfViewerScreen(
annotation: PdfAnnotation, annotation: PdfAnnotation,
hitPoint: PdfPoint, hitPoint: PdfPoint,
pageAspectRatio: Float, pageAspectRatio: Float,
threshold: Float = 0.025f threshold: Float
): Boolean { ): Boolean {
if (annotation.points.isEmpty()) return false if (annotation.points.isEmpty()) return false
@ -4242,7 +4243,7 @@ fun PdfViewerScreen(
val aspectRatio = pageAspectRatios.getOrElse(pageIndex) { 1f } val aspectRatio = pageAspectRatios.getOrElse(pageIndex) { 1f }
val existing = allAnnotations[pageIndex] ?: emptyList() val existing = allAnnotations[pageIndex] ?: emptyList()
val toRemove = existing.filter { val toRemove = existing.filter {
isAnnotationHit(it, point, aspectRatio) isAnnotationHit(it, point, aspectRatio, activeToolThickness)
} }
if (toRemove.isNotEmpty()) { if (toRemove.isNotEmpty()) {
val batch = val batch =
@ -4289,7 +4290,7 @@ fun PdfViewerScreen(
val aspectRatio = pageAspectRatios.getOrElse(pageIndex) { 1f } val aspectRatio = pageAspectRatios.getOrElse(pageIndex) { 1f }
val existing = allAnnotations[pageIndex] ?: emptyList() val existing = allAnnotations[pageIndex] ?: emptyList()
val toRemove = existing.filter { val toRemove = existing.filter {
isAnnotationHit(it, point, aspectRatio) isAnnotationHit(it, point, aspectRatio, activeToolThickness)
} }
if (toRemove.isNotEmpty()) { if (toRemove.isNotEmpty()) {
val batch = val batch =
@ -4405,6 +4406,7 @@ fun PdfViewerScreen(
onHighlightUpdate = onHighlightUpdate, onHighlightUpdate = onHighlightUpdate,
onHighlightDelete = onHighlightDelete, onHighlightDelete = onHighlightDelete,
onTts = { pageIdx, charIdx -> startTtsWithPermissionCheck(pageIdx, charIdx) }, onTts = { pageIdx, charIdx -> startTtsWithPermissionCheck(pageIdx, charIdx) },
activeToolThickness = currentStrokeWidthState,
onTwoFingerSwipe = { direction -> onTwoFingerSwipe = { direction ->
coroutineScope.launch { coroutineScope.launch {
val targetPage = val targetPage =
@ -4632,7 +4634,7 @@ fun PdfViewerScreen(
val aspectRatio = pageAspectRatios.getOrElse(pageIndex) { 1f } val aspectRatio = pageAspectRatios.getOrElse(pageIndex) { 1f }
val existing = allAnnotations[pageIndex] ?: emptyList() val existing = allAnnotations[pageIndex] ?: emptyList()
val toRemove = existing.filter { val toRemove = existing.filter {
isAnnotationHit(it, point, aspectRatio) isAnnotationHit(it, point, aspectRatio, activeToolThickness)
} }
if (toRemove.isNotEmpty()) { if (toRemove.isNotEmpty()) {
val batch = val batch =
@ -4670,7 +4672,7 @@ fun PdfViewerScreen(
val aspectRatio = pageAspectRatios.getOrElse(pageIndex) { 1f } val aspectRatio = pageAspectRatios.getOrElse(pageIndex) { 1f }
val existing = allAnnotations[pageIndex] ?: emptyList() val existing = allAnnotations[pageIndex] ?: emptyList()
val toRemove = existing.filter { val toRemove = existing.filter {
isAnnotationHit(it, point, aspectRatio) isAnnotationHit(it, point, aspectRatio, activeToolThickness)
} }
if (toRemove.isNotEmpty()) { if (toRemove.isNotEmpty()) {
val batch = val batch =
@ -4737,6 +4739,7 @@ fun PdfViewerScreen(
onHighlightUpdate = onHighlightUpdate, onHighlightUpdate = onHighlightUpdate,
onHighlightDelete = onHighlightDelete, onHighlightDelete = onHighlightDelete,
onTts = { pageIdx, charIdx -> startTtsWithPermissionCheck(pageIdx, charIdx) }, onTts = { pageIdx, charIdx -> startTtsWithPermissionCheck(pageIdx, charIdx) },
activeToolThickness = currentStrokeWidthState,
onLinkClicked = onLinkClickedStable, onLinkClicked = onLinkClickedStable,
onInternalLinkClicked = onInternalLinkNavStable, onInternalLinkClicked = onInternalLinkNavStable,
bookmarks = bookmarksHolder, bookmarks = bookmarksHolder,
@ -6386,19 +6389,20 @@ fun PdfViewerScreen(
activePenColor = dockPenColor, activePenColor = dockPenColor,
activeHighlighterColor = dockHighlighterColor, activeHighlighterColor = dockHighlighterColor,
lastPenTool = lastPenTool, lastPenTool = lastPenTool,
lastHighlighterTool = lastHighlighterTool,
isStylusOnlyMode = isStylusOnlyMode, isStylusOnlyMode = isStylusOnlyMode,
onToggleStylusOnlyMode = { onToggleStylusOnlyMode = {
isStylusOnlyMode = !isStylusOnlyMode isStylusOnlyMode = !isStylusOnlyMode
saveStylusOnlyMode(context, isStylusOnlyMode) saveStylusOnlyMode(context, isStylusOnlyMode)
}, },
onToolClick = { clickedTool -> onToolClick = { clickedTool ->
if (clickedTool == InkType.ERASER || clickedTool == InkType.TEXT) { if (clickedTool == InkType.TEXT) {
annotationSettingsRepo.updateSelectedTool( annotationSettingsRepo.updateSelectedTool(
clickedTool clickedTool
) )
showToolSettings = false showToolSettings = false
} else if (selectedTool == clickedTool) { } else if (selectedTool == clickedTool) {
if (clickedTool == InkType.PEN || clickedTool == InkType.FOUNTAIN_PEN || clickedTool == InkType.PENCIL || clickedTool == InkType.HIGHLIGHTER || clickedTool == InkType.HIGHLIGHTER_ROUND) { if (clickedTool == InkType.PEN || clickedTool == InkType.FOUNTAIN_PEN || clickedTool == InkType.PENCIL || clickedTool == InkType.HIGHLIGHTER || clickedTool == InkType.HIGHLIGHTER_ROUND || clickedTool == InkType.ERASER) {
showToolSettings = !showToolSettings showToolSettings = !showToolSettings
} }
} else { } else {

View file

@ -110,6 +110,7 @@ fun ToolSettingsPopup(
onSnapToggle: (Boolean) -> Unit = {} onSnapToggle: (Boolean) -> Unit = {}
) { ) {
val isHighlighter = selectedTool == InkType.HIGHLIGHTER || selectedTool == InkType.HIGHLIGHTER_ROUND val isHighlighter = selectedTool == InkType.HIGHLIGHTER || selectedTool == InkType.HIGHLIGHTER_ROUND
val isEraser = selectedTool == InkType.ERASER
val activeColor = when (selectedTool) { val activeColor = when (selectedTool) {
InkType.FOUNTAIN_PEN -> fountainPenColor InkType.FOUNTAIN_PEN -> fountainPenColor
@ -117,6 +118,7 @@ fun ToolSettingsPopup(
InkType.PENCIL -> pencilColor InkType.PENCIL -> pencilColor
InkType.HIGHLIGHTER -> highlighterColor InkType.HIGHLIGHTER -> highlighterColor
InkType.HIGHLIGHTER_ROUND -> highlighterRoundColor InkType.HIGHLIGHTER_ROUND -> highlighterRoundColor
InkType.ERASER -> Color.White
else -> markerColor else -> markerColor
} }
@ -130,8 +132,11 @@ fun ToolSettingsPopup(
} }
} }
// Thickness settings val thicknessRange = when {
val thicknessRange = if (isHighlighter) 0.01f..0.06f else 0.001f..0.015f isHighlighter -> 0.01f..0.06f
isEraser -> 0.01f..0.1f
else -> 0.001f..0.015f
}
@Suppress("UnusedExpression") if (isHighlighter) 0.005f else 0.001f @Suppress("UnusedExpression") if (isHighlighter) 0.005f else 0.001f
var showColorPicker by remember { mutableStateOf(false) } var showColorPicker by remember { mutableStateOf(false) }
@ -164,6 +169,32 @@ fun ToolSettingsPopup(
modifier = Modifier.padding(20.dp), modifier = Modifier.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
if (isEraser) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(125.dp),
contentAlignment = Alignment.Center
) {
val radiusDp = (activeToolThickness * 1000).coerceIn(10f, 100f).dp
Box(
modifier = Modifier.size(radiusDp),
contentAlignment = Alignment.Center
) {
Canvas(modifier = Modifier.fillMaxSize()) {
drawCircle(
color = Color.White.copy(alpha = 0.3f),
radius = size.width / 2
)
drawCircle(
color = Color.White,
radius = size.width / 2,
style = Stroke(width = 2.dp.toPx())
)
}
}
}
} else {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -226,6 +257,7 @@ fun ToolSettingsPopup(
} }
} }
} }
}
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
@ -262,7 +294,7 @@ fun ToolSettingsPopup(
valueRange = thicknessRange, isOpacity = false, valueRange = thicknessRange, isOpacity = false,
trackColor = Color(0xFF424242), trackColor = Color(0xFF424242),
thumbColor = Color(0xFF757575), thumbColor = Color(0xFF757575),
activeColor = activeColor activeColor = if (isEraser) Color.White else activeColor
) )
// Darkness (Opacity) Slider for Highlighters // Darkness (Opacity) Slider for Highlighters
@ -281,6 +313,7 @@ fun ToolSettingsPopup(
) )
} }
if (!isEraser) {
Spacer(Modifier.height(16.dp)) // Reduced spacing Spacer(Modifier.height(16.dp)) // Reduced spacing
// --- Color Palette --- // --- Color Palette ---
@ -358,6 +391,7 @@ fun ToolSettingsPopup(
} }
} }
} }
}
if (showColorPicker && colorPickerSlotIndex != -1) { if (showColorPicker && colorPickerSlotIndex != -1) {
val initialColor = activePalette.getOrElse(colorPickerSlotIndex) { Color.Black } val initialColor = activePalette.getOrElse(colorPickerSlotIndex) { Color.Black }

View file

@ -59,6 +59,7 @@ data class TextStyleConfig(
data class AnnotationToolSettings( data class AnnotationToolSettings(
val selectedToolName: String = "PEN", val selectedToolName: String = "PEN",
val lastActivePenType: String = "PEN", val lastActivePenType: String = "PEN",
val lastActiveHighlighterType: String = "HIGHLIGHTER",
val toolConfigs: Map<String, ToolConfig> = emptyMap(), val toolConfigs: Map<String, ToolConfig> = emptyMap(),
val penPaletteArgb: List<Int> = listOf( val penPaletteArgb: List<Int> = listOf(
android.graphics.Color.BLACK, android.graphics.Color.BLACK,
@ -101,6 +102,12 @@ data class AnnotationToolSettings(
fun getPenPalette(): List<Color> = penPaletteArgb.map { Color(it) } fun getPenPalette(): List<Color> = penPaletteArgb.map { Color(it) }
fun getHighlighterPalette(): List<Color> = highlighterPaletteArgb.map { Color(it) } fun getHighlighterPalette(): List<Color> = highlighterPaletteArgb.map { Color(it) }
fun getLastHighlighterTool(): InkType = try {
InkType.valueOf(lastActiveHighlighterType)
} catch (_: Exception) {
InkType.HIGHLIGHTER
}
} }
class AnnotationSettingsRepository(context: Context) { class AnnotationSettingsRepository(context: Context) {
@ -154,6 +161,8 @@ class AnnotationSettingsRepository(context: Context) {
if (tool == InkType.PEN || tool == InkType.FOUNTAIN_PEN || tool == InkType.PENCIL) { if (tool == InkType.PEN || tool == InkType.FOUNTAIN_PEN || tool == InkType.PENCIL) {
currentSettings = currentSettings.copy(lastActivePenType = tool.name) currentSettings = currentSettings.copy(lastActivePenType = tool.name)
} else if (tool == InkType.HIGHLIGHTER || tool == InkType.HIGHLIGHTER_ROUND) {
currentSettings = currentSettings.copy(lastActiveHighlighterType = tool.name) // ADD THIS
} }
saveSettings(currentSettings) saveSettings(currentSettings)