From 6500c085121116d82d6abe640f8c92be908429f6 Mon Sep 17 00:00:00 2001 From: Acclorite <140836141+Acclorite@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:36:03 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Highlight=20words=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Highlights the beginning of each word * May help some people read faster|understand more * Thickness option to change highlighting strength Resolves: #109 --- .../domain/reader/ReaderTextAlignment.kt | 11 +- .../core/components/common/HighlightedText.kt | 88 ++++++++++++ .../core/constants/DataStoreConstants.kt | 2 + .../presentation/reader/ReaderContent.kt | 8 ++ .../presentation/reader/ReaderLayout.kt | 8 ++ .../presentation/reader/ReaderLayoutText.kt | 12 +- .../reader/ReaderLayoutTextChapter.kt | 34 ++++- .../reader/ReaderLayoutTextParagraph.kt | 134 ++++++++++++------ .../presentation/reader/ReaderScaffold.kt | 8 ++ .../reading_speed/ReadingSpeedSubcategory.kt | 10 ++ .../components/HighlightedReadingOption.kt | 29 ++++ .../HighlightedReadingThicknessOption.kt | 31 ++++ .../acclorite/book_story/ui/main/MainEvent.kt | 2 + .../acclorite/book_story/ui/main/MainModel.kt | 16 +++ .../acclorite/book_story/ui/main/MainState.kt | 10 ++ .../book_story/ui/reader/ReaderScreen.kt | 21 +++ app/src/main/res/values-uk/strings.xml | 3 + app/src/main/res/values/strings.xml | 3 + 18 files changed, 373 insertions(+), 57 deletions(-) create mode 100644 app/src/main/java/ua/acclorite/book_story/presentation/core/components/common/HighlightedText.kt create mode 100644 app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/components/HighlightedReadingOption.kt create mode 100644 app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/components/HighlightedReadingThicknessOption.kt diff --git a/app/src/main/java/ua/acclorite/book_story/domain/reader/ReaderTextAlignment.kt b/app/src/main/java/ua/acclorite/book_story/domain/reader/ReaderTextAlignment.kt index 1e1ba915..8d1dff2c 100644 --- a/app/src/main/java/ua/acclorite/book_story/domain/reader/ReaderTextAlignment.kt +++ b/app/src/main/java/ua/acclorite/book_story/domain/reader/ReaderTextAlignment.kt @@ -1,7 +1,14 @@ package ua.acclorite.book_story.domain.reader -enum class ReaderTextAlignment { - START, JUSTIFY, CENTER, END +import androidx.compose.runtime.Immutable +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 { diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/core/components/common/HighlightedText.kt b/app/src/main/java/ua/acclorite/book_story/presentation/core/components/common/HighlightedText.kt new file mode 100644 index 00000000..22e4c016 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/presentation/core/components/common/HighlightedText.kt @@ -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 { + val result = mutableListOf() + + 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 +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/core/constants/DataStoreConstants.kt b/app/src/main/java/ua/acclorite/book_story/presentation/core/constants/DataStoreConstants.kt index 27634333..242f8a83 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/core/constants/DataStoreConstants.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/core/constants/DataStoreConstants.kt @@ -45,6 +45,8 @@ object DataStoreConstants { val HORIZONTAL_GESTURE_SCROLL = doublePreferencesKey("horizontal_gesture_scroll") val HORIZONTAL_GESTURE_SENSITIVITY = doublePreferencesKey("horizontal_gesture_sensitivity") val BOTTOM_BAR_PADDING = intPreferencesKey("bottom_bar_padding") + val HIGHLIGHTED_READING = booleanPreferencesKey("highlighted_reading") + val HIGHLIGHTED_READING_THICKNESS = intPreferencesKey("highlighted_reading_thickness") // Browse settings val BROWSE_FILES_STRUCTURE = stringPreferencesKey("browse_files_structure") diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderContent.kt b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderContent.kt index b331bb9f..a8aabc3f 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderContent.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderContent.kt @@ -4,9 +4,11 @@ import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.LazyListState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.NestedScrollConnection 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.TextUnit import ua.acclorite.book_story.domain.library.book.Book @@ -46,6 +48,8 @@ fun ReaderContent( horizontalGesture: ReaderHorizontalGesture, horizontalGestureScroll: Float, horizontalGestureSensitivity: Dp, + highlightedReading: Boolean, + highlightedReadingThickness: FontWeight, paragraphHeight: Dp, sidePadding: Dp, bottomBarPadding: Dp, @@ -55,6 +59,7 @@ fun ReaderContent( lineHeight: TextUnit, fontStyle: FontStyle, textAlignment: ReaderTextAlignment, + horizontalAlignment: Alignment.Horizontal, fontSize: TextUnit, letterSpacing: TextUnit, paragraphIndentation: TextUnit, @@ -107,6 +112,8 @@ fun ReaderContent( horizontalGesture = horizontalGesture, horizontalGestureScroll = horizontalGestureScroll, horizontalGestureSensitivity = horizontalGestureSensitivity, + highlightedReading = highlightedReading, + highlightedReadingThickness = highlightedReadingThickness, paragraphHeight = paragraphHeight, sidePadding = sidePadding, bottomBarPadding = bottomBarPadding, @@ -116,6 +123,7 @@ fun ReaderContent( lineHeight = lineHeight, fontStyle = fontStyle, textAlignment = textAlignment, + horizontalAlignment = horizontalAlignment, fontSize = fontSize, letterSpacing = letterSpacing, paragraphIndentation = paragraphIndentation, diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayout.kt b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayout.kt index 823c66bd..ad5352f4 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayout.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayout.kt @@ -12,9 +12,11 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color 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.TextUnit import androidx.compose.ui.unit.coerceAtLeast @@ -40,6 +42,8 @@ fun ReaderLayout( horizontalGesture: ReaderHorizontalGesture, horizontalGestureScroll: Float, horizontalGestureSensitivity: Dp, + highlightedReading: Boolean, + highlightedReadingThickness: FontWeight, paragraphHeight: Dp, sidePadding: Dp, backgroundColor: Color, @@ -48,6 +52,7 @@ fun ReaderLayout( lineHeight: TextUnit, fontStyle: FontStyle, textAlignment: ReaderTextAlignment, + horizontalAlignment: Alignment.Horizontal, fontSize: TextUnit, letterSpacing: TextUnit, paragraphIndentation: TextUnit, @@ -156,12 +161,15 @@ fun ReaderLayout( lineHeight = lineHeight, fontStyle = fontStyle, textAlignment = textAlignment, + horizontalAlignment = horizontalAlignment, fontSize = fontSize, letterSpacing = letterSpacing, sidePadding = sidePadding, paragraphIndentation = paragraphIndentation, fullscreenMode = fullscreenMode, doubleClickTranslation = doubleClickTranslation, + highlightedReading = highlightedReading, + highlightedReadingThickness = highlightedReadingThickness, toolbarHidden = toolbarHidden, openTranslator = openTranslator, menuVisibility = menuVisibility diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutText.kt b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutText.kt index 9e020c3d..01a8258a 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutText.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutText.kt @@ -3,8 +3,10 @@ package ua.acclorite.book_story.presentation.reader import androidx.activity.ComponentActivity import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.graphics.Color 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.TextUnit import ua.acclorite.book_story.domain.reader.FontWithName @@ -22,12 +24,15 @@ fun LazyItemScope.ReaderLayoutText( lineHeight: TextUnit, fontStyle: FontStyle, textAlignment: ReaderTextAlignment, + horizontalAlignment: Alignment.Horizontal, fontSize: TextUnit, letterSpacing: TextUnit, sidePadding: Dp, paragraphIndentation: TextUnit, fullscreenMode: Boolean, doubleClickTranslation: Boolean, + highlightedReading: Boolean, + highlightedReadingThickness: FontWeight, toolbarHidden: Boolean, openTranslator: (ReaderEvent.OnOpenTranslator) -> Unit, menuVisibility: (ReaderEvent.OnMenuVisibility) -> Unit @@ -44,7 +49,9 @@ fun LazyItemScope.ReaderLayoutText( ReaderLayoutTextChapter( chapter = textEntry, fontColor = fontColor, - sidePadding = sidePadding + sidePadding = sidePadding, + highlightedReading = highlightedReading, + highlightedReadingThickness = highlightedReadingThickness ) } @@ -58,12 +65,15 @@ fun LazyItemScope.ReaderLayoutText( lineHeight = lineHeight, fontStyle = fontStyle, textAlignment = textAlignment, + horizontalAlignment = horizontalAlignment, fontSize = fontSize, letterSpacing = letterSpacing, sidePadding = sidePadding, paragraphIndentation = paragraphIndentation, fullscreenMode = fullscreenMode, doubleClickTranslation = doubleClickTranslation, + highlightedReading = highlightedReading, + highlightedReadingThickness = highlightedReadingThickness, toolbarHidden = toolbarHidden, openTranslator = openTranslator, menuVisibility = menuVisibility diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutTextChapter.kt b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutTextChapter.kt index 181c8f9c..61dc14ea 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutTextChapter.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutTextChapter.kt @@ -7,25 +7,45 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Modifier 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 ua.acclorite.book_story.domain.reader.ReaderText.Chapter +import ua.acclorite.book_story.presentation.core.components.common.HighlightedText @Composable fun ReaderLayoutTextChapter( chapter: Chapter, fontColor: Color, - sidePadding: Dp + sidePadding: Dp, + highlightedReading: Boolean, + highlightedReadingThickness: FontWeight ) { Spacer(modifier = Modifier.height(22.dp)) - Text( - text = chapter.title, - style = MaterialTheme.typography.headlineMedium, - color = fontColor, - modifier = Modifier.padding(horizontal = sidePadding) - ) + 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 = chapter.title, + style = MaterialTheme.typography.headlineMedium, + color = fontColor, + modifier = Modifier.padding(horizontal = sidePadding) + ) + } Spacer(modifier = Modifier.height(16.dp)) HorizontalDivider(color = fontColor.copy(0.4f)) Spacer(modifier = Modifier.height(16.dp)) diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutTextParagraph.kt b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutTextParagraph.kt index d1a0f282..d31f4b61 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutTextParagraph.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderLayoutTextParagraph.kt @@ -13,14 +13,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.TextStyle 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.TextAlign import androidx.compose.ui.text.style.TextIndent import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.TextUnit import ua.acclorite.book_story.domain.reader.FontWithName import ua.acclorite.book_story.domain.reader.ReaderText.Text 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.ui.reader.ReaderEvent @@ -34,12 +36,15 @@ fun LazyItemScope.ReaderLayoutTextParagraph( lineHeight: TextUnit, fontStyle: FontStyle, textAlignment: ReaderTextAlignment, + horizontalAlignment: Alignment.Horizontal, fontSize: TextUnit, letterSpacing: TextUnit, sidePadding: Dp, paragraphIndentation: TextUnit, fullscreenMode: Boolean, doubleClickTranslation: Boolean, + highlightedReading: Boolean, + highlightedReadingThickness: FontWeight, toolbarHidden: Boolean, openTranslator: (ReaderEvent.OnOpenTranslator) -> Unit, menuVisibility: (ReaderEvent.OnMenuVisibility) -> Unit @@ -50,55 +55,90 @@ fun LazyItemScope.ReaderLayoutTextParagraph( .fillMaxWidth() .padding(horizontal = sidePadding), verticalArrangement = Arrangement.Center, - horizontalAlignment = when (textAlignment) { - ReaderTextAlignment.START, ReaderTextAlignment.JUSTIFY -> Alignment.Start - ReaderTextAlignment.CENTER -> Alignment.CenterHorizontally - ReaderTextAlignment.END -> Alignment.End - } + horizontalAlignment = horizontalAlignment ) { - BasicText( - text = paragraph.line, - modifier = Modifier.then( - if (doubleClickTranslation && toolbarHidden) { - Modifier.noRippleClickable( - onDoubleClick = { - openTranslator( - ReaderEvent.OnOpenTranslator( - textToTranslate = paragraph.line.text, - translateWholeParagraph = true, - activity = activity + 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 + }, + onClick = { + menuVisibility( + ReaderEvent.OnMenuVisibility( + show = !showMenu, + fullscreenMode = fullscreenMode, + saveCheckpoint = true, + activity = activity + ) ) - ) - } - ) - } else Modifier - ), - style = TextStyle( - fontFamily = fontFamily.font, - textAlign = when (textAlignment) { - ReaderTextAlignment.START -> TextAlign.Start - ReaderTextAlignment.JUSTIFY -> TextAlign.Justify - ReaderTextAlignment.CENTER -> TextAlign.Center - ReaderTextAlignment.END -> TextAlign.End - }, - textIndent = TextIndent(firstLine = paragraphIndentation), - fontStyle = fontStyle, - letterSpacing = letterSpacing, - fontSize = fontSize, - lineHeight = lineHeight, - color = fontColor, - lineBreak = LineBreak.Paragraph + } + ) + } 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( + text = paragraph.line, + 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, + textAlign = textAlignment.textAlignment, + textIndent = TextIndent(firstLine = paragraphIndentation), + fontStyle = fontStyle, + letterSpacing = letterSpacing, + fontSize = fontSize, + lineHeight = lineHeight, + color = fontColor, + lineBreak = LineBreak.Paragraph + ) + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderScaffold.kt b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderScaffold.kt index 107178a6..b5dfb6d0 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderScaffold.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/reader/ReaderScaffold.kt @@ -10,11 +10,13 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.nestedScroll 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.TextUnit import ua.acclorite.book_story.domain.library.book.Book @@ -52,6 +54,8 @@ fun ReaderScaffold( horizontalGesture: ReaderHorizontalGesture, horizontalGestureScroll: Float, horizontalGestureSensitivity: Dp, + highlightedReading: Boolean, + highlightedReadingThickness: FontWeight, paragraphHeight: Dp, sidePadding: Dp, bottomBarPadding: Dp, @@ -61,6 +65,7 @@ fun ReaderScaffold( lineHeight: TextUnit, fontStyle: FontStyle, textAlignment: ReaderTextAlignment, + horizontalAlignment: Alignment.Horizontal, fontSize: TextUnit, letterSpacing: TextUnit, paragraphIndentation: TextUnit, @@ -141,6 +146,8 @@ fun ReaderScaffold( horizontalGesture = horizontalGesture, horizontalGestureScroll = horizontalGestureScroll, horizontalGestureSensitivity = horizontalGestureSensitivity, + highlightedReading = highlightedReading, + highlightedReadingThickness = highlightedReadingThickness, paragraphHeight = paragraphHeight, sidePadding = sidePadding, backgroundColor = backgroundColor, @@ -149,6 +156,7 @@ fun ReaderScaffold( lineHeight = lineHeight, fontStyle = fontStyle, textAlignment = textAlignment, + horizontalAlignment = horizontalAlignment, fontSize = fontSize, letterSpacing = letterSpacing, paragraphIndentation = paragraphIndentation, diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/ReadingSpeedSubcategory.kt b/app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/ReadingSpeedSubcategory.kt index 43aa95aa..c30989c5 100644 --- a/app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/ReadingSpeedSubcategory.kt +++ b/app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/ReadingSpeedSubcategory.kt @@ -9,6 +9,8 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import ua.acclorite.book_story.R 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.PerceptionExpanderPaddingOption import ua.acclorite.book_story.presentation.settings.reader.reading_speed.components.PerceptionExpanderThicknessOption @@ -25,6 +27,14 @@ fun LazyListScope.ReadingSpeedSubcategory( showTitle = showTitle, showDivider = showDivider ) { + item { + HighlightedReadingOption() + } + + item { + HighlightedReadingThicknessOption() + } + item { PerceptionExpanderOption() } diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/components/HighlightedReadingOption.kt b/app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/components/HighlightedReadingOption.kt new file mode 100644 index 00000000..6345d1f0 --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/components/HighlightedReadingOption.kt @@ -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() + 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 + ) + ) + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/components/HighlightedReadingThicknessOption.kt b/app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/components/HighlightedReadingThicknessOption.kt new file mode 100644 index 00000000..ffeb2cac --- /dev/null +++ b/app/src/main/java/ua/acclorite/book_story/presentation/settings/reader/reading_speed/components/HighlightedReadingThicknessOption.kt @@ -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() + 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) + ) + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/ui/main/MainEvent.kt b/app/src/main/java/ua/acclorite/book_story/ui/main/MainEvent.kt index e1489fb4..6259d34c 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/main/MainEvent.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/main/MainEvent.kt @@ -47,4 +47,6 @@ sealed class MainEvent { data class OnChangeHorizontalGestureScroll(val value: Float) : MainEvent() data class OnChangeHorizontalGestureSensitivity(val value: Float) : MainEvent() data class OnChangeBottomBarPadding(val value: Int) : MainEvent() + data class OnChangeHighlightedReading(val value: Boolean) : MainEvent() + data class OnChangeHighlightedReadingThickness(val value: Int) : MainEvent() } \ No newline at end of file diff --git a/app/src/main/java/ua/acclorite/book_story/ui/main/MainModel.kt b/app/src/main/java/ua/acclorite/book_story/ui/main/MainModel.kt index 50f72120..197449be 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/main/MainModel.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/main/MainModel.kt @@ -398,6 +398,22 @@ class MainModel @Inject constructor( 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) + } + ) } } diff --git a/app/src/main/java/ua/acclorite/book_story/ui/main/MainState.kt b/app/src/main/java/ua/acclorite/book_story/ui/main/MainState.kt index d5cd958b..62f52e6b 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/main/MainState.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/main/MainState.kt @@ -90,6 +90,8 @@ data class MainState( val horizontalGestureScroll: Float = provideDefaultValue { 0.7f }, val horizontalGestureSensitivity: Float = provideDefaultValue { 0.6f }, val bottomBarPadding: Int = provideDefaultValue { 0 }, + val highlightedReading: Boolean = provideDefaultValue { false }, + val highlightedReadingThickness: Int = provideDefaultValue { 3 }, // Browse Settings val browseFilesStructure: BrowseFilesStructure = provideDefaultValue { @@ -295,6 +297,14 @@ data class MainState( bottomBarPadding = provideValue( BOTTOM_BAR_PADDING ) { bottomBarPadding }, + + highlightedReading = provideValue( + HIGHLIGHTED_READING + ) { highlightedReading }, + + highlightedReadingThickness = provideValue( + HIGHLIGHTED_READING_THICKNESS + ) { highlightedReadingThickness }, ) } } diff --git a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderScreen.kt b/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderScreen.kt index 63f60a3c..72d965b7 100644 --- a/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderScreen.kt +++ b/app/src/main/java/ua/acclorite/book_story/ui/reader/ReaderScreen.kt @@ -19,12 +19,14 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment import androidx.compose.ui.geometry.Offset import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.nestedscroll.NestedScrollSource import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection 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.em import androidx.compose.ui.unit.sp @@ -162,6 +164,22 @@ data class ReaderScreen(val bookId: Int) : Screen, Parcelable { val horizontalGestureSensitivity = remember(mainState.value.horizontalGestureSensitivity) { (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 layoutDirection = LocalLayoutDirection.current @@ -309,6 +327,8 @@ data class ReaderScreen(val bookId: Int) : Screen, Parcelable { horizontalGesture = mainState.value.horizontalGesture, horizontalGestureScroll = mainState.value.horizontalGestureScroll, horizontalGestureSensitivity = horizontalGestureSensitivity, + highlightedReading = mainState.value.highlightedReading, + highlightedReadingThickness = highlightedReadingThickness, paragraphHeight = paragraphHeight, sidePadding = sidePadding, bottomBarPadding = bottomBarPadding, @@ -318,6 +338,7 @@ data class ReaderScreen(val bookId: Int) : Screen, Parcelable { lineHeight = lineHeight, fontStyle = fontStyle, textAlignment = mainState.value.textAlignment, + horizontalAlignment = horizontalAlignment, fontSize = mainState.value.fontSize.sp, letterSpacing = letterSpacing, paragraphIndentation = paragraphIndentation, diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index e6f15835..48b695d5 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -191,6 +191,9 @@ Частка прокрутки Чутливість Відступ нижньої панелі + Підсвічування слів + Підсвічувати початок кожного слова + Товщина підсвічування Структура файлів diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 23c80ecf..4ab4dba7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -246,6 +246,9 @@ Scroll fraction Sensitivity Bottom bar margin + Highlight words + Highlight the beginning of each word + Highlight thickness Files structure