diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 1e03f0868..7f167f9a9 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -90,6 +90,7 @@ class HomeScreen : public UIScreen { bool _shutdown_init; AdvertPath recent[UI_RECENT_LIST_SIZE]; + void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts) { // Convert millivolts to percentage const int minMilliVolts = 3000; // Minimum voltage (e.g., 3.0V) @@ -157,10 +158,12 @@ class HomeScreen : public UIScreen { int render(DisplayDriver& display) override { char tmp[80]; // node name - display.setCursor(0, 0); display.setTextSize(1); display.setColor(DisplayDriver::GREEN); - display.print(_node_prefs->node_name); + char filtered_name[sizeof(_node_prefs->node_name)]; + display.translateUTF8ToBlocks(filtered_name, _node_prefs->node_name, sizeof(filtered_name)); + display.setCursor(0, 0); + display.print(filtered_name); // battery voltage renderBatteryIndicator(display, _task->getBattMilliVolts()); @@ -199,8 +202,6 @@ class HomeScreen : public UIScreen { for (int i = 0; i < UI_RECENT_LIST_SIZE; i++, y += 11) { auto a = &recent[i]; if (a->name[0] == 0) continue; // empty slot - display.setCursor(0, y); - display.print(a->name); int secs = _rtc->getCurrentTime() - a->recv_timestamp; if (secs < 60) { sprintf(tmp, "%ds", secs); @@ -209,7 +210,14 @@ class HomeScreen : public UIScreen { } else { sprintf(tmp, "%dh", secs / (60*60)); } - display.setCursor(display.width() - display.getTextWidth(tmp) - 1, y); + + int timestamp_width = display.getTextWidth(tmp); + int max_name_width = display.width() - timestamp_width - 1; + + char filtered_recent_name[sizeof(a->name)]; + display.translateUTF8ToBlocks(filtered_recent_name, a->name, sizeof(filtered_recent_name)); + display.drawTextEllipsized(0, y, max_name_width, filtered_recent_name); + display.setCursor(display.width() - timestamp_width - 1, y); display.print(tmp); } } else if (_page == HomePage::RADIO) { @@ -427,11 +435,15 @@ class MsgPreviewScreen : public UIScreen { display.setCursor(0, 14); display.setColor(DisplayDriver::YELLOW); - display.print(p->origin); + char filtered_origin[sizeof(p->origin)]; + display.translateUTF8ToBlocks(filtered_origin, p->origin, sizeof(filtered_origin)); + display.print(filtered_origin); display.setCursor(0, 25); display.setColor(DisplayDriver::LIGHT); - display.printWordWrap(p->msg, display.width()); + char filtered_msg[sizeof(p->msg)]; + display.translateUTF8ToBlocks(filtered_msg, p->msg, sizeof(filtered_msg)); + display.printWordWrap(filtered_msg, display.width()); #if AUTO_OFF_MILLIS==0 // probably e-ink return 10000; // 10 s diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h index d81d99fbb..32839edc2 100644 --- a/src/helpers/ui/DisplayDriver.h +++ b/src/helpers/ui/DisplayDriver.h @@ -1,6 +1,7 @@ #pragma once #include +#include class DisplayDriver { int _w, _h; @@ -31,5 +32,60 @@ class DisplayDriver { setCursor(mid_x - w/2, y); print(str); } + + // convert UTF-8 characters to displayable block characters for compatibility + virtual void translateUTF8ToBlocks(char* dest, const char* src, size_t dest_size) { + size_t j = 0; + for (size_t i = 0; src[i] != 0 && j < dest_size - 1; i++) { + unsigned char c = (unsigned char)src[i]; + if (c >= 32 && c <= 126) { + dest[j++] = c; // ASCII printable + } else if (c >= 0x80) { + dest[j++] = '\xDB'; // CP437 full block █ + while (src[i+1] && (src[i+1] & 0xC0) == 0x80) + i++; // skip UTF-8 continuation bytes + } + } + dest[j] = 0; + } + + // draw text with ellipsis if it exceeds max_width + virtual void drawTextEllipsized(int x, int y, int max_width, const char* str) { + char temp_str[256]; // reasonable buffer size + size_t len = strlen(str); + if (len >= sizeof(temp_str)) len = sizeof(temp_str) - 1; + memcpy(temp_str, str, len); + temp_str[len] = 0; + + if (getTextWidth(temp_str) <= max_width) { + setCursor(x, y); + print(temp_str); + return; + } + + // for variable-width fonts (GxEPD), add space after ellipsis + // for fixed-width fonts (OLED), keep tight spacing to save precious characters + const char* ellipsis; + // use a simple heuristic: if 'i' and 'l' have different widths, it's variable-width + int i_width = getTextWidth("i"); + int l_width = getTextWidth("l"); + if (i_width != l_width) { + ellipsis = "... "; // variable-width fonts: add space + } else { + ellipsis = "..."; // fixed-width fonts: no space + } + + int ellipsis_width = getTextWidth(ellipsis); + int str_len = strlen(temp_str); + + while (str_len > 0 && getTextWidth(temp_str) > max_width - ellipsis_width) { + temp_str[--str_len] = 0; + } + strcat(temp_str, ellipsis); + + setCursor(x, y); + print(temp_str); + } + virtual void endFrame() = 0; };