summaryrefslogtreecommitdiff
path: root/lib/lvgl/src/core
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-06-01 15:41:47 +1000
committerjacqueline <me@jacqueline.id.au>2023-06-01 15:41:47 +1000
commitdd27c3530432ea0b09f01e604bf577f31d8ef841 (patch)
treebbf86cf81a78f0ff0b07f31f1c390db473f26fd3 /lib/lvgl/src/core
parent6fd588e970470b15936187980829916d0dbe77bb (diff)
downloadtangara-fw-dd27c3530432ea0b09f01e604bf577f31d8ef841.tar.gz
convert lvgl from submodule to a plain old directory
Diffstat (limited to 'lib/lvgl/src/core')
m---------lib/lvgl0
-rw-r--r--lib/lvgl/src/core/lv_core.mk20
-rw-r--r--lib/lvgl/src/core/lv_disp.c534
-rw-r--r--lib/lvgl/src/core/lv_disp.h264
-rw-r--r--lib/lvgl/src/core/lv_event.c527
-rw-r--r--lib/lvgl/src/core/lv_event.h363
-rw-r--r--lib/lvgl/src/core/lv_group.c497
-rw-r--r--lib/lvgl/src/core/lv_group.h266
-rw-r--r--lib/lvgl/src/core/lv_indev.c1161
-rw-r--r--lib/lvgl/src/core/lv_indev.h176
-rw-r--r--lib/lvgl/src/core/lv_indev_scroll.c652
-rw-r--r--lib/lvgl/src/core/lv_indev_scroll.h65
-rw-r--r--lib/lvgl/src/core/lv_obj.c948
-rw-r--r--lib/lvgl/src/core/lv_obj.h409
-rw-r--r--lib/lvgl/src/core/lv_obj_class.c202
-rw-r--r--lib/lvgl/src/core/lv_obj_class.h94
-rw-r--r--lib/lvgl/src/core/lv_obj_draw.c406
-rw-r--r--lib/lvgl/src/core/lv_obj_draw.h172
-rw-r--r--lib/lvgl/src/core/lv_obj_pos.c1172
-rw-r--r--lib/lvgl/src/core/lv_obj_pos.h449
-rw-r--r--lib/lvgl/src/core/lv_obj_scroll.c800
-rw-r--r--lib/lvgl/src/core/lv_obj_scroll.h307
-rw-r--r--lib/lvgl/src/core/lv_obj_style.c866
-rw-r--r--lib/lvgl/src/core/lv_obj_style.h248
-rw-r--r--lib/lvgl/src/core/lv_obj_style_gen.c673
-rw-r--r--lib/lvgl/src/core/lv_obj_style_gen.h648
-rw-r--r--lib/lvgl/src/core/lv_obj_tree.c452
-rw-r--r--lib/lvgl/src/core/lv_obj_tree.h172
-rw-r--r--lib/lvgl/src/core/lv_refr.c1255
-rw-r--r--lib/lvgl/src/core/lv_refr.h115
-rw-r--r--lib/lvgl/src/core/lv_theme.c118
-rw-r--r--lib/lvgl/src/core/lv_theme.h120
32 files changed, 14151 insertions, 0 deletions
diff --git a/lib/lvgl b/lib/lvgl
deleted file mode 160000
-Subproject 0732400e7b564dd0e7dc4a924619d8e19c5b23a
diff --git a/lib/lvgl/src/core/lv_core.mk b/lib/lvgl/src/core/lv_core.mk
new file mode 100644
index 00000000..677a9f6b
--- /dev/null
+++ b/lib/lvgl/src/core/lv_core.mk
@@ -0,0 +1,20 @@
+CSRCS += lv_disp.c
+CSRCS += lv_group.c
+CSRCS += lv_indev.c
+CSRCS += lv_indev_scroll.c
+CSRCS += lv_obj.c
+CSRCS += lv_obj_class.c
+CSRCS += lv_obj_draw.c
+CSRCS += lv_obj_pos.c
+CSRCS += lv_obj_scroll.c
+CSRCS += lv_obj_style.c
+CSRCS += lv_obj_style_gen.c
+CSRCS += lv_obj_tree.c
+CSRCS += lv_event.c
+CSRCS += lv_refr.c
+CSRCS += lv_theme.c
+
+DEPPATH += --dep-path $(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core
+VPATH += :$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core
+
+CFLAGS += "-I$(LVGL_DIR)/$(LVGL_DIR_NAME)/src/core"
diff --git a/lib/lvgl/src/core/lv_disp.c b/lib/lvgl/src/core/lv_disp.c
new file mode 100644
index 00000000..a1022b56
--- /dev/null
+++ b/lib/lvgl/src/core/lv_disp.c
@@ -0,0 +1,534 @@
+/**
+ * @file lv_disp.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_disp.h"
+#include "../misc/lv_math.h"
+#include "../core/lv_refr.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void scr_load_internal(lv_obj_t * scr);
+static void scr_load_anim_start(lv_anim_t * a);
+static void opa_scale_anim(void * obj, int32_t v);
+static void set_x_anim(void * obj, int32_t v);
+static void set_y_anim(void * obj, int32_t v);
+static void scr_anim_ready(lv_anim_t * a);
+static bool is_out_anim(lv_scr_load_anim_t a);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Return with a pointer to the active screen
+ * @param disp pointer to display which active screen should be get. (NULL to use the default
+ * screen)
+ * @return pointer to the active screen object (loaded by 'lv_scr_load()')
+ */
+lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("no display registered to get its active screen");
+ return NULL;
+ }
+
+ return disp->act_scr;
+}
+
+/**
+ * Return with a pointer to the previous screen. Only used during screen transitions.
+ * @param disp pointer to display which previous screen should be get. (NULL to use the default
+ * screen)
+ * @return pointer to the previous screen object or NULL if not used now
+ */
+lv_obj_t * lv_disp_get_scr_prev(lv_disp_t * disp)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("no display registered to get its previous screen");
+ return NULL;
+ }
+
+ return disp->prev_scr;
+}
+
+/**
+ * Make a screen active
+ * @param scr pointer to a screen
+ */
+void lv_disp_load_scr(lv_obj_t * scr)
+{
+ lv_scr_load_anim(scr, LV_SCR_LOAD_ANIM_NONE, 0, 0, false);
+}
+
+/**
+ * Return with the top layer. (Same on every screen and it is above the normal screen layer)
+ * @param disp pointer to display which top layer should be get. (NULL to use the default screen)
+ * @return pointer to the top layer object (transparent screen sized lv_obj)
+ */
+lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("lv_layer_top: no display registered to get its top layer");
+ return NULL;
+ }
+
+ return disp->top_layer;
+}
+
+/**
+ * Return with the sys. layer. (Same on every screen and it is above the normal screen and the top
+ * layer)
+ * @param disp pointer to display which sys. layer should be retrieved. (NULL to use the default screen)
+ * @return pointer to the sys layer object (transparent screen sized lv_obj)
+ */
+lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("lv_layer_sys: no display registered to get its sys. layer");
+ return NULL;
+ }
+
+ return disp->sys_layer;
+}
+
+/**
+ * Set the theme of a display
+ * @param disp pointer to a display
+ */
+void lv_disp_set_theme(lv_disp_t * disp, lv_theme_t * th)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("no display registered");
+ return;
+ }
+
+ disp->theme = th;
+
+ if(disp->screen_cnt == 3 &&
+ lv_obj_get_child_cnt(disp->screens[0]) == 0 &&
+ lv_obj_get_child_cnt(disp->screens[1]) == 0 &&
+ lv_obj_get_child_cnt(disp->screens[2]) == 0) {
+ lv_theme_apply(disp->screens[0]);
+ }
+}
+/**
+ * Get the theme of a display
+ * @param disp pointer to a display
+ * @return the display's theme (can be NULL)
+ */
+lv_theme_t * lv_disp_get_theme(lv_disp_t * disp)
+{
+ if(disp == NULL) disp = lv_disp_get_default();
+ return disp->theme;
+}
+
+/**
+ * Set the background color of a display
+ * @param disp pointer to a display
+ * @param color color of the background
+ */
+void lv_disp_set_bg_color(lv_disp_t * disp, lv_color_t color)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("no display registered");
+ return;
+ }
+
+ disp->bg_color = color;
+
+ lv_area_t a;
+ lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
+ _lv_inv_area(disp, &a);
+
+}
+
+/**
+ * Set the background image of a display
+ * @param disp pointer to a display
+ * @param img_src path to file or pointer to an `lv_img_dsc_t` variable
+ */
+void lv_disp_set_bg_image(lv_disp_t * disp, const void * img_src)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("no display registered");
+ return;
+ }
+
+ disp->bg_img = img_src;
+
+ lv_area_t a;
+ lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
+ _lv_inv_area(disp, &a);
+}
+
+/**
+ * Set opacity of the background
+ * @param disp pointer to a display
+ * @param opa opacity (0..255)
+ */
+void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("no display registered");
+ return;
+ }
+
+ disp->bg_opa = opa;
+
+ lv_area_t a;
+ lv_area_set(&a, 0, 0, lv_disp_get_hor_res(disp) - 1, lv_disp_get_ver_res(disp) - 1);
+ _lv_inv_area(disp, &a);
+}
+
+/**
+ * Switch screen with animation
+ * @param scr pointer to the new screen to load
+ * @param anim_type type of the animation from `lv_scr_load_anim_t`, e.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT`
+ * @param time time of the animation
+ * @param delay delay before the transition
+ * @param auto_del true: automatically delete the old screen
+ */
+void lv_scr_load_anim(lv_obj_t * new_scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del)
+{
+
+ lv_disp_t * d = lv_obj_get_disp(new_scr);
+ lv_obj_t * act_scr = lv_scr_act();
+
+ /*If an other screen load animation is in progress
+ *make target screen loaded immediately. */
+ if(d->scr_to_load && act_scr != d->scr_to_load) {
+ scr_load_internal(d->scr_to_load);
+ lv_anim_del(d->scr_to_load, NULL);
+ lv_obj_set_pos(d->scr_to_load, 0, 0);
+ lv_obj_remove_local_style_prop(d->scr_to_load, LV_STYLE_OPA, 0);
+
+ if(d->del_prev) {
+ lv_obj_del(act_scr);
+ }
+ act_scr = d->scr_to_load;
+ }
+
+ d->scr_to_load = new_scr;
+
+ if(d->prev_scr && d->del_prev) {
+ lv_obj_del(d->prev_scr);
+ d->prev_scr = NULL;
+ }
+
+ d->draw_prev_over_act = is_out_anim(anim_type);
+ d->del_prev = auto_del;
+
+ /*Be sure there is no other animation on the screens*/
+ lv_anim_del(new_scr, NULL);
+ lv_anim_del(lv_scr_act(), NULL);
+
+ /*Be sure both screens are in a normal position*/
+ lv_obj_set_pos(new_scr, 0, 0);
+ lv_obj_set_pos(lv_scr_act(), 0, 0);
+ lv_obj_remove_local_style_prop(new_scr, LV_STYLE_OPA, 0);
+ lv_obj_remove_local_style_prop(lv_scr_act(), LV_STYLE_OPA, 0);
+
+
+ /*Shortcut for immediate load*/
+ if(time == 0 && delay == 0) {
+ scr_load_internal(new_scr);
+ return;
+ }
+
+ lv_anim_t a_new;
+ lv_anim_init(&a_new);
+ lv_anim_set_var(&a_new, new_scr);
+ lv_anim_set_start_cb(&a_new, scr_load_anim_start);
+ lv_anim_set_ready_cb(&a_new, scr_anim_ready);
+ lv_anim_set_time(&a_new, time);
+ lv_anim_set_delay(&a_new, delay);
+
+ lv_anim_t a_old;
+ lv_anim_init(&a_old);
+ lv_anim_set_var(&a_old, d->act_scr);
+ lv_anim_set_time(&a_old, time);
+ lv_anim_set_delay(&a_old, delay);
+
+ switch(anim_type) {
+ case LV_SCR_LOAD_ANIM_NONE:
+ /*Create a dummy animation to apply the delay*/
+ lv_anim_set_exec_cb(&a_new, set_x_anim);
+ lv_anim_set_values(&a_new, 0, 0);
+ break;
+ case LV_SCR_LOAD_ANIM_OVER_LEFT:
+ lv_anim_set_exec_cb(&a_new, set_x_anim);
+ lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0);
+ break;
+ case LV_SCR_LOAD_ANIM_OVER_RIGHT:
+ lv_anim_set_exec_cb(&a_new, set_x_anim);
+ lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0);
+ break;
+ case LV_SCR_LOAD_ANIM_OVER_TOP:
+ lv_anim_set_exec_cb(&a_new, set_y_anim);
+ lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0);
+ break;
+ case LV_SCR_LOAD_ANIM_OVER_BOTTOM:
+ lv_anim_set_exec_cb(&a_new, set_y_anim);
+ lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0);
+ break;
+ case LV_SCR_LOAD_ANIM_MOVE_LEFT:
+ lv_anim_set_exec_cb(&a_new, set_x_anim);
+ lv_anim_set_values(&a_new, lv_disp_get_hor_res(d), 0);
+
+ lv_anim_set_exec_cb(&a_old, set_x_anim);
+ lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d));
+ break;
+ case LV_SCR_LOAD_ANIM_MOVE_RIGHT:
+ lv_anim_set_exec_cb(&a_new, set_x_anim);
+ lv_anim_set_values(&a_new, -lv_disp_get_hor_res(d), 0);
+
+ lv_anim_set_exec_cb(&a_old, set_x_anim);
+ lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d));
+ break;
+ case LV_SCR_LOAD_ANIM_MOVE_TOP:
+ lv_anim_set_exec_cb(&a_new, set_y_anim);
+ lv_anim_set_values(&a_new, lv_disp_get_ver_res(d), 0);
+
+ lv_anim_set_exec_cb(&a_old, set_y_anim);
+ lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d));
+ break;
+ case LV_SCR_LOAD_ANIM_MOVE_BOTTOM:
+ lv_anim_set_exec_cb(&a_new, set_y_anim);
+ lv_anim_set_values(&a_new, -lv_disp_get_ver_res(d), 0);
+
+ lv_anim_set_exec_cb(&a_old, set_y_anim);
+ lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d));
+ break;
+ case LV_SCR_LOAD_ANIM_FADE_IN:
+ lv_anim_set_exec_cb(&a_new, opa_scale_anim);
+ lv_anim_set_values(&a_new, LV_OPA_TRANSP, LV_OPA_COVER);
+ break;
+ case LV_SCR_LOAD_ANIM_FADE_OUT:
+ lv_anim_set_exec_cb(&a_old, opa_scale_anim);
+ lv_anim_set_values(&a_old, LV_OPA_COVER, LV_OPA_TRANSP);
+ break;
+ case LV_SCR_LOAD_ANIM_OUT_LEFT:
+ lv_anim_set_exec_cb(&a_old, set_x_anim);
+ lv_anim_set_values(&a_old, 0, -lv_disp_get_hor_res(d));
+ break;
+ case LV_SCR_LOAD_ANIM_OUT_RIGHT:
+ lv_anim_set_exec_cb(&a_old, set_x_anim);
+ lv_anim_set_values(&a_old, 0, lv_disp_get_hor_res(d));
+ break;
+ case LV_SCR_LOAD_ANIM_OUT_TOP:
+ lv_anim_set_exec_cb(&a_old, set_y_anim);
+ lv_anim_set_values(&a_old, 0, -lv_disp_get_ver_res(d));
+ break;
+ case LV_SCR_LOAD_ANIM_OUT_BOTTOM:
+ lv_anim_set_exec_cb(&a_old, set_y_anim);
+ lv_anim_set_values(&a_old, 0, lv_disp_get_ver_res(d));
+ break;
+ }
+
+ lv_event_send(act_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL);
+
+ lv_anim_start(&a_new);
+ lv_anim_start(&a_old);
+}
+
+/**
+ * Get elapsed time since last user activity on a display (e.g. click)
+ * @param disp pointer to a display (NULL to get the overall smallest inactivity)
+ * @return elapsed ticks (milliseconds) since the last activity
+ */
+uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp)
+{
+ if(disp) return lv_tick_elaps(disp->last_activity_time);
+
+ lv_disp_t * d;
+ uint32_t t = UINT32_MAX;
+ d = lv_disp_get_next(NULL);
+ while(d) {
+ uint32_t elaps = lv_tick_elaps(d->last_activity_time);
+ t = LV_MIN(t, elaps);
+ d = lv_disp_get_next(d);
+ }
+
+ return t;
+}
+
+/**
+ * Manually trigger an activity on a display
+ * @param disp pointer to a display (NULL to use the default display)
+ */
+void lv_disp_trig_activity(lv_disp_t * disp)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("lv_disp_trig_activity: no display registered");
+ return;
+ }
+
+ disp->last_activity_time = lv_tick_get();
+}
+
+/**
+ * Clean any CPU cache that is related to the display.
+ * @param disp pointer to a display (NULL to use the default display)
+ */
+void lv_disp_clean_dcache(lv_disp_t * disp)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("lv_disp_clean_dcache: no display registered");
+ return;
+ }
+
+ if(disp->driver->clean_dcache_cb)
+ disp->driver->clean_dcache_cb(disp->driver);
+}
+
+/**
+ * Temporarily enable and disable the invalidation of the display.
+ * @param disp pointer to a display (NULL to use the default display)
+ * @param en true: enable invalidation; false: invalidation
+ */
+void lv_disp_enable_invalidation(lv_disp_t * disp, bool en)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("no display registered");
+ return;
+ }
+
+ disp->inv_en_cnt += en ? 1 : -1;
+}
+
+/**
+ * Get display invalidation is enabled.
+ * @param disp pointer to a display (NULL to use the default display)
+ * @return return true if invalidation is enabled
+ */
+bool lv_disp_is_invalidation_enabled(lv_disp_t * disp)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("no display registered");
+ return false;
+ }
+
+ return (disp->inv_en_cnt > 0);
+}
+
+/**
+ * Get a pointer to the screen refresher timer to
+ * modify its parameters with `lv_timer_...` functions.
+ * @param disp pointer to a display
+ * @return pointer to the display refresher timer. (NULL on error)
+ */
+lv_timer_t * _lv_disp_get_refr_timer(lv_disp_t * disp)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("lv_disp_get_refr_timer: no display registered");
+ return NULL;
+ }
+
+ return disp->refr_timer;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void scr_load_internal(lv_obj_t * scr)
+{
+ lv_disp_t * d = lv_obj_get_disp(scr);
+ if(!d) return; /*Shouldn't happen, just to be sure*/
+
+ lv_obj_t * old_scr = d->act_scr;
+
+ if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOAD_START, NULL);
+ if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOAD_START, NULL);
+
+ d->act_scr = scr;
+
+ if(d->act_scr) lv_event_send(scr, LV_EVENT_SCREEN_LOADED, NULL);
+ if(d->act_scr) lv_event_send(old_scr, LV_EVENT_SCREEN_UNLOADED, NULL);
+
+ lv_obj_invalidate(scr);
+}
+
+static void scr_load_anim_start(lv_anim_t * a)
+{
+ lv_disp_t * d = lv_obj_get_disp(a->var);
+
+ d->prev_scr = lv_scr_act();
+ d->act_scr = a->var;
+
+ lv_event_send(d->act_scr, LV_EVENT_SCREEN_LOAD_START, NULL);
+}
+
+static void opa_scale_anim(void * obj, int32_t v)
+{
+ lv_obj_set_style_opa(obj, v, 0);
+}
+
+static void set_x_anim(void * obj, int32_t v)
+{
+ lv_obj_set_x(obj, v);
+}
+
+static void set_y_anim(void * obj, int32_t v)
+{
+ lv_obj_set_y(obj, v);
+}
+
+static void scr_anim_ready(lv_anim_t * a)
+{
+ lv_disp_t * d = lv_obj_get_disp(a->var);
+
+ lv_event_send(d->act_scr, LV_EVENT_SCREEN_LOADED, NULL);
+ lv_event_send(d->prev_scr, LV_EVENT_SCREEN_UNLOADED, NULL);
+
+ if(d->prev_scr && d->del_prev) lv_obj_del(d->prev_scr);
+ d->prev_scr = NULL;
+ d->draw_prev_over_act = false;
+ d->scr_to_load = NULL;
+ lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
+ lv_obj_invalidate(d->act_scr);
+}
+
+static bool is_out_anim(lv_scr_load_anim_t anim_type)
+{
+ return anim_type == LV_SCR_LOAD_ANIM_FADE_OUT ||
+ anim_type == LV_SCR_LOAD_ANIM_OUT_LEFT ||
+ anim_type == LV_SCR_LOAD_ANIM_OUT_RIGHT ||
+ anim_type == LV_SCR_LOAD_ANIM_OUT_TOP ||
+ anim_type == LV_SCR_LOAD_ANIM_OUT_BOTTOM;
+}
diff --git a/lib/lvgl/src/core/lv_disp.h b/lib/lvgl/src/core/lv_disp.h
new file mode 100644
index 00000000..7854cb7f
--- /dev/null
+++ b/lib/lvgl/src/core/lv_disp.h
@@ -0,0 +1,264 @@
+/**
+ * @file lv_disp.h
+ *
+ */
+
+#ifndef LV_DISP_H
+#define LV_DISP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../hal/lv_hal.h"
+#include "lv_obj.h"
+#include "lv_theme.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef enum {
+ LV_SCR_LOAD_ANIM_NONE,
+ LV_SCR_LOAD_ANIM_OVER_LEFT,
+ LV_SCR_LOAD_ANIM_OVER_RIGHT,
+ LV_SCR_LOAD_ANIM_OVER_TOP,
+ LV_SCR_LOAD_ANIM_OVER_BOTTOM,
+ LV_SCR_LOAD_ANIM_MOVE_LEFT,
+ LV_SCR_LOAD_ANIM_MOVE_RIGHT,
+ LV_SCR_LOAD_ANIM_MOVE_TOP,
+ LV_SCR_LOAD_ANIM_MOVE_BOTTOM,
+ LV_SCR_LOAD_ANIM_FADE_IN,
+ LV_SCR_LOAD_ANIM_FADE_ON = LV_SCR_LOAD_ANIM_FADE_IN, /*For backward compatibility*/
+ LV_SCR_LOAD_ANIM_FADE_OUT,
+ LV_SCR_LOAD_ANIM_OUT_LEFT,
+ LV_SCR_LOAD_ANIM_OUT_RIGHT,
+ LV_SCR_LOAD_ANIM_OUT_TOP,
+ LV_SCR_LOAD_ANIM_OUT_BOTTOM,
+} lv_scr_load_anim_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Return with a pointer to the active screen
+ * @param disp pointer to display which active screen should be get. (NULL to use the default
+ * screen)
+ * @return pointer to the active screen object (loaded by 'lv_scr_load()')
+ */
+lv_obj_t * lv_disp_get_scr_act(lv_disp_t * disp);
+
+/**
+ * Return with a pointer to the previous screen. Only used during screen transitions.
+ * @param disp pointer to display which previous screen should be get. (NULL to use the default
+ * screen)
+ * @return pointer to the previous screen object or NULL if not used now
+ */
+lv_obj_t * lv_disp_get_scr_prev(lv_disp_t * disp);
+
+/**
+ * Make a screen active
+ * @param scr pointer to a screen
+ */
+void lv_disp_load_scr(lv_obj_t * scr);
+
+/**
+ * Return with the top layer. (Same on every screen and it is above the normal screen layer)
+ * @param disp pointer to display which top layer should be get. (NULL to use the default screen)
+ * @return pointer to the top layer object (transparent screen sized lv_obj)
+ */
+lv_obj_t * lv_disp_get_layer_top(lv_disp_t * disp);
+
+/**
+ * Return with the sys. layer. (Same on every screen and it is above the normal screen and the top
+ * layer)
+ * @param disp pointer to display which sys. layer should be retrieved. (NULL to use the default screen)
+ * @return pointer to the sys layer object (transparent screen sized lv_obj)
+ */
+lv_obj_t * lv_disp_get_layer_sys(lv_disp_t * disp);
+
+/**
+ * Set the theme of a display
+ * @param disp pointer to a display
+ */
+void lv_disp_set_theme(lv_disp_t * disp, lv_theme_t * th);
+
+/**
+ * Get the theme of a display
+ * @param disp pointer to a display
+ * @return the display's theme (can be NULL)
+ */
+lv_theme_t * lv_disp_get_theme(lv_disp_t * disp);
+
+/**
+ * Set the background color of a display
+ * @param disp pointer to a display
+ * @param color color of the background
+ */
+void lv_disp_set_bg_color(lv_disp_t * disp, lv_color_t color);
+
+/**
+ * Set the background image of a display
+ * @param disp pointer to a display
+ * @param img_src path to file or pointer to an `lv_img_dsc_t` variable
+ */
+void lv_disp_set_bg_image(lv_disp_t * disp, const void * img_src);
+
+/**
+ * Set opacity of the background
+ * @param disp pointer to a display
+ * @param opa opacity (0..255)
+ */
+void lv_disp_set_bg_opa(lv_disp_t * disp, lv_opa_t opa);
+
+/**
+ * Switch screen with animation
+ * @param scr pointer to the new screen to load
+ * @param anim_type type of the animation from `lv_scr_load_anim_t`, e.g. `LV_SCR_LOAD_ANIM_MOVE_LEFT`
+ * @param time time of the animation
+ * @param delay delay before the transition
+ * @param auto_del true: automatically delete the old screen
+ */
+void lv_scr_load_anim(lv_obj_t * scr, lv_scr_load_anim_t anim_type, uint32_t time, uint32_t delay, bool auto_del);
+
+/**
+ * Get elapsed time since last user activity on a display (e.g. click)
+ * @param disp pointer to a display (NULL to get the overall smallest inactivity)
+ * @return elapsed ticks (milliseconds) since the last activity
+ */
+uint32_t lv_disp_get_inactive_time(const lv_disp_t * disp);
+
+/**
+ * Manually trigger an activity on a display
+ * @param disp pointer to a display (NULL to use the default display)
+ */
+void lv_disp_trig_activity(lv_disp_t * disp);
+
+/**
+ * Clean any CPU cache that is related to the display.
+ * @param disp pointer to a display (NULL to use the default display)
+ */
+void lv_disp_clean_dcache(lv_disp_t * disp);
+
+/**
+ * Temporarily enable and disable the invalidation of the display.
+ * @param disp pointer to a display (NULL to use the default display)
+ * @param en true: enable invalidation; false: invalidation
+ */
+void lv_disp_enable_invalidation(lv_disp_t * disp, bool en);
+
+/**
+ * Get display invalidation is enabled.
+ * @param disp pointer to a display (NULL to use the default display)
+ * @return return true if invalidation is enabled
+ */
+bool lv_disp_is_invalidation_enabled(lv_disp_t * disp);
+
+/**
+ * Get a pointer to the screen refresher timer to
+ * modify its parameters with `lv_timer_...` functions.
+ * @param disp pointer to a display
+ * @return pointer to the display refresher timer. (NULL on error)
+ */
+lv_timer_t * _lv_disp_get_refr_timer(lv_disp_t * disp);
+
+/*------------------------------------------------
+ * To improve backward compatibility
+ * Recommended only if you have one display
+ *------------------------------------------------*/
+
+/**
+ * Get the active screen of the default display
+ * @return pointer to the active screen
+ */
+static inline lv_obj_t * lv_scr_act(void)
+{
+ return lv_disp_get_scr_act(lv_disp_get_default());
+}
+
+/**
+ * Get the top layer of the default display
+ * @return pointer to the top layer
+ */
+static inline lv_obj_t * lv_layer_top(void)
+{
+ return lv_disp_get_layer_top(lv_disp_get_default());
+}
+
+/**
+ * Get the active screen of the default display
+ * @return pointer to the sys layer
+ */
+static inline lv_obj_t * lv_layer_sys(void)
+{
+ return lv_disp_get_layer_sys(lv_disp_get_default());
+}
+
+static inline void lv_scr_load(lv_obj_t * scr)
+{
+ lv_disp_load_scr(scr);
+}
+
+/**********************
+ * MACROS
+ **********************/
+
+/*------------------------------------------------
+ * To improve backward compatibility
+ * Recommended only if you have one display
+ *------------------------------------------------*/
+
+#ifndef LV_HOR_RES
+/**
+ * The horizontal resolution of the currently active display.
+ */
+#define LV_HOR_RES lv_disp_get_hor_res(lv_disp_get_default())
+#endif
+
+#ifndef LV_VER_RES
+/**
+ * The vertical resolution of the currently active display.
+ */
+#define LV_VER_RES lv_disp_get_ver_res(lv_disp_get_default())
+#endif
+
+/**
+ * Scale the given number of pixels (a distance or size) relative to a 160 DPI display
+ * considering the DPI of the default display.
+ * It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
+ * DPI of the display.
+ * @param n the number of pixels to scale
+ * @return `n x current_dpi/160`
+ */
+static inline lv_coord_t lv_dpx(lv_coord_t n)
+{
+ return LV_DPX(n);
+}
+
+/**
+ * Scale the given number of pixels (a distance or size) relative to a 160 DPI display
+ * considering the DPI of the given display.
+ * It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
+ * DPI of the display.
+ * @param obj a display whose dpi should be considered
+ * @param n the number of pixels to scale
+ * @return `n x current_dpi/160`
+ */
+static inline lv_coord_t lv_disp_dpx(const lv_disp_t * disp, lv_coord_t n)
+{
+ return _LV_DPX_CALC(lv_disp_get_dpi(disp), n);
+}
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_DISP_H*/
diff --git a/lib/lvgl/src/core/lv_event.c b/lib/lvgl/src/core/lv_event.c
new file mode 100644
index 00000000..f53ceac1
--- /dev/null
+++ b/lib/lvgl/src/core/lv_event.c
@@ -0,0 +1,527 @@
+/**
+ * @file lv_event.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include "lv_indev.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_obj_class
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef struct _lv_event_dsc_t {
+ lv_event_cb_t cb;
+ void * user_data;
+ lv_event_code_t filter : 8;
+} lv_event_dsc_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static lv_event_dsc_t * lv_obj_get_event_dsc(const lv_obj_t * obj, uint32_t id);
+static lv_res_t event_send_core(lv_event_t * e);
+static bool event_is_bubbled(lv_event_t * e);
+
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static lv_event_t * event_head;
+
+/**********************
+ * MACROS
+ **********************/
+#if LV_LOG_TRACE_EVENT
+ #define EVENT_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
+#else
+ #define EVENT_TRACE(...)
+#endif
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_res_t lv_event_send(lv_obj_t * obj, lv_event_code_t event_code, void * param)
+{
+ if(obj == NULL) return LV_RES_OK;
+
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_event_t e;
+ e.target = obj;
+ e.current_target = obj;
+ e.code = event_code;
+ e.user_data = NULL;
+ e.param = param;
+ e.deleted = 0;
+ e.stop_bubbling = 0;
+ e.stop_processing = 0;
+
+ /*Build a simple linked list from the objects used in the events
+ *It's important to know if this object was deleted by a nested event
+ *called from this `event_cb`.*/
+ e.prev = event_head;
+ event_head = &e;
+
+ /*Send the event*/
+ lv_res_t res = event_send_core(&e);
+
+ /*Remove this element from the list*/
+ event_head = e.prev;
+
+ return res;
+}
+
+
+lv_res_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e)
+{
+ const lv_obj_class_t * base;
+ if(class_p == NULL) base = e->current_target->class_p;
+ else base = class_p->base_class;
+
+ /*Find a base in which call the ancestor's event handler_cb if set*/
+ while(base && base->event_cb == NULL) base = base->base_class;
+
+ if(base == NULL) return LV_RES_OK;
+ if(base->event_cb == NULL) return LV_RES_OK;
+
+ /*Call the actual event callback*/
+ e->user_data = NULL;
+ base->event_cb(base, e);
+
+ lv_res_t res = LV_RES_OK;
+ /*Stop if the object is deleted*/
+ if(e->deleted) res = LV_RES_INV;
+
+ return res;
+}
+
+
+lv_obj_t * lv_event_get_target(lv_event_t * e)
+{
+ return e->target;
+}
+
+lv_obj_t * lv_event_get_current_target(lv_event_t * e)
+{
+ return e->current_target;
+}
+
+lv_event_code_t lv_event_get_code(lv_event_t * e)
+{
+ return e->code & ~LV_EVENT_PREPROCESS;
+}
+
+void * lv_event_get_param(lv_event_t * e)
+{
+ return e->param;
+}
+
+void * lv_event_get_user_data(lv_event_t * e)
+{
+ return e->user_data;
+}
+
+void lv_event_stop_bubbling(lv_event_t * e)
+{
+ e->stop_bubbling = 1;
+}
+
+void lv_event_stop_processing(lv_event_t * e)
+{
+ e->stop_processing = 1;
+}
+
+
+uint32_t lv_event_register_id(void)
+{
+ static uint32_t last_id = _LV_EVENT_LAST;
+ last_id ++;
+ return last_id;
+}
+
+void _lv_event_mark_deleted(lv_obj_t * obj)
+{
+ lv_event_t * e = event_head;
+
+ while(e) {
+ if(e->current_target == obj || e->target == obj) e->deleted = 1;
+ e = e->prev;
+ }
+}
+
+
+struct _lv_event_dsc_t * lv_obj_add_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter,
+ void * user_data)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ lv_obj_allocate_spec_attr(obj);
+
+ obj->spec_attr->event_dsc_cnt++;
+ obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
+ obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
+ LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
+
+ obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].cb = event_cb;
+ obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].filter = filter;
+ obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1].user_data = user_data;
+
+ return &obj->spec_attr->event_dsc[obj->spec_attr->event_dsc_cnt - 1];
+}
+
+bool lv_obj_remove_event_cb(lv_obj_t * obj, lv_event_cb_t event_cb)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ if(obj->spec_attr == NULL) return false;
+
+ int32_t i = 0;
+ for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
+ if(event_cb == NULL || obj->spec_attr->event_dsc[i].cb == event_cb) {
+ /*Shift the remaining event handlers forward*/
+ for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) {
+ obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1];
+ }
+ obj->spec_attr->event_dsc_cnt--;
+ obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
+ obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
+ LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
+ return true;
+ }
+ }
+
+ /*No event handler found*/
+ return false;
+}
+
+bool lv_obj_remove_event_cb_with_user_data(lv_obj_t * obj, lv_event_cb_t event_cb, const void * user_data)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ if(obj->spec_attr == NULL) return false;
+
+ int32_t i = 0;
+ for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
+ if((event_cb == NULL || obj->spec_attr->event_dsc[i].cb == event_cb) &&
+ obj->spec_attr->event_dsc[i].user_data == user_data) {
+ /*Shift the remaining event handlers forward*/
+ for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) {
+ obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1];
+ }
+ obj->spec_attr->event_dsc_cnt--;
+ obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
+ obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
+ LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
+ return true;
+ }
+ }
+
+ /*No event handler found*/
+ return false;
+}
+
+
+bool lv_obj_remove_event_dsc(lv_obj_t * obj, struct _lv_event_dsc_t * event_dsc)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ if(obj->spec_attr == NULL) return false;
+
+ int32_t i = 0;
+ for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
+ if(&obj->spec_attr->event_dsc[i] == event_dsc) {
+ /*Shift the remaining event handlers forward*/
+ for(; i < (obj->spec_attr->event_dsc_cnt - 1); i++) {
+ obj->spec_attr->event_dsc[i] = obj->spec_attr->event_dsc[i + 1];
+ }
+ obj->spec_attr->event_dsc_cnt--;
+ obj->spec_attr->event_dsc = lv_mem_realloc(obj->spec_attr->event_dsc,
+ obj->spec_attr->event_dsc_cnt * sizeof(lv_event_dsc_t));
+ LV_ASSERT_MALLOC(obj->spec_attr->event_dsc);
+ return true;
+ }
+ }
+
+ /*No event handler found*/
+ return false;
+}
+
+void * lv_obj_get_event_user_data(struct _lv_obj_t * obj, lv_event_cb_t event_cb)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ if(obj->spec_attr == NULL) return NULL;
+
+ int32_t i = 0;
+ for(i = 0; i < obj->spec_attr->event_dsc_cnt; i++) {
+ if(event_cb == obj->spec_attr->event_dsc[i].cb) return obj->spec_attr->event_dsc[i].user_data;
+ }
+ return NULL;
+}
+
+lv_indev_t * lv_event_get_indev(lv_event_t * e)
+{
+
+ if(e->code == LV_EVENT_PRESSED ||
+ e->code == LV_EVENT_PRESSING ||
+ e->code == LV_EVENT_PRESS_LOST ||
+ e->code == LV_EVENT_SHORT_CLICKED ||
+ e->code == LV_EVENT_LONG_PRESSED ||
+ e->code == LV_EVENT_LONG_PRESSED_REPEAT ||
+ e->code == LV_EVENT_CLICKED ||
+ e->code == LV_EVENT_RELEASED ||
+ e->code == LV_EVENT_SCROLL_BEGIN ||
+ e->code == LV_EVENT_SCROLL_END ||
+ e->code == LV_EVENT_SCROLL ||
+ e->code == LV_EVENT_GESTURE ||
+ e->code == LV_EVENT_KEY ||
+ e->code == LV_EVENT_FOCUSED ||
+ e->code == LV_EVENT_DEFOCUSED ||
+ e->code == LV_EVENT_LEAVE) {
+ return lv_event_get_param(e);
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ return NULL;
+ }
+}
+
+lv_obj_draw_part_dsc_t * lv_event_get_draw_part_dsc(lv_event_t * e)
+{
+ if(e->code == LV_EVENT_DRAW_PART_BEGIN ||
+ e->code == LV_EVENT_DRAW_PART_END) {
+ return lv_event_get_param(e);
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ return NULL;
+ }
+}
+
+lv_draw_ctx_t * lv_event_get_draw_ctx(lv_event_t * e)
+{
+ if(e->code == LV_EVENT_DRAW_MAIN ||
+ e->code == LV_EVENT_DRAW_MAIN_BEGIN ||
+ e->code == LV_EVENT_DRAW_MAIN_END ||
+ e->code == LV_EVENT_DRAW_POST ||
+ e->code == LV_EVENT_DRAW_POST_BEGIN ||
+ e->code == LV_EVENT_DRAW_POST_END) {
+ return lv_event_get_param(e);
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ return NULL;
+ }
+}
+
+const lv_area_t * lv_event_get_old_size(lv_event_t * e)
+{
+ if(e->code == LV_EVENT_SIZE_CHANGED) {
+ return lv_event_get_param(e);
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ return NULL;
+ }
+}
+
+uint32_t lv_event_get_key(lv_event_t * e)
+{
+ if(e->code == LV_EVENT_KEY) {
+ uint32_t * k = lv_event_get_param(e);
+ if(k) return *k;
+ else return 0;
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ return 0;
+ }
+}
+
+lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e)
+{
+ if(e->code == LV_EVENT_SCROLL_BEGIN) {
+ return lv_event_get_param(e);
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ return 0;
+ }
+}
+
+void lv_event_set_ext_draw_size(lv_event_t * e, lv_coord_t size)
+{
+ if(e->code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
+ lv_coord_t * cur_size = lv_event_get_param(e);
+ *cur_size = LV_MAX(*cur_size, size);
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ }
+}
+
+lv_point_t * lv_event_get_self_size_info(lv_event_t * e)
+{
+ if(e->code == LV_EVENT_GET_SELF_SIZE) {
+ return lv_event_get_param(e);
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ return 0;
+ }
+}
+
+lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e)
+{
+ if(e->code == LV_EVENT_HIT_TEST) {
+ return lv_event_get_param(e);
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ return 0;
+ }
+}
+
+const lv_area_t * lv_event_get_cover_area(lv_event_t * e)
+{
+ if(e->code == LV_EVENT_COVER_CHECK) {
+ lv_cover_check_info_t * p = lv_event_get_param(e);
+ return p->area;
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ return NULL;
+ }
+}
+
+void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res)
+{
+ if(e->code == LV_EVENT_COVER_CHECK) {
+ lv_cover_check_info_t * p = lv_event_get_param(e);
+ if(res > p->res) p->res = res; /*Save only "stronger" results*/
+ }
+ else {
+ LV_LOG_WARN("Not interpreted with this event code");
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static lv_event_dsc_t * lv_obj_get_event_dsc(const lv_obj_t * obj, uint32_t id)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ if(!obj->spec_attr) return NULL;
+ if(id >= obj->spec_attr->event_dsc_cnt) return NULL;
+
+ return &obj->spec_attr->event_dsc[id];
+}
+
+static lv_res_t event_send_core(lv_event_t * e)
+{
+ EVENT_TRACE("Sending event %d to %p with %p param", e->code, (void *)e->current_target, e->param);
+
+ /*Call the input device's feedback callback if set*/
+ lv_indev_t * indev_act = lv_indev_get_act();
+ if(indev_act) {
+ if(indev_act->driver->feedback_cb) indev_act->driver->feedback_cb(indev_act->driver, e->code);
+ if(e->stop_processing) return LV_RES_OK;
+ if(e->deleted) return LV_RES_INV;
+ }
+
+ lv_res_t res = LV_RES_OK;
+ lv_event_dsc_t * event_dsc = lv_obj_get_event_dsc(e->current_target, 0);
+
+ uint32_t i = 0;
+ while(event_dsc && res == LV_RES_OK) {
+ if(event_dsc->cb && ((event_dsc->filter & LV_EVENT_PREPROCESS) == LV_EVENT_PREPROCESS)
+ && (event_dsc->filter == (LV_EVENT_ALL | LV_EVENT_PREPROCESS) ||
+ (event_dsc->filter & ~LV_EVENT_PREPROCESS) == e->code)) {
+ e->user_data = event_dsc->user_data;
+ event_dsc->cb(e);
+
+ if(e->stop_processing) return LV_RES_OK;
+ /*Stop if the object is deleted*/
+ if(e->deleted) return LV_RES_INV;
+ }
+
+ i++;
+ event_dsc = lv_obj_get_event_dsc(e->current_target, i);
+ }
+
+ res = lv_obj_event_base(NULL, e);
+
+ event_dsc = res == LV_RES_INV ? NULL : lv_obj_get_event_dsc(e->current_target, 0);
+
+ i = 0;
+ while(event_dsc && res == LV_RES_OK) {
+ if(event_dsc->cb && ((event_dsc->filter & LV_EVENT_PREPROCESS) == 0)
+ && (event_dsc->filter == LV_EVENT_ALL || event_dsc->filter == e->code)) {
+ e->user_data = event_dsc->user_data;
+ event_dsc->cb(e);
+
+ if(e->stop_processing) return LV_RES_OK;
+ /*Stop if the object is deleted*/
+ if(e->deleted) return LV_RES_INV;
+ }
+
+ i++;
+ event_dsc = lv_obj_get_event_dsc(e->current_target, i);
+ }
+
+ if(res == LV_RES_OK && e->current_target->parent && event_is_bubbled(e)) {
+ e->current_target = e->current_target->parent;
+ res = event_send_core(e);
+ if(res != LV_RES_OK) return LV_RES_INV;
+ }
+
+ return res;
+}
+
+static bool event_is_bubbled(lv_event_t * e)
+{
+ if(e->stop_bubbling) return false;
+
+ /*Event codes that always bubble*/
+ switch(e->code) {
+ case LV_EVENT_CHILD_CREATED:
+ case LV_EVENT_CHILD_DELETED:
+ return true;
+ default:
+ break;
+ }
+
+ /*Check other codes only if bubbling is enabled*/
+ if(lv_obj_has_flag(e->current_target, LV_OBJ_FLAG_EVENT_BUBBLE) == false) return false;
+
+ switch(e->code) {
+ case LV_EVENT_HIT_TEST:
+ case LV_EVENT_COVER_CHECK:
+ case LV_EVENT_REFR_EXT_DRAW_SIZE:
+ case LV_EVENT_DRAW_MAIN_BEGIN:
+ case LV_EVENT_DRAW_MAIN:
+ case LV_EVENT_DRAW_MAIN_END:
+ case LV_EVENT_DRAW_POST_BEGIN:
+ case LV_EVENT_DRAW_POST:
+ case LV_EVENT_DRAW_POST_END:
+ case LV_EVENT_DRAW_PART_BEGIN:
+ case LV_EVENT_DRAW_PART_END:
+ case LV_EVENT_REFRESH:
+ case LV_EVENT_DELETE:
+ case LV_EVENT_CHILD_CREATED:
+ case LV_EVENT_CHILD_DELETED:
+ case LV_EVENT_CHILD_CHANGED:
+ case LV_EVENT_SIZE_CHANGED:
+ case LV_EVENT_STYLE_CHANGED:
+ case LV_EVENT_GET_SELF_SIZE:
+ return false;
+ default:
+ return true;
+ }
+}
diff --git a/lib/lvgl/src/core/lv_event.h b/lib/lvgl/src/core/lv_event.h
new file mode 100644
index 00000000..d5a9eb6b
--- /dev/null
+++ b/lib/lvgl/src/core/lv_event.h
@@ -0,0 +1,363 @@
+/**
+ * @file lv_event.h
+ *
+ */
+
+#ifndef LV_EVENT_H
+#define LV_EVENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdbool.h>
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_obj_t;
+struct _lv_event_dsc_t;
+
+/**
+ * Type of event being sent to the object.
+ */
+typedef enum {
+ LV_EVENT_ALL = 0,
+
+ /** Input device events*/
+ LV_EVENT_PRESSED, /**< The object has been pressed*/
+ LV_EVENT_PRESSING, /**< The object is being pressed (called continuously while pressing)*/
+ LV_EVENT_PRESS_LOST, /**< The object is still being pressed but slid cursor/finger off of the object */
+ LV_EVENT_SHORT_CLICKED, /**< The object was pressed for a short period of time, then released it. Not called if scrolled.*/
+ LV_EVENT_LONG_PRESSED, /**< Object has been pressed for at least `long_press_time`. Not called if scrolled.*/
+ LV_EVENT_LONG_PRESSED_REPEAT, /**< Called after `long_press_time` in every `long_press_repeat_time` ms. Not called if scrolled.*/
+ LV_EVENT_CLICKED, /**< Called on release if not scrolled (regardless to long press)*/
+ LV_EVENT_RELEASED, /**< Called in every cases when the object has been released*/
+ LV_EVENT_SCROLL_BEGIN, /**< Scrolling begins. The event parameter is a pointer to the animation of the scroll. Can be modified*/
+ LV_EVENT_SCROLL_END, /**< Scrolling ends*/
+ LV_EVENT_SCROLL, /**< Scrolling*/
+ LV_EVENT_GESTURE, /**< A gesture is detected. Get the gesture with `lv_indev_get_gesture_dir(lv_indev_get_act());` */
+ LV_EVENT_KEY, /**< A key is sent to the object. Get the key with `lv_indev_get_key(lv_indev_get_act());`*/
+ LV_EVENT_FOCUSED, /**< The object is focused*/
+ LV_EVENT_DEFOCUSED, /**< The object is defocused*/
+ LV_EVENT_LEAVE, /**< The object is defocused but still selected*/
+ LV_EVENT_HIT_TEST, /**< Perform advanced hit-testing*/
+
+ /** Drawing events*/
+ LV_EVENT_COVER_CHECK, /**< Check if the object fully covers an area. The event parameter is `lv_cover_check_info_t *`.*/
+ LV_EVENT_REFR_EXT_DRAW_SIZE, /**< Get the required extra draw area around the object (e.g. for shadow). The event parameter is `lv_coord_t *` to store the size.*/
+ LV_EVENT_DRAW_MAIN_BEGIN, /**< Starting the main drawing phase*/
+ LV_EVENT_DRAW_MAIN, /**< Perform the main drawing*/
+ LV_EVENT_DRAW_MAIN_END, /**< Finishing the main drawing phase*/
+ LV_EVENT_DRAW_POST_BEGIN, /**< Starting the post draw phase (when all children are drawn)*/
+ LV_EVENT_DRAW_POST, /**< Perform the post draw phase (when all children are drawn)*/
+ LV_EVENT_DRAW_POST_END, /**< Finishing the post draw phase (when all children are drawn)*/
+ LV_EVENT_DRAW_PART_BEGIN, /**< Starting to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */
+ LV_EVENT_DRAW_PART_END, /**< Finishing to draw a part. The event parameter is `lv_obj_draw_dsc_t *`. */
+
+ /** Special events*/
+ LV_EVENT_VALUE_CHANGED, /**< The object's value has changed (i.e. slider moved)*/
+ LV_EVENT_INSERT, /**< A text is inserted to the object. The event data is `char *` being inserted.*/
+ LV_EVENT_REFRESH, /**< Notify the object to refresh something on it (for the user)*/
+ LV_EVENT_READY, /**< A process has finished*/
+ LV_EVENT_CANCEL, /**< A process has been cancelled */
+
+ /** Other events*/
+ LV_EVENT_DELETE, /**< Object is being deleted*/
+ LV_EVENT_CHILD_CHANGED, /**< Child was removed, added, or its size, position were changed */
+ LV_EVENT_CHILD_CREATED, /**< Child was created, always bubbles up to all parents*/
+ LV_EVENT_CHILD_DELETED, /**< Child was deleted, always bubbles up to all parents*/
+ LV_EVENT_SCREEN_UNLOAD_START, /**< A screen unload started, fired immediately when scr_load is called*/
+ LV_EVENT_SCREEN_LOAD_START, /**< A screen load started, fired when the screen change delay is expired*/
+ LV_EVENT_SCREEN_LOADED, /**< A screen was loaded*/
+ LV_EVENT_SCREEN_UNLOADED, /**< A screen was unloaded*/
+ LV_EVENT_SIZE_CHANGED, /**< Object coordinates/size have changed*/
+ LV_EVENT_STYLE_CHANGED, /**< Object's style has changed*/
+ LV_EVENT_LAYOUT_CHANGED, /**< The children position has changed due to a layout recalculation*/
+ LV_EVENT_GET_SELF_SIZE, /**< Get the internal size of a widget*/
+
+ _LV_EVENT_LAST, /** Number of default events*/
+
+
+ LV_EVENT_PREPROCESS = 0x80, /** This is a flag that can be set with an event so it's processed
+ before the class default event processing */
+} lv_event_code_t;
+
+typedef struct _lv_event_t {
+ struct _lv_obj_t * target;
+ struct _lv_obj_t * current_target;
+ lv_event_code_t code;
+ void * user_data;
+ void * param;
+ struct _lv_event_t * prev;
+ uint8_t deleted : 1;
+ uint8_t stop_processing : 1;
+ uint8_t stop_bubbling : 1;
+} lv_event_t;
+
+/**
+ * @brief Event callback.
+ * Events are used to notify the user of some action being taken on the object.
+ * For details, see ::lv_event_t.
+ */
+typedef void (*lv_event_cb_t)(lv_event_t * e);
+
+/**
+ * Used as the event parameter of ::LV_EVENT_HIT_TEST to check if an `point` can click the object or not.
+ * `res` should be set like this:
+ * - If already set to `false` an other event wants that point non clickable. If you want to respect it leave it as `false` or set `true` to overwrite it.
+ * - If already set `true` and `point` shouldn't be clickable set to `false`
+ * - If already set to `true` you agree that `point` can click the object leave it as `true`
+ */
+typedef struct {
+ const lv_point_t * point; /**< A point relative to screen to check if it can click the object or not*/
+ bool res; /**< true: `point` can click the object; false: it cannot*/
+} lv_hit_test_info_t;
+
+/**
+ * Used as the event parameter of ::LV_EVENT_COVER_CHECK to check if an area is covered by the object or not.
+ * In the event use `const lv_area_t * area = lv_event_get_cover_area(e)` to get the area to check
+ * and `lv_event_set_cover_res(e, res)` to set the result.
+ */
+typedef struct {
+ lv_cover_res_t res;
+ const lv_area_t * area;
+} lv_cover_check_info_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Send an event to the object
+ * @param obj pointer to an object
+ * @param event_code the type of the event from `lv_event_t`
+ * @param param arbitrary data depending on the widget type and the event. (Usually `NULL`)
+ * @return LV_RES_OK: `obj` was not deleted in the event; LV_RES_INV: `obj` was deleted in the event_code
+ */
+lv_res_t lv_event_send(struct _lv_obj_t * obj, lv_event_code_t event_code, void * param);
+
+/**
+ * Used by the widgets internally to call the ancestor widget types's event handler
+ * @param class_p pointer to the class of the widget (NOT the ancestor class)
+ * @param e pointer to the event descriptor
+ * @return LV_RES_OK: the target object was not deleted in the event; LV_RES_INV: it was deleted in the event_code
+ */
+lv_res_t lv_obj_event_base(const lv_obj_class_t * class_p, lv_event_t * e);
+
+/**
+ * Get the object originally targeted by the event. It's the same even if the event is bubbled.
+ * @param e pointer to the event descriptor
+ * @return the target of the event_code
+ */
+struct _lv_obj_t * lv_event_get_target(lv_event_t * e);
+
+/**
+ * Get the current target of the event. It's the object which event handler being called.
+ * If the event is not bubbled it's the same as "normal" target.
+ * @param e pointer to the event descriptor
+ * @return pointer to the current target of the event_code
+ */
+struct _lv_obj_t * lv_event_get_current_target(lv_event_t * e);
+
+/**
+ * Get the event code of an event
+ * @param e pointer to the event descriptor
+ * @return the event code. (E.g. `LV_EVENT_CLICKED`, `LV_EVENT_FOCUSED`, etc)
+ */
+lv_event_code_t lv_event_get_code(lv_event_t * e);
+
+/**
+ * Get the parameter passed when the event was sent
+ * @param e pointer to the event descriptor
+ * @return pointer to the parameter
+ */
+void * lv_event_get_param(lv_event_t * e);
+
+/**
+ * Get the user_data passed when the event was registered on the object
+ * @param e pointer to the event descriptor
+ * @return pointer to the user_data
+ */
+void * lv_event_get_user_data(lv_event_t * e);
+
+/**
+ * Stop the event from bubbling.
+ * This is only valid when called in the middle of an event processing chain.
+ * @param e pointer to the event descriptor
+ */
+void lv_event_stop_bubbling(lv_event_t * e);
+
+/**
+ * Stop processing this event.
+ * This is only valid when called in the middle of an event processing chain.
+ * @param e pointer to the event descriptor
+ */
+void lv_event_stop_processing(lv_event_t * e);
+
+/**
+ * Register a new, custom event ID.
+ * It can be used the same way as e.g. `LV_EVENT_CLICKED` to send custom events
+ * @return the new event id
+ * @example
+ * uint32_t LV_EVENT_MINE = 0;
+ * ...
+ * e = lv_event_register_id();
+ * ...
+ * lv_event_send(obj, LV_EVENT_MINE, &some_data);
+ */
+uint32_t lv_event_register_id(void);
+
+/**
+ * Nested events can be called and one of them might belong to an object that is being deleted.
+ * Mark this object's `event_temp_data` deleted to know that its `lv_event_send` should return `LV_RES_INV`
+ * @param obj pointer to an object to mark as deleted
+ */
+void _lv_event_mark_deleted(struct _lv_obj_t * obj);
+
+
+/**
+ * Add an event handler function for an object.
+ * Used by the user to react on event which happens with the object.
+ * An object can have multiple event handler. They will be called in the same order as they were added.
+ * @param obj pointer to an object
+ * @param filter and event code (e.g. `LV_EVENT_CLICKED`) on which the event should be called. `LV_EVENT_ALL` can be sued the receive all the events.
+ * @param event_cb the new event function
+ * @param user_data custom data data will be available in `event_cb`
+ * @return a pointer the event descriptor. Can be used in ::lv_obj_remove_event_dsc
+ */
+struct _lv_event_dsc_t * lv_obj_add_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb, lv_event_code_t filter,
+ void * user_data);
+
+/**
+ * Remove an event handler function for an object.
+ * @param obj pointer to an object
+ * @param event_cb the event function to remove, or `NULL` to remove the firstly added event callback
+ * @return true if any event handlers were removed
+ */
+bool lv_obj_remove_event_cb(struct _lv_obj_t * obj, lv_event_cb_t event_cb);
+
+/**
+ * Remove an event handler function with a specific user_data from an object.
+ * @param obj pointer to an object
+ * @param event_cb the event function to remove, or `NULL` only `user_data` matters.
+ * @param event_user_data the user_data specified in ::lv_obj_add_event_cb
+ * @return true if any event handlers were removed
+ */
+bool lv_obj_remove_event_cb_with_user_data(struct _lv_obj_t * obj, lv_event_cb_t event_cb,
+ const void * event_user_data);
+
+/**
+ * DEPRECATED because doesn't work if multiple event handlers are added to an object.
+ * Remove an event handler function for an object.
+ * @param obj pointer to an object
+ * @param event_dsc pointer to an event descriptor to remove (returned by ::lv_obj_add_event_cb)
+ * @return true if any event handlers were removed
+ */
+bool lv_obj_remove_event_dsc(struct _lv_obj_t * obj, struct _lv_event_dsc_t * event_dsc);
+
+/**
+ * The user data of an event object event callback. Always the first match with `event_cb` will be returned.
+ * @param obj pointer to an object
+ * @param event_cb the event function
+ * @return the user_data
+ */
+void * lv_obj_get_event_user_data(struct _lv_obj_t * obj, lv_event_cb_t event_cb);
+
+/**
+ * Get the input device passed as parameter to indev related events.
+ * @param e pointer to an event
+ * @return the indev that triggered the event or NULL if called on a not indev related event
+ */
+lv_indev_t * lv_event_get_indev(lv_event_t * e);
+
+/**
+ * Get the part draw descriptor passed as parameter to `LV_EVENT_DRAW_PART_BEGIN/END`.
+ * @param e pointer to an event
+ * @return the part draw descriptor to hook the drawing or NULL if called on an unrelated event
+ */
+lv_obj_draw_part_dsc_t * lv_event_get_draw_part_dsc(lv_event_t * e);
+
+/**
+ * Get the draw context which should be the first parameter of the draw functions.
+ * Namely: `LV_EVENT_DRAW_MAIN/POST`, `LV_EVENT_DRAW_MAIN/POST_BEGIN`, `LV_EVENT_DRAW_MAIN/POST_END`
+ * @param e pointer to an event
+ * @return pointer to a draw context or NULL if called on an unrelated event
+ */
+lv_draw_ctx_t * lv_event_get_draw_ctx(lv_event_t * e);
+
+/**
+ * Get the old area of the object before its size was changed. Can be used in `LV_EVENT_SIZE_CHANGED`
+ * @param e pointer to an event
+ * @return the old absolute area of the object or NULL if called on an unrelated event
+ */
+const lv_area_t * lv_event_get_old_size(lv_event_t * e);
+
+/**
+ * Get the key passed as parameter to an event. Can be used in `LV_EVENT_KEY`
+ * @param e pointer to an event
+ * @return the triggering key or NULL if called on an unrelated event
+ */
+uint32_t lv_event_get_key(lv_event_t * e);
+
+/**
+ * Get the animation descriptor of a scrolling. Can be used in `LV_EVENT_SCROLL_BEGIN`
+ * @param e pointer to an event
+ * @return the animation that will scroll the object. (can be modified as required)
+ */
+lv_anim_t * lv_event_get_scroll_anim(lv_event_t * e);
+
+/**
+ * Set the new extra draw size. Can be used in `LV_EVENT_REFR_EXT_DRAW_SIZE`
+ * @param e pointer to an event
+ * @param size The new extra draw size
+ */
+void lv_event_set_ext_draw_size(lv_event_t * e, lv_coord_t size);
+
+/**
+ * Get a pointer to an `lv_point_t` variable in which the self size should be saved (width in `point->x` and height `point->y`).
+ * Can be used in `LV_EVENT_GET_SELF_SIZE`
+ * @param e pointer to an event
+ * @return pointer to `lv_point_t` or NULL if called on an unrelated event
+ */
+lv_point_t * lv_event_get_self_size_info(lv_event_t * e);
+
+/**
+ * Get a pointer to an `lv_hit_test_info_t` variable in which the hit test result should be saved. Can be used in `LV_EVENT_HIT_TEST`
+ * @param e pointer to an event
+ * @return pointer to `lv_hit_test_info_t` or NULL if called on an unrelated event
+ */
+lv_hit_test_info_t * lv_event_get_hit_test_info(lv_event_t * e);
+
+/**
+ * Get a pointer to an area which should be examined whether the object fully covers it or not.
+ * Can be used in `LV_EVENT_HIT_TEST`
+ * @param e pointer to an event
+ * @return an area with absolute coordinates to check
+ */
+const lv_area_t * lv_event_get_cover_area(lv_event_t * e);
+
+/**
+ * Set the result of cover checking. Can be used in `LV_EVENT_COVER_CHECK`
+ * @param e pointer to an event
+ * @param res an element of ::lv_cover_check_info_t
+ */
+void lv_event_set_cover_res(lv_event_t * e, lv_cover_res_t res);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_EVENT_H*/
diff --git a/lib/lvgl/src/core/lv_group.c b/lib/lvgl/src/core/lv_group.c
new file mode 100644
index 00000000..2c4fa93e
--- /dev/null
+++ b/lib/lvgl/src/core/lv_group.c
@@ -0,0 +1,497 @@
+/**
+ * @file lv_group.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stddef.h>
+
+#include "lv_group.h"
+#include "../misc/lv_gc.h"
+#include "../core/lv_obj.h"
+#include "../core/lv_indev.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
+ void * (*move)(const lv_ll_t *, const void *));
+static void lv_group_refocus(lv_group_t * g);
+static lv_indev_t * get_indev(const lv_group_t * g);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static lv_group_t * default_group;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void _lv_group_init(void)
+{
+ _lv_ll_init(&LV_GC_ROOT(_lv_group_ll), sizeof(lv_group_t));
+}
+
+lv_group_t * lv_group_create(void)
+{
+ lv_group_t * group = _lv_ll_ins_head(&LV_GC_ROOT(_lv_group_ll));
+ LV_ASSERT_MALLOC(group);
+ if(group == NULL) return NULL;
+ _lv_ll_init(&group->obj_ll, sizeof(lv_obj_t *));
+
+ group->obj_focus = NULL;
+ group->frozen = 0;
+ group->focus_cb = NULL;
+ group->edge_cb = NULL;
+ group->editing = 0;
+ group->refocus_policy = LV_GROUP_REFOCUS_POLICY_PREV;
+ group->wrap = 1;
+
+#if LV_USE_USER_DATA
+ group->user_data = NULL;
+#endif
+
+ return group;
+}
+
+void lv_group_del(lv_group_t * group)
+{
+ /*Defocus the currently focused object*/
+ if(group->obj_focus != NULL) {
+ lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
+ lv_obj_invalidate(*group->obj_focus);
+ }
+
+ /*Remove the objects from the group*/
+ lv_obj_t ** obj;
+ _LV_LL_READ(&group->obj_ll, obj) {
+ if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
+ }
+
+ /*Remove the group from any indev devices */
+ lv_indev_t * indev = lv_indev_get_next(NULL);
+ while(indev) {
+ if(indev->group == group) {
+ lv_indev_set_group(indev, NULL);
+ }
+ indev = lv_indev_get_next(indev);
+ }
+
+ _lv_ll_clear(&(group->obj_ll));
+ _lv_ll_remove(&LV_GC_ROOT(_lv_group_ll), group);
+ lv_mem_free(group);
+}
+
+void lv_group_set_default(lv_group_t * group)
+{
+ default_group = group;
+}
+
+lv_group_t * lv_group_get_default(void)
+{
+ return default_group;
+}
+
+void lv_group_add_obj(lv_group_t * group, lv_obj_t * obj)
+{
+ if(group == NULL) return;
+
+ LV_LOG_TRACE("begin");
+
+ /*Be sure the object is removed from its current group*/
+ lv_group_remove_obj(obj);
+
+ /*Do not add the object twice*/
+ lv_obj_t ** obj_i;
+ _LV_LL_READ(&group->obj_ll, obj_i) {
+ if((*obj_i) == obj) {
+ LV_LOG_INFO("the object is already added to this group");
+ return;
+ }
+ }
+
+ /*If the object is already in a group and focused then refocus it*/
+ lv_group_t * group_cur = lv_obj_get_group(obj);
+ if(group_cur) {
+ if(obj->spec_attr->group_p && *(obj->spec_attr->group_p->obj_focus) == obj) {
+ lv_group_refocus(group_cur);
+
+ LV_LOG_INFO("changing object's group");
+ }
+ }
+
+ if(obj->spec_attr == NULL) lv_obj_allocate_spec_attr(obj);
+ obj->spec_attr->group_p = group;
+
+ lv_obj_t ** next = _lv_ll_ins_tail(&group->obj_ll);
+ LV_ASSERT_MALLOC(next);
+ if(next == NULL) return;
+ *next = obj;
+
+ /*If the head and the tail is equal then there is only one object in the linked list.
+ *In this case automatically activate it*/
+ if(_lv_ll_get_head(&group->obj_ll) == next) {
+ lv_group_refocus(group);
+ }
+
+ LV_LOG_TRACE("finished");
+}
+
+void lv_group_swap_obj(lv_obj_t * obj1, lv_obj_t * obj2)
+{
+ lv_group_t * g1 = lv_obj_get_group(obj1);
+ lv_group_t * g2 = lv_obj_get_group(obj2);
+ if(g1 != g2) return;
+ if(g1 == NULL) return;
+
+ /*Do not add the object twice*/
+ lv_obj_t ** obj_i;
+ _LV_LL_READ(&g1->obj_ll, obj_i) {
+ if((*obj_i) == obj1)(*obj_i) = obj2;
+ else if((*obj_i) == obj2)(*obj_i) = obj1;
+ }
+
+ if(*g1->obj_focus == obj1) lv_group_focus_obj(obj2);
+ else if(*g1->obj_focus == obj2) lv_group_focus_obj(obj1);
+
+}
+
+void lv_group_remove_obj(lv_obj_t * obj)
+{
+ lv_group_t * g = lv_obj_get_group(obj);
+ if(g == NULL) return;
+
+ LV_LOG_TRACE("begin");
+
+ /*Focus on the next object*/
+ if(g->obj_focus && *g->obj_focus == obj) {
+ if(g->frozen) g->frozen = 0;
+
+ /*If this is the only object in the group then focus to nothing.*/
+ if(_lv_ll_get_head(&g->obj_ll) == g->obj_focus && _lv_ll_get_tail(&g->obj_ll) == g->obj_focus) {
+ lv_event_send(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
+ }
+ /*If there more objects in the group then focus to the next/prev object*/
+ else {
+ lv_group_refocus(g);
+ }
+ }
+
+ /*If the focuses object is still the same then it was the only object in the group but it will
+ *be deleted. Set the `obj_focus` to NULL to get back to the initial state of the group with
+ *zero objects*/
+ if(g->obj_focus && *g->obj_focus == obj) {
+ g->obj_focus = NULL;
+ }
+
+ /*Search the object and remove it from its group*/
+ lv_obj_t ** i;
+ _LV_LL_READ(&g->obj_ll, i) {
+ if(*i == obj) {
+ _lv_ll_remove(&g->obj_ll, i);
+ lv_mem_free(i);
+ if(obj->spec_attr) obj->spec_attr->group_p = NULL;
+ break;
+ }
+ }
+ LV_LOG_TRACE("finished");
+}
+
+void lv_group_remove_all_objs(lv_group_t * group)
+{
+ /*Defocus the currently focused object*/
+ if(group->obj_focus != NULL) {
+ lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
+ lv_obj_invalidate(*group->obj_focus);
+ group->obj_focus = NULL;
+ }
+
+ /*Remove the objects from the group*/
+ lv_obj_t ** obj;
+ _LV_LL_READ(&group->obj_ll, obj) {
+ if((*obj)->spec_attr)(*obj)->spec_attr->group_p = NULL;
+ }
+
+ _lv_ll_clear(&(group->obj_ll));
+}
+
+void lv_group_focus_obj(lv_obj_t * obj)
+{
+ if(obj == NULL) return;
+ lv_group_t * g = lv_obj_get_group(obj);
+ if(g == NULL) return;
+
+ if(g->frozen != 0) return;
+
+ /*On defocus edit mode must be leaved*/
+ lv_group_set_editing(g, false);
+
+ lv_obj_t ** i;
+ _LV_LL_READ(&g->obj_ll, i) {
+ if(*i == obj) {
+ if(g->obj_focus != NULL && obj != *g->obj_focus) { /*Do not defocus if the same object needs to be focused again*/
+ lv_res_t res = lv_event_send(*g->obj_focus, LV_EVENT_DEFOCUSED, get_indev(g));
+ if(res != LV_RES_OK) return;
+ lv_obj_invalidate(*g->obj_focus);
+ }
+
+ g->obj_focus = i;
+
+ if(g->obj_focus != NULL) {
+ if(g->focus_cb) g->focus_cb(g);
+ lv_res_t res = lv_event_send(*g->obj_focus, LV_EVENT_FOCUSED, get_indev(g));
+ if(res != LV_RES_OK) return;
+ lv_obj_invalidate(*g->obj_focus);
+ }
+ break;
+ }
+ }
+}
+
+void lv_group_focus_next(lv_group_t * group)
+{
+ bool focus_changed = focus_next_core(group, _lv_ll_get_head, _lv_ll_get_next);
+ if(group->edge_cb) {
+ if(!focus_changed)
+ group->edge_cb(group, true);
+ }
+}
+
+void lv_group_focus_prev(lv_group_t * group)
+{
+ bool focus_changed = focus_next_core(group, _lv_ll_get_tail, _lv_ll_get_prev);
+ if(group->edge_cb) {
+ if(!focus_changed)
+ group->edge_cb(group, false);
+ }
+}
+
+void lv_group_focus_freeze(lv_group_t * group, bool en)
+{
+ if(en == false) group->frozen = 0;
+ else group->frozen = 1;
+}
+
+lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c)
+{
+ lv_obj_t * act = lv_group_get_focused(group);
+ if(act == NULL) return LV_RES_OK;
+
+ if(lv_obj_has_state(act, LV_STATE_DISABLED)) return LV_RES_OK;
+
+ return lv_event_send(act, LV_EVENT_KEY, &c);
+}
+
+void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb)
+{
+ group->focus_cb = focus_cb;
+}
+
+void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb)
+{
+ group->edge_cb = edge_cb;
+}
+
+void lv_group_set_editing(lv_group_t * group, bool edit)
+{
+ if(group == NULL) return;
+ uint8_t en_val = edit ? 1 : 0;
+
+ if(en_val == group->editing) return; /*Do not set the same mode again*/
+
+ group->editing = en_val;
+ lv_obj_t * focused = lv_group_get_focused(group);
+
+ if(focused) {
+ lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
+ if(res != LV_RES_OK) return;
+
+ lv_obj_invalidate(focused);
+ }
+}
+
+void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy)
+{
+ group->refocus_policy = policy & 0x01;
+}
+
+void lv_group_set_wrap(lv_group_t * group, bool en)
+{
+ group->wrap = en ? 1 : 0;
+}
+
+lv_obj_t * lv_group_get_focused(const lv_group_t * group)
+{
+ if(!group) return NULL;
+ if(group->obj_focus == NULL) return NULL;
+
+ return *group->obj_focus;
+}
+
+lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group)
+{
+ if(!group) return NULL;
+ return group->focus_cb;
+}
+
+lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group)
+{
+ if(!group) return NULL;
+ return group->edge_cb;
+}
+
+bool lv_group_get_editing(const lv_group_t * group)
+{
+ if(!group) return false;
+ return group->editing ? true : false;
+}
+
+bool lv_group_get_wrap(lv_group_t * group)
+{
+ if(!group) return false;
+ return group->wrap ? true : false;
+}
+
+uint32_t lv_group_get_obj_count(lv_group_t * group)
+{
+ return _lv_ll_get_len(&group->obj_ll);
+}
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_group_refocus(lv_group_t * g)
+{
+ /*Refocus must temporarily allow wrapping to work correctly*/
+ uint8_t temp_wrap = g->wrap;
+ g->wrap = 1;
+
+ if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_NEXT)
+ lv_group_focus_next(g);
+ else if(g->refocus_policy == LV_GROUP_REFOCUS_POLICY_PREV)
+ lv_group_focus_prev(g);
+ /*Restore wrap property*/
+ g->wrap = temp_wrap;
+}
+
+static bool focus_next_core(lv_group_t * group, void * (*begin)(const lv_ll_t *),
+ void * (*move)(const lv_ll_t *, const void *))
+{
+ bool focus_changed = false;
+ if(group->frozen) return focus_changed;
+
+ lv_obj_t ** obj_next = group->obj_focus;
+ lv_obj_t ** obj_sentinel = NULL;
+ bool can_move = true;
+ bool can_begin = true;
+
+ for(;;) {
+ if(obj_next == NULL) {
+ if(group->wrap || obj_sentinel == NULL) {
+ if(!can_begin) return focus_changed;
+ obj_next = begin(&group->obj_ll);
+ can_move = false;
+ can_begin = false;
+ }
+ else {
+ /*Currently focused object is the last/first in the group, keep it that way*/
+ return focus_changed;
+ }
+ }
+
+ if(obj_sentinel == NULL) {
+ obj_sentinel = obj_next;
+ if(obj_sentinel == NULL) return focus_changed; /*Group is empty*/
+ }
+
+ if(can_move) {
+ obj_next = move(&group->obj_ll, obj_next);
+
+ /*Give up if we walked the entire list and haven't found another visible object*/
+ if(obj_next == obj_sentinel) return focus_changed;
+ }
+
+ can_move = true;
+
+ if(obj_next == NULL) continue;
+ if(lv_obj_get_state(*obj_next) & LV_STATE_DISABLED) continue;
+
+ /*Hidden objects don't receive focus.
+ *If any parent is hidden, the object is also hidden)*/
+ lv_obj_t * parent = *obj_next;
+ while(parent) {
+ if(lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) break;
+ parent = lv_obj_get_parent(parent);
+ }
+
+ if(parent && lv_obj_has_flag(parent, LV_OBJ_FLAG_HIDDEN)) continue;
+
+ /*If we got her a good candidate is found*/
+ break;
+ }
+
+ if(obj_next == group->obj_focus) return focus_changed; /*There's only one visible object and it's already focused*/
+
+ if(group->obj_focus) {
+ lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_DEFOCUSED, get_indev(group));
+ if(res != LV_RES_OK) return focus_changed;
+ lv_obj_invalidate(*group->obj_focus);
+ }
+
+ group->obj_focus = obj_next;
+
+ lv_res_t res = lv_event_send(*group->obj_focus, LV_EVENT_FOCUSED, get_indev(group));
+ if(res != LV_RES_OK) return focus_changed;
+
+ lv_obj_invalidate(*group->obj_focus);
+
+ if(group->focus_cb) group->focus_cb(group);
+ focus_changed = true;
+ return focus_changed;
+}
+
+/**
+ * Find an indev preferably with KEYPAD or ENCOEDR type that uses the given group.
+ * In other words, find an indev, that is related to the given group.
+ * In the worst case simply return the latest indev
+ * @param g a group the find in the indevs
+ * @return the suggested indev
+ */
+static lv_indev_t * get_indev(const lv_group_t * g)
+{
+ lv_indev_t * indev_encoder = NULL;
+ lv_indev_t * indev_group = NULL;
+ lv_indev_t * indev = lv_indev_get_next(NULL);
+ while(indev) {
+ lv_indev_type_t indev_type = lv_indev_get_type(indev);
+ if(indev->group == g) {
+ /*Prefer KEYPAD*/
+ if(indev_type == LV_INDEV_TYPE_KEYPAD) return indev;
+ if(indev_type == LV_INDEV_TYPE_ENCODER) indev_encoder = indev;
+ indev_group = indev;
+ }
+ indev = lv_indev_get_next(indev);
+ }
+
+ if(indev_encoder) return indev_encoder;
+ if(indev_group) return indev_group;
+
+ /*In lack of a better option use the first input device. (It can be NULL if there is no input device)*/
+ return lv_indev_get_next(NULL);
+}
+
diff --git a/lib/lvgl/src/core/lv_group.h b/lib/lvgl/src/core/lv_group.h
new file mode 100644
index 00000000..09105973
--- /dev/null
+++ b/lib/lvgl/src/core/lv_group.h
@@ -0,0 +1,266 @@
+/**
+ * @file lv_group.h
+ *
+ */
+
+#ifndef LV_GROUP_H
+#define LV_GROUP_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+
+#include "../lv_conf_internal.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "../misc/lv_ll.h"
+#include "../misc/lv_types.h"
+
+/*********************
+ * DEFINES
+ *********************/
+/*Predefined keys to control the focused object via lv_group_send(group, c)*/
+
+enum {
+ LV_KEY_UP = 17, /*0x11*/
+ LV_KEY_DOWN = 18, /*0x12*/
+ LV_KEY_RIGHT = 19, /*0x13*/
+ LV_KEY_LEFT = 20, /*0x14*/
+ LV_KEY_ESC = 27, /*0x1B*/
+ LV_KEY_DEL = 127, /*0x7F*/
+ LV_KEY_BACKSPACE = 8, /*0x08*/
+ LV_KEY_ENTER = 10, /*0x0A, '\n'*/
+ LV_KEY_NEXT = 9, /*0x09, '\t'*/
+ LV_KEY_PREV = 11, /*0x0B, '*/
+ LV_KEY_HOME = 2, /*0x02, STX*/
+ LV_KEY_END = 3, /*0x03, ETX*/
+};
+typedef uint8_t lv_key_t;
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_obj_t;
+struct _lv_group_t;
+
+typedef void (*lv_group_focus_cb_t)(struct _lv_group_t *);
+typedef void (*lv_group_edge_cb_t)(struct _lv_group_t *, bool);
+
+/**
+ * Groups can be used to logically hold objects so that they can be individually focused.
+ * They are NOT for laying out objects on a screen (try layouts for that).
+ */
+typedef struct _lv_group_t {
+ lv_ll_t obj_ll; /**< Linked list to store the objects in the group*/
+ struct _lv_obj_t ** obj_focus; /**< The object in focus*/
+
+ lv_group_focus_cb_t focus_cb; /**< A function to call when a new object is focused (optional)*/
+ lv_group_edge_cb_t edge_cb; /**< A function to call when an edge is reached, no more focus
+ targets are available in this direction (to allow edge feedback
+ like a sound or a scroll bounce) */
+
+#if LV_USE_USER_DATA
+ void * user_data;
+#endif
+
+ uint8_t frozen : 1; /**< 1: can't focus to new object*/
+ uint8_t editing : 1; /**< 1: Edit mode, 0: Navigate mode*/
+ uint8_t refocus_policy : 1; /**< 1: Focus prev if focused on deletion. 0: Focus next if focused on
+ deletion.*/
+ uint8_t wrap : 1; /**< 1: Focus next/prev can wrap at end of list. 0: Focus next/prev stops at end
+ of list.*/
+} lv_group_t;
+
+
+typedef enum {
+ LV_GROUP_REFOCUS_POLICY_NEXT = 0,
+ LV_GROUP_REFOCUS_POLICY_PREV = 1
+} lv_group_refocus_policy_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Init. the group module
+ * @remarks Internal function, do not call directly.
+ */
+void _lv_group_init(void);
+
+/**
+ * Create a new object group
+ * @return pointer to the new object group
+ */
+lv_group_t * lv_group_create(void);
+
+/**
+ * Delete a group object
+ * @param group pointer to a group
+ */
+void lv_group_del(lv_group_t * group);
+
+/**
+ * Set a default group. New object are added to this group if it's enabled in their class with `add_to_def_group = true`
+ * @param group pointer to a group (can be `NULL`)
+ */
+void lv_group_set_default(lv_group_t * group);
+
+/**
+ * Get the default group
+ * @return pointer to the default group
+ */
+lv_group_t * lv_group_get_default(void);
+
+/**
+ * Add an object to a group
+ * @param group pointer to a group
+ * @param obj pointer to an object to add
+ */
+void lv_group_add_obj(lv_group_t * group, struct _lv_obj_t * obj);
+
+/**
+ * Swap 2 object in a group. The object must be in the same group
+ * @param obj1 pointer to an object
+ * @param obj2 pointer to an other object
+ */
+void lv_group_swap_obj(struct _lv_obj_t * obj1, struct _lv_obj_t * obj2);
+
+/**
+ * Remove an object from its group
+ * @param obj pointer to an object to remove
+ */
+void lv_group_remove_obj(struct _lv_obj_t * obj);
+
+/**
+ * Remove all objects from a group
+ * @param group pointer to a group
+ */
+void lv_group_remove_all_objs(lv_group_t * group);
+
+/**
+ * Focus on an object (defocus the current)
+ * @param obj pointer to an object to focus on
+ */
+void lv_group_focus_obj(struct _lv_obj_t * obj);
+
+/**
+ * Focus the next object in a group (defocus the current)
+ * @param group pointer to a group
+ */
+void lv_group_focus_next(lv_group_t * group);
+
+/**
+ * Focus the previous object in a group (defocus the current)
+ * @param group pointer to a group
+ */
+void lv_group_focus_prev(lv_group_t * group);
+
+/**
+ * Do not let to change the focus from the current object
+ * @param group pointer to a group
+ * @param en true: freeze, false: release freezing (normal mode)
+ */
+void lv_group_focus_freeze(lv_group_t * group, bool en);
+
+/**
+ * Send a control character to the focuses object of a group
+ * @param group pointer to a group
+ * @param c a character (use LV_KEY_.. to navigate)
+ * @return result of focused object in group.
+ */
+lv_res_t lv_group_send_data(lv_group_t * group, uint32_t c);
+
+/**
+ * Set a function for a group which will be called when a new object is focused
+ * @param group pointer to a group
+ * @param focus_cb the call back function or NULL if unused
+ */
+void lv_group_set_focus_cb(lv_group_t * group, lv_group_focus_cb_t focus_cb);
+
+/**
+ * Set a function for a group which will be called when a focus edge is reached
+ * @param group pointer to a group
+ * @param edge_cb the call back function or NULL if unused
+ */
+void lv_group_set_edge_cb(lv_group_t * group, lv_group_edge_cb_t edge_cb);
+
+
+/**
+ * Set whether the next or previous item in a group is focused if the currently focused obj is
+ * deleted.
+ * @param group pointer to a group
+ * @param policy new refocus policy enum
+ */
+void lv_group_set_refocus_policy(lv_group_t * group, lv_group_refocus_policy_t policy);
+
+/**
+ * Manually set the current mode (edit or navigate).
+ * @param group pointer to group
+ * @param edit true: edit mode; false: navigate mode
+ */
+void lv_group_set_editing(lv_group_t * group, bool edit);
+
+/**
+ * Set whether focus next/prev will allow wrapping from first->last or last->first object.
+ * @param group pointer to group
+ * @param en true: wrapping enabled; false: wrapping disabled
+ */
+void lv_group_set_wrap(lv_group_t * group, bool en);
+
+/**
+ * Get the focused object or NULL if there isn't one
+ * @param group pointer to a group
+ * @return pointer to the focused object
+ */
+struct _lv_obj_t * lv_group_get_focused(const lv_group_t * group);
+
+/**
+ * Get the focus callback function of a group
+ * @param group pointer to a group
+ * @return the call back function or NULL if not set
+ */
+lv_group_focus_cb_t lv_group_get_focus_cb(const lv_group_t * group);
+
+/**
+ * Get the edge callback function of a group
+ * @param group pointer to a group
+ * @return the call back function or NULL if not set
+ */
+lv_group_edge_cb_t lv_group_get_edge_cb(const lv_group_t * group);
+
+/**
+ * Get the current mode (edit or navigate).
+ * @param group pointer to group
+ * @return true: edit mode; false: navigate mode
+ */
+bool lv_group_get_editing(const lv_group_t * group);
+
+/**
+ * Get whether focus next/prev will allow wrapping from first->last or last->first object.
+ * @param group pointer to group
+ * @param en true: wrapping enabled; false: wrapping disabled
+ */
+bool lv_group_get_wrap(lv_group_t * group);
+
+/**
+ * Get the number of object in the group
+ * @param group pointer to a group
+ * @return number of objects in the group
+ */
+uint32_t lv_group_get_obj_count(lv_group_t * group);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GROUP_H*/
diff --git a/lib/lvgl/src/core/lv_indev.c b/lib/lvgl/src/core/lv_indev.c
new file mode 100644
index 00000000..ab1265ec
--- /dev/null
+++ b/lib/lvgl/src/core/lv_indev.c
@@ -0,0 +1,1161 @@
+/**
+ * @file lv_indev.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ ********************/
+#include "lv_indev.h"
+#include "lv_disp.h"
+#include "lv_obj.h"
+#include "lv_indev_scroll.h"
+#include "lv_group.h"
+#include "lv_refr.h"
+
+#include "../hal/lv_hal_tick.h"
+#include "../misc/lv_timer.h"
+#include "../misc/lv_math.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#if LV_INDEV_DEF_SCROLL_THROW <= 0
+ #warning "LV_INDEV_DRAG_THROW must be greater than 0"
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data);
+static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data);
+static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data);
+static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data);
+static void indev_proc_press(_lv_indev_proc_t * proc);
+static void indev_proc_release(_lv_indev_proc_t * proc);
+static void indev_proc_reset_query_handler(lv_indev_t * indev);
+static void indev_click_focus(_lv_indev_proc_t * proc);
+static void indev_gesture(_lv_indev_proc_t * proc);
+static bool indev_reset_check(_lv_indev_proc_t * proc);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static lv_indev_t * indev_act;
+static lv_obj_t * indev_obj_act = NULL;
+
+/**********************
+ * MACROS
+ **********************/
+#if LV_LOG_TRACE_INDEV
+ #define INDEV_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
+#else
+ #define INDEV_TRACE(...)
+#endif
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_indev_read_timer_cb(lv_timer_t * timer)
+{
+ INDEV_TRACE("begin");
+
+ lv_indev_data_t data;
+
+ indev_act = timer->user_data;
+
+ /*Read and process all indevs*/
+ if(indev_act->driver->disp == NULL) return; /*Not assigned to any displays*/
+
+ /*Handle reset query before processing the point*/
+ indev_proc_reset_query_handler(indev_act);
+
+ if(indev_act->proc.disabled ||
+ indev_act->driver->disp->prev_scr != NULL) return; /*Input disabled or screen animation active*/
+ bool continue_reading;
+ do {
+ /*Read the data*/
+ _lv_indev_read(indev_act, &data);
+ continue_reading = data.continue_reading;
+
+ /*The active object might be deleted even in the read function*/
+ indev_proc_reset_query_handler(indev_act);
+ indev_obj_act = NULL;
+
+ indev_act->proc.state = data.state;
+
+ /*Save the last activity time*/
+ if(indev_act->proc.state == LV_INDEV_STATE_PRESSED) {
+ indev_act->driver->disp->last_activity_time = lv_tick_get();
+ }
+ else if(indev_act->driver->type == LV_INDEV_TYPE_ENCODER && data.enc_diff) {
+ indev_act->driver->disp->last_activity_time = lv_tick_get();
+ }
+
+ if(indev_act->driver->type == LV_INDEV_TYPE_POINTER) {
+ indev_pointer_proc(indev_act, &data);
+ }
+ else if(indev_act->driver->type == LV_INDEV_TYPE_KEYPAD) {
+ indev_keypad_proc(indev_act, &data);
+ }
+ else if(indev_act->driver->type == LV_INDEV_TYPE_ENCODER) {
+ indev_encoder_proc(indev_act, &data);
+ }
+ else if(indev_act->driver->type == LV_INDEV_TYPE_BUTTON) {
+ indev_button_proc(indev_act, &data);
+ }
+ /*Handle reset query if it happened in during processing*/
+ indev_proc_reset_query_handler(indev_act);
+ } while(continue_reading);
+
+ /*End of indev processing, so no act indev*/
+ indev_act = NULL;
+ indev_obj_act = NULL;
+
+ INDEV_TRACE("finished");
+}
+
+void lv_indev_enable(lv_indev_t * indev, bool en)
+{
+ uint8_t enable = en ? 0 : 1;
+
+ if(indev) {
+ indev->proc.disabled = enable;
+ }
+ else {
+ lv_indev_t * i = lv_indev_get_next(NULL);
+ while(i) {
+ i->proc.disabled = enable;
+ i = lv_indev_get_next(i);
+ }
+ }
+}
+
+lv_indev_t * lv_indev_get_act(void)
+{
+ return indev_act;
+}
+
+lv_indev_type_t lv_indev_get_type(const lv_indev_t * indev)
+{
+ if(indev == NULL) return LV_INDEV_TYPE_NONE;
+
+ return indev->driver->type;
+}
+
+void lv_indev_reset(lv_indev_t * indev, lv_obj_t * obj)
+{
+ if(indev) {
+ indev->proc.reset_query = 1;
+ if(indev_act == indev) indev_obj_act = NULL;
+ if(indev->driver->type == LV_INDEV_TYPE_POINTER || indev->driver->type == LV_INDEV_TYPE_KEYPAD) {
+ if(obj == NULL || indev->proc.types.pointer.last_pressed == obj) {
+ indev->proc.types.pointer.last_pressed = NULL;
+ }
+ if(obj == NULL || indev->proc.types.pointer.act_obj == obj) {
+ indev->proc.types.pointer.act_obj = NULL;
+ }
+ if(obj == NULL || indev->proc.types.pointer.last_obj == obj) {
+ indev->proc.types.pointer.last_obj = NULL;
+ }
+ }
+ }
+ else {
+ lv_indev_t * i = lv_indev_get_next(NULL);
+ while(i) {
+ i->proc.reset_query = 1;
+ if(i->driver->type == LV_INDEV_TYPE_POINTER || i->driver->type == LV_INDEV_TYPE_KEYPAD) {
+ if(obj == NULL || i->proc.types.pointer.last_pressed == obj) {
+ i->proc.types.pointer.last_pressed = NULL;
+ }
+ if(obj == NULL || i->proc.types.pointer.act_obj == obj) {
+ i->proc.types.pointer.act_obj = NULL;
+ }
+ if(obj == NULL || i->proc.types.pointer.last_obj == obj) {
+ i->proc.types.pointer.last_obj = NULL;
+ }
+ }
+ i = lv_indev_get_next(i);
+ }
+ indev_obj_act = NULL;
+ }
+}
+
+void lv_indev_reset_long_press(lv_indev_t * indev)
+{
+ indev->proc.long_pr_sent = 0;
+ indev->proc.longpr_rep_timestamp = lv_tick_get();
+ indev->proc.pr_timestamp = lv_tick_get();
+}
+
+void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj)
+{
+ if(indev->driver->type != LV_INDEV_TYPE_POINTER) return;
+
+ indev->cursor = cur_obj;
+ lv_obj_set_parent(indev->cursor, lv_disp_get_layer_sys(indev->driver->disp));
+ lv_obj_set_pos(indev->cursor, indev->proc.types.pointer.act_point.x, indev->proc.types.pointer.act_point.y);
+ lv_obj_clear_flag(indev->cursor, LV_OBJ_FLAG_CLICKABLE);
+ lv_obj_add_flag(indev->cursor, LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_FLOATING);
+}
+
+void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group)
+{
+ if(indev->driver->type == LV_INDEV_TYPE_KEYPAD || indev->driver->type == LV_INDEV_TYPE_ENCODER) {
+ indev->group = group;
+ }
+}
+
+void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t points[])
+{
+ if(indev->driver->type == LV_INDEV_TYPE_BUTTON) {
+ indev->btn_points = points;
+ }
+}
+
+void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point)
+{
+ if(indev == NULL) {
+ point->x = 0;
+ point->y = 0;
+ return;
+ }
+ if(indev->driver->type != LV_INDEV_TYPE_POINTER && indev->driver->type != LV_INDEV_TYPE_BUTTON) {
+ point->x = -1;
+ point->y = -1;
+ }
+ else {
+ point->x = indev->proc.types.pointer.act_point.x;
+ point->y = indev->proc.types.pointer.act_point.y;
+ }
+}
+
+lv_dir_t lv_indev_get_gesture_dir(const lv_indev_t * indev)
+{
+ return indev->proc.types.pointer.gesture_dir;
+}
+
+uint32_t lv_indev_get_key(const lv_indev_t * indev)
+{
+ if(indev->driver->type != LV_INDEV_TYPE_KEYPAD)
+ return 0;
+ else
+ return indev->proc.types.keypad.last_key;
+}
+
+lv_dir_t lv_indev_get_scroll_dir(const lv_indev_t * indev)
+{
+ if(indev == NULL) return false;
+ if(indev->driver->type != LV_INDEV_TYPE_POINTER && indev->driver->type != LV_INDEV_TYPE_BUTTON) return false;
+ return indev->proc.types.pointer.scroll_dir;
+}
+
+lv_obj_t * lv_indev_get_scroll_obj(const lv_indev_t * indev)
+{
+ if(indev == NULL) return NULL;
+ if(indev->driver->type != LV_INDEV_TYPE_POINTER && indev->driver->type != LV_INDEV_TYPE_BUTTON) return NULL;
+ return indev->proc.types.pointer.scroll_obj;
+}
+
+void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point)
+{
+ point->x = 0;
+ point->y = 0;
+
+ if(indev == NULL) return;
+
+ if(indev->driver->type == LV_INDEV_TYPE_POINTER || indev->driver->type == LV_INDEV_TYPE_BUTTON) {
+ point->x = indev->proc.types.pointer.vect.x;
+ point->y = indev->proc.types.pointer.vect.y;
+ }
+}
+
+void lv_indev_wait_release(lv_indev_t * indev)
+{
+ if(indev == NULL)return;
+ indev->proc.wait_until_release = 1;
+}
+
+lv_obj_t * lv_indev_get_obj_act(void)
+{
+ return indev_obj_act;
+}
+
+lv_timer_t * lv_indev_get_read_timer(lv_disp_t * indev)
+{
+ if(!indev) {
+ LV_LOG_WARN("lv_indev_get_read_timer: indev was NULL");
+ return NULL;
+ }
+
+ return indev->refr_timer;
+}
+
+
+lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point)
+{
+ lv_obj_t * found_p = NULL;
+
+ /*If this obj is hidden the children are hidden too so return immediately*/
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return NULL;
+
+ lv_point_t p_trans = *point;
+ lv_obj_transform_point(obj, &p_trans, false, true);
+
+ bool hit_test_ok = lv_obj_hit_test(obj, &p_trans);
+
+ /*If the point is on this object or has overflow visible check its children too*/
+ if(_lv_area_is_point_on(&obj->coords, &p_trans, 0) || lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) {
+ int32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+
+ /*If a child matches use it*/
+ for(i = child_cnt - 1; i >= 0; i--) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ found_p = lv_indev_search_obj(child, &p_trans);
+ if(found_p) return found_p;
+ }
+ }
+
+ /*If not return earlier for a clicked child and this obj's hittest was ok use it
+ *else return NULL*/
+ if(hit_test_ok) return obj;
+ else return NULL;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Process a new point from LV_INDEV_TYPE_POINTER input device
+ * @param i pointer to an input device
+ * @param data pointer to the data read from the input device
+ */
+static void indev_pointer_proc(lv_indev_t * i, lv_indev_data_t * data)
+{
+ lv_disp_t * disp = i->driver->disp;
+ /*Save the raw points so they can be used again in _lv_indev_read*/
+ i->proc.types.pointer.last_raw_point.x = data->point.x;
+ i->proc.types.pointer.last_raw_point.y = data->point.y;
+
+ if(disp->driver->rotated == LV_DISP_ROT_180 || disp->driver->rotated == LV_DISP_ROT_270) {
+ data->point.x = disp->driver->hor_res - data->point.x - 1;
+ data->point.y = disp->driver->ver_res - data->point.y - 1;
+ }
+ if(disp->driver->rotated == LV_DISP_ROT_90 || disp->driver->rotated == LV_DISP_ROT_270) {
+ lv_coord_t tmp = data->point.y;
+ data->point.y = data->point.x;
+ data->point.x = disp->driver->ver_res - tmp - 1;
+ }
+
+ /*Simple sanity check*/
+ if(data->point.x < 0) {
+ LV_LOG_WARN("X is %d which is smaller than zero", data->point.x);
+ }
+ if(data->point.x >= lv_disp_get_hor_res(i->driver->disp)) {
+ LV_LOG_WARN("X is %d which is greater than hor. res", data->point.x);
+ }
+ if(data->point.y < 0) {
+ LV_LOG_WARN("Y is %d which is smaller than zero", data->point.y);
+ }
+ if(data->point.y >= lv_disp_get_ver_res(i->driver->disp)) {
+ LV_LOG_WARN("Y is %d which is greater than ver. res", data->point.y);
+ }
+
+ /*Move the cursor if set and moved*/
+ if(i->cursor != NULL &&
+ (i->proc.types.pointer.last_point.x != data->point.x || i->proc.types.pointer.last_point.y != data->point.y)) {
+ lv_obj_set_pos(i->cursor, data->point.x, data->point.y);
+ }
+
+ i->proc.types.pointer.act_point.x = data->point.x;
+ i->proc.types.pointer.act_point.y = data->point.y;
+
+ if(i->proc.state == LV_INDEV_STATE_PRESSED) {
+ indev_proc_press(&i->proc);
+ }
+ else {
+ indev_proc_release(&i->proc);
+ }
+
+ i->proc.types.pointer.last_point.x = i->proc.types.pointer.act_point.x;
+ i->proc.types.pointer.last_point.y = i->proc.types.pointer.act_point.y;
+}
+
+/**
+ * Process a new point from LV_INDEV_TYPE_KEYPAD input device
+ * @param i pointer to an input device
+ * @param data pointer to the data read from the input device
+ */
+static void indev_keypad_proc(lv_indev_t * i, lv_indev_data_t * data)
+{
+ if(data->state == LV_INDEV_STATE_PRESSED && i->proc.wait_until_release) return;
+
+ if(i->proc.wait_until_release) {
+ i->proc.wait_until_release = 0;
+ i->proc.pr_timestamp = 0;
+ i->proc.long_pr_sent = 0;
+ i->proc.types.keypad.last_state = LV_INDEV_STATE_RELEASED; /*To skip the processing of release*/
+ }
+
+ lv_group_t * g = i->group;
+ if(g == NULL) return;
+
+ indev_obj_act = lv_group_get_focused(g);
+ if(indev_obj_act == NULL) return;
+
+ bool dis = lv_obj_has_state(indev_obj_act, LV_STATE_DISABLED);
+
+ /*Save the last key to compare it with the current latter on RELEASE*/
+ uint32_t prev_key = i->proc.types.keypad.last_key;
+
+ /*Save the last key.
+ *It must be done here else `lv_indev_get_key` will return the last key in events*/
+ i->proc.types.keypad.last_key = data->key;
+
+ /*Save the previous state so we can detect state changes below and also set the last state now
+ *so if any event handler on the way returns `LV_RES_INV` the last state is remembered
+ *for the next time*/
+ uint32_t prev_state = i->proc.types.keypad.last_state;
+ i->proc.types.keypad.last_state = data->state;
+
+ /*Key press happened*/
+ if(data->state == LV_INDEV_STATE_PRESSED && prev_state == LV_INDEV_STATE_RELEASED) {
+ LV_LOG_INFO("%" LV_PRIu32 " key is pressed", data->key);
+ i->proc.pr_timestamp = lv_tick_get();
+
+ /*Move the focus on NEXT*/
+ if(data->key == LV_KEY_NEXT) {
+ lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
+ lv_group_focus_next(g);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ /*Move the focus on PREV*/
+ else if(data->key == LV_KEY_PREV) {
+ lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
+ lv_group_focus_prev(g);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ else if(!dis) {
+ /*Simulate a press on the object if ENTER was pressed*/
+ if(data->key == LV_KEY_ENTER) {
+ /*Send the ENTER as a normal KEY*/
+ lv_group_send_data(g, LV_KEY_ENTER);
+ if(indev_reset_check(&i->proc)) return;
+
+ if(!dis) lv_event_send(indev_obj_act, LV_EVENT_PRESSED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ else if(data->key == LV_KEY_ESC) {
+ /*Send the ESC as a normal KEY*/
+ lv_group_send_data(g, LV_KEY_ESC);
+ if(indev_reset_check(&i->proc)) return;
+
+ lv_event_send(indev_obj_act, LV_EVENT_CANCEL, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ /*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/
+ else {
+ lv_group_send_data(g, data->key);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ }
+ }
+ /*Pressing*/
+ else if(!dis && data->state == LV_INDEV_STATE_PRESSED && prev_state == LV_INDEV_STATE_PRESSED) {
+
+ if(data->key == LV_KEY_ENTER) {
+ lv_event_send(indev_obj_act, LV_EVENT_PRESSING, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+ }
+
+ /*Long press time has elapsed?*/
+ if(i->proc.long_pr_sent == 0 && lv_tick_elaps(i->proc.pr_timestamp) > i->driver->long_press_time) {
+ i->proc.long_pr_sent = 1;
+ if(data->key == LV_KEY_ENTER) {
+ i->proc.longpr_rep_timestamp = lv_tick_get();
+ lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ }
+ /*Long press repeated time has elapsed?*/
+ else if(i->proc.long_pr_sent != 0 &&
+ lv_tick_elaps(i->proc.longpr_rep_timestamp) > i->driver->long_press_repeat_time) {
+
+ i->proc.longpr_rep_timestamp = lv_tick_get();
+
+ /*Send LONG_PRESS_REP on ENTER*/
+ if(data->key == LV_KEY_ENTER) {
+ lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ /*Move the focus on NEXT again*/
+ else if(data->key == LV_KEY_NEXT) {
+ lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
+ lv_group_focus_next(g);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ /*Move the focus on PREV again*/
+ else if(data->key == LV_KEY_PREV) {
+ lv_group_set_editing(g, false); /*Editing is not used by KEYPAD is be sure it is disabled*/
+ lv_group_focus_prev(g);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ /*Just send other keys again to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT)*/
+ else {
+ lv_group_send_data(g, data->key);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ }
+ }
+ /*Release happened*/
+ else if(!dis && data->state == LV_INDEV_STATE_RELEASED && prev_state == LV_INDEV_STATE_PRESSED) {
+ LV_LOG_INFO("%" LV_PRIu32 " key is released", data->key);
+ /*The user might clear the key when it was released. Always release the pressed key*/
+ data->key = prev_key;
+ if(data->key == LV_KEY_ENTER) {
+
+ lv_event_send(indev_obj_act, LV_EVENT_RELEASED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+
+ if(i->proc.long_pr_sent == 0) {
+ lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+ }
+
+ lv_event_send(indev_obj_act, LV_EVENT_CLICKED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+
+ }
+ i->proc.pr_timestamp = 0;
+ i->proc.long_pr_sent = 0;
+ }
+ indev_obj_act = NULL;
+}
+
+/**
+ * Process a new point from LV_INDEV_TYPE_ENCODER input device
+ * @param i pointer to an input device
+ * @param data pointer to the data read from the input device
+ */
+static void indev_encoder_proc(lv_indev_t * i, lv_indev_data_t * data)
+{
+ if(data->state == LV_INDEV_STATE_PRESSED && i->proc.wait_until_release) return;
+
+ if(i->proc.wait_until_release) {
+ i->proc.wait_until_release = 0;
+ i->proc.pr_timestamp = 0;
+ i->proc.long_pr_sent = 0;
+ i->proc.types.keypad.last_state = LV_INDEV_STATE_RELEASED; /*To skip the processing of release*/
+ }
+
+ /*Save the last keys before anything else.
+ *They need to be already saved if the function returns for any reason*/
+ lv_indev_state_t last_state = i->proc.types.keypad.last_state;
+ i->proc.types.keypad.last_state = data->state;
+ i->proc.types.keypad.last_key = data->key;
+
+ lv_group_t * g = i->group;
+ if(g == NULL) return;
+
+ indev_obj_act = lv_group_get_focused(g);
+ if(indev_obj_act == NULL) return;
+
+ /*Process the steps they are valid only with released button*/
+ if(data->state != LV_INDEV_STATE_RELEASED) {
+ data->enc_diff = 0;
+ }
+
+ /*Refresh the focused object. It might change due to lv_group_focus_prev/next*/
+ indev_obj_act = lv_group_get_focused(g);
+ if(indev_obj_act == NULL) return;
+
+ /*Button press happened*/
+ if(data->state == LV_INDEV_STATE_PRESSED && last_state == LV_INDEV_STATE_RELEASED) {
+ LV_LOG_INFO("pressed");
+
+ i->proc.pr_timestamp = lv_tick_get();
+
+ if(data->key == LV_KEY_ENTER) {
+ bool editable_or_scrollable = lv_obj_is_editable(indev_obj_act) ||
+ lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_SCROLLABLE);
+ if(lv_group_get_editing(g) == true || editable_or_scrollable == false) {
+ lv_event_send(indev_obj_act, LV_EVENT_PRESSED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ }
+ else if(data->key == LV_KEY_LEFT) {
+ /*emulate encoder left*/
+ data->enc_diff--;
+ }
+ else if(data->key == LV_KEY_RIGHT) {
+ /*emulate encoder right*/
+ data->enc_diff++;
+ }
+ else if(data->key == LV_KEY_ESC) {
+ /*Send the ESC as a normal KEY*/
+ lv_group_send_data(g, LV_KEY_ESC);
+ if(indev_reset_check(&i->proc)) return;
+
+ lv_event_send(indev_obj_act, LV_EVENT_CANCEL, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ /*Just send other keys to the object (e.g. 'A' or `LV_GROUP_KEY_RIGHT`)*/
+ else {
+ lv_group_send_data(g, data->key);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ }
+ /*Pressing*/
+ else if(data->state == LV_INDEV_STATE_PRESSED && last_state == LV_INDEV_STATE_PRESSED) {
+ /*Long press*/
+ if(i->proc.long_pr_sent == 0 && lv_tick_elaps(i->proc.pr_timestamp) > i->driver->long_press_time) {
+
+ i->proc.long_pr_sent = 1;
+ i->proc.longpr_rep_timestamp = lv_tick_get();
+
+ if(data->key == LV_KEY_ENTER) {
+ bool editable_or_scrollable = lv_obj_is_editable(indev_obj_act) ||
+ lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_SCROLLABLE);
+
+ /*On enter long press toggle edit mode.*/
+ if(editable_or_scrollable) {
+ /*Don't leave edit mode if there is only one object (nowhere to navigate)*/
+ if(lv_group_get_obj_count(g) > 1) {
+ LV_LOG_INFO("toggling edit mode");
+ lv_group_set_editing(g, lv_group_get_editing(g) ? false : true); /*Toggle edit mode on long press*/
+ lv_obj_clear_state(indev_obj_act, LV_STATE_PRESSED); /*Remove the pressed state manually*/
+ }
+ }
+ /*If not editable then just send a long press Call the ancestor's event handler*/
+ else {
+ lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ }
+
+ i->proc.long_pr_sent = 1;
+ }
+ /*Long press repeated time has elapsed?*/
+ else if(i->proc.long_pr_sent != 0 && lv_tick_elaps(i->proc.longpr_rep_timestamp) > i->driver->long_press_repeat_time) {
+
+ i->proc.longpr_rep_timestamp = lv_tick_get();
+
+ if(data->key == LV_KEY_ENTER) {
+ lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ else if(data->key == LV_KEY_LEFT) {
+ /*emulate encoder left*/
+ data->enc_diff--;
+ }
+ else if(data->key == LV_KEY_RIGHT) {
+ /*emulate encoder right*/
+ data->enc_diff++;
+ }
+ else {
+ lv_group_send_data(g, data->key);
+ if(indev_reset_check(&i->proc)) return;
+ }
+
+ }
+
+ }
+ /*Release happened*/
+ else if(data->state == LV_INDEV_STATE_RELEASED && last_state == LV_INDEV_STATE_PRESSED) {
+ LV_LOG_INFO("released");
+
+ if(data->key == LV_KEY_ENTER) {
+ bool editable_or_scrollable = lv_obj_is_editable(indev_obj_act) ||
+ lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_SCROLLABLE);
+
+ /*The button was released on a non-editable object. Just send enter*/
+ if(editable_or_scrollable == false) {
+ lv_event_send(indev_obj_act, LV_EVENT_RELEASED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+
+ if(i->proc.long_pr_sent == 0) lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+
+ lv_event_send(indev_obj_act, LV_EVENT_CLICKED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+
+ }
+ /*An object is being edited and the button is released.*/
+ else if(lv_group_get_editing(g)) {
+ /*Ignore long pressed enter release because it comes from mode switch*/
+ if(!i->proc.long_pr_sent || lv_group_get_obj_count(g) <= 1) {
+ lv_event_send(indev_obj_act, LV_EVENT_RELEASED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+
+ lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+
+ lv_event_send(indev_obj_act, LV_EVENT_CLICKED, indev_act);
+ if(indev_reset_check(&i->proc)) return;
+
+ lv_group_send_data(g, LV_KEY_ENTER);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ else {
+ lv_obj_clear_state(indev_obj_act, LV_STATE_PRESSED); /*Remove the pressed state manually*/
+ }
+ }
+ /*If the focused object is editable and now in navigate mode then on enter switch edit
+ mode*/
+ else if(!i->proc.long_pr_sent) {
+ LV_LOG_INFO("entering edit mode");
+ lv_group_set_editing(g, true); /*Set edit mode*/
+ }
+ }
+
+ i->proc.pr_timestamp = 0;
+ i->proc.long_pr_sent = 0;
+ }
+ indev_obj_act = NULL;
+
+ /*if encoder steps or simulated steps via left/right keys*/
+ if(data->enc_diff != 0) {
+ /*In edit mode send LEFT/RIGHT keys*/
+ if(lv_group_get_editing(g)) {
+ LV_LOG_INFO("rotated by %+d (edit)", data->enc_diff);
+ int32_t s;
+ if(data->enc_diff < 0) {
+ for(s = 0; s < -data->enc_diff; s++) {
+ lv_group_send_data(g, LV_KEY_LEFT);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ }
+ else if(data->enc_diff > 0) {
+ for(s = 0; s < data->enc_diff; s++) {
+ lv_group_send_data(g, LV_KEY_RIGHT);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ }
+ }
+ /*In navigate mode focus on the next/prev objects*/
+ else {
+ LV_LOG_INFO("rotated by %+d (nav)", data->enc_diff);
+ int32_t s;
+ if(data->enc_diff < 0) {
+ for(s = 0; s < -data->enc_diff; s++) {
+ lv_group_focus_prev(g);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ }
+ else if(data->enc_diff > 0) {
+ for(s = 0; s < data->enc_diff; s++) {
+ lv_group_focus_next(g);
+ if(indev_reset_check(&i->proc)) return;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Process new points from an input device. indev->state.pressed has to be set
+ * @param indev pointer to an input device state
+ * @param x x coordinate of the next point
+ * @param y y coordinate of the next point
+ */
+static void indev_button_proc(lv_indev_t * i, lv_indev_data_t * data)
+{
+ /*Die gracefully if i->btn_points is NULL*/
+ if(i->btn_points == NULL) {
+ LV_LOG_WARN("btn_points is NULL");
+ return;
+ }
+
+ lv_coord_t x = i->btn_points[data->btn_id].x;
+ lv_coord_t y = i->btn_points[data->btn_id].y;
+
+ static lv_indev_state_t prev_state = LV_INDEV_STATE_RELEASED;
+ if(prev_state != data->state) {
+ if(data->state == LV_INDEV_STATE_PRESSED) {
+ LV_LOG_INFO("button %" LV_PRIu32 " is pressed (x:%d y:%d)", data->btn_id, x, y);
+ }
+ else {
+ LV_LOG_INFO("button %" LV_PRIu32 " is released (x:%d y:%d)", data->btn_id, x, y);
+ }
+ }
+
+ /*If a new point comes always make a release*/
+ if(data->state == LV_INDEV_STATE_PRESSED) {
+ if(i->proc.types.pointer.last_point.x != x ||
+ i->proc.types.pointer.last_point.y != y) {
+ indev_proc_release(&i->proc);
+ }
+ }
+
+ if(indev_reset_check(&i->proc)) return;
+
+ /*Save the new points*/
+ i->proc.types.pointer.act_point.x = x;
+ i->proc.types.pointer.act_point.y = y;
+
+ if(data->state == LV_INDEV_STATE_PRESSED) indev_proc_press(&i->proc);
+ else indev_proc_release(&i->proc);
+
+ if(indev_reset_check(&i->proc)) return;
+
+ i->proc.types.pointer.last_point.x = i->proc.types.pointer.act_point.x;
+ i->proc.types.pointer.last_point.y = i->proc.types.pointer.act_point.y;
+}
+
+/**
+ * Process the pressed state of LV_INDEV_TYPE_POINTER input devices
+ * @param indev pointer to an input device 'proc'
+ * @return LV_RES_OK: no indev reset required; LV_RES_INV: indev reset is required
+ */
+static void indev_proc_press(_lv_indev_proc_t * proc)
+{
+ LV_LOG_INFO("pressed at x:%d y:%d", proc->types.pointer.act_point.x, proc->types.pointer.act_point.y);
+ indev_obj_act = proc->types.pointer.act_obj;
+
+ if(proc->wait_until_release != 0) return;
+
+ lv_disp_t * disp = indev_act->driver->disp;
+ bool new_obj_searched = false;
+
+ /*If there is no last object then search*/
+ if(indev_obj_act == NULL) {
+ indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_sys(disp), &proc->types.pointer.act_point);
+ if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_top(disp),
+ &proc->types.pointer.act_point);
+ if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_scr_act(disp),
+ &proc->types.pointer.act_point);
+ new_obj_searched = true;
+ }
+ /*If there is last object but it is not scrolled and not protected also search*/
+ else if(proc->types.pointer.scroll_obj == NULL &&
+ lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_PRESS_LOCK) == false) {
+ indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_sys(disp), &proc->types.pointer.act_point);
+ if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_layer_top(disp),
+ &proc->types.pointer.act_point);
+ if(indev_obj_act == NULL) indev_obj_act = lv_indev_search_obj(lv_disp_get_scr_act(disp),
+ &proc->types.pointer.act_point);
+ new_obj_searched = true;
+ }
+
+ /*The last object might have scroll throw. Stop it manually*/
+ if(new_obj_searched && proc->types.pointer.last_obj) {
+ proc->types.pointer.scroll_throw_vect.x = 0;
+ proc->types.pointer.scroll_throw_vect.y = 0;
+ _lv_indev_scroll_throw_handler(proc);
+ if(indev_reset_check(proc)) return;
+ }
+
+ lv_obj_transform_point(indev_obj_act, &proc->types.pointer.act_point, true, true);
+
+ /*If a new object was found reset some variables and send a pressed event handler*/
+ if(indev_obj_act != proc->types.pointer.act_obj) {
+ proc->types.pointer.last_point.x = proc->types.pointer.act_point.x;
+ proc->types.pointer.last_point.y = proc->types.pointer.act_point.y;
+
+ /*If a new object found the previous was lost, so send a Call the ancestor's event handler*/
+ if(proc->types.pointer.act_obj != NULL) {
+ /*Save the obj because in special cases `act_obj` can change in the Call the ancestor's event handler function*/
+ lv_obj_t * last_obj = proc->types.pointer.act_obj;
+
+ lv_event_send(last_obj, LV_EVENT_PRESS_LOST, indev_act);
+ if(indev_reset_check(proc)) return;
+ }
+
+ proc->types.pointer.act_obj = indev_obj_act; /*Save the pressed object*/
+ proc->types.pointer.last_obj = indev_obj_act;
+
+ if(indev_obj_act != NULL) {
+ /*Save the time when the obj pressed to count long press time.*/
+ proc->pr_timestamp = lv_tick_get();
+ proc->long_pr_sent = 0;
+ proc->types.pointer.scroll_sum.x = 0;
+ proc->types.pointer.scroll_sum.y = 0;
+ proc->types.pointer.scroll_dir = LV_DIR_NONE;
+ proc->types.pointer.gesture_dir = LV_DIR_NONE;
+ proc->types.pointer.gesture_sent = 0;
+ proc->types.pointer.gesture_sum.x = 0;
+ proc->types.pointer.gesture_sum.y = 0;
+ proc->types.pointer.vect.x = 0;
+ proc->types.pointer.vect.y = 0;
+
+ /*Call the ancestor's event handler about the press*/
+ lv_event_send(indev_obj_act, LV_EVENT_PRESSED, indev_act);
+ if(indev_reset_check(proc)) return;
+
+ if(indev_act->proc.wait_until_release) return;
+
+ /*Handle focus*/
+ indev_click_focus(&indev_act->proc);
+ if(indev_reset_check(proc)) return;
+
+ }
+ }
+
+ /*Calculate the vector and apply a low pass filter: new value = 0.5 * old_value + 0.5 * new_value*/
+ proc->types.pointer.vect.x = proc->types.pointer.act_point.x - proc->types.pointer.last_point.x;
+ proc->types.pointer.vect.y = proc->types.pointer.act_point.y - proc->types.pointer.last_point.y;
+
+ proc->types.pointer.scroll_throw_vect.x = (proc->types.pointer.scroll_throw_vect.x + proc->types.pointer.vect.x) / 2;
+ proc->types.pointer.scroll_throw_vect.y = (proc->types.pointer.scroll_throw_vect.y + proc->types.pointer.vect.y) / 2;
+
+ proc->types.pointer.scroll_throw_vect_ori = proc->types.pointer.scroll_throw_vect;
+
+ if(indev_obj_act) {
+ lv_event_send(indev_obj_act, LV_EVENT_PRESSING, indev_act);
+ if(indev_reset_check(proc)) return;
+
+ if(indev_act->proc.wait_until_release) return;
+
+ _lv_indev_scroll_handler(proc);
+ if(indev_reset_check(proc)) return;
+ indev_gesture(proc);
+ if(indev_reset_check(proc)) return;
+
+ /*If there is no scrolling then check for long press time*/
+ if(proc->types.pointer.scroll_obj == NULL && proc->long_pr_sent == 0) {
+ /*Call the ancestor's event handler about the long press if enough time elapsed*/
+ if(lv_tick_elaps(proc->pr_timestamp) > indev_act->driver->long_press_time) {
+ lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED, indev_act);
+ if(indev_reset_check(proc)) return;
+
+ /*Mark the Call the ancestor's event handler sending to do not send it again*/
+ proc->long_pr_sent = 1;
+
+ /*Save the long press time stamp for the long press repeat handler*/
+ proc->longpr_rep_timestamp = lv_tick_get();
+ }
+ }
+
+ /*Send long press repeated Call the ancestor's event handler*/
+ if(proc->types.pointer.scroll_obj == NULL && proc->long_pr_sent == 1) {
+ /*Call the ancestor's event handler about the long press repeat if enough time elapsed*/
+ if(lv_tick_elaps(proc->longpr_rep_timestamp) > indev_act->driver->long_press_repeat_time) {
+ lv_event_send(indev_obj_act, LV_EVENT_LONG_PRESSED_REPEAT, indev_act);
+ if(indev_reset_check(proc)) return;
+ proc->longpr_rep_timestamp = lv_tick_get();
+ }
+ }
+ }
+}
+
+/**
+ * Process the released state of LV_INDEV_TYPE_POINTER input devices
+ * @param proc pointer to an input device 'proc'
+ */
+static void indev_proc_release(_lv_indev_proc_t * proc)
+{
+ if(proc->wait_until_release != 0) {
+ lv_event_send(proc->types.pointer.act_obj, LV_EVENT_PRESS_LOST, indev_act);
+ if(indev_reset_check(proc)) return;
+
+ proc->types.pointer.act_obj = NULL;
+ proc->types.pointer.last_obj = NULL;
+ proc->pr_timestamp = 0;
+ proc->longpr_rep_timestamp = 0;
+ proc->wait_until_release = 0;
+ }
+ indev_obj_act = proc->types.pointer.act_obj;
+ lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
+
+ /*Forget the act obj and send a released Call the ancestor's event handler*/
+ if(indev_obj_act) {
+ LV_LOG_INFO("released");
+
+ /*Send RELEASE Call the ancestor's event handler and event*/
+ lv_event_send(indev_obj_act, LV_EVENT_RELEASED, indev_act);
+ if(indev_reset_check(proc)) return;
+
+ /*Send CLICK if no scrolling*/
+ if(scroll_obj == NULL) {
+ if(proc->long_pr_sent == 0) {
+ lv_event_send(indev_obj_act, LV_EVENT_SHORT_CLICKED, indev_act);
+ if(indev_reset_check(proc)) return;
+ }
+
+ lv_event_send(indev_obj_act, LV_EVENT_CLICKED, indev_act);
+ if(indev_reset_check(proc)) return;
+ }
+
+ proc->types.pointer.act_obj = NULL;
+ proc->pr_timestamp = 0;
+ proc->longpr_rep_timestamp = 0;
+
+ }
+
+ /*The reset can be set in the Call the ancestor's event handler function.
+ * In case of reset query ignore the remaining parts.*/
+ if(scroll_obj) {
+ _lv_indev_scroll_throw_handler(proc);
+ if(indev_reset_check(proc)) return;
+ }
+}
+
+/**
+ * Process a new point from LV_INDEV_TYPE_BUTTON input device
+ * @param i pointer to an input device
+ * @param data pointer to the data read from the input device
+ * Reset input device if a reset query has been sent to it
+ * @param indev pointer to an input device
+ */
+static void indev_proc_reset_query_handler(lv_indev_t * indev)
+{
+ if(indev->proc.reset_query) {
+ indev->proc.types.pointer.act_obj = NULL;
+ indev->proc.types.pointer.last_obj = NULL;
+ indev->proc.types.pointer.scroll_obj = NULL;
+ indev->proc.long_pr_sent = 0;
+ indev->proc.pr_timestamp = 0;
+ indev->proc.longpr_rep_timestamp = 0;
+ indev->proc.types.pointer.scroll_sum.x = 0;
+ indev->proc.types.pointer.scroll_sum.y = 0;
+ indev->proc.types.pointer.scroll_dir = LV_DIR_NONE;
+ indev->proc.types.pointer.scroll_throw_vect.x = 0;
+ indev->proc.types.pointer.scroll_throw_vect.y = 0;
+ indev->proc.types.pointer.gesture_sum.x = 0;
+ indev->proc.types.pointer.gesture_sum.y = 0;
+ indev->proc.reset_query = 0;
+ indev_obj_act = NULL;
+ }
+}
+
+/**
+ * Handle focus/defocus on click for POINTER input devices
+ * @param proc pointer to the state of the indev
+ */
+static void indev_click_focus(_lv_indev_proc_t * proc)
+{
+ /*Handle click focus*/
+ if(lv_obj_has_flag(indev_obj_act, LV_OBJ_FLAG_CLICK_FOCUSABLE) == false ||
+ proc->types.pointer.last_pressed == indev_obj_act) {
+ return;
+ }
+
+ lv_group_t * g_act = lv_obj_get_group(indev_obj_act);
+ lv_group_t * g_prev = proc->types.pointer.last_pressed ? lv_obj_get_group(proc->types.pointer.last_pressed) : NULL;
+
+ /*If both the last and act. obj. are in the same group (or have no group)*/
+ if(g_act == g_prev) {
+ /*The objects are in a group*/
+ if(g_act) {
+ lv_group_focus_obj(indev_obj_act);
+ if(indev_reset_check(proc)) return;
+ }
+ /*The object are not in group*/
+ else {
+ if(proc->types.pointer.last_pressed) {
+ lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, indev_act);
+ if(indev_reset_check(proc)) return;
+ }
+
+ lv_event_send(indev_obj_act, LV_EVENT_FOCUSED, indev_act);
+ if(indev_reset_check(proc)) return;
+ }
+ }
+ /*The object are not in the same group (in different groups or one has no group)*/
+ else {
+ /*If the prev. obj. is not in a group then defocus it.*/
+ if(g_prev == NULL && proc->types.pointer.last_pressed) {
+ lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, indev_act);
+ if(indev_reset_check(proc)) return;
+ }
+ /*Focus on a non-group object*/
+ else {
+ if(proc->types.pointer.last_pressed) {
+ /*If the prev. object also wasn't in a group defocus it*/
+ if(g_prev == NULL) {
+ lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_DEFOCUSED, indev_act);
+ if(indev_reset_check(proc)) return;
+ }
+ /*If the prev. object also was in a group at least "LEAVE" it instead of defocus*/
+ else {
+ lv_event_send(proc->types.pointer.last_pressed, LV_EVENT_LEAVE, indev_act);
+ if(indev_reset_check(proc)) return;
+ }
+ }
+ }
+
+ /*Focus to the act. in its group*/
+ if(g_act) {
+ lv_group_focus_obj(indev_obj_act);
+ if(indev_reset_check(proc)) return;
+ }
+ else {
+ lv_event_send(indev_obj_act, LV_EVENT_FOCUSED, indev_act);
+ if(indev_reset_check(proc)) return;
+ }
+ }
+ proc->types.pointer.last_pressed = indev_obj_act;
+}
+
+/**
+* Handle the gesture of indev_proc_p->types.pointer.act_obj
+* @param indev pointer to an input device state
+*/
+void indev_gesture(_lv_indev_proc_t * proc)
+{
+
+ if(proc->types.pointer.scroll_obj) return;
+ if(proc->types.pointer.gesture_sent) return;
+
+ lv_obj_t * gesture_obj = proc->types.pointer.act_obj;
+
+ /*If gesture parent is active check recursively the gesture attribute*/
+ while(gesture_obj && lv_obj_has_flag(gesture_obj, LV_OBJ_FLAG_GESTURE_BUBBLE)) {
+ gesture_obj = lv_obj_get_parent(gesture_obj);
+ }
+
+ if(gesture_obj == NULL) return;
+
+ if((LV_ABS(proc->types.pointer.vect.x) < indev_act->driver->gesture_min_velocity) &&
+ (LV_ABS(proc->types.pointer.vect.y) < indev_act->driver->gesture_min_velocity)) {
+ proc->types.pointer.gesture_sum.x = 0;
+ proc->types.pointer.gesture_sum.y = 0;
+ }
+
+ /*Count the movement by gesture*/
+ proc->types.pointer.gesture_sum.x += proc->types.pointer.vect.x;
+ proc->types.pointer.gesture_sum.y += proc->types.pointer.vect.y;
+
+ if((LV_ABS(proc->types.pointer.gesture_sum.x) > indev_act->driver->gesture_limit) ||
+ (LV_ABS(proc->types.pointer.gesture_sum.y) > indev_act->driver->gesture_limit)) {
+
+ proc->types.pointer.gesture_sent = 1;
+
+ if(LV_ABS(proc->types.pointer.gesture_sum.x) > LV_ABS(proc->types.pointer.gesture_sum.y)) {
+ if(proc->types.pointer.gesture_sum.x > 0)
+ proc->types.pointer.gesture_dir = LV_DIR_RIGHT;
+ else
+ proc->types.pointer.gesture_dir = LV_DIR_LEFT;
+ }
+ else {
+ if(proc->types.pointer.gesture_sum.y > 0)
+ proc->types.pointer.gesture_dir = LV_DIR_BOTTOM;
+ else
+ proc->types.pointer.gesture_dir = LV_DIR_TOP;
+ }
+
+ lv_event_send(gesture_obj, LV_EVENT_GESTURE, indev_act);
+ if(indev_reset_check(proc)) return;
+ }
+}
+
+/**
+ * Checks if the reset_query flag has been set. If so, perform necessary global indev cleanup actions
+ * @param proc pointer to an input device 'proc'
+ * @return true if indev query should be immediately truncated.
+ */
+static bool indev_reset_check(_lv_indev_proc_t * proc)
+{
+ if(proc->reset_query) {
+ indev_obj_act = NULL;
+ }
+
+ return proc->reset_query ? true : false;
+}
diff --git a/lib/lvgl/src/core/lv_indev.h b/lib/lvgl/src/core/lv_indev.h
new file mode 100644
index 00000000..a98df86e
--- /dev/null
+++ b/lib/lvgl/src/core/lv_indev.h
@@ -0,0 +1,176 @@
+/**
+ * @file lv_indev.h
+ *
+ */
+
+#ifndef LV_INDEV_H
+#define LV_INDEV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include "../hal/lv_hal_indev.h"
+#include "lv_group.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Called periodically to read the input devices
+ * @param timer pointer to a timer to read
+ */
+void lv_indev_read_timer_cb(lv_timer_t * timer);
+
+/**
+ * Enable or disable one or all input devices (default enabled)
+ * @param indev pointer to an input device or NULL to enable/disable all of them
+ * @param en true to enable, false to disable
+ */
+void lv_indev_enable(lv_indev_t * indev, bool en);
+
+/**
+ * Get the currently processed input device. Can be used in action functions too.
+ * @return pointer to the currently processed input device or NULL if no input device processing
+ * right now
+ */
+lv_indev_t * lv_indev_get_act(void);
+
+/**
+ * Get the type of an input device
+ * @param indev pointer to an input device
+ * @return the type of the input device from `lv_hal_indev_type_t` (`LV_INDEV_TYPE_...`)
+ */
+lv_indev_type_t lv_indev_get_type(const lv_indev_t * indev);
+
+/**
+ * Reset one or all input devices
+ * @param indev pointer to an input device to reset or NULL to reset all of them
+ * @param obj pointer to an object which triggers the reset.
+ */
+void lv_indev_reset(lv_indev_t * indev, lv_obj_t * obj);
+
+/**
+ * Reset the long press state of an input device
+ * @param indev pointer to an input device
+ */
+void lv_indev_reset_long_press(lv_indev_t * indev);
+
+/**
+ * Set a cursor for a pointer input device (for LV_INPUT_TYPE_POINTER and LV_INPUT_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @param cur_obj pointer to an object to be used as cursor
+ */
+void lv_indev_set_cursor(lv_indev_t * indev, lv_obj_t * cur_obj);
+
+/**
+ * Set a destination group for a keypad input device (for LV_INDEV_TYPE_KEYPAD)
+ * @param indev pointer to an input device
+ * @param group point to a group
+ */
+void lv_indev_set_group(lv_indev_t * indev, lv_group_t * group);
+
+/**
+ * Set the an array of points for LV_INDEV_TYPE_BUTTON.
+ * These points will be assigned to the buttons to press a specific point on the screen
+ * @param indev pointer to an input device
+ * @param group point to a group
+ */
+void lv_indev_set_button_points(lv_indev_t * indev, const lv_point_t points[]);
+
+/**
+ * Get the last point of an input device (for LV_INDEV_TYPE_POINTER and LV_INDEV_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @param point pointer to a point to store the result
+ */
+void lv_indev_get_point(const lv_indev_t * indev, lv_point_t * point);
+
+/**
+* Get the current gesture direct
+* @param indev pointer to an input device
+* @return current gesture direct
+*/
+lv_dir_t lv_indev_get_gesture_dir(const lv_indev_t * indev);
+
+/**
+ * Get the last pressed key of an input device (for LV_INDEV_TYPE_KEYPAD)
+ * @param indev pointer to an input device
+ * @return the last pressed key (0 on error)
+ */
+uint32_t lv_indev_get_key(const lv_indev_t * indev);
+
+/**
+ * Check the current scroll direction of an input device (for LV_INDEV_TYPE_POINTER and
+ * LV_INDEV_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @return LV_DIR_NONE: no scrolling now
+ * LV_DIR_HOR/VER
+ */
+lv_dir_t lv_indev_get_scroll_dir(const lv_indev_t * indev);
+
+/**
+ * Get the currently scrolled object (for LV_INDEV_TYPE_POINTER and
+ * LV_INDEV_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @return pointer to the currently scrolled object or NULL if no scrolling by this indev
+ */
+lv_obj_t * lv_indev_get_scroll_obj(const lv_indev_t * indev);
+
+/**
+ * Get the movement vector of an input device (for LV_INDEV_TYPE_POINTER and
+ * LV_INDEV_TYPE_BUTTON)
+ * @param indev pointer to an input device
+ * @param point pointer to a point to store the types.pointer.vector
+ */
+void lv_indev_get_vect(const lv_indev_t * indev, lv_point_t * point);
+
+/**
+ * Do nothing until the next release
+ * @param indev pointer to an input device
+ */
+void lv_indev_wait_release(lv_indev_t * indev);
+
+/**
+ * Gets a pointer to the currently active object in the currently processed input device.
+ * @return pointer to currently active object or NULL if no active object
+ */
+lv_obj_t * lv_indev_get_obj_act(void);
+
+/**
+ * Get a pointer to the indev read timer to
+ * modify its parameters with `lv_timer_...` functions.
+ * @param indev pointer to an input device
+ * @return pointer to the indev read refresher timer. (NULL on error)
+ */
+lv_timer_t * lv_indev_get_read_timer(lv_disp_t * indev);
+
+/**
+ * Search the most top, clickable object by a point
+ * @param obj pointer to a start object, typically the screen
+ * @param point pointer to a point for searching the most top child
+ * @return pointer to the found object or NULL if there was no suitable object
+ */
+lv_obj_t * lv_indev_search_obj(lv_obj_t * obj, lv_point_t * point);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_INDEV_H*/
diff --git a/lib/lvgl/src/core/lv_indev_scroll.c b/lib/lvgl/src/core/lv_indev_scroll.c
new file mode 100644
index 00000000..c05e3459
--- /dev/null
+++ b/lib/lvgl/src/core/lv_indev_scroll.c
@@ -0,0 +1,652 @@
+/**
+ * @file lv_indev_scroll.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_indev.h"
+#include "lv_indev_scroll.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define ELASTIC_SLOWNESS_FACTOR 4 /*Scrolling on elastic parts are slower by this factor*/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc);
+static void init_scroll_limits(_lv_indev_proc_t * proc);
+static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
+static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs);
+static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y);
+static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc);
+static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc);
+static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end,
+ lv_dir_t dir);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void _lv_indev_scroll_handler(_lv_indev_proc_t * proc)
+{
+ lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
+ /*If there is no scroll object yet try to find one*/
+ if(scroll_obj == NULL) {
+ proc->types.pointer.scroll_sum.x += proc->types.pointer.vect.x;
+ proc->types.pointer.scroll_sum.y += proc->types.pointer.vect.y;
+
+ scroll_obj = find_scroll_obj(proc);
+ if(scroll_obj == NULL) return;
+
+ init_scroll_limits(proc);
+
+ lv_event_send(scroll_obj, LV_EVENT_SCROLL_BEGIN, NULL);
+ if(proc->reset_query) return;
+ }
+
+ /*Set new position or scroll if the vector is not zero*/
+ if(proc->types.pointer.vect.x != 0 || proc->types.pointer.vect.y != 0) {
+ lv_coord_t diff_x = 0;
+ lv_coord_t diff_y = 0;
+
+ if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
+ lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
+ lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
+ diff_x = elastic_diff(scroll_obj, proc->types.pointer.vect.x, sl, sr, LV_DIR_HOR);
+ }
+ else {
+ lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
+ lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
+ diff_y = elastic_diff(scroll_obj, proc->types.pointer.vect.y, st, sb, LV_DIR_VER);
+ }
+
+ lv_dir_t scroll_dir = lv_obj_get_scroll_dir(scroll_obj);
+ if((scroll_dir & LV_DIR_LEFT) == 0 && diff_x > 0) diff_x = 0;
+ if((scroll_dir & LV_DIR_RIGHT) == 0 && diff_x < 0) diff_x = 0;
+ if((scroll_dir & LV_DIR_TOP) == 0 && diff_y > 0) diff_y = 0;
+ if((scroll_dir & LV_DIR_BOTTOM) == 0 && diff_y < 0) diff_y = 0;
+
+ /*Respect the scroll limit area*/
+ scroll_limit_diff(proc, &diff_x, &diff_y);
+
+ _lv_obj_scroll_by_raw(scroll_obj, diff_x, diff_y);
+ if(proc->reset_query) return;
+ proc->types.pointer.scroll_sum.x += diff_x;
+ proc->types.pointer.scroll_sum.y += diff_y;
+ }
+}
+
+
+void _lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc)
+{
+ lv_obj_t * scroll_obj = proc->types.pointer.scroll_obj;
+ if(scroll_obj == NULL) return;
+ if(proc->types.pointer.scroll_dir == LV_DIR_NONE) return;
+
+
+ lv_indev_t * indev_act = lv_indev_get_act();
+ lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
+
+ if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_MOMENTUM) == false) {
+ proc->types.pointer.scroll_throw_vect.y = 0;
+ proc->types.pointer.scroll_throw_vect.x = 0;
+ }
+
+ lv_scroll_snap_t align_x = lv_obj_get_scroll_snap_x(scroll_obj);
+ lv_scroll_snap_t align_y = lv_obj_get_scroll_snap_y(scroll_obj);
+
+ if(proc->types.pointer.scroll_dir == LV_DIR_VER) {
+ proc->types.pointer.scroll_throw_vect.x = 0;
+ /*If no snapping "throw"*/
+ if(align_y == LV_SCROLL_SNAP_NONE) {
+ proc->types.pointer.scroll_throw_vect.y =
+ proc->types.pointer.scroll_throw_vect.y * (100 - scroll_throw) / 100;
+
+ lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
+ lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
+
+ proc->types.pointer.scroll_throw_vect.y = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.y, st, sb,
+ LV_DIR_VER);
+
+ lv_obj_scroll_by(scroll_obj, 0, proc->types.pointer.scroll_throw_vect.y, LV_ANIM_OFF);
+ }
+ /*With snapping find the nearest snap point and scroll there*/
+ else {
+ lv_coord_t diff_y = scroll_throw_predict_y(proc);
+ proc->types.pointer.scroll_throw_vect.y = 0;
+ scroll_limit_diff(proc, NULL, &diff_y);
+ lv_coord_t y = find_snap_point_y(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_y);
+ lv_obj_scroll_by(scroll_obj, 0, diff_y + y, LV_ANIM_ON);
+ }
+ }
+ else if(proc->types.pointer.scroll_dir == LV_DIR_HOR) {
+ proc->types.pointer.scroll_throw_vect.y = 0;
+ /*If no snapping "throw"*/
+ if(align_x == LV_SCROLL_SNAP_NONE) {
+ proc->types.pointer.scroll_throw_vect.x =
+ proc->types.pointer.scroll_throw_vect.x * (100 - scroll_throw) / 100;
+
+ lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
+ lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
+
+ proc->types.pointer.scroll_throw_vect.x = elastic_diff(scroll_obj, proc->types.pointer.scroll_throw_vect.x, sl, sr,
+ LV_DIR_HOR);
+
+ lv_obj_scroll_by(scroll_obj, proc->types.pointer.scroll_throw_vect.x, 0, LV_ANIM_OFF);
+ }
+ /*With snapping find the nearest snap point and scroll there*/
+ else {
+ lv_coord_t diff_x = scroll_throw_predict_x(proc);
+ proc->types.pointer.scroll_throw_vect.x = 0;
+ scroll_limit_diff(proc, &diff_x, NULL);
+ lv_coord_t x = find_snap_point_x(scroll_obj, LV_COORD_MIN, LV_COORD_MAX, diff_x);
+ lv_obj_scroll_by(scroll_obj, x + diff_x, 0, LV_ANIM_ON);
+ }
+ }
+
+ /*Check if the scroll has finished*/
+ if(proc->types.pointer.scroll_throw_vect.x == 0 && proc->types.pointer.scroll_throw_vect.y == 0) {
+ /*Revert if scrolled in*/
+ /*If vertically scrollable and not controlled by snap*/
+ if(align_y == LV_SCROLL_SNAP_NONE) {
+ lv_coord_t st = lv_obj_get_scroll_top(scroll_obj);
+ lv_coord_t sb = lv_obj_get_scroll_bottom(scroll_obj);
+ if(st > 0 || sb > 0) {
+ if(st < 0) {
+ lv_obj_scroll_by(scroll_obj, 0, st, LV_ANIM_ON);
+ }
+ else if(sb < 0) {
+ lv_obj_scroll_by(scroll_obj, 0, -sb, LV_ANIM_ON);
+ }
+ }
+ }
+
+ /*If horizontally scrollable and not controlled by snap*/
+ if(align_x == LV_SCROLL_SNAP_NONE) {
+ lv_coord_t sl = lv_obj_get_scroll_left(scroll_obj);
+ lv_coord_t sr = lv_obj_get_scroll_right(scroll_obj);
+ if(sl > 0 || sr > 0) {
+ if(sl < 0) {
+ lv_obj_scroll_by(scroll_obj, sl, 0, LV_ANIM_ON);
+ }
+ else if(sr < 0) {
+ lv_obj_scroll_by(scroll_obj, -sr, 0, LV_ANIM_ON);
+ }
+ }
+ }
+
+ lv_event_send(scroll_obj, LV_EVENT_SCROLL_END, indev_act);
+ if(proc->reset_query) return;
+
+ proc->types.pointer.scroll_dir = LV_DIR_NONE;
+ proc->types.pointer.scroll_obj = NULL;
+ }
+}
+
+/**
+ * Predict where would a scroll throw end
+ * @param indev pointer to an input device
+ * @param dir `LV_DIR_VER` or `LV_DIR_HOR`
+ * @return the difference compared to the current position when the throw would be finished
+ */
+lv_coord_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir)
+{
+ if(indev == NULL) return 0;
+ lv_coord_t v;
+ switch(dir) {
+ case LV_DIR_VER:
+ v = indev->proc.types.pointer.scroll_throw_vect_ori.y;
+ break;
+ case LV_DIR_HOR:
+ v = indev->proc.types.pointer.scroll_throw_vect_ori.x;
+ break;
+ default:
+ return 0;
+ }
+
+ lv_coord_t scroll_throw = indev->driver->scroll_throw;
+ lv_coord_t sum = 0;
+ while(v) {
+ sum += v;
+ v = v * (100 - scroll_throw) / 100;
+ }
+
+ return sum;
+}
+
+void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p)
+{
+ p->x = find_snap_point_x(obj, obj->coords.x1, obj->coords.x2, 0);
+ p->y = find_snap_point_y(obj, obj->coords.y1, obj->coords.y2, 0);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static lv_obj_t * find_scroll_obj(_lv_indev_proc_t * proc)
+{
+ lv_obj_t * obj_candidate = NULL;
+ lv_dir_t dir_candidate = LV_DIR_NONE;
+ lv_indev_t * indev_act = lv_indev_get_act();
+ lv_coord_t scroll_limit = indev_act->driver->scroll_limit;
+
+ /*Go until find a scrollable object in the current direction
+ *More precisely:
+ * 1. Check the pressed object and all of its ancestors and try to find an object which is scrollable
+ * 2. Scrollable means it has some content out of its area
+ * 3. If an object can be scrolled into the current direction then use it ("real match"")
+ * 4. If can be scrolled on the current axis (hor/ver) save it as candidate (at least show an elastic scroll effect)
+ * 5. Use the last candidate. Always the "deepest" parent or the object from point 3*/
+ lv_obj_t * obj_act = proc->types.pointer.act_obj;
+
+ /*Decide if it's a horizontal or vertical scroll*/
+ bool hor_en = false;
+ bool ver_en = false;
+ if(LV_ABS(proc->types.pointer.scroll_sum.x) > LV_ABS(proc->types.pointer.scroll_sum.y)) {
+ hor_en = true;
+ }
+ else {
+ ver_en = true;
+ }
+
+ while(obj_act) {
+ if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLLABLE) == false) {
+ /*If this object don't want to chain the scroll to the parent stop searching*/
+ if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_HOR) == false && hor_en) break;
+ if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_VER) == false && ver_en) break;
+
+ obj_act = lv_obj_get_parent(obj_act);
+ continue;
+ }
+
+ /*Consider both up-down or left/right scrollable according to the current direction*/
+ bool up_en = ver_en;
+ bool down_en = ver_en;
+ bool left_en = hor_en;
+ bool right_en = hor_en;
+
+ /*The object might have disabled some directions.*/
+ lv_dir_t scroll_dir = lv_obj_get_scroll_dir(obj_act);
+ if((scroll_dir & LV_DIR_LEFT) == 0) left_en = false;
+ if((scroll_dir & LV_DIR_RIGHT) == 0) right_en = false;
+ if((scroll_dir & LV_DIR_TOP) == 0) up_en = false;
+ if((scroll_dir & LV_DIR_BOTTOM) == 0) down_en = false;
+
+ /*The object is scrollable to a direction if its content overflow in that direction.*/
+ lv_coord_t st = lv_obj_get_scroll_top(obj_act);
+ lv_coord_t sb = lv_obj_get_scroll_bottom(obj_act);
+ lv_coord_t sl = lv_obj_get_scroll_left(obj_act);
+ lv_coord_t sr = lv_obj_get_scroll_right(obj_act);
+
+ /*If this object is scrollable into the current scroll direction then save it as a candidate.
+ *It's important only to be scrollable on the current axis (hor/ver) because if the scroll
+ *is propagated to this object it can show at least elastic scroll effect.
+ *But if not hor/ver scrollable do not scroll it at all (so it's not a good candidate)*/
+ if((st > 0 || sb > 0) &&
+ ((up_en && proc->types.pointer.scroll_sum.y >= scroll_limit) ||
+ (down_en && proc->types.pointer.scroll_sum.y <= - scroll_limit))) {
+ obj_candidate = obj_act;
+ dir_candidate = LV_DIR_VER;
+ }
+
+ if((sl > 0 || sr > 0) &&
+ ((left_en && proc->types.pointer.scroll_sum.x >= scroll_limit) ||
+ (right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit))) {
+ obj_candidate = obj_act;
+ dir_candidate = LV_DIR_HOR;
+ }
+
+ if(st <= 0) up_en = false;
+ if(sb <= 0) down_en = false;
+ if(sl <= 0) left_en = false;
+ if(sr <= 0) right_en = false;
+
+ /*If the object really can be scrolled into the current direction the use it.*/
+ if((left_en && proc->types.pointer.scroll_sum.x >= scroll_limit) ||
+ (right_en && proc->types.pointer.scroll_sum.x <= - scroll_limit) ||
+ (up_en && proc->types.pointer.scroll_sum.y >= scroll_limit) ||
+ (down_en && proc->types.pointer.scroll_sum.y <= - scroll_limit)) {
+ proc->types.pointer.scroll_dir = hor_en ? LV_DIR_HOR : LV_DIR_VER;
+ break;
+ }
+
+ /*If this object don't want to chain the scroll to the parent stop searching*/
+ if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_HOR) == false && hor_en) break;
+ if(lv_obj_has_flag(obj_act, LV_OBJ_FLAG_SCROLL_CHAIN_VER) == false && ver_en) break;
+
+ /*Try the parent*/
+ obj_act = lv_obj_get_parent(obj_act);
+ }
+
+ /*Use the last candidate*/
+ if(obj_candidate) {
+ proc->types.pointer.scroll_dir = dir_candidate;
+ proc->types.pointer.scroll_obj = obj_candidate;
+ proc->types.pointer.scroll_sum.x = 0;
+ proc->types.pointer.scroll_sum.y = 0;
+ }
+
+ return obj_candidate;
+}
+
+static void init_scroll_limits(_lv_indev_proc_t * proc)
+{
+ lv_obj_t * obj = proc->types.pointer.scroll_obj;
+ /*If there no STOP allow scrolling anywhere*/
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ONE) == false) {
+ lv_area_set(&proc->types.pointer.scroll_area, LV_COORD_MIN, LV_COORD_MIN, LV_COORD_MAX, LV_COORD_MAX);
+ }
+ /*With STOP limit the scrolling to the perv and next snap point*/
+ else {
+ switch(lv_obj_get_scroll_snap_y(obj)) {
+ case LV_SCROLL_SNAP_START:
+ proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y1 + 1, LV_COORD_MAX, 0);
+ proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y1 - 1, 0);
+ break;
+ case LV_SCROLL_SNAP_END:
+ proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, obj->coords.y2, LV_COORD_MAX, 0);
+ proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, obj->coords.y2, 0);
+ break;
+ case LV_SCROLL_SNAP_CENTER: {
+ lv_coord_t y_mid = obj->coords.y1 + lv_area_get_height(&obj->coords) / 2;
+ proc->types.pointer.scroll_area.y1 = find_snap_point_y(obj, y_mid + 1, LV_COORD_MAX, 0);
+ proc->types.pointer.scroll_area.y2 = find_snap_point_y(obj, LV_COORD_MIN, y_mid - 1, 0);
+ break;
+ }
+ default:
+ proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
+ proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
+ break;
+ }
+
+ switch(lv_obj_get_scroll_snap_x(obj)) {
+ case LV_SCROLL_SNAP_START:
+ proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x1, LV_COORD_MAX, 0);
+ proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x1, 0);
+ break;
+ case LV_SCROLL_SNAP_END:
+ proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, obj->coords.x2, LV_COORD_MAX, 0);
+ proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, obj->coords.x2, 0);
+ break;
+ case LV_SCROLL_SNAP_CENTER: {
+ lv_coord_t x_mid = obj->coords.x1 + lv_area_get_width(&obj->coords) / 2;
+ proc->types.pointer.scroll_area.x1 = find_snap_point_x(obj, x_mid + 1, LV_COORD_MAX, 0);
+ proc->types.pointer.scroll_area.x2 = find_snap_point_x(obj, LV_COORD_MIN, x_mid - 1, 0);
+ break;
+ }
+ default:
+ proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
+ proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
+ break;
+ }
+ }
+
+ /*Allow scrolling on the edges. It will be reverted to the edge due to snapping anyway*/
+ if(proc->types.pointer.scroll_area.x1 == 0) proc->types.pointer.scroll_area.x1 = LV_COORD_MIN;
+ if(proc->types.pointer.scroll_area.x2 == 0) proc->types.pointer.scroll_area.x2 = LV_COORD_MAX;
+ if(proc->types.pointer.scroll_area.y1 == 0) proc->types.pointer.scroll_area.y1 = LV_COORD_MIN;
+ if(proc->types.pointer.scroll_area.y2 == 0) proc->types.pointer.scroll_area.y2 = LV_COORD_MAX;
+}
+
+/**
+ * Search for snap point in the `min` - `max` range.
+ * @param obj the object on which snap point should be found
+ * @param min ignore snap points smaller than this. (Absolute coordinate)
+ * @param max ignore snap points greater than this. (Absolute coordinate)
+ * @param ofs offset to snap points. Useful the get a snap point in an imagined case
+ * what if children are already moved by this value
+ * @return the distance of the snap point.
+ */
+static lv_coord_t find_snap_point_x(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
+{
+ lv_scroll_snap_t align = lv_obj_get_scroll_snap_x(obj);
+ if(align == LV_SCROLL_SNAP_NONE) return 0;
+
+ lv_coord_t dist = LV_COORD_MAX;
+
+ lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
+ lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
+
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+ if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
+ lv_coord_t x_child = 0;
+ lv_coord_t x_parent = 0;
+ switch(align) {
+ case LV_SCROLL_SNAP_START:
+ x_child = child->coords.x1;
+ x_parent = obj->coords.x1 + pad_left;
+ break;
+ case LV_SCROLL_SNAP_END:
+ x_child = child->coords.x2;
+ x_parent = obj->coords.x2 - pad_right;
+ break;
+ case LV_SCROLL_SNAP_CENTER:
+ x_child = child->coords.x1 + lv_area_get_width(&child->coords) / 2;
+ x_parent = obj->coords.x1 + pad_left + (lv_area_get_width(&obj->coords) - pad_left - pad_right) / 2;
+ break;
+ default:
+ continue;
+ }
+
+ x_child += ofs;
+ if(x_child >= min && x_child <= max) {
+ lv_coord_t x = x_child - x_parent;
+ if(LV_ABS(x) < LV_ABS(dist)) dist = x;
+ }
+ }
+ }
+
+ return dist == LV_COORD_MAX ? 0 : -dist;
+}
+
+/**
+ * Search for snap point in the `min` - `max` range.
+ * @param obj the object on which snap point should be found
+ * @param min ignore snap points smaller than this. (Absolute coordinate)
+ * @param max ignore snap points greater than this. (Absolute coordinate)
+ * @param ofs offset to snap points. Useful to get a snap point in an imagined case
+ * what if children are already moved by this value
+ * @return the distance of the snap point.
+ */
+static lv_coord_t find_snap_point_y(const lv_obj_t * obj, lv_coord_t min, lv_coord_t max, lv_coord_t ofs)
+{
+ lv_scroll_snap_t align = lv_obj_get_scroll_snap_y(obj);
+ if(align == LV_SCROLL_SNAP_NONE) return 0;
+
+ lv_coord_t dist = LV_COORD_MAX;
+
+ lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
+ lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
+
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+ if(lv_obj_has_flag(child, LV_OBJ_FLAG_SNAPPABLE)) {
+ lv_coord_t y_child = 0;
+ lv_coord_t y_parent = 0;
+ switch(align) {
+ case LV_SCROLL_SNAP_START:
+ y_child = child->coords.y1;
+ y_parent = obj->coords.y1 + pad_top;
+ break;
+ case LV_SCROLL_SNAP_END:
+ y_child = child->coords.y2;
+ y_parent = obj->coords.y2 - pad_bottom;
+ break;
+ case LV_SCROLL_SNAP_CENTER:
+ y_child = child->coords.y1 + lv_area_get_height(&child->coords) / 2;
+ y_parent = obj->coords.y1 + pad_top + (lv_area_get_height(&obj->coords) - pad_top - pad_bottom) / 2;
+ break;
+ default:
+ continue;
+ }
+
+ y_child += ofs;
+ if(y_child >= min && y_child <= max) {
+ lv_coord_t y = y_child - y_parent;
+ if(LV_ABS(y) < LV_ABS(dist)) dist = y;
+ }
+ }
+ }
+
+ return dist == LV_COORD_MAX ? 0 : -dist;
+}
+
+static void scroll_limit_diff(_lv_indev_proc_t * proc, lv_coord_t * diff_x, lv_coord_t * diff_y)
+{
+ if(diff_y) {
+ if(proc->types.pointer.scroll_sum.y + *diff_y < proc->types.pointer.scroll_area.y1) {
+ *diff_y = proc->types.pointer.scroll_area.y1 - proc->types.pointer.scroll_sum.y;
+ }
+
+ if(proc->types.pointer.scroll_sum.y + *diff_y > proc->types.pointer.scroll_area.y2) {
+ *diff_y = proc->types.pointer.scroll_area.y2 - proc->types.pointer.scroll_sum.y;
+ }
+ }
+
+ if(diff_x) {
+ if(proc->types.pointer.scroll_sum.x + *diff_x < proc->types.pointer.scroll_area.x1) {
+ *diff_x = proc->types.pointer.scroll_area.x1 - proc->types.pointer.scroll_sum.x;
+ }
+
+ if(proc->types.pointer.scroll_sum.x + *diff_x > proc->types.pointer.scroll_area.x2) {
+ *diff_x = proc->types.pointer.scroll_area.x2 - proc->types.pointer.scroll_sum.x;
+ }
+ }
+}
+
+
+
+static lv_coord_t scroll_throw_predict_y(_lv_indev_proc_t * proc)
+{
+ lv_coord_t y = proc->types.pointer.scroll_throw_vect.y;
+ lv_coord_t move = 0;
+
+ lv_indev_t * indev_act = lv_indev_get_act();
+ lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
+
+ while(y) {
+ move += y;
+ y = y * (100 - scroll_throw) / 100;
+ }
+ return move;
+}
+
+
+static lv_coord_t scroll_throw_predict_x(_lv_indev_proc_t * proc)
+{
+ lv_coord_t x = proc->types.pointer.scroll_throw_vect.x;
+ lv_coord_t move = 0;
+
+ lv_indev_t * indev_act = lv_indev_get_act();
+ lv_coord_t scroll_throw = indev_act->driver->scroll_throw;
+
+ while(x) {
+ move += x;
+ x = x * (100 - scroll_throw) / 100;
+ }
+ return move;
+}
+
+static lv_coord_t elastic_diff(lv_obj_t * scroll_obj, lv_coord_t diff, lv_coord_t scroll_start, lv_coord_t scroll_end,
+ lv_dir_t dir)
+{
+ if(lv_obj_has_flag(scroll_obj, LV_OBJ_FLAG_SCROLL_ELASTIC)) {
+ /*If there is snapping in the current direction don't use the elastic factor because
+ *it's natural that the first and last items are scrolled (snapped) in.*/
+ lv_scroll_snap_t snap;
+ snap = dir == LV_DIR_HOR ? lv_obj_get_scroll_snap_x(scroll_obj) : lv_obj_get_scroll_snap_y(scroll_obj);
+
+ lv_obj_t * act_obj = lv_indev_get_obj_act();
+ lv_coord_t snap_point = 0;
+ lv_coord_t act_obj_point = 0;
+
+ if(dir == LV_DIR_HOR) {
+ lv_coord_t pad_left = lv_obj_get_style_pad_left(scroll_obj, LV_PART_MAIN);
+ lv_coord_t pad_right = lv_obj_get_style_pad_right(scroll_obj, LV_PART_MAIN);
+
+ switch(snap) {
+ case LV_SCROLL_SNAP_CENTER:
+ snap_point = pad_left + (lv_area_get_width(&scroll_obj->coords) - pad_left - pad_right) / 2 + scroll_obj->coords.x1;
+ act_obj_point = lv_area_get_width(&act_obj->coords) / 2 + act_obj->coords.x1;
+ break;
+ case LV_SCROLL_SNAP_START:
+ snap_point = scroll_obj->coords.x1 + pad_left;
+ act_obj_point = act_obj->coords.x1;
+ break;
+ case LV_SCROLL_SNAP_END:
+ snap_point = scroll_obj->coords.x2 - pad_right;
+ act_obj_point = act_obj->coords.x2;
+ break;
+ }
+ }
+ else {
+ lv_coord_t pad_top = lv_obj_get_style_pad_top(scroll_obj, LV_PART_MAIN);
+ lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(scroll_obj, LV_PART_MAIN);
+
+ switch(snap) {
+ case LV_SCROLL_SNAP_CENTER:
+ snap_point = pad_top + (lv_area_get_height(&scroll_obj->coords) - pad_top - pad_bottom) / 2 + scroll_obj->coords.y1;
+ act_obj_point = lv_area_get_height(&act_obj->coords) / 2 + act_obj->coords.y1;
+ break;
+ case LV_SCROLL_SNAP_START:
+ snap_point = scroll_obj->coords.y1 + pad_top;
+ act_obj_point = act_obj->coords.y1;
+ break;
+ case LV_SCROLL_SNAP_END:
+ snap_point = scroll_obj->coords.y2 - pad_bottom;
+ act_obj_point = act_obj->coords.y2;
+ break;
+ }
+ }
+
+ if(scroll_end < 0) {
+ if(snap != LV_SCROLL_SNAP_NONE && act_obj_point > snap_point) return diff;
+
+ /*Rounding*/
+ if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
+ if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
+ return diff / ELASTIC_SLOWNESS_FACTOR;
+ }
+ else if(scroll_start < 0) {
+ if(snap != LV_SCROLL_SNAP_NONE && act_obj_point < snap_point) return diff;
+
+ /*Rounding*/
+ if(diff < 0) diff -= ELASTIC_SLOWNESS_FACTOR / 2;
+ if(diff > 0) diff += ELASTIC_SLOWNESS_FACTOR / 2;
+ return diff / ELASTIC_SLOWNESS_FACTOR;
+ }
+ }
+ else {
+ /*Scroll back to the boundary if required*/
+ if(scroll_end + diff < 0) diff = - scroll_end;
+ if(scroll_start - diff < 0) diff = scroll_start;
+ }
+
+ return diff;
+}
+
+
diff --git a/lib/lvgl/src/core/lv_indev_scroll.h b/lib/lvgl/src/core/lv_indev_scroll.h
new file mode 100644
index 00000000..76c64d17
--- /dev/null
+++ b/lib/lvgl/src/core/lv_indev_scroll.h
@@ -0,0 +1,65 @@
+/**
+ * @file lv_indev_scroll.h
+ *
+ */
+
+#ifndef LV_INDEV_SCROLL_H
+#define LV_INDEV_SCROLL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Handle scrolling. Called by LVGL during input device processing
+ * @param proc pointer to an input device's proc field
+ */
+void _lv_indev_scroll_handler(_lv_indev_proc_t * proc);
+
+/**
+ * Handle throwing after scrolling. Called by LVGL during input device processing
+ * @param proc pointer to an input device's proc field
+ */
+void _lv_indev_scroll_throw_handler(_lv_indev_proc_t * proc);
+
+/**
+ * Predict where would a scroll throw end
+ * @param indev pointer to an input device
+ * @param dir ` LV_DIR_VER` or `LV_DIR_HOR`
+ * @return the difference compared to the current position when the throw would be finished
+ */
+lv_coord_t lv_indev_scroll_throw_predict(lv_indev_t * indev, lv_dir_t dir);
+
+/**
+ * Get the distance of the nearest snap point
+ * @param obj the object on which snap points should be found
+ * @param p save the distance of the found snap point there
+ */
+void lv_indev_scroll_get_snap_dist(lv_obj_t * obj, lv_point_t * p);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_INDEV_SCROLL_H*/
diff --git a/lib/lvgl/src/core/lv_obj.c b/lib/lvgl/src/core/lv_obj.c
new file mode 100644
index 00000000..8890fcbe
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj.c
@@ -0,0 +1,948 @@
+/**
+ * @file lv_obj.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include "lv_indev.h"
+#include "lv_refr.h"
+#include "lv_group.h"
+#include "lv_disp.h"
+#include "lv_theme.h"
+#include "../misc/lv_assert.h"
+#include "../draw/lv_draw.h"
+#include "../misc/lv_anim.h"
+#include "../misc/lv_timer.h"
+#include "../misc/lv_async.h"
+#include "../misc/lv_fs.h"
+#include "../misc/lv_gc.h"
+#include "../misc/lv_math.h"
+#include "../misc/lv_log.h"
+#include "../hal/lv_hal.h"
+#include "../extra/lv_extra.h"
+#include <stdint.h>
+#include <string.h>
+
+#if LV_USE_GPU_STM32_DMA2D
+ #include "../draw/stm32_dma2d/lv_gpu_stm32_dma2d.h"
+#endif
+
+#if LV_USE_GPU_SWM341_DMA2D
+ #include "../draw/swm341_dma2d/lv_gpu_swm341_dma2d.h"
+#endif
+
+#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
+ #include "../draw/nxp/pxp/lv_gpu_nxp_pxp.h"
+#endif
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_obj_class
+#define LV_OBJ_DEF_WIDTH (LV_DPX(100))
+#define LV_OBJ_DEF_HEIGHT (LV_DPX(50))
+#define STYLE_TRANSITION_MAX 32
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
+static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
+static void lv_obj_draw(lv_event_t * e);
+static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e);
+static void draw_scrollbar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx);
+static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc);
+static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
+static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static bool lv_initialized = false;
+const lv_obj_class_t lv_obj_class = {
+ .constructor_cb = lv_obj_constructor,
+ .destructor_cb = lv_obj_destructor,
+ .event_cb = lv_obj_event,
+ .width_def = LV_DPI_DEF,
+ .height_def = LV_DPI_DEF,
+ .editable = LV_OBJ_CLASS_EDITABLE_FALSE,
+ .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
+ .instance_size = (sizeof(lv_obj_t)),
+ .base_class = NULL,
+};
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+bool lv_is_initialized(void)
+{
+ return lv_initialized;
+}
+
+void lv_init(void)
+{
+ /*Do nothing if already initialized*/
+ if(lv_initialized) {
+ LV_LOG_WARN("lv_init: already inited");
+ return;
+ }
+
+ LV_LOG_INFO("begin");
+
+ /*Initialize the misc modules*/
+ lv_mem_init();
+
+ _lv_timer_core_init();
+
+ _lv_fs_init();
+
+ _lv_anim_core_init();
+
+ _lv_group_init();
+
+ lv_draw_init();
+
+#if LV_USE_GPU_STM32_DMA2D
+ /*Initialize DMA2D GPU*/
+ lv_draw_stm32_dma2d_init();
+#endif
+
+#if LV_USE_GPU_SWM341_DMA2D
+ /*Initialize DMA2D GPU*/
+ lv_draw_swm341_dma2d_init();
+#endif
+
+#if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
+ PXP_COND_STOP(!lv_gpu_nxp_pxp_init(), "PXP init failed.");
+#endif
+
+ _lv_obj_style_init();
+ _lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));
+ _lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t));
+
+ /*Initialize the screen refresh system*/
+ _lv_refr_init();
+
+ _lv_img_decoder_init();
+#if LV_IMG_CACHE_DEF_SIZE
+ lv_img_cache_set_size(LV_IMG_CACHE_DEF_SIZE);
+#endif
+ /*Test if the IDE has UTF-8 encoding*/
+ char * txt = "Á";
+
+ uint8_t * txt_u8 = (uint8_t *)txt;
+ if(txt_u8[0] != 0xc3 || txt_u8[1] != 0x81 || txt_u8[2] != 0x00) {
+ LV_LOG_WARN("The strings have no UTF-8 encoding. Non-ASCII characters won't be displayed.");
+ }
+
+ uint32_t endianess_test = 0x11223344;
+ uint8_t * endianess_test_p = (uint8_t *) &endianess_test;
+ bool big_endian = endianess_test_p[0] == 0x11 ? true : false;
+
+ if(big_endian) {
+ LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 1,
+ "It's a big endian system but LV_BIG_ENDIAN_SYSTEM is not enabled in lv_conf.h");
+ }
+ else {
+ LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 0,
+ "It's a little endian system but LV_BIG_ENDIAN_SYSTEM is enabled in lv_conf.h");
+ }
+
+#if LV_USE_ASSERT_MEM_INTEGRITY
+ LV_LOG_WARN("Memory integrity checks are enabled via LV_USE_ASSERT_MEM_INTEGRITY which makes LVGL much slower");
+#endif
+
+#if LV_USE_ASSERT_OBJ
+ LV_LOG_WARN("Object sanity checks are enabled via LV_USE_ASSERT_OBJ which makes LVGL much slower");
+#endif
+
+#if LV_USE_ASSERT_STYLE
+ LV_LOG_WARN("Style sanity checks are enabled that uses more RAM");
+#endif
+
+#if LV_LOG_LEVEL == LV_LOG_LEVEL_TRACE
+ LV_LOG_WARN("Log level is set to 'Trace' which makes LVGL much slower");
+#endif
+
+ lv_extra_init();
+
+ lv_initialized = true;
+
+ LV_LOG_TRACE("finished");
+}
+
+#if LV_ENABLE_GC || !LV_MEM_CUSTOM
+
+void lv_deinit(void)
+{
+ _lv_gc_clear_roots();
+
+ lv_disp_set_default(NULL);
+ lv_mem_deinit();
+ lv_initialized = false;
+
+ LV_LOG_INFO("lv_deinit done");
+
+#if LV_USE_LOG
+ lv_log_register_print_cb(NULL);
+#endif
+}
+#endif
+
+lv_obj_t * lv_obj_create(lv_obj_t * parent)
+{
+ LV_LOG_INFO("begin");
+ lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
+ lv_obj_class_init_obj(obj);
+ return obj;
+}
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/*-----------------
+ * Attribute set
+ *----------------*/
+
+void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ bool was_on_layout = lv_obj_is_layout_positioned(obj);
+
+ if(f & LV_OBJ_FLAG_HIDDEN) lv_obj_invalidate(obj);
+
+ obj->flags |= f;
+
+ if(f & LV_OBJ_FLAG_HIDDEN) {
+ lv_obj_invalidate(obj);
+ }
+
+ if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) {
+ lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
+ lv_obj_mark_layout_as_dirty(obj);
+ }
+
+ if(f & LV_OBJ_FLAG_SCROLLABLE) {
+ lv_area_t hor_area, ver_area;
+ lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
+ lv_obj_invalidate_area(obj, &hor_area);
+ lv_obj_invalidate_area(obj, &ver_area);
+ }
+}
+
+void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ bool was_on_layout = lv_obj_is_layout_positioned(obj);
+ if(f & LV_OBJ_FLAG_SCROLLABLE) {
+ lv_area_t hor_area, ver_area;
+ lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
+ lv_obj_invalidate_area(obj, &hor_area);
+ lv_obj_invalidate_area(obj, &ver_area);
+ }
+
+ obj->flags &= (~f);
+
+ if(f & LV_OBJ_FLAG_HIDDEN) {
+ lv_obj_invalidate(obj);
+ if(lv_obj_is_layout_positioned(obj)) {
+ lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
+ lv_obj_mark_layout_as_dirty(obj);
+ }
+ }
+
+ if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 | LV_OBJ_FLAG_LAYOUT_2))) {
+ lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
+ }
+
+}
+
+void lv_obj_add_state(lv_obj_t * obj, lv_state_t state)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_state_t new_state = obj->state | state;
+ if(obj->state != new_state) {
+ lv_obj_set_state(obj, new_state);
+ }
+}
+
+void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_state_t new_state = obj->state & (~state);
+ if(obj->state != new_state) {
+ lv_obj_set_state(obj, new_state);
+ }
+}
+
+/*=======================
+ * Getter functions
+ *======================*/
+
+bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ return (obj->flags & f) == f ? true : false;
+}
+
+bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ return (obj->flags & f) ? true : false;
+}
+
+lv_state_t lv_obj_get_state(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ return obj->state;
+}
+
+bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ return obj->state & state ? true : false;
+}
+
+void * lv_obj_get_group(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ if(obj->spec_attr) return obj->spec_attr->group_p;
+ else return NULL;
+}
+
+/*-------------------
+ * OTHER FUNCTIONS
+ *------------------*/
+
+void lv_obj_allocate_spec_attr(lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ if(obj->spec_attr == NULL) {
+ static uint32_t x = 0;
+ x++;
+ obj->spec_attr = lv_mem_alloc(sizeof(_lv_obj_spec_attr_t));
+ LV_ASSERT_MALLOC(obj->spec_attr);
+ if(obj->spec_attr == NULL) return;
+
+ lv_memset_00(obj->spec_attr, sizeof(_lv_obj_spec_attr_t));
+
+ obj->spec_attr->scroll_dir = LV_DIR_ALL;
+ obj->spec_attr->scrollbar_mode = LV_SCROLLBAR_MODE_AUTO;
+ }
+}
+
+bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
+{
+ if(obj == NULL) return false;
+ return obj->class_p == class_p ? true : false;
+}
+
+bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p)
+{
+ const lv_obj_class_t * obj_class = obj->class_p;
+ while(obj_class) {
+ if(obj_class == class_p) return true;
+ obj_class = obj_class->base_class;
+ }
+
+ return false;
+}
+
+const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj)
+{
+ return obj->class_p;
+}
+
+bool lv_obj_is_valid(const lv_obj_t * obj)
+{
+ lv_disp_t * disp = lv_disp_get_next(NULL);
+ while(disp) {
+ uint32_t i;
+ for(i = 0; i < disp->screen_cnt; i++) {
+ if(disp->screens[i] == obj) return true;
+ bool found = obj_valid_child(disp->screens[i], obj);
+ if(found) return true;
+ }
+
+ disp = lv_disp_get_next(disp);
+ }
+
+ return false;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
+{
+ LV_UNUSED(class_p);
+ LV_TRACE_OBJ_CREATE("begin");
+
+ lv_obj_t * parent = obj->parent;
+ if(parent) {
+ lv_coord_t sl = lv_obj_get_scroll_left(parent);
+ lv_coord_t st = lv_obj_get_scroll_top(parent);
+
+ obj->coords.y1 = parent->coords.y1 + lv_obj_get_style_pad_top(parent, LV_PART_MAIN) - st;
+ obj->coords.y2 = obj->coords.y1 - 1;
+ obj->coords.x1 = parent->coords.x1 + lv_obj_get_style_pad_left(parent, LV_PART_MAIN) - sl;
+ obj->coords.x2 = obj->coords.x1 - 1;
+ }
+
+ /*Set attributes*/
+ obj->flags = LV_OBJ_FLAG_CLICKABLE;
+ obj->flags |= LV_OBJ_FLAG_SNAPPABLE;
+ if(parent) obj->flags |= LV_OBJ_FLAG_PRESS_LOCK;
+ if(parent) obj->flags |= LV_OBJ_FLAG_SCROLL_CHAIN;
+ obj->flags |= LV_OBJ_FLAG_CLICK_FOCUSABLE;
+ obj->flags |= LV_OBJ_FLAG_SCROLLABLE;
+ obj->flags |= LV_OBJ_FLAG_SCROLL_ELASTIC;
+ obj->flags |= LV_OBJ_FLAG_SCROLL_MOMENTUM;
+ obj->flags |= LV_OBJ_FLAG_SCROLL_WITH_ARROW;
+ if(parent) obj->flags |= LV_OBJ_FLAG_GESTURE_BUBBLE;
+
+ LV_TRACE_OBJ_CREATE("finished");
+}
+
+static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
+{
+ LV_UNUSED(class_p);
+
+ _lv_event_mark_deleted(obj);
+
+ /*Remove all style*/
+ lv_obj_enable_style_refresh(false); /*No need to refresh the style because the object will be deleted*/
+ lv_obj_remove_style_all(obj);
+ lv_obj_enable_style_refresh(true);
+
+ /*Remove the animations from this object*/
+ lv_anim_del(obj, NULL);
+
+ /*Delete from the group*/
+ lv_group_t * group = lv_obj_get_group(obj);
+ if(group) lv_group_remove_obj(obj);
+
+ if(obj->spec_attr) {
+ if(obj->spec_attr->children) {
+ lv_mem_free(obj->spec_attr->children);
+ obj->spec_attr->children = NULL;
+ }
+ if(obj->spec_attr->event_dsc) {
+ lv_mem_free(obj->spec_attr->event_dsc);
+ obj->spec_attr->event_dsc = NULL;
+ }
+
+ lv_mem_free(obj->spec_attr);
+ obj->spec_attr = NULL;
+ }
+}
+
+static void lv_obj_draw(lv_event_t * e)
+{
+ lv_event_code_t code = lv_event_get_code(e);
+ lv_obj_t * obj = lv_event_get_target(e);
+ if(code == LV_EVENT_COVER_CHECK) {
+ lv_cover_check_info_t * info = lv_event_get_param(e);
+ if(info->res == LV_COVER_RES_MASKED) return;
+ if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
+ info->res = LV_COVER_RES_MASKED;
+ return;
+ }
+
+ /*Most trivial test. Is the mask fully IN the object? If no it surely doesn't cover it*/
+ lv_coord_t r = lv_obj_get_style_radius(obj, LV_PART_MAIN);
+ lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
+ lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
+ lv_area_t coords;
+ lv_area_copy(&coords, &obj->coords);
+ coords.x1 -= w;
+ coords.x2 += w;
+ coords.y1 -= h;
+ coords.y2 += h;
+
+ if(_lv_area_is_in(info->area, &coords, r) == false) {
+ info->res = LV_COVER_RES_NOT_COVER;
+ return;
+ }
+
+ if(lv_obj_get_style_bg_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
+ info->res = LV_COVER_RES_NOT_COVER;
+ return;
+ }
+
+ info->res = LV_COVER_RES_COVER;
+
+ }
+ else if(code == LV_EVENT_DRAW_MAIN) {
+ lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
+ lv_draw_rect_dsc_t draw_dsc;
+ lv_draw_rect_dsc_init(&draw_dsc);
+ /*If the border is drawn later disable loading its properties*/
+ if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
+ draw_dsc.border_post = 1;
+ }
+
+ lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
+ lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
+ lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
+ lv_area_t coords;
+ lv_area_copy(&coords, &obj->coords);
+ coords.x1 -= w;
+ coords.x2 += w;
+ coords.y1 -= h;
+ coords.y2 += h;
+
+ lv_obj_draw_part_dsc_t part_dsc;
+ lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
+ part_dsc.class_p = MY_CLASS;
+ part_dsc.type = LV_OBJ_DRAW_PART_RECTANGLE;
+ part_dsc.rect_dsc = &draw_dsc;
+ part_dsc.draw_area = &coords;
+ part_dsc.part = LV_PART_MAIN;
+ lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
+
+#if LV_DRAW_COMPLEX
+ /*With clip corner enabled draw the bg img separately to make it clipped*/
+ bool clip_corner = (lv_obj_get_style_clip_corner(obj, LV_PART_MAIN) && draw_dsc.radius != 0) ? true : false;
+ const void * bg_img_src = draw_dsc.bg_img_src;
+ if(clip_corner) {
+ draw_dsc.bg_img_src = NULL;
+ }
+#endif
+
+ lv_draw_rect(draw_ctx, &draw_dsc, &coords);
+
+
+#if LV_DRAW_COMPLEX
+ if(clip_corner) {
+ lv_draw_mask_radius_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_radius_param_t));
+ lv_draw_mask_radius_init(mp, &obj->coords, draw_dsc.radius, false);
+ /*Add the mask and use `obj+8` as custom id. Don't use `obj` directly because it might be used by the user*/
+ lv_draw_mask_add(mp, obj + 8);
+
+ if(bg_img_src) {
+ draw_dsc.bg_opa = LV_OPA_TRANSP;
+ draw_dsc.border_opa = LV_OPA_TRANSP;
+ draw_dsc.outline_opa = LV_OPA_TRANSP;
+ draw_dsc.shadow_opa = LV_OPA_TRANSP;
+ draw_dsc.bg_img_src = bg_img_src;
+ lv_draw_rect(draw_ctx, &draw_dsc, &coords);
+ }
+
+ }
+#endif
+ lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
+ }
+ else if(code == LV_EVENT_DRAW_POST) {
+ lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
+ draw_scrollbar(obj, draw_ctx);
+
+#if LV_DRAW_COMPLEX
+ if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
+ lv_draw_mask_radius_param_t * param = lv_draw_mask_remove_custom(obj + 8);
+ if(param) {
+ lv_draw_mask_free_param(param);
+ lv_mem_buf_release(param);
+ }
+ }
+#endif
+
+ /*If the border is drawn later disable loading other properties*/
+ if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
+ lv_draw_rect_dsc_t draw_dsc;
+ lv_draw_rect_dsc_init(&draw_dsc);
+ draw_dsc.bg_opa = LV_OPA_TRANSP;
+ draw_dsc.bg_img_opa = LV_OPA_TRANSP;
+ draw_dsc.outline_opa = LV_OPA_TRANSP;
+ draw_dsc.shadow_opa = LV_OPA_TRANSP;
+ lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
+
+ lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
+ lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
+ lv_area_t coords;
+ lv_area_copy(&coords, &obj->coords);
+ coords.x1 -= w;
+ coords.x2 += w;
+ coords.y1 -= h;
+ coords.y2 += h;
+
+ lv_obj_draw_part_dsc_t part_dsc;
+ lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
+ part_dsc.class_p = MY_CLASS;
+ part_dsc.type = LV_OBJ_DRAW_PART_BORDER_POST;
+ part_dsc.rect_dsc = &draw_dsc;
+ part_dsc.draw_area = &coords;
+ part_dsc.part = LV_PART_MAIN;
+ lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
+
+ lv_draw_rect(draw_ctx, &draw_dsc, &coords);
+ lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
+ }
+ }
+}
+
+static void draw_scrollbar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx)
+{
+
+ lv_area_t hor_area;
+ lv_area_t ver_area;
+ lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
+
+ if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
+
+ lv_draw_rect_dsc_t draw_dsc;
+ lv_res_t sb_res = scrollbar_init_draw_dsc(obj, &draw_dsc);
+ if(sb_res != LV_RES_OK) return;
+
+ lv_obj_draw_part_dsc_t part_dsc;
+ lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
+ part_dsc.class_p = MY_CLASS;
+ part_dsc.type = LV_OBJ_DRAW_PART_SCROLLBAR;
+ part_dsc.rect_dsc = &draw_dsc;
+ part_dsc.part = LV_PART_SCROLLBAR;
+
+ if(lv_area_get_size(&hor_area) > 0) {
+ part_dsc.draw_area = &hor_area;
+ lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
+ lv_draw_rect(draw_ctx, &draw_dsc, &hor_area);
+ lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
+ }
+ if(lv_area_get_size(&ver_area) > 0) {
+ part_dsc.draw_area = &ver_area;
+ lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
+ part_dsc.draw_area = &ver_area;
+ lv_draw_rect(draw_ctx, &draw_dsc, &ver_area);
+ lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
+ }
+}
+
+/**
+ * Initialize the draw descriptor for the scrollbar
+ * @param obj pointer to an object
+ * @param dsc the draw descriptor to initialize
+ * @return LV_RES_OK: the scrollbar is visible; LV_RES_INV: the scrollbar is not visible
+ */
+static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc)
+{
+ lv_draw_rect_dsc_init(dsc);
+ dsc->bg_opa = lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR);
+ if(dsc->bg_opa > LV_OPA_MIN) {
+ dsc->bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SCROLLBAR);
+ }
+
+ dsc->border_opa = lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR);
+ if(dsc->border_opa > LV_OPA_MIN) {
+ dsc->border_width = lv_obj_get_style_border_width(obj, LV_PART_SCROLLBAR);
+ if(dsc->border_width > 0) {
+ dsc->border_color = lv_obj_get_style_border_color(obj, LV_PART_SCROLLBAR);
+ }
+ else {
+ dsc->border_opa = LV_OPA_TRANSP;
+ }
+ }
+
+#if LV_DRAW_COMPLEX
+ dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, LV_PART_SCROLLBAR);
+ if(dsc->shadow_opa > LV_OPA_MIN) {
+ dsc->shadow_width = lv_obj_get_style_shadow_width(obj, LV_PART_SCROLLBAR);
+ if(dsc->shadow_width > 0) {
+ dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, LV_PART_SCROLLBAR);
+ dsc->shadow_color = lv_obj_get_style_shadow_color(obj, LV_PART_SCROLLBAR);
+ }
+ else {
+ dsc->shadow_opa = LV_OPA_TRANSP;
+ }
+ }
+
+ lv_opa_t opa = lv_obj_get_style_opa(obj, LV_PART_SCROLLBAR);
+ if(opa < LV_OPA_MAX) {
+ dsc->bg_opa = (dsc->bg_opa * opa) >> 8;
+ dsc->border_opa = (dsc->bg_opa * opa) >> 8;
+ dsc->shadow_opa = (dsc->bg_opa * opa) >> 8;
+ }
+
+ if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP || dsc->shadow_opa != LV_OPA_TRANSP) {
+ dsc->radius = lv_obj_get_style_radius(obj, LV_PART_SCROLLBAR);
+ return LV_RES_OK;
+ }
+ else {
+ return LV_RES_INV;
+ }
+#else
+ if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP) return LV_RES_OK;
+ else return LV_RES_INV;
+#endif
+}
+
+static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e)
+{
+ LV_UNUSED(class_p);
+
+ lv_event_code_t code = lv_event_get_code(e);
+ lv_obj_t * obj = lv_event_get_current_target(e);
+ if(code == LV_EVENT_PRESSED) {
+ lv_obj_add_state(obj, LV_STATE_PRESSED);
+ }
+ else if(code == LV_EVENT_RELEASED) {
+ lv_obj_clear_state(obj, LV_STATE_PRESSED);
+ void * param = lv_event_get_param(e);
+ /*Go the checked state if enabled*/
+ if(lv_indev_get_scroll_obj(param) == NULL && lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
+ if(!(lv_obj_get_state(obj) & LV_STATE_CHECKED)) lv_obj_add_state(obj, LV_STATE_CHECKED);
+ else lv_obj_clear_state(obj, LV_STATE_CHECKED);
+
+ lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
+ if(res != LV_RES_OK) return;
+ }
+ }
+ else if(code == LV_EVENT_PRESS_LOST) {
+ lv_obj_clear_state(obj, LV_STATE_PRESSED);
+ }
+ else if(code == LV_EVENT_STYLE_CHANGED) {
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(uint32_t i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ lv_obj_mark_layout_as_dirty(child);
+ }
+ }
+ else if(code == LV_EVENT_KEY) {
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
+ char c = *((char *)lv_event_get_param(e));
+ if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
+ lv_obj_add_state(obj, LV_STATE_CHECKED);
+ }
+ else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
+ lv_obj_clear_state(obj, LV_STATE_CHECKED);
+ }
+
+ /*With Enter LV_EVENT_RELEASED will send VALUE_CHANGE event*/
+ if(c != LV_KEY_ENTER) {
+ lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
+ if(res != LV_RES_OK) return;
+ }
+ }
+ else if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_WITH_ARROW) && !lv_obj_is_editable(obj)) {
+ /*scroll by keypad or encoder*/
+ lv_anim_enable_t anim_enable = LV_ANIM_OFF;
+ lv_coord_t sl = lv_obj_get_scroll_left(obj);
+ lv_coord_t sr = lv_obj_get_scroll_right(obj);
+ char c = *((char *)lv_event_get_param(e));
+ if(c == LV_KEY_DOWN) {
+ /*use scroll_to_x/y functions to enforce scroll limits*/
+ lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
+ }
+ else if(c == LV_KEY_UP) {
+ lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
+ }
+ else if(c == LV_KEY_RIGHT) {
+ /*If the object can't be scrolled horizontally then scroll it vertically*/
+ if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
+ lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
+ else
+ lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) + lv_obj_get_width(obj) / 4, anim_enable);
+ }
+ else if(c == LV_KEY_LEFT) {
+ /*If the object can't be scrolled horizontally then scroll it vertically*/
+ if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
+ lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
+ else
+ lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) - lv_obj_get_width(obj) / 4, anim_enable);
+ }
+ }
+ }
+ else if(code == LV_EVENT_FOCUSED) {
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS)) {
+ lv_obj_scroll_to_view_recursive(obj, LV_ANIM_ON);
+ }
+
+ bool editing = false;
+ editing = lv_group_get_editing(lv_obj_get_group(obj));
+ lv_state_t state = LV_STATE_FOCUSED;
+
+ /* Use the indev for then indev handler.
+ * But if the obj was focused manually it returns NULL so try to
+ * use the indev from the event*/
+ lv_indev_t * indev = lv_indev_get_act();
+ if(indev == NULL) indev = lv_event_get_indev(e);
+
+ lv_indev_type_t indev_type = lv_indev_get_type(indev);
+ if(indev_type == LV_INDEV_TYPE_KEYPAD || indev_type == LV_INDEV_TYPE_ENCODER) state |= LV_STATE_FOCUS_KEY;
+ if(editing) {
+ state |= LV_STATE_EDITED;
+ lv_obj_add_state(obj, state);
+ }
+ else {
+ lv_obj_add_state(obj, state);
+ lv_obj_clear_state(obj, LV_STATE_EDITED);
+ }
+ }
+ else if(code == LV_EVENT_SCROLL_BEGIN) {
+ lv_obj_add_state(obj, LV_STATE_SCROLLED);
+ }
+ else if(code == LV_EVENT_SCROLL_END) {
+ lv_obj_clear_state(obj, LV_STATE_SCROLLED);
+ if(lv_obj_get_scrollbar_mode(obj) == LV_SCROLLBAR_MODE_ACTIVE) {
+ lv_area_t hor_area, ver_area;
+ lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
+ lv_obj_invalidate_area(obj, &hor_area);
+ lv_obj_invalidate_area(obj, &ver_area);
+ }
+ }
+ else if(code == LV_EVENT_DEFOCUSED) {
+ lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED | LV_STATE_FOCUS_KEY);
+ }
+ else if(code == LV_EVENT_SIZE_CHANGED) {
+ lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
+ uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
+ if(layout || align) {
+ lv_obj_mark_layout_as_dirty(obj);
+ }
+
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ lv_obj_mark_layout_as_dirty(child);
+ }
+ }
+ else if(code == LV_EVENT_CHILD_CHANGED) {
+ lv_coord_t w = lv_obj_get_style_width(obj, LV_PART_MAIN);
+ lv_coord_t h = lv_obj_get_style_height(obj, LV_PART_MAIN);
+ lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
+ uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
+ if(layout || align || w == LV_SIZE_CONTENT || h == LV_SIZE_CONTENT) {
+ lv_obj_mark_layout_as_dirty(obj);
+ }
+ }
+ else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
+ lv_coord_t d = lv_obj_calculate_ext_draw_size(obj, LV_PART_MAIN);
+ lv_event_set_ext_draw_size(e, d);
+ }
+ else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) {
+ lv_obj_draw(e);
+ }
+}
+
+/**
+ * Set the state (fully overwrite) of an object.
+ * If specified in the styles, transition animations will be started from the previous state to the current.
+ * @param obj pointer to an object
+ * @param state the new state
+ */
+static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state)
+{
+ if(obj->state == new_state) return;
+
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_state_t prev_state = obj->state;
+ obj->state = new_state;
+
+ _lv_style_state_cmp_t cmp_res = _lv_obj_style_state_compare(obj, prev_state, new_state);
+ /*If there is no difference in styles there is nothing else to do*/
+ if(cmp_res == _LV_STYLE_STATE_CMP_SAME) return;
+
+ _lv_obj_style_transition_dsc_t * ts = lv_mem_buf_get(sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
+ lv_memset_00(ts, sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
+ uint32_t tsi = 0;
+ uint32_t i;
+ for(i = 0; i < obj->style_cnt && tsi < STYLE_TRANSITION_MAX; i++) {
+ _lv_obj_style_t * obj_style = &obj->styles[i];
+ lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
+ lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
+ if(state_act & (~new_state)) continue; /*Skip unrelated styles*/
+ if(obj_style->is_trans) continue;
+
+ lv_style_value_t v;
+ if(lv_style_get_prop_inlined(obj_style->style, LV_STYLE_TRANSITION, &v) != LV_STYLE_RES_FOUND) continue;
+ const lv_style_transition_dsc_t * tr = v.ptr;
+
+ /*Add the props to the set if not added yet or added but with smaller weight*/
+ uint32_t j;
+ for(j = 0; tr->props[j] != 0 && tsi < STYLE_TRANSITION_MAX; j++) {
+ uint32_t t;
+ for(t = 0; t < tsi; t++) {
+ lv_style_selector_t selector = ts[t].selector;
+ lv_state_t state_ts = lv_obj_style_get_selector_state(selector);
+ lv_part_t part_ts = lv_obj_style_get_selector_part(selector);
+ if(ts[t].prop == tr->props[j] && part_ts == part_act && state_ts >= state_act) break;
+ }
+
+ /*If not found add it*/
+ if(t == tsi) {
+ ts[tsi].time = tr->time;
+ ts[tsi].delay = tr->delay;
+ ts[tsi].path_cb = tr->path_xcb;
+ ts[tsi].prop = tr->props[j];
+#if LV_USE_USER_DATA
+ ts[tsi].user_data = tr->user_data;
+#endif
+ ts[tsi].selector = obj_style->selector;
+ tsi++;
+ }
+ }
+ }
+
+ for(i = 0; i < tsi; i++) {
+ lv_part_t part_act = lv_obj_style_get_selector_part(ts[i].selector);
+ _lv_obj_style_create_transition(obj, part_act, prev_state, new_state, &ts[i]);
+ }
+
+ lv_mem_buf_release(ts);
+
+ if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_REDRAW) {
+ lv_obj_invalidate(obj);
+ }
+ else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_LAYOUT) {
+ lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
+ }
+ else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD) {
+ lv_obj_invalidate(obj);
+ lv_obj_refresh_ext_draw_size(obj);
+ }
+}
+
+static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find)
+{
+ /*Check all children of `parent`*/
+ uint32_t child_cnt = 0;
+ if(parent->spec_attr) child_cnt = parent->spec_attr->child_cnt;
+ uint32_t i;
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = parent->spec_attr->children[i];
+ if(child == obj_to_find) {
+ return true;
+ }
+
+ /*Check the children*/
+ bool found = obj_valid_child(child, obj_to_find);
+ if(found) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/lib/lvgl/src/core/lv_obj.h b/lib/lvgl/src/core/lv_obj.h
new file mode 100644
index 00000000..c89e4609
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj.h
@@ -0,0 +1,409 @@
+/**
+ * @file lv_obj.h
+ *
+ */
+
+#ifndef LV_OBJ_H
+#define LV_OBJ_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../lv_conf_internal.h"
+
+#include <stddef.h>
+#include <stdbool.h>
+#include "../misc/lv_style.h"
+#include "../misc/lv_types.h"
+#include "../misc/lv_area.h"
+#include "../misc/lv_color.h"
+#include "../misc/lv_assert.h"
+#include "../hal/lv_hal.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_obj_t;
+
+/**
+ * Possible states of a widget.
+ * OR-ed values are possible
+ */
+enum {
+ LV_STATE_DEFAULT = 0x0000,
+ LV_STATE_CHECKED = 0x0001,
+ LV_STATE_FOCUSED = 0x0002,
+ LV_STATE_FOCUS_KEY = 0x0004,
+ LV_STATE_EDITED = 0x0008,
+ LV_STATE_HOVERED = 0x0010,
+ LV_STATE_PRESSED = 0x0020,
+ LV_STATE_SCROLLED = 0x0040,
+ LV_STATE_DISABLED = 0x0080,
+
+ LV_STATE_USER_1 = 0x1000,
+ LV_STATE_USER_2 = 0x2000,
+ LV_STATE_USER_3 = 0x4000,
+ LV_STATE_USER_4 = 0x8000,
+
+ LV_STATE_ANY = 0xFFFF, /**< Special value can be used in some functions to target all states*/
+};
+
+typedef uint16_t lv_state_t;
+
+/**
+ * The possible parts of widgets.
+ * The parts can be considered as the internal building block of the widgets.
+ * E.g. slider = background + indicator + knob
+ * Not all parts are used by every widget
+ */
+enum {
+ LV_PART_MAIN = 0x000000, /**< A background like rectangle*/
+ LV_PART_SCROLLBAR = 0x010000, /**< The scrollbar(s)*/
+ LV_PART_INDICATOR = 0x020000, /**< Indicator, e.g. for slider, bar, switch, or the tick box of the checkbox*/
+ LV_PART_KNOB = 0x030000, /**< Like handle to grab to adjust the value*/
+ LV_PART_SELECTED = 0x040000, /**< Indicate the currently selected option or section*/
+ LV_PART_ITEMS = 0x050000, /**< Used if the widget has multiple similar elements (e.g. table cells)*/
+ LV_PART_TICKS = 0x060000, /**< Ticks on scale e.g. for a chart or meter*/
+ LV_PART_CURSOR = 0x070000, /**< Mark a specific place e.g. for text area's cursor or on a chart*/
+
+ LV_PART_CUSTOM_FIRST = 0x080000, /**< Extension point for custom widgets*/
+
+ LV_PART_ANY = 0x0F0000, /**< Special value can be used in some functions to target all parts*/
+};
+
+typedef uint32_t lv_part_t;
+
+/**
+ * On/Off features controlling the object's behavior.
+ * OR-ed values are possible
+ */
+enum {
+ LV_OBJ_FLAG_HIDDEN = (1L << 0), /**< Make the object hidden. (Like it wasn't there at all)*/
+ LV_OBJ_FLAG_CLICKABLE = (1L << 1), /**< Make the object clickable by the input devices*/
+ LV_OBJ_FLAG_CLICK_FOCUSABLE = (1L << 2), /**< Add focused state to the object when clicked*/
+ LV_OBJ_FLAG_CHECKABLE = (1L << 3), /**< Toggle checked state when the object is clicked*/
+ LV_OBJ_FLAG_SCROLLABLE = (1L << 4), /**< Make the object scrollable*/
+ LV_OBJ_FLAG_SCROLL_ELASTIC = (1L << 5), /**< Allow scrolling inside but with slower speed*/
+ LV_OBJ_FLAG_SCROLL_MOMENTUM = (1L << 6), /**< Make the object scroll further when "thrown"*/
+ LV_OBJ_FLAG_SCROLL_ONE = (1L << 7), /**< Allow scrolling only one snappable children*/
+ LV_OBJ_FLAG_SCROLL_CHAIN_HOR = (1L << 8), /**< Allow propagating the horizontal scroll to a parent*/
+ LV_OBJ_FLAG_SCROLL_CHAIN_VER = (1L << 9), /**< Allow propagating the vertical scroll to a parent*/
+ LV_OBJ_FLAG_SCROLL_CHAIN = (LV_OBJ_FLAG_SCROLL_CHAIN_HOR | LV_OBJ_FLAG_SCROLL_CHAIN_VER),
+ LV_OBJ_FLAG_SCROLL_ON_FOCUS = (1L << 10), /**< Automatically scroll object to make it visible when focused*/
+ LV_OBJ_FLAG_SCROLL_WITH_ARROW = (1L << 11), /**< Allow scrolling the focused object with arrow keys*/
+ LV_OBJ_FLAG_SNAPPABLE = (1L << 12), /**< If scroll snap is enabled on the parent it can snap to this object*/
+ LV_OBJ_FLAG_PRESS_LOCK = (1L << 13), /**< Keep the object pressed even if the press slid from the object*/
+ LV_OBJ_FLAG_EVENT_BUBBLE = (1L << 14), /**< Propagate the events to the parent too*/
+ LV_OBJ_FLAG_GESTURE_BUBBLE = (1L << 15), /**< Propagate the gestures to the parent*/
+ LV_OBJ_FLAG_ADV_HITTEST = (1L << 16), /**< Allow performing more accurate hit (click) test. E.g. consider rounded corners.*/
+ LV_OBJ_FLAG_IGNORE_LAYOUT = (1L << 17), /**< Make the object position-able by the layouts*/
+ LV_OBJ_FLAG_FLOATING = (1L << 18), /**< Do not scroll the object when the parent scrolls and ignore layout*/
+ LV_OBJ_FLAG_OVERFLOW_VISIBLE = (1L << 19), /**< Do not clip the children's content to the parent's boundary*/
+
+ LV_OBJ_FLAG_LAYOUT_1 = (1L << 23), /**< Custom flag, free to use by layouts*/
+ LV_OBJ_FLAG_LAYOUT_2 = (1L << 24), /**< Custom flag, free to use by layouts*/
+
+ LV_OBJ_FLAG_WIDGET_1 = (1L << 25), /**< Custom flag, free to use by widget*/
+ LV_OBJ_FLAG_WIDGET_2 = (1L << 26), /**< Custom flag, free to use by widget*/
+ LV_OBJ_FLAG_USER_1 = (1L << 27), /**< Custom flag, free to use by user*/
+ LV_OBJ_FLAG_USER_2 = (1L << 28), /**< Custom flag, free to use by user*/
+ LV_OBJ_FLAG_USER_3 = (1L << 29), /**< Custom flag, free to use by user*/
+ LV_OBJ_FLAG_USER_4 = (1L << 30), /**< Custom flag, free to use by user*/
+
+};
+
+
+typedef uint32_t lv_obj_flag_t;
+
+/**
+ * `type` field in `lv_obj_draw_part_dsc_t` if `class_p = lv_obj_class`
+ * Used in `LV_EVENT_DRAW_PART_BEGIN` and `LV_EVENT_DRAW_PART_END`
+ */
+typedef enum {
+ LV_OBJ_DRAW_PART_RECTANGLE, /**< The main rectangle*/
+ LV_OBJ_DRAW_PART_BORDER_POST,/**< The border if style_border_post = true*/
+ LV_OBJ_DRAW_PART_SCROLLBAR, /**< The scrollbar*/
+} lv_obj_draw_part_type_t;
+
+#include "lv_obj_tree.h"
+#include "lv_obj_pos.h"
+#include "lv_obj_scroll.h"
+#include "lv_obj_style.h"
+#include "lv_obj_draw.h"
+#include "lv_obj_class.h"
+#include "lv_event.h"
+#include "lv_group.h"
+
+/**
+ * Make the base object's class publicly available.
+ */
+extern const lv_obj_class_t lv_obj_class;
+
+/**
+ * Special, rarely used attributes.
+ * They are allocated automatically if any elements is set.
+ */
+typedef struct {
+ struct _lv_obj_t ** children; /**< Store the pointer of the children in an array.*/
+ uint32_t child_cnt; /**< Number of children*/
+ lv_group_t * group_p;
+
+ struct _lv_event_dsc_t * event_dsc; /**< Dynamically allocated event callback and user data array*/
+ lv_point_t scroll; /**< The current X/Y scroll offset*/
+
+ lv_coord_t ext_click_pad; /**< Extra click padding in all direction*/
+ lv_coord_t ext_draw_size; /**< EXTend the size in every direction for drawing.*/
+
+ lv_scrollbar_mode_t scrollbar_mode : 2; /**< How to display scrollbars*/
+ lv_scroll_snap_t scroll_snap_x : 2; /**< Where to align the snappable children horizontally*/
+ lv_scroll_snap_t scroll_snap_y : 2; /**< Where to align the snappable children vertically*/
+ lv_dir_t scroll_dir : 4; /**< The allowed scroll direction(s)*/
+ uint8_t event_dsc_cnt : 6; /**< Number of event callbacks stored in `event_dsc` array*/
+ uint8_t layer_type : 2; /**< Cache the layer type here. Element of @lv_intermediate_layer_type_t */
+} _lv_obj_spec_attr_t;
+
+typedef struct _lv_obj_t {
+ const lv_obj_class_t * class_p;
+ struct _lv_obj_t * parent;
+ _lv_obj_spec_attr_t * spec_attr;
+ _lv_obj_style_t * styles;
+#if LV_USE_USER_DATA
+ void * user_data;
+#endif
+ lv_area_t coords;
+ lv_obj_flag_t flags;
+ lv_state_t state;
+ uint16_t layout_inv : 1;
+ uint16_t scr_layout_inv : 1;
+ uint16_t skip_trans : 1;
+ uint16_t style_cnt : 6;
+ uint16_t h_layout : 1;
+ uint16_t w_layout : 1;
+} lv_obj_t;
+
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize LVGL library.
+ * Should be called before any other LVGL related function.
+ */
+void lv_init(void);
+
+#if LV_ENABLE_GC || !LV_MEM_CUSTOM
+
+/**
+ * Deinit the 'lv' library
+ * Currently only implemented when not using custom allocators, or GC is enabled.
+ */
+void lv_deinit(void);
+
+#endif
+
+/**
+ * Returns whether the 'lv' library is currently initialized
+ */
+bool lv_is_initialized(void);
+
+/**
+ * Create a base object (a rectangle)
+ * @param parent pointer to a parent object. If NULL then a screen will be created.
+ * @return pointer to the new object
+ */
+lv_obj_t * lv_obj_create(lv_obj_t * parent);
+
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/**
+ * Set one or more flags
+ * @param obj pointer to an object
+ * @param f R-ed values from `lv_obj_flag_t` to set.
+ */
+void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f);
+
+/**
+ * Clear one or more flags
+ * @param obj pointer to an object
+ * @param f OR-ed values from `lv_obj_flag_t` to set.
+ */
+void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f);
+
+
+/**
+ * Add one or more states to the object. The other state bits will remain unchanged.
+ * If specified in the styles, transition animation will be started from the previous state to the current.
+ * @param obj pointer to an object
+ * @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
+ */
+void lv_obj_add_state(lv_obj_t * obj, lv_state_t state);
+
+/**
+ * Remove one or more states to the object. The other state bits will remain unchanged.
+ * If specified in the styles, transition animation will be started from the previous state to the current.
+ * @param obj pointer to an object
+ * @param state the states to add. E.g `LV_STATE_PRESSED | LV_STATE_FOCUSED`
+ */
+void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state);
+
+/**
+ * Set the user_data field of the object
+ * @param obj pointer to an object
+ * @param user_data pointer to the new user_data.
+ */
+#if LV_USE_USER_DATA
+static inline void lv_obj_set_user_data(lv_obj_t * obj, void * user_data)
+{
+ obj->user_data = user_data;
+}
+#endif
+
+/*=======================
+ * Getter functions
+ *======================*/
+
+/**
+ * Check if a given flag or all the given flags are set on an object.
+ * @param obj pointer to an object
+ * @param f the flag(s) to check (OR-ed values can be used)
+ * @return true: all flags are set; false: not all flags are set
+ */
+bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f);
+
+/**
+ * Check if a given flag or any of the flags are set on an object.
+ * @param obj pointer to an object
+ * @param f the flag(s) to check (OR-ed values can be used)
+ * @return true: at lest one flag flag is set; false: none of the flags are set
+ */
+bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f);
+
+/**
+ * Get the state of an object
+ * @param obj pointer to an object
+ * @return the state (OR-ed values from `lv_state_t`)
+ */
+lv_state_t lv_obj_get_state(const lv_obj_t * obj);
+
+/**
+ * Check if the object is in a given state or not.
+ * @param obj pointer to an object
+ * @param state a state or combination of states to check
+ * @return true: `obj` is in `state`; false: `obj` is not in `state`
+ */
+bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state);
+
+/**
+ * Get the group of the object
+ * @param obj pointer to an object
+ * @return the pointer to group of the object
+ */
+void * lv_obj_get_group(const lv_obj_t * obj);
+
+/**
+ * Get the user_data field of the object
+ * @param obj pointer to an object
+ * @return the pointer to the user_data of the object
+ */
+#if LV_USE_USER_DATA
+static inline void * lv_obj_get_user_data(lv_obj_t * obj)
+{
+ return obj->user_data;
+}
+#endif
+
+/*=======================
+ * Other functions
+ *======================*/
+
+/**
+ * Allocate special data for an object if not allocated yet.
+ * @param obj pointer to an object
+ */
+void lv_obj_allocate_spec_attr(lv_obj_t * obj);
+
+/**
+ * Check the type of obj.
+ * @param obj pointer to an object
+ * @param class_p a class to check (e.g. `lv_slider_class`)
+ * @return true: `class_p` is the `obj` class.
+ */
+bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p);
+
+/**
+ * Check if any object has a given class (type).
+ * It checks the ancestor classes too.
+ * @param obj pointer to an object
+ * @param class_p a class to check (e.g. `lv_slider_class`)
+ * @return true: `obj` has the given class
+ */
+bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p);
+
+/**
+ * Get the class (type) of the object
+ * @param obj pointer to an object
+ * @return the class (type) of the object
+ */
+const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj);
+
+/**
+ * Check if any object is still "alive".
+ * @param obj pointer to an object
+ * @return true: valid
+ */
+bool lv_obj_is_valid(const lv_obj_t * obj);
+
+/**
+ * Scale the given number of pixels (a distance or size) relative to a 160 DPI display
+ * considering the DPI of the `obj`'s display.
+ * It ensures that e.g. `lv_dpx(100)` will have the same physical size regardless to the
+ * DPI of the display.
+ * @param obj an object whose display's dpi should be considered
+ * @param n the number of pixels to scale
+ * @return `n x current_dpi/160`
+ */
+static inline lv_coord_t lv_obj_dpx(const lv_obj_t * obj, lv_coord_t n)
+{
+ return _LV_DPX_CALC(lv_disp_get_dpi(lv_obj_get_disp(obj)), n);
+}
+
+/**********************
+ * MACROS
+ **********************/
+
+#if LV_USE_ASSERT_OBJ
+# define LV_ASSERT_OBJ(obj_p, obj_class) \
+ do { \
+ LV_ASSERT_MSG(obj_p != NULL, "The object is NULL"); \
+ LV_ASSERT_MSG(lv_obj_has_class(obj_p, obj_class) == true, "Incompatible object type."); \
+ LV_ASSERT_MSG(lv_obj_is_valid(obj_p) == true, "The object is invalid, deleted or corrupted?"); \
+ } while(0)
+# else
+# define LV_ASSERT_OBJ(obj_p, obj_class) do{}while(0)
+#endif
+
+#if LV_USE_LOG && LV_LOG_TRACE_OBJ_CREATE
+# define LV_TRACE_OBJ_CREATE(...) LV_LOG_TRACE(__VA_ARGS__)
+#else
+# define LV_TRACE_OBJ_CREATE(...)
+#endif
+
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_H*/
diff --git a/lib/lvgl/src/core/lv_obj_class.c b/lib/lvgl/src/core/lv_obj_class.c
new file mode 100644
index 00000000..fe204482
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_class.c
@@ -0,0 +1,202 @@
+/**
+ * @file lv_obj_class.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include "lv_theme.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_obj_class
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_obj_construct(lv_obj_t * obj);
+static uint32_t get_instance_size(const lv_obj_class_t * class_p);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_obj_t * lv_obj_class_create_obj(const lv_obj_class_t * class_p, lv_obj_t * parent)
+{
+ LV_TRACE_OBJ_CREATE("Creating object with %p class on %p parent", (void *)class_p, (void *)parent);
+ uint32_t s = get_instance_size(class_p);
+ lv_obj_t * obj = lv_mem_alloc(s);
+ if(obj == NULL) return NULL;
+ lv_memset_00(obj, s);
+ obj->class_p = class_p;
+ obj->parent = parent;
+
+ /*Create a screen*/
+ if(parent == NULL) {
+ LV_TRACE_OBJ_CREATE("creating a screen");
+ lv_disp_t * disp = lv_disp_get_default();
+ if(!disp) {
+ LV_LOG_WARN("No display created yet. No place to assign the new screen");
+ lv_mem_free(obj);
+ return NULL;
+ }
+
+ if(disp->screens == NULL) {
+ disp->screens = lv_mem_alloc(sizeof(lv_obj_t *));
+ disp->screens[0] = obj;
+ disp->screen_cnt = 1;
+ }
+ else {
+ disp->screen_cnt++;
+ disp->screens = lv_mem_realloc(disp->screens, sizeof(lv_obj_t *) * disp->screen_cnt);
+ disp->screens[disp->screen_cnt - 1] = obj;
+ }
+
+ /*Set coordinates to full screen size*/
+ obj->coords.x1 = 0;
+ obj->coords.y1 = 0;
+ obj->coords.x2 = lv_disp_get_hor_res(NULL) - 1;
+ obj->coords.y2 = lv_disp_get_ver_res(NULL) - 1;
+ }
+ /*Create a normal object*/
+ else {
+ LV_TRACE_OBJ_CREATE("creating normal object");
+ LV_ASSERT_OBJ(parent, MY_CLASS);
+ if(parent->spec_attr == NULL) {
+ lv_obj_allocate_spec_attr(parent);
+ }
+
+ if(parent->spec_attr->children == NULL) {
+ parent->spec_attr->children = lv_mem_alloc(sizeof(lv_obj_t *));
+ parent->spec_attr->children[0] = obj;
+ parent->spec_attr->child_cnt = 1;
+ }
+ else {
+ parent->spec_attr->child_cnt++;
+ parent->spec_attr->children = lv_mem_realloc(parent->spec_attr->children,
+ sizeof(lv_obj_t *) * parent->spec_attr->child_cnt);
+ parent->spec_attr->children[parent->spec_attr->child_cnt - 1] = obj;
+ }
+ }
+
+ return obj;
+}
+
+void lv_obj_class_init_obj(lv_obj_t * obj)
+{
+ lv_obj_mark_layout_as_dirty(obj);
+ lv_obj_enable_style_refresh(false);
+
+ lv_theme_apply(obj);
+ lv_obj_construct(obj);
+
+ lv_obj_enable_style_refresh(true);
+ lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
+
+ lv_obj_refresh_self_size(obj);
+
+ lv_group_t * def_group = lv_group_get_default();
+ if(def_group && lv_obj_is_group_def(obj)) {
+ lv_group_add_obj(def_group, obj);
+ }
+
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ if(parent) {
+ /*Call the ancestor's event handler to the parent to notify it about the new child.
+ *Also triggers layout update*/
+ lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
+ lv_event_send(parent, LV_EVENT_CHILD_CREATED, obj);
+
+ /*Invalidate the area if not screen created*/
+ lv_obj_invalidate(obj);
+ }
+}
+
+void _lv_obj_destruct(lv_obj_t * obj)
+{
+ if(obj->class_p->destructor_cb) obj->class_p->destructor_cb(obj->class_p, obj);
+
+ if(obj->class_p->base_class) {
+ /*Don't let the descendant methods run during destructing the ancestor type*/
+ obj->class_p = obj->class_p->base_class;
+
+ /*Call the base class's destructor too*/
+ _lv_obj_destruct(obj);
+ }
+}
+
+bool lv_obj_is_editable(lv_obj_t * obj)
+{
+ const lv_obj_class_t * class_p = obj->class_p;
+
+ /*Find a base in which editable is set*/
+ while(class_p && class_p->editable == LV_OBJ_CLASS_EDITABLE_INHERIT) class_p = class_p->base_class;
+
+ if(class_p == NULL) return false;
+
+ return class_p->editable == LV_OBJ_CLASS_EDITABLE_TRUE ? true : false;
+}
+
+bool lv_obj_is_group_def(lv_obj_t * obj)
+{
+ const lv_obj_class_t * class_p = obj->class_p;
+
+ /*Find a base in which group_def is set*/
+ while(class_p && class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_INHERIT) class_p = class_p->base_class;
+
+ if(class_p == NULL) return false;
+
+ return class_p->group_def == LV_OBJ_CLASS_GROUP_DEF_TRUE ? true : false;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_obj_construct(lv_obj_t * obj)
+{
+ const lv_obj_class_t * original_class_p = obj->class_p;
+
+ if(obj->class_p->base_class) {
+ /*Don't let the descendant methods run during constructing the ancestor type*/
+ obj->class_p = obj->class_p->base_class;
+
+ /*Construct the base first*/
+ lv_obj_construct(obj);
+ }
+
+ /*Restore the original class*/
+ obj->class_p = original_class_p;
+
+ if(obj->class_p->constructor_cb) obj->class_p->constructor_cb(obj->class_p, obj);
+}
+
+static uint32_t get_instance_size(const lv_obj_class_t * class_p)
+{
+ /*Find a base in which instance size is set*/
+ const lv_obj_class_t * base = class_p;
+ while(base && base->instance_size == 0) base = base->base_class;
+
+ if(base == NULL) return 0; /*Never happens: set at least in `lv_obj` class*/
+
+ return base->instance_size;
+}
diff --git a/lib/lvgl/src/core/lv_obj_class.h b/lib/lvgl/src/core/lv_obj_class.h
new file mode 100644
index 00000000..01a7248b
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_class.h
@@ -0,0 +1,94 @@
+/**
+ * @file lv_obj_class.h
+ *
+ */
+
+#ifndef LV_OBJ_CLASS_H
+#define LV_OBJ_CLASS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdint.h>
+#include <stdbool.h>
+
+/*********************
+ * DEFINES
+ *********************/
+
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_obj_t;
+struct _lv_obj_class_t;
+struct _lv_event_t;
+
+typedef enum {
+ LV_OBJ_CLASS_EDITABLE_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
+ LV_OBJ_CLASS_EDITABLE_TRUE,
+ LV_OBJ_CLASS_EDITABLE_FALSE,
+} lv_obj_class_editable_t;
+
+typedef enum {
+ LV_OBJ_CLASS_GROUP_DEF_INHERIT, /**< Check the base class. Must have 0 value to let zero initialized class inherit*/
+ LV_OBJ_CLASS_GROUP_DEF_TRUE,
+ LV_OBJ_CLASS_GROUP_DEF_FALSE,
+} lv_obj_class_group_def_t;
+
+typedef void (*lv_obj_class_event_cb_t)(struct _lv_obj_class_t * class_p, struct _lv_event_t * e);
+/**
+ * Describe the common methods of every object.
+ * Similar to a C++ class.
+ */
+typedef struct _lv_obj_class_t {
+ const struct _lv_obj_class_t * base_class;
+ void (*constructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj);
+ void (*destructor_cb)(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * obj);
+#if LV_USE_USER_DATA
+ void * user_data;
+#endif
+ void (*event_cb)(const struct _lv_obj_class_t * class_p,
+ struct _lv_event_t * e); /**< Widget type specific event function*/
+ lv_coord_t width_def;
+ lv_coord_t height_def;
+ uint32_t editable : 2; /**< Value from ::lv_obj_class_editable_t*/
+ uint32_t group_def : 2; /**< Value from ::lv_obj_class_group_def_t*/
+ uint32_t instance_size : 16;
+} lv_obj_class_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Create an object form a class descriptor
+ * @param class_p pointer to a class
+ * @param parent pointer to an object where the new object should be created
+ * @return pointer to the created object
+ */
+struct _lv_obj_t * lv_obj_class_create_obj(const struct _lv_obj_class_t * class_p, struct _lv_obj_t * parent);
+
+void lv_obj_class_init_obj(struct _lv_obj_t * obj);
+
+void _lv_obj_destruct(struct _lv_obj_t * obj);
+
+bool lv_obj_is_editable(struct _lv_obj_t * obj);
+
+bool lv_obj_is_group_def(struct _lv_obj_t * obj);
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_CLASS_H*/
diff --git a/lib/lvgl/src/core/lv_obj_draw.c b/lib/lvgl/src/core/lv_obj_draw.c
new file mode 100644
index 00000000..f2428cdd
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_draw.c
@@ -0,0 +1,406 @@
+/**
+ * @file lv_obj_draw.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj_draw.h"
+#include "lv_obj.h"
+#include "lv_disp.h"
+#include "lv_indev.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_obj_class
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_obj_init_draw_rect_dsc(lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t * draw_dsc)
+{
+ lv_opa_t opa = LV_OPA_COVER;
+ if(part != LV_PART_MAIN) {
+ opa = lv_obj_get_style_opa(obj, part);
+ if(opa <= LV_OPA_MIN) {
+ draw_dsc->bg_opa = LV_OPA_TRANSP;
+ draw_dsc->bg_img_opa = LV_OPA_TRANSP;
+ draw_dsc->border_opa = LV_OPA_TRANSP;
+ draw_dsc->outline_opa = LV_OPA_TRANSP;
+ draw_dsc->shadow_opa = LV_OPA_TRANSP;
+ return;
+ }
+ }
+
+#if LV_DRAW_COMPLEX
+ if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
+
+ draw_dsc->radius = lv_obj_get_style_radius(obj, part);
+
+ if(draw_dsc->bg_opa != LV_OPA_TRANSP) {
+ draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
+ if(draw_dsc->bg_opa > LV_OPA_MIN) {
+ draw_dsc->bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
+ const lv_grad_dsc_t * grad = lv_obj_get_style_bg_grad(obj, part);
+ if(grad && grad->dir != LV_GRAD_DIR_NONE) {
+ lv_memcpy(&draw_dsc->bg_grad, grad, sizeof(*grad));
+ }
+ else {
+ draw_dsc->bg_grad.dir = lv_obj_get_style_bg_grad_dir(obj, part);
+ if(draw_dsc->bg_grad.dir != LV_GRAD_DIR_NONE) {
+ draw_dsc->bg_grad.stops[0].color = lv_obj_get_style_bg_color_filtered(obj, part);
+ draw_dsc->bg_grad.stops[1].color = lv_obj_get_style_bg_grad_color_filtered(obj, part);
+ draw_dsc->bg_grad.stops[0].frac = lv_obj_get_style_bg_main_stop(obj, part);
+ draw_dsc->bg_grad.stops[1].frac = lv_obj_get_style_bg_grad_stop(obj, part);
+ }
+ draw_dsc->bg_grad.dither = lv_obj_get_style_bg_dither_mode(obj, part);
+ }
+ }
+ }
+
+ draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
+ if(draw_dsc->border_width) {
+ if(draw_dsc->border_opa != LV_OPA_TRANSP) {
+ draw_dsc->border_opa = lv_obj_get_style_border_opa(obj, part);
+ if(draw_dsc->border_opa > LV_OPA_MIN) {
+ draw_dsc->border_side = lv_obj_get_style_border_side(obj, part);
+ draw_dsc->border_color = lv_obj_get_style_border_color_filtered(obj, part);
+ }
+ }
+ }
+
+ draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
+ if(draw_dsc->outline_width) {
+ if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
+ draw_dsc->outline_opa = lv_obj_get_style_outline_opa(obj, part);
+ if(draw_dsc->outline_opa > LV_OPA_MIN) {
+ draw_dsc->outline_pad = lv_obj_get_style_outline_pad(obj, part);
+ draw_dsc->outline_color = lv_obj_get_style_outline_color_filtered(obj, part);
+ }
+ }
+ }
+
+ if(draw_dsc->bg_img_opa != LV_OPA_TRANSP) {
+ draw_dsc->bg_img_src = lv_obj_get_style_bg_img_src(obj, part);
+ if(draw_dsc->bg_img_src) {
+ draw_dsc->bg_img_opa = lv_obj_get_style_bg_img_opa(obj, part);
+ if(draw_dsc->bg_img_opa > LV_OPA_MIN) {
+ if(lv_img_src_get_type(draw_dsc->bg_img_src) == LV_IMG_SRC_SYMBOL) {
+ draw_dsc->bg_img_symbol_font = lv_obj_get_style_text_font(obj, part);
+ draw_dsc->bg_img_recolor = lv_obj_get_style_text_color_filtered(obj, part);
+ }
+ else {
+ draw_dsc->bg_img_recolor = lv_obj_get_style_bg_img_recolor_filtered(obj, part);
+ draw_dsc->bg_img_recolor_opa = lv_obj_get_style_bg_img_recolor_opa(obj, part);
+ draw_dsc->bg_img_tiled = lv_obj_get_style_bg_img_tiled(obj, part);
+ }
+ }
+ }
+ }
+
+ if(draw_dsc->shadow_opa) {
+ draw_dsc->shadow_width = lv_obj_get_style_shadow_width(obj, part);
+ if(draw_dsc->shadow_width) {
+ if(draw_dsc->shadow_opa > LV_OPA_MIN) {
+ draw_dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, part);
+ if(draw_dsc->shadow_opa > LV_OPA_MIN) {
+ draw_dsc->shadow_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
+ draw_dsc->shadow_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
+ draw_dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, part);
+ draw_dsc->shadow_color = lv_obj_get_style_shadow_color_filtered(obj, part);
+ }
+ }
+ }
+ }
+
+#else /*LV_DRAW_COMPLEX*/
+ if(draw_dsc->bg_opa != LV_OPA_TRANSP) {
+ draw_dsc->bg_opa = lv_obj_get_style_bg_opa(obj, part);
+ if(draw_dsc->bg_opa > LV_OPA_MIN) {
+ draw_dsc->bg_color = lv_obj_get_style_bg_color_filtered(obj, part);
+ }
+ }
+
+ draw_dsc->border_width = lv_obj_get_style_border_width(obj, part);
+ if(draw_dsc->border_width) {
+ if(draw_dsc->border_opa != LV_OPA_TRANSP) {
+ draw_dsc->border_opa = lv_obj_get_style_border_opa(obj, part);
+ if(draw_dsc->border_opa > LV_OPA_MIN) {
+ draw_dsc->border_color = lv_obj_get_style_border_color_filtered(obj, part);
+ draw_dsc->border_side = lv_obj_get_style_border_side(obj, part);
+ }
+ }
+ }
+
+ draw_dsc->outline_width = lv_obj_get_style_outline_width(obj, part);
+ if(draw_dsc->outline_width) {
+ if(draw_dsc->outline_opa != LV_OPA_TRANSP) {
+ draw_dsc->outline_opa = lv_obj_get_style_outline_opa(obj, part);
+ if(draw_dsc->outline_opa > LV_OPA_MIN) {
+ draw_dsc->outline_pad = lv_obj_get_style_outline_pad(obj, part);
+ draw_dsc->outline_color = lv_obj_get_style_outline_color_filtered(obj, part);
+ }
+ }
+ }
+
+ if(draw_dsc->bg_img_opa != LV_OPA_TRANSP) {
+ draw_dsc->bg_img_src = lv_obj_get_style_bg_img_src(obj, part);
+ if(draw_dsc->bg_img_src) {
+ draw_dsc->bg_img_opa = lv_obj_get_style_bg_img_opa(obj, part);
+ if(draw_dsc->bg_img_opa > LV_OPA_MIN) {
+ if(lv_img_src_get_type(draw_dsc->bg_img_src) == LV_IMG_SRC_SYMBOL) {
+ draw_dsc->bg_img_symbol_font = lv_obj_get_style_text_font(obj, part);
+ draw_dsc->bg_img_recolor = lv_obj_get_style_text_color_filtered(obj, part);
+ }
+ else {
+ draw_dsc->bg_img_recolor = lv_obj_get_style_bg_img_recolor_filtered(obj, part);
+ draw_dsc->bg_img_recolor_opa = lv_obj_get_style_bg_img_recolor_opa(obj, part);
+ draw_dsc->bg_img_tiled = lv_obj_get_style_bg_img_tiled(obj, part);
+ }
+ }
+ }
+ }
+#endif
+
+ if(part != LV_PART_MAIN) {
+ if(opa < LV_OPA_MAX) {
+ draw_dsc->bg_opa = (opa * draw_dsc->shadow_opa) >> 8;
+ draw_dsc->bg_img_opa = (opa * draw_dsc->shadow_opa) >> 8;
+ draw_dsc->border_opa = (opa * draw_dsc->shadow_opa) >> 8;
+ draw_dsc->outline_opa = (opa * draw_dsc->shadow_opa) >> 8;
+ draw_dsc->shadow_opa = (opa * draw_dsc->shadow_opa) >> 8;
+ }
+ }
+}
+
+void lv_obj_init_draw_label_dsc(lv_obj_t * obj, uint32_t part, lv_draw_label_dsc_t * draw_dsc)
+{
+ draw_dsc->opa = lv_obj_get_style_text_opa(obj, part);
+ if(draw_dsc->opa <= LV_OPA_MIN) return;
+
+ if(part != LV_PART_MAIN) {
+ lv_opa_t opa = lv_obj_get_style_opa(obj, part);
+ if(opa <= LV_OPA_MIN) {
+ draw_dsc->opa = LV_OPA_TRANSP;
+ return;
+ }
+ if(opa < LV_OPA_MAX) {
+ draw_dsc->opa = (opa * draw_dsc->opa) >> 8;
+ }
+ }
+
+ draw_dsc->color = lv_obj_get_style_text_color_filtered(obj, part);
+ draw_dsc->letter_space = lv_obj_get_style_text_letter_space(obj, part);
+ draw_dsc->line_space = lv_obj_get_style_text_line_space(obj, part);
+ draw_dsc->decor = lv_obj_get_style_text_decor(obj, part);
+#if LV_DRAW_COMPLEX
+ if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
+#endif
+
+ draw_dsc->font = lv_obj_get_style_text_font(obj, part);
+
+#if LV_USE_BIDI
+ draw_dsc->bidi_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
+#endif
+
+ draw_dsc->align = lv_obj_get_style_text_align(obj, part);
+}
+
+void lv_obj_init_draw_img_dsc(lv_obj_t * obj, uint32_t part, lv_draw_img_dsc_t * draw_dsc)
+{
+ draw_dsc->opa = lv_obj_get_style_img_opa(obj, part);
+ if(draw_dsc->opa <= LV_OPA_MIN) return;
+
+ if(part != LV_PART_MAIN) {
+ lv_opa_t opa = lv_obj_get_style_opa(obj, part);
+ if(opa <= LV_OPA_MIN) {
+ draw_dsc->opa = LV_OPA_TRANSP;
+ return;
+ }
+ if(opa < LV_OPA_MAX) {
+ draw_dsc->opa = (opa * draw_dsc->opa) >> 8;
+ }
+ }
+
+ draw_dsc->angle = 0;
+ draw_dsc->zoom = LV_IMG_ZOOM_NONE;
+ draw_dsc->pivot.x = lv_area_get_width(&obj->coords) / 2;
+ draw_dsc->pivot.y = lv_area_get_height(&obj->coords) / 2;
+
+ draw_dsc->recolor_opa = lv_obj_get_style_img_recolor_opa(obj, part);
+ if(draw_dsc->recolor_opa > 0) {
+ draw_dsc->recolor = lv_obj_get_style_img_recolor_filtered(obj, part);
+ }
+#if LV_DRAW_COMPLEX
+ if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
+#endif
+}
+
+void lv_obj_init_draw_line_dsc(lv_obj_t * obj, uint32_t part, lv_draw_line_dsc_t * draw_dsc)
+{
+ draw_dsc->opa = lv_obj_get_style_line_opa(obj, part);
+ if(draw_dsc->opa <= LV_OPA_MIN) return;
+
+ if(part != LV_PART_MAIN) {
+ lv_opa_t opa = lv_obj_get_style_opa(obj, part);
+ if(opa <= LV_OPA_MIN) {
+ draw_dsc->opa = LV_OPA_TRANSP;
+ return;
+ }
+ if(opa < LV_OPA_MAX) {
+ draw_dsc->opa = (opa * draw_dsc->opa) >> 8;
+ }
+ }
+
+ draw_dsc->width = lv_obj_get_style_line_width(obj, part);
+ if(draw_dsc->width == 0) return;
+
+ draw_dsc->color = lv_obj_get_style_line_color_filtered(obj, part);
+
+ draw_dsc->dash_width = lv_obj_get_style_line_dash_width(obj, part);
+ if(draw_dsc->dash_width) {
+ draw_dsc->dash_gap = lv_obj_get_style_line_dash_gap(obj, part);
+ }
+
+ draw_dsc->round_start = lv_obj_get_style_line_rounded(obj, part);
+ draw_dsc->round_end = draw_dsc->round_start;
+
+#if LV_DRAW_COMPLEX
+ if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
+#endif
+}
+
+void lv_obj_init_draw_arc_dsc(lv_obj_t * obj, uint32_t part, lv_draw_arc_dsc_t * draw_dsc)
+{
+ draw_dsc->width = lv_obj_get_style_arc_width(obj, part);
+ if(draw_dsc->width == 0) return;
+
+ draw_dsc->opa = lv_obj_get_style_arc_opa(obj, part);
+ if(draw_dsc->opa <= LV_OPA_MIN) return;
+
+ if(part != LV_PART_MAIN) {
+ lv_opa_t opa = lv_obj_get_style_opa(obj, part);
+ if(opa <= LV_OPA_MIN) {
+ draw_dsc->opa = LV_OPA_TRANSP;
+ return;
+ }
+ if(opa < LV_OPA_MAX) {
+ draw_dsc->opa = (opa * draw_dsc->opa) >> 8;
+ }
+ }
+
+ draw_dsc->color = lv_obj_get_style_arc_color_filtered(obj, part);
+ draw_dsc->img_src = lv_obj_get_style_arc_img_src(obj, part);
+
+ draw_dsc->rounded = lv_obj_get_style_arc_rounded(obj, part);
+
+#if LV_DRAW_COMPLEX
+ if(part != LV_PART_MAIN) draw_dsc->blend_mode = lv_obj_get_style_blend_mode(obj, part);
+#endif
+}
+
+lv_coord_t lv_obj_calculate_ext_draw_size(lv_obj_t * obj, uint32_t part)
+{
+ lv_coord_t s = 0;
+
+ lv_coord_t sh_width = lv_obj_get_style_shadow_width(obj, part);
+ if(sh_width) {
+ lv_opa_t sh_opa = lv_obj_get_style_shadow_opa(obj, part);
+ if(sh_opa > LV_OPA_MIN) {
+ sh_width = sh_width / 2 + 1; /*The blur adds only half width*/
+ sh_width += lv_obj_get_style_shadow_spread(obj, part);
+ lv_coord_t sh_ofs_x = lv_obj_get_style_shadow_ofs_x(obj, part);
+ lv_coord_t sh_ofs_y = lv_obj_get_style_shadow_ofs_y(obj, part);
+ sh_width += LV_MAX(LV_ABS(sh_ofs_x), LV_ABS(sh_ofs_y));
+ s = LV_MAX(s, sh_width);
+ }
+ }
+
+ lv_coord_t outline_width = lv_obj_get_style_outline_width(obj, part);
+ if(outline_width) {
+ lv_opa_t outline_opa = lv_obj_get_style_outline_opa(obj, part);
+ if(outline_opa > LV_OPA_MIN) {
+ lv_coord_t outline_pad = lv_obj_get_style_outline_pad(obj, part);
+ s = LV_MAX(s, outline_pad + outline_width);
+ }
+ }
+
+ lv_coord_t w = lv_obj_get_style_transform_width(obj, part);
+ lv_coord_t h = lv_obj_get_style_transform_height(obj, part);
+ lv_coord_t wh = LV_MAX(w, h);
+ if(wh > 0) s += wh;
+
+ return s;
+}
+
+void lv_obj_draw_dsc_init(lv_obj_draw_part_dsc_t * dsc, lv_draw_ctx_t * draw_ctx)
+{
+ lv_memset_00(dsc, sizeof(lv_obj_draw_part_dsc_t));
+ dsc->draw_ctx = draw_ctx;
+}
+
+bool lv_obj_draw_part_check_type(lv_obj_draw_part_dsc_t * dsc, const lv_obj_class_t * class_p, uint32_t type)
+{
+ if(dsc->class_p == class_p && dsc->type == type) return true;
+ else return false;
+}
+
+void lv_obj_refresh_ext_draw_size(lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_coord_t s_old = _lv_obj_get_ext_draw_size(obj);
+ lv_coord_t s_new = 0;
+ lv_event_send(obj, LV_EVENT_REFR_EXT_DRAW_SIZE, &s_new);
+
+ if(s_new != s_old) lv_obj_invalidate(obj);
+
+ /*Store the result if the special attrs already allocated*/
+ if(obj->spec_attr) {
+ obj->spec_attr->ext_draw_size = s_new;
+ }
+ /*Allocate spec. attrs. only if the result is not zero.
+ *Zero is the default value if the spec. attr. are not defined.*/
+ else if(s_new != 0) {
+ lv_obj_allocate_spec_attr(obj);
+ obj->spec_attr->ext_draw_size = s_new;
+ }
+
+ if(s_new != s_old) lv_obj_invalidate(obj);
+}
+
+lv_coord_t _lv_obj_get_ext_draw_size(const lv_obj_t * obj)
+{
+ if(obj->spec_attr) return obj->spec_attr->ext_draw_size;
+ else return 0;
+}
+
+lv_layer_type_t _lv_obj_get_layer_type(const lv_obj_t * obj)
+{
+
+ if(obj->spec_attr) return obj->spec_attr->layer_type;
+ else return LV_LAYER_TYPE_NONE;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
diff --git a/lib/lvgl/src/core/lv_obj_draw.h b/lib/lvgl/src/core/lv_obj_draw.h
new file mode 100644
index 00000000..3f9d0f30
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_draw.h
@@ -0,0 +1,172 @@
+/**
+ * @file lv_obj_draw.h
+ *
+ */
+
+#ifndef LV_OBJ_DRAW_H
+#define LV_OBJ_DRAW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../draw/lv_draw.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_obj_t;
+struct _lv_obj_class_t;
+
+/** Cover check results.*/
+typedef enum {
+ LV_COVER_RES_COVER = 0,
+ LV_COVER_RES_NOT_COVER = 1,
+ LV_COVER_RES_MASKED = 2,
+} lv_cover_res_t;
+
+typedef enum {
+ LV_LAYER_TYPE_NONE,
+ LV_LAYER_TYPE_SIMPLE,
+ LV_LAYER_TYPE_TRANSFORM,
+} lv_layer_type_t;
+
+typedef struct {
+ lv_draw_ctx_t * draw_ctx; /**< Draw context*/
+ const struct _lv_obj_class_t * class_p; /**< The class that sent the event */
+ uint32_t type; /**< The type if part being draw. Element of `lv_<name>_draw_part_type_t` */
+ lv_area_t * draw_area; /**< The area of the part being drawn*/
+ lv_draw_rect_dsc_t *
+ rect_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for rectangle-like parts*/
+ lv_draw_label_dsc_t *
+ label_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for text-like parts*/
+ lv_draw_line_dsc_t *
+ line_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for line-like parts*/
+ lv_draw_img_dsc_t *
+ img_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for image-like parts*/
+ lv_draw_arc_dsc_t *
+ arc_dsc; /**< A draw descriptor that can be modified to changed what LVGL will draw. Set only for arc-like parts*/
+ const lv_point_t *
+ p1; /**< A point calculated during drawing. E.g. a point of chart or the center of an arc.*/
+ const lv_point_t * p2; /**< A point calculated during drawing. E.g. a point of chart.*/
+ char * text; /**< A text calculated during drawing. Can be modified. E.g. tick labels on a chart axis.*/
+ uint32_t text_length; /**< Size of the text buffer containing null-terminated text string calculated during drawing.*/
+ uint32_t part; /**< The current part for which the event is sent*/
+ uint32_t id; /**< The index of the part. E.g. a button's index on button matrix or table cell index.*/
+ lv_coord_t radius; /**< E.g. the radius of an arc (not the corner radius).*/
+ int32_t value; /**< A value calculated during drawing. E.g. Chart's tick line value.*/
+ const void * sub_part_ptr; /**< A pointer the identifies something in the part. E.g. chart series. */
+} lv_obj_draw_part_dsc_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize a rectangle draw descriptor from an object's styles in its current state
+ * @param obj pointer to an object
+ * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
+ * @param draw_dsc the descriptor to initialize.
+ * If an `..._opa` field is set to `LV_OPA_TRANSP` the related properties won't be initialized.
+ * Should be initialized with `lv_draw_rect_dsc_init(draw_dsc)`.
+ * @note Only the relevant fields will be set.
+ * E.g. if `border width == 0` the other border properties won't be evaluated.
+ */
+void lv_obj_init_draw_rect_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_rect_dsc_t * draw_dsc);
+
+/**
+ * Initialize a label draw descriptor from an object's styles in its current state
+ * @param obj pointer to an object
+ * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
+ * @param draw_dsc the descriptor to initialize.
+ * If the `opa` field is set to or the property is equal to `LV_OPA_TRANSP` the rest won't be initialized.
+ * Should be initialized with `lv_draw_label_dsc_init(draw_dsc)`.
+ */
+void lv_obj_init_draw_label_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_label_dsc_t * draw_dsc);
+
+/**
+ * Initialize an image draw descriptor from an object's styles in its current state
+ * @param obj pointer to an object
+ * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
+ * @param draw_dsc the descriptor to initialize.
+ * Should be initialized with `lv_draw_image_dsc_init(draw_dsc)`.
+ */
+void lv_obj_init_draw_img_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_img_dsc_t * draw_dsc);
+
+
+/**
+ * Initialize a line draw descriptor from an object's styles in its current state
+ * @param obj pointer to an object
+ * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
+ * @param draw_dsc the descriptor to initialize.
+ * Should be initialized with `lv_draw_line_dsc_init(draw_dsc)`.
+ */
+void lv_obj_init_draw_line_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_line_dsc_t * draw_dsc);
+
+/**
+ * Initialize an arc draw descriptor from an object's styles in its current state
+ * @param obj pointer to an object
+ * @param part part of the object, e.g. `LV_PART_MAIN`, `LV_PART_SCROLLBAR`, `LV_PART_KNOB`, etc
+ * @param draw_dsc the descriptor to initialize.
+ * Should be initialized with `lv_draw_arc_dsc_init(draw_dsc)`.
+ */
+void lv_obj_init_draw_arc_dsc(struct _lv_obj_t * obj, uint32_t part, lv_draw_arc_dsc_t * draw_dsc);
+
+/**
+ * Get the required extra size (around the object's part) to draw shadow, outline, value etc.
+ * @param obj pointer to an object
+ * @param part part of the object
+ * @return the extra size required around the object
+ */
+lv_coord_t lv_obj_calculate_ext_draw_size(struct _lv_obj_t * obj, uint32_t part);
+
+/**
+ * Initialize a draw descriptor used in events.
+ * @param dsc pointer to a descriptor. Later it should be passed as parameter to an `LV_EVENT_DRAW_PART_BEGIN/END` event.
+ * @param draw the current draw context. (usually returned by `lv_event_get_draw_ctx(e)`)
+ */
+void lv_obj_draw_dsc_init(lv_obj_draw_part_dsc_t * dsc, lv_draw_ctx_t * draw_ctx);
+
+/**
+ * Check the type obj a part draw descriptor
+ * @param dsc the descriptor (normally the event parameter)
+ * @param class_p pointer to class to which `type` is related
+ * @param type element of `lv_<name>_draw_part_type_t`
+ * @return true if ::dsc is related to ::class_p and ::type
+ */
+bool lv_obj_draw_part_check_type(lv_obj_draw_part_dsc_t * dsc, const struct _lv_obj_class_t * class_p, uint32_t type);
+
+/**
+ * Send a 'LV_EVENT_REFR_EXT_DRAW_SIZE' Call the ancestor's event handler to the object to refresh the value of the extended draw size.
+ * The result will be saved in `obj`.
+ * @param obj pointer to an object
+ */
+void lv_obj_refresh_ext_draw_size(struct _lv_obj_t * obj);
+
+/**
+ * Get the extended draw area of an object.
+ * @param obj pointer to an object
+ * @return the size extended draw area around the real coordinates
+ */
+lv_coord_t _lv_obj_get_ext_draw_size(const struct _lv_obj_t * obj);
+
+
+lv_layer_type_t _lv_obj_get_layer_type(const struct _lv_obj_t * obj);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_DRAW_H*/
diff --git a/lib/lvgl/src/core/lv_obj_pos.c b/lib/lvgl/src/core/lv_obj_pos.c
new file mode 100644
index 00000000..a31c11db
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_pos.c
@@ -0,0 +1,1172 @@
+/**
+ * @file lv_obj_pos.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include "lv_disp.h"
+#include "lv_refr.h"
+#include "../misc/lv_gc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_obj_class
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static lv_coord_t calc_content_width(lv_obj_t * obj);
+static lv_coord_t calc_content_height(lv_obj_t * obj);
+static void layout_update_core(lv_obj_t * obj);
+static void transform_point(const lv_obj_t * obj, lv_point_t * p, bool inv);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static uint32_t layout_cnt;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_obj_set_pos(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_obj_set_x(obj, x);
+ lv_obj_set_y(obj, y);
+}
+
+void lv_obj_set_x(lv_obj_t * obj, lv_coord_t x)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_res_t res_x;
+ lv_style_value_t v_x;
+
+ res_x = lv_obj_get_local_style_prop(obj, LV_STYLE_X, &v_x, 0);
+
+ if((res_x == LV_RES_OK && v_x.num != x) || res_x == LV_RES_INV) {
+ lv_obj_set_style_x(obj, x, 0);
+ }
+}
+
+void lv_obj_set_y(lv_obj_t * obj, lv_coord_t y)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_res_t res_y;
+ lv_style_value_t v_y;
+
+ res_y = lv_obj_get_local_style_prop(obj, LV_STYLE_Y, &v_y, 0);
+
+ if((res_y == LV_RES_OK && v_y.num != y) || res_y == LV_RES_INV) {
+ lv_obj_set_style_y(obj, y, 0);
+ }
+}
+
+bool lv_obj_refr_size(lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ /*If the width or height is set by a layout do not modify them*/
+ if(obj->w_layout && obj->h_layout) return false;
+
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ if(parent == NULL) return false;
+
+ lv_coord_t sl_ori = lv_obj_get_scroll_left(obj);
+ bool w_is_content = false;
+ bool w_is_pct = false;
+
+ lv_coord_t w;
+ if(obj->w_layout) {
+ w = lv_obj_get_width(obj);
+ }
+ else {
+ w = lv_obj_get_style_width(obj, LV_PART_MAIN);
+ w_is_content = w == LV_SIZE_CONTENT ? true : false;
+ w_is_pct = LV_COORD_IS_PCT(w) ? true : false;
+ lv_coord_t parent_w = lv_obj_get_content_width(parent);
+
+ if(w_is_content) {
+ w = calc_content_width(obj);
+ }
+ else if(w_is_pct) {
+ /*If parent has content size and the child has pct size
+ *a circular dependency will occur. To solve it keep child size at zero */
+ if(parent->w_layout == 0 && lv_obj_get_style_width(parent, 0) == LV_SIZE_CONTENT) {
+ lv_coord_t border_w = lv_obj_get_style_border_width(obj, 0);
+ w = lv_obj_get_style_pad_left(obj, 0) + border_w;
+ w += lv_obj_get_style_pad_right(obj, 0) + border_w;
+ }
+ else {
+ w = (LV_COORD_GET_PCT(w) * parent_w) / 100;
+ }
+ }
+
+ lv_coord_t minw = lv_obj_get_style_min_width(obj, LV_PART_MAIN);
+ lv_coord_t maxw = lv_obj_get_style_max_width(obj, LV_PART_MAIN);
+ w = lv_clamp_width(w, minw, maxw, parent_w);
+ }
+
+ lv_coord_t st_ori = lv_obj_get_scroll_top(obj);
+ lv_coord_t h;
+ bool h_is_content = false;
+ bool h_is_pct = false;
+ if(obj->h_layout) {
+ h = lv_obj_get_height(obj);
+ }
+ else {
+ h = lv_obj_get_style_height(obj, LV_PART_MAIN);
+ h_is_content = h == LV_SIZE_CONTENT ? true : false;
+ h_is_pct = LV_COORD_IS_PCT(h) ? true : false;
+ lv_coord_t parent_h = lv_obj_get_content_height(parent);
+
+ if(h_is_content) {
+ h = calc_content_height(obj);
+ }
+ else if(h_is_pct) {
+ /*If parent has content size and the child has pct size
+ *a circular dependency will occur. To solve it keep child size at zero */
+ if(parent->h_layout == 0 && lv_obj_get_style_height(parent, 0) == LV_SIZE_CONTENT) {
+ lv_coord_t border_w = lv_obj_get_style_border_width(obj, 0);
+ h = lv_obj_get_style_pad_top(obj, 0) + border_w;
+ h += lv_obj_get_style_pad_bottom(obj, 0) + border_w;
+ }
+ else {
+ h = (LV_COORD_GET_PCT(h) * parent_h) / 100;
+ }
+ }
+
+ lv_coord_t minh = lv_obj_get_style_min_height(obj, LV_PART_MAIN);
+ lv_coord_t maxh = lv_obj_get_style_max_height(obj, LV_PART_MAIN);
+ h = lv_clamp_height(h, minh, maxh, parent_h);
+ }
+
+ /*calc_auto_size set the scroll x/y to 0 so revert the original value*/
+ if(w_is_content || h_is_content) {
+ lv_obj_scroll_to(obj, sl_ori, st_ori, LV_ANIM_OFF);
+ }
+
+ /*Do nothing if the size is not changed*/
+ /*It is very important else recursive resizing can occur without size change*/
+ if(lv_obj_get_width(obj) == w && lv_obj_get_height(obj) == h) return false;
+
+ /*Invalidate the original area*/
+ lv_obj_invalidate(obj);
+
+ /*Save the original coordinates*/
+ lv_area_t ori;
+ lv_obj_get_coords(obj, &ori);
+
+ /*Check if the object inside the parent or not*/
+ lv_area_t parent_fit_area;
+ lv_obj_get_content_coords(parent, &parent_fit_area);
+
+ /*If the object is already out of the parent and its position is changes
+ *surely the scrollbars also changes so invalidate them*/
+ bool on1 = _lv_area_is_in(&ori, &parent_fit_area, 0);
+ if(!on1) lv_obj_scrollbar_invalidate(parent);
+
+ /*Set the length and height
+ *Be sure the content is not scrolled in an invalid position on the new size*/
+ obj->coords.y2 = obj->coords.y1 + h - 1;
+ if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
+ obj->coords.x1 = obj->coords.x2 - w + 1;
+ }
+ else {
+ obj->coords.x2 = obj->coords.x1 + w - 1;
+ }
+
+ /*Call the ancestor's event handler to the object with its new coordinates*/
+ lv_event_send(obj, LV_EVENT_SIZE_CHANGED, &ori);
+
+ /*Call the ancestor's event handler to the parent too*/
+ lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
+
+ /*Invalidate the new area*/
+ lv_obj_invalidate(obj);
+
+ lv_obj_readjust_scroll(obj, LV_ANIM_OFF);
+
+ /*If the object was out of the parent invalidate the new scrollbar area too.
+ *If it wasn't out of the parent but out now, also invalidate the scrollbars*/
+ bool on2 = _lv_area_is_in(&obj->coords, &parent_fit_area, 0);
+ if(on1 || (!on1 && on2)) lv_obj_scrollbar_invalidate(parent);
+
+ lv_obj_refresh_ext_draw_size(obj);
+
+ return true;
+}
+
+void lv_obj_set_size(lv_obj_t * obj, lv_coord_t w, lv_coord_t h)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_obj_set_width(obj, w);
+ lv_obj_set_height(obj, h);
+}
+
+void lv_obj_set_width(lv_obj_t * obj, lv_coord_t w)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ lv_res_t res_w;
+ lv_style_value_t v_w;
+
+ res_w = lv_obj_get_local_style_prop(obj, LV_STYLE_WIDTH, &v_w, 0);
+
+ if((res_w == LV_RES_OK && v_w.num != w) || res_w == LV_RES_INV) {
+ lv_obj_set_style_width(obj, w, 0);
+ }
+}
+
+void lv_obj_set_height(lv_obj_t * obj, lv_coord_t h)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ lv_res_t res_h;
+ lv_style_value_t v_h;
+
+ res_h = lv_obj_get_local_style_prop(obj, LV_STYLE_HEIGHT, &v_h, 0);
+
+ if((res_h == LV_RES_OK && v_h.num != h) || res_h == LV_RES_INV) {
+ lv_obj_set_style_height(obj, h, 0);
+ }
+}
+
+void lv_obj_set_content_width(lv_obj_t * obj, lv_coord_t w)
+{
+ lv_coord_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
+ lv_coord_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
+ lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
+
+ lv_obj_set_width(obj, w + pleft + pright + 2 * border_width);
+}
+
+void lv_obj_set_content_height(lv_obj_t * obj, lv_coord_t h)
+{
+ lv_coord_t ptop = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
+ lv_coord_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
+ lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
+
+ lv_obj_set_height(obj, h + ptop + pbottom + 2 * border_width);
+}
+
+void lv_obj_set_layout(lv_obj_t * obj, uint32_t layout)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_obj_set_style_layout(obj, layout, 0);
+
+ lv_obj_mark_layout_as_dirty(obj);
+}
+
+bool lv_obj_is_layout_positioned(const lv_obj_t * obj)
+{
+ if(lv_obj_has_flag_any(obj, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_IGNORE_LAYOUT | LV_OBJ_FLAG_FLOATING)) return false;
+
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ if(parent == NULL) return false;
+
+ uint32_t layout = lv_obj_get_style_layout(parent, LV_PART_MAIN);
+ if(layout) return true;
+ else return false;
+}
+
+void lv_obj_mark_layout_as_dirty(lv_obj_t * obj)
+{
+ obj->layout_inv = 1;
+
+ /*Mark the screen as dirty too to mark that there is something to do on this screen*/
+ lv_obj_t * scr = lv_obj_get_screen(obj);
+ scr->scr_layout_inv = 1;
+
+ /*Make the display refreshing*/
+ lv_disp_t * disp = lv_obj_get_disp(scr);
+ if(disp->refr_timer) lv_timer_resume(disp->refr_timer);
+}
+
+void lv_obj_update_layout(const lv_obj_t * obj)
+{
+ static bool mutex = false;
+ if(mutex) {
+ LV_LOG_TRACE("Already running, returning");
+ return;
+ }
+ mutex = true;
+
+ lv_obj_t * scr = lv_obj_get_screen(obj);
+
+ /*Repeat until there where layout invalidations*/
+ while(scr->scr_layout_inv) {
+ LV_LOG_INFO("Layout update begin");
+ scr->scr_layout_inv = 0;
+ layout_update_core(scr);
+ LV_LOG_TRACE("Layout update end");
+ }
+
+ mutex = false;
+}
+
+uint32_t lv_layout_register(lv_layout_update_cb_t cb, void * user_data)
+{
+ layout_cnt++;
+ LV_GC_ROOT(_lv_layout_list) = lv_mem_realloc(LV_GC_ROOT(_lv_layout_list), layout_cnt * sizeof(lv_layout_dsc_t));
+ LV_ASSERT_MALLOC(LV_GC_ROOT(_lv_layout_list));
+
+ LV_GC_ROOT(_lv_layout_list)[layout_cnt - 1].cb = cb;
+ LV_GC_ROOT(_lv_layout_list)[layout_cnt - 1].user_data = user_data;
+ return layout_cnt; /*No -1 to skip 0th index*/
+}
+
+void lv_obj_set_align(lv_obj_t * obj, lv_align_t align)
+{
+ lv_obj_set_style_align(obj, align, 0);
+}
+
+void lv_obj_align(lv_obj_t * obj, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs)
+{
+ lv_obj_set_style_align(obj, align, 0);
+ lv_obj_set_pos(obj, x_ofs, y_ofs);
+}
+
+void lv_obj_align_to(lv_obj_t * obj, const lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_obj_update_layout(obj);
+ if(base == NULL) base = lv_obj_get_parent(obj);
+
+ LV_ASSERT_OBJ(base, MY_CLASS);
+
+ lv_coord_t x = 0;
+ lv_coord_t y = 0;
+
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ lv_coord_t pborder = lv_obj_get_style_border_width(parent, LV_PART_MAIN);
+ lv_coord_t pleft = lv_obj_get_style_pad_left(parent, LV_PART_MAIN) + pborder;
+ lv_coord_t ptop = lv_obj_get_style_pad_top(parent, LV_PART_MAIN) + pborder;
+
+ lv_coord_t bborder = lv_obj_get_style_border_width(base, LV_PART_MAIN);
+ lv_coord_t bleft = lv_obj_get_style_pad_left(base, LV_PART_MAIN) + bborder;
+ lv_coord_t btop = lv_obj_get_style_pad_top(base, LV_PART_MAIN) + bborder;
+
+ if(align == LV_ALIGN_DEFAULT) {
+ if(lv_obj_get_style_base_dir(base, LV_PART_MAIN) == LV_BASE_DIR_RTL) align = LV_ALIGN_TOP_RIGHT;
+ else align = LV_ALIGN_TOP_LEFT;
+ }
+
+ switch(align) {
+ case LV_ALIGN_CENTER:
+ x = lv_obj_get_content_width(base) / 2 - lv_obj_get_width(obj) / 2 + bleft;
+ y = lv_obj_get_content_height(base) / 2 - lv_obj_get_height(obj) / 2 + btop;
+ break;
+ case LV_ALIGN_TOP_LEFT:
+ x = bleft;
+ y = btop;
+ break;
+ case LV_ALIGN_TOP_MID:
+ x = lv_obj_get_content_width(base) / 2 - lv_obj_get_width(obj) / 2 + bleft;
+ y = btop;
+ break;
+
+ case LV_ALIGN_TOP_RIGHT:
+ x = lv_obj_get_content_width(base) - lv_obj_get_width(obj) + bleft;
+ y = btop;
+ break;
+
+ case LV_ALIGN_BOTTOM_LEFT:
+ x = bleft;
+ y = lv_obj_get_content_height(base) - lv_obj_get_height(obj) + btop;
+ break;
+ case LV_ALIGN_BOTTOM_MID:
+ x = lv_obj_get_content_width(base) / 2 - lv_obj_get_width(obj) / 2 + bleft;
+ y = lv_obj_get_content_height(base) - lv_obj_get_height(obj) + btop;
+ break;
+
+ case LV_ALIGN_BOTTOM_RIGHT:
+ x = lv_obj_get_content_width(base) - lv_obj_get_width(obj) + bleft;
+ y = lv_obj_get_content_height(base) - lv_obj_get_height(obj) + btop;
+ break;
+
+ case LV_ALIGN_LEFT_MID:
+ x = bleft;
+ y = lv_obj_get_content_height(base) / 2 - lv_obj_get_height(obj) / 2 + btop;
+ break;
+
+ case LV_ALIGN_RIGHT_MID:
+ x = lv_obj_get_content_width(base) - lv_obj_get_width(obj) + bleft;
+ y = lv_obj_get_content_height(base) / 2 - lv_obj_get_height(obj) / 2 + btop;
+ break;
+
+ case LV_ALIGN_OUT_TOP_LEFT:
+ x = 0;
+ y = -lv_obj_get_height(obj);
+ break;
+
+ case LV_ALIGN_OUT_TOP_MID:
+ x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
+ y = -lv_obj_get_height(obj);
+ break;
+
+ case LV_ALIGN_OUT_TOP_RIGHT:
+ x = lv_obj_get_width(base) - lv_obj_get_width(obj);
+ y = -lv_obj_get_height(obj);
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_LEFT:
+ x = 0;
+ y = lv_obj_get_height(base);
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_MID:
+ x = lv_obj_get_width(base) / 2 - lv_obj_get_width(obj) / 2;
+ y = lv_obj_get_height(base);
+ break;
+
+ case LV_ALIGN_OUT_BOTTOM_RIGHT:
+ x = lv_obj_get_width(base) - lv_obj_get_width(obj);
+ y = lv_obj_get_height(base);
+ break;
+
+ case LV_ALIGN_OUT_LEFT_TOP:
+ x = -lv_obj_get_width(obj);
+ y = 0;
+ break;
+
+ case LV_ALIGN_OUT_LEFT_MID:
+ x = -lv_obj_get_width(obj);
+ y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
+ break;
+
+ case LV_ALIGN_OUT_LEFT_BOTTOM:
+ x = -lv_obj_get_width(obj);
+ y = lv_obj_get_height(base) - lv_obj_get_height(obj);
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_TOP:
+ x = lv_obj_get_width(base);
+ y = 0;
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_MID:
+ x = lv_obj_get_width(base);
+ y = lv_obj_get_height(base) / 2 - lv_obj_get_height(obj) / 2;
+ break;
+
+ case LV_ALIGN_OUT_RIGHT_BOTTOM:
+ x = lv_obj_get_width(base);
+ y = lv_obj_get_height(base) - lv_obj_get_height(obj);
+ break;
+ }
+
+ if(lv_obj_get_style_base_dir(parent, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
+ x += x_ofs + base->coords.x1 - parent->coords.x1 + lv_obj_get_scroll_right(parent) - pleft;
+ }
+ else {
+ x += x_ofs + base->coords.x1 - parent->coords.x1 + lv_obj_get_scroll_left(parent) - pleft;
+ }
+ y += y_ofs + base->coords.y1 - parent->coords.y1 + lv_obj_get_scroll_top(parent) - ptop;
+ lv_obj_set_style_align(obj, LV_ALIGN_TOP_LEFT, 0);
+ lv_obj_set_pos(obj, x, y);
+
+}
+
+void lv_obj_get_coords(const lv_obj_t * obj, lv_area_t * coords)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_area_copy(coords, &obj->coords);
+}
+
+lv_coord_t lv_obj_get_x(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_coord_t rel_x;
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ if(parent) {
+ rel_x = obj->coords.x1 - parent->coords.x1;
+ rel_x += lv_obj_get_scroll_x(parent);
+ rel_x -= lv_obj_get_style_pad_left(parent, LV_PART_MAIN);
+ rel_x -= lv_obj_get_style_border_width(parent, LV_PART_MAIN);
+ }
+ else {
+ rel_x = obj->coords.x1;
+ }
+ return rel_x;
+}
+
+lv_coord_t lv_obj_get_x2(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ return lv_obj_get_x(obj) + lv_obj_get_width(obj);
+}
+
+lv_coord_t lv_obj_get_y(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_coord_t rel_y;
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ if(parent) {
+ rel_y = obj->coords.y1 - parent->coords.y1;
+ rel_y += lv_obj_get_scroll_y(parent);
+ rel_y -= lv_obj_get_style_pad_top(parent, LV_PART_MAIN);
+ rel_y -= lv_obj_get_style_border_width(parent, LV_PART_MAIN);
+ }
+ else {
+ rel_y = obj->coords.y1;
+ }
+ return rel_y;
+}
+
+lv_coord_t lv_obj_get_y2(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ return lv_obj_get_y(obj) + lv_obj_get_height(obj);
+}
+
+lv_coord_t lv_obj_get_x_aligned(const lv_obj_t * obj)
+{
+ return lv_obj_get_style_x(obj, LV_PART_MAIN);
+}
+
+lv_coord_t lv_obj_get_y_aligned(const lv_obj_t * obj)
+{
+ return lv_obj_get_style_y(obj, LV_PART_MAIN);
+}
+
+
+lv_coord_t lv_obj_get_width(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ return lv_area_get_width(&obj->coords);
+}
+
+lv_coord_t lv_obj_get_height(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ return lv_area_get_height(&obj->coords);
+}
+
+lv_coord_t lv_obj_get_content_width(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_coord_t left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
+ lv_coord_t right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
+ lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
+
+ return lv_obj_get_width(obj) - left - right - 2 * border_width;
+}
+
+lv_coord_t lv_obj_get_content_height(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_coord_t top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
+ lv_coord_t bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
+ lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
+
+ return lv_obj_get_height(obj) - top - bottom - 2 * border_width;
+}
+
+void lv_obj_get_content_coords(const lv_obj_t * obj, lv_area_t * area)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
+
+ lv_obj_get_coords(obj, area);
+ lv_area_increase(area, -border_width, -border_width);
+ area->x1 += lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
+ area->x2 -= lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
+ area->y1 += lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
+ area->y2 -= lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
+
+}
+
+lv_coord_t lv_obj_get_self_width(const lv_obj_t * obj)
+{
+ lv_point_t p = {0, LV_COORD_MIN};
+ lv_event_send((lv_obj_t *)obj, LV_EVENT_GET_SELF_SIZE, &p);
+ return p.x;
+}
+
+lv_coord_t lv_obj_get_self_height(const lv_obj_t * obj)
+{
+ lv_point_t p = {LV_COORD_MIN, 0};
+ lv_event_send((lv_obj_t *)obj, LV_EVENT_GET_SELF_SIZE, &p);
+ return p.y;
+}
+
+bool lv_obj_refresh_self_size(lv_obj_t * obj)
+{
+ lv_coord_t w_set = lv_obj_get_style_width(obj, LV_PART_MAIN);
+ lv_coord_t h_set = lv_obj_get_style_height(obj, LV_PART_MAIN);
+ if(w_set != LV_SIZE_CONTENT && h_set != LV_SIZE_CONTENT) return false;
+
+ lv_obj_mark_layout_as_dirty(obj);
+ return true;
+}
+
+void lv_obj_refr_pos(lv_obj_t * obj)
+{
+ if(lv_obj_is_layout_positioned(obj)) return;
+
+
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ lv_coord_t x = lv_obj_get_style_x(obj, LV_PART_MAIN);
+ lv_coord_t y = lv_obj_get_style_y(obj, LV_PART_MAIN);
+
+ if(parent == NULL) {
+ lv_obj_move_to(obj, x, y);
+ return;
+ }
+
+ /*Handle percentage value*/
+ lv_coord_t pw = lv_obj_get_content_width(parent);
+ lv_coord_t ph = lv_obj_get_content_height(parent);
+ if(LV_COORD_IS_PCT(x)) x = (pw * LV_COORD_GET_PCT(x)) / 100;
+ if(LV_COORD_IS_PCT(y)) y = (ph * LV_COORD_GET_PCT(y)) / 100;
+
+ /*Handle percentage value of translate*/
+ lv_coord_t tr_x = lv_obj_get_style_translate_x(obj, LV_PART_MAIN);
+ lv_coord_t tr_y = lv_obj_get_style_translate_y(obj, LV_PART_MAIN);
+ lv_coord_t w = lv_obj_get_width(obj);
+ lv_coord_t h = lv_obj_get_height(obj);
+ if(LV_COORD_IS_PCT(tr_x)) tr_x = (w * LV_COORD_GET_PCT(tr_x)) / 100;
+ if(LV_COORD_IS_PCT(tr_y)) tr_y = (h * LV_COORD_GET_PCT(tr_y)) / 100;
+
+ /*Use the translation*/
+ x += tr_x;
+ y += tr_y;
+
+ lv_align_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
+
+ if(align == LV_ALIGN_DEFAULT) {
+ if(lv_obj_get_style_base_dir(parent, LV_PART_MAIN) == LV_BASE_DIR_RTL) align = LV_ALIGN_TOP_RIGHT;
+ else align = LV_ALIGN_TOP_LEFT;
+ }
+
+ if(align == LV_ALIGN_TOP_LEFT) {
+ lv_obj_move_to(obj, x, y);
+ }
+ else {
+
+ switch(align) {
+ case LV_ALIGN_TOP_MID:
+ x += pw / 2 - w / 2;
+ break;
+ case LV_ALIGN_TOP_RIGHT:
+ x += pw - w;
+ break;
+ case LV_ALIGN_LEFT_MID:
+ y += ph / 2 - h / 2;
+ break;
+ case LV_ALIGN_BOTTOM_LEFT:
+ y += ph - h;
+ break;
+ case LV_ALIGN_BOTTOM_MID:
+ x += pw / 2 - w / 2;
+ y += ph - h;
+ break;
+ case LV_ALIGN_BOTTOM_RIGHT:
+ x += pw - w;
+ y += ph - h;
+ break;
+ case LV_ALIGN_RIGHT_MID:
+ x += pw - w;
+ y += ph / 2 - h / 2;
+ break;
+ case LV_ALIGN_CENTER:
+ x += pw / 2 - w / 2;
+ y += ph / 2 - h / 2;
+ break;
+ default:
+ break;
+ }
+ lv_obj_move_to(obj, x, y);
+ }
+}
+
+void lv_obj_move_to(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
+{
+ /*Convert x and y to absolute coordinates*/
+ lv_obj_t * parent = obj->parent;
+
+ if(parent) {
+ lv_coord_t pad_left = lv_obj_get_style_pad_left(parent, LV_PART_MAIN);
+ lv_coord_t pad_top = lv_obj_get_style_pad_top(parent, LV_PART_MAIN);
+
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_FLOATING)) {
+ x += pad_left + parent->coords.x1;
+ y += pad_top + parent->coords.y1;
+ }
+ else {
+ x += pad_left + parent->coords.x1 - lv_obj_get_scroll_x(parent);
+ y += pad_top + parent->coords.y1 - lv_obj_get_scroll_y(parent);
+ }
+
+ lv_coord_t border_width = lv_obj_get_style_border_width(parent, LV_PART_MAIN);
+ x += border_width;
+ y += border_width;
+ }
+
+ /*Calculate and set the movement*/
+ lv_point_t diff;
+ diff.x = x - obj->coords.x1;
+ diff.y = y - obj->coords.y1;
+
+ /*Do nothing if the position is not changed*/
+ /*It is very important else recursive positioning can
+ *occur without position change*/
+ if(diff.x == 0 && diff.y == 0) return;
+
+ /*Invalidate the original area*/
+ lv_obj_invalidate(obj);
+
+ /*Save the original coordinates*/
+ lv_area_t ori;
+ lv_obj_get_coords(obj, &ori);
+
+ /*Check if the object inside the parent or not*/
+ lv_area_t parent_fit_area;
+ bool on1 = false;
+ if(parent) {
+ lv_obj_get_content_coords(parent, &parent_fit_area);
+
+ /*If the object is already out of the parent and its position is changes
+ *surely the scrollbars also changes so invalidate them*/
+ on1 = _lv_area_is_in(&ori, &parent_fit_area, 0);
+ if(!on1) lv_obj_scrollbar_invalidate(parent);
+ }
+
+ obj->coords.x1 += diff.x;
+ obj->coords.y1 += diff.y;
+ obj->coords.x2 += diff.x;
+ obj->coords.y2 += diff.y;
+
+ lv_obj_move_children_by(obj, diff.x, diff.y, false);
+
+ /*Call the ancestor's event handler to the parent too*/
+ if(parent) lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
+
+ /*Invalidate the new area*/
+ lv_obj_invalidate(obj);
+
+ /*If the object was out of the parent invalidate the new scrollbar area too.
+ *If it wasn't out of the parent but out now, also invalidate the srollbars*/
+ if(parent) {
+ bool on2 = _lv_area_is_in(&obj->coords, &parent_fit_area, 0);
+ if(on1 || (!on1 && on2)) lv_obj_scrollbar_invalidate(parent);
+ }
+}
+
+void lv_obj_move_children_by(lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff, bool ignore_floating)
+{
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ if(ignore_floating && lv_obj_has_flag(child, LV_OBJ_FLAG_FLOATING)) continue;
+ child->coords.x1 += x_diff;
+ child->coords.y1 += y_diff;
+ child->coords.x2 += x_diff;
+ child->coords.y2 += y_diff;
+
+ lv_obj_move_children_by(child, x_diff, y_diff, false);
+ }
+}
+
+void lv_obj_transform_point(const lv_obj_t * obj, lv_point_t * p, bool recursive, bool inv)
+{
+ if(obj) {
+ lv_layer_type_t layer_type = _lv_obj_get_layer_type(obj);
+ bool do_tranf = layer_type == LV_LAYER_TYPE_TRANSFORM;
+ if(inv) {
+ if(recursive) lv_obj_transform_point(lv_obj_get_parent(obj), p, recursive, inv);
+ if(do_tranf) transform_point(obj, p, inv);
+ }
+ else {
+ if(do_tranf) transform_point(obj, p, inv);
+ if(recursive) lv_obj_transform_point(lv_obj_get_parent(obj), p, recursive, inv);
+ }
+ }
+}
+
+void lv_obj_get_transformed_area(const lv_obj_t * obj, lv_area_t * area, bool recursive,
+ bool inv)
+{
+ lv_point_t p[4] = {
+ {area->x1, area->y1},
+ {area->x1, area->y2},
+ {area->x2, area->y1},
+ {area->x2, area->y2},
+ };
+
+ lv_obj_transform_point(obj, &p[0], recursive, inv);
+ lv_obj_transform_point(obj, &p[1], recursive, inv);
+ lv_obj_transform_point(obj, &p[2], recursive, inv);
+ lv_obj_transform_point(obj, &p[3], recursive, inv);
+
+ area->x1 = LV_MIN4(p[0].x, p[1].x, p[2].x, p[3].x);
+ area->x2 = LV_MAX4(p[0].x, p[1].x, p[2].x, p[3].x);
+ area->y1 = LV_MIN4(p[0].y, p[1].y, p[2].y, p[3].y);
+ area->y2 = LV_MAX4(p[0].y, p[1].y, p[2].y, p[3].y);
+ lv_area_increase(area, 5, 5);
+}
+
+
+void lv_obj_invalidate_area(const lv_obj_t * obj, const lv_area_t * area)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_disp_t * disp = lv_obj_get_disp(obj);
+ if(!lv_disp_is_invalidation_enabled(disp)) return;
+
+ lv_area_t area_tmp;
+ lv_area_copy(&area_tmp, area);
+ if(!lv_obj_area_is_visible(obj, &area_tmp)) return;
+
+ _lv_inv_area(lv_obj_get_disp(obj), &area_tmp);
+}
+
+void lv_obj_invalidate(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ /*Truncate the area to the object*/
+ lv_area_t obj_coords;
+ lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
+ lv_area_copy(&obj_coords, &obj->coords);
+ obj_coords.x1 -= ext_size;
+ obj_coords.y1 -= ext_size;
+ obj_coords.x2 += ext_size;
+ obj_coords.y2 += ext_size;
+
+ lv_obj_invalidate_area(obj, &obj_coords);
+
+}
+
+bool lv_obj_area_is_visible(const lv_obj_t * obj, lv_area_t * area)
+{
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return false;
+
+ /*Invalidate the object only if it belongs to the current or previous or one of the layers'*/
+ lv_obj_t * obj_scr = lv_obj_get_screen(obj);
+ lv_disp_t * disp = lv_obj_get_disp(obj_scr);
+ if(obj_scr != lv_disp_get_scr_act(disp) &&
+ obj_scr != lv_disp_get_scr_prev(disp) &&
+ obj_scr != lv_disp_get_layer_top(disp) &&
+ obj_scr != lv_disp_get_layer_sys(disp)) {
+ return false;
+ }
+
+ /*Truncate the area to the object*/
+ if(!lv_obj_has_flag_any(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) {
+ lv_area_t obj_coords;
+ lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
+ lv_area_copy(&obj_coords, &obj->coords);
+ obj_coords.x1 -= ext_size;
+ obj_coords.y1 -= ext_size;
+ obj_coords.x2 += ext_size;
+ obj_coords.y2 += ext_size;
+
+ /*The area is not on the object*/
+ if(!_lv_area_intersect(area, area, &obj_coords)) return false;
+ }
+
+ lv_obj_get_transformed_area(obj, area, true, false);
+
+
+ /*Truncate recursively to the parents*/
+ lv_obj_t * par = lv_obj_get_parent(obj);
+ while(par != NULL) {
+ /*If the parent is hidden then the child is hidden and won't be drawn*/
+ if(lv_obj_has_flag(par, LV_OBJ_FLAG_HIDDEN)) return false;
+
+ /*Truncate to the parent and if no common parts break*/
+ if(!lv_obj_has_flag_any(par, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) {
+ lv_area_t par_area = par->coords;
+ lv_obj_get_transformed_area(par, &par_area, true, false);
+ if(!_lv_area_intersect(area, area, &par_area)) return false;
+ }
+
+ par = lv_obj_get_parent(par);
+ }
+
+ return true;
+}
+
+bool lv_obj_is_visible(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_area_t obj_coords;
+ lv_coord_t ext_size = _lv_obj_get_ext_draw_size(obj);
+ lv_area_copy(&obj_coords, &obj->coords);
+ obj_coords.x1 -= ext_size;
+ obj_coords.y1 -= ext_size;
+ obj_coords.x2 += ext_size;
+ obj_coords.y2 += ext_size;
+
+ return lv_obj_area_is_visible(obj, &obj_coords);
+
+}
+
+void lv_obj_set_ext_click_area(lv_obj_t * obj, lv_coord_t size)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_obj_allocate_spec_attr(obj);
+ obj->spec_attr->ext_click_pad = size;
+}
+
+void lv_obj_get_click_area(const lv_obj_t * obj, lv_area_t * area)
+{
+ lv_area_copy(area, &obj->coords);
+ if(obj->spec_attr) {
+ area->x1 -= obj->spec_attr->ext_click_pad;
+ area->x2 += obj->spec_attr->ext_click_pad;
+ area->y1 -= obj->spec_attr->ext_click_pad;
+ area->y2 += obj->spec_attr->ext_click_pad;
+ }
+}
+
+bool lv_obj_hit_test(lv_obj_t * obj, const lv_point_t * point)
+{
+ if(!lv_obj_has_flag(obj, LV_OBJ_FLAG_CLICKABLE)) return false;
+ if(lv_obj_has_state(obj, LV_STATE_DISABLED)) return false;
+
+ lv_area_t a;
+ lv_obj_get_click_area(obj, &a);
+ bool res = _lv_area_is_point_on(&a, point, 0);
+ if(res == false) return false;
+
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_ADV_HITTEST)) {
+ lv_hit_test_info_t hit_info;
+ hit_info.point = point;
+ hit_info.res = true;
+ lv_event_send(obj, LV_EVENT_HIT_TEST, &hit_info);
+ return hit_info.res;
+ }
+
+ return res;
+}
+
+lv_coord_t lv_clamp_width(lv_coord_t width, lv_coord_t min_width, lv_coord_t max_width, lv_coord_t ref_width)
+{
+ if(LV_COORD_IS_PCT(min_width)) min_width = (ref_width * LV_COORD_GET_PCT(min_width)) / 100;
+ if(LV_COORD_IS_PCT(max_width)) max_width = (ref_width * LV_COORD_GET_PCT(max_width)) / 100;
+ return LV_CLAMP(min_width, width, max_width);
+}
+
+lv_coord_t lv_clamp_height(lv_coord_t height, lv_coord_t min_height, lv_coord_t max_height, lv_coord_t ref_height)
+{
+ if(LV_COORD_IS_PCT(min_height)) min_height = (ref_height * LV_COORD_GET_PCT(min_height)) / 100;
+ if(LV_COORD_IS_PCT(max_height)) max_height = (ref_height * LV_COORD_GET_PCT(max_height)) / 100;
+ return LV_CLAMP(min_height, height, max_height);
+}
+
+
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static lv_coord_t calc_content_width(lv_obj_t * obj)
+{
+ lv_obj_scroll_to_x(obj, 0, LV_ANIM_OFF);
+
+ lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
+ lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN) + border_width;
+ lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN) + border_width;
+
+ lv_coord_t self_w;
+ self_w = lv_obj_get_self_width(obj) + pad_left + pad_right;
+
+ lv_coord_t child_res = LV_COORD_MIN;
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ /*With RTL find the left most coordinate*/
+ if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+
+ if(!lv_obj_is_layout_positioned(child)) {
+ lv_align_t align = lv_obj_get_style_align(child, 0);
+ switch(align) {
+ case LV_ALIGN_DEFAULT:
+ case LV_ALIGN_TOP_RIGHT:
+ case LV_ALIGN_BOTTOM_RIGHT:
+ case LV_ALIGN_RIGHT_MID:
+ /*Normal right aligns. Other are ignored due to possible circular dependencies*/
+ child_res = LV_MAX(child_res, obj->coords.x2 - child->coords.x1 + 1);
+ break;
+ default:
+ /* Consider other cases only if x=0 and use the width of the object.
+ * With x!=0 circular dependency could occur. */
+ if(lv_obj_get_style_x(child, 0) == 0) {
+ child_res = LV_MAX(child_res, lv_area_get_width(&child->coords) + pad_right);
+ }
+ }
+ }
+ else {
+ child_res = LV_MAX(child_res, obj->coords.x2 - child->coords.x1 + 1);
+ }
+ }
+ if(child_res != LV_COORD_MIN) {
+ child_res += pad_left;
+ }
+ }
+ /*Else find the right most coordinate*/
+ else {
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+
+ if(!lv_obj_is_layout_positioned(child)) {
+ lv_align_t align = lv_obj_get_style_align(child, 0);
+ switch(align) {
+ case LV_ALIGN_DEFAULT:
+ case LV_ALIGN_TOP_LEFT:
+ case LV_ALIGN_BOTTOM_LEFT:
+ case LV_ALIGN_LEFT_MID:
+ /*Normal left aligns.*/
+ child_res = LV_MAX(child_res, child->coords.x2 - obj->coords.x1 + 1);
+ break;
+ default:
+ /* Consider other cases only if x=0 and use the width of the object.
+ * With x!=0 circular dependency could occur. */
+ if(lv_obj_get_style_y(child, 0) == 0) {
+ child_res = LV_MAX(child_res, lv_area_get_width(&child->coords) + pad_left);
+ }
+ }
+ }
+ else {
+ child_res = LV_MAX(child_res, child->coords.x2 - obj->coords.x1 + 1);
+ }
+ }
+
+ if(child_res != LV_COORD_MIN) {
+ child_res += pad_right;
+ }
+ }
+
+ if(child_res == LV_COORD_MIN) return self_w;
+ else return LV_MAX(child_res, self_w);
+}
+
+static lv_coord_t calc_content_height(lv_obj_t * obj)
+{
+ lv_obj_scroll_to_y(obj, 0, LV_ANIM_OFF);
+
+ lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
+ lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN) + border_width;
+ lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN) + border_width;
+
+ lv_coord_t self_h;
+ self_h = lv_obj_get_self_height(obj) + pad_top + pad_bottom;
+
+ lv_coord_t child_res = LV_COORD_MIN;
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+
+
+ if(!lv_obj_is_layout_positioned(child)) {
+ lv_align_t align = lv_obj_get_style_align(child, 0);
+ switch(align) {
+ case LV_ALIGN_DEFAULT:
+ case LV_ALIGN_TOP_RIGHT:
+ case LV_ALIGN_TOP_MID:
+ case LV_ALIGN_TOP_LEFT:
+ /*Normal top aligns. */
+ child_res = LV_MAX(child_res, child->coords.y2 - obj->coords.y1 + 1);
+ break;
+ default:
+ /* Consider other cases only if y=0 and use the height of the object.
+ * With y!=0 circular dependency could occur. */
+ if(lv_obj_get_style_y(child, 0) == 0) {
+ child_res = LV_MAX(child_res, lv_area_get_height(&child->coords) + pad_top);
+ }
+ break;
+ }
+ }
+ else {
+ child_res = LV_MAX(child_res, child->coords.y2 - obj->coords.y1 + 1);
+ }
+ }
+
+ if(child_res != LV_COORD_MIN) {
+ child_res += pad_bottom;
+ return LV_MAX(child_res, self_h);
+ }
+ else {
+ return self_h;
+ }
+
+}
+
+static void layout_update_core(lv_obj_t * obj)
+{
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ layout_update_core(child);
+ }
+
+ if(obj->layout_inv == 0) return;
+
+ obj->layout_inv = 0;
+
+ lv_obj_refr_size(obj);
+ lv_obj_refr_pos(obj);
+
+ if(child_cnt > 0) {
+ uint32_t layout_id = lv_obj_get_style_layout(obj, LV_PART_MAIN);
+ if(layout_id > 0 && layout_id <= layout_cnt) {
+ void * user_data = LV_GC_ROOT(_lv_layout_list)[layout_id - 1].user_data;
+ LV_GC_ROOT(_lv_layout_list)[layout_id - 1].cb(obj, user_data);
+ }
+ }
+}
+
+static void transform_point(const lv_obj_t * obj, lv_point_t * p, bool inv)
+{
+ int16_t angle = lv_obj_get_style_transform_angle(obj, 0);
+ int16_t zoom = lv_obj_get_style_transform_zoom(obj, 0);
+
+ if(angle == 0 && zoom == LV_IMG_ZOOM_NONE) return;
+
+ lv_point_t pivot;
+ pivot.x = obj->coords.x1 + lv_obj_get_style_transform_pivot_x(obj, 0);
+ pivot.y = obj->coords.y1 + lv_obj_get_style_transform_pivot_y(obj, 0);
+ if(inv) {
+ angle = -angle;
+ zoom = (256 * 256) / zoom;
+ }
+
+ lv_point_transform(p, angle, zoom, &pivot);
+}
diff --git a/lib/lvgl/src/core/lv_obj_pos.h b/lib/lvgl/src/core/lv_obj_pos.h
new file mode 100644
index 00000000..d20ee965
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_pos.h
@@ -0,0 +1,449 @@
+/**
+ * @file lv_obj_pos.h
+ *
+ */
+
+#ifndef LV_OBJ_POS_H
+#define LV_OBJ_POS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../misc/lv_area.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+struct _lv_obj_t;
+
+typedef void (*lv_layout_update_cb_t)(struct _lv_obj_t *, void * user_data);
+typedef struct {
+ lv_layout_update_cb_t cb;
+ void * user_data;
+} lv_layout_dsc_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Set the position of an object relative to the set alignment.
+ * @param obj pointer to an object
+ * @param x new x coordinate
+ * @param y new y coordinate
+ * @note With default alignment it's the distance from the top left corner
+ * @note E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
+ * @note The position is interpreted on the content area of the parent
+ * @note The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
+ */
+void lv_obj_set_pos(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
+
+/**
+ * Set the x coordinate of an object
+ * @param obj pointer to an object
+ * @param x new x coordinate
+ * @note With default alignment it's the distance from the top left corner
+ * @note E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
+ * @note The position is interpreted on the content area of the parent
+ * @note The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
+ */
+void lv_obj_set_x(struct _lv_obj_t * obj, lv_coord_t x);
+
+/**
+ * Set the y coordinate of an object
+ * @param obj pointer to an object
+ * @param y new y coordinate
+ * @note With default alignment it's the distance from the top left corner
+ * @note E.g. LV_ALIGN_CENTER alignment it's the offset from the center of the parent
+ * @note The position is interpreted on the content area of the parent
+ * @note The values can be set in pixel or in percentage of parent size with `lv_pct(v)`
+ */
+void lv_obj_set_y(struct _lv_obj_t * obj, lv_coord_t y);
+
+/**
+ * Set the size of an object.
+ * @param obj pointer to an object
+ * @param w the new width
+ * @param h the new height
+ * @note possible values are:
+ * pixel simple set the size accordingly
+ * LV_SIZE_CONTENT set the size to involve all children in the given direction
+ * LV_SIZE_PCT(x) to set size in percentage of the parent's content area size (the size without paddings).
+ * x should be in [0..1000]% range
+ */
+void lv_obj_set_size(struct _lv_obj_t * obj, lv_coord_t w, lv_coord_t h);
+
+/**
+ * Recalculate the size of the object
+ * @param obj pointer to an object
+ * @return true: the size has been changed
+ */
+bool lv_obj_refr_size(struct _lv_obj_t * obj);
+
+/**
+ * Set the width of an object
+ * @param obj pointer to an object
+ * @param w the new width
+ * @note possible values are:
+ * pixel simple set the size accordingly
+ * LV_SIZE_CONTENT set the size to involve all children in the given direction
+ * lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
+ * x should be in [0..1000]% range
+ */
+void lv_obj_set_width(struct _lv_obj_t * obj, lv_coord_t w);
+
+/**
+ * Set the height of an object
+ * @param obj pointer to an object
+ * @param h the new height
+ * @note possible values are:
+ * pixel simple set the size accordingly
+ * LV_SIZE_CONTENT set the size to involve all children in the given direction
+ * lv_pct(x) to set size in percentage of the parent's content area size (the size without paddings).
+ * x should be in [0..1000]% range
+ */
+void lv_obj_set_height(struct _lv_obj_t * obj, lv_coord_t h);
+
+/**
+ * Set the width reduced by the left and right padding and the border width.
+ * @param obj pointer to an object
+ * @param w the width without paddings in pixels
+ */
+void lv_obj_set_content_width(struct _lv_obj_t * obj, lv_coord_t w);
+
+/**
+ * Set the height reduced by the top and bottom padding and the border width.
+ * @param obj pointer to an object
+ * @param h the height without paddings in pixels
+ */
+void lv_obj_set_content_height(struct _lv_obj_t * obj, lv_coord_t h);
+
+/**
+ * Set a layout for an object
+ * @param obj pointer to an object
+ * @param layout pointer to a layout descriptor to set
+ */
+void lv_obj_set_layout(struct _lv_obj_t * obj, uint32_t layout);
+
+/**
+ * Test whether the and object is positioned by a layout or not
+ * @param obj pointer to an object to test
+ * @return true: positioned by a layout; false: not positioned by a layout
+ */
+bool lv_obj_is_layout_positioned(const struct _lv_obj_t * obj);
+
+/**
+ * Mark the object for layout update.
+ * @param obj pointer to an object whose children needs to be updated
+ */
+void lv_obj_mark_layout_as_dirty(struct _lv_obj_t * obj);
+
+/**
+ * Update the layout of an object.
+ * @param obj pointer to an object whose children needs to be updated
+ */
+void lv_obj_update_layout(const struct _lv_obj_t * obj);
+
+/**
+ * Register a new layout
+ * @param cb the layout update callback
+ * @param user_data custom data that will be passed to `cb`
+ * @return the ID of the new layout
+ */
+uint32_t lv_layout_register(lv_layout_update_cb_t cb, void * user_data);
+
+/**
+ * Change the alignment of an object.
+ * @param obj pointer to an object to align
+ * @param align type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
+ */
+void lv_obj_set_align(struct _lv_obj_t * obj, lv_align_t align);
+
+/**
+ * Change the alignment of an object and set new coordinates.
+ * Equivalent to:
+ * lv_obj_set_align(obj, align);
+ * lv_obj_set_pos(obj, x_ofs, y_ofs);
+ * @param obj pointer to an object to align
+ * @param align type of alignment (see 'lv_align_t' enum) `LV_ALIGN_OUT_...` can't be used.
+ * @param x_ofs x coordinate offset after alignment
+ * @param y_ofs y coordinate offset after alignment
+ */
+void lv_obj_align(struct _lv_obj_t * obj, lv_align_t align, lv_coord_t x_ofs, lv_coord_t y_ofs);
+
+/**
+ * Align an object to an other object.
+ * @param obj pointer to an object to align
+ * @param base pointer to an other object (if NULL `obj`s parent is used). 'obj' will be aligned to it.
+ * @param align type of alignment (see 'lv_align_t' enum)
+ * @param x_ofs x coordinate offset after alignment
+ * @param y_ofs y coordinate offset after alignment
+ * @note if the position or size of `base` changes `obj` needs to be aligned manually again
+ */
+void lv_obj_align_to(struct _lv_obj_t * obj, const struct _lv_obj_t * base, lv_align_t align, lv_coord_t x_ofs,
+ lv_coord_t y_ofs);
+
+/**
+ * Align an object to the center on its parent.
+ * @param obj pointer to an object to align
+ * @note if the parent size changes `obj` needs to be aligned manually again
+ */
+static inline void lv_obj_center(struct _lv_obj_t * obj)
+{
+ lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
+}
+
+
+/**
+ * Copy the coordinates of an object to an area
+ * @param obj pointer to an object
+ * @param coords pointer to an area to store the coordinates
+ */
+void lv_obj_get_coords(const struct _lv_obj_t * obj, lv_area_t * coords);
+
+/**
+ * Get the x coordinate of object.
+ * @param obj pointer to an object
+ * @return distance of `obj` from the left side of its parent plus the parent's left padding
+ * @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ * call `lv_obj_update_layout(obj)`.
+ * @note Zero return value means the object is on the left padding of the parent, and not on the left edge.
+ * @note Scrolling of the parent doesn't change the returned value.
+ * @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
+ */
+lv_coord_t lv_obj_get_x(const struct _lv_obj_t * obj);
+
+/**
+ * Get the x2 coordinate of object.
+ * @param obj pointer to an object
+ * @return distance of `obj` from the right side of its parent plus the parent's right padding
+ * @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ * call `lv_obj_update_layout(obj)`.
+ * @note Zero return value means the object is on the right padding of the parent, and not on the right edge.
+ * @note Scrolling of the parent doesn't change the returned value.
+ * @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
+ */
+lv_coord_t lv_obj_get_x2(const struct _lv_obj_t * obj);
+
+/**
+ * Get the y coordinate of object.
+ * @param obj pointer to an object
+ * @return distance of `obj` from the top side of its parent plus the parent's top padding
+ * @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ * call `lv_obj_update_layout(obj)`.
+ * @note Zero return value means the object is on the top padding of the parent, and not on the top edge.
+ * @note Scrolling of the parent doesn't change the returned value.
+ * @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
+ */
+lv_coord_t lv_obj_get_y(const struct _lv_obj_t * obj);
+
+/**
+ * Get the y2 coordinate of object.
+ * @param obj pointer to an object
+ * @return distance of `obj` from the bottom side of its parent plus the parent's bottom padding
+ * @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ * call `lv_obj_update_layout(obj)`.
+ * @note Zero return value means the object is on the bottom padding of the parent, and not on the bottom edge.
+ * @note Scrolling of the parent doesn't change the returned value.
+ * @note The returned value is always the distance from the parent even if `obj` is positioned by a layout.
+ */
+lv_coord_t lv_obj_get_y2(const struct _lv_obj_t * obj);
+
+/**
+ * Get the actually set x coordinate of object, i.e. the offset form the set alignment
+ * @param obj pointer to an object
+ * @return the set x coordinate
+ */
+lv_coord_t lv_obj_get_x_aligned(const struct _lv_obj_t * obj);
+
+/**
+ * Get the actually set y coordinate of object, i.e. the offset form the set alignment
+ * @param obj pointer to an object
+ * @return the set y coordinate
+ */
+lv_coord_t lv_obj_get_y_aligned(const struct _lv_obj_t * obj);
+
+/**
+ * Get the width of an object
+ * @param obj pointer to an object
+ * @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ * call `lv_obj_update_layout(obj)`.
+ * @return the width in pixels
+ */
+lv_coord_t lv_obj_get_width(const struct _lv_obj_t * obj);
+
+/**
+ * Get the height of an object
+ * @param obj pointer to an object
+ * @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ * call `lv_obj_update_layout(obj)`.
+ * @return the height in pixels
+ */
+lv_coord_t lv_obj_get_height(const struct _lv_obj_t * obj);
+
+/**
+ * Get the width reduced by the left and right padding and the border width.
+ * @param obj pointer to an object
+ * @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ * call `lv_obj_update_layout(obj)`.
+ * @return the width which still fits into its parent without causing overflow (making the parent scrollable)
+ */
+lv_coord_t lv_obj_get_content_width(const struct _lv_obj_t * obj);
+
+/**
+ * Get the height reduced by the top and bottom padding and the border width.
+ * @param obj pointer to an object
+ * @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ * call `lv_obj_update_layout(obj)`.
+ * @return the height which still fits into the parent without causing overflow (making the parent scrollable)
+ */
+lv_coord_t lv_obj_get_content_height(const struct _lv_obj_t * obj);
+
+/**
+ * Get the area reduced by the paddings and the border width.
+ * @param obj pointer to an object
+ * @note The position of the object is recalculated only on the next redraw. To force coordinate recalculation
+ * call `lv_obj_update_layout(obj)`.
+ * @param area the area which still fits into the parent without causing overflow (making the parent scrollable)
+ */
+void lv_obj_get_content_coords(const struct _lv_obj_t * obj, lv_area_t * area);
+
+/**
+ * Get the width occupied by the "parts" of the widget. E.g. the width of all columns of a table.
+ * @param obj pointer to an objects
+ * @return the width of the virtually drawn content
+ * @note This size independent from the real size of the widget.
+ * It just tells how large the internal ("virtual") content is.
+ */
+lv_coord_t lv_obj_get_self_width(const struct _lv_obj_t * obj);
+
+/**
+ * Get the height occupied by the "parts" of the widget. E.g. the height of all rows of a table.
+ * @param obj pointer to an objects
+ * @return the width of the virtually drawn content
+ * @note This size independent from the real size of the widget.
+ * It just tells how large the internal ("virtual") content is.
+ */
+lv_coord_t lv_obj_get_self_height(const struct _lv_obj_t * obj);
+
+/**
+ * Handle if the size of the internal ("virtual") content of an object has changed.
+ * @param obj pointer to an object
+ * @return false: nothing happened; true: refresh happened
+ */
+bool lv_obj_refresh_self_size(struct _lv_obj_t * obj);
+
+void lv_obj_refr_pos(struct _lv_obj_t * obj);
+
+void lv_obj_move_to(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
+
+
+void lv_obj_move_children_by(struct _lv_obj_t * obj, lv_coord_t x_diff, lv_coord_t y_diff, bool ignore_floating);
+
+/**
+ * Transform a point using the angle and zoom style properties of an object
+ * @param obj pointer to an object whose style properties should be used
+ * @param p a point to transform, the result will be written back here too
+ * @param recursive consider the transformation properties of the parents too
+ * @param inv do the inverse of the transformation (-angle and 1/zoom)
+ */
+void lv_obj_transform_point(const struct _lv_obj_t * obj, lv_point_t * p, bool recursive, bool inv);
+
+/**
+ * Transform an area using the angle and zoom style properties of an object
+ * @param obj pointer to an object whose style properties should be used
+ * @param area an area to transform, the result will be written back here too
+ * @param recursive consider the transformation properties of the parents too
+ * @param inv do the inverse of the transformation (-angle and 1/zoom)
+ */
+void lv_obj_get_transformed_area(const struct _lv_obj_t * obj, lv_area_t * area, bool recursive, bool inv);
+
+/**
+ * Mark an area of an object as invalid.
+ * The area will be truncated to the object's area and marked for redraw.
+ * @param obj pointer to an object
+ * @param area the area to redraw
+ */
+void lv_obj_invalidate_area(const struct _lv_obj_t * obj, const lv_area_t * area);
+
+/**
+ * Mark the object as invalid to redrawn its area
+ * @param obj pointer to an object
+ */
+void lv_obj_invalidate(const struct _lv_obj_t * obj);
+
+/**
+ * Tell whether an area of an object is visible (even partially) now or not
+ * @param obj pointer to an object
+ * @param area the are to check. The visible part of the area will be written back here.
+ * @return true visible; false not visible (hidden, out of parent, on other screen, etc)
+ */
+bool lv_obj_area_is_visible(const struct _lv_obj_t * obj, lv_area_t * area);
+
+/**
+ * Tell whether an object is visible (even partially) now or not
+ * @param obj pointer to an object
+ * @return true: visible; false not visible (hidden, out of parent, on other screen, etc)
+ */
+bool lv_obj_is_visible(const struct _lv_obj_t * obj);
+
+/**
+ * Set the size of an extended clickable area
+ * @param obj pointer to an object
+ * @param size extended clickable area in all 4 directions [px]
+ */
+void lv_obj_set_ext_click_area(struct _lv_obj_t * obj, lv_coord_t size);
+
+/**
+ * Get the an area where to object can be clicked.
+ * It's the object's normal area plus the extended click area.
+ * @param obj pointer to an object
+ * @param area store the result area here
+ */
+void lv_obj_get_click_area(const struct _lv_obj_t * obj, lv_area_t * area);
+
+/**
+ * Hit-test an object given a particular point in screen space.
+ * @param obj object to hit-test
+ * @param point screen-space point (absolute coordinate)
+ * @return true: if the object is considered under the point
+ */
+bool lv_obj_hit_test(struct _lv_obj_t * obj, const lv_point_t * point);
+
+/**
+ * Clamp a width between min and max width. If the min/max width is in percentage value use the ref_width
+ * @param width width to clamp
+ * @param min_width the minimal width
+ * @param max_width the maximal width
+ * @param ref_width the reference width used when min/max width is in percentage
+ * @return the clamped width
+ */
+lv_coord_t lv_clamp_width(lv_coord_t width, lv_coord_t min_width, lv_coord_t max_width, lv_coord_t ref_width);
+
+/**
+ * Clamp a height between min and max height. If the min/max height is in percentage value use the ref_height
+ * @param height height to clamp
+ * @param min_height the minimal height
+ * @param max_height the maximal height
+ * @param ref_height the reference height used when min/max height is in percentage
+ * @return the clamped height
+ */
+lv_coord_t lv_clamp_height(lv_coord_t height, lv_coord_t min_height, lv_coord_t max_height, lv_coord_t ref_height);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_POS_H*/
diff --git a/lib/lvgl/src/core/lv_obj_scroll.c b/lib/lvgl/src/core/lv_obj_scroll.c
new file mode 100644
index 00000000..02b98261
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_scroll.c
@@ -0,0 +1,800 @@
+/**
+ * @file lv_obj_scroll.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj_scroll.h"
+#include "lv_obj.h"
+#include "lv_indev.h"
+#include "lv_disp.h"
+#include "lv_indev_scroll.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_obj_class
+#define SCROLL_ANIM_TIME_MIN 200 /*ms*/
+#define SCROLL_ANIM_TIME_MAX 400 /*ms*/
+#define SCROLLBAR_MIN_SIZE (LV_DPX(10))
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void scroll_x_anim(void * obj, int32_t v);
+static void scroll_y_anim(void * obj, int32_t v);
+static void scroll_anim_ready_cb(lv_anim_t * a);
+static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
+ lv_anim_enable_t anim_en);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+void lv_obj_set_scrollbar_mode(lv_obj_t * obj, lv_scrollbar_mode_t mode)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_obj_allocate_spec_attr(obj);
+
+ if(obj->spec_attr->scrollbar_mode == mode) return;
+ obj->spec_attr->scrollbar_mode = mode;
+ lv_obj_invalidate(obj);
+}
+
+void lv_obj_set_scroll_dir(lv_obj_t * obj, lv_dir_t dir)
+{
+ lv_obj_allocate_spec_attr(obj);
+
+ if(dir != obj->spec_attr->scroll_dir) {
+ obj->spec_attr->scroll_dir = dir;
+ }
+}
+
+void lv_obj_set_scroll_snap_x(lv_obj_t * obj, lv_scroll_snap_t align)
+{
+ lv_obj_allocate_spec_attr(obj);
+ obj->spec_attr->scroll_snap_x = align;
+}
+
+void lv_obj_set_scroll_snap_y(lv_obj_t * obj, lv_scroll_snap_t align)
+{
+ lv_obj_allocate_spec_attr(obj);
+ obj->spec_attr->scroll_snap_y = align;
+}
+
+/*=====================
+ * Getter functions
+ *====================*/
+
+lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const lv_obj_t * obj)
+{
+ if(obj->spec_attr) return obj->spec_attr->scrollbar_mode;
+ else return LV_SCROLLBAR_MODE_AUTO;
+}
+
+lv_dir_t lv_obj_get_scroll_dir(const lv_obj_t * obj)
+{
+ if(obj->spec_attr) return obj->spec_attr->scroll_dir;
+ else return LV_DIR_ALL;
+}
+
+lv_scroll_snap_t lv_obj_get_scroll_snap_x(const lv_obj_t * obj)
+{
+ if(obj->spec_attr) return obj->spec_attr->scroll_snap_x;
+ else return LV_SCROLL_SNAP_NONE;
+}
+
+lv_scroll_snap_t lv_obj_get_scroll_snap_y(const lv_obj_t * obj)
+{
+ if(obj->spec_attr) return obj->spec_attr->scroll_snap_y;
+ else return LV_SCROLL_SNAP_NONE;
+}
+
+lv_coord_t lv_obj_get_scroll_x(const lv_obj_t * obj)
+{
+ if(obj->spec_attr == NULL) return 0;
+ return -obj->spec_attr->scroll.x;
+}
+
+lv_coord_t lv_obj_get_scroll_y(const lv_obj_t * obj)
+{
+ if(obj->spec_attr == NULL) return 0;
+ return -obj->spec_attr->scroll.y;
+}
+
+lv_coord_t lv_obj_get_scroll_top(lv_obj_t * obj)
+{
+ if(obj->spec_attr == NULL) return 0;
+ return -obj->spec_attr->scroll.y;
+}
+
+lv_coord_t lv_obj_get_scroll_bottom(lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_coord_t child_res = LV_COORD_MIN;
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+ child_res = LV_MAX(child_res, child->coords.y2);
+ }
+
+ lv_coord_t pad_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
+ lv_coord_t pad_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
+ lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
+
+ if(child_res != LV_COORD_MIN) {
+ child_res -= (obj->coords.y2 - pad_bottom - border_width);
+ }
+
+ lv_coord_t self_h = lv_obj_get_self_height(obj);
+ self_h = self_h - (lv_obj_get_height(obj) - pad_top - pad_bottom - 2 * border_width);
+ self_h -= lv_obj_get_scroll_y(obj);
+ return LV_MAX(child_res, self_h);
+}
+
+lv_coord_t lv_obj_get_scroll_left(lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ /*Normally can't scroll the object out on the left.
+ *So simply use the current scroll position as "left size"*/
+ if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
+ if(obj->spec_attr == NULL) return 0;
+ return -obj->spec_attr->scroll.x;
+ }
+
+ /*With RTL base direction scrolling the left is normal so find the left most coordinate*/
+ lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
+ lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
+ lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
+
+ lv_coord_t child_res = 0;
+
+ uint32_t i;
+ lv_coord_t x1 = LV_COORD_MAX;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+ x1 = LV_MIN(x1, child->coords.x1);
+
+ }
+
+ if(x1 != LV_COORD_MAX) {
+ child_res = x1;
+ child_res = (obj->coords.x1 + pad_left + border_width) - child_res;
+ }
+ else {
+ child_res = LV_COORD_MIN;
+ }
+
+ lv_coord_t self_w = lv_obj_get_self_width(obj);
+ self_w = self_w - (lv_obj_get_width(obj) - pad_right - pad_left - 2 * border_width);
+ self_w += lv_obj_get_scroll_x(obj);
+
+ return LV_MAX(child_res, self_w);
+}
+
+lv_coord_t lv_obj_get_scroll_right(lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ /*With RTL base dir can't scroll to the object out on the right.
+ *So simply use the current scroll position as "right size"*/
+ if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
+ if(obj->spec_attr == NULL) return 0;
+ return obj->spec_attr->scroll.x;
+ }
+
+ /*With other base direction (LTR) scrolling to the right is normal so find the right most coordinate*/
+ lv_coord_t child_res = LV_COORD_MIN;
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ if(lv_obj_has_flag_any(child, LV_OBJ_FLAG_HIDDEN | LV_OBJ_FLAG_FLOATING)) continue;
+ child_res = LV_MAX(child_res, child->coords.x2);
+ }
+
+ lv_coord_t pad_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
+ lv_coord_t pad_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
+ lv_coord_t border_width = lv_obj_get_style_border_width(obj, LV_PART_MAIN);
+
+ if(child_res != LV_COORD_MIN) {
+ child_res -= (obj->coords.x2 - pad_right - border_width);
+ }
+
+ lv_coord_t self_w;
+ self_w = lv_obj_get_self_width(obj);
+ self_w = self_w - (lv_obj_get_width(obj) - pad_right - pad_left - 2 * border_width);
+ self_w -= lv_obj_get_scroll_x(obj);
+ return LV_MAX(child_res, self_w);
+}
+
+void lv_obj_get_scroll_end(struct _lv_obj_t * obj, lv_point_t * end)
+{
+ lv_anim_t * a;
+ a = lv_anim_get(obj, scroll_x_anim);
+ end->x = a ? -a->end_value : lv_obj_get_scroll_x(obj);
+
+ a = lv_anim_get(obj, scroll_y_anim);
+ end->y = a ? -a->end_value : lv_obj_get_scroll_y(obj);
+}
+
+/*=====================
+ * Other functions
+ *====================*/
+
+void lv_obj_scroll_by_bounded(lv_obj_t * obj, lv_coord_t dx, lv_coord_t dy, lv_anim_enable_t anim_en)
+{
+ if(dx == 0 && dy == 0) return;
+
+ /*We need to know the final sizes for bound check*/
+ lv_obj_update_layout(obj);
+
+ /*Don't let scroll more then naturally possible by the size of the content*/
+ lv_coord_t x_current = -lv_obj_get_scroll_x(obj);
+ lv_coord_t x_bounded = x_current + dx;
+
+ if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
+ if(x_bounded > 0) x_bounded = 0;
+ if(x_bounded < 0) {
+ lv_coord_t scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
+ if(scroll_max < 0) scroll_max = 0;
+
+ if(x_bounded < -scroll_max) x_bounded = -scroll_max;
+ }
+ }
+ else {
+ if(x_bounded < 0) x_bounded = 0;
+ if(x_bounded > 0) {
+ lv_coord_t scroll_max = lv_obj_get_scroll_left(obj) + lv_obj_get_scroll_right(obj);
+ if(scroll_max < 0) scroll_max = 0;
+
+ if(x_bounded > scroll_max) x_bounded = scroll_max;
+ }
+ }
+
+ /*Don't let scroll more then naturally possible by the size of the content*/
+ lv_coord_t y_current = -lv_obj_get_scroll_y(obj);
+ lv_coord_t y_bounded = y_current + dy;
+
+ if(y_bounded > 0) y_bounded = 0;
+ if(y_bounded < 0) {
+ lv_coord_t scroll_max = lv_obj_get_scroll_top(obj) + lv_obj_get_scroll_bottom(obj);
+ if(scroll_max < 0) scroll_max = 0;
+ if(y_bounded < -scroll_max) y_bounded = -scroll_max;
+ }
+
+ dx = x_bounded - x_current;
+ dy = y_bounded - y_current;
+ if(dx || dy) {
+ lv_obj_scroll_by(obj, dx, dy, anim_en);
+ }
+}
+
+
+void lv_obj_scroll_by(lv_obj_t * obj, lv_coord_t dx, lv_coord_t dy, lv_anim_enable_t anim_en)
+{
+ if(dx == 0 && dy == 0) return;
+ if(anim_en == LV_ANIM_ON) {
+ lv_disp_t * d = lv_obj_get_disp(obj);
+ lv_anim_t a;
+ lv_anim_init(&a);
+ lv_anim_set_var(&a, obj);
+ lv_anim_set_ready_cb(&a, scroll_anim_ready_cb);
+
+ if(dx) {
+ uint32_t t = lv_anim_speed_to_time((lv_disp_get_hor_res(d) * 2) >> 2, 0, dx);
+ if(t < SCROLL_ANIM_TIME_MIN) t = SCROLL_ANIM_TIME_MIN;
+ if(t > SCROLL_ANIM_TIME_MAX) t = SCROLL_ANIM_TIME_MAX;
+ lv_anim_set_time(&a, t);
+ lv_coord_t sx = lv_obj_get_scroll_x(obj);
+ lv_anim_set_values(&a, -sx, -sx + dx);
+ lv_anim_set_exec_cb(&a, scroll_x_anim);
+ lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
+
+ lv_res_t res;
+ res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, &a);
+ if(res != LV_RES_OK) return;
+ lv_anim_start(&a);
+ }
+
+ if(dy) {
+ uint32_t t = lv_anim_speed_to_time((lv_disp_get_ver_res(d) * 2) >> 2, 0, dy);
+ if(t < SCROLL_ANIM_TIME_MIN) t = SCROLL_ANIM_TIME_MIN;
+ if(t > SCROLL_ANIM_TIME_MAX) t = SCROLL_ANIM_TIME_MAX;
+ lv_anim_set_time(&a, t);
+ lv_coord_t sy = lv_obj_get_scroll_y(obj);
+ lv_anim_set_values(&a, -sy, -sy + dy);
+ lv_anim_set_exec_cb(&a, scroll_y_anim);
+ lv_anim_set_path_cb(&a, lv_anim_path_ease_out);
+
+ lv_res_t res;
+ res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, &a);
+ if(res != LV_RES_OK) return;
+ lv_anim_start(&a);
+ }
+ }
+ else {
+ /*Remove pending animations*/
+ lv_anim_del(obj, scroll_y_anim);
+ lv_anim_del(obj, scroll_x_anim);
+
+ lv_res_t res;
+ res = lv_event_send(obj, LV_EVENT_SCROLL_BEGIN, NULL);
+ if(res != LV_RES_OK) return;
+
+ res = _lv_obj_scroll_by_raw(obj, dx, dy);
+ if(res != LV_RES_OK) return;
+
+ res = lv_event_send(obj, LV_EVENT_SCROLL_END, NULL);
+ if(res != LV_RES_OK) return;
+ }
+}
+
+void lv_obj_scroll_to(lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en)
+{
+ lv_obj_scroll_to_x(obj, x, anim_en);
+ lv_obj_scroll_to_y(obj, y, anim_en);
+}
+
+void lv_obj_scroll_to_x(lv_obj_t * obj, lv_coord_t x, lv_anim_enable_t anim_en)
+{
+ lv_anim_del(obj, scroll_x_anim);
+
+ lv_coord_t scroll_x = lv_obj_get_scroll_x(obj);
+ lv_coord_t diff = -x + scroll_x;
+
+ lv_obj_scroll_by_bounded(obj, diff, 0, anim_en);
+}
+
+void lv_obj_scroll_to_y(lv_obj_t * obj, lv_coord_t y, lv_anim_enable_t anim_en)
+{
+ lv_anim_del(obj, scroll_y_anim);
+
+ lv_coord_t scroll_y = lv_obj_get_scroll_y(obj);
+ lv_coord_t diff = -y + scroll_y;
+
+ lv_obj_scroll_by_bounded(obj, 0, diff, anim_en);
+}
+
+void lv_obj_scroll_to_view(lv_obj_t * obj, lv_anim_enable_t anim_en)
+{
+ /*Be sure the screens layout is correct*/
+ lv_obj_update_layout(obj);
+
+ lv_point_t p = {0, 0};
+ scroll_area_into_view(&obj->coords, obj, &p, anim_en);
+}
+
+void lv_obj_scroll_to_view_recursive(lv_obj_t * obj, lv_anim_enable_t anim_en)
+{
+ /*Be sure the screens layout is correct*/
+ lv_obj_update_layout(obj);
+
+ lv_point_t p = {0, 0};
+ lv_obj_t * child = obj;
+ lv_obj_t * parent = lv_obj_get_parent(child);
+ while(parent) {
+ scroll_area_into_view(&obj->coords, child, &p, anim_en);
+ child = parent;
+ parent = lv_obj_get_parent(parent);
+ }
+}
+
+lv_res_t _lv_obj_scroll_by_raw(lv_obj_t * obj, lv_coord_t x, lv_coord_t y)
+{
+ if(x == 0 && y == 0) return LV_RES_OK;
+
+ lv_obj_allocate_spec_attr(obj);
+
+ obj->spec_attr->scroll.x += x;
+ obj->spec_attr->scroll.y += y;
+
+ lv_obj_move_children_by(obj, x, y, true);
+ lv_res_t res = lv_event_send(obj, LV_EVENT_SCROLL, NULL);
+ if(res != LV_RES_OK) return res;
+ lv_obj_invalidate(obj);
+ return LV_RES_OK;
+}
+
+
+bool lv_obj_is_scrolling(const lv_obj_t * obj)
+{
+ lv_indev_t * indev = lv_indev_get_next(NULL);
+ while(indev) {
+ if(lv_indev_get_scroll_obj(indev) == obj) return true;
+ indev = lv_indev_get_next(indev);
+ }
+
+ return false;
+}
+
+void lv_obj_update_snap(lv_obj_t * obj, lv_anim_enable_t anim_en)
+{
+ lv_obj_update_layout(obj);
+ lv_point_t p;
+ lv_indev_scroll_get_snap_dist(obj, &p);
+ lv_obj_scroll_by(obj, p.x, p.y, anim_en);
+}
+
+void lv_obj_get_scrollbar_area(lv_obj_t * obj, lv_area_t * hor_area, lv_area_t * ver_area)
+{
+ lv_area_set(hor_area, 0, 0, -1, -1);
+ lv_area_set(ver_area, 0, 0, -1, -1);
+
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE) == false) return;
+
+ lv_dir_t sm = lv_obj_get_scrollbar_mode(obj);
+ if(sm == LV_SCROLLBAR_MODE_OFF) return;
+
+ /*If there is no indev scrolling this object but `mode==active` return*/
+ lv_indev_t * indev = lv_indev_get_next(NULL);
+ if(sm == LV_SCROLLBAR_MODE_ACTIVE) {
+ while(indev) {
+ if(lv_indev_get_scroll_obj(indev) == obj) break;
+ indev = lv_indev_get_next(indev);
+ }
+ if(indev == NULL) return;
+ }
+
+ lv_coord_t st = lv_obj_get_scroll_top(obj);
+ lv_coord_t sb = lv_obj_get_scroll_bottom(obj);
+ lv_coord_t sl = lv_obj_get_scroll_left(obj);
+ lv_coord_t sr = lv_obj_get_scroll_right(obj);
+
+ lv_dir_t dir = lv_obj_get_scroll_dir(obj);
+
+ bool ver_draw = false;
+ if((dir & LV_DIR_VER) &&
+ ((sm == LV_SCROLLBAR_MODE_ON) ||
+ (sm == LV_SCROLLBAR_MODE_AUTO && (st > 0 || sb > 0)) ||
+ (sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_VER))) {
+ ver_draw = true;
+ }
+
+
+ bool hor_draw = false;
+ if((dir & LV_DIR_HOR) &&
+ ((sm == LV_SCROLLBAR_MODE_ON) ||
+ (sm == LV_SCROLLBAR_MODE_AUTO && (sl > 0 || sr > 0)) ||
+ (sm == LV_SCROLLBAR_MODE_ACTIVE && lv_indev_get_scroll_dir(indev) == LV_DIR_HOR))) {
+ hor_draw = true;
+ }
+
+ if(!hor_draw && !ver_draw) return;
+
+ bool rtl = lv_obj_get_style_base_dir(obj, LV_PART_SCROLLBAR) == LV_BASE_DIR_RTL ? true : false;
+
+ lv_coord_t top_space = lv_obj_get_style_pad_top(obj, LV_PART_SCROLLBAR);
+ lv_coord_t bottom_space = lv_obj_get_style_pad_bottom(obj, LV_PART_SCROLLBAR);
+ lv_coord_t left_space = lv_obj_get_style_pad_left(obj, LV_PART_SCROLLBAR);
+ lv_coord_t right_space = lv_obj_get_style_pad_right(obj, LV_PART_SCROLLBAR);
+ lv_coord_t tickness = lv_obj_get_style_width(obj, LV_PART_SCROLLBAR);
+
+ lv_coord_t obj_h = lv_obj_get_height(obj);
+ lv_coord_t obj_w = lv_obj_get_width(obj);
+
+ /*Space required for the vertical and horizontal scrollbars*/
+ lv_coord_t ver_reg_space = ver_draw ? tickness : 0;
+ lv_coord_t hor_req_space = hor_draw ? tickness : 0;
+ lv_coord_t rem;
+
+ if(lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR) < LV_OPA_MIN &&
+ lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR) < LV_OPA_MIN) {
+ return;
+ }
+
+ /*Draw vertical scrollbar if the mode is ON or can be scrolled in this direction*/
+ lv_coord_t content_h = obj_h + st + sb;
+ if(ver_draw && content_h) {
+ ver_area->y1 = obj->coords.y1;
+ ver_area->y2 = obj->coords.y2;
+ if(rtl) {
+ ver_area->x1 = obj->coords.x1 + left_space;
+ ver_area->x2 = ver_area->x1 + tickness - 1;
+ }
+ else {
+ ver_area->x2 = obj->coords.x2 - right_space;
+ ver_area->x1 = ver_area->x2 - tickness + 1;
+ }
+
+ lv_coord_t sb_h = ((obj_h - top_space - bottom_space - hor_req_space) * obj_h) / content_h;
+ sb_h = LV_MAX(sb_h, SCROLLBAR_MIN_SIZE);
+ rem = (obj_h - top_space - bottom_space - hor_req_space) -
+ sb_h; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
+ lv_coord_t scroll_h = content_h - obj_h; /*The size of the content which can be really scrolled*/
+ if(scroll_h <= 0) {
+ ver_area->y1 = obj->coords.y1 + top_space;
+ ver_area->y2 = obj->coords.y2 - bottom_space - hor_req_space - 1;
+ }
+ else {
+ lv_coord_t sb_y = (rem * sb) / scroll_h;
+ sb_y = rem - sb_y;
+
+ ver_area->y1 = obj->coords.y1 + sb_y + top_space;
+ ver_area->y2 = ver_area->y1 + sb_h - 1;
+ if(ver_area->y1 < obj->coords.y1 + top_space) {
+ ver_area->y1 = obj->coords.y1 + top_space;
+ if(ver_area->y1 + SCROLLBAR_MIN_SIZE > ver_area->y2) {
+ ver_area->y2 = ver_area->y1 + SCROLLBAR_MIN_SIZE;
+ }
+ }
+ if(ver_area->y2 > obj->coords.y2 - hor_req_space - bottom_space) {
+ ver_area->y2 = obj->coords.y2 - hor_req_space - bottom_space;
+ if(ver_area->y2 - SCROLLBAR_MIN_SIZE < ver_area->y1) {
+ ver_area->y1 = ver_area->y2 - SCROLLBAR_MIN_SIZE;
+ }
+ }
+ }
+ }
+
+ /*Draw horizontal scrollbar if the mode is ON or can be scrolled in this direction*/
+ lv_coord_t content_w = obj_w + sl + sr;
+ if(hor_draw && content_w) {
+ hor_area->y2 = obj->coords.y2 - bottom_space;
+ hor_area->y1 = hor_area->y2 - tickness + 1;
+ hor_area->x1 = obj->coords.x1;
+ hor_area->x2 = obj->coords.x2;
+
+ lv_coord_t sb_w = ((obj_w - left_space - right_space - ver_reg_space) * obj_w) / content_w;
+ sb_w = LV_MAX(sb_w, SCROLLBAR_MIN_SIZE);
+ rem = (obj_w - left_space - right_space - ver_reg_space) -
+ sb_w; /*Remaining size from the scrollbar track that is not the scrollbar itself*/
+ lv_coord_t scroll_w = content_w - obj_w; /*The size of the content which can be really scrolled*/
+ if(scroll_w <= 0) {
+ if(rtl) {
+ hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space - 1;
+ hor_area->x2 = obj->coords.x2 - right_space;
+ }
+ else {
+ hor_area->x1 = obj->coords.x1 + left_space;
+ hor_area->x2 = obj->coords.x2 - right_space - ver_reg_space - 1;
+ }
+ }
+ else {
+ lv_coord_t sb_x = (rem * sr) / scroll_w;
+ sb_x = rem - sb_x;
+
+ if(rtl) {
+ hor_area->x1 = obj->coords.x1 + sb_x + left_space + ver_reg_space;
+ hor_area->x2 = hor_area->x1 + sb_w - 1;
+ if(hor_area->x1 < obj->coords.x1 + left_space + ver_reg_space) {
+ hor_area->x1 = obj->coords.x1 + left_space + ver_reg_space;
+ if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
+ hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
+ }
+ }
+ if(hor_area->x2 > obj->coords.x2 - right_space) {
+ hor_area->x2 = obj->coords.x2 - right_space;
+ if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
+ hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
+ }
+ }
+ }
+ else {
+ hor_area->x1 = obj->coords.x1 + sb_x + left_space;
+ hor_area->x2 = hor_area->x1 + sb_w - 1;
+ if(hor_area->x1 < obj->coords.x1 + left_space) {
+ hor_area->x1 = obj->coords.x1 + left_space;
+ if(hor_area->x1 + SCROLLBAR_MIN_SIZE > hor_area->x2) {
+ hor_area->x2 = hor_area->x1 + SCROLLBAR_MIN_SIZE;
+ }
+ }
+ if(hor_area->x2 > obj->coords.x2 - ver_reg_space - right_space) {
+ hor_area->x2 = obj->coords.x2 - ver_reg_space - right_space;
+ if(hor_area->x2 - SCROLLBAR_MIN_SIZE < hor_area->x1) {
+ hor_area->x1 = hor_area->x2 - SCROLLBAR_MIN_SIZE;
+ }
+ }
+ }
+ }
+ }
+}
+
+void lv_obj_scrollbar_invalidate(lv_obj_t * obj)
+{
+ lv_area_t hor_area;
+ lv_area_t ver_area;
+ lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
+
+ if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
+
+ if(lv_area_get_size(&hor_area) > 0) lv_obj_invalidate_area(obj, &hor_area);
+ if(lv_area_get_size(&ver_area) > 0) lv_obj_invalidate_area(obj, &ver_area);
+}
+
+void lv_obj_readjust_scroll(lv_obj_t * obj, lv_anim_enable_t anim_en)
+{
+ /*Be sure the bottom side is not remains scrolled in*/
+ /*With snapping the content can't be scrolled in*/
+ if(lv_obj_get_scroll_snap_y(obj) == LV_SCROLL_SNAP_NONE) {
+ lv_coord_t st = lv_obj_get_scroll_top(obj);
+ lv_coord_t sb = lv_obj_get_scroll_bottom(obj);
+ if(sb < 0 && st > 0) {
+ sb = LV_MIN(st, -sb);
+ lv_obj_scroll_by(obj, 0, sb, anim_en);
+ }
+ }
+
+ if(lv_obj_get_scroll_snap_x(obj) == LV_SCROLL_SNAP_NONE) {
+ lv_coord_t sl = lv_obj_get_scroll_left(obj);
+ lv_coord_t sr = lv_obj_get_scroll_right(obj);
+ if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) != LV_BASE_DIR_RTL) {
+ /*Be sure the left side is not remains scrolled in*/
+ if(sr < 0 && sl > 0) {
+ sr = LV_MIN(sl, -sr);
+ lv_obj_scroll_by(obj, sr, 0, anim_en);
+ }
+ }
+ else {
+ /*Be sure the right side is not remains scrolled in*/
+ if(sl < 0 && sr > 0) {
+ sr = LV_MIN(sr, -sl);
+ lv_obj_scroll_by(obj, sl, 0, anim_en);
+ }
+ }
+ }
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+
+static void scroll_x_anim(void * obj, int32_t v)
+{
+ _lv_obj_scroll_by_raw(obj, v + lv_obj_get_scroll_x(obj), 0);
+}
+
+static void scroll_y_anim(void * obj, int32_t v)
+{
+ _lv_obj_scroll_by_raw(obj, 0, v + lv_obj_get_scroll_y(obj));
+}
+
+static void scroll_anim_ready_cb(lv_anim_t * a)
+{
+ lv_event_send(a->var, LV_EVENT_SCROLL_END, NULL);
+}
+
+static void scroll_area_into_view(const lv_area_t * area, lv_obj_t * child, lv_point_t * scroll_value,
+ lv_anim_enable_t anim_en)
+{
+ lv_obj_t * parent = lv_obj_get_parent(child);
+ if(!lv_obj_has_flag(parent, LV_OBJ_FLAG_SCROLLABLE)) return;
+
+ lv_dir_t scroll_dir = lv_obj_get_scroll_dir(parent);
+ lv_coord_t snap_goal = 0;
+ lv_coord_t act = 0;
+ const lv_area_t * area_tmp;
+
+ lv_coord_t y_scroll = 0;
+ lv_scroll_snap_t snap_y = lv_obj_get_scroll_snap_y(parent);
+ if(snap_y != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
+ else area_tmp = area;
+
+ lv_coord_t border_width = lv_obj_get_style_border_width(parent, LV_PART_MAIN);
+ lv_coord_t ptop = lv_obj_get_style_pad_top(parent, LV_PART_MAIN) + border_width;
+ lv_coord_t pbottom = lv_obj_get_style_pad_bottom(parent, LV_PART_MAIN) + border_width;
+ lv_coord_t top_diff = parent->coords.y1 + ptop - area_tmp->y1 - scroll_value->y;
+ lv_coord_t bottom_diff = -(parent->coords.y2 - pbottom - area_tmp->y2 - scroll_value->y);
+ lv_coord_t parent_h = lv_obj_get_height(parent) - ptop - pbottom;
+ if((top_diff >= 0 && bottom_diff >= 0)) y_scroll = 0;
+ else if(top_diff > 0) {
+ y_scroll = top_diff;
+ /*Do not let scrolling in*/
+ lv_coord_t st = lv_obj_get_scroll_top(parent);
+ if(st - y_scroll < 0) y_scroll = 0;
+ }
+ else if(bottom_diff > 0) {
+ y_scroll = -bottom_diff;
+ /*Do not let scrolling in*/
+ lv_coord_t sb = lv_obj_get_scroll_bottom(parent);
+ if(sb + y_scroll < 0) y_scroll = 0;
+ }
+
+ switch(snap_y) {
+ case LV_SCROLL_SNAP_START:
+ snap_goal = parent->coords.y1 + ptop;
+ act = area_tmp->y1 + y_scroll;
+ y_scroll += snap_goal - act;
+ break;
+ case LV_SCROLL_SNAP_END:
+ snap_goal = parent->coords.y2 - pbottom;
+ act = area_tmp->y2 + y_scroll;
+ y_scroll += snap_goal - act;
+ break;
+ case LV_SCROLL_SNAP_CENTER:
+ snap_goal = parent->coords.y1 + ptop + parent_h / 2;
+ act = lv_area_get_height(area_tmp) / 2 + area_tmp->y1 + y_scroll;
+ y_scroll += snap_goal - act;
+ break;
+ }
+
+ lv_coord_t x_scroll = 0;
+ lv_scroll_snap_t snap_x = lv_obj_get_scroll_snap_x(parent);
+ if(snap_x != LV_SCROLL_SNAP_NONE) area_tmp = &child->coords;
+ else area_tmp = area;
+
+ lv_coord_t pleft = lv_obj_get_style_pad_left(parent, LV_PART_MAIN) + border_width;
+ lv_coord_t pright = lv_obj_get_style_pad_right(parent, LV_PART_MAIN) + border_width;
+ lv_coord_t left_diff = parent->coords.x1 + pleft - area_tmp->x1 - scroll_value->x;
+ lv_coord_t right_diff = -(parent->coords.x2 - pright - area_tmp->x2 - scroll_value->x);
+ if((left_diff >= 0 && right_diff >= 0)) x_scroll = 0;
+ else if(left_diff > 0) {
+ x_scroll = left_diff;
+ /*Do not let scrolling in*/
+ lv_coord_t sl = lv_obj_get_scroll_left(parent);
+ if(sl - x_scroll < 0) x_scroll = 0;
+ }
+ else if(right_diff > 0) {
+ x_scroll = -right_diff;
+ /*Do not let scrolling in*/
+ lv_coord_t sr = lv_obj_get_scroll_right(parent);
+ if(sr + x_scroll < 0) x_scroll = 0;
+ }
+
+ lv_coord_t parent_w = lv_obj_get_width(parent) - pleft - pright;
+ switch(snap_x) {
+ case LV_SCROLL_SNAP_START:
+ snap_goal = parent->coords.x1 + pleft;
+ act = area_tmp->x1 + x_scroll;
+ x_scroll += snap_goal - act;
+ break;
+ case LV_SCROLL_SNAP_END:
+ snap_goal = parent->coords.x2 - pright;
+ act = area_tmp->x2 + x_scroll;
+ x_scroll += snap_goal - act;
+ break;
+ case LV_SCROLL_SNAP_CENTER:
+ snap_goal = parent->coords.x1 + pleft + parent_w / 2;
+ act = lv_area_get_width(area_tmp) / 2 + area_tmp->x1 + x_scroll;
+ x_scroll += snap_goal - act;
+ break;
+ }
+
+ /*Remove any pending scroll animations.*/
+ bool y_del = lv_anim_del(parent, scroll_y_anim);
+ bool x_del = lv_anim_del(parent, scroll_x_anim);
+ if(y_del || x_del) {
+ lv_res_t res;
+ res = lv_event_send(parent, LV_EVENT_SCROLL_END, NULL);
+ if(res != LV_RES_OK) return;
+ }
+
+ if((scroll_dir & LV_DIR_LEFT) == 0 && x_scroll < 0) x_scroll = 0;
+ if((scroll_dir & LV_DIR_RIGHT) == 0 && x_scroll > 0) x_scroll = 0;
+ if((scroll_dir & LV_DIR_TOP) == 0 && y_scroll < 0) y_scroll = 0;
+ if((scroll_dir & LV_DIR_BOTTOM) == 0 && y_scroll > 0) y_scroll = 0;
+
+ scroll_value->x += anim_en == LV_ANIM_OFF ? 0 : x_scroll;
+ scroll_value->y += anim_en == LV_ANIM_OFF ? 0 : y_scroll;
+ lv_obj_scroll_by(parent, x_scroll, y_scroll, anim_en);
+}
diff --git a/lib/lvgl/src/core/lv_obj_scroll.h b/lib/lvgl/src/core/lv_obj_scroll.h
new file mode 100644
index 00000000..e1da245b
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_scroll.h
@@ -0,0 +1,307 @@
+/**
+ * @file lv_obj_scroll.h
+ *
+ */
+
+#ifndef LV_OBJ_SCROLL_H
+#define LV_OBJ_SCROLL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../misc/lv_area.h"
+#include "../misc/lv_anim.h"
+#include "../misc/lv_types.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/*Can't include lv_obj.h because it includes this header file*/
+struct _lv_obj_t;
+
+/** Scrollbar modes: shows when should the scrollbars be visible*/
+enum {
+ LV_SCROLLBAR_MODE_OFF, /**< Never show scrollbars*/
+ LV_SCROLLBAR_MODE_ON, /**< Always show scrollbars*/
+ LV_SCROLLBAR_MODE_ACTIVE, /**< Show scroll bars when object is being scrolled*/
+ LV_SCROLLBAR_MODE_AUTO, /**< Show scroll bars when the content is large enough to be scrolled*/
+};
+typedef uint8_t lv_scrollbar_mode_t;
+
+
+/** Scroll span align options. Tells where to align the snappable children when scroll stops.*/
+enum {
+ LV_SCROLL_SNAP_NONE, /**< Do not align, leave where it is*/
+ LV_SCROLL_SNAP_START, /**< Align to the left/top*/
+ LV_SCROLL_SNAP_END, /**< Align to the right/bottom*/
+ LV_SCROLL_SNAP_CENTER /**< Align to the center*/
+};
+typedef uint8_t lv_scroll_snap_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/*=====================
+ * Setter functions
+ *====================*/
+
+/**
+ * Set how the scrollbars should behave.
+ * @param obj pointer to an object
+ * @param mode LV_SCROLL_MODE_ON/OFF/AUTO/ACTIVE
+ */
+void lv_obj_set_scrollbar_mode(struct _lv_obj_t * obj, lv_scrollbar_mode_t mode);
+
+/**
+ * Set the object in which directions can be scrolled
+ * @param obj pointer to an object
+ * @param dir the allow scroll directions. An element or OR-ed values of `lv_dir_t`
+ */
+void lv_obj_set_scroll_dir(struct _lv_obj_t * obj, lv_dir_t dir);
+
+/**
+ * Set where to snap the children when scrolling ends horizontally
+ * @param obj pointer to an object
+ * @param align the snap align to set from `lv_scroll_snap_t`
+ */
+void lv_obj_set_scroll_snap_x(struct _lv_obj_t * obj, lv_scroll_snap_t align);
+
+/**
+ * Set where to snap the children when scrolling ends vertically
+ * @param obj pointer to an object
+ * @param align the snap align to set from `lv_scroll_snap_t`
+ */
+void lv_obj_set_scroll_snap_y(struct _lv_obj_t * obj, lv_scroll_snap_t align);
+
+/*=====================
+ * Getter functions
+ *====================*/
+
+/**
+ * Get the current scroll mode (when to hide the scrollbars)
+ * @param obj pointer to an object
+ * @return the current scroll mode from `lv_scrollbar_mode_t`
+ */
+lv_scrollbar_mode_t lv_obj_get_scrollbar_mode(const struct _lv_obj_t * obj);
+
+/**
+ * Get the object in which directions can be scrolled
+ * @param obj pointer to an object
+ * @param dir the allow scroll directions. An element or OR-ed values of `lv_dir_t`
+ */
+lv_dir_t lv_obj_get_scroll_dir(const struct _lv_obj_t * obj);
+
+/**
+ * Get where to snap the children when scrolling ends horizontally
+ * @param obj pointer to an object
+ * @return the current snap align from `lv_scroll_snap_t`
+ */
+lv_scroll_snap_t lv_obj_get_scroll_snap_x(const struct _lv_obj_t * obj);
+
+/**
+ * Get where to snap the children when scrolling ends vertically
+ * @param obj pointer to an object
+ * @return the current snap align from `lv_scroll_snap_t`
+ */
+lv_scroll_snap_t lv_obj_get_scroll_snap_y(const struct _lv_obj_t * obj);
+
+/**
+ * Get current X scroll position.
+ * @param obj pointer to an object
+ * @return the current scroll position from the left edge.
+ * If the object is not scrolled return 0
+ * If scrolled return > 0
+ * If scrolled in (elastic scroll) return < 0
+ */
+lv_coord_t lv_obj_get_scroll_x(const struct _lv_obj_t * obj);
+
+/**
+ * Get current Y scroll position.
+ * @param obj pointer to an object
+ * @return the current scroll position from the top edge.
+ * If the object is not scrolled return 0
+ * If scrolled return > 0
+ * If scrolled inside return < 0
+ */
+lv_coord_t lv_obj_get_scroll_y(const struct _lv_obj_t * obj);
+
+/**
+ * Return the height of the area above the object.
+ * That is the number of pixels the object can be scrolled down.
+ * Normally positive but can be negative when scrolled inside.
+ * @param obj pointer to an object
+ * @return the scrollable area above the object in pixels
+ */
+lv_coord_t lv_obj_get_scroll_top(struct _lv_obj_t * obj);
+
+/**
+ * Return the height of the area below the object.
+ * That is the number of pixels the object can be scrolled down.
+ * Normally positive but can be negative when scrolled inside.
+ * @param obj pointer to an object
+ * @return the scrollable area below the object in pixels
+ */
+lv_coord_t lv_obj_get_scroll_bottom(struct _lv_obj_t * obj);
+
+/**
+ * Return the width of the area on the left the object.
+ * That is the number of pixels the object can be scrolled down.
+ * Normally positive but can be negative when scrolled inside.
+ * @param obj pointer to an object
+ * @return the scrollable area on the left the object in pixels
+ */
+lv_coord_t lv_obj_get_scroll_left(struct _lv_obj_t * obj);
+
+/**
+ * Return the width of the area on the right the object.
+ * That is the number of pixels the object can be scrolled down.
+ * Normally positive but can be negative when scrolled inside.
+ * @param obj pointer to an object
+ * @return the scrollable area on the right the object in pixels
+ */
+lv_coord_t lv_obj_get_scroll_right(struct _lv_obj_t * obj);
+
+/**
+ * Get the X and Y coordinates where the scrolling will end for this object if a scrolling animation is in progress.
+ * If no scrolling animation, give the current `x` or `y` scroll position.
+ * @param obj pointer to an object
+ * @param end pointer to store the result
+ */
+void lv_obj_get_scroll_end(struct _lv_obj_t * obj, lv_point_t * end);
+
+/*=====================
+ * Other functions
+ *====================*/
+
+/**
+ * Scroll by a given amount of pixels
+ * @param obj pointer to an object to scroll
+ * @param dx pixels to scroll horizontally
+ * @param dy pixels to scroll vertically
+ * @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ * @note > 0 value means scroll right/bottom (show the more content on the right/bottom)
+ * @note e.g. dy = -20 means scroll down 20 px
+ */
+void lv_obj_scroll_by(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll by a given amount of pixels.
+ * `dx` and `dy` will be limited internally to allow scrolling only on the content area.
+ * @param obj pointer to an object to scroll
+ * @param dx pixels to scroll horizontally
+ * @param dy pixels to scroll vertically
+ * @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ * @note e.g. dy = -20 means scroll down 20 px
+ */
+void lv_obj_scroll_by_bounded(struct _lv_obj_t * obj, lv_coord_t dx, lv_coord_t dy, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll to a given coordinate on an object.
+ * `x` and `y` will be limited internally to allow scrolling only on the content area.
+ * @param obj pointer to an object to scroll
+ * @param x pixels to scroll horizontally
+ * @param y pixels to scroll vertically
+ * @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ */
+void lv_obj_scroll_to(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll to a given X coordinate on an object.
+ * `x` will be limited internally to allow scrolling only on the content area.
+ * @param obj pointer to an object to scroll
+ * @param x pixels to scroll horizontally
+ * @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ */
+void lv_obj_scroll_to_x(struct _lv_obj_t * obj, lv_coord_t x, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll to a given Y coordinate on an object
+ * `y` will be limited internally to allow scrolling only on the content area.
+ * @param obj pointer to an object to scroll
+ * @param y pixels to scroll vertically
+ * @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ */
+void lv_obj_scroll_to_y(struct _lv_obj_t * obj, lv_coord_t y, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll to an object until it becomes visible on its parent
+ * @param obj pointer to an object to scroll into view
+ * @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ */
+void lv_obj_scroll_to_view(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
+
+/**
+ * Scroll to an object until it becomes visible on its parent.
+ * Do the same on the parent's parent, and so on.
+ * Therefore the object will be scrolled into view even it has nested scrollable parents
+ * @param obj pointer to an object to scroll into view
+ * @param anim_en LV_ANIM_ON: scroll with animation; LV_ANIM_OFF: scroll immediately
+ */
+void lv_obj_scroll_to_view_recursive(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
+
+
+/**
+ * Low level function to scroll by given x and y coordinates.
+ * `LV_EVENT_SCROLL` is sent.
+ * @param obj pointer to an object to scroll
+ * @param x pixels to scroll horizontally
+ * @param y pixels to scroll vertically
+ * @return `LV_RES_INV`: to object was deleted in `LV_EVENT_SCROLL`;
+ * `LV_RES_OK`: if the object is still valid
+ */
+lv_res_t _lv_obj_scroll_by_raw(struct _lv_obj_t * obj, lv_coord_t x, lv_coord_t y);
+
+/**
+ * Tell whether an object is being scrolled or not at this moment
+ * @param obj pointer to an object
+ * @return true: `obj` is being scrolled
+ */
+bool lv_obj_is_scrolling(const struct _lv_obj_t * obj);
+
+/**
+ * Check the children of `obj` and scroll `obj` to fulfill the scroll_snap settings
+ * @param obj an object whose children needs to checked and snapped
+ * @param anim_en LV_ANIM_ON/OFF
+ */
+void lv_obj_update_snap(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
+
+/**
+ * Get the area of the scrollbars
+ * @param obj pointer to an object
+ * @param hor pointer to store the area of the horizontal scrollbar
+ * @param ver pointer to store the area of the vertical scrollbar
+ */
+void lv_obj_get_scrollbar_area(struct _lv_obj_t * obj, lv_area_t * hor, lv_area_t * ver);
+
+/**
+ * Invalidate the area of the scrollbars
+ * @param obj pointer to an object
+ */
+void lv_obj_scrollbar_invalidate(struct _lv_obj_t * obj);
+
+/**
+ * Checked if the content is scrolled "in" and adjusts it to a normal position.
+ * @param obj pointer to an object
+ * @param anim_en LV_ANIM_ON/OFF
+ */
+void lv_obj_readjust_scroll(struct _lv_obj_t * obj, lv_anim_enable_t anim_en);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_SCROLL_H*/
diff --git a/lib/lvgl/src/core/lv_obj_style.c b/lib/lvgl/src/core/lv_obj_style.c
new file mode 100644
index 00000000..c6cdf825
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_style.c
@@ -0,0 +1,866 @@
+/**
+ * @file lv_obj_style.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include "lv_disp.h"
+#include "../misc/lv_gc.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_obj_class
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+typedef struct {
+ lv_obj_t * obj;
+ lv_style_prop_t prop;
+ lv_style_selector_t selector;
+ lv_style_value_t start_value;
+ lv_style_value_t end_value;
+} trans_t;
+
+typedef enum {
+ CACHE_ZERO = 0,
+ CACHE_TRUE = 1,
+ CACHE_UNSET = 2,
+ CACHE_255 = 3,
+ CACHE_NEED_CHECK = 4,
+} cache_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector);
+static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, uint32_t part);
+static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v);
+static void report_style_change_core(void * style, lv_obj_t * obj);
+static void refresh_children_style(lv_obj_t * obj);
+static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit);
+static void trans_anim_cb(void * _tr, int32_t v);
+static void trans_anim_start_cb(lv_anim_t * a);
+static void trans_anim_ready_cb(lv_anim_t * a);
+static lv_layer_type_t calculate_layer_type(lv_obj_t * obj);
+static void fade_anim_cb(void * obj, int32_t v);
+static void fade_in_anim_ready(lv_anim_t * a);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static bool style_refr = true;
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void _lv_obj_style_init(void)
+{
+ _lv_ll_init(&LV_GC_ROOT(_lv_obj_style_trans_ll), sizeof(trans_t));
+}
+
+void lv_obj_add_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
+{
+ trans_del(obj, selector, LV_STYLE_PROP_ANY, NULL);
+
+ uint32_t i;
+ /*Go after the transition and local styles*/
+ for(i = 0; i < obj->style_cnt; i++) {
+ if(obj->styles[i].is_trans) continue;
+ if(obj->styles[i].is_local) continue;
+ break;
+ }
+
+ /*Now `i` is at the first normal style. Insert the new style before this*/
+
+ /*Allocate space for the new style and shift the rest of the style to the end*/
+ obj->style_cnt++;
+ obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
+
+ uint32_t j;
+ for(j = obj->style_cnt - 1; j > i ; j--) {
+ obj->styles[j] = obj->styles[j - 1];
+ }
+
+ lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
+ obj->styles[i].style = style;
+ obj->styles[i].selector = selector;
+
+ lv_obj_refresh_style(obj, selector, LV_STYLE_PROP_ANY);
+}
+
+void lv_obj_remove_style(lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector)
+{
+ lv_state_t state = lv_obj_style_get_selector_state(selector);
+ lv_part_t part = lv_obj_style_get_selector_part(selector);
+ lv_style_prop_t prop = LV_STYLE_PROP_ANY;
+ if(style && style->prop_cnt == 0) prop = LV_STYLE_PROP_INV;
+
+ uint32_t i = 0;
+ bool deleted = false;
+ while(i < obj->style_cnt) {
+ lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
+ lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
+ if((state != LV_STATE_ANY && state_act != state) ||
+ (part != LV_PART_ANY && part_act != part) ||
+ (style != NULL && style != obj->styles[i].style)) {
+ i++;
+ continue;
+ }
+
+ if(obj->styles[i].is_trans) {
+ trans_del(obj, part, LV_STYLE_PROP_ANY, NULL);
+ }
+
+ if(obj->styles[i].is_local || obj->styles[i].is_trans) {
+ lv_style_reset(obj->styles[i].style);
+ lv_mem_free(obj->styles[i].style);
+ obj->styles[i].style = NULL;
+ }
+
+ /*Shift the styles after `i` by one*/
+ uint32_t j;
+ for(j = i; j < (uint32_t)obj->style_cnt - 1 ; j++) {
+ obj->styles[j] = obj->styles[j + 1];
+ }
+
+ obj->style_cnt--;
+ obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
+
+ deleted = true;
+ /*The style from the current `i` index is removed, so `i` points to the next style.
+ *Therefore it doesn't needs to be incremented*/
+ }
+ if(deleted && prop != LV_STYLE_PROP_INV) {
+ lv_obj_refresh_style(obj, part, prop);
+ }
+}
+
+void lv_obj_report_style_change(lv_style_t * style)
+{
+ if(!style_refr) return;
+ lv_disp_t * d = lv_disp_get_next(NULL);
+
+ while(d) {
+ uint32_t i;
+ for(i = 0; i < d->screen_cnt; i++) {
+ report_style_change_core(style, d->screens[i]);
+ }
+ d = lv_disp_get_next(d);
+ }
+}
+
+void lv_obj_refresh_style(lv_obj_t * obj, lv_style_selector_t selector, lv_style_prop_t prop)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ if(!style_refr) return;
+
+ lv_obj_invalidate(obj);
+
+ lv_part_t part = lv_obj_style_get_selector_part(selector);
+
+ bool is_layout_refr = lv_style_prop_has_flag(prop, LV_STYLE_PROP_LAYOUT_REFR);
+ bool is_ext_draw = lv_style_prop_has_flag(prop, LV_STYLE_PROP_EXT_DRAW);
+ bool is_inheritable = lv_style_prop_has_flag(prop, LV_STYLE_PROP_INHERIT);
+ bool is_layer_refr = lv_style_prop_has_flag(prop, LV_STYLE_PROP_LAYER_REFR);
+
+ if(is_layout_refr) {
+ if(part == LV_PART_ANY ||
+ part == LV_PART_MAIN ||
+ lv_obj_get_style_height(obj, 0) == LV_SIZE_CONTENT ||
+ lv_obj_get_style_width(obj, 0) == LV_SIZE_CONTENT) {
+ lv_event_send(obj, LV_EVENT_STYLE_CHANGED, NULL);
+ lv_obj_mark_layout_as_dirty(obj);
+ }
+ }
+ if((part == LV_PART_ANY || part == LV_PART_MAIN) && (prop == LV_STYLE_PROP_ANY || is_layout_refr)) {
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ if(parent) lv_obj_mark_layout_as_dirty(parent);
+ }
+
+ /*Cache the layer type*/
+ if((part == LV_PART_ANY || part == LV_PART_MAIN) && is_layer_refr) {
+ lv_layer_type_t layer_type = calculate_layer_type(obj);
+ if(obj->spec_attr) obj->spec_attr->layer_type = layer_type;
+ else if(layer_type != LV_LAYER_TYPE_NONE) {
+ lv_obj_allocate_spec_attr(obj);
+ obj->spec_attr->layer_type = layer_type;
+ }
+ }
+
+ if(prop == LV_STYLE_PROP_ANY || is_ext_draw) {
+ lv_obj_refresh_ext_draw_size(obj);
+ }
+ lv_obj_invalidate(obj);
+
+ if(prop == LV_STYLE_PROP_ANY || (is_inheritable && (is_ext_draw || is_layout_refr))) {
+ if(part != LV_PART_SCROLLBAR) {
+ refresh_children_style(obj);
+ }
+ }
+}
+
+void lv_obj_enable_style_refresh(bool en)
+{
+ style_refr = en;
+}
+
+lv_style_value_t lv_obj_get_style_prop(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop)
+{
+ lv_style_value_t value_act;
+ bool inheritable = lv_style_prop_has_flag(prop, LV_STYLE_PROP_INHERIT);
+ lv_style_res_t found = LV_STYLE_RES_NOT_FOUND;
+ while(obj) {
+ found = get_prop_core(obj, part, prop, &value_act);
+ if(found == LV_STYLE_RES_FOUND) break;
+ if(!inheritable) break;
+
+ /*If not found, check the `MAIN` style first*/
+ if(found != LV_STYLE_RES_INHERIT && part != LV_PART_MAIN) {
+ part = LV_PART_MAIN;
+ continue;
+ }
+
+ /*Check the parent too.*/
+ obj = lv_obj_get_parent(obj);
+ }
+
+ if(found != LV_STYLE_RES_FOUND) {
+ if(part == LV_PART_MAIN && (prop == LV_STYLE_WIDTH || prop == LV_STYLE_HEIGHT)) {
+ const lv_obj_class_t * cls = obj->class_p;
+ while(cls) {
+ if(prop == LV_STYLE_WIDTH) {
+ if(cls->width_def != 0) break;
+ }
+ else {
+ if(cls->height_def != 0) break;
+ }
+ cls = cls->base_class;
+ }
+
+ if(cls) {
+ value_act.num = prop == LV_STYLE_WIDTH ? cls->width_def : cls->height_def;
+ }
+ else {
+ value_act.num = 0;
+ }
+ }
+ else {
+ value_act = lv_style_prop_get_default(prop);
+ }
+ }
+ return value_act;
+}
+
+void lv_obj_set_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
+ lv_style_selector_t selector)
+{
+ lv_style_t * style = get_local_style(obj, selector);
+ lv_style_set_prop(style, prop, value);
+ lv_obj_refresh_style(obj, selector, prop);
+}
+
+void lv_obj_set_local_style_prop_meta(lv_obj_t * obj, lv_style_prop_t prop, uint16_t meta,
+ lv_style_selector_t selector)
+{
+ lv_style_t * style = get_local_style(obj, selector);
+ lv_style_set_prop_meta(style, prop, meta);
+ lv_obj_refresh_style(obj, selector, prop);
+}
+
+
+lv_style_res_t lv_obj_get_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value,
+ lv_style_selector_t selector)
+{
+ uint32_t i;
+ for(i = 0; i < obj->style_cnt; i++) {
+ if(obj->styles[i].is_local &&
+ obj->styles[i].selector == selector) {
+ return lv_style_get_prop(obj->styles[i].style, prop, value);
+ }
+ }
+
+ return LV_STYLE_RES_NOT_FOUND;
+}
+
+bool lv_obj_remove_local_style_prop(lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ uint32_t i;
+ /*Find the style*/
+ for(i = 0; i < obj->style_cnt; i++) {
+ if(obj->styles[i].is_local &&
+ obj->styles[i].selector == selector) {
+ break;
+ }
+ }
+
+ /*The style is not found*/
+ if(i == obj->style_cnt) return false;
+
+ lv_res_t res = lv_style_remove_prop(obj->styles[i].style, prop);
+ if(res == LV_RES_OK) {
+ lv_obj_refresh_style(obj, selector, prop);
+ }
+
+ return res;
+}
+
+void _lv_obj_style_create_transition(lv_obj_t * obj, lv_part_t part, lv_state_t prev_state, lv_state_t new_state,
+ const _lv_obj_style_transition_dsc_t * tr_dsc)
+{
+ trans_t * tr;
+
+ /*Get the previous and current values*/
+ obj->skip_trans = 1;
+ obj->state = prev_state;
+ lv_style_value_t v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
+ obj->state = new_state;
+ lv_style_value_t v2 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
+ obj->skip_trans = 0;
+
+ if(v1.ptr == v2.ptr && v1.num == v2.num && v1.color.full == v2.color.full) return;
+ obj->state = prev_state;
+ v1 = lv_obj_get_style_prop(obj, part, tr_dsc->prop);
+ obj->state = new_state;
+
+ _lv_obj_style_t * style_trans = get_trans_style(obj, part);
+ lv_style_set_prop(style_trans->style, tr_dsc->prop, v1); /*Be sure `trans_style` has a valid value*/
+
+ if(tr_dsc->prop == LV_STYLE_RADIUS) {
+ if(v1.num == LV_RADIUS_CIRCLE || v2.num == LV_RADIUS_CIRCLE) {
+ lv_coord_t whalf = lv_obj_get_width(obj) / 2;
+ lv_coord_t hhalf = lv_obj_get_height(obj) / 2;
+ if(v1.num == LV_RADIUS_CIRCLE) v1.num = LV_MIN(whalf + 1, hhalf + 1);
+ if(v2.num == LV_RADIUS_CIRCLE) v2.num = LV_MIN(whalf + 1, hhalf + 1);
+ }
+ }
+
+ tr = _lv_ll_ins_head(&LV_GC_ROOT(_lv_obj_style_trans_ll));
+ LV_ASSERT_MALLOC(tr);
+ if(tr == NULL) return;
+ tr->start_value = v1;
+ tr->end_value = v2;
+ tr->obj = obj;
+ tr->prop = tr_dsc->prop;
+ tr->selector = part;
+
+ lv_anim_t a;
+ lv_anim_init(&a);
+ lv_anim_set_var(&a, tr);
+ lv_anim_set_exec_cb(&a, trans_anim_cb);
+ lv_anim_set_start_cb(&a, trans_anim_start_cb);
+ lv_anim_set_ready_cb(&a, trans_anim_ready_cb);
+ lv_anim_set_values(&a, 0x00, 0xFF);
+ lv_anim_set_time(&a, tr_dsc->time);
+ lv_anim_set_delay(&a, tr_dsc->delay);
+ lv_anim_set_path_cb(&a, tr_dsc->path_cb);
+ lv_anim_set_early_apply(&a, false);
+#if LV_USE_USER_DATA
+ a.user_data = tr_dsc->user_data;
+#endif
+ lv_anim_start(&a);
+}
+
+
+lv_style_value_t _lv_obj_style_apply_color_filter(const lv_obj_t * obj, uint32_t part, lv_style_value_t v)
+{
+ if(obj == NULL) return v;
+ const lv_color_filter_dsc_t * f = lv_obj_get_style_color_filter_dsc(obj, part);
+ if(f && f->filter_cb) {
+ lv_opa_t f_opa = lv_obj_get_style_color_filter_opa(obj, part);
+ if(f_opa != 0) v.color = f->filter_cb(f, v.color, f_opa);
+ }
+ return v;
+}
+
+_lv_style_state_cmp_t _lv_obj_style_state_compare(lv_obj_t * obj, lv_state_t state1, lv_state_t state2)
+{
+ _lv_style_state_cmp_t res = _LV_STYLE_STATE_CMP_SAME;
+
+ /*Are there any new styles for the new state?*/
+ uint32_t i;
+ for(i = 0; i < obj->style_cnt; i++) {
+ if(obj->styles[i].is_trans) continue;
+
+ lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
+ /*The style is valid for a state but not the other*/
+ bool valid1 = state_act & (~state1) ? false : true;
+ bool valid2 = state_act & (~state2) ? false : true;
+ if(valid1 != valid2) {
+ lv_style_t * style = obj->styles[i].style;
+ lv_style_value_t v;
+ /*If there is layout difference on the main part, return immediately. There is no more serious difference*/
+ bool layout_diff = false;
+ if(lv_style_get_prop(style, LV_STYLE_PAD_TOP, &v))layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_PAD_BOTTOM, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_PAD_LEFT, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_PAD_RIGHT, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_PAD_COLUMN, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_PAD_ROW, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_LAYOUT, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_X, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_TRANSLATE_Y, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_WIDTH, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_HEIGHT, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_MIN_WIDTH, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_MAX_WIDTH, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_MIN_HEIGHT, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_MAX_HEIGHT, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_BORDER_WIDTH, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) layout_diff = true;
+ else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) layout_diff = true;
+
+ if(layout_diff) {
+ return _LV_STYLE_STATE_CMP_DIFF_LAYOUT;
+ }
+
+ /*Check for draw pad changes*/
+ if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_HEIGHT, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ANGLE, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_TRANSFORM_ZOOM, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_PAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_OUTLINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_SHADOW_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OPA, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_X, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_SHADOW_OFS_Y, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_SHADOW_SPREAD, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(lv_style_get_prop(style, LV_STYLE_LINE_WIDTH, &v)) res = _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD;
+ else if(res == _LV_STYLE_STATE_CMP_SAME) res = _LV_STYLE_STATE_CMP_DIFF_REDRAW;
+ }
+ }
+
+ return res;
+}
+
+void lv_obj_fade_in(lv_obj_t * obj, uint32_t time, uint32_t delay)
+{
+ lv_anim_t a;
+ lv_anim_init(&a);
+ lv_anim_set_var(&a, obj);
+ lv_anim_set_values(&a, 0, LV_OPA_COVER);
+ lv_anim_set_exec_cb(&a, fade_anim_cb);
+ lv_anim_set_ready_cb(&a, fade_in_anim_ready);
+ lv_anim_set_time(&a, time);
+ lv_anim_set_delay(&a, delay);
+ lv_anim_start(&a);
+}
+
+void lv_obj_fade_out(lv_obj_t * obj, uint32_t time, uint32_t delay)
+{
+ lv_anim_t a;
+ lv_anim_init(&a);
+ lv_anim_set_var(&a, obj);
+ lv_anim_set_values(&a, lv_obj_get_style_opa(obj, 0), LV_OPA_TRANSP);
+ lv_anim_set_exec_cb(&a, fade_anim_cb);
+ lv_anim_set_time(&a, time);
+ lv_anim_set_delay(&a, delay);
+ lv_anim_start(&a);
+}
+
+lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector)
+{
+ return selector & 0xFFFF;
+}
+
+lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector)
+{
+ return selector & 0xFF0000;
+}
+
+
+lv_text_align_t lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj, lv_part_t part, const char * txt)
+{
+ lv_text_align_t align = lv_obj_get_style_text_align(obj, part);
+ lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, part);
+ lv_bidi_calculate_align(&align, &base_dir, txt);
+ return align;
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Get the local style of an object for a given part and for a given state.
+ * If the local style for the part-state pair doesn't exist allocate and return it.
+ * @param obj pointer to an object
+ * @param selector OR-ed value of parts and state for which the style should be get
+ * @return pointer to the local style
+ */
+static lv_style_t * get_local_style(lv_obj_t * obj, lv_style_selector_t selector)
+{
+ uint32_t i;
+ for(i = 0; i < obj->style_cnt; i++) {
+ if(obj->styles[i].is_local &&
+ obj->styles[i].selector == selector) {
+ return obj->styles[i].style;
+ }
+ }
+
+ obj->style_cnt++;
+ obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
+ LV_ASSERT_MALLOC(obj->styles);
+
+ for(i = obj->style_cnt - 1; i > 0 ; i--) {
+ /*Copy only normal styles (not local and transition).
+ *The new local style will be added as the last local style*/
+ if(obj->styles[i - 1].is_local || obj->styles[i - 1].is_trans) break;
+ obj->styles[i] = obj->styles[i - 1];
+ }
+
+ lv_memset_00(&obj->styles[i], sizeof(_lv_obj_style_t));
+ obj->styles[i].style = lv_mem_alloc(sizeof(lv_style_t));
+ lv_style_init(obj->styles[i].style);
+ obj->styles[i].is_local = 1;
+ obj->styles[i].selector = selector;
+ return obj->styles[i].style;
+}
+
+/**
+ * Get the transition style of an object for a given part and for a given state.
+ * If the transition style for the part-state pair doesn't exist allocate and return it.
+ * @param obj pointer to an object
+ * @param selector OR-ed value of parts and state for which the style should be get
+ * @return pointer to the transition style
+ */
+static _lv_obj_style_t * get_trans_style(lv_obj_t * obj, lv_style_selector_t selector)
+{
+ uint32_t i;
+ for(i = 0; i < obj->style_cnt; i++) {
+ if(obj->styles[i].is_trans && obj->styles[i].selector == selector) break;
+ }
+
+ /*Already have a transition style for it*/
+ if(i != obj->style_cnt) return &obj->styles[i];
+
+ obj->style_cnt++;
+ obj->styles = lv_mem_realloc(obj->styles, obj->style_cnt * sizeof(_lv_obj_style_t));
+
+ for(i = obj->style_cnt - 1; i > 0 ; i--) {
+ obj->styles[i] = obj->styles[i - 1];
+ }
+
+ lv_memset_00(&obj->styles[0], sizeof(_lv_obj_style_t));
+ obj->styles[0].style = lv_mem_alloc(sizeof(lv_style_t));
+ lv_style_init(obj->styles[0].style);
+ obj->styles[0].is_trans = 1;
+ obj->styles[0].selector = selector;
+ return &obj->styles[0];
+}
+
+
+static lv_style_res_t get_prop_core(const lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, lv_style_value_t * v)
+{
+ uint8_t group = 1 << _lv_style_get_prop_group(prop);
+ int32_t weight = -1;
+ lv_state_t state = obj->state;
+ lv_state_t state_inv = ~state;
+ lv_style_value_t value_tmp;
+ bool skip_trans = obj->skip_trans;
+ uint32_t i;
+ lv_style_res_t found;
+ for(i = 0; i < obj->style_cnt; i++) {
+ _lv_obj_style_t * obj_style = &obj->styles[i];
+ if(obj_style->is_trans == false) break;
+ if(skip_trans) continue;
+
+ lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
+
+ if(part_act != part) continue;
+ if((obj_style->style->has_group & group) == 0) continue;
+ found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
+ if(found == LV_STYLE_RES_FOUND) {
+ *v = value_tmp;
+ return LV_STYLE_RES_FOUND;
+ }
+ else if(found == LV_STYLE_RES_INHERIT) {
+ return LV_STYLE_RES_INHERIT;
+ }
+ }
+
+ for(; i < obj->style_cnt; i++) {
+ if((obj->styles[i].style->has_group & group) == 0) continue;
+ _lv_obj_style_t * obj_style = &obj->styles[i];
+ lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
+ lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
+ if(part_act != part) continue;
+
+ /*Be sure the style not specifies other state than the requested.
+ *E.g. For HOVER+PRESS object state, HOVER style only is OK, but HOVER+FOCUS style is not*/
+ if((state_act & state_inv)) continue;
+
+ /*Check only better candidates*/
+ if(state_act <= weight) continue;
+
+ found = lv_style_get_prop(obj_style->style, prop, &value_tmp);
+
+ if(found == LV_STYLE_RES_FOUND) {
+ if(state_act == state) {
+ *v = value_tmp;
+ return LV_STYLE_RES_FOUND;
+ }
+ if(weight < state_act) {
+ weight = state_act;
+ *v = value_tmp;
+ }
+ }
+ else if(found == LV_STYLE_RES_INHERIT) {
+ return LV_STYLE_RES_INHERIT;
+ }
+ }
+
+ if(weight >= 0) {
+ *v = value_tmp;
+ return LV_STYLE_RES_FOUND;
+ }
+ else return LV_STYLE_RES_NOT_FOUND;
+}
+
+/**
+ * Refresh the style of all children of an object. (Called recursively)
+ * @param style refresh objects only with this
+ * @param obj pointer to an object
+ */
+static void report_style_change_core(void * style, lv_obj_t * obj)
+{
+ uint32_t i;
+ for(i = 0; i < obj->style_cnt; i++) {
+ if(style == NULL || obj->styles[i].style == style) {
+ lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
+ break;
+ }
+ }
+
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ report_style_change_core(style, obj->spec_attr->children[i]);
+ }
+}
+
+/**
+ * Recursively refresh the style of the children. Go deeper until a not NULL style is found
+ * because the NULL styles are inherited from the parent
+ * @param obj pointer to an object
+ */
+static void refresh_children_style(lv_obj_t * obj)
+{
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ lv_obj_invalidate(child);
+ lv_event_send(child, LV_EVENT_STYLE_CHANGED, NULL);
+ lv_obj_invalidate(child);
+
+ refresh_children_style(child); /*Check children too*/
+ }
+}
+
+/**
+ * Remove the transition from object's part's property.
+ * - Remove the transition from `_lv_obj_style_trans_ll` and free it
+ * - Delete pending transitions
+ * @param obj pointer to an object which transition(s) should be removed
+ * @param part a part of object or 0xFF to remove from all parts
+ * @param prop a property or 0xFF to remove all properties
+ * @param tr_limit delete transitions only "older" than this. `NULL` if not used
+ */
+static bool trans_del(lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop, trans_t * tr_limit)
+{
+ trans_t * tr;
+ trans_t * tr_prev;
+ bool removed = false;
+ tr = _lv_ll_get_tail(&LV_GC_ROOT(_lv_obj_style_trans_ll));
+ while(tr != NULL) {
+ if(tr == tr_limit) break;
+
+ /*'tr' might be deleted, so get the next object while 'tr' is valid*/
+ tr_prev = _lv_ll_get_prev(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
+
+ if(tr->obj == obj && (part == tr->selector || part == LV_PART_ANY) && (prop == tr->prop || prop == LV_STYLE_PROP_ANY)) {
+ /*Remove any transitioned properties from the trans. style
+ *to allow changing it by normal styles*/
+ uint32_t i;
+ for(i = 0; i < obj->style_cnt; i++) {
+ if(obj->styles[i].is_trans && (part == LV_PART_ANY || obj->styles[i].selector == part)) {
+ lv_style_remove_prop(obj->styles[i].style, tr->prop);
+ }
+ }
+
+ /*Free the transition descriptor too*/
+ lv_anim_del(tr, NULL);
+ _lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
+ lv_mem_free(tr);
+ removed = true;
+
+ }
+ tr = tr_prev;
+ }
+ return removed;
+}
+
+static void trans_anim_cb(void * _tr, int32_t v)
+{
+ trans_t * tr = _tr;
+ lv_obj_t * obj = tr->obj;
+
+ uint32_t i;
+ for(i = 0; i < obj->style_cnt; i++) {
+ if(obj->styles[i].is_trans == 0 || obj->styles[i].selector != tr->selector) continue;
+
+ lv_style_value_t value_final;
+ switch(tr->prop) {
+
+ case LV_STYLE_BORDER_SIDE:
+ case LV_STYLE_BORDER_POST:
+ case LV_STYLE_BLEND_MODE:
+ if(v < 255) value_final.num = tr->start_value.num;
+ else value_final.num = tr->end_value.num;
+ break;
+ case LV_STYLE_TRANSITION:
+ case LV_STYLE_TEXT_FONT:
+ if(v < 255) value_final.ptr = tr->start_value.ptr;
+ else value_final.ptr = tr->end_value.ptr;
+ break;
+ case LV_STYLE_COLOR_FILTER_DSC:
+ if(tr->start_value.ptr == NULL) value_final.ptr = tr->end_value.ptr;
+ else if(tr->end_value.ptr == NULL) value_final.ptr = tr->start_value.ptr;
+ else if(v < 128) value_final.ptr = tr->start_value.ptr;
+ else value_final.ptr = tr->end_value.ptr;
+ break;
+ case LV_STYLE_BG_COLOR:
+ case LV_STYLE_BORDER_COLOR:
+ case LV_STYLE_TEXT_COLOR:
+ case LV_STYLE_SHADOW_COLOR:
+ case LV_STYLE_OUTLINE_COLOR:
+ case LV_STYLE_IMG_RECOLOR:
+ if(v <= 0) value_final.color = tr->start_value.color;
+ else if(v >= 255) value_final.color = tr->end_value.color;
+ else value_final.color = lv_color_mix(tr->end_value.color, tr->start_value.color, v);
+ break;
+
+ default:
+ if(v == 0) value_final.num = tr->start_value.num;
+ else if(v == 255) value_final.num = tr->end_value.num;
+ else value_final.num = tr->start_value.num + ((int32_t)((int32_t)(tr->end_value.num - tr->start_value.num) * v) >> 8);
+ break;
+ }
+
+ lv_style_value_t old_value;
+ bool refr = true;
+ if(lv_style_get_prop(obj->styles[i].style, tr->prop, &old_value)) {
+ if(value_final.ptr == old_value.ptr && value_final.color.full == old_value.color.full &&
+ value_final.num == old_value.num) {
+ refr = false;
+ }
+ }
+ lv_style_set_prop(obj->styles[i].style, tr->prop, value_final);
+ if(refr) lv_obj_refresh_style(tr->obj, tr->selector, tr->prop);
+ break;
+
+ }
+
+}
+
+static void trans_anim_start_cb(lv_anim_t * a)
+{
+ trans_t * tr = a->var;
+
+ lv_part_t part = lv_obj_style_get_selector_part(tr->selector);
+ tr->start_value = lv_obj_get_style_prop(tr->obj, part, tr->prop);
+
+ /*Init prop to an invalid values to be sure `trans_del` won't delete this added `tr`*/
+ lv_style_prop_t prop_tmp = tr->prop;
+ tr->prop = LV_STYLE_PROP_INV;
+
+ /*Delete the related transitions if any*/
+ trans_del(tr->obj, part, prop_tmp, tr);
+
+ tr->prop = prop_tmp;
+
+ _lv_obj_style_t * style_trans = get_trans_style(tr->obj, tr->selector);
+ lv_style_set_prop(style_trans->style, tr->prop, tr->start_value); /*Be sure `trans_style` has a valid value*/
+
+}
+
+static void trans_anim_ready_cb(lv_anim_t * a)
+{
+ trans_t * tr = a->var;
+ lv_obj_t * obj = tr->obj;
+ lv_style_prop_t prop = tr->prop;
+
+ /*Remove the transitioned property from trans. style
+ *if there no more transitions for this property
+ *It allows changing it by normal styles*/
+ bool running = false;
+ trans_t * tr_i;
+ _LV_LL_READ(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr_i) {
+ if(tr_i != tr && tr_i->obj == tr->obj && tr_i->selector == tr->selector && tr_i->prop == tr->prop) {
+ running = true;
+ break;
+ }
+ }
+
+ if(!running) {
+ uint32_t i;
+ for(i = 0; i < obj->style_cnt; i++) {
+ if(obj->styles[i].is_trans && obj->styles[i].selector == tr->selector) {
+ _lv_ll_remove(&LV_GC_ROOT(_lv_obj_style_trans_ll), tr);
+ lv_mem_free(tr);
+
+ _lv_obj_style_t * obj_style = &obj->styles[i];
+ lv_style_remove_prop(obj_style->style, prop);
+
+ if(lv_style_is_empty(obj->styles[i].style)) {
+ lv_obj_remove_style(obj, obj_style->style, obj_style->selector);
+
+ }
+ break;
+ }
+ }
+ }
+}
+
+static lv_layer_type_t calculate_layer_type(lv_obj_t * obj)
+{
+ if(lv_obj_get_style_transform_angle(obj, 0) != 0) return LV_LAYER_TYPE_TRANSFORM;
+ if(lv_obj_get_style_transform_zoom(obj, 0) != 256) return LV_LAYER_TYPE_TRANSFORM;
+ if(lv_obj_get_style_opa(obj, 0) != LV_OPA_COVER) return LV_LAYER_TYPE_SIMPLE;
+
+#if LV_DRAW_COMPLEX
+ if(lv_obj_get_style_blend_mode(obj, 0) != LV_BLEND_MODE_NORMAL) return LV_LAYER_TYPE_SIMPLE;
+#endif
+ return LV_LAYER_TYPE_NONE;
+}
+
+static void fade_anim_cb(void * obj, int32_t v)
+{
+ lv_obj_set_style_opa(obj, v, 0);
+}
+
+static void fade_in_anim_ready(lv_anim_t * a)
+{
+ lv_obj_remove_local_style_prop(a->var, LV_STYLE_OPA, 0);
+}
+
+
diff --git a/lib/lvgl/src/core/lv_obj_style.h b/lib/lvgl/src/core/lv_obj_style.h
new file mode 100644
index 00000000..5d122ca6
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_style.h
@@ -0,0 +1,248 @@
+/**
+ * @file lv_obj_style.h
+ *
+ */
+
+#ifndef LV_OBJ_STYLE_H
+#define LV_OBJ_STYLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdint.h>
+#include <stdbool.h>
+#include "../misc/lv_bidi.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+/*Can't include lv_obj.h because it includes this header file*/
+struct _lv_obj_t;
+
+typedef enum {
+ _LV_STYLE_STATE_CMP_SAME, /*The style properties in the 2 states are identical*/
+ _LV_STYLE_STATE_CMP_DIFF_REDRAW, /*The differences can be shown with a simple redraw*/
+ _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD, /*The differences can be shown with a simple redraw*/
+ _LV_STYLE_STATE_CMP_DIFF_LAYOUT, /*The differences can be shown with a simple redraw*/
+} _lv_style_state_cmp_t;
+
+typedef uint32_t lv_style_selector_t;
+
+typedef struct {
+ lv_style_t * style;
+ uint32_t selector : 24;
+ uint32_t is_local : 1;
+ uint32_t is_trans : 1;
+} _lv_obj_style_t;
+
+typedef struct {
+ uint16_t time;
+ uint16_t delay;
+ lv_style_selector_t selector;
+ lv_style_prop_t prop;
+ lv_anim_path_cb_t path_cb;
+#if LV_USE_USER_DATA
+ void * user_data;
+#endif
+} _lv_obj_style_transition_dsc_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Initialize the object related style manager module.
+ * Called by LVGL in `lv_init()`
+ */
+void _lv_obj_style_init(void);
+
+/**
+ * Add a style to an object.
+ * @param obj pointer to an object
+ * @param style pointer to a style to add
+ * @param selector OR-ed value of parts and state to which the style should be added
+ * @example lv_obj_add_style(btn, &style_btn, 0); //Default button style
+ * @example lv_obj_add_style(btn, &btn_red, LV_STATE_PRESSED); //Overwrite only some colors to red when pressed
+ */
+void lv_obj_add_style(struct _lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector);
+
+/**
+ * Add a style to an object.
+ * @param obj pointer to an object
+ * @param style pointer to a style to remove. Can be NULL to check only the selector
+ * @param selector OR-ed values of states and a part to remove only styles with matching selectors. LV_STATE_ANY and LV_PART_ANY can be used
+ * @example lv_obj_remove_style(obj, &style, LV_PART_ANY | LV_STATE_ANY); //Remove a specific style
+ * @example lv_obj_remove_style(obj, NULL, LV_PART_MAIN | LV_STATE_ANY); //Remove all styles from the main part
+ * @example lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY); //Remove all styles
+ */
+void lv_obj_remove_style(struct _lv_obj_t * obj, lv_style_t * style, lv_style_selector_t selector);
+
+/**
+ * Remove all styles from an object
+ * @param obj pointer to an object
+ */
+static inline void lv_obj_remove_style_all(struct _lv_obj_t * obj)
+{
+ lv_obj_remove_style(obj, NULL, LV_PART_ANY | LV_STATE_ANY);
+}
+
+/**
+ * Notify all object if a style is modified
+ * @param style pointer to a style. Only the objects with this style will be notified
+ * (NULL to notify all objects)
+ */
+void lv_obj_report_style_change(lv_style_t * style);
+
+/**
+ * Notify an object and its children about its style is modified.
+ * @param obj pointer to an object
+ * @param part the part whose style was changed. E.g. `LV_PART_ANY`, `LV_PART_MAIN`
+ * @param prop `LV_STYLE_PROP_ANY` or an `LV_STYLE_...` property.
+ * It is used to optimize what needs to be refreshed.
+ * `LV_STYLE_PROP_INV` to perform only a style cache update
+ */
+void lv_obj_refresh_style(struct _lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
+
+/**
+ * Enable or disable automatic style refreshing when a new style is added/removed to/from an object
+ * or any other style change happens.
+ * @param en true: enable refreshing; false: disable refreshing
+ */
+void lv_obj_enable_style_refresh(bool en);
+
+/**
+ * Get the value of a style property. The current state of the object will be considered.
+ * Inherited properties will be inherited.
+ * If a property is not set a default value will be returned.
+ * @param obj pointer to an object
+ * @param part a part from which the property should be get
+ * @param prop the property to get
+ * @return the value of the property.
+ * Should be read from the correct field of the `lv_style_value_t` according to the type of the property.
+ */
+lv_style_value_t lv_obj_get_style_prop(const struct _lv_obj_t * obj, lv_part_t part, lv_style_prop_t prop);
+
+/**
+ * Set local style property on an object's part and state.
+ * @param obj pointer to an object
+ * @param prop the property
+ * @param value value of the property. The correct element should be set according to the type of the property
+ * @param selector OR-ed value of parts and state for which the style should be set
+ */
+void lv_obj_set_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t value,
+ lv_style_selector_t selector);
+
+void lv_obj_set_local_style_prop_meta(struct _lv_obj_t * obj, lv_style_prop_t prop, uint16_t meta,
+ lv_style_selector_t selector);
+
+lv_style_res_t lv_obj_get_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_value_t * value,
+ lv_style_selector_t selector);
+
+/**
+ * Remove a local style property from a part of an object with a given state.
+ * @param obj pointer to an object
+ * @param prop a style property to remove.
+ * @param selector OR-ed value of parts and state for which the style should be removed
+ * @return true the property was found and removed; false: the property was not found
+ */
+bool lv_obj_remove_local_style_prop(struct _lv_obj_t * obj, lv_style_prop_t prop, lv_style_selector_t selector);
+
+/**
+ * Used internally for color filtering
+ */
+lv_style_value_t _lv_obj_style_apply_color_filter(const struct _lv_obj_t * obj, uint32_t part, lv_style_value_t v);
+
+/**
+ * Used internally to create a style transition
+ * @param obj
+ * @param part
+ * @param prev_state
+ * @param new_state
+ * @param tr
+ */
+void _lv_obj_style_create_transition(struct _lv_obj_t * obj, lv_part_t part, lv_state_t prev_state,
+ lv_state_t new_state, const _lv_obj_style_transition_dsc_t * tr);
+
+/**
+ * Used internally to compare the appearance of an object in 2 states
+ * @param obj
+ * @param state1
+ * @param state2
+ * @return
+ */
+_lv_style_state_cmp_t _lv_obj_style_state_compare(struct _lv_obj_t * obj, lv_state_t state1, lv_state_t state2);
+
+/**
+ * Fade in an an object and all its children.
+ * @param obj the object to fade in
+ * @param time time of fade
+ * @param delay delay to start the animation
+ */
+void lv_obj_fade_in(struct _lv_obj_t * obj, uint32_t time, uint32_t delay);
+
+/**
+ * Fade out an an object and all its children.
+ * @param obj the object to fade out
+ * @param time time of fade
+ * @param delay delay to start the animation
+ */
+void lv_obj_fade_out(struct _lv_obj_t * obj, uint32_t time, uint32_t delay);
+
+lv_state_t lv_obj_style_get_selector_state(lv_style_selector_t selector);
+
+lv_part_t lv_obj_style_get_selector_part(lv_style_selector_t selector);
+
+#include "lv_obj_style_gen.h"
+
+static inline void lv_obj_set_style_pad_all(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_obj_set_style_pad_left(obj, value, selector);
+ lv_obj_set_style_pad_right(obj, value, selector);
+ lv_obj_set_style_pad_top(obj, value, selector);
+ lv_obj_set_style_pad_bottom(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_pad_hor(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_obj_set_style_pad_left(obj, value, selector);
+ lv_obj_set_style_pad_right(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_pad_ver(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_obj_set_style_pad_top(obj, value, selector);
+ lv_obj_set_style_pad_bottom(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_pad_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_obj_set_style_pad_row(obj, value, selector);
+ lv_obj_set_style_pad_column(obj, value, selector);
+}
+
+static inline void lv_obj_set_style_size(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_obj_set_style_width(obj, value, selector);
+ lv_obj_set_style_height(obj, value, selector);
+}
+
+lv_text_align_t lv_obj_calculate_style_text_align(const struct _lv_obj_t * obj, lv_part_t part, const char * txt);
+
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_STYLE_H*/
diff --git a/lib/lvgl/src/core/lv_obj_style_gen.c b/lib/lvgl/src/core/lv_obj_style_gen.c
new file mode 100644
index 00000000..64a0bb28
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_style_gen.c
@@ -0,0 +1,673 @@
+#include "lv_obj.h"
+
+void lv_obj_set_style_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_min_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_MIN_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_max_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_MAX_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_HEIGHT, v, selector);
+}
+
+void lv_obj_set_style_min_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_MIN_HEIGHT, v, selector);
+}
+
+void lv_obj_set_style_max_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_MAX_HEIGHT, v, selector);
+}
+
+void lv_obj_set_style_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_X, v, selector);
+}
+
+void lv_obj_set_style_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_Y, v, selector);
+}
+
+void lv_obj_set_style_align(struct _lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_ALIGN, v, selector);
+}
+
+void lv_obj_set_style_transform_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_transform_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_HEIGHT, v, selector);
+}
+
+void lv_obj_set_style_translate_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_X, v, selector);
+}
+
+void lv_obj_set_style_translate_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSLATE_Y, v, selector);
+}
+
+void lv_obj_set_style_transform_zoom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_ZOOM, v, selector);
+}
+
+void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_ANGLE, v, selector);
+}
+
+void lv_obj_set_style_transform_pivot_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_PIVOT_X, v, selector);
+}
+
+void lv_obj_set_style_transform_pivot_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSFORM_PIVOT_Y, v, selector);
+}
+
+void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_TOP, v, selector);
+}
+
+void lv_obj_set_style_pad_bottom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_BOTTOM, v, selector);
+}
+
+void lv_obj_set_style_pad_left(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_LEFT, v, selector);
+}
+
+void lv_obj_set_style_pad_right(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_RIGHT, v, selector);
+}
+
+void lv_obj_set_style_pad_row(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_ROW, v, selector);
+}
+
+void lv_obj_set_style_pad_column(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_PAD_COLUMN, v, selector);
+}
+
+void lv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_COLOR, v, selector);
+}
+
+void lv_obj_set_style_bg_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_OPA, v, selector);
+}
+
+void lv_obj_set_style_bg_grad_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_COLOR, v, selector);
+}
+
+void lv_obj_set_style_bg_grad_dir(struct _lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_DIR, v, selector);
+}
+
+void lv_obj_set_style_bg_main_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_MAIN_STOP, v, selector);
+}
+
+void lv_obj_set_style_bg_grad_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD_STOP, v, selector);
+}
+
+void lv_obj_set_style_bg_grad(struct _lv_obj_t * obj, const lv_grad_dsc_t * value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_GRAD, v, selector);
+}
+
+void lv_obj_set_style_bg_dither_mode(struct _lv_obj_t * obj, lv_dither_mode_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_DITHER_MODE, v, selector);
+}
+
+void lv_obj_set_style_bg_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_SRC, v, selector);
+}
+
+void lv_obj_set_style_bg_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_OPA, v, selector);
+}
+
+void lv_obj_set_style_bg_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_RECOLOR, v, selector);
+}
+
+void lv_obj_set_style_bg_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_RECOLOR_OPA, v, selector);
+}
+
+void lv_obj_set_style_bg_img_tiled(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BG_IMG_TILED, v, selector);
+}
+
+void lv_obj_set_style_border_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_COLOR, v, selector);
+}
+
+void lv_obj_set_style_border_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_OPA, v, selector);
+}
+
+void lv_obj_set_style_border_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_border_side(struct _lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_SIDE, v, selector);
+}
+
+void lv_obj_set_style_border_post(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BORDER_POST, v, selector);
+}
+
+void lv_obj_set_style_outline_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_outline_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_COLOR, v, selector);
+}
+
+void lv_obj_set_style_outline_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_OPA, v, selector);
+}
+
+void lv_obj_set_style_outline_pad(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_OUTLINE_PAD, v, selector);
+}
+
+void lv_obj_set_style_shadow_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_shadow_ofs_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OFS_X, v, selector);
+}
+
+void lv_obj_set_style_shadow_ofs_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OFS_Y, v, selector);
+}
+
+void lv_obj_set_style_shadow_spread(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_SPREAD, v, selector);
+}
+
+void lv_obj_set_style_shadow_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_COLOR, v, selector);
+}
+
+void lv_obj_set_style_shadow_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_SHADOW_OPA, v, selector);
+}
+
+void lv_obj_set_style_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_OPA, v, selector);
+}
+
+void lv_obj_set_style_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_RECOLOR, v, selector);
+}
+
+void lv_obj_set_style_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_IMG_RECOLOR_OPA, v, selector);
+}
+
+void lv_obj_set_style_line_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_line_dash_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_DASH_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_line_dash_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_DASH_GAP, v, selector);
+}
+
+void lv_obj_set_style_line_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_ROUNDED, v, selector);
+}
+
+void lv_obj_set_style_line_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_COLOR, v, selector);
+}
+
+void lv_obj_set_style_line_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_LINE_OPA, v, selector);
+}
+
+void lv_obj_set_style_arc_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_WIDTH, v, selector);
+}
+
+void lv_obj_set_style_arc_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_ROUNDED, v, selector);
+}
+
+void lv_obj_set_style_arc_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_COLOR, v, selector);
+}
+
+void lv_obj_set_style_arc_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_OPA, v, selector);
+}
+
+void lv_obj_set_style_arc_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_ARC_IMG_SRC, v, selector);
+}
+
+void lv_obj_set_style_text_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .color = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_COLOR, v, selector);
+}
+
+void lv_obj_set_style_text_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_OPA, v, selector);
+}
+
+void lv_obj_set_style_text_font(struct _lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_FONT, v, selector);
+}
+
+void lv_obj_set_style_text_letter_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_LETTER_SPACE, v, selector);
+}
+
+void lv_obj_set_style_text_line_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_LINE_SPACE, v, selector);
+}
+
+void lv_obj_set_style_text_decor(struct _lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_DECOR, v, selector);
+}
+
+void lv_obj_set_style_text_align(struct _lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TEXT_ALIGN, v, selector);
+}
+
+void lv_obj_set_style_radius(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_RADIUS, v, selector);
+}
+
+void lv_obj_set_style_clip_corner(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_CLIP_CORNER, v, selector);
+}
+
+void lv_obj_set_style_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_OPA, v, selector);
+}
+
+void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_DSC, v, selector);
+}
+
+void lv_obj_set_style_color_filter_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_COLOR_FILTER_OPA, v, selector);
+}
+
+void lv_obj_set_style_anim(struct _lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM, v, selector);
+}
+
+void lv_obj_set_style_anim_time(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM_TIME, v, selector);
+}
+
+void lv_obj_set_style_anim_speed(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_ANIM_SPEED, v, selector);
+}
+
+void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .ptr = value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_TRANSITION, v, selector);
+}
+
+void lv_obj_set_style_blend_mode(struct _lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BLEND_MODE, v, selector);
+}
+
+void lv_obj_set_style_layout(struct _lv_obj_t * obj, uint16_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_LAYOUT, v, selector);
+}
+
+void lv_obj_set_style_base_dir(struct _lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector)
+{
+ lv_style_value_t v = {
+ .num = (int32_t)value
+ };
+ lv_obj_set_local_style_prop(obj, LV_STYLE_BASE_DIR, v, selector);
+}
diff --git a/lib/lvgl/src/core/lv_obj_style_gen.h b/lib/lvgl/src/core/lv_obj_style_gen.h
new file mode 100644
index 00000000..576927d9
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_style_gen.h
@@ -0,0 +1,648 @@
+static inline lv_coord_t lv_obj_get_style_width(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_WIDTH);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_min_width(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_WIDTH);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_max_width(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_WIDTH);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_height(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_HEIGHT);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_min_height(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MIN_HEIGHT);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_max_height(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_MAX_HEIGHT);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_x(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_X);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_y(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_Y);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_align_t lv_obj_get_style_align(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ALIGN);
+ return (lv_align_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_transform_width(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_WIDTH);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_transform_height(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_HEIGHT);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_translate_x(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_X);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_translate_y(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSLATE_Y);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_transform_zoom(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_ZOOM);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_transform_angle(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_ANGLE);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_transform_pivot_x(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_PIVOT_X);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_transform_pivot_y(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSFORM_PIVOT_Y);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_pad_top(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_TOP);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_pad_bottom(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_BOTTOM);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_pad_left(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_LEFT);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_pad_right(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_RIGHT);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_pad_row(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_ROW);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_pad_column(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_PAD_COLUMN);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_color(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR);
+ return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_COLOR));
+ return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_bg_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_grad_color(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR);
+ return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_grad_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_COLOR));
+ return v.color;
+}
+
+static inline lv_grad_dir_t lv_obj_get_style_bg_grad_dir(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_DIR);
+ return (lv_grad_dir_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_bg_main_stop(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_MAIN_STOP);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_bg_grad_stop(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD_STOP);
+ return (lv_coord_t)v.num;
+}
+
+static inline const lv_grad_dsc_t * lv_obj_get_style_bg_grad(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_GRAD);
+ return (const lv_grad_dsc_t *)v.ptr;
+}
+
+static inline lv_dither_mode_t lv_obj_get_style_bg_dither_mode(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_DITHER_MODE);
+ return (lv_dither_mode_t)v.num;
+}
+
+static inline const void * lv_obj_get_style_bg_img_src(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_SRC);
+ return (const void *)v.ptr;
+}
+
+static inline lv_opa_t lv_obj_get_style_bg_img_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_img_recolor(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR);
+ return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_bg_img_recolor_filtered(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR));
+ return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_bg_img_recolor_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_RECOLOR_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline bool lv_obj_get_style_bg_img_tiled(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BG_IMG_TILED);
+ return (bool)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_border_color(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR);
+ return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_border_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_COLOR));
+ return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_border_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_border_width(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_WIDTH);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_border_side_t lv_obj_get_style_border_side(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_SIDE);
+ return (lv_border_side_t)v.num;
+}
+
+static inline bool lv_obj_get_style_border_post(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BORDER_POST);
+ return (bool)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_outline_width(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_WIDTH);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_outline_color(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR);
+ return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_outline_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_COLOR));
+ return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_outline_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_outline_pad(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OUTLINE_PAD);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_shadow_width(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_WIDTH);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_shadow_ofs_x(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFS_X);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_shadow_ofs_y(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OFS_Y);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_shadow_spread(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_SPREAD);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_shadow_color(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR);
+ return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_shadow_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_COLOR));
+ return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_shadow_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_SHADOW_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline lv_opa_t lv_obj_get_style_img_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_img_recolor(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR);
+ return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_img_recolor_filtered(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR));
+ return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_img_recolor_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_IMG_RECOLOR_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_line_width(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_WIDTH);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_line_dash_width(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_WIDTH);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_line_dash_gap(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_DASH_GAP);
+ return (lv_coord_t)v.num;
+}
+
+static inline bool lv_obj_get_style_line_rounded(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_ROUNDED);
+ return (bool)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_line_color(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR);
+ return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_line_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_COLOR));
+ return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_line_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LINE_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_arc_width(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_WIDTH);
+ return (lv_coord_t)v.num;
+}
+
+static inline bool lv_obj_get_style_arc_rounded(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_ROUNDED);
+ return (bool)v.num;
+}
+
+static inline lv_color_t lv_obj_get_style_arc_color(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR);
+ return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_arc_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_COLOR));
+ return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_arc_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline const void * lv_obj_get_style_arc_img_src(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ARC_IMG_SRC);
+ return (const void *)v.ptr;
+}
+
+static inline lv_color_t lv_obj_get_style_text_color(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR);
+ return v.color;
+}
+
+static inline lv_color_t lv_obj_get_style_text_color_filtered(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = _lv_obj_style_apply_color_filter(obj, part, lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_COLOR));
+ return v.color;
+}
+
+static inline lv_opa_t lv_obj_get_style_text_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline const lv_font_t * lv_obj_get_style_text_font(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_FONT);
+ return (const lv_font_t *)v.ptr;
+}
+
+static inline lv_coord_t lv_obj_get_style_text_letter_space(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LETTER_SPACE);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_text_line_space(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_LINE_SPACE);
+ return (lv_coord_t)v.num;
+}
+
+static inline lv_text_decor_t lv_obj_get_style_text_decor(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_DECOR);
+ return (lv_text_decor_t)v.num;
+}
+
+static inline lv_text_align_t lv_obj_get_style_text_align(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TEXT_ALIGN);
+ return (lv_text_align_t)v.num;
+}
+
+static inline lv_coord_t lv_obj_get_style_radius(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_RADIUS);
+ return (lv_coord_t)v.num;
+}
+
+static inline bool lv_obj_get_style_clip_corner(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_CLIP_CORNER);
+ return (bool)v.num;
+}
+
+static inline lv_opa_t lv_obj_get_style_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline const lv_color_filter_dsc_t * lv_obj_get_style_color_filter_dsc(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_DSC);
+ return (const lv_color_filter_dsc_t *)v.ptr;
+}
+
+static inline lv_opa_t lv_obj_get_style_color_filter_opa(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_COLOR_FILTER_OPA);
+ return (lv_opa_t)v.num;
+}
+
+static inline const lv_anim_t * lv_obj_get_style_anim(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM);
+ return (const lv_anim_t *)v.ptr;
+}
+
+static inline uint32_t lv_obj_get_style_anim_time(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_TIME);
+ return (uint32_t)v.num;
+}
+
+static inline uint32_t lv_obj_get_style_anim_speed(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_ANIM_SPEED);
+ return (uint32_t)v.num;
+}
+
+static inline const lv_style_transition_dsc_t * lv_obj_get_style_transition(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_TRANSITION);
+ return (const lv_style_transition_dsc_t *)v.ptr;
+}
+
+static inline lv_blend_mode_t lv_obj_get_style_blend_mode(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BLEND_MODE);
+ return (lv_blend_mode_t)v.num;
+}
+
+static inline uint16_t lv_obj_get_style_layout(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_LAYOUT);
+ return (uint16_t)v.num;
+}
+
+static inline lv_base_dir_t lv_obj_get_style_base_dir(const struct _lv_obj_t * obj, uint32_t part)
+{
+ lv_style_value_t v = lv_obj_get_style_prop(obj, part, LV_STYLE_BASE_DIR);
+ return (lv_base_dir_t)v.num;
+}
+
+void lv_obj_set_style_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_min_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_max_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_min_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_max_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_align(struct _lv_obj_t * obj, lv_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_height(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_translate_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_translate_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_zoom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_angle(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_pivot_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transform_pivot_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_top(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_bottom(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_left(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_right(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_row(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_pad_column(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_grad_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_grad_dir(struct _lv_obj_t * obj, lv_grad_dir_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_main_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_grad_stop(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_grad(struct _lv_obj_t * obj, const lv_grad_dsc_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_dither_mode(struct _lv_obj_t * obj, lv_dither_mode_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_bg_img_tiled(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
+void lv_obj_set_style_border_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_border_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_border_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_border_side(struct _lv_obj_t * obj, lv_border_side_t value, lv_style_selector_t selector);
+void lv_obj_set_style_border_post(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
+void lv_obj_set_style_outline_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_outline_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_outline_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_outline_pad(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_ofs_x(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_ofs_y(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_spread(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_shadow_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_img_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_img_recolor(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_img_recolor_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_line_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_line_dash_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_line_dash_gap(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_line_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
+void lv_obj_set_style_line_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_line_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_arc_width(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_arc_rounded(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
+void lv_obj_set_style_arc_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_arc_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_arc_img_src(struct _lv_obj_t * obj, const void * value, lv_style_selector_t selector);
+void lv_obj_set_style_text_color(struct _lv_obj_t * obj, lv_color_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_font(struct _lv_obj_t * obj, const lv_font_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_text_letter_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_line_space(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_decor(struct _lv_obj_t * obj, lv_text_decor_t value, lv_style_selector_t selector);
+void lv_obj_set_style_text_align(struct _lv_obj_t * obj, lv_text_align_t value, lv_style_selector_t selector);
+void lv_obj_set_style_radius(struct _lv_obj_t * obj, lv_coord_t value, lv_style_selector_t selector);
+void lv_obj_set_style_clip_corner(struct _lv_obj_t * obj, bool value, lv_style_selector_t selector);
+void lv_obj_set_style_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_color_filter_dsc(struct _lv_obj_t * obj, const lv_color_filter_dsc_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_color_filter_opa(struct _lv_obj_t * obj, lv_opa_t value, lv_style_selector_t selector);
+void lv_obj_set_style_anim(struct _lv_obj_t * obj, const lv_anim_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_anim_time(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_anim_speed(struct _lv_obj_t * obj, uint32_t value, lv_style_selector_t selector);
+void lv_obj_set_style_transition(struct _lv_obj_t * obj, const lv_style_transition_dsc_t * value, lv_style_selector_t selector);
+void lv_obj_set_style_blend_mode(struct _lv_obj_t * obj, lv_blend_mode_t value, lv_style_selector_t selector);
+void lv_obj_set_style_layout(struct _lv_obj_t * obj, uint16_t value, lv_style_selector_t selector);
+void lv_obj_set_style_base_dir(struct _lv_obj_t * obj, lv_base_dir_t value, lv_style_selector_t selector);
diff --git a/lib/lvgl/src/core/lv_obj_tree.c b/lib/lvgl/src/core/lv_obj_tree.c
new file mode 100644
index 00000000..d3ad16ae
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_tree.c
@@ -0,0 +1,452 @@
+/**
+ * @file lv_obj_tree.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stdlib.h>
+
+#include "lv_obj.h"
+#include "lv_indev.h"
+#include "../misc/lv_anim.h"
+#include "../misc/lv_gc.h"
+#include "../misc/lv_async.h"
+
+/*********************
+ * DEFINES
+ *********************/
+#define MY_CLASS &lv_obj_class
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_obj_del_async_cb(void * obj);
+static void obj_del_core(lv_obj_t * obj);
+static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+void lv_obj_del(lv_obj_t * obj)
+{
+ LV_LOG_TRACE("begin (delete %p)", (void *)obj);
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ lv_obj_invalidate(obj);
+
+ lv_obj_t * par = lv_obj_get_parent(obj);
+ if(par) {
+ lv_obj_scrollbar_invalidate(par);
+ }
+
+ lv_disp_t * disp = NULL;
+ bool act_scr_del = false;
+ if(par == NULL) {
+ disp = lv_obj_get_disp(obj);
+ if(!disp) return; /*Shouldn't happen*/
+ if(disp->act_scr == obj) act_scr_del = true;
+ }
+
+ obj_del_core(obj);
+
+ /*Call the ancestor's event handler to the parent to notify it about the child delete*/
+ if(par) {
+ lv_obj_update_layout(par);
+ lv_obj_readjust_scroll(par, LV_ANIM_OFF);
+ lv_obj_scrollbar_invalidate(par);
+ lv_event_send(par, LV_EVENT_CHILD_CHANGED, NULL);
+ lv_event_send(par, LV_EVENT_CHILD_DELETED, NULL);
+ }
+
+ /*Handle if the active screen was deleted*/
+ if(act_scr_del) {
+ LV_LOG_WARN("the active screen was deleted");
+ disp->act_scr = NULL;
+ }
+
+ LV_ASSERT_MEM_INTEGRITY();
+ LV_LOG_TRACE("finished (delete %p)", (void *)obj);
+}
+
+void lv_obj_clean(lv_obj_t * obj)
+{
+ LV_LOG_TRACE("begin (delete %p)", (void *)obj);
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_obj_invalidate(obj);
+
+ lv_obj_t * child = lv_obj_get_child(obj, 0);
+ while(child) {
+ obj_del_core(child);
+ child = lv_obj_get_child(obj, 0);
+ }
+ /*Just to remove scroll animations if any*/
+ lv_obj_scroll_to(obj, 0, 0, LV_ANIM_OFF);
+ if(obj->spec_attr) {
+ obj->spec_attr->scroll.x = 0;
+ obj->spec_attr->scroll.y = 0;
+ }
+
+ LV_ASSERT_MEM_INTEGRITY();
+
+ LV_LOG_TRACE("finished (delete %p)", (void *)obj);
+}
+
+void lv_obj_del_delayed(lv_obj_t * obj, uint32_t delay_ms)
+{
+ lv_anim_t a;
+ lv_anim_init(&a);
+ lv_anim_set_var(&a, obj);
+ lv_anim_set_exec_cb(&a, NULL);
+ lv_anim_set_time(&a, 1);
+ lv_anim_set_delay(&a, delay_ms);
+ lv_anim_set_ready_cb(&a, lv_obj_del_anim_ready_cb);
+ lv_anim_start(&a);
+}
+
+void lv_obj_del_anim_ready_cb(lv_anim_t * a)
+{
+ lv_obj_del(a->var);
+}
+
+void lv_obj_del_async(lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ lv_async_call(lv_obj_del_async_cb, obj);
+}
+
+void lv_obj_set_parent(lv_obj_t * obj, lv_obj_t * parent)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ LV_ASSERT_OBJ(parent, MY_CLASS);
+
+ if(obj->parent == NULL) {
+ LV_LOG_WARN("Can't set the parent of a screen");
+ return;
+ }
+
+ if(parent == NULL) {
+ LV_LOG_WARN("Can't set parent == NULL to an object");
+ return;
+ }
+
+ lv_obj_invalidate(obj);
+
+ lv_obj_allocate_spec_attr(parent);
+
+ lv_obj_t * old_parent = obj->parent;
+ /*Remove the object from the old parent's child list*/
+ int32_t i;
+ for(i = lv_obj_get_index(obj); i <= (int32_t)lv_obj_get_child_cnt(old_parent) - 2; i++) {
+ old_parent->spec_attr->children[i] = old_parent->spec_attr->children[i + 1];
+ }
+ old_parent->spec_attr->child_cnt--;
+ if(old_parent->spec_attr->child_cnt) {
+ old_parent->spec_attr->children = lv_mem_realloc(old_parent->spec_attr->children,
+ old_parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
+ }
+ else {
+ lv_mem_free(old_parent->spec_attr->children);
+ old_parent->spec_attr->children = NULL;
+ }
+
+ /*Add the child to the new parent as the last (newest child)*/
+ parent->spec_attr->child_cnt++;
+ parent->spec_attr->children = lv_mem_realloc(parent->spec_attr->children,
+ parent->spec_attr->child_cnt * (sizeof(lv_obj_t *)));
+ parent->spec_attr->children[lv_obj_get_child_cnt(parent) - 1] = obj;
+
+ obj->parent = parent;
+
+ /*Notify the original parent because one of its children is lost*/
+ lv_obj_readjust_scroll(old_parent, LV_ANIM_OFF);
+ lv_obj_scrollbar_invalidate(old_parent);
+ lv_event_send(old_parent, LV_EVENT_CHILD_CHANGED, obj);
+ lv_event_send(old_parent, LV_EVENT_CHILD_DELETED, NULL);
+
+ /*Notify the new parent about the child*/
+ lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj);
+ lv_event_send(parent, LV_EVENT_CHILD_CREATED, NULL);
+
+ lv_obj_mark_layout_as_dirty(obj);
+
+ lv_obj_invalidate(obj);
+}
+
+void lv_obj_move_to_index(lv_obj_t * obj, int32_t index)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ if(index < 0) {
+ index = lv_obj_get_child_cnt(lv_obj_get_parent(obj)) + index;
+ }
+
+ const int32_t old_index = lv_obj_get_index(obj);
+
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+
+ if(index < 0) return;
+ if(index >= (int32_t) lv_obj_get_child_cnt(parent)) return;
+ if(index == old_index) return;
+
+ int32_t i = old_index;
+ if(index < old_index) {
+ while(i > index) {
+ parent->spec_attr->children[i] = parent->spec_attr->children[i - 1];
+ i--;
+ }
+ }
+ else {
+ while(i < index) {
+ parent->spec_attr->children[i] = parent->spec_attr->children[i + 1];
+ i++;
+ }
+ }
+
+ parent->spec_attr->children[index] = obj;
+ lv_event_send(parent, LV_EVENT_CHILD_CHANGED, NULL);
+ lv_obj_invalidate(parent);
+}
+
+void lv_obj_swap(lv_obj_t * obj1, lv_obj_t * obj2)
+{
+ LV_ASSERT_OBJ(obj1, MY_CLASS);
+ LV_ASSERT_OBJ(obj2, MY_CLASS);
+
+ lv_obj_t * parent = lv_obj_get_parent(obj1);
+ lv_obj_t * parent2 = lv_obj_get_parent(obj2);
+
+ uint_fast32_t index1 = lv_obj_get_index(obj1);
+ uint_fast32_t index2 = lv_obj_get_index(obj2);
+
+ lv_event_send(parent2, LV_EVENT_CHILD_DELETED, obj2);
+ lv_event_send(parent, LV_EVENT_CHILD_DELETED, obj1);
+
+ parent->spec_attr->children[index1] = obj2;
+ parent2->spec_attr->children[index2] = obj1;
+
+ lv_event_send(parent, LV_EVENT_CHILD_CHANGED, obj2);
+ lv_event_send(parent, LV_EVENT_CHILD_CREATED, obj2);
+ lv_event_send(parent2, LV_EVENT_CHILD_CHANGED, obj1);
+ lv_event_send(parent2, LV_EVENT_CHILD_CREATED, obj1);
+
+ lv_obj_invalidate(parent);
+
+ if(parent != parent2) {
+ lv_obj_invalidate(parent2);
+ }
+ lv_group_swap_obj(obj1, obj2);
+}
+
+lv_obj_t * lv_obj_get_screen(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ const lv_obj_t * par = obj;
+ const lv_obj_t * act_par;
+
+ do {
+ act_par = par;
+ par = lv_obj_get_parent(act_par);
+ } while(par != NULL);
+
+ return (lv_obj_t *)act_par;
+}
+
+lv_disp_t * lv_obj_get_disp(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ const lv_obj_t * scr;
+
+ if(obj->parent == NULL) scr = obj; /*`obj` is a screen*/
+ else scr = lv_obj_get_screen(obj); /*get the screen of `obj`*/
+
+ lv_disp_t * d;
+ _LV_LL_READ(&LV_GC_ROOT(_lv_disp_ll), d) {
+ uint32_t i;
+ for(i = 0; i < d->screen_cnt; i++) {
+ if(d->screens[i] == scr) return d;
+ }
+ }
+
+ LV_LOG_WARN("No screen found");
+ return NULL;
+}
+
+lv_obj_t * lv_obj_get_parent(const lv_obj_t * obj)
+{
+ if(obj == NULL) return NULL;
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ return obj->parent;
+}
+
+lv_obj_t * lv_obj_get_child(const lv_obj_t * obj, int32_t id)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ if(obj->spec_attr == NULL) return NULL;
+
+ uint32_t idu;
+ if(id < 0) {
+ id = obj->spec_attr->child_cnt + id;
+ if(id < 0) return NULL;
+ idu = (uint32_t) id;
+ }
+ else {
+ idu = id;
+ }
+
+ if(idu >= obj->spec_attr->child_cnt) return NULL;
+ else return obj->spec_attr->children[id];
+}
+
+uint32_t lv_obj_get_child_cnt(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+ if(obj->spec_attr == NULL) return 0;
+ return obj->spec_attr->child_cnt;
+}
+
+uint32_t lv_obj_get_index(const lv_obj_t * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_obj_t * parent = lv_obj_get_parent(obj);
+ if(parent == NULL) return 0;
+
+ uint32_t i = 0;
+ for(i = 0; i < lv_obj_get_child_cnt(parent); i++) {
+ if(lv_obj_get_child(parent, i) == obj) return i;
+ }
+
+ return 0xFFFFFFFF; /*Shouldn't happen*/
+}
+
+void lv_obj_tree_walk(lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data)
+{
+ walk_core(start_obj, cb, user_data);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void lv_obj_del_async_cb(void * obj)
+{
+ LV_ASSERT_OBJ(obj, MY_CLASS);
+
+ lv_obj_del(obj);
+}
+
+static void obj_del_core(lv_obj_t * obj)
+{
+ /*Let the user free the resources used in `LV_EVENT_DELETE`*/
+ lv_res_t res = lv_event_send(obj, LV_EVENT_DELETE, NULL);
+ if(res == LV_RES_INV) return;
+
+ /*Recursively delete the children*/
+ lv_obj_t * child = lv_obj_get_child(obj, 0);
+ while(child) {
+ obj_del_core(child);
+ child = lv_obj_get_child(obj, 0);
+ }
+
+ lv_group_t * group = lv_obj_get_group(obj);
+
+ /*Reset all input devices if the object to delete is used*/
+ lv_indev_t * indev = lv_indev_get_next(NULL);
+ while(indev) {
+ if(indev->proc.types.pointer.act_obj == obj || indev->proc.types.pointer.last_obj == obj) {
+ lv_indev_reset(indev, obj);
+ }
+ if(indev->proc.types.pointer.last_pressed == obj) {
+ indev->proc.types.pointer.last_pressed = NULL;
+ }
+
+ if(indev->group == group && obj == lv_indev_get_obj_act()) {
+ lv_indev_reset(indev, obj);
+ }
+ indev = lv_indev_get_next(indev);
+ }
+
+ /*All children deleted. Now clean up the object specific data*/
+ _lv_obj_destruct(obj);
+
+ /*Remove the screen for the screen list*/
+ if(obj->parent == NULL) {
+ lv_disp_t * disp = lv_obj_get_disp(obj);
+ uint32_t i;
+ /*Find the screen in the list*/
+ for(i = 0; i < disp->screen_cnt; i++) {
+ if(disp->screens[i] == obj) break;
+ }
+
+ uint32_t id = i;
+ for(i = id; i < disp->screen_cnt - 1; i++) {
+ disp->screens[i] = disp->screens[i + 1];
+ }
+ disp->screen_cnt--;
+ disp->screens = lv_mem_realloc(disp->screens, disp->screen_cnt * sizeof(lv_obj_t *));
+ }
+ /*Remove the object from the child list of its parent*/
+ else {
+ uint32_t id = lv_obj_get_index(obj);
+ uint32_t i;
+ for(i = id; i < obj->parent->spec_attr->child_cnt - 1; i++) {
+ obj->parent->spec_attr->children[i] = obj->parent->spec_attr->children[i + 1];
+ }
+ obj->parent->spec_attr->child_cnt--;
+ obj->parent->spec_attr->children = lv_mem_realloc(obj->parent->spec_attr->children,
+ obj->parent->spec_attr->child_cnt * sizeof(lv_obj_t *));
+ }
+
+ /*Free the object itself*/
+ lv_mem_free(obj);
+}
+
+
+static lv_obj_tree_walk_res_t walk_core(lv_obj_t * obj, lv_obj_tree_walk_cb_t cb, void * user_data)
+{
+ lv_obj_tree_walk_res_t res = LV_OBJ_TREE_WALK_NEXT;
+
+ if(obj == NULL) {
+ lv_disp_t * disp = lv_disp_get_next(NULL);
+ while(disp) {
+ uint32_t i;
+ for(i = 0; i < disp->screen_cnt; i++) {
+ walk_core(disp->screens[i], cb, user_data);
+ }
+ disp = lv_disp_get_next(disp);
+ }
+ return LV_OBJ_TREE_WALK_END; /*The value doesn't matter as it wasn't called recursively*/
+ }
+
+ res = cb(obj, user_data);
+
+ if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
+
+ if(res != LV_OBJ_TREE_WALK_SKIP_CHILDREN) {
+ uint32_t i;
+ for(i = 0; i < lv_obj_get_child_cnt(obj); i++) {
+ res = walk_core(lv_obj_get_child(obj, i), cb, user_data);
+ if(res == LV_OBJ_TREE_WALK_END) return LV_OBJ_TREE_WALK_END;
+ }
+ }
+ return LV_OBJ_TREE_WALK_NEXT;
+}
diff --git a/lib/lvgl/src/core/lv_obj_tree.h b/lib/lvgl/src/core/lv_obj_tree.h
new file mode 100644
index 00000000..bee9e160
--- /dev/null
+++ b/lib/lvgl/src/core/lv_obj_tree.h
@@ -0,0 +1,172 @@
+/**
+ * @file struct _lv_obj_tree.h
+ *
+ */
+
+#ifndef LV_OBJ_TREE_H
+#define LV_OBJ_TREE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stddef.h>
+#include <stdbool.h>
+
+/*********************
+ * DEFINES
+ *********************/
+
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_obj_t;
+struct _lv_obj_class_t;
+
+typedef enum {
+ LV_OBJ_TREE_WALK_NEXT,
+ LV_OBJ_TREE_WALK_SKIP_CHILDREN,
+ LV_OBJ_TREE_WALK_END,
+} lv_obj_tree_walk_res_t;
+
+typedef lv_obj_tree_walk_res_t (*lv_obj_tree_walk_cb_t)(struct _lv_obj_t *, void *);
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Delete an object and all of its children.
+ * Also remove the objects from their group and remove all animations (if any).
+ * Send `LV_EVENT_DELETED` to deleted objects.
+ * @param obj pointer to an object
+ */
+void lv_obj_del(struct _lv_obj_t * obj);
+
+/**
+ * Delete all children of an object.
+ * Also remove the objects from their group and remove all animations (if any).
+ * Send `LV_EVENT_DELETED` to deleted objects.
+ * @param obj pointer to an object
+ */
+void lv_obj_clean(struct _lv_obj_t * obj);
+
+/**
+ * Delete an object after some delay
+ * @param obj pointer to an object
+ * @param delay_ms time to wait before delete in milliseconds
+ */
+void lv_obj_del_delayed(struct _lv_obj_t * obj, uint32_t delay_ms);
+
+/**
+ * A function to be easily used in animation ready callback to delete an object when the animation is ready
+ * @param a pointer to the animation
+ */
+void lv_obj_del_anim_ready_cb(lv_anim_t * a);
+
+/**
+ * Helper function for asynchronously deleting objects.
+ * Useful for cases where you can't delete an object directly in an `LV_EVENT_DELETE` handler (i.e. parent).
+ * @param obj object to delete
+ * @see lv_async_call
+ */
+void lv_obj_del_async(struct _lv_obj_t * obj);
+
+/**
+ * Move the parent of an object. The relative coordinates will be kept.
+ *
+ * @param obj pointer to an object whose parent needs to be changed
+ * @param parent pointer to the new parent
+ */
+void lv_obj_set_parent(struct _lv_obj_t * obj, struct _lv_obj_t * parent);
+
+/**
+ * Swap the positions of two objects.
+ * When used in listboxes, it can be used to sort the listbox items.
+ * @param obj1 pointer to the first object
+ * @param obj2 pointer to the second object
+ */
+void lv_obj_swap(struct _lv_obj_t * obj1, struct _lv_obj_t * obj2);
+
+/**
+ * moves the object to the given index in its parent.
+ * When used in listboxes, it can be used to sort the listbox items.
+ * @param obj pointer to the object to be moved.
+ * @param index new index in parent. -1 to count from the back
+ * @note to move to the background: lv_obj_move_to_index(obj, 0)
+ * @note to move forward (up): lv_obj_move_to_index(obj, lv_obj_get_index(obj) - 1)
+ */
+void lv_obj_move_to_index(struct _lv_obj_t * obj, int32_t index);
+
+/**
+ * Get the screen of an object
+ * @param obj pointer to an object
+ * @return pointer to the object's screen
+ */
+struct _lv_obj_t * lv_obj_get_screen(const struct _lv_obj_t * obj);
+
+/**
+ * Get the display of the object
+ * @param obj pointer to an object
+ * @return pointer to the object's display
+ */
+lv_disp_t * lv_obj_get_disp(const struct _lv_obj_t * obj);
+
+/**
+ * Get the parent of an object
+ * @param obj pointer to an object
+ * @return the parent of the object. (NULL if `obj` was a screen)
+ */
+struct _lv_obj_t * lv_obj_get_parent(const struct _lv_obj_t * obj);
+
+/**
+ * Get the child of an object by the child's index.
+ * @param obj pointer to an object whose child should be get
+ * @param id the index of the child.
+ * 0: the oldest (firstly created) child
+ * 1: the second oldest
+ * child count-1: the youngest
+ * -1: the youngest
+ * -2: the second youngest
+ * @return pointer to the child or NULL if the index was invalid
+ */
+struct _lv_obj_t * lv_obj_get_child(const struct _lv_obj_t * obj, int32_t id);
+
+/**
+ * Get the number of children
+ * @param obj pointer to an object
+ * @return the number of children
+ */
+uint32_t lv_obj_get_child_cnt(const struct _lv_obj_t * obj);
+
+/**
+ * Get the index of a child.
+ * @param obj pointer to an object
+ * @return the child index of the object.
+ * E.g. 0: the oldest (firstly created child)
+ */
+uint32_t lv_obj_get_index(const struct _lv_obj_t * obj);
+
+/**
+ * Iterate through all children of any object.
+ * @param start_obj start integrating from this object
+ * @param cb call this callback on the objects
+ * @param user_data pointer to any user related data (will be passed to `cb`)
+ */
+void lv_obj_tree_walk(struct _lv_obj_t * start_obj, lv_obj_tree_walk_cb_t cb, void * user_data);
+
+/**********************
+ * MACROS
+ **********************/
+
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_OBJ_TREE_H*/
diff --git a/lib/lvgl/src/core/lv_refr.c b/lib/lvgl/src/core/lv_refr.c
new file mode 100644
index 00000000..1a56fed0
--- /dev/null
+++ b/lib/lvgl/src/core/lv_refr.c
@@ -0,0 +1,1255 @@
+/**
+ * @file lv_refr.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <stddef.h>
+#include "lv_refr.h"
+#include "lv_disp.h"
+#include "../hal/lv_hal_tick.h"
+#include "../hal/lv_hal_disp.h"
+#include "../misc/lv_timer.h"
+#include "../misc/lv_mem.h"
+#include "../misc/lv_math.h"
+#include "../misc/lv_gc.h"
+#include "../draw/lv_draw.h"
+#include "../font/lv_font_fmt_txt.h"
+#include "../extra/others/snapshot/lv_snapshot.h"
+
+#if LV_USE_PERF_MONITOR || LV_USE_MEM_MONITOR
+ #include "../widgets/lv_label.h"
+#endif
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+typedef struct {
+ uint32_t perf_last_time;
+ uint32_t elaps_sum;
+ uint32_t frame_cnt;
+ uint32_t fps_sum_cnt;
+ uint32_t fps_sum_all;
+#if LV_USE_LABEL
+ lv_obj_t * perf_label;
+#endif
+} perf_monitor_t;
+
+typedef struct {
+ uint32_t mem_last_time;
+#if LV_USE_LABEL
+ lv_obj_t * mem_label;
+#endif
+} mem_monitor_t;
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void lv_refr_join_area(void);
+static void refr_invalid_areas(void);
+static void refr_area(const lv_area_t * area_p);
+static void refr_area_part(lv_draw_ctx_t * draw_ctx);
+static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj);
+static void refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj);
+static void refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj);
+static uint32_t get_max_row(lv_disp_t * disp, lv_coord_t area_w, lv_coord_t area_h);
+static void draw_buf_flush(lv_disp_t * disp);
+static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p);
+
+#if LV_USE_PERF_MONITOR
+ static void perf_monitor_init(perf_monitor_t * perf_monitor);
+#endif
+#if LV_USE_MEM_MONITOR
+ static void mem_monitor_init(mem_monitor_t * mem_monitor);
+#endif
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+static uint32_t px_num;
+static lv_disp_t * disp_refr; /*Display being refreshed*/
+
+#if LV_USE_PERF_MONITOR
+ static perf_monitor_t perf_monitor;
+#endif
+
+#if LV_USE_MEM_MONITOR
+ static mem_monitor_t mem_monitor;
+#endif
+
+/**********************
+ * MACROS
+ **********************/
+#if LV_LOG_TRACE_DISP_REFR
+ #define REFR_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
+#else
+ #define REFR_TRACE(...)
+#endif
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize the screen refresh subsystem
+ */
+void _lv_refr_init(void)
+{
+#if LV_USE_PERF_MONITOR
+ perf_monitor_init(&perf_monitor);
+#endif
+#if LV_USE_MEM_MONITOR
+ mem_monitor_init(&mem_monitor);
+#endif
+}
+
+void lv_refr_now(lv_disp_t * disp)
+{
+ lv_anim_refr_now();
+
+ if(disp) {
+ if(disp->refr_timer) _lv_disp_refr_timer(disp->refr_timer);
+ }
+ else {
+ lv_disp_t * d;
+ d = lv_disp_get_next(NULL);
+ while(d) {
+ if(d->refr_timer) _lv_disp_refr_timer(d->refr_timer);
+ d = lv_disp_get_next(d);
+ }
+ }
+}
+
+void lv_obj_redraw(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj)
+{
+ const lv_area_t * clip_area_ori = draw_ctx->clip_area;
+ lv_area_t clip_coords_for_obj;
+
+ /*Truncate the clip area to `obj size + ext size` area*/
+ lv_area_t obj_coords_ext;
+ lv_obj_get_coords(obj, &obj_coords_ext);
+ lv_coord_t ext_draw_size = _lv_obj_get_ext_draw_size(obj);
+ lv_area_increase(&obj_coords_ext, ext_draw_size, ext_draw_size);
+ bool com_clip_res = _lv_area_intersect(&clip_coords_for_obj, clip_area_ori, &obj_coords_ext);
+ /*If the object is visible on the current clip area OR has overflow visible draw it.
+ *With overflow visible drawing should happen to apply the masks which might affect children */
+ bool should_draw = com_clip_res || lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE);
+ if(should_draw) {
+ draw_ctx->clip_area = &clip_coords_for_obj;
+
+ lv_event_send(obj, LV_EVENT_DRAW_MAIN_BEGIN, draw_ctx);
+ lv_event_send(obj, LV_EVENT_DRAW_MAIN, draw_ctx);
+ lv_event_send(obj, LV_EVENT_DRAW_MAIN_END, draw_ctx);
+#if LV_USE_REFR_DEBUG
+ lv_color_t debug_color = lv_color_make(lv_rand(0, 0xFF), lv_rand(0, 0xFF), lv_rand(0, 0xFF));
+ lv_draw_rect_dsc_t draw_dsc;
+ lv_draw_rect_dsc_init(&draw_dsc);
+ draw_dsc.bg_color.full = debug_color.full;
+ draw_dsc.bg_opa = LV_OPA_20;
+ draw_dsc.border_width = 1;
+ draw_dsc.border_opa = LV_OPA_30;
+ draw_dsc.border_color = debug_color;
+ lv_draw_rect(draw_ctx, &draw_dsc, &obj_coords_ext);
+#endif
+ }
+
+ /*With overflow visible keep the previous clip area to let the children visible out of this object too
+ *With not overflow visible limit the clip are to the object's coordinates to clip the children*/
+ lv_area_t clip_coords_for_children;
+ bool refr_children = true;
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_OVERFLOW_VISIBLE)) {
+ clip_coords_for_children = *clip_area_ori;
+ }
+ else {
+ if(!_lv_area_intersect(&clip_coords_for_children, clip_area_ori, &obj->coords)) {
+ refr_children = false;
+ }
+ }
+
+ if(refr_children) {
+ draw_ctx->clip_area = &clip_coords_for_children;
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ refr_obj(draw_ctx, child);
+ }
+ }
+
+ /*If the object was visible on the clip area call the post draw events too*/
+ if(should_draw) {
+ draw_ctx->clip_area = &clip_coords_for_obj;
+
+ /*If all the children are redrawn make 'post draw' draw*/
+ lv_event_send(obj, LV_EVENT_DRAW_POST_BEGIN, draw_ctx);
+ lv_event_send(obj, LV_EVENT_DRAW_POST, draw_ctx);
+ lv_event_send(obj, LV_EVENT_DRAW_POST_END, draw_ctx);
+ }
+
+ draw_ctx->clip_area = clip_area_ori;
+}
+
+
+/**
+ * Invalidate an area on display to redraw it
+ * @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
+ * @param disp pointer to display where the area should be invalidated (NULL can be used if there is
+ * only one display)
+ */
+void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p)
+{
+ if(!disp) disp = lv_disp_get_default();
+ if(!disp) return;
+ if(!lv_disp_is_invalidation_enabled(disp)) return;
+
+ if(disp->rendering_in_progress) {
+ LV_LOG_ERROR("detected modifying dirty areas in render");
+ return;
+ }
+
+ /*Clear the invalidate buffer if the parameter is NULL*/
+ if(area_p == NULL) {
+ disp->inv_p = 0;
+ return;
+ }
+
+ lv_area_t scr_area;
+ scr_area.x1 = 0;
+ scr_area.y1 = 0;
+ scr_area.x2 = lv_disp_get_hor_res(disp) - 1;
+ scr_area.y2 = lv_disp_get_ver_res(disp) - 1;
+
+ lv_area_t com_area;
+ bool suc;
+
+ suc = _lv_area_intersect(&com_area, area_p, &scr_area);
+ if(suc == false) return; /*Out of the screen*/
+
+ /*If there were at least 1 invalid area in full refresh mode, redraw the whole screen*/
+ if(disp->driver->full_refresh) {
+ disp->inv_areas[0] = scr_area;
+ disp->inv_p = 1;
+ if(disp->refr_timer) lv_timer_resume(disp->refr_timer);
+ return;
+ }
+
+ if(disp->driver->rounder_cb) disp->driver->rounder_cb(disp->driver, &com_area);
+
+ /*Save only if this area is not in one of the saved areas*/
+ uint16_t i;
+ for(i = 0; i < disp->inv_p; i++) {
+ if(_lv_area_is_in(&com_area, &disp->inv_areas[i], 0) != false) return;
+ }
+
+ /*Save the area*/
+ if(disp->inv_p < LV_INV_BUF_SIZE) {
+ lv_area_copy(&disp->inv_areas[disp->inv_p], &com_area);
+ }
+ else { /*If no place for the area add the screen*/
+ disp->inv_p = 0;
+ lv_area_copy(&disp->inv_areas[disp->inv_p], &scr_area);
+ }
+ disp->inv_p++;
+ if(disp->refr_timer) lv_timer_resume(disp->refr_timer);
+}
+
+/**
+ * Get the display which is being refreshed
+ * @return the display being refreshed
+ */
+lv_disp_t * _lv_refr_get_disp_refreshing(void)
+{
+ return disp_refr;
+}
+
+/**
+ * Set the display which is being refreshed.
+ * It shouldn't be used directly by the user.
+ * It can be used to trick the drawing functions about there is an active display.
+ * @param the display being refreshed
+ */
+void _lv_refr_set_disp_refreshing(lv_disp_t * disp)
+{
+ disp_refr = disp;
+}
+
+/**
+ * Called periodically to handle the refreshing
+ * @param tmr pointer to the timer itself
+ */
+void _lv_disp_refr_timer(lv_timer_t * tmr)
+{
+ REFR_TRACE("begin");
+
+ uint32_t start = lv_tick_get();
+ volatile uint32_t elaps = 0;
+
+ if(tmr) {
+ disp_refr = tmr->user_data;
+#if LV_USE_PERF_MONITOR == 0 && LV_USE_MEM_MONITOR == 0
+ /**
+ * Ensure the timer does not run again automatically.
+ * This is done before refreshing in case refreshing invalidates something else.
+ */
+ lv_timer_pause(tmr);
+#endif
+ }
+ else {
+ disp_refr = lv_disp_get_default();
+ }
+
+ /*Refresh the screen's layout if required*/
+ lv_obj_update_layout(disp_refr->act_scr);
+ if(disp_refr->prev_scr) lv_obj_update_layout(disp_refr->prev_scr);
+
+ lv_obj_update_layout(disp_refr->top_layer);
+ lv_obj_update_layout(disp_refr->sys_layer);
+
+ /*Do nothing if there is no active screen*/
+ if(disp_refr->act_scr == NULL) {
+ disp_refr->inv_p = 0;
+ LV_LOG_WARN("there is no active screen");
+ REFR_TRACE("finished");
+ return;
+ }
+
+ lv_refr_join_area();
+
+ refr_invalid_areas();
+
+ /*If refresh happened ...*/
+ if(disp_refr->inv_p != 0) {
+
+ /*Clean up*/
+ lv_memset_00(disp_refr->inv_areas, sizeof(disp_refr->inv_areas));
+ lv_memset_00(disp_refr->inv_area_joined, sizeof(disp_refr->inv_area_joined));
+ disp_refr->inv_p = 0;
+
+ elaps = lv_tick_elaps(start);
+
+ /*Call monitor cb if present*/
+ if(disp_refr->driver->monitor_cb) {
+ disp_refr->driver->monitor_cb(disp_refr->driver, elaps, px_num);
+ }
+ }
+
+ lv_mem_buf_free_all();
+ _lv_font_clean_up_fmt_txt();
+
+#if LV_DRAW_COMPLEX
+ _lv_draw_mask_cleanup();
+#endif
+
+#if LV_USE_PERF_MONITOR && LV_USE_LABEL
+ lv_obj_t * perf_label = perf_monitor.perf_label;
+ if(perf_label == NULL) {
+ perf_label = lv_label_create(lv_layer_sys());
+ lv_obj_set_style_bg_opa(perf_label, LV_OPA_50, 0);
+ lv_obj_set_style_bg_color(perf_label, lv_color_black(), 0);
+ lv_obj_set_style_text_color(perf_label, lv_color_white(), 0);
+ lv_obj_set_style_pad_top(perf_label, 3, 0);
+ lv_obj_set_style_pad_bottom(perf_label, 3, 0);
+ lv_obj_set_style_pad_left(perf_label, 3, 0);
+ lv_obj_set_style_pad_right(perf_label, 3, 0);
+ lv_obj_set_style_text_align(perf_label, LV_TEXT_ALIGN_RIGHT, 0);
+ lv_label_set_text(perf_label, "?");
+ lv_obj_align(perf_label, LV_USE_PERF_MONITOR_POS, 0, 0);
+ perf_monitor.perf_label = perf_label;
+ }
+
+ if(lv_tick_elaps(perf_monitor.perf_last_time) < 300) {
+ if(px_num > 5000) {
+ perf_monitor.elaps_sum += elaps;
+ perf_monitor.frame_cnt ++;
+ }
+ }
+ else {
+ perf_monitor.perf_last_time = lv_tick_get();
+ uint32_t fps_limit;
+ uint32_t fps;
+
+ if(disp_refr->refr_timer) {
+ fps_limit = 1000 / disp_refr->refr_timer->period;
+ }
+ else {
+ fps_limit = 1000 / LV_DISP_DEF_REFR_PERIOD;
+ }
+
+ if(perf_monitor.elaps_sum == 0) {
+ perf_monitor.elaps_sum = 1;
+ }
+ if(perf_monitor.frame_cnt == 0) {
+ fps = fps_limit;
+ }
+ else {
+ fps = (1000 * perf_monitor.frame_cnt) / perf_monitor.elaps_sum;
+ }
+ perf_monitor.elaps_sum = 0;
+ perf_monitor.frame_cnt = 0;
+ if(fps > fps_limit) {
+ fps = fps_limit;
+ }
+
+ perf_monitor.fps_sum_all += fps;
+ perf_monitor.fps_sum_cnt ++;
+ uint32_t cpu = 100 - lv_timer_get_idle();
+ lv_label_set_text_fmt(perf_label, "%"LV_PRIu32" FPS\n%"LV_PRIu32"%% CPU", fps, cpu);
+ }
+#endif
+
+#if LV_USE_MEM_MONITOR && LV_MEM_CUSTOM == 0 && LV_USE_LABEL
+ lv_obj_t * mem_label = mem_monitor.mem_label;
+ if(mem_label == NULL) {
+ mem_label = lv_label_create(lv_layer_sys());
+ lv_obj_set_style_bg_opa(mem_label, LV_OPA_50, 0);
+ lv_obj_set_style_bg_color(mem_label, lv_color_black(), 0);
+ lv_obj_set_style_text_color(mem_label, lv_color_white(), 0);
+ lv_obj_set_style_pad_top(mem_label, 3, 0);
+ lv_obj_set_style_pad_bottom(mem_label, 3, 0);
+ lv_obj_set_style_pad_left(mem_label, 3, 0);
+ lv_obj_set_style_pad_right(mem_label, 3, 0);
+ lv_label_set_text(mem_label, "?");
+ lv_obj_align(mem_label, LV_USE_MEM_MONITOR_POS, 0, 0);
+ mem_monitor.mem_label = mem_label;
+ }
+
+ if(lv_tick_elaps(mem_monitor.mem_last_time) > 300) {
+ mem_monitor.mem_last_time = lv_tick_get();
+ lv_mem_monitor_t mon;
+ lv_mem_monitor(&mon);
+ uint32_t used_size = mon.total_size - mon.free_size;;
+ uint32_t used_kb = used_size / 1024;
+ uint32_t used_kb_tenth = (used_size - (used_kb * 1024)) / 102;
+ lv_label_set_text_fmt(mem_label,
+ "%"LV_PRIu32 ".%"LV_PRIu32 " kB used (%d %%)\n"
+ "%d%% frag.",
+ used_kb, used_kb_tenth, mon.used_pct,
+ mon.frag_pct);
+ }
+#endif
+
+ REFR_TRACE("finished");
+}
+
+#if LV_USE_PERF_MONITOR
+void lv_refr_reset_fps_counter(void)
+{
+ perf_monitor.fps_sum_all = 0;
+ perf_monitor.fps_sum_cnt = 0;
+}
+
+uint32_t lv_refr_get_fps_avg(void)
+{
+ if(perf_monitor.fps_sum_cnt == 0) {
+ return 0;
+ }
+ return perf_monitor.fps_sum_all / perf_monitor.fps_sum_cnt;
+}
+#endif
+
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+/**
+ * Join the areas which has got common parts
+ */
+static void lv_refr_join_area(void)
+{
+ uint32_t join_from;
+ uint32_t join_in;
+ lv_area_t joined_area;
+ for(join_in = 0; join_in < disp_refr->inv_p; join_in++) {
+ if(disp_refr->inv_area_joined[join_in] != 0) continue;
+
+ /*Check all areas to join them in 'join_in'*/
+ for(join_from = 0; join_from < disp_refr->inv_p; join_from++) {
+ /*Handle only unjoined areas and ignore itself*/
+ if(disp_refr->inv_area_joined[join_from] != 0 || join_in == join_from) {
+ continue;
+ }
+
+ /*Check if the areas are on each other*/
+ if(_lv_area_is_on(&disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]) == false) {
+ continue;
+ }
+
+ _lv_area_join(&joined_area, &disp_refr->inv_areas[join_in], &disp_refr->inv_areas[join_from]);
+
+ /*Join two area only if the joined area size is smaller*/
+ if(lv_area_get_size(&joined_area) < (lv_area_get_size(&disp_refr->inv_areas[join_in]) +
+ lv_area_get_size(&disp_refr->inv_areas[join_from]))) {
+ lv_area_copy(&disp_refr->inv_areas[join_in], &joined_area);
+
+ /*Mark 'join_form' is joined into 'join_in'*/
+ disp_refr->inv_area_joined[join_from] = 1;
+ }
+ }
+ }
+}
+
+/**
+ * Refresh the joined areas
+ */
+static void refr_invalid_areas(void)
+{
+ px_num = 0;
+
+ if(disp_refr->inv_p == 0) return;
+
+ /*Find the last area which will be drawn*/
+ int32_t i;
+ int32_t last_i = 0;
+ for(i = disp_refr->inv_p - 1; i >= 0; i--) {
+ if(disp_refr->inv_area_joined[i] == 0) {
+ last_i = i;
+ break;
+ }
+ }
+
+ /*Notify the display driven rendering has started*/
+ if(disp_refr->driver->render_start_cb) {
+ disp_refr->driver->render_start_cb(disp_refr->driver);
+ }
+
+ disp_refr->driver->draw_buf->last_area = 0;
+ disp_refr->driver->draw_buf->last_part = 0;
+ disp_refr->rendering_in_progress = true;
+
+ for(i = 0; i < disp_refr->inv_p; i++) {
+ /*Refresh the unjoined areas*/
+ if(disp_refr->inv_area_joined[i] == 0) {
+
+ if(i == last_i) disp_refr->driver->draw_buf->last_area = 1;
+ disp_refr->driver->draw_buf->last_part = 0;
+ refr_area(&disp_refr->inv_areas[i]);
+
+ px_num += lv_area_get_size(&disp_refr->inv_areas[i]);
+ }
+ }
+
+ disp_refr->rendering_in_progress = false;
+}
+
+/**
+ * Refresh an area if there is Virtual Display Buffer
+ * @param area_p pointer to an area to refresh
+ */
+static void refr_area(const lv_area_t * area_p)
+{
+ lv_draw_ctx_t * draw_ctx = disp_refr->driver->draw_ctx;
+ draw_ctx->buf = disp_refr->driver->draw_buf->buf_act;
+
+ /*With full refresh just redraw directly into the buffer*/
+ /*In direct mode draw directly on the absolute coordinates of the buffer*/
+ if(disp_refr->driver->full_refresh || disp_refr->driver->direct_mode) {
+ lv_area_t disp_area;
+ lv_area_set(&disp_area, 0, 0, lv_disp_get_hor_res(disp_refr) - 1, lv_disp_get_ver_res(disp_refr) - 1);
+ draw_ctx->buf_area = &disp_area;
+
+ if(disp_refr->driver->full_refresh) {
+ disp_refr->driver->draw_buf->last_part = 1;
+ draw_ctx->clip_area = &disp_area;
+ refr_area_part(draw_ctx);
+ }
+ else {
+ disp_refr->driver->draw_buf->last_part = disp_refr->driver->draw_buf->last_area;
+ draw_ctx->clip_area = area_p;
+ refr_area_part(draw_ctx);
+ }
+ return;
+ }
+
+ /*Normal refresh: draw the area in parts*/
+ /*Calculate the max row num*/
+ lv_coord_t w = lv_area_get_width(area_p);
+ lv_coord_t h = lv_area_get_height(area_p);
+ lv_coord_t y2 = area_p->y2 >= lv_disp_get_ver_res(disp_refr) ?
+ lv_disp_get_ver_res(disp_refr) - 1 : area_p->y2;
+
+ int32_t max_row = get_max_row(disp_refr, w, h);
+
+ lv_coord_t row;
+ lv_coord_t row_last = 0;
+ lv_area_t sub_area;
+ for(row = area_p->y1; row + max_row - 1 <= y2; row += max_row) {
+ /*Calc. the next y coordinates of draw_buf*/
+ sub_area.x1 = area_p->x1;
+ sub_area.x2 = area_p->x2;
+ sub_area.y1 = row;
+ sub_area.y2 = row + max_row - 1;
+ draw_ctx->buf_area = &sub_area;
+ draw_ctx->clip_area = &sub_area;
+ draw_ctx->buf = disp_refr->driver->draw_buf->buf_act;
+ if(sub_area.y2 > y2) sub_area.y2 = y2;
+ row_last = sub_area.y2;
+ if(y2 == row_last) disp_refr->driver->draw_buf->last_part = 1;
+ refr_area_part(draw_ctx);
+ }
+
+ /*If the last y coordinates are not handled yet ...*/
+ if(y2 != row_last) {
+ /*Calc. the next y coordinates of draw_buf*/
+ sub_area.x1 = area_p->x1;
+ sub_area.x2 = area_p->x2;
+ sub_area.y1 = row;
+ sub_area.y2 = y2;
+ draw_ctx->buf_area = &sub_area;
+ draw_ctx->clip_area = &sub_area;
+ draw_ctx->buf = disp_refr->driver->draw_buf->buf_act;
+ disp_refr->driver->draw_buf->last_part = 1;
+ refr_area_part(draw_ctx);
+ }
+}
+
+static void refr_area_part(lv_draw_ctx_t * draw_ctx)
+{
+ lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
+
+ /* Below the `area_p` area will be redrawn into the draw buffer.
+ * In single buffered mode wait here until the buffer is freed.
+ * In full double buffered mode wait here while the buffers are swapped and a buffer becomes available*/
+ bool full_sized = draw_buf->size == (uint32_t)disp_refr->driver->hor_res * disp_refr->driver->ver_res;
+ if((draw_buf->buf1 && !draw_buf->buf2) ||
+ (draw_buf->buf1 && draw_buf->buf2 && full_sized)) {
+ while(draw_buf->flushing) {
+ if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver);
+ }
+
+ /*If the screen is transparent initialize it when the flushing is ready*/
+#if LV_COLOR_SCREEN_TRANSP
+ if(disp_refr->driver->screen_transp) {
+ if(disp_refr->driver->clear_cb) {
+ disp_refr->driver->clear_cb(disp_refr->driver, disp_refr->driver->draw_buf->buf_act, disp_refr->driver->draw_buf->size);
+ }
+ else {
+ lv_memset_00(disp_refr->driver->draw_buf->buf_act, disp_refr->driver->draw_buf->size * LV_IMG_PX_SIZE_ALPHA_BYTE);
+ }
+ }
+#endif
+ }
+
+ lv_obj_t * top_act_scr = NULL;
+ lv_obj_t * top_prev_scr = NULL;
+
+ /*Get the most top object which is not covered by others*/
+ top_act_scr = lv_refr_get_top_obj(draw_ctx->buf_area, lv_disp_get_scr_act(disp_refr));
+ if(disp_refr->prev_scr) {
+ top_prev_scr = lv_refr_get_top_obj(draw_ctx->buf_area, disp_refr->prev_scr);
+ }
+
+ /*Draw a display background if there is no top object*/
+ if(top_act_scr == NULL && top_prev_scr == NULL) {
+ lv_area_t a;
+ lv_area_set(&a, 0, 0,
+ lv_disp_get_hor_res(disp_refr) - 1, lv_disp_get_ver_res(disp_refr) - 1);
+ if(draw_ctx->draw_bg) {
+ lv_draw_rect_dsc_t dsc;
+ lv_draw_rect_dsc_init(&dsc);
+ dsc.bg_img_src = disp_refr->bg_img;
+ dsc.bg_img_opa = disp_refr->bg_opa;
+ dsc.bg_color = disp_refr->bg_color;
+ dsc.bg_opa = disp_refr->bg_opa;
+ draw_ctx->draw_bg(draw_ctx, &dsc, &a);
+ }
+ else if(disp_refr->bg_img) {
+ lv_img_header_t header;
+ lv_res_t res = lv_img_decoder_get_info(disp_refr->bg_img, &header);
+ if(res == LV_RES_OK) {
+ lv_draw_img_dsc_t dsc;
+ lv_draw_img_dsc_init(&dsc);
+ dsc.opa = disp_refr->bg_opa;
+ lv_draw_img(draw_ctx, &dsc, &a, disp_refr->bg_img);
+ }
+ else {
+ LV_LOG_WARN("Can't draw the background image");
+ }
+ }
+ else {
+ lv_draw_rect_dsc_t dsc;
+ lv_draw_rect_dsc_init(&dsc);
+ dsc.bg_color = disp_refr->bg_color;
+ dsc.bg_opa = disp_refr->bg_opa;
+ lv_draw_rect(draw_ctx, &dsc, draw_ctx->buf_area);
+ }
+ }
+
+ if(disp_refr->draw_prev_over_act) {
+ if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr;
+ refr_obj_and_children(draw_ctx, top_act_scr);
+
+ /*Refresh the previous screen if any*/
+ if(disp_refr->prev_scr) {
+ if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr;
+ refr_obj_and_children(draw_ctx, top_prev_scr);
+ }
+ }
+ else {
+ /*Refresh the previous screen if any*/
+ if(disp_refr->prev_scr) {
+ if(top_prev_scr == NULL) top_prev_scr = disp_refr->prev_scr;
+ refr_obj_and_children(draw_ctx, top_prev_scr);
+ }
+
+ if(top_act_scr == NULL) top_act_scr = disp_refr->act_scr;
+ refr_obj_and_children(draw_ctx, top_act_scr);
+ }
+
+ /*Also refresh top and sys layer unconditionally*/
+ refr_obj_and_children(draw_ctx, lv_disp_get_layer_top(disp_refr));
+ refr_obj_and_children(draw_ctx, lv_disp_get_layer_sys(disp_refr));
+
+ draw_buf_flush(disp_refr);
+}
+
+/**
+ * Search the most top object which fully covers an area
+ * @param area_p pointer to an area
+ * @param obj the first object to start the searching (typically a screen)
+ * @return
+ */
+static lv_obj_t * lv_refr_get_top_obj(const lv_area_t * area_p, lv_obj_t * obj)
+{
+ lv_obj_t * found_p = NULL;
+
+ if(_lv_area_is_in(area_p, &obj->coords, 0) == false) return NULL;
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return NULL;
+ if(_lv_obj_get_layer_type(obj) != LV_LAYER_TYPE_NONE) return NULL;
+
+ /*If this object is fully cover the draw area then check the children too*/
+ lv_cover_check_info_t info;
+ info.res = LV_COVER_RES_COVER;
+ info.area = area_p;
+ lv_event_send(obj, LV_EVENT_COVER_CHECK, &info);
+ if(info.res == LV_COVER_RES_MASKED) return NULL;
+
+ int32_t i;
+ int32_t child_cnt = lv_obj_get_child_cnt(obj);
+ for(i = child_cnt - 1; i >= 0; i--) {
+ lv_obj_t * child = obj->spec_attr->children[i];
+ found_p = lv_refr_get_top_obj(area_p, child);
+
+ /*If a children is ok then break*/
+ if(found_p != NULL) {
+ break;
+ }
+ }
+
+ /*If no better children use this object*/
+ if(found_p == NULL && info.res == LV_COVER_RES_COVER) {
+ found_p = obj;
+ }
+
+ return found_p;
+}
+
+/**
+ * Make the refreshing from an object. Draw all its children and the youngers too.
+ * @param top_p pointer to an objects. Start the drawing from it.
+ * @param mask_p pointer to an area, the objects will be drawn only here
+ */
+static void refr_obj_and_children(lv_draw_ctx_t * draw_ctx, lv_obj_t * top_obj)
+{
+ /*Normally always will be a top_obj (at least the screen)
+ *but in special cases (e.g. if the screen has alpha) it won't.
+ *In this case use the screen directly*/
+ if(top_obj == NULL) top_obj = lv_disp_get_scr_act(disp_refr);
+ if(top_obj == NULL) return; /*Shouldn't happen*/
+
+ /*Refresh the top object and its children*/
+ refr_obj(draw_ctx, top_obj);
+
+ /*Draw the 'younger' sibling objects because they can be on top_obj*/
+ lv_obj_t * parent;
+ lv_obj_t * border_p = top_obj;
+
+ parent = lv_obj_get_parent(top_obj);
+
+ /*Do until not reach the screen*/
+ while(parent != NULL) {
+ bool go = false;
+ uint32_t i;
+ uint32_t child_cnt = lv_obj_get_child_cnt(parent);
+ for(i = 0; i < child_cnt; i++) {
+ lv_obj_t * child = parent->spec_attr->children[i];
+ if(!go) {
+ if(child == border_p) go = true;
+ }
+ else {
+ /*Refresh the objects*/
+ refr_obj(draw_ctx, child);
+ }
+ }
+
+ /*Call the post draw draw function of the parents of the to object*/
+ lv_event_send(parent, LV_EVENT_DRAW_POST_BEGIN, (void *)draw_ctx);
+ lv_event_send(parent, LV_EVENT_DRAW_POST, (void *)draw_ctx);
+ lv_event_send(parent, LV_EVENT_DRAW_POST_END, (void *)draw_ctx);
+
+ /*The new border will be the last parents,
+ *so the 'younger' brothers of parent will be refreshed*/
+ border_p = parent;
+ /*Go a level deeper*/
+ parent = lv_obj_get_parent(parent);
+ }
+}
+
+
+static lv_res_t layer_get_area(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj, lv_layer_type_t layer_type,
+ lv_area_t * layer_area_out)
+{
+ lv_coord_t ext_draw_size = _lv_obj_get_ext_draw_size(obj);
+ lv_area_t obj_coords_ext;
+ lv_obj_get_coords(obj, &obj_coords_ext);
+ lv_area_increase(&obj_coords_ext, ext_draw_size, ext_draw_size);
+
+ if(layer_type == LV_LAYER_TYPE_TRANSFORM) {
+ /*Get the transformed area and clip it to the current clip area.
+ *This area needs to be updated on the screen.*/
+ lv_area_t clip_coords_for_obj;
+ lv_area_t tranf_coords = obj_coords_ext;
+ lv_obj_get_transformed_area(obj, &tranf_coords, false, false);
+ if(!_lv_area_intersect(&clip_coords_for_obj, draw_ctx->clip_area, &tranf_coords)) {
+ return LV_RES_INV;
+ }
+
+ /*Transform back (inverse) the transformed area.
+ *It will tell which area of the non-transformed widget needs to be redrawn
+ *in order to cover transformed area after transformation.*/
+ lv_area_t inverse_clip_coords_for_obj = clip_coords_for_obj;
+ lv_obj_get_transformed_area(obj, &inverse_clip_coords_for_obj, false, true);
+ if(!_lv_area_intersect(&inverse_clip_coords_for_obj, &inverse_clip_coords_for_obj, &obj_coords_ext)) {
+ return LV_RES_INV;
+ }
+
+ *layer_area_out = inverse_clip_coords_for_obj;
+ }
+ else if(layer_type == LV_LAYER_TYPE_SIMPLE) {
+ lv_area_t clip_coords_for_obj;
+ if(!_lv_area_intersect(&clip_coords_for_obj, draw_ctx->clip_area, &obj_coords_ext)) {
+ return LV_RES_INV;
+ }
+ *layer_area_out = clip_coords_for_obj;
+ }
+ else {
+ LV_LOG_WARN("Unhandled intermediate layer type");
+ return LV_RES_INV;
+ }
+
+ return LV_RES_OK;
+}
+
+static void layer_alpha_test(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx, lv_draw_layer_ctx_t * layer_ctx,
+ lv_draw_layer_flags_t flags)
+{
+ bool has_alpha;
+ /*If globally the layer has alpha maybe this smaller section has not (e.g. not on a rounded corner)
+ *If turns out that this section has no alpha renderer can choose faster algorithms*/
+ if(flags & LV_DRAW_LAYER_FLAG_HAS_ALPHA) {
+ /*Test for alpha by assuming there is no alpha. If it fails, fall back to rendering with alpha*/
+ has_alpha = true;
+ if(_lv_area_is_in(&layer_ctx->area_act, &obj->coords, 0)) {
+ lv_cover_check_info_t info;
+ info.res = LV_COVER_RES_COVER;
+ info.area = &layer_ctx->area_act;
+ lv_event_send(obj, LV_EVENT_COVER_CHECK, &info);
+ if(info.res == LV_COVER_RES_COVER) has_alpha = false;
+ }
+
+ if(has_alpha) {
+ layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_alpha - 1;
+ }
+ }
+ else {
+ has_alpha = false;
+ }
+
+ if(layer_ctx->area_act.y2 > layer_ctx->area_full.y2) layer_ctx->area_act.y2 = layer_ctx->area_full.y2;
+ lv_draw_layer_adjust(draw_ctx, layer_ctx, has_alpha ? LV_DRAW_LAYER_FLAG_HAS_ALPHA : LV_DRAW_LAYER_FLAG_NONE);
+}
+
+
+void refr_obj(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj)
+{
+ /*Do not refresh hidden objects*/
+ if(lv_obj_has_flag(obj, LV_OBJ_FLAG_HIDDEN)) return;
+ lv_layer_type_t layer_type = _lv_obj_get_layer_type(obj);
+ if(layer_type == LV_LAYER_TYPE_NONE) {
+ lv_obj_redraw(draw_ctx, obj);
+ }
+ else {
+ lv_opa_t opa = lv_obj_get_style_opa(obj, 0);
+ if(opa < LV_OPA_MIN) return;
+
+ lv_area_t layer_area_full;
+ lv_res_t res = layer_get_area(draw_ctx, obj, layer_type, &layer_area_full);
+ if(res != LV_RES_OK) return;
+
+ lv_draw_layer_flags_t flags = LV_DRAW_LAYER_FLAG_HAS_ALPHA;
+
+ if(_lv_area_is_in(&layer_area_full, &obj->coords, 0)) {
+ lv_cover_check_info_t info;
+ info.res = LV_COVER_RES_COVER;
+ info.area = &layer_area_full;
+ lv_event_send(obj, LV_EVENT_COVER_CHECK, &info);
+ if(info.res == LV_COVER_RES_COVER) flags &= ~LV_DRAW_LAYER_FLAG_HAS_ALPHA;
+ }
+
+ if(layer_type == LV_LAYER_TYPE_SIMPLE) flags |= LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE;
+
+ lv_draw_layer_ctx_t * layer_ctx = lv_draw_layer_create(draw_ctx, &layer_area_full, flags);
+ if(layer_ctx == NULL) {
+ LV_LOG_WARN("Couldn't create a new layer context");
+ return;
+ }
+ lv_point_t pivot = {
+ .x = lv_obj_get_style_transform_pivot_x(obj, 0),
+ .y = lv_obj_get_style_transform_pivot_y(obj, 0)
+ };
+
+ lv_draw_img_dsc_t draw_dsc;
+ lv_draw_img_dsc_init(&draw_dsc);
+ draw_dsc.opa = opa;
+ draw_dsc.angle = lv_obj_get_style_transform_angle(obj, 0);
+ if(draw_dsc.angle > 3600) draw_dsc.angle -= 3600;
+ else if(draw_dsc.angle < 0) draw_dsc.angle += 3600;
+
+ draw_dsc.zoom = lv_obj_get_style_transform_zoom(obj, 0);
+ draw_dsc.blend_mode = lv_obj_get_style_blend_mode(obj, 0);
+ draw_dsc.antialias = disp_refr->driver->antialiasing;
+
+ if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) {
+ layer_ctx->area_act = layer_ctx->area_full;
+ layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_no_alpha - 1;
+ if(layer_ctx->area_act.y2 > layer_ctx->area_full.y2) layer_ctx->area_act.y2 = layer_ctx->area_full.y2;
+ }
+
+ while(layer_ctx->area_act.y1 <= layer_area_full.y2) {
+ if(flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) {
+ layer_alpha_test(obj, draw_ctx, layer_ctx, flags);
+ }
+
+ lv_obj_redraw(draw_ctx, obj);
+
+ draw_dsc.pivot.x = obj->coords.x1 + pivot.x - draw_ctx->buf_area->x1;
+ draw_dsc.pivot.y = obj->coords.y1 + pivot.y - draw_ctx->buf_area->y1;
+
+ /*With LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE it should also go the next chunk*/
+ lv_draw_layer_blend(draw_ctx, layer_ctx, &draw_dsc);
+
+ if((flags & LV_DRAW_LAYER_FLAG_CAN_SUBDIVIDE) == 0) break;
+
+ layer_ctx->area_act.y1 = layer_ctx->area_act.y2 + 1;
+ layer_ctx->area_act.y2 = layer_ctx->area_act.y1 + layer_ctx->max_row_with_no_alpha - 1;
+ }
+
+ lv_draw_layer_destroy(draw_ctx, layer_ctx);
+ }
+}
+
+
+static uint32_t get_max_row(lv_disp_t * disp, lv_coord_t area_w, lv_coord_t area_h)
+{
+ int32_t max_row = (uint32_t)disp->driver->draw_buf->size / area_w;
+
+ if(max_row > area_h) max_row = area_h;
+
+ /*Round down the lines of draw_buf if rounding is added*/
+ if(disp_refr->driver->rounder_cb) {
+ lv_area_t tmp;
+ tmp.x1 = 0;
+ tmp.x2 = 0;
+ tmp.y1 = 0;
+
+ lv_coord_t h_tmp = max_row;
+ do {
+ tmp.y2 = h_tmp - 1;
+ disp_refr->driver->rounder_cb(disp_refr->driver, &tmp);
+
+ /*If this height fits into `max_row` then fine*/
+ if(lv_area_get_height(&tmp) <= max_row) break;
+
+ /*Decrement the height of the area until it fits into `max_row` after rounding*/
+ h_tmp--;
+ } while(h_tmp > 0);
+
+ if(h_tmp <= 0) {
+ LV_LOG_WARN("Can't set draw_buf height using the round function. (Wrong round_cb or to "
+ "small draw_buf)");
+ return 0;
+ }
+ else {
+ max_row = tmp.y2 + 1;
+ }
+ }
+
+ return max_row;
+}
+
+static void draw_buf_rotate_180(lv_disp_drv_t * drv, lv_area_t * area, lv_color_t * color_p)
+{
+ lv_coord_t area_w = lv_area_get_width(area);
+ lv_coord_t area_h = lv_area_get_height(area);
+ uint32_t total = area_w * area_h;
+ /*Swap the beginning and end values*/
+ lv_color_t tmp;
+ uint32_t i = total - 1, j = 0;
+ while(i > j) {
+ tmp = color_p[i];
+ color_p[i] = color_p[j];
+ color_p[j] = tmp;
+ i--;
+ j++;
+ }
+ lv_coord_t tmp_coord;
+ tmp_coord = area->y2;
+ area->y2 = drv->ver_res - area->y1 - 1;
+ area->y1 = drv->ver_res - tmp_coord - 1;
+ tmp_coord = area->x2;
+ area->x2 = drv->hor_res - area->x1 - 1;
+ area->x1 = drv->hor_res - tmp_coord - 1;
+}
+
+static LV_ATTRIBUTE_FAST_MEM void draw_buf_rotate_90(bool invert_i, lv_coord_t area_w, lv_coord_t area_h,
+ lv_color_t * orig_color_p, lv_color_t * rot_buf)
+{
+
+ uint32_t invert = (area_w * area_h) - 1;
+ uint32_t initial_i = ((area_w - 1) * area_h);
+ for(lv_coord_t y = 0; y < area_h; y++) {
+ uint32_t i = initial_i + y;
+ if(invert_i)
+ i = invert - i;
+ for(lv_coord_t x = 0; x < area_w; x++) {
+ rot_buf[i] = *(orig_color_p++);
+ if(invert_i)
+ i += area_h;
+ else
+ i -= area_h;
+ }
+ }
+}
+
+/**
+ * Helper function for draw_buf_rotate_90_sqr. Given a list of four numbers, rotate the entire list to the left.
+ */
+static inline void draw_buf_rotate4(lv_color_t * a, lv_color_t * b, lv_color_t * c, lv_color_t * d)
+{
+ lv_color_t tmp;
+ tmp = *a;
+ *a = *b;
+ *b = *c;
+ *c = *d;
+ *d = tmp;
+}
+
+/**
+ * Rotate a square image 90/270 degrees in place.
+ * @note inspired by https://stackoverflow.com/a/43694906
+ */
+static void draw_buf_rotate_90_sqr(bool is_270, lv_coord_t w, lv_color_t * color_p)
+{
+ for(lv_coord_t i = 0; i < w / 2; i++) {
+ for(lv_coord_t j = 0; j < (w + 1) / 2; j++) {
+ lv_coord_t inv_i = (w - 1) - i;
+ lv_coord_t inv_j = (w - 1) - j;
+ if(is_270) {
+ draw_buf_rotate4(
+ &color_p[i * w + j],
+ &color_p[inv_j * w + i],
+ &color_p[inv_i * w + inv_j],
+ &color_p[j * w + inv_i]
+ );
+ }
+ else {
+ draw_buf_rotate4(
+ &color_p[i * w + j],
+ &color_p[j * w + inv_i],
+ &color_p[inv_i * w + inv_j],
+ &color_p[inv_j * w + i]
+ );
+ }
+
+ }
+ }
+}
+
+/**
+ * Rotate the draw_buf to the display's native orientation.
+ */
+static void draw_buf_rotate(lv_area_t * area, lv_color_t * color_p)
+{
+ lv_disp_drv_t * drv = disp_refr->driver;
+ if(disp_refr->driver->full_refresh && drv->sw_rotate) {
+ LV_LOG_ERROR("cannot rotate a full refreshed display!");
+ return;
+ }
+ if(drv->rotated == LV_DISP_ROT_180) {
+ draw_buf_rotate_180(drv, area, color_p);
+ call_flush_cb(drv, area, color_p);
+ }
+ else if(drv->rotated == LV_DISP_ROT_90 || drv->rotated == LV_DISP_ROT_270) {
+ /*Allocate a temporary buffer to store rotated image*/
+ lv_color_t * rot_buf = NULL;
+ lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
+ lv_coord_t area_w = lv_area_get_width(area);
+ lv_coord_t area_h = lv_area_get_height(area);
+ /*Determine the maximum number of rows that can be rotated at a time*/
+ lv_coord_t max_row = LV_MIN((lv_coord_t)((LV_DISP_ROT_MAX_BUF / sizeof(lv_color_t)) / area_w), area_h);
+ lv_coord_t init_y_off;
+ init_y_off = area->y1;
+ if(drv->rotated == LV_DISP_ROT_90) {
+ area->y2 = drv->ver_res - area->x1 - 1;
+ area->y1 = area->y2 - area_w + 1;
+ }
+ else {
+ area->y1 = area->x1;
+ area->y2 = area->y1 + area_w - 1;
+ }
+
+ /*Rotate the screen in chunks, flushing after each one*/
+ lv_coord_t row = 0;
+ while(row < area_h) {
+ lv_coord_t height = LV_MIN(max_row, area_h - row);
+ draw_buf->flushing = 1;
+ if((row == 0) && (area_h >= area_w)) {
+ /*Rotate the initial area as a square*/
+ height = area_w;
+ draw_buf_rotate_90_sqr(drv->rotated == LV_DISP_ROT_270, area_w, color_p);
+ if(drv->rotated == LV_DISP_ROT_90) {
+ area->x1 = init_y_off;
+ area->x2 = init_y_off + area_w - 1;
+ }
+ else {
+ area->x2 = drv->hor_res - 1 - init_y_off;
+ area->x1 = area->x2 - area_w + 1;
+ }
+ }
+ else {
+ /*Rotate other areas using a maximum buffer size*/
+ if(rot_buf == NULL) rot_buf = lv_mem_buf_get(LV_DISP_ROT_MAX_BUF);
+ draw_buf_rotate_90(drv->rotated == LV_DISP_ROT_270, area_w, height, color_p, rot_buf);
+
+ if(drv->rotated == LV_DISP_ROT_90) {
+ area->x1 = init_y_off + row;
+ area->x2 = init_y_off + row + height - 1;
+ }
+ else {
+ area->x2 = drv->hor_res - 1 - init_y_off - row;
+ area->x1 = area->x2 - height + 1;
+ }
+ }
+
+ /* The original part (chunk of the current area) were split into more parts here.
+ * Set the original last_part flag on the last part of rotation. */
+ if(row + height >= area_h && draw_buf->last_area && draw_buf->last_part) {
+ draw_buf->flushing_last = 1;
+ }
+ else {
+ draw_buf->flushing_last = 0;
+ }
+
+ /*Flush the completed area to the display*/
+ call_flush_cb(drv, area, rot_buf == NULL ? color_p : rot_buf);
+ /*FIXME: Rotation forces legacy behavior where rendering and flushing are done serially*/
+ while(draw_buf->flushing) {
+ if(drv->wait_cb) drv->wait_cb(drv);
+ }
+ color_p += area_w * height;
+ row += height;
+ }
+ /*Free the allocated buffer at the end if necessary*/
+ if(rot_buf != NULL) lv_mem_buf_release(rot_buf);
+ }
+}
+
+/**
+ * Flush the content of the draw buffer
+ */
+static void draw_buf_flush(lv_disp_t * disp)
+{
+ lv_disp_draw_buf_t * draw_buf = lv_disp_get_draw_buf(disp_refr);
+
+ /*Flush the rendered content to the display*/
+ lv_draw_ctx_t * draw_ctx = disp->driver->draw_ctx;
+ if(draw_ctx->wait_for_finish) draw_ctx->wait_for_finish(draw_ctx);
+
+ /* In partial double buffered mode wait until the other buffer is freed
+ * and driver is ready to receive the new buffer */
+ bool full_sized = draw_buf->size == (uint32_t)disp_refr->driver->hor_res * disp_refr->driver->ver_res;
+ if(draw_buf->buf1 && draw_buf->buf2 && !full_sized) {
+ while(draw_buf->flushing) {
+ if(disp_refr->driver->wait_cb) disp_refr->driver->wait_cb(disp_refr->driver);
+ }
+ }
+
+ draw_buf->flushing = 1;
+
+ if(disp_refr->driver->draw_buf->last_area && disp_refr->driver->draw_buf->last_part) draw_buf->flushing_last = 1;
+ else draw_buf->flushing_last = 0;
+
+ bool flushing_last = draw_buf->flushing_last;
+
+ if(disp->driver->flush_cb) {
+ /*Rotate the buffer to the display's native orientation if necessary*/
+ if(disp->driver->rotated != LV_DISP_ROT_NONE && disp->driver->sw_rotate) {
+ draw_buf_rotate(draw_ctx->buf_area, draw_ctx->buf);
+ }
+ else {
+ call_flush_cb(disp->driver, draw_ctx->buf_area, draw_ctx->buf);
+ }
+ }
+
+ /*If there are 2 buffers swap them. With direct mode swap only on the last area*/
+ if(draw_buf->buf1 && draw_buf->buf2 && (!disp->driver->direct_mode || flushing_last)) {
+ if(draw_buf->buf_act == draw_buf->buf1)
+ draw_buf->buf_act = draw_buf->buf2;
+ else
+ draw_buf->buf_act = draw_buf->buf1;
+ }
+}
+
+static void call_flush_cb(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p)
+{
+ REFR_TRACE("Calling flush_cb on (%d;%d)(%d;%d) area with %p image pointer", area->x1, area->y1, area->x2, area->y2,
+ (void *)color_p);
+
+ lv_area_t offset_area = {
+ .x1 = area->x1 + drv->offset_x,
+ .y1 = area->y1 + drv->offset_y,
+ .x2 = area->x2 + drv->offset_x,
+ .y2 = area->y2 + drv->offset_y
+ };
+
+ drv->flush_cb(drv, &offset_area, color_p);
+}
+
+#if LV_USE_PERF_MONITOR
+static void perf_monitor_init(perf_monitor_t * _perf_monitor)
+{
+ LV_ASSERT_NULL(_perf_monitor);
+ _perf_monitor->elaps_sum = 0;
+ _perf_monitor->fps_sum_all = 0;
+ _perf_monitor->fps_sum_cnt = 0;
+ _perf_monitor->frame_cnt = 0;
+ _perf_monitor->perf_last_time = 0;
+ _perf_monitor->perf_label = NULL;
+}
+#endif
+
+#if LV_USE_MEM_MONITOR
+static void mem_monitor_init(mem_monitor_t * _mem_monitor)
+{
+ LV_ASSERT_NULL(_mem_monitor);
+ _mem_monitor->mem_last_time = 0;
+ _mem_monitor->mem_label = NULL;
+}
+#endif
+
diff --git a/lib/lvgl/src/core/lv_refr.h b/lib/lvgl/src/core/lv_refr.h
new file mode 100644
index 00000000..72e8d6c3
--- /dev/null
+++ b/lib/lvgl/src/core/lv_refr.h
@@ -0,0 +1,115 @@
+/**
+ * @file lv_refr.h
+ *
+ */
+
+#ifndef LV_REFR_H
+#define LV_REFR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "lv_obj.h"
+#include <stdbool.h>
+
+/*********************
+ * DEFINES
+ *********************/
+
+#define LV_REFR_TASK_PRIO LV_TASK_PRIO_MID
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Initialize the screen refresh subsystem
+ */
+void _lv_refr_init(void);
+
+/**
+ * Redraw the invalidated areas now.
+ * Normally the redrawing is periodically executed in `lv_timer_handler` but a long blocking process
+ * can prevent the call of `lv_timer_handler`. In this case if the GUI is updated in the process
+ * (e.g. progress bar) this function can be called when the screen should be updated.
+ * @param disp pointer to display to refresh. NULL to refresh all displays.
+ */
+void lv_refr_now(lv_disp_t * disp);
+
+/**
+ * Redrawn on object an all its children using the passed draw context
+ * @param draw pointer to an initialized draw context
+ * @param obj the start object from the redraw should start
+ */
+void lv_obj_redraw(lv_draw_ctx_t * draw_ctx, lv_obj_t * obj);
+
+/**
+ * Invalidate an area on display to redraw it
+ * @param area_p pointer to area which should be invalidated (NULL: delete the invalidated areas)
+ * @param disp pointer to display where the area should be invalidated (NULL can be used if there is
+ * only one display)
+ */
+void _lv_inv_area(lv_disp_t * disp, const lv_area_t * area_p);
+
+/**
+ * Get the display which is being refreshed
+ * @return the display being refreshed
+ */
+lv_disp_t * _lv_refr_get_disp_refreshing(void);
+
+/**
+ * Set the display which is being refreshed.
+ * It shouldn't be used directly by the user.
+ * It can be used to trick the drawing functions about there is an active display.
+ * @param the display being refreshed
+ */
+void _lv_refr_set_disp_refreshing(lv_disp_t * disp);
+
+#if LV_USE_PERF_MONITOR
+/**
+ * Reset FPS counter
+ */
+void lv_refr_reset_fps_counter(void);
+
+/**
+ * Get the average FPS
+ * @return the average FPS
+ */
+uint32_t lv_refr_get_fps_avg(void);
+#endif
+
+/**
+ * Called periodically to handle the refreshing
+ * @param timer pointer to the timer itself
+ */
+void _lv_disp_refr_timer(lv_timer_t * timer);
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_REFR_H*/
diff --git a/lib/lvgl/src/core/lv_theme.c b/lib/lvgl/src/core/lv_theme.c
new file mode 100644
index 00000000..b46cdcc4
--- /dev/null
+++ b/lib/lvgl/src/core/lv_theme.c
@@ -0,0 +1,118 @@
+/**
+ * @file lv_theme.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../lvgl.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+static void apply_theme(lv_theme_t * th, lv_obj_t * obj);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+lv_theme_t * lv_theme_get_from_obj(lv_obj_t * obj)
+{
+ lv_disp_t * disp = obj ? lv_obj_get_disp(obj) : lv_disp_get_default();
+ return lv_disp_get_theme(disp);
+}
+
+/**
+ * Apply the active theme on an object
+ * @param obj pointer to an object
+ * @param name the name of the theme element to apply. E.g. `LV_THEME_BTN`
+ */
+void lv_theme_apply(lv_obj_t * obj)
+{
+ lv_theme_t * th = lv_theme_get_from_obj(obj);
+ if(th == NULL) return;
+
+ lv_obj_remove_style_all(obj);
+
+ apply_theme(th, obj); /*Apply the theme including the base theme(s)*/
+}
+
+/**
+ * Set a base theme for a theme.
+ * The styles from the base them will be added before the styles of the current theme.
+ * Arbitrary long chain of themes can be created by setting base themes.
+ * @param new_theme pointer to theme which base should be set
+ * @param base pointer to the base theme
+ */
+void lv_theme_set_parent(lv_theme_t * new_theme, lv_theme_t * base)
+{
+ new_theme->parent = base;
+}
+
+/**
+ * Set a callback for a theme.
+ * The callback is used to add styles to different objects
+ * @param theme pointer to theme which callback should be set
+ * @param cb pointer to the callback
+ */
+void lv_theme_set_apply_cb(lv_theme_t * theme, lv_theme_apply_cb_t apply_cb)
+{
+ theme->apply_cb = apply_cb;
+}
+
+const lv_font_t * lv_theme_get_font_small(lv_obj_t * obj)
+{
+ lv_theme_t * th = lv_theme_get_from_obj(obj);
+ return th ? th->font_small : LV_FONT_DEFAULT;
+}
+
+const lv_font_t * lv_theme_get_font_normal(lv_obj_t * obj)
+{
+ lv_theme_t * th = lv_theme_get_from_obj(obj);
+ return th ? th->font_normal : LV_FONT_DEFAULT;
+}
+
+const lv_font_t * lv_theme_get_font_large(lv_obj_t * obj)
+{
+ lv_theme_t * th = lv_theme_get_from_obj(obj);
+ return th ? th->font_large : LV_FONT_DEFAULT;
+}
+
+lv_color_t lv_theme_get_color_primary(lv_obj_t * obj)
+{
+ lv_theme_t * th = lv_theme_get_from_obj(obj);
+ return th ? th->color_primary : lv_palette_main(LV_PALETTE_BLUE_GREY);
+}
+
+lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj)
+{
+ lv_theme_t * th = lv_theme_get_from_obj(obj);
+ return th ? th->color_secondary : lv_palette_main(LV_PALETTE_BLUE);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+static void apply_theme(lv_theme_t * th, lv_obj_t * obj)
+{
+ if(th->parent) apply_theme(th->parent, obj);
+ if(th->apply_cb) th->apply_cb(th, obj);
+}
diff --git a/lib/lvgl/src/core/lv_theme.h b/lib/lvgl/src/core/lv_theme.h
new file mode 100644
index 00000000..ef46336c
--- /dev/null
+++ b/lib/lvgl/src/core/lv_theme.h
@@ -0,0 +1,120 @@
+/**
+ *@file lv_theme.h
+ *
+ */
+
+#ifndef LV_THEME_H
+#define LV_THEME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../core/lv_obj.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+struct _lv_theme_t;
+struct _lv_disp_t;
+
+typedef void (*lv_theme_apply_cb_t)(struct _lv_theme_t *, lv_obj_t *);
+
+typedef struct _lv_theme_t {
+ lv_theme_apply_cb_t apply_cb;
+ struct _lv_theme_t * parent; /**< Apply the current theme's style on top of this theme.*/
+ void * user_data;
+ struct _lv_disp_t * disp;
+ lv_color_t color_primary;
+ lv_color_t color_secondary;
+ const lv_font_t * font_small;
+ const lv_font_t * font_normal;
+ const lv_font_t * font_large;
+ uint32_t flags; /*Any custom flag used by the theme*/
+} lv_theme_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Get the theme assigned to the display of the object
+ * @param obj pointer to a theme object
+ * @return the theme of the object's display (can be NULL)
+ */
+lv_theme_t * lv_theme_get_from_obj(lv_obj_t * obj);
+
+/**
+ * Apply the active theme on an object
+ * @param obj pointer to an object
+ */
+void lv_theme_apply(lv_obj_t * obj);
+
+/**
+ * Set a base theme for a theme.
+ * The styles from the base them will be added before the styles of the current theme.
+ * Arbitrary long chain of themes can be created by setting base themes.
+ * @param new_theme pointer to theme which base should be set
+ * @param parent pointer to the base theme
+ */
+void lv_theme_set_parent(lv_theme_t * new_theme, lv_theme_t * parent);
+
+/**
+ * Set an apply callback for a theme.
+ * The apply callback is used to add styles to different objects
+ * @param theme pointer to theme which callback should be set
+ * @param apply_cb pointer to the callback
+ */
+void lv_theme_set_apply_cb(lv_theme_t * theme, lv_theme_apply_cb_t apply_cb);
+
+/**
+ * Get the small font of the theme
+ * @param obj pointer to an object
+ * @return pointer to the font
+ */
+const lv_font_t * lv_theme_get_font_small(lv_obj_t * obj);
+/**
+ * Get the normal font of the theme
+ * @param obj pointer to an object
+ * @return pointer to the font
+ */
+const lv_font_t * lv_theme_get_font_normal(lv_obj_t * obj);
+
+/**
+ * Get the subtitle font of the theme
+ * @param obj pointer to an object
+ * @return pointer to the font
+ */
+const lv_font_t * lv_theme_get_font_large(lv_obj_t * obj);
+
+/**
+ * Get the primary color of the theme
+ * @param obj pointer to an object
+ * @return the color
+ */
+lv_color_t lv_theme_get_color_primary(lv_obj_t * obj);
+
+/**
+ * Get the secondary color of the theme
+ * @param obj pointer to an object
+ * @return the color
+ */
+lv_color_t lv_theme_get_color_secondary(lv_obj_t * obj);
+
+/**********************
+ * MACROS
+ **********************/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_THEME_H*/