fix: align battery icon based on context (UI / Reader) (#796)
Issues solved: #729 and #739 ## Summary * **What is the goal of this PR?** Currently, the battery icon and charge percentage were aligned to the left even for the UI, where they were positioned on the right side of the screen. This meant that when changing values of different numbers of digits, the battery would shift, creating a block of icons and text that was illegible. * **What changes are included?** - Add drawBatteryUi() method for right-aligned battery display in UI headers - Keep drawBattery() for left-aligned display in reader mode - Extract drawBatteryIcon() helper to reduce code duplication - Battery icon now stays fixed at right edge regardless of percentage digits - Text adjusts to left of icon in UI mode, to right of icon in reader mode ## Additional Context * Add any other information that might be helpful for the reviewer * This fix applies to both themes (Base and Lyra). --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**< YES >**_
This commit is contained in:
@@ -741,8 +741,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) {
|
||||
|
||||
@@ -509,8 +509,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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<TabInfo>& tabs,
|
||||
bool selected) const override;
|
||||
|
||||
Reference in New Issue
Block a user