feat: Integrate bookmark support into reader activities
Adds bookmark add/remove functionality to EpubReaderActivity and base ReaderActivity, with visual indicator for bookmarked pages.
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include <Serialization.h>
|
||||
|
||||
#include "BookManager.h"
|
||||
#include "BookmarkStore.h"
|
||||
#include "CrossPointSettings.h"
|
||||
#include "CrossPointState.h"
|
||||
#include "EpubReaderChapterSelectionActivity.h"
|
||||
@@ -17,6 +18,7 @@
|
||||
#include "activities/dictionary/DictionaryMenuActivity.h"
|
||||
#include "activities/dictionary/DictionarySearchActivity.h"
|
||||
#include "activities/dictionary/EpubWordSelectionActivity.h"
|
||||
#include "activities/util/QuickMenuActivity.h"
|
||||
#include "fontIds.h"
|
||||
|
||||
namespace {
|
||||
@@ -366,6 +368,149 @@ void EpubReaderActivity::loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Quick Menu power button press
|
||||
if (SETTINGS.shortPwrBtn == CrossPointSettings::SHORT_PWRBTN::QUICK_MENU &&
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Power)) {
|
||||
xSemaphoreTake(renderingMutex, portMAX_DELAY);
|
||||
|
||||
// Check if current page is bookmarked
|
||||
bool isBookmarked = false;
|
||||
if (section) {
|
||||
const uint32_t contentOffset = section->getContentOffsetForPage(section->currentPage);
|
||||
isBookmarked = BookmarkStore::isPageBookmarked(epub->getPath(), currentSpineIndex, contentOffset);
|
||||
}
|
||||
|
||||
exitActivity();
|
||||
enterNewActivity(new QuickMenuActivity(
|
||||
renderer, mappedInput,
|
||||
[this](QuickMenuAction action) {
|
||||
// Cache values before exitActivity
|
||||
EpubReaderActivity* self = this;
|
||||
GfxRenderer& cachedRenderer = renderer;
|
||||
MappedInputManager& cachedMappedInput = mappedInput;
|
||||
Section* cachedSection = section.get();
|
||||
SemaphoreHandle_t cachedMutex = renderingMutex;
|
||||
|
||||
exitActivity();
|
||||
|
||||
if (action == QuickMenuAction::DICTIONARY) {
|
||||
// Open dictionary menu
|
||||
self->enterNewActivity(new DictionaryMenuActivity(
|
||||
cachedRenderer, cachedMappedInput,
|
||||
[self](DictionaryMode mode) {
|
||||
GfxRenderer& r = self->renderer;
|
||||
MappedInputManager& m = self->mappedInput;
|
||||
Section* s = self->section.get();
|
||||
SemaphoreHandle_t mtx = self->renderingMutex;
|
||||
|
||||
self->exitActivity();
|
||||
|
||||
if (mode == DictionaryMode::ENTER_WORD) {
|
||||
self->enterNewActivity(new DictionarySearchActivity(r, m,
|
||||
[self]() {
|
||||
self->exitActivity();
|
||||
self->updateRequired = true;
|
||||
}, ""));
|
||||
} else if (s) {
|
||||
xSemaphoreTake(mtx, portMAX_DELAY);
|
||||
auto page = s->loadPageFromSectionFile();
|
||||
if (page) {
|
||||
int mt, mr, mb, ml;
|
||||
r.getOrientedViewableTRBL(&mt, &mr, &mb, &ml);
|
||||
mt += SETTINGS.screenMargin;
|
||||
ml += SETTINGS.screenMargin;
|
||||
const int fontId = SETTINGS.getReaderFontId();
|
||||
|
||||
self->enterNewActivity(new EpubWordSelectionActivity(
|
||||
r, m, std::move(page), fontId, ml, mt,
|
||||
[self](const std::string& word) {
|
||||
self->exitActivity();
|
||||
self->enterNewActivity(new DictionarySearchActivity(
|
||||
self->renderer, self->mappedInput,
|
||||
[self]() {
|
||||
self->exitActivity();
|
||||
self->updateRequired = true;
|
||||
}, word));
|
||||
},
|
||||
[self]() {
|
||||
self->exitActivity();
|
||||
self->updateRequired = true;
|
||||
}));
|
||||
xSemaphoreGive(mtx);
|
||||
} else {
|
||||
xSemaphoreGive(mtx);
|
||||
self->updateRequired = true;
|
||||
}
|
||||
} else {
|
||||
self->updateRequired = true;
|
||||
}
|
||||
},
|
||||
[self]() {
|
||||
self->exitActivity();
|
||||
self->updateRequired = true;
|
||||
},
|
||||
self->section != nullptr));
|
||||
} else if (action == QuickMenuAction::ADD_BOOKMARK) {
|
||||
// Toggle bookmark on current page
|
||||
if (self->section) {
|
||||
const uint32_t contentOffset = self->section->getContentOffsetForPage(self->section->currentPage);
|
||||
const std::string& bookPath = self->epub->getPath();
|
||||
|
||||
if (BookmarkStore::isPageBookmarked(bookPath, self->currentSpineIndex, contentOffset)) {
|
||||
// Remove bookmark
|
||||
BookmarkStore::removeBookmark(bookPath, self->currentSpineIndex, contentOffset);
|
||||
} else {
|
||||
// Add bookmark with auto-generated name
|
||||
Bookmark bm;
|
||||
bm.spineIndex = self->currentSpineIndex;
|
||||
bm.contentOffset = contentOffset;
|
||||
bm.pageNumber = self->section->currentPage;
|
||||
bm.timestamp = millis() / 1000; // Approximate timestamp
|
||||
|
||||
// Generate name: "Chapter - Page X" or fallback
|
||||
std::string chapterTitle;
|
||||
const int tocIndex = self->epub->getTocIndexForSpineIndex(self->currentSpineIndex);
|
||||
if (tocIndex >= 0) {
|
||||
chapterTitle = self->epub->getTocItem(tocIndex).title;
|
||||
}
|
||||
if (!chapterTitle.empty()) {
|
||||
bm.name = chapterTitle + " - Page " + std::to_string(self->section->currentPage + 1);
|
||||
} else {
|
||||
bm.name = "Page " + std::to_string(self->section->currentPage + 1);
|
||||
}
|
||||
|
||||
BookmarkStore::addBookmark(bookPath, bm);
|
||||
}
|
||||
}
|
||||
self->updateRequired = true;
|
||||
} else if (action == QuickMenuAction::CLEAR_CACHE) {
|
||||
// Navigate to Clear Cache activity
|
||||
if (self->onGoToClearCache) {
|
||||
xSemaphoreGive(cachedMutex);
|
||||
self->onGoToClearCache();
|
||||
return;
|
||||
}
|
||||
self->updateRequired = true;
|
||||
} else if (action == QuickMenuAction::GO_TO_SETTINGS) {
|
||||
// Navigate to Settings activity
|
||||
if (self->onGoToSettings) {
|
||||
xSemaphoreGive(cachedMutex);
|
||||
self->onGoToSettings();
|
||||
return;
|
||||
}
|
||||
self->updateRequired = true;
|
||||
}
|
||||
},
|
||||
[this]() {
|
||||
EpubReaderActivity* self = this;
|
||||
exitActivity();
|
||||
self->updateRequired = true;
|
||||
},
|
||||
isBookmarked));
|
||||
xSemaphoreGive(renderingMutex);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool prevReleased = mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
|
||||
mappedInput.wasReleased(MappedInputManager::Button::Left);
|
||||
const bool nextReleased = mappedInput.wasReleased(MappedInputManager::Button::PageForward) ||
|
||||
@@ -632,6 +777,24 @@ void EpubReaderActivity::renderContents(std::unique_ptr<Page> page, const int or
|
||||
const int orientedMarginRight, const int orientedMarginBottom,
|
||||
const int orientedMarginLeft) {
|
||||
page->render(renderer, SETTINGS.getReaderFontId(), orientedMarginLeft, orientedMarginTop);
|
||||
|
||||
// Draw bookmark indicator (folded corner) if this page is bookmarked
|
||||
if (section) {
|
||||
const uint32_t contentOffset = section->getContentOffsetForPage(section->currentPage);
|
||||
if (BookmarkStore::isPageBookmarked(epub->getPath(), currentSpineIndex, contentOffset)) {
|
||||
// Draw folded corner in top-right
|
||||
const int screenWidth = renderer.getScreenWidth();
|
||||
constexpr int cornerSize = 20;
|
||||
const int cornerX = screenWidth - orientedMarginRight - cornerSize;
|
||||
const int cornerY = orientedMarginTop;
|
||||
|
||||
// Draw triangle (folded corner effect)
|
||||
const int xPoints[3] = {cornerX, cornerX + cornerSize, cornerX + cornerSize};
|
||||
const int yPoints[3] = {cornerY, cornerY, cornerY + cornerSize};
|
||||
renderer.fillPolygon(xPoints, yPoints, 3, true); // Black triangle
|
||||
}
|
||||
}
|
||||
|
||||
renderStatusBar(orientedMarginRight, orientedMarginBottom, orientedMarginLeft);
|
||||
if (pagesUntilFullRefresh <= 1) {
|
||||
renderer.displayBuffer(EInkDisplay::HALF_REFRESH);
|
||||
|
||||
Reference in New Issue
Block a user