diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index c6ec4863..547511fb 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -3469,8 +3469,9 @@ void MyMesh::loop() { if ((dirty_contacts_expiry && millisHasNowPassed(dirty_contacts_expiry)) || (dirty_paths_expiry && millisHasNowPassed(dirty_paths_expiry))) { if (millis() - _last_input_ms < USER_QUIET_MILLIS) { - if (dirty_contacts_expiry) dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); - if (dirty_paths_expiry) dirty_paths_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); + unsigned long later = futureMillis(LAZY_CONTACTS_WRITE_DELAY); + if (dirty_contacts_expiry) dirty_contacts_expiry = later; + if (dirty_paths_expiry) dirty_paths_expiry = later; } else { saveContacts(); dirty_contacts_expiry = dirty_paths_expiry = 0; diff --git a/examples/companion_radio/ui-lvgl/UITask.cpp b/examples/companion_radio/ui-lvgl/UITask.cpp index 4baa0194..faafac41 100644 --- a/examples/companion_radio/ui-lvgl/UITask.cpp +++ b/examples/companion_radio/ui-lvgl/UITask.cpp @@ -1914,12 +1914,16 @@ void UITask::onMessageArrived(const UiEvent& ev) { } // On screen: a conversation left open under the lock or a dark display isn't read. +bool UITask::threadInView() const { + return !_asleep && !locked() && _screen == SCR_THREAD; +} + bool UITask::isViewingChannel(uint8_t channel_idx) { - return !_asleep && !locked() && _screen == SCR_THREAD && _thread_is_channel && _thread_channel == channel_idx; + return threadInView() && _thread_is_channel && _thread_channel == channel_idx; } bool UITask::isViewingDM(const uint8_t* pub_key) { - return !_asleep && !locked() && _screen == SCR_THREAD && !_thread_is_channel && memcmp(_thread_key, pub_key, 4) == 0; + return threadInView() && !_thread_is_channel && memcmp(_thread_key, pub_key, 4) == 0; } // ── Status bar + toast (top layer, over every screen) ───────────────────────── diff --git a/examples/companion_radio/ui-lvgl/UITask.h b/examples/companion_radio/ui-lvgl/UITask.h index effdc99e..be007e3a 100644 --- a/examples/companion_radio/ui-lvgl/UITask.h +++ b/examples/companion_radio/ui-lvgl/UITask.h @@ -430,6 +430,7 @@ private: void buildThread(); void threadOpenAt(int first); void reopenThreadIfUnread(); + bool threadInView() const; // a thread on a lit, unlocked screen void refreshThread(); void refreshThreadAges(); uint32_t threadSignature() const; diff --git a/examples/companion_radio/ui-new/KeyboardWidget.h b/examples/companion_radio/ui-new/KeyboardWidget.h index fccd9b7c..e75bdf72 100644 --- a/examples/companion_radio/ui-new/KeyboardWidget.h +++ b/examples/companion_radio/ui-new/KeyboardWidget.h @@ -546,6 +546,7 @@ struct KeyboardWidget { return p; } + // Backspace: the codepoint before the cursor. void deleteBeforeCursor() { t9_cell = -1; if (cursor_pos <= 0) return; @@ -600,9 +601,7 @@ struct KeyboardWidget { // Compact mode only ever draws 2 short hint lines (no grid, no status line // -- see below), but reserves at least as much height as the smallest real // grid (T9's 3 rows) would need, rather than shrinking to just those 2 - // lines: cursor_mode's own 3-line hint (drawn in this same region, - // regardless of Compact, for a physical-button user) already relies on - // that floor and would otherwise get clipped. + // lines, so the preview keeps the same height whichever mode is on. // A tall screen (the 4.2" panel: 20 lines) gives the keys half a line more each. const int key_lh = display.height() >= 18 * lh ? lh * 3 / 2 : lh; const int kb_h = compact_ui ? (KB_T9_ROWS + 1) * lh : (rows + 1) * key_lh; @@ -1077,13 +1076,7 @@ struct KeyboardWidget { // no ambiguity to resolve at this layer -- pollCardKB() already decided. if (c == KEY_KB_ENTER) return DONE; if (c == 0x08) { - t9_cell = -1; // invalidate any pending T9 cycle -- see the grid paths below - if (cursor_pos > 0) { - int n = kbUtf8LastCharBytes(buf, cursor_pos); - memmove(buf + cursor_pos - n, buf + cursor_pos, len - cursor_pos); - len -= n; cursor_pos -= n; - buf[len] = '\0'; - } + deleteBeforeCursor(); // also ends any pending T9 cycle -- see the grid paths below return NONE; } if (c >= 0x20 && c <= 0x7E) { @@ -1140,13 +1133,7 @@ struct KeyboardWidget { } // special row if (col == 0) { - t9_cell = -1; - if (cursor_pos > 0) { - int n = kbUtf8LastCharBytes(buf, cursor_pos); - memmove(buf + cursor_pos - n, buf + cursor_pos, len - cursor_pos); - len -= n; cursor_pos -= n; - buf[len] = '\0'; - } + deleteBeforeCursor(); } else if (col == 1) { pin_mode = false; // ABC switch: the user's own layout row = col = 0; @@ -1304,14 +1291,7 @@ struct KeyboardWidget { buf[len] = '\0'; } break; - case 2: - if (cursor_pos > 0) { - int n = kbUtf8LastCharBytes(buf, cursor_pos); - memmove(buf + cursor_pos - n, buf + cursor_pos, len - cursor_pos); - len -= n; cursor_pos -= n; - buf[len] = '\0'; - } - break; + case 2: deleteBeforeCursor(); break; case 3: openPlaceholders(); break; diff --git a/examples/companion_radio/ui-new/MessagesScreen.h b/examples/companion_radio/ui-new/MessagesScreen.h index a0a8ab5c..ef7b8053 100644 --- a/examples/companion_radio/ui-new/MessagesScreen.h +++ b/examples/companion_radio/ui-new/MessagesScreen.h @@ -1063,13 +1063,6 @@ public: _dm_hist_scroll = 0; _dm_fs.active = false; } - // The display came back on over a conversation: what was filed while it was - // dark is unread -- read on from its first message, as on opening. - void onDisplayOn() { - if (_ctx_menu.active) return; - if (_phase == CHANNEL_HIST && !_fs.active && _history.chUnread(_sel_channel_idx) > 0) beginChannelView(_sel_channel_idx); - else if (_phase == DM_HIST && !_dm_fs.active && _task->getDMUnread(_sel_contact.id.pub_key) > 0) beginDmView(); - } void beginChannelView(uint8_t channel_idx) { _sel_channel_idx = channel_idx; _unread_at_entry = (int)_history.chUnread(channel_idx); @@ -1081,6 +1074,13 @@ public: _hist_sel = _new_idx >= 0 ? _new_idx : _history.histCountForChannel(channel_idx) > 0 ? 0 : -1; _fs.active = false; } + // The display came back on over a conversation: what was filed while it was + // dark is unread -- read on from its first message, as on opening. + void onDisplayOn() { + if (_ctx_menu.active) return; + if (_phase == CHANNEL_HIST && !_fs.active && _history.chUnread(_sel_channel_idx) > 0) beginChannelView(_sel_channel_idx); + else if (_phase == DM_HIST && !_dm_fs.active && _task->getDMUnread(_sel_contact.id.pub_key) > 0) beginDmView(); + } // On the first unread (sel), opening: the scroll showing it highest with // "New" above it (stack(s) lays out from s, sets div_y when "New" fits). diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 412f2ca6..0beb5ec3 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -2280,13 +2280,14 @@ void UITask::pickBotRoomTarget() { MyMesh::Listener* UITask::meshListener() { return _core; } // On screen: a conversation left open under the lock or a dark display isn't read. +bool UITask::messagesInView() const { + return curr == messages_screen && _display && _display->isOn() && !_locked; +} bool UITask::isViewingChannel(uint8_t channel_idx) { - return curr == messages_screen && _display && _display->isOn() && !_locked - && ((MessagesScreen*)messages_screen)->isViewingChannel(channel_idx); + return messagesInView() && ((MessagesScreen*)messages_screen)->isViewingChannel(channel_idx); } bool UITask::isViewingDM(const uint8_t* pub_key) { - return curr == messages_screen && _display && _display->isOn() && !_locked - && ((MessagesScreen*)messages_screen)->isViewingDM(pub_key); + return messagesInView() && ((MessagesScreen*)messages_screen)->isViewingDM(pub_key); } void UITask::onViewedHistoryGrew(bool channel) { ((MessagesScreen*)messages_screen)->onViewedHistoryGrew(channel); @@ -3170,8 +3171,8 @@ void UITask::loop() { pollConnection(); // BLE link state -> hasConnection() (see UITaskBase) drainCoreEvents(); // react to what the Core filed during mesh processing (alerts, wake, sounds) { // a conversation in view again (display on, unlocked): on to what came in meanwhile - bool on = _display && _display->isOn() && !_locked; - if (on && !_display_was_on && curr == messages_screen) ((MessagesScreen*)messages_screen)->onDisplayOn(); + bool on = messagesInView(); + if (on && !_display_was_on) ((MessagesScreen*)messages_screen)->onDisplayOn(); _display_was_on = on; } if (status_screen) ((StatusScreen*)status_screen)->sample(); // Status history lines, once a minute diff --git a/examples/companion_radio/ui-new/UITask.h b/examples/companion_radio/ui-new/UITask.h index 04a61bff..d04be9f5 100644 --- a/examples/companion_radio/ui-new/UITask.h +++ b/examples/companion_radio/ui-new/UITask.h @@ -60,10 +60,11 @@ class UITask : public UITaskBase, public UiCoreHost { GenericVibration vibration; #endif unsigned long _next_refresh, _auto_off; - bool _display_was_on = true; // loop(): the display back on and unlocked + bool _display_was_on = true; // loop(): messagesInView() last time round NodePrefs* _node_prefs; bool _locked; bool _tool_from_home = false; + bool messagesInView() const; // Messages on a lit, unlocked display unsigned long _lock_wake_until; // when to blank screen again after locked wake (5s) int _lock_seq_count; // Enter presses while Back held (lock/unlock sequence) unsigned long _lock_seq_ms; // millis() of last lock-sequence press (for timeout)