From 9f54c0aca088ac8c6839504755d1530dbf1f3707 Mon Sep 17 00:00:00 2001 From: sergeych Date: Mon, 25 May 2026 20:48:44 +0300 Subject: [PATCH] Improved TOC scrolling behavior and fixed incorrect content indentation logic. Added test for image-only sections. --- .../net/sergeych/toread/ReaderContent.kt | 15 +++++++- .../net/sergeych/toread/ReaderScreen.kt | 12 ++++++ .../toread/ReadAloudContentPlanTest.kt | 37 +++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/net/sergeych/toread/ReaderContent.kt b/composeApp/src/commonMain/kotlin/net/sergeych/toread/ReaderContent.kt index 61c6de0..98fc8f2 100644 --- a/composeApp/src/commonMain/kotlin/net/sergeych/toread/ReaderContent.kt +++ b/composeApp/src/commonMain/kotlin/net/sergeych/toread/ReaderContent.kt @@ -1551,7 +1551,7 @@ internal fun buildReaderContentPlan(book: Fb2Book): ReaderContentPlan { } } } - val childDepth = if (blocks.isEmpty()) readerDepth else readerDepth + 1 + val childDepth = if (blocks.hasIndentBearingContent()) readerDepth + 1 else readerDepth addSections(section.sections, childDepth) } @@ -1811,6 +1811,19 @@ private fun Fb2Section.readableBlocks(): List = images.map(Fb2Block::Image) + paragraphs.map { Fb2Block.Paragraph(Fb2Text(listOf(Fb2TextSpan(it)))) } } +private fun List.hasIndentBearingContent(): Boolean = + any { block -> + when (block) { + Fb2Block.EmptyLine, + is Fb2Block.Image, + is Fb2Block.Subtitle -> false + is Fb2Block.Cite, + is Fb2Block.Epigraph, + is Fb2Block.Paragraph, + is Fb2Block.Poem -> true + } + } + private data class ChapterEntry( val title: String, val section: Fb2Section, diff --git a/composeApp/src/commonMain/kotlin/net/sergeych/toread/ReaderScreen.kt b/composeApp/src/commonMain/kotlin/net/sergeych/toread/ReaderScreen.kt index 8612ce0..77b8ca7 100644 --- a/composeApp/src/commonMain/kotlin/net/sergeych/toread/ReaderScreen.kt +++ b/composeApp/src/commonMain/kotlin/net/sergeych/toread/ReaderScreen.kt @@ -693,11 +693,23 @@ private fun TableOfContentsDialog( onOpenEntry: (ReaderTocEntry) -> Unit, onDismiss: () -> Unit, ) { + val selectedEntryIndex = currentItemIndex?.let { itemIndex -> + contentPlan.tocEntries.indexOfFirst { it.itemIndex == itemIndex }.takeIf { it >= 0 } + } + val listState = rememberLazyListState( + initialFirstVisibleItemIndex = selectedEntryIndex?.let { it + 1 } ?: 0, + ) + + LaunchedEffect(contentPlan, selectedEntryIndex) { + selectedEntryIndex?.let { listState.scrollToItem(it + 1) } + } + AlertDialog( onDismissRequest = onDismiss, title = { Text(strings.tableOfContents) }, text = { LazyColumn( + state = listState, modifier = Modifier.fillMaxWidth().heightIn(max = 480.dp), ) { item(key = "back") { diff --git a/composeApp/src/commonTest/kotlin/net/sergeych/toread/ReadAloudContentPlanTest.kt b/composeApp/src/commonTest/kotlin/net/sergeych/toread/ReadAloudContentPlanTest.kt index 1213f2b..764c1e3 100644 --- a/composeApp/src/commonTest/kotlin/net/sergeych/toread/ReadAloudContentPlanTest.kt +++ b/composeApp/src/commonTest/kotlin/net/sergeych/toread/ReadAloudContentPlanTest.kt @@ -6,6 +6,7 @@ import net.sergeych.toread.fb2.Fb2Block import net.sergeych.toread.fb2.Fb2Book import net.sergeych.toread.fb2.Fb2Epigraph import net.sergeych.toread.fb2.Fb2EpigraphBlock +import net.sergeych.toread.fb2.Fb2ImageRef import net.sergeych.toread.fb2.Fb2Poem import net.sergeych.toread.fb2.Fb2PoemBlock import net.sergeych.toread.fb2.Fb2Section @@ -275,6 +276,42 @@ class ReadAloudContentPlanTest { ) } + @Test + fun imageOnlyPartStructureDoesNotIndentWholeBook() { + val plan = buildReaderContentPlan( + Fb2Book( + title = "Book", + sections = listOf( + Fb2Section( + title = "Wrapper title", + sections = listOf( + Fb2Section( + images = listOf(Fb2ImageRef("#front.jpg")), + ), + Fb2Section( + title = "Part one", + blocks = listOf(Fb2Block.Image(Fb2ImageRef("#part.png"))), + sections = listOf( + Fb2Section( + title = "Chapter one", + blocks = listOf(paragraph("Chapter text.")), + ), + ), + ), + ), + ), + ), + ), + ) + + assertEquals(0, plan.elements.filterIsInstance().single().depth) + assertEquals( + listOf(0, 0, 0), + plan.elements.filterIsInstance().map { it.depth }, + ) + assertEquals(listOf(0, 0, 0, 0), plan.tocEntries.map { it.depth }) + } + @Test fun realNestedSectionsKeepRelativeIndent() { val plan = buildReaderContentPlan(