yep it works

This commit is contained in:
cottongin 2026-01-24 02:44:11 -05:00
parent 5c3828efe8
commit 1e20d30875
No known key found for this signature in database
GPG Key ID: 0ECC91FE4655C262

View File

@ -6,6 +6,7 @@
#include <SDCardManager.h> #include <SDCardManager.h>
#include <Xtc.h> #include <Xtc.h>
#include <algorithm>
#include <cstring> #include <cstring>
#include <vector> #include <vector>
@ -218,12 +219,32 @@ void HomeActivity::render() {
constexpr int margin = 20; constexpr int margin = 20;
constexpr int bottomMargin = 60; constexpr int bottomMargin = 60;
// --- Top "book" card for the current title (selectorIndex == 0) ---
const int bookWidth = pageWidth / 2;
const int bookHeight = pageHeight / 2;
const int bookX = (pageWidth - bookWidth) / 2;
constexpr int bookY = 30; constexpr int bookY = 30;
constexpr int elementSpacing = 15;
// --- Calculate layout from bottom up ---
// Build menu items dynamically (need count for layout calculation)
std::vector<const char*> menuItems = {"My Library", "File Transfer", "Settings"};
if (hasOpdsUrl) {
menuItems.insert(menuItems.begin() + 1, "Calibre Library");
}
const int menuTileWidth = pageWidth - 2 * margin;
constexpr int menuTileHeight = 45;
constexpr int menuSpacing = 8;
const int totalMenuHeight =
static_cast<int>(menuItems.size()) * menuTileHeight + (static_cast<int>(menuItems.size()) - 1) * menuSpacing;
// Anchor menu to bottom of screen
const int menuStartY = pageHeight - bottomMargin - totalMenuHeight - margin;
// Calculate book card dimensions - larger, filling available space
const int bookWidth = pageWidth - 2 * margin;
// Card extends to just above menu
const int bookCardBottomY = menuStartY - elementSpacing;
const int bookHeight = bookCardBottomY - bookY;
const int bookX = margin;
const bool bookSelected = hasContinueReading && selectorIndex == 0; const bool bookSelected = hasContinueReading && selectorIndex == 0;
// Bookmark dimensions (used in multiple places) // Bookmark dimensions (used in multiple places)
@ -242,27 +263,26 @@ void HomeActivity::render() {
if (SdMan.openFileForRead("HOME", coverBmpPath, file)) { if (SdMan.openFileForRead("HOME", coverBmpPath, file)) {
Bitmap bitmap(file); Bitmap bitmap(file);
if (bitmap.parseHeaders() == BmpReaderError::Ok) { if (bitmap.parseHeaders() == BmpReaderError::Ok) {
// Calculate position to center image within the book card // Add padding around the cover image so it doesn't touch the frame
int coverX, coverY; constexpr int coverPadding = 10;
const int availableWidth = bookWidth - 2 * coverPadding;
const int availableHeight = bookHeight - 2 * coverPadding;
if (bitmap.getWidth() > bookWidth || bitmap.getHeight() > bookHeight) { // Calculate scale to fit image within padded area while maintaining aspect ratio
const float imgRatio = static_cast<float>(bitmap.getWidth()) / static_cast<float>(bitmap.getHeight()); const float scaleX = static_cast<float>(availableWidth) / static_cast<float>(bitmap.getWidth());
const float boxRatio = static_cast<float>(bookWidth) / static_cast<float>(bookHeight); const float scaleY = static_cast<float>(availableHeight) / static_cast<float>(bitmap.getHeight());
const float scale = std::min(scaleX, scaleY);
if (imgRatio > boxRatio) { // Calculate actual scaled dimensions
coverX = bookX; const int scaledWidth = static_cast<int>(bitmap.getWidth() * scale);
coverY = bookY + (bookHeight - static_cast<int>(bookWidth / imgRatio)) / 2; const int scaledHeight = static_cast<int>(bitmap.getHeight() * scale);
} else {
coverX = bookX + (bookWidth - static_cast<int>(bookHeight * imgRatio)) / 2; // Center the scaled image within the book card (accounting for padding)
coverY = bookY; const int coverX = bookX + (bookWidth - scaledWidth) / 2;
} const int coverY = bookY + (bookHeight - scaledHeight) / 2;
} else {
coverX = bookX + (bookWidth - bitmap.getWidth()) / 2;
coverY = bookY + (bookHeight - bitmap.getHeight()) / 2;
}
// Draw the cover image centered within the book card // Draw the cover image centered within the book card
renderer.drawBitmap(bitmap, coverX, coverY, bookWidth, bookHeight); renderer.drawBitmap(bitmap, coverX, coverY, scaledWidth, scaledHeight);
// Draw border around the card // Draw border around the card
renderer.drawRect(bookX, bookY, bookWidth, bookHeight); renderer.drawRect(bookX, bookY, bookWidth, bookHeight);
@ -325,9 +345,66 @@ void HomeActivity::render() {
} }
if (hasContinueReading) { if (hasContinueReading) {
// Invert text colors based on selection state: if (coverRendered) {
// - With cover: selected = white text on black box, unselected = black text on white box // --- Cover image present: draw combined label at the bottom of the card ---
// - Without cover: selected = white text on black card, unselected = black text on white card // Box contains: "Continue Reading" (larger) and "Title - Author" (smaller)
const char* continueText = "Continue Reading";
constexpr int boxPadding = 8;
constexpr int lineSpacing = 2;
// Build subtitle: "Title - Author" or just "Title"
std::string subtitle = lastBookTitle;
if (!lastBookAuthor.empty()) {
subtitle += " - " + lastBookAuthor;
}
// Calculate box dimensions based on both lines
const int continueTextWidth = renderer.getTextWidth(UI_10_FONT_ID, continueText);
const int continueLineHeight = renderer.getLineHeight(UI_10_FONT_ID);
const int subtitleLineHeight = renderer.getLineHeight(SMALL_FONT_ID);
// Truncate subtitle to fit within card (with padding)
const int maxSubtitleWidth = bookWidth - 2 * boxPadding - 20; // Extra margin for aesthetics
bool wasTrimmed = false;
while (renderer.getTextWidth(SMALL_FONT_ID, subtitle.c_str()) > maxSubtitleWidth && !subtitle.empty()) {
StringUtils::utf8RemoveLastChar(subtitle);
wasTrimmed = true;
}
if (wasTrimmed && !subtitle.empty()) {
// Make room for ellipsis
while (renderer.getTextWidth(SMALL_FONT_ID, (subtitle + "...").c_str()) > maxSubtitleWidth &&
!subtitle.empty()) {
StringUtils::utf8RemoveLastChar(subtitle);
}
subtitle.append("...");
}
const int subtitleTextWidth = renderer.getTextWidth(SMALL_FONT_ID, subtitle.c_str());
// Box width is the wider of the two lines plus padding
const int boxContentWidth = std::max(continueTextWidth, subtitleTextWidth);
const int boxWidth = boxContentWidth + boxPadding * 2;
const int boxHeight = continueLineHeight + lineSpacing + subtitleLineHeight + boxPadding * 2;
// Position box at the bottom of the card, centered
const int boxX = (pageWidth - boxWidth) / 2;
const int boxY = bookY + bookHeight - boxHeight - boxPadding;
// Draw box background and border
renderer.fillRect(boxX, boxY, boxWidth, boxHeight, bookSelected);
renderer.drawRect(boxX, boxY, boxWidth, boxHeight, !bookSelected);
// Draw "Continue Reading" line
const int continueY = boxY + boxPadding;
renderer.drawCenteredText(UI_10_FONT_ID, continueY, continueText, !bookSelected);
// Draw "Title - Author" line below
const int subtitleY = continueY + continueLineHeight + lineSpacing;
renderer.drawCenteredText(SMALL_FONT_ID, subtitleY, subtitle.c_str(), !bookSelected);
} else {
// --- No cover image: draw title/author inside the card (existing behavior) ---
// Invert text colors based on selection state
// Split into words (avoid stringstream to keep this light on the MCU) // Split into words (avoid stringstream to keep this light on the MCU)
std::vector<std::string> words; std::vector<std::string> words;
@ -360,7 +437,8 @@ void HomeActivity::render() {
// Still have words left, so add ellipsis to last line // Still have words left, so add ellipsis to last line
lines.back().append("..."); lines.back().append("...");
while (!lines.back().empty() && renderer.getTextWidth(UI_12_FONT_ID, lines.back().c_str()) > maxLineWidth) { while (!lines.back().empty() &&
renderer.getTextWidth(UI_12_FONT_ID, lines.back().c_str()) > maxLineWidth) {
// Remove "..." first, then remove one UTF-8 char, then add "..." back // Remove "..." first, then remove one UTF-8 char, then add "..." back
lines.back().resize(lines.back().size() - 3); // Remove "..." lines.back().resize(lines.back().size() - 3); // Remove "..."
StringUtils::utf8RemoveLastChar(lines.back()); StringUtils::utf8RemoveLastChar(lines.back());
@ -411,43 +489,6 @@ void HomeActivity::render() {
// 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;
// If cover image was rendered, draw box behind title and author
if (coverRendered) {
constexpr int boxPadding = 8;
// Calculate the max text width for the box
int maxTextWidth = 0;
for (const auto& line : lines) {
const int lineWidth = renderer.getTextWidth(UI_12_FONT_ID, line.c_str());
if (lineWidth > maxTextWidth) {
maxTextWidth = lineWidth;
}
}
if (!lastBookAuthor.empty()) {
std::string trimmedAuthor = lastBookAuthor;
while (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) > maxLineWidth && !trimmedAuthor.empty()) {
StringUtils::utf8RemoveLastChar(trimmedAuthor);
}
if (renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str()) <
renderer.getTextWidth(UI_10_FONT_ID, lastBookAuthor.c_str())) {
trimmedAuthor.append("...");
}
const int authorWidth = renderer.getTextWidth(UI_10_FONT_ID, trimmedAuthor.c_str());
if (authorWidth > maxTextWidth) {
maxTextWidth = authorWidth;
}
}
const int boxWidth = maxTextWidth + boxPadding * 2;
const int boxHeight = totalTextHeight + boxPadding * 2;
const int boxX = (pageWidth - boxWidth) / 2;
const int boxY = titleYStart - boxPadding;
// Draw box (inverted when selected: black box instead of white)
renderer.fillRect(boxX, boxY, boxWidth, boxHeight, bookSelected);
// Draw border around the box (inverted when selected: white border instead of black)
renderer.drawRect(boxX, boxY, boxWidth, boxHeight, !bookSelected);
}
for (const auto& line : lines) { for (const auto& line : lines) {
renderer.drawCenteredText(UI_12_FONT_ID, titleYStart, line.c_str(), !bookSelected); renderer.drawCenteredText(UI_12_FONT_ID, titleYStart, line.c_str(), !bookSelected);
titleYStart += renderer.getLineHeight(UI_12_FONT_ID); titleYStart += renderer.getLineHeight(UI_12_FONT_ID);
@ -473,21 +514,8 @@ void HomeActivity::render() {
renderer.drawCenteredText(UI_10_FONT_ID, titleYStart, trimmedAuthor.c_str(), !bookSelected); renderer.drawCenteredText(UI_10_FONT_ID, titleYStart, trimmedAuthor.c_str(), !bookSelected);
} }
// "Continue Reading" label at the bottom // "Continue Reading" label at the bottom of card (only when no cover)
const int continueY = bookY + bookHeight - renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2; const int continueY = bookY + bookHeight - renderer.getLineHeight(UI_10_FONT_ID) * 3 / 2;
if (coverRendered) {
// Draw box behind "Continue Reading" text (inverted when selected: black box instead of white)
const char* continueText = "Continue Reading";
const int continueTextWidth = renderer.getTextWidth(UI_10_FONT_ID, continueText);
constexpr int continuePadding = 6;
const int continueBoxWidth = continueTextWidth + continuePadding * 2;
const int continueBoxHeight = renderer.getLineHeight(UI_10_FONT_ID) + continuePadding;
const int continueBoxX = (pageWidth - continueBoxWidth) / 2;
const int continueBoxY = continueY - continuePadding / 2;
renderer.fillRect(continueBoxX, continueBoxY, continueBoxWidth, continueBoxHeight, bookSelected);
renderer.drawRect(continueBoxX, continueBoxY, continueBoxWidth, continueBoxHeight, !bookSelected);
renderer.drawCenteredText(UI_10_FONT_ID, continueY, continueText, !bookSelected);
} else {
renderer.drawCenteredText(UI_10_FONT_ID, continueY, "Continue Reading", !bookSelected); renderer.drawCenteredText(UI_10_FONT_ID, continueY, "Continue Reading", !bookSelected);
} }
} else { } else {
@ -498,27 +526,7 @@ void HomeActivity::render() {
renderer.drawCenteredText(UI_10_FONT_ID, y + renderer.getLineHeight(UI_12_FONT_ID), "Start reading below"); renderer.drawCenteredText(UI_10_FONT_ID, y + renderer.getLineHeight(UI_12_FONT_ID), "Start reading below");
} }
// --- Bottom menu tiles --- // --- Bottom menu tiles (anchored to bottom) ---
// Build menu items dynamically
std::vector<const char*> menuItems = {"My Library", "File Transfer", "Settings"};
if (hasOpdsUrl) {
// Insert Calibre Library after My Library
menuItems.insert(menuItems.begin() + 1, "Calibre Library");
}
const int menuTileWidth = pageWidth - 2 * margin;
constexpr int menuTileHeight = 45;
constexpr int menuSpacing = 8;
const int totalMenuHeight =
static_cast<int>(menuItems.size()) * menuTileHeight + (static_cast<int>(menuItems.size()) - 1) * menuSpacing;
int menuStartY = bookY + bookHeight + 15;
// Ensure we don't collide with the bottom button legend
const int maxMenuStartY = pageHeight - bottomMargin - totalMenuHeight - margin;
if (menuStartY > maxMenuStartY) {
menuStartY = maxMenuStartY;
}
for (size_t i = 0; i < menuItems.size(); ++i) { for (size_t i = 0; i < menuItems.size(); ++i) {
const int overallIndex = static_cast<int>(i) + (hasContinueReading ? 1 : 0); const int overallIndex = static_cast<int>(i) + (hasContinueReading ? 1 : 0);
constexpr int tileX = margin; constexpr int tileX = margin;