fix(ui-lvgl): tighter info rows, clock repeat list fits, credits wrap cleanly

Info cards drop the default 8 px row gap so text sits centred between
hairlines. The alarm Repeat list is compacted to fit above the dropdown.
creditText() keeps "(c) Name" and licence names like CC-BY-SA together
in About and the map credits toast.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-27 00:42:35 +02:00
co-authored by Claude Opus 5.5
parent 02c2ba1313
commit e502a491f4
3 changed files with 28 additions and 4 deletions
@@ -168,6 +168,9 @@ void UITask::buildClock() {
lv_dropdown_set_options(dd, opts);
lv_dropdown_set_selected(dd, NodePrefs::alarmRepeatIdxForMask(_prefs->alarm_repeat_mask));
lv_obj_set_width(dd, 112);
lv_obj_t* list = lv_dropdown_get_list(dd); // opens upwards: tighter so all four fit under the status bar
lv_obj_set_style_pad_ver(list, 4, 0);
lv_obj_set_style_text_line_space(list, 12, 0);
lv_obj_add_event_cb(dd, onAlarmRepeat, LV_EVENT_VALUE_CHANGED, NULL);
bool synced = rtc_clock.getCurrentTime() > 1000000000UL;
label(body, synced ? "Rings at this local time (Settings time zone)." : "The clock isn't set yet: the alarm waits for a time sync.",
+1 -1
View File
@@ -154,7 +154,7 @@ static void onDlZoomPlus(lv_event_t* e) { (void)e; s_ui->mapDownloadZmax(+1); }
static void onDlResume(lv_event_t* e) { (void)e; s_ui->mapDownloadResume(); }
static void onDlDiscard(lv_event_t* e) { (void)e; s_ui->mapDownloadDiscard(); }
static void onNavTools(lv_event_t* e); // NavMap.h
static void onMapCredits(lv_event_t* e) { (void)e; s_ui->showToast(mapview::s_provider->attribution(), 4000); }
static void onMapCredits(lv_event_t* e) { (void)e; s_ui->showToast(creditText(mapview::s_provider->attribution()), 4000); }
static void onMapMarker(lv_event_t* e) {
if (mapview::s_drag > 8) return; // the press was a pan that ended on a marker
s_ui->mapOpenMarker((int)(uintptr_t)lv_event_get_user_data(e));
+24 -3
View File
@@ -183,9 +183,30 @@ static lv_obj_t* infoCard(lv_obj_t* parent) {
lv_obj_set_style_radius(c, theme::RADIUS, 0);
lv_obj_set_style_pad_hor(c, theme::PAD, 0);
lv_obj_set_flex_flow(c, LV_FLEX_FLOW_COLUMN);
lv_obj_set_style_pad_row(c, 0, 0); // the rows' own padding spaces them, around the hairline
return c;
}
// A credit line that wraps cleanly: "(c) Name" and "CC-BY-SA" never split.
static const char* creditText(const char* in) {
static char out[160];
size_t o = 0;
int paren = 0;
for (const char* p = in; *p && o + 4 < sizeof(out); p++) {
if (*p == '(') paren++;
else if (*p == ')' && paren) paren--;
if ((uint8_t)p[0] == 0xC2 && (uint8_t)p[1] == 0xA9 && p[2] == ' ') { // "(c) " -> "(c)" + no-break space
memcpy(out + o, "\xC2\xA9\xC2\xA0", 4); o += 4; p += 2;
} else if (*p == '-' && paren) { // licence names: a non-breaking hyphen
memcpy(out + o, "\xE2\x80\x91", 3); o += 3;
} else {
out[o++] = *p;
}
}
out[o] = '\0';
return out;
}
static lv_obj_t* infoLine(lv_obj_t* card) { // one row's box, the hairline above all but the first
lv_obj_t* r = lv_obj_create(card);
lv_obj_remove_style_all(r);
@@ -2801,9 +2822,9 @@ void UITask::buildSettings() {
infoRow(about, "Node", the_mesh.getNodeName());
infoRow(about, "Firmware", FIRMWARE_VERSION);
if (!strstr(FIRMWARE_VERSION, FIRMWARE_BUILD_DATE)) infoRow(about, "Built", FIRMWARE_BUILD_DATE);
infoNote(about, "Map data", (lvport::mountStorage() && mapview::s_provider->available())
? mapview::s_provider->attribution() : "\xC2\xA9 OpenStreetMap contributors");
infoNote(about, "Emoji", "Twemoji \xC2\xA9 Twitter, Inc. and contributors (CC\xE2\x80\x91" "BY\xC2\xA0" "4.0)");
infoNote(about, "Map data", creditText((lvport::mountStorage() && mapview::s_provider->available())
? mapview::s_provider->attribution() : "\xC2\xA9 OpenStreetMap contributors"));
infoNote(about, "Emoji", creditText("Twemoji \xC2\xA9 Twitter, Inc. and contributors (CC-BY\xC2\xA0" "4.0)"));
}
void UITask::setKeyboardAlphabets(int main_idx, int alt_sel) {