port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
#include "BookInfoActivity.h"
|
|
|
|
|
|
2026-03-09 01:52:07 -04:00
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
#include <Bitmap.h>
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
#include <Epub.h>
|
|
|
|
|
#include <FsHelpers.h>
|
|
|
|
|
#include <GfxRenderer.h>
|
|
|
|
|
#include <HalStorage.h>
|
|
|
|
|
#include <I18n.h>
|
|
|
|
|
#include <Logging.h>
|
2026-03-09 01:52:07 -04:00
|
|
|
#include <PlaceholderCoverGenerator.h>
|
|
|
|
|
#include <Xtc.h>
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
|
|
|
|
|
#include "components/UITheme.h"
|
|
|
|
|
#include "fontIds.h"
|
|
|
|
|
|
2026-03-09 01:52:07 -04:00
|
|
|
namespace {
|
|
|
|
|
constexpr int MARGIN = 20;
|
|
|
|
|
constexpr int LABEL_VALUE_GAP = 4;
|
|
|
|
|
constexpr int SECTION_GAP = 14;
|
|
|
|
|
constexpr int MAX_WRAPPED_LINES = 60;
|
|
|
|
|
constexpr int COVER_GAP = 16;
|
|
|
|
|
|
|
|
|
|
std::string normalizeWhitespace(const std::string& s) {
|
|
|
|
|
std::string out;
|
|
|
|
|
out.reserve(s.size());
|
|
|
|
|
bool prevSpace = false;
|
|
|
|
|
for (const char c : s) {
|
|
|
|
|
if (c == '\n' || c == '\r' || c == '\t') {
|
|
|
|
|
if (!prevSpace) {
|
|
|
|
|
out += ' ';
|
|
|
|
|
prevSpace = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
out += c;
|
|
|
|
|
prevSpace = (c == ' ');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
} // namespace
|
|
|
|
|
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
void BookInfoActivity::onEnter() {
|
|
|
|
|
Activity::onEnter();
|
|
|
|
|
|
|
|
|
|
std::string fileName = filePath;
|
|
|
|
|
const size_t lastSlash = filePath.rfind('/');
|
|
|
|
|
if (lastSlash != std::string::npos) {
|
|
|
|
|
fileName = filePath.substr(lastSlash + 1);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 01:52:07 -04:00
|
|
|
size_t fileSize = 0;
|
|
|
|
|
{
|
|
|
|
|
FsFile file;
|
|
|
|
|
if (Storage.openFileForRead("BIF", filePath, file)) {
|
|
|
|
|
fileSize = file.fileSize();
|
|
|
|
|
file.close();
|
|
|
|
|
}
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
}
|
|
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
BookMetadataCache::BookMetadata meta;
|
2026-03-09 01:52:07 -04:00
|
|
|
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
if (FsHelpers::hasEpubExtension(fileName)) {
|
|
|
|
|
Epub epub(filePath, "/.crosspoint");
|
2026-03-09 02:22:51 -04:00
|
|
|
bool needsBuild = !epub.load(false, true);
|
|
|
|
|
Rect popupRect{};
|
|
|
|
|
if (needsBuild) {
|
|
|
|
|
popupRect = GUI.drawPopup(renderer, tr(STR_LOADING));
|
|
|
|
|
GUI.fillPopupProgress(renderer, popupRect, 10);
|
feat: BookInfo button mapping, ManageBook integration, cover regen fix
- Fix BookInfo buttons: Left/Right front = scroll down/up, Confirm = no-op,
side buttons retained. Separate Up/Down hints on btn3/btn4.
- Fallback load: try epub.load(true, true) when cache-only load fails,
so Book Info works for unopened books.
- Add "Book Info" to ManageBook menu (BOOK_INFO action) with handlers in
all 4 result sites (Home, Recent, FileBrowser, Reader).
- Fix HomeActivity cover regen: call generateCoverBmp(false) + validate
with isValidThumbnailBmp before falling to placeholder, matching the
reader's multi-tier fallback pipeline. Same for XTC branch.
Made-with: Cursor
2026-03-09 02:06:25 -04:00
|
|
|
epub.load(true, true);
|
2026-03-09 02:22:51 -04:00
|
|
|
GUI.fillPopupProgress(renderer, popupRect, 50);
|
feat: BookInfo button mapping, ManageBook integration, cover regen fix
- Fix BookInfo buttons: Left/Right front = scroll down/up, Confirm = no-op,
side buttons retained. Separate Up/Down hints on btn3/btn4.
- Fallback load: try epub.load(true, true) when cache-only load fails,
so Book Info works for unopened books.
- Add "Book Info" to ManageBook menu (BOOK_INFO action) with handlers in
all 4 result sites (Home, Recent, FileBrowser, Reader).
- Fix HomeActivity cover regen: call generateCoverBmp(false) + validate
with isValidThumbnailBmp before falling to placeholder, matching the
reader's multi-tier fallback pipeline. Same for XTC branch.
Made-with: Cursor
2026-03-09 02:06:25 -04:00
|
|
|
}
|
2026-03-09 01:52:07 -04:00
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
meta = epub.getMetadata();
|
|
|
|
|
meta.description = normalizeWhitespace(meta.description);
|
2026-03-09 02:22:51 -04:00
|
|
|
|
|
|
|
|
const int coverH = renderer.getScreenHeight() * 2 / 5;
|
|
|
|
|
if (epub.generateThumbBmp(coverH)) {
|
|
|
|
|
coverBmpPath = epub.getThumbBmpPath(coverH);
|
|
|
|
|
} else {
|
|
|
|
|
const int thumbW = static_cast<int>(coverH * 0.6);
|
|
|
|
|
const std::string placeholderPath = epub.getCachePath() + "/placeholder_" + std::to_string(coverH) + ".bmp";
|
2026-03-09 02:56:35 -04:00
|
|
|
if (PlaceholderCoverGenerator::generate(placeholderPath, meta.title.empty() ? fileName : meta.title, meta.author,
|
|
|
|
|
thumbW, coverH)) {
|
2026-03-09 02:22:51 -04:00
|
|
|
coverBmpPath = placeholderPath;
|
2026-03-09 01:52:07 -04:00
|
|
|
}
|
|
|
|
|
}
|
2026-03-09 02:22:51 -04:00
|
|
|
if (needsBuild) {
|
|
|
|
|
GUI.fillPopupProgress(renderer, popupRect, 100);
|
|
|
|
|
}
|
2026-03-09 01:52:07 -04:00
|
|
|
} else if (FsHelpers::hasXtcExtension(fileName)) {
|
|
|
|
|
Xtc xtc(filePath, "/.crosspoint");
|
2026-03-09 02:22:51 -04:00
|
|
|
bool needsBuild = !Storage.exists(xtc.getCachePath().c_str());
|
|
|
|
|
Rect popupRect{};
|
|
|
|
|
if (needsBuild) {
|
|
|
|
|
popupRect = GUI.drawPopup(renderer, tr(STR_LOADING));
|
|
|
|
|
GUI.fillPopupProgress(renderer, popupRect, 10);
|
|
|
|
|
}
|
2026-03-09 01:52:07 -04:00
|
|
|
if (xtc.load()) {
|
2026-03-09 02:22:51 -04:00
|
|
|
if (needsBuild) {
|
|
|
|
|
GUI.fillPopupProgress(renderer, popupRect, 50);
|
|
|
|
|
}
|
2026-03-09 02:56:35 -04:00
|
|
|
meta.title = xtc.getTitle();
|
|
|
|
|
meta.author = xtc.getAuthor();
|
2026-03-09 01:52:07 -04:00
|
|
|
|
|
|
|
|
const int coverH = renderer.getScreenHeight() * 2 / 5;
|
|
|
|
|
if (xtc.generateThumbBmp(coverH)) {
|
|
|
|
|
coverBmpPath = xtc.getThumbBmpPath(coverH);
|
|
|
|
|
} else {
|
|
|
|
|
const int thumbW = static_cast<int>(coverH * 0.6);
|
|
|
|
|
const std::string placeholderPath = xtc.getCachePath() + "/placeholder_" + std::to_string(coverH) + ".bmp";
|
2026-03-09 02:56:35 -04:00
|
|
|
if (PlaceholderCoverGenerator::generate(placeholderPath, meta.title.empty() ? fileName : meta.title, meta.author,
|
|
|
|
|
thumbW, coverH)) {
|
2026-03-09 01:52:07 -04:00
|
|
|
coverBmpPath = placeholderPath;
|
|
|
|
|
}
|
|
|
|
|
}
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
}
|
2026-03-09 02:22:51 -04:00
|
|
|
if (needsBuild) {
|
|
|
|
|
GUI.fillPopupProgress(renderer, popupRect, 100);
|
|
|
|
|
}
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
}
|
|
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
if (meta.title.empty()) {
|
|
|
|
|
meta.title = fileName;
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
}
|
|
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
buildLayout(meta, fileSize);
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
requestUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BookInfoActivity::onExit() { Activity::onExit(); }
|
|
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
void BookInfoActivity::buildLayout(const BookMetadataCache::BookMetadata& meta, size_t fileSize) {
|
2026-03-09 01:52:07 -04:00
|
|
|
const int contentW = renderer.getScreenWidth() - MARGIN * 2;
|
2026-03-09 02:43:10 -04:00
|
|
|
fields.reserve(13);
|
2026-03-09 01:52:07 -04:00
|
|
|
|
|
|
|
|
auto addField = [&](const char* label, const std::string& text, bool bold, EpdFontFamily::Style style) {
|
|
|
|
|
if (text.empty()) return;
|
|
|
|
|
InfoField field;
|
|
|
|
|
field.label = label;
|
|
|
|
|
field.bold = bold;
|
|
|
|
|
field.lines = renderer.wrappedText(UI_12_FONT_ID, text.c_str(), contentW, MAX_WRAPPED_LINES, style);
|
|
|
|
|
fields.push_back(std::move(field));
|
|
|
|
|
};
|
|
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
addField(nullptr, meta.title, true, EpdFontFamily::BOLD);
|
|
|
|
|
addField(tr(STR_AUTHOR), meta.author, false, EpdFontFamily::REGULAR);
|
2026-03-09 01:52:07 -04:00
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
if (!meta.series.empty()) {
|
|
|
|
|
std::string seriesStr = meta.series;
|
|
|
|
|
if (!meta.seriesIndex.empty()) {
|
|
|
|
|
seriesStr += " #" + meta.seriesIndex;
|
2026-03-09 01:52:07 -04:00
|
|
|
}
|
|
|
|
|
addField(tr(STR_SERIES), seriesStr, false, EpdFontFamily::REGULAR);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
addField(tr(STR_PUBLISHER), meta.publisher, false, EpdFontFamily::REGULAR);
|
|
|
|
|
addField(tr(STR_DATE), meta.date, false, EpdFontFamily::REGULAR);
|
|
|
|
|
addField(tr(STR_SUBJECTS), meta.subjects, false, EpdFontFamily::REGULAR);
|
2026-03-09 02:43:10 -04:00
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
if (!meta.rating.empty()) {
|
|
|
|
|
int ratingVal = atoi(meta.rating.c_str());
|
2026-03-09 02:43:10 -04:00
|
|
|
if (ratingVal > 0 && ratingVal <= 10) {
|
|
|
|
|
char ratingBuf[8];
|
|
|
|
|
snprintf(ratingBuf, sizeof(ratingBuf), "%d / 5", (ratingVal + 1) / 2);
|
|
|
|
|
addField(tr(STR_RATING), std::string(ratingBuf), false, EpdFontFamily::REGULAR);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
addField(tr(STR_LANGUAGE), meta.language, false, EpdFontFamily::REGULAR);
|
|
|
|
|
addField(tr(STR_ISBN), meta.identifier, false, EpdFontFamily::REGULAR);
|
|
|
|
|
addField(tr(STR_CONTRIBUTOR), meta.contributor, false, EpdFontFamily::REGULAR);
|
2026-03-09 01:52:07 -04:00
|
|
|
|
|
|
|
|
if (fileSize > 0) {
|
|
|
|
|
addField(tr(STR_FILE_SIZE), formatFileSize(fileSize), false, EpdFontFamily::REGULAR);
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 02:56:35 -04:00
|
|
|
addField(tr(STR_RIGHTS), meta.rights, false, EpdFontFamily::REGULAR);
|
|
|
|
|
addField(tr(STR_DESCRIPTION), meta.description, false, EpdFontFamily::REGULAR);
|
2026-03-09 01:52:07 -04:00
|
|
|
|
|
|
|
|
const int lineH10 = renderer.getLineHeight(UI_10_FONT_ID);
|
|
|
|
|
const int lineH12 = renderer.getLineHeight(UI_12_FONT_ID);
|
|
|
|
|
int h = MARGIN;
|
|
|
|
|
|
|
|
|
|
if (!coverBmpPath.empty()) {
|
|
|
|
|
FsFile file;
|
|
|
|
|
if (Storage.openFileForRead("BIF", coverBmpPath, file)) {
|
|
|
|
|
Bitmap bitmap(file);
|
|
|
|
|
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
|
|
|
|
|
coverDisplayHeight = bitmap.getHeight();
|
|
|
|
|
coverDisplayWidth = bitmap.getWidth();
|
|
|
|
|
}
|
|
|
|
|
file.close();
|
|
|
|
|
}
|
|
|
|
|
if (coverDisplayHeight > 0) {
|
|
|
|
|
h += coverDisplayHeight + COVER_GAP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto& field : fields) {
|
|
|
|
|
if (field.label) {
|
|
|
|
|
h += lineH10 + LABEL_VALUE_GAP;
|
|
|
|
|
}
|
|
|
|
|
h += static_cast<int>(field.lines.size()) * lineH12;
|
|
|
|
|
h += SECTION_GAP;
|
|
|
|
|
}
|
|
|
|
|
contentHeight = h;
|
|
|
|
|
}
|
|
|
|
|
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
void BookInfoActivity::loop() {
|
feat: BookInfo button mapping, ManageBook integration, cover regen fix
- Fix BookInfo buttons: Left/Right front = scroll down/up, Confirm = no-op,
side buttons retained. Separate Up/Down hints on btn3/btn4.
- Fallback load: try epub.load(true, true) when cache-only load fails,
so Book Info works for unopened books.
- Add "Book Info" to ManageBook menu (BOOK_INFO action) with handlers in
all 4 result sites (Home, Recent, FileBrowser, Reader).
- Fix HomeActivity cover regen: call generateCoverBmp(false) + validate
with isValidThumbnailBmp before falling to placeholder, matching the
reader's multi-tier fallback pipeline. Same for XTC branch.
Made-with: Cursor
2026-03-09 02:06:25 -04:00
|
|
|
if (mappedInput.wasReleased(MappedInputManager::Button::Back)) {
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
ActivityResult r;
|
|
|
|
|
r.isCancelled = true;
|
|
|
|
|
setResult(std::move(r));
|
|
|
|
|
finish();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 01:52:07 -04:00
|
|
|
const int pageH = renderer.getScreenHeight();
|
|
|
|
|
const int scrollStep = pageH / 3;
|
|
|
|
|
|
feat: BookInfo button mapping, ManageBook integration, cover regen fix
- Fix BookInfo buttons: Left/Right front = scroll down/up, Confirm = no-op,
side buttons retained. Separate Up/Down hints on btn3/btn4.
- Fallback load: try epub.load(true, true) when cache-only load fails,
so Book Info works for unopened books.
- Add "Book Info" to ManageBook menu (BOOK_INFO action) with handlers in
all 4 result sites (Home, Recent, FileBrowser, Reader).
- Fix HomeActivity cover regen: call generateCoverBmp(false) + validate
with isValidThumbnailBmp before falling to placeholder, matching the
reader's multi-tier fallback pipeline. Same for XTC branch.
Made-with: Cursor
2026-03-09 02:06:25 -04:00
|
|
|
if (mappedInput.wasReleased(MappedInputManager::Button::Down) ||
|
|
|
|
|
mappedInput.wasReleased(MappedInputManager::Button::PageForward) ||
|
|
|
|
|
mappedInput.wasReleased(MappedInputManager::Button::Left)) {
|
2026-03-09 01:52:07 -04:00
|
|
|
if (scrollOffset + pageH < contentHeight) {
|
|
|
|
|
scrollOffset += scrollStep;
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
requestUpdate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat: BookInfo button mapping, ManageBook integration, cover regen fix
- Fix BookInfo buttons: Left/Right front = scroll down/up, Confirm = no-op,
side buttons retained. Separate Up/Down hints on btn3/btn4.
- Fallback load: try epub.load(true, true) when cache-only load fails,
so Book Info works for unopened books.
- Add "Book Info" to ManageBook menu (BOOK_INFO action) with handlers in
all 4 result sites (Home, Recent, FileBrowser, Reader).
- Fix HomeActivity cover regen: call generateCoverBmp(false) + validate
with isValidThumbnailBmp before falling to placeholder, matching the
reader's multi-tier fallback pipeline. Same for XTC branch.
Made-with: Cursor
2026-03-09 02:06:25 -04:00
|
|
|
if (mappedInput.wasReleased(MappedInputManager::Button::Up) ||
|
|
|
|
|
mappedInput.wasReleased(MappedInputManager::Button::PageBack) ||
|
|
|
|
|
mappedInput.wasReleased(MappedInputManager::Button::Right)) {
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
if (scrollOffset > 0) {
|
2026-03-09 01:52:07 -04:00
|
|
|
scrollOffset -= scrollStep;
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
if (scrollOffset < 0) scrollOffset = 0;
|
|
|
|
|
requestUpdate();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BookInfoActivity::render(RenderLock&&) {
|
|
|
|
|
renderer.clearScreen();
|
|
|
|
|
|
|
|
|
|
const int pageH = renderer.getScreenHeight();
|
|
|
|
|
const int lineH10 = renderer.getLineHeight(UI_10_FONT_ID);
|
|
|
|
|
const int lineH12 = renderer.getLineHeight(UI_12_FONT_ID);
|
2026-03-09 01:52:07 -04:00
|
|
|
int y = MARGIN - scrollOffset;
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
|
2026-03-09 01:52:07 -04:00
|
|
|
// Cover image — only draw if at least partially visible
|
|
|
|
|
if (!coverBmpPath.empty() && coverDisplayHeight > 0) {
|
|
|
|
|
if (y + coverDisplayHeight > 0 && y < pageH) {
|
|
|
|
|
FsFile file;
|
|
|
|
|
if (Storage.openFileForRead("BIF", coverBmpPath, file)) {
|
|
|
|
|
Bitmap bitmap(file);
|
|
|
|
|
if (bitmap.parseHeaders() == BmpReaderError::Ok) {
|
|
|
|
|
const int coverX = (renderer.getScreenWidth() - coverDisplayWidth) / 2;
|
|
|
|
|
renderer.drawBitmap1Bit(bitmap, coverX, y, coverDisplayWidth, std::min(coverDisplayHeight, pageH - y));
|
|
|
|
|
}
|
|
|
|
|
file.close();
|
|
|
|
|
}
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
}
|
2026-03-09 01:52:07 -04:00
|
|
|
y += coverDisplayHeight + COVER_GAP;
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
}
|
|
|
|
|
|
2026-03-09 01:52:07 -04:00
|
|
|
for (const auto& field : fields) {
|
|
|
|
|
if (y >= pageH) break;
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
|
2026-03-09 01:52:07 -04:00
|
|
|
if (field.label) {
|
|
|
|
|
if (y + lineH10 > 0 && y < pageH) {
|
|
|
|
|
renderer.drawText(UI_10_FONT_ID, MARGIN, y, field.label, true, EpdFontFamily::BOLD);
|
|
|
|
|
}
|
|
|
|
|
y += lineH10 + LABEL_VALUE_GAP;
|
|
|
|
|
}
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
|
2026-03-09 01:52:07 -04:00
|
|
|
const auto style = field.bold ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR;
|
|
|
|
|
for (const auto& line : field.lines) {
|
|
|
|
|
if (y >= pageH) break;
|
|
|
|
|
if (y + lineH12 > 0) {
|
|
|
|
|
renderer.drawText(UI_12_FONT_ID, MARGIN, y, line.c_str(), true, style);
|
|
|
|
|
}
|
|
|
|
|
y += lineH12;
|
|
|
|
|
}
|
|
|
|
|
y += SECTION_GAP;
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
}
|
|
|
|
|
|
2026-03-09 01:52:07 -04:00
|
|
|
const bool canScrollDown = scrollOffset + pageH < contentHeight;
|
|
|
|
|
const bool canScrollUp = scrollOffset > 0;
|
feat: BookInfo button mapping, ManageBook integration, cover regen fix
- Fix BookInfo buttons: Left/Right front = scroll down/up, Confirm = no-op,
side buttons retained. Separate Up/Down hints on btn3/btn4.
- Fallback load: try epub.load(true, true) when cache-only load fails,
so Book Info works for unopened books.
- Add "Book Info" to ManageBook menu (BOOK_INFO action) with handlers in
all 4 result sites (Home, Recent, FileBrowser, Reader).
- Fix HomeActivity cover regen: call generateCoverBmp(false) + validate
with isValidThumbnailBmp before falling to placeholder, matching the
reader's multi-tier fallback pipeline. Same for XTC branch.
Made-with: Cursor
2026-03-09 02:06:25 -04:00
|
|
|
const char* downHint = canScrollDown ? tr(STR_DIR_DOWN) : "";
|
|
|
|
|
const char* upHint = canScrollUp ? tr(STR_DIR_UP) : "";
|
|
|
|
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", downHint, upHint);
|
port: upstream PR #1342 - Book Info screen, richer metadata, safer controls
Ports upstream PR #1342 (feat: Add Book Info screen, richer metadata,
and safer file-browser controls) with mod-specific adaptations:
- Parse and cache series, seriesIndex, description from EPUB OPF
- Bump book.bin cache version to 6 for new metadata fields
- Add BookInfoActivity (new screen) accessible via Right button in FileBrowser
- Add ManageBook menu via Left button in FileBrowser (replaces upstream hidden delete)
- Guard all delete/archive actions with ConfirmationActivity (10 call sites)
- Add inputArmed gating to ConfirmationActivity to prevent accidental confirmation
- Safe deserialization: readString now returns bool with MAX_STRING_LENGTH guard
- Add series field to RecentBooksStore with JSON and binary serialization
- Add i18n keys: STR_BOOK_INFO, STR_AUTHOR, STR_SERIES, STR_FILE_SIZE, etc.
Made-with: Cursor
2026-03-09 00:39:32 -04:00
|
|
|
GUI.drawButtonHints(renderer, labels.btn1, labels.btn2, labels.btn3, labels.btn4);
|
|
|
|
|
|
|
|
|
|
renderer.displayBuffer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string BookInfoActivity::formatFileSize(size_t bytes) {
|
|
|
|
|
char buf[32];
|
|
|
|
|
if (bytes < 1024) {
|
|
|
|
|
snprintf(buf, sizeof(buf), "%u B", static_cast<unsigned>(bytes));
|
|
|
|
|
} else if (bytes < 1024 * 1024) {
|
|
|
|
|
snprintf(buf, sizeof(buf), "%.1f KB", static_cast<float>(bytes) / 1024.0f);
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(buf, sizeof(buf), "%.1f MB", static_cast<float>(bytes) / (1024.0f * 1024.0f));
|
|
|
|
|
}
|
|
|
|
|
return buf;
|
|
|
|
|
}
|