fix(ui-lvgl): unlock fades away on release, channel names shown as named

- Unlocking fades the lock screen out while the screen underneath drifts
  into place; the slider unlocks on letting go at the end and springs back
  when let go earlier
- Channel names no longer get a "# " prefix (thread title, channel popup,
  live share targets) -- "#" marks a hashtag channel; favourite contacts in
  the live share list are starred instead

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-26 13:18:43 +02:00
co-authored by Claude Opus 5.5
parent 3fd204489e
commit b99ca6b4ce
6 changed files with 35 additions and 14 deletions
+3
View File
@@ -9,11 +9,14 @@ namespace anim {
static const uint32_t POP_MS = 160; // popup / toast in
static const uint32_t OUT_MS = 120; // toast out
static const uint32_t PAGE_MS = 160; // Home page after a swipe
static const uint32_t UNLOCK_MS = 260; // lock screen away
static const uint32_t SPRING_MS = 200; // a let-go slider knob back home
static void setTy(void* o, int32_t v) { lv_obj_set_style_translate_y((lv_obj_t*)o, v, 0); }
static void setTx(void* o, int32_t v) { lv_obj_set_style_translate_x((lv_obj_t*)o, v, 0); }
static void setOpa(void* o, int32_t v) { lv_obj_set_style_opa((lv_obj_t*)o, (lv_opa_t)v, 0); }
static void setBgOpa(void* o, int32_t v) { lv_obj_set_style_bg_opa((lv_obj_t*)o, (lv_opa_t)v, 0); }
static void setSlider(void* o, int32_t v) { lv_slider_set_value((lv_obj_t*)o, v, LV_ANIM_OFF); }
static void run(lv_obj_t* o, lv_anim_exec_xcb_t cb, int32_t from, int32_t to, uint32_t ms,
lv_anim_completed_cb_t done = nullptr) {
@@ -76,7 +76,7 @@ void UITask::channelMenu(int idx) {
s_del_armed_ms = 0;
s_del_lbl = s_fav_btn = nullptr;
char title[40];
snprintf(title, sizeof(title), "%s%s", ch.name[0] == '#' ? "" : "# ", ch.name); // "#topic" has its own
snprintf(title, sizeof(title), "%s", ch.name); // as named: "#" marks a hashtag channel
lv_obj_t* panel = navPopupPanel(title, false);
// Notifications: three segments in the row (a dropdown's list ran off the
@@ -237,8 +237,19 @@ void UITask::unlockScreen() {
using namespace devview;
if (!s_lock) return;
lv_indev_wait_release(lv_indev_active()); // the slide isn't also a tap underneath
lv_obj_delete_async(s_lock);
s_lock = s_lock_clock = s_lock_date = s_lock_unread = s_lock_slider = s_pin_dots = s_pin_msg = nullptr;
// The lock's content goes at once and its background fades away as the
// screen underneath drifts up into place -- the same cheap cover fade as a
// screen change (Anim.h), which deletes the overlay at the end.
lv_obj_t* fading = s_lock;
for (uint32_t i = 0; i < lv_obj_get_child_count(fading); i++)
lv_obj_add_flag(lv_obj_get_child(fading, i), LV_OBJ_FLAG_HIDDEN);
lv_obj_remove_flag(fading, LV_OBJ_FLAG_CLICKABLE);
if (_asleep) lv_obj_delete_async(fading);
else {
anim::run(fading, anim::setBgOpa, LV_OPA_COVER, LV_OPA_TRANSP, anim::UNLOCK_MS, anim::coverDone);
if (_body) anim::run(_body, anim::setTy, 12, 0, anim::UNLOCK_MS);
}
s_lock =s_lock_clock = s_lock_date = s_lock_unread = s_lock_slider = s_pin_dots = s_pin_msg = nullptr;
_pin_entry[0] = '\0';
if (_screen == SCR_HOME) refreshHome();
else if (_screen == SCR_CHATS) buildChats(); // counts moved on while locked
@@ -246,9 +257,10 @@ void UITask::unlockScreen() {
bool UITask::locked() const { return devview::s_lock != nullptr; }
// From loop() while locked: the knob follows the finger. Tracked from the
// touch itself, like the Home page swipe -- LVGL stops feeding a pressed
// widget once the finger has moved past its scroll threshold.
// From loop() while locked: the knob follows the finger and unlocks when let
// go at the end (a knob let go earlier springs back). Tracked from the touch
// itself, like the Home page swipe -- LVGL stops feeding a pressed widget
// once the finger has moved past its scroll threshold.
void UITask::lockPoll() {
using namespace devview;
static bool s_dragging = false, s_was_down = false;
@@ -262,19 +274,22 @@ void UITask::lockPoll() {
int32_t knob = lv_area_get_height(&a); // round knob, as tall as the track
if (down && !s_was_down) { // a drag starts on the knob only
s_dragging = p.y >= a.y1 - 10 && p.y <= a.y2 + 10 && p.x <= a.x1 + knob + 16;
if (s_dragging) lv_anim_delete(s_lock_slider, anim::setSlider);
}
s_was_down = down;
if (!down) {
if (s_dragging || lv_slider_get_value(s_lock_slider) != 0) lv_slider_set_value(s_lock_slider, 0, LV_ANIM_OFF);
if (!s_dragging) return;
s_dragging = false;
int32_t v = lv_slider_get_value(s_lock_slider);
if (v >= 100) unlockScreen();
else if (v > 0) anim::run(s_lock_slider, anim::setSlider, v, 0, anim::SPRING_MS);
return;
}
if (!s_dragging) return;
int32_t span = lv_area_get_width(&a) - knob;
int32_t v = span > 0 ? (p.x - a.x1 - knob / 2) * 100 / span : 0;
v = v < 0 ? 0 : v > 100 ? 100 : v;
v = v < 0 ? 0 : v >= 92 ? 100 : v; // snaps to the end: let go to unlock
lv_slider_set_value(s_lock_slider, v, LV_ANIM_OFF);
if (v >= 95) { s_dragging = s_was_down = false; unlockScreen(); }
}
void UITask::refreshLock() {
+5 -4
View File
@@ -836,8 +836,9 @@ void UITask::navToolsPopup() {
_nav_share_lbl = label(list, "", THEME_FONT_SMALL, theme::TEXT);
lv_label_set_long_mode(_nav_share_lbl, LV_LABEL_LONG_WRAP);
lv_obj_set_width(_nav_share_lbl, LV_PCT(100));
// Target: channels, then favourite contacts.
char opts[MAX_GROUP_CHANNELS * 24 + 16 * 36];
// Target: channels (as named -- "#" marks a hashtag channel), then
// favourite contacts (starred, so the two stay apart).
char opts[MAX_GROUP_CHANNELS * 24 + 16 * 40];
int o = 0, sel = 0;
navmap::s_share_n = 0;
for (int i = 0; i < MAX_GROUP_CHANNELS && navmap::s_share_n < navmap::SHARE_TARGETS; i++) {
@@ -846,7 +847,7 @@ void UITask::navToolsPopup() {
if (_prefs->loc_share_target_type == 0 && _prefs->loc_share_channel_idx == i) sel = navmap::s_share_n;
navmap::s_share_kind[navmap::s_share_n] = 0;
navmap::s_share_ch[navmap::s_share_n++] = (uint8_t)i;
o += snprintf(opts + o, sizeof(opts) - o, "%s# %s", o ? "\n" : "", ch.name);
o += snprintf(opts + o, sizeof(opts) - o, "%s%s", o ? "\n" : "", ch.name);
}
int favs = 0;
for (int i = 0; i < the_mesh.getNumContacts() && favs < 16 && navmap::s_share_n < navmap::SHARE_TARGETS; i++) {
@@ -857,7 +858,7 @@ void UITask::navToolsPopup() {
memcmp(_prefs->loc_share_dm_prefix, c.id.pub_key, NodePrefs::FAVOURITE_PREFIX_LEN) == 0) sel = navmap::s_share_n;
navmap::s_share_kind[navmap::s_share_n] = 1;
memcpy(navmap::s_share_key[navmap::s_share_n++], c.id.pub_key, NodePrefs::FAVOURITE_PREFIX_LEN);
o += snprintf(opts + o, sizeof(opts) - o, "%s%s", o ? "\n" : "", c.name);
o += snprintf(opts + o, sizeof(opts) - o, "%s" UI_SYMBOL_STAR " %s", o ? "\n" : "", c.name);
favs++;
}
lv_obj_t* tr = lv_obj_create(list);
+1 -1
View File
@@ -1788,7 +1788,7 @@ void UITask::buildThread() {
bool can_send = true;
if (_thread_is_channel) {
ChannelDetails ch;
if (the_mesh.getChannel(_thread_channel, ch)) snprintf(title, sizeof(title), "%s%s", ch.name[0] == '#' ? "" : "# ", ch.name);
if (the_mesh.getChannel(_thread_channel, ch)) snprintf(title, sizeof(title), "%s", ch.name); // as named: "#" marks a hashtag channel
else snprintf(title, sizeof(title), "Channel %d", _thread_channel);
} else {
contactName(_thread_key, title, sizeof(title));