Use and populate metadata
This commit is contained in:
@@ -301,6 +301,7 @@ bool JsonSettingsIO::saveRecentBooks(const RecentBooksStore& store, const char*
|
|||||||
obj["path"] = book.path;
|
obj["path"] = book.path;
|
||||||
obj["title"] = book.title;
|
obj["title"] = book.title;
|
||||||
obj["author"] = book.author;
|
obj["author"] = book.author;
|
||||||
|
obj["series"] = book.series;
|
||||||
obj["coverBmpPath"] = book.coverBmpPath;
|
obj["coverBmpPath"] = book.coverBmpPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,6 +326,7 @@ bool JsonSettingsIO::loadRecentBooks(RecentBooksStore& store, const char* json)
|
|||||||
book.path = obj["path"] | std::string("");
|
book.path = obj["path"] | std::string("");
|
||||||
book.title = obj["title"] | std::string("");
|
book.title = obj["title"] | std::string("");
|
||||||
book.author = obj["author"] | std::string("");
|
book.author = obj["author"] | std::string("");
|
||||||
|
book.series = obj["series"] | std::string("");
|
||||||
book.coverBmpPath = obj["coverBmpPath"] | std::string("");
|
book.coverBmpPath = obj["coverBmpPath"] | std::string("");
|
||||||
store.recentBooks.push_back(book);
|
store.recentBooks.push_back(book);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ constexpr int MAX_RECENT_BOOKS = 10;
|
|||||||
RecentBooksStore RecentBooksStore::instance;
|
RecentBooksStore RecentBooksStore::instance;
|
||||||
|
|
||||||
void RecentBooksStore::addBook(const std::string& path, const std::string& title, const std::string& author,
|
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
|
// Remove existing entry if present
|
||||||
auto it =
|
auto it =
|
||||||
std::find_if(recentBooks.begin(), recentBooks.end(), [&](const RecentBook& book) { return book.path == path; });
|
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
|
// Add to front
|
||||||
recentBooks.insert(recentBooks.begin(), {path, title, author, coverBmpPath});
|
recentBooks.insert(recentBooks.begin(), {path, title, author, series, coverBmpPath});
|
||||||
|
|
||||||
// Trim to max size
|
// Trim to max size
|
||||||
if (recentBooks.size() > MAX_RECENT_BOOKS) {
|
if (recentBooks.size() > MAX_RECENT_BOOKS) {
|
||||||
@@ -73,17 +73,18 @@ RecentBook RecentBooksStore::getDataFromBook(std::string path) const {
|
|||||||
if (FsHelpers::hasEpubExtension(lastBookFileName)) {
|
if (FsHelpers::hasEpubExtension(lastBookFileName)) {
|
||||||
Epub epub(path, "/.crosspoint");
|
Epub epub(path, "/.crosspoint");
|
||||||
epub.load(false, true);
|
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)) {
|
} else if (FsHelpers::hasXtcExtension(lastBookFileName)) {
|
||||||
// Handle XTC file
|
|
||||||
Xtc xtc(path, "/.crosspoint");
|
Xtc xtc(path, "/.crosspoint");
|
||||||
if (xtc.load()) {
|
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)) {
|
} 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() {
|
bool RecentBooksStore::loadFromFile() {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ struct RecentBook {
|
|||||||
std::string path;
|
std::string path;
|
||||||
std::string title;
|
std::string title;
|
||||||
std::string author;
|
std::string author;
|
||||||
|
std::string series;
|
||||||
std::string coverBmpPath;
|
std::string coverBmpPath;
|
||||||
|
|
||||||
bool operator==(const RecentBook& other) const { return path == other.path; }
|
bool operator==(const RecentBook& other) const { return path == other.path; }
|
||||||
@@ -31,7 +32,7 @@ class RecentBooksStore {
|
|||||||
static RecentBooksStore& getInstance() { return instance; }
|
static RecentBooksStore& getInstance() { return instance; }
|
||||||
|
|
||||||
// Add a book to the recent list (moves to front if already exists)
|
// 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);
|
const std::string& coverBmpPath);
|
||||||
|
|
||||||
void updateBook(const std::string& path, const std::string& title, const std::string& author,
|
void updateBook(const std::string& path, const std::string& title, const std::string& author,
|
||||||
|
|||||||
@@ -103,7 +103,9 @@ void EpubReaderActivity::onEnter() {
|
|||||||
// Save current epub as last opened epub and add to recent books
|
// Save current epub as last opened epub and add to recent books
|
||||||
APP_STATE.openEpubPath = epub->getPath();
|
APP_STATE.openEpubPath = epub->getPath();
|
||||||
APP_STATE.saveToFile();
|
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
|
// Trigger first update
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ void TxtReaderActivity::onEnter() {
|
|||||||
auto fileName = filePath.substr(filePath.rfind('/') + 1);
|
auto fileName = filePath.substr(filePath.rfind('/') + 1);
|
||||||
APP_STATE.openEpubPath = filePath;
|
APP_STATE.openEpubPath = filePath;
|
||||||
APP_STATE.saveToFile();
|
APP_STATE.saveToFile();
|
||||||
RECENT_BOOKS.addBook(filePath, fileName, "", "");
|
RECENT_BOOKS.addBook(filePath, fileName, "", "", "");
|
||||||
|
|
||||||
// Trigger first update
|
// Trigger first update
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ void XtcReaderActivity::onEnter() {
|
|||||||
// Save current XTC as last opened book and add to recent books
|
// Save current XTC as last opened book and add to recent books
|
||||||
APP_STATE.openEpubPath = xtc->getPath();
|
APP_STATE.openEpubPath = xtc->getPath();
|
||||||
APP_STATE.saveToFile();
|
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
|
// Trigger first update
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
|
|||||||
@@ -482,6 +482,7 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
|
|||||||
if (hasContinueReading) {
|
if (hasContinueReading) {
|
||||||
const std::string& lastBookTitle = recentBooks[0].title;
|
const std::string& lastBookTitle = recentBooks[0].title;
|
||||||
const std::string& lastBookAuthor = recentBooks[0].author;
|
const std::string& lastBookAuthor = recentBooks[0].author;
|
||||||
|
const std::string& lastBookSeries = recentBooks[0].series;
|
||||||
|
|
||||||
// Invert text colors based on selection state:
|
// Invert text colors based on selection state:
|
||||||
// - With cover: selected = white text on black box, unselected = black text on white box
|
// - 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()) {
|
if (!lastBookAuthor.empty()) {
|
||||||
totalTextHeight += renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2;
|
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
|
// Vertically center the title block within the card
|
||||||
int titleYStart = bookY + (bookHeight - totalTextHeight) / 2;
|
int titleYStart = bookY + (bookHeight - totalTextHeight) / 2;
|
||||||
@@ -501,8 +505,11 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
|
|||||||
const auto truncatedAuthor = lastBookAuthor.empty()
|
const auto truncatedAuthor = lastBookAuthor.empty()
|
||||||
? std::string{}
|
? std::string{}
|
||||||
: renderer.truncatedText(UI_10_FONT_ID, lastBookAuthor.c_str(), bookWidth - 40);
|
: 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) {
|
if (coverRendered) {
|
||||||
constexpr int boxPadding = 8;
|
constexpr int boxPadding = 8;
|
||||||
// Calculate the max text width for the box
|
// Calculate the max text width for the box
|
||||||
@@ -519,6 +526,12 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
|
|||||||
maxTextWidth = authorWidth;
|
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 boxWidth = maxTextWidth + boxPadding * 2;
|
||||||
const int boxHeight = totalTextHeight + boxPadding * 2;
|
const int boxHeight = totalTextHeight + boxPadding * 2;
|
||||||
@@ -539,6 +552,11 @@ void BaseTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
|
|||||||
if (!truncatedAuthor.empty()) {
|
if (!truncatedAuthor.empty()) {
|
||||||
titleYStart += renderer.getLineHeight(UI_10_FONT_ID) / 2;
|
titleYStart += renderer.getLineHeight(UI_10_FONT_ID) / 2;
|
||||||
renderer.drawCenteredText(UI_10_FONT_ID, titleYStart, truncatedAuthor.c_str(), !bookSelected);
|
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
|
// "Continue Reading" label at the bottom
|
||||||
|
|||||||
@@ -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 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 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 titleLineHeight = renderer.getLineHeight(UI_12_FONT_ID);
|
||||||
const int titleBlockHeight = titleLineHeight * static_cast<int>(titleLines.size());
|
const int titleBlockHeight = titleLineHeight * static_cast<int>(titleLines.size());
|
||||||
const int authorHeight = book.author.empty() ? 0 : (renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2);
|
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;
|
int titleY = tileY + tileHeight / 2 - totalBlockHeight / 2;
|
||||||
const int textX = tileX + hPaddingInSelection + coverWidth + LyraMetrics::values.verticalSpacing;
|
const int textX = tileX + hPaddingInSelection + coverWidth + LyraMetrics::values.verticalSpacing;
|
||||||
for (const auto& line : titleLines) {
|
for (const auto& line : titleLines) {
|
||||||
@@ -500,6 +502,10 @@ void LyraTheme::drawRecentBookCover(GfxRenderer& renderer, Rect rect, const std:
|
|||||||
if (!book.author.empty()) {
|
if (!book.author.empty()) {
|
||||||
titleY += renderer.getLineHeight(UI_10_FONT_ID) / 2;
|
titleY += renderer.getLineHeight(UI_10_FONT_ID) / 2;
|
||||||
renderer.drawText(UI_10_FONT_ID, textX, titleY, author.c_str(), true);
|
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 {
|
} else {
|
||||||
drawEmptyRecents(renderer, rect);
|
drawEmptyRecents(renderer, rect);
|
||||||
|
|||||||
Reference in New Issue
Block a user