fix: add header bar and fix bottom spacing in BookInfo

Draw the standard header (clock, battery, title) via GUI.drawHeader().
Replace hardcoded MARGIN with theme metrics for content positioning.
Content area now starts below the header and stops above button hints
so the last lines are never obscured.

Made-with: Cursor
This commit is contained in:
cottongin
2026-03-09 03:17:49 -04:00
parent efa727eff2
commit 4c62437689
2 changed files with 34 additions and 15 deletions

View File

@@ -36,9 +36,17 @@
- Display order: Title, Author, Series, Publisher, Date, Subjects, Rating (N/5), Language, ISBN, Contributor, File Size, Rights, Description - Display order: Title, Author, Series, Publisher, Date, Subjects, Rating (N/5), Language, ISBN, Contributor, File Size, Rights, Description
- Rating displayed as `N / 5` (Calibre stores 0-10, divided by 2) - Rating displayed as `N / 5` (Calibre stores 0-10, divided by 2)
### 6. BookInfoActivity UI Integration (`src/activities/home/BookInfoActivity.cpp`)
- Added `GUI.drawHeader()` call to show the standard header bar (clock, battery, "Book Info" title)
- Replaced hardcoded `MARGIN = 20` with theme metrics (`contentSidePadding`, `topPadding`, `headerHeight`, etc.)
- Content now starts below the header bar using `metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing`
- Y-culling stops content above button hints using `pageH - metrics.buttonHintsHeight - metrics.verticalSpacing`
- `contentHeight` includes bottom padding for button hints so scrolling accounts for the reserved hint area
## Commits ## Commits
- `8025e6f``feat: parse and display all available EPUB metadata fields` - `8025e6f``feat: parse and display all available EPUB metadata fields`
- (pending)`refactor: consolidate Epub blank strings, simplify BookInfo buildLayout` - `efa727e``refactor: consolidate Epub blank strings, simplify BookInfo buildLayout`
- (pending) — `fix: add header bar and fix bottom spacing in BookInfo`
## Follow-up ## Follow-up
- Existing book caches will auto-invalidate (version 6 → 7) and regenerate on next load - Existing book caches will auto-invalidate (version 6 → 7) and regenerate on next load

View File

@@ -16,7 +16,6 @@
#include "fontIds.h" #include "fontIds.h"
namespace { namespace {
constexpr int MARGIN = 20;
constexpr int LABEL_VALUE_GAP = 4; constexpr int LABEL_VALUE_GAP = 4;
constexpr int SECTION_GAP = 14; constexpr int SECTION_GAP = 14;
constexpr int MAX_WRAPPED_LINES = 60; constexpr int MAX_WRAPPED_LINES = 60;
@@ -132,7 +131,9 @@ void BookInfoActivity::onEnter() {
void BookInfoActivity::onExit() { Activity::onExit(); } void BookInfoActivity::onExit() { Activity::onExit(); }
void BookInfoActivity::buildLayout(const BookMetadataCache::BookMetadata& meta, size_t fileSize) { void BookInfoActivity::buildLayout(const BookMetadataCache::BookMetadata& meta, size_t fileSize) {
const int contentW = renderer.getScreenWidth() - MARGIN * 2; const auto& metrics = UITheme::getInstance().getMetrics();
const int sidePad = metrics.contentSidePadding;
const int contentW = renderer.getScreenWidth() - sidePad * 2;
fields.reserve(13); fields.reserve(13);
auto addField = [&](const char* label, const std::string& text, bool bold, EpdFontFamily::Style style) { auto addField = [&](const char* label, const std::string& text, bool bold, EpdFontFamily::Style style) {
@@ -181,7 +182,8 @@ void BookInfoActivity::buildLayout(const BookMetadataCache::BookMetadata& meta,
const int lineH10 = renderer.getLineHeight(UI_10_FONT_ID); const int lineH10 = renderer.getLineHeight(UI_10_FONT_ID);
const int lineH12 = renderer.getLineHeight(UI_12_FONT_ID); const int lineH12 = renderer.getLineHeight(UI_12_FONT_ID);
int h = MARGIN; const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
int h = contentTop;
if (!coverBmpPath.empty()) { if (!coverBmpPath.empty()) {
FsFile file; FsFile file;
@@ -205,6 +207,7 @@ void BookInfoActivity::buildLayout(const BookMetadataCache::BookMetadata& meta,
h += static_cast<int>(field.lines.size()) * lineH12; h += static_cast<int>(field.lines.size()) * lineH12;
h += SECTION_GAP; h += SECTION_GAP;
} }
h += metrics.buttonHintsHeight + metrics.verticalSpacing;
contentHeight = h; contentHeight = h;
} }
@@ -243,20 +246,28 @@ void BookInfoActivity::loop() {
void BookInfoActivity::render(RenderLock&&) { void BookInfoActivity::render(RenderLock&&) {
renderer.clearScreen(); renderer.clearScreen();
const auto& metrics = UITheme::getInstance().getMetrics();
const int pageW = renderer.getScreenWidth();
const int pageH = renderer.getScreenHeight(); const int pageH = renderer.getScreenHeight();
const int sidePad = metrics.contentSidePadding;
const int lineH10 = renderer.getLineHeight(UI_10_FONT_ID); const int lineH10 = renderer.getLineHeight(UI_10_FONT_ID);
const int lineH12 = renderer.getLineHeight(UI_12_FONT_ID); const int lineH12 = renderer.getLineHeight(UI_12_FONT_ID);
int y = MARGIN - scrollOffset; const int contentTop = metrics.topPadding + metrics.headerHeight + metrics.verticalSpacing;
const int contentBottom = pageH - metrics.buttonHintsHeight - metrics.verticalSpacing;
GUI.drawHeader(renderer, Rect(0, metrics.topPadding, pageW, metrics.headerHeight), tr(STR_BOOK_INFO));
int y = contentTop - scrollOffset;
// Cover image — only draw if at least partially visible
if (!coverBmpPath.empty() && coverDisplayHeight > 0) { if (!coverBmpPath.empty() && coverDisplayHeight > 0) {
if (y + coverDisplayHeight > 0 && y < pageH) { if (y + coverDisplayHeight > contentTop && y < contentBottom) {
FsFile file; FsFile file;
if (Storage.openFileForRead("BIF", coverBmpPath, file)) { if (Storage.openFileForRead("BIF", coverBmpPath, file)) {
Bitmap bitmap(file); Bitmap bitmap(file);
if (bitmap.parseHeaders() == BmpReaderError::Ok) { if (bitmap.parseHeaders() == BmpReaderError::Ok) {
const int coverX = (renderer.getScreenWidth() - coverDisplayWidth) / 2; const int coverX = (pageW - coverDisplayWidth) / 2;
renderer.drawBitmap1Bit(bitmap, coverX, y, coverDisplayWidth, std::min(coverDisplayHeight, pageH - y)); renderer.drawBitmap1Bit(bitmap, coverX, y, coverDisplayWidth,
std::min(coverDisplayHeight, contentBottom - y));
} }
file.close(); file.close();
} }
@@ -265,20 +276,20 @@ void BookInfoActivity::render(RenderLock&&) {
} }
for (const auto& field : fields) { for (const auto& field : fields) {
if (y >= pageH) break; if (y >= contentBottom) break;
if (field.label) { if (field.label) {
if (y + lineH10 > 0 && y < pageH) { if (y + lineH10 > contentTop && y < contentBottom) {
renderer.drawText(UI_10_FONT_ID, MARGIN, y, field.label, true, EpdFontFamily::BOLD); renderer.drawText(UI_10_FONT_ID, sidePad, y, field.label, true, EpdFontFamily::BOLD);
} }
y += lineH10 + LABEL_VALUE_GAP; y += lineH10 + LABEL_VALUE_GAP;
} }
const auto style = field.bold ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR; const auto style = field.bold ? EpdFontFamily::BOLD : EpdFontFamily::REGULAR;
for (const auto& line : field.lines) { for (const auto& line : field.lines) {
if (y >= pageH) break; if (y >= contentBottom) break;
if (y + lineH12 > 0) { if (y + lineH12 > contentTop) {
renderer.drawText(UI_12_FONT_ID, MARGIN, y, line.c_str(), true, style); renderer.drawText(UI_12_FONT_ID, sidePad, y, line.c_str(), true, style);
} }
y += lineH12; y += lineH12;
} }