From 821db1cff6d01175f79acf619662991b287c2632 Mon Sep 17 00:00:00 2001 From: Pelgraine Date: Wed, 8 Jul 2026 19:22:35 +1000 Subject: [PATCH] T-Watch touch ui fixes: repeater admin, path editor. Add boot cpu freq 80 flag to platformio --- examples/companion_radio/main.cpp | 32 +++++++++++-- .../companion_radio/ui-new/PathEditorScreen.h | 8 +++- .../ui-new/RepeaterAdminScreen.h | 48 +++++++++++++++++++ examples/companion_radio/ui-new/UITask.cpp | 16 ++++++- variants/lilygo_twatch_s3_plus/platformio.ini | 5 ++ 5 files changed, 104 insertions(+), 5 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 5534f545..2d264c0d 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -1613,6 +1613,28 @@ static void lastHeardToggleContact() { if (ui_task.isOnSMSScreen()) return 0; #endif +#if defined(LILYGO_TWATCH_S3_PLUS) + // Repeater admin (watch): tap selects a menu row (tap again = activate); + // param entry opens the keyboard for the value. + if (ui_task.isOnRepeaterAdmin()) { + RepeaterAdminScreen* admin = (RepeaterAdminScreen*)ui_task.getRepeaterAdminScreen(); + if (admin) { + RepeaterAdminScreen::AdminState astate = admin->getState(); + if (astate == RepeaterAdminScreen::STATE_CATEGORY_MENU || + astate == RepeaterAdminScreen::STATE_COMMAND_MENU) { + int result = admin->selectRowAtVY(vy); + if (result == 1) { ui_task.forceRefresh(); return 0; } + if (result == 2) return KEY_ENTER; // tapped current row -- activate + return 0; + } + if (astate == RepeaterAdminScreen::STATE_PARAM_ENTRY) { + ui_task.openTWatchKeyboard(3, 0); // tap opens keyboard for the value + return 0; + } + } + } +#endif + // All other screens: tap = select return KEY_ENTER; } @@ -1988,9 +2010,13 @@ static void lastHeardToggleContact() { #endif } #if defined(LILYGO_TWATCH_S3_PLUS) - // Watch: any other admin state long-press opens the CLI keyboard. - ui_task.openTWatchKeyboard(3, 0); // TWKB_ADMIN_CLI - return 0; + // Watch: keyboard only for param entry; menu/other states activate the + // highlighted item (no raw CLI keyboard). + if (astate == RepeaterAdminScreen::STATE_PARAM_ENTRY) { + ui_task.openTWatchKeyboard(3, 0); // param value entry + return 0; + } + return KEY_ENTER; #endif } } diff --git a/examples/companion_radio/ui-new/PathEditorScreen.h b/examples/companion_radio/ui-new/PathEditorScreen.h index 07d269b0..b47ed585 100644 --- a/examples/companion_radio/ui-new/PathEditorScreen.h +++ b/examples/companion_radio/ui-new/PathEditorScreen.h @@ -281,7 +281,7 @@ public: if (selected) { display.setColor(DisplayDriver::LIGHT); -#if defined(LilyGo_T5S3_EPaper_Pro) +#if defined(LilyGo_T5S3_EPaper_Pro) || defined(LILYGO_TWATCH_S3_PLUS) display.fillRect(0, y, display.width(), lineH); #else display.fillRect(0, y + 5, display.width(), lineH); @@ -383,6 +383,12 @@ public: const char* right = "Hold:Select"; display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); display.print(right); +#elif defined(LILYGO_TWATCH_S3_PLUS) + display.setCursor(0, footerY); + display.print("Tap:Slct"); + const char* right = "Hold:Entr"; + display.setCursor(display.width() - display.getTextWidth(right) - 2, footerY); + display.print(right); #else display.setCursor(0, footerY); display.print("Sh+Del:Bk W/S:Nav"); diff --git a/examples/companion_radio/ui-new/RepeaterAdminScreen.h b/examples/companion_radio/ui-new/RepeaterAdminScreen.h index 931a15de..f70ca853 100644 --- a/examples/companion_radio/ui-new/RepeaterAdminScreen.h +++ b/examples/companion_radio/ui-new/RepeaterAdminScreen.h @@ -479,6 +479,39 @@ public: AdminState getState() const { return _state; } uint8_t getPermissions() const { return _permissions; } + // Map a touch Y (128-virtual space) to a menu row and select it. + // Returns 0 = miss/no-op, 1 = selection moved, 2 = tapped current selection. + // Mirrors the row layout in renderCategoryMenu / renderCommandMenu. + int selectRowAtVY(int vy) { + const int headerH = 14, footerH = 14; + int lineH = the_mesh.getNodePrefs()->smallLineH(); + + if (_state == STATE_CATEGORY_MENU) { + int bodyTop = headerH; + if (_serverTime > 0) bodyTop += lineH + 2; // clock drift line + if (_telemHasVoltage || _telemHasTemp || _telemRequested) bodyTop += lineH + 2; // telemetry line + if (vy < bodyTop || vy >= 128 - footerH) return 0; + int row = (vy - bodyTop) / lineH; + if (row < 0 || row >= CAT_COUNT) return 0; + if (row == _catSel) return 2; + _catSel = row; + return 1; + } + + if (_state == STATE_COMMAND_MENU) { + int bodyTop = headerH + lineH + 2; // category title line + if (vy < bodyTop || vy >= 128 - footerH) return 0; + int idx = _scrollOffset + (vy - bodyTop) / lineH; + int count = CATEGORIES[_catSel].count; + if (idx < 0 || idx >= count) return 0; + if (idx == _cmdSel) return 2; + _cmdSel = idx; + return 1; + } + + return 0; + } + void onLoginResult(bool success, uint8_t permissions, uint32_t server_time) { _waitingForLogin = false; if (success) { @@ -562,6 +595,11 @@ public: // --- Header --- display.setTextSize(1); +#if defined(LILYGO_TWATCH_S3_PLUS) + // Clip long lines at the screen edge instead of wrapping them onto the row + // below (restored before returning so other screens are unaffected). + ((LGFXDisplay*)&display)->setTextWrap(false); +#endif display.setColor(DisplayDriver::GREEN); display.setCursor(0, 0); const char* hdrPrefix = (_state == STATE_PASSWORD_ENTRY || _state == STATE_LOGGING_IN) @@ -627,6 +665,8 @@ public: #if defined(LilyGo_T5S3_EPaper_Pro) display.print("Boot:Exit"); renderFooterMidRight(display, footerY, "Back:Exit", "Tap:Open", "Swipe:Sel"); +#elif defined(LILYGO_TWATCH_S3_PLUS) + display.print("Long Press: Select"); #else display.print("Sh+Del:Exit"); renderFooterMidRight(display, footerY, "Sh+Del:Exit", "Ent:Open", "W/S:Sel"); @@ -637,6 +677,8 @@ public: #if defined(LilyGo_T5S3_EPaper_Pro) display.print("Boot:Back"); renderFooterMidRight(display, footerY, "Back:Back", "Tap:Run", "Swipe:Sel"); +#elif defined(LILYGO_TWATCH_S3_PLUS) + display.print("Long Press: Run"); #else display.print("Sh+Del:Back"); renderFooterMidRight(display, footerY, "Sh+Del:Back", "Ent:Run", "W/S:Sel"); @@ -647,6 +689,8 @@ public: #if defined(LilyGo_T5S3_EPaper_Pro) display.print("Boot:Cancel"); renderFooterRight(display, footerY, "Tap:Send"); +#elif defined(LILYGO_TWATCH_S3_PLUS) + display.print("Tap: Enter value"); #else display.print("Sh+Del:Cancel"); renderFooterRight(display, footerY, "Ent:Send"); @@ -679,6 +723,10 @@ public: break; } +#if defined(LILYGO_TWATCH_S3_PLUS) + ((LGFXDisplay*)&display)->setTextWrap(true); // restore default before returning +#endif + if (_state == STATE_LOGGING_IN || _state == STATE_COMMAND_PENDING) return 30000; // static text; poll()/callbacks force refresh on state change if (_state == STATE_PASSWORD_ENTRY && _lastCharAt > 0 && (millis() - _lastCharAt) < 800) { return _lastCharAt + 800 - millis() + 50; diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 27c576ee..9e03edd7 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -34,7 +34,7 @@ #endif #ifndef AUTO_OFF_MILLIS - #define AUTO_OFF_MILLIS 15000 // 15 seconds + #define AUTO_OFF_MILLIS 45000 // 45 seconds #endif // Right-aligned values on the dense home pages compensate for the e-ink X // origin offset so they are not clipped at the right edge. Default 0 for @@ -2223,6 +2223,20 @@ void UITask::loop() { } else { c = checkDisplayOn(KEY_NEXT); } +#elif defined(LILYGO_TWATCH_S3_PLUS) + // Watch: on the contacts screen a button click opens the path editor for + // the selected contact; elsewhere it wakes the display / emits KEY_NEXT. + if (isOnContactsScreen()) { + int idx = ((ContactsScreen*)contacts_screen)->getSelectedContactIdx(); + if (idx >= 0) { + gotoPathEditor(idx); + c = 0; + } else { + c = checkDisplayOn(KEY_NEXT); + } + } else { + c = checkDisplayOn(KEY_NEXT); + } #else c = checkDisplayOn(KEY_NEXT); #endif diff --git a/variants/lilygo_twatch_s3_plus/platformio.ini b/variants/lilygo_twatch_s3_plus/platformio.ini index ab07e2be..3c0f9f38 100644 --- a/variants/lilygo_twatch_s3_plus/platformio.ini +++ b/variants/lilygo_twatch_s3_plus/platformio.ini @@ -96,7 +96,10 @@ build_flags = -D MAX_CONTACTS=2000 -D MAX_GROUP_CHANNELS=40 -D OFFLINE_QUEUE_SIZE=1 + -D ESP32_CPU_FREQ=80 -D FIRMWARE_VERSION='"Meck TWatch v0.1"' +; -D MESH_PACKET_LOGGING=1 +; -D MESH_DEBUG=1 build_src_filter = ${LilyGo_TWatchS3Plus.build_src_filter} + - @@ -133,6 +136,8 @@ build_flags = -D OFFLINE_QUEUE_SIZE=256 -D ESP32_CPU_FREQ=80 -D FIRMWARE_VERSION='"Meck TWatch BLE v0.1"' + ;-D MESH_PACKET_LOGGING=1 + ;-D MESH_DEBUG=1 build_src_filter = ${LilyGo_TWatchS3Plus.build_src_filter} + +