diff --git a/examples/companion_radio/ui-new/QuickMsgScreen.h b/examples/companion_radio/ui-new/QuickMsgScreen.h index dfc19656..575f5953 100644 --- a/examples/companion_radio/ui-new/QuickMsgScreen.h +++ b/examples/companion_radio/ui-new/QuickMsgScreen.h @@ -87,8 +87,9 @@ class QuickMsgScreen : public UIScreen { static const char* skipReplyPrefix(const char* text) { if (text[0] == '@' && text[1] == '[') { const char* close = strchr(text + 2, ']'); - if (close && close[1] == ' ' && close[2]) return close + 2; + if (close && close[1] == ' ' && close[2]) text = close + 2; } + while (*text == '\n' || *text == '\r' || *text == ' ') text++; return text; } @@ -630,13 +631,12 @@ public: return 500; } - int hist_box_h = 2 * lh + 3; - int hist_item_h = hist_box_h + 2; + int hist_box_h = 2 * lh + 1; + int hist_item_h = hist_box_h + 1; int hist_start_y = display.headerH(); - int btn_h = lh + 4; - _hist_visible = (display.height() - hist_start_y - btn_h) / hist_item_h; - if (_hist_visible < 1) _hist_visible = 1; int cby = display.height() - lh - 2; + _hist_visible = (cby - 1 - hist_start_y) / hist_item_h; + if (_hist_visible < 1) _hist_visible = 1; char title[24]; display.setColor(DisplayDriver::LIGHT); @@ -666,7 +666,7 @@ public: display.setColor(DisplayDriver::DARK); display.drawTextEllipsized(3, y + 1, display.width() - cw - 2 - age_w, sender); if (age[0]) { display.setCursor(display.width() - 3 - display.getTextWidth(age), y + 1); display.print(age); } - display.drawTextEllipsized(3, y + lh + 2, display.width() - cw - 2, skipReplyPrefix(e.text)); + display.drawTextEllipsized(3, y + lh + 1, display.width() - cw - 2, skipReplyPrefix(e.text)); } else { display.setColor(DisplayDriver::LIGHT); display.drawRect(0, y, display.width(), hist_box_h); @@ -675,7 +675,7 @@ public: display.drawTextEllipsized(3, y + 1, display.width() - cw - 2 - age_w, sender); if (age[0]) { display.setCursor(display.width() - 3 - display.getTextWidth(age), y + 1); display.print(age); } display.setColor(DisplayDriver::LIGHT); - display.drawTextEllipsized(3, y + lh + 2, display.width() - cw - 2, skipReplyPrefix(e.text)); + display.drawTextEllipsized(3, y + lh + 1, display.width() - cw - 2, skipReplyPrefix(e.text)); } } @@ -733,13 +733,12 @@ public: return 2000; } - int hist_box_h = 2 * lh + 3; - int hist_item_h = hist_box_h + 2; + int hist_box_h = 2 * lh + 1; + int hist_item_h = hist_box_h + 1; int hist_start_y = display.headerH(); - int btn_h = lh + 4; - _hist_visible = (display.height() - hist_start_y - btn_h) / hist_item_h; - if (_hist_visible < 1) _hist_visible = 1; int cby = display.height() - lh - 2; + _hist_visible = (cby - 1 - hist_start_y) / hist_item_h; + if (_hist_visible < 1) _hist_visible = 1; ChannelDetails ch; the_mesh.getChannel(_sel_channel_idx, ch); @@ -781,7 +780,7 @@ public: display.setColor(DisplayDriver::DARK); display.drawTextEllipsized(3, y + 1, display.width() - cw - 2 - age_w, sender); if (age[0]) { display.setCursor(display.width() - 3 - display.getTextWidth(age), y + 1); display.print(age); } - display.drawTextEllipsized(3, y + lh + 2, display.width() - cw - 2, skipReplyPrefix(msg_part)); + display.drawTextEllipsized(3, y + lh + 1, display.width() - cw - 2, skipReplyPrefix(msg_part)); } else { display.setColor(DisplayDriver::LIGHT); display.drawRect(0, y, display.width(), hist_box_h); @@ -790,7 +789,7 @@ public: display.drawTextEllipsized(3, y + 1, display.width() - cw - 2 - age_w, sender); if (age[0]) { display.setCursor(display.width() - 3 - display.getTextWidth(age), y + 1); display.print(age); } display.setColor(DisplayDriver::LIGHT); - display.drawTextEllipsized(3, y + lh + 2, display.width() - cw - 2, skipReplyPrefix(msg_part)); + display.drawTextEllipsized(3, y + lh + 1, display.width() - cw - 2, skipReplyPrefix(msg_part)); } } diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 0203f572..2bef598a 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -291,7 +291,8 @@ class HomeScreen : public UIScreen { const int lh = display.getLineHeight(); const int cw = display.getCharWidth(); const int ind = cw + 2; // single-char indicator width - const int ind_gap = (lh >= 16) ? 3 : 1; // gap between indicator boxes + const int ind_h = display.isLemonFont() ? lh - 2 : lh; + const int ind_gap = display.isLandscape() ? 3 : 1; // gap between indicator boxes int battLeftX; if (mode == 1) { // percent @@ -309,7 +310,7 @@ class HomeScreen : public UIScreen { } else { // icon — scales with lh const int iconH = lh; const int iconW = lh * 2; - const int bm = (lh >= 16) ? 3 : 2; // inner margin: 3px on landscape (2px border), 2px on OLED + const int bm = display.isLandscape() ? 3 : 2; // inner margin: 3px on landscape e-ink, 2px on OLED/portrait battLeftX = display.width() - iconW - 3; display.drawRect(battLeftX, 0, iconW, iconH); display.fillRect(battLeftX + iconW, iconH / 4, 2, iconH / 2); @@ -321,7 +322,7 @@ class HomeScreen : public UIScreen { if (_task->isBuzzerQuiet()) { display.setColor(DisplayDriver::LIGHT); int mx = battLeftX - ind - ind_gap; - display.fillRect(mx, 0, ind, lh); + display.fillRect(mx, 0, ind, ind_h); display.setColor(DisplayDriver::DARK); display.setCursor(mx + 1, 0); display.print("M"); @@ -336,7 +337,7 @@ class HomeScreen : public UIScreen { int btX = battLeftX - ind - ind_gap; if (_task->hasConnection()) { display.setColor(DisplayDriver::LIGHT); - display.fillRect(btX, 0, ind, lh); + display.fillRect(btX, 0, ind, ind_h); display.setColor(DisplayDriver::DARK); display.setCursor(btX + 1, 0); display.print("B"); @@ -357,7 +358,7 @@ class HomeScreen : public UIScreen { #endif if (show_a) { display.setColor(DisplayDriver::LIGHT); - display.fillRect(aX, 0, ind, lh); + display.fillRect(aX, 0, ind, ind_h); display.setColor(DisplayDriver::DARK); display.setCursor(aX + 1, 0); display.print("A"); @@ -435,7 +436,7 @@ public: for (int i = 0; i < n; i++) if (order[i] == _page) { curr_vis = i; break; } int x = display.width() / 2 - 5 * (n - 1); for (int i = 0; i < n; i++) { - int ds = (lh >= 16) ? 2 : 1; + int ds = display.isLandscape() ? 2 : 1; if (i == curr_vis) display.fillRect(x-ds, dots_y-ds, 2*ds+1, 2*ds+1); else display.fillRect(x-ds+1, dots_y-ds+1, 2*ds-1, 2*ds-1); x += 10; @@ -460,7 +461,11 @@ public: char buf[24]; display.setColor(DisplayDriver::LIGHT); display.setTextSize(2); +#ifdef EINK_DISPLAY_MODEL + bool show_sec = false; +#else bool show_sec = !_node_prefs || !_node_prefs->clock_hide_seconds; +#endif bool h12 = _node_prefs && _node_prefs->clock_12h; if (h12) { int h = ti->tm_hour % 12; if (h == 0) h = 12; @@ -609,19 +614,23 @@ public: display.print(tmp); } else if (_page == HomePage::BLUETOOTH) { display.setColor(DisplayDriver::LIGHT); - int icon_y = content_y; - display.drawXbm((display.width() - 32) / 2, icon_y, - _task->isSerialEnabled() ? bluetooth_on : bluetooth_off, - 32, 32); display.setTextSize(1); - int pin_y = icon_y + 32 + 3; - int hint_y = pin_y + step; - if (_task->isSerialEnabled() && !_task->hasConnection() && the_mesh.getBLEPin() != 0) { + display.drawXbm((display.width() - 32) / 2, content_y, + _task->isSerialEnabled() ? bluetooth_on : bluetooth_off, 32, 32); + const int text_y = content_y + 32 + 3; + const bool waiting_for_pair = _task->isSerialEnabled() && !_task->hasConnection() && the_mesh.getBLEPin() != 0; + if (waiting_for_pair && !display.isLandscape()) { char pin_buf[16]; snprintf(pin_buf, sizeof(pin_buf), "PIN: %d", the_mesh.getBLEPin()); - display.drawTextCentered(display.width() / 2, pin_y, pin_buf); + display.drawTextCentered(display.width() / 2, text_y, pin_buf); + } else if (waiting_for_pair) { + char pin_buf[16]; + snprintf(pin_buf, sizeof(pin_buf), "PIN: %d", the_mesh.getBLEPin()); + display.drawTextCentered(display.width() / 2, text_y, pin_buf); + display.drawTextCentered(display.width() / 2, text_y + step, "toggle: " PRESS_LABEL); + } else { + display.drawTextCentered(display.width() / 2, text_y, "toggle: " PRESS_LABEL); } - display.drawTextCentered(display.width() / 2, hint_y, "toggle: " PRESS_LABEL); } else if (_page == HomePage::ADVERT) { display.setColor(DisplayDriver::LIGHT); display.drawXbm((display.width() - 32) / 2, content_y, advert_icon, 32, 32); @@ -766,9 +775,19 @@ public: display.drawTextCentered(display.width() / 2, content_y + step, "hibernating..."); } else { display.drawXbm((display.width() - 32) / 2, content_y, power_icon, 32, 32); - int hint_base = content_y + 32 + 3; - display.drawTextCentered(display.width() / 2, hint_base, "hibernate:"); - display.drawTextCentered(display.width() / 2, hint_base + step, PRESS_LABEL); + const int text_y = content_y + 32 + 3; + const int lh1 = display.getLineHeight(); + if (text_y + lh1 <= display.height()) { + char hib_hint[32]; + snprintf(hib_hint, sizeof(hib_hint), "hibernate: %s", PRESS_LABEL); + if (display.getTextWidth(hib_hint) < display.width()) { + display.drawTextCentered(display.width() / 2, text_y, hib_hint); + } else { + display.drawTextCentered(display.width() / 2, text_y, "hibernate:"); + if (text_y + step + lh1 <= display.height()) + display.drawTextCentered(display.width() / 2, text_y + step, PRESS_LABEL); + } + } } } bool auto_adv = _node_prefs && _node_prefs->advert_auto_interval_sec > 0; diff --git a/examples/companion_radio/ui-new/icons.h b/examples/companion_radio/ui-new/icons.h index cbe23790..f1806a36 100644 --- a/examples/companion_radio/ui-new/icons.h +++ b/examples/companion_radio/ui-new/icons.h @@ -116,7 +116,3 @@ static const uint8_t advert_icon[] = { 0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; - -static const uint8_t muted_icon[] = { - 0x20, 0x6a, 0xea, 0xe4, 0xe4, 0xea, 0x6a, 0x20 -}; \ No newline at end of file diff --git a/src/helpers/ui/DisplayDriver.h b/src/helpers/ui/DisplayDriver.h index c489f9cb..e0fd0b82 100644 --- a/src/helpers/ui/DisplayDriver.h +++ b/src/helpers/ui/DisplayDriver.h @@ -31,6 +31,7 @@ public: virtual int getCharWidth() const { return 6; } // typical character advance width (px) virtual int getLineHeight() const { return 8; } // pixel rows per text line virtual void setLemonFont(bool) { } // no-op; overridden by displays that support Lemon + virtual bool isLemonFont() const { return false; } // Layout helpers — derived from font metrics and screen size. // Use these instead of hardcoded pixel values so layouts adapt to any display. @@ -41,21 +42,30 @@ public: int listVisible() const { return listVisible(lineStep()); } // x where a right-side value column starts (leaves ~8 chars for the value) int valCol() const { return width() - getCharWidth() * 8; } - // separator line thickness: 2px on landscape e-ink, 1px on OLED - int sepH() const { return (width() >= height()) ? 2 : 1; } - virtual void drawTextCentered(int mid_x, int y, const char* str) { // helper method (override to optimise) - int w = getTextWidth(str); + // true only on landscape e-ink; use instead of comparing pixel counts or getLineHeight() +#ifdef EINK_DISPLAY_MODEL + bool isLandscape() const { return width() >= height(); } +#else + bool isLandscape() const { return false; } +#endif + // separator line thickness: 2px on landscape e-ink, 1px everywhere else + int sepH() const { return isLandscape() ? 2 : 1; } + virtual void drawTextCentered(int mid_x, int y, const char* str) { + char tmp[256]; translateUTF8ToBlocks(tmp, str, sizeof(tmp)); + int w = getTextWidth(tmp); setCursor(mid_x - w/2, y); - print(str); + print(tmp); } virtual void drawTextRightAlign(int x_anch, int y, const char* str) { - int w = getTextWidth(str); + char tmp[256]; translateUTF8ToBlocks(tmp, str, sizeof(tmp)); + int w = getTextWidth(tmp); setCursor(x_anch - w, y); - print(str); + print(tmp); } virtual void drawTextLeftAlign(int x_anch, int y, const char* str) { + char tmp[256]; translateUTF8ToBlocks(tmp, str, sizeof(tmp)); setCursor(x_anch, y); - print(str); + print(tmp); } static char transliterateCodepoint(uint32_t cp) { @@ -137,7 +147,7 @@ public: case 0x00E7: return 'c'; case 0x00C7: return 'C'; // ç Ç case 0x00F1: return 'n'; case 0x00D1: return 'N'; // ñ Ñ case 0x00FD: return 'y'; case 0x00DD: return 'Y'; // ý Ý - default: return '?'; + default: return '\xDB'; } } diff --git a/src/helpers/ui/GxEPDDisplay.cpp b/src/helpers/ui/GxEPDDisplay.cpp index 7d337a3a..c30b8ee9 100644 --- a/src/helpers/ui/GxEPDDisplay.cpp +++ b/src/helpers/ui/GxEPDDisplay.cpp @@ -122,7 +122,22 @@ void GxEPDDisplay::setCursor(int x, int y) { void GxEPDDisplay::print(const char* str) { display_crc.update(str, strlen(str)); - display.print(str); + if (!_use_lemon) { + display.print(str); + return; + } + int sc = (width() >= height()) ? 2 : 1; + for (const char* p = str; *p; p++) { + if ((uint8_t)*p == 0xDB) { + int cx = display.getCursorX(); + int cy = display.getCursorY(); + display.fillRect(cx, cy - 8 * sc, 5 * sc, 8 * sc, _curr_color); + display.setCursor(cx + 6 * sc, cy); + } else { + char tmp[2] = {*p, 0}; + display.print(tmp); + } + } } void GxEPDDisplay::fillRect(int x, int y, int w, int h) { @@ -162,9 +177,11 @@ void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) { } uint16_t GxEPDDisplay::getTextWidth(const char* str) { + display.setTextWrap(false); int16_t x1, y1; uint16_t w, h; display.getTextBounds(str, 0, 0, &x1, &y1, &w, &h); + display.setTextWrap(true); return w; } diff --git a/src/helpers/ui/GxEPDDisplay.h b/src/helpers/ui/GxEPDDisplay.h index 6cffc323..8cde8e0e 100644 --- a/src/helpers/ui/GxEPDDisplay.h +++ b/src/helpers/ui/GxEPDDisplay.h @@ -83,7 +83,7 @@ public: return (_use_lemon ? 10 : 8) * sc; } void setLemonFont(bool enabled) override { _use_lemon = enabled; } - + bool isLemonFont() const override { return _use_lemon; } bool begin(); bool isOn() override { return _isOn; } diff --git a/src/helpers/ui/SH1106Display.h b/src/helpers/ui/SH1106Display.h index 53900804..b5994697 100644 --- a/src/helpers/ui/SH1106Display.h +++ b/src/helpers/ui/SH1106Display.h @@ -51,6 +51,7 @@ public: int getCharWidth() const override { return (_use_lemon ? 5 : 6) * _text_sz; } int getLineHeight() const override { return (_use_lemon ? 9 : 8) * _text_sz; } void setLemonFont(bool enabled) override { _use_lemon = enabled; } + bool isLemonFont() const override { return _use_lemon; } void translateUTF8ToBlocks(char* dest, const char* src, size_t dest_size) override; void setBrightness(uint8_t level) override; void endFrame() override;