diff --git a/src/activities/reader/EpubReaderActivity.cpp b/src/activities/reader/EpubReaderActivity.cpp index 672300dd..f867dc80 100644 --- a/src/activities/reader/EpubReaderActivity.cpp +++ b/src/activities/reader/EpubReaderActivity.cpp @@ -1108,8 +1108,8 @@ void EpubReaderActivity::renderStatusBar(const int orientedMarginRight, const in } if (showBattery) { - GUI.drawBattery(renderer, Rect{orientedMarginLeft + 1, textY, metrics.batteryWidth, metrics.batteryHeight}, - showBatteryPercentage); + GUI.drawBatteryLeft(renderer, Rect{orientedMarginLeft + 1, textY, metrics.batteryWidth, metrics.batteryHeight}, + showBatteryPercentage); } if (showChapterTitle) { diff --git a/src/activities/reader/TxtReaderActivity.cpp b/src/activities/reader/TxtReaderActivity.cpp index d3accfa5..c3586b98 100644 --- a/src/activities/reader/TxtReaderActivity.cpp +++ b/src/activities/reader/TxtReaderActivity.cpp @@ -550,8 +550,8 @@ void TxtReaderActivity::renderStatusBar(const int orientedMarginRight, const int } if (showBattery) { - GUI.drawBattery(renderer, Rect{orientedMarginLeft, textY, metrics.batteryWidth, metrics.batteryHeight}, - showBatteryPercentage); + GUI.drawBatteryLeft(renderer, Rect{orientedMarginLeft, textY, metrics.batteryWidth, metrics.batteryHeight}, + showBatteryPercentage); } if (showTitle) { diff --git a/src/components/themes/BaseTheme.cpp b/src/components/themes/BaseTheme.cpp index bc7c7669..ade43ef0 100644 --- a/src/components/themes/BaseTheme.cpp +++ b/src/components/themes/BaseTheme.cpp @@ -19,41 +19,63 @@ namespace { constexpr int batteryPercentSpacing = 4; constexpr int homeMenuMargin = 20; constexpr int homeMarginTop = 30; + +// Helper: draw battery icon at given position +void drawBatteryIcon(const GfxRenderer& renderer, int x, int y, int battWidth, int rectHeight, uint16_t percentage) { + // Top line + renderer.drawLine(x + 1, y, x + battWidth - 3, y); + // Bottom line + renderer.drawLine(x + 1, y + rectHeight - 1, x + battWidth - 3, y + rectHeight - 1); + // Left line + renderer.drawLine(x, y + 1, x, y + rectHeight - 2); + // Battery end + renderer.drawLine(x + battWidth - 2, y + 1, x + battWidth - 2, y + rectHeight - 2); + renderer.drawPixel(x + battWidth - 1, y + 3); + renderer.drawPixel(x + battWidth - 1, y + rectHeight - 4); + renderer.drawLine(x + battWidth - 0, y + 4, x + battWidth - 0, y + rectHeight - 5); + + // The +1 is to round up, so that we always fill at least one pixel + int filledWidth = percentage * (battWidth - 5) / 100 + 1; + if (filledWidth > battWidth - 5) { + filledWidth = battWidth - 5; // Ensure we don't overflow + } + + renderer.fillRect(x + 2, y + 2, filledWidth, rectHeight - 4); +} } // namespace -void BaseTheme::drawBattery(const GfxRenderer& renderer, Rect rect, const bool showPercentage) const { - // Left aligned battery icon and percentage - // TODO refactor this so the percentage doesnt change after we position it +void BaseTheme::drawBatteryLeft(const GfxRenderer& renderer, Rect rect, const bool showPercentage) const { + // Left aligned: icon on left, percentage on right (reader mode) const uint16_t percentage = battery.readPercentage(); + const int y = rect.y + 6; + if (showPercentage) { const auto percentageText = std::to_string(percentage) + "%"; renderer.drawText(SMALL_FONT_ID, rect.x + batteryPercentSpacing + BaseMetrics::values.batteryWidth, rect.y, percentageText.c_str()); } - // 1 column on left, 2 columns on right, 5 columns of battery body - const int x = rect.x; + + drawBatteryIcon(renderer, rect.x, y, BaseMetrics::values.batteryWidth, rect.height, percentage); +} + +void BaseTheme::drawBatteryRight(const GfxRenderer& renderer, Rect rect, const bool showPercentage) const { + // Right aligned: percentage on left, icon on right (UI headers) + // rect.x is already positioned for the icon (drawHeader calculated it) + const uint16_t percentage = battery.readPercentage(); const int y = rect.y + 6; - const int battWidth = BaseMetrics::values.batteryWidth; - // Top line - renderer.drawLine(x + 1, y, x + battWidth - 3, y); - // Bottom line - renderer.drawLine(x + 1, y + rect.height - 1, x + battWidth - 3, y + rect.height - 1); - // Left line - renderer.drawLine(x, y + 1, x, y + rect.height - 2); - // Battery end - renderer.drawLine(x + battWidth - 2, y + 1, x + battWidth - 2, y + rect.height - 2); - renderer.drawPixel(x + battWidth - 1, y + 3); - renderer.drawPixel(x + battWidth - 1, y + rect.height - 4); - renderer.drawLine(x + battWidth - 0, y + 4, x + battWidth - 0, y + rect.height - 5); - - // The +1 is to round up, so that we always fill at least one pixel - int filledWidth = percentage * (rect.width - 5) / 100 + 1; - if (filledWidth > rect.width - 5) { - filledWidth = rect.width - 5; // Ensure we don't overflow + if (showPercentage) { + const auto percentageText = std::to_string(percentage) + "%"; + const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str()); + // Clear the area where we're going to draw the text to prevent ghosting + const auto textHeight = renderer.getTextHeight(SMALL_FONT_ID); + renderer.fillRect(rect.x - textWidth - batteryPercentSpacing, rect.y, textWidth, textHeight, false); + // Draw text to the left of the icon + renderer.drawText(SMALL_FONT_ID, rect.x - textWidth - batteryPercentSpacing, rect.y, percentageText.c_str()); } - renderer.fillRect(x + 2, y + 2, filledWidth, rect.height - 4); + // Icon is already at correct position from rect.x + drawBatteryIcon(renderer, rect.x, y, BaseMetrics::values.batteryWidth, rect.height, percentage); } void BaseTheme::drawProgressBar(const GfxRenderer& renderer, Rect rect, const size_t current, @@ -232,17 +254,14 @@ void BaseTheme::drawList(const GfxRenderer& renderer, Rect rect, int itemCount, void BaseTheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* title) const { const bool showBatteryPercentage = SETTINGS.hideBatteryPercentage != CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_ALWAYS; - int batteryX = rect.x + rect.width - BaseMetrics::values.contentSidePadding - BaseMetrics::values.batteryWidth; - if (showBatteryPercentage) { - const uint16_t percentage = battery.readPercentage(); - const auto percentageText = std::to_string(percentage) + "%"; - batteryX -= renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str()); - } - drawBattery(renderer, Rect{batteryX, rect.y + 5, BaseMetrics::values.batteryWidth, BaseMetrics::values.batteryHeight}, - showBatteryPercentage); + // Position icon at right edge, drawBatteryRight will place text to the left + const int batteryX = rect.x + rect.width - 12 - BaseMetrics::values.batteryWidth; + drawBatteryRight(renderer, + Rect{batteryX, rect.y + 5, BaseMetrics::values.batteryWidth, BaseMetrics::values.batteryHeight}, + showBatteryPercentage); if (title) { - int padding = rect.width - batteryX; + int padding = rect.width - batteryX + BaseMetrics::values.batteryWidth; auto truncatedTitle = renderer.truncatedText(UI_12_FONT_ID, title, rect.width - padding * 2 - BaseMetrics::values.contentSidePadding * 2, EpdFontFamily::BOLD); diff --git a/src/components/themes/BaseTheme.h b/src/components/themes/BaseTheme.h index 038a2913..0abd1f28 100644 --- a/src/components/themes/BaseTheme.h +++ b/src/components/themes/BaseTheme.h @@ -93,7 +93,10 @@ class BaseTheme { // Component drawing methods virtual void drawProgressBar(const GfxRenderer& renderer, Rect rect, size_t current, size_t total) const; - virtual void drawBattery(const GfxRenderer& renderer, Rect rect, bool showPercentage = true) const; + virtual void drawBatteryLeft(const GfxRenderer& renderer, Rect rect, + bool showPercentage = true) const; // Left aligned (reader mode) + virtual void drawBatteryRight(const GfxRenderer& renderer, Rect rect, + bool showPercentage = true) const; // Right aligned (UI headers) virtual void drawButtonHints(GfxRenderer& renderer, const char* btn1, const char* btn2, const char* btn3, const char* btn4) const; virtual void drawSideButtonHints(const GfxRenderer& renderer, const char* topBtn, const char* bottomBtn) const; diff --git a/src/components/themes/lyra/LyraTheme.cpp b/src/components/themes/lyra/LyraTheme.cpp index 73b56874..16df6a8d 100644 --- a/src/components/themes/lyra/LyraTheme.cpp +++ b/src/components/themes/lyra/LyraTheme.cpp @@ -20,19 +20,61 @@ constexpr int cornerRadius = 6; constexpr int topHintButtonY = 345; } // namespace -void LyraTheme::drawBattery(const GfxRenderer& renderer, Rect rect, const bool showPercentage) const { - // Left aligned battery icon and percentage +void LyraTheme::drawBatteryLeft(const GfxRenderer& renderer, Rect rect, const bool showPercentage) const { + // Left aligned: icon on left, percentage on right (reader mode) const uint16_t percentage = battery.readPercentage(); - if (showPercentage) { - const auto percentageText = std::to_string(percentage) + "%"; - renderer.drawText(SMALL_FONT_ID, rect.x + batteryPercentSpacing + LyraMetrics::values.batteryWidth, rect.y, - percentageText.c_str()); - } - // 1 column on left, 2 columns on right, 5 columns of battery body - const int x = rect.x; const int y = rect.y + 6; const int battWidth = LyraMetrics::values.batteryWidth; + if (showPercentage) { + const auto percentageText = std::to_string(percentage) + "%"; + renderer.drawText(SMALL_FONT_ID, rect.x + batteryPercentSpacing + battWidth, rect.y, percentageText.c_str()); + } + + // Draw icon + const int x = rect.x; + // Top line + renderer.drawLine(x + 1, y, x + battWidth - 3, y); + // Bottom line + renderer.drawLine(x + 1, y + rect.height - 1, x + battWidth - 3, y + rect.height - 1); + // Left line + renderer.drawLine(x, y + 1, x, y + rect.height - 2); + // Battery end + renderer.drawLine(x + battWidth - 2, y + 1, x + battWidth - 2, y + rect.height - 2); + renderer.drawPixel(x + battWidth - 1, y + 3); + renderer.drawPixel(x + battWidth - 1, y + rect.height - 4); + renderer.drawLine(x + battWidth - 0, y + 4, x + battWidth - 0, y + rect.height - 5); + + // Draw bars + if (percentage > 10) { + renderer.fillRect(x + 2, y + 2, 3, rect.height - 4); + } + if (percentage > 40) { + renderer.fillRect(x + 6, y + 2, 3, rect.height - 4); + } + if (percentage > 70) { + renderer.fillRect(x + 10, y + 2, 3, rect.height - 4); + } +} + +void LyraTheme::drawBatteryRight(const GfxRenderer& renderer, Rect rect, const bool showPercentage) const { + // Right aligned: percentage on left, icon on right (UI headers) + const uint16_t percentage = battery.readPercentage(); + const int y = rect.y + 6; + const int battWidth = LyraMetrics::values.batteryWidth; + + if (showPercentage) { + const auto percentageText = std::to_string(percentage) + "%"; + const int textWidth = renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str()); + // Clear the area where we're going to draw the text to prevent ghosting + const auto textHeight = renderer.getTextHeight(SMALL_FONT_ID); + renderer.fillRect(rect.x - textWidth - batteryPercentSpacing, rect.y, textWidth, textHeight, false); + // Draw text to the left of the icon + renderer.drawText(SMALL_FONT_ID, rect.x - textWidth - batteryPercentSpacing, rect.y, percentageText.c_str()); + } + + // Draw icon at rect.x + const int x = rect.x; // Top line renderer.drawLine(x + 1, y, x + battWidth - 3, y); // Bottom line @@ -62,15 +104,11 @@ void LyraTheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* t const bool showBatteryPercentage = SETTINGS.hideBatteryPercentage != CrossPointSettings::HIDE_BATTERY_PERCENTAGE::HIDE_ALWAYS; - int batteryX = rect.x + rect.width - LyraMetrics::values.contentSidePadding - LyraMetrics::values.batteryWidth; - if (showBatteryPercentage) { - const uint16_t percentage = battery.readPercentage(); - const auto percentageText = std::to_string(percentage) + "%"; - batteryX -= renderer.getTextWidth(SMALL_FONT_ID, percentageText.c_str()); - } - drawBattery(renderer, - Rect{batteryX, rect.y + 10, LyraMetrics::values.batteryWidth, LyraMetrics::values.batteryHeight}, - showBatteryPercentage); + // Position icon at right edge, drawBatteryRight will place text to the left + const int batteryX = rect.x + rect.width - 12 - LyraMetrics::values.batteryWidth; + drawBatteryRight(renderer, + Rect{batteryX, rect.y + 5, LyraMetrics::values.batteryWidth, LyraMetrics::values.batteryHeight}, + showBatteryPercentage); if (title) { auto truncatedTitle = renderer.truncatedText( diff --git a/src/components/themes/lyra/LyraTheme.h b/src/components/themes/lyra/LyraTheme.h index 93ec0579..0a76471a 100644 --- a/src/components/themes/lyra/LyraTheme.h +++ b/src/components/themes/lyra/LyraTheme.h @@ -36,7 +36,8 @@ class LyraTheme : public BaseTheme { public: // Component drawing methods // void drawProgressBar(const GfxRenderer& renderer, Rect rect, size_t current, size_t total) override; - void drawBattery(const GfxRenderer& renderer, Rect rect, bool showPercentage = true) const override; + void drawBatteryLeft(const GfxRenderer& renderer, Rect rect, bool showPercentage = true) const override; + void drawBatteryRight(const GfxRenderer& renderer, Rect rect, bool showPercentage = true) const override; void drawHeader(const GfxRenderer& renderer, Rect rect, const char* title) const override; void drawTabBar(const GfxRenderer& renderer, Rect rect, const std::vector& tabs, bool selected) const override;