fix(ui): unified unknown-char block rendering and display layout fixes

- GxEPDDisplay::print() renders '\xDB' as filled rect for Lemon font,
  matching OLED Lemon block behavior
- Remove translateUTF8ToBlocks override (base class passthrough sufficient)
- transliterateCodepoint fallback changed '?' → '\xDB' for consistency
- drawTextCentered/Right/LeftAlign now call translateUTF8ToBlocks
- isLemonFont() added to DisplayDriver, SH1106Display, GxEPDDisplay
- isLandscape() guarded by EINK_DISPLAY_MODEL to distinguish OLED vs e-ink
- ind_h trims indicator box height only for Lemon font
- getTextWidth disables wrap before getTextBounds, restores after
- QuickMsgScreen: tighter hist_item padding; 2 messages visible with Lemon
- Hibernate hint fits on one line when width allows, splits otherwise
- BLE PIN page shows toggle hint on landscape e-ink
- Remove unused muted_icon bitmap

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-05-23 14:45:24 +02:00
parent 6cdd9d01d4
commit 85cfb6f1af
7 changed files with 90 additions and 48 deletions

View File

@@ -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';
}
}

View File

@@ -122,7 +122,22 @@ void GxEPDDisplay::setCursor(int x, int y) {
void GxEPDDisplay::print(const char* str) {
display_crc.update<char>(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;
}

View File

@@ -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; }

View File

@@ -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;