WioTrackerL1 UI v1.15.1: bugfixes and UX polish

- Fix placeholder picker colors: dark box + white border, light fill
  for selected item (consistent with rest of UI)
- Fix placeholders not expanded when sending from keyboard (expandMsg
  was only called in MSG_PICK path, not KEYBOARD path)
- Fix sent messages incrementing unread counter: set _phase=CHANNEL_HIST
  before addChannelMsg so viewing=true
- Fix keyboard column inverse mapping on UP from special row
- Remove old_row dead code from QuickMsgScreen keyboard handler
- Alert banner shown only on home screen, suppressed in settings/messages
- Bump FIRMWARE_VERSION to v1.15.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-05-11 17:19:03 +02:00
co-authored by Claude Sonnet 4.6
parent 886ee62015
commit 21005aa74c
2 changed files with 59 additions and 39 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
#endif #endif
#ifndef FIRMWARE_VERSION #ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.15.0" #define FIRMWARE_VERSION "v1.15.1"
#endif #endif
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM) #if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
+58 -38
View File
@@ -391,25 +391,26 @@ public:
// placeholder picker overlay // placeholder picker overlay
if (_edit_ph_mode) { if (_edit_ph_mode) {
display.setColor(DisplayDriver::LIGHT);
display.fillRect(20, 20, 88, 12 + KB_PH_COUNT * 10);
display.setColor(DisplayDriver::DARK); display.setColor(DisplayDriver::DARK);
display.fillRect(20, 20, 88, 10); display.fillRect(20, 20, 88, 12 + KB_PH_COUNT * 10);
display.setColor(DisplayDriver::LIGHT);
display.drawRect(20, 20, 88, 12 + KB_PH_COUNT * 10);
display.setCursor(24, 21); display.setCursor(24, 21);
display.print("Placeholder:"); display.print("Placeholder:");
display.setColor(DisplayDriver::LIGHT); display.fillRect(20, 30, 88, 1);
for (int i = 0; i < KB_PH_COUNT; i++) { for (int i = 0; i < KB_PH_COUNT; i++) {
int py = 32 + i * 10; int py = 33 + i * 10;
if (i == _edit_ph_sel) { if (i == _edit_ph_sel) {
display.fillRect(20, py - 1, 88, 10); display.setColor(DisplayDriver::LIGHT);
display.fillRect(21, py - 1, 86, 10);
display.setColor(DisplayDriver::DARK); display.setColor(DisplayDriver::DARK);
} else { } else {
display.setColor(DisplayDriver::LIGHT); display.setColor(DisplayDriver::LIGHT);
} }
display.setCursor(24, py); display.setCursor(24, py);
display.print(KB_PH_LIST[i]); display.print(KB_PH_LIST[i]);
display.setColor(DisplayDriver::LIGHT);
} }
display.setColor(DisplayDriver::LIGHT);
} }
return 50; return 50;
@@ -446,19 +447,16 @@ public:
if (c == KEY_UP) { if (c == KEY_UP) {
if (_edit_kb_row > 0) { if (_edit_kb_row > 0) {
_edit_kb_row--; _edit_kb_row--;
if (_edit_kb_row == KB_ROWS_CHAR - 1 && _edit_kb_col > KB_COLS_CHAR - 1) if (_edit_kb_row == KB_ROWS_CHAR - 1) // leaving special row upward
_edit_kb_col = KB_COLS_CHAR - 1; _edit_kb_col = _edit_kb_col * KB_COLS_CHAR / KB_SPECIAL;
} }
return true; return true;
} }
if (c == KEY_DOWN) { if (c == KEY_DOWN) {
if (_edit_kb_row < KB_ROWS_CHAR) { if (_edit_kb_row < KB_ROWS_CHAR) {
int old_row = _edit_kb_row;
_edit_kb_row++; _edit_kb_row++;
if (_edit_kb_row == KB_ROWS_CHAR) { if (_edit_kb_row == KB_ROWS_CHAR) // entering special row
_edit_kb_col = _edit_kb_col * KB_SPECIAL / KB_COLS_CHAR; _edit_kb_col = _edit_kb_col * KB_SPECIAL / KB_COLS_CHAR;
}
(void)old_row;
} }
return true; return true;
} }
@@ -809,12 +807,12 @@ class QuickMsgScreen : public UIScreen {
void afterSend(bool ok, const char* msg) { void afterSend(bool ok, const char* msg) {
if (ok && _sending_to_channel) { if (ok && _sending_to_channel) {
_hist_sel = 0;
_hist_scroll = 0;
_phase = CHANNEL_HIST; // set before addChannelMsg so viewing=true, no unread bump
char entry[sizeof(ChHistEntry::text)]; char entry[sizeof(ChHistEntry::text)];
snprintf(entry, sizeof(entry), "Me: %s", msg); snprintf(entry, sizeof(entry), "Me: %s", msg);
addChannelMsg(_sel_channel_idx, entry); addChannelMsg(_sel_channel_idx, entry);
_hist_sel = 0;
_hist_scroll = 0;
_phase = CHANNEL_HIST;
_task->showAlert("Sent!", 600); _task->showAlert("Sent!", 600);
} else { } else {
_task->showAlert(ok ? "Sent!" : "Send failed", 1500); _task->showAlert(ok ? "Sent!" : "Send failed", 1500);
@@ -917,6 +915,7 @@ public:
display.drawTextCentered(display.width()/2, 0, "MESSAGE"); display.drawTextCentered(display.width()/2, 0, "MESSAGE");
display.fillRect(0, 10, display.width(), 1); display.fillRect(0, 10, display.width(), 1);
const char* opts[] = { "Direct message", "Channels" }; const char* opts[] = { "Direct message", "Channels" };
int dm_unread = _task->getMsgCount();
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
int y = START_Y + i * ITEM_H; int y = START_Y + i * ITEM_H;
bool sel = (i == _mode_sel); bool sel = (i == _mode_sel);
@@ -931,6 +930,14 @@ public:
display.print(sel ? ">" : " "); display.print(sel ? ">" : " ");
display.setCursor(8, y); display.setCursor(8, y);
display.print(opts[i]); display.print(opts[i]);
// DM unread badge on "Direct message" row
if (i == 0 && dm_unread > 0) {
char badge[5];
snprintf(badge, sizeof(badge), "%d", dm_unread);
int bw = display.getTextWidth(badge) + 2;
display.setCursor(display.width() - bw, y);
display.print(badge);
}
} }
display.setColor(DisplayDriver::LIGHT); display.setColor(DisplayDriver::LIGHT);
@@ -1192,25 +1199,29 @@ public:
// placeholder picker overlay // placeholder picker overlay
if (_kb_ph_mode) { if (_kb_ph_mode) {
display.setColor(DisplayDriver::LIGHT); // Black box with white border
display.fillRect(20, 20, 88, 12 + KB_PH_COUNT * 10);
display.setColor(DisplayDriver::DARK); display.setColor(DisplayDriver::DARK);
display.fillRect(20, 20, 88, 10); display.fillRect(20, 20, 88, 12 + KB_PH_COUNT * 10);
display.setColor(DisplayDriver::LIGHT);
display.drawRect(20, 20, 88, 12 + KB_PH_COUNT * 10);
display.setCursor(24, 21); display.setCursor(24, 21);
display.print("Placeholder:"); display.print("Placeholder:");
display.setColor(DisplayDriver::LIGHT); display.fillRect(20, 30, 88, 1); // separator
for (int i = 0; i < KB_PH_COUNT; i++) { for (int i = 0; i < KB_PH_COUNT; i++) {
int py = 32 + i * 10; int py = 33 + i * 10;
if (i == _kb_ph_sel) { if (i == _kb_ph_sel) {
display.fillRect(20, py - 1, 88, 10); // Selected: white fill + black text
display.setColor(DisplayDriver::LIGHT);
display.fillRect(21, py - 1, 86, 10);
display.setColor(DisplayDriver::DARK); display.setColor(DisplayDriver::DARK);
} else { } else {
// Unselected: white text on black
display.setColor(DisplayDriver::LIGHT); display.setColor(DisplayDriver::LIGHT);
} }
display.setCursor(24, py); display.setCursor(24, py);
display.print(KB_PH_LIST[i]); display.print(KB_PH_LIST[i]);
display.setColor(DisplayDriver::LIGHT);
} }
display.setColor(DisplayDriver::LIGHT);
} }
} else { // MSG_PICK } else { // MSG_PICK
@@ -1368,19 +1379,16 @@ public:
if (c == KEY_UP) { if (c == KEY_UP) {
if (_kb_row > 0) { if (_kb_row > 0) {
_kb_row--; _kb_row--;
if (_kb_row == KB_ROWS_CHAR - 1 && _kb_col > KB_COLS_CHAR - 1) if (_kb_row == KB_ROWS_CHAR - 1) // leaving special row upward
_kb_col = KB_COLS_CHAR - 1; _kb_col = _kb_col * KB_COLS_CHAR / KB_SPECIAL;
} }
return true; return true;
} }
if (c == KEY_DOWN) { if (c == KEY_DOWN) {
if (_kb_row < KB_ROWS_CHAR) { if (_kb_row < KB_ROWS_CHAR) {
int old_row = _kb_row;
_kb_row++; _kb_row++;
if (_kb_row == KB_ROWS_CHAR) { if (_kb_row == KB_ROWS_CHAR) // entering special row
_kb_col = _kb_col * KB_SPECIAL / KB_COLS_CHAR; _kb_col = _kb_col * KB_SPECIAL / KB_COLS_CHAR;
} else if (old_row == KB_ROWS_CHAR) {
}
} }
return true; return true;
} }
@@ -1419,10 +1427,12 @@ public:
_kb_ph_mode = true; _kb_ph_mode = true;
_kb_ph_sel = 0; _kb_ph_sel = 0;
} else { } else {
// OK — send // OK — send (expand placeholders first)
if (_kb_len > 0) { if (_kb_len > 0) {
bool ok = sendText(_kb_text); char expanded[KB_MAX_LEN + 1];
afterSend(ok, _kb_text); expandMsg(_kb_text, expanded, sizeof(expanded));
bool ok = sendText(expanded);
afterSend(ok, expanded);
} }
} }
} }
@@ -1859,7 +1869,15 @@ public:
display.setColor(DisplayDriver::LIGHT); display.setColor(DisplayDriver::LIGHT);
display.setTextSize(1); display.setTextSize(1);
display.drawTextCentered(display.width() / 2, 30, "Messages"); display.drawTextCentered(display.width() / 2, 30, "Messages");
display.drawTextCentered(display.width() / 2, 46, PRESS_LABEL " to open"); int dm_unread = _task->getMsgCount();
if (dm_unread > 0) {
char badge[16];
snprintf(badge, sizeof(badge), "%d unread DM", dm_unread);
display.drawTextCentered(display.width() / 2, 41, badge);
display.drawTextCentered(display.width() / 2, 54, PRESS_LABEL " to open");
} else {
display.drawTextCentered(display.width() / 2, 46, PRESS_LABEL " to open");
}
} else if (_page == HomePage::SHUTDOWN) { } else if (_page == HomePage::SHUTDOWN) {
display.setColor(DisplayDriver::LIGHT); display.setColor(DisplayDriver::LIGHT);
display.setTextSize(1); display.setTextSize(1);
@@ -2201,16 +2219,18 @@ void UITask::msgRead(int msgcount) {
void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) { void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) {
_msgcount = msgcount; _msgcount = msgcount;
((MsgPreviewScreen *) msg_preview)->addPreview(path_len, from_name, text); char alert_buf[80];
setCurrScreen(msg_preview); snprintf(alert_buf, sizeof(alert_buf), "Msg: %.20s", from_name);
showAlert(alert_buf, 3000);
if (_display != NULL) { if (_display != NULL) {
if (!_display->isOn() && !hasConnection()) { if (!_display->isOn() && !hasConnection()) {
_display->turnOn(); _display->turnOn();
} }
if (_display->isOn()) { if (_display->isOn()) {
{ uint32_t aoff = autoOffMillis(); if (aoff > 0) _auto_off = millis() + aoff; } // extend the auto-off timer uint32_t aoff = autoOffMillis();
_next_refresh = 100; // trigger refresh if (aoff > 0) _auto_off = millis() + aoff;
_next_refresh = 100;
} }
} }
} }
@@ -2371,7 +2391,7 @@ void UITask::loop() {
if (millis() >= _next_refresh && curr) { if (millis() >= _next_refresh && curr) {
_display->startFrame(); _display->startFrame();
int delay_millis = curr->render(*_display); int delay_millis = curr->render(*_display);
if (millis() < _alert_expiry) { // render alert popup if (millis() < _alert_expiry && curr == home) { // render alert only on home screen
_display->setTextSize(1); _display->setTextSize(1);
int y = _display->height() / 3; int y = _display->height() / 3;
int p = _display->height() / 32; int p = _display->height() / 32;