## Summary * **What is the goal of this PR?** All praise goes to @didacta for his PR #537. Just picked up the reviewer comments to contain the changes as suggested (there was no response for more than 6 weeks, so I wanted to reanimate this feature). Just one addition: should recognize usb cable plug ins / retractions and update the icon immediately * **What changes are included?** ## Additional Context see #537 --- ### 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? _**< NO >**_
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
#include <HalStorage.h>
|
||||
#include <Logging.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
@@ -34,13 +35,40 @@ void drawBatteryIcon(const GfxRenderer& renderer, int x, int y, int battWidth, i
|
||||
renderer.drawPixel(x + battWidth - 1, y + rectHeight - 4);
|
||||
renderer.drawLine(x + battWidth - 0, y + 4, x + battWidth - 0, y + rectHeight - 5);
|
||||
|
||||
const bool charging = gpio.isUsbConnected();
|
||||
|
||||
// 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
|
||||
const int maxFillWidth = battWidth - 5;
|
||||
const int fillHeight = rectHeight - 4;
|
||||
if (maxFillWidth <= 0 || fillHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
int filledWidth = percentage * maxFillWidth / 100 + 1;
|
||||
if (filledWidth > maxFillWidth) {
|
||||
filledWidth = maxFillWidth;
|
||||
}
|
||||
|
||||
renderer.fillRect(x + 2, y + 2, filledWidth, rectHeight - 4);
|
||||
// When charging, ensure minimum fill so lightning bolt is fully visible
|
||||
constexpr int minFillForBolt = 8;
|
||||
if (charging && filledWidth < minFillForBolt) {
|
||||
filledWidth = std::min(minFillForBolt, maxFillWidth);
|
||||
}
|
||||
|
||||
renderer.fillRect(x + 2, y + 2, filledWidth, fillHeight);
|
||||
|
||||
// Draw lightning bolt when charging (white/inverted on black fill for visibility)
|
||||
if (charging) {
|
||||
const int boltX = x + 4;
|
||||
const int boltY = y + 2;
|
||||
renderer.drawLine(boltX + 4, boltY + 0, boltX + 5, boltY + 0, false);
|
||||
renderer.drawLine(boltX + 3, boltY + 1, boltX + 4, boltY + 1, false);
|
||||
renderer.drawLine(boltX + 2, boltY + 2, boltX + 5, boltY + 2, false);
|
||||
renderer.drawLine(boltX + 3, boltY + 3, boltX + 4, boltY + 3, false);
|
||||
renderer.drawLine(boltX + 2, boltY + 4, boltX + 3, boltY + 4, false);
|
||||
renderer.drawLine(boltX + 1, boltY + 5, boltX + 4, boltY + 5, false);
|
||||
renderer.drawLine(boltX + 2, boltY + 6, boltX + 3, boltY + 6, false);
|
||||
renderer.drawLine(boltX + 1, boltY + 7, boltX + 2, boltY + 7, false);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "LyraTheme.h"
|
||||
|
||||
#include <GfxRenderer.h>
|
||||
#include <HalGPIO.h>
|
||||
#include <HalPowerManager.h>
|
||||
#include <HalStorage.h>
|
||||
#include <I18n.h>
|
||||
@@ -42,6 +43,47 @@ constexpr int listIconSize = 24;
|
||||
constexpr int mainMenuColumns = 2;
|
||||
int coverWidth = 0;
|
||||
|
||||
void drawLyraBatteryIcon(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);
|
||||
|
||||
const bool charging = gpio.isUsbConnected();
|
||||
|
||||
// Draw bars
|
||||
if (percentage > 10 || charging) {
|
||||
renderer.fillRect(x + 2, y + 2, 3, rectHeight - 4);
|
||||
}
|
||||
if (percentage > 40 || charging) {
|
||||
renderer.fillRect(x + 6, y + 2, 3, rectHeight - 4);
|
||||
}
|
||||
if (percentage > 70) {
|
||||
renderer.fillRect(x + 10, y + 2, 3, rectHeight - 4);
|
||||
}
|
||||
|
||||
if (charging) {
|
||||
const int boltX = x + 4;
|
||||
const int boltY = y + 2;
|
||||
renderer.drawLine(boltX + 4, boltY + 0, boltX + 5, boltY + 0, false);
|
||||
renderer.drawLine(boltX + 3, boltY + 1, boltX + 4, boltY + 1, false);
|
||||
renderer.drawLine(boltX + 2, boltY + 2, boltX + 5, boltY + 2, false);
|
||||
renderer.drawLine(boltX + 3, boltY + 3, boltX + 4, boltY + 3, false);
|
||||
renderer.drawLine(boltX + 2, boltY + 4, boltX + 3, boltY + 4, false);
|
||||
renderer.drawLine(boltX + 1, boltY + 5, boltX + 4, boltY + 5, false);
|
||||
renderer.drawLine(boltX + 2, boltY + 6, boltX + 3, boltY + 6, false);
|
||||
renderer.drawLine(boltX + 1, boltY + 7, boltX + 2, boltY + 7, false);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t* iconForName(UIIcon icon, int size) {
|
||||
if (size == 24) {
|
||||
switch (icon) {
|
||||
@@ -87,45 +129,19 @@ const uint8_t* iconForName(UIIcon icon, int size) {
|
||||
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 = powerManager.getBatteryPercentage();
|
||||
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());
|
||||
renderer.drawText(SMALL_FONT_ID, rect.x + batteryPercentSpacing + LyraMetrics::values.batteryWidth, 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);
|
||||
}
|
||||
drawLyraBatteryIcon(renderer, rect.x, rect.y + 6, LyraMetrics::values.batteryWidth, rect.height, percentage);
|
||||
}
|
||||
|
||||
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 = powerManager.getBatteryPercentage();
|
||||
const int y = rect.y + 6;
|
||||
const int battWidth = LyraMetrics::values.batteryWidth;
|
||||
|
||||
if (showPercentage) {
|
||||
const auto percentageText = std::to_string(percentage) + "%";
|
||||
@@ -137,30 +153,7 @@ void LyraTheme::drawBatteryRight(const GfxRenderer& renderer, Rect rect, const b
|
||||
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
|
||||
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);
|
||||
}
|
||||
drawLyraBatteryIcon(renderer, rect.x, rect.y + 6, LyraMetrics::values.batteryWidth, rect.height, percentage);
|
||||
}
|
||||
|
||||
void LyraTheme::drawHeader(const GfxRenderer& renderer, Rect rect, const char* title, const char* subtitle) const {
|
||||
|
||||
@@ -379,6 +379,12 @@ void loop() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh the battery icon when USB is plugged or unplugged.
|
||||
// Placed after sleep guards so we never queue a render that won't be processed.
|
||||
if (gpio.wasUsbStateChanged()) {
|
||||
activityManager.requestUpdate();
|
||||
}
|
||||
|
||||
const unsigned long activityStartTime = millis();
|
||||
activityManager.loop();
|
||||
const unsigned long activityDuration = millis() - activityStartTime;
|
||||
|
||||
Reference in New Issue
Block a user