diff --git a/lib/PlaceholderCover/PlaceholderCoverGenerator.cpp b/lib/PlaceholderCover/PlaceholderCoverGenerator.cpp index 2752b093..b2854c2c 100644 --- a/lib/PlaceholderCover/PlaceholderCoverGenerator.cpp +++ b/lib/PlaceholderCover/PlaceholderCoverGenerator.cpp @@ -235,12 +235,12 @@ int getCharAdvance(const EpdFontData* font, uint32_t cp) { return glyph->advanceX; } -/// Split a string into words (splitting on spaces). +/// Split a string into words (splitting on whitespace: space, newline, tab, CR). std::vector splitWords(const std::string& text) { std::vector words; std::string current; for (size_t i = 0; i < text.size(); i++) { - if (text[i] == ' ') { + if (text[i] == ' ' || text[i] == '\n' || text[i] == '\r' || text[i] == '\t') { if (!current.empty()) { words.push_back(current); current.clear(); @@ -324,23 +324,17 @@ bool PlaceholderCoverGenerator::generate(const std::string& outputPath, const st } // Proportional layout constants based on cover dimensions. - // The device bezel covers ~2-3px on each edge, so we pad inward from the edge. - const int edgePadding = std::max(3, width / 48); // ~10px at 480w, ~3px at 136w - const int borderWidth = std::max(2, width / 96); // ~5px at 480w, ~2px at 136w const int innerPadding = std::max(4, width / 32); // ~15px at 480w, ~4px at 136w // Text scaling: 2x for full-size covers, 1x for thumbnails const int titleScale = (height >= 600) ? 2 : 1; - const int authorScale = (height >= 600) ? 2 : 1; // Author also larger on full covers + const int authorScale = (height >= 600) ? 2 : 1; // Icon: 2x for full cover, 1x for medium thumb, skip for small const int iconScale = (height >= 600) ? 2 : (height >= 350 ? 1 : 0); - // Draw border inset from edge - buf.drawBorder(edgePadding, edgePadding, width - 2 * edgePadding, height - 2 * edgePadding, borderWidth); - - // Content area (inside border + inner padding) - const int contentX = edgePadding + borderWidth + innerPadding; - const int contentY = edgePadding + borderWidth + innerPadding; + // Content area (the UI draws its own border around book cards) + const int contentX = innerPadding; + const int contentY = innerPadding; const int contentW = width - 2 * contentX; const int contentH = height - 2 * contentY; diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index 53d82a99..8ea2f788 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include "I18n.h" @@ -266,6 +267,27 @@ void BaseTheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* t Rect{batteryX, rect.y + 5, BaseMetrics::values.batteryWidth, BaseMetrics::values.batteryHeight}, showBatteryPercentage); + if (SETTINGS.clockFormat != CrossPointSettings::CLOCK_OFF) { + time_t now = time(nullptr); + struct tm* t = localtime(&now); + if (t != nullptr && t->tm_year > 100) { + char timeBuf[16]; + if (SETTINGS.clockFormat == CrossPointSettings::CLOCK_24H) { + snprintf(timeBuf, sizeof(timeBuf), "%02d:%02d", t->tm_hour, t->tm_min); + } else { + int hour12 = t->tm_hour % 12; + if (hour12 == 0) hour12 = 12; + snprintf(timeBuf, sizeof(timeBuf), "%d:%02d %s", hour12, t->tm_min, t->tm_hour >= 12 ? "PM" : "AM"); + } + int clockFont = SMALL_FONT_ID; + if (SETTINGS.clockSize == CrossPointSettings::CLOCK_SIZE_MEDIUM) + clockFont = UI_10_FONT_ID; + else if (SETTINGS.clockSize == CrossPointSettings::CLOCK_SIZE_LARGE) + clockFont = UI_12_FONT_ID; + renderer.drawText(clockFont, rect.x + 12, rect.y + 5, timeBuf, true); + } + } + if (title) { int padding = rect.width - batteryX + BaseMetrics::values.batteryWidth; auto truncatedTitle = renderer.truncatedText(UI_12_FONT_ID, title, diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index 36c19501..00e9d1cb 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -174,6 +175,27 @@ void LyraTheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* t Rect{batteryX, rect.y + 5, LyraMetrics::values.batteryWidth, LyraMetrics::values.batteryHeight}, showBatteryPercentage); + if (SETTINGS.clockFormat != CrossPointSettings::CLOCK_OFF) { + time_t now = time(nullptr); + struct tm* t = localtime(&now); + if (t != nullptr && t->tm_year > 100) { + char timeBuf[16]; + if (SETTINGS.clockFormat == CrossPointSettings::CLOCK_24H) { + snprintf(timeBuf, sizeof(timeBuf), "%02d:%02d", t->tm_hour, t->tm_min); + } else { + int hour12 = t->tm_hour % 12; + if (hour12 == 0) hour12 = 12; + snprintf(timeBuf, sizeof(timeBuf), "%d:%02d %s", hour12, t->tm_min, t->tm_hour >= 12 ? "PM" : "AM"); + } + int clockFont = SMALL_FONT_ID; + if (SETTINGS.clockSize == CrossPointSettings::CLOCK_SIZE_MEDIUM) + clockFont = UI_10_FONT_ID; + else if (SETTINGS.clockSize == CrossPointSettings::CLOCK_SIZE_LARGE) + clockFont = UI_12_FONT_ID; + renderer.drawText(clockFont, rect.x + 12, rect.y + 5, timeBuf, true); + } + } + int maxTitleWidth = rect.width - LyraMetrics::values.contentSidePadding * 2 - (subtitle != nullptr ? maxSubtitleWidth : 0);