This commit is contained in:
HarukiToreda 2025-04-06 16:41:06 -04:00
parent d5d20fe33f
commit cdbf0bec2d

View File

@ -1048,16 +1048,12 @@ void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y)
display->drawRect(batteryX, batteryY, batteryLong, batteryShort); display->drawRect(batteryX, batteryY, batteryLong, batteryShort);
// Nub // Nub
display->fillRect( display->fillRect(batteryX + batteryLong, batteryY + (batteryShort / 2) - 3, nubSize, 6);
batteryX + batteryLong,
batteryY + (batteryShort / 2) - 3,
nubSize, 6
);
if (isCharging && isBoltVisible) { if (isCharging && isBoltVisible) {
// Lightning bolt // Lightning bolt
const int boltX = batteryX + batteryLong / 2 - 4; const int boltX = batteryX + batteryLong / 2 - 4;
const int boltY = batteryY + 2; // Padding top const int boltY = batteryY + 2; // Padding top
// Top fat bar (same) // Top fat bar (same)
display->fillRect(boltX, boltY, 6, 2); display->fillRect(boltX, boltY, 6, 2);
@ -1143,9 +1139,8 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
const int screenHeight = display->getHeight();
const int navHeight = FONT_HEIGHT_SMALL; const int navHeight = FONT_HEIGHT_SMALL;
const int scrollBottom = screenHeight - navHeight; const int scrollBottom = SCREEN_HEIGHT - navHeight;
const int usableHeight = scrollBottom; const int usableHeight = scrollBottom;
const int textWidth = SCREEN_WIDTH; const int textWidth = SCREEN_WIDTH;
const int cornerRadius = 2; const int cornerRadius = 2;
@ -1166,22 +1161,28 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
std::string prefix = (daysAgo == 1 && SCREEN_WIDTH >= 200) ? "Yesterday" : "At"; std::string prefix = (daysAgo == 1 && SCREEN_WIDTH >= 200) ? "Yesterday" : "At";
std::string meridiem = "AM"; std::string meridiem = "AM";
if (config.display.use_12h_clock) { if (config.display.use_12h_clock) {
if (timestampHours >= 12) meridiem = "PM"; if (timestampHours >= 12)
if (timestampHours > 12) timestampHours -= 12; meridiem = "PM";
if (timestampHours == 0) timestampHours = 12; if (timestampHours > 12)
snprintf(headerStr, sizeof(headerStr), "%s %d:%02d%s from %s", prefix.c_str(), timestampHours, timestampMinutes, meridiem.c_str(), sender); timestampHours -= 12;
if (timestampHours == 0)
timestampHours = 12;
snprintf(headerStr, sizeof(headerStr), "%s %d:%02d%s from %s", prefix.c_str(), timestampHours, timestampMinutes,
meridiem.c_str(), sender);
} else { } else {
snprintf(headerStr, sizeof(headerStr), "%s %d:%02d from %s", prefix.c_str(), timestampHours, timestampMinutes, sender); snprintf(headerStr, sizeof(headerStr), "%s %d:%02d from %s", prefix.c_str(), timestampHours, timestampMinutes,
sender);
} }
} else { } else {
snprintf(headerStr, sizeof(headerStr), "%s ago from %s", screen->drawTimeDelta(days, hours, minutes, seconds).c_str(), sender); snprintf(headerStr, sizeof(headerStr), "%s ago from %s", screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
sender);
} }
#ifndef EXCLUDE_EMOJI #ifndef EXCLUDE_EMOJI
// === Bounce animation setup === // === Bounce animation setup ===
static uint32_t lastBounceTime = 0; static uint32_t lastBounceTime = 0;
static int bounceY = 0; static int bounceY = 0;
const int bounceRange = 2; // Max pixels to bounce up/down const int bounceRange = 2; // Max pixels to bounce up/down
const int bounceInterval = 60; // How quickly to change bounce direction (ms) const int bounceInterval = 60; // How quickly to change bounce direction (ms)
uint32_t now = millis(); uint32_t now = millis();
@ -1236,14 +1237,15 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
drawRoundedHighlight(display, x, 0, SCREEN_WIDTH, FONT_HEIGHT_SMALL - 1, cornerRadius); drawRoundedHighlight(display, x, 0, SCREEN_WIDTH, FONT_HEIGHT_SMALL - 1, cornerRadius);
display->setColor(BLACK); display->setColor(BLACK);
display->drawString(x + 3, 0, headerStr); display->drawString(x + 3, 0, headerStr);
if (isBold) display->drawString(x + 4, 0, headerStr); if (isBold)
display->drawString(x + 4, 0, headerStr);
display->setColor(WHITE); display->setColor(WHITE);
} else { } else {
display->drawString(x, 0, headerStr); display->drawString(x, 0, headerStr);
} }
// Center the emote below header + apply bounce // Center the emote below header + apply bounce
int remainingHeight = screenHeight - FONT_HEIGHT_SMALL - navHeight; int remainingHeight = SCREEN_HEIGHT - FONT_HEIGHT_SMALL - navHeight;
int emoteY = FONT_HEIGHT_SMALL + (remainingHeight - e.height) / 2 + bounceY - bounceRange; int emoteY = FONT_HEIGHT_SMALL + (remainingHeight - e.height) / 2 + bounceY - bounceRange;
display->drawXbm((SCREEN_WIDTH - e.width) / 2, emoteY, e.width, e.height, e.bitmap); display->drawXbm((SCREEN_WIDTH - e.width) / 2, emoteY, e.width, e.height, e.bitmap);
return; return;
@ -1256,14 +1258,16 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
snprintf(messageBuf, sizeof(messageBuf), "%s", msg); snprintf(messageBuf, sizeof(messageBuf), "%s", msg);
std::vector<std::string> lines; std::vector<std::string> lines;
lines.push_back(std::string(headerStr)); // Header line is always first lines.push_back(std::string(headerStr)); // Header line is always first
std::string line, word; std::string line, word;
for (int i = 0; messageBuf[i]; ++i) { for (int i = 0; messageBuf[i]; ++i) {
char ch = messageBuf[i]; char ch = messageBuf[i];
if (ch == '\n') { if (ch == '\n') {
if (!word.empty()) line += word; if (!word.empty())
if (!line.empty()) lines.push_back(line); line += word;
if (!line.empty())
lines.push_back(line);
line.clear(); line.clear();
word.clear(); word.clear();
} else if (ch == ' ') { } else if (ch == ' ') {
@ -1273,14 +1277,17 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
word += ch; word += ch;
std::string test = line + word; std::string test = line + word;
if (display->getStringWidth(test.c_str()) > textWidth + 4) { if (display->getStringWidth(test.c_str()) > textWidth + 4) {
if (!line.empty()) lines.push_back(line); if (!line.empty())
lines.push_back(line);
line = word; line = word;
word.clear(); word.clear();
} }
} }
} }
if (!word.empty()) line += word; if (!word.empty())
if (!line.empty()) lines.push_back(line); line += word;
if (!line.empty())
lines.push_back(line);
// === Scrolling logic === // === Scrolling logic ===
const float rowHeight = FONT_HEIGHT_SMALL - 1; const float rowHeight = FONT_HEIGHT_SMALL - 1;
@ -1290,7 +1297,7 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
static float scrollY = 0.0f; static float scrollY = 0.0f;
static uint32_t lastTime = 0, scrollStartDelay = 0, pauseStart = 0; static uint32_t lastTime = 0, scrollStartDelay = 0, pauseStart = 0;
static bool waitingToReset = false, scrollStarted = false; static bool waitingToReset = false, scrollStarted = false;
// === Smooth scrolling adjustment === // === Smooth scrolling adjustment ===
// You can tweak this divisor to change how smooth it scrolls. // You can tweak this divisor to change how smooth it scrolls.
// Lower = smoother, but can feel slow. // Lower = smoother, but can feel slow.
@ -1300,8 +1307,10 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
const float scrollSpeed = 2.0f; // pixels per second const float scrollSpeed = 2.0f; // pixels per second
// Delay scrolling start by 2 seconds // Delay scrolling start by 2 seconds
if (scrollStartDelay == 0) scrollStartDelay = now; if (scrollStartDelay == 0)
if (!scrollStarted && now - scrollStartDelay > 2000) scrollStarted = true; scrollStartDelay = now;
if (!scrollStarted && now - scrollStartDelay > 2000)
scrollStarted = true;
if (totalHeight > usableHeight) { if (totalHeight > usableHeight) {
if (scrollStarted) { if (scrollStarted) {
@ -1334,7 +1343,8 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
drawRoundedHighlight(display, x, lineY, SCREEN_WIDTH, FONT_HEIGHT_SMALL - 1, cornerRadius); drawRoundedHighlight(display, x, lineY, SCREEN_WIDTH, FONT_HEIGHT_SMALL - 1, cornerRadius);
display->setColor(BLACK); display->setColor(BLACK);
display->drawString(x + 3, lineY, lines[i].c_str()); display->drawString(x + 3, lineY, lines[i].c_str());
if (isBold) display->drawString(x + 4, lineY, lines[i].c_str()); if (isBold)
display->drawString(x + 4, lineY, lines[i].c_str());
display->setColor(WHITE); display->setColor(WHITE);
} else { } else {
display->drawString(x, lineY, lines[i].c_str()); display->drawString(x, lineY, lines[i].c_str());
@ -1717,7 +1727,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
} }
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(nodeIndex); meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(nodeIndex);
if (!node || !node->is_favorite || node->num == nodeDB->getNodeNum()) return; if (!node || !node->is_favorite || node->num == nodeDB->getNodeNum())
return;
// === Draw Title (centered safe short name or ID) === // === Draw Title (centered safe short name or ID) ===
static char titleBuf[20]; static char titleBuf[20];
@ -1786,7 +1797,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
const int16_t usableHeight = bottomY - topY - 5; const int16_t usableHeight = bottomY - topY - 5;
int16_t compassRadius = usableHeight / 2; int16_t compassRadius = usableHeight / 2;
if (compassRadius < 8) compassRadius = 8; if (compassRadius < 8)
compassRadius = 8;
const int16_t compassDiam = compassRadius * 2; const int16_t compassDiam = compassRadius * 2;
const int16_t compassX = x + SCREEN_WIDTH - compassRadius - 8; const int16_t compassX = x + SCREEN_WIDTH - compassRadius - 8;
const int16_t compassY = topY + (usableHeight / 2) + ((FONT_HEIGHT_SMALL - 1) / 2) + 2; const int16_t compassY = topY + (usableHeight / 2) + ((FONT_HEIGHT_SMALL - 1) / 2) + 2;
@ -1794,9 +1806,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
bool hasNodeHeading = false; bool hasNodeHeading = false;
if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading())) { if (ourNode && (nodeDB->hasValidPosition(ourNode) || screen->hasHeading())) {
const meshtastic_PositionLite &op = ourNode->position; const meshtastic_PositionLite &op = ourNode->position;
float myHeading = screen->hasHeading() float myHeading = screen->hasHeading() ? radians(screen->getHeading())
? radians(screen->getHeading()) : screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
: screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
screen->drawCompassNorth(display, compassX, compassY, myHeading); screen->drawCompassNorth(display, compassX, compassY, myHeading);
@ -1804,11 +1815,11 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
hasNodeHeading = true; hasNodeHeading = true;
const meshtastic_PositionLite &p = node->position; const meshtastic_PositionLite &p = node->position;
float d = GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), float d =
DegD(op.latitude_i), DegD(op.longitude_i)); GeoCoord::latLongToMeter(DegD(p.latitude_i), DegD(p.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
float bearingToOther = GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), float bearingToOther =
DegD(p.latitude_i), DegD(p.longitude_i)); GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(p.latitude_i), DegD(p.longitude_i));
if (!config.display.compass_north_top) if (!config.display.compass_north_top)
bearingToOther -= myHeading; bearingToOther -= myHeading;
@ -1822,7 +1833,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
if (d < (2 * MILES_TO_FEET)) if (d < (2 * MILES_TO_FEET))
snprintf(distStr, sizeof(distStr), "%.0fft %.0f°", d * METERS_TO_FEET, bearingToOtherDegrees); snprintf(distStr, sizeof(distStr), "%.0fft %.0f°", d * METERS_TO_FEET, bearingToOtherDegrees);
else else
snprintf(distStr, sizeof(distStr), "%.1fmi %.0f°", d * METERS_TO_FEET / MILES_TO_FEET, bearingToOtherDegrees); snprintf(distStr, sizeof(distStr), "%.1fmi %.0f°", d * METERS_TO_FEET / MILES_TO_FEET,
bearingToOtherDegrees);
} else { } else {
if (d < 2000) if (d < 2000)
snprintf(distStr, sizeof(distStr), "%.0fm %.0f°", d, bearingToOtherDegrees); snprintf(distStr, sizeof(distStr), "%.0fm %.0f°", d, bearingToOtherDegrees);
@ -1961,11 +1973,12 @@ void drawColumnSeparator(OLEDDisplay *display, int16_t x, int16_t yStart, int16_
} }
typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int); typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int);
typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int columnWidth, float heading, double lat, double lon); typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int columnWidth, float heading,
double lat, double lon);
void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *title,
const char *title, EntryRenderer renderer, NodeExtrasRenderer extras = nullptr, EntryRenderer renderer, NodeExtrasRenderer extras = nullptr, float heading = 0, double lat = 0,
float heading = 0, double lat = 0, double lon = 0) double lon = 0)
{ {
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1; const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
const int rowYOffset = FONT_HEIGHT_SMALL - 3; const int rowYOffset = FONT_HEIGHT_SMALL - 3;
@ -2124,7 +2137,7 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int
for (int b = 0; b < 4; b++) { for (int b = 0; b < 4; b++) {
if (b < bars) { if (b < bars) {
int height = (b * 2); int height = (b * 2);
display->fillRect(barStartX + (b * (barWidth + 1)), barStartY - height, barWidth, height); display->fillRect(barStartX + (b * (barWidth + 1)), barStartY - height, barWidth, height);
} }
} }
@ -2265,7 +2278,6 @@ void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16
display->fillTriangle(tipX, tipY, notchX, notchY, rightX, rightY); display->fillTriangle(tipX, tipY, notchX, notchY, rightX, rightY);
} }
// Public screen entry for compass // Public screen entry for compass
static void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
@ -2281,7 +2293,7 @@ static void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *
lon = geoCoord.getLongitude() * 1e-7; lon = geoCoord.getLongitude() * 1e-7;
if (screen->hasHeading()) { if (screen->hasHeading()) {
heading = screen->getHeading(); // degrees heading = screen->getHeading(); // degrees
validHeading = true; validHeading = true;
} else { } else {
heading = screen->estimatedHeading(lat, lon); heading = screen->estimatedHeading(lat, lon);
@ -2289,7 +2301,8 @@ static void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *
} }
#endif #endif
if (!validHeading) return; if (!validHeading)
return;
drawNodeListScreen(display, state, x, y, "Bearings", drawEntryCompass, drawCompassArrow, heading, lat, lon); drawNodeListScreen(display, state, x, y, "Bearings", drawEntryCompass, drawCompassArrow, heading, lat, lon);
} }
@ -2725,15 +2738,15 @@ static void drawMemoryScreen(OLEDDisplay *display, OLEDDisplayUiState *state, in
uint32_t sdUsed = 0, sdTotal = 0; uint32_t sdUsed = 0, sdTotal = 0;
bool hasSD = false; bool hasSD = false;
/* /*
#ifdef HAS_SDCARD #ifdef HAS_SDCARD
hasSD = SD.cardType() != CARD_NONE; hasSD = SD.cardType() != CARD_NONE;
if (hasSD) { if (hasSD) {
sdUsed = SD.usedBytes(); sdUsed = SD.usedBytes();
sdTotal = SD.totalBytes(); sdTotal = SD.totalBytes();
} }
#endif #endif
*/ */
// === Draw memory rows // === Draw memory rows
drawUsageRow("Heap:", heapUsed, heapTotal, true); drawUsageRow("Heap:", heapUsed, heapTotal, true);
drawUsageRow("PSRAM:", psramUsed, psramTotal); drawUsageRow("PSRAM:", psramUsed, psramTotal);
@ -3385,22 +3398,22 @@ void Screen::setFrames(FrameFocus focus)
normalFrames[numframes++] = drawLoRaFocused; normalFrames[numframes++] = drawLoRaFocused;
normalFrames[numframes++] = drawMemoryScreen; normalFrames[numframes++] = drawMemoryScreen;
// then all the nodes // then all the nodes
// We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens // We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens
//size_t numToShow = min(numMeshNodes, 4U); // size_t numToShow = min(numMeshNodes, 4U);
//for (size_t i = 0; i < numToShow; i++) // for (size_t i = 0; i < numToShow; i++)
//normalFrames[numframes++] = drawNodeInfo; // normalFrames[numframes++] = drawNodeInfo;
// then the debug info // then the debug info
// Since frames are basic function pointers, we have to use a helper to // Since frames are basic function pointers, we have to use a helper to
// call a method on debugInfo object. // call a method on debugInfo object.
//fsi.positions.log = numframes; // fsi.positions.log = numframes;
//normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline; // normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline;
// call a method on debugInfoScreen object (for more details) // call a method on debugInfoScreen object (for more details)
//fsi.positions.settings = numframes; // fsi.positions.settings = numframes;
//normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline; // normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
fsi.positions.wifi = numframes; fsi.positions.wifi = numframes;
#if HAS_WIFI && !defined(ARCH_PORTDUINO) #if HAS_WIFI && !defined(ARCH_PORTDUINO)