🚀 Highlight words option

* Highlights the beginning of each word
* May help some people read faster|understand more
* Thickness option to change highlighting strength

Resolves: #109
This commit is contained in:
Acclorite 2025-01-03 16:36:03 +02:00
parent 50a645fb32
commit 6500c08512
18 changed files with 373 additions and 57 deletions

View file

@ -1,7 +1,14 @@
package ua.acclorite.book_story.domain.reader package ua.acclorite.book_story.domain.reader
enum class ReaderTextAlignment { import androidx.compose.runtime.Immutable
START, JUSTIFY, CENTER, END import androidx.compose.ui.text.style.TextAlign
@Immutable
enum class ReaderTextAlignment(val textAlignment: TextAlign) {
START(TextAlign.Start),
JUSTIFY(TextAlign.Justify),
CENTER(TextAlign.Center),
END(TextAlign.End)
} }
fun String.toTextAlignment(): ReaderTextAlignment { fun String.toTextAlignment(): ReaderTextAlignment {

View file

@ -0,0 +1,88 @@
package ua.acclorite.book_story.presentation.core.components.common
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.text.withStyle
import kotlin.math.roundToInt
@Composable
fun HighlightedText(
modifier: Modifier = Modifier,
text: AnnotatedString,
highlightThickness: FontWeight,
style: TextStyle
) {
val highlightedText = remember(text, highlightThickness) {
buildAnnotatedString {
text.splitAnnotatedString().forEach { word ->
if (word.text.none { it.isLetter() }) {
append(word)
append(" ")
return@forEach
}
val textWord = word.text.filterDigitsAtStartAndEnd()
val digitsAtStart = word.text.digitsAtStart()
val focusArea = when (textWord.length) {
in 1..3 -> 1
4 -> 2
else -> (textWord.length * 0.5f).roundToInt()
} + digitsAtStart
if (digitsAtStart > 0) {
append(word.subSequence(0, digitsAtStart))
}
withStyle(style = SpanStyle(fontWeight = highlightThickness)) {
append(word.subSequence(digitsAtStart, focusArea))
}
append(word.subSequence(focusArea, word.text.length))
append(" ")
}
}
}
BasicText(
text = highlightedText,
modifier = modifier,
style = style,
overflow = TextOverflow.Ellipsis
)
}
private fun String.digitsAtStart(): Int {
var count = 0
for (char in this) {
if (!char.isLetter()) count++
else break
}
return count
}
private fun String.filterDigitsAtStartAndEnd(): String {
return dropWhile { !it.isLetter() }.dropLastWhile { !it.isLetter() }
}
private fun AnnotatedString.splitAnnotatedString(): List<AnnotatedString> {
val result = mutableListOf<AnnotatedString>()
val wordRegex = Regex("\\S+")
wordRegex.findAll(this).forEach { matchResult ->
val wordStart = matchResult.range.first
val wordEnd = matchResult.range.last + 1
val word = subSequence(wordStart, wordEnd)
result.add(word)
}
return result
}

View file

@ -45,6 +45,8 @@ object DataStoreConstants {
val HORIZONTAL_GESTURE_SCROLL = doublePreferencesKey("horizontal_gesture_scroll") val HORIZONTAL_GESTURE_SCROLL = doublePreferencesKey("horizontal_gesture_scroll")
val HORIZONTAL_GESTURE_SENSITIVITY = doublePreferencesKey("horizontal_gesture_sensitivity") val HORIZONTAL_GESTURE_SENSITIVITY = doublePreferencesKey("horizontal_gesture_sensitivity")
val BOTTOM_BAR_PADDING = intPreferencesKey("bottom_bar_padding") val BOTTOM_BAR_PADDING = intPreferencesKey("bottom_bar_padding")
val HIGHLIGHTED_READING = booleanPreferencesKey("highlighted_reading")
val HIGHLIGHTED_READING_THICKNESS = intPreferencesKey("highlighted_reading_thickness")
// Browse settings // Browse settings
val BROWSE_FILES_STRUCTURE = stringPreferencesKey("browse_files_structure") val BROWSE_FILES_STRUCTURE = stringPreferencesKey("browse_files_structure")

View file

@ -4,9 +4,11 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import ua.acclorite.book_story.domain.library.book.Book import ua.acclorite.book_story.domain.library.book.Book
@ -46,6 +48,8 @@ fun ReaderContent(
horizontalGesture: ReaderHorizontalGesture, horizontalGesture: ReaderHorizontalGesture,
horizontalGestureScroll: Float, horizontalGestureScroll: Float,
horizontalGestureSensitivity: Dp, horizontalGestureSensitivity: Dp,
highlightedReading: Boolean,
highlightedReadingThickness: FontWeight,
paragraphHeight: Dp, paragraphHeight: Dp,
sidePadding: Dp, sidePadding: Dp,
bottomBarPadding: Dp, bottomBarPadding: Dp,
@ -55,6 +59,7 @@ fun ReaderContent(
lineHeight: TextUnit, lineHeight: TextUnit,
fontStyle: FontStyle, fontStyle: FontStyle,
textAlignment: ReaderTextAlignment, textAlignment: ReaderTextAlignment,
horizontalAlignment: Alignment.Horizontal,
fontSize: TextUnit, fontSize: TextUnit,
letterSpacing: TextUnit, letterSpacing: TextUnit,
paragraphIndentation: TextUnit, paragraphIndentation: TextUnit,
@ -107,6 +112,8 @@ fun ReaderContent(
horizontalGesture = horizontalGesture, horizontalGesture = horizontalGesture,
horizontalGestureScroll = horizontalGestureScroll, horizontalGestureScroll = horizontalGestureScroll,
horizontalGestureSensitivity = horizontalGestureSensitivity, horizontalGestureSensitivity = horizontalGestureSensitivity,
highlightedReading = highlightedReading,
highlightedReadingThickness = highlightedReadingThickness,
paragraphHeight = paragraphHeight, paragraphHeight = paragraphHeight,
sidePadding = sidePadding, sidePadding = sidePadding,
bottomBarPadding = bottomBarPadding, bottomBarPadding = bottomBarPadding,
@ -116,6 +123,7 @@ fun ReaderContent(
lineHeight = lineHeight, lineHeight = lineHeight,
fontStyle = fontStyle, fontStyle = fontStyle,
textAlignment = textAlignment, textAlignment = textAlignment,
horizontalAlignment = horizontalAlignment,
fontSize = fontSize, fontSize = fontSize,
letterSpacing = letterSpacing, letterSpacing = letterSpacing,
paragraphIndentation = paragraphIndentation, paragraphIndentation = paragraphIndentation,

View file

@ -12,9 +12,11 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.coerceAtLeast import androidx.compose.ui.unit.coerceAtLeast
@ -40,6 +42,8 @@ fun ReaderLayout(
horizontalGesture: ReaderHorizontalGesture, horizontalGesture: ReaderHorizontalGesture,
horizontalGestureScroll: Float, horizontalGestureScroll: Float,
horizontalGestureSensitivity: Dp, horizontalGestureSensitivity: Dp,
highlightedReading: Boolean,
highlightedReadingThickness: FontWeight,
paragraphHeight: Dp, paragraphHeight: Dp,
sidePadding: Dp, sidePadding: Dp,
backgroundColor: Color, backgroundColor: Color,
@ -48,6 +52,7 @@ fun ReaderLayout(
lineHeight: TextUnit, lineHeight: TextUnit,
fontStyle: FontStyle, fontStyle: FontStyle,
textAlignment: ReaderTextAlignment, textAlignment: ReaderTextAlignment,
horizontalAlignment: Alignment.Horizontal,
fontSize: TextUnit, fontSize: TextUnit,
letterSpacing: TextUnit, letterSpacing: TextUnit,
paragraphIndentation: TextUnit, paragraphIndentation: TextUnit,
@ -156,12 +161,15 @@ fun ReaderLayout(
lineHeight = lineHeight, lineHeight = lineHeight,
fontStyle = fontStyle, fontStyle = fontStyle,
textAlignment = textAlignment, textAlignment = textAlignment,
horizontalAlignment = horizontalAlignment,
fontSize = fontSize, fontSize = fontSize,
letterSpacing = letterSpacing, letterSpacing = letterSpacing,
sidePadding = sidePadding, sidePadding = sidePadding,
paragraphIndentation = paragraphIndentation, paragraphIndentation = paragraphIndentation,
fullscreenMode = fullscreenMode, fullscreenMode = fullscreenMode,
doubleClickTranslation = doubleClickTranslation, doubleClickTranslation = doubleClickTranslation,
highlightedReading = highlightedReading,
highlightedReadingThickness = highlightedReadingThickness,
toolbarHidden = toolbarHidden, toolbarHidden = toolbarHidden,
openTranslator = openTranslator, openTranslator = openTranslator,
menuVisibility = menuVisibility menuVisibility = menuVisibility

View file

@ -3,8 +3,10 @@ package ua.acclorite.book_story.presentation.reader
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyItemScope
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import ua.acclorite.book_story.domain.reader.FontWithName import ua.acclorite.book_story.domain.reader.FontWithName
@ -22,12 +24,15 @@ fun LazyItemScope.ReaderLayoutText(
lineHeight: TextUnit, lineHeight: TextUnit,
fontStyle: FontStyle, fontStyle: FontStyle,
textAlignment: ReaderTextAlignment, textAlignment: ReaderTextAlignment,
horizontalAlignment: Alignment.Horizontal,
fontSize: TextUnit, fontSize: TextUnit,
letterSpacing: TextUnit, letterSpacing: TextUnit,
sidePadding: Dp, sidePadding: Dp,
paragraphIndentation: TextUnit, paragraphIndentation: TextUnit,
fullscreenMode: Boolean, fullscreenMode: Boolean,
doubleClickTranslation: Boolean, doubleClickTranslation: Boolean,
highlightedReading: Boolean,
highlightedReadingThickness: FontWeight,
toolbarHidden: Boolean, toolbarHidden: Boolean,
openTranslator: (ReaderEvent.OnOpenTranslator) -> Unit, openTranslator: (ReaderEvent.OnOpenTranslator) -> Unit,
menuVisibility: (ReaderEvent.OnMenuVisibility) -> Unit menuVisibility: (ReaderEvent.OnMenuVisibility) -> Unit
@ -44,7 +49,9 @@ fun LazyItemScope.ReaderLayoutText(
ReaderLayoutTextChapter( ReaderLayoutTextChapter(
chapter = textEntry, chapter = textEntry,
fontColor = fontColor, fontColor = fontColor,
sidePadding = sidePadding sidePadding = sidePadding,
highlightedReading = highlightedReading,
highlightedReadingThickness = highlightedReadingThickness
) )
} }
@ -58,12 +65,15 @@ fun LazyItemScope.ReaderLayoutText(
lineHeight = lineHeight, lineHeight = lineHeight,
fontStyle = fontStyle, fontStyle = fontStyle,
textAlignment = textAlignment, textAlignment = textAlignment,
horizontalAlignment = horizontalAlignment,
fontSize = fontSize, fontSize = fontSize,
letterSpacing = letterSpacing, letterSpacing = letterSpacing,
sidePadding = sidePadding, sidePadding = sidePadding,
paragraphIndentation = paragraphIndentation, paragraphIndentation = paragraphIndentation,
fullscreenMode = fullscreenMode, fullscreenMode = fullscreenMode,
doubleClickTranslation = doubleClickTranslation, doubleClickTranslation = doubleClickTranslation,
highlightedReading = highlightedReading,
highlightedReadingThickness = highlightedReadingThickness,
toolbarHidden = toolbarHidden, toolbarHidden = toolbarHidden,
openTranslator = openTranslator, openTranslator = openTranslator,
menuVisibility = menuVisibility menuVisibility = menuVisibility

View file

@ -7,25 +7,45 @@ import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ua.acclorite.book_story.domain.reader.ReaderText.Chapter import ua.acclorite.book_story.domain.reader.ReaderText.Chapter
import ua.acclorite.book_story.presentation.core.components.common.HighlightedText
@Composable @Composable
fun ReaderLayoutTextChapter( fun ReaderLayoutTextChapter(
chapter: Chapter, chapter: Chapter,
fontColor: Color, fontColor: Color,
sidePadding: Dp sidePadding: Dp,
highlightedReading: Boolean,
highlightedReadingThickness: FontWeight
) { ) {
Spacer(modifier = Modifier.height(22.dp)) Spacer(modifier = Modifier.height(22.dp))
if (highlightedReading) {
HighlightedText(
text = rememberSaveable(saver = AnnotatedString.Saver) {
buildAnnotatedString { append(chapter.title) }
},
highlightThickness = highlightedReadingThickness,
style = MaterialTheme.typography.headlineMedium.copy(
color = fontColor
),
modifier = Modifier.padding(horizontal = sidePadding)
)
} else {
Text( Text(
text = chapter.title, text = chapter.title,
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineMedium,
color = fontColor, color = fontColor,
modifier = Modifier.padding(horizontal = sidePadding) modifier = Modifier.padding(horizontal = sidePadding)
) )
}
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
HorizontalDivider(color = fontColor.copy(0.4f)) HorizontalDivider(color = fontColor.copy(0.4f))
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))

View file

@ -13,14 +13,16 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontSynthesis
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.LineBreak import androidx.compose.ui.text.style.LineBreak
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextIndent import androidx.compose.ui.text.style.TextIndent
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import ua.acclorite.book_story.domain.reader.FontWithName import ua.acclorite.book_story.domain.reader.FontWithName
import ua.acclorite.book_story.domain.reader.ReaderText.Text import ua.acclorite.book_story.domain.reader.ReaderText.Text
import ua.acclorite.book_story.domain.reader.ReaderTextAlignment import ua.acclorite.book_story.domain.reader.ReaderTextAlignment
import ua.acclorite.book_story.presentation.core.components.common.HighlightedText
import ua.acclorite.book_story.presentation.core.util.noRippleClickable import ua.acclorite.book_story.presentation.core.util.noRippleClickable
import ua.acclorite.book_story.ui.reader.ReaderEvent import ua.acclorite.book_story.ui.reader.ReaderEvent
@ -34,12 +36,15 @@ fun LazyItemScope.ReaderLayoutTextParagraph(
lineHeight: TextUnit, lineHeight: TextUnit,
fontStyle: FontStyle, fontStyle: FontStyle,
textAlignment: ReaderTextAlignment, textAlignment: ReaderTextAlignment,
horizontalAlignment: Alignment.Horizontal,
fontSize: TextUnit, fontSize: TextUnit,
letterSpacing: TextUnit, letterSpacing: TextUnit,
sidePadding: Dp, sidePadding: Dp,
paragraphIndentation: TextUnit, paragraphIndentation: TextUnit,
fullscreenMode: Boolean, fullscreenMode: Boolean,
doubleClickTranslation: Boolean, doubleClickTranslation: Boolean,
highlightedReading: Boolean,
highlightedReadingThickness: FontWeight,
toolbarHidden: Boolean, toolbarHidden: Boolean,
openTranslator: (ReaderEvent.OnOpenTranslator) -> Unit, openTranslator: (ReaderEvent.OnOpenTranslator) -> Unit,
menuVisibility: (ReaderEvent.OnMenuVisibility) -> Unit menuVisibility: (ReaderEvent.OnMenuVisibility) -> Unit
@ -50,12 +55,51 @@ fun LazyItemScope.ReaderLayoutTextParagraph(
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = sidePadding), .padding(horizontal = sidePadding),
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = when (textAlignment) { horizontalAlignment = horizontalAlignment
ReaderTextAlignment.START, ReaderTextAlignment.JUSTIFY -> Alignment.Start
ReaderTextAlignment.CENTER -> Alignment.CenterHorizontally
ReaderTextAlignment.END -> Alignment.End
}
) { ) {
if (highlightedReading) {
HighlightedText(
text = paragraph.line,
highlightThickness = highlightedReadingThickness,
modifier = Modifier.then(
if (doubleClickTranslation && toolbarHidden) {
Modifier.noRippleClickable(
onDoubleClick = {
openTranslator(
ReaderEvent.OnOpenTranslator(
textToTranslate = paragraph.line.text,
translateWholeParagraph = true,
activity = activity
)
)
},
onClick = {
menuVisibility(
ReaderEvent.OnMenuVisibility(
show = !showMenu,
fullscreenMode = fullscreenMode,
saveCheckpoint = true,
activity = activity
)
)
}
)
} else Modifier
),
style = TextStyle(
fontFamily = fontFamily.font,
fontSynthesis = FontSynthesis.All,
textAlign = textAlignment.textAlignment,
textIndent = TextIndent(firstLine = paragraphIndentation),
fontStyle = fontStyle,
letterSpacing = letterSpacing,
fontSize = fontSize,
lineHeight = lineHeight,
color = fontColor,
lineBreak = LineBreak.Paragraph
)
)
} else {
BasicText( BasicText(
text = paragraph.line, text = paragraph.line,
modifier = Modifier.then( modifier = Modifier.then(
@ -85,12 +129,7 @@ fun LazyItemScope.ReaderLayoutTextParagraph(
), ),
style = TextStyle( style = TextStyle(
fontFamily = fontFamily.font, fontFamily = fontFamily.font,
textAlign = when (textAlignment) { textAlign = textAlignment.textAlignment,
ReaderTextAlignment.START -> TextAlign.Start
ReaderTextAlignment.JUSTIFY -> TextAlign.Justify
ReaderTextAlignment.CENTER -> TextAlign.Center
ReaderTextAlignment.END -> TextAlign.End
},
textIndent = TextIndent(firstLine = paragraphIndentation), textIndent = TextIndent(firstLine = paragraphIndentation),
fontStyle = fontStyle, fontStyle = fontStyle,
letterSpacing = letterSpacing, letterSpacing = letterSpacing,
@ -101,4 +140,5 @@ fun LazyItemScope.ReaderLayoutTextParagraph(
) )
) )
} }
}
} }

View file

@ -10,11 +10,13 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.TextUnit
import ua.acclorite.book_story.domain.library.book.Book import ua.acclorite.book_story.domain.library.book.Book
@ -52,6 +54,8 @@ fun ReaderScaffold(
horizontalGesture: ReaderHorizontalGesture, horizontalGesture: ReaderHorizontalGesture,
horizontalGestureScroll: Float, horizontalGestureScroll: Float,
horizontalGestureSensitivity: Dp, horizontalGestureSensitivity: Dp,
highlightedReading: Boolean,
highlightedReadingThickness: FontWeight,
paragraphHeight: Dp, paragraphHeight: Dp,
sidePadding: Dp, sidePadding: Dp,
bottomBarPadding: Dp, bottomBarPadding: Dp,
@ -61,6 +65,7 @@ fun ReaderScaffold(
lineHeight: TextUnit, lineHeight: TextUnit,
fontStyle: FontStyle, fontStyle: FontStyle,
textAlignment: ReaderTextAlignment, textAlignment: ReaderTextAlignment,
horizontalAlignment: Alignment.Horizontal,
fontSize: TextUnit, fontSize: TextUnit,
letterSpacing: TextUnit, letterSpacing: TextUnit,
paragraphIndentation: TextUnit, paragraphIndentation: TextUnit,
@ -141,6 +146,8 @@ fun ReaderScaffold(
horizontalGesture = horizontalGesture, horizontalGesture = horizontalGesture,
horizontalGestureScroll = horizontalGestureScroll, horizontalGestureScroll = horizontalGestureScroll,
horizontalGestureSensitivity = horizontalGestureSensitivity, horizontalGestureSensitivity = horizontalGestureSensitivity,
highlightedReading = highlightedReading,
highlightedReadingThickness = highlightedReadingThickness,
paragraphHeight = paragraphHeight, paragraphHeight = paragraphHeight,
sidePadding = sidePadding, sidePadding = sidePadding,
backgroundColor = backgroundColor, backgroundColor = backgroundColor,
@ -149,6 +156,7 @@ fun ReaderScaffold(
lineHeight = lineHeight, lineHeight = lineHeight,
fontStyle = fontStyle, fontStyle = fontStyle,
textAlignment = textAlignment, textAlignment = textAlignment,
horizontalAlignment = horizontalAlignment,
fontSize = fontSize, fontSize = fontSize,
letterSpacing = letterSpacing, letterSpacing = letterSpacing,
paragraphIndentation = paragraphIndentation, paragraphIndentation = paragraphIndentation,

View file

@ -9,6 +9,8 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import ua.acclorite.book_story.R import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.settings.components.SettingsSubcategory import ua.acclorite.book_story.presentation.settings.components.SettingsSubcategory
import ua.acclorite.book_story.presentation.settings.reader.reading_speed.components.HighlightedReadingOption
import ua.acclorite.book_story.presentation.settings.reader.reading_speed.components.HighlightedReadingThicknessOption
import ua.acclorite.book_story.presentation.settings.reader.reading_speed.components.PerceptionExpanderOption import ua.acclorite.book_story.presentation.settings.reader.reading_speed.components.PerceptionExpanderOption
import ua.acclorite.book_story.presentation.settings.reader.reading_speed.components.PerceptionExpanderPaddingOption import ua.acclorite.book_story.presentation.settings.reader.reading_speed.components.PerceptionExpanderPaddingOption
import ua.acclorite.book_story.presentation.settings.reader.reading_speed.components.PerceptionExpanderThicknessOption import ua.acclorite.book_story.presentation.settings.reader.reading_speed.components.PerceptionExpanderThicknessOption
@ -25,6 +27,14 @@ fun LazyListScope.ReadingSpeedSubcategory(
showTitle = showTitle, showTitle = showTitle,
showDivider = showDivider showDivider = showDivider
) { ) {
item {
HighlightedReadingOption()
}
item {
HighlightedReadingThicknessOption()
}
item { item {
PerceptionExpanderOption() PerceptionExpanderOption()
} }

View file

@ -0,0 +1,29 @@
package ua.acclorite.book_story.presentation.settings.reader.reading_speed.components
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.settings.SwitchWithTitle
import ua.acclorite.book_story.ui.main.MainEvent
import ua.acclorite.book_story.ui.main.MainModel
@Composable
fun HighlightedReadingOption() {
val mainModel = hiltViewModel<MainModel>()
val state = mainModel.state.collectAsStateWithLifecycle()
SwitchWithTitle(
selected = state.value.highlightedReading,
title = stringResource(id = R.string.highlighted_reading_option),
description = stringResource(id = R.string.highlighted_reading_option_desc),
onClick = {
mainModel.onEvent(
MainEvent.OnChangeHighlightedReading(
!state.value.highlightedReading
)
)
}
)
}

View file

@ -0,0 +1,31 @@
package ua.acclorite.book_story.presentation.settings.reader.reading_speed.components
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import ua.acclorite.book_story.R
import ua.acclorite.book_story.presentation.core.components.settings.SliderWithTitle
import ua.acclorite.book_story.ui.main.MainEvent
import ua.acclorite.book_story.ui.main.MainModel
import ua.acclorite.book_story.ui.theme.ExpandingTransition
@Composable
fun HighlightedReadingThicknessOption() {
val mainModel = hiltViewModel<MainModel>()
val state = mainModel.state.collectAsStateWithLifecycle()
ExpandingTransition(visible = state.value.highlightedReading) {
SliderWithTitle(
value = state.value.highlightedReadingThickness to "pt",
fromValue = 1,
toValue = 5,
title = stringResource(id = R.string.highlighted_reading_thickness_option),
onValueChange = {
mainModel.onEvent(
MainEvent.OnChangeHighlightedReadingThickness(it)
)
}
)
}
}

View file

@ -47,4 +47,6 @@ sealed class MainEvent {
data class OnChangeHorizontalGestureScroll(val value: Float) : MainEvent() data class OnChangeHorizontalGestureScroll(val value: Float) : MainEvent()
data class OnChangeHorizontalGestureSensitivity(val value: Float) : MainEvent() data class OnChangeHorizontalGestureSensitivity(val value: Float) : MainEvent()
data class OnChangeBottomBarPadding(val value: Int) : MainEvent() data class OnChangeBottomBarPadding(val value: Int) : MainEvent()
data class OnChangeHighlightedReading(val value: Boolean) : MainEvent()
data class OnChangeHighlightedReadingThickness(val value: Int) : MainEvent()
} }

View file

@ -398,6 +398,22 @@ class MainModel @Inject constructor(
it.copy(bottomBarPadding = this) it.copy(bottomBarPadding = this)
} }
) )
is MainEvent.OnChangeHighlightedReading -> handleDatastoreUpdate(
key = DataStoreConstants.HIGHLIGHTED_READING,
value = event.value,
updateState = {
it.copy(highlightedReading = this)
}
)
is MainEvent.OnChangeHighlightedReadingThickness -> handleDatastoreUpdate(
key = DataStoreConstants.HIGHLIGHTED_READING_THICKNESS,
value = event.value,
updateState = {
it.copy(highlightedReadingThickness = this)
}
)
} }
} }

View file

@ -90,6 +90,8 @@ data class MainState(
val horizontalGestureScroll: Float = provideDefaultValue { 0.7f }, val horizontalGestureScroll: Float = provideDefaultValue { 0.7f },
val horizontalGestureSensitivity: Float = provideDefaultValue { 0.6f }, val horizontalGestureSensitivity: Float = provideDefaultValue { 0.6f },
val bottomBarPadding: Int = provideDefaultValue { 0 }, val bottomBarPadding: Int = provideDefaultValue { 0 },
val highlightedReading: Boolean = provideDefaultValue { false },
val highlightedReadingThickness: Int = provideDefaultValue { 3 },
// Browse Settings // Browse Settings
val browseFilesStructure: BrowseFilesStructure = provideDefaultValue { val browseFilesStructure: BrowseFilesStructure = provideDefaultValue {
@ -295,6 +297,14 @@ data class MainState(
bottomBarPadding = provideValue( bottomBarPadding = provideValue(
BOTTOM_BAR_PADDING BOTTOM_BAR_PADDING
) { bottomBarPadding }, ) { bottomBarPadding },
highlightedReading = provideValue(
HIGHLIGHTED_READING
) { highlightedReading },
highlightedReadingThickness = provideValue(
HIGHLIGHTED_READING_THICKNESS
) { highlightedReadingThickness },
) )
} }
} }

View file

@ -19,12 +19,14 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.em import androidx.compose.ui.unit.em
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@ -162,6 +164,22 @@ data class ReaderScreen(val bookId: Int) : Screen, Parcelable {
val horizontalGestureSensitivity = remember(mainState.value.horizontalGestureSensitivity) { val horizontalGestureSensitivity = remember(mainState.value.horizontalGestureSensitivity) {
(36f + mainState.value.horizontalGestureSensitivity * (4f - 36f)).dp (36f + mainState.value.horizontalGestureSensitivity * (4f - 36f)).dp
} }
val highlightedReadingThickness = remember(mainState.value.highlightedReadingThickness) {
when (mainState.value.highlightedReadingThickness) {
2 -> FontWeight.W600
3 -> FontWeight.W700
4 -> FontWeight.W800
5 -> FontWeight.W900
else -> FontWeight.W500
}
}
val horizontalAlignment = remember(mainState.value.textAlignment) {
when (mainState.value.textAlignment) {
ReaderTextAlignment.START, ReaderTextAlignment.JUSTIFY -> Alignment.Start
ReaderTextAlignment.CENTER -> Alignment.CenterHorizontally
ReaderTextAlignment.END -> Alignment.End
}
}
val density = LocalDensity.current val density = LocalDensity.current
val layoutDirection = LocalLayoutDirection.current val layoutDirection = LocalLayoutDirection.current
@ -309,6 +327,8 @@ data class ReaderScreen(val bookId: Int) : Screen, Parcelable {
horizontalGesture = mainState.value.horizontalGesture, horizontalGesture = mainState.value.horizontalGesture,
horizontalGestureScroll = mainState.value.horizontalGestureScroll, horizontalGestureScroll = mainState.value.horizontalGestureScroll,
horizontalGestureSensitivity = horizontalGestureSensitivity, horizontalGestureSensitivity = horizontalGestureSensitivity,
highlightedReading = mainState.value.highlightedReading,
highlightedReadingThickness = highlightedReadingThickness,
paragraphHeight = paragraphHeight, paragraphHeight = paragraphHeight,
sidePadding = sidePadding, sidePadding = sidePadding,
bottomBarPadding = bottomBarPadding, bottomBarPadding = bottomBarPadding,
@ -318,6 +338,7 @@ data class ReaderScreen(val bookId: Int) : Screen, Parcelable {
lineHeight = lineHeight, lineHeight = lineHeight,
fontStyle = fontStyle, fontStyle = fontStyle,
textAlignment = mainState.value.textAlignment, textAlignment = mainState.value.textAlignment,
horizontalAlignment = horizontalAlignment,
fontSize = mainState.value.fontSize.sp, fontSize = mainState.value.fontSize.sp,
letterSpacing = letterSpacing, letterSpacing = letterSpacing,
paragraphIndentation = paragraphIndentation, paragraphIndentation = paragraphIndentation,

View file

@ -191,6 +191,9 @@
<string name="horizontal_gesture_scroll_option">Частка прокрутки</string> <string name="horizontal_gesture_scroll_option">Частка прокрутки</string>
<string name="horizontal_gesture_sensitivity_option">Чутливість</string> <string name="horizontal_gesture_sensitivity_option">Чутливість</string>
<string name="bottom_bar_padding_option">Відступ нижньої панелі</string> <string name="bottom_bar_padding_option">Відступ нижньої панелі</string>
<string name="highlighted_reading_option">Підсвічування слів</string>
<string name="highlighted_reading_option_desc">Підсвічувати початок кожного слова</string>
<string name="highlighted_reading_thickness_option">Товщина підсвічування</string>
<!-- Browse --> <!-- Browse -->
<string name="browse_files_structure_option">Структура файлів</string> <string name="browse_files_structure_option">Структура файлів</string>

View file

@ -246,6 +246,9 @@
<string name="horizontal_gesture_scroll_option">Scroll fraction</string> <string name="horizontal_gesture_scroll_option">Scroll fraction</string>
<string name="horizontal_gesture_sensitivity_option">Sensitivity</string> <string name="horizontal_gesture_sensitivity_option">Sensitivity</string>
<string name="bottom_bar_padding_option">Bottom bar margin</string> <string name="bottom_bar_padding_option">Bottom bar margin</string>
<string name="highlighted_reading_option">Highlight words</string>
<string name="highlighted_reading_option_desc">Highlight the beginning of each word</string>
<string name="highlighted_reading_thickness_option">Highlight thickness</string>
<!-- Browse --> <!-- Browse -->
<string name="browse_files_structure_option">Files structure</string> <string name="browse_files_structure_option">Files structure</string>