From 3ed96a7e490ae39a51e06ce184aed9ff545d116c Mon Sep 17 00:00:00 2001 From: jpirnay Date: Sat, 7 Mar 2026 18:02:21 +0100 Subject: [PATCH] Use and populate metadata --- src/JsonSettingsIO.cpp | 2 ++ src/RecentBooksStore.cpp | 15 ++++++++------- src/RecentBooksStore.h | 3 ++- src/activities/reader/EpubReaderActivity.cpp | 4 +++- src/activities/reader/TxtReaderActivity.cpp | 2 +- src/activities/reader/XtcReaderActivity.cpp | 2 +- src/components/themes/BaseTheme.cpp | 20 +++++++++++++++++++- src/components/themes/lyra/LyraTheme.cpp | 8 +++++++- 8 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/JsonSettingsIO.cpp b/src/JsonSettingsIO.cpp index 4a563142..eaaf9ecf 100644 --- a/src/JsonSettingsIO.cpp +++ b/src/JsonSettingsIO.cpp @@ -301,6 +301,7 @@ bool JsonSettingsIO::saveRecentBooks(const RecentBooksStore& store, const char* obj["path"] = book.path; obj["title"] = book.title; obj["author"] = book.author; + obj["series"] = book.series; obj["coverBmpPath"] = book.coverBmpPath; } @@ -325,6 +326,7 @@ bool JsonSettingsIO::loadRecentBooks(RecentBooksStore& store, const char* json) book.path = obj["path"] | std::string(""); book.title = obj["title"] | std::string(""); book.author = obj["author"] | std::string(""); + book.series = obj["series"] | std::string(""); book.coverBmpPath = obj["coverBmpPath"] | std::string(""); store.recentBooks.push_back(book); } diff --git a/src/RecentBooksStore.cpp b/src/RecentBooksStore.cpp index f5a2c048..075947e8 100644 --- a/src/RecentBooksStore.cpp +++ b/src/RecentBooksStore.cpp @@ -21,7 +21,7 @@ constexpr int MAX_RECENT_BOOKS = 10; RecentBooksStore RecentBooksStore::instance; void RecentBooksStore::addBook(const std::string& path, const std::string& title, const std::string& author, - const std::string& coverBmpPath) { + const std::string& series, const std::string& coverBmpPath) { // Remove existing entry if present auto it = std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; }); @@ -30,7 +30,7 @@ void RecentBooksStore::addBook(const std::string& path, const std::string& title } // Add to front - recentBooks.insert(recentBooks.begin(), {path, title, author, coverBmpPath}); + recentBooks.insert(recentBooks.begin(), {path, title, author, series, coverBmpPath}); // Trim to max size if (recentBooks.size() > MAX_RECENT_BOOKS) { @@ -73,17 +73,18 @@ RecentBook RecentBooksStore::getDataFromBook(std::string path) const { if (FsHelpers::hasEpubExtension(lastBookFileName)) { Epub epub(path, "/.crosspoint"); epub.load(false, true); - return RecentBook{path, epub.getTitle(), epub.getAuthor(), epub.getThumbBmpPath()}; + std::string series = epub.getSeries(); + if (!series.empty() && !epub.getSeriesIndex().empty()) series += " #" + epub.getSeriesIndex(); + return RecentBook{path, epub.getTitle(), epub.getAuthor(), series, epub.getThumbBmpPath()}; } else if (FsHelpers::hasXtcExtension(lastBookFileName)) { - // Handle XTC file Xtc xtc(path, "/.crosspoint"); if (xtc.load()) { - return RecentBook{path, xtc.getTitle(), xtc.getAuthor(), xtc.getThumbBmpPath()}; + return RecentBook{path, xtc.getTitle(), xtc.getAuthor(), "", xtc.getThumbBmpPath()}; } } else if (FsHelpers::hasTxtExtension(lastBookFileName) || FsHelpers::hasMarkdownExtension(lastBookFileName)) { - return RecentBook{path, lastBookFileName, "", ""}; + return RecentBook{path, lastBookFileName, "", "", ""}; } - return RecentBook{path, "", "", ""}; + return RecentBook{path, "", "", "", ""}; } bool RecentBooksStore::loadFromFile() { diff --git a/src/RecentBooksStore.h b/src/RecentBooksStore.h index 5d98ce83..100bff0d 100644 --- a/src/RecentBooksStore.h +++ b/src/RecentBooksStore.h @@ -6,6 +6,7 @@ struct RecentBook { std::string path; std::string title; std::string author; + std::string series; std::string coverBmpPath; bool operator==(const RecentBook& other) const { return path == other.path; } @@ -31,7 +32,7 @@ class RecentBooksStore { static RecentBooksStore& getInstance() { return instance; } // Add a book to the recent list (moves to front if already exists) - void addBook(const std::string& path, const std::string& title, const std::string& author, + void addBook(const std::string& path, const std::string& title, const std::string& author, const std::string& series, const std::string& coverBmpPath); void updateBook(const std::string& path, const std::string& title, const std::string& author, diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index d2af6751..fd986d55 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -103,7 +103,9 @@ void EpubReaderActivity::onEnter() { // Save current epub as last opened epub and add to recent books APP_STATE.openEpubPath = epub->getPath(); APP_STATE.saveToFile(); - RECENT_BOOKS.addBook(epub->getPath(), epub->getTitle(), epub->getAuthor(), epub->getThumbBmpPath()); + std::string series = epub->getSeries(); + if (!series.empty() && !epub->getSeriesIndex().empty()) series += " #" + epub->getSeriesIndex(); + RECENT_BOOKS.addBook(epub->getPath(), epub->getTitle(), epub->getAuthor(), series, epub->getThumbBmpPath()); // Trigger first update requestUpdate(); diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index 756b7749..d07bd97c 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -54,7 +54,7 @@ void TxtReaderActivity::onEnter() { auto fileName = filePath.substr(filePath.rfind('/') + 1); APP_STATE.openEpubPath = filePath; APP_STATE.saveToFile(); - RECENT_BOOKS.addBook(filePath, fileName, "", ""); + RECENT_BOOKS.addBook(filePath, fileName, "", "", ""); // Trigger first update requestUpdate(); diff --git a/src/activities/reader/XtcReaderActivity.cpp b/src/activities/reader/XtcReaderActivity.cpp index 84cc51da..aeffb255 100644 --- a/src/activities/reader/XtcReaderActivity.cpp +++ b/src/activities/reader/XtcReaderActivity.cpp @@ -40,7 +40,7 @@ void XtcReaderActivity::onEnter() { // Save current XTC as last opened book and add to recent books APP_STATE.openEpubPath = xtc->getPath(); APP_STATE.saveToFile(); - RECENT_BOOKS.addBook(xtc->getPath(), xtc->getTitle(), xtc->getAuthor(), xtc->getThumbBmpPath()); + RECENT_BOOKS.addBook(xtc->getPath(), xtc->getTitle(), xtc->getAuthor(), "", xtc->getThumbBmpPath()); // Trigger first update requestUpdate(); diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index 53d82a99..03c4dbbe 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -482,6 +482,7 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std: if (hasContinueReading) { const std::string& lastBookTitle = recentBooks[0].title; const std::string& lastBookAuthor = recentBooks[0].author; + const std::string& lastBookSeries = recentBooks[0].series; // Invert text colors based on selection state: // - With cover: selected = white text on black box, unselected = black text on white box @@ -494,6 +495,9 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std: if (!lastBookAuthor.empty()) { totalTextHeight += renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2; } + if (!lastBookSeries.empty()) { + totalTextHeight += renderer.getLineHeight(UI_12_FONT_ID); + } // Vertically center the title block within the card int titleYStart = bookY + (bookHeight - totalTextHeight) / 2; @@ -501,8 +505,11 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std: const auto truncatedAuthor = lastBookAuthor.empty() ? std::string{} : renderer.truncatedText(UI_10_FONT_ID, lastBookAuthor.c_str(), bookWidth - 40); + const auto truncatedSeries = lastBookSeries.empty() + ? std::string{} + : renderer.truncatedText(UI_12_FONT_ID, lastBookSeries.c_str(), bookWidth - 40); - // If cover image was rendered, draw box behind title and author + // If cover image was rendered, draw box behind title, author, and series if (coverRendered) { constexpr int boxPadding = 8; // Calculate the max text width for the box @@ -519,6 +526,12 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std: maxTextWidth = authorWidth; } } + if (!truncatedSeries.empty()) { + const int seriesWidth = renderer.getTextWidth(UI_12_FONT_ID, truncatedSeries.c_str()); + if (seriesWidth > maxTextWidth) { + maxTextWidth = seriesWidth; + } + } const int boxWidth = maxTextWidth + boxPadding * 2; const int boxHeight = totalTextHeight + boxPadding * 2; @@ -539,6 +552,11 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std: if (!truncatedAuthor.empty()) { titleYStart += renderer.getLineHeight(UI_10_FONT_ID) / 2; renderer.drawCenteredText(UI_10_FONT_ID, titleYStart, truncatedAuthor.c_str(), !bookSelected); + titleYStart += renderer.getLineHeight(UI_10_FONT_ID); + } + + if (!truncatedSeries.empty()) { + renderer.drawCenteredText(UI_12_FONT_ID, titleYStart, truncatedSeries.c_str(), !bookSelected); } // "Continue Reading" label at the bottom diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index 36c19501..db7d1518 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -487,10 +487,12 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std: auto titleLines = renderer.wrappedText(UI_12_FONT_ID, book.title.c_str(), textWidth, 3, EpdFontFamily::BOLD); auto author = renderer.truncatedText(UI_10_FONT_ID, book.author.c_str(), textWidth); + auto series = renderer.truncatedText(UI_12_FONT_ID, book.series.c_str(), textWidth); const int titleLineHeight = renderer.getLineHeight(UI_12_FONT_ID); const int titleBlockHeight = titleLineHeight * static_cast(titleLines.size()); const int authorHeight = book.author.empty() ? 0 : (renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2); - const int totalBlockHeight = titleBlockHeight + authorHeight; + const int seriesHeight = book.series.empty() ? 0 : renderer.getLineHeight(UI_12_FONT_ID); + const int totalBlockHeight = titleBlockHeight + authorHeight + seriesHeight; int titleY = tileY + tileHeight / 2 - totalBlockHeight / 2; const int textX = tileX + hPaddingInSelection + coverWidth + LyraMetrics::values.verticalSpacing; for (const auto& line : titleLines) { @@ -500,6 +502,10 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std: if (!book.author.empty()) { titleY += renderer.getLineHeight(UI_10_FONT_ID) / 2; renderer.drawText(UI_10_FONT_ID, textX, titleY, author.c_str(), true); + titleY += renderer.getLineHeight(UI_10_FONT_ID); + } + if (!book.series.empty()) { + renderer.drawText(UI_12_FONT_ID, textX, titleY, series.c_str(), true); } } else { drawEmptyRecents(renderer, rect);