mirror of
https://github.com/pelgraine/Meck.git
synced 2026-08-06 16:52:46 +02:00
t5s3 ui, screen refresh and ghosting fixes
This commit is contained in:
@@ -519,7 +519,7 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store
|
||||
|
||||
// Home screen FIRST page: tile taps (virtual coordinate hit test)
|
||||
if (ui_task.isOnHomeScreen() && ui_task.isHomeShowingTiles()) {
|
||||
const int tileW = 40, tileH = 32, gapX = 1, gapY = 2;
|
||||
const int tileW = 40, tileH = 28, gapX = 1, gapY = 1;
|
||||
const int gridW = tileW * 3 + gapX * 2;
|
||||
const int gridX = (128 - gridW) / 2; // =3
|
||||
int gridY = ui_task.getTileGridVY();
|
||||
@@ -539,13 +539,14 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store
|
||||
if (row == 1 && col == 1) { ui_task.gotoNotesScreen(); return 0; }
|
||||
if (row == 1 && col == 2) { ui_task.gotoDiscoveryScreen(); return 0; }
|
||||
}
|
||||
// Tap outside tiles — cycle home pages
|
||||
return (char)KEY_NEXT;
|
||||
// Tap outside tiles — left half backward, right half forward
|
||||
return (vx < 64) ? (char)KEY_PREV : (char)KEY_NEXT;
|
||||
}
|
||||
|
||||
// Home screen (non-tile pages): tap cycles pages
|
||||
// Home screen (non-tile pages): left half taps backward, right half forward
|
||||
if (ui_task.isOnHomeScreen()) {
|
||||
return (char)KEY_NEXT;
|
||||
int vx = (int)(x / 7.5f);
|
||||
return (vx < 64) ? (char)KEY_PREV : (char)KEY_NEXT;
|
||||
}
|
||||
|
||||
// Reader (reading mode): tap = next page
|
||||
@@ -586,9 +587,12 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store
|
||||
}
|
||||
}
|
||||
|
||||
// Home screen: horizontal swipe cycles pages
|
||||
// Home screen: swipe left = next page, swipe right = previous page
|
||||
if (ui_task.isOnHomeScreen()) {
|
||||
return (char)KEY_NEXT;
|
||||
if (horizontal) {
|
||||
return (dx < 0) ? (char)KEY_NEXT : (char)KEY_PREV;
|
||||
}
|
||||
return (char)KEY_NEXT; // vertical swipe = next (default)
|
||||
}
|
||||
|
||||
// Settings: horizontal swipe → a/d for picker/number editing
|
||||
|
||||
@@ -854,7 +854,7 @@ private:
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
display.setCursor(0, footerY);
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
display.print("Back:Cancel");
|
||||
display.print("Boot:Cancel");
|
||||
const char* right = "Tap:Confirm";
|
||||
#else
|
||||
display.print("Q:Cancel");
|
||||
@@ -893,7 +893,7 @@ private:
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
display.setCursor(0, footerY);
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
display.print("Back:Cancel");
|
||||
display.print("Boot:Cancel");
|
||||
const char* right = "Tap:Delete";
|
||||
#else
|
||||
display.print("Q:Cancel");
|
||||
|
||||
@@ -599,7 +599,7 @@ public:
|
||||
switch (_state) {
|
||||
case STATE_PASSWORD_ENTRY:
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
display.print("Back:Exit");
|
||||
display.print("Boot:Exit");
|
||||
renderFooterRight(display, footerY, "Hold:Type");
|
||||
#else
|
||||
display.print("Sh+Del:Exit");
|
||||
@@ -610,7 +610,7 @@ public:
|
||||
case STATE_LOGGING_IN:
|
||||
case STATE_COMMAND_PENDING:
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
display.print("Back:Cancel");
|
||||
display.print("Boot:Cancel");
|
||||
#else
|
||||
display.print("Sh+Del:Cancel");
|
||||
#endif
|
||||
@@ -618,7 +618,7 @@ public:
|
||||
|
||||
case STATE_CATEGORY_MENU:
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
display.print("Back:Exit");
|
||||
display.print("Boot:Exit");
|
||||
renderFooterMidRight(display, footerY, "Back:Exit", "Tap:Open", "Swipe:Sel");
|
||||
#else
|
||||
display.print("Sh+Del:Exit");
|
||||
@@ -628,7 +628,7 @@ public:
|
||||
|
||||
case STATE_COMMAND_MENU:
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
display.print("Back:Back");
|
||||
display.print("Boot:Back");
|
||||
renderFooterMidRight(display, footerY, "Back:Back", "Tap:Run", "Swipe:Sel");
|
||||
#else
|
||||
display.print("Sh+Del:Back");
|
||||
@@ -638,7 +638,7 @@ public:
|
||||
|
||||
case STATE_PARAM_ENTRY:
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
display.print("Back:Cancel");
|
||||
display.print("Boot:Cancel");
|
||||
renderFooterRight(display, footerY, "Tap:Send");
|
||||
#else
|
||||
display.print("Sh+Del:Cancel");
|
||||
@@ -648,7 +648,7 @@ public:
|
||||
|
||||
case STATE_CONFIRM:
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
display.print("Back:No");
|
||||
display.print("Boot:No");
|
||||
renderFooterRight(display, footerY, "Tap:Yes");
|
||||
#else
|
||||
display.print("Sh+Del:No");
|
||||
@@ -659,7 +659,7 @@ public:
|
||||
case STATE_RESPONSE_VIEW:
|
||||
case STATE_ERROR:
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
display.print("Back:Back");
|
||||
display.print("Boot:Back");
|
||||
if (_responseTotalLines > bodyHeight / 9) {
|
||||
renderFooterRight(display, footerY, "Swipe:Scroll");
|
||||
}
|
||||
@@ -1160,7 +1160,11 @@ private:
|
||||
bool selected, const char* label, bool warn) {
|
||||
if (selected) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
display.fillRect(0, y, display.width(), lineHeight);
|
||||
#else
|
||||
display.fillRect(0, y + 5, display.width(), lineHeight);
|
||||
#endif
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
} else if (warn) {
|
||||
display.setColor(DisplayDriver::YELLOW);
|
||||
|
||||
@@ -26,7 +26,11 @@
|
||||
#define LONG_PRESS_MILLIS 1200
|
||||
|
||||
#ifndef UI_RECENT_LIST_SIZE
|
||||
#define UI_RECENT_LIST_SIZE 4
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
#define UI_RECENT_LIST_SIZE 8
|
||||
#else
|
||||
#define UI_RECENT_LIST_SIZE 4
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if UI_HAS_JOYSTICK
|
||||
@@ -145,9 +149,19 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts,
|
||||
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
|
||||
// battery icon dimensions (smaller to match tiny percentage text)
|
||||
// battery icon dimensions
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
int iconWidth = 10; // Narrower — non-square scaling stretches width
|
||||
#else
|
||||
int iconWidth = 16;
|
||||
#endif
|
||||
int iconHeight = 6;
|
||||
int iconY = 0;
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
int textY = 0; // T5S3: percentage beside icon, same baseline
|
||||
#else
|
||||
int textY = iconY - 3; // T-Deck Pro: offset up to center with icon
|
||||
#endif
|
||||
|
||||
// measure percentage text width to position icon + text together at right edge
|
||||
display.setTextSize(0);
|
||||
@@ -155,16 +169,9 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts,
|
||||
sprintf(pctStr, "%d%%", batteryPercentage);
|
||||
uint16_t textWidth = display.getTextWidth(pctStr);
|
||||
|
||||
// layout: [icon 16px][cap 2px][gap 2px][text][margin 2px]
|
||||
// layout: [icon][cap 2px][gap 2px][text][margin 2px]
|
||||
int totalWidth = iconWidth + 2 + 2 + textWidth + 2;
|
||||
int iconX = display.width() - totalWidth;
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
int iconY = 6; // Align with FreeSans12pt text baseline
|
||||
int textY = 1; // Percentage text — setCursor adds +5 → baseline at (6)*scale_y
|
||||
#else
|
||||
int iconY = 0; // vertically align with node name text
|
||||
int textY = iconY - 3; // offset up to vertically center with icon
|
||||
#endif
|
||||
|
||||
if (outIconX) *outIconX = iconX;
|
||||
|
||||
@@ -178,8 +185,7 @@ void renderBatteryIndicator(DisplayDriver& display, uint16_t batteryMilliVolts,
|
||||
int fillWidth = (batteryPercentage * (iconWidth - 4)) / 100;
|
||||
display.fillRect(iconX + 2, iconY + 2, fillWidth, iconHeight - 4);
|
||||
|
||||
// draw percentage text after the battery cap, offset upward to center with icon
|
||||
// (setCursor adds +5 internally for baseline, so compensate for the tiny font)
|
||||
// draw percentage text after the battery cap
|
||||
int textX = iconX + iconWidth + 2 + 2; // after cap + gap
|
||||
display.setCursor(textX, textY);
|
||||
display.print(pctStr);
|
||||
@@ -306,7 +312,7 @@ public:
|
||||
}
|
||||
// curr page indicator
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
int y = 18;
|
||||
int y = 14; // Closer to header
|
||||
#else
|
||||
int y = 14;
|
||||
#endif
|
||||
@@ -324,7 +330,7 @@ public:
|
||||
_task->setHomeShowingTiles(true);
|
||||
#endif
|
||||
#if defined(LilyGo_T5S3_EPaper_Pro)
|
||||
int y = 24;
|
||||
int y = 18; // Tighter spacing
|
||||
#else
|
||||
int y = 20;
|
||||
#endif
|
||||
@@ -332,23 +338,23 @@ public:
|
||||
display.setTextSize(2);
|
||||
sprintf(tmp, "MSG: %d", _task->getUnreadMsgCount());
|
||||
display.drawTextCentered(display.width() / 2, y, tmp);
|
||||
y += 18;
|
||||
y += 14; // Reduced from 18
|
||||
|
||||
#if defined(WIFI_SSID) || defined(MECK_WIFI_COMPANION)
|
||||
IPAddress ip = WiFi.localIP();
|
||||
if (ip != IPAddress(0,0,0,0)) {
|
||||
snprintf(tmp, sizeof(tmp), "IP: %d.%d.%d.%d:%d", ip[0], ip[1], ip[2], ip[3], TCP_PORT);
|
||||
display.setTextSize(1);
|
||||
display.setTextSize(0); // Tiny font for IP
|
||||
display.drawTextCentered(display.width() / 2, y, tmp);
|
||||
y += 12;
|
||||
y += 8;
|
||||
}
|
||||
#endif
|
||||
#if defined(BLE_PIN_CODE) || defined(WIFI_SSID) || defined(MECK_WIFI_COMPANION)
|
||||
if (_task->hasConnection()) {
|
||||
display.setColor(DisplayDriver::GREEN);
|
||||
display.setTextSize(1);
|
||||
display.setTextSize(0); // Tiny font for Connected
|
||||
display.drawTextCentered(display.width() / 2, y, "< Connected >");
|
||||
y += 12;
|
||||
y += 8; // Reduced from 12
|
||||
#ifdef BLE_PIN_CODE
|
||||
} else if (_task->isSerialEnabled() && the_mesh.getBLEPin() != 0) {
|
||||
display.setColor(DisplayDriver::RED);
|
||||
@@ -372,9 +378,9 @@ public:
|
||||
};
|
||||
|
||||
const int tileW = 40;
|
||||
const int tileH = 32;
|
||||
const int tileH = 28; // Reduced from 32 to fit with Connected text
|
||||
const int gapX = 1;
|
||||
const int gapY = 2;
|
||||
const int gapY = 1; // Reduced from 2
|
||||
const int gridW = tileW * 3 + gapX * 2;
|
||||
const int gridX = (display.width() - gridW) / 2;
|
||||
const int gridY = y + 2;
|
||||
@@ -391,11 +397,11 @@ public:
|
||||
|
||||
// Letter centered in tile (pushed down for vertical centering)
|
||||
display.setTextSize(2);
|
||||
display.drawTextCentered(tx + tileW / 2, ty + 10, tiles[row][col].letter);
|
||||
display.drawTextCentered(tx + tileW / 2, ty + 8, tiles[row][col].letter);
|
||||
|
||||
// Label centered below letter
|
||||
display.setTextSize(0);
|
||||
display.drawTextCentered(tx + tileW / 2, ty + 20, tiles[row][col].label);
|
||||
display.drawTextCentered(tx + tileW / 2, ty + 18, tiles[row][col].label);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
// CLEAR_FAST, CLEAR_SLOW — full refresh modes
|
||||
|
||||
// Periodic slow (deep) refresh to clear ghosting
|
||||
#define FULL_SLOW_PERIOD 20 // every 20 fast-refreshes, do a slow refresh
|
||||
#define FULL_SLOW_PERIOD 2 // every 2 partial-refreshes, do a full cleanup
|
||||
|
||||
FastEPDDisplay::~FastEPDDisplay() {
|
||||
delete _canvas;
|
||||
@@ -282,7 +282,6 @@ void FastEPDDisplay::endFrame() {
|
||||
|
||||
// Copy GFXcanvas1 buffer to FastEPD's current buffer — direct copy.
|
||||
// Both use same polarity: bit 1 = white, bit 0 = black.
|
||||
// (Meshtastic inverts because OLEDDisplay uses opposite convention — not us.)
|
||||
uint8_t* src = _canvas->getBuffer();
|
||||
uint8_t* dst = _epd->currentBuffer();
|
||||
size_t bufSize = ((uint32_t)EPD_WIDTH * EPD_HEIGHT) / 8;
|
||||
@@ -291,9 +290,19 @@ void FastEPDDisplay::endFrame() {
|
||||
|
||||
memcpy(dst, src, bufSize);
|
||||
|
||||
// fullUpdate(true) is the only refresh mode that gives clean transitions
|
||||
// on the ED047TC1 panel. CLEAR_FAST causes ghosting/mashing on this hardware.
|
||||
// The brief white flash between frames is inherent to this panel's waveform.
|
||||
_epd->fullUpdate(true);
|
||||
// Refresh strategy:
|
||||
// partialUpdate(true) — no flash, differential, keeps previous buffer
|
||||
// fullUpdate(false) — brief flash, clears ghosting (CLEAR_FAST)
|
||||
// fullUpdate(true) — full white flash, cleanest (boot only)
|
||||
//
|
||||
// Use partial for most frames. Periodic full refresh every N frames
|
||||
// to clear accumulated ghosting artifacts.
|
||||
_fullRefreshCount++;
|
||||
if (_fullRefreshCount >= FULL_SLOW_PERIOD) {
|
||||
_fullRefreshCount = 0;
|
||||
_epd->fullUpdate(true); // Full clean refresh — clears all ghosting
|
||||
} else {
|
||||
_epd->partialUpdate(true); // No flash — differential
|
||||
}
|
||||
_epd->backupPlane();
|
||||
}
|
||||
Reference in New Issue
Block a user