Merge branch 'master' of https://github.com/crosspoint-reader/crosspoint-reader into feat-page-overlay

This commit is contained in:
jpirnay
2026-03-07 10:22:18 +01:00
41 changed files with 924 additions and 193 deletions
+18 -1
View File
@@ -597,6 +597,16 @@ void EpubReaderActivity::render(RenderLock&& lock) {
section->currentPage = nextPageNumber;
}
if (!pendingAnchor.empty()) {
if (const auto page = section->getPageForAnchor(pendingAnchor)) {
section->currentPage = *page;
LOG_DBG("ERS", "Resolved anchor '%s' to page %d", pendingAnchor.c_str(), *page);
} else {
LOG_DBG("ERS", "Anchor '%s' not found in section %d", pendingAnchor.c_str(), currentSpineIndex);
}
pendingAnchor.clear();
}
// handles changes in reader settings and reset to approximate position based on cached progress
if (cachedChapterTotalPageCount > 0) {
// only goes to relative position if spine index matches cached value
@@ -792,12 +802,18 @@ void EpubReaderActivity::navigateToHref(const std::string& hrefStr, const bool s
LOG_DBG("ERS", "Saved position [%d]: spine %d, page %d", footnoteDepth, currentSpineIndex, section->currentPage);
}
// Extract fragment anchor (e.g. "#note1" or "chapter2.xhtml#note1")
std::string anchor;
const auto hashPos = hrefStr.find('#');
if (hashPos != std::string::npos && hashPos + 1 < hrefStr.size()) {
anchor = hrefStr.substr(hashPos + 1);
}
// Check for same-file anchor reference (#anchor only)
bool sameFile = !hrefStr.empty() && hrefStr[0] == '#';
int targetSpineIndex;
if (sameFile) {
// Same file — navigate to page 0 of current spine item
targetSpineIndex = currentSpineIndex;
} else {
targetSpineIndex = epub->resolveHrefToSpineIndex(hrefStr);
@@ -811,6 +827,7 @@ void EpubReaderActivity::navigateToHref(const std::string& hrefStr, const bool s
{
RenderLock lock(*this);
pendingAnchor = std::move(anchor);
currentSpineIndex = targetSpineIndex;
nextPageNumber = 0;
section.reset();
@@ -11,6 +11,9 @@ class EpubReaderActivity final : public Activity {
std::unique_ptr<Section> section = nullptr;
int currentSpineIndex = 0;
int nextPageNumber = 0;
// Set when navigating to a footnote href with a fragment (e.g. #note1).
// Cleared on the next render after the new section loads and resolves it to a page.
std::string pendingAnchor;
int pagesUntilFullRefresh = 0;
int cachedSpineIndex = 0;
int cachedChapterTotalPageCount = 0;
+5 -7
View File
@@ -1,5 +1,6 @@
#include "ReaderActivity.h"
#include <FsHelpers.h>
#include <HalStorage.h>
#include "CrossPointSettings.h"
@@ -11,7 +12,6 @@
#include "XtcReaderActivity.h"
#include "activities/util/BmpViewerActivity.h"
#include "activities/util/FullScreenMessageActivity.h"
#include "util/StringUtils.h"
std::string ReaderActivity::extractFolderPath(const std::string& filePath) {
const auto lastSlash = filePath.find_last_of('/');
@@ -21,16 +21,14 @@ std::string ReaderActivity::extractFolderPath(const std::string& filePath) {
return filePath.substr(0, lastSlash);
}
bool ReaderActivity::isXtcFile(const std::string& path) {
return StringUtils::checkFileExtension(path, ".xtc") || StringUtils::checkFileExtension(path, ".xtch");
}
bool ReaderActivity::isXtcFile(const std::string& path) { return FsHelpers::hasXtcExtension(path); }
bool ReaderActivity::isTxtFile(const std::string& path) {
return StringUtils::checkFileExtension(path, ".txt") ||
StringUtils::checkFileExtension(path, ".md"); // Treat .md as txt files (until we have a markdown reader)
return FsHelpers::hasTxtExtension(path) ||
FsHelpers::hasMarkdownExtension(path); // Treat .md as txt files (until we have a markdown reader)
}
bool ReaderActivity::isBmpFile(const std::string& path) { return StringUtils::checkFileExtension(path, ".bmp"); }
bool ReaderActivity::isBmpFile(const std::string& path) { return FsHelpers::hasBmpExtension(path); }
std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) {
if (!Storage.exists(path.c_str())) {