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 01:52:07 -04:00
|
|
|
std::string title, author, series, seriesIndex, description, language;
|
|
|
|
|
|
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");
|
|
|
|
|
if (epub.load(false, true)) {
|
|
|
|
|
title = epub.getTitle();
|
|
|
|
|
author = epub.getAuthor();
|
|
|
|
|
series = epub.getSeries();
|
|
|
|
|
seriesIndex = epub.getSeriesIndex();
|
2026-03-09 01:52:07 -04:00
|
|
|
description = normalizeWhitespace(epub.getDescription());
|
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
|
|
|
language = epub.getLanguage();
|
2026-03-09 01:52:07 -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";
|
|
|
|
|
if (PlaceholderCoverGenerator::generate(placeholderPath, title.empty() ? fileName : title, author, thumbW,
|
|
|
|
|
coverH)) {
|
|
|
|
|
coverBmpPath = placeholderPath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (FsHelpers::hasXtcExtension(fileName)) {
|
|
|
|
|
Xtc xtc(filePath, "/.crosspoint");
|
|
|
|
|
if (xtc.load()) {
|
|
|
|
|
title = xtc.getTitle();
|
|
|
|
|
author = xtc.getAuthor();
|
|
|
|
|
|
|
|
|
|
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";
|
|
|
|
|
if (PlaceholderCoverGenerator::generate(placeholderPath, title.empty() ? fileName : title, author, thumbW,
|
|
|
|
|
coverH)) {
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (title.empty()) {
|
|
|
|
|
title = fileName;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-09 01:52:07 -04:00
|
|
|
buildLayout(title, author, series, seriesIndex, description, language, 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 01:52:07 -04:00
|
|
|
void BookInfoActivity::buildLayout(const std::string& title, const std::string& author, const std::string& series,
|
|
|
|
|
const std::string& seriesIndex, const std::string& description,
|
|
|
|
|
const std::string& language, size_t fileSize) {
|
|
|
|
|
const int contentW = renderer.getScreenWidth() - MARGIN * 2;
|
|
|
|
|
fields.reserve(6);
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
addField(nullptr, title, true, EpdFontFamily::BOLD);
|
|
|
|
|
addField(tr(STR_AUTHOR), author, false, EpdFontFamily::REGULAR);
|
|
|
|
|
|
|
|
|
|
if (!series.empty()) {
|
|
|
|
|
std::string seriesStr = series;
|
|
|
|
|
if (!seriesIndex.empty()) {
|
|
|
|
|
seriesStr += " #" + seriesIndex;
|
|
|
|
|
}
|
|
|
|
|
addField(tr(STR_SERIES), seriesStr, false, EpdFontFamily::REGULAR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addField(tr(STR_LANGUAGE), language, false, EpdFontFamily::REGULAR);
|
|
|
|
|
|
|
|
|
|
if (fileSize > 0) {
|
|
|
|
|
addField(tr(STR_FILE_SIZE), formatFileSize(fileSize), false, EpdFontFamily::REGULAR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addField(tr(STR_DESCRIPTION), description, false, EpdFontFamily::REGULAR);
|
|
|
|
|
|
|
|
|
|
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() {
|
|
|
|
|
if (mappedInput.wasReleased(MappedInputManager::Button::Back) ||
|
|
|
|
|
mappedInput.wasReleased(MappedInputManager::Button::Confirm)) {
|
|
|
|
|
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;
|
|
|
|
|
|
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 (mappedInput.wasReleased(MappedInputManager::Button::Down)) {
|
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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mappedInput.wasReleased(MappedInputManager::Button::Up)) {
|
|
|
|
|
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;
|
|
|
|
|
const char* scrollHint = canScrollDown ? tr(STR_DIR_DOWN) : (canScrollUp ? tr(STR_DIR_UP) : "");
|
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
|
|
|
const auto labels = mappedInput.mapLabels(tr(STR_BACK), "", scrollHint, "");
|
|
|
|
|
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;
|
|
|
|
|
}
|