diff --git a/examples/companion_radio/ui-lvgl/ChannelScreen.h b/examples/companion_radio/ui-lvgl/ChannelScreen.h index 73dcda0c..e978ed93 100644 --- a/examples/companion_radio/ui-lvgl/ChannelScreen.h +++ b/examples/companion_radio/ui-lvgl/ChannelScreen.h @@ -270,7 +270,7 @@ void UITask::channelEditField(lv_obj_t* ta) { lv_obj_remove_flag(s_kb, LV_OBJ_FLAG_HIDDEN); if (_body) lv_obj_set_height(_body, lv_obj_get_height(_body) - lv_obj_get_height(s_kb)); } - lv_obj_update_layout(screen()); + layoutNow(screen()); lv_obj_scroll_to_view(ta, LV_ANIM_OFF); } diff --git a/examples/companion_radio/ui-lvgl/DeviceScreen.h b/examples/companion_radio/ui-lvgl/DeviceScreen.h index 5fde59fd..f25aef6e 100644 --- a/examples/companion_radio/ui-lvgl/DeviceScreen.h +++ b/examples/companion_radio/ui-lvgl/DeviceScreen.h @@ -392,7 +392,7 @@ void UITask::pinRowRefresh() { if (_screen != SCR_SETTINGS_NAV) return; buildSchemaSettings(); fillFlush(); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_by(_body, 0, -lv_obj_get_scroll_bottom(_body), LV_ANIM_OFF); } + if (_body) { layoutNow(_body); lv_obj_scroll_by(_body, 0, -lv_obj_get_scroll_bottom(_body), LV_ANIM_OFF); } } // ── Accent colour ───────────────────────────────────────────────────────────── diff --git a/examples/companion_radio/ui-lvgl/HomeScreen.h b/examples/companion_radio/ui-lvgl/HomeScreen.h index 0c4b6a54..3cda3b99 100644 --- a/examples/companion_radio/ui-lvgl/HomeScreen.h +++ b/examples/companion_radio/ui-lvgl/HomeScreen.h @@ -616,7 +616,7 @@ void UITask::buildHomeMap(lv_obj_t* box) { lv_obj_align(s_caption, LV_ALIGN_BOTTOM_LEFT, 6, -6); mapview::s_available = lvport::mountStorage() && mapview::s_provider->available(); mapview::s_trails_on = lvport::trailsOn(); - lv_obj_update_layout(s_area); + layoutNow(s_area); homeMapFit(); homeMapLayout(); } diff --git a/examples/companion_radio/ui-lvgl/MapAreas.h b/examples/companion_radio/ui-lvgl/MapAreas.h index c60a1cec..f03ee60a 100644 --- a/examples/companion_radio/ui-lvgl/MapAreas.h +++ b/examples/companion_radio/ui-lvgl/MapAreas.h @@ -418,7 +418,7 @@ void UITask::mapAreaPopup(int idx) { s_del_lbl = actButton(r, LV_SYMBOL_TRASH, "Delete", A_DELETE); // Framed in the map above the sheet: the view is kept to go back to. - lv_obj_update_layout(panel); + layoutNow(panel); int w = _map_area ? lv_obj_get_width(_map_area) : 320, h = _map_area ? lv_obj_get_height(_map_area) : 218; int sheet = lv_obj_get_height(panel) + 8; // its inset int above = h - sheet; diff --git a/examples/companion_radio/ui-lvgl/MapScreen.h b/examples/companion_radio/ui-lvgl/MapScreen.h index 1b125995..42a63335 100644 --- a/examples/companion_radio/ui-lvgl/MapScreen.h +++ b/examples/companion_radio/ui-lvgl/MapScreen.h @@ -493,7 +493,7 @@ void UITask::buildMap() { lv_obj_add_flag(_map_hint, LV_OBJ_FLAG_HIDDEN); if (_map_nav) buildNavControls(body); - lv_obj_update_layout(body); + layoutNow(body); mapview::s_mark_count = 0; // the previous markers went with the previous screen rebuildMapMarkers(); layoutMap(); @@ -691,7 +691,7 @@ void UITask::rebuildMapMarkers() { else rebuildNodeMarkers(); for (int k = mapview::s_mark_count; k < mapview::s_mark_prev; k++) lv_obj_delete(mapview::s_marks[k].obj); mapview::s_mark_prev = 0; - lv_obj_update_layout(_map_marks); // so layoutMap() knows where each dot sits in its marker + layoutNow(_map_marks); // so layoutMap() knows where each dot sits in its marker } void UITask::rebuildNodeMarkers() { diff --git a/examples/companion_radio/ui-lvgl/QuickScreen.h b/examples/companion_radio/ui-lvgl/QuickScreen.h index 18e463cd..ebbd1078 100644 --- a/examples/companion_radio/ui-lvgl/QuickScreen.h +++ b/examples/companion_radio/ui-lvgl/QuickScreen.h @@ -185,7 +185,7 @@ void UITask::quickEditDone(bool ok) { lv_obj_t* body = _body; int32_t y = body ? lv_obj_get_scroll_y(body) : 0; buildQuickMsgs(); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } + if (_body) { layoutNow(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } } } diff --git a/examples/companion_radio/ui-lvgl/RadioScreen.h b/examples/companion_radio/ui-lvgl/RadioScreen.h index 9ab2c665..c3c6cb34 100644 --- a/examples/companion_radio/ui-lvgl/RadioScreen.h +++ b/examples/companion_radio/ui-lvgl/RadioScreen.h @@ -139,7 +139,7 @@ void UITask::rebuildRadio() { int32_t y = _body ? lv_obj_get_scroll_y(_body) : 0; buildRadio(); fillFlush(); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } + if (_body) { layoutNow(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } } void UITask::radioFreqPopup(bool repeater) { diff --git a/examples/companion_radio/ui-lvgl/RepeaterScreen.h b/examples/companion_radio/ui-lvgl/RepeaterScreen.h index 22692548..02f74519 100644 --- a/examples/companion_radio/ui-lvgl/RepeaterScreen.h +++ b/examples/companion_radio/ui-lvgl/RepeaterScreen.h @@ -114,7 +114,7 @@ void UITask::rebuildRepeater() { int32_t y = _body ? lv_obj_get_scroll_y(_body) : 0; buildRepeater(); fillFlush(); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } + if (_body) { layoutNow(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } } void UITask::repeaterSet(int which, int v) { diff --git a/examples/companion_radio/ui-lvgl/SoundScreen.h b/examples/companion_radio/ui-lvgl/SoundScreen.h index 62ea5e9a..02470edc 100644 --- a/examples/companion_radio/ui-lvgl/SoundScreen.h +++ b/examples/companion_radio/ui-lvgl/SoundScreen.h @@ -107,7 +107,7 @@ static void rebuildStrip() { cellText(i); styleCell(i, i == s_sel); } - lv_obj_update_layout(s_strip); + layoutNow(s_strip); if (s_sel < s_mel.len) lv_obj_scroll_to_view(s_cells[s_sel], LV_ANIM_OFF); } diff --git a/examples/companion_radio/ui-lvgl/StorageScreen.h b/examples/companion_radio/ui-lvgl/StorageScreen.h index 86a35883..d778045d 100644 --- a/examples/companion_radio/ui-lvgl/StorageScreen.h +++ b/examples/companion_radio/ui-lvgl/StorageScreen.h @@ -196,7 +196,7 @@ static lv_obj_t* catRow(lv_obj_t* g, int c) { // Segment widths: each category's share of the card, the rest of `used` grey. static void showSegments(uint64_t total, uint64_t used) { if (!s_sd_bar || !total) return; - lv_obj_update_layout(s_sd_bar); + layoutNow(s_sd_bar); int w = lv_obj_get_width(s_sd_bar), x = 0; uint64_t counted = 0; for (int c = 0; c <= C_COUNT; c++) { diff --git a/examples/companion_radio/ui-lvgl/UITask.cpp b/examples/companion_radio/ui-lvgl/UITask.cpp index 67a92d96..efe59f2f 100644 --- a/examples/companion_radio/ui-lvgl/UITask.cpp +++ b/examples/companion_radio/ui-lvgl/UITask.cpp @@ -138,12 +138,18 @@ static void styleOpaque(lv_obj_t* o, uint32_t bg) { lv_obj_set_style_bg_opa(o, LV_OPA_COVER, 0); } +static bool growsWide(lv_obj_t* o) { // a flex grow along a row: the width is the layout's + lv_obj_t* p = lv_obj_get_parent(o); + if (!p || !lv_obj_get_style_flex_grow(o, LV_PART_MAIN) || lv_obj_get_style_layout(p, LV_PART_MAIN) != LV_LAYOUT_FLEX) return false; + lv_flex_flow_t f = lv_obj_get_style_flex_flow(p, LV_PART_MAIN); + return !(f & LV_FLEX_COLUMN); +} // The width of `o`'s content, worked out from the styles -- or -1 where it -// hangs on its content or a flex grow. A new box's own size is only known +// hangs on its content or a row's flex grow. A new box's own size is only known // after a layout pass, and LVGL lays out children before their parent, so // each box sized in % of the one above adds a whole pass over the screen. static int32_t innerW(lv_obj_t* o) { - if (!o || lv_obj_get_style_flex_grow(o, LV_PART_MAIN)) return -1; + if (!o || growsWide(o)) return -1; int32_t w = lv_obj_get_style_width(o, LV_PART_MAIN); if (LV_COORD_IS_PCT(w)) { int32_t pw = innerW(lv_obj_get_parent(o)); @@ -159,10 +165,27 @@ static int32_t innerW(lv_obj_t* o) { if (side & LV_BORDER_SIDE_RIGHT) w -= bw; return w; } +extern "C" bool lv_obj_refr_size(lv_obj_t* obj); // lv_obj_pos.c, in no header +// A wrapping label's size settled now, `w` wide. LVGL's first layout of a +// label works out its height at the width it had before -- 0 for a new one, +// a line per letter -- and only then measures it again at the new width. +static void labelFit(lv_obj_t* l, int32_t w) { + l->coords.x2 = l->coords.x1 + w - 1; + lv_obj_refr_size(l); +} +// A flex row whose one growing child gets its width before the first layout +// (rowsFit(), further down). +static bool s_fit_pending; +static lv_obj_t* s_fit_screen; // newScreen()'s, maybe still fading in: not lv_screen_active() yet +static void fitLater(lv_obj_t* row) { + lv_obj_add_flag(row, LV_OBJ_FLAG_USER_2); + s_fit_pending = true; +} // The parent's full width, in pixels when that's known now (see innerW()). static void fillWidth(lv_obj_t* o) { int32_t w = innerW(lv_obj_get_parent(o)); lv_obj_set_width(o, w >= 0 ? w : LV_PCT(100)); + if (w > 0 && lv_obj_check_type(o, &lv_label_class)) labelFit(o, w); } static lv_obj_t* label(lv_obj_t* parent, const char* text, const lv_font_t* font, uint32_t color) { @@ -361,6 +384,7 @@ namespace freeze { enum PopFit : uint8_t { POP_FIT, POP_FULL, POP_TOP, POP_BOTTOM }; static lv_obj_t* popupOpen(lv_obj_t* parent, PopFit fit, lv_obj_t*& overlay, int32_t inset = 8) { overlay = dimOverlay(parent); + s_fit_pending = true; // rowsFit() lv_obj_t* panel = lv_obj_create(overlay); int32_t w = lv_display_get_horizontal_resolution(NULL), h = lv_display_get_vertical_resolution(NULL); lv_obj_set_width(panel, w - 2 * inset); @@ -468,6 +492,7 @@ static lv_obj_t* infoRow(lv_obj_t* card, const char* key, const char* value, uin lv_obj_set_width(v, LV_SIZE_CONTENT); lv_obj_set_style_max_width(v, LV_PCT(66), 0); lv_obj_set_style_text_align(v, LV_TEXT_ALIGN_RIGHT, 0); + fitLater(r); return v; } @@ -569,9 +594,102 @@ static lv_obj_t* groupText(lv_obj_t* row, const char* text, const char* hint, ui noteLabel(t, hint); } lv_obj_set_user_data(row, l); // rowTitle() + fitLater(row); return l; } +// A group row's text column grows into what the controls on its right leave, +// and LVGL lays out children before their parent: its labels were first +// measured 1 px wide (a line per letter), then at the real width, and each +// height that changed took another pass up the screen -- four in all. Before +// a frame's layout, rowsFit() gives each new column the width the flex will: +// its row's minus the controls', worked out from their styles and text. The +// grow stays, so a control this can't size only costs the old passes. +static constexpr lv_obj_flag_t NOT_LAID_OUT = + (lv_obj_flag_t)(LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING | LV_OBJ_FLAG_IGNORE_LAYOUT); + +// `o`'s outer width as the layout will make it; -1 when that's unclear. +static int32_t layoutW(lv_obj_t* o) { + int32_t w = lv_obj_get_style_width(o, LV_PART_MAIN); + if (LV_COORD_IS_PX(w)) { + // as it is + } else if (w != LV_SIZE_CONTENT) { + return -1; + } else if (lv_obj_check_type(o, &lv_label_class)) { + lv_obj_refr_size(o); // the label's own measure, cached for the layout + w = lv_obj_get_width(o); + } else if (lv_obj_get_style_layout(o, LV_PART_MAIN) == LV_LAYOUT_FLEX && + lv_obj_get_style_flex_flow(o, LV_PART_MAIN) == LV_FLEX_FLOW_ROW) { + int32_t gap = lv_obj_get_style_pad_column(o, LV_PART_MAIN), n = 0; + w = lv_obj_get_style_pad_left(o, LV_PART_MAIN) + lv_obj_get_style_pad_right(o, LV_PART_MAIN); + for (uint32_t i = 0; i < lv_obj_get_child_count(o); i++) { + lv_obj_t* c = lv_obj_get_child(o, i); + if (lv_obj_has_flag_any(c, NOT_LAID_OUT)) continue; + int32_t cw = layoutW(c); + if (cw < 0) return -1; + w += cw + (n++ ? gap : 0); + } + w = LV_CLAMP(lv_obj_get_style_min_width(o, LV_PART_MAIN), w, lv_obj_get_style_max_width(o, LV_PART_MAIN)); + } else { + return -1; + } + return w + lv_obj_get_style_margin_left(o, LV_PART_MAIN) + lv_obj_get_style_margin_right(o, LV_PART_MAIN); +} + +static void rowFit(lv_obj_t* row) { + lv_obj_refr_size(row); // its own width now: a child's % size is of it + int32_t w = innerW(row), gap = lv_obj_get_style_pad_column(row, LV_PART_MAIN); + if (w < 0) return; + lv_obj_t* grow = nullptr; + for (uint32_t i = 0; i < lv_obj_get_child_count(row); i++) { + lv_obj_t* c = lv_obj_get_child(row, i); + if (lv_obj_has_flag_any(c, NOT_LAID_OUT)) continue; + if (lv_obj_get_style_flex_grow(c, LV_PART_MAIN)) { + if (grow) return; // one grows, or the share isn't worth working out + grow = c; + continue; + } + int32_t cw = layoutW(c); + if (cw < 0) return; + w -= cw + gap; + } + if (!grow || w < 1) return; + lv_obj_set_width(grow, w); + if (lv_obj_check_type(grow, &lv_label_class)) { labelFit(grow, w); return; } + lv_obj_refr_size(grow); + for (uint32_t i = 0; i < lv_obj_get_child_count(grow); i++) { // its labels, 100 % of it + lv_obj_t* l = lv_obj_get_child(grow, i); + if (lv_obj_check_type(l, &lv_label_class)) labelFit(l, w); + } +} + +// ... and every label given a width in pixels, which labelFit() then measures +// at it (the screen titles, a list row's cut-off name ...). +static void rowsFit(lv_obj_t* root) { + lv_obj_tree_walk(root, [](lv_obj_t* o, void*) { + if (lv_obj_has_flag(o, LV_OBJ_FLAG_USER_2)) { + lv_obj_remove_flag(o, LV_OBJ_FLAG_USER_2); + rowFit(o); + } else if (lv_obj_check_type(o, &lv_label_class)) { + int32_t w = lv_obj_get_style_width(o, LV_PART_MAIN); + if (LV_COORD_IS_PX(w) && w > 0 && lv_obj_get_width(o) != w) labelFit(o, w); + } + return LV_OBJ_TREE_WALK_NEXT; + }, nullptr); +} +static void rowsFitPending() { + if (!s_fit_pending) return; + s_fit_pending = false; + if (s_fit_screen && s_fit_screen != lv_screen_active()) rowsFit(s_fit_screen); + rowsFit(lv_screen_active()); + rowsFit(lv_layer_top()); +} +// lv_obj_update_layout() for a screen just built: its rows fitted first. +static void layoutNow(lv_obj_t* o) { + rowsFitPending(); + lv_obj_update_layout(o); +} + // The title label of a settingRow() / listRow(), to recolour or rename it. static lv_obj_t* rowTitle(lv_obj_t* row) { return (lv_obj_t*)lv_obj_get_user_data(row); } @@ -719,7 +837,7 @@ static void pickerOpen(lv_obj_t* c) { if (cur) { label(r, LV_SYMBOL_OK, THEME_FONT_BODY, theme::ACCENT); sel_row = r; } lv_obj_add_event_cb(r, onPickOption, LV_EVENT_CLICKED, (void*)(uintptr_t)i); } - if (sel_row) { lv_obj_update_layout(panel); lv_obj_scroll_to_view_recursive(sel_row, LV_ANIM_OFF); } + if (sel_row) { layoutNow(panel); lv_obj_scroll_to_view_recursive(sel_row, LV_ANIM_OFF); } } // A choice: in a group row the value is bare text (accent) and a chevron; @@ -1128,6 +1246,7 @@ void UITask::begin(DisplayDriver* display_drv, SensorManager* sensors, NodePrefs } theme::setAccent(lvport::loadAccent()); theme::install(lv_display_get_default()); + lv_display_add_event_cb(lv_display_get_default(), [](lv_event_t*) { rowsFitPending(); }, LV_EVENT_REFR_START, NULL); buildStatusBar(); applyDisplayPrefs(); // a slider percentage overrides the level main.cpp set @@ -1417,7 +1536,7 @@ void UITask::loop() { uint32_t b = micros() - t; TM_PRINT("build"); TM_RESET(); uint32_t tl = micros(); - lv_obj_update_layout(lv_screen_active()); // layout apart from drawing + layoutNow(lv_screen_active()); // layout apart from drawing tl = micros() - tl; TM_PRINT("layout"); TM_RESET(); uint32_t fl0 = lvport::s_flush_us, td = micros(); @@ -1777,7 +1896,7 @@ static void bannerShow(const char* icon, const char* title, const char* text) { lv_anim_delete(s_banner, NULL); lv_obj_remove_flag(s_banner, LV_OBJ_FLAG_HIDDEN); if (!shown) { // a new one while it's up just changes the text - lv_obj_update_layout(s_banner); + layoutNow(s_banner); anim::run(s_banner, anim::setTy, -(lv_obj_get_height(s_banner) + theme::STATUS_H), 0, anim::SCREEN_MS); } else { lv_obj_set_style_translate_y(s_banner, 0, 0); @@ -1891,7 +2010,7 @@ void UITask::buildStatusBar() { // for what's refreshed every second: LVGL restyles the box on every call, so // it redraws moved or not -- here it's only moved. static void placeLeftOf(lv_obj_t* o, lv_obj_t* base, int32_t dx) { - lv_obj_update_layout(o); + layoutNow(o); int32_t x = lv_obj_get_x(base) - lv_obj_get_width(o) + dx; int32_t y = lv_obj_get_y(base) + (lv_obj_get_height(base) - lv_obj_get_height(o)) / 2; if (lv_obj_get_style_align(o, LV_PART_MAIN) != LV_ALIGN_TOP_LEFT || lv_obj_get_x(o) != x || lv_obj_get_y(o) != y) @@ -2104,7 +2223,7 @@ void UITask::showToast(const char* text, uint32_t ms) { int32_t y = theme::STATUS_H + 4; if (s_banner && !lv_obj_has_flag(s_banner, LV_OBJ_FLAG_HIDDEN)) y = lv_obj_get_y(s_banner) + lv_obj_get_height(s_banner) + 4; lv_obj_align(_toast, LV_ALIGN_TOP_MID, 0, y); - lv_obj_update_layout(_toast); + layoutNow(_toast); if (!shown) anim::run(_toast, anim::setTy, -(y + lv_obj_get_height(_toast)), 0, anim::POP_MS); // a replaced text just changes else lv_obj_set_style_translate_y(_toast, 0, 0); if (!_toast_timer) _toast_timer = lv_timer_create(toastTimerCb, ms, _toast); @@ -2204,6 +2323,8 @@ lv_obj_t* UITask::newScreen(const char* title, bool with_back) { for (lv_obj_t*& t : _map_tiles) t = nullptr; lv_obj_t* prev = _scr; lv_obj_t* scr = lv_obj_create(NULL); + s_fit_screen = scr; // rowsFit() + s_fit_pending = true; styleOpaque(scr, theme::BG); lv_obj_remove_flag(scr, LV_OBJ_FLAG_SCROLLABLE); @@ -2299,13 +2420,13 @@ void UITask::back() { case SCR_QUICK: // back to Messages & contacts' bottom, where the row is if (_nav_overlay) { navClosePopup(); break; } showSchemaSettings(settings::PG_MESSAGES); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_by(_body, 0, -lv_obj_get_scroll_bottom(_body), LV_ANIM_OFF); } + if (_body) { layoutNow(_body); lv_obj_scroll_by(_body, 0, -lv_obj_get_scroll_bottom(_body), LV_ANIM_OFF); } break; case SCR_MELODY: // back to the Sound page's bottom, where the melodies are melodySave(); stopMelody(); showSchemaSettings(settings::PG_SOUND); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_by(_body, 0, -lv_obj_get_scroll_bottom(_body), LV_ANIM_OFF); } + if (_body) { layoutNow(_body); lv_obj_scroll_by(_body, 0, -lv_obj_get_scroll_bottom(_body), LV_ANIM_OFF); } break; case SCR_CLOCK: showHome(); break; case SCR_RADIO: @@ -2321,7 +2442,7 @@ void UITask::back() { case SCR_SCOPES: // back to the Radio screen's bottom, where Scopes is if (_nav_overlay) { navClosePopup(); break; } showRadio(); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_by(_body, 0, -lv_obj_get_scroll_bottom(_body), LV_ANIM_OFF); } + if (_body) { layoutNow(_body); lv_obj_scroll_by(_body, 0, -lv_obj_get_scroll_bottom(_body), LV_ANIM_OFF); } break; case SCR_CHATS: if (_nav_overlay) navClosePopup(); else showHome(); break; case SCR_NEARBY: @@ -2518,7 +2639,7 @@ void UITask::buildChats() { stylePrimary(nb); if (unreadTotal() > 0) { lv_obj_t* rb = headerButton(_header, LV_SYMBOL_OK " Read all", onMarkAllRead, 0, NULL); - lv_obj_update_layout(_header); + layoutNow(_header); lv_obj_align_to(rb, nb, LV_ALIGN_OUT_LEFT_MID, -6, 0); } } @@ -2627,7 +2748,7 @@ void UITask::chatFold(int which) { lvport::saveChatFold(s_chat_fold); int y = _body ? lv_obj_get_scroll_y(_body) : 0; buildChats(); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } + if (_body) { layoutNow(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } } // ── Contact picker (start a DM) ─────────────────────────────────────────────── @@ -2931,7 +3052,7 @@ void UITask::refreshNearbyList() { lv_obj_set_style_pad_top(l, 12, 0); } if (scroll > 0) { - lv_obj_update_layout(_nearby_list); + layoutNow(_nearby_list); lv_obj_scroll_to_y(_nearby_list, scroll, LV_ANIM_OFF); } } @@ -3186,7 +3307,7 @@ void UITask::setKeyboardVisible(bool show) { lv_obj_set_height(_body, lv_display_get_vertical_resolution(NULL) - top); } if (_thread_list) { // keep the newest message in view - lv_obj_update_layout(_thread_list); + layoutNow(_thread_list); lv_obj_scroll_to_y(_thread_list, LV_COORD_MAX, LV_ANIM_OFF); } } @@ -3273,7 +3394,7 @@ void UITask::buildThread() { if (_compose_ta && _share_text[0]) { // shareToMessage(): the text waits in the field lv_textarea_set_text(_compose_ta, _share_text); _share_text[0] = '\0'; - lv_obj_update_layout(screen()); + layoutNow(screen()); setKeyboardVisible(true); } } @@ -3694,7 +3815,7 @@ void UITask::refreshThread() { if (s_older_lbl) { char t[40]; olderText(t, sizeof(t)); setText(s_older_lbl, t); } s_th_rows_n = s_msg_meta_n = s_msg_loc_n = n; if (n > kept) { // new messages: down to them - lv_obj_update_layout(_thread_list); + layoutNow(_thread_list); lv_obj_scroll_to_y(_thread_list, LV_COORD_MAX, LV_ANIM_OFF); } return; @@ -3714,7 +3835,7 @@ void UITask::refreshThread() { s_th_rows_newest = _thread_skip == 0; // a page back is built whole each time if (_thread_skip > 0) pageButton(_thread_list, LV_SYMBOL_DOWN " Newer messages", -1); if (n == 0) label(_thread_list, "No messages yet", THEME_FONT_BODY, theme::TEXT_MUTED); - lv_obj_update_layout(_thread_list); + layoutNow(_thread_list); lv_obj_scroll_to_y(_thread_list, _thread_scroll_top ? 0 : LV_COORD_MAX, LV_ANIM_OFF); _thread_scroll_top = false; } @@ -4025,7 +4146,7 @@ void UITask::setSchemaValue(int idx, int v) { int32_t y = body ? lv_obj_get_scroll_y(body) : 0; buildSchemaSettings(); fillFlush(); - if (_body) { lv_obj_update_layout(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } + if (_body) { layoutNow(_body); lv_obj_scroll_to_y(_body, y, LV_ANIM_OFF); } } } @@ -4045,7 +4166,7 @@ void UITask::buildSettings() { // A group at a time (fillStart): the first shows at once. Coming back to // where it was scrolled needs them all first. fillStart(SETTINGS_GROUPS, 1, &UITask::settingsGroup); - if (restore) { fillFlush(); lv_obj_update_layout(body); lv_obj_scroll_to_y(body, s_settings_y, LV_ANIM_OFF); } + if (restore) { fillFlush(); layoutNow(body); lv_obj_scroll_to_y(body, s_settings_y, LV_ANIM_OFF); } } void UITask::settingsGroup(int i) { diff --git a/examples/companion_radio/ui-lvgl/WifiScreen.h b/examples/companion_radio/ui-lvgl/WifiScreen.h index aa72a8ce..98ca4084 100644 --- a/examples/companion_radio/ui-lvgl/WifiScreen.h +++ b/examples/companion_radio/ui-lvgl/WifiScreen.h @@ -147,7 +147,7 @@ void UITask::buildWifi() { static void wifiRebuild(UITask* ui, lv_obj_t*& body) { int32_t y = body ? lv_obj_get_scroll_y(body) : 0; ui->showWifi(); - if (body) { lv_obj_update_layout(body); lv_obj_scroll_to_y(body, y, LV_ANIM_OFF); } + if (body) { layoutNow(body); lv_obj_scroll_to_y(body, y, LV_ANIM_OFF); } } void UITask::wifiEdit(lv_obj_t* ta) { @@ -160,7 +160,7 @@ void UITask::wifiEdit(lv_obj_t* ta) { lv_obj_remove_flag(_wifi_kb, LV_OBJ_FLAG_HIDDEN); if (_body) lv_obj_set_height(_body, lv_obj_get_height(_body) - lv_obj_get_height(_wifi_kb)); } - lv_obj_update_layout(screen()); + layoutNow(screen()); lv_obj_scroll_to_view(ta, LV_ANIM_OFF); } @@ -241,7 +241,7 @@ void UITask::wifiTestShow(const char* msg, const char* ssid, const char* pass) { lv_textarea_set_text(_wifi_ssid, ssid); lv_textarea_set_text(_wifi_pass, pass); } - lv_obj_update_layout(_body); + layoutNow(_body); lv_obj_scroll_to_view(_wifi_status, LV_ANIM_ON); }