This commit is contained in:
Kaj Schittecat
2026-08-27 10:28:15 +02:00
7 changed files with 367 additions and 8 deletions
+7
View File
@@ -61,6 +61,13 @@
#define LV_FONT_MONTSERRAT_20 1
#define LV_FONT_MONTSERRAT_24 1
#endif
/* T-Deck-only, unrelated to the Large/Huge UI-scale block above: an experiment
* to shrink the "at a glance" notification's message body from 28px to 20px
* on this board only (see atGlanceEnsureFont() in UITask.cpp). Easy to revert
* by dropping this block + the T-Deck branch in atGlanceEnsureFont(). */
#if defined(HAS_TDECK_GT911)
#define LV_FONT_MONTSERRAT_20 1
#endif
/* 28 px Montserrat for the boot splash title — keeps the rest of the UI on
* the smaller fonts so the .data cost stays modest. */
#define LV_FONT_MONTSERRAT_28 1
+9
View File
@@ -613,6 +613,15 @@ build_flags =
-D HAS_TDECK_GT911=1
-D HAS_TDECK_TRACKBALL=1
-D HAS_TDECK_KEYBOARD=1
; Forced here (not just in lv_conf.h) because .pio/libdeps/<env>/MeshCore/include/lv_conf.h
; -- a stale snapshot vendored by the pinned core tag -- sits earlier in the -I search
; order than this repo's own include/lv_conf.h, so LV_CONF_INCLUDE_SIMPLE's quoted
; #include "lv_conf.h" silently resolves to that stale copy for src/ compiles (though
; library .c files, e.g. lvgl's own, still see the right one -- inconsistent per TU). A
; -D here is immune to that: it's set before any header is processed and isn't
; overridden by a plain #define. Only 20px is forced, for the "at a glance" experiment
; below (atGlanceEnsureFont() in UITask.cpp) -- not 18/24, which this board doesn't use.
-D LV_FONT_MONTSERRAT_20=1
-D ENABLE_ADVERT_ON_BOOT=0
-D PIN_TB_UP=15
-D PIN_TB_DOWN=3
+4
View File
@@ -1849,6 +1849,10 @@ bool touchPrefsGetEdgeScroll() { if (!s_begun) touchPrefsBegin(); return s_
void touchPrefsSetEdgeScroll(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("tb_edgesc", on ? 1 : 0); }
bool touchPrefsGetLockOnScreenOff() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("lock_off", 0) != 0; }
void touchPrefsSetLockOnScreenOff(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("lock_off", on ? 1 : 0); }
bool touchPrefsGetGlanceWhenLocked() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("glance_lck", 0) != 0; }
void touchPrefsSetGlanceWhenLocked(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("glance_lck", on ? 1 : 0); }
bool touchPrefsGetGlanceEnabled() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("glance_en", 1) != 0; }
void touchPrefsSetGlanceEnabled(bool on) { if (!s_begun) touchPrefsBegin(); prefsPutUChar("glance_en", on ? 1 : 0); }
#if defined(HAS_TANMATSU) // only the Tanmatsu has the message LED — keep S3 (T-Deck/V4) bins unchanged
bool touchPrefsGetMsgLed() { if (!s_begun) touchPrefsBegin(); return s_prefs.getUChar("msg_led", 1) != 0; } // default ON
+4
View File
@@ -451,6 +451,10 @@ bool touchPrefsGetEdgeScroll(); // push cursor past edge to scrol
void touchPrefsSetEdgeScroll(bool on);
bool touchPrefsGetLockOnScreenOff(); // idle screen-off auto-locks; only a deliberate hold wakes (default false)
void touchPrefsSetLockOnScreenOff(bool on);
bool touchPrefsGetGlanceWhenLocked(); // "at a glance" also fires while manually/idle locked, not just unlocked+dimmed (default false)
void touchPrefsSetGlanceWhenLocked(bool on);
bool touchPrefsGetGlanceEnabled(); // master "at a glance" feature toggle (default true)
void touchPrefsSetGlanceEnabled(bool on);
/** Per-channel mute, keyed by channel name. Bit 0 = mute messages, bit 1 =
* mute @-mentions. Suppresses the notification SOUND for that channel (the
+333 -2
View File
@@ -507,6 +507,18 @@ extern "C" const lv_font_t cc_icons_16;
extern "C" const lv_font_t extras_12;
extern "C" const lv_font_t extras_14;
extern "C" const lv_font_t extras_16;
// extras_lat_28 used to be compiled HAS_TANMATSU-only (Large/Huge UI-scale Latin-
// accent fallback, issue #129); it's now compiled on every board (extras_lat_28.c)
// for the "at a glance" notification's message body (atGlanceEnsureFont()) --
// montserrat_28 was picked over 24 because only 12/14/16/28 are actually built
// into this project's vendored LVGL config (LV_FONT_MONTSERRAT_24 isn't enabled).
extern "C" const lv_font_t extras_lat_28;
// extras_lat_20 was HAS_TANMATSU-only too (Large/Huge UI-scale fallback); the
// T-Deck now also builds it, for its 20px at-glance body experiment (see
// atGlanceEnsureFont()) -- extras_lat_20.c's own gate was widened to match.
#if defined(HAS_TANMATSU) || defined(HAS_TDECK_GT911)
extern "C" const lv_font_t extras_lat_20;
#endif
#if defined(TLORA_PAGER)
// Full multilingual fallbacks at the Pager's accessible text sizes. These keep
// accented Latin, Greek, Cyrillic, Arabic, punctuation, and units aligned with
@@ -519,9 +531,7 @@ extern "C" const lv_font_t extras_24;
// neighbours at Large/Huge UI scale (issue #129). Room for these was made by
// storing the extras fonts compressed (LV_USE_FONT_COMPRESSED) — nothing lost,
// net binary SMALLER, so the P4 app-load ceiling is respected.
extern "C" const lv_font_t extras_lat_20;
extern "C" const lv_font_t extras_lat_24;
extern "C" const lv_font_t extras_lat_28;
#endif
static lv_font_t g_font_12;
static lv_font_t g_font_14;
@@ -2307,6 +2317,8 @@ struct SettingsModalState {
lv_obj_t* exp_boost_sw;
lv_obj_t* exp_dc_sw;
lv_obj_t* wifi_sw;
lv_obj_t* glance_en_sw;
lv_obj_t* glance_locked_sw;
lv_obj_t* wifi_ssid_ta;
lv_obj_t* wifi_pwd_ta;
/** Transports modal: live STA line (IP when connected, else Arduino WiFi status). */
@@ -11840,6 +11852,38 @@ static void lockOnScreenOffToggleCb(lv_event_t* e) {
if (g_lv.task) g_lv.task->showAlert(unlock_hint, 1600);
}
// "At a glance" is read straight from NVS at message time (not cached), same as
// the sound/DND checks right next to its call site in newMsgImpl -- it's not a
// per-loop hot path, just once per inbound message.
static void glanceWhenLockedToggleCb(lv_event_t* e) {
if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
const bool on = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED);
#if defined(ESP32)
touchPrefsSetGlanceWhenLocked(on);
#endif
if (g_lv.task) {
g_lv.task->showAlert(on ? TR("Glance shows message previews\nwhile the screen is locked")
: TR("Glance only shows when unlocked"), 1600);
}
}
// Master "At a glance" toggle (Options > Display): gates the whole feature.
// The secondary "while locked" switch's own saved preference is left
// alone -- only its editability follows the master, so re-enabling always
// restores its true prior state.
static void glanceEnabledToggleCb(lv_event_t* e) {
if (lv_event_get_code(e) != LV_EVENT_VALUE_CHANGED) return;
const bool on = lv_obj_has_state(lv_event_get_target(e), LV_STATE_CHECKED);
#if defined(ESP32)
touchPrefsSetGlanceEnabled(on);
#endif
if (g_set_modal.glance_locked_sw) {
if (on) lv_obj_clear_state(g_set_modal.glance_locked_sw, LV_STATE_DISABLED);
else lv_obj_add_state(g_set_modal.glance_locked_sw, LV_STATE_DISABLED);
}
if (g_lv.task) g_lv.task->showAlert(on ? TR("At a glance enabled") : TR("At a glance disabled"), 1200);
}
#if CAP_TRACKBALL
// Invert the scrollball direction everywhere it drives motion (cursor, map pan,
// emoji selector). Cached in s_tb_reverse so the per-tick poll never hits NVS.
@@ -12929,6 +12973,41 @@ static void buildDeviceSettings(int sec) {
y += LV_MAX(40, h + 12);
}
/* At a glance: master enable for the "at a glance" message-preview overlay
(see atGlanceShow()/newMsgImpl()) -- on by default, matching the
previously-unconditional behavior. Gates the "while locked" switch below. */
bool glance_on = true;
{
int h = settingsRowLabel(body, y, 6, TR("At a glance"), COLOR_SUB, nullptr, 56);
g_set_modal.glance_en_sw = lv_switch_create(body);
lv_obj_align(g_set_modal.glance_en_sw, LV_ALIGN_TOP_RIGHT, 0, y);
#if defined(ESP32)
glance_on = touchPrefsGetGlanceEnabled();
if (glance_on) lv_obj_add_state(g_set_modal.glance_en_sw, LV_STATE_CHECKED);
#endif
lv_obj_add_event_cb(g_set_modal.glance_en_sw, glanceEnabledToggleCb, LV_EVENT_VALUE_CHANGED, nullptr);
y += LV_MAX(40, h + 12);
}
#if !defined(HAS_TDISPLAY_P4)
/* "At a glance" also fires while manually/idle locked, not just while
unlocked+dimmed -- off by default since it's a real trade-off (message
text becomes readable off a locked device). Disabled (not cleared) while
the master switch above is off. Moved here from Options > Lock screen so
it sits with the feature it modifies. */
{
int h = settingsRowLabel(body, y, 6, TR("At a glance while locked"), COLOR_SUB, nullptr, 56);
g_set_modal.glance_locked_sw = lv_switch_create(body);
lv_obj_align(g_set_modal.glance_locked_sw, LV_ALIGN_TOP_RIGHT, 0, y);
#if defined(ESP32)
if (touchPrefsGetGlanceWhenLocked()) lv_obj_add_state(g_set_modal.glance_locked_sw, LV_STATE_CHECKED);
if (!glance_on) lv_obj_add_state(g_set_modal.glance_locked_sw, LV_STATE_DISABLED);
#endif
lv_obj_add_event_cb(g_set_modal.glance_locked_sw, glanceWhenLockedToggleCb, LV_EVENT_VALUE_CHANGED, nullptr);
y += LV_MAX(40, h + 12);
}
#endif
#if defined(HAS_EXPANSION_KIT)
/* Show Sensors tab (V4 Expansion Kit): toggles the bottom Sensors tab + the
Home env widget. Also auto-hidden when no environment sensor is attached.
@@ -52266,6 +52345,169 @@ void consoleHostRebootToUi() {
void UITask::msgRead(int msgcount) { _msgcount = msgcount; }
// ---- "At a glance" notification --------------------------------------------
// New, board-agnostic feature (distinct from the T-Deck's existing opt-in
// "msgFlash", which fully wakes to the live app + pulses the keyboard
// backlight): while the device is UNLOCKED but its screen has idle-dimmed to
// off, an incoming message (DND permitting) briefly lights a plain black
// overlay showing just the channel/sender name and the message text, then
// dims back out on its own after a fixed window -- a glance, not a wake.
// Never opens the chat, so the message stays unread, and whatever tab/chat
// was open underneath is left exactly as it was. atGlanceShow() below only
// builds/updates the overlay; UITask::newMsgImpl() decides whether to fire it
// (and wakes the screen + arms the auto-hide window); UITask::loop() clears it.
static lv_obj_t* s_glance_root = nullptr;
static lv_obj_t* s_glance_title = nullptr;
static lv_obj_t* s_glance_body = nullptr;
static uint32_t s_glance_lit_ms = 0; // millis() when (re)shown, 0 = not showing -- loop() re-dims 5s later
static bool s_glance_fading_out = false; // one-shot guard: the 200ms fade-out anim has already been started
static void atGlanceOpaCb(void* var, int32_t v) {
lv_obj_set_style_opa(static_cast<lv_obj_t*>(var), static_cast<lv_opa_t>(v), LV_PART_MAIN);
}
// Message-body font, built once: stock Montserrat 28 (ASCII) -> extras_lat_28
// (accented Latin, em-dash, ellipsis -- now compiled on every board, not just the
// Tanmatsu; see extras_lat_28.c) -> extras_16 tail (Cyrillic/Greek/Arabic, baked at
// 16 px so those scripts render a bit small relative to Latin text, but never a
// missing-glyph box). 28, not 24: only 12/14/16/28 are actually enabled in this
// project's vendored LVGL config (LV_FONT_MONTSERRAT_24 isn't). g_font_16 itself
// is deliberately left alone -- it's the UI-scale-driven font hundreds of OTHER
// widgets use, not something to repurpose for one feature.
//
// T-Deck experiment: 20px instead of 28px (LV_FONT_MONTSERRAT_20 + extras_lat_20
// widened for this board specifically, see lv_conf.h/extras_lat_20.c) -- trying
// a smaller glance body on this panel. Revert by dropping this branch + those
// two gate widenings.
static lv_font_t s_glance_body_font;
static bool s_glance_font_ready = false;
static void atGlanceEnsureFont() {
if (s_glance_font_ready) return;
s_glance_font_ready = true;
#if defined(HAS_TDECK_GT911)
static lv_font_t s_lat20;
s_lat20 = extras_lat_20;
s_lat20.fallback = &extras_16;
s_glance_body_font = lv_font_montserrat_20;
s_glance_body_font.fallback = &s_lat20;
#else
static lv_font_t s_lat28;
s_lat28 = extras_lat_28;
s_lat28.fallback = &extras_16;
s_glance_body_font = lv_font_montserrat_28;
s_glance_body_font.fallback = &s_lat28;
#endif
}
static void atGlanceHide() {
if (s_glance_root) {
// Only the TEXT fades (see atGlanceShow()) -- the root's own opa is never
// touched, so nothing to reset on it. Just cancel any in-flight label fades.
if (s_glance_title) { lv_anim_del(s_glance_title, atGlanceOpaCb); lv_obj_set_style_opa(s_glance_title, LV_OPA_COVER, LV_PART_MAIN); }
if (s_glance_body) { lv_anim_del(s_glance_body, atGlanceOpaCb); lv_obj_set_style_opa(s_glance_body, LV_OPA_COVER, LV_PART_MAIN); }
lv_obj_add_flag(s_glance_root, LV_OBJ_FLAG_HIDDEN);
}
s_glance_lit_ms = 0;
s_glance_fading_out = false;
}
// `fade_in`: true on the first reveal of a burst (screen was off), false when
// a later message in the same burst just updates the text on an already-lit
// overlay -- no need to re-fade something already fully visible.
static void atGlanceShow(const char* title, const char* body, bool fade_in) {
if (!g_lv.ready) return;
if (!s_glance_root) {
s_glance_root = lv_obj_create(lv_layer_top());
lv_obj_remove_style_all(s_glance_root);
lv_obj_set_style_bg_color(s_glance_root, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(s_glance_root, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_clear_flag(s_glance_root, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(s_glance_root, LV_OBJ_FLAG_CLICKABLE); // absorb taps -- no UI leak underneath
// Never a keyboard/encoder nav target on any board: same reasoning as the
// lock screen's NAV_SKIP_FLAG (see lockscreenShow()) -- a CLICKABLE
// top-layer overlay with nothing to navigate to would otherwise be
// collected as the sole focus target, and navFocusCb's focus-highlight
// would invert its text to dark the instant the nav group (re)builds.
lv_obj_add_flag(s_glance_root, NAV_SKIP_FLAG);
// Small "eyebrow" label (channel/sender) above the message, in the app's
// accent colour -- the message itself is the thing that needs to be
// readable from a few feet away (desk-mounted device), so it gets the
// dominant size/position below and this stays a secondary context cue.
s_glance_title = lv_label_create(s_glance_root);
lv_obj_set_style_text_font(s_glance_title, &g_font_16, LV_PART_MAIN);
lv_obj_set_style_text_color(s_glance_title, lv_color_hex(COLOR_ACCENT), LV_PART_MAIN);
lv_obj_set_width(s_glance_title, lv_pct(85));
lv_obj_set_style_text_align(s_glance_title, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN);
lv_label_set_long_mode(s_glance_title, LV_LABEL_LONG_DOT); // single line, ellipsize -- guaranteed to fit every board's width
lv_obj_align(s_glance_title, LV_ALIGN_TOP_LEFT, 14, 30); // below the ~22 px status bar band (the real status bar renders above this overlay)
// Message body: 28 px (bumped up from 16 -- 16 wasn't legible from a few
// feet away on a desk-mounted device) via atGlanceEnsureFont()'s own
// fallback chain, so arbitrary chat text (accents, em-dash, ellipsis)
// still never shows a missing-glyph box at this size.
atGlanceEnsureFont();
s_glance_body = lv_label_create(s_glance_root);
lv_obj_set_style_text_font(s_glance_body, &s_glance_body_font, LV_PART_MAIN);
lv_obj_set_style_text_color(s_glance_body, lv_color_hex(0xFFFFFFu), LV_PART_MAIN);
lv_obj_set_width(s_glance_body, lv_pct(85));
lv_obj_set_style_text_align(s_glance_body, LV_TEXT_ALIGN_LEFT, LV_PART_MAIN);
// LV_LABEL_LONG_DOT only wraps across multiple lines (dot-ellipsizing the LAST
// one on overflow) when the label has a fixed HEIGHT, not just a fixed width --
// left at auto/content height (as this was), it sizes to exactly one line, so
// every board was effectively single-line regardless of panel size (reported:
// text cropped at the end of the first line instead of continuing on a second).
// Give it the actual remaining space below this label's own y-offset down to a
// small bottom margin, computed from this board's resolution + active body font,
// so it uses exactly what's available -- more lines on tall panels, fewer on the
// 222 px-tall Pager -- instead of a guessed fixed line count.
{
const lv_coord_t avail_h = lv_disp_get_ver_res(nullptr) - 60 - 10;
const lv_coord_t line_h = lv_font_get_line_height(&s_glance_body_font);
lv_obj_set_height(s_glance_body, avail_h > line_h ? avail_h : line_h);
}
lv_label_set_long_mode(s_glance_body, LV_LABEL_LONG_DOT);
lv_obj_align(s_glance_body, LV_ALIGN_TOP_LEFT, 14, 60); // a bit more clearance now the title above is 16 px, not 12
}
const lv_coord_t sw = lv_disp_get_hor_res(nullptr);
const lv_coord_t sh = lv_disp_get_ver_res(nullptr);
lv_obj_set_size(s_glance_root, sw, sh);
lv_obj_set_pos(s_glance_root, 0, 0);
lv_label_set_text(s_glance_title, title ? title : "");
lv_label_set_text(s_glance_body, body ? body : "");
// Cancel any fade-out already begun (by an earlier message in this burst) on
// either label.
lv_anim_del(s_glance_title, atGlanceOpaCb);
lv_anim_del(s_glance_body, atGlanceOpaCb);
lv_obj_clear_flag(s_glance_root, LV_OBJ_FLAG_HIDDEN);
lv_obj_move_foreground(s_glance_root);
if (fade_in) {
// Only the TEXT fades in, never the root: the root's bg_opa is COVER (solid
// black) from the moment it's created and stays that way, so the very first
// frame -- forced-painted BEFORE the backlight comes on (see newMsgImpl()) --
// is already a clean black screen, not a transparent one letting the live
// app screen show through underneath for a frame (reported: "I see the app
// screen then I see the at-glance"). Fading the whole root's opa used to fade
// the background along with the text, which is what caused that.
lv_obj_set_style_opa(s_glance_title, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_set_style_opa(s_glance_body, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_t* targets[2] = { s_glance_title, s_glance_body };
for (lv_obj_t* t : targets) {
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, t);
lv_anim_set_time(&a, 180);
lv_anim_set_values(&a, LV_OPA_TRANSP, LV_OPA_COVER);
lv_anim_set_exec_cb(&a, atGlanceOpaCb);
lv_anim_start(&a);
}
} else {
// Already visible -- just swap the text, no re-fade.
lv_obj_set_style_opa(s_glance_title, LV_OPA_COVER, LV_PART_MAIN);
lv_obj_set_style_opa(s_glance_body, LV_OPA_COVER, LV_PART_MAIN);
}
}
// Shared core for newMsg / newMsgFromPubWithMeta. Bundles the channel-vs-DM
// sender parsing + thread routing in one place so the meta-aware path doesn't
// drift from the plain path. `meta_flags` is 0 (no RX metadata) when called
@@ -52368,6 +52610,61 @@ void UITask::newMsgImpl(uint8_t path_len, const char* from_name, const char* tex
else if (touchPrefsGetSoundMessages() && !(cmute & TOUCH_CHMUTE_MSG)) uiPlaySlot(TOUCH_SND_MSG);
}
}
#endif
#if defined(HAS_TOUCH_UI)
// "At a glance": device unlocked but idle-dimmed to off -- light the plain
// black glance overlay instead of the full app (see atGlanceShow() above).
// Master-disabled via Options > Display > "At a glance"
// (touchPrefsGetGlanceEnabled()) skips this entirely. Skipped if DND is on.
// Manually locked is ALSO skipped unless the user opted in via Options >
// Display > "At a glance while locked" (touchPrefsGetGlanceWhenLocked())
// -- off by default since it's a real trade-off (message text becomes
// readable off a locked device). `_screen_off` covers the first message of
// a burst (wake + show); `s_glance_lit_ms` (already showing, from an
// earlier message in the same window) covers later ones -- update the text
// and restart the 5 s window instead of dropping them, since our own wake
// already cleared _screen_off by then.
#if defined(ESP32)
const bool glance_enabled_ok = touchPrefsGetGlanceEnabled();
const bool glance_locked_ok = !_manual_lock || touchPrefsGetGlanceWhenLocked();
#else
const bool glance_enabled_ok = true;
const bool glance_locked_ok = !_manual_lock;
#endif
if (glance_enabled_ok && !dndActive() && glance_locked_ok && (_screen_off || s_glance_lit_ms)) {
const bool was_off = _screen_off;
atGlanceShow(thread, body, was_off); // fade in only on the initial reveal of a burst
if (was_off) {
lv_refr_now(nullptr); // paint before the backlight comes on -- no stale-frame flash
if (_manual_lock) {
// Locked: light the panel WITHOUT clearing the lock -- wakeScreen()
// unconditionally clears _manual_lock, which would silently unlock the
// device (touch/input live again) just to show a glance. Mirrors
// lockscreenReveal()'s peek path instead: same fields, lock untouched.
// The auto-hide re-dim below (UITask::loop()) is symmetric -- it never
// touches _manual_lock either, so the device is exactly as locked after
// the glance as before it.
setCpuForScreen(true);
touchScreenBacklight(true);
_screen_off = false;
// MUST bump this even though it's not real input: loop()'s generic idle
// screen-timeout check fires on !_screen_off && (now - _last_input_ms) >=
// _screen_timeout_ms regardless of _manual_lock -- leaving it stale (from
// before the lock) made that check true on the very next loop() tick,
// instantly flipping the backlight back off before the glance was ever
// seen (reported: sound played but nothing showed). wakeScreen() bumps
// this for the same reason; omitting it here was the bug.
_last_input_ms = millis();
#if CAP_LOCK_SCREEN
_lock_lit_ms = millis(); // keep the lock screen's own burn-in guard armed too
#endif
} else {
wakeScreen();
}
}
s_glance_lit_ms = millis();
s_glance_fading_out = false; // a new message cancels any fade-out already begun
}
#endif
// Inbound route (repeater hashes) for the Info popup — flood RX only; read
// synchronously from MyMesh, which stashed it just before this call.
@@ -53362,6 +53659,40 @@ void UITask::loop() {
}
#endif // HAS_TDECK_KEYBOARD || HAS_M9_KEYBOARD (notify-wake re-dim)
// "At a glance" auto-hide: same re-dim shape as the msgflash window above,
// but this feature's own fixed 5 s window (the last 200 ms of it spent
// fading out, not a hard cut) and unconditional on every board (see
// atGlanceShow()/newMsgImpl() above). Real input during the window hands
// off to the user actually looking at the screen now -- don't snatch it
// back off from underneath them; just stop tracking it and let the normal
// idle timeout (already bumped by that real input) take over.
if (s_glance_lit_ms) {
if (_screen_off) atGlanceHide(); // already dark again some other way
else if (_last_input_ms > s_glance_lit_ms) atGlanceHide(); // user took over
else {
const int32_t elapsed = (int32_t)(now - s_glance_lit_ms);
if (!s_glance_fading_out && elapsed >= 4800) {
s_glance_fading_out = true;
lv_obj_t* targets[2] = { s_glance_title, s_glance_body }; // text only -- see atGlanceShow()
for (lv_obj_t* t : targets) {
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, t);
lv_anim_set_time(&a, 200);
lv_anim_set_values(&a, LV_OPA_COVER, LV_OPA_TRANSP);
lv_anim_set_exec_cb(&a, atGlanceOpaCb);
lv_anim_start(&a);
}
}
if (elapsed >= 5000) {
atGlanceHide();
touchScreenBacklight(false);
setCpuForScreen(false);
_screen_off = true;
}
}
}
#if defined(HAS_TOUCH_UI)
if (!g_lv.ready) return;
+6 -2
View File
@@ -1,5 +1,9 @@
#include "device_caps.h"
#if defined(HAS_TANMATSU)
// Was HAS_TANMATSU-only (Large/Huge UI-scale accented-Latin fallback); the T-Deck
// now also builds this for the "at a glance" notification's 20 px message body
// (see atGlanceEnsureFont() in UITask.cpp) -- an experiment to see whether a
// smaller-than-28px glance body is still legible on that panel.
#if defined(HAS_TANMATSU) || defined(HAS_TDECK_GT911)
/*******************************************************************************
* Size: 20 px
@@ -2563,4 +2567,4 @@ lv_font_t extras_lat_20 = {
#endif /*#if EXTRAS_LAT_20*/
#endif /* HAS_TANMATSU */
#endif /* HAS_TANMATSU || HAS_TDECK_GT911 */
+4 -4
View File
@@ -1,5 +1,8 @@
#include "device_caps.h"
#if defined(HAS_TANMATSU)
// Was HAS_TANMATSU-only (only consumer used to be the Tanmatsu's Large/Huge UI-scale
// accented-Latin fallback); now compiled on every board too for the "at a glance"
// notification's 28 px message body (see atGlanceEnsureFont() in UITask.cpp), which
// needs accented Latin / em-dash / ellipsis glyph coverage at that size on any board.
/*******************************************************************************
* Size: 28 px
@@ -3637,6 +3640,3 @@ lv_font_t extras_lat_28 = {
#endif /*#if EXTRAS_LAT_28*/
#endif /* HAS_TANMATSU */