From 80d94bd343e34c5e2be4ebc5e20a3f8efb068048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominic=20H=C3=B6glinger?= Date: Sat, 27 Sep 2025 21:15:10 +0200 Subject: [PATCH] LVGL Wrappers: Proposition to make oversized parents scrollable --- Tactility/Source/lvgl/wrappers/obj.cpp | 102 ++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/Tactility/Source/lvgl/wrappers/obj.cpp b/Tactility/Source/lvgl/wrappers/obj.cpp index 402a73f2..8be02ada 100644 --- a/Tactility/Source/lvgl/wrappers/obj.cpp +++ b/Tactility/Source/lvgl/wrappers/obj.cpp @@ -9,6 +9,101 @@ extern "C" { extern void __real_lv_obj_set_flex_flow(lv_obj_t* obj, lv_flex_flow_t flow); extern lv_obj_t* __real_lv_obj_create(lv_obj_t* parent); +static bool get_children_bby(lv_obj_t * parent, lv_coord_t * out_min_y, lv_coord_t * out_max_y) { + lv_coord_t min_y = INT_MAX; + lv_coord_t max_y = INT_MIN; + bool has_children = false; + lv_area_t parent_coords; + lv_obj_get_coords(parent, &parent_coords); + + for(size_t i = 0; i < lv_obj_get_child_count(parent); i++) { + lv_obj_t * child = lv_obj_get_child(parent, i); + // Get absolute position of child + lv_area_t child_coords; + lv_obj_get_coords(child, &child_coords); + + // convert to local coordinates + lv_coord_t c_y1 = child_coords.y1 - parent_coords.y1; + lv_coord_t c_y2 = child_coords.y2 - parent_coords.y1; + + // Update minimum/maximum + if(c_y1 < min_y) min_y = c_y1; + if(c_y2 > max_y) max_y = c_y2; + + has_children = true; + } + + if(has_children) { + *out_min_y = min_y; + *out_max_y = max_y; + } + + return has_children; +} + +static bool is_overflowing(lv_obj_t* obj) { + lv_coord_t min_y = 0; + lv_coord_t max_y = 0; + + // Compute childrens' bounding box relative to parent + if(!get_children_bby(obj, &min_y, &max_y)) { + // Can't be overflowing when no children are present + return false; + } + + lv_coord_t parent_h = lv_obj_get_height(obj); + + return ((min_y < 0) || (max_y >= parent_h)); +} + +static void apply_scroll_styles(lv_obj_t* obj) { + static bool init = false; + static lv_style_t style_scroll_focus; + static lv_style_t style_scrolling; + + if (!init) { + // Style which applies if scrollbar is focused + lv_style_init(&style_scroll_focus); + lv_style_set_bg_color(&style_scroll_focus, lv_color_make(0x40,0xA0,0xFF)); + lv_style_set_bg_opa(&style_scroll_focus, LV_OPA_COVER); + lv_style_set_border_width(&style_scroll_focus, 1); + lv_style_set_border_color(&style_scroll_focus, lv_theme_get_color_primary(nullptr)); + + // Style which applies if scrollbar is clicked enough that it defocuses + lv_style_init(&style_scrolling); + lv_style_set_bg_color(&style_scrolling, lv_color_make(0x40,0xA0,0xFF)); + lv_style_set_bg_opa(&style_scrolling, LV_OPA_COVER); + lv_style_set_border_width(&style_scrolling, 1); + // In Tactilitys' default theme, this is red, maybe there is a better color? + lv_style_set_border_color(&style_scrolling, lv_theme_get_color_secondary(nullptr)); + + init = true; + } + lv_obj_add_style(obj, &style_scroll_focus, (lv_style_selector_t)(LV_PART_SCROLLBAR | LV_STATE_FOCUSED)); + lv_obj_add_style(obj, &style_scrolling, (lv_style_selector_t)(LV_PART_SCROLLBAR | LV_STATE_PRESSED)); +} + +static bool is_scrollable(lv_obj_t* obj) { + return lv_obj_has_flag(obj, LV_OBJ_FLAG_CLICKABLE) && lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS); +} + +static void make_scrollable(lv_obj_t* obj) { + // Make the object interactive, i.e focusable + lv_obj_add_flag(obj, (lv_obj_flag_t)(LV_OBJ_FLAG_CLICKABLE | LV_OBJ_FLAG_SCROLL_ON_FOCUS)); + + // Add to default group to get it into the focus list + auto* group = lv_group_get_default(); + lv_group_add_obj(group, obj); + + // Apply style to scrollbar to make it visible if focused + apply_scroll_style(obj); +} + +static void child_added_event_handler(lv_event_t* e) { + lv_obj_t* obj = (lv_obj_t*)lv_event_get_target(e); + +} + void __wrap_lv_obj_set_flex_flow(lv_obj_t* obj, lv_flex_flow_t flow) { __real_lv_obj_set_flex_flow(obj, flow); @@ -25,9 +120,14 @@ lv_obj_t* __wrap_lv_obj_create(lv_obj_t* parent) { lv_obj_set_style_radius(obj, 3, LV_STATE_DEFAULT); lv_obj_set_style_border_width(obj, 1, LV_STATE_DEFAULT); } + + if (parent && !is_scrollable(parent) && is_overflowing(parent)) { + make_scrollable(parent); + } + return obj; } } -#endif // ESP_PLATFORM \ No newline at end of file +#endif // ESP_PLATFORM