fix: navigate to TOC anchor when selecting sub-chapters (#1981)

Chapter selection previously only used the spine index, causing
navigation to always land on page 0 of the spine item. Sub-chapters that
share a spine file but differ by anchor (e.g. `chapter.xhtml#sec2`) were
silently ignored. Now the TOC anchor is passed through `ChapterResult`
and applied via the existing `pendingAnchor` mechanism.

## Summary

* This PR implements navigation to sub-chapters which didn't work
correctly previously. If a sub-chapter of the current top-level chapter
was selected, nothing happened. If a sub-chapter of another top-level
chapter was selected, reader switched to the beginning of the top-level
chapter.
* In addition, chapters now always start from a new page. This fixes
anchor to page calculation for the cases when the actual chapter content
doesn't fit on the page where the corresponding ToC anchor was found.

## Additional Context

* I might misuse `pendingAnchor` here which was previously used for
footnote navigation, please double check. I'm open to suggestions for
improvements.
* Note that the chapter selected by default when
`EpubReaderChapterSelectionActivity` opens is still wrong. I'm going to
fix this separately. This PR addresses only navigation to the selected
chapter.
* I tested this PR on my X4 and verified that navigation to a different
sub-chapter works correctly, both inside and outside the current spine.
* Some of the changes were borrowed from
https://github.com/crosspoint-reader/crosspoint-reader/pull/1455
---

### AI Usage

While CrossPoint doesn't have restrictions on AI tools in contributing,
please be transparent about their usage as it
helps set the right context for reviewers.

Did you use AI tools to help write this code? _**PARTIALLY**_

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadim Kaushan
2026-05-26 12:14:32 -05:00
committed by GitHub
co-authored by Claude Sonnet 4.6
parent c5e861d71c
commit 213972badc
6 changed files with 59 additions and 18 deletions
+1
View File
@@ -25,6 +25,7 @@ struct MenuResult {
struct ChapterResult {
int spineIndex = 0;
std::string anchor;
};
struct PercentResult {
+10 -2
View File
@@ -417,10 +417,18 @@ void EpubReaderActivity::onReaderMenuConfirm(EpubReaderMenuActivity::MenuAction
startActivityForResult(
std::make_unique<EpubReaderChapterSelectionActivity>(renderer, mappedInput, epub, path, spineIdx),
[this](const ActivityResult& result) {
if (!result.isCancelled && currentSpineIndex != std::get<ChapterResult>(result.data).spineIndex) {
if (!result.isCancelled) {
const auto& chapterResult = std::get<ChapterResult>(result.data);
RenderLock lock(*this);
currentSpineIndex = std::get<ChapterResult>(result.data).spineIndex;
currentSpineIndex = chapterResult.spineIndex;
// If anchor is not empty, it will be used later to calculate the page number.
pendingAnchor = chapterResult.anchor;
// Otherwise page 0 will be used.
nextPageNumber = 0;
section.reset();
}
});
@@ -32,14 +32,14 @@ void EpubReaderChapterSelectionActivity::loop() {
const int totalItems = getTotalItems();
if (mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
const auto newSpineIndex = epub->getSpineIndexForTocIndex(selectorIndex);
if (newSpineIndex == -1) {
const auto tocItem = epub->getTocItem(selectorIndex);
if (tocItem.spineIndex == -1) {
ActivityResult result;
result.isCancelled = true;
setResult(std::move(result));
finish();
} else {
setResult(ChapterResult{newSpineIndex});
setResult(ChapterResult{tocItem.spineIndex, tocItem.anchor});
finish();
}
} else if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {