From 09f58aa2e11c04e05fb480f9e0e89ce8aad14231 Mon Sep 17 00:00:00 2001 From: Storm21 <61840624+Storm21CH@users.noreply.github.com> Date: Sat, 30 Jan 2021 16:54:44 +0100 Subject: [PATCH] Add files via upload --- bdk/libs/lvgl/lv_misc/lv_anim.c | 445 ++++++++ bdk/libs/lvgl/lv_misc/lv_anim.h | 176 ++++ bdk/libs/lvgl/lv_misc/lv_area.c | 200 ++++ bdk/libs/lvgl/lv_misc/lv_area.h | 168 +++ bdk/libs/lvgl/lv_misc/lv_circ.c | 79 ++ bdk/libs/lvgl/lv_misc/lv_circ.h | 80 ++ bdk/libs/lvgl/lv_misc/lv_color.c | 167 +++ bdk/libs/lvgl/lv_misc/lv_color.h | 455 +++++++++ bdk/libs/lvgl/lv_misc/lv_font.c | 269 +++++ bdk/libs/lvgl/lv_misc/lv_font.h | 192 ++++ bdk/libs/lvgl/lv_misc/lv_fs.c | 627 ++++++++++++ bdk/libs/lvgl/lv_misc/lv_fs.h | 276 +++++ bdk/libs/lvgl/lv_misc/lv_gc.c | 40 + bdk/libs/lvgl/lv_misc/lv_gc.h | 75 ++ bdk/libs/lvgl/lv_misc/lv_ll.c | 376 +++++++ bdk/libs/lvgl/lv_misc/lv_ll.h | 145 +++ bdk/libs/lvgl/lv_misc/lv_log.c | 82 ++ bdk/libs/lvgl/lv_misc/lv_log.h | 86 ++ bdk/libs/lvgl/lv_misc/lv_math.c | 165 +++ bdk/libs/lvgl/lv_misc/lv_math.h | 73 ++ bdk/libs/lvgl/lv_misc/lv_mem.c | 470 +++++++++ bdk/libs/lvgl/lv_misc/lv_mem.h | 127 +++ bdk/libs/lvgl/lv_misc/lv_misc.mk | 19 + bdk/libs/lvgl/lv_misc/lv_symbol_def.h | 223 ++++ bdk/libs/lvgl/lv_misc/lv_task.c | 367 +++++++ bdk/libs/lvgl/lv_misc/lv_task.h | 186 ++++ bdk/libs/lvgl/lv_misc/lv_templ.c | 36 + bdk/libs/lvgl/lv_misc/lv_templ.h | 38 + bdk/libs/lvgl/lv_misc/lv_txt.c | 793 ++++++++++++++ bdk/libs/lvgl/lv_misc/lv_txt.h | 197 ++++ bdk/libs/lvgl/lv_misc/lv_ufs.c | 516 ++++++++++ bdk/libs/lvgl/lv_misc/lv_ufs.h | 213 ++++ bdk/libs/lvgl/lv_objx/lv_arc.c | 310 ++++++ bdk/libs/lvgl/lv_objx/lv_arc.h | 127 +++ bdk/libs/lvgl/lv_objx/lv_bar.c | 429 ++++++++ bdk/libs/lvgl/lv_objx/lv_bar.h | 160 +++ bdk/libs/lvgl/lv_objx/lv_btn.c | 763 ++++++++++++++ bdk/libs/lvgl/lv_objx/lv_btn.h | 280 +++++ bdk/libs/lvgl/lv_objx/lv_btnm.c | 881 ++++++++++++++++ bdk/libs/lvgl/lv_objx/lv_btnm.h | 197 ++++ bdk/libs/lvgl/lv_objx/lv_calendar.c | 1038 +++++++++++++++++++ bdk/libs/lvgl/lv_objx/lv_calendar.h | 246 +++++ bdk/libs/lvgl/lv_objx/lv_canvas.c | 593 +++++++++++ bdk/libs/lvgl/lv_objx/lv_canvas.h | 229 +++++ bdk/libs/lvgl/lv_objx/lv_cb.c | 347 +++++++ bdk/libs/lvgl/lv_objx/lv_cb.h | 174 ++++ bdk/libs/lvgl/lv_objx/lv_chart.c | 824 +++++++++++++++ bdk/libs/lvgl/lv_objx/lv_chart.h | 262 +++++ bdk/libs/lvgl/lv_objx/lv_cont.c | 642 ++++++++++++ bdk/libs/lvgl/lv_objx/lv_cont.h | 163 +++ bdk/libs/lvgl/lv_objx/lv_ddlist.c | 981 ++++++++++++++++++ bdk/libs/lvgl/lv_objx/lv_ddlist.h | 286 ++++++ bdk/libs/lvgl/lv_objx/lv_gauge.c | 466 +++++++++ bdk/libs/lvgl/lv_objx/lv_gauge.h | 222 ++++ bdk/libs/lvgl/lv_objx/lv_img.c | 408 ++++++++ bdk/libs/lvgl/lv_objx/lv_img.h | 195 ++++ bdk/libs/lvgl/lv_objx/lv_imgbtn.c | 391 +++++++ bdk/libs/lvgl/lv_objx/lv_imgbtn.h | 249 +++++ bdk/libs/lvgl/lv_objx/lv_kb.c | 490 +++++++++ bdk/libs/lvgl/lv_objx/lv_kb.h | 230 +++++ bdk/libs/lvgl/lv_objx/lv_label.c | 987 ++++++++++++++++++ bdk/libs/lvgl/lv_objx/lv_label.h | 313 ++++++ bdk/libs/lvgl/lv_objx/lv_led.c | 244 +++++ bdk/libs/lvgl/lv_objx/lv_led.h | 116 +++ bdk/libs/lvgl/lv_objx/lv_line.c | 301 ++++++ bdk/libs/lvgl/lv_objx/lv_line.h | 158 +++ bdk/libs/lvgl/lv_objx/lv_list.c | 1056 +++++++++++++++++++ bdk/libs/lvgl/lv_objx/lv_list.h | 336 ++++++ bdk/libs/lvgl/lv_objx/lv_lmeter.c | 382 +++++++ bdk/libs/lvgl/lv_objx/lv_lmeter.h | 153 +++ bdk/libs/lvgl/lv_objx/lv_mbox.c | 532 ++++++++++ bdk/libs/lvgl/lv_objx/lv_mbox.h | 221 ++++ bdk/libs/lvgl/lv_objx/lv_objx.mk | 36 + bdk/libs/lvgl/lv_objx/lv_objx_templ.c | 231 +++++ bdk/libs/lvgl/lv_objx/lv_objx_templ.h | 111 ++ bdk/libs/lvgl/lv_objx/lv_page.c | 1205 ++++++++++++++++++++++ bdk/libs/lvgl/lv_objx/lv_page.h | 402 ++++++++ bdk/libs/lvgl/lv_objx/lv_preload.c | 411 ++++++++ bdk/libs/lvgl/lv_objx/lv_preload.h | 168 +++ bdk/libs/lvgl/lv_objx/lv_roller.c | 582 +++++++++++ bdk/libs/lvgl/lv_objx/lv_roller.h | 224 ++++ bdk/libs/lvgl/lv_objx/lv_slider.c | 520 ++++++++++ bdk/libs/lvgl/lv_objx/lv_slider.h | 202 ++++ bdk/libs/lvgl/lv_objx/lv_spinbox.c | 471 +++++++++ bdk/libs/lvgl/lv_objx/lv_spinbox.h | 201 ++++ bdk/libs/lvgl/lv_objx/lv_sw.c | 445 ++++++++ bdk/libs/lvgl/lv_objx/lv_sw.h | 194 ++++ bdk/libs/lvgl/lv_objx/lv_ta.c | 1365 +++++++++++++++++++++++++ bdk/libs/lvgl/lv_objx/lv_ta.h | 390 +++++++ bdk/libs/lvgl/lv_objx/lv_table.c | 855 ++++++++++++++++ bdk/libs/lvgl/lv_objx/lv_table.h | 261 +++++ bdk/libs/lvgl/lv_objx/lv_tabview.c | 910 +++++++++++++++++ bdk/libs/lvgl/lv_objx/lv_tabview.h | 252 +++++ bdk/libs/lvgl/lv_objx/lv_tileview.c | 578 +++++++++++ bdk/libs/lvgl/lv_objx/lv_tileview.h | 163 +++ bdk/libs/lvgl/lv_objx/lv_win.c | 592 +++++++++++ bdk/libs/lvgl/lv_objx/lv_win.h | 299 ++++++ 97 files changed, 34811 insertions(+) create mode 100644 bdk/libs/lvgl/lv_misc/lv_anim.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_anim.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_area.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_area.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_circ.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_circ.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_color.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_color.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_font.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_font.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_fs.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_fs.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_gc.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_gc.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_ll.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_ll.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_log.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_log.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_math.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_math.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_mem.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_mem.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_misc.mk create mode 100644 bdk/libs/lvgl/lv_misc/lv_symbol_def.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_task.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_task.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_templ.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_templ.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_txt.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_txt.h create mode 100644 bdk/libs/lvgl/lv_misc/lv_ufs.c create mode 100644 bdk/libs/lvgl/lv_misc/lv_ufs.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_arc.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_arc.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_bar.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_bar.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_btn.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_btn.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_btnm.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_btnm.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_calendar.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_calendar.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_canvas.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_canvas.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_cb.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_cb.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_chart.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_chart.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_cont.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_cont.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_ddlist.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_ddlist.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_gauge.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_gauge.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_img.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_img.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_imgbtn.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_imgbtn.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_kb.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_kb.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_label.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_label.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_led.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_led.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_line.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_line.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_list.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_list.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_lmeter.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_lmeter.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_mbox.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_mbox.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_objx.mk create mode 100644 bdk/libs/lvgl/lv_objx/lv_objx_templ.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_objx_templ.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_page.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_page.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_preload.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_preload.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_roller.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_roller.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_slider.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_slider.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_spinbox.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_spinbox.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_sw.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_sw.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_ta.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_ta.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_table.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_table.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_tabview.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_tabview.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_tileview.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_tileview.h create mode 100644 bdk/libs/lvgl/lv_objx/lv_win.c create mode 100644 bdk/libs/lvgl/lv_objx/lv_win.h diff --git a/bdk/libs/lvgl/lv_misc/lv_anim.c b/bdk/libs/lvgl/lv_misc/lv_anim.c new file mode 100644 index 00000000..578c5566 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_anim.c @@ -0,0 +1,445 @@ +/** + * @file anim.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_anim.h" + +#if USE_LV_ANIMATION +#include +#include +#include "../lv_hal/lv_hal_tick.h" +#include "lv_task.h" +#include "lv_math.h" +#include "lv_gc.h" + +#if defined(LV_GC_INCLUDE) +# include LV_GC_INCLUDE +#endif /* LV_ENABLE_GC */ + + +/********************* + * DEFINES + *********************/ +#define LV_ANIM_RESOLUTION 1024 +#define LV_ANIM_RES_SHIFT 10 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void anim_task(void * param); +static bool anim_ready_handler(lv_anim_t * a); + +/********************** + * STATIC VARIABLES + **********************/ +static uint32_t last_task_run; +static bool anim_list_changed; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Init. the animation module + */ +void lv_anim_init(void) +{ + lv_ll_init(&LV_GC_ROOT(_lv_anim_ll), sizeof(lv_anim_t)); + last_task_run = lv_tick_get(); + lv_task_create(anim_task, LV_REFR_PERIOD, LV_TASK_PRIO_MID, NULL); +} + +/** + * Create an animation + * @param anim_p an initialized 'anim_t' variable. Not required after call. + */ +void lv_anim_create(lv_anim_t * anim_p) +{ + LV_LOG_TRACE("animation create started") + /* Do not let two animations for the same 'var' with the same 'fp'*/ + if(anim_p->fp != NULL) lv_anim_del(anim_p->var, anim_p->fp); /*fp == NULL would delete all animations of var*/ + + /*Add the new animation to the animation linked list*/ + lv_anim_t * new_anim = lv_ll_ins_head(&LV_GC_ROOT(_lv_anim_ll)); + lv_mem_assert(new_anim); + if(new_anim == NULL) return; + + /*Initialize the animation descriptor*/ + anim_p->playback_now = 0; + memcpy(new_anim, anim_p, sizeof(lv_anim_t)); + + /*Set the start value*/ + if(new_anim->fp != NULL) new_anim->fp(new_anim->var, new_anim->start); + + /* Creating an animation changed the linked list. + * It's important if it happens in a ready callback. (see `anim_task`)*/ + anim_list_changed = true; + + LV_LOG_TRACE("animation created") +} + +/** + * Delete an animation for a variable with a given animator function + * @param var pointer to variable + * @param fp a function pointer which is animating 'var', + * or NULL to delete all animations of 'var' + * @return true: at least 1 animation is deleted, false: no animation is deleted + */ +bool lv_anim_del(void * var, lv_anim_fp_t fp) +{ + lv_anim_t * a; + lv_anim_t * a_next; + bool del = false; + a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)); + while(a != NULL) { + /*'a' might be deleted, so get the next object while 'a' is valid*/ + a_next = lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a); + + if(a->var == var && (a->fp == fp || fp == NULL)) { + lv_ll_rem(&LV_GC_ROOT(_lv_anim_ll), a); + lv_mem_free(a); + anim_list_changed = true; /*Read by `anim_task`. It need to know if a delete occurred in the linked list*/ + del = true; + } + + a = a_next; + } + + return del; +} + +/** + * Get the number of currently running animations + * @return the number of running animations + */ +uint16_t lv_anim_count_running(void) +{ + uint16_t cnt = 0; + lv_anim_t * a; + LL_READ(LV_GC_ROOT(_lv_anim_ll), a) cnt++; + + return cnt++; +} + +/** + * Calculate the time of an animation with a given speed and the start and end values + * @param speed speed of animation in unit/sec + * @param start start value of the animation + * @param end end value of the animation + * @return the required time [ms] for the animation with the given parameters + */ +uint16_t lv_anim_speed_to_time(uint16_t speed, int32_t start, int32_t end) +{ + int32_t d = LV_MATH_ABS((int32_t) start - end); + uint32_t time = (int32_t)((int32_t)(d * 1000) / speed); + + if(time > UINT16_MAX) time = UINT16_MAX; + + if(time == 0) { + time++; + } + + return time; +} + +/** + * Calculate the current value of an animation applying linear characteristic + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_linear(const lv_anim_t * a) +{ + /*Calculate the current step*/ + uint16_t step; + if(a->time == a->act_time) step = LV_ANIM_RESOLUTION; /*Use the last value if the time fully elapsed*/ + else step = (a->act_time * LV_ANIM_RESOLUTION) / a->time; + + /* Get the new value which will be proportional to `step` + * and the `start` and `end` values*/ + int32_t new_value; + new_value = (int32_t) step * (a->end - a->start); + new_value = new_value >> LV_ANIM_RES_SHIFT; + new_value += a->start; + + return new_value; +} + +/** + * Calculate the current value of an animation slowing down the start phase + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_ease_in(const lv_anim_t * a) +{ + /*Calculate the current step*/ + uint32_t t; + if(a->time == a->act_time) t = 1024; + else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time; + + int32_t step = lv_bezier3(t, 0, 1, 1, 1024); + + int32_t new_value; + new_value = (int32_t) step * (a->end - a->start); + new_value = new_value >> 10; + new_value += a->start; + + + return new_value; +} + +/** + * Calculate the current value of an animation slowing down the end phase + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_ease_out(const lv_anim_t * a) +{ + /*Calculate the current step*/ + + uint32_t t; + if(a->time == a->act_time) t = 1024; + else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time; + + int32_t step = lv_bezier3(t, 0, 1023, 1023, 1024); + + int32_t new_value; + new_value = (int32_t) step * (a->end - a->start); + new_value = new_value >> 10; + new_value += a->start; + + + return new_value; +} + +/** + * Calculate the current value of an animation applying an "S" characteristic (cosine) + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_ease_in_out(const lv_anim_t * a) +{ + /*Calculate the current step*/ + + uint32_t t; + if(a->time == a->act_time) t = 1024; + else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time; + + int32_t step = lv_bezier3(t, 0, 100, 924, 1024); + + int32_t new_value; + new_value = (int32_t) step * (a->end - a->start); + new_value = new_value >> 10; + new_value += a->start; + + + return new_value; +} + +/** + * Calculate the current value of an animation with overshoot at the end + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_overshoot(const lv_anim_t * a) +{ + /*Calculate the current step*/ + + uint32_t t; + if(a->time == a->act_time) t = 1024; + else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time; + + int32_t step = lv_bezier3(t, 0, 600, 1300, 1024); + + int32_t new_value; + new_value = (int32_t) step * (a->end - a->start); + new_value = new_value >> 10; + new_value += a->start; + + + return new_value; +} + +/** + * Calculate the current value of an animation with 3 bounces + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_bounce(const lv_anim_t * a) +{ + /*Calculate the current step*/ + uint32_t t; + if(a->time == a->act_time) t = 1024; + else t = (uint32_t)((uint32_t)a->act_time * 1024) / a->time; + + int32_t diff = (a->end - a->start); + + /*3 bounces has 5 parts: 3 down and 2 up. One part is t / 5 long*/ + + if(t < 408){ + /*Go down*/ + t = (t * 2500) >> 10; /*[0..1024] range*/ + } + else if(t >= 408 && t < 614) { + /*First bounce back*/ + t -= 408; + t = t * 5; /*to [0..1024] range*/ + t = 1024 - t; + diff = diff / 6; + } + else if(t >= 614 && t < 819) { + /*Fall back*/ + t -= 614; + t = t * 5; /*to [0..1024] range*/ + diff = diff / 6; + } + else if(t >= 819 && t < 921) { + /*Second bounce back*/ + t -= 819; + t = t * 10; /*to [0..1024] range*/ + t = 1024 - t; + diff = diff / 16; + } + else if(t >= 921 && t <= 1024) { + /*Fall back*/ + t -= 921; + t = t * 10; /*to [0..1024] range*/ + diff = diff / 16; + } + + if(t > 1024) t = 1024; + + int32_t step = lv_bezier3(t, 1024, 1024, 800, 0); + + int32_t new_value; + + new_value = (int32_t) step * diff; + new_value = new_value >> 10; + new_value = a->end - new_value; + + + return new_value; +} + +/** + * Calculate the current value of an animation applying step characteristic. + * (Set end value on the end of the animation) + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_step(const lv_anim_t * a) +{ + if(a->act_time >= a->time) return a->end; + else return a->start; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Periodically handle the animations. + * @param param unused + */ +static void anim_task(void * param) +{ + (void)param; + + lv_anim_t * a; + LL_READ(LV_GC_ROOT(_lv_anim_ll), a) { + a->has_run = 0; + } + + uint32_t elaps = lv_tick_elaps(last_task_run); + a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)); + + while(a != NULL) { + /*It can be set by `lv_anim_del()` typically in `end_cb`. If set then an animation delete happened in `anim_ready_handler` + * which could make this linked list reading corrupt because the list is changed meanwhile + */ + anim_list_changed = false; + + if(!a->has_run) { + a->has_run = 1; /*The list readying might be reseted so need to know which anim has run already*/ + a->act_time += elaps; + if(a->act_time >= 0) { + if(a->act_time > a->time) a->act_time = a->time; + + int32_t new_value; + new_value = a->path(a); + + if(a->fp != NULL) a->fp(a->var, new_value); /*Apply the calculated value*/ + + /*If the time is elapsed the animation is ready*/ + if(a->act_time >= a->time) { + anim_ready_handler(a); + } + } + } + + /* If the linked list changed due to anim. delete then it's not safe to continue + * the reading of the list from here -> start from the head*/ + if(anim_list_changed) a = lv_ll_get_head(&LV_GC_ROOT(_lv_anim_ll)); + else a = lv_ll_get_next(&LV_GC_ROOT(_lv_anim_ll), a); + } + + last_task_run = lv_tick_get(); +} + +/** + * Called when an animation is ready to do the necessary thinks + * e.g. repeat, play back, delete etc. + * @param a pointer to an animation descriptor + * @return true: animation delete occurred nnd the `LV_GC_ROOT(_lv_anim_ll)` has changed + * */ +static bool anim_ready_handler(lv_anim_t * a) +{ + + /*Delete the animation if + * - no repeat and no play back (simple one shot animation) + * - no repeat, play back is enabled and play back is ready */ + if((a->repeat == 0 && a->playback == 0) || + (a->repeat == 0 && a->playback == 1 && a->playback_now == 1)) { + void (*cb)(void *) = a->end_cb; + void * p = a->var; + lv_ll_rem(&LV_GC_ROOT(_lv_anim_ll), a); + lv_mem_free(a); + anim_list_changed = true; + + /* Call the callback function at the end*/ + /* Check if an animation is deleted in the cb function + * if yes then the caller function has to know this*/ + if(cb != NULL) cb(p); + } + /*If the animation is not deleted then restart it*/ + else { + a->act_time = - a->repeat_pause; /*Restart the animation*/ + /*Swap the start and end values in play back mode*/ + if(a->playback != 0) { + /*If now turning back use the 'playback_pause*/ + if(a->playback_now == 0) a->act_time = - a->playback_pause; + + /*Toggle the play back state*/ + a->playback_now = a->playback_now == 0 ? 1 : 0; + /*Swap the start and end values*/ + int32_t tmp; + tmp = a->start; + a->start = a->end; + a->end = tmp; + } + } + + return anim_list_changed; +} +#endif diff --git a/bdk/libs/lvgl/lv_misc/lv_anim.h b/bdk/libs/lvgl/lv_misc/lv_anim.h new file mode 100644 index 00000000..6625ae27 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_anim.h @@ -0,0 +1,176 @@ +/** + * @file anim.h + * + */ + +#ifndef ANIM_H +#define ANIM_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_ANIMATION + +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +struct _lv_anim_t; + +typedef int32_t(*lv_anim_path_t)(const struct _lv_anim_t*); + +typedef void (*lv_anim_fp_t)(void *, int32_t); +typedef void (*lv_anim_cb_t)(void *); + +typedef struct _lv_anim_t +{ + void * var; /*Variable to animate*/ + lv_anim_fp_t fp; /*Animator function*/ + lv_anim_cb_t end_cb; /*Call it when the animation is ready*/ + lv_anim_path_t path; /*An array with the steps of animations*/ + int32_t start; /*Start value*/ + int32_t end; /*End value*/ + uint16_t time; /*Animation time in ms*/ + int16_t act_time; /*Current time in animation. Set to negative to make delay.*/ + uint16_t playback_pause; /*Wait before play back*/ + uint16_t repeat_pause; /*Wait before repeat*/ + uint8_t playback :1; /*When the animation is ready play it back*/ + uint8_t repeat :1; /*Repeat the animation infinitely*/ + /*Animation system use these - user shouldn't set*/ + uint8_t playback_now :1; /*Play back is in progress*/ + uint32_t has_run :1; /*Indicates the animation has run it this round*/ +} lv_anim_t; + +/*Example initialization +lv_anim_t a; +a.var = obj; +a.start = lv_obj_get_height(obj); +a.end = new_height; +a.fp = (lv_anim_fp_t)lv_obj_set_height; +a.path = lv_anim_path_linear; +a.end_cb = NULL; +a.act_time = 0; +a.time = 200; +a.playback = 0; +a.playback_pause = 0; +a.repeat = 0; +a.repeat_pause = 0; +lv_anim_create(&a); + */ +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Init. the animation module + */ +void lv_anim_init(void); + +/** + * Create an animation + * @param anim_p an initialized 'anim_t' variable. Not required after call. + */ +void lv_anim_create(lv_anim_t * anim_p); + +/** + * Delete an animation for a variable with a given animatior function + * @param var pointer to variable + * @param fp a function pointer which is animating 'var', + * or NULL to ignore it and delete all animation with 'var + * @return true: at least 1 animation is deleted, false: no animation is deleted + */ +bool lv_anim_del(void * var, lv_anim_fp_t fp); + +/** + * Get the number of currently running animations + * @return the number of running animations + */ +uint16_t lv_anim_count_running(void); + +/** + * Calculate the time of an animation with a given speed and the start and end values + * @param speed speed of animation in unit/sec + * @param start start value of the animation + * @param end end value of the animation + * @return the required time [ms] for the animation with the given parameters + */ +uint16_t lv_anim_speed_to_time(uint16_t speed, int32_t start, int32_t end); + +/** + * Calculate the current value of an animation applying linear characteristic + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_linear(const lv_anim_t *a); + +/** + * Calculate the current value of an animation slowing down the start phase + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_ease_in(const lv_anim_t * a); + +/** + * Calculate the current value of an animation slowing down the end phase + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_ease_out(const lv_anim_t * a); + +/** + * Calculate the current value of an animation applying an "S" characteristic (cosine) + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_ease_in_out(const lv_anim_t *a); + +/** + * Calculate the current value of an animation with overshoot at the end + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_overshoot(const lv_anim_t * a); + +/** + * Calculate the current value of an animation with 3 bounces + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_bounce(const lv_anim_t * a); + +/** + * Calculate the current value of an animation applying step characteristic. + * (Set end value on the end of the animation) + * @param a pointer to an animation + * @return the current value to set + */ +int32_t lv_anim_path_step(const lv_anim_t *a); +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_ANIMATION == 0*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_ANIM_H*/ + diff --git a/bdk/libs/lvgl/lv_misc/lv_area.c b/bdk/libs/lvgl/lv_misc/lv_area.c new file mode 100644 index 00000000..f340690a --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_area.c @@ -0,0 +1,200 @@ +/** + * @file lv_area.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_area.h" +#include "lv_math.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize an area + * @param area_p pointer to an area + * @param x1 left coordinate of the area + * @param y1 top coordinate of the area + * @param x2 right coordinate of the area + * @param y2 bottom coordinate of the area + */ +void lv_area_set(lv_area_t * area_p, lv_coord_t x1, lv_coord_t y1, lv_coord_t x2, lv_coord_t y2) +{ + area_p->x1 = x1; + area_p->y1 = y1; + area_p->x2 = x2; + area_p->y2 = y2; +} + +/** + * Set the width of an area + * @param area_p pointer to an area + * @param w the new width of the area (w == 1 makes x1 == x2) + */ +void lv_area_set_width(lv_area_t * area_p, lv_coord_t w) +{ + area_p->x2 = area_p->x1 + w - 1; +} + +/** + * Set the height of an area + * @param area_p pointer to an area + * @param h the new height of the area (h == 1 makes y1 == y2) + */ +void lv_area_set_height(lv_area_t * area_p, lv_coord_t h) +{ + area_p->y2 = area_p->y1 + h - 1; +} + +/** + * Set the position of an area (width and height will be kept) + * @param area_p pointer to an area + * @param x the new x coordinate of the area + * @param y the new y coordinate of the area + */ +void lv_area_set_pos(lv_area_t * area_p, lv_coord_t x, lv_coord_t y) +{ + lv_coord_t w = lv_area_get_width(area_p); + lv_coord_t h = lv_area_get_height(area_p); + area_p->x1 = x; + area_p->y1 = y; + lv_area_set_width(area_p, w); + lv_area_set_height(area_p, h); +} + +/** + * Return with area of an area (x * y) + * @param area_p pointer to an area + * @return size of area + */ +uint32_t lv_area_get_size(const lv_area_t * area_p) +{ + uint32_t size; + + size = (uint32_t)(area_p->x2 - area_p->x1 + 1) * + (area_p->y2 - area_p->y1 + 1); + + return size; +} + +/** + * Get the common parts of two areas + * @param res_p pointer to an area, the result will be stored here + * @param a1_p pointer to the first area + * @param a2_p pointer to the second area + * @return false: the two area has NO common parts, res_p is invalid + */ +bool lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p) +{ + /* Get the smaller area from 'a1_p' and 'a2_p' */ + res_p->x1 = LV_MATH_MAX(a1_p->x1, a2_p->x1); + res_p->y1 = LV_MATH_MAX(a1_p->y1, a2_p->y1); + res_p->x2 = LV_MATH_MIN(a1_p->x2, a2_p->x2); + res_p->y2 = LV_MATH_MIN(a1_p->y2, a2_p->y2); + + /*If x1 or y1 greater then x2 or y2 then the areas union is empty*/ + bool union_ok = true; + if((res_p->x1 > res_p->x2) || + (res_p->y1 > res_p->y2)) { + union_ok = false; + } + + return union_ok; +} +/** + * Join two areas into a third which involves the other two + * @param res_p pointer to an area, the result will be stored here + * @param a1_p pointer to the first area + * @param a2_p pointer to the second area + */ +void lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p) +{ + a_res_p->x1 = LV_MATH_MIN(a1_p->x1, a2_p->x1); + a_res_p->y1 = LV_MATH_MIN(a1_p->y1, a2_p->y1); + a_res_p->x2 = LV_MATH_MAX(a1_p->x2, a2_p->x2); + a_res_p->y2 = LV_MATH_MAX(a1_p->y2, a2_p->y2); +} + +/** + * Check if a point is on an area + * @param a_p pointer to an area + * @param p_p pointer to a point + * @return false:the point is out of the area + */ +bool lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p) +{ + bool is_on = false; + + if((p_p->x >= a_p->x1 && p_p->x <= a_p->x2) && + ((p_p->y >= a_p->y1 && p_p->y <= a_p->y2))) { + is_on = true; + } + + return is_on; +} + +/** + * Check if two area has common parts + * @param a1_p pointer to an area. + * @param a2_p pointer to an other area + * @return false: a1_p and a2_p has no common parts + */ +bool lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p) +{ + if((a1_p->x1 <= a2_p->x2) && + (a1_p->x2 >= a2_p->x1) && + (a1_p->y1 <= a2_p->y2) && + (a1_p->y2 >= a2_p->y1)) { + return true; + } else { + return false; + } + +} + +/** + * Check if an area is fully on an other + * @param ain_p pointer to an area which could be in 'aholder_p' + * @param aholder pointer to an area which could involve 'ain_p' + * @return + */ +bool lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p) +{ + bool is_in = false; + + if(ain_p->x1 >= aholder_p->x1 && + ain_p->y1 >= aholder_p->y1 && + ain_p->x2 <= aholder_p->x2 && + ain_p->y2 <= aholder_p->y2) { + is_in = true; + } + + return is_in; +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/bdk/libs/lvgl/lv_misc/lv_area.h b/bdk/libs/lvgl/lv_misc/lv_area.h new file mode 100644 index 00000000..bae07537 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_area.h @@ -0,0 +1,168 @@ +/** + * @file lv_area.h + * + */ + +#ifndef LV_AREA_H +#define LV_AREA_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************* + * INCLUDES + *********************/ +#include +#include "../../../utils/types.h" + +/********************* + * DEFINES + *********************/ +#define LV_COORD_MAX (16383) /*To avoid overflow don't let the max [-32,32k] range */ +#define LV_COORD_MIN (-16384) + +/********************** + * TYPEDEFS + **********************/ +typedef int16_t lv_coord_t; + +typedef struct +{ + lv_coord_t x; + lv_coord_t y; +} lv_point_t; + +typedef struct +{ + lv_coord_t x1; + lv_coord_t y1; + lv_coord_t x2; + lv_coord_t y2; +} lv_area_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize an area + * @param area_p pointer to an area + * @param x1 left coordinate of the area + * @param y1 top coordinate of the area + * @param x2 right coordinate of the area + * @param y2 bottom coordinate of the area + */ +void lv_area_set(lv_area_t * area_p, lv_coord_t x1, lv_coord_t y1, lv_coord_t x2, lv_coord_t y2); + +/** + * Copy an area + * @param dest pointer to the destination area + * @param src pointer to the source area + */ +inline static void lv_area_copy(lv_area_t * dest, const lv_area_t * src) +{ + memcpy(dest, src, sizeof(lv_area_t)); +} + +/** + * Get the width of an area + * @param area_p pointer to an area + * @return the width of the area (if x1 == x2 -> width = 1) + */ +static inline lv_coord_t lv_area_get_width(const lv_area_t * area_p) +{ + return area_p->x2 - area_p->x1 + 1; +} + +/** + * Get the height of an area + * @param area_p pointer to an area + * @return the height of the area (if y1 == y2 -> height = 1) + */ +static inline lv_coord_t lv_area_get_height(const lv_area_t * area_p) +{ + return area_p->y2 - area_p->y1 + 1; +} + +/** + * Set the width of an area + * @param area_p pointer to an area + * @param w the new width of the area (w == 1 makes x1 == x2) + */ +void lv_area_set_width(lv_area_t * area_p, lv_coord_t w); + +/** + * Set the height of an area + * @param area_p pointer to an area + * @param h the new height of the area (h == 1 makes y1 == y2) + */ +void lv_area_set_height(lv_area_t * area_p, lv_coord_t h); + +/** + * Set the position of an area (width and height will be kept) + * @param area_p pointer to an area + * @param x the new x coordinate of the area + * @param y the new y coordinate of the area + */ +void lv_area_set_pos(lv_area_t * area_p, lv_coord_t x, lv_coord_t y); + +/** + * Return with area of an area (x * y) + * @param area_p pointer to an area + * @return size of area + */ +uint32_t lv_area_get_size(const lv_area_t * area_p); + +/** + * Get the common parts of two areas + * @param res_p pointer to an area, the result will be stored her + * @param a1_p pointer to the first area + * @param a2_p pointer to the second area + * @return false: the two area has NO common parts, res_p is invalid + */ +bool lv_area_intersect(lv_area_t * res_p, const lv_area_t * a1_p, const lv_area_t * a2_p); + +/** + * Join two areas into a third which involves the other two + * @param res_p pointer to an area, the result will be stored here + * @param a1_p pointer to the first area + * @param a2_p pointer to the second area + */ +void lv_area_join(lv_area_t * a_res_p, const lv_area_t * a1_p, const lv_area_t * a2_p); + +/** + * Check if a point is on an area + * @param a_p pointer to an area + * @param p_p pointer to a point + * @return false:the point is out of the area + */ +bool lv_area_is_point_on(const lv_area_t * a_p, const lv_point_t * p_p); + +/** + * Check if two area has common parts + * @param a1_p pointer to an area. + * @param a2_p pointer to an other area + * @return false: a1_p and a2_p has no common parts + */ +bool lv_area_is_on(const lv_area_t * a1_p, const lv_area_t * a2_p); + +/** + * Check if an area is fully on an other + * @param ain_p pointer to an area which could be on aholder_p + * @param aholder pointer to an area which could involve ain_p + * @return + */ +bool lv_area_is_in(const lv_area_t * ain_p, const lv_area_t * aholder_p); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif diff --git a/bdk/libs/lvgl/lv_misc/lv_circ.c b/bdk/libs/lvgl/lv_misc/lv_circ.c new file mode 100644 index 00000000..d89d833d --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_circ.c @@ -0,0 +1,79 @@ +/** + * @file lv_circ.c + * Circle drawing algorithm (with Bresenham) + * Only a 1/8 circle is calculated. Use CIRC_OCT1_X, CIRC_OCT1_Y macros to get + * the other octets. + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_circ.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the circle drawing + * @param c pointer to a point. The coordinates will be calculated here + * @param tmp point to a variable. It will store temporary data + * @param radius radius of the circle + */ +void lv_circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius) +{ + c->x = radius; + c->y = 0; + *tmp = 1 - radius; +} + +/** + * Test the circle drawing is ready or not + * @param c same as in circ_init + * @return true if the circle is not ready yet + */ +bool lv_circ_cont(lv_point_t * c) +{ + return c->y <= c->x ? true : false; +} + +/** + * Get the next point from the circle + * @param c same as in circ_init. The next point stored here. + * @param tmp same as in circ_init. + */ +void lv_circ_next(lv_point_t * c, lv_coord_t * tmp) +{ + c->y++; + + if(*tmp <= 0) { + (*tmp) += 2 * c->y + 1; // Change in decision criterion for y -> y+1 + } else { + c->x--; + (*tmp) += 2 * (c->y - c->x) + 1; // Change for y -> y+1, x -> x-1 + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/bdk/libs/lvgl/lv_misc/lv_circ.h b/bdk/libs/lvgl/lv_misc/lv_circ.h new file mode 100644 index 00000000..f0fbb3f5 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_circ.h @@ -0,0 +1,80 @@ +/** + * @file lv_circ.h + * + */ + +#ifndef LV_CIRC_H +#define LV_CIRC_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************* + * INCLUDES + *********************/ +#include +#include "lv_area.h" +#include "../../../utils/types.h" + +/********************* + * DEFINES + *********************/ +#define LV_CIRC_OCT1_X(p) (p.x) +#define LV_CIRC_OCT1_Y(p) (p.y) +#define LV_CIRC_OCT2_X(p) (p.y) +#define LV_CIRC_OCT2_Y(p) (p.x) +#define LV_CIRC_OCT3_X(p) (-p.y) +#define LV_CIRC_OCT3_Y(p) (p.x) +#define LV_CIRC_OCT4_X(p) (-p.x) +#define LV_CIRC_OCT4_Y(p) (p.y) +#define LV_CIRC_OCT5_X(p) (-p.x) +#define LV_CIRC_OCT5_Y(p) (-p.y) +#define LV_CIRC_OCT6_X(p) (-p.y) +#define LV_CIRC_OCT6_Y(p) (-p.x) +#define LV_CIRC_OCT7_X(p) (p.y) +#define LV_CIRC_OCT7_Y(p) (-p.x) +#define LV_CIRC_OCT8_X(p) (p.x) +#define LV_CIRC_OCT8_Y(p) (-p.y) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the circle drawing + * @param c pointer to a point. The coordinates will be calculated here + * @param tmp point to a variable. It will store temporary data + * @param radius radius of the circle + */ +void lv_circ_init(lv_point_t * c, lv_coord_t * tmp, lv_coord_t radius); + +/** + * Test the circle drawing is ready or not + * @param c same as in circ_init + * @return true if the circle is not ready yet + */ +bool lv_circ_cont(lv_point_t * c); + +/** + * Get the next point from the circle + * @param c same as in circ_init. The next point stored here. + * @param tmp same as in circ_init. + */ +void lv_circ_next(lv_point_t * c, lv_coord_t * tmp); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif diff --git a/bdk/libs/lvgl/lv_misc/lv_color.c b/bdk/libs/lvgl/lv_misc/lv_color.c new file mode 100644 index 00000000..8c121934 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_color.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2019-2020 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_color.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_color.h" + +/********************* + * DEFINES + *********************/ + +#define HUE_DEGREE 512 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Convert a HSV color to RGB + * @param h hue [0..359] + * @param s saturation [0..100] + * @param v value [0..100] + * @return the given RGB color in RGB (with LV_COLOR_DEPTH depth) + */ +lv_color_t lv_color_hsv_to_rgb(uint16_t hue, uint8_t sat, uint8_t val) +{ + uint8_t r, g, b; + + uint32_t h = (hue * 360 * HUE_DEGREE -1) / 360; + uint32_t s = sat * 255 / 100; + uint32_t v = val * 255 / 100; + uint32_t p = (256 * v - s * v) / 256; + uint32_t region = h / (60 * 512); + + if(sat == 0) + return LV_COLOR_MAKE(v, v, v); + + if (region & 1) + { + uint32_t q = (256 * 60 * HUE_DEGREE * v - h * s * v + 60 * HUE_DEGREE * s * v * region) / + (256 * 60 * HUE_DEGREE); + + switch (region) + { + case 1: + r = q; + g = v; + b = p; + break; + case 3: + r = p; + g = q; + b = v; + break; + case 5: + default: + r = v; + g = p; + b = q; + break; + } + } + else + { + uint32_t t = (256 * 60 * HUE_DEGREE * v + h * s * v - 60 * HUE_DEGREE * s * v * (region + 1)) / + (256 * 60 * HUE_DEGREE); + + switch (region) + { + case 0: + r = v; + g = t; + b = p; + break; + case 2: + r = p; + g = v; + b = t; + break; + case 4: + default: + r = t; + g = p; + b = v; + break; + } + } + + return LV_COLOR_MAKE(r, g, b); +} + +/** + * Convert an RGB color to HSV + * @param r red + * @param g green + * @param b blue + * @return the given RGB color n HSV + */ +lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r, uint8_t g, uint8_t b) +{ + lv_color_hsv_t hsv; + uint8_t rgbMin, rgbMax; + + rgbMin = r < g ? (r < b ? r : b) : (g < b ? g : b); + rgbMax = r > g ? (r > b ? r : b) : (g > b ? g : b); + + hsv.v = rgbMax; + if(hsv.v == 0) { + hsv.h = 0; + hsv.s = 0; + return hsv; + } + + hsv.s = 255 * (long)(rgbMax - rgbMin) / hsv.v; + if(hsv.s == 0) { + hsv.h = 0; + return hsv; + } + + if(rgbMax == r) + hsv.h = 0 + 43 * (g - b) / (rgbMax - rgbMin); + else if(rgbMax == g) + hsv.h = 85 + 43 * (b - r) / (rgbMax - rgbMin); + else + hsv.h = 171 + 43 * (r - g) / (rgbMax - rgbMin); + + return hsv; +} diff --git a/bdk/libs/lvgl/lv_misc/lv_color.h b/bdk/libs/lvgl/lv_misc/lv_color.h new file mode 100644 index 00000000..3459b639 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_color.h @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_color.h + * + */ + +#ifndef LV_COLOR_H +#define LV_COLOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +/*Error checking*/ +#if LV_COLOR_DEPTH == 24 +#error "LV_COLOR_DEPTH 24 is deprecated. Use LV_COLOR_DEPTH 32 instead (lv_conf.h)" +#endif + +#if LV_COLOR_DEPTH != 32 && LV_COLOR_SCREEN_TRANSP != 0 +#error "LV_COLOR_SCREEN_TRANSP requires LV_COLOR_DEPTH == 32. Set it in lv_conf.h" +#endif + +#if LV_COLOR_DEPTH != 16 && LV_COLOR_16_SWAP != 0 +#error "LV_COLOR_16_SWAP requires LV_COLOR_DEPTH == 16. Set it in lv_conf.h" +#endif + + +#include + +/********************* + * DEFINES + *********************/ +#define LV_COLOR_WHITE LV_COLOR_MAKE(0xFF,0xFF,0xFF) +#define LV_COLOR_SILVER LV_COLOR_MAKE(0xC0,0xC0,0xC0) +#define LV_COLOR_GRAY LV_COLOR_MAKE(0x80,0x80,0x80) +#define LV_COLOR_BLACK LV_COLOR_MAKE(0x00,0x00,0x00) +#define LV_COLOR_RED LV_COLOR_MAKE(0xFF,0x00,0x00) +#define LV_COLOR_MAROON LV_COLOR_MAKE(0x80,0x00,0x00) +#define LV_COLOR_YELLOW LV_COLOR_MAKE(0xFF,0xFF,0x00) +#define LV_COLOR_OLIVE LV_COLOR_MAKE(0x80,0x80,0x00) +#define LV_COLOR_LIME LV_COLOR_MAKE(0x00,0xFF,0x00) +#define LV_COLOR_GREEN LV_COLOR_MAKE(0x00,0x80,0x00) +#define LV_COLOR_CYAN LV_COLOR_MAKE(0x00,0xFF,0xFF) +#define LV_COLOR_AQUA LV_COLOR_CYAN +#define LV_COLOR_TEAL LV_COLOR_MAKE(0x00,0x80,0x80) +#define LV_COLOR_BLUE LV_COLOR_MAKE(0x00,0x00,0xFF) +#define LV_COLOR_NAVY LV_COLOR_MAKE(0x00,0x00,0x80) +#define LV_COLOR_MAGENTA LV_COLOR_MAKE(0xFF,0x00,0xFF) +#define LV_COLOR_PURPLE LV_COLOR_MAKE(0x80,0x00,0x80) +#define LV_COLOR_ORANGE LV_COLOR_MAKE(0xFF,0xA5,0x00) + +enum { + LV_OPA_TRANSP = 0, + LV_OPA_0 = 0, + LV_OPA_10 = 25, + LV_OPA_20 = 51, + LV_OPA_30 = 76, + LV_OPA_40 = 102, + LV_OPA_50 = 127, + LV_OPA_60 = 153, + LV_OPA_70 = 178, + LV_OPA_80 = 204, + LV_OPA_90 = 229, + LV_OPA_100 = 255, + LV_OPA_COVER = 255, +}; + +#define LV_OPA_MIN 16 /*Opacities below this will be transparent*/ +#define LV_OPA_MAX 251 /*Opacities above this will fully cover*/ + +#if LV_COLOR_DEPTH == 1 +#define LV_COLOR_SIZE 8 +#elif LV_COLOR_DEPTH == 8 +#define LV_COLOR_SIZE 8 +#elif LV_COLOR_DEPTH == 16 +#define LV_COLOR_SIZE 16 +#elif LV_COLOR_DEPTH == 32 +#define LV_COLOR_SIZE 32 +#else +#error "Invalid LV_COLOR_DEPTH in lv_conf.h! Set it to 1, 8, 16 or 32!" +#endif + +/********************** + * TYPEDEFS + **********************/ + +typedef union +{ + uint8_t blue :1; + uint8_t green :1; + uint8_t red :1; + uint8_t full :1; +} lv_color1_t; + +typedef union +{ + struct + { + uint8_t blue :2; + uint8_t green :3; + uint8_t red :3; + }; + uint8_t full; +} lv_color8_t; + +typedef union +{ + struct + { +#if LV_COLOR_16_SWAP == 0 + uint16_t blue :5; + uint16_t green :6; + uint16_t red :5; +#else + uint16_t green_h :3; + uint16_t red :5; + uint16_t blue :5; + uint16_t green_l :3; +#endif + }; + uint16_t full; +} lv_color16_t; + +typedef union +{ + struct + { + uint8_t blue; + uint8_t green; + uint8_t red; + uint8_t alpha; + }; + uint32_t full; +} lv_color32_t; + +#if LV_COLOR_DEPTH == 1 +typedef uint8_t lv_color_int_t; +typedef lv_color1_t lv_color_t; +#elif LV_COLOR_DEPTH == 8 +typedef uint8_t lv_color_int_t; +typedef lv_color8_t lv_color_t; +#elif LV_COLOR_DEPTH == 16 +typedef uint16_t lv_color_int_t; +typedef lv_color16_t lv_color_t; +#elif LV_COLOR_DEPTH == 32 +typedef uint32_t lv_color_int_t; +typedef lv_color32_t lv_color_t; +#else +#error "Invalid LV_COLOR_DEPTH in lv_conf.h! Set it to 1, 8, 16 or 32!" +#endif + +typedef uint8_t lv_opa_t; + +typedef struct +{ + uint16_t h; + uint8_t s; + uint8_t v; +} lv_color_hsv_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/*In color conversations: + * - When converting to bigger color type the LSB weight of 1 LSB is calculated + * E.g. 16 bit Red has 5 bits + * 8 bit Red has 2 bits + * ---------------------- + * 8 bit red LSB = (2^5 - 1) / (2^2 - 1) = 31 / 3 = 10 + * + * - When calculating to smaller color type simply shift out the LSBs + * E.g. 8 bit Red has 2 bits + * 16 bit Red has 5 bits + * ---------------------- + * Shift right with 5 - 3 = 2 + */ + +static inline uint8_t lv_color_to1(lv_color_t color) +{ +#if LV_COLOR_DEPTH == 1 + return color.full; +#elif LV_COLOR_DEPTH == 8 + if((color.red & 0x4) || + (color.green & 0x4) || + (color.blue & 0x2)) { + return 1; + } else { + return 0; + } +#elif LV_COLOR_DEPTH == 16 +# if LV_COLOR_16_SWAP == 0 + if((color.red & 0x10) || + (color.green & 0x20) || + (color.blue & 0x10)) { + return 1; +# else + if((color.red & 0x10) || + (color.green_h & 0x20) || + (color.blue & 0x10)) { + return 1; +# endif + } else { + return 0; + } +#elif LV_COLOR_DEPTH == 32 + if((color.red & 0x80) || + (color.green & 0x80) || + (color.blue & 0x80)) { + return 1; + } else { + return 0; + } +#endif +} + +static inline uint8_t lv_color_to8(lv_color_t color) +{ +#if LV_COLOR_DEPTH == 1 + if(color.full == 0) return 0; + else return 0xFF; +#elif LV_COLOR_DEPTH == 8 + return color.full; +#elif LV_COLOR_DEPTH == 16 + +# if LV_COLOR_16_SWAP == 0 + lv_color8_t ret; + ret.red = color.red >> 2; /* 5 - 3 = 2*/ + ret.green = color.green >> 3; /* 6 - 3 = 3*/ + ret.blue = color.blue >> 3; /* 5 - 2 = 3*/ + return ret.full; +# else + lv_color8_t ret; + ret.red = color.red >> 2; /* 5 - 3 = 2*/ + ret.green = color.green_h; /* 6 - 3 = 3*/ + ret.blue = color.blue >> 3; /* 5 - 2 = 3*/ + return ret.full; +# endif +#elif LV_COLOR_DEPTH == 32 + lv_color8_t ret; + ret.red = color.red >> 5; /* 8 - 3 = 5*/ + ret.green = color.green >> 5; /* 8 - 3 = 5*/ + ret.blue = color.blue >> 6; /* 8 - 2 = 6*/ + return ret.full; +#endif +} + +static inline uint16_t lv_color_to16(lv_color_t color) +{ +#if LV_COLOR_DEPTH == 1 + if(color.full == 0) return 0; + else return 0xFFFF; +#elif LV_COLOR_DEPTH == 8 + lv_color16_t ret; +# if LV_COLOR_16_SWAP == 0 + ret.red = color.red * 4; /*(2^5 - 1)/(2^3 - 1) = 31/7 = 4*/ + ret.green = color.green * 9; /*(2^6 - 1)/(2^3 - 1) = 63/7 = 9*/ + ret.blue = color.blue * 10; /*(2^5 - 1)/(2^2 - 1) = 31/3 = 10*/ +# else + ret.red = color.red * 4; + uint8_t g_tmp = color.green * 9; + ret.green_h = (g_tmp & 0x1F) >> 3; + ret.green_l = g_tmp & 0x07; + ret.blue = color.blue * 10; +# endif + return ret.full; +#elif LV_COLOR_DEPTH == 16 + return color.full; +#elif LV_COLOR_DEPTH == 32 + lv_color16_t ret; +# if LV_COLOR_16_SWAP == 0 + ret.red = color.red >> 3; /* 8 - 5 = 3*/ + ret.green = color.green >> 2; /* 8 - 6 = 2*/ + ret.blue = color.blue >> 3; /* 8 - 5 = 3*/ +# else + ret.red = color.red >> 3; + ret.green_h = (color.green & 0xE0) >> 5; + ret.green_l = (color.green & 0x1C) >> 2; + ret.blue = color.blue >> 3; +# endif + return ret.full; +#endif +} + +static inline uint32_t lv_color_to32(lv_color_t color) +{ +#if LV_COLOR_DEPTH == 1 + if(color.full == 0) return 0; + else return 0xFFFFFFFF; +#elif LV_COLOR_DEPTH == 8 + lv_color32_t ret; + ret.red = color.red * 36; /*(2^8 - 1)/(2^3 - 1) = 255/7 = 36*/ + ret.green = color.green * 36; /*(2^8 - 1)/(2^3 - 1) = 255/7 = 36*/ + ret.blue = color.blue * 85; /*(2^8 - 1)/(2^2 - 1) = 255/3 = 85*/ + ret.alpha = 0xFF; + return ret.full; +#elif LV_COLOR_DEPTH == 16 +# if LV_COLOR_16_SWAP == 0 + lv_color32_t ret; + ret.red = color.red * 8; /*(2^8 - 1)/(2^5 - 1) = 255/31 = 8*/ + ret.green = color.green * 4; /*(2^8 - 1)/(2^6 - 1) = 255/63 = 4*/ + ret.blue = color.blue * 8; /*(2^8 - 1)/(2^5 - 1) = 255/31 = 8*/ + ret.alpha = 0xFF; + return ret.full; +# else + lv_color32_t ret; + ret.red = color.red * 8; /*(2^8 - 1)/(2^5 - 1) = 255/31 = 8*/ + ret.green = ((color.green_h << 3) + color.green_l) * 4; /*(2^8 - 1)/(2^6 - 1) = 255/63 = 4*/ + ret.blue = color.blue * 8; /*(2^8 - 1)/(2^5 - 1) = 255/31 = 8*/ + ret.alpha = 0xFF; + return ret.full; +# endif +#elif LV_COLOR_DEPTH == 32 + return color.full; +#endif +} + +static inline lv_color_t lv_color_mix(const lv_color_t c1, const lv_color_t c2, uint8_t mix) +{ + lv_color_t ret; +#if LV_COLOR_DEPTH != 1 && LV_COLOR_DEPTH != 32 + /*LV_COLOR_DEPTH == 8, 16 or 32*/ + ret.red = (uint16_t)((uint16_t) c1.red * mix + (c2.red * (255 - mix))) >> 8; +# if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP + /*If swapped Green is in 2 parts*/ + uint16_t g_1 = (c1.green_h << 3) + c1.green_l; + uint16_t g_2 = (c2.green_h << 3) + c2.green_l; + uint16_t g_out = (uint16_t)((uint16_t) g_1 * mix + (g_2 * (255 - mix))) >> 8; + ret.green_h = g_out >> 3; + ret.green_l = g_out & 0x7; +# else + ret.green = (uint16_t)((uint16_t) c1.green * mix + (c2.green * (255 - mix))) >> 8; +# endif + ret.blue = (uint16_t)((uint16_t) c1.blue * mix + (c2.blue * (255 - mix))) >> 8; +#else +# if LV_COLOR_DEPTH == 32 + uint32_t rb = (((c1.full & 0x00FF00FF) * mix) + ((c2.full & 0x00FF00FF) * (255 - mix))) >> 8; + uint32_t g = (((((c1.full & 0x0000FF00) >> 8) * mix) + (((c2.full & 0x0000FF00) >> 8) * (255 - mix))) >> 8) << 8; + ret.full = 0xFF000000 | (0x00FF00FF & rb) | (0x0000FF00 & g); +# else + /*LV_COLOR_DEPTH == 1*/ + ret.full = mix > LV_OPA_50 ? c1.full : c2.full; +# endif +#endif + + return ret; +} + +/** + * Get the brightness of a color + * @param color a color + * @return the brightness [0..255] + */ +static inline uint8_t lv_color_brightness(lv_color_t color) +{ + lv_color32_t c32; + c32.full = lv_color_to32(color); + uint16_t bright = 3 * c32.red + c32.blue + 4 * c32.green; + return (uint16_t) bright >> 3; +} + +/* The most simple macro to create a color from R,G and B values + * The order of bit field is different on Big-endian and Little-endian machines*/ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#if LV_COLOR_DEPTH == 1 +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){(b8 >> 7 | g8 >> 7 | r8 >> 7)}) +#elif LV_COLOR_DEPTH == 8 +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{b8 >> 6, g8 >> 5, r8 >> 5}}) +#elif LV_COLOR_DEPTH == 16 +# if LV_COLOR_16_SWAP == 0 +# define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{b8 >> 3, g8 >> 2, r8 >> 3}}) +# else +# define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{g8 >> 5, r8 >> 3, b8 >> 3, (g8 >> 2) & 0x7}}) +# endif +#elif LV_COLOR_DEPTH == 32 +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{b8, g8, r8, 0xff}}) /*Fix 0xff alpha*/ +#endif +#else +#if LV_COLOR_DEPTH == 1 +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){(r8 >> 7 | g8 >> 7 | b8 >> 7)}) +#elif LV_COLOR_DEPTH == 8 +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{r8 >> 6, g8 >> 5, b8 >> 5}}) +#elif LV_COLOR_DEPTH == 16 +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{r8 >> 3, g8 >> 2, b8 >> 3}}) +#elif LV_COLOR_DEPTH == 32 +#define LV_COLOR_MAKE(r8, g8, b8) ((lv_color_t){{0xff, r8, g8, b8}}) /*Fix 0xff alpha*/ +#endif +#endif + +#if LV_COLOR_DEPTH == 32 // Concatenate into one 32-bit set. +#define LV_COLOR_HEX(c) ((lv_color_t){.full = (c | 0xFF000000)}) +#else +#define LV_COLOR_HEX(c) LV_COLOR_MAKE(((uint32_t)((uint32_t)c >> 16) & 0xFF), \ + ((uint32_t)((uint32_t)c >> 8) & 0xFF), \ + ((uint32_t) c & 0xFF)) +#endif + +/*Usage LV_COLOR_HEX3(0x16C) which means LV_COLOR_HEX(0x1166CC)*/ +#define LV_COLOR_HEX3(c) LV_COLOR_MAKE((((c >> 4) & 0xF0) | ((c >> 8) & 0xF)), \ + ((uint32_t)(c & 0xF0) | ((c & 0xF0) >> 4)), \ + ((uint32_t)(c & 0xF) | ((c & 0xF) << 4))) + + +/** + * Convert a HSV color to RGB + * @param h hue [0..359] + * @param s saturation [0..100] + * @param v value [0..100] + * @return the given RGB color in RGB (with LV_COLOR_DEPTH depth) + */ +lv_color_t lv_color_hsv_to_rgb(uint16_t hue, uint8_t sat, uint8_t val); + +/** + * Convert an RGB color to HSV + * @param r red + * @param g green + * @param b blue + * @return the given RGB color n HSV + */ +lv_color_hsv_t lv_color_rgb_to_hsv(uint8_t r, uint8_t g, uint8_t b); + + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*USE_COLOR*/ diff --git a/bdk/libs/lvgl/lv_misc/lv_font.c b/bdk/libs/lvgl/lv_misc/lv_font.c new file mode 100644 index 00000000..0aa7fe22 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_font.c @@ -0,0 +1,269 @@ +/** + * @file lv_font.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include +#include "lv_font.h" +#include "lv_log.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the fonts + */ +void lv_font_init(void) +{ + lv_font_builtin_init(); +} + +/** + * Add a font to an other to extend the character set. + * @param child the font to add + * @param parent this font will be extended. Using it later will contain the characters from `child` + */ +void lv_font_add(lv_font_t * child, lv_font_t * parent) +{ + if(parent == NULL) return; + + while(parent->next_page != NULL) { + parent = parent->next_page; /*Got to the last page and add the new font there*/ + } + + parent->next_page = child; + +} + +/** + * Remove a font from a character set. + * @param child the font to remove + * @param parent remove `child` from here + */ +void lv_font_remove(lv_font_t * child, lv_font_t * parent) +{ + if(parent == NULL) return; + if(child == NULL) return; + + while(parent->next_page != child) { + parent = parent->next_page; /*Got to the last page and add the new font there*/ + } + + parent->next_page = child->next_page; +} + + +/** + * Tells if font which contains `letter` is monospace or not + * @param font_p point to font + * @param letter an UNICODE character code + * @return true: the letter is monospace; false not monospace + */ +bool lv_font_is_monospace(const lv_font_t * font_p, uint32_t letter) +{ + const lv_font_t * font_i = font_p; + int16_t w; + while(font_i != NULL) { + w = font_i->get_width(font_i, letter); + if(w >= 0) { + /*Glyph found*/ + if(font_i->monospace) return true; + return false; + } + + font_i = font_i->next_page; + } + + return 0; +} + +/** + * Return with the bitmap of a font. + * @param font_p pointer to a font + * @param letter an UNICODE character code + * @return pointer to the bitmap of the letter + */ +const uint8_t * lv_font_get_bitmap(const lv_font_t * font_p, uint32_t letter) +{ + const lv_font_t * font_i = font_p; + while(font_i != NULL) { + const uint8_t * bitmap = font_i->get_bitmap(font_i, letter); + if(bitmap) return bitmap; + + font_i = font_i->next_page; + } + + return NULL; +} + +/** + * Get the width of a letter in a font. If `monospace` is set then return with it. + * @param font_p pointer to a font + * @param letter an UNICODE character code + * @return the width of a letter + */ +uint8_t lv_font_get_width(const lv_font_t * font_p, uint32_t letter) +{ + const lv_font_t * font_i = font_p; + int16_t w; + while(font_i != NULL) { + w = font_i->get_width(font_i, letter); + if(w >= 0) { + /*Glyph found*/ + uint8_t m = font_i->monospace; + if(m) w = m; + return w; + } + + font_i = font_i->next_page; + } + + return 0; + +} + +/** + * Get the width of the letter without overwriting it with the `monospace` attribute + * @param font_p pointer to a font + * @param letter an UNICODE character code + * @return the width of a letter + */ +uint8_t lv_font_get_real_width(const lv_font_t * font_p, uint32_t letter) +{ + const lv_font_t * font_i = font_p; + int16_t w; + while(font_i != NULL) { + w = font_i->get_width(font_i, letter); + if(w >= 0) return w; + + font_i = font_i->next_page; + } + + return 0; +} + +/** + * Get the bit-per-pixel of font + * @param font pointer to font + * @param letter a letter from font (font extensions can have different bpp) + * @return bpp of the font (or font extension) + */ +uint8_t lv_font_get_bpp(const lv_font_t * font, uint32_t letter) +{ + const lv_font_t * font_i = font; + while(font_i != NULL) { + if(letter >= font_i->unicode_first && letter <= font_i->unicode_last) { + return font_i->bpp; + } + font_i = font_i->next_page; + } + + return 0; + +} + +/** + * Generic bitmap get function used in 'font->get_bitmap' when the font contains all characters in the range + * @param font pointer to font + * @param unicode_letter an unicode letter which bitmap should be get + * @return pointer to the bitmap or NULL if not found + */ +const uint8_t * lv_font_get_bitmap_continuous(const lv_font_t * font, uint32_t unicode_letter) +{ + /*Check the range*/ + if(unicode_letter < font->unicode_first || unicode_letter > font->unicode_last) return NULL; + + uint32_t index = (unicode_letter - font->unicode_first); + return &font->glyph_bitmap[font->glyph_dsc[index].glyph_index]; +} + +/** + * Generic bitmap get function used in 'font->get_bitmap' when the font NOT contains all characters in the range (sparse) + * @param font pointer to font + * @param unicode_letter an unicode letter which bitmap should be get + * @return pointer to the bitmap or NULL if not found + */ +const uint8_t * lv_font_get_bitmap_sparse(const lv_font_t * font, uint32_t unicode_letter) +{ + /*Check the range*/ + if(unicode_letter < font->unicode_first || unicode_letter > font->unicode_last) return NULL; + + uint32_t i; + for(i = 0; font->unicode_list[i] != 0; i++) { + if(font->unicode_list[i] == unicode_letter) { + return &font->glyph_bitmap[font->glyph_dsc[i].glyph_index]; + } + } + + return NULL; +} + +/** + * Generic glyph width get function used in 'font->get_width' when the font contains all characters in the range + * @param font pointer to font + * @param unicode_letter an unicode letter which width should be get + * @return width of the gylph or -1 if not found + */ +int16_t lv_font_get_width_continuous(const lv_font_t * font, uint32_t unicode_letter) +{ + /*Check the range*/ + if(unicode_letter < font->unicode_first || unicode_letter > font->unicode_last) { + return -1; + } + + uint32_t index = (unicode_letter - font->unicode_first); + return font->glyph_dsc[index].w_px; +} + +/** + * Generic glyph width get function used in 'font->get_bitmap' when the font NOT contains all characters in the range (sparse) + * @param font pointer to font + * @param unicode_letter an unicode letter which width should be get + * @return width of the glyph or -1 if not found + */ +int16_t lv_font_get_width_sparse(const lv_font_t * font, uint32_t unicode_letter) +{ + /*Check the range*/ + if(unicode_letter < font->unicode_first || unicode_letter > font->unicode_last) return -1; + + uint32_t i; + for(i = 0; font->unicode_list[i] != 0; i++) { + if(font->unicode_list[i] == unicode_letter) { + return font->glyph_dsc[i].w_px; + } + } + + return -1; +} + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/bdk/libs/lvgl/lv_misc/lv_font.h b/bdk/libs/lvgl/lv_misc/lv_font.h new file mode 100644 index 00000000..7a4bfbae --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_font.h @@ -0,0 +1,192 @@ +/** + * @file lv_font.h + * + */ + +#ifndef LV_FONT_H +#define LV_FONT_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#include +#include + +#include "lv_symbol_def.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct +{ + uint32_t w_px :8; + uint32_t glyph_index :24; +} lv_font_glyph_dsc_t; + +typedef struct +{ + uint32_t unicode :21; + uint32_t glyph_dsc_index :11; +} lv_font_unicode_map_t; + +typedef struct _lv_font_struct +{ + uint32_t unicode_first; + uint32_t unicode_last; + const uint8_t * glyph_bitmap; + const lv_font_glyph_dsc_t * glyph_dsc; + const uint32_t * unicode_list; + const uint8_t * (*get_bitmap)(const struct _lv_font_struct *,uint32_t); /*Get a glyph's bitmap from a font*/ + int16_t (*get_width)(const struct _lv_font_struct *,uint32_t); /*Get a glyph's with with a given font*/ + struct _lv_font_struct * next_page; /*Pointer to a font extension*/ + uint32_t h_px :8; + uint32_t bpp :4; /*Bit per pixel: 1, 2 or 4*/ + uint32_t monospace :8; /*Fix width (0: normal width)*/ + uint16_t glyph_cnt; /*Number of glyphs (letters) in the font*/ +} lv_font_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the fonts + */ +void lv_font_init(void); + +/** + * Add a font to an other to extend the character set. + * @param child the font to add + * @param parent this font will be extended. Using it later will contain the characters from `child` + */ +void lv_font_add(lv_font_t *child, lv_font_t *parent); + +/** + * Remove a font from a character set. + * @param child the font to remove + * @param parent remove `child` from here + */ +void lv_font_remove(lv_font_t * child, lv_font_t * parent); + +/** + * Tells if font which contains `letter` is monospace or not + * @param font_p point to font + * @param letter an UNICODE character code + * @return true: the letter is monospace; false not monospace + */ +bool lv_font_is_monospace(const lv_font_t * font_p, uint32_t letter); + +/** + * Return with the bitmap of a font. + * @param font_p pointer to a font + * @param letter an UNICODE character code + * @return pointer to the bitmap of the letter + */ +const uint8_t * lv_font_get_bitmap(const lv_font_t * font_p, uint32_t letter); + +/** + * Get the width of a letter in a font. If `monospace` is set then return with it. + * @param font_p pointer to a font + * @param letter an UNICODE character code + * @return the width of a letter + */ +uint8_t lv_font_get_width(const lv_font_t * font_p, uint32_t letter); + + +/** + * Get the width of the letter without overwriting it with the `monospace` attribute + * @param font_p pointer to a font + * @param letter an UNICODE character code + * @return the width of a letter + */ +uint8_t lv_font_get_real_width(const lv_font_t * font_p, uint32_t letter); + +/** + * Get the height of a font + * @param font_p pointer to a font + * @return the height of a font + */ +static inline uint8_t lv_font_get_height(const lv_font_t * font_p) +{ + return font_p->h_px; +} + +/** + * Get the bit-per-pixel of font + * @param font pointer to font + * @param letter a letter from font (font extensions can have different bpp) + * @return bpp of the font (or font extension) + */ +uint8_t lv_font_get_bpp(const lv_font_t * font, uint32_t letter); + +/** + * Generic bitmap get function used in 'font->get_bitmap' when the font contains all characters in the range + * @param font pointer to font + * @param unicode_letter an unicode letter which bitmap should be get + * @return pointer to the bitmap or NULL if not found + */ +const uint8_t * lv_font_get_bitmap_continuous(const lv_font_t * font, uint32_t unicode_letter); + +/** + * Generic bitmap get function used in 'font->get_bitmap' when the font NOT contains all characters in the range (sparse) + * @param font pointer to font + * @param unicode_letter an unicode letter which bitmap should be get + * @return pointer to the bitmap or NULL if not found + */ +const uint8_t * lv_font_get_bitmap_sparse(const lv_font_t * font, uint32_t unicode_letter); +/** + * Generic glyph width get function used in 'font->get_width' when the font contains all characters in the range + * @param font pointer to font + * @param unicode_letter an unicode letter which width should be get + * @return width of the gylph or -1 if not found + */ +int16_t lv_font_get_width_continuous(const lv_font_t * font, uint32_t unicode_letter); + +/** + * Generic glyph width get function used in 'font->get_bitmap' when the font NOT contains all characters in the range (sparse) + * @param font pointer to font + * @param unicode_letter an unicode letter which width should be get + * @return width of the glyph or -1 if not found + */ +int16_t lv_font_get_width_sparse(const lv_font_t * font, uint32_t unicode_letter); + +/********************** + * MACROS + **********************/ + +#define LV_FONT_DECLARE(font_name) extern lv_font_t font_name; + + +/********************** + * ADD BUILT IN FONTS + **********************/ + +#include "../lv_fonts/lv_font_builtin.h" + +/*Declare the custom (user defined) fonts*/ +#ifdef LV_FONT_CUSTOM_DECLARE +LV_FONT_CUSTOM_DECLARE +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*USE_FONT*/ + diff --git a/bdk/libs/lvgl/lv_misc/lv_fs.c b/bdk/libs/lvgl/lv_misc/lv_fs.c new file mode 100644 index 00000000..1828e39f --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_fs.c @@ -0,0 +1,627 @@ +/** + * @file lv_fs.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_fs.h" +#if USE_LV_FILESYSTEM + +#include "lv_ll.h" +#include +#include "lv_gc.h" + +#if defined(LV_GC_INCLUDE) +# include LV_GC_INCLUDE +#endif /* LV_ENABLE_GC */ + +/********************* + * DEFINES + *********************/ + +/* "free" is used as a function pointer (in lv_fs_drv_t). + * We must make sure "free" was not defined to a platform specific + * free function, otherwise compilation would fail. + */ +#ifdef free +#undef free +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static const char * lv_fs_get_real_path(const char * path); +static lv_fs_drv_t * lv_fs_get_drv(char letter); + + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize the File system interface + */ +void lv_fs_init(void) +{ + lv_ll_init(&LV_GC_ROOT(_lv_drv_ll), sizeof(lv_fs_drv_t)); +} + +/** + * Test if a drive is rady or not. If the `ready` function was not initialized `true` will be returned. + * @param letter letter of the drive + * @return true: drive is ready; false: drive is not ready + */ +bool lv_fs_is_ready(char letter) +{ + lv_fs_drv_t * drv = lv_fs_get_drv(letter); + + if(drv == NULL) return false; /*An unknown driver in not ready*/ + + if(drv->ready == NULL) return true; /*Assume the driver is always ready if no handler provided*/ + + return drv->ready(); +} + +/** + * Open a file + * @param file_p pointer to a lv_fs_file_t variable + * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt) + * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_open(lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode) +{ + file_p->drv = NULL; + file_p->file_d = NULL; + + if(path == NULL) return LV_FS_RES_INV_PARAM; + + char letter = path[0]; + + file_p->drv = lv_fs_get_drv(letter); + + if(file_p->drv == NULL) { + file_p->file_d = NULL; + return LV_FS_RES_NOT_EX; + } + + if(file_p->drv->ready != NULL) { + if(file_p->drv->ready() == false) { + file_p->drv = NULL; + file_p->file_d = NULL; + return LV_FS_RES_HW_ERR; + } + } + + file_p->file_d = lv_mem_alloc(file_p->drv->file_size); + lv_mem_assert(file_p->file_d); + if(file_p->file_d == NULL) { + file_p->drv = NULL; + return LV_FS_RES_OUT_OF_MEM; /* Out of memory */ + } + + if(file_p->drv->open == NULL) { + return LV_FS_RES_NOT_IMP; + } + + const char * real_path = lv_fs_get_real_path(path); + lv_fs_res_t res = file_p->drv->open(file_p->file_d, real_path, mode); + + if(res != LV_FS_RES_OK) { + lv_mem_free(file_p->file_d); + file_p->file_d = NULL; + file_p->drv = NULL; + } + + return res; +} + +/** + * Close an already opened file + * @param file_p pointer to a lv_fs_file_t variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_close(lv_fs_file_t * file_p) +{ + if(file_p->drv == NULL) { + return LV_FS_RES_INV_PARAM; + } + + if(file_p->drv->close == NULL) { + return LV_FS_RES_NOT_IMP; + } + + lv_fs_res_t res = file_p->drv->close(file_p->file_d); + + lv_mem_free(file_p->file_d); /*Clean up*/ + file_p->file_d = NULL; + file_p->drv = NULL; + file_p->file_d = NULL; + + return res; +} + +/** + * Delete a file + * @param path path of the file to delete + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_remove(const char * path) +{ + if(path == NULL) return LV_FS_RES_INV_PARAM; + lv_fs_drv_t * drv = NULL; + + char letter = path[0]; + + drv = lv_fs_get_drv(letter); + if(drv == NULL) return LV_FS_RES_NOT_EX; + if(drv->ready != NULL) { + if(drv->ready() == false) return LV_FS_RES_HW_ERR; + } + + if(drv->remove == NULL) return LV_FS_RES_NOT_IMP; + + const char * real_path = lv_fs_get_real_path(path); + lv_fs_res_t res = drv->remove(real_path); + + return res; +} + +/** + * Read from a file + * @param file_p pointer to a lv_fs_file_t variable + * @param buf pointer to a buffer where the read bytes are stored + * @param btr Bytes To Read + * @param br the number of real read bytes (Bytes Read). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_read(lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + if(br != NULL) *br = 0; + if(file_p->drv == NULL) return LV_FS_RES_INV_PARAM; + if(file_p->drv->read == NULL) return LV_FS_RES_NOT_IMP; + + uint32_t br_tmp = 0; + lv_fs_res_t res = file_p->drv->read(file_p->file_d, buf, btr, &br_tmp); + if(br != NULL) *br = br_tmp; + + return res; +} + +/** + * Write into a file + * @param file_p pointer to a lv_fs_file_t variable + * @param buf pointer to a buffer with the bytes to write + * @param btr Bytes To Write + * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_write(lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + if(bw != NULL) *bw = 0; + + if(file_p->drv == NULL) { + return LV_FS_RES_INV_PARAM; + } + + if(file_p->drv->write == NULL) { + return LV_FS_RES_NOT_IMP; + } + + uint32_t bw_tmp = 0; + lv_fs_res_t res = file_p->drv->write(file_p->file_d, buf, btw, &bw_tmp); + if(bw != NULL) *bw = bw_tmp; + + return res; +} + +/** + * Set the position of the 'cursor' (read write pointer) in a file + * @param file_p pointer to a lv_fs_file_t variable + * @param pos the new position expressed in bytes index (0: start of file) + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_seek(lv_fs_file_t * file_p, uint32_t pos) +{ + if(file_p->drv == NULL) { + return LV_FS_RES_INV_PARAM; + } + + if(file_p->drv->seek == NULL) { + return LV_FS_RES_NOT_IMP; + } + + lv_fs_res_t res = file_p->drv->seek(file_p->file_d, pos); + + return res; +} + +/** + * Give the position of the read write pointer + * @param file_p pointer to a lv_fs_file_t variable + * @param pos_p pointer to store the position of the read write pointer + * @return LV_FS_RES_OK or any error from 'fs_res_t' + */ +lv_fs_res_t lv_fs_tell(lv_fs_file_t * file_p, uint32_t * pos) +{ + if(file_p->drv == NULL) { + pos = 0; + return LV_FS_RES_INV_PARAM; + } + + if(file_p->drv->tell == NULL) { + pos = 0; + return LV_FS_RES_NOT_IMP; + } + + lv_fs_res_t res = file_p->drv->tell(file_p->file_d, pos); + + return res; +} + +/** + * Truncate the file size to the current position of the read write pointer + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_fs_open ) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_trunc(lv_fs_file_t * file_p) +{ + if(file_p->drv == NULL) { + return LV_FS_RES_INV_PARAM; + } + + if(file_p->drv->tell == NULL) { + return LV_FS_RES_NOT_IMP; + } + + lv_fs_res_t res = file_p->drv->trunc(file_p->file_d); + + return res; +} +/** + * Give the size of a file bytes + * @param file_p pointer to a lv_fs_file_t variable + * @param size pointer to a variable to store the size + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_size(lv_fs_file_t * file_p, uint32_t * size) +{ + if(file_p->drv == NULL) { + return LV_FS_RES_INV_PARAM; + } + + if(file_p->drv->size == NULL) return LV_FS_RES_NOT_IMP; + + + if(size == NULL) return LV_FS_RES_INV_PARAM; + + lv_fs_res_t res = file_p->drv->size(file_p->file_d, size); + + return res; +} + +/** + * Rename a file + * @param oldname path to the file + * @param newname path with the new name + * @return LV_FS_RES_OK or any error from 'fs_res_t' + */ +lv_fs_res_t lv_fs_rename(const char * oldname, const char * newname) +{ + if(!oldname || !newname) return LV_FS_RES_INV_PARAM; + + char letter = oldname[0]; + + lv_fs_drv_t * drv = lv_fs_get_drv(letter); + + if(!drv) { + return LV_FS_RES_NOT_EX; + } + + if(drv->ready != NULL) { + if(drv->ready() == false) { + return LV_FS_RES_HW_ERR; + } + } + + if(drv->rename == NULL) return LV_FS_RES_NOT_IMP; + + const char * old_real = lv_fs_get_real_path(oldname); + const char * new_real = lv_fs_get_real_path(newname); + lv_fs_res_t res = drv->rename(old_real, new_real); + + return res; +} + + +/** + * Initialize a 'fs_read_dir_t' variable for directory reading + * @param rddir_p pointer to a 'fs_read_dir_t' variable + * @param path path to a directory + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path) +{ + if(path == NULL) return LV_FS_RES_INV_PARAM; + + char letter = path[0]; + + rddir_p->drv = lv_fs_get_drv(letter); + + if(rddir_p->drv == NULL) { + rddir_p->dir_d = NULL; + return LV_FS_RES_NOT_EX; + } + + rddir_p->dir_d = lv_mem_alloc(rddir_p->drv->rddir_size); + lv_mem_assert(rddir_p->dir_d); + if(rddir_p->dir_d == NULL) { + rddir_p->dir_d = NULL; + return LV_FS_RES_OUT_OF_MEM; /* Out of memory */ + } + + if(rddir_p->drv->dir_open == NULL) { + return LV_FS_RES_NOT_IMP; + } + + const char * real_path = lv_fs_get_real_path(path); + lv_fs_res_t res = rddir_p->drv->dir_open(rddir_p->dir_d, real_path); + + return res; +} + +/** + * Read the next filename form a directory. + * The name of the directories will begin with '/' + * @param rddir_p pointer to an initialized 'fs_read_dir_t' variable + * @param fn pointer to a buffer to store the filename + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_dir_read(lv_fs_dir_t * rddir_p, char * fn) +{ + if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) { + fn[0] = '\0'; + return LV_FS_RES_INV_PARAM; + } + + if(rddir_p->drv->dir_read == NULL) { + return LV_FS_RES_NOT_IMP; + } + + lv_fs_res_t res = rddir_p->drv->dir_read(rddir_p->dir_d, fn); + + return res; +} + +/** + * Close the directory reading + * @param rddir_p pointer to an initialized 'fs_read_dir_t' variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_dir_close(lv_fs_dir_t * rddir_p) +{ + if(rddir_p->drv == NULL || rddir_p->dir_d == NULL) { + return LV_FS_RES_INV_PARAM; + } + + lv_fs_res_t res; + + if(rddir_p->drv->dir_close == NULL) { + res = LV_FS_RES_NOT_IMP; + } else { + res = rddir_p->drv->dir_close(rddir_p->dir_d); + } + + lv_mem_free(rddir_p->dir_d); /*Clean up*/ + rddir_p->dir_d = NULL; + rddir_p->drv = NULL; + rddir_p->dir_d = NULL; + + return res; +} + +/** + * Get the free and total size of a driver in kB + * @param letter the driver letter + * @param total_p pointer to store the total size [kB] + * @param free_p pointer to store the free size [kB] + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_free(char letter, uint32_t * total_p, uint32_t * free_p) +{ + lv_fs_drv_t * drv = lv_fs_get_drv(letter); + + if(drv == NULL) { + return LV_FS_RES_INV_PARAM; + } + + lv_fs_res_t res; + + if(drv->free == NULL) { + res = LV_FS_RES_NOT_IMP; + } else { + uint32_t total_tmp = 0; + uint32_t free_tmp = 0; + res = drv->free(&total_tmp, &free_tmp); + + if(total_p != NULL) *total_p = total_tmp; + if(free_p != NULL) *free_p = free_tmp; + } + + return res; +} + +/** + * Add a new drive + * @param drv_p pointer to an lv_fs_drv_t structure which is inited with the + * corresponding function pointers. The data will be copied so the variable can be local. + */ +void lv_fs_add_drv(lv_fs_drv_t * drv_p) +{ + /*Save the new driver*/ + lv_fs_drv_t * new_drv; + new_drv = lv_ll_ins_head(&LV_GC_ROOT(_lv_drv_ll)); + lv_mem_assert(new_drv); + if(new_drv == NULL) return; + + memcpy(new_drv, drv_p, sizeof(lv_fs_drv_t)); +} + +/** + * Fill a buffer with the letters of existing drivers + * @param buf buffer to store the letters ('\0' added after the last letter) + * @return the buffer + */ +char * lv_fs_get_letters(char * buf) +{ + lv_fs_drv_t * drv; + uint8_t i = 0; + + LL_READ(LV_GC_ROOT(_lv_drv_ll), drv) { + buf[i] = drv->letter; + i++; + } + + buf[i] = '\0'; + + return buf; +} + + +/** + * Return with the extension of the filename + * @param fn string with a filename + * @return pointer to the beginning extension or empty string if no extension + */ +const char * lv_fs_get_ext(const char * fn) +{ + uint16_t i; + for(i = strlen(fn); i > 0; i --) { + if(fn[i] == '.') { + return &fn[i + 1]; + } else if(fn[i] == '/' || fn[i] == '\\') { + return ""; /*No extension if a '\' or '/' found*/ + } + } + + return ""; /*Empty string if no '.' in the file name. */ +} + +/** + * Step up one level + * @param path pointer to a file name + * @return the truncated file name + */ +char * lv_fs_up(char * path) +{ + uint16_t len = strlen(path); + if(len == 0) return path; + + len --; /*Go before the trailing '\0'*/ + + /*Ignore trailing '/' or '\'*/ + while(path[len] == '/' || path[len] == '\\') { + path[len] = '\0'; + if(len > 0) len --; + else return path; + } + + uint16_t i; + for(i = len; i > 0; i --) { + if(path[i] == '/' || path[i] == '\\') break; + } + + if(i > 0) path[i] = '\0'; + + return path; +} + +/** + * Get the last element of a path (e.g. U:/folder/file -> file) + * @param path a character sting with the path to search in + * @return pointer to the beginning of the last element in the path + */ +const char * lv_fs_get_last(const char * path) +{ + uint16_t len = strlen(path); + if(len == 0) return path; + + len --; /*Go before the trailing '\0'*/ + + /*Ignore trailing '/' or '\'*/ + while(path[len] == '/' || path[len] == '\\') { + if(len > 0) len --; + else return path; + } + + uint16_t i; + for(i = len; i > 0; i --) { + if(path[i] == '/' || path[i] == '\\') break; + } + + /*No '/' or '\' in the path so return with path itself*/ + if(i == 0) return path; + + return &path[i + 1]; +} +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Leave the driver letters and / or \ letters from beginning of the path + * @param path path string (E.g. S:/folder/file.txt) + * @return pointer to the beginning of the real path (E.g. folder/file.txt) + */ +static const char * lv_fs_get_real_path(const char * path) +{ + /* Example path: "S:/folder/file.txt" + * Leave the letter and the : / \ characters*/ + + path ++; /*Ignore the driver letter*/ + + while(*path != '\0') { + if(*path == ':' || *path == '\\' || *path == '/') { + path ++; + } else { + break; + } + } + + return path; +} + +/** + * Give a pointer to a driver from its letter + * @param letter the driver letter + * @return pointer to a driver or NULL if not found + */ +static lv_fs_drv_t * lv_fs_get_drv(char letter) +{ + lv_fs_drv_t * drv; + + LL_READ(LV_GC_ROOT(_lv_drv_ll), drv) { + if(drv->letter == letter) { + return drv; + } + } + + return NULL; +} + +#endif /*USE_LV_FILESYSTEM*/ diff --git a/bdk/libs/lvgl/lv_misc/lv_fs.h b/bdk/libs/lvgl/lv_misc/lv_fs.h new file mode 100644 index 00000000..1eb2bfa6 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_fs.h @@ -0,0 +1,276 @@ +/** + * @file lv_fs.h + * + */ + +#ifndef LV_FS_H +#define LV_FS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_FILESYSTEM + +#include +#include "lv_mem.h" + +/********************* + * DEFINES + *********************/ +#define LV_FS_MAX_FN_LENGTH 64 + +/********************** + * TYPEDEFS + **********************/ +enum +{ + LV_FS_RES_OK = 0, + LV_FS_RES_HW_ERR, /*Low level hardware error*/ + LV_FS_RES_FS_ERR, /*Error in the file system structure */ + LV_FS_RES_NOT_EX, /*Driver, file or directory is not exists*/ + LV_FS_RES_FULL, /*Disk full*/ + LV_FS_RES_LOCKED, /*The file is already opened*/ + LV_FS_RES_DENIED, /*Access denied. Check 'fs_open' modes and write protect*/ + LV_FS_RES_BUSY, /*The file system now can't handle it, try later*/ + LV_FS_RES_TOUT, /*Process time outed*/ + LV_FS_RES_NOT_IMP, /*Requested function is not implemented*/ + LV_FS_RES_OUT_OF_MEM, /*Not enough memory for an internal operation*/ + LV_FS_RES_INV_PARAM, /*Invalid parameter among arguments*/ + LV_FS_RES_UNKNOWN, /*Other unknown error*/ +}; +typedef uint8_t lv_fs_res_t; + +struct __lv_fs_drv_t; + +typedef struct +{ + void * file_d; + struct __lv_fs_drv_t* drv; +} lv_fs_file_t; + + +typedef struct +{ + void * dir_d; + struct __lv_fs_drv_t * drv; +} lv_fs_dir_t; + +enum +{ + LV_FS_MODE_WR = 0x01, + LV_FS_MODE_RD = 0x02, +}; +typedef uint8_t lv_fs_mode_t; + +typedef struct __lv_fs_drv_t +{ + char letter; + uint16_t file_size; + uint16_t rddir_size; + bool (*ready) (void); + + lv_fs_res_t (*open) (void * file_p, const char * path, lv_fs_mode_t mode); + lv_fs_res_t (*close) (void * file_p); + lv_fs_res_t (*remove) (const char * fn); + lv_fs_res_t (*read) (void * file_p, void * buf, uint32_t btr, uint32_t * br); + lv_fs_res_t (*write) (void * file_p, const void * buf, uint32_t btw, uint32_t * bw); + lv_fs_res_t (*seek) (void * file_p, uint32_t pos); + lv_fs_res_t (*tell) (void * file_p, uint32_t * pos_p); + lv_fs_res_t (*trunc) (void * file_p); + lv_fs_res_t (*size) (void * file_p, uint32_t * size_p); + lv_fs_res_t (*rename) (const char * oldname, const char * newname); + lv_fs_res_t (*free) (uint32_t * total_p, uint32_t * free_p); + + lv_fs_res_t (*dir_open) (void * rddir_p, const char * path); + lv_fs_res_t (*dir_read) (void * rddir_p, char * fn); + lv_fs_res_t (*dir_close) (void * rddir_p); +} lv_fs_drv_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize the File system interface + */ +void lv_fs_init(void); + +/** + * Add a new drive + * @param drv_p pointer to an lv_fs_drv_t structure which is inited with the + * corresponding function pointers. The data will be copied so the variable can be local. + */ +void lv_fs_add_drv(lv_fs_drv_t * drv_p); + +/** + * Test if a drive is rady or not. If the `ready` function was not initialized `true` will be returned. + * @param letter letter of the drive + * @return true: drive is ready; false: drive is not ready + */ +bool lv_fs_is_ready(char letter); + +/** + * Open a file + * @param file_p pointer to a lv_fs_file_t variable + * @param path path to the file beginning with the driver letter (e.g. S:/folder/file.txt) + * @param mode read: FS_MODE_RD, write: FS_MODE_WR, both: FS_MODE_RD | FS_MODE_WR + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_open (lv_fs_file_t * file_p, const char * path, lv_fs_mode_t mode); + +/** + * Close an already opened file + * @param file_p pointer to a lv_fs_file_t variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_close (lv_fs_file_t * file_p); + +/** + * Delete a file + * @param path path of the file to delete + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_remove (const char * path); + +/** + * Read from a file + * @param file_p pointer to a lv_fs_file_t variable + * @param buf pointer to a buffer where the read bytes are stored + * @param btr Bytes To Read + * @param br the number of real read bytes (Bytes Read). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_read (lv_fs_file_t * file_p, void * buf, uint32_t btr, uint32_t * br); + +/** + * Write into a file + * @param file_p pointer to a lv_fs_file_t variable + * @param buf pointer to a buffer with the bytes to write + * @param btr Bytes To Write + * @param br the number of real written bytes (Bytes Written). NULL if unused. + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_write (lv_fs_file_t * file_p, const void * buf, uint32_t btw, uint32_t * bw); + +/** + * Set the position of the 'cursor' (read write pointer) in a file + * @param file_p pointer to a lv_fs_file_t variable + * @param pos the new position expressed in bytes index (0: start of file) + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_seek (lv_fs_file_t * file_p, uint32_t pos); + +/** + * Give the position of the read write pointer + * @param file_p pointer to a lv_fs_file_t variable + * @param pos_p pointer to store the position of the read write pointer + * @return LV_FS_RES_OK or any error from 'fs_res_t' + */ +lv_fs_res_t lv_fs_tell (lv_fs_file_t * file_p, uint32_t * pos); + +/** + * Truncate the file size to the current position of the read write pointer + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_fs_open ) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_trunc (lv_fs_file_t * file_p); + +/** + * Give the size of a file bytes + * @param file_p pointer to a lv_fs_file_t variable + * @param size pointer to a variable to store the size + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_size (lv_fs_file_t * file_p, uint32_t * size); + +/** + * Rename a file + * @param oldname path to the file + * @param newname path with the new name + * @return LV_FS_RES_OK or any error from 'fs_res_t' + */ +lv_fs_res_t lv_fs_rename (const char * oldname, const char * newname); + +/** + * Initialize a 'fs_dir_t' variable for directory reading + * @param rddir_p pointer to a 'fs_read_dir_t' variable + * @param path path to a directory + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_dir_open(lv_fs_dir_t * rddir_p, const char * path); + +/** + * Read the next filename form a directory. + * The name of the directories will begin with '/' + * @param rddir_p pointer to an initialized 'fs_rdir_t' variable + * @param fn pointer to a buffer to store the filename + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_dir_read (lv_fs_dir_t * rddir_p, char * fn); + +/** + * Close the directory reading + * @param rddir_p pointer to an initialized 'fs_dir_t' variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_dir_close (lv_fs_dir_t * rddir_p); + +/** + * Get the free and total size of a driver in kB + * @param letter the driver letter + * @param total_p pointer to store the total size [kB] + * @param free_p pointer to store the free size [kB] + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_fs_free (char letter, uint32_t * total_p, uint32_t * free_p); + +/** + * Fill a buffer with the letters of existing drivers + * @param buf buffer to store the letters ('\0' added after the last letter) + * @return the buffer + */ +char * lv_fs_get_letters(char * buf); + +/** + * Return with the extension of the filename + * @param fn string with a filename + * @return pointer to the beginning extension or empty string if no extension + */ +const char * lv_fs_get_ext(const char * fn); + +/** + * Step up one level + * @param path pointer to a file name + * @return the truncated file name + */ +char * lv_fs_up(char * path); + +/** + * Get the last element of a path (e.g. U:/folder/file -> file) + * @param buf buffer to store the letters ('\0' added after the last letter) + * @return pointer to the beginning of the last element in the path + */ +const char * lv_fs_get_last(const char * path); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_FILESYSTEM*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_FS_H*/ diff --git a/bdk/libs/lvgl/lv_misc/lv_gc.c b/bdk/libs/lvgl/lv_misc/lv_gc.c new file mode 100644 index 00000000..5ec5832f --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_gc.c @@ -0,0 +1,40 @@ +/** + * @file lv_gc.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_gc.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +#if (!defined(LV_ENABLE_GC)) || LV_ENABLE_GC == 0 +LV_ROOTS +#endif /* LV_ENABLE_GC */ +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/bdk/libs/lvgl/lv_misc/lv_gc.h b/bdk/libs/lvgl/lv_misc/lv_gc.h new file mode 100644 index 00000000..660d7f01 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_gc.h @@ -0,0 +1,75 @@ +/** + * @file lv_gc.h + * + */ + +#ifndef LV_GC_H +#define LV_GC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#include +#include "lv_mem.h" +#include "lv_ll.h" + +/********************* + * DEFINES + *********************/ + +#define LV_GC_ROOTS(prefix) \ + prefix lv_ll_t _lv_task_ll; /*Linked list to store the lv_tasks*/ \ + prefix lv_ll_t _lv_scr_ll; /*Linked list of screens*/ \ + prefix lv_ll_t _lv_drv_ll;\ + prefix lv_ll_t _lv_file_ll;\ + prefix lv_ll_t _lv_anim_ll;\ + prefix void * _lv_def_scr;\ + prefix void * _lv_act_scr;\ + prefix void * _lv_top_layer;\ + prefix void * _lv_sys_layer;\ + prefix void * _lv_task_act;\ + prefix void * _lv_indev_list;\ + prefix void * _lv_disp_list;\ + +#define LV_NO_PREFIX +#define LV_ROOTS LV_GC_ROOTS(LV_NO_PREFIX) + +#if LV_ENABLE_GC == 1 +# if LV_MEM_CUSTOM != 1 +# error "GC requires CUSTOM_MEM" +# endif /* LV_MEM_CUSTOM */ +#else /* LV_ENABLE_GC */ +# define LV_GC_ROOT(x) x + LV_GC_ROOTS(extern) +#endif /* LV_ENABLE_GC */ + + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_GC_H*/ diff --git a/bdk/libs/lvgl/lv_misc/lv_ll.c b/bdk/libs/lvgl/lv_misc/lv_ll.c new file mode 100644 index 00000000..43d8847d --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_ll.c @@ -0,0 +1,376 @@ +/** + * @file lv_ll.c + * Handle linked lists. + * The nodes are dynamically allocated by the 'lv_mem' module, + */ + +/********************* + * INCLUDES + *********************/ +#include +#include + +#include "lv_ll.h" +#include "lv_mem.h" + +/********************* + * DEFINES + *********************/ +#define LL_NODE_META_SIZE (sizeof(lv_ll_node_t*) + sizeof(lv_ll_node_t*)) +#define LL_PREV_P_OFFSET(ll_p) (ll_p->n_size) +#define LL_NEXT_P_OFFSET(ll_p) (ll_p->n_size + sizeof(lv_ll_node_t*)) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev); +static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next); + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initialize linked list + * @param ll_dsc pointer to ll_dsc variable + * @param node_size the size of 1 node in bytes + */ +void lv_ll_init(lv_ll_t * ll_p, uint32_t node_size) +{ + ll_p->head = NULL; + ll_p->tail = NULL; +#ifdef LV_MEM_ENV64 + /*Round the size up to 8*/ + if(node_size & 0x7) { + node_size = node_size & (~0x7); + node_size += 8; + } +#else + /*Round the size up to 4*/ + if(node_size & 0x3) { + node_size = node_size & (~0x3); + node_size += 4; + } +#endif + + ll_p->n_size = node_size; +} + +/** + * Add a new head to a linked list + * @param ll_p pointer to linked list + * @return pointer to the new head + */ +void * lv_ll_ins_head(lv_ll_t * ll_p) +{ + lv_ll_node_t * n_new; + + n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE); + + if(n_new != NULL) { + node_set_prev(ll_p, n_new, NULL); /*No prev. before the new head*/ + node_set_next(ll_p, n_new, ll_p->head); /*After new comes the old head*/ + + if(ll_p->head != NULL) { /*If there is old head then before it goes the new*/ + node_set_prev(ll_p, ll_p->head, n_new); + } + + ll_p->head = n_new; /*Set the new head in the dsc.*/ + if(ll_p->tail == NULL) {/*If there is no tail (1. node) set the tail too*/ + ll_p->tail = n_new; + } + } + + return n_new; +} + +/** + * Insert a new node in front of the n_act node + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the new head + */ +void * lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act) +{ + lv_ll_node_t * n_new; + lv_ll_node_t * n_prev; + + if(NULL == ll_p || NULL == n_act) return NULL; + + if(lv_ll_get_head(ll_p) == n_act) { + n_new = lv_ll_ins_head(ll_p); + if(n_new == NULL) return NULL; + } else { + n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE); + if(n_new == NULL) return NULL; + + n_prev = lv_ll_get_prev(ll_p, n_act); + node_set_next(ll_p, n_prev, n_new); + node_set_prev(ll_p, n_new, n_prev); + node_set_prev(ll_p, n_act, n_new); + node_set_next(ll_p, n_new, n_act); + } + + return n_new; +} + +/** + * Add a new tail to a linked list + * @param ll_p pointer to linked list + * @return pointer to the new tail + */ +void * lv_ll_ins_tail(lv_ll_t * ll_p) +{ + lv_ll_node_t * n_new; + + n_new = lv_mem_alloc(ll_p->n_size + LL_NODE_META_SIZE); + if(n_new == NULL) return NULL; + + if(n_new != NULL) { + node_set_next(ll_p, n_new, NULL); /*No next after the new tail*/ + node_set_prev(ll_p, n_new, ll_p->tail); /*The prev. before new is tho old tail*/ + if(ll_p->tail != NULL) { /*If there is old tail then the new comes after it*/ + node_set_next(ll_p, ll_p->tail, n_new); + } + + ll_p->tail = n_new; /*Set the new tail in the dsc.*/ + if(ll_p->head == NULL) { /*If there is no head (1. node) set the head too*/ + ll_p->head = n_new; + } + } + + return n_new; +} + + +/** + * Remove the node 'node_p' from 'll_p' linked list. + * It does not free the the memory of node. + * @param ll_p pointer to the linked list of 'node_p' + * @param node_p pointer to node in 'll_p' linked list + */ +void lv_ll_rem(lv_ll_t * ll_p, void * node_p) +{ + if(lv_ll_get_head(ll_p) == node_p) { + /*The new head will be the node after 'n_act'*/ + ll_p->head = lv_ll_get_next(ll_p, node_p); + if(ll_p->head == NULL) { + ll_p->tail = NULL; + } else { + node_set_prev(ll_p, ll_p->head, NULL); + } + } else if(lv_ll_get_tail(ll_p) == node_p) { + /*The new tail will be the node before 'n_act'*/ + ll_p->tail = lv_ll_get_prev(ll_p, node_p); + if(ll_p->tail == NULL) { + ll_p->head = NULL; + } else { + node_set_next(ll_p, ll_p->tail, NULL); + } + } else { + lv_ll_node_t * n_prev = lv_ll_get_prev(ll_p, node_p); + lv_ll_node_t * n_next = lv_ll_get_next(ll_p, node_p); + + node_set_next(ll_p, n_prev, n_next); + node_set_prev(ll_p, n_next, n_prev); + } +} + +/** + * Remove and free all elements from a linked list. The list remain valid but become empty. + * @param ll_p pointer to linked list + */ +void lv_ll_clear(lv_ll_t * ll_p) +{ + void * i; + void * i_next; + + i = lv_ll_get_head(ll_p); + i_next = NULL; + + while(i != NULL) { + i_next = lv_ll_get_next(ll_p, i); + + lv_ll_rem(ll_p, i); + lv_mem_free(i); + + i = i_next; + } +} + +/** + * Move a node to a new linked list + * @param ll_ori_p pointer to the original (old) linked list + * @param ll_new_p pointer to the new linked list + * @param node pointer to a node + */ +void lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node) +{ + lv_ll_rem(ll_ori_p, node); + + /*Set node as head*/ + node_set_prev(ll_new_p, node, NULL); + node_set_next(ll_new_p, node, ll_new_p->head); + + if(ll_new_p->head != NULL) { /*If there is old head then before it goes the new*/ + node_set_prev(ll_new_p, ll_new_p->head, node); + } + + ll_new_p->head = node; /*Set the new head in the dsc.*/ + if(ll_new_p->tail == NULL) { /*If there is no tail (first node) set the tail too*/ + ll_new_p->tail = node; + } +} + +/** + * Return with head node of the linked list + * @param ll_p pointer to linked list + * @return pointer to the head of 'll_p' + */ +void * lv_ll_get_head(const lv_ll_t * ll_p) +{ + void * head = NULL; + + if(ll_p != NULL) { + head = ll_p->head; + } + + return head; +} + +/** + * Return with tail node of the linked list + * @param ll_p pointer to linked list + * @return pointer to the head of 'll_p' + */ +void * lv_ll_get_tail(const lv_ll_t * ll_p) +{ + void * tail = NULL; + + if(ll_p != NULL) { + tail = ll_p->tail; + } + + return tail; +} + +/** + * Return with the pointer of the next node after 'n_act' + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the next node + */ +void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act) +{ + void * next = NULL; + + if(ll_p != NULL) { + const lv_ll_node_t * n_act_d = n_act; + memcpy(&next, n_act_d + LL_NEXT_P_OFFSET(ll_p), sizeof(void *)); + } + + return next; +} + +/** + * Return with the pointer of the previous node after 'n_act' + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the previous node + */ +void * lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act) +{ + void * prev = NULL; + + if(ll_p != NULL) { + const lv_ll_node_t * n_act_d = n_act; + memcpy(&prev, n_act_d + LL_PREV_P_OFFSET(ll_p), sizeof(void *)); + } + + return prev; +} + +void lv_ll_swap(lv_ll_t * ll_p, void * n1_p, void * n2_p) +{ + (void)(ll_p); + (void)(n1_p); + (void)(n2_p); + /*TODO*/ +} + +/** + * Move a nodw before an other node in the same linked list + * @param ll_p pointer to a linked list + * @param n_act pointer to node to move + * @param n_after pointer to a node which should be after `n_act` + */ +void lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after) +{ + if(n_act == n_after) return; /*Can't move before itself*/ + + + void * n_before; + if(n_after != NULL) n_before = lv_ll_get_prev(ll_p, n_after); + else n_before = lv_ll_get_tail(ll_p); /*if `n_after` is NULL `n_act` should be the new tail*/ + + if(n_act == n_before) return; /*Already before `n_after`*/ + + /*It's much easier to remove from the list and add again*/ + lv_ll_rem(ll_p, n_act); + + /*Add again by setting the prev. and next nodes*/ + node_set_next(ll_p, n_before, n_act); + node_set_prev(ll_p, n_act, n_before); + node_set_prev(ll_p, n_after, n_act); + node_set_next(ll_p, n_act, n_after); + + /*If `n_act` was moved before NULL then it become the new tail*/ + if(n_after == NULL) ll_p->tail = n_act; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Set the 'pervious node pointer' of a node + * @param ll_p pointer to linked list + * @param act pointer to a node which prev. node pointer should be set + * @param prev pointer to a node which should be the previous node before 'act' + */ +static void node_set_prev(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * prev) +{ + if(act == NULL) return; /*Can't set the prev node of `NULL`*/ + + uint32_t node_p_size = sizeof(lv_ll_node_t *); + if(prev) memcpy(act + LL_PREV_P_OFFSET(ll_p), &prev, node_p_size); + else memset(act + LL_PREV_P_OFFSET(ll_p), 0, node_p_size); +} + +/** + * Set the 'next node pointer' of a node + * @param ll_p pointer to linked list + * @param act pointer to a node which next node pointer should be set + * @param next pointer to a node which should be the next node before 'act' + */ +static void node_set_next(lv_ll_t * ll_p, lv_ll_node_t * act, lv_ll_node_t * next) +{ + if(act == NULL) return; /*Can't set the next node of `NULL`*/ + + uint32_t node_p_size = sizeof(lv_ll_node_t *); + if(next) memcpy(act + LL_NEXT_P_OFFSET(ll_p), &next, node_p_size); + else memset(act + LL_NEXT_P_OFFSET(ll_p), 0, node_p_size); +} + diff --git a/bdk/libs/lvgl/lv_misc/lv_ll.h b/bdk/libs/lvgl/lv_misc/lv_ll.h new file mode 100644 index 00000000..086ba405 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_ll.h @@ -0,0 +1,145 @@ +/** + * @file lv_ll.c + * Handle linked lists. The nodes are dynamically allocated by the 'lv_mem' module. + */ + +#ifndef LV_LL_H +#define LV_LL_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************* + * INCLUDES + *********************/ +#include "lv_mem.h" +#include +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Dummy type to make handling easier*/ +typedef uint8_t lv_ll_node_t; + +/*Description of a linked list*/ +typedef struct +{ + uint32_t n_size; + lv_ll_node_t* head; + lv_ll_node_t* tail; +} lv_ll_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Initialize linked list + * @param ll_dsc pointer to ll_dsc variable + * @param node_size the size of 1 node in bytes + */ +void lv_ll_init(lv_ll_t * ll_p, uint32_t node_size); + +/** + * Add a new head to a linked list + * @param ll_p pointer to linked list + * @return pointer to the new head + */ +void * lv_ll_ins_head(lv_ll_t * ll_p); + +/** + * Insert a new node in front of the n_act node + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the new head + */ +void * lv_ll_ins_prev(lv_ll_t * ll_p, void * n_act); + +/** + * Add a new tail to a linked list + * @param ll_p pointer to linked list + * @return pointer to the new tail + */ +void * lv_ll_ins_tail(lv_ll_t * ll_p); + +/** + * Remove the node 'node_p' from 'll_p' linked list. + * It does not free the the memory of node. + * @param ll_p pointer to the linked list of 'node_p' + * @param node_p pointer to node in 'll_p' linked list + */ +void lv_ll_rem(lv_ll_t * ll_p, void * node_p); + +/** + * Remove and free all elements from a linked list. The list remain valid but become empty. + * @param ll_p pointer to linked list + */ +void lv_ll_clear(lv_ll_t * ll_p); + +/** + * Move a node to a new linked list + * @param ll_ori_p pointer to the original (old) linked list + * @param ll_new_p pointer to the new linked list + * @param node pointer to a node + */ +void lv_ll_chg_list(lv_ll_t * ll_ori_p, lv_ll_t * ll_new_p, void * node); + +/** + * Return with head node of the linked list + * @param ll_p pointer to linked list + * @return pointer to the head of 'll_p' + */ +void * lv_ll_get_head(const lv_ll_t * ll_p); + +/** + * Return with tail node of the linked list + * @param ll_p pointer to linked list + * @return pointer to the head of 'll_p' + */ +void * lv_ll_get_tail(const lv_ll_t * ll_p); + +/** + * Return with the pointer of the next node after 'n_act' + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the next node + */ +void * lv_ll_get_next(const lv_ll_t * ll_p, const void * n_act); + +/** + * Return with the pointer of the previous node after 'n_act' + * @param ll_p pointer to linked list + * @param n_act pointer a node + * @return pointer to the previous node + */ +void * lv_ll_get_prev(const lv_ll_t * ll_p, const void * n_act); + +/** + * Move a nodw before an other node in the same linked list + * @param ll_p pointer to a linked list + * @param n_act pointer to node to move + * @param n_after pointer to a node which should be after `n_act` + */ +void lv_ll_move_before(lv_ll_t * ll_p, void * n_act, void * n_after); + +/********************** + * MACROS + **********************/ + +#define LL_READ(list, i) for(i = lv_ll_get_head(&list); i != NULL; i = lv_ll_get_next(&list, i)) + +#define LL_READ_BACK(list, i) for(i = lv_ll_get_tail(&list); i != NULL; i = lv_ll_get_prev(&list, i)) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/bdk/libs/lvgl/lv_misc/lv_log.c b/bdk/libs/lvgl/lv_misc/lv_log.c new file mode 100644 index 00000000..79f81132 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_log.c @@ -0,0 +1,82 @@ +/** + * @file lv_log.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_log.h" +#if USE_LV_LOG + +#if LV_LOG_PRINTF +#include +#include "../../../mem/heap.h" +#include "../../../soc/uart.h" +#include "../../../utils/sprintf.h" +#endif +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +static void (*print_cb)(lv_log_level_t, const char *, uint32_t, const char *); + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Register custom print (or anything else) function to call when log is added + * @param f a function pointer: + * `void my_print (lv_log_level_t level, const char * file, uint32_t line, const char * dsc)` + */ +void lv_log_register_print(void f(lv_log_level_t, const char *, uint32_t, const char *)) +{ + print_cb = f; +} + +/** + * Add a log + * @param level the level of log. (From `lv_log_level_t` enum) + * @param file name of the file when the log added + * @param line line number in the source code where the log added + * @param dsc description of the log + */ +void lv_log_add(lv_log_level_t level, const char * file, int line, const char * dsc) +{ + if(level >= _LV_LOG_LEVEL_NUM) return; /*Invalid level*/ + + if(level >= LV_LOG_LEVEL) { + +#if LV_LOG_PRINTF + static const char * lvl_prefix[] = {"Trace", "Info", "Warn", "Error"}; + char *log = (char *)malloc(0x1000); + s_printf(log, "%s: %s \t(%s #%d)\r\n", lvl_prefix[level], dsc, file, line); + uart_send(UART_B, (u8 *)log, strlen(log) + 1); + //gfx_printf("%s: %s \t(%s #%d)\n", lvl_prefix[level], dsc, file, line); +#else + if(print_cb) print_cb(level, file, line, dsc); +#endif + } +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +#endif /*USE_LV_LOG*/ diff --git a/bdk/libs/lvgl/lv_misc/lv_log.h b/bdk/libs/lvgl/lv_misc/lv_log.h new file mode 100644 index 00000000..8e99763d --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_log.h @@ -0,0 +1,86 @@ +/** + * @file lv_log.h + * + */ + +#ifndef LV_LOG_H +#define LV_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif +#include + +/********************* + * DEFINES + *********************/ + +/*Possible log level. For compatibility declare it independently from `USE_LV_LOG`*/ + +#define LV_LOG_LEVEL_TRACE 0 /*A lot of logs to give detailed information*/ +#define LV_LOG_LEVEL_INFO 1 /*Log important events*/ +#define LV_LOG_LEVEL_WARN 2 /*Log if something unwanted happened but didn't caused problem*/ +#define LV_LOG_LEVEL_ERROR 3 /*Only critical issue, when the system may fail*/ +#define _LV_LOG_LEVEL_NUM 4 + +typedef int8_t lv_log_level_t; + +#if USE_LV_LOG +/********************** + * TYPEDEFS + **********************/ + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Register custom print (or anything else) function to call when log is added + * @param f a function pointer: + * `void my_print (lv_log_level_t level, const char * file, uint32_t line, const char * dsc)` + */ +void lv_log_register_print(void f(lv_log_level_t, const char *, uint32_t, const char *)); + +/** + * Add a log + * @param level the level of log. (From `lv_log_level_t` enum) + * @param file name of the file when the log added + * @param line line number in the source code where the log added + * @param dsc description of the log + */ +void lv_log_add(lv_log_level_t level, const char * file, int line, const char * dsc); + +/********************** + * MACROS + **********************/ + +#define LV_LOG_TRACE(dsc) lv_log_add(LV_LOG_LEVEL_TRACE, __FILE__, __LINE__, dsc); +#define LV_LOG_INFO(dsc) lv_log_add(LV_LOG_LEVEL_INFO, __FILE__, __LINE__, dsc); +#define LV_LOG_WARN(dsc) lv_log_add(LV_LOG_LEVEL_WARN, __FILE__, __LINE__, dsc); +#define LV_LOG_ERROR(dsc) lv_log_add(LV_LOG_LEVEL_ERROR, __FILE__, __LINE__, dsc); + +#else /*USE_LV_LOG*/ + +/*Do nothing if `USE_LV_LOG 0`*/ +#define lv_log_add(level, file, line, dsc) {;} +#define LV_LOG_TRACE(dsc) {;} +#define LV_LOG_INFO(dsc) {;} +#define LV_LOG_WARN(dsc) {;} +#define LV_LOG_ERROR(dsc) {;} +#endif /*USE_LV_LOG*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_LOG_H*/ diff --git a/bdk/libs/lvgl/lv_misc/lv_math.c b/bdk/libs/lvgl/lv_misc/lv_math.c new file mode 100644 index 00000000..a0d26053 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_math.c @@ -0,0 +1,165 @@ +/** + * @file lv_math.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_math.h" +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ +static int16_t sin0_90_table[] = { + 0, 572, 1144, 1715, 2286, 2856, 3425, 3993, 4560, 5126, + 5690, 6252, 6813, 7371, 7927, 8481, 9032, 9580, 10126, 10668, + 11207, 11743, 12275, 12803, 13328, 13848, 14364, 14876, 15383, 15886, + 16383, 16876, 17364, 17846, 18323, 18794, 19260, 19720, 20173, 20621, + 21062, 21497, 21925, 22347, 22762, 23170, 23571, 23964, 24351, 24730, + 25101, 25465, 25821, 26169, 26509, 26841, 27165, 27481, 27788, 28087, + 28377, 28659, 28932, 29196, 29451, 29697, 29934, 30162, 30381, 30591, + 30791, 30982, 31163, 31335, 31498, 31650, 31794, 31927, 32051, 32165, + 32269, 32364, 32448, 32523, 32587, 32642, 32687, 32722, 32747, 32762, + 32767 +}; + + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Convert a number to string + * @param num a number + * @param buf pointer to a `char` buffer. The result will be stored here (max 10 elements) + * @return same as `buf` (just for convenience) + */ +char * lv_math_num_to_str(int32_t num, char * buf) +{ + char * buf_ori = buf; + if(num == 0) { + buf[0] = '0'; + buf[1] = '\0'; + return buf; + } else if(num < 0) { + (*buf) = '-'; + buf++; + num = LV_MATH_ABS(num); + } + uint32_t output = 0; + int8_t i; + + for(i = 31; i >= 0; i--) { + if((output & 0xF) >= 5) + output += 3; + if(((output & 0xF0) >> 4) >= 5) + output += (3 << 4); + if(((output & 0xF00) >> 8) >= 5) + output += (3 << 8); + if(((output & 0xF000) >> 12) >= 5) + output += (3 << 12); + if(((output & 0xF0000) >> 16) >= 5) + output += (3 << 16); + if(((output & 0xF00000) >> 20) >= 5) + output += (3 << 20); + if(((output & 0xF000000) >> 24) >= 5) + output += (3 << 24); + if(((output & 0xF0000000) >> 28) >= 5) + output += (3 << 28); + output = (output << 1) | ((num >> i) & 1); + } + + uint8_t digit; + bool leading_zero_ready = false; + for(i = 28; i >= 0; i -= 4) { + digit = ((output >> i) & 0xF) + '0'; + if(digit == '0' && leading_zero_ready == false) continue; + + leading_zero_ready = true; + (*buf) = digit; + buf++; + } + + (*buf) = '\0'; + + return buf_ori; +} + +/** + * Return with sinus of an angle + * @param angle + * @return sinus of 'angle'. sin(-90) = -32767, sin(90) = 32767 + */ +int16_t lv_trigo_sin(int16_t angle) +{ + int16_t ret = 0; + angle = angle % 360; + + if(angle < 0) angle = 360 + angle; + + if(angle < 90) { + ret = sin0_90_table[angle]; + } else if(angle >= 90 && angle < 180) { + angle = 180 - angle; + ret = sin0_90_table[angle]; + } else if(angle >= 180 && angle < 270) { + angle = angle - 180; + ret = - sin0_90_table[angle]; + } else { /*angle >=270*/ + angle = 360 - angle; + ret = - sin0_90_table[angle]; + } + + return ret; +} + +/** + * Calculate a value of a Cubic Bezier function. + * @param t time in range of [0..LV_BEZIER_VAL_MAX] + * @param u0 start values in range of [0..LV_BEZIER_VAL_MAX] + * @param u1 control value 1 values in range of [0..LV_BEZIER_VAL_MAX] + * @param u2 control value 2 in range of [0..LV_BEZIER_VAL_MAX] + * @param u3 end values in range of [0..LV_BEZIER_VAL_MAX] + * @return the value calculated from the given parameters in range of [0..LV_BEZIER_VAL_MAX] + */ +int32_t lv_bezier3(uint32_t t, int32_t u0, int32_t u1, int32_t u2, int32_t u3) +{ + uint32_t t_rem = 1024 - t; + uint32_t t_rem2 = (t_rem * t_rem) >> 10; + uint32_t t_rem3 = (t_rem2 * t_rem) >> 10; + uint32_t t2 = (t * t) >> 10; + uint32_t t3 = (t2 * t) >> 10; + + + uint32_t v1 = ((uint32_t)t_rem3 * u0) >> 10; + uint32_t v2 = ((uint32_t)3 * t_rem2 * t * u1) >> 20; + uint32_t v3 = ((uint32_t)3 * t_rem * t2 * u2) >> 20; + uint32_t v4 = ((uint32_t)t3 * u3) >> 10; + + return v1 + v2 + v3 + v4; + +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + diff --git a/bdk/libs/lvgl/lv_misc/lv_math.h b/bdk/libs/lvgl/lv_misc/lv_math.h new file mode 100644 index 00000000..a0229eb1 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_math.h @@ -0,0 +1,73 @@ +/** + * @file math_base.h + * + */ + +#ifndef LV_MATH_H +#define LV_MATH_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************* + * INCLUDES + *********************/ +#include + +/********************* + * DEFINES + *********************/ +#define LV_MATH_MIN(a,b) ((a) < (b) ? (a) : (b)) +#define LV_MATH_MAX(a,b) ((a) > (b) ? (a) : (b)) +#define LV_MATH_ABS(x) ((x) > 0 ? (x) : (-(x))) + +#define LV_TRIGO_SIN_MAX 32767 +#define LV_TRIGO_SHIFT 15 /* >> LV_TRIGO_SHIFT to normalize*/ + +#define LV_BEZIER_VAL_MAX 1024 /*Max time in Bezier functions (not [0..1] to use integers) */ +#define LV_BEZIER_VAL_SHIFT 10 /*log2(LV_BEZIER_VAL_MAX): used to normalize up scaled values*/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +/** + * Convert a number to string + * @param num a number + * @param buf pointer to a `char` buffer. The result will be stored here (max 10 elements) + * @return same as `buf` (just for convenience) + */ +char * lv_math_num_to_str(int32_t num, char * buf); + +/** + * Return with sinus of an angle + * @param angle + * @return sinus of 'angle'. sin(-90) = -32767, sin(90) = 32767 + */ +int16_t lv_trigo_sin(int16_t angle); + +/** + * Calculate a value of a Cubic Bezier function. + * @param t time in range of [0..LV_BEZIER_VAL_MAX] + * @param u0 start values in range of [0..LV_BEZIER_VAL_MAX] + * @param u1 control value 1 values in range of [0..LV_BEZIER_VAL_MAX] + * @param u2 control value 2 in range of [0..LV_BEZIER_VAL_MAX] + * @param u3 end values in range of [0..LV_BEZIER_VAL_MAX] + * @return the value calculated from the given parameters in range of [0..LV_BEZIER_VAL_MAX] + */ +int32_t lv_bezier3(uint32_t t, int32_t u0, int32_t u1, int32_t u2, int32_t u3); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/bdk/libs/lvgl/lv_misc/lv_mem.c b/bdk/libs/lvgl/lv_misc/lv_mem.c new file mode 100644 index 00000000..08f52d5e --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_mem.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2019-2020 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_mem.c + * General and portable implementation of malloc and free. + * The dynamic memory monitoring is also supported. + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_mem.h" +#include "lv_math.h" +#include + +#include + +#if LV_MEM_CUSTOM != 0 +#include LV_MEM_CUSTOM_INCLUDE +#endif + +/********************* + * DEFINES + *********************/ +#define LV_MEM_ADD_JUNK 0 /*Add memory junk on alloc (0xaa) and free(0xbb) (just for testing purposes)*/ + + +#ifdef LV_MEM_ENV64 +# define MEM_UNIT uint64_t +#else +# define MEM_UNIT uint32_t +#endif + + +/********************** + * TYPEDEFS + **********************/ + +#if LV_ENABLE_GC == 0 /*gc custom allocations must not include header*/ + +/*The size of this union must be 32 bytes (uint32_t * 8)*/ +typedef union { + struct { + MEM_UNIT used: 1; //1: if the entry is used + MEM_UNIT d_size: 31; //Size of the data + }; + MEM_UNIT header; //The header (used + d_size) + MEM_UNIT align[8]; //Align header size to MEM_UNIT * 8 bytes +} lv_mem_header_t; + +static_assert(sizeof(lv_mem_header_t) == 32, "Node header must be 32 bytes!"); + +typedef struct { + lv_mem_header_t header; + uint8_t first_data; /*First data byte in the allocated data (Just for easily create a pointer)*/ +} lv_mem_ent_t; + +#endif /* LV_ENABLE_GC */ + +/********************** + * STATIC PROTOTYPES + **********************/ +#if LV_MEM_CUSTOM == 0 +static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e); +static void * ent_alloc(lv_mem_ent_t * e, uint32_t size); +static void ent_trunc(lv_mem_ent_t * e, uint32_t size); +#endif + +/********************** + * STATIC VARIABLES + **********************/ +#if LV_MEM_CUSTOM == 0 +static uint8_t * work_mem; +#endif + +static uint32_t zero_mem; /*Give the address of this variable if 0 byte should be allocated*/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Initiaiize the dyn_mem module (work memory and other variables) + */ +void lv_mem_init(void) +{ +#if LV_MEM_CUSTOM == 0 + +#if LV_MEM_ADR == 0 + /*Allocate a large array to store the dynamically allocated data*/ + static LV_MEM_ATTR MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)]; + work_mem = (uint8_t *) work_mem_int; +#else + work_mem = (uint8_t *) LV_MEM_ADR; +#endif + + lv_mem_ent_t * full = (lv_mem_ent_t *)work_mem; + full->header.used = 0; + /*The total mem size id reduced by the first header and the close patterns */ + full->header.d_size = LV_MEM_SIZE - sizeof(lv_mem_header_t); +#endif +} + +/** + * Allocate a memory dynamically + * @param size size of the memory to allocate in bytes + * @return pointer to the allocated memory + */ +void * lv_mem_alloc(uint32_t size) +{ + if(size == 0) { + return &zero_mem; + } + + /*Round the size to lv_mem_header_t*/ + if(size & (sizeof(lv_mem_header_t) - 1)) { + size = size & (~(sizeof(lv_mem_header_t) - 1)); + size += sizeof(lv_mem_header_t); + } + + void * alloc = NULL; + +#if LV_MEM_CUSTOM == 0 /*Use the allocation from dyn_mem*/ + lv_mem_ent_t * e = NULL; + + //Search for a appropriate entry + do { + //Get the next entry + e = ent_get_next(e); + + /*If there is next entry then try to allocate there*/ + if(e != NULL) { + alloc = ent_alloc(e, size); + } + //End if there is not next entry OR the alloc. is successful + } while(e != NULL && alloc == NULL); + + +#else /*Use custom, user defined malloc function*/ +#if LV_ENABLE_GC == 1 /*gc must not include header*/ + alloc = LV_MEM_CUSTOM_ALLOC(size); +#else /* LV_ENABLE_GC */ + /*Allocate a header too to store the size*/ + alloc = LV_MEM_CUSTOM_ALLOC(size + sizeof(lv_mem_header_t)); + if(alloc != NULL) { + ((lv_mem_ent_t *) alloc)->header.d_size = size; + ((lv_mem_ent_t *) alloc)->header.used = 1; + alloc = &((lv_mem_ent_t *) alloc)->first_data; + } +#endif /* LV_ENABLE_GC */ +#endif /* LV_MEM_CUSTOM */ + +#if LV_MEM_ADD_JUNK + if(alloc != NULL) memset(alloc, 0xaa, size); +#endif + + if(alloc == NULL) LV_LOG_WARN("Couldn't allocate memory"); + + return alloc; +} + +/** + * Free an allocated data + * @param data pointer to an allocated memory + */ +void lv_mem_free(const void * data) +{ + if(data == &zero_mem) return; + if(data == NULL) return; + + +#if LV_MEM_ADD_JUNK + memset((void *)data, 0xbb, lv_mem_get_size(data)); +#endif + +#if LV_ENABLE_GC==0 + /*e points to the header*/ + lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data - sizeof(lv_mem_header_t)); + e->header.used = 0; +#endif + +#if LV_MEM_CUSTOM == 0 +#if LV_MEM_AUTO_DEFRAG + /* Make a simple defrag. + * Join the following free entries after this*/ + lv_mem_ent_t * e_next; + e_next = ent_get_next(e); + while(e_next != NULL) { + if(e_next->header.used == 0) { + e->header.d_size += e_next->header.d_size + sizeof(e->header); + } else { + break; + } + e_next = ent_get_next(e_next); + } +#endif +#else /*Use custom, user defined free function*/ +#if LV_ENABLE_GC==0 + LV_MEM_CUSTOM_FREE(e); +#else + LV_MEM_CUSTOM_FREE((void*)data); +#endif /*LV_ENABLE_GC*/ +#endif +} + +/** + * Reallocate a memory with a new size. The old content will be kept. + * @param data pointer to an allocated memory. + * Its content will be copied to the new memory block and freed + * @param new_size the desired new size in byte + * @return pointer to the new memory + */ + +#if LV_ENABLE_GC==0 + +void * lv_mem_realloc(void * data_p, uint32_t new_size) +{ + /*Round the size to lv_mem_header_t*/ + if(new_size & (sizeof(lv_mem_header_t) - 1)) { + new_size = new_size & (~(sizeof(lv_mem_header_t) - 1)); + new_size += sizeof(lv_mem_header_t); + } + + /*data_p could be previously freed pointer (in this case it is invalid)*/ + if(data_p != NULL) { + lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data_p - sizeof(lv_mem_header_t)); + if(e->header.used == 0) { + data_p = NULL; + } + } + + uint32_t old_size = lv_mem_get_size(data_p); + if(old_size == new_size) return data_p; /*Also avoid reallocating the same memory*/ + +#if LV_MEM_CUSTOM == 0 + /* Only truncate the memory is possible + * If the 'old_size' was extended by a header size in 'ent_trunc' it avoids reallocating this same memory */ + if(new_size < old_size) { + lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data_p - sizeof(lv_mem_header_t)); + ent_trunc(e, new_size); + return &e->first_data; + } +#endif + + void * new_p; + new_p = lv_mem_alloc(new_size); + + if(new_p != NULL && data_p != NULL) { + /*Copy the old data to the new. Use the smaller size*/ + if(old_size != 0) { + memcpy(new_p, data_p, LV_MATH_MIN(new_size, old_size)); + lv_mem_free(data_p); + } + } + + + if(new_p == NULL) LV_LOG_WARN("Couldn't allocate memory"); + + return new_p; +} + +#else /* LV_ENABLE_GC */ + +void * lv_mem_realloc(void * data_p, uint32_t new_size) +{ + void * new_p = LV_MEM_CUSTOM_REALLOC(data_p, new_size); + if(new_p == NULL) LV_LOG_WARN("Couldn't allocate memory"); + return new_p; +} + +#endif /* lv_enable_gc */ + +/** + * Join the adjacent free memory blocks + */ +void lv_mem_defrag(void) +{ +#if LV_MEM_CUSTOM == 0 + lv_mem_ent_t * e_free; + lv_mem_ent_t * e_next; + e_free = ent_get_next(NULL); + + while(1) { + /*Search the next free entry*/ + while(e_free != NULL) { + if(e_free->header.used != 0) { + e_free = ent_get_next(e_free); + } else { + break; + } + } + + if(e_free == NULL) return; + + /*Joint the following free entries to the free*/ + e_next = ent_get_next(e_free); + while(e_next != NULL) { + if(e_next->header.used == 0) { + e_free->header.d_size += e_next->header.d_size + sizeof(e_next->header); + } else { + break; + } + + e_next = ent_get_next(e_next); + } + + if(e_next == NULL) return; + + /*Continue from the lastly checked entry*/ + e_free = e_next; + } +#endif +} + +/** + * Give information about the work memory of dynamic allocation + * @param mon_p pointer to a dm_mon_p variable, + * the result of the analysis will be stored here + */ +void lv_mem_monitor(lv_mem_monitor_t * mon_p) +{ + /*Init the data*/ + memset(mon_p, 0, sizeof(lv_mem_monitor_t)); +#if LV_MEM_CUSTOM == 0 + lv_mem_ent_t * e; + e = NULL; + + e = ent_get_next(e); + + while(e != NULL) { + if(e->header.used == 0) { + mon_p->free_cnt++; + mon_p->free_size += e->header.d_size; + if(e->header.d_size > mon_p->free_biggest_size) { + mon_p->free_biggest_size = e->header.d_size; + } + } else { + mon_p->used_cnt++; + } + + e = ent_get_next(e); + } + mon_p->total_size = LV_MEM_SIZE; + mon_p->used_pct = 100 - ((uint64_t)100U * mon_p->free_size) / mon_p->total_size; + mon_p->frag_pct = (uint32_t)mon_p->free_biggest_size * 100U / mon_p->free_size; + mon_p->frag_pct = 100 - mon_p->frag_pct; +#endif +} + +/** + * Give the size of an allocated memory + * @param data pointer to an allocated memory + * @return the size of data memory in bytes + */ + +#if LV_ENABLE_GC==0 + +uint32_t lv_mem_get_size(const void * data) +{ + if(data == NULL) return 0; + if(data == &zero_mem) return 0; + + lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data - sizeof(lv_mem_header_t)); + + return e->header.d_size; +} + +#else /* LV_ENABLE_GC */ + +uint32_t lv_mem_get_size(const void * data) +{ + return LV_MEM_CUSTOM_GET_SIZE(data); +} + +#endif /*LV_ENABLE_GC*/ + +/********************** + * STATIC FUNCTIONS + **********************/ + +#if LV_MEM_CUSTOM == 0 +/** + * Give the next entry after 'act_e' + * @param act_e pointer to an entry + * @return pointer to an entry after 'act_e' + */ +static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e) +{ + lv_mem_ent_t * next_e = NULL; + + if(act_e == NULL) { /*NULL means: get the first entry*/ + next_e = (lv_mem_ent_t *) work_mem; + } else { /*Get the next entry */ + uint8_t * data = &act_e->first_data; + next_e = (lv_mem_ent_t *)&data[act_e->header.d_size]; + + if(&next_e->first_data >= &work_mem[LV_MEM_SIZE]) next_e = NULL; + } + + return next_e; +} + + +/** + * Try to do the real allocation with a given size + * @param e try to allocate to this entry + * @param size size of the new memory in bytes + * @return pointer to the allocated memory or NULL if not enough memory in the entry + */ +static void * ent_alloc(lv_mem_ent_t * e, uint32_t size) +{ + void * alloc = NULL; + + /*If the memory is free and big enough then use it */ + if(e->header.used == 0 && e->header.d_size >= size) { + /*Truncate the entry to the desired size */ + ent_trunc(e, size), + + e->header.used = 1; + + /*Save the allocated data*/ + alloc = &e->first_data; + } + + return alloc; +} + +/** + * Truncate the data of entry to the given size + * @param e Pointer to an entry + * @param size new size in bytes + */ +static void ent_trunc(lv_mem_ent_t * e, uint32_t size) +{ + /*Don't let empty space only for a header without data*/ + if(e->header.d_size == size + sizeof(lv_mem_header_t)) { + size = e->header.d_size; + } + + /* Create the new entry after the current if there is space for it */ + if(e->header.d_size != size) { + uint8_t * e_data = &e->first_data; + lv_mem_ent_t * after_new_e = (lv_mem_ent_t *)&e_data[size]; + after_new_e->header.used = 0; + after_new_e->header.d_size = e->header.d_size - size - sizeof(lv_mem_header_t); + } + + /* Set the new size for the original entry */ + e->header.d_size = size; +} + +#endif diff --git a/bdk/libs/lvgl/lv_misc/lv_mem.h b/bdk/libs/lvgl/lv_misc/lv_mem.h new file mode 100644 index 00000000..c33caa44 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_mem.h @@ -0,0 +1,127 @@ +/** + * @file lv_mem.h + * + */ + +#ifndef LV_MEM_H +#define LV_MEM_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#include +#include +#include "lv_log.h" + +/********************* + * DEFINES + *********************/ +// Check windows +#ifdef __WIN64 +//# define LV_MEM_ENV64 +#endif + +// Check GCC +#ifdef __GNUC__ +# if defined(__x86_64__) || defined(__ppc64__) +//# define LV_MEM_ENV64 +# endif +#endif + +/********************** + * TYPEDEFS + **********************/ + +typedef struct +{ + uint32_t total_size; + uint32_t free_cnt; + uint32_t free_size; + uint32_t free_biggest_size; + uint32_t used_cnt; + uint8_t used_pct; + uint8_t frag_pct; +} lv_mem_monitor_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + + +/** + * Initiaize the dyn_mem module (work memory and other variables) + */ +void lv_mem_init(void); + +/** + * Allocate a memory dynamically + * @param size size of the memory to allocate in bytes + * @return pointer to the allocated memory + */ +void * lv_mem_alloc(uint32_t size); + +/** + * Free an allocated data + * @param data pointer to an allocated memory + */ +void lv_mem_free(const void * data); + +/** + * Reallocate a memory with a new size. The old content will be kept. + * @param data pointer to an allocated memory. + * Its content will be copied to the new memory block and freed + * @param new_size the desired new size in byte + * @return pointer to the new memory + */ +void * lv_mem_realloc(void * data_p, uint32_t new_size); + +/** + * Join the adjacent free memory blocks + */ +void lv_mem_defrag(void); + +/** + * Give information about the work memory of dynamic allocation + * @param mon_p pointer to a dm_mon_p variable, + * the result of the analysis will be stored here + */ +void lv_mem_monitor(lv_mem_monitor_t * mon_p); + +/** + * Give the size of an allocated memory + * @param data pointer to an allocated memory + * @return the size of data memory in bytes + */ +uint32_t lv_mem_get_size(const void * data); + + +/********************** + * MACROS + **********************/ + +/** + * Halt on NULL pointer + * p pointer to a memory + */ +#if USE_LV_LOG == 0 +# define lv_mem_assert(p) {if(p == NULL) while(1); } +#else +# define lv_mem_assert(p) {if(p == NULL) {LV_LOG_ERROR("Out of memory!"); while(1); }} +#endif +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_MEM_H*/ + diff --git a/bdk/libs/lvgl/lv_misc/lv_misc.mk b/bdk/libs/lvgl/lv_misc/lv_misc.mk new file mode 100644 index 00000000..470f1230 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_misc.mk @@ -0,0 +1,19 @@ +CSRCS += lv_font.c +CSRCS += lv_circ.c +CSRCS += lv_area.c +CSRCS += lv_task.c +CSRCS += lv_fs.c +CSRCS += lv_anim.c +CSRCS += lv_mem.c +CSRCS += lv_ll.c +CSRCS += lv_color.c +CSRCS += lv_txt.c +CSRCS += lv_ufs.c +CSRCS += lv_math.c +CSRCS += lv_log.c +CSRCS += lv_gc.c + +DEPPATH += --dep-path $(LVGL_DIR)/lvgl/lv_misc +VPATH += :$(LVGL_DIR)/lvgl/lv_misc + +CFLAGS += "-I$(LVGL_DIR)/lvgl/lv_misc" diff --git a/bdk/libs/lvgl/lv_misc/lv_symbol_def.h b/bdk/libs/lvgl/lv_misc/lv_symbol_def.h new file mode 100644 index 00000000..762bab99 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_symbol_def.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LV_SYMBOL_DEF_H +#define LV_SYMBOL_DEF_H + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +/* + * With no UTF-8 support (192- 255) (192..241 is used) + * + * With UTF-8 support (in Supplemental Private Use Area-A): 0xF800 .. 0xF831 + * - Basic symbols: 0xE000..0xE01F + * - File symbols: 0xE020..0xE03F + * - Feedback symbols: 0xE040..0xE05F + * - Reserved: 0xE060..0xE07F + */ + +#if LV_TXT_UTF8 == 0 +#define LV_SYMBOL_GLYPH_FIRST 0xC0 +#define SYMBOL_DOT _SYMBOL_VALUE1(C0) +#define SYMBOL_CLOCK _SYMBOL_VALUE1(C1) +#define SYMBOL_LIST _SYMBOL_VALUE1(C2) +#define SYMBOL_OK _SYMBOL_VALUE1(C3) +#define SYMBOL_CLOSE _SYMBOL_VALUE1(C4) +#define SYMBOL_POWER _SYMBOL_VALUE1(C5) +#define SYMBOL_SETTINGS _SYMBOL_VALUE1(C6) +#define SYMBOL_TRASH _SYMBOL_VALUE1(C7) +#define SYMBOL_HOME _SYMBOL_VALUE1(C8) +#define SYMBOL_DOWNLOAD _SYMBOL_VALUE1(C9) +#define SYMBOL_DRIVE _SYMBOL_VALUE1(CA) +#define SYMBOL_REFRESH _SYMBOL_VALUE1(CB) +#define SYMBOL_REBOOT _SYMBOL_VALUE1(CC) +#define SYMBOL_CHIP _SYMBOL_VALUE1(CD) +#define SYMBOL_SD _SYMBOL_VALUE1(CE) +#define SYMBOL_CIRCUIT _SYMBOL_VALUE1(CF) +#define SYMBOL_EDIT _SYMBOL_VALUE1(D0) +#define SYMBOL_FILE_ALT _SYMBOL_VALUE1(D1) +#define SYMBOL_FILE_CODE _SYMBOL_VALUE1(D2) +#define SYMBOL_FILE_ARC _SYMBOL_VALUE1(D3) +#define SYMBOL_TEMPERATURE _SYMBOL_VALUE1(D4) +#define SYMBOL_MODULES _SYMBOL_VALUE1(D5) +#define SYMBOL_MODULES_ALT _SYMBOL_VALUE1(D6) +#define SYMBOL_LEFT _SYMBOL_VALUE1(D7) +#define SYMBOL_RIGHT _SYMBOL_VALUE1(D8) +#define SYMBOL_KEY _SYMBOL_VALUE1(D9) +#define SYMBOL_INFO _SYMBOL_VALUE1(DA) +#define SYMBOL_WARNING _SYMBOL_VALUE1(DB) +#define SYMBOL_SHUFFLE _SYMBOL_VALUE1(DC) +#define SYMBOL_UP _SYMBOL_VALUE1(DD) +#define SYMBOL_DOWN _SYMBOL_VALUE1(DE) +#define SYMBOL_BRIGHTNESS _SYMBOL_VALUE1(DF) +#define SYMBOL_DIRECTORY _SYMBOL_VALUE1(E0) +#define SYMBOL_UPLOAD _SYMBOL_VALUE1(E1) +#define SYMBOL_USB _SYMBOL_VALUE1(E2) +#define SYMBOL_TOOLS _SYMBOL_VALUE1(E3) +#define SYMBOL_COPY _SYMBOL_VALUE1(E4) +#define SYMBOL_SAVE _SYMBOL_VALUE1(E5) +#define SYMBOL_CHARGE _SYMBOL_VALUE1(E6) +#define SYMBOL_HINT _SYMBOL_VALUE1(E7) +#define SYMBOL_KEYBOARD _SYMBOL_VALUE1(E8) +#define SYMBOL_GPS _SYMBOL_VALUE1(E9) +#define SYMBOL_FILE _SYMBOL_VALUE1(EA) +#define SYMBOL_CAMERA _SYMBOL_VALUE1(EB) +#define SYMBOL_BATTERY_FULL _SYMBOL_VALUE1(EC) +#define SYMBOL_BATTERY_3 _SYMBOL_VALUE1(ED) +#define SYMBOL_BATTERY_2 _SYMBOL_VALUE1(EE) +#define SYMBOL_BATTERY_1 _SYMBOL_VALUE1(EF) +#define SYMBOL_BATTERY_EMPTY _SYMBOL_VALUE1(F0) +#define SYMBOL_SHRK _SYMBOL_VALUE1(F1) +#define LV_SYMBOL_GLYPH_LAST 0xF1 +#define SYMBOL_DUMMY _SYMBOL_VALUE1(FF) /*Invalid symbol. If written before a string then `lv_img` will show it as a label*/ + +#else +#define LV_SYMBOL_GLYPH_FIRST 0xF800 +#define SYMBOL_DOT _SYMBOL_VALUE3(EF,A0,80) +#define SYMBOL_CLOCK _SYMBOL_VALUE3(EF,A0,81) +#define SYMBOL_LIST _SYMBOL_VALUE3(EF,A0,82) +#define SYMBOL_OK _SYMBOL_VALUE3(EF,A0,83) +#define SYMBOL_CLOSE _SYMBOL_VALUE3(EF,A0,84) +#define SYMBOL_POWER _SYMBOL_VALUE3(EF,A0,85) +#define SYMBOL_SETTINGS _SYMBOL_VALUE3(EF,A0,86) +#define SYMBOL_TRASH _SYMBOL_VALUE3(EF,A0,87) +#define SYMBOL_HOME _SYMBOL_VALUE3(EF,A0,88) +#define SYMBOL_DOWNLOAD _SYMBOL_VALUE3(EF,A0,89) +#define SYMBOL_DRIVE _SYMBOL_VALUE3(EF,A0,8A) +#define SYMBOL_REFRESH _SYMBOL_VALUE3(EF,A0,8B) +#define SYMBOL_REBOOT _SYMBOL_VALUE3(EF,A0,8C) +#define SYMBOL_CHIP _SYMBOL_VALUE3(EF,A0,8D) +#define SYMBOL_SD _SYMBOL_VALUE3(EF,A0,8E) +#define SYMBOL_CIRCUIT _SYMBOL_VALUE3(EF,A0,8F) +#define SYMBOL_EDIT _SYMBOL_VALUE3(EF,A0,90) +#define SYMBOL_FILE_ALT _SYMBOL_VALUE3(EF,A0,91) +#define SYMBOL_FILE_CODE _SYMBOL_VALUE3(EF,A0,92) +#define SYMBOL_FILE_ARC _SYMBOL_VALUE3(EF,A0,93) +#define SYMBOL_TEMPERATURE _SYMBOL_VALUE3(EF,A0,94) +#define SYMBOL_MODULES _SYMBOL_VALUE3(EF,A0,95) +#define SYMBOL_MODULES_ALT _SYMBOL_VALUE3(EF,A0,96) +#define SYMBOL_LEFT _SYMBOL_VALUE3(EF,A0,97) +#define SYMBOL_RIGHT _SYMBOL_VALUE3(EF,A0,98) +#define SYMBOL_KEY _SYMBOL_VALUE3(EF,A0,99) +#define SYMBOL_INFO _SYMBOL_VALUE3(EF,A0,9A) +#define SYMBOL_WARNING _SYMBOL_VALUE3(EF,A0,9B) +#define SYMBOL_SHUFFLE _SYMBOL_VALUE3(EF,A0,9C) +#define SYMBOL_UP _SYMBOL_VALUE3(EF,A0,9D) +#define SYMBOL_DOWN _SYMBOL_VALUE3(EF,A0,9E) +#define SYMBOL_BRIGHTNESS _SYMBOL_VALUE3(EF,A0,9F) +#define SYMBOL_DIRECTORY _SYMBOL_VALUE3(EF,A0,A0) +#define SYMBOL_UPLOAD _SYMBOL_VALUE3(EF,A0,A1) +#define SYMBOL_USB _SYMBOL_VALUE3(EF,A0,A2) +#define SYMBOL_TOOLS _SYMBOL_VALUE3(EF,A0,A3) +#define SYMBOL_COPY _SYMBOL_VALUE3(EF,A0,A4) +#define SYMBOL_SAVE _SYMBOL_VALUE3(EF,A0,A5) +#define SYMBOL_CHARGE _SYMBOL_VALUE3(EF,A0,A6) +#define SYMBOL_HINT _SYMBOL_VALUE3(EF,A0,A7) +#define SYMBOL_KEYBOARD _SYMBOL_VALUE3(EF,A0,A8) +#define SYMBOL_GPS _SYMBOL_VALUE3(EF,A0,A9) +#define SYMBOL_FILE _SYMBOL_VALUE3(EF,A0,AA) +#define SYMBOL_CAMERA _SYMBOL_VALUE3(EF,A0,AB) +#define SYMBOL_BATTERY_FULL _SYMBOL_VALUE3(EF,A0,AC) +#define SYMBOL_BATTERY_3 _SYMBOL_VALUE3(EF,A0,AD) +#define SYMBOL_BATTERY_2 _SYMBOL_VALUE3(EF,A0,AE) +#define SYMBOL_BATTERY_1 _SYMBOL_VALUE3(EF,A0,AF) +#define SYMBOL_BATTERY_EMPTY _SYMBOL_VALUE3(EF,A0,B0) +#define SYMBOL_SHRK _SYMBOL_VALUE3(EF,A0,B1) +#define LV_SYMBOL_GLYPH_LAST 0xF831 +#define SYMBOL_DUMMY _SYMBOL_VALUE3(EF,A3,BF) /*Invalid symbol at (U+F831). If written before a string then `lv_img` will show it as a label*/ +#endif + +#define _SYMBOL_VALUE1(x) (0x ## x) +#define _SYMBOL_VALUE3(x, y, z) (0x ## z ## y ## x) +#define _SYMBOL_NUMSTR(sym) LV_ ## sym ## _NUMSTR = sym + +enum +{ + _SYMBOL_NUMSTR(SYMBOL_DOT), + _SYMBOL_NUMSTR(SYMBOL_CLOCK), + _SYMBOL_NUMSTR(SYMBOL_LIST), + _SYMBOL_NUMSTR(SYMBOL_OK), + _SYMBOL_NUMSTR(SYMBOL_CLOSE), + _SYMBOL_NUMSTR(SYMBOL_POWER), + _SYMBOL_NUMSTR(SYMBOL_SETTINGS), + _SYMBOL_NUMSTR(SYMBOL_TRASH), + _SYMBOL_NUMSTR(SYMBOL_HOME), + _SYMBOL_NUMSTR(SYMBOL_DOWNLOAD), + _SYMBOL_NUMSTR(SYMBOL_DRIVE), + _SYMBOL_NUMSTR(SYMBOL_REFRESH), + _SYMBOL_NUMSTR(SYMBOL_REBOOT), + _SYMBOL_NUMSTR(SYMBOL_CHIP), + _SYMBOL_NUMSTR(SYMBOL_SD), + _SYMBOL_NUMSTR(SYMBOL_CIRCUIT), + _SYMBOL_NUMSTR(SYMBOL_EDIT), + _SYMBOL_NUMSTR(SYMBOL_FILE_ALT), + _SYMBOL_NUMSTR(SYMBOL_FILE_CODE), + _SYMBOL_NUMSTR(SYMBOL_FILE_ARC), + _SYMBOL_NUMSTR(SYMBOL_TEMPERATURE), + _SYMBOL_NUMSTR(SYMBOL_MODULES), + _SYMBOL_NUMSTR(SYMBOL_MODULES_ALT), + _SYMBOL_NUMSTR(SYMBOL_LEFT), + _SYMBOL_NUMSTR(SYMBOL_RIGHT), + _SYMBOL_NUMSTR(SYMBOL_KEY), + _SYMBOL_NUMSTR(SYMBOL_INFO), + _SYMBOL_NUMSTR(SYMBOL_WARNING), + _SYMBOL_NUMSTR(SYMBOL_SHUFFLE), + _SYMBOL_NUMSTR(SYMBOL_UP), + _SYMBOL_NUMSTR(SYMBOL_DOWN), + _SYMBOL_NUMSTR(SYMBOL_BRIGHTNESS), + _SYMBOL_NUMSTR(SYMBOL_DIRECTORY), + _SYMBOL_NUMSTR(SYMBOL_UPLOAD), + _SYMBOL_NUMSTR(SYMBOL_USB), + _SYMBOL_NUMSTR(SYMBOL_TOOLS), + _SYMBOL_NUMSTR(SYMBOL_COPY), + _SYMBOL_NUMSTR(SYMBOL_SAVE), + _SYMBOL_NUMSTR(SYMBOL_CHARGE), + _SYMBOL_NUMSTR(SYMBOL_HINT), + _SYMBOL_NUMSTR(SYMBOL_KEYBOARD), + _SYMBOL_NUMSTR(SYMBOL_GPS), + _SYMBOL_NUMSTR(SYMBOL_FILE), + _SYMBOL_NUMSTR(SYMBOL_CAMERA), + _SYMBOL_NUMSTR(SYMBOL_BATTERY_FULL), + _SYMBOL_NUMSTR(SYMBOL_BATTERY_3), + _SYMBOL_NUMSTR(SYMBOL_BATTERY_2), + _SYMBOL_NUMSTR(SYMBOL_BATTERY_1), + _SYMBOL_NUMSTR(SYMBOL_BATTERY_EMPTY), + _SYMBOL_NUMSTR(SYMBOL_SHRK), + _SYMBOL_NUMSTR(SYMBOL_DUMMY), +}; + +#undef _SYMBOL_VALUE1 +#undef _SYMBOL_VALUE3 + +#define _SYMBOL_STR_(x) #x +#define _SYMBOL_STR(x) _SYMBOL_STR_(x) +#define _SYMBOL_CHAR(c) \x ## c +#define _SYMBOL_VALUE1(x) _SYMBOL_STR(_SYMBOL_CHAR(x)) +#define _SYMBOL_VALUE3(x, y, z) _SYMBOL_STR(_SYMBOL_CHAR(x)_SYMBOL_CHAR(y)_SYMBOL_CHAR(z)) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + + +#endif /*LV_SYMBOL_DEF_H*/ diff --git a/bdk/libs/lvgl/lv_misc/lv_task.c b/bdk/libs/lvgl/lv_misc/lv_task.c new file mode 100644 index 00000000..6fa7b1be --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_task.c @@ -0,0 +1,367 @@ +/** + * @file lv_task.c + * An 'lv_task' is a void (*fp) (void* param) type function which will be called periodically. + * A priority (5 levels + disable) can be assigned to lv_tasks. + */ + +/********************* + * INCLUDES + *********************/ +#include +#include "lv_task.h" +#include "../lv_hal/lv_hal_tick.h" +#include "lv_gc.h" + +#if defined(LV_GC_INCLUDE) +# include LV_GC_INCLUDE +#endif /* LV_ENABLE_GC */ + +/********************* + * DEFINES + *********************/ +#define IDLE_MEAS_PERIOD 500 /*[ms]*/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_task_exec(lv_task_t * lv_task_p); + +/********************** + * STATIC VARIABLES + **********************/ +static bool lv_task_run = false; +static uint8_t idle_last = 0; +static bool task_deleted; +static bool task_created; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Init the lv_task module + */ +void lv_task_init(void) +{ + lv_ll_init(&LV_GC_ROOT(_lv_task_ll), sizeof(lv_task_t)); + + /*Initially enable the lv_task handling*/ + lv_task_enable(true); +} + +/** + * Call it periodically to handle lv_tasks. + */ +LV_ATTRIBUTE_TASK_HANDLER void lv_task_handler(void) +{ + LV_LOG_TRACE("lv_task_handler started"); + + /*Avoid concurrent running of the task handler*/ + static bool task_handler_mutex = false; + if(task_handler_mutex) return; + task_handler_mutex = true; + + static uint32_t idle_period_start = 0; + static uint32_t handler_start = 0; + static uint32_t busy_time = 0; + + if(lv_task_run == false) return; + + handler_start = lv_tick_get(); + + /* Run all task from the highest to the lowest priority + * If a lower priority task is executed check task again from the highest priority + * but on the priority of executed tasks don't run tasks before the executed*/ + lv_task_t * task_interrupter = NULL; + lv_task_t * next; + bool end_flag; + do { + end_flag = true; + task_deleted = false; + task_created = false; + LV_GC_ROOT(_lv_task_act) = lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll)); + while(LV_GC_ROOT(_lv_task_act)) { + /* The task might be deleted if it runs only once ('once = 1') + * So get next element until the current is surely valid*/ + next = lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), LV_GC_ROOT(_lv_task_act)); + + /*We reach priority of the turned off task. There is nothing more to do.*/ + if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio == LV_TASK_PRIO_OFF) { + break; + } + + /*Here is the interrupter task. Don't execute it again.*/ + if(LV_GC_ROOT(_lv_task_act) == task_interrupter) { + task_interrupter = NULL; /*From this point only task after the interrupter comes, so the interrupter is not interesting anymore*/ + LV_GC_ROOT(_lv_task_act) = next; + continue; /*Load the next task*/ + } + + /*Just try to run the tasks with highest priority.*/ + if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio == LV_TASK_PRIO_HIGHEST) { + lv_task_exec(LV_GC_ROOT(_lv_task_act)); + } + /*Tasks with higher priority then the interrupted shall be run in every case*/ + else if(task_interrupter) { + if(((lv_task_t *)LV_GC_ROOT(_lv_task_act))->prio > task_interrupter->prio) { + if(lv_task_exec(LV_GC_ROOT(_lv_task_act))) { + task_interrupter = LV_GC_ROOT(_lv_task_act); /*Check all tasks again from the highest priority */ + end_flag = false; + break; + } + } + } + /* It is no interrupter task or we already reached it earlier. + * Just run the remaining tasks*/ + else { + if(lv_task_exec(LV_GC_ROOT(_lv_task_act))) { + task_interrupter = LV_GC_ROOT(_lv_task_act); /*Check all tasks again from the highest priority */ + end_flag = false; + break; + } + } + + if(task_deleted) break; /*If a task was deleted then this or the next item might be corrupted*/ + if(task_created) break; /*If a task was deleted then this or the next item might be corrupted*/ + + LV_GC_ROOT(_lv_task_act) = next; /*Load the next task*/ + } + } while(!end_flag); + + busy_time += lv_tick_elaps(handler_start); + uint32_t idle_period_time = lv_tick_elaps(idle_period_start); + if(idle_period_time >= IDLE_MEAS_PERIOD) { + + idle_last = (uint32_t)((uint32_t)busy_time * 100) / IDLE_MEAS_PERIOD; /*Calculate the busy percentage*/ + idle_last = idle_last > 100 ? 0 : 100 - idle_last; /*But we need idle time*/ + busy_time = 0; + idle_period_start = lv_tick_get(); + + + } + + task_handler_mutex = false; /*Release the mutex*/ + + LV_LOG_TRACE("lv_task_handler ready"); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/** + * Create a new lv_task + * @param task a function which is the task itself + * @param period call period in ms unit + * @param prio priority of the task (LV_TASK_PRIO_OFF means the task is stopped) + * @param param free parameter + * @return pointer to the new task + */ +lv_task_t * lv_task_create(void (*task)(void *), uint32_t period, lv_task_prio_t prio, void * param) +{ + lv_task_t * new_lv_task = NULL; + lv_task_t * tmp; + + /*Create task lists in order of priority from high to low*/ + tmp = lv_ll_get_head(&LV_GC_ROOT(_lv_task_ll)); + if(NULL == tmp) { /*First task*/ + new_lv_task = lv_ll_ins_head(&LV_GC_ROOT(_lv_task_ll)); + lv_mem_assert(new_lv_task); + if(new_lv_task == NULL) return NULL; + } else { + do { + if(tmp->prio <= prio) { + new_lv_task = lv_ll_ins_prev(&LV_GC_ROOT(_lv_task_ll), tmp); + lv_mem_assert(new_lv_task); + if(new_lv_task == NULL) return NULL; + break; + } + tmp = lv_ll_get_next(&LV_GC_ROOT(_lv_task_ll), tmp); + } while(tmp != NULL); + + if(tmp == NULL) { /*Only too high priority tasks were found*/ + new_lv_task = lv_ll_ins_tail(&LV_GC_ROOT(_lv_task_ll)); + lv_mem_assert(new_lv_task); + if(new_lv_task == NULL) return NULL; + } + } + + new_lv_task->period = period; + new_lv_task->task = task; + new_lv_task->prio = prio; + new_lv_task->param = param; + new_lv_task->once = 0; + new_lv_task->last_run = lv_tick_get(); + + task_created = true; + + return new_lv_task; +} + +/** + * Delete a lv_task + * @param lv_task_p pointer to task created by lv_task_p + */ +void lv_task_del(lv_task_t * lv_task_p) +{ + lv_ll_rem(&LV_GC_ROOT(_lv_task_ll), lv_task_p); + + lv_mem_free(lv_task_p); + + if(LV_GC_ROOT(_lv_task_act) == lv_task_p) task_deleted = true; /*The active task was deleted*/ +} + +/** + * Set new priority for a lv_task + * @param lv_task_p pointer to a lv_task + * @param prio the new priority + */ +void lv_task_set_prio(lv_task_t * lv_task_p, lv_task_prio_t prio) +{ + /*Find the tasks with new priority*/ + lv_task_t * i; + LL_READ(LV_GC_ROOT(_lv_task_ll), i) { + if(i->prio <= prio) { + if(i != lv_task_p) lv_ll_move_before(&LV_GC_ROOT(_lv_task_ll), lv_task_p, i); + break; + } + } + + /*There was no such a low priority so far then add the node to the tail*/ + if(i == NULL) { + lv_ll_move_before(&LV_GC_ROOT(_lv_task_ll), lv_task_p, NULL); + } + + + lv_task_p->prio = prio; +} + +/** + * Set new period for a lv_task + * @param lv_task_p pointer to a lv_task + * @param period the new period + */ +void lv_task_set_period(lv_task_t * lv_task_p, uint32_t period) +{ + lv_task_p->period = period; +} + +/** + * Make a lv_task ready. It will not wait its period. + * @param lv_task_p pointer to a lv_task. + */ +void lv_task_ready(lv_task_t * lv_task_p) +{ + lv_task_p->last_run = lv_tick_get() - lv_task_p->period - 1; +} + +/** + * Delete the lv_task after one call + * @param lv_task_p pointer to a lv_task. + */ +void lv_task_once(lv_task_t * lv_task_p) +{ + lv_task_p->once = 1; +} + +/** + * Reset a lv_task. + * It will be called the previously set period milliseconds later. + * @param lv_task_p pointer to a lv_task. + */ +void lv_task_reset(lv_task_t * lv_task_p) +{ + lv_task_p->last_run = lv_tick_get(); +} + +/** + * Enable or disable the whole lv_task handling + * @param en: true: lv_task handling is running, false: lv_task handling is suspended + */ +void lv_task_enable(bool en) +{ + lv_task_run = en; +} + +/** + * Get idle percentage + * @return the lv_task idle in percentage + */ +uint8_t lv_task_get_idle(void) +{ + return idle_last; +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Execute task if its the priority is appropriate + * @param lv_task_p pointer to lv_task + * @return true: execute, false: not executed + */ +static bool lv_task_exec(lv_task_t * lv_task_p) +{ + bool exec = false; + + /*Execute if at least 'period' time elapsed*/ + uint32_t elp = lv_tick_elaps(lv_task_p->last_run); + if(elp >= lv_task_p->period) { + lv_task_p->last_run = lv_tick_get(); + task_deleted = false; + task_created = false; + lv_task_p->task(lv_task_p->param); + + /*Delete if it was a one shot lv_task*/ + if(task_deleted == false) { /*The task might be deleted by itself as well*/ + if(lv_task_p->once != 0) { + lv_task_del(lv_task_p); + } + } + exec = true; + } + + return exec; +} + diff --git a/bdk/libs/lvgl/lv_misc/lv_task.h b/bdk/libs/lvgl/lv_misc/lv_task.h new file mode 100644 index 00000000..bf2b5485 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_task.h @@ -0,0 +1,186 @@ +/** + * @file lv_task.c + * An 'lv_task' is a void (*fp) (void* param) type function which will be called periodically. + * A priority (5 levels + disable) can be assigned to lv_tasks. + */ + +#ifndef LV_TASK_H +#define LV_TASK_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#include +#include "lv_mem.h" +#include "lv_ll.h" + +/********************* + * DEFINES + *********************/ +#ifndef LV_ATTRIBUTE_TASK_HANDLER +#define LV_ATTRIBUTE_TASK_HANDLER +#endif +/********************** + * TYPEDEFS + **********************/ +/** + * Possible priorities for lv_tasks + */ +#define LV_TASK_ONESHOT 0 +enum +{ + LV_TASK_PRIO_OFF = 0, + LV_TASK_PRIO_LOWEST, + LV_TASK_PRIO_LOW, + LV_TASK_PRIO_MID, + LV_TASK_PRIO_HIGH, + LV_TASK_PRIO_HIGHEST, + LV_TASK_PRIO_NUM, +}; +typedef uint8_t lv_task_prio_t; + +/** + * Descriptor of a lv_task + */ + + +#include "../../../utils/types.h" + + +struct _lv_task_t; +typedef void (*lv_task_cb_t)(struct _lv_task_t*); + + +typedef struct _lv_task_t +{ + uint32_t period; + uint32_t last_run; + void (*task) (void*); + void * param; + + //lv_task_cb_t task_cb; /**< Task function */ + + void* user_data; /**< Custom user data */ + + + + uint8_t prio:3; + uint8_t once:1; +} lv_task_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Init the lv_task module + */ +void lv_task_init(void); + +/** + * Call it periodically to handle lv_tasks. + */ +LV_ATTRIBUTE_TASK_HANDLER void lv_task_handler(void); + + + + + + + +/** + * Create an "empty" task. It needs to initialzed with at least + * `lv_task_set_cb` and `lv_task_set_period` + * @return pointer to the craeted task + */ + + +//lv_task_t* lv_task_create_basic(void); + + + + + + + +/** + * Create a new lv_task + * @param task a function which is the task itself + * @param period call period in ms unit + * @param prio priority of the task (LV_TASK_PRIO_OFF means the task is stopped) + * @param param free parameter + * @return pointer to the new task + */ +lv_task_t* lv_task_create(void (*task) (void *), uint32_t period, lv_task_prio_t prio, void * param); + +/** + * Delete a lv_task + * @param lv_task_p pointer to task created by lv_task_p + */ +void lv_task_del(lv_task_t* lv_task_p); + +/** + * Set new priority for a lv_task + * @param lv_task_p pointer to a lv_task + * @param prio the new priority + */ +void lv_task_set_prio(lv_task_t* lv_task_p, lv_task_prio_t prio); + +/** + * Set new period for a lv_task + * @param lv_task_p pointer to a lv_task + * @param period the new period + */ +void lv_task_set_period(lv_task_t* lv_task_p, uint32_t period); + +/** + * Make a lv_task ready. It will not wait its period. + * @param lv_task_p pointer to a lv_task. + */ +void lv_task_ready(lv_task_t* lv_task_p); + + +/** + * Delete the lv_task after one call + * @param lv_task_p pointer to a lv_task. + */ +void lv_task_once(lv_task_t * lv_task_p); + +/** + * Reset a lv_task. + * It will be called the previously set period milliseconds later. + * @param lv_task_p pointer to a lv_task. + */ +void lv_task_reset(lv_task_t* lv_task_p); + +/** + * Enable or disable the whole lv_task handling + * @param en: true: lv_task handling is running, false: lv_task handling is suspended + */ +void lv_task_enable(bool en); + +/** + * Get idle percentage + * @return the lv_task idle in percentage + */ +uint8_t lv_task_get_idle(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/bdk/libs/lvgl/lv_misc/lv_templ.c b/bdk/libs/lvgl/lv_misc/lv_templ.c new file mode 100644 index 00000000..11478b71 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_templ.c @@ -0,0 +1,36 @@ +/** + * @file lv_templ.c + * + */ + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +/********************** + * STATIC VARIABLES + **********************/ + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/********************** + * STATIC FUNCTIONS + **********************/ diff --git a/bdk/libs/lvgl/lv_misc/lv_templ.h b/bdk/libs/lvgl/lv_misc/lv_templ.h new file mode 100644 index 00000000..a5459e8d --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_templ.h @@ -0,0 +1,38 @@ +/** + * @file lv_templ.h + * + */ + +#ifndef LV_TEMPL_H +#define LV_TEMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/********************** + * MACROS + **********************/ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_TEMPL_H*/ diff --git a/bdk/libs/lvgl/lv_misc/lv_txt.c b/bdk/libs/lvgl/lv_misc/lv_txt.c new file mode 100644 index 00000000..d80c3b4f --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_txt.c @@ -0,0 +1,793 @@ +/** + * @file lv_text.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_txt.h" +#include "lv_math.h" + +/********************* + * DEFINES + *********************/ +#define NO_BREAK_FOUND UINT32_MAX + +#ifndef LV_TXT_LINE_BREAK_LONG_LEN +#define LV_TXT_LINE_BREAK_LONG_LEN 12 /* If a character is at least this long, will break wherever "prettiest" */ +#endif + +#ifndef LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN +#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 /* Minimum number of characters of a word to put on a line before a break */ +#endif + +#ifndef LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN +#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 1 /* Minimum number of characters of a word to put on a line after a break */ +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool is_break_char(uint32_t letter); + +#if LV_TXT_UTF8 +static uint8_t lv_txt_utf8_size(const char * str); +static uint32_t lv_txt_unicode_to_utf8(uint32_t letter_uni); +static uint32_t lv_txt_utf8_conv_wc(uint32_t c); +static uint32_t lv_txt_utf8_next(const char * txt, uint32_t * i); +static uint32_t lv_txt_utf8_prev(const char * txt, uint32_t * i_start); +static uint32_t lv_txt_utf8_get_byte_id(const char * txt, uint32_t utf8_id); +static uint32_t lv_txt_utf8_get_char_id(const char * txt, uint32_t byte_id); +static uint32_t lv_txt_utf8_get_length(const char * txt); +#else +static uint8_t lv_txt_ascii_size(const char * str); +static uint32_t lv_txt_unicode_to_ascii(uint32_t letter_uni); +static uint32_t lv_txt_ascii_conv_wc(uint32_t c); +static uint32_t lv_txt_ascii_next(const char * txt, uint32_t * i); +static uint32_t lv_txt_ascii_prev(const char * txt, uint32_t * i_start); +static uint32_t lv_txt_ascii_get_byte_id(const char * txt, uint32_t utf8_id); +static uint32_t lv_txt_ascii_get_char_id(const char * txt, uint32_t byte_id); +static uint32_t lv_txt_ascii_get_length(const char * txt); +#endif + +/********************** + * STATIC VARIABLES + **********************/ + + +/********************** + * GLOBAL VARIABLES + **********************/ +#if LV_TXT_UTF8 +uint8_t (*lv_txt_encoded_size)(const char *) = lv_txt_utf8_size; +uint32_t (*lv_txt_unicode_to_encoded)(uint32_t) = lv_txt_unicode_to_utf8; +uint32_t (*lv_txt_encoded_conv_wc)(uint32_t) = lv_txt_utf8_conv_wc; +uint32_t (*lv_txt_encoded_next)(const char *, uint32_t *) = lv_txt_utf8_next; +uint32_t (*lv_txt_encoded_prev)(const char *, uint32_t *) = lv_txt_utf8_prev; +uint32_t (*lv_txt_encoded_get_byte_id)(const char *, uint32_t) = lv_txt_utf8_get_byte_id; +uint32_t (*lv_encoded_get_char_id)(const char *, uint32_t) = lv_txt_utf8_get_char_id; +uint32_t (*lv_txt_get_encoded_length)(const char *) = lv_txt_utf8_get_length; +#else +uint8_t (*lv_txt_encoded_size)(const char *) = lv_txt_ascii_size; +uint32_t (*lv_txt_unicode_to_encoded)(uint32_t) = lv_txt_unicode_to_ascii; +uint32_t (*lv_txt_encoded_conv_wc)(uint32_t) = lv_txt_ascii_conv_wc; +uint32_t (*lv_txt_encoded_next)(const char *, uint32_t *) = lv_txt_ascii_next; +uint32_t (*lv_txt_encoded_prev)(const char *, uint32_t *) = lv_txt_ascii_prev; +uint32_t (*lv_txt_encoded_get_byte_id)(const char *, uint32_t) = lv_txt_ascii_get_byte_id; +uint32_t (*lv_encoded_get_char_id)(const char *, uint32_t) = lv_txt_ascii_get_char_id; +uint32_t (*lv_txt_get_encoded_length)(const char *) = lv_txt_ascii_get_length; +#endif +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Get size of a text + * @param size_res pointer to a 'point_t' variable to store the result + * @param text pointer to a text + * @param font pinter to font of the text + * @param letter_space letter space of the text + * @param txt.line_space line space of the text + * @param flags settings for the text from 'txt_flag_t' enum + * @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid line breaks + */ +void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font, + lv_coord_t letter_space, lv_coord_t line_space, lv_coord_t max_width, lv_txt_flag_t flag) +{ + size_res->x = 0; + size_res->y = 0; + + if(text == NULL) return; + if(font == NULL) return; + + if(flag & LV_TXT_FLAG_EXPAND) max_width = LV_COORD_MAX; + + uint32_t line_start = 0; + uint32_t new_line_start = 0; + lv_coord_t act_line_length; + uint8_t letter_height = lv_font_get_height(font); + + /*Calc. the height and longest line*/ + while(text[line_start] != '\0') { + new_line_start += lv_txt_get_next_line(&text[line_start], font, letter_space, max_width, flag); + size_res->y += letter_height ; + size_res->y += line_space; + + /*Calculate the the longest line*/ + act_line_length = lv_txt_get_width(&text[line_start], new_line_start - line_start, + font, letter_space, flag); + + size_res->x = LV_MATH_MAX(act_line_length, size_res->x); + line_start = new_line_start; + } + + /*Make the text one line taller if the last character is '\n' or '\r'*/ + if((line_start != 0) && (text[line_start - 1] == '\n' || text[line_start - 1] == '\r')) { + size_res->y += letter_height + line_space; + } + + /*Correction with the last line space or set the height manually if the text is empty*/ + if(size_res->y == 0) size_res->y = letter_height; + else size_res->y -= line_space; + +} + +/** + * Get the next line of text. Check line length and break chars too. + * @param txt a '\0' terminated string + * @param font pointer to a font + * @param letter_space letter space + * @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid line breaks + * @param flags settings for the text from 'txt_flag_type' enum + * @return the index of the first char of the new line (in byte index not letter index. With UTF-8 they are different) + */ +uint16_t lv_txt_get_next_line(const char * txt, const lv_font_t * font, + lv_coord_t letter_space, lv_coord_t max_width, lv_txt_flag_t flag) +{ + if(txt == NULL) return 0; + if(font == NULL) return 0; + + if(flag & LV_TXT_FLAG_EXPAND) max_width = LV_COORD_MAX; + + uint32_t i = 0; + lv_coord_t cur_w = 0; + lv_coord_t w_at_last_break = 0; + uint32_t n_char_since_last_break = 0; /* Used count word length of long words */ + uint32_t last_break = NO_BREAK_FOUND; + lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT; + uint32_t letter = 0; + + while(txt[i] != '\0') { + lv_coord_t letter_width; + letter = lv_txt_encoded_next(txt, &i); + + /*Handle the recolor command*/ + if((flag & LV_TXT_FLAG_RECOLOR) != 0) { + if(lv_txt_is_cmd(&cmd_state, letter) != false) { + continue; /*Skip the letter is it is part of a command*/ + } + } + + + /*Check for new line chars*/ + if(letter == '\n' || letter == '\r') { + uint32_t i_tmp = i; + uint32_t letter_next = lv_txt_encoded_next(txt, &i_tmp); + if(letter == '\r' && letter_next == '\n') i = i_tmp; + + return i; /*Return with the first letter of the next line*/ + + } else { /*Check the actual length*/ + n_char_since_last_break++; + letter_width = lv_font_get_width(font, letter); + cur_w += letter_width; + + /* Get the length of the current work and determine best place + * to break the line. */ + if(cur_w > max_width) { + if( last_break != NO_BREAK_FOUND ) { + /* Continue searching for next breakable character to see if the next word will fit */ + uint32_t n_char_fit = n_char_since_last_break - 1; + if( n_char_since_last_break <= LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN ) { + i = last_break; + } + else { + uint32_t i_tmp = i; + cur_w -= w_at_last_break + letter_space; /*ignore the first letter_space after the break char */ + bool other = true; + while(txt[i_tmp] != '\0') { + letter = lv_txt_encoded_next(txt, &i_tmp); + + /*Handle the recolor command*/ + if((flag & LV_TXT_FLAG_RECOLOR) != 0) { + if(lv_txt_is_cmd(&cmd_state, letter) != false) { + continue; /*Skip the letter is it is part of a command*/ + } + } + + /*Check for new line chars*/ + if(letter == '\n' || letter == '\r' || is_break_char(letter)) { + if(n_char_since_last_break >= LV_TXT_LINE_BREAK_LONG_LEN) { + /* Figure out the prettiest place to break */ + uint32_t char_remain; + lv_txt_encoded_prev(txt, &i); + for(char_remain=n_char_since_last_break - n_char_fit; + char_remain < LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN; + char_remain++) { + lv_txt_encoded_prev(txt, &i); + } + } + else{ + i = last_break; + } + other = false; + break; + } + n_char_since_last_break++; + lv_coord_t letter_width2 = lv_font_get_width(font, letter); + cur_w += letter_width2; + if(cur_w > max_width) { + /* Current letter already exceeds, return previous */ + lv_txt_encoded_prev(txt, &i); + other = false; + break; + } + if(letter_width2 > 0){ + cur_w += letter_space; + } + } + if( other ) { + if(n_char_since_last_break >= LV_TXT_LINE_BREAK_LONG_LEN) { + /* Figure out the prettiest place to break */ + uint32_t char_remain; + lv_txt_encoded_prev(txt, &i); + for(char_remain=n_char_since_last_break - n_char_fit; + char_remain < LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN; + char_remain++){ + lv_txt_encoded_prev(txt, &i); + } + } + else{ + i = last_break; + } + } + } + } else { + /* Now this character is out of the area so it will be first character of the next line*/ + /* But 'i' already points to the next character (because of lv_txt_utf8_next) step beck one*/ + lv_txt_encoded_prev(txt, &i); + } + + /* Do not let to return without doing nothing. + * Find at least one character (Avoid infinite loop )*/ + if(i == 0) lv_txt_encoded_next(txt, &i); + + return i; + } + /*If this char still can fit to this line then check if + * txt can be broken here later */ + else if(is_break_char(letter)) { + last_break = i; /*Save the first char index after break*/ + w_at_last_break = cur_w; + if(letter_width > 0) { + w_at_last_break += letter_space; + } + n_char_since_last_break = 0; + } + } + + if(letter_width > 0) { + cur_w += letter_space; + } + } + + return i; +} + +/** + * Give the length of a text with a given font + * @param txt a '\0' terminate string + * @param length length of 'txt' in byte count and not characters (Á is 1 character but 2 bytes in UTF-8) + * @param font pointer to a font + * @param letter_space letter space + * @param flags settings for the text from 'txt_flag_t' enum + * @return length of a char_num long text + */ +lv_coord_t lv_txt_get_width(const char * txt, uint16_t length, + const lv_font_t * font, lv_coord_t letter_space, lv_txt_flag_t flag) +{ + if(txt == NULL) return 0; + if(font == NULL) return 0; + + uint32_t i = 0; + lv_coord_t width = 0; + lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT; + uint32_t letter; + + if(length != 0) { + while(i< length){ + letter = lv_txt_encoded_next(txt, &i); + if((flag & LV_TXT_FLAG_RECOLOR) != 0) { + if(lv_txt_is_cmd(&cmd_state, letter) != false) { + continue; + } + } + + lv_coord_t char_width = lv_font_get_width(font, letter); + if(char_width > 0){ + width += char_width; + width += letter_space; + } + } + + if(width > 0) { + width -= letter_space; /*Trim the last letter space. Important if the text is center aligned */ + } + } + + return width; +} + +/** + * Check next character in a string and decide if the character is part of the command or not + * @param state pointer to a txt_cmd_state_t variable which stores the current state of command processing + * (Initied. to TXT_CMD_STATE_WAIT ) + * @param c the current character + * @return true: the character is part of a command and should not be written, + * false: the character should be written + */ +bool lv_txt_is_cmd(lv_txt_cmd_state_t * state, uint32_t c) +{ + bool ret = false; + + if(c == (uint32_t)LV_TXT_COLOR_CMD[0]) { + if(*state == LV_TXT_CMD_STATE_WAIT) { /*Start char*/ + *state = LV_TXT_CMD_STATE_PAR; + ret = true; + } else if(*state == LV_TXT_CMD_STATE_PAR) { /*Other start char in parameter is escaped cmd. char */ + *state = LV_TXT_CMD_STATE_WAIT; + } else if(*state == LV_TXT_CMD_STATE_IN) { /*Command end */ + *state = LV_TXT_CMD_STATE_WAIT; + ret = true; + } + } + + /*Skip the color parameter and wait the space after it*/ + if(*state == LV_TXT_CMD_STATE_PAR) { + if(c == ' ') { + *state = LV_TXT_CMD_STATE_IN; /*After the parameter the text is in the command*/ + } + ret = true; + } + + return ret; +} + +/** + * Insert a string into an other + * @param txt_buf the original text (must be big enough for the result text) + * @param pos position to insert. Expressed in character index and not byte index (Different in UTF-8) + * 0: before the original text, 1: after the first char etc. + * @param ins_txt text to insert + */ +void lv_txt_ins(char * txt_buf, uint32_t pos, const char * ins_txt) +{ + uint32_t old_len = strlen(txt_buf); + uint32_t ins_len = strlen(ins_txt); + uint32_t new_len = ins_len + old_len; +#if LV_TXT_UTF8 != 0 + pos = lv_txt_encoded_get_byte_id(txt_buf, pos); /*Convert to byte index instead of letter index*/ +#endif + /*Copy the second part into the end to make place to text to insert*/ + uint32_t i; + for(i = new_len; i >= pos + ins_len; i--) { + txt_buf[i] = txt_buf[i - ins_len]; + } + + /* Copy the text into the new space*/ + memcpy(txt_buf + pos, ins_txt, ins_len); +} + +/** + * Delete a part of a string + * @param txt string to modify + * @param pos position where to start the deleting (0: before the first char, 1: after the first char etc.) + * @param len number of characters to delete + */ +void lv_txt_cut(char * txt, uint32_t pos, uint32_t len) +{ + + uint32_t old_len = strlen(txt); +#if LV_TXT_UTF8 != 0 + pos = lv_txt_encoded_get_byte_id(txt, pos); /*Convert to byte index instead of letter index*/ + len = lv_txt_encoded_get_byte_id(&txt[pos], len); +#endif + + /*Copy the second part into the end to make place to text to insert*/ + uint32_t i; + for(i = pos; i <= old_len - len; i++) { + txt[i] = txt[i + len]; + } +} + + +/******************************* + * UTF-8 ENCODER/DECOER + ******************************/ + +#if LV_TXT_UTF8 + +/** + * Give the size of an UTF-8 coded character + * @param str pointer to a character in a string + * @return length of the UTF-8 character (1,2,3 or 4). O on invalid code + */ +static uint8_t lv_txt_utf8_size(const char * str) +{ + if((str[0] & 0x80) == 0) return 1; + else if((str[0] & 0xE0) == 0xC0) return 2; + else if((str[0] & 0xF0) == 0xE0) return 3; + else if((str[0] & 0xF8) == 0xF0) return 4; + return 0; +} + + +/** + * Convert an Unicode letter to UTF-8. + * @param letter_uni an Unicode letter + * @return UTF-8 coded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ĺ°') + */ +static uint32_t lv_txt_unicode_to_utf8(uint32_t letter_uni) +{ + if(letter_uni < 128) return letter_uni; + uint8_t bytes[4]; + + if(letter_uni < 0x0800) { + bytes[0] = ((letter_uni >> 6) & 0x1F) | 0xC0; + bytes[1] = ((letter_uni >> 0) & 0x3F) | 0x80; + bytes[2] = 0; + bytes[3] = 0; + } else if(letter_uni < 0x010000) { + bytes[0] = ((letter_uni >> 12) & 0x0F) | 0xE0; + bytes[1] = ((letter_uni >> 6) & 0x3F) | 0x80; + bytes[2] = ((letter_uni >> 0) & 0x3F) | 0x80; + bytes[3] = 0; + } else if(letter_uni < 0x110000) { + bytes[0] = ((letter_uni >> 18) & 0x07) | 0xF0; + bytes[1] = ((letter_uni >> 12) & 0x3F) | 0x80; + bytes[2] = ((letter_uni >> 6) & 0x3F) | 0x80; + bytes[3] = ((letter_uni >> 0) & 0x3F) | 0x80; + } + + uint32_t * res_p = (uint32_t *)bytes; + return *res_p; +} + +/** + * Convert a wide character, e.g. 'Á' little endian to be UTF-8 compatible + * @param c a wide character or a Little endian number + * @return `c` in big endian + */ +static uint32_t lv_txt_utf8_conv_wc(uint32_t c) +{ + /*Swap the bytes (UTF-8 is big endian, but the MCUs are little endian)*/ + if((c & 0x80) != 0) { + uint32_t swapped; + uint8_t c8[4]; + memcpy(c8, &c, 4); + swapped = (c8[0] << 24) + (c8[1] << 16) + (c8[2] << 8) + (c8[3]); + uint8_t i; + for(i = 0; i < 4; i++) { + if((swapped & 0xFF) == 0) swapped = (swapped >> 8); /*Ignore leading zeros (they were in the end originally)*/ + } + c = swapped; + } + + return c; +} + +/** + * Decode an UTF-8 character from a string. + * @param txt pointer to '\0' terminated string + * @param i start byte index in 'txt' where to start. + * After call it will point to the next UTF-8 char in 'txt'. + * NULL to use txt[0] as index + * @return the decoded Unicode character or 0 on invalid UTF-8 code + */ +static uint32_t lv_txt_utf8_next(const char * txt, uint32_t * i) +{ + /* Unicode to UTF-8 + * 00000000 00000000 00000000 0xxxxxxx -> 0xxxxxxx + * 00000000 00000000 00000yyy yyxxxxxx -> 110yyyyy 10xxxxxx + * 00000000 00000000 zzzzyyyy yyxxxxxx -> 1110zzzz 10yyyyyy 10xxxxxx + * 00000000 000wwwzz zzzzyyyy yyxxxxxx -> 11110www 10zzzzzz 10yyyyyy 10xxxxxx + * */ + + uint32_t result = 0; + + /*Dummy 'i' pointer is required*/ + uint32_t i_tmp = 0; + if(i == NULL) i = &i_tmp; + + /*Normal ASCII*/ + if((txt[*i] & 0x80) == 0) { + result = txt[*i]; + (*i)++; + } + /*Real UTF-8 decode*/ + else { + /*2 bytes UTF-8 code*/ + if((txt[*i] & 0xE0) == 0xC0) { + result = (uint32_t)(txt[*i] & 0x1F) << 6; + (*i)++; + if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/ + result += (txt[*i] & 0x3F); + (*i)++; + } + /*3 bytes UTF-8 code*/ + else if((txt[*i] & 0xF0) == 0xE0) { + result = (uint32_t)(txt[*i] & 0x0F) << 12; + (*i)++; + + if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/ + result += (uint32_t)(txt[*i] & 0x3F) << 6; + (*i)++; + + if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/ + result += (txt[*i] & 0x3F); + (*i)++; + } + /*4 bytes UTF-8 code*/ + else if((txt[*i] & 0xF8) == 0xF0) { + result = (uint32_t)(txt[*i] & 0x07) << 18; + (*i)++; + + if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/ + result += (uint32_t)(txt[*i] & 0x3F) << 12; + (*i)++; + + if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/ + result += (uint32_t)(txt[*i] & 0x3F) << 6; + (*i)++; + + if((txt[*i] & 0xC0) != 0x80) return 0; /*Invalid UTF-8 code*/ + result += txt[*i] & 0x3F; + (*i)++; + } else { + (*i)++; /*Not UTF-8 char. Go the next.*/ + } + } + return result; +} + +/** + * Get previous UTF-8 character form a string. + * @param txt pointer to '\0' terminated string + * @param i start byte index in 'txt' where to start. After the call it will point to the previous UTF-8 char in 'txt'. + * @return the decoded Unicode character or 0 on invalid UTF-8 code + */ +static uint32_t lv_txt_utf8_prev(const char * txt, uint32_t * i) +{ + uint8_t c_size; + uint8_t cnt = 0; + + /*Try to find a !0 long UTF-8 char by stepping one character back*/ + (*i)--; + do { + if(cnt >= 4) return 0; /*No UTF-8 char found before the initial*/ + + c_size = lv_txt_encoded_size(&txt[*i]); + if(c_size == 0) { + if(*i != 0)(*i)--; + else return 0; + } + cnt++; + } while(c_size == 0); + + uint32_t i_tmp = *i; + uint32_t letter = lv_txt_encoded_next(txt, &i_tmp); /*Character found, get it*/ + + return letter; + +} + +/** + * Convert a character index (in an UTF-8 text) to byte index. + * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param utf8_id character index + * @return byte index of the 'utf8_id'th letter + */ +static uint32_t lv_txt_utf8_get_byte_id(const char * txt, uint32_t utf8_id) +{ + uint32_t i; + uint32_t byte_cnt = 0; + for(i = 0; i < utf8_id; i++) { + byte_cnt += lv_txt_encoded_size(&txt[byte_cnt]); + } + + return byte_cnt; + +} + + +/** + * Convert a byte index (in an UTF-8 text) to character index. + * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param byte_id byte index + * @return character index of the letter at 'byte_id'th position + */ +static uint32_t lv_txt_utf8_get_char_id(const char * txt, uint32_t byte_id) +{ + uint32_t i = 0; + uint32_t char_cnt = 0; + + while(i < byte_id) { + lv_txt_encoded_next(txt, &i); /*'i' points to the next letter so use the prev. value*/ + char_cnt++; + } + + return char_cnt; +} + +/** + * Get the number of characters (and NOT bytes) in a string. Decode it with UTF-8 if enabled. + * E.g.: "ÁBC" is 3 characters (but 4 bytes) + * @param txt a '\0' terminated char string + * @return number of characters + */ +static uint32_t lv_txt_utf8_get_length(const char * txt) +{ +#if LV_TXT_UTF8 == 0 + return strlen(txt); +#else + uint32_t len = 0; + uint32_t i = 0; + + while(txt[i] != '\0') { + lv_txt_encoded_next(txt, &i); + len++; + } + + return len; +#endif +} + +#else +/** + * Give the size of an UTF-8 coded character + * @param str pointer to a character in a string + * @return length of the UTF-8 character (1,2,3 or 4). O on invalid code + */ +static uint8_t lv_txt_ascii_size(const char * str) +{ + return 1; +} + + +/** + * Convert an Unicode letter to UTF-8. + * @param letter_uni an Unicode letter + * @return UTF-8 coded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ĺ°') + */ +static uint32_t lv_txt_unicode_to_ascii(uint32_t letter_uni) +{ + if(letter_uni < 128) return letter_uni; + else return ' '; +} + +/** + * Convert wide characters to ASCII, however wide characters in ASCII range (e.g. 'A') are ASCII compatible by default. + * So this function does nothing just returns with `c`. + * @param c a character, e.g. 'A' + * @return same as `c` + */ +static uint32_t lv_txt_ascii_conv_wc(uint32_t c) +{ + return c; +} + +/** + * Decode an UTF-8 character from a string. + * @param txt pointer to '\0' terminated string + * @param i start byte index in 'txt' where to start. + * After call it will point to the next UTF-8 char in 'txt'. + * NULL to use txt[0] as index + * @return the decoded Unicode character or 0 on invalid UTF-8 code + */ +static uint32_t lv_txt_ascii_next(const char * txt, uint32_t * i) +{ + if(i == NULL) return txt[1]; /*Get the next char */ + + uint8_t letter = txt[*i] ; + (*i)++; + return letter; +} + +/** + * Get previous UTF-8 character form a string. + * @param txt pointer to '\0' terminated string + * @param i start byte index in 'txt' where to start. After the call it will point to the previous UTF-8 char in 'txt'. + * @return the decoded Unicode character or 0 on invalid UTF-8 code + */ +static uint32_t lv_txt_ascii_prev(const char * txt, uint32_t * i) +{ + if(i == NULL) return *(txt - 1); /*Get the prev. char */ + + (*i)--; + uint8_t letter = txt[*i] ; + + return letter; +} + +/** + * Convert a character index (in an UTF-8 text) to byte index. + * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param utf8_id character index + * @return byte index of the 'utf8_id'th letter + */ +static uint32_t lv_txt_ascii_get_byte_id(const char * txt, uint32_t utf8_id) +{ + return utf8_id; /*In Non encoded no difference*/ +} + + +/** + * Convert a byte index (in an UTF-8 text) to character index. + * E.g. in "AÁRT" index of 'R' is 2th char but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param byte_id byte index + * @return character index of the letter at 'byte_id'th position + */ +static uint32_t lv_txt_ascii_get_char_id(const char * txt, uint32_t byte_id) +{ + return byte_id; /*In Non encoded no difference*/ +} + +/** + * Get the number of characters (and NOT bytes) in a string. Decode it with UTF-8 if enabled. + * E.g.: "ÁBC" is 3 characters (but 4 bytes) + * @param txt a '\0' terminated char string + * @return number of characters + */ +static uint32_t lv_txt_ascii_get_length(const char * txt) +{ + return strlen(txt); +} +#endif +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Test if char is break char or not (a text can broken here or not) + * @param letter a letter + * @return false: 'letter' is not break char + */ +static bool is_break_char(uint32_t letter) +{ + uint8_t i; + bool ret = false; + + /*Compare the letter to TXT_BREAK_CHARS*/ + for(i = 0; LV_TXT_BREAK_CHARS[i] != '\0'; i++) { + if(letter == (uint32_t)LV_TXT_BREAK_CHARS[i]) { + ret = true; /*If match then it is break char*/ + break; + } + } + + return ret; +} + diff --git a/bdk/libs/lvgl/lv_misc/lv_txt.h b/bdk/libs/lvgl/lv_misc/lv_txt.h new file mode 100644 index 00000000..81b55f94 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_txt.h @@ -0,0 +1,197 @@ +/** + * @file lv_text.h + * + */ + +#ifndef LV_TXT_H +#define LV_TXT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#include "lv_area.h" +#include "lv_font.h" +#include "lv_area.h" + +/********************* + * DEFINES + *********************/ +#define LV_TXT_COLOR_CMD "#" + +/********************** + * TYPEDEFS + **********************/ +enum +{ + LV_TXT_FLAG_NONE = 0x00, + LV_TXT_FLAG_RECOLOR = 0x01, /*Enable parsing of recolor command*/ + LV_TXT_FLAG_EXPAND = 0x02, /*Ignore width to avoid automatic word wrapping*/ + LV_TXT_FLAG_CENTER = 0x04, /*Align the text to the middle*/ + LV_TXT_FLAG_RIGHT = 0x08, /*Align the text to the right*/ +}; +typedef uint8_t lv_txt_flag_t; + +enum +{ + LV_TXT_CMD_STATE_WAIT, /*Waiting for command*/ + LV_TXT_CMD_STATE_PAR, /*Processing the parameter*/ + LV_TXT_CMD_STATE_IN, /*Processing the command*/ +}; +typedef uint8_t lv_txt_cmd_state_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Get size of a text + * @param size_res pointer to a 'point_t' variable to store the result + * @param text pointer to a text + * @param font pinter to font of the text + * @param letter_space letter space of the text + * @param line_space line space of the text + * @param flags settings for the text from 'txt_flag_t' enum + * @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid line breaks + */ +void lv_txt_get_size(lv_point_t * size_res, const char * text, const lv_font_t * font, + lv_coord_t letter_space, lv_coord_t line_space, lv_coord_t max_width, lv_txt_flag_t flag); + +/** + * Get the next line of text. Check line length and break chars too. + * @param txt a '\0' terminated string + * @param font pointer to a font + * @param letter_space letter space + * @param max_width max with of the text (break the lines to fit this size) Set CORD_MAX to avoid line breaks + * @param flags settings for the text from 'txt_flag_type' enum + * @return the index of the first char of the new line (in byte index not letter index. With UTF-8 they are different) + */ +uint16_t lv_txt_get_next_line(const char * txt, const lv_font_t * font, + lv_coord_t letter_space, lv_coord_t max_width, lv_txt_flag_t flag); + +/** + * Give the length of a text with a given font + * @param txt a '\0' terminate string + * @param length length of 'txt' in byte count and not characters (Á is 1 character but 2 bytes in UTF-8) + * @param font pointer to a font + * @param letter_space letter space + * @param flags settings for the text from 'txt_flag_t' enum + * @return length of a char_num long text + */ +lv_coord_t lv_txt_get_width(const char * txt, uint16_t length, + const lv_font_t * font, lv_coord_t letter_space, lv_txt_flag_t flag); + + +/** + * Check next character in a string and decide if te character is part of the command or not + * @param state pointer to a txt_cmd_state_t variable which stores the current state of command processing + * @param c the current character + * @return true: the character is part of a command and should not be written, + * false: the character should be written + */ +bool lv_txt_is_cmd(lv_txt_cmd_state_t * state, uint32_t c); + +/** + * Insert a string into an other + * @param txt_buf the original text (must be big enough for the result text) + * @param pos position to insert (0: before the original text, 1: after the first char etc.) + * @param ins_txt text to insert + */ +void lv_txt_ins(char * txt_buf, uint32_t pos, const char * ins_txt); + +/** + * Delete a part of a string + * @param txt string to modify + * @param pos position where to start the deleting (0: before the first char, 1: after the first char etc.) + * @param len number of characters to delete + */ +void lv_txt_cut(char * txt, uint32_t pos, uint32_t len); + +/*************************************************************** + * GLOBAL FUNCTION POINTERS FOR CAHRACTER ENCODING INTERFACE + ***************************************************************/ + +/** + * Give the size of an encoded character + * @param str pointer to a character in a string + * @return length of the encoded character (1,2,3 ...). O in invalid + */ +extern uint8_t (*lv_txt_encoded_size)(const char *); + + +/** + * Convert an Unicode letter to encoded + * @param letter_uni an Unicode letter + * @return Encoded character in Little Endian to be compatible with C chars (e.g. 'Á', 'Ü') + */ +extern uint32_t (*lv_txt_unicode_to_encoded)(uint32_t ); + +/** + * Convert a wide character, e.g. 'Á' little endian to be compatible with the encoded format. + * @param c a wide character + * @return `c` in the encoded format + */ +extern uint32_t (*lv_txt_encoded_conv_wc) (uint32_t c); + +/** + * Decode the next encoded character from a string. + * @param txt pointer to '\0' terminated string + * @param i start index in 'txt' where to start. + * After the call it will point to the next encoded char in 'txt'. + * NULL to use txt[0] as index + * @return the decoded Unicode character or 0 on invalid data code + */ +extern uint32_t (*lv_txt_encoded_next)(const char *, uint32_t * ); + +/** + * Get the previous encoded character form a string. + * @param txt pointer to '\0' terminated string + * @param i_start index in 'txt' where to start. After the call it will point to the previous encoded char in 'txt'. + * @return the decoded Unicode character or 0 on invalid data + */ +extern uint32_t (*lv_txt_encoded_prev)(const char *, uint32_t *); + +/** + * Convert a letter index (in an the encoded text) to byte index. + * E.g. in UTF-8 "AÁRT" index of 'R' is 2 but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param enc_id letter index + * @return byte index of the 'enc_id'th letter + */ +extern uint32_t (*lv_txt_encoded_get_byte_id)(const char *, uint32_t); + +/** + * Convert a byte index (in an encoded text) to character index. + * E.g. in UTF-8 "AÁRT" index of 'R' is 2 but start at byte 3 because 'Á' is 2 bytes long + * @param txt a '\0' terminated UTF-8 string + * @param byte_id byte index + * @return character index of the letter at 'byte_id'th position + */ +extern uint32_t (*lv_encoded_get_char_id)(const char *, uint32_t); + +/** + * Get the number of characters (and NOT bytes) in a string. + * E.g. in UTF-8 "ÁBC" is 3 characters (but 4 bytes) + * @param txt a '\0' terminated char string + * @return number of characters + */ +extern uint32_t (*lv_txt_get_encoded_length)(const char *); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*USE_TXT*/ diff --git a/bdk/libs/lvgl/lv_misc/lv_ufs.c b/bdk/libs/lvgl/lv_misc/lv_ufs.c new file mode 100644 index 00000000..ef1a1653 --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_ufs.c @@ -0,0 +1,516 @@ +/** + * @file lv_ufs.c + * Implementation of RAM file system which do NOT support directories. + * The API is compatible with the lv_fs_int module. + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_ufs.h" +#if USE_LV_FILESYSTEM + +#include "lv_ll.h" +#include +#include +#include +#include "lv_gc.h" + +#if defined(LV_GC_INCLUDE) +# include LV_GC_INCLUDE +#endif /* LV_ENABLE_GC */ + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_ufs_ent_t * lv_ufs_ent_get(const char * fn); +static lv_ufs_ent_t * lv_ufs_ent_new(const char * fn); + +/********************** + * STATIC VARIABLES + **********************/ +static bool inited = false; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a driver for ufs and initialize it. + */ +void lv_ufs_init(void) +{ + lv_ll_init(&LV_GC_ROOT(_lv_file_ll), sizeof(lv_ufs_ent_t)); + + lv_fs_drv_t ufs_drv; + memset(&ufs_drv, 0, sizeof(lv_fs_drv_t)); /*Initialization*/ + + ufs_drv.file_size = sizeof(lv_ufs_file_t); + ufs_drv.rddir_size = sizeof(lv_ufs_dir_t); + ufs_drv.letter = UFS_LETTER; + ufs_drv.ready = lv_ufs_ready; + + ufs_drv.open = lv_ufs_open; + ufs_drv.close = lv_ufs_close; + ufs_drv.remove = lv_ufs_remove; + ufs_drv.read = lv_ufs_read; + ufs_drv.write = lv_ufs_write; + ufs_drv.seek = lv_ufs_seek; + ufs_drv.tell = lv_ufs_tell; + ufs_drv.size = lv_ufs_size; + ufs_drv.trunc = lv_ufs_trunc; + ufs_drv.free = lv_ufs_free; + + ufs_drv.dir_open = lv_ufs_dir_open; + ufs_drv.dir_read = lv_ufs_dir_read; + ufs_drv.dir_close = lv_ufs_dir_close; + + lv_fs_add_drv(&ufs_drv); + + inited = true; +} + +/** + * Give the state of the ufs + * @return true if ufs is initialized and can be used else false + */ +bool lv_ufs_ready(void) +{ + return inited; +} + +/** + * Open a file in ufs + * @param file_p pointer to a lv_ufs_file_t variable + * @param fn name of the file. There are no directories so e.g. "myfile.txt" + * @param mode element of 'fs_mode_t' enum or its 'OR' connection (e.g. FS_MODE_WR | FS_MODE_RD) + * @return LV_FS_RES_OK: no error, the file is opened + * any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_open(void * file_p, const char * fn, lv_fs_mode_t mode) +{ + lv_ufs_file_t * fp = file_p; /*Convert type*/ + lv_ufs_ent_t * ent = lv_ufs_ent_get(fn); + + fp->ent = NULL; + + /*If the file not exists ...*/ + if(ent == NULL) { + if((mode & LV_FS_MODE_WR) != 0) { /*Create the file if opened for write*/ + ent = lv_ufs_ent_new(fn); + if(ent == NULL) return LV_FS_RES_FULL; /*No space for the new file*/ + } else { + return LV_FS_RES_NOT_EX; /*Can not read not existing file*/ + } + } + + /*Can not write already opened and const data files*/ + if((mode & LV_FS_MODE_WR) != 0) { + if(ent->oc != 0) return LV_FS_RES_LOCKED; + if(ent->const_data != 0) return LV_FS_RES_DENIED; + } + + /*No error, the file can be opened*/ + fp->ent = ent; + fp->ar = mode & LV_FS_MODE_RD ? 1 : 0; + fp->aw = mode & LV_FS_MODE_WR ? 1 : 0; + fp->rwp = 0; + ent->oc ++; + + return LV_FS_RES_OK; +} + + +/** + * Create a file with a constant data + * @param fn name of the file (directories are not supported) + * @param const_p pointer to a constant data + * @param len length of the data pointed by 'const_p' in bytes + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_create_const(const char * fn, const void * const_p, uint32_t len) +{ + lv_ufs_file_t file; + lv_fs_res_t res; + + /*Error if the file already exists*/ + res = lv_ufs_open(&file, fn, LV_FS_MODE_RD); + if(res == LV_FS_RES_OK) { + lv_ufs_close(&file); + return LV_FS_RES_DENIED; + } + + lv_ufs_close(&file); + + res = lv_ufs_open(&file, fn, LV_FS_MODE_WR); + if(res != LV_FS_RES_OK) return res; + + lv_ufs_ent_t * ent = file.ent; + + if(ent->data_d != NULL) return LV_FS_RES_DENIED; + + ent->data_d = (void *) const_p; + ent->size = len; + ent->const_data = 1; + + res = lv_ufs_close(&file); + if(res != LV_FS_RES_OK) return res; + + return LV_FS_RES_OK; +} + +/** + * Close an opened file + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_close(void * file_p) +{ + lv_ufs_file_t * fp = file_p; /*Convert type*/ + + if(fp->ent == NULL) return LV_FS_RES_OK; + + /*Decrement the Open counter*/ + if(fp->ent->oc > 0) { + fp->ent->oc--; + } + + return LV_FS_RES_OK; +} + +/** + * Remove a file. The file can not be opened. + * @param fn '\0' terminated string + * @return LV_FS_RES_OK: no error, the file is removed + * LV_FS_RES_DENIED: the file was opened, remove failed + */ +lv_fs_res_t lv_ufs_remove(const char * fn) +{ + lv_ufs_ent_t * ent = lv_ufs_ent_get(fn); + if(ent == NULL) return LV_FS_RES_DENIED; /*File not exists*/ + + /*Can not be deleted is opened*/ + if(ent->oc != 0) return LV_FS_RES_DENIED; + + lv_ll_rem(&LV_GC_ROOT(_lv_file_ll), ent); + lv_mem_free(ent->fn_d); + ent->fn_d = NULL; + if(ent->const_data == 0) { + lv_mem_free(ent->data_d); + ent->data_d = NULL; + } + + lv_mem_free(ent); + + return LV_FS_RES_OK; +} + +/** + * Read data from an opened file + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open ) + * @param buf pointer to a memory block where to store the read data + * @param btr number of Bytes To Read + * @param br the real number of read bytes (Byte Read) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_read(void * file_p, void * buf, uint32_t btr, uint32_t * br) +{ + lv_ufs_file_t * fp = file_p; /*Convert type*/ + + lv_ufs_ent_t * ent = fp->ent; + *br = 0; + + if(ent->data_d == NULL || ent->size == 0) { /*Don't read empty files*/ + return LV_FS_RES_OK; + } else if(fp->ar == 0) { /*The file is not opened for read*/ + return LV_FS_RES_DENIED; + } + + /*No error, read the file*/ + if(fp->rwp + btr > ent->size) { /*Check too much bytes read*/ + *br = ent->size - fp->rwp; + } else { + *br = btr; + } + + /*Read the data*/ + uint8_t * data8_p; + if(ent->const_data == 0) { + data8_p = (uint8_t *) ent->data_d; + } else { + data8_p = ent->data_d; + } + + data8_p += fp->rwp; + memcpy(buf, data8_p, *br); + + fp->rwp += *br; /*Refresh the read write pointer*/ + + return LV_FS_RES_OK; +} + +/** + * Write data to an opened file + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open) + * @param buf pointer to a memory block which content will be written + * @param btw the number Bytes To Write + * @param bw The real number of written bytes (Byte Written) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_write(void * file_p, const void * buf, uint32_t btw, uint32_t * bw) +{ + lv_ufs_file_t * fp = file_p; /*Convert type*/ + *bw = 0; + + if(fp->aw == 0) return LV_FS_RES_DENIED; /*Not opened for write*/ + + lv_ufs_ent_t * ent = fp->ent; + + /*Reallocate data array if it necessary*/ + uint32_t new_size = fp->rwp + btw; + if(new_size > ent->size) { + uint8_t * new_data = lv_mem_realloc(ent->data_d, new_size); + lv_mem_assert(new_data); + if(new_data == NULL) return LV_FS_RES_FULL; /*Cannot allocate the new memory*/ + + ent->data_d = new_data; + ent->size = new_size; + } + + /*Write the file*/ + uint8_t * data8_p = (uint8_t *) ent->data_d; + data8_p += fp->rwp; + memcpy(data8_p, buf, btw); + *bw = btw; + fp->rwp += *bw; + + return LV_FS_RES_OK; +} + +/** + * Set the read write pointer. Also expand the file size if necessary. + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open ) + * @param pos the new position of read write pointer + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_seek(void * file_p, uint32_t pos) +{ + lv_ufs_file_t * fp = file_p; /*Convert type*/ + lv_ufs_ent_t * ent = fp->ent; + + /*Simply move the rwp before EOF*/ + if(pos < ent->size) { + fp->rwp = pos; + } else { /*Expand the file size*/ + if(fp->aw == 0) return LV_FS_RES_DENIED; /*Not opened for write*/ + + uint8_t * new_data = lv_mem_realloc(ent->data_d, pos); + lv_mem_assert(new_data); + if(new_data == NULL) return LV_FS_RES_FULL; /*Out of memory*/ + + ent->data_d = new_data; + ent->size = pos; + fp->rwp = pos; + } + + return LV_FS_RES_OK; +} + +/** + * Give the position of the read write pointer + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open ) + * @param pos_p pointer to to store the result + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_tell(void * file_p, uint32_t * pos_p) +{ + lv_ufs_file_t * fp = file_p; /*Convert type*/ + + *pos_p = fp->rwp; + + return LV_FS_RES_OK; +} + +/** + * Truncate the file size to the current position of the read write pointer + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open ) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_trunc(void * file_p) +{ + lv_ufs_file_t * fp = file_p; /*Convert type*/ + lv_ufs_ent_t * ent = fp->ent; + + if(fp->aw == 0) return LV_FS_RES_DENIED; /*Not opened for write*/ + + void * new_data = lv_mem_realloc(ent->data_d, fp->rwp); + lv_mem_assert(new_data); + if(new_data == NULL) return LV_FS_RES_FULL; /*Out of memory*/ + + ent->data_d = new_data; + ent->size = fp->rwp; + + return LV_FS_RES_OK; +} + +/** + * Give the size of the file in bytes + * @param file_p file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open ) + * @param size_p pointer to store the size + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_size(void * file_p, uint32_t * size_p) +{ + lv_ufs_file_t * fp = file_p; /*Convert type*/ + lv_ufs_ent_t * ent = fp->ent; + + *size_p = ent->size; + + return LV_FS_RES_OK; +} + +/** + * Initialize a lv_ufs_read_dir_t variable to directory reading + * @param rddir_p pointer to a 'ufs_dir_t' variable + * @param path uFS doesn't support folders so it has to be "" + * @return LV_FS_RES_OK or any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_dir_open(void * rddir_p, const char * path) +{ + lv_ufs_dir_t * lv_ufs_rddir_p = rddir_p; + + lv_ufs_rddir_p->last_ent = NULL; + + if(path[0] != '\0') return LV_FS_RES_NOT_EX; /*Must be "" */ + else return LV_FS_RES_OK; +} + +/** + * Read the next file name + * @param dir_p pointer to an initialized 'ufs_dir_t' variable + * @param fn pointer to buffer to sore the file name + * @return LV_FS_RES_OK or any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_dir_read(void * dir_p, char * fn) +{ + lv_ufs_dir_t * ufs_dir_p = dir_p; + + if(ufs_dir_p->last_ent == NULL) { + ufs_dir_p->last_ent = lv_ll_get_head(&LV_GC_ROOT(_lv_file_ll)); + } else { + ufs_dir_p->last_ent = lv_ll_get_next(&LV_GC_ROOT(_lv_file_ll), ufs_dir_p->last_ent); + } + + if(ufs_dir_p->last_ent != NULL) { + strcpy(fn, ufs_dir_p->last_ent->fn_d); + } else { + fn[0] = '\0'; + } + + return LV_FS_RES_OK; +} + +/** + * Close the directory reading + * @param rddir_p pointer to an initialized 'ufs_dir_t' variable + * @return LV_FS_RES_OK or any error from lv__fs_res_t enum + */ +lv_fs_res_t lv_ufs_dir_close(void * rddir_p) +{ + (void)rddir_p; + return LV_FS_RES_OK; +} + +/** + * Give the size of a drive + * @param total_p pointer to store the total size [kB] + * @param free_p pointer to store the free site [kB] + * @return LV_FS_RES_OK or any error from 'lv_fs_res_t' + */ +lv_fs_res_t lv_ufs_free(uint32_t * total_p, uint32_t * free_p) +{ + +#if LV_MEM_CUSTOM == 0 + lv_mem_monitor_t mon; + + lv_mem_monitor(&mon); + *total_p = LV_MEM_SIZE >> 10; /*Convert bytes to kB*/ + *free_p = mon.free_size >> 10; +#else + *free_p = 0; +#endif + return LV_FS_RES_OK; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Gives the lv_ufs_entry from a filename + * @param fn filename ('\0' terminated string) + * @return pointer to the dynamically allocated entry with 'fn' filename. + * NULL if no entry found with that name. + */ +static lv_ufs_ent_t * lv_ufs_ent_get(const char * fn) +{ + lv_ufs_ent_t * fp; + + LL_READ(LV_GC_ROOT(_lv_file_ll), fp) { + if(strcmp(fp->fn_d, fn) == 0) { + return fp; + } + } + + return NULL; +} + +/** + * Create a new entry with 'fn' filename + * @param fn filename ('\0' terminated string) + * @return pointer to the dynamically allocated new entry. + * NULL if no space for the entry. + */ +static lv_ufs_ent_t * lv_ufs_ent_new(const char * fn) +{ + lv_ufs_ent_t * new_ent = NULL; + new_ent = lv_ll_ins_head(&LV_GC_ROOT(_lv_file_ll)); /*Create a new file*/ + lv_mem_assert(new_ent); + if(new_ent == NULL) return NULL; + + new_ent->fn_d = lv_mem_alloc(strlen(fn) + 1); /*Save the name*/ + lv_mem_assert(new_ent->fn_d); + if(new_ent->fn_d == NULL) return NULL; + + strcpy(new_ent->fn_d, fn); + new_ent->data_d = NULL; + new_ent->size = 0; + new_ent->oc = 0; + new_ent->const_data = 0; + + return new_ent; +} + +#endif /*USE_LV_FILESYSTEM*/ + diff --git a/bdk/libs/lvgl/lv_misc/lv_ufs.h b/bdk/libs/lvgl/lv_misc/lv_ufs.h new file mode 100644 index 00000000..543104fb --- /dev/null +++ b/bdk/libs/lvgl/lv_misc/lv_ufs.h @@ -0,0 +1,213 @@ +/** + * @file lv_ufs.h + * Implementation of RAM file system which do NOT support directories. + * The API is compatible with the lv_fs_int module. + */ + +#ifndef LV_UFS_H +#define LV_UFS_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_FILESYSTEM + +#include "lv_fs.h" +#include "lv_mem.h" + +/********************* + * DEFINES + *********************/ +#define UFS_LETTER 'U' + +/********************** + * TYPEDEFS + **********************/ +/*Description of a file entry */ +typedef struct +{ + char * fn_d; + void * data_d; + uint32_t size; /*Data length in bytes*/ + uint16_t oc; /*Open Count*/ + uint8_t const_data :1; +} lv_ufs_ent_t; + +/*File descriptor, used to handle opening an entry more times simultaneously + Contains unique informations about the specific opening*/ +typedef struct +{ + lv_ufs_ent_t* ent; /*Pointer to the entry*/ + uint32_t rwp; /*Read Write Pointer*/ + uint8_t ar :1; /*1: Access for read is enabled */ + uint8_t aw :1; /*1: Access for write is enabled */ +} lv_ufs_file_t; + +/* Read directory descriptor. + * It is used to to iterate through the entries in a directory*/ +typedef struct +{ + lv_ufs_ent_t * last_ent; +} lv_ufs_dir_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a driver for ufs and initialize it. + */ +void lv_ufs_init(void); + +/** + * Give the state of the ufs + * @return true if ufs is initialized and can be used else false + */ +bool lv_ufs_ready(void); + +/** + * Open a file in ufs + * @param file_p pointer to a lv_ufs_file_t variable + * @param fn name of the file. There are no directories so e.g. "myfile.txt" + * @param mode element of 'fs_mode_t' enum or its 'OR' connection (e.g. FS_MODE_WR | FS_MODE_RD) + * @return LV_FS_RES_OK: no error, the file is opened + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_open (void * file_p, const char * fn, lv_fs_mode_t mode); + +/** + * Create a file with a constant data + * @param fn name of the file (directories are not supported) + * @param const_p pointer to a constant data + * @param len length of the data pointed by 'const_p' in bytes + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_create_const(const char * fn, const void * const_p, uint32_t len); + +/** + * Close an opened file + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_close (void * file_p); + +/** + * Remove a file. The file can not be opened. + * @param fn '\0' terminated string + * @return LV_FS_RES_OK: no error, the file is removed + * LV_FS_RES_DENIED: the file was opened, remove failed + */ +lv_fs_res_t lv_ufs_remove(const char * fn); + +/** + * Read data from an opened file + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open ) + * @param buf pointer to a memory block where to store the read data + * @param btr number of Bytes To Read + * @param br the real number of read bytes (Byte Read) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_read (void * file_p, void * buf, uint32_t btr, uint32_t * br); + +/** + * Write data to an opened file + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open) + * @param buf pointer to a memory block which content will be written + * @param btw the number Bytes To Write + * @param bw The real number of written bytes (Byte Written) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_write (void * file_p, const void * buf, uint32_t btw, uint32_t * bw); + +/** + * Set the read write pointer. Also expand the file size if necessary. + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open ) + * @param pos the new position of read write pointer + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_seek (void * file_p, uint32_t pos); + +/** + * Give the position of the read write pointer + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open ) + * @param pos_p pointer to to store the result + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_tell (void * file_p, uint32_t * pos_p); + +/** + * Truncate the file size to the current position of the read write pointer + * @param file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open ) + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_trunc (void * file_p); + +/** + * Give the size of the file in bytes + * @param file_p file_p pointer to an 'ufs_file_t' variable. (opened with lv_ufs_open ) + * @param size_p pointer to store the size + * @return LV_FS_RES_OK: no error, the file is read + * any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_size (void * file_p, uint32_t * size_p); + +/** + * Initialize a lv_ufs_read_dir_t variable to directory reading + * @param rddir_p pointer to a 'ufs_read_dir_t' variable + * @param path uFS doesn't support folders so it has to be "" + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_dir_open(void * rddir_p, const char * path); + +/** + * Read the next file name + * @param dir_p pointer to an initialized 'ufs_read_dir_t' variable + * @param fn pointer to buffer to sore the file name + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_dir_read(void * dir_p, char * fn); + +/** + * Close the directory reading + * @param rddir_p pointer to an initialized 'ufs_read_dir_t' variable + * @return LV_FS_RES_OK or any error from lv_fs_res_t enum + */ +lv_fs_res_t lv_ufs_dir_close(void * rddir_p); + +/** + * Give the size of a drive + * @param total_p pointer to store the total size [kB] + * @param free_p pointer to store the free site [kB] + * @return LV_FS_RES_OK or any error from 'fs_res_t' + */ +lv_fs_res_t lv_ufs_free (uint32_t * total_p, uint32_t * free_p); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_FILESYSTEM*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_arc.c b/bdk/libs/lvgl/lv_objx/lv_arc.c new file mode 100644 index 00000000..683d3434 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_arc.c @@ -0,0 +1,310 @@ +/** + * @file lv_arc.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "lv_arc.h" +#if USE_LV_ARC != 0 + +#include "../lv_misc/lv_math.h" +#include "../lv_draw/lv_draw_arc.h" +#include "../lv_themes/lv_theme.h" + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_arc_design(lv_obj_t * arc, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_arc_signal(lv_obj_t * arc, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_design_func_t ancestor_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a arc object + * @param par pointer to an object, it will be the parent of the new arc + * @param copy pointer to a arc object, if not NULL then the new object will be copied from it + * @return pointer to the created arc + */ +lv_obj_t * lv_arc_create(lv_obj_t * par, const lv_obj_t * copy) +{ + + LV_LOG_TRACE("arc create started"); + + /*Create the ancestor of arc*/ + lv_obj_t * new_arc = lv_obj_create(par, copy); + lv_mem_assert(new_arc); + if(new_arc == NULL) return NULL; + + /*Allocate the arc type specific extended data*/ + lv_arc_ext_t * ext = lv_obj_allocate_ext_attr(new_arc, sizeof(lv_arc_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_arc); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_arc); + + /*Initialize the allocated 'ext' */ + ext->angle_start = 45; + ext->angle_end = 315; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_arc, lv_arc_signal); + lv_obj_set_design_func(new_arc, lv_arc_design); + + /*Init the new arc arc*/ + if(copy == NULL) { + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_arc_set_style(new_arc, LV_ARC_STYLE_MAIN, th->arc); + } else { + lv_arc_set_style(new_arc, LV_ARC_STYLE_MAIN, &lv_style_plain_color); + } + + } + /*Copy an existing arc*/ + else { + lv_arc_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->angle_start = copy_ext->angle_start; + ext->angle_end = copy_ext->angle_end; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_arc); + } + + LV_LOG_INFO("arc created"); + + return new_arc; +} + +/*====================== + * Add/remove functions + *=====================*/ + +/* + * New object specific "add" or "remove" functions come here + */ + + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the start and end angles of an arc. 0 deg: bottom, 90 deg: right etc. + * @param arc pointer to an arc object + * @param start the start angle [0..360] + * @param end the end angle [0..360] + */ +void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end) +{ + lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc); + + if(start > 360) start = 360; + if(end > 360) end = 360; + + ext->angle_start = start; + ext->angle_end = end; + + lv_obj_invalidate(arc); +} + +/** + * Set a style of a arc. + * @param arc pointer to arc object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_arc_set_style(lv_obj_t * arc, lv_arc_style_t type, lv_style_t * style) +{ + switch(type) { + case LV_ARC_STYLE_MAIN: + lv_obj_set_style(arc, style); + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the start angle of an arc. + * @param arc pointer to an arc object + * @return the start angle [0..360] + */ +uint16_t lv_arc_get_angle_start(lv_obj_t * arc) +{ + lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc); + + return ext->angle_start; +} + +/** + * Get the end angle of an arc. + * @param arc pointer to an arc object + * @return the end angle [0..360] + */ +uint16_t lv_arc_get_angle_end(lv_obj_t * arc) +{ + lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc); + + return ext->angle_end; +} + +/** + * Get style of a arc. + * @param arc pointer to arc object + * @param type which style should be get + * @return style pointer to the style + * */ +lv_style_t * lv_arc_get_style(const lv_obj_t * arc, lv_arc_style_t type) +{ + lv_style_t * style = NULL; + + switch(type) { + case LV_ARC_STYLE_MAIN: + style = lv_obj_get_style(arc); + break; + default: + style = NULL; + break; + } + + return style; +} + +/*===================== + * Other functions + *====================*/ + +/* + * New object specific "other" functions come here + */ + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the arcs + * @param arc pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_arc_design(lv_obj_t * arc, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + lv_arc_ext_t * ext = lv_obj_get_ext_attr(arc); + lv_style_t * style = lv_arc_get_style(arc, LV_ARC_STYLE_MAIN); + + lv_coord_t r = (LV_MATH_MIN(lv_obj_get_width(arc), lv_obj_get_height(arc))) / 2; + lv_coord_t x = arc->coords.x1 + lv_obj_get_width(arc) / 2; + lv_coord_t y = arc->coords.y1 + lv_obj_get_height(arc) / 2; + lv_opa_t opa_scale = lv_obj_get_opa_scale(arc); + lv_draw_arc(x, y, r, mask, ext->angle_start, ext->angle_end, style, opa_scale); + + + /*Draw circle on the ends if enabled */ + if(style->line.rounded) { + lv_coord_t thick_half = style->line.width / 2; + lv_coord_t cir_x = ((r - thick_half) * lv_trigo_sin(ext->angle_start) >> LV_TRIGO_SHIFT); + lv_coord_t cir_y = ((r - thick_half) * lv_trigo_sin(ext->angle_start + 90) >> LV_TRIGO_SHIFT); + + lv_style_t cir_style; + lv_style_copy(&cir_style, &lv_style_plain); + cir_style.body.grad_color = style->line.color; + cir_style.body.main_color = cir_style.body.grad_color; + cir_style.body.radius = LV_RADIUS_CIRCLE; + lv_area_t cir_area; + cir_area.x1 = cir_x + x - thick_half; + cir_area.y1 = cir_y + y - thick_half; + cir_area.x2 = cir_x + x + thick_half; + cir_area.y2 = cir_y + y + thick_half; + + lv_draw_rect(&cir_area, mask, &cir_style, opa_scale); + + cir_x = ((r - thick_half) * lv_trigo_sin(ext->angle_end) >> LV_TRIGO_SHIFT); + cir_y = ((r - thick_half) * lv_trigo_sin(ext->angle_end + 90) >> LV_TRIGO_SHIFT); + + cir_area.x1 = cir_x + x - thick_half; + cir_area.y1 = cir_y + y - thick_half; + cir_area.x2 = cir_x + x + thick_half; + cir_area.y2 = cir_y + y + thick_half; + + lv_draw_rect(&cir_area, mask, &cir_style, opa_scale); + } + + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + + } + + return true; +} + +/** + * Signal function of the arc + * @param arc pointer to a arc object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_arc_signal(lv_obj_t * arc, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(arc, sign, param); + if(res != LV_RES_OK) return res; + + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_arc"; + } + + return res; +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_arc.h b/bdk/libs/lvgl/lv_objx/lv_arc.h new file mode 100644 index 00000000..76231d87 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_arc.h @@ -0,0 +1,127 @@ +/** + * @file lv_arc.h + * + */ + + +#ifndef LV_ARC_H +#define LV_ARC_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_ARC != 0 + +#include "../lv_core/lv_obj.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of arc*/ +typedef struct { + /*New data for this type */ + lv_coord_t angle_start; + lv_coord_t angle_end; +} lv_arc_ext_t; + + +/*Styles*/ +enum { + LV_ARC_STYLE_MAIN, +}; +typedef uint8_t lv_arc_style_t; + + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a arc objects + * @param par pointer to an object, it will be the parent of the new arc + * @param copy pointer to a arc object, if not NULL then the new object will be copied from it + * @return pointer to the created arc + */ +lv_obj_t * lv_arc_create(lv_obj_t * par, const lv_obj_t * copy); + +/*====================== + * Add/remove functions + *=====================*/ + + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the start and end angles of an arc. 0 deg: bottom, 90 deg: right etc. + * @param arc pointer to an arc object + * @param start the start angle [0..360] + * @param end the end angle [0..360] + */ +void lv_arc_set_angles(lv_obj_t * arc, uint16_t start, uint16_t end); + +/** + * Set a style of a arc. + * @param arc pointer to arc object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_arc_set_style(lv_obj_t * arc, lv_arc_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the start angle of an arc. + * @param arc pointer to an arc object + * @return the start angle [0..360] + */ +uint16_t lv_arc_get_angle_start(lv_obj_t * arc); + +/** + * Get the end angle of an arc. + * @param arc pointer to an arc object + * @return the end angle [0..360] + */ +uint16_t lv_arc_get_angle_end(lv_obj_t * arc); + +/** + * Get style of a arc. + * @param arc pointer to arc object + * @param type which style should be get + * @return style pointer to the style + * */ +lv_style_t * lv_arc_get_style(const lv_obj_t * arc, lv_arc_style_t type); + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_ARC*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_ARC_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_bar.c b/bdk/libs/lvgl/lv_objx/lv_bar.c new file mode 100644 index 00000000..d83a6093 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_bar.c @@ -0,0 +1,429 @@ + + +/** + * @file lv_bar.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_bar.h" +#if USE_LV_BAR != 0 + +#include "../lv_draw/lv_draw.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_anim.h" +#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_bar_design(lv_obj_t * bar, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_design_func_t ancestor_design_f; +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a bar objects + * @param par pointer to an object, it will be the parent of the new bar + * @param copy pointer to a bar object, if not NULL then the new object will be copied from it + * @return pointer to the created bar + */ +lv_obj_t * lv_bar_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("lv_bar create started"); + + /*Create the ancestor basic object*/ + lv_obj_t * new_bar = lv_obj_create(par, copy); + lv_mem_assert(new_bar); + if(new_bar == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_bar); + if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_bar); + + /*Allocate the object type specific extended data*/ + lv_bar_ext_t * ext = lv_obj_allocate_ext_attr(new_bar, sizeof(lv_bar_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->min_value = 0; + ext->max_value = 100; + ext->cur_value = 0; + ext->sym = 0; + ext->style_indic = &lv_style_pretty_color; + + lv_obj_set_signal_func(new_bar, lv_bar_signal); + lv_obj_set_design_func(new_bar, lv_bar_design); + + /*Init the new bar object*/ + if(copy == NULL) { + lv_obj_set_click(new_bar, false); + lv_obj_set_size(new_bar, LV_DPI * 2, LV_DPI / 3); + lv_bar_set_value(new_bar, ext->cur_value); + + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_bar_set_style(new_bar, LV_BAR_STYLE_BG, th->bar.bg); + lv_bar_set_style(new_bar, LV_BAR_STYLE_INDIC, th->bar.indic); + } else { + lv_obj_set_style(new_bar, &lv_style_pretty); + } + } else { + lv_bar_ext_t * ext_copy = lv_obj_get_ext_attr(copy); + ext->min_value = ext_copy->min_value; + ext->max_value = ext_copy->max_value; + ext->cur_value = ext_copy->cur_value; + ext->style_indic = ext_copy->style_indic; + ext->sym = ext_copy->sym; + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_bar); + + lv_bar_set_value(new_bar, ext->cur_value); + } + + LV_LOG_INFO("bar created"); + + return new_bar; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new value on the bar + * @param bar pointer to a bar object + * @param value new value + */ +void lv_bar_set_value(lv_obj_t * bar, int16_t value) +{ + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + if(ext->cur_value == value) return; + + ext->cur_value = value > ext->max_value ? ext->max_value : value; + ext->cur_value = ext->cur_value < ext->min_value ? ext->min_value : ext->cur_value; + lv_obj_invalidate(bar); +} + +#if USE_LV_ANIMATION +/** + * Set a new value with animation on the bar + * @param bar pointer to a bar object + * @param value new value + * @param anim_time animation time in milliseconds + */ +void lv_bar_set_value_anim(lv_obj_t * bar, int16_t value, uint16_t anim_time) +{ + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + if(ext->cur_value == value) return; + + int16_t new_value; + new_value = value > ext->max_value ? ext->max_value : value; + new_value = new_value < ext->min_value ? ext->min_value : new_value; + + lv_anim_t a; + a.var = bar; + a.start = ext->cur_value; + a.end = new_value; + a.fp = (lv_anim_fp_t)lv_bar_set_value; + a.path = lv_anim_path_linear; + a.end_cb = NULL; + a.act_time = 0; + a.time = anim_time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + + lv_anim_create(&a); +} +#endif + + +/** + * Set minimum and the maximum values of a bar + * @param bar pointer to the bar object + * @param min minimum value + * @param max maximum value + */ +void lv_bar_set_range(lv_obj_t * bar, int16_t min, int16_t max) +{ + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + if(ext->min_value == min && ext->max_value == max) return; + + ext->max_value = max; + ext->min_value = min; + if(ext->cur_value > max) { + ext->cur_value = max; + lv_bar_set_value(bar, ext->cur_value); + } + if(ext->cur_value < min) { + ext->cur_value = min; + lv_bar_set_value(bar, ext->cur_value); + } + lv_obj_invalidate(bar); +} + +/** + * Make the bar symmetric to zero. The indicator will grow from zero instead of the minimum position. + * @param bar pointer to a bar object + * @param en true: enable disable symmetric behavior; false: disable + */ +void lv_bar_set_sym(lv_obj_t * bar, bool en) +{ + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + ext->sym = en ? 1 : 0; +} + +/** + * Set a style of a bar + * @param bar pointer to a bar object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_bar_set_style(lv_obj_t * bar, lv_bar_style_t type, lv_style_t * style) +{ + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + + switch(type) { + case LV_BAR_STYLE_BG: + lv_obj_set_style(bar, style); + break; + case LV_BAR_STYLE_INDIC: + ext->style_indic = style; + lv_obj_refresh_ext_size(bar); + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the value of a bar + * @param bar pointer to a bar object + * @return the value of the bar + */ +int16_t lv_bar_get_value(const lv_obj_t * bar) +{ + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + return ext->cur_value; +} + +/** + * Get the minimum value of a bar + * @param bar pointer to a bar object + * @return the minimum value of the bar + */ +int16_t lv_bar_get_min_value(const lv_obj_t * bar) +{ + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + return ext->min_value; +} + +/** + * Get the maximum value of a bar + * @param bar pointer to a bar object + * @return the maximum value of the bar + */ +int16_t lv_bar_get_max_value(const lv_obj_t * bar) +{ + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + return ext->max_value; +} + +/** + * Get whether the bar is symmetric or not. + * @param bar pointer to a bar object + * @return true: symmetric is enabled; false: disable + */ +bool lv_bar_get_sym(lv_obj_t * bar) +{ + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + return ext->sym ? true : false; +} + +/** + * Get a style of a bar + * @param bar pointer to a bar object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_bar_get_style(const lv_obj_t * bar, lv_bar_style_t type) +{ + lv_style_t * style = NULL; + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + + switch(type) { + case LV_BAR_STYLE_BG: + style = lv_obj_get_style(bar); + break; + case LV_BAR_STYLE_INDIC: + style = ext->style_indic; + break; + default: + style = NULL; + break; + } + + return style; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the bars + * @param bar pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_bar_design(lv_obj_t * bar, const lv_area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + /*Return false if the object is not covers the mask area*/ + return ancestor_design_f(bar, mask, mode); + } else if(mode == LV_DESIGN_DRAW_MAIN) { + lv_opa_t opa_scale = lv_obj_get_opa_scale(bar); + +#if USE_LV_GROUP == 0 + ancestor_design_f(bar, mask, mode); +#else + /* Draw the borders later if the bar is focused. + * At value = 100% the indicator can cover to whole background and the focused style won't be visible*/ + if(lv_obj_is_focused(bar)) { + lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG); + lv_style_t style_tmp; + lv_style_copy(&style_tmp, style_bg); + style_tmp.body.border.width = 0; + lv_draw_rect(&bar->coords, mask, &style_tmp, opa_scale); + } else { + ancestor_design_f(bar, mask, mode); + } +#endif + lv_bar_ext_t * ext = lv_obj_get_ext_attr(bar); + + if(ext->cur_value != ext->min_value || ext->sym) { + lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC); + lv_area_t indic_area; + lv_area_copy(&indic_area, &bar->coords); + indic_area.x1 += style_indic->body.padding.hor; + indic_area.x2 -= style_indic->body.padding.hor; + indic_area.y1 += style_indic->body.padding.ver; + indic_area.y2 -= style_indic->body.padding.ver; + + lv_coord_t w = lv_area_get_width(&indic_area); + lv_coord_t h = lv_area_get_height(&indic_area); + + if(w >= h) { + /*Horizontal*/ + indic_area.x2 = (int32_t)((int32_t)w * (ext->cur_value - ext->min_value)) / (ext->max_value - ext->min_value); + indic_area.x2 = indic_area.x1 + indic_area.x2 - 1; + + if(ext->sym && ext->min_value < 0 && ext->max_value > 0) { + /*Calculate the coordinate of the zero point*/ + lv_coord_t zero; + zero = indic_area.x1 + (-ext->min_value * w) / (ext->max_value - ext->min_value); + if(indic_area.x2 > zero) indic_area.x1 = zero; + else { + indic_area.x1 = indic_area.x2; + indic_area.x2 = zero; + } + } + } else { + indic_area.y1 = (int32_t)((int32_t)h * (ext->cur_value - ext->min_value)) / (ext->max_value - ext->min_value); + indic_area.y1 = indic_area.y2 - indic_area.y1 + 1; + + if(ext->sym && ext->min_value < 0 && ext->max_value > 0) { + /*Calculate the coordinate of the zero point*/ + lv_coord_t zero; + zero = indic_area.y2 - (-ext->min_value * h) / (ext->max_value - ext->min_value); + if(indic_area.y1 < zero) indic_area.y2 = zero; + else { + indic_area.y2 = indic_area.y1; + indic_area.y1 = zero; + } + } + } + + + /*Draw the indicator*/ + lv_draw_rect(&indic_area, mask, style_indic, opa_scale); + } + } else if(mode == LV_DESIGN_DRAW_POST) { +#if USE_LV_GROUP + /*Draw the border*/ + if(lv_obj_is_focused(bar)) { + lv_opa_t opa_scale = lv_obj_get_opa_scale(bar); + lv_style_t * style_bg = lv_bar_get_style(bar, LV_BAR_STYLE_BG); + lv_style_t style_tmp; + lv_style_copy(&style_tmp, style_bg); + style_tmp.body.empty = 1; + style_tmp.body.shadow.width = 0; + lv_draw_rect(&bar->coords, mask, &style_tmp, opa_scale); + } +#endif + + } + return true; +} + +/** + * Signal function of the bar + * @param bar pointer to a bar object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_bar_signal(lv_obj_t * bar, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(bar, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_REFR_EXT_SIZE) { + lv_style_t * style_indic = lv_bar_get_style(bar, LV_BAR_STYLE_INDIC); + if(style_indic->body.shadow.width > bar->ext_size) bar->ext_size = style_indic->body.shadow.width; + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_bar"; + } + + return res; +} + + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_bar.h b/bdk/libs/lvgl/lv_objx/lv_bar.h new file mode 100644 index 00000000..21903842 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_bar.h @@ -0,0 +1,160 @@ +/** + * @file lv_bar.h + * + */ + +#ifndef LV_BAR_H +#define LV_BAR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_BAR != 0 + +#include "../lv_core/lv_obj.h" +#include "lv_cont.h" +#include "lv_btn.h" +#include "lv_label.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Data of bar*/ +typedef struct +{ + /*No inherited ext*/ /*Ext. of ancestor*/ + /*New data for this type */ + int16_t cur_value; /*Current value of the bar*/ + int16_t min_value; /*Minimum value of the bar*/ + int16_t max_value; /*Maximum value of the bar*/ + uint8_t sym :1; /*Symmetric: means the center is around zero value*/ + lv_style_t *style_indic; /*Style of the indicator*/ +} lv_bar_ext_t; + +enum { + LV_BAR_STYLE_BG, + LV_BAR_STYLE_INDIC, +}; +typedef uint8_t lv_bar_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a bar objects + * @param par pointer to an object, it will be the parent of the new bar + * @param copy pointer to a bar object, if not NULL then the new object will be copied from it + * @return pointer to the created bar + */ +lv_obj_t * lv_bar_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new value on the bar + * @param bar pointer to a bar object + * @param value new value + */ +void lv_bar_set_value(lv_obj_t * bar, int16_t value); + +/** + * Set a new value with animation on the bar + * @param bar pointer to a bar object + * @param value new value + * @param anim_time animation time in milliseconds + */ +void lv_bar_set_value_anim(lv_obj_t * bar, int16_t value, uint16_t anim_time); + + +/** + * Set minimum and the maximum values of a bar + * @param bar pointer to the bar object + * @param min minimum value + * @param max maximum value + */ +void lv_bar_set_range(lv_obj_t * bar, int16_t min, int16_t max); + +/** + * Make the bar symmetric to zero. The indicator will grow from zero instead of the minimum position. + * @param bar pointer to a bar object + * @param en true: enable disable symmetric behavior; false: disable + */ +void lv_bar_set_sym(lv_obj_t * bar, bool en); + +/** + * Set a style of a bar + * @param bar pointer to a bar object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_bar_set_style(lv_obj_t *bar, lv_bar_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the value of a bar + * @param bar pointer to a bar object + * @return the value of the bar + */ +int16_t lv_bar_get_value(const lv_obj_t * bar); + +/** + * Get the minimum value of a bar + * @param bar pointer to a bar object + * @return the minimum value of the bar + */ +int16_t lv_bar_get_min_value(const lv_obj_t * bar); + +/** + * Get the maximum value of a bar + * @param bar pointer to a bar object + * @return the maximum value of the bar + */ +int16_t lv_bar_get_max_value(const lv_obj_t * bar); + +/** + * Get whether the bar is symmetric or not. + * @param bar pointer to a bar object + * @return true: symmetric is enabled; false: disable + */ +bool lv_bar_get_sym(lv_obj_t * bar); + +/** + * Get a style of a bar + * @param bar pointer to a bar object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_bar_get_style(const lv_obj_t *bar, lv_bar_style_t type); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_BAR*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_BAR_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_btn.c b/bdk/libs/lvgl/lv_objx/lv_btn.c new file mode 100644 index 00000000..f46250cb --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_btn.c @@ -0,0 +1,763 @@ +/** + * @file lv_btn.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_btn.h" +#if USE_LV_BTN != 0 + +#include +#include "../lv_core/lv_group.h" +#include "../lv_draw/lv_draw.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_area.h" +#include "../lv_misc/lv_color.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ +#define LV_BTN_INK_VALUE_MAX 256 +#define LV_BTN_INK_VALUE_MAX_SHIFT 8 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_btn_design(lv_obj_t * btn, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param); + +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT +static void lv_btn_ink_effect_anim(lv_obj_t * btn, int32_t val); +static void lv_btn_ink_effect_anim_ready(void * p); +#endif + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_design_func_t ancestor_design; + +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT +static lv_coord_t ink_act_value; +static lv_obj_t * ink_obj; +static lv_btn_state_t ink_bg_state; +static lv_btn_state_t ink_top_state; +static bool ink_ready; +static bool ink_playback; +static lv_point_t ink_point; +#endif + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a button objects + * @param par pointer to an object, it will be the parent of the new button + * @param copy pointer to a button object, if not NULL then the new object will be copied from it + * @return pointer to the created button + */ +lv_obj_t * lv_btn_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("button create started"); + + lv_obj_t * new_btn; + + new_btn = lv_cont_create(par, copy); + lv_mem_assert(new_btn); + if(new_btn == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_btn); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_btn); + + /*Allocate the extended data*/ + lv_btn_ext_t * ext = lv_obj_allocate_ext_attr(new_btn, sizeof(lv_btn_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->state = LV_BTN_STATE_REL; + + ext->actions[LV_BTN_ACTION_PR] = NULL; + ext->actions[LV_BTN_ACTION_CLICK] = NULL; + ext->actions[LV_BTN_ACTION_LONG_PR] = NULL; + ext->actions[LV_BTN_ACTION_LONG_PR_REPEAT] = NULL; + + ext->styles[LV_BTN_STATE_REL] = &lv_style_btn_rel; + ext->styles[LV_BTN_STATE_PR] = &lv_style_btn_pr; + ext->styles[LV_BTN_STATE_TGL_REL] = &lv_style_btn_tgl_rel; + ext->styles[LV_BTN_STATE_TGL_PR] = &lv_style_btn_tgl_pr; + ext->styles[LV_BTN_STATE_INA] = &lv_style_btn_ina; + + ext->long_pr_action_executed = 0; + ext->toggle = 0; + ext->idx = 0; +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + ext->ink_in_time = 0; + ext->ink_wait_time = 0; + ext->ink_out_time = 0; +#endif + + lv_obj_set_signal_func(new_btn, lv_btn_signal); + lv_obj_set_design_func(new_btn, lv_btn_design); + + /*If no copy do the basic initialization*/ + if(copy == NULL) { + /*Set layout if the button is not a screen*/ + if(par != NULL) { + lv_btn_set_layout(new_btn, LV_LAYOUT_CENTER); + } + + lv_obj_set_click(new_btn, true); /*Be sure the button is clickable*/ + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_btn_set_style(new_btn, LV_BTN_STYLE_REL, th->btn.rel); + lv_btn_set_style(new_btn, LV_BTN_STYLE_PR, th->btn.pr); + lv_btn_set_style(new_btn, LV_BTN_STYLE_TGL_REL, th->btn.tgl_rel); + lv_btn_set_style(new_btn, LV_BTN_STYLE_TGL_PR, th->btn.tgl_pr); + lv_btn_set_style(new_btn, LV_BTN_STYLE_INA, th->btn.ina); + } else { + lv_obj_set_style(new_btn, ext->styles[LV_BTN_STATE_REL]); + } + } + /*Copy 'copy'*/ + else { + lv_btn_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->state = copy_ext->state; + ext->toggle = copy_ext->toggle; +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + ext->ink_in_time = copy_ext->ink_in_time; + ext->ink_wait_time = copy_ext->ink_wait_time; + ext->ink_out_time = copy_ext->ink_out_time; +#endif + memcpy(ext->actions, copy_ext->actions, sizeof(ext->actions)); + memcpy(ext->styles, copy_ext->styles, sizeof(ext->styles)); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_btn); + } + + LV_LOG_INFO("button created"); + + return new_btn; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Enable the toggled states + * @param btn pointer to a button object + * @param tgl true: enable toggled states, false: disable + */ +void lv_btn_set_toggle(lv_obj_t * btn, bool tgl) +{ + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + + ext->toggle = tgl != false ? 1 : 0; +} + +/** + * Set the state of the button + * @param btn pointer to a button object + * @param state the new state of the button (from lv_btn_state_t enum) + */ +void lv_btn_set_state(lv_obj_t * btn, lv_btn_state_t state) +{ + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + if(ext->state != state) { + ext->state = state; + lv_obj_set_style(btn, ext->styles[state]); + } +} + +/** + * Toggle the state of the button (ON->OFF, OFF->ON) + * @param btn pointer to a button object + */ +void lv_btn_toggle(lv_obj_t * btn) +{ + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + switch(ext->state) { + case LV_BTN_STATE_REL: + lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL); + break; + case LV_BTN_STATE_PR: + lv_btn_set_state(btn, LV_BTN_STATE_TGL_PR); + break; + case LV_BTN_STATE_TGL_REL: + lv_btn_set_state(btn, LV_BTN_STATE_REL); + break; + case LV_BTN_STATE_TGL_PR: + lv_btn_set_state(btn, LV_BTN_STATE_PR); + break; + default: + break; + } +} + +/** + * Set a function to call when a button event happens + * @param btn pointer to a button object + * @param action type of event form 'lv_action_t' (press, release, long press, long press repeat) + */ +void lv_btn_set_action(lv_obj_t * btn, lv_btn_action_t type, lv_action_t action) +{ + if(type >= LV_BTN_ACTION_NUM) return; + + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + ext->actions[type] = action; +} + +/** + * Set time of the ink effect (draw a circle on click to animate in the new state) + * @param btn pointer to a button object + * @param time the time of the ink animation + */ +void lv_btn_set_ink_in_time(lv_obj_t * btn, uint16_t time) +{ +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + ext->ink_in_time = time; +#else + (void)btn; /*Unused*/ + (void)time; /*Unused*/ + LV_LOG_WARN("`lv_btn_set_ink_ink_time` has no effect if LV_BTN_INK_EFEFCT or USE_LV_ANIMATION is disabled") +#endif +} + +/** + * Set the wait time before the ink disappears + * @param btn pointer to a button object + * @param time the time of the ink animation + */ +void lv_btn_set_ink_wait_time(lv_obj_t * btn, uint16_t time) +{ + +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + ext->ink_wait_time = time; +#else + (void)btn; /*Unused*/ + (void)time; /*Unused*/ + LV_LOG_WARN("`lv_btn_set_ink_wait_time` has no effect if LV_BTN_INK_EFEFCT or USE_LV_ANIMATION is disabled") +#endif +} + +/** + * Set time of the ink out effect (animate to the released state) + * @param btn pointer to a button object + * @param time the time of the ink animation + */ +void lv_btn_set_ink_out_time(lv_obj_t * btn, uint16_t time) +{ +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + ext->ink_out_time = time; +#else + (void)btn; /*Unused*/ + (void)time; /*Unused*/ + LV_LOG_WARN("`lv_btn_set_ink_out_time` has no effect if LV_BTN_INK_EFEFCT or USE_LV_ANIMATION is disabled") +#endif +} + +/** + * Set a style of a button + * @param btn pointer to a button object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_btn_set_style(lv_obj_t * btn, lv_btn_style_t type, lv_style_t * style) +{ + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + + switch(type) { + case LV_BTN_STYLE_REL: + ext->styles[LV_BTN_STATE_REL] = style; + break; + case LV_BTN_STYLE_PR: + ext->styles[LV_BTN_STATE_PR] = style; + break; + case LV_BTN_STYLE_TGL_REL: + ext->styles[LV_BTN_STATE_TGL_REL] = style; + break; + case LV_BTN_STYLE_TGL_PR: + ext->styles[LV_BTN_STATE_TGL_PR] = style; + break; + case LV_BTN_STYLE_INA: + ext->styles[LV_BTN_STATE_INA] = style; + break; + } + + /*Refresh the object with the new style*/ + lv_obj_set_style(btn, ext->styles[ext->state]); +} + + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the current state of the button + * @param btn pointer to a button object + * @return the state of the button (from lv_btn_state_t enum) + */ +lv_btn_state_t lv_btn_get_state(const lv_obj_t * btn) +{ + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + return ext->state; +} + +/** + * Get the toggle enable attribute of the button + * @param btn pointer to a button object + * @return ture: toggle enabled, false: disabled + */ +bool lv_btn_get_toggle(const lv_obj_t * btn) +{ + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + + return ext->toggle != 0 ? true : false; +} + +/** + * Get the release action of a button + * @param btn pointer to a button object + * @return pointer to the release action function + */ +lv_action_t lv_btn_get_action(const lv_obj_t * btn, lv_btn_action_t type) +{ + if(type >= LV_BTN_ACTION_NUM) return NULL; + + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + return ext->actions[type]; +} + +/** + * Get time of the ink in effect (draw a circle on click to animate in the new state) + * @param btn pointer to a button object + * @return the time of the ink animation + */ +uint16_t lv_btn_get_ink_in_time(const lv_obj_t * btn) +{ +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + return ext->ink_in_time; +#else + (void)btn; /*Unused*/ + return 0; +#endif +} + + +/** + * Get the wait time before the ink disappears + * @param btn pointer to a button object + * @return the time of the ink animation + */ +uint16_t lv_btn_get_ink_wait_time(const lv_obj_t * btn) +{ +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + return ext->ink_wait_time; +#else + (void)btn; /*Unused*/ + return 0; +#endif +} +/** + * Get time of the ink out effect (animate to the releases state) + * @param btn pointer to a button object + * @return the time of the ink animation + */ +uint16_t lv_btn_get_ink_out_time(const lv_obj_t * btn) +{ +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + return ext->ink_in_time; +#else + (void)btn; /*Unused*/ + return 0; +#endif +} + +/** + * Get a style of a button + * @param btn pointer to a button object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_btn_get_style(const lv_obj_t * btn, lv_btn_style_t type) +{ + lv_style_t * style = NULL; + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + + switch(type) { + case LV_BTN_STYLE_REL: + style = ext->styles[LV_BTN_STATE_REL]; + break; + case LV_BTN_STYLE_PR: + style = ext->styles[LV_BTN_STATE_PR]; + break; + case LV_BTN_STYLE_TGL_REL: + style = ext->styles[LV_BTN_STATE_TGL_REL]; + break; + case LV_BTN_STYLE_TGL_PR: + style = ext->styles[LV_BTN_STATE_TGL_PR]; + break; + case LV_BTN_STYLE_INA: + style = ext->styles[LV_BTN_STATE_INA]; + break; + default: + style = NULL; + break; + } + + return style; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +/** + * Handle the drawing related tasks of the drop down lists + * @param btn pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_btn_design(lv_obj_t * btn, const lv_area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } else if(mode == LV_DESIGN_DRAW_MAIN) { + +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + if(btn != ink_obj) { + ancestor_design(btn, mask, mode); + } else { + lv_opa_t opa_scale = lv_obj_get_opa_scale(btn); + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + + /*Draw the normal button*/ + if(ink_playback == false) { + lv_style_t style_tmp; + lv_style_copy(&style_tmp, ext->styles[ink_bg_state]); + style_tmp.body.shadow.width = ext->styles[ink_top_state]->body.shadow.width; + lv_draw_rect(&btn->coords, mask, &style_tmp, opa_scale); + + lv_coord_t w = lv_obj_get_width(btn); + lv_coord_t h = lv_obj_get_height(btn); + lv_coord_t r_max = LV_MATH_MIN(w, h) / 2; + + /*In the first part of the animation increase the size of the circle (ink effect) */ + lv_area_t cir_area; + + lv_coord_t coord_state = ink_act_value < LV_BTN_INK_VALUE_MAX / 2 ? ink_act_value : LV_BTN_INK_VALUE_MAX / 2; + lv_point_t p_act; + p_act.x = ink_point.x; + p_act.y = ink_point.y; + lv_coord_t x_err = (btn->coords.x1 + w / 2) - p_act.x; + lv_coord_t y_err = (btn->coords.y1 + h / 2) - p_act.y; + + p_act.x += (x_err * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1); + p_act.y += (y_err * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1); + + lv_coord_t half_side = LV_MATH_MAX(w, h) / 2; + cir_area.x1 = p_act.x - ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1)); + cir_area.y1 = p_act.y - ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1)); + cir_area.x2 = p_act.x + ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1)); + cir_area.y2 = p_act.y + ((half_side * coord_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1)); + + lv_area_intersect(&cir_area, &btn->coords, &cir_area); /*Limit the area. (It might be too big on the smaller side)*/ + + /*In the second part animate the radius. Circle -> body.radius*/ + lv_coord_t r_state = ink_act_value > LV_BTN_INK_VALUE_MAX / 2 ? ink_act_value - LV_BTN_INK_VALUE_MAX / 2 : 0; + + lv_style_copy(&style_tmp, ext->styles[ink_top_state]); + style_tmp.body.radius = r_max + (((ext->styles[ink_bg_state]->body.radius - r_max) * r_state) >> (LV_BTN_INK_VALUE_MAX_SHIFT - 1)); + style_tmp.body.border.width = 0; + + /*Draw the circle*/ + lv_draw_rect(&cir_area, mask, &style_tmp, opa_scale); + } else { + lv_style_t res; + lv_style_copy(&res, ext->styles[ink_bg_state]); + lv_style_mix(ext->styles[ink_bg_state], ext->styles[ink_top_state], &res, ink_act_value); + lv_draw_rect(&btn->coords, mask, &res, opa_scale); + + } + } +#else + ancestor_design(btn, mask, mode); +#endif + } else if(mode == LV_DESIGN_DRAW_POST) { + ancestor_design(btn, mask, mode); + } + + return true; +} + +/** + * Signal function of the button + * @param btn pointer to a button object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(btn, sign, param); + if(res != LV_RES_OK) return res; + + lv_btn_ext_t * ext = lv_obj_get_ext_attr(btn); + lv_btn_state_t state = lv_btn_get_state(btn); + bool tgl = lv_btn_get_toggle(btn); + + if(sign == LV_SIGNAL_PRESSED) { + /*Refresh the state*/ + if(ext->state == LV_BTN_STATE_REL) { + lv_btn_set_state(btn, LV_BTN_STATE_PR); +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + ink_bg_state = LV_BTN_STATE_REL; + ink_top_state = LV_BTN_STATE_PR; +#endif + } else if(ext->state == LV_BTN_STATE_TGL_REL) { + lv_btn_set_state(btn, LV_BTN_STATE_TGL_PR); +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + ink_bg_state = LV_BTN_STATE_TGL_REL; + ink_top_state = LV_BTN_STATE_TGL_PR; +#endif + } + + ext->long_pr_action_executed = 0; + +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + /*Forget the old inked button*/ + if(ink_obj != NULL && ink_obj != btn) { + lv_anim_del(ink_obj, (lv_anim_fp_t)lv_btn_ink_effect_anim); + lv_obj_invalidate(ink_obj); + ink_obj = NULL; + } + /*Save the new data for inking and start it's animation if enabled*/ + if(ext->ink_in_time > 0) { + ink_obj = btn; + ink_playback = false; + ink_ready = false; + lv_indev_get_point(lv_indev_get_act(), &ink_point); + + lv_anim_t a; + a.var = btn; + a.start = 0; + a.end = LV_BTN_INK_VALUE_MAX; + a.fp = (lv_anim_fp_t)lv_btn_ink_effect_anim; + a.path = lv_anim_path_linear; + a.end_cb = lv_btn_ink_effect_anim_ready; + a.act_time = 0; + a.time = ext->ink_in_time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); + } +#endif + /*Call the press action, 'param' is the caller indev_proc*/ + if(ext->actions[LV_BTN_ACTION_PR] && state != LV_BTN_STATE_INA) { + res = ext->actions[LV_BTN_ACTION_PR](btn); + } + } else if(sign == LV_SIGNAL_PRESS_LOST) { + /*Refresh the state*/ + if(ext->state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL); + else if(ext->state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL); + } else if(sign == LV_SIGNAL_PRESSING) { + /*When the button begins to drag revert pressed states to released*/ + if(lv_indev_is_dragging(param) != false) { + if(ext->state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL); + else if(ext->state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL); + } + } else if(sign == LV_SIGNAL_RELEASED) { + /*If not dragged and it was not long press action then + *change state and run the action*/ + if(lv_indev_is_dragging(param) == false && ext->long_pr_action_executed == 0) { + if(ext->state == LV_BTN_STATE_PR && tgl == false) { + lv_btn_set_state(btn, LV_BTN_STATE_REL); + } else if(ext->state == LV_BTN_STATE_TGL_PR && tgl == false) { + lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL); + } else if(ext->state == LV_BTN_STATE_PR && tgl == true) { + lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL); + } else if(ext->state == LV_BTN_STATE_TGL_PR && tgl == true) { + lv_btn_set_state(btn, LV_BTN_STATE_REL); + } + + if(ext->actions[LV_BTN_ACTION_CLICK] && state != LV_BTN_STATE_INA) { + res = ext->actions[LV_BTN_ACTION_CLICK](btn); + } + } else { /*If dragged change back the state*/ + if(ext->state == LV_BTN_STATE_PR) { + lv_btn_set_state(btn, LV_BTN_STATE_REL); + } else if(ext->state == LV_BTN_STATE_TGL_PR) { + lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL); + } + } + +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + /*Draw the toggled state in the inking instead*/ + if(ext->toggle) { + ink_top_state = ext->state; + } + /*If not a toggle button and the "IN" inking is ready then start an "OUT" inking*/ + else if(ink_ready && ext->ink_out_time > 0) { + ink_obj = btn; + ink_playback = true; /*It is the playback. If not set `lv_btn_ink_effect_anim_ready` will start its own playback*/ + lv_indev_get_point(lv_indev_get_act(), &ink_point); + + lv_anim_t a; + a.var = ink_obj; + a.start = LV_BTN_INK_VALUE_MAX; + a.end = 0; + a.fp = (lv_anim_fp_t)lv_btn_ink_effect_anim; + a.path = lv_anim_path_linear; + a.end_cb = lv_btn_ink_effect_anim_ready; + a.act_time = 0; + a.time = ext->ink_out_time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); + } +#endif + } else if(sign == LV_SIGNAL_LONG_PRESS) { + if(ext->actions[LV_BTN_ACTION_LONG_PR] && state != LV_BTN_STATE_INA) { + ext->long_pr_action_executed = 1; + res = ext->actions[LV_BTN_ACTION_LONG_PR](btn); + } + } else if(sign == LV_SIGNAL_LONG_PRESS_REP) { + if(ext->actions[LV_BTN_ACTION_LONG_PR_REPEAT] && state != LV_BTN_STATE_INA) { + res = ext->actions[LV_BTN_ACTION_LONG_PR_REPEAT](btn); + } + } else if(sign == LV_SIGNAL_CONTROLL) { + char c = *((char *)param); + if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_UP) { + if(lv_btn_get_toggle(btn) != false) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL); + if(ext->actions[LV_BTN_ACTION_CLICK] && lv_btn_get_state(btn) != LV_BTN_STATE_INA) { + res = ext->actions[LV_BTN_ACTION_CLICK](btn); + } + } else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_DOWN) { + if(lv_btn_get_toggle(btn) != false) lv_btn_set_state(btn, LV_BTN_STATE_REL); + if(ext->actions[LV_BTN_ACTION_CLICK] && lv_btn_get_state(btn) != LV_BTN_STATE_INA) { + res = ext->actions[LV_BTN_ACTION_CLICK](btn); + } + } else if(c == LV_GROUP_KEY_ENTER) { + if(!ext->long_pr_action_executed) { + if(lv_btn_get_toggle(btn)) { + if(state == LV_BTN_STATE_REL || state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL); + else if(state == LV_BTN_STATE_TGL_REL || state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL); + } else { + if(state == LV_BTN_STATE_REL || state == LV_BTN_STATE_PR) lv_btn_set_state(btn, LV_BTN_STATE_REL); + else if(state == LV_BTN_STATE_TGL_REL || state == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn, LV_BTN_STATE_TGL_REL); + } + if(ext->actions[LV_BTN_ACTION_CLICK] && state != LV_BTN_STATE_INA) { + res = ext->actions[LV_BTN_ACTION_CLICK](btn); + } + } + if(res != LV_RES_INV) { + ext->long_pr_action_executed = 0; + } + } + } else if(sign == LV_SIGNAL_CLEANUP) { +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + if(btn == ink_obj) { + lv_anim_del(ink_obj, (lv_anim_fp_t)lv_btn_ink_effect_anim); + ink_obj = NULL; + } +#endif + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_btn"; + } + + return res; +} + +#if USE_LV_ANIMATION && LV_BTN_INK_EFFECT + +/** + * The animator function of inking. CAlled to increase the radius of ink + * @param btn pointer to the animated button + * @param val the new radius + */ +static void lv_btn_ink_effect_anim(lv_obj_t * btn, int32_t val) +{ + if(btn) { + ink_act_value = val; + lv_obj_invalidate(btn); + } +} + +/** + * Called to clean up when the ink animation is ready + * @param p unused + */ +static void lv_btn_ink_effect_anim_ready(void * p) +{ + (void) p; /*Unused*/ + + lv_btn_ext_t * ext = lv_obj_get_ext_attr(ink_obj); + lv_btn_state_t state = lv_btn_get_state(ink_obj); + + lv_obj_invalidate(ink_obj); + ink_ready = true; + + if((state == LV_BTN_STATE_REL || state == LV_BTN_STATE_TGL_REL) && ext->toggle == 0 && ink_playback == false) { + lv_anim_t a; + a.var = ink_obj; + a.start = LV_BTN_INK_VALUE_MAX; + a.end = 0; + a.fp = (lv_anim_fp_t)lv_btn_ink_effect_anim; + a.path = lv_anim_path_linear; + a.end_cb = lv_btn_ink_effect_anim_ready; + a.act_time = -ext->ink_wait_time; + a.time = ext->ink_out_time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); + + ink_playback = true; + } else { + ink_obj = NULL; + } +} +#endif /*USE_LV_ANIMATION*/ + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_btn.h b/bdk/libs/lvgl/lv_objx/lv_btn.h new file mode 100644 index 00000000..3a48b622 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_btn.h @@ -0,0 +1,280 @@ +/** + * @file lv_btn.h + * + */ + +#ifndef LV_BTN_H +#define LV_BTN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_BTN != 0 + +/*Testing of dependencies*/ +#if USE_LV_CONT == 0 +#error "lv_btn: lv_cont is required. Enable it in lv_conf.h (USE_LV_CONT 1) " +#endif + +#include "lv_cont.h" +#include "../lv_core/lv_indev.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/* Button states + * It can be used not only by buttons but other button-like objects too*/ +enum +{ + LV_BTN_STATE_REL, + LV_BTN_STATE_PR, + LV_BTN_STATE_TGL_REL, + LV_BTN_STATE_TGL_PR, + LV_BTN_STATE_INA, + LV_BTN_STATE_NUM, +}; +typedef uint8_t lv_btn_state_t; + +enum +{ + LV_BTN_ACTION_CLICK, + LV_BTN_ACTION_PR, + LV_BTN_ACTION_LONG_PR, + LV_BTN_ACTION_LONG_PR_REPEAT, + LV_BTN_ACTION_NUM, +}; +typedef uint8_t lv_btn_action_t; + + +/*Data of button*/ +typedef struct +{ + lv_cont_ext_t cont; /*Ext. of ancestor*/ + /*New data for this type */ + lv_action_t actions[LV_BTN_ACTION_NUM]; + lv_style_t * styles[LV_BTN_STATE_NUM]; /*Styles in each state*/ + lv_btn_state_t state; /*Current state of the button from 'lv_btn_state_t' enum*/ + int idx; +#if LV_BTN_INK_EFFECT + uint16_t ink_in_time; /*[ms] Time of ink fill effect (0: disable ink effect)*/ + uint16_t ink_wait_time; /*[ms] Wait before the ink disappears */ + uint16_t ink_out_time; /*[ms] Time of ink disappearing*/ +#endif + uint8_t toggle :1; /*1: Toggle enabled*/ + uint8_t long_pr_action_executed :1; /*1: Long press action executed (Handled by the library)*/ +} lv_btn_ext_t; + +/*Styles*/ +enum { + LV_BTN_STYLE_REL, + LV_BTN_STYLE_PR, + LV_BTN_STYLE_TGL_REL, + LV_BTN_STYLE_TGL_PR, + LV_BTN_STYLE_INA, +}; +typedef uint8_t lv_btn_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a button objects + * @param par pointer to an object, it will be the parent of the new button + * @param copy pointer to a button object, if not NULL then the new object will be copied from it + * @return pointer to the created button + */ +lv_obj_t * lv_btn_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Enable the toggled states. On release the button will change from/to toggled state. + * @param btn pointer to a button object + * @param tgl true: enable toggled states, false: disable + */ +void lv_btn_set_toggle(lv_obj_t * btn, bool tgl); + +/** + * Set the state of the button + * @param btn pointer to a button object + * @param state the new state of the button (from lv_btn_state_t enum) + */ +void lv_btn_set_state(lv_obj_t * btn, lv_btn_state_t state); + +/** + * Toggle the state of the button (ON->OFF, OFF->ON) + * @param btn pointer to a button object + */ +void lv_btn_toggle(lv_obj_t * btn); + +/** + * Set a function to call when a button event happens + * @param btn pointer to a button object + * @param action type of event form 'lv_action_t' (press, release, long press, long press repeat) + */ +void lv_btn_set_action(lv_obj_t * btn, lv_btn_action_t type, lv_action_t action); + +/** + * Set the layout on a button + * @param btn pointer to a button object + * @param layout a layout from 'lv_cont_layout_t' + */ +static inline void lv_btn_set_layout(lv_obj_t * btn, lv_layout_t layout) +{ + lv_cont_set_layout(btn, layout); +} + +/** + * Enable the horizontal or vertical fit. + * The button size will be set to involve the children horizontally or vertically. + * @param btn pointer to a button object + * @param hor_en true: enable the horizontal fit + * @param ver_en true: enable the vertical fit + */ +static inline void lv_btn_set_fit(lv_obj_t * btn, bool hor_en, bool ver_en) +{ + lv_cont_set_fit(btn, hor_en, ver_en); +} + +/** + * Set time of the ink effect (draw a circle on click to animate in the new state) + * @param btn pointer to a button object + * @param time the time of the ink animation + */ +void lv_btn_set_ink_in_time(lv_obj_t * btn, uint16_t time); + +/** + * Set the wait time before the ink disappears + * @param btn pointer to a button object + * @param time the time of the ink animation + */ +void lv_btn_set_ink_wait_time(lv_obj_t * btn, uint16_t time); + +/** + * Set time of the ink out effect (animate to the released state) + * @param btn pointer to a button object + * @param time the time of the ink animation + */ +void lv_btn_set_ink_out_time(lv_obj_t * btn, uint16_t time); + +/** + * Set a style of a button. + * @param btn pointer to button object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_btn_set_style(lv_obj_t * btn, lv_btn_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the current state of the button + * @param btn pointer to a button object + * @return the state of the button (from lv_btn_state_t enum) + */ +lv_btn_state_t lv_btn_get_state(const lv_obj_t * btn); + +/** + * Get the toggle enable attribute of the button + * @param btn pointer to a button object + * @return ture: toggle enabled, false: disabled + */ +bool lv_btn_get_toggle(const lv_obj_t * btn); + +/** + * Get the release action of a button + * @param btn pointer to a button object + * @return pointer to the release action function + */ +lv_action_t lv_btn_get_action(const lv_obj_t * btn, lv_btn_action_t type); + +/** + * Get the layout of a button + * @param btn pointer to button object + * @return the layout from 'lv_cont_layout_t' + */ +static inline lv_layout_t lv_btn_get_layout(const lv_obj_t * btn) +{ + return lv_cont_get_layout(btn); +} + +/** + * Get horizontal fit enable attribute of a button + * @param btn pointer to a button object + * @return true: horizontal fit is enabled; false: disabled + */ +static inline bool lv_btn_get_hor_fit(const lv_obj_t * btn) +{ + return lv_cont_get_hor_fit(btn); +} + +/** + * Get vertical fit enable attribute of a container + * @param btn pointer to a button object + * @return true: vertical fit is enabled; false: disabled + */ +static inline bool lv_btn_get_ver_fit(const lv_obj_t * btn) +{ + return lv_cont_get_ver_fit(btn); +} + +/** + * Get time of the ink in effect (draw a circle on click to animate in the new state) + * @param btn pointer to a button object + * @return the time of the ink animation + */ +uint16_t lv_btn_get_ink_in_time(const lv_obj_t * btn); + +/** + * Get the wait time before the ink disappears + * @param btn pointer to a button object + * @return the time of the ink animation + */ +uint16_t lv_btn_get_ink_wait_time(const lv_obj_t * btn); + +/** + * Get time of the ink out effect (animate to the releases state) + * @param btn pointer to a button object + * @return the time of the ink animation + */ +uint16_t lv_btn_get_ink_out_time(const lv_obj_t * btn); + +/** + * Get style of a button. + * @param btn pointer to button object + * @param type which style should be get + * @return style pointer to the style + * */ +lv_style_t * lv_btn_get_style(const lv_obj_t * btn, lv_btn_style_t type); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_BUTTON*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_BTN_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_btnm.c b/bdk/libs/lvgl/lv_objx/lv_btnm.c new file mode 100644 index 00000000..68d04e19 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_btnm.c @@ -0,0 +1,881 @@ +/** + * @file lv_btnm.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_btnm.h" +#if USE_LV_BTNM != 0 + +#include "../lv_core/lv_group.h" +#include "../lv_draw/lv_draw.h" +#include "../lv_core/lv_refr.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_txt.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param); +static bool lv_btnm_design(lv_obj_t * btnm, const lv_area_t * mask, lv_design_mode_t mode); +static uint8_t get_button_width(const char * btn_str); +static bool button_is_hidden(const char * btn_str); +static bool button_is_repeat_disabled(const char * btn_str); +static bool button_is_inactive(const char * btn_str); +const char * cut_ctrl_byte(const char * btn_str); +static uint16_t get_button_from_point(lv_obj_t * btnm, lv_point_t * p); +static uint16_t get_button_text(lv_obj_t * btnm, uint16_t btn_id); +static void allocate_btn_areas(lv_obj_t * btnm, const char ** map); + +/********************** + * STATIC VARIABLES + **********************/ +static const char * lv_btnm_def_map[] = {"Btn1", "Btn2", "Btn3", "\n", + "\002Btn4", "Btn5", "" + }; + +static lv_design_func_t ancestor_design_f; +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a button matrix objects + * @param par pointer to an object, it will be the parent of the new button matrix + * @param copy pointer to a button matrix object, if not NULL then the new object will be copied from it + * @return pointer to the created button matrix + */ +lv_obj_t * lv_btnm_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("button matrix create started"); + + /*Create the ancestor object*/ + lv_obj_t * new_btnm = lv_obj_create(par, copy); + lv_mem_assert(new_btnm); + if(new_btnm == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_btnm); + + /*Allocate the object type specific extended data*/ + lv_btnm_ext_t * ext = lv_obj_allocate_ext_attr(new_btnm, sizeof(lv_btnm_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->btn_cnt = 0; + ext->btn_id_pr = LV_BTNM_PR_NONE; + ext->btn_id_tgl = LV_BTNM_PR_NONE; + ext->button_areas = NULL; + ext->action = NULL; + ext->map_p = NULL; + ext->toggle = 0; + ext->recolor = 0; + ext->styles_btn[LV_BTN_STATE_REL] = &lv_style_btn_rel; + ext->styles_btn[LV_BTN_STATE_PR] = &lv_style_btn_pr; + ext->styles_btn[LV_BTN_STATE_TGL_REL] = &lv_style_btn_tgl_rel; + ext->styles_btn[LV_BTN_STATE_TGL_PR] = &lv_style_btn_tgl_pr; + ext->styles_btn[LV_BTN_STATE_INA] = &lv_style_btn_ina; + + if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_btnm); + + lv_obj_set_signal_func(new_btnm, lv_btnm_signal); + lv_obj_set_design_func(new_btnm, lv_btnm_design); + + /*Init the new button matrix object*/ + if(copy == NULL) { + lv_obj_set_size(new_btnm, LV_HOR_RES / 2, LV_VER_RES / 4); + lv_btnm_set_map(new_btnm, lv_btnm_def_map); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BG, th->btnm.bg); + lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_REL, th->btnm.btn.rel); + lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_PR, th->btnm.btn.pr); + lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_TGL_REL, th->btnm.btn.tgl_rel); + lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_TGL_PR, th->btnm.btn.tgl_pr); + lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_INA, th->btnm.btn.ina); + } else { + lv_obj_set_style(new_btnm, &lv_style_pretty); + } + } + /*Copy an existing object*/ + else { + lv_btnm_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + memcpy(ext->styles_btn, copy_ext->styles_btn, sizeof(ext->styles_btn)); + ext->action = copy_ext->action; + ext->toggle = copy_ext->toggle; + ext->btn_id_tgl = copy_ext->btn_id_tgl; + lv_btnm_set_map(new_btnm, lv_btnm_get_map(copy)); + } + + LV_LOG_INFO("button matrix created"); + + return new_btnm; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new map. Buttons will be created/deleted according to the map. + * @param btnm pointer to a button matrix object + * @param map pointer a string array. The last string has to be: "". + * Use "\n" to begin a new line. + * The first byte can be a control data: + * - bit 7: always 1 + * - bit 6: always 0 + * - bit 5: inactive (disabled) (\24x) + * - bit 4: no repeat (on long press) (\22x) + * - bit 3: hidden (\21x) + * - bit 2..0: button relative width + * Example (practically use octal numbers): "\224abc": "abc" text with 4 width and no long press + */ +void lv_btnm_set_map(lv_obj_t * btnm, const char ** map) +{ + if(map == NULL) return; + + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + ext->map_p = map; + + /*Analyze the map and create the required number of buttons*/ + allocate_btn_areas(btnm, map); + + /*Set size and positions of the buttons*/ + lv_style_t * style_bg = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BG); + lv_coord_t max_w = lv_obj_get_width(btnm) - 2 * style_bg->body.padding.hor; + lv_coord_t max_h = lv_obj_get_height(btnm) - 2 * style_bg->body.padding.ver; + lv_coord_t act_y = style_bg->body.padding.ver; + + /*Count the lines to calculate button height*/ + uint8_t line_cnt = 1; + uint8_t li; + for(li = 0; strlen(map[li]) != 0; li++) { + if(strcmp(map[li], "\n") == 0) line_cnt ++; + } + + lv_coord_t btn_h = max_h - ((line_cnt - 1) * style_bg->body.padding.inner); + btn_h = btn_h / line_cnt; + btn_h --; /*-1 because e.g. height = 100 means 101 pixels (0..100)*/ + + /* Count the units and the buttons in a line + * (A button can be 1,2,3... unit wide)*/ + uint16_t unit_cnt; /*Number of units in a row*/ + uint16_t unit_act_cnt; /*Number of units currently put in a row*/ + uint16_t btn_cnt; /*Number of buttons in a row*/ + uint16_t i_tot = 0; /*Act. index in the str map*/ + uint16_t btn_i = 0; /*Act. index of button areas*/ + const char ** map_p_tmp = map; + + /*Count the units and the buttons in a line*/ + while(1) { + unit_cnt = 0; + btn_cnt = 0; + /*Count the buttons in a line*/ + while(strcmp(map_p_tmp[btn_cnt], "\n") != 0 && + strlen(map_p_tmp[btn_cnt]) != 0) { /*Check a line*/ + unit_cnt += get_button_width(map_p_tmp[btn_cnt]); + btn_cnt ++; + } + + /*Make sure the last row is at the bottom of 'btnm'*/ + if(map_p_tmp[btn_cnt][0] == '\0') { /*Last row?*/ + btn_h = max_h - act_y + style_bg->body.padding.ver - 1; + } + + /*Only deal with the non empty lines*/ + if(btn_cnt != 0) { + /*Calculate the width of all units*/ + lv_coord_t all_unit_w = max_w - ((btn_cnt - 1) * style_bg->body.padding.inner); + + /*Set the button size and positions and set the texts*/ + uint16_t i; + lv_coord_t act_x = style_bg->body.padding.hor; + lv_coord_t act_unit_w; + unit_act_cnt = 0; + for(i = 0; i < btn_cnt; i++) { + /* one_unit_w = all_unit_w / unit_cnt + * act_unit_w = one_unit_w * button_width + * do this two operations but the multiply first to divide a greater number */ + act_unit_w = (all_unit_w * get_button_width(map_p_tmp[i])) / unit_cnt; + act_unit_w --; /*-1 because e.g. width = 100 means 101 pixels (0..100)*/ + + /*Always recalculate act_x because of rounding errors */ + act_x = (unit_act_cnt * all_unit_w) / unit_cnt + i * style_bg->body.padding.inner + style_bg->body.padding.hor; + + /* Set the button's area. + * If inner padding is zero then use the prev. button x2 as x1 to avoid rounding errors*/ + if(style_bg->body.padding.inner == 0 && act_x != style_bg->body.padding.hor) { + lv_area_set(&ext->button_areas[btn_i], ext->button_areas[btn_i - 1].x2, act_y, + act_x + act_unit_w, act_y + btn_h); + } else { + lv_area_set(&ext->button_areas[btn_i], act_x, act_y, + act_x + act_unit_w, act_y + btn_h); + } + + unit_act_cnt += get_button_width(map_p_tmp[i]); + + i_tot ++; + btn_i ++; + } + } + act_y += btn_h + style_bg->body.padding.inner; + + + if(strlen(map_p_tmp[btn_cnt]) == 0) break; /*Break on end of map*/ + map_p_tmp = &map_p_tmp[btn_cnt + 1]; /*Set the map to the next line*/ + i_tot ++; /*Skip the '\n'*/ + } + + lv_obj_invalidate(btnm); +} + +/** + * Set a new callback function for the buttons (It will be called when a button is released) + * @param btnm: pointer to button matrix object + * @param cb pointer to a callback function + */ +void lv_btnm_set_action(lv_obj_t * btnm, lv_btnm_action_t action) +{ + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + ext->action = action; +} + +/** + * Enable or disable button toggling + * @param btnm pointer to button matrix object + * @param en true: enable toggling; false: disable toggling + * @param id index of the currently toggled button (ignored if 'en' == false) + */ +void lv_btnm_set_toggle(lv_obj_t * btnm, bool en, uint16_t id) +{ + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + + ext->toggle = en == false ? 0 : 1; + if(ext->toggle != 0) { + if(id >= ext->btn_cnt) id = ext->btn_cnt - 1; + ext->btn_id_tgl = id; + } else { + ext->btn_id_tgl = LV_BTNM_PR_NONE; + } + + lv_obj_invalidate(btnm); +} + +/** + * Set a style of a button matrix + * @param btnm pointer to a button matrix object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_btnm_set_style(lv_obj_t * btnm, lv_btnm_style_t type, lv_style_t * style) +{ + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + + switch(type) { + case LV_BTNM_STYLE_BG: + lv_obj_set_style(btnm, style); + break; + case LV_BTNM_STYLE_BTN_REL: + ext->styles_btn[LV_BTN_STATE_REL] = style; + lv_obj_invalidate(btnm); + break; + case LV_BTNM_STYLE_BTN_PR: + ext->styles_btn[LV_BTN_STATE_PR] = style; + lv_obj_invalidate(btnm); + break; + case LV_BTNM_STYLE_BTN_TGL_REL: + ext->styles_btn[LV_BTN_STATE_TGL_REL] = style; + lv_obj_invalidate(btnm); + break; + case LV_BTNM_STYLE_BTN_TGL_PR: + ext->styles_btn[LV_BTN_STATE_TGL_PR] = style; + lv_obj_invalidate(btnm); + break; + case LV_BTNM_STYLE_BTN_INA: + ext->styles_btn[LV_BTN_STATE_INA] = style; + lv_obj_invalidate(btnm); + break; + } +} + +void lv_btnm_set_recolor(const lv_obj_t * btnm, bool en) +{ + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + + ext->recolor = en; + lv_obj_invalidate(btnm); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the current map of a button matrix + * @param btnm pointer to a button matrix object + * @return the current map + */ +const char ** lv_btnm_get_map(const lv_obj_t * btnm) +{ + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + return ext->map_p; +} + +/** + * Get a the callback function of the buttons on a button matrix + * @param btnm: pointer to button matrix object + * @return pointer to the callback function + */ +lv_btnm_action_t lv_btnm_get_action(const lv_obj_t * btnm) +{ + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + return ext->action; +} + +/** + * Get the pressed button + * @param btnm pointer to button matrix object + * @return index of the currently pressed button (LV_BTNM_PR_NONE: if unset) + */ +uint16_t lv_btnm_get_pressed(const lv_obj_t * btnm) +{ + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + return ext->btn_id_pr; +} + +/** + * Get the toggled button + * @param btnm pointer to button matrix object + * @return index of the currently toggled button (LV_BTNM_PR_NONE: if unset) + */ +uint16_t lv_btnm_get_toggled(const lv_obj_t * btnm) +{ + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + + if(ext->toggle == 0) return LV_BTNM_PR_NONE; + else return ext->btn_id_tgl; +} + +/** + * Get a style of a button matrix + * @param btnm pointer to a button matrix object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_btnm_get_style(const lv_obj_t * btnm, lv_btnm_style_t type) +{ + lv_style_t * style = NULL; + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + + switch(type) { + case LV_BTNM_STYLE_BG: + style = lv_obj_get_style(btnm); + break; + case LV_BTNM_STYLE_BTN_REL: + style = ext->styles_btn[LV_BTN_STATE_REL]; + break; + case LV_BTNM_STYLE_BTN_PR: + style = ext->styles_btn[LV_BTN_STATE_PR]; + break; + case LV_BTNM_STYLE_BTN_TGL_REL: + style = ext->styles_btn[LV_BTN_STATE_TGL_REL]; + break; + case LV_BTNM_STYLE_BTN_TGL_PR: + style = ext->styles_btn[LV_BTN_STATE_TGL_PR]; + break; + case LV_BTNM_STYLE_BTN_INA: + style = ext->styles_btn[LV_BTN_STATE_INA]; + break; + default: + style = NULL; + break; + } + + return style; +} + +bool lv_btnm_get_recolor(const lv_obj_t * btnm) +{ + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + + return ext->recolor; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the button matrixs + * @param btnm pointer to a button matrix object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_btnm_design(lv_obj_t * btnm, const lv_area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + return ancestor_design_f(btnm, mask, mode); + /*Return false if the object is not covers the mask_p area*/ + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + + ancestor_design_f(btnm, mask, mode); + + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + lv_style_t * bg_style = lv_obj_get_style(btnm); + lv_style_t * btn_style; + lv_opa_t opa_scale = lv_obj_get_opa_scale(btnm); + + lv_area_t area_btnm; + lv_obj_get_coords(btnm, &area_btnm); + + lv_area_t area_tmp; + lv_coord_t btn_w; + lv_coord_t btn_h; + + uint16_t btn_i = 0; + uint16_t txt_i = 0; + lv_style_t style_tmp; + lv_txt_flag_t txt_flag = LV_TXT_FLAG_NONE; + + if(ext->recolor) txt_flag = LV_TXT_FLAG_RECOLOR; + + for(btn_i = 0; btn_i < ext->btn_cnt; btn_i ++, txt_i ++) { + /*Search the next valid text in the map*/ + while(strcmp(ext->map_p[txt_i], "\n") == 0) { + txt_i ++; + } + + /*Skip hidden buttons*/ + if(button_is_hidden(ext->map_p[txt_i])) continue; + + lv_area_copy(&area_tmp, &ext->button_areas[btn_i]); + area_tmp.x1 += area_btnm.x1; + area_tmp.y1 += area_btnm.y1; + area_tmp.x2 += area_btnm.x1; + area_tmp.y2 += area_btnm.y1; + + btn_w = lv_area_get_width(&area_tmp); + btn_h = lv_area_get_height(&area_tmp); + + /*Load the style*/ + if(button_is_inactive(ext->map_p[txt_i])) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_INA); + else if(btn_i != ext->btn_id_pr && btn_i != ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_REL); + else if(btn_i == ext->btn_id_pr && btn_i != ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_PR); + else if(btn_i != ext->btn_id_pr && btn_i == ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_TGL_REL); + else if(btn_i == ext->btn_id_pr && btn_i == ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_TGL_PR); + else btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_REL); /*Not possible option, just to be sure*/ + + lv_style_copy(&style_tmp, btn_style); + + /*Remove borders on the edges if `LV_BORDER_INTERNAL`*/ + if(style_tmp.body.border.part & LV_BORDER_INTERNAL) { + if(area_tmp.y1 == btnm->coords.y1 + bg_style->body.padding.ver) { + style_tmp.body.border.part &= ~LV_BORDER_TOP; + } + if(area_tmp.y2 == btnm->coords.y2 - bg_style->body.padding.ver) { + style_tmp.body.border.part &= ~LV_BORDER_BOTTOM; + } + + if(txt_i == 0) { + style_tmp.body.border.part &= ~LV_BORDER_LEFT; + } + else if(strcmp(ext->map_p[txt_i - 1],"\n") == 0) { + style_tmp.body.border.part &= ~LV_BORDER_LEFT; + } + + if(ext->map_p[txt_i + 1][0] == '\0' || strcmp(ext->map_p[txt_i + 1], "\n") == 0) { + style_tmp.body.border.part &= ~LV_BORDER_RIGHT; + } + } + lv_draw_rect(&area_tmp, mask, &style_tmp, opa_scale); + + /*Calculate the size of the text*/ + if(btn_style->glass) btn_style = bg_style; + const lv_font_t * font = btn_style->text.font; + lv_point_t txt_size; + lv_txt_get_size(&txt_size, ext->map_p[txt_i], font, + btn_style->text.letter_space, btn_style->text.line_space, + lv_area_get_width(&area_btnm), txt_flag); + + area_tmp.x1 += (btn_w - txt_size.x) / 2; + area_tmp.y1 += (btn_h - txt_size.y) / 2; + area_tmp.x2 = area_tmp.x1 + txt_size.x; + area_tmp.y2 = area_tmp.y1 + txt_size.y; + + lv_draw_label(&area_tmp, mask, btn_style, opa_scale, ext->map_p[txt_i], txt_flag, NULL); + } + } + return true; +} + +/** + * Signal function of the button matrix + * @param btnm pointer to a button matrix object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(btnm, sign, param); + if(res != LV_RES_OK) return res; + + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + lv_area_t btnm_area; + lv_area_t btn_area; + lv_point_t p; + if(sign == LV_SIGNAL_CLEANUP) { + lv_mem_free(ext->button_areas); + } else if(sign == LV_SIGNAL_STYLE_CHG || sign == LV_SIGNAL_CORD_CHG) { + lv_btnm_set_map(btnm, ext->map_p); + } else if(sign == LV_SIGNAL_PRESSING) { + uint16_t btn_pr; + /*Search the pressed area*/ + lv_indev_get_point(param, &p); + btn_pr = get_button_from_point(btnm, &p); + /*Invalidate to old and the new areas*/; + lv_obj_get_coords(btnm, &btnm_area); + if(btn_pr != ext->btn_id_pr) { + lv_indev_reset_lpr(param); + if(ext->btn_id_pr != LV_BTNM_PR_NONE) { + lv_area_copy(&btn_area, &ext->button_areas[ext->btn_id_pr]); + btn_area.x1 += btnm_area.x1; + btn_area.y1 += btnm_area.y1; + btn_area.x2 += btnm_area.x1; + btn_area.y2 += btnm_area.y1; + lv_inv_area(&btn_area); + } + if(btn_pr != LV_BTNM_PR_NONE) { + lv_area_copy(&btn_area, &ext->button_areas[btn_pr]); + btn_area.x1 += btnm_area.x1; + btn_area.y1 += btnm_area.y1; + btn_area.x2 += btnm_area.x1; + btn_area.y2 += btnm_area.y1; + lv_inv_area(&btn_area); + } + } + + ext->btn_id_pr = btn_pr; + } + + else if(sign == LV_SIGNAL_LONG_PRESS_REP) { + if(ext->action && ext->btn_id_pr != LV_BTNM_PR_NONE) { + uint16_t txt_i = get_button_text(btnm, ext->btn_id_pr); + if(txt_i != LV_BTNM_PR_NONE) { + if(button_is_repeat_disabled(ext->map_p[txt_i]) == false && + button_is_inactive(ext->map_p[txt_i]) == false) { + res = ext->action(btnm, cut_ctrl_byte(ext->map_p[txt_i])); + } + } + } + } else if(sign == LV_SIGNAL_RELEASED) { + if(ext->btn_id_pr != LV_BTNM_PR_NONE) { + uint16_t txt_i = get_button_text(btnm, ext->btn_id_pr); + if(button_is_inactive(ext->map_p[txt_i]) == false && txt_i != LV_BTNM_PR_NONE) { /*Ignore the inactive buttons anf click between the buttons*/ + if(ext->action) res = ext->action(btnm, cut_ctrl_byte(ext->map_p[txt_i])); + if(res == LV_RES_OK) { + + /*Invalidate to old pressed area*/; + lv_obj_get_coords(btnm, &btnm_area); + lv_area_copy(&btn_area, &ext->button_areas[ext->btn_id_pr]); + btn_area.x1 += btnm_area.x1; + btn_area.y1 += btnm_area.y1; + btn_area.x2 += btnm_area.x1; + btn_area.y2 += btnm_area.y1; + lv_inv_area(&btn_area); + + if(ext->toggle != 0) { + /*Invalidate to old toggled area*/; + lv_area_copy(&btn_area, &ext->button_areas[ext->btn_id_tgl]); + btn_area.x1 += btnm_area.x1; + btn_area.y1 += btnm_area.y1; + btn_area.x2 += btnm_area.x1; + btn_area.y2 += btnm_area.y1; + lv_inv_area(&btn_area); + ext->btn_id_tgl = ext->btn_id_pr; + + } + + #if USE_LV_GROUP + /*Leave the clicked button when releases if this not the focused object in a group*/ + lv_group_t * g = lv_obj_get_group(btnm); + if(lv_group_get_focused(g) != btnm) { + ext->btn_id_pr = LV_BTNM_PR_NONE; + } + #else + ext->btn_id_pr = LV_BTNM_PR_NONE; + #endif + + } + } + } + } else if(sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_DEFOCUS) { + ext->btn_id_pr = LV_BTNM_PR_NONE; + lv_obj_invalidate(btnm); + } else if(sign == LV_SIGNAL_FOCUS) { +#if USE_LV_GROUP + lv_indev_t * indev = lv_indev_get_act(); + lv_hal_indev_type_t indev_type = lv_indev_get_type(indev); + if(indev_type == LV_INDEV_TYPE_POINTER) { + /*Select the clicked button*/ + lv_point_t p1; + lv_indev_get_point(indev, &p1); + uint16_t btn_i = get_button_from_point(btnm, &p1); + ext->btn_id_pr = btn_i; + } else if(indev_type == LV_INDEV_TYPE_ENCODER) { + /*In navigation mode don't select any button but in edit mode select the fist*/ + if(lv_group_get_editing(lv_obj_get_group(btnm))) ext->btn_id_pr = 0; + else ext->btn_id_pr = LV_BTNM_PR_NONE; + } else { + ext->btn_id_pr = 0; + } +#else + ext->btn_id_pr = 0; +#endif + lv_obj_invalidate(btnm); + } else if(sign == LV_SIGNAL_CONTROLL) { + char c = *((char *)param); + if(c == LV_GROUP_KEY_RIGHT) { + if(ext->btn_id_pr == LV_BTNM_PR_NONE) ext->btn_id_pr = 0; + else ext->btn_id_pr++; + if(ext->btn_id_pr >= ext->btn_cnt - 1) ext->btn_id_pr = ext->btn_cnt - 1; + lv_obj_invalidate(btnm); + } else if(c == LV_GROUP_KEY_LEFT) { + if(ext->btn_id_pr == LV_BTNM_PR_NONE) ext->btn_id_pr = 0; + if(ext->btn_id_pr > 0) ext->btn_id_pr--; + lv_obj_invalidate(btnm); + } else if(c == LV_GROUP_KEY_DOWN) { + lv_style_t * style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BG); + /*Find the area below the the current*/ + if(ext->btn_id_pr == LV_BTNM_PR_NONE) { + ext->btn_id_pr = 0; + } else { + uint16_t area_below; + lv_coord_t pr_center = ext->button_areas[ext->btn_id_pr].x1 + (lv_area_get_width(&ext->button_areas[ext->btn_id_pr]) >> 1); + + for(area_below = ext->btn_id_pr; area_below < ext->btn_cnt; area_below ++) { + if(ext->button_areas[area_below].y1 > ext->button_areas[ext->btn_id_pr].y1 && + pr_center >= ext->button_areas[area_below].x1 && + pr_center <= ext->button_areas[area_below].x2 + style->body.padding.hor) { + break; + } + } + + if(area_below < ext->btn_cnt) ext->btn_id_pr = area_below; + } + lv_obj_invalidate(btnm); + } else if(c == LV_GROUP_KEY_UP) { + lv_style_t * style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BG); + /*Find the area below the the current*/ + if(ext->btn_id_pr == LV_BTNM_PR_NONE) { + ext->btn_id_pr = 0; + } else { + int16_t area_above; + lv_coord_t pr_center = ext->button_areas[ext->btn_id_pr].x1 + (lv_area_get_width(&ext->button_areas[ext->btn_id_pr]) >> 1); + + for(area_above = ext->btn_id_pr; area_above >= 0; area_above --) { + if(ext->button_areas[area_above].y1 < ext->button_areas[ext->btn_id_pr].y1 && + pr_center >= ext->button_areas[area_above].x1 - style->body.padding.hor && + pr_center <= ext->button_areas[area_above].x2) { + break; + } + } + if(area_above >= 0) ext->btn_id_pr = area_above; + + } + lv_obj_invalidate(btnm); + } else if(c == LV_GROUP_KEY_ENTER) { + if(ext->action != NULL) { + uint16_t txt_i = get_button_text(btnm, ext->btn_id_pr); + if(txt_i != LV_BTNM_PR_NONE) { + res = ext->action(btnm, cut_ctrl_byte(ext->map_p[txt_i])); + } + } + } + } else if(sign == LV_SIGNAL_GET_EDITABLE) { + bool * editable = (bool *)param; + *editable = true; + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_btnm"; + } + + + return res; +} + +/** + * Create the required number of buttons according to a map + * @param btnm pointer to button matrix object + * @param map_p pointer to a string array + */ +static void allocate_btn_areas(lv_obj_t * btnm, const char ** map) +{ + /*Count the buttons in the map*/ + uint16_t btn_cnt = 0; + uint16_t i = 0; + while(strlen(map[i]) != 0) { + if(strcmp(map[i], "\n") != 0) { /*Do not count line breaks*/ + btn_cnt ++; + } + i++; + } + + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + + if(ext->button_areas != NULL) { + lv_mem_free(ext->button_areas); + ext->button_areas = NULL; + } + + ext->button_areas = lv_mem_alloc(sizeof(lv_area_t) * btn_cnt); + lv_mem_assert(ext->button_areas); + if(ext->button_areas == NULL) btn_cnt = 0; + + ext->btn_cnt = btn_cnt; +} + +/** + * Get the width of a button in units. It comes from the first "letter". + * @param btn_str The descriptor string of a button. E.g. "apple" or "\004banana" + * @return the width of the button in units + */ +static uint8_t get_button_width(const char * btn_str) +{ + if((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) { + return btn_str[0] & LV_BTNM_WIDTH_MASK; + } + + return 1; /*Default width is 1*/ +} + +static bool button_is_hidden(const char * btn_str) +{ + /*If control byte presents and hidden bit is '1' then the button is hidden*/ + if(((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) && + (btn_str[0] & LV_BTNM_HIDE_MASK)) { + return true; + } + + return false; +} + +static bool button_is_repeat_disabled(const char * btn_str) +{ + /*If control byte presents and hidden bit is '1' then the button is hidden*/ + if(((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) && + (btn_str[0] & LV_BTNM_REPEAT_DISABLE_MASK)) { + return true; + } + + return false; +} + +static bool button_is_inactive(const char * btn_str) +{ + /*If control byte presents and hidden bit is '1' then the button is hidden*/ + if(((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) && + (btn_str[0] & LV_BTNM_INACTIVE_MASK)) { + return true; + } + + return false; +} + + +const char * cut_ctrl_byte(const char * btn_str) +{ + /*Cut the control byte if present*/ + if((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) return &btn_str[1]; + else return btn_str; +} + +/** + * Gives the button id of a button under a given point + * @param btnm pointer to a button matrix object + * @param p a point with absolute coordinates + * @return the id of the button or LV_BTNM_PR_NONE. + */ +static uint16_t get_button_from_point(lv_obj_t * btnm, lv_point_t * p) +{ + lv_area_t btnm_cords; + lv_area_t btn_area; + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + uint16_t i; + lv_obj_get_coords(btnm, &btnm_cords); + + for(i = 0; i < ext->btn_cnt; i++) { + lv_area_copy(&btn_area, &ext->button_areas[i]); + btn_area.x1 += btnm_cords.x1; + btn_area.y1 += btnm_cords.y1; + btn_area.x2 += btnm_cords.x1; + btn_area.y2 += btnm_cords.y1; + if(lv_area_is_point_on(&btn_area, p) != false) { + break; + } + } + + if(i == ext->btn_cnt) i = LV_BTNM_PR_NONE; + + return i; +} + +/** + * Get the text of a button + * @param btnm pointer to a button matrix object + * @param btn_id button id + * @return text id in ext->map_p or LV_BTNM_PR_NONE if 'btn_id' was invalid + */ +static uint16_t get_button_text(lv_obj_t * btnm, uint16_t btn_id) +{ + lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm); + if(btn_id > ext->btn_cnt) return LV_BTNM_PR_NONE; + + uint16_t txt_i = 0; + uint16_t btn_i = 0; + + /* Search the text of ext->btn_pr the buttons text in the map + * Skip "\n"-s*/ + while(btn_i != btn_id) { + btn_i ++; + txt_i ++; + if(strcmp(ext->map_p[txt_i], "\n") == 0) txt_i ++; + } + + if(btn_i == ext->btn_cnt) return LV_BTNM_PR_NONE; + + return txt_i; +} + + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_btnm.h b/bdk/libs/lvgl/lv_objx/lv_btnm.h new file mode 100644 index 00000000..de334b75 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_btnm.h @@ -0,0 +1,197 @@ +/** + * @file lv_btnm.h + * + */ + + +#ifndef LV_BTNM_H +#define LV_BTNM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_BTNM != 0 + +#include "../lv_core/lv_obj.h" +#include "lv_label.h" +#include "lv_btn.h" + +/********************* + * DEFINES + *********************/ + +/*Control byte*/ +#define LV_BTNM_CTRL_CODE 0x80 /*The control byte has to begin (if present) with 0b10xxxxxx*/ +#define LV_BTNM_CTRL_MASK 0xC0 +#define LV_BTNM_WIDTH_MASK 0x07 +#define LV_BTNM_HIDE_MASK 0x08 +#define LV_BTNM_REPEAT_DISABLE_MASK 0x10 +#define LV_BTNM_INACTIVE_MASK 0x20 + + +#define LV_BTNM_PR_NONE 0xFFFF +/********************** + * TYPEDEFS + **********************/ + +/* Type of callback function which is called when a button is released or long pressed on the button matrix + * Parameters: button matrix, text of the released button + * return LV_ACTION_RES_INV if the button matrix is deleted else LV_ACTION_RES_OK*/ +typedef lv_res_t (*lv_btnm_action_t) (lv_obj_t *, const char *txt); + +/*Data of button matrix*/ +typedef struct +{ + /*No inherited ext.*/ /*Ext. of ancestor*/ + /*New data for this type */ + const char ** map_p; /*Pointer to the current map*/ + lv_area_t *button_areas; /*Array of areas of buttons*/ + lv_btnm_action_t action; /*A function to call when a button is releases*/ + lv_style_t *styles_btn[LV_BTN_STATE_NUM]; /*Styles of buttons in each state*/ + uint16_t btn_cnt; /*Number of button in 'map_p'(Handled by the library)*/ + uint16_t btn_id_pr; /*Index of the currently pressed button (in `button_areas`) or LV_BTNM_PR_NONE*/ + uint16_t btn_id_tgl; /*Index of the currently toggled button (in `button_areas`) or LV_BTNM_PR_NONE */ + uint8_t toggle :1; /*Enable toggling*/ + uint8_t recolor :1; /*Enable button recoloring*/ +} lv_btnm_ext_t; + +enum { + LV_BTNM_STYLE_BG, + LV_BTNM_STYLE_BTN_REL, + LV_BTNM_STYLE_BTN_PR, + LV_BTNM_STYLE_BTN_TGL_REL, + LV_BTNM_STYLE_BTN_TGL_PR, + LV_BTNM_STYLE_BTN_INA, +}; +typedef uint8_t lv_btnm_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a button matrix objects + * @param par pointer to an object, it will be the parent of the new button matrix + * @param copy pointer to a button matrix object, if not NULL then the new object will be copied from it + * @return pointer to the created button matrix + */ +lv_obj_t * lv_btnm_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new map. Buttons will be created/deleted according to the map. + * @param btnm pointer to a button matrix object + * @param map pointer a string array. The last string has to be: "". + * Use "\n" to begin a new line. + * The first byte can be a control data: + * - bit 7: always 1 + * - bit 6: always 0 + * - bit 5: inactive (disabled) + * - bit 4: no repeat (on long press) + * - bit 3: hidden + * - bit 2..0: button relative width + * Example (practically use octal numbers): "\224abc": "abc" text with 4 width and no long press + */ +void lv_btnm_set_map(lv_obj_t * btnm, const char ** map); + +/** + * Set a new callback function for the buttons (It will be called when a button is released) + * @param btnm: pointer to button matrix object + * @param action pointer to a callback function + */ +void lv_btnm_set_action(lv_obj_t * btnm, lv_btnm_action_t action); + +/** + * Enable or disable button toggling + * @param btnm pointer to button matrix object + * @param en true: enable toggling; false: disable toggling + * @param id index of the currently toggled button (ignored if 'en' == false) + */ +void lv_btnm_set_toggle(lv_obj_t * btnm, bool en, uint16_t id); + +/** + * Set a style of a button matrix + * @param btnm pointer to a button matrix object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_btnm_set_style(lv_obj_t *btnm, lv_btnm_style_t type, lv_style_t *style); + +/** + * Set whether recoloring is enabled + * @param btnm pointer to button matrix object + * @param en whether recoloring is enabled + */ +void lv_btnm_set_recolor(const lv_obj_t * btnm, bool en); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the current map of a button matrix + * @param btnm pointer to a button matrix object + * @return the current map + */ +const char ** lv_btnm_get_map(const lv_obj_t * btnm); + +/** + * Get a the callback function of the buttons on a button matrix + * @param btnm: pointer to button matrix object + * @return pointer to the callback function + */ +lv_btnm_action_t lv_btnm_get_action(const lv_obj_t * btnm); + +/** + * Get the pressed button + * @param btnm pointer to button matrix object + * @return index of the currently pressed button (LV_BTNM_PR_NONE: if unset) + */ +uint16_t lv_btnm_get_pressed(const lv_obj_t * btnm); + +/** + * Get the toggled button + * @param btnm pointer to button matrix object + * @return index of the currently toggled button (LV_BTNM_PR_NONE: if unset) + */ +uint16_t lv_btnm_get_toggled(const lv_obj_t * btnm); + +/** + * Get a style of a button matrix + * @param btnm pointer to a button matrix object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_btnm_get_style(const lv_obj_t *btnm, lv_btnm_style_t type); + +/** + * Find whether recoloring is enabled + * @param btnm pointer to button matrix object + * @return whether recoloring is enabled + */ +bool lv_btnm_get_recolor(const lv_obj_t * btnm); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_BTNM*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_BTNM_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_calendar.c b/bdk/libs/lvgl/lv_objx/lv_calendar.c new file mode 100644 index 00000000..50304c3f --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_calendar.c @@ -0,0 +1,1038 @@ +/** + * @file lv_calendar.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_calendar.h" +#if USE_LV_CALENDAR != 0 + +#include "../lv_draw/lv_draw.h" +#include "../lv_hal/lv_hal_indev.h" +#include "../lv_misc/lv_math.h" +#include "../lv_core/lv_indev.h" +#include "../lv_themes/lv_theme.h" +//#include + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +enum { + DAY_DRAW_PREV_MONTH, + DAY_DRAW_ACT_MONTH, + DAY_DRAW_NEXT_MONTH, +}; +typedef uint8_t day_draw_state_t; + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_calendar_design(lv_obj_t * calendar, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_calendar_signal(lv_obj_t * calendar, lv_signal_t sign, void * param); +static bool calculate_touched_day(lv_obj_t * calendar, const lv_point_t * touched_point); +static lv_coord_t get_header_height(lv_obj_t * calendar); +static lv_coord_t get_day_names_height(lv_obj_t * calendar); +static void draw_header(lv_obj_t * calendar, const lv_area_t * mask); +static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask); +static void draw_days(lv_obj_t * calendar, const lv_area_t * mask); +static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day); +static bool is_highlighted(lv_obj_t * calendar, int32_t year, int32_t month, int32_t day); +static const char * get_day_name(lv_obj_t * calendar, uint8_t day); +static const char * get_month_name(lv_obj_t * calendar, int32_t month); +static uint8_t get_month_length(int32_t year, int32_t month); +static uint8_t is_leap_year(uint32_t year); + + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_design_func_t ancestor_design; +static const char * day_name[7] = {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"}; +static const char * month_name[12] = {"January", "February", "March", "April", + "May", "June", "July", "August", + "September", "October", "November", "December" +}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a calendar object + * @param par pointer to an object, it will be the parent of the new calendar + * @param copy pointer to a calendar object, if not NULL then the new object will be copied from it + * @return pointer to the created calendar + */ +lv_obj_t * lv_calendar_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("calendar create started"); + + /*Create the ancestor of calendar*/ + lv_obj_t * new_calendar = lv_obj_create(par, copy); + lv_mem_assert(new_calendar); + if(new_calendar == NULL) return NULL; + + /*Allocate the calendar type specific extended data*/ + lv_calendar_ext_t * ext = lv_obj_allocate_ext_attr(new_calendar, sizeof(lv_calendar_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_calendar); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_calendar); + + /*Initialize the allocated 'ext' */ + ext->today.year = 2018; + ext->today.month = 1; + ext->today.day = 1; + + ext->showed_date.year = 2018; + ext->showed_date.month = 1; + ext->showed_date.day = 1; + + ext->pressed_date.year = 0; + ext->pressed_date.month = 0; + ext->pressed_date.day = 0; + + ext->highlighted_dates = NULL; + ext->highlighted_dates_num = 0; + ext->day_names = NULL; + ext->month_names = NULL; + ext->actions[LV_CALENDAR_ACTION_PR] = NULL; + ext->actions[LV_CALENDAR_ACTION_CLICK] = NULL; + ext->actions[LV_CALENDAR_ACTION_LONG_PR] = NULL; + ext->actions[LV_CALENDAR_ACTION_LONG_PR_REPEAT] = NULL; + ext->style_header = &lv_style_plain_color; + ext->style_header_pr = &lv_style_pretty_color; + ext->style_highlighted_days = &lv_style_plain_color; + ext->style_inactive_days = &lv_style_btn_ina; + ext->style_week_box = &lv_style_plain_color; + ext->style_today_box = &lv_style_pretty_color; + ext->style_day_names = &lv_style_pretty; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_calendar, lv_calendar_signal); + lv_obj_set_design_func(new_calendar, lv_calendar_design); + + /*Init the new calendar calendar*/ + if(copy == NULL) { + lv_obj_set_size(new_calendar, LV_DPI * 2, LV_DPI * 2); + lv_obj_set_style(new_calendar, &lv_style_pretty); + + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_BG, th->calendar.bg); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER, th->calendar.header); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER_PR, th->calendar.header_pr); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_DAY_NAMES, th->calendar.day_names); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_WEEK_BOX, th->calendar.week_box); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_TODAY_BOX, th->calendar.today_box); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS, th->calendar.highlighted_days); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_INACTIVE_DAYS, th->calendar.inactive_days); + } else { + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_BG, &lv_style_pretty); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER, ext->style_header); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HEADER_PR, ext->style_header_pr); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_DAY_NAMES, ext->style_day_names); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_WEEK_BOX, ext->style_week_box); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_TODAY_BOX, ext->style_today_box); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS, ext->style_highlighted_days); + lv_calendar_set_style(new_calendar, LV_CALENDAR_STYLE_INACTIVE_DAYS, ext->style_inactive_days); + + } + + } + /*Copy an existing calendar*/ + else { + lv_calendar_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->today.year = copy_ext->today.year; + ext->today.month = copy_ext->today.month; + ext->today.day = copy_ext->today.day; + + ext->showed_date.year = copy_ext->showed_date.year; + ext->showed_date.month = copy_ext->showed_date.month; + ext->showed_date.day = copy_ext->showed_date.day; + + ext->highlighted_dates = copy_ext->highlighted_dates; + ext->highlighted_dates_num = copy_ext->highlighted_dates_num; + ext->day_names = copy_ext->day_names; + + memcpy(ext->actions, copy_ext->actions, sizeof(ext->actions)); + + ext->month_names = copy_ext->month_names; + ext->style_header = copy_ext->style_header; + ext->style_header_pr = copy_ext->style_header_pr; + ext->style_highlighted_days = copy_ext->style_highlighted_days; + ext->style_inactive_days = copy_ext->style_inactive_days; + ext->style_week_box = copy_ext->style_week_box; + ext->style_today_box = copy_ext->style_today_box; + ext->style_day_names = copy_ext->style_day_names; + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_calendar); + } + + LV_LOG_INFO("calendar created"); + + return new_calendar; +} + +/*====================== + * Add/remove functions + *=====================*/ + +/* + * New object specific "add" or "remove" functions come here + */ + + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a function to call when a calendar event happens + * @param calendar pointer to a calendar object + * @param action type of event form 'lv_action_t' (press, release, long press, long press repeat) + */ +void lv_calendar_set_action(lv_obj_t * calendar, lv_calendar_action_t type, lv_action_t action) +{ + if(type >= LV_CALENDAR_ACTION_NUM) return; + + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + ext->actions[type] = action; +} + +/** + * Set the today's date + * @param calendar pointer to a calendar object + * @param today pointer to an `lv_calendar_date_t` variable containing the date of today. The value will be saved it can be local variable too. + */ +void lv_calendar_set_today_date(lv_obj_t * calendar, lv_calendar_date_t * today) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + ext->today.year = today->year; + ext->today.month = today->month; + ext->today.day = today->day; + + lv_obj_invalidate(calendar); +} + +/** + * Set the currently showed + * @param calendar pointer to a calendar object + * @param showed pointer to an `lv_calendar_date_t` variable containing the date to show. The value will be saved it can be local variable too. + */ +void lv_calendar_set_showed_date(lv_obj_t * calendar, lv_calendar_date_t * showed) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + ext->showed_date.year = showed->year; + ext->showed_date.month = showed->month; + ext->showed_date.day = showed->day; + + lv_obj_invalidate(calendar); +} + +/** + * Set the the highlighted dates + * @param calendar pointer to a calendar object + * @param highlighted pointer to an `lv_calendar_date_t` array containing the dates. ONLY A POINTER WILL BE SAVED! CAN'T BE LOCAL ARRAY. + * @param date_num number of dates in the array + */ +void lv_calendar_set_highlighted_dates(lv_obj_t * calendar, lv_calendar_date_t * highlighted, uint16_t date_num) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + ext->highlighted_dates = highlighted; + ext->highlighted_dates_num = date_num; + + lv_obj_invalidate(calendar); +} + + +/** + * Set the name of the days + * @param calendar pointer to a calendar object + * @param day_names pointer to an array with the names. E.g. `const char * days[7] = {"Sun", "Mon", ...}` + * Only the pointer will be saved so this variable can't be local which will be destroyed later. + */ +void lv_calendar_set_day_names(lv_obj_t * calendar, const char ** day_names) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + ext->day_names = day_names; + lv_obj_invalidate(calendar); +} + +/** + * Set the name of the month + * @param calendar pointer to a calendar object + * @param day_names pointer to an array with the names. E.g. `const char * days[12] = {"Jan", "Feb", ...}` + * Only the pointer will be saved so this variable can't be local which will be destroyed later. + */ +void lv_calendar_set_month_names(lv_obj_t * calendar, const char ** day_names) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + ext->month_names = day_names; + lv_obj_invalidate(calendar); +} + +/** + * Set a style of a calendar. + * @param calendar pointer to calendar object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_calendar_set_style(lv_obj_t * calendar, lv_calendar_style_t type, lv_style_t * style) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + + switch(type) { + case LV_CALENDAR_STYLE_BG: + lv_obj_set_style(calendar, style); + break; + case LV_CALENDAR_STYLE_DAY_NAMES: + ext->style_day_names = style; + break; + case LV_CALENDAR_STYLE_HEADER: + ext->style_header = style; + break; + case LV_CALENDAR_STYLE_HEADER_PR: + ext->style_header_pr = style; + break; + case LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS: + ext->style_highlighted_days = style; + break; + case LV_CALENDAR_STYLE_INACTIVE_DAYS: + ext->style_inactive_days = style; + break; + case LV_CALENDAR_STYLE_TODAY_BOX: + ext->style_today_box = style; + break; + case LV_CALENDAR_STYLE_WEEK_BOX: + ext->style_week_box = style; + break; + } + + lv_obj_invalidate(calendar); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the action of a calendar + * @param calendar pointer to a calendar object + * @return pointer to the action function + */ +lv_action_t lv_calendar_get_action(const lv_obj_t * calendar, lv_calendar_action_t type) +{ + if(type >= LV_CALENDAR_ACTION_NUM) return NULL; + + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + return ext->actions[type]; +} + +/** + * Get the today's date + * @param calendar pointer to a calendar object + * @return return pointer to an `lv_calendar_date_t` variable containing the date of today. + */ +lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * calendar) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + return &ext->today; +} + +/** + * Get the currently showed + * @param calendar pointer to a calendar object + * @return pointer to an `lv_calendar_date_t` variable containing the date is being shown. + */ +lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * calendar) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + return &ext->showed_date; +} + +/** + * Get the the pressed date. + * @param calendar pointer to a calendar object + * @return pointer to an `lv_calendar_date_t` variable containing the pressed date. + */ +lv_calendar_date_t * lv_calendar_get_pressed_date(const lv_obj_t * calendar) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + return &ext->pressed_date; +} + +/** + * Get the the highlighted dates + * @param calendar pointer to a calendar object + * @return pointer to an `lv_calendar_date_t` array containing the dates. + */ +lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * calendar) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + return ext->highlighted_dates; +} + +/** + * Get the number of the highlighted dates + * @param calendar pointer to a calendar object + * @return number of highlighted days + */ +uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * calendar) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + return ext->highlighted_dates_num; +} + +/** + * Get the name of the days + * @param calendar pointer to a calendar object + * @return pointer to the array of day names + */ +const char ** lv_calendar_get_day_names(const lv_obj_t * calendar) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + return ext->day_names; +} + +/** + * Get the name of the month + * @param calendar pointer to a calendar object + * @return pointer to the array of month names + */ +const char ** lv_calendar_get_month_names(const lv_obj_t * calendar) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + return ext->month_names; +} + +/** + * Get style of a calendar. + * @param calendar pointer to calendar object + * @param type which style should be get + * @return style pointer to the style + * */ +lv_style_t * lv_calendar_get_style(const lv_obj_t * calendar, lv_calendar_style_t type) +{ + lv_style_t * style = NULL; + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + + switch(type) { + case LV_CALENDAR_STYLE_BG: + style = lv_obj_get_style(calendar); + break; + case LV_CALENDAR_STYLE_HEADER: + style = ext->style_header; + break; + case LV_CALENDAR_STYLE_HEADER_PR: + style = ext->style_header_pr; + break; + case LV_CALENDAR_STYLE_DAY_NAMES: + style = ext->style_day_names; + break; + case LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS: + style = ext->style_highlighted_days; + break; + case LV_CALENDAR_STYLE_INACTIVE_DAYS: + style = ext->style_inactive_days; + break; + case LV_CALENDAR_STYLE_WEEK_BOX: + style = ext->style_week_box; + break; + case LV_CALENDAR_STYLE_TODAY_BOX: + style = ext->style_today_box; + break; + default: + style = NULL; + break; + } + + return style; +} + +/*===================== + * Other functions + *====================*/ + +/* + * New object specific "other" functions come here + */ + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the calendars + * @param calendar pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_calendar_design(lv_obj_t * calendar, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar); + lv_draw_rect(&calendar->coords, mask, lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG), opa_scale); + + draw_header(calendar, mask); + draw_day_names(calendar, mask); + draw_days(calendar, mask); + + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + + } + + return true; +} + +/** + * Signal function of the calendar + * @param calendar pointer to a calendar object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_calendar_signal(lv_obj_t * calendar, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(calendar, sign, param); + if(res != LV_RES_OK) return res; + + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } else if(sign == LV_SIGNAL_PRESSED) { + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + /*Call the press action, 'param' is the caller indev_proc*/ + if(ext->actions[LV_CALENDAR_ACTION_PR]) { + lv_indev_t * indev = lv_indev_get_act(); + lv_point_t p; + lv_indev_get_point(indev, &p); + + if(calculate_touched_day(calendar, &p)){ + if(ext->btn_pressing != 0) lv_obj_invalidate(calendar); + ext->btn_pressing = 0; + res = ext->actions[LV_CALENDAR_ACTION_PR](calendar); + } + } + } else if(sign == LV_SIGNAL_PRESSING) { + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + lv_area_t header_area; + lv_area_copy(&header_area, &calendar->coords); + header_area.y2 = header_area.y1 + get_header_height(calendar); + + lv_indev_t * indev = lv_indev_get_act(); + lv_point_t p; + lv_indev_get_point(indev, &p); + + if(lv_area_is_point_on(&header_area, &p)) { + if(p.x < header_area.x1 + lv_area_get_width(&header_area) / 2) { + if(ext->btn_pressing != -1) lv_obj_invalidate(calendar); + ext->btn_pressing = -1; + } else { + if(ext->btn_pressing != 1) lv_obj_invalidate(calendar); + ext->btn_pressing = 1; + } + + ext->pressed_date.year = 0; + } else if(calculate_touched_day(calendar, &p)) { + if(ext->btn_pressing != 0) lv_obj_invalidate(calendar); + ext->btn_pressing = 0; + } else { + if(ext->btn_pressing != 0) lv_obj_invalidate(calendar); + ext->btn_pressing = 0; + ext->pressed_date.year = 0; + } + } else if(sign == LV_SIGNAL_PRESS_LOST) { + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + ext->pressed_date.year = 0; + ext->btn_pressing = 0; + lv_obj_invalidate(calendar); + + } else if(sign == LV_SIGNAL_RELEASED) { + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + if(ext->btn_pressing < 0) { + if(ext->showed_date.month <= 1) { + ext->showed_date.month = 12; + ext->showed_date.year --; + } else { + ext->showed_date.month --; + } + } else if(ext->btn_pressing > 0) { + if(ext->showed_date.month >= 12) { + ext->showed_date.month = 1; + ext->showed_date.year ++; + } else { + ext->showed_date.month ++; + } + } + else if(ext->pressed_date.year != 0) + { + if(ext->actions[LV_CALENDAR_ACTION_CLICK]) { + res = ext->actions[LV_CALENDAR_ACTION_CLICK](calendar); + } + } + + ext->pressed_date.year = 0; + ext->btn_pressing = 0; + lv_obj_invalidate(calendar); + + + } else if(sign == LV_SIGNAL_LONG_PRESS) { + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + if(ext->actions[LV_CALENDAR_ACTION_LONG_PR] && (ext->pressed_date.year != 0)) { + res = ext->actions[LV_CALENDAR_ACTION_LONG_PR](calendar); + } + } else if(sign == LV_SIGNAL_LONG_PRESS_REP) { + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + if(ext->actions[LV_CALENDAR_ACTION_LONG_PR_REPEAT] && (ext->pressed_date.year != 0)) { + res = ext->actions[LV_CALENDAR_ACTION_LONG_PR_REPEAT](calendar); + } + } else if(sign == LV_SIGNAL_CONTROLL) { + uint8_t c = *((uint8_t *) param); + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_UP) { + if(ext->showed_date.month >= 12) { + ext->showed_date.month = 1; + ext->showed_date.year ++; + } else { + ext->showed_date.month ++; + } + lv_obj_invalidate(calendar); + } else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_DOWN) { + if(ext->showed_date.month <= 1) { + ext->showed_date.month = 12; + ext->showed_date.year --; + } else { + ext->showed_date.month --; + } + lv_obj_invalidate(calendar); + } + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set date*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_calendar"; + } + + return res; +} + +/** + * It will check if the days part of calendar is touched + * and if it is, it will calculate the day and put it in pressed_date of calendar object. + * @param calendar pointer to a calendar object + * @param pointer to a point + * @return true: days part of calendar is touched and its related date is put in pressed date + * false: the point is out of days part area. + */ +static bool calculate_touched_day(lv_obj_t * calendar, const lv_point_t * touched_point) +{ + lv_area_t days_area; + lv_area_copy(&days_area, &calendar->coords); + lv_style_t * style_bg = lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG); + days_area.x1 += style_bg->body.padding.hor; + days_area.x2 -= style_bg->body.padding.hor; + days_area.y1 = calendar->coords.y1 + get_header_height(calendar) + get_day_names_height(calendar) - style_bg->body.padding.ver; + + if(lv_area_is_point_on(&days_area, touched_point)) { + lv_coord_t w = (days_area.x2 - days_area.x1 + 1) / 7; + lv_coord_t h = (days_area.y2 - days_area.y1 + 1) / 6; + uint8_t x_pos = 0; + x_pos = (touched_point->x - days_area.x1) / w; + if(x_pos > 6) x_pos = 6; + uint8_t y_pos = 0; + y_pos = (touched_point->y - days_area.y1) / h; + if(y_pos > 5) y_pos = 5; + + uint8_t i_pos = 0; + i_pos = (y_pos * 7) + x_pos; + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + if(i_pos < get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) ) { + ext->pressed_date.year = ext->showed_date.year - (ext->showed_date.month == 1 ? 1 : 0); + ext->pressed_date.month = ext->showed_date.month == 1 ? 12 : (ext->showed_date.month - 1); + ext->pressed_date.day = get_month_length(ext->pressed_date.year, ext->pressed_date.month) - + get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) + 1 + i_pos; + } + else if(i_pos < (get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) + + get_month_length(ext->showed_date.year, ext->showed_date.month))) { + ext->pressed_date.year = ext->showed_date.year; + ext->pressed_date.month = ext->showed_date.month; + ext->pressed_date.day = i_pos + 1 - get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1); + } + else if(i_pos < 42) { + ext->pressed_date.year = ext->showed_date.year + (ext->showed_date.month == 12 ? 1 : 0); + ext->pressed_date.month = ext->showed_date.month == 12 ? 1 : (ext->showed_date.month + 1); + ext->pressed_date.day = i_pos + 1 - get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1) + - get_month_length(ext->showed_date.year, ext->showed_date.month); + } + return true; + }else { + return false; + } +} + +/** + * Get the height of a calendar's header based on it's style + * @param calendar point to a calendar + * @return the header's height + */ +static lv_coord_t get_header_height(lv_obj_t * calendar) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + + return lv_font_get_height(ext->style_header->text.font) + ext->style_header->body.padding.ver * 2; +} + +/** + * Get the height of a calendar's day_names based on it's style + * @param calendar point to a calendar + * @return the day_names's height + */ +static lv_coord_t get_day_names_height(lv_obj_t * calendar) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + + return lv_font_get_height(ext->style_day_names->text.font) + ext->style_day_names->body.padding.ver * 2; +} + +/** + * Draw the calendar header with month name and arrows + * @param calendar point to a calendar + * @param mask a mask for drawing + */ +static void draw_header(lv_obj_t * calendar, const lv_area_t * mask) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar); + + lv_area_t header_area; + header_area.x1 = calendar->coords.x1; + header_area.x2 = calendar->coords.x2; + header_area.y1 = calendar->coords.y1; + header_area.y2 = calendar->coords.y1 + get_header_height(calendar); + + lv_draw_rect(&header_area, mask, ext->style_header, opa_scale); + + /*Add the year + month name*/ + char txt_buf[64]; + lv_math_num_to_str(ext->showed_date.year, txt_buf); + txt_buf[4] = ' '; + txt_buf[5] = '\0'; + strcpy(&txt_buf[5], get_month_name(calendar, ext->showed_date.month)); + header_area.y1 += ext->style_header->body.padding.ver; + lv_draw_label(&header_area, mask, ext->style_header, opa_scale, txt_buf, LV_TXT_FLAG_CENTER, NULL); + + /*Add the left arrow*/ + lv_style_t * arrow_style = ext->btn_pressing < 0 ? ext->style_header_pr : ext->style_header; + header_area.x1 += ext->style_header->body.padding.hor; + lv_draw_label(&header_area, mask, arrow_style, opa_scale, SYMBOL_LEFT, LV_TXT_FLAG_NONE, NULL); + + /*Add the right arrow*/ + arrow_style = ext->btn_pressing > 0 ? ext->style_header_pr : ext->style_header; + header_area.x1 = header_area.x2 - ext->style_header->body.padding.hor - + lv_txt_get_width(SYMBOL_RIGHT, strlen(SYMBOL_RIGHT), arrow_style->text.font, + arrow_style->text.line_space, LV_TXT_FLAG_NONE); + lv_draw_label(&header_area, mask, arrow_style, opa_scale, SYMBOL_RIGHT, LV_TXT_FLAG_NONE, NULL); + +} + +/** + * Draw the day's name below the header + * @param calendar point to a calendar + * @param mask a mask for drawing + */ +static void draw_day_names(lv_obj_t * calendar, const lv_area_t * mask) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar); + + lv_coord_t hpad = ext->style_day_names->body.padding.hor; + lv_coord_t w = lv_obj_get_width(calendar) - 2 * hpad; + lv_coord_t box_w = w / 7; + lv_area_t label_area; + label_area.y1 = calendar->coords.y1 + get_header_height(calendar) + ext->style_day_names->body.padding.ver; + label_area.y2 = label_area.y1 + lv_font_get_height(ext->style_day_names->text.font); + uint32_t i; + for(i = 0; i < 7; i++) { + label_area.x1 = calendar->coords.x1 + (w * i) / 7 + hpad; + label_area.x2 = label_area.x1 + box_w; + lv_draw_label(&label_area, mask, ext->style_day_names, opa_scale, get_day_name(calendar, i), LV_TXT_FLAG_CENTER, NULL); + } + +} + +/** + * Draw the date numbers in a matrix + * @param calendar point to a calendar + * @param mask a mask for drawing + */ +static void draw_days(lv_obj_t * calendar, const lv_area_t * mask) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + lv_style_t * style_bg = lv_calendar_get_style(calendar, LV_CALENDAR_STYLE_BG); + lv_coord_t hpad = style_bg->body.padding.hor; + lv_area_t label_area; + lv_opa_t opa_scale = lv_obj_get_opa_scale(calendar); + label_area.y1 = calendar->coords.y1 + get_header_height(calendar) + + ext->style_day_names->body.padding.ver + lv_font_get_height(ext->style_day_names->text.font) + + ext->style_day_names->body.padding.ver; + label_area.y2 = label_area.y1 + lv_font_get_height(style_bg->text.font); + + lv_coord_t w = lv_obj_get_width(calendar) - 2 * hpad; + lv_coord_t h = calendar->coords.y2 - label_area.y1 - style_bg->body.padding.ver; + lv_coord_t box_w = w / 7; + lv_coord_t vert_space = (h - (6 * lv_font_get_height(style_bg->text.font))) / 5; + + uint32_t week; + uint8_t day_cnt; + uint8_t month_start_day = get_day_of_week(ext->showed_date.year, ext->showed_date.month, 1); + day_draw_state_t draw_state; /*true: Not the prev. or next month is drawn*/ + lv_style_t * act_style; + + /*If starting with the first day of the week then the previous month is not visible*/ + if(month_start_day == 0) { + day_cnt = 1; + draw_state = DAY_DRAW_ACT_MONTH; + act_style = style_bg; + } else { + draw_state = DAY_DRAW_PREV_MONTH; + day_cnt = get_month_length(ext->showed_date.year, ext->showed_date.month - 1); /*Length of the previous month*/ + day_cnt -= month_start_day - 1; /*First visible number of the previous month*/ + act_style = ext->style_inactive_days; + } + + + bool month_of_today_shown = false; + if(ext->showed_date.year == ext->today.year && + ext->showed_date.month == ext->today.month) { + month_of_today_shown = true; + } + + char buf[3]; + bool in_week_box = false; + + /*Draw 6 weeks*/ + for(week = 0; week < 6; week++) { + + /*Draw the "week box"*/ + if(month_of_today_shown && + ((draw_state == DAY_DRAW_ACT_MONTH && ext->today.day >= day_cnt && ext->today.day < day_cnt + 7) || + (draw_state == DAY_DRAW_PREV_MONTH && ext->today.day <= 7 - month_start_day && week == 0))) { + lv_area_t week_box_area; + lv_area_copy(&week_box_area, &label_area); /*'label_area' is already set for this row*/ + week_box_area.x1 = calendar->coords.x1 + style_bg->body.padding.hor - ext->style_week_box->body.padding.hor; + week_box_area.x2 = calendar->coords.x2 - style_bg->body.padding.hor + ext->style_week_box->body.padding.hor; + + week_box_area.y1 -= ext->style_week_box->body.padding.ver; + week_box_area.y2 += ext->style_week_box->body.padding.ver; + lv_draw_rect(&week_box_area, mask, ext->style_week_box, opa_scale); + + in_week_box = true; + } else { + in_week_box = false; + } + + /*Draw the 7 days of a week*/ + uint32_t day; + for(day = 0; day < 7; day++) { + /*The previous month is over*/ + if(draw_state == DAY_DRAW_PREV_MONTH && day == month_start_day) { + draw_state = DAY_DRAW_ACT_MONTH; + day_cnt = 1; + act_style = style_bg; + } + /*The current month is over*/ + if(draw_state == DAY_DRAW_ACT_MONTH && + day_cnt > get_month_length(ext->showed_date.year, ext->showed_date.month)) { + draw_state = DAY_DRAW_NEXT_MONTH; + day_cnt = 1; + act_style = ext->style_inactive_days; + } + + label_area.x1 = calendar->coords.x1 + (w * day) / 7 + hpad; + label_area.x2 = label_area.x1 + box_w; + + /*Draw the "today box"*/ + if(draw_state == DAY_DRAW_ACT_MONTH && month_of_today_shown && ext->today.day == day_cnt) { + lv_area_t today_box_area; + lv_area_copy(&today_box_area, &label_area); + today_box_area.x1 = label_area.x1; + today_box_area.x2 = label_area.x2; + + today_box_area.y1 = label_area.y1 - ext->style_today_box->body.padding.ver; + today_box_area.y2 = label_area.y2 + ext->style_today_box->body.padding.ver; + lv_draw_rect(&today_box_area, mask, ext->style_today_box, opa_scale); + } + + /*Get the final style : highlighted/week box/today box/normal*/ + lv_style_t * final_style; + if(draw_state == DAY_DRAW_PREV_MONTH && + is_highlighted(calendar, ext->showed_date.year - (ext->showed_date.month == 1 ? 1 : 0), + ext->showed_date.month == 1 ? 12 : ext->showed_date.month - 1, + day_cnt)) { + final_style = ext->style_highlighted_days; + } else if(draw_state == DAY_DRAW_ACT_MONTH && + is_highlighted(calendar, ext->showed_date.year, + ext->showed_date.month, + day_cnt)) { + final_style = ext->style_highlighted_days; + } else if(draw_state == DAY_DRAW_NEXT_MONTH && + is_highlighted(calendar, ext->showed_date.year + (ext->showed_date.month == 12 ? 1 : 0), + ext->showed_date.month == 12 ? 1 : ext->showed_date.month + 1, + day_cnt)) { + final_style = ext->style_highlighted_days; + } else if(month_of_today_shown && day_cnt == ext->today.day && draw_state == DAY_DRAW_ACT_MONTH) final_style = ext->style_today_box; + else if(in_week_box && draw_state == DAY_DRAW_ACT_MONTH) final_style = ext->style_week_box; + else final_style = act_style; + + /*Write the day's number*/ + lv_math_num_to_str(day_cnt, buf); + lv_draw_label(&label_area, mask, final_style, opa_scale, buf, LV_TXT_FLAG_CENTER, NULL); + + /*Go to the next day*/ + day_cnt ++; + + } + + /*Got to the next weeks row*/ + label_area.y1 += vert_space + lv_font_get_height(style_bg->text.font); + label_area.y2 += vert_space + lv_font_get_height(style_bg->text.font); + } +} + +/** + * Check weather a date is highlighted or not + * @param calendar pointer to a calendar object + * @param year a year + * @param month a month [1..12] + * @param day a day [1..31] + * @return true: highlighted + */ +static bool is_highlighted(lv_obj_t * calendar, int32_t year, int32_t month, int32_t day) +{ + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + + if(ext->highlighted_dates == NULL || ext->highlighted_dates_num == 0) return false; + + uint32_t i; + for(i = 0; i < ext->highlighted_dates_num; i++) { + if(ext->highlighted_dates[i].year == year && + ext->highlighted_dates[i].month == month && + ext->highlighted_dates[i].day == day) { + return true; + } + } + + return false; +} + +/** + * Get the day name + * @param calendar pointer to a calendar object + * @param day a day in [0..6] + * @return + */ +static const char * get_day_name(lv_obj_t * calendar, uint8_t day) +{ + + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + if(ext->day_names) return ext->day_names[day]; + else return day_name[day]; +} + +/** + * Get the month name + * @param calendar pointer to a calendar object + * @param month a month. The range is basically [1..12] but [-11..1] is also supported to handle previous year + * @return + */ +static const char * get_month_name(lv_obj_t * calendar, int32_t month) +{ + month --; /*Range of months id [1..12] but range of indexes is [0..11]*/ + if(month < 0) month = 12 + month; + + lv_calendar_ext_t * ext = lv_obj_get_ext_attr(calendar); + if(ext->month_names) return ext->month_names[month]; + else return month_name[month]; +} + +/** + * Get the number of days in a month + * @param year a year + * @param month a month. The range is basically [1..12] but [-11..1] is also supported to handle previous year + * @return [28..31] + */ +static uint8_t get_month_length(int32_t year, int32_t month) +{ + month --; /*Range of months id [1..12] but range of indexes is [0..11]*/ + if(month < 0) { + year--; /*Already in the previous year (won't be less then -12 to skip a whole year)*/ + month = 12 + month; /*`month` is negative, the result will be < 12*/ + } + if(month >= 12) { + year ++; + month -= 12; + } + + /*month == 1 is february*/ + return (month == 1) ? (28 + is_leap_year(year)) : 31 - month % 7 % 2; + + +} + +/** + * Tells whether a year is leap year or not + * @param year a year + * @return 0: not leap year; 1: leap year + */ +static uint8_t is_leap_year(uint32_t year) +{ + return (year % 4) || ((year % 100 == 0) && (year % 400)) ? 0 : 1; +} + +/** + * Get the day of the week + * @param year a year + * @param month a month + * @param day a day + * @return [0..6] which means [Sun..Sat] + */ +static uint8_t get_day_of_week(uint32_t year, uint32_t month, uint32_t day) +{ + uint32_t a = month < 3 ? 1 : 0; + uint32_t b = year - a; + + uint32_t day_of_week = (day + (31 * (month - 2 + 12 * a) / 12) + + b + (b / 4) - (b / 100) + (b / 400)) % 7; + + return day_of_week; +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_calendar.h b/bdk/libs/lvgl/lv_objx/lv_calendar.h new file mode 100644 index 00000000..e573ae5d --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_calendar.h @@ -0,0 +1,246 @@ +/** + * @file lv_calendar.h + * + */ + +#ifndef LV_CALENDAR_H +#define LV_CALENDAR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_CALENDAR != 0 + +#include "../lv_core/lv_obj.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +typedef struct { + uint16_t year; + int8_t month; + int8_t day; +} lv_calendar_date_t; + +enum +{ + LV_CALENDAR_ACTION_CLICK, + LV_CALENDAR_ACTION_PR, + LV_CALENDAR_ACTION_LONG_PR, + LV_CALENDAR_ACTION_LONG_PR_REPEAT, + LV_CALENDAR_ACTION_NUM, +}; +typedef uint8_t lv_calendar_action_t; + +/*Data of calendar*/ +typedef struct { + /*None*/ /*Ext. of ancestor*/ + /*New data for this type */ + lv_calendar_date_t today; /*Date of today*/ + lv_calendar_date_t showed_date; /*Currently visible month (day is ignored)*/ + lv_calendar_date_t * highlighted_dates; /*Apply different style on these days (pointer to an array defined by the user)*/ + uint8_t highlighted_dates_num; /*Number of elements in `highlighted_days`*/ + int8_t btn_pressing; /*-1: prev month pressing, +1 next month pressing on the header*/ + lv_calendar_date_t pressed_date; + const char ** day_names; /*Pointer to an array with the name of the days (NULL: use default names)*/ + const char ** month_names; /*Pointer to an array with the name of the month (NULL. use default names)*/ + lv_action_t actions[LV_CALENDAR_ACTION_NUM]; + + /*Styles*/ + lv_style_t * style_header; + lv_style_t * style_header_pr; + lv_style_t * style_day_names; + lv_style_t * style_highlighted_days; + lv_style_t * style_inactive_days; + lv_style_t * style_week_box; + lv_style_t * style_today_box; +} lv_calendar_ext_t; + +/*Styles*/ +enum { + LV_CALENDAR_STYLE_BG, /*Also the style of the "normal" date numbers*/ + LV_CALENDAR_STYLE_HEADER, + LV_CALENDAR_STYLE_HEADER_PR, + LV_CALENDAR_STYLE_DAY_NAMES, + LV_CALENDAR_STYLE_HIGHLIGHTED_DAYS, + LV_CALENDAR_STYLE_INACTIVE_DAYS, + LV_CALENDAR_STYLE_WEEK_BOX, + LV_CALENDAR_STYLE_TODAY_BOX, +}; +typedef uint8_t lv_calendar_style_t; + + + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a calendar objects + * @param par pointer to an object, it will be the parent of the new calendar + * @param copy pointer to a calendar object, if not NULL then the new object will be copied from it + * @return pointer to the created calendar + */ +lv_obj_t * lv_calendar_create(lv_obj_t * par, const lv_obj_t * copy); + +/*====================== + * Add/remove functions + *=====================*/ + + +/*===================== + * Setter functions + *====================*/ +/** + * Set a function to call when a calendar event happens + * @param calendar pointer to a calendar object + * @param action type of event form 'lv_action_t' (press, release, long press, long press repeat) + */ +void lv_calendar_set_action(lv_obj_t * calendar, lv_calendar_action_t type, lv_action_t action); + +/** + * Set the today's date + * @param calendar pointer to a calendar object + * @param today pointer to an `lv_calendar_date_t` variable containing the date of today. The value will be saved it can be local variable too. + */ +void lv_calendar_set_today_date(lv_obj_t * calendar, lv_calendar_date_t * today); + +/** + * Set the currently showed + * @param calendar pointer to a calendar object + * @param showed pointer to an `lv_calendar_date_t` variable containing the date to show. The value will be saved it can be local variable too. + */ +void lv_calendar_set_showed_date(lv_obj_t * calendar, lv_calendar_date_t * showed); + +/** + * Set the the highlighted dates + * @param calendar pointer to a calendar object + * @param highlighted pointer to an `lv_calendar_date_t` array containing the dates. ONLY A POINTER WILL BE SAVED! CAN'T BE LOCAL ARRAY. + * @param date_num number of dates in the array + */ +void lv_calendar_set_highlighted_dates(lv_obj_t * calendar, lv_calendar_date_t * highlighted, uint16_t date_num); + + +/** + * Set the name of the days + * @param calendar pointer to a calendar object + * @param day_names pointer to an array with the names. E.g. `const char * days[7] = {"Sun", "Mon", ...}` + * Only the pointer will be saved so this variable can't be local which will be destroyed later. + */ +void lv_calendar_set_day_names(lv_obj_t * calendar, const char ** day_names); + +/** + * Set the name of the month + * @param calendar pointer to a calendar object + * @param day_names pointer to an array with the names. E.g. `const char * days[12] = {"Jan", "Feb", ...}` + * Only the pointer will be saved so this variable can't be local which will be destroyed later. + */ +void lv_calendar_set_month_names(lv_obj_t * calendar, const char ** day_names); + +/** + * Set a style of a calendar. + * @param calendar pointer to calendar object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_calendar_set_style(lv_obj_t * calendar, lv_calendar_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ +/** + * Get the action of a calendar + * @param calendar pointer to a calendar object + * @return pointer to the action function + */ +lv_action_t lv_calendar_get_action(const lv_obj_t * calendar, lv_calendar_action_t type); + +/** + * Get the today's date + * @param calendar pointer to a calendar object + * @return return pointer to an `lv_calendar_date_t` variable containing the date of today. + */ +lv_calendar_date_t * lv_calendar_get_today_date(const lv_obj_t * calendar); + +/** + * Get the currently showed + * @param calendar pointer to a calendar object + * @return pointer to an `lv_calendar_date_t` variable containing the date is being shown. + */ +lv_calendar_date_t * lv_calendar_get_showed_date(const lv_obj_t * calendar); + +/** + * Get the the pressed date. + * @param calendar pointer to a calendar object + * @return pointer to an `lv_calendar_date_t` variable containing the pressed date. + */ +lv_calendar_date_t * lv_calendar_get_pressed_date(const lv_obj_t * calendar); + +/** + * Get the the highlighted dates + * @param calendar pointer to a calendar object + * @return pointer to an `lv_calendar_date_t` array containing the dates. + */ +lv_calendar_date_t * lv_calendar_get_highlighted_dates(const lv_obj_t * calendar); + +/** + * Get the number of the highlighted dates + * @param calendar pointer to a calendar object + * @return number of highlighted days + */ +uint16_t lv_calendar_get_highlighted_dates_num(const lv_obj_t * calendar); + + +/** + * Get the name of the days + * @param calendar pointer to a calendar object + * @return pointer to the array of day names + */ +const char ** lv_calendar_get_day_names(const lv_obj_t * calendar); + +/** + * Get the name of the month + * @param calendar pointer to a calendar object + * @return pointer to the array of month names + */ +const char ** lv_calendar_get_month_names(const lv_obj_t * calendar); + +/** + * Get style of a calendar. + * @param calendar pointer to calendar object + * @param type which style should be get + * @return style pointer to the style + * */ +lv_style_t * lv_calendar_get_style(const lv_obj_t * calendar, lv_calendar_style_t type); + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_CALENDAR*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_CALENDAR_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_canvas.c b/bdk/libs/lvgl/lv_objx/lv_canvas.c new file mode 100644 index 00000000..0c6d9dd9 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_canvas.c @@ -0,0 +1,593 @@ +/** + * @file lv_canvas.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_canvas.h" +#if USE_LV_CANVAS != 0 + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_canvas_signal(lv_obj_t * canvas, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_design_func_t ancestor_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a canvas object + * @param par pointer to an object, it will be the parent of the new canvas + * @param copy pointer to a canvas object, if not NULL then the new object will be copied from it + * @return pointer to the created canvas + */ +lv_obj_t * lv_canvas_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("canvas create started"); + + /*Create the ancestor of canvas*/ + lv_obj_t * new_canvas = lv_img_create(par, copy); + lv_mem_assert(new_canvas); + if(new_canvas == NULL) return NULL; + + /*Allocate the canvas type specific extended data*/ + lv_canvas_ext_t * ext = lv_obj_allocate_ext_attr(new_canvas, sizeof(lv_canvas_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_canvas); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_canvas); + + /*Initialize the allocated 'ext' */ + ext->dsc.header.always_zero = 0; + ext->dsc.header.cf = LV_IMG_CF_TRUE_COLOR; + ext->dsc.header.h = 0; + ext->dsc.header.w = 0; + ext->dsc.data_size = 0; + ext->dsc.data = NULL; + + lv_img_set_src(new_canvas, &ext->dsc); + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_canvas, lv_canvas_signal); + + /*Init the new canvas canvas*/ + if(copy == NULL) { + + } + /*Copy an existing canvas*/ + else { + //lv_canvas_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_canvas); + } + + LV_LOG_INFO("canvas created"); + + return new_canvas; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a buffer for the canvas. + * @param buf a buffer where the content of the canvas will be. + * The required size is (lv_img_color_format_get_px_size(cf) * w * h) / 8) + * It can be allocated with `lv_mem_alloc()` or + * it can be statically allocated array (e.g. static lv_color_t buf[100*50]) or + * it can be an address in RAM or external SRAM + * @param canvas pointer to a canvas object + * @param w width of the canvas + * @param h height of the canvas + * @param cf color format. The following formats are supported: + * LV_IMG_CF_TRUE_COLOR, LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, LV_IMG_CF_INDEXES_1/2/4/8BIT + * + */ +void lv_canvas_set_buffer(lv_obj_t * canvas, void * buf, lv_coord_t w, lv_coord_t h, lv_img_cf_t cf) +{ + lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas); + + ext->dsc.header.cf = cf; + ext->dsc.header.w = w; + ext->dsc.header.h = h; + ext->dsc.data = buf; + ext->dsc.data_size = (lv_img_color_format_get_px_size(cf) * w * h) / 8; + + lv_img_set_src(canvas, &ext->dsc); +} +/** + * Set the color of a pixel on the canvas + * @param canvas + * @param x x coordinate of the point to set + * @param y x coordinate of the point to set + * @param c color of the point + */ +void lv_canvas_set_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t c) +{ + + lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas); + if(x >= ext->dsc.header.w || y >= ext->dsc.header.h) { + LV_LOG_WARN("lv_canvas_set_px: x or y out of the canvas"); + return; + } + + uint8_t * buf_u8 = (uint8_t *) ext->dsc.data; + + if(ext->dsc.header.cf == LV_IMG_CF_TRUE_COLOR || + ext->dsc.header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) + { + uint32_t px = ext->dsc.header.w * y * sizeof(lv_color_t) + x * sizeof(lv_color_t); + + memcpy(&buf_u8[px], &c, sizeof(lv_color_t)); + } + else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_1BIT) { + buf_u8 += 4 * 2; + uint8_t bit = x & 0x7; + x = x >> 3; + + uint32_t px = (ext->dsc.header.w >> 3) * y + x; + buf_u8[px] = buf_u8[px] & ~(1 << (7 - bit)); + buf_u8[px] = buf_u8[px] | ((c.full & 0x1) << (7 - bit)); + } + else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_2BIT) { + buf_u8 += 4 * 4; + uint8_t bit = (x & 0x3) * 2; + x = x >> 2; + + uint32_t px = (ext->dsc.header.w >> 2) * y + x; + + buf_u8[px] = buf_u8[px] & ~(3 << (6 - bit)); + buf_u8[px] = buf_u8[px] | ((c.full & 0x3) << (6 - bit)); + } + else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_4BIT) { + buf_u8 += 4 * 16; + uint8_t bit = (x & 0x1) * 4; + x = x >> 1; + + uint32_t px = (ext->dsc.header.w >> 1) * y + x; + + buf_u8[px] = buf_u8[px] & ~(0xF << (4 - bit)); + buf_u8[px] = buf_u8[px] | ((c.full & 0xF) << (4 - bit)); + } + else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_8BIT) { + buf_u8 += 4 * 256; + uint32_t px = ext->dsc.header.w * y + x; + buf_u8[px] = c.full; + } +} + +/** + * Set a style of a canvas. + * @param canvas pointer to canvas object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_canvas_set_style(lv_obj_t * canvas, lv_canvas_style_t type, lv_style_t * style) +{ + switch(type) { + case LV_CANVAS_STYLE_MAIN: + lv_img_set_style(canvas, style); + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the color of a pixel on the canvas + * @param canvas + * @param x x coordinate of the point to set + * @param y x coordinate of the point to set + * @return color of the point + */ +lv_color_t lv_canvas_get_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y) +{ + lv_color_t p_color = LV_COLOR_BLACK; + lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas); + if(x >= ext->dsc.header.w || y >= ext->dsc.header.h) { + LV_LOG_WARN("lv_canvas_get_px: x or y out of the canvas"); + return p_color; + } + + uint8_t * buf_u8 = (uint8_t *) ext->dsc.data; + + if(ext->dsc.header.cf == LV_IMG_CF_TRUE_COLOR || + ext->dsc.header.cf == LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED) + { + uint32_t px = ext->dsc.header.w * y * sizeof(lv_color_t) + x * sizeof(lv_color_t); + memcpy(&p_color, &buf_u8[px], sizeof(lv_color_t)); + } + else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_1BIT) { + buf_u8 += 4 * 2; + uint8_t bit = x & 0x7; + x = x >> 3; + + uint32_t px = (ext->dsc.header.w >> 3) * y + x; + p_color.full = (buf_u8[px] & (1 << (7 - bit))) >> (7 - bit); + } + else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_2BIT) { + buf_u8 += 4 * 4; + uint8_t bit = (x & 0x3) * 2; + x = x >> 2; + + uint32_t px = (ext->dsc.header.w >> 2) * y + x; + p_color.full = (buf_u8[px] & (3 << (6 - bit))) >> (6 - bit); + } + else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_4BIT) { + buf_u8 += 4 * 16; + uint8_t bit = (x & 0x1) * 4; + x = x >> 1; + + uint32_t px = (ext->dsc.header.w >> 1) * y + x; + p_color.full = (buf_u8[px] & (0xF << (4 - bit))) >> (4 - bit); + } + else if(ext->dsc.header.cf == LV_IMG_CF_INDEXED_8BIT) { + buf_u8 += 4 * 256; + uint32_t px = ext->dsc.header.w * y + x; + p_color.full = buf_u8[px]; + } + return p_color; +} + +/** + * Get style of a canvas. + * @param canvas pointer to canvas object + * @param type which style should be get + * @return style pointer to the style + */ +lv_style_t * lv_canvas_get_style(const lv_obj_t * canvas, lv_canvas_style_t type) +{ + // lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas); + lv_style_t * style = NULL; + + switch(type) { + case LV_CANVAS_STYLE_MAIN: + style = lv_img_get_style(canvas); + break; + default: + style = NULL; + } + + return style; +} + +/*===================== + * Other functions + *====================*/ + +/** + * Copy a buffer to the canvas + * @param canvas pointer to a canvas object + * @param to_copy buffer to copy. The color format has to match with the canvas's buffer color format + * @param w width of the buffer to copy + * @param h height of the buffer to copy + * @param x left side of the destination position + * @param y top side of the destination position + */ +void lv_canvas_copy_buf(lv_obj_t * canvas, const void * to_copy, lv_coord_t w, lv_coord_t h, lv_coord_t x, lv_coord_t y) +{ + lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas); + if(x + w >= ext->dsc.header.w || y + h >= ext->dsc.header.h) { + LV_LOG_WARN("lv_canvas_copy_buf: x or y out of the canvas"); + return; + } + + uint32_t px_size = lv_img_color_format_get_px_size(ext->dsc.header.cf) >> 3; + uint32_t px = ext->dsc.header.w * y * px_size + x * px_size; + uint8_t * to_copy8 = (uint8_t *) to_copy; + lv_coord_t i; + for(i = 0; i < h; i++) { + memcpy((void*)&ext->dsc.data[px], to_copy8, w * px_size); + px += ext->dsc.header.w * px_size; + to_copy8 += w * px_size; + } +} + +/** + * Multiply a buffer with the canvas + * @param canvas pointer to a canvas object + * @param to_copy buffer to copy (multiply). LV_IMG_CF_TRUE_COLOR_ALPHA is not supported + * @param w width of the buffer to copy + * @param h height of the buffer to copy + * @param x left side of the destination position + * @param y top side of the destination position + */ +void lv_canvas_mult_buf(lv_obj_t * canvas, void * to_copy, lv_coord_t w, lv_coord_t h, lv_coord_t x, lv_coord_t y) +{ + lv_canvas_ext_t * ext = lv_obj_get_ext_attr(canvas); + if(x + w >= ext->dsc.header.w || y + h >= ext->dsc.header.h) { + LV_LOG_WARN("lv_canvas_mult_buf: x or y out of the canvas"); + return; + } + + if(ext->dsc.header.cf == LV_IMG_CF_TRUE_COLOR_ALPHA) { + LV_LOG_WARN("lv_canvas_mult_buf: LV_IMG_CF_TRUE_COLOR_ALPHA is not supported"); + return; + } + + uint32_t px_size = lv_img_color_format_get_px_size(ext->dsc.header.cf) >> 3; + uint32_t px = ext->dsc.header.w * y * px_size + x * px_size; + lv_color_t * copy_buf_color = (lv_color_t *) to_copy; + lv_color_t * canvas_buf_color = (lv_color_t *) &ext->dsc.data[px]; + + lv_coord_t i; + lv_coord_t j; + for(i = 0; i < h; i++) { + for(j = 0; j < w; j++) { +#if LV_COLOR_DEPTH == 32 + canvas_buf_color[j].red = (uint16_t) ((uint16_t) canvas_buf_color[j].red * copy_buf_color[j].red) >> 8; + canvas_buf_color[j].green = (uint16_t) ((uint16_t) canvas_buf_color[j].green * copy_buf_color[j].green) >> 8; + canvas_buf_color[j].blue = (uint16_t) ((uint16_t) canvas_buf_color[j].blue * copy_buf_color[j].blue) >> 8; +#elif LV_COLOR_DEPTH == 16 + + canvas_buf_color[j].red = (uint16_t) ((uint16_t) canvas_buf_color[j].red * copy_buf_color[j].red) >> 5; + canvas_buf_color[j].blue = (uint16_t) ((uint16_t) canvas_buf_color[j].blue * copy_buf_color[j].blue) >> 5; +# if LV_COLOR_16_SWAP == 0 + canvas_buf_color[j].green = (uint16_t) ((uint16_t) canvas_buf_color[j].green * copy_buf_color[j].green) >> 6; +# else + uint8_t green_canvas = (canvas_buf_color[j].green_h << 3) + (canvas_buf_color[j].green_l); + uint8_t green_buf = (copy_buf_color[j].green_h << 3) + (copy_buf_color[j].green_l); + uint8_t green_res = (uint16_t)((uint16_t)green_canvas * green_buf) >> 6; + canvas_buf_color[j].green_h = (green_res >> 3) & 0x07; + canvas_buf_color[j].green_l = green_res & 0x07; +# endif /*LV_COLOR_16_SWAP*/ + +#elif LV_COLOR_DEPTH == 8 + canvas_buf_color[j].red = (uint16_t) ((uint16_t) canvas_buf_color[j].red * copy_buf_color[j].red) >> 3; + canvas_buf_color[j].green = (uint16_t) ((uint16_t) canvas_buf_color[j].green * copy_buf_color[j].green) >> 3; + canvas_buf_color[j].blue = (uint16_t) ((uint16_t) canvas_buf_color[j].blue * copy_buf_color[j].blue) >> 2; +#endif + } + copy_buf_color += w; + canvas_buf_color += ext->dsc.header.w; + } +} + +/** + * Draw circle function of the canvas + * @param canvas pointer to a canvas object + * @param x0 x coordinate of the circle + * @param y0 y coordinate of the circle + * @param radius radius of the circle + * @param color border color of the circle + */ +void lv_canvas_draw_circle(lv_obj_t * canvas, lv_coord_t x0, lv_coord_t y0, lv_coord_t radius, lv_color_t color) +{ + int x = radius; + int y = 0; + int err = 0; + + while (x >= y) + { + lv_canvas_set_px(canvas, x0 + x, y0 + y, color); + lv_canvas_set_px(canvas, x0 + y, y0 + x, color); + lv_canvas_set_px(canvas, x0 - y, y0 + x, color); + lv_canvas_set_px(canvas, x0 - x, y0 + y, color); + lv_canvas_set_px(canvas, x0 - x, y0 - y, color); + lv_canvas_set_px(canvas, x0 - y, y0 - x, color); + lv_canvas_set_px(canvas, x0 + y, y0 - x, color); + lv_canvas_set_px(canvas, x0 + x, y0 - y, color); + + if (err <= 0) + { + y += 1; + err += 2*y + 1; + } + + if (err > 0) + { + x -= 1; + err -= 2*x + 1; + } + } +} + +/** + * Draw line function of the canvas + * @param canvas pointer to a canvas object + * @param point1 start point of the line + * @param point2 end point of the line + * @param color color of the line + * + * NOTE: The lv_canvas_draw_line function originates from https://github.com/jb55/bresenham-line.c. + */ +/* + * NOTE: The lv_canvas_draw_line function originates from https://github.com/jb55/bresenham-line.c. + */ +void lv_canvas_draw_line(lv_obj_t * canvas, lv_point_t point1, lv_point_t point2, lv_color_t color) +{ + lv_coord_t x0, y0, x1, y1; + + x0 = point1.x; + y0 = point1.y; + x1 = point2.x; + y1 = point2.y; + + int dx = abs(x1-x0), sx = x0dy ? dx : -dy)/2, e2; + + for(;;){ + lv_canvas_set_px(canvas, x0, y0, color); + + if (x0==x1 && y0==y1) break; + e2 = err; + if (e2 >-dx) { err -= dy; x0 += sx; } + if (e2 < dy) { err += dx; y0 += sy; } + } +} + +/** + * Draw triangle function of the canvas + * @param canvas pointer to a canvas object + * @param points edge points of the triangle + * @param color line color of the triangle + */ +void lv_canvas_draw_triangle(lv_obj_t * canvas, lv_point_t * points, lv_color_t color) +{ + lv_canvas_draw_polygon(canvas, points, 3, color); +} + +/** + * Draw rectangle function of the canvas + * @param canvas pointer to a canvas object + * @param points edge points of the rectangle + * @param color line color of the rectangle + */ +void lv_canvas_draw_rect(lv_obj_t * canvas, lv_point_t * points, lv_color_t color) +{ + lv_canvas_draw_polygon(canvas, points, 4, color); +} + +/** + * Draw polygon function of the canvas + * @param canvas pointer to a canvas object + * @param points edge points of the polygon + * @param size edge count of the polygon + * @param color line color of the polygon + */ +void lv_canvas_draw_polygon(lv_obj_t * canvas, lv_point_t * points, size_t size, lv_color_t color) +{ + uint8_t i; + + for(i=0; i < (size - 1); i++) { + lv_canvas_draw_line(canvas, points[i], points[i + 1], color); + } + + lv_canvas_draw_line(canvas, points[size - 1], points[0], color); +} + +/** + * Fill polygon function of the canvas + * @param canvas pointer to a canvas object + * @param points edge points of the polygon + * @param size edge count of the polygon + * @param boundary_color line color of the polygon + * @param fill_color fill color of the polygon + */ +void lv_canvas_fill_polygon(lv_obj_t * canvas, lv_point_t * points, size_t size, lv_color_t boundary_color, lv_color_t fill_color) +{ + uint32_t x = 0, y = 0; + uint8_t i; + + for(i=0; itype[i] == NULL) break; + } + buf->type[i] = "lv_canvas"; + } + + return res; +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_canvas.h b/bdk/libs/lvgl/lv_objx/lv_canvas.h new file mode 100644 index 00000000..cd5827fe --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_canvas.h @@ -0,0 +1,229 @@ +/** + * @file lv_canvas.h + * + */ + +#ifndef LV_CANVAS_H +#define LV_CANVAS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_CANVAS != 0 + +#include "../lv_core/lv_obj.h" +#include "../lv_objx/lv_img.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of canvas*/ +typedef struct { + lv_img_ext_t img; /*Ext. of ancestor*/ + /*New data for this type */ + lv_img_dsc_t dsc; +} lv_canvas_ext_t; + + +/*Styles*/ +enum { + LV_CANVAS_STYLE_MAIN, +}; +typedef uint8_t lv_canvas_style_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a canvas object + * @param par pointer to an object, it will be the parent of the new canvas + * @param copy pointer to a canvas object, if not NULL then the new object will be copied from it + * @return pointer to the created canvas + */ +lv_obj_t * lv_canvas_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a buffer for the canvas. + * @param buf a buffer where the content of the canvas will be. + * The required size is (lv_img_color_format_get_px_size(cf) * w * h) / 8) + * It can be allocated with `lv_mem_alloc()` or + * it can be statically allocated array (e.g. static lv_color_t buf[100*50]) or + * it can be an address in RAM or external SRAM + * @param canvas pointer to a canvas object + * @param w width of the canvas + * @param h height of the canvas + * @param cf color format. The following formats are supported: + * LV_IMG_CF_TRUE_COLOR, LV_IMG_CF_TRUE_COLOR_CHROMA_KEYED, LV_IMG_CF_INDEXES_1/2/4/8BIT + */ +void lv_canvas_set_buffer(lv_obj_t * canvas, void * buf, lv_coord_t w, lv_coord_t h, lv_img_cf_t cf); + +/** + * Set the color of a pixel on the canvas + * @param canvas + * @param x x coordinate of the point to set + * @param y x coordinate of the point to set + * @param c color of the point + */ +void lv_canvas_set_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t c); + +/** + * Set a style of a canvas. + * @param canvas pointer to canvas object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_canvas_set_style(lv_obj_t * canvas, lv_canvas_style_t type, lv_style_t * style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the color of a pixel on the canvas + * @param canvas + * @param x x coordinate of the point to set + * @param y x coordinate of the point to set + * @return color of the point + */ +lv_color_t lv_canvas_get_px(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y); + +/** + * Get style of a canvas. + * @param canvas pointer to canvas object + * @param type which style should be get + * @return style pointer to the style + */ +lv_style_t * lv_canvas_get_style(const lv_obj_t * canvas, lv_canvas_style_t type); + +/*===================== + * Other functions + *====================*/ + +/** + * Copy a buffer to the canvas + * @param canvas pointer to a canvas object + * @param to_copy buffer to copy. The color format has to match with the canvas's buffer color format + * @param w width of the buffer to copy + * @param h height of the buffer to copy + * @param x left side of the destination position + * @param y top side of the destination position + */ +void lv_canvas_copy_buf(lv_obj_t * canvas, const void * to_copy, lv_coord_t w, lv_coord_t h, lv_coord_t x, lv_coord_t y); + +/** + * Multiply a buffer with the canvas + * @param canvas pointer to a canvas object + * @param to_copy buffer to copy (multiply). LV_IMG_CF_TRUE_COLOR_ALPHA is not supported + * @param w width of the buffer to copy + * @param h height of the buffer to copy + * @param x left side of the destination position + * @param y top side of the destination position + */ +void lv_canvas_mult_buf(lv_obj_t * canvas, void * to_copy, lv_coord_t w, lv_coord_t h, lv_coord_t x, lv_coord_t y); + +/** + * Draw circle function of the canvas + * @param canvas pointer to a canvas object + * @param x0 x coordinate of the circle + * @param y0 y coordinate of the circle + * @param radius radius of the circle + * @param color border color of the circle + */ +void lv_canvas_draw_circle(lv_obj_t * canvas, lv_coord_t x0, lv_coord_t y0, lv_coord_t radius, lv_color_t color); + +/** + * Draw line function of the canvas + * @param canvas pointer to a canvas object + * @param point1 start point of the line + * @param point2 end point of the line + * @param color color of the line + * + * NOTE: The lv_canvas_draw_line function originates from https://github.com/jb55/bresenham-line.c. + */ +void lv_canvas_draw_line(lv_obj_t * canvas, lv_point_t point1, lv_point_t point2, lv_color_t color); + +/** + * Draw triangle function of the canvas + * @param canvas pointer to a canvas object + * @param points edge points of the triangle + * @param color line color of the triangle + */ +void lv_canvas_draw_triangle(lv_obj_t * canvas, lv_point_t * points, lv_color_t color); + +/** + * Draw rectangle function of the canvas + * @param canvas pointer to a canvas object + * @param points edge points of the rectangle + * @param color line color of the rectangle + */ +void lv_canvas_draw_rect(lv_obj_t * canvas, lv_point_t * points, lv_color_t color); + +/** + * Draw polygon function of the canvas + * @param canvas pointer to a canvas object + * @param points edge points of the polygon + * @param size edge count of the polygon + * @param color line color of the polygon + */ +void lv_canvas_draw_polygon(lv_obj_t * canvas, lv_point_t * points, size_t size, lv_color_t color); + +/** + * Fill polygon function of the canvas + * @param canvas pointer to a canvas object + * @param points edge points of the polygon + * @param size edge count of the polygon + * @param boundary_color line color of the polygon + * @param fill_color fill color of the polygon + */ +void lv_canvas_fill_polygon(lv_obj_t * canvas, lv_point_t * points, size_t size, lv_color_t boundary_color, lv_color_t fill_color); +/** + * Boundary fill function of the canvas + * @param canvas pointer to a canvas object + * @param x x coordinate of the start position (seed) + * @param y y coordinate of the start position (seed) + * @param boundary_color edge/boundary color of the area + * @param fill_color fill color of the area + */ +void lv_canvas_boundary_fill4(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t boundary_color, lv_color_t fill_color); + +/** + * Flood fill function of the canvas + * @param canvas pointer to a canvas object + * @param x x coordinate of the start position (seed) + * @param y y coordinate of the start position (seed) + * @param fill_color fill color of the area + * @param bg_color background color of the area + */ +void lv_canvas_flood_fill(lv_obj_t * canvas, lv_coord_t x, lv_coord_t y, lv_color_t fill_color, lv_color_t bg_color); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_CANVAS*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_CANVAS_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_cb.c b/bdk/libs/lvgl/lv_objx/lv_cb.c new file mode 100644 index 00000000..6b0277e1 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_cb.c @@ -0,0 +1,347 @@ +/** + * @file lv_cb.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_cb.h" +#if USE_LV_CB != 0 + +#include "../lv_core/lv_group.h" +#include "../lv_themes/lv_theme.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_cb_design(lv_obj_t * cb, const lv_area_t * mask, lv_design_mode_t mode); +static bool lv_bullet_design(lv_obj_t * bullet, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_cb_signal(lv_obj_t * cb, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_design_func_t ancestor_bg_design; +static lv_design_func_t ancestor_bullet_design; +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a check box objects + * @param par pointer to an object, it will be the parent of the new check box + * @param copy pointer to a check box object, if not NULL then the new object will be copied from it + * @return pointer to the created check box + */ +lv_obj_t * lv_cb_create(lv_obj_t * par, const lv_obj_t * copy) +{ + + LV_LOG_TRACE("check box create started"); + + /*Create the ancestor basic object*/ + lv_obj_t * new_cb = lv_btn_create(par, copy); + lv_mem_assert(new_cb); + if(new_cb == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_cb); + if(ancestor_bg_design == NULL) ancestor_bg_design = lv_obj_get_design_func(new_cb); + + lv_cb_ext_t * ext = lv_obj_allocate_ext_attr(new_cb, sizeof(lv_cb_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->bullet = NULL; + ext->label = NULL; + + lv_obj_set_signal_func(new_cb, lv_cb_signal); + lv_obj_set_design_func(new_cb, lv_cb_design); + + /*Init the new checkbox object*/ + if(copy == NULL) { + ext->bullet = lv_btn_create(new_cb, NULL); + if(ancestor_bullet_design == NULL) ancestor_bullet_design = lv_obj_get_design_func(ext->bullet); + lv_obj_set_click(ext->bullet, false); + + ext->label = lv_label_create(new_cb, NULL); + + lv_cb_set_text(new_cb, "Check box"); + lv_btn_set_layout(new_cb, LV_LAYOUT_ROW_M); + lv_btn_set_fit(new_cb, true, true); + lv_btn_set_toggle(new_cb, true); + lv_obj_set_protect(new_cb, LV_PROTECT_PRESS_LOST); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_cb_set_style(new_cb, LV_CB_STYLE_BG, th->cb.bg); + lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_REL, th->cb.box.rel); + lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_PR, th->cb.box.pr); + lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_TGL_REL, th->cb.box.tgl_rel); + lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_TGL_PR, th->cb.box.tgl_pr); + lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_INA, th->cb.box.ina); + } else { + lv_cb_set_style(new_cb, LV_CB_STYLE_BG, &lv_style_transp); + lv_cb_set_style(new_cb, LV_CB_STYLE_BOX_REL, &lv_style_pretty); + } + } else { + lv_cb_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->bullet = lv_btn_create(new_cb, copy_ext->bullet); + ext->label = lv_label_create(new_cb, copy_ext->label); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_cb); + } + + lv_obj_set_design_func(ext->bullet, lv_bullet_design); + + + LV_LOG_INFO("check box created"); + + return new_cb; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the text of a check box + * @param cb pointer to a check box + * @param txt the text of the check box + */ +void lv_cb_set_text(lv_obj_t * cb, const char * txt) +{ + lv_cb_ext_t * ext = lv_obj_get_ext_attr(cb); + lv_label_set_text(ext->label, txt); +} + +/** + * Set a style of a check box + * @param cb pointer to check box object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_cb_set_style(lv_obj_t * cb, lv_cb_style_t type, lv_style_t * style) +{ + lv_cb_ext_t * ext = lv_obj_get_ext_attr(cb); + + switch(type) { + case LV_CB_STYLE_BG: + lv_btn_set_style(cb, LV_BTN_STYLE_REL, style); + lv_btn_set_style(cb, LV_BTN_STYLE_PR, style); + lv_btn_set_style(cb, LV_BTN_STYLE_TGL_REL, style); + lv_btn_set_style(cb, LV_BTN_STYLE_TGL_PR, style); + lv_btn_set_style(cb, LV_BTN_STYLE_INA, style); + break; + case LV_CB_STYLE_BOX_REL: + lv_btn_set_style(ext->bullet, LV_BTN_STYLE_REL, style); + break; + case LV_CB_STYLE_BOX_PR: + lv_btn_set_style(ext->bullet, LV_BTN_STYLE_PR, style); + break; + case LV_CB_STYLE_BOX_TGL_REL: + lv_btn_set_style(ext->bullet, LV_BTN_STYLE_TGL_REL, style); + break; + case LV_CB_STYLE_BOX_TGL_PR: + lv_btn_set_style(ext->bullet, LV_BTN_STYLE_TGL_PR, style); + break; + case LV_CB_STYLE_BOX_INA: + lv_btn_set_style(ext->bullet, LV_BTN_STYLE_INA, style); + break; + } +} + + + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the text of a check box + * @param cb pointer to check box object + * @return pointer to the text of the check box + */ +const char * lv_cb_get_text(const lv_obj_t * cb) +{ + lv_cb_ext_t * ext = lv_obj_get_ext_attr(cb); + return lv_label_get_text(ext->label); +} + + +/** + * Get a style of a button + * @param cb pointer to check box object + * @param type which style should be get + * @return style pointer to the style + * */ +lv_style_t * lv_cb_get_style(const lv_obj_t * cb, lv_cb_style_t type) +{ + lv_style_t * style = NULL; + lv_cb_ext_t * ext = lv_obj_get_ext_attr(cb); + + switch(type) { + case LV_CB_STYLE_BOX_REL: + style = lv_btn_get_style(ext->bullet, LV_BTN_STYLE_REL); + break; + case LV_CB_STYLE_BOX_PR: + style = lv_btn_get_style(ext->bullet, LV_BTN_STYLE_PR); + break; + case LV_CB_STYLE_BOX_TGL_REL: + style = lv_btn_get_style(ext->bullet, LV_BTN_STYLE_TGL_REL); + break; + case LV_CB_STYLE_BOX_TGL_PR: + style = lv_btn_get_style(ext->bullet, LV_BTN_STYLE_TGL_PR); + break; + case LV_CB_STYLE_BOX_INA: + style = lv_btn_get_style(ext->bullet, LV_BTN_STYLE_INA); + break; + default: + style = NULL; + break; + } + + return style; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the check boxes + * @param cb pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_cb_design(lv_obj_t * cb, const lv_area_t * mask, lv_design_mode_t mode) +{ + bool result = true; + + if(mode == LV_DESIGN_COVER_CHK) { + /*Return false if the object is not covers the mask_p area*/ + result = ancestor_bg_design(cb, mask, mode); + } else if(mode == LV_DESIGN_DRAW_MAIN || mode == LV_DESIGN_DRAW_POST) { + lv_cb_ext_t * cb_ext = lv_obj_get_ext_attr(cb); + lv_btn_ext_t * bullet_ext = lv_obj_get_ext_attr(cb_ext->bullet); + + /*Be sure the state of the bullet is the same as the parent button*/ + bullet_ext->state = cb_ext->bg_btn.state; + + result = ancestor_bg_design(cb, mask, mode); + + } else { + result = ancestor_bg_design(cb, mask, mode); + } + + return result; +} + +/** + * Handle the drawing related tasks of the check boxes + * @param bullet pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_bullet_design(lv_obj_t * bullet, const lv_area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + return ancestor_bullet_design(bullet, mask, mode); + } else if(mode == LV_DESIGN_DRAW_MAIN) { +#if USE_LV_GROUP + /* If the check box is the active in a group and + * the background is not visible (transparent or empty) + * then activate the style of the bullet*/ + lv_style_t * style_ori = lv_obj_get_style(bullet); + lv_obj_t * bg = lv_obj_get_parent(bullet); + lv_style_t * style_page = lv_obj_get_style(bg); + lv_group_t * g = lv_obj_get_group(bg); + if(style_page->body.empty != 0 || style_page->body.opa == LV_OPA_TRANSP) { /*Background is visible?*/ + if(lv_group_get_focused(g) == bg) { + lv_style_t * style_mod; + style_mod = lv_group_mod_style(g, style_ori); + bullet->style_p = style_mod; /*Temporally change the style to the activated */ + } + } +#endif + ancestor_bullet_design(bullet, mask, mode); + +#if USE_LV_GROUP + bullet->style_p = style_ori; /*Revert the style*/ +#endif + } else if(mode == LV_DESIGN_DRAW_POST) { + ancestor_bullet_design(bullet, mask, mode); + } + + return true; +} + + +/** + * Signal function of the check box + * @param cb pointer to a check box object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_cb_signal(lv_obj_t * cb, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(cb, sign, param); + if(res != LV_RES_OK) return res; + + lv_cb_ext_t * ext = lv_obj_get_ext_attr(cb); + + if(sign == LV_SIGNAL_STYLE_CHG) { + lv_style_t * label_style = lv_label_get_style(ext->label); + lv_obj_set_size(ext->bullet, lv_font_get_height(label_style->text.font), lv_font_get_height(label_style->text.font)); + lv_btn_set_state(ext->bullet, lv_btn_get_state(cb)); + } else if(sign == LV_SIGNAL_PRESSED || + sign == LV_SIGNAL_RELEASED || + sign == LV_SIGNAL_PRESS_LOST) { + lv_btn_set_state(ext->bullet, lv_btn_get_state(cb)); + } else if(sign == LV_SIGNAL_CONTROLL) { + char c = *((char *)param); + if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_DOWN || + c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_UP || + c == LV_GROUP_KEY_ENTER) { + lv_btn_set_state(ext->bullet, lv_btn_get_state(cb)); + } + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_cb"; + } + + return res; +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_cb.h b/bdk/libs/lvgl/lv_objx/lv_cb.h new file mode 100644 index 00000000..b5877334 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_cb.h @@ -0,0 +1,174 @@ +/** + * @file lv_cb.h + * + */ + +#ifndef LV_CB_H +#define LV_CB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_CB != 0 + +/*Testing of dependencies*/ +#if USE_LV_BTN == 0 +#error "lv_cb: lv_btn is required. Enable it in lv_conf.h (USE_LV_BTN 1) " +#endif + +#if USE_LV_LABEL == 0 +#error "lv_cb: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_btn.h" +#include "lv_label.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Data of check box*/ +typedef struct +{ + lv_btn_ext_t bg_btn; /*Ext. of ancestor*/ + /*New data for this type */ + lv_obj_t * bullet; /*Pointer to button*/ + lv_obj_t * label; /*Pointer to label*/ +} lv_cb_ext_t; + +enum { + LV_CB_STYLE_BG, + LV_CB_STYLE_BOX_REL, + LV_CB_STYLE_BOX_PR, + LV_CB_STYLE_BOX_TGL_REL, + LV_CB_STYLE_BOX_TGL_PR, + LV_CB_STYLE_BOX_INA, +}; +typedef uint8_t lv_cb_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a check box objects + * @param par pointer to an object, it will be the parent of the new check box + * @param copy pointer to a check box object, if not NULL then the new object will be copied from it + * @return pointer to the created check box + */ +lv_obj_t * lv_cb_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the text of a check box + * @param cb pointer to a check box + * @param txt the text of the check box + */ +void lv_cb_set_text(lv_obj_t * cb, const char * txt); + +/** + * Set the state of the check box + * @param cb pointer to a check box object + * @param checked true: make the check box checked; false: make it unchecked + */ +static inline void lv_cb_set_checked(lv_obj_t * cb, bool checked) +{ + lv_btn_set_state(cb, checked ? LV_BTN_STATE_TGL_REL : LV_BTN_STATE_REL); +} + +/** + * Make the check box inactive (disabled) + * @param cb pointer to a check box object + */ +static inline void lv_cb_set_inactive(lv_obj_t * cb) +{ + lv_btn_set_state(cb, LV_BTN_STATE_INA); +} + +/** + * Set a function to call when the check box is clicked + * @param cb pointer to a check box object + */ +static inline void lv_cb_set_action(lv_obj_t * cb, lv_action_t action) +{ + lv_btn_set_action(cb, LV_BTN_ACTION_CLICK, action); +} + + +/** + * Set a style of a check box + * @param cb pointer to check box object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_cb_set_style(lv_obj_t * cb, lv_cb_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the text of a check box + * @param cb pointer to check box object + * @return pointer to the text of the check box + */ +const char * lv_cb_get_text(const lv_obj_t * cb); + +/** + * Get the current state of the check box + * @param cb pointer to a check box object + * @return true: checked; false: not checked + */ +static inline bool lv_cb_is_checked(const lv_obj_t * cb) +{ + return lv_btn_get_state(cb) == LV_BTN_STATE_REL ? false : true; +} + +/** + * Get the action of a check box + * @param cb pointer to a button object + * @return pointer to the action function + */ +static inline lv_action_t lv_cb_get_action(const lv_obj_t * cb) +{ + return lv_btn_get_action(cb, LV_BTN_ACTION_CLICK); +} + + +/** + * Get a style of a button + * @param cb pointer to check box object + * @param type which style should be get + * @return style pointer to the style + * */ +lv_style_t * lv_cb_get_style(const lv_obj_t * cb, lv_cb_style_t type); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_CB*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_CB_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_chart.c b/bdk/libs/lvgl/lv_objx/lv_chart.c new file mode 100644 index 00000000..0060c149 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_chart.c @@ -0,0 +1,824 @@ +/** + * @file lv_chart.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_chart.h" +#if USE_LV_CHART != 0 + +#include "../lv_draw/lv_draw.h" +#include "../lv_themes/lv_theme.h" + +/********************* + * DEFINES + *********************/ +#define LV_CHART_YMIN_DEF 0 +#define LV_CHART_YMAX_DEF 100 +#define LV_CHART_HDIV_DEF 3 +#define LV_CHART_VDIV_DEF 5 +#define LV_CHART_PNUM_DEF 10 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_chart_design(lv_obj_t * chart, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_chart_signal(lv_obj_t * chart, lv_signal_t sign, void * param); +static void lv_chart_draw_div(lv_obj_t * chart, const lv_area_t * mask); +static void lv_chart_draw_lines(lv_obj_t * chart, const lv_area_t * mask); +static void lv_chart_draw_points(lv_obj_t * chart, const lv_area_t * mask); +static void lv_chart_draw_cols(lv_obj_t * chart, const lv_area_t * mask); +static void lv_chart_draw_vertical_lines(lv_obj_t * chart, const lv_area_t * mask); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_design_func_t ancestor_design_f; +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a chart background objects + * @param par pointer to an object, it will be the parent of the new chart background + * @param copy pointer to a chart background object, if not NULL then the new object will be copied from it + * @return pointer to the created chart background + */ +lv_obj_t * lv_chart_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("chart create started"); + + /*Create the ancestor basic object*/ + lv_obj_t * new_chart = lv_obj_create(par, copy); + lv_mem_assert(new_chart); + if(new_chart == NULL) return NULL; + + /*Allocate the object type specific extended data*/ + lv_chart_ext_t * ext = lv_obj_allocate_ext_attr(new_chart, sizeof(lv_chart_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + lv_ll_init(&ext->series_ll, sizeof(lv_chart_series_t)); + ext->series.num = 0; + ext->ymin = LV_CHART_YMIN_DEF; + ext->ymax = LV_CHART_YMAX_DEF; + ext->hdiv_cnt = LV_CHART_HDIV_DEF; + ext->vdiv_cnt = LV_CHART_VDIV_DEF; + ext->point_cnt = LV_CHART_PNUM_DEF; + ext->type = LV_CHART_TYPE_LINE; + ext->series.opa = LV_OPA_COVER; + ext->series.dark = LV_OPA_50; + ext->series.width = 2; + + if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_chart); + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_chart); + + lv_obj_set_signal_func(new_chart, lv_chart_signal); + lv_obj_set_design_func(new_chart, lv_chart_design); + + /*Init the new chart background object*/ + if(copy == NULL) { + lv_obj_set_size(new_chart, LV_HOR_RES / 3, LV_VER_RES / 3); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_chart_set_style(new_chart, th->chart); + } else { + lv_chart_set_style(new_chart, &lv_style_pretty); + } + + } else { + lv_chart_ext_t * ext_copy = lv_obj_get_ext_attr(copy); + ext->type = ext_copy->type; + ext->ymin = ext_copy->ymin; + ext->ymax = ext_copy->ymax; + ext->hdiv_cnt = ext_copy->hdiv_cnt; + ext->vdiv_cnt = ext_copy->vdiv_cnt; + ext->point_cnt = ext_copy->point_cnt; + ext->series.opa = ext_copy->series.opa; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_chart); + } + + LV_LOG_INFO("chart created"); + + + return new_chart; +} + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Allocate and add a data series to the chart + * @param chart pointer to a chart object + * @param color color of the data series + * @return pointer to the allocated data series + */ +lv_chart_series_t * lv_chart_add_series(lv_obj_t * chart, lv_color_t color) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + lv_chart_series_t * ser = lv_ll_ins_head(&ext->series_ll); + lv_mem_assert(ser); + if(ser == NULL) return NULL; + + lv_coord_t def = LV_CHART_POINT_DEF; + + if(ser == NULL) return NULL; + + ser->color = color; + + ser->points = lv_mem_alloc(sizeof(lv_coord_t) * ext->point_cnt); + lv_mem_assert(ser->points); + if(ser->points == NULL) { + lv_ll_rem(&ext->series_ll, ser); + lv_mem_free(ser); + return NULL; + } + + ser->start_point = 0; + + uint16_t i; + lv_coord_t * p_tmp = ser->points; + for(i = 0; i < ext->point_cnt; i++) { + *p_tmp = def; + p_tmp++; + } + + ext->series.num++; + + return ser; +} + +/** + * Clear the point of a serie + * @param chart pointer to a chart object + * @param serie pointer to the chart's serie to clear + */ +void lv_chart_clear_serie(lv_obj_t * chart, lv_chart_series_t * serie) +{ + if(chart == NULL || serie == NULL) + return; + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + if(ext == NULL) return; + + uint32_t i; + for(i = 0; i < ext->point_cnt; i++) + { + serie->points[i] = LV_CHART_POINT_DEF; + } + + serie->start_point = 0; + +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the number of horizontal and vertical division lines + * @param chart pointer to a graph background object + * @param hdiv number of horizontal division lines + * @param vdiv number of vertical division lines + */ +void lv_chart_set_div_line_count(lv_obj_t * chart, uint8_t hdiv, uint8_t vdiv) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + if(ext->hdiv_cnt == hdiv && ext->vdiv_cnt == vdiv) return; + + ext->hdiv_cnt = hdiv; + ext->vdiv_cnt = vdiv; + + lv_obj_invalidate(chart); +} + +/** + * Set the minimal and maximal y values + * @param chart pointer to a graph background object + * @param ymin y minimum value + * @param ymax y maximum value + */ +void lv_chart_set_range(lv_obj_t * chart, lv_coord_t ymin, lv_coord_t ymax) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + if(ext->ymin == ymin && ext->ymax == ymax) return; + + ext->ymin = ymin; + ext->ymax = ymax; + + lv_chart_refresh(chart); +} + +/** + * Set a new type for a chart + * @param chart pointer to a chart object + * @param type new type of the chart (from 'lv_chart_type_t' enum) + */ +void lv_chart_set_type(lv_obj_t * chart, lv_chart_type_t type) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + if(ext->type == type) return; + + ext->type = type; + + lv_chart_refresh(chart); +} + +/** + * Set the number of points on a data line on a chart + * @param chart pointer r to chart object + * @param point_cnt new number of points on the data lines + */ +void lv_chart_set_point_count(lv_obj_t * chart, uint16_t point_cnt) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + if(ext->point_cnt == point_cnt) return; + + lv_chart_series_t * ser; + uint16_t point_cnt_old = ext->point_cnt; + uint16_t i; + lv_coord_t def = LV_CHART_POINT_DEF; + + if(point_cnt < 1) point_cnt = 1; + + LL_READ_BACK(ext->series_ll, ser) { + if(ser->start_point != 0) { + lv_coord_t * new_points = lv_mem_alloc(sizeof(lv_coord_t) * point_cnt); + lv_mem_assert(new_points); + if(new_points == NULL) return; + + if(point_cnt >= point_cnt_old) { + for(i = 0; i < point_cnt_old; i++) { + new_points[i] = ser->points[(i + ser->start_point) % point_cnt_old]; /*Copy old contents to new array*/ + } + for(i = point_cnt_old; i < point_cnt; i++) { + new_points[i] = def; /*Fill up the rest with default value*/ + } + } else { + for(i = 0; i < point_cnt; i++) { + new_points[i] = ser->points[(i + ser->start_point) % point_cnt_old]; /*Copy old contents to new array*/ + } + } + + /*Switch over pointer from old to new*/ + lv_mem_free(ser->points); + ser->points = new_points; + } else { + ser->points = lv_mem_realloc(ser->points, sizeof(lv_coord_t) * point_cnt); + lv_mem_assert(ser->points); + if(ser->points == NULL) return; + /*Initialize the new points*/ + if(point_cnt > point_cnt_old) { + for(i = point_cnt_old - 1; i < point_cnt; i++) { + ser->points[i] = def; + } + } + } + + ser->start_point = 0; + } + + ext->point_cnt = point_cnt; + + lv_chart_refresh(chart); +} + +/** + * Set the opacity of the data series + * @param chart pointer to a chart object + * @param opa opacity of the data series + */ +void lv_chart_set_series_opa(lv_obj_t * chart, lv_opa_t opa) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + if(ext->series.opa == opa) return; + + ext->series.opa = opa; + lv_obj_invalidate(chart); +} + +/** + * Set the line width or point radius of the data series + * @param chart pointer to a chart object + * @param width the new width + */ +void lv_chart_set_series_width(lv_obj_t * chart, lv_coord_t width) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + if(ext->series.width == width) return; + + ext->series.width = width; + lv_obj_invalidate(chart); +} +/** + * Set the dark effect on the bottom of the points or columns + * @param chart pointer to a chart object + * @param dark_eff dark effect level (LV_OPA_TRANSP to turn off) + */ +void lv_chart_set_series_darking(lv_obj_t * chart, lv_opa_t dark_eff) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + if(ext->series.dark == dark_eff) return; + + ext->series.dark = dark_eff; + lv_obj_invalidate(chart); +} + +/** + * Initialize all data points with a value + * @param chart pointer to chart object + * @param ser pointer to a data series on 'chart' + * @param y the new value for all points + */ +void lv_chart_init_points(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t y) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + uint16_t i; + for(i = 0; i < ext->point_cnt; i++) { + ser->points[i] = y; + } + ser->start_point = 0; + lv_chart_refresh(chart); +} + +/** + * Set the value s of points from an array + * @param chart pointer to chart object + * @param ser pointer to a data series on 'chart' + * @param y_array array of 'lv_coord_t' points (with 'points count' elements ) + */ +void lv_chart_set_points(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t * y_array) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + memcpy(ser->points, y_array, ext->point_cnt * (sizeof(lv_coord_t))); + ser->start_point = 0; + lv_chart_refresh(chart); +} + +/** + * Shift all data left and set the rightmost data on a data line + * @param chart pointer to chart object + * @param ser pointer to a data series on 'chart' + * @param y the new value of the rightmost data + */ +void lv_chart_set_next(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t y) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + + ser->points[ser->start_point] = y; /*This was the place of the former left most value, after shifting it is the rightmost*/ + ser->start_point = (ser->start_point + 1) % ext->point_cnt; + + lv_chart_refresh(chart); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the type of a chart + * @param chart pointer to chart object + * @return type of the chart (from 'lv_chart_t' enum) + */ +lv_chart_type_t lv_chart_get_type(const lv_obj_t * chart) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + return ext->type; +} + +/** + * Get the data point number per data line on chart + * @param chart pointer to chart object + * @return point number on each data line + */ +uint16_t lv_chart_get_point_cnt(const lv_obj_t * chart) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + return ext->point_cnt; +} + +/** + * Get the opacity of the data series + * @param chart pointer to chart object + * @return the opacity of the data series + */ +lv_opa_t lv_chart_get_series_opa(const lv_obj_t * chart) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + return ext->series.opa; +} + +/** + * Get the data series width + * @param chart pointer to chart object + * @return the width the data series (lines or points) + */ +lv_coord_t lv_chart_get_series_width(const lv_obj_t * chart) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + return ext->series.width; +} + +/** + * Get the dark effect level on the bottom of the points or columns + * @param chart pointer to chart object + * @return dark effect level (LV_OPA_TRANSP to turn off) + */ +lv_opa_t lv_chart_get_series_darking(const lv_obj_t * chart) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + return ext->series.dark; +} + +/*===================== + * Other functions + *====================*/ + +/** + * Refresh a chart if its data line has changed + * @param chart pointer to chart object + */ +void lv_chart_refresh(lv_obj_t * chart) +{ + lv_obj_invalidate(chart); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the chart backgrounds + * @param chart pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_chart_design(lv_obj_t * chart, const lv_area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + /*Return false if the object is not covers the mask_p area*/ + return ancestor_design_f(chart, mask, mode); + } else if(mode == LV_DESIGN_DRAW_MAIN) { + /*Draw the background*/ + lv_draw_rect(&chart->coords, mask, lv_obj_get_style(chart), lv_obj_get_opa_scale(chart)); + + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + + lv_chart_draw_div(chart, mask); + + if(ext->type & LV_CHART_TYPE_LINE) lv_chart_draw_lines(chart, mask); + if(ext->type & LV_CHART_TYPE_COLUMN) lv_chart_draw_cols(chart, mask); + if(ext->type & LV_CHART_TYPE_POINT) lv_chart_draw_points(chart, mask); + if(ext->type & LV_CHART_TYPE_VERTICAL_LINE) lv_chart_draw_vertical_lines(chart, mask); + } + return true; +} + +/** + * Signal function of the chart background + * @param chart pointer to a chart background object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + */ +static lv_res_t lv_chart_signal(lv_obj_t * chart, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(chart, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_CLEANUP) { + lv_coord_t ** datal; + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + LL_READ(ext->series_ll, datal) { + lv_mem_free(*datal); + } + lv_ll_clear(&ext->series_ll); + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_chart"; + } + + return res; +} + +/** + * Draw the division lines on chart background + * @param chart pointer to chart object + * @param mask mask, inherited from the design function + */ +static void lv_chart_draw_div(lv_obj_t * chart, const lv_area_t * mask) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + lv_style_t * style = lv_obj_get_style(chart); + lv_opa_t opa_scale = lv_obj_get_opa_scale(chart); + + uint8_t div_i; + uint8_t div_i_end; + uint8_t div_i_start; + lv_point_t p1; + lv_point_t p2; + lv_coord_t w = lv_obj_get_width(chart); + lv_coord_t h = lv_obj_get_height(chart); + lv_coord_t x_ofs = chart->coords.x1; + lv_coord_t y_ofs = chart->coords.y1; + + if(ext->hdiv_cnt != 0) { + /*Draw slide lines if no border*/ + if(style->body.border.width != 0) { + div_i_start = 1; + div_i_end = ext->hdiv_cnt; + } else { + div_i_start = 0; + div_i_end = ext->hdiv_cnt + 1; + } + + p1.x = 0 + x_ofs; + p2.x = w + x_ofs; + for(div_i = div_i_start; div_i <= div_i_end; div_i++) { + p1.y = (int32_t)((int32_t)h * div_i) / (ext->hdiv_cnt + 1); + p1.y += y_ofs; + if(div_i == div_i_start) p1.y += (style->line.width >> 1) + 1; /*The first line might not be visible*/ + if(div_i == div_i_end) p1.y -= (style->line.width >> 1) + 1; /*The last line might not be visible*/ + + p2.y = p1.y; + lv_draw_line(&p1, &p2, mask, style, opa_scale); + } + } + + if(ext->vdiv_cnt != 0) { + /*Draw slide lines if no border*/ + if(style->body.border.width != 0) { + div_i_start = 1; + div_i_end = ext->vdiv_cnt; + } else { + div_i_start = 0; + div_i_end = ext->vdiv_cnt + 1; + } + + p1.y = 0 + y_ofs; + p2.y = h + y_ofs; + for(div_i = div_i_start; div_i <= div_i_end; div_i ++) { + p1.x = (int32_t)((int32_t)w * div_i) / (ext->vdiv_cnt + 1); + p1.x += x_ofs; + if(div_i == div_i_start) p1.x += (style->line.width >> 1) + 1; /*The first line might not be visible*/ + if(div_i == div_i_end) p1.x -= (style->line.width >> 1) + 1; /*The last line might not be visible*/ + p2.x = p1.x; + lv_draw_line(&p1, &p2, mask, style, opa_scale); + } + } +} + +/** + * Draw the data lines as lines on a chart + * @param obj pointer to chart object + */ +static void lv_chart_draw_lines(lv_obj_t * chart, const lv_area_t * mask) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + + uint16_t i; + lv_point_t p1; + lv_point_t p2; + lv_coord_t w = lv_obj_get_width(chart); + lv_coord_t h = lv_obj_get_height(chart); + lv_coord_t x_ofs = chart->coords.x1; + lv_coord_t y_ofs = chart->coords.y1; + int32_t y_tmp; + lv_coord_t p_prev; + lv_coord_t p_act; + lv_chart_series_t * ser; + lv_opa_t opa_scale = lv_obj_get_opa_scale(chart); + lv_style_t style; + lv_style_copy(&style, &lv_style_plain); + style.line.opa = ext->series.opa; + style.line.width = ext->series.width; + + /*Go through all data lines*/ + LL_READ_BACK(ext->series_ll, ser) { + style.line.color = ser->color; + + p1.x = 0 + x_ofs; + p2.x = 0 + x_ofs; + + p_prev = ser->start_point; + y_tmp = (int32_t)((int32_t) ser->points[p_prev] - ext->ymin) * h; + y_tmp = y_tmp / (ext->ymax - ext->ymin); + p2.y = h - y_tmp + y_ofs; + + for(i = 1; i < ext->point_cnt; i ++) { + p1.x = p2.x; + p1.y = p2.y; + + p2.x = ((w * i) / (ext->point_cnt - 1)) + x_ofs; + + p_act = (ser->start_point + i) % ext->point_cnt; + + y_tmp = (int32_t)((int32_t) ser->points[p_act] - ext->ymin) * h; + y_tmp = y_tmp / (ext->ymax - ext->ymin); + p2.y = h - y_tmp + y_ofs; + + if(ser->points[p_prev] != LV_CHART_POINT_DEF && ser->points[p_act] != LV_CHART_POINT_DEF) + lv_draw_line(&p1, &p2, mask, &style, opa_scale); + + p_prev = p_act; + } + } +} + +/** + * Draw the data lines as points on a chart + * @param chart pointer to chart object + * @param mask mask, inherited from the design function + */ +static void lv_chart_draw_points(lv_obj_t * chart, const lv_area_t * mask) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + + uint16_t i; + lv_area_t cir_a; + lv_coord_t w = lv_obj_get_width(chart); + lv_coord_t h = lv_obj_get_height(chart); + lv_coord_t x_ofs = chart->coords.x1; + lv_coord_t y_ofs = chart->coords.y1; + int32_t y_tmp; + lv_coord_t p_act; + lv_chart_series_t * ser; + uint8_t series_cnt = 0; + lv_style_t style_point; + lv_style_copy(&style_point, &lv_style_plain); + + style_point.body.border.width = 0; + style_point.body.empty = 0; + style_point.body.radius = LV_RADIUS_CIRCLE; + style_point.body.opa = ext->series.opa; + style_point.body.radius = ext->series.width; + + /*Go through all data lines*/ + LL_READ_BACK(ext->series_ll, ser) { + style_point.body.main_color = ser->color; + style_point.body.grad_color = lv_color_mix(LV_COLOR_BLACK, ser->color, ext->series.dark); + + for(i = 0; i < ext->point_cnt; i ++) { + cir_a.x1 = ((w * i) / (ext->point_cnt - 1)) + x_ofs; + cir_a.x2 = cir_a.x1 + style_point.body.radius; + cir_a.x1 -= style_point.body.radius; + p_act = (ser->start_point + i) % ext->point_cnt; + y_tmp = (int32_t)((int32_t) ser->points[p_act] - ext->ymin) * h; + y_tmp = y_tmp / (ext->ymax - ext->ymin); + cir_a.y1 = h - y_tmp + y_ofs; + cir_a.y2 = cir_a.y1 + style_point.body.radius; + cir_a.y1 -= style_point.body.radius; + + if(ser->points[p_act] != LV_CHART_POINT_DEF) + lv_draw_rect(&cir_a, mask, &style_point, lv_obj_get_opa_scale(chart)); + } + series_cnt++; + } +} + +/** + * Draw the data lines as columns on a chart + * @param chart pointer to chart object + * @param mask mask, inherited from the design function + */ +static void lv_chart_draw_cols(lv_obj_t * chart, const lv_area_t * mask) +{ + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + + uint16_t i; + lv_area_t col_a; + lv_area_t col_mask; + bool mask_ret; + lv_coord_t w = lv_obj_get_width(chart); + lv_coord_t h = lv_obj_get_height(chart); + int32_t y_tmp; + lv_chart_series_t * ser; + lv_style_t rects; + lv_coord_t col_w = w / ((ext->series.num + 1) * ext->point_cnt); /* Suppose + 1 series as separator*/ + lv_coord_t x_ofs = col_w / 2; /*Shift with a half col.*/ + + lv_style_copy(&rects, &lv_style_plain); + rects.body.border.width = 0; + rects.body.empty = 0; + rects.body.radius = 0; + rects.body.opa = ext->series.opa; + + col_a.y2 = chart->coords.y2; + + lv_coord_t x_act; + + /*Go through all points*/ + for(i = 0; i < ext->point_cnt; i ++) { + x_act = (int32_t)((int32_t) w * i) / ext->point_cnt; + x_act += chart->coords.x1 + x_ofs; + + /*Draw the current point of all data line*/ + LL_READ_BACK(ext->series_ll, ser) { + rects.body.main_color = ser->color; + rects.body.grad_color = lv_color_mix(LV_COLOR_BLACK, ser->color, ext->series.dark); + col_a.x1 = x_act; + col_a.x2 = col_a.x1 + col_w; + x_act += col_w; + + lv_coord_t p_act = (ser->start_point + i) % ext->point_cnt; + y_tmp = (int32_t)((int32_t) ser->points[p_act] - ext->ymin) * h; + y_tmp = y_tmp / (ext->ymax - ext->ymin); + col_a.y1 = h - y_tmp + chart->coords.y1; + + mask_ret = lv_area_intersect(&col_mask, mask, &col_a); + if(mask_ret != false && ser->points[p_act] != LV_CHART_POINT_DEF) { + lv_draw_rect(&chart->coords, &col_mask, &rects, lv_obj_get_opa_scale(chart)); + } + } + } +} + +/** + * Draw the data lines as vertical lines on a chart if there is only 1px between point + * @param obj pointer to chart object + */ +static void lv_chart_draw_vertical_lines(lv_obj_t * chart, const lv_area_t * mask) +{ + + lv_chart_ext_t * ext = lv_obj_get_ext_attr(chart); + lv_coord_t w = lv_obj_get_width(chart); + /*Vertical lines works only if the width == point count. Else use the normal line type*/ + if(ext->point_cnt != w) { + lv_chart_draw_lines(chart, mask); + return; + } + + uint16_t i; + lv_point_t p1; + lv_point_t p2; + lv_coord_t h = lv_obj_get_height(chart); + lv_coord_t x_ofs = chart->coords.x1; + lv_coord_t y_ofs = chart->coords.y1; + int32_t y_tmp; + lv_chart_series_t * ser; + lv_opa_t opa_scale = lv_obj_get_opa_scale(chart); + lv_style_t style; + lv_style_copy(&style, &lv_style_plain); + style.line.opa = ext->series.opa; + style.line.width = ext->series.width; + + /*Go through all data lines*/ + LL_READ_BACK(ext->series_ll, ser) { + style.line.color = ser->color; + + p1.x = 0 + x_ofs; + p2.x = 0 + x_ofs; + y_tmp = (int32_t)((int32_t) ser->points[0] - ext->ymin) * h; + y_tmp = y_tmp / (ext->ymax - ext->ymin); + p2.y = h - y_tmp + y_ofs; + p1.y = p2.y; + + for(i = 0; i < ext->point_cnt; i++) + { + + y_tmp = (int32_t)((int32_t) ser->points[i] - ext->ymin) * h; + y_tmp = y_tmp / (ext->ymax - ext->ymin); + p2.y = h - y_tmp + y_ofs; + + if(p1.y == p2.y) + { + p2.x++; + } + + if(ser->points[i] != LV_CHART_POINT_DEF) { + lv_draw_line(&p1, &p2, mask, &style, opa_scale); + } + + p2.x = ((w * i) / (ext->point_cnt - 1)) + x_ofs; + p1.x = p2.x; + p1.y = p2.y; + } + } +} +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_chart.h b/bdk/libs/lvgl/lv_objx/lv_chart.h new file mode 100644 index 00000000..baea9d0f --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_chart.h @@ -0,0 +1,262 @@ +/** + * @file lv_chart.h + * + */ + +#ifndef LV_CHART_H +#define LV_CHART_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_CHART != 0 + +#include "../lv_core/lv_obj.h" +#include "lv_line.h" + +/********************* + * DEFINES + *********************/ +#define LV_CHART_POINT_DEF (LV_COORD_MIN) + +/********************** + * TYPEDEFS + **********************/ +typedef struct +{ + lv_coord_t * points; + lv_color_t color; + uint16_t start_point; +} lv_chart_series_t; + +/*Data of chart */ +typedef struct +{ + /*No inherited ext*/ /*Ext. of ancestor*/ + /*New data for this type */ + lv_ll_t series_ll; /*Linked list for the data line pointers (stores lv_chart_dl_t)*/ + lv_coord_t ymin; /*y min value (used to scale the data)*/ + lv_coord_t ymax; /*y max value (used to scale the data)*/ + uint8_t hdiv_cnt; /*Number of horizontal division lines*/ + uint8_t vdiv_cnt; /*Number of vertical division lines*/ + uint16_t point_cnt; /*Point number in a data line*/ + uint8_t type :4; /*Line, column or point chart (from 'lv_chart_type_t')*/ + struct { + lv_coord_t width; /*Line width or point radius*/ + uint8_t num; /*Number of data lines in dl_ll*/ + lv_opa_t opa; /*Opacity of data lines*/ + lv_opa_t dark; /*Dark level of the point/column bottoms*/ + } series; +} lv_chart_ext_t; + +/*Chart types*/ +enum +{ + LV_CHART_TYPE_LINE = 0x01, /*Connect the points with lines*/ + LV_CHART_TYPE_COLUMN = 0x02, /*Draw columns*/ + LV_CHART_TYPE_POINT = 0x04, /*Draw circles on the points*/ + LV_CHART_TYPE_VERTICAL_LINE = 0x08, /*Draw vertical lines on points (useful when chart width == point count)*/ +}; +typedef uint8_t lv_chart_type_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a chart background objects + * @param par pointer to an object, it will be the parent of the new chart background + * @param copy pointer to a chart background object, if not NULL then the new object will be copied from it + * @return pointer to the created chart background + */ +lv_obj_t * lv_chart_create(lv_obj_t * par, const lv_obj_t * copy); + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Allocate and add a data series to the chart + * @param chart pointer to a chart object + * @param color color of the data series + * @return pointer to the allocated data series + */ +lv_chart_series_t * lv_chart_add_series(lv_obj_t * chart, lv_color_t color); + +/** + * Clear the point of a serie + * @param chart pointer to a chart object + * @param serie pointer to the chart's serie to clear + */ +void lv_chart_clear_serie(lv_obj_t * chart, lv_chart_series_t * serie); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the number of horizontal and vertical division lines + * @param chart pointer to a graph background object + * @param hdiv number of horizontal division lines + * @param vdiv number of vertical division lines + */ +void lv_chart_set_div_line_count(lv_obj_t * chart, uint8_t hdiv, uint8_t vdiv); + +/** + * Set the minimal and maximal y values + * @param chart pointer to a graph background object + * @param ymin y minimum value + * @param ymax y maximum value + */ +void lv_chart_set_range(lv_obj_t * chart, lv_coord_t ymin, lv_coord_t ymax); + +/** + * Set a new type for a chart + * @param chart pointer to a chart object + * @param type new type of the chart (from 'lv_chart_type_t' enum) + */ +void lv_chart_set_type(lv_obj_t * chart, lv_chart_type_t type); + +/** + * Set the number of points on a data line on a chart + * @param chart pointer r to chart object + * @param point_cnt new number of points on the data lines + */ +void lv_chart_set_point_count(lv_obj_t * chart, uint16_t point_cnt); + +/** + * Set the opacity of the data series + * @param chart pointer to a chart object + * @param opa opacity of the data series + */ +void lv_chart_set_series_opa(lv_obj_t * chart, lv_opa_t opa); + +/** + * Set the line width or point radius of the data series + * @param chart pointer to a chart object + * @param width the new width + */ +void lv_chart_set_series_width(lv_obj_t * chart, lv_coord_t width); + +/** + * Set the dark effect on the bottom of the points or columns + * @param chart pointer to a chart object + * @param dark_eff dark effect level (LV_OPA_TRANSP to turn off) + */ +void lv_chart_set_series_darking(lv_obj_t * chart, lv_opa_t dark_eff); + +/** + * Initialize all data points with a value + * @param chart pointer to chart object + * @param ser pointer to a data series on 'chart' + * @param y the new value for all points + */ +void lv_chart_init_points(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t y); + +/** + * Set the value s of points from an array + * @param chart pointer to chart object + * @param ser pointer to a data series on 'chart' + * @param y_array array of 'lv_coord_t' points (with 'points count' elements ) + */ +void lv_chart_set_points(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t * y_array); + +/** + * Shift all data right and set the most right data on a data line + * @param chart pointer to chart object + * @param ser pointer to a data series on 'chart' + * @param y the new value of the most right data + */ +void lv_chart_set_next(lv_obj_t * chart, lv_chart_series_t * ser, lv_coord_t y); + +/** + * Set the style of a chart + * @param chart pointer to a chart object + * @param style pointer to a style + */ +static inline void lv_chart_set_style(lv_obj_t *chart, lv_style_t *style) +{ + lv_obj_set_style(chart, style); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the type of a chart + * @param chart pointer to chart object + * @return type of the chart (from 'lv_chart_t' enum) + */ +lv_chart_type_t lv_chart_get_type(const lv_obj_t * chart); + +/** + * Get the data point number per data line on chart + * @param chart pointer to chart object + * @return point number on each data line + */ +uint16_t lv_chart_get_point_cnt(const lv_obj_t * chart); + +/** + * Get the opacity of the data series + * @param chart pointer to chart object + * @return the opacity of the data series + */ +lv_opa_t lv_chart_get_series_opa(const lv_obj_t * chart); + +/** + * Get the data series width + * @param chart pointer to chart object + * @return the width the data series (lines or points) + */ +lv_coord_t lv_chart_get_series_width(const lv_obj_t * chart); + +/** + * Get the dark effect level on the bottom of the points or columns + * @param chart pointer to chart object + * @return dark effect level (LV_OPA_TRANSP to turn off) + */ +lv_opa_t lv_chart_get_series_darking(const lv_obj_t * chart); + +/** + * Get the style of an chart object + * @param chart pointer to an chart object + * @return pointer to the chart's style + */ +static inline lv_style_t* lv_chart_get_style(const lv_obj_t *chart) +{ + return lv_obj_get_style(chart); +} + +/*===================== + * Other functions + *====================*/ + +/** + * Refresh a chart if its data line has changed + * @param chart pointer to chart object + */ +void lv_chart_refresh(lv_obj_t * chart); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_CHART*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_CHART_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_cont.c b/bdk/libs/lvgl/lv_objx/lv_cont.c new file mode 100644 index 00000000..bd84cd94 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_cont.c @@ -0,0 +1,642 @@ +/** + * @file lv_cont.c + * + */ + +/********************* + * INCLUDES + *********************/ + +#include "lv_cont.h" +#if USE_LV_CONT != 0 + +#include +#include + +#include "../lv_draw/lv_draw.h" +#include "../lv_draw/lv_draw_vbasic.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_area.h" +#include "../lv_misc/lv_color.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_cont_signal(lv_obj_t * cont, lv_signal_t sign, void * param); +static void lv_cont_refr_layout(lv_obj_t * cont); +static void lv_cont_layout_col(lv_obj_t * cont); +static void lv_cont_layout_row(lv_obj_t * cont); +static void lv_cont_layout_center(lv_obj_t * cont); +static void lv_cont_layout_pretty(lv_obj_t * cont); +static void lv_cont_layout_grid(lv_obj_t * cont); +static void lv_cont_refr_autofit(lv_obj_t * cont); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a container objects + * @param par pointer to an object, it will be the parent of the new container + * @param copy pointer to a container object, if not NULL then the new object will be copied from it + * @return pointer to the created container + */ +lv_obj_t * lv_cont_create(lv_obj_t * par, const lv_obj_t * copy) +{ + + + LV_LOG_TRACE("container create started"); + + /*Create a basic object*/ + lv_obj_t * new_cont = lv_obj_create(par, copy); + lv_mem_assert(new_cont); + if(new_cont == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_cont); + + lv_obj_allocate_ext_attr(new_cont, sizeof(lv_cont_ext_t)); + lv_cont_ext_t * ext = lv_obj_get_ext_attr(new_cont); + if(ext == NULL) return NULL; + + lv_mem_assert(ext); + ext->hor_fit = 0; + ext->ver_fit = 0; + ext->layout = LV_LAYOUT_OFF; + + lv_obj_set_signal_func(new_cont, lv_cont_signal); + + /*Init the new container*/ + if(copy == NULL) { + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_cont_set_style(new_cont, th->cont); + } else { + lv_cont_set_style(new_cont, &lv_style_pretty); + } + } + /*Copy an existing object*/ + else { + lv_cont_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->hor_fit = copy_ext->hor_fit; + ext->ver_fit = copy_ext->ver_fit; + ext->layout = copy_ext->layout; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_cont); + } + + LV_LOG_INFO("container created"); + + + return new_cont; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a layout on a container + * @param cont pointer to a container object + * @param layout a layout from 'lv_cont_layout_t' + */ +void lv_cont_set_layout(lv_obj_t * cont, lv_layout_t layout) +{ + lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont); + if(ext->layout == layout) return; + + ext->layout = layout; + + /*Send a signal to refresh the layout*/ + cont->signal_func(cont, LV_SIGNAL_CHILD_CHG, NULL); +} + + +/** + * Enable the horizontal or vertical fit. + * The container size will be set to involve the children horizontally or vertically. + * @param cont pointer to a container object + * @param hor_en true: enable the horizontal fit + * @param ver_en true: enable the vertical fit + */ +void lv_cont_set_fit(lv_obj_t * cont, bool hor_en, bool ver_en) +{ + lv_obj_invalidate(cont); + lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont); + if(ext->hor_fit == hor_en && ext->ver_fit == ver_en) return; + + ext->hor_fit = hor_en == false ? 0 : 1; + ext->ver_fit = ver_en == false ? 0 : 1; + + /*Send a signal to refresh the layout*/ + cont->signal_func(cont, LV_SIGNAL_CHILD_CHG, NULL); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the layout of a container + * @param cont pointer to container object + * @return the layout from 'lv_cont_layout_t' + */ +lv_layout_t lv_cont_get_layout(const lv_obj_t * cont) +{ + lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont); + return ext->layout; +} + +/** + * Get horizontal fit enable attribute of a container + * @param cont pointer to a container object + * @return true: horizontal fit is enabled; false: disabled + */ +bool lv_cont_get_hor_fit(const lv_obj_t * cont) +{ + lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont); + return ext->hor_fit == 0 ? false : true; +} + +/** + * Get vertical fit enable attribute of a container + * @param cont pointer to a container object + * @return true: vertical fit is enabled; false: disabled + */ +bool lv_cont_get_ver_fit(const lv_obj_t * cont) +{ + lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont); + return ext->ver_fit == 0 ? false : true; +} + +/** + * Get that width reduced by the horizontal padding. Useful if a layout is used. + * @param cont pointer to a container object + * @return the width which still fits into the container + */ +lv_coord_t lv_cont_get_fit_width(lv_obj_t * cont) +{ + lv_style_t * style = lv_cont_get_style(cont); + + return lv_obj_get_width(cont) - 2 * style->body.padding.hor; +} + +/** + * Get that height reduced by the vertical padding. Useful if a layout is used. + * @param cont pointer to a container object + * @return the height which still fits into the container + */ +lv_coord_t lv_cont_get_fit_height(lv_obj_t * cont) +{ + lv_style_t * style = lv_cont_get_style(cont); + + return lv_obj_get_height(cont) - 2 * style->body.padding.ver; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Signal function of the container + * @param cont pointer to a container object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_cont_signal(lv_obj_t * cont, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(cont, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_STYLE_CHG) { /*Recalculate the padding if the style changed*/ + lv_cont_refr_layout(cont); + lv_cont_refr_autofit(cont); + } else if(sign == LV_SIGNAL_CHILD_CHG) { + lv_cont_refr_layout(cont); + lv_cont_refr_autofit(cont); + } else if(sign == LV_SIGNAL_CORD_CHG) { + if(lv_obj_get_width(cont) != lv_area_get_width(param) || + lv_obj_get_height(cont) != lv_area_get_height(param)) { + lv_cont_refr_layout(cont); + lv_cont_refr_autofit(cont); + } + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_cont"; + } + + return res; +} + + +/** + * Refresh the layout of a container + * @param cont pointer to an object which layout should be refreshed + */ +static void lv_cont_refr_layout(lv_obj_t * cont) +{ + lv_layout_t type = lv_cont_get_layout(cont); + + /*'cont' has to be at least 1 child*/ + if(lv_obj_get_child(cont, NULL) == NULL) return; + + if(type == LV_LAYOUT_OFF) return; + + if(type == LV_LAYOUT_CENTER) { + lv_cont_layout_center(cont); + } else if(type == LV_LAYOUT_COL_L || type == LV_LAYOUT_COL_M || type == LV_LAYOUT_COL_R) { + lv_cont_layout_col(cont); + } else if(type == LV_LAYOUT_ROW_T || type == LV_LAYOUT_ROW_M || type == LV_LAYOUT_ROW_B) { + lv_cont_layout_row(cont); + } else if(type == LV_LAYOUT_PRETTY) { + lv_cont_layout_pretty(cont); + } else if(type == LV_LAYOUT_GRID) { + lv_cont_layout_grid(cont); + } +} + +/** + * Handle column type layouts + * @param cont pointer to an object which layout should be handled + */ +static void lv_cont_layout_col(lv_obj_t * cont) +{ + lv_layout_t type = lv_cont_get_layout(cont); + lv_obj_t * child; + + /*Adjust margin and get the alignment type*/ + lv_align_t align; + lv_style_t * style = lv_obj_get_style(cont); + lv_coord_t hpad_corr; + + switch(type) { + case LV_LAYOUT_COL_L: + hpad_corr = style->body.padding.hor; + align = LV_ALIGN_IN_TOP_LEFT; + break; + case LV_LAYOUT_COL_M: + hpad_corr = 0; + align = LV_ALIGN_IN_TOP_MID; + break; + case LV_LAYOUT_COL_R: + hpad_corr = -style->body.padding.hor; + align = LV_ALIGN_IN_TOP_RIGHT; + break; + default: + hpad_corr = 0; + align = LV_ALIGN_IN_TOP_LEFT; + break; + } + + /* Disable child change action because the children will be moved a lot + * an unnecessary child change signals could be sent*/ + lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG); + /* Align the children */ + lv_coord_t last_cord = style->body.padding.ver; + LL_READ_BACK(cont->child_ll, child) { + if(lv_obj_get_hidden(child) != false || + lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue; + + lv_obj_align(child, cont, align, hpad_corr, last_cord); + last_cord += lv_obj_get_height(child) + style->body.padding.inner; + } + + lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG); +} + +/** + * Handle row type layouts + * @param cont pointer to an object which layout should be handled + */ +static void lv_cont_layout_row(lv_obj_t * cont) +{ + lv_layout_t type = lv_cont_get_layout(cont); + lv_obj_t * child; + + /*Adjust margin and get the alignment type*/ + lv_align_t align; + lv_style_t * style = lv_obj_get_style(cont); + lv_coord_t vpad_corr = style->body.padding.ver; + + switch(type) { + case LV_LAYOUT_ROW_T: + vpad_corr = style->body.padding.ver; + align = LV_ALIGN_IN_TOP_LEFT; + break; + case LV_LAYOUT_ROW_M: + vpad_corr = 0; + align = LV_ALIGN_IN_LEFT_MID; + break; + case LV_LAYOUT_ROW_B: + vpad_corr = -style->body.padding.ver; + align = LV_ALIGN_IN_BOTTOM_LEFT; + break; + default: + vpad_corr = 0; + align = LV_ALIGN_IN_TOP_LEFT; + break; + } + + /* Disable child change action because the children will be moved a lot + * an unnecessary child change signals could be sent*/ + lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG); + + /* Align the children */ + lv_coord_t last_cord = style->body.padding.hor; + LL_READ_BACK(cont->child_ll, child) { + if(lv_obj_get_hidden(child) != false || + lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue; + + lv_obj_align(child, cont, align, last_cord, vpad_corr); + last_cord += lv_obj_get_width(child) + style->body.padding.inner; + } + + lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG); +} + +/** + * Handle the center layout + * @param cont pointer to an object which layout should be handled + */ +static void lv_cont_layout_center(lv_obj_t * cont) +{ + lv_obj_t * child; + lv_style_t * style = lv_obj_get_style(cont); + uint32_t obj_num = 0; + lv_coord_t h_tot = 0; + + LL_READ(cont->child_ll, child) { + if(lv_obj_get_hidden(child) != false || + lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue; + h_tot += lv_obj_get_height(child) + style->body.padding.inner; + obj_num ++; + } + + if(obj_num == 0) return; + + h_tot -= style->body.padding.inner; + + /* Disable child change action because the children will be moved a lot + * an unnecessary child change signals could be sent*/ + lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG); + + /* Align the children */ + lv_coord_t last_cord = - (h_tot / 2); + LL_READ_BACK(cont->child_ll, child) { + if(lv_obj_get_hidden(child) != false || + lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue; + + lv_obj_align(child, cont, LV_ALIGN_CENTER, 0, last_cord + lv_obj_get_height(child) / 2); + last_cord += lv_obj_get_height(child) + style->body.padding.inner; + } + + lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG); +} + +/** + * Handle the pretty layout. Put as many object as possible in row + * then begin a new row + * @param cont pointer to an object which layout should be handled + */ +static void lv_cont_layout_pretty(lv_obj_t * cont) +{ + lv_obj_t * child_rs; /* Row starter child */ + lv_obj_t * child_rc; /* Row closer child */ + lv_obj_t * child_tmp; /* Temporary child */ + lv_style_t * style = lv_obj_get_style(cont); + lv_coord_t w_obj = lv_obj_get_width(cont); + lv_coord_t act_y = style->body.padding.ver; + /* Disable child change action because the children will be moved a lot + * an unnecessary child change signals could be sent*/ + + child_rs = lv_ll_get_tail(&cont->child_ll); /*Set the row starter child*/ + if(child_rs == NULL) return; /*Return if no child*/ + + lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG); + + child_rc = child_rs; /*Initially the the row starter and closer is the same*/ + while(child_rs != NULL) { + lv_coord_t h_row = 0; + lv_coord_t w_row = style->body.padding.hor * 2; /*The width is at least the left+right hpad*/ + uint32_t obj_num = 0; + + /*Find the row closer object and collect some data*/ + do { + if(lv_obj_get_hidden(child_rc) == false && + lv_obj_is_protected(child_rc, LV_PROTECT_POS) == false) { + /*If this object is already not fit then break*/ + if(w_row + lv_obj_get_width(child_rc) > w_obj) { + /*Step back one child because the last already not fit, so the previous is the closer*/ + if(child_rc != NULL && obj_num != 0) { + child_rc = lv_ll_get_next(&cont->child_ll, child_rc); + } + break; + } + w_row += lv_obj_get_width(child_rc) + style->body.padding.inner; /*Add the object width + opad*/ + h_row = LV_MATH_MAX(h_row, lv_obj_get_height(child_rc)); /*Search the highest object*/ + obj_num ++; + if(lv_obj_is_protected(child_rc, LV_PROTECT_FOLLOW)) break; /*If can not be followed by an other object then break here*/ + + } + child_rc = lv_ll_get_prev(&cont->child_ll, child_rc); /*Load the next object*/ + if(obj_num == 0) child_rs = child_rc; /*If the first object was hidden (or too long) then set the next as first */ + } while(child_rc != NULL); + + /*If the object is too long then align it to the middle*/ + if(obj_num == 0) { + if(child_rc != NULL) { + lv_obj_align(child_rc, cont, LV_ALIGN_IN_TOP_MID, 0, act_y); + h_row = lv_obj_get_height(child_rc); /*Not set previously because of the early break*/ + } + } + /*If there is only one object in the row then align it to the middle*/ + else if(obj_num == 1) { + lv_obj_align(child_rs, cont, LV_ALIGN_IN_TOP_MID, 0, act_y); + } + /*If there are two object in the row then align them proportionally*/ + else if(obj_num == 2) { + lv_obj_t * obj1 = child_rs; + lv_obj_t * obj2 = lv_ll_get_prev(&cont->child_ll, child_rs); + w_row = lv_obj_get_width(obj1) + lv_obj_get_width(obj2); + lv_coord_t pad = (w_obj - w_row) / 3; + lv_obj_align(obj1, cont, LV_ALIGN_IN_TOP_LEFT, pad, act_y + (h_row - lv_obj_get_height(obj1)) / 2); + lv_obj_align(obj2, cont, LV_ALIGN_IN_TOP_RIGHT, -pad, act_y + (h_row - lv_obj_get_height(obj2)) / 2); + } + /* Align the children (from child_rs to child_rc)*/ + else { + w_row -= style->body.padding.inner * obj_num; + lv_coord_t new_opad = (w_obj - w_row) / (obj_num - 1); + lv_coord_t act_x = style->body.padding.hor; /*x init*/ + child_tmp = child_rs; + while(child_tmp != NULL) { + if(lv_obj_get_hidden(child_tmp) == false && + lv_obj_is_protected(child_tmp, LV_PROTECT_POS) == false) { + lv_obj_align(child_tmp, cont, LV_ALIGN_IN_TOP_LEFT, act_x, act_y + (h_row - lv_obj_get_height(child_tmp)) / 2); + act_x += lv_obj_get_width(child_tmp) + new_opad; + } + if(child_tmp == child_rc) break; + child_tmp = lv_ll_get_prev(&cont->child_ll, child_tmp); + } + + } + + if(child_rc == NULL) break; + act_y += style->body.padding.inner + h_row; /*y increment*/ + child_rs = lv_ll_get_prev(&cont->child_ll, child_rc); /*Go to the next object*/ + child_rc = child_rs; + } + lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG); +} + +/** + * Handle the grid layout. Align same-sized objects in a grid + * @param cont pointer to an object which layout should be handled + */ +static void lv_cont_layout_grid(lv_obj_t * cont) +{ + lv_obj_t * child; + lv_style_t * style = lv_obj_get_style(cont); + lv_coord_t w_tot = lv_obj_get_width(cont); + lv_coord_t w_obj = lv_obj_get_width(lv_obj_get_child(cont, NULL)); + lv_coord_t h_obj = lv_obj_get_height(lv_obj_get_child(cont, NULL)); + uint16_t obj_row = (w_tot - (2 * style->body.padding.hor)) / (w_obj + style->body.padding.inner); /*Obj. num. in a row*/ + lv_coord_t x_ofs; + if(obj_row > 1) { + x_ofs = w_obj + (w_tot - (2 * style->body.padding.hor) - (obj_row * w_obj)) / (obj_row - 1); + } else { + x_ofs = w_tot / 2 - w_obj / 2; + } + lv_coord_t y_ofs = h_obj + style->body.padding.inner; + + /* Disable child change action because the children will be moved a lot + * an unnecessary child change signals could be sent*/ + lv_obj_set_protect(cont, LV_PROTECT_CHILD_CHG); + + /* Align the children */ + lv_coord_t act_x = style->body.padding.hor; + lv_coord_t act_y = style->body.padding.ver; + uint16_t obj_cnt = 0; + LL_READ_BACK(cont->child_ll, child) { + if(lv_obj_get_hidden(child) != false || + lv_obj_is_protected(child, LV_PROTECT_POS) != false) continue; + + if(obj_row > 1) { + lv_obj_set_pos(child, act_x, act_y); + act_x += x_ofs; + } else { + lv_obj_set_pos(child, x_ofs, act_y); + } + obj_cnt ++; + + if(obj_cnt >= obj_row) { + obj_cnt = 0; + act_x = style->body.padding.hor; + act_y += y_ofs; + } + } + + lv_obj_clear_protect(cont, LV_PROTECT_CHILD_CHG); +} + +/** + * Handle auto fit. Set the size of the object to involve all children. + * @param cont pointer to an object which size will be modified + */ +static void lv_cont_refr_autofit(lv_obj_t * cont) +{ + lv_cont_ext_t * ext = lv_obj_get_ext_attr(cont); + + if(ext->hor_fit == 0 && + ext->ver_fit == 0) { + return; + } + + lv_area_t new_cords; + lv_area_t ori; + lv_style_t * style = lv_obj_get_style(cont); + lv_obj_t * i; + lv_coord_t hpad = style->body.padding.hor; + lv_coord_t vpad = style->body.padding.ver; + + /*Search the side coordinates of the children*/ + lv_obj_get_coords(cont, &ori); + lv_obj_get_coords(cont, &new_cords); + + new_cords.x1 = LV_COORD_MAX; + new_cords.y1 = LV_COORD_MAX; + new_cords.x2 = LV_COORD_MIN; + new_cords.y2 = LV_COORD_MIN; + + LL_READ(cont->child_ll, i) { + if(lv_obj_get_hidden(i) != false) continue; + new_cords.x1 = LV_MATH_MIN(new_cords.x1, i->coords.x1); + new_cords.y1 = LV_MATH_MIN(new_cords.y1, i->coords.y1); + new_cords.x2 = LV_MATH_MAX(new_cords.x2, i->coords.x2); + new_cords.y2 = LV_MATH_MAX(new_cords.y2, i->coords.y2); + } + + /*If the value is not the init value then the page has >=1 child.*/ + if(new_cords.x1 != LV_COORD_MAX) { + if(ext->hor_fit != 0) { + new_cords.x1 -= hpad; + new_cords.x2 += hpad; + } else { + new_cords.x1 = cont->coords.x1; + new_cords.x2 = cont->coords.x2; + } + if(ext->ver_fit != 0) { + new_cords.y1 -= vpad; + new_cords.y2 += vpad; + } else { + new_cords.y1 = cont->coords.y1; + new_cords.y2 = cont->coords.y2; + } + + /*Do nothing if the coordinates are not changed*/ + if(cont->coords.x1 != new_cords.x1 || + cont->coords.y1 != new_cords.y1 || + cont->coords.x2 != new_cords.x2 || + cont->coords.y2 != new_cords.y2) { + + lv_obj_invalidate(cont); + lv_area_copy(&cont->coords, &new_cords); + lv_obj_invalidate(cont); + + /*Notify the object about its new coordinates*/ + cont->signal_func(cont, LV_SIGNAL_CORD_CHG, &ori); + + /*Inform the parent about the new coordinates*/ + lv_obj_t * par = lv_obj_get_parent(cont); + par->signal_func(par, LV_SIGNAL_CHILD_CHG, cont); + } + } +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_cont.h b/bdk/libs/lvgl/lv_objx/lv_cont.h new file mode 100644 index 00000000..3259c772 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_cont.h @@ -0,0 +1,163 @@ +/** + * @file lv_cont.h + * + */ + +#ifndef LV_CONT_H +#define LV_CONT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_CONT != 0 + +#include "../lv_core/lv_obj.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Layout options*/ +enum +{ + LV_LAYOUT_OFF = 0, + LV_LAYOUT_CENTER, + LV_LAYOUT_COL_L, /*Column left align*/ + LV_LAYOUT_COL_M, /*Column middle align*/ + LV_LAYOUT_COL_R, /*Column right align*/ + LV_LAYOUT_ROW_T, /*Row top align*/ + LV_LAYOUT_ROW_M, /*Row middle align*/ + LV_LAYOUT_ROW_B, /*Row bottom align*/ + LV_LAYOUT_PRETTY, /*Put as many object as possible in row and begin a new row*/ + LV_LAYOUT_GRID, /*Align same-sized object into a grid*/ +}; +typedef uint8_t lv_layout_t; + +typedef struct +{ + /*Inherited from 'base_obj' so no inherited ext. */ /*Ext. of ancestor*/ + /*New data for this type */ + uint8_t layout :4; /*A layout from 'lv_cont_layout_t' enum*/ + uint8_t hor_fit :1; /*1: Enable horizontal fit to involve all children*/ + uint8_t ver_fit :1; /*1: Enable horizontal fit to involve all children*/ +} lv_cont_ext_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a container objects + * @param par pointer to an object, it will be the parent of the new container + * @param copy pointer to a container object, if not NULL then the new object will be copied from it + * @return pointer to the created container + */ +lv_obj_t * lv_cont_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a layout on a container + * @param cont pointer to a container object + * @param layout a layout from 'lv_cont_layout_t' + */ +void lv_cont_set_layout(lv_obj_t * cont, lv_layout_t layout); + + +/** + * Enable the horizontal or vertical fit. + * The container size will be set to involve the children horizontally or vertically. + * @param cont pointer to a container object + * @param hor_en true: enable the horizontal fit + * @param ver_en true: enable the vertical fit + */ +void lv_cont_set_fit(lv_obj_t * cont, bool hor_en, bool ver_en); + +/** + * Set the style of a container + * @param cont pointer to a container object + * @param style pointer to the new style + */ +static inline void lv_cont_set_style(lv_obj_t *cont, lv_style_t * style) +{ + lv_obj_set_style(cont, style); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the layout of a container + * @param cont pointer to container object + * @return the layout from 'lv_cont_layout_t' + */ +lv_layout_t lv_cont_get_layout(const lv_obj_t * cont); + +/** + * Get horizontal fit enable attribute of a container + * @param cont pointer to a container object + * @return true: horizontal fit is enabled; false: disabled + */ +bool lv_cont_get_hor_fit(const lv_obj_t * cont); + +/** + * Get vertical fit enable attribute of a container + * @param cont pointer to a container object + * @return true: vertical fit is enabled; false: disabled + */ +bool lv_cont_get_ver_fit(const lv_obj_t * cont); + + +/** + * Get that width reduced by the horizontal padding. Useful if a layout is used. + * @param cont pointer to a container object + * @return the width which still fits into the container + */ +lv_coord_t lv_cont_get_fit_width(lv_obj_t * cont); + +/** + * Get that height reduced by the vertical padding. Useful if a layout is used. + * @param cont pointer to a container object + * @return the height which still fits into the container + */ +lv_coord_t lv_cont_get_fit_height(lv_obj_t * cont); + +/** + * Get the style of a container + * @param cont pointer to a container object + * @return pointer to the container's style + */ +static inline lv_style_t * lv_cont_get_style(const lv_obj_t *cont) +{ + return lv_obj_get_style(cont); +} + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_CONT*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_CONT_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_ddlist.c b/bdk/libs/lvgl/lv_objx/lv_ddlist.c new file mode 100644 index 00000000..c3776e8a --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_ddlist.c @@ -0,0 +1,981 @@ +/* + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_ddlist.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "lv_ddlist.h" +#if USE_LV_DDLIST != 0 + +#include "../lv_draw/lv_draw.h" +#include "../lv_core/lv_group.h" +#include "../lv_core/lv_indev.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_symbol_def.h" +#include "../lv_misc/lv_anim.h" +//#include + +/********************* + * DEFINES + *********************/ +#if USE_LV_ANIMATION +# ifndef LV_DDLIST_ANIM_TIME +# define LV_DDLIST_ANIM_TIME 200 /*ms*/ +# endif +#else +# undef LV_DDLIST_ANIM_TIME +# define LV_DDLIST_ANIM_TIME 0 /*No animation*/ +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_ddlist_signal(lv_obj_t * ddlist, lv_signal_t sign, void * param); +static lv_res_t lv_ddlist_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param); +static lv_res_t lv_ddlist_release_action(lv_obj_t * ddlist); +static lv_res_t lv_ddlist_press_action(lv_obj_t * ddlist); +static void lv_ddlist_refr_size(lv_obj_t * ddlist, bool anim_en); +static void lv_ddlist_pos_current_option(lv_obj_t * ddlist); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_signal_func_t ancestor_scrl_signal; +static lv_design_func_t ancestor_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a drop down list objects + * @param par pointer to an object, it will be the parent of the new drop down list + * @param copy pointer to a drop down list object, if not NULL then the new object will be copied from it + * @return pointer to the created drop down list + */ +lv_obj_t * lv_ddlist_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("drop down list create started"); + + /*Create the ancestor drop down list*/ + lv_obj_t * new_ddlist = lv_page_create(par, copy); + lv_mem_assert(new_ddlist); + if(new_ddlist == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_ddlist); + if(ancestor_scrl_signal == NULL) ancestor_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(new_ddlist)); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_ddlist); + + /*Allocate the drop down list type specific extended data*/ + lv_ddlist_ext_t * ext = lv_obj_allocate_ext_attr(new_ddlist, sizeof(lv_ddlist_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + /*Initialize the allocated 'ext' */ + ext->label = NULL; + ext->action = NULL; + ext->opened = 0; + ext->fix_height = 0; + ext->sel_opt_id = 0; + ext->sel_opt_id_ori = 0; + ext->option_cnt = 0; + ext->anim_time = LV_DDLIST_ANIM_TIME; + ext->sel_style = &lv_style_plain_color; + ext->draw_arrow = 0; /*Do not draw arrow by default*/ + ext->direction_up = 0; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_ddlist, lv_ddlist_signal); + lv_obj_set_signal_func(lv_page_get_scrl(new_ddlist), lv_ddlist_scrl_signal); + lv_obj_set_design_func(new_ddlist, lv_ddlist_design); + + /*Init the new drop down list drop down list*/ + if(copy == NULL) { + lv_obj_t * scrl = lv_page_get_scrl(new_ddlist); + lv_obj_set_drag(scrl, false); + lv_page_set_scrl_fit(new_ddlist, true, true); + + ext->label = lv_label_create(new_ddlist, NULL); + lv_cont_set_fit(new_ddlist, true, false); + lv_page_set_rel_action(new_ddlist, lv_ddlist_release_action); + lv_page_set_pr_action(new_ddlist, lv_ddlist_press_action); + lv_page_set_sb_mode(new_ddlist, LV_SB_MODE_DRAG); + lv_page_set_sb_mode(new_ddlist, LV_SB_MODE_HIDE); + lv_page_set_style(new_ddlist, LV_PAGE_STYLE_SCRL, &lv_style_transp_tight); + + lv_ddlist_set_options(new_ddlist, "Option 1\nOption 2\nOption 3"); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BG, th->ddlist.bg); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BGO, th->ddlist.bgo); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_PR, th->ddlist.pr); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SEL, th->ddlist.sel); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SB, th->ddlist.sb); + } else { + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BG, &lv_style_pretty); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_BGO, &lv_style_pretty); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_PR, &lv_style_pretty); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SEL, &lv_style_plain_color); + lv_ddlist_set_style(new_ddlist, LV_DDLIST_STYLE_SB, &lv_style_pretty_color); + } + } + /*Copy an existing drop down list*/ + else { + lv_ddlist_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->label = lv_label_create(new_ddlist, copy_ext->label); + lv_label_set_text(ext->label, lv_label_get_text(copy_ext->label)); + ext->sel_opt_id = copy_ext->sel_opt_id; + ext->fix_height = copy_ext->fix_height; + ext->action = copy_ext->action; + ext->option_cnt = copy_ext->option_cnt; + ext->sel_style = copy_ext->sel_style; + ext->anim_time = copy_ext->anim_time; + ext->draw_arrow = copy_ext->draw_arrow; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_ddlist); + } + + LV_LOG_INFO("drop down list created"); + + + return new_ddlist; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set arrow draw in a drop down list + * @param ddlist pointer to drop down list object + * @param en enable/disable a arrow draw. E.g. "true" for draw. + */ +void lv_ddlist_set_draw_arrow(lv_obj_t * ddlist, bool en) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + /*Set the flag*/ + ext->draw_arrow = en; +} + +/** + * Set the options in a drop down list from a string + * @param ddlist pointer to drop down list object + * @param options a string with '\n' separated options. E.g. "One\nTwo\nThree" + */ +void lv_ddlist_set_options(lv_obj_t * ddlist, const char * options) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + /*Count the '\n'-s to determine the number of options*/ + ext->option_cnt = 0; + uint16_t i; + for(i = 0; options[i] != '\0'; i++) { + if(options[i] == '\n') ext->option_cnt++; + } + ext->option_cnt++; /*Last option in the at row*/ + + lv_label_set_text(ext->label, options); + lv_ddlist_refr_size(ddlist, false); +} + +/** + * Set the selected option + * @param ddlist pointer to drop down list object + * @param sel_opt id of the selected option (0 ... number of option - 1); + */ +void lv_ddlist_set_selected(lv_obj_t * ddlist, uint16_t sel_opt) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + if(ext->sel_opt_id == sel_opt) return; + + ext->sel_opt_id = sel_opt < ext->option_cnt ? sel_opt : ext->option_cnt - 1; + ext->sel_opt_id_ori = ext->sel_opt_id; + /*Move the list to show the current option*/ + if(ext->opened == 0) { + lv_ddlist_pos_current_option(ddlist); + } else { + lv_obj_invalidate(ddlist); + } +} + +/** + * Set a function to call when a new option is chosen + * @param ddlist pointer to a drop down list + * @param action pointer to a call back function + */ +void lv_ddlist_set_action(lv_obj_t * ddlist, lv_action_t action) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + ext->action = action; +} + +/** + * Set the fix height for the drop down list + * If 0 then the opened ddlist will be auto. sized else the set height will be applied. + * @param ddlist pointer to a drop down list + * @param h the height when the list is opened (0: auto size) + */ +void lv_ddlist_set_fix_height(lv_obj_t * ddlist, lv_coord_t h) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + if(ext->fix_height == h) return; + + ext->fix_height = h; + + lv_ddlist_refr_size(ddlist, false); +} + +/** + * Enable or disable the horizontal fit to the content + * @param ddlist pointer to a drop down list + * @param en true: enable auto fit; false: disable auto fit + */ +void lv_ddlist_set_hor_fit(lv_obj_t * ddlist, bool en) +{ + lv_cont_set_fit(ddlist, en, lv_cont_get_ver_fit(ddlist)); + lv_page_set_scrl_fit(ddlist, en, lv_page_get_scrl_fit_ver(ddlist)); + + lv_ddlist_refr_size(ddlist, false); +} + +/** + * Set the open/close animation time. + * @param ddlist pointer to a drop down list + * @param anim_time: open/close animation time [ms] + */ +void lv_ddlist_set_anim_time(lv_obj_t * ddlist, uint16_t anim_time) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); +#if USE_LV_ANIMATION == 0 + anim_time = 0; +#endif + + ext->anim_time = anim_time; +} + +/** + * Set a style of a drop down list + * @param ddlist pointer to a drop down list object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_ddlist_set_style(lv_obj_t * ddlist, lv_ddlist_style_t type, lv_style_t * style) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + switch(type) { + case LV_DDLIST_STYLE_BG: + lv_page_set_style(ddlist, LV_PAGE_STYLE_BG, style); + break; + case LV_DDLIST_STYLE_BGO: + lv_page_set_style(ddlist, LV_PAGE_STYLE_BGO, style); + break; + case LV_DDLIST_STYLE_PR: + lv_page_set_style(ddlist, LV_PAGE_STYLE_PR, style); + break; + case LV_DDLIST_STYLE_SB: + lv_page_set_style(ddlist, LV_PAGE_STYLE_SB, style); + break; + case LV_DDLIST_STYLE_SEL: + ext->sel_style = style; + lv_obj_t * scrl = lv_page_get_scrl(ddlist); + lv_obj_refresh_ext_size(scrl); /*Because of the wider selected rectangle*/ + break; + } +} + +void lv_ddlist_set_align(lv_obj_t *ddlist, lv_label_align_t align) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + lv_label_set_align(ext->label, align); +} + +void lv_ddlist_set_direction_up(lv_obj_t *ddlist, bool enable) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + ext->direction_up = enable; +} +/*===================== + * Getter functions + *====================*/ + +/** + * Get arrow draw in a drop down list + * @param ddlist pointer to drop down list object + */ +bool lv_ddlist_get_draw_arrow(lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + return ext->draw_arrow; +} + +/** + * Get the options of a drop down list + * @param ddlist pointer to drop down list object + * @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3") + */ +const char * lv_ddlist_get_options(const lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + return lv_label_get_text(ext->label); +} + +/** + * Get the selected option + * @param ddlist pointer to drop down list object + * @return id of the selected option (0 ... number of option - 1); + */ +uint16_t lv_ddlist_get_selected(const lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + return ext->sel_opt_id; +} + +/** + * Get the current selected option as a string + * @param ddlist pointer to ddlist object + * @param buf pointer to an array to store the string + */ +void lv_ddlist_get_selected_str(const lv_obj_t * ddlist, char * buf) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + uint16_t i; + uint16_t line = 0; + const char * opt_txt = lv_label_get_text(ext->label); + uint16_t txt_len = strlen(opt_txt); + + + for(i = 0; i < txt_len && line != ext->sel_opt_id; i++) { + if(opt_txt[i] == '\n') line ++; + } + + uint16_t c; + for(c = 0; opt_txt[i] != '\n' && i < txt_len; c++, i++) buf[c] = opt_txt[i]; + + buf[c] = '\0'; +} + +/** + * Get the "option selected" callback function + * @param ddlist pointer to a drop down list + * @return pointer to the call back function + */ +lv_action_t lv_ddlist_get_action(const lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + return ext->action; +} + +/** + * Get the fix height value. + * @param ddlist pointer to a drop down list object + * @return the height if the ddlist is opened (0: auto size) + */ +lv_coord_t lv_ddlist_get_fix_height(const lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + return ext->fix_height; +} + +/** + * Get the open/close animation time. + * @param ddlist pointer to a drop down list + * @return open/close animation time [ms] + */ +uint16_t lv_ddlist_get_anim_time(const lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + return ext->anim_time; +} + +/** + * Get a style of a drop down list + * @param ddlist pointer to a drop down list object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_ddlist_get_style(const lv_obj_t * ddlist, lv_ddlist_style_t type) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + switch(type) { + case LV_DDLIST_STYLE_BG: + return lv_page_get_style(ddlist, LV_PAGE_STYLE_BG); + case LV_DDLIST_STYLE_BGO: + return lv_page_get_style(ddlist, LV_PAGE_STYLE_BGO); + case LV_DDLIST_STYLE_PR: + return lv_page_get_style(ddlist, LV_PAGE_STYLE_PR); + case LV_DDLIST_STYLE_SB: + return lv_page_get_style(ddlist, LV_PAGE_STYLE_SB); + case LV_DDLIST_STYLE_SEL: + return ext->sel_style; + default: + return NULL; + } + + /*To avoid warning*/ + return NULL; +} + +lv_label_align_t lv_ddlist_get_align(const lv_obj_t *ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + return lv_label_get_align(ext->label); +} + +/*===================== + * Other functions + *====================*/ + +/** + * Open the drop down list with or without animation + * @param ddlist pointer to drop down list object + * @param anim_en true: use animation; false: not use animations + */ +void lv_ddlist_open(lv_obj_t * ddlist, bool anim_en) +{ +#if USE_LV_ANIMATION == 0 + anim_en = false; +#endif + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + ext->opened = 1; + lv_obj_set_drag(lv_page_get_scrl(ddlist), true); + lv_ddlist_refr_size(ddlist, anim_en); +} + +/** + * Close (Collapse) the drop down list + * @param ddlist pointer to drop down list object + * @param anim_en true: use animation; false: not use animations + */ +void lv_ddlist_close(lv_obj_t * ddlist, bool anim_en) +{ +#if USE_LV_ANIMATION == 0 + anim_en = false; +#endif + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + ext->opened = 0; + lv_obj_set_drag(lv_page_get_scrl(ddlist), false); + lv_ddlist_refr_size(ddlist, anim_en); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Get the text alignment flag for a drop down list. + * @param ddlist drop down list + * @return text alignment flag + */ +static lv_txt_flag_t lv_ddlist_get_txt_flag(const lv_obj_t *ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + /*The label might be already deleted so just return with some value*/ + if(!ext->label) return LV_TXT_FLAG_CENTER; + + lv_label_align_t align = lv_label_get_align(ext->label); + + switch(align) + { + default: + case LV_LABEL_ALIGN_LEFT: + return LV_TXT_FLAG_NONE; + case LV_LABEL_ALIGN_CENTER: + return LV_TXT_FLAG_CENTER; + case LV_LABEL_ALIGN_RIGHT: + return LV_TXT_FLAG_RIGHT; + } +} + +/** + * Handle the drawing related tasks of the drop down lists + * @param ddlist pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_ddlist_design(lv_obj_t * ddlist, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return ancestor_design(ddlist, mask, mode); + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + ancestor_design(ddlist, mask, mode); + + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + lv_opa_t opa_scale = lv_obj_get_opa_scale(ddlist); + /*If the list is opened draw a rectangle under the selected item*/ + if(ext->opened != 0) { + lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG); + const lv_font_t * font = style->text.font; + lv_coord_t font_h = lv_font_get_height(font); + + /*Draw the selected*/ + lv_area_t rect_area; + rect_area.y1 = ext->label->coords.y1; + rect_area.y1 += ext->sel_opt_id * (font_h + style->text.line_space); + rect_area.y1 -= style->text.line_space / 2; + + rect_area.y2 = rect_area.y1 + font_h + style->text.line_space - 1; + rect_area.x1 = ddlist->coords.x1; + rect_area.x2 = ddlist->coords.x2; + + lv_draw_rect(&rect_area, mask, ext->sel_style, opa_scale); + } + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + /*Redraw the text on the selected area with a different color*/ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + lv_opa_t opa_scale = lv_obj_get_opa_scale(ddlist); + + /*Redraw only in opened state*/ + if(ext->opened) { + lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG); + const lv_font_t * font = style->text.font; + lv_coord_t font_h = lv_font_get_height(font); + + lv_area_t area_sel; + area_sel.y1 = ext->label->coords.y1; + area_sel.y1 += ext->sel_opt_id * (font_h + style->text.line_space); + area_sel.y1 -= style->text.line_space / 2; + + area_sel.y2 = area_sel.y1 + font_h + style->text.line_space - 1; + area_sel.x1 = ddlist->coords.x1; + area_sel.x2 = ddlist->coords.x2; + lv_area_t mask_sel; + bool area_ok; + area_ok = lv_area_intersect(&mask_sel, mask, &area_sel); + if(area_ok) { + lv_style_t * sel_style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_SEL); + lv_style_t new_style; + lv_style_copy(&new_style, style); + new_style.text.color = sel_style->text.color; + new_style.text.opa = sel_style->text.opa; + lv_txt_flag_t flag = lv_ddlist_get_txt_flag(ddlist); + lv_draw_label(&ext->label->coords, &mask_sel, &new_style, opa_scale, + lv_label_get_text(ext->label), flag, NULL); + } + } + + /*Add a down symbol in ddlist when closed*/ + else + { + /*Draw a arrow in ddlist if enabled*/ + if(ext->draw_arrow) + { + lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG); + const lv_font_t * font = style->text.font; + lv_style_t * sel_style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG); + lv_coord_t font_h = lv_font_get_height(font); + lv_style_t new_style; + lv_style_copy(&new_style, style); + new_style.text.color = sel_style->text.color; + new_style.text.opa = sel_style->text.opa; + lv_area_t area_arrow; + area_arrow.x2 = ddlist->coords.x2 - style->body.padding.hor; + if (!ext->direction_up) + area_arrow.x1 = area_arrow.x2 - lv_txt_get_width(SYMBOL_DOWN, strlen(SYMBOL_DOWN), sel_style->text.font, 0, 0); + else + area_arrow.x1 = area_arrow.x2 - lv_txt_get_width(SYMBOL_UP, strlen(SYMBOL_UP), sel_style->text.font, 0, 0); + + area_arrow.y1 = ddlist->coords.y1 + style->text.line_space; + area_arrow.y2 = area_arrow.y1 + font_h; + + + lv_area_t mask_arrow; + bool area_ok; + area_ok = lv_area_intersect(&mask_arrow, mask, &area_arrow); + if (area_ok) + { + if (!ext->direction_up) + lv_draw_label(&area_arrow, &mask_arrow, &new_style, opa_scale, + SYMBOL_DOWN, LV_TXT_FLAG_NONE, NULL); /*Use a down arrow in ddlist, you can replace it with your custom symbol*/ + else + lv_draw_label(&area_arrow, &mask_arrow, &new_style, opa_scale, + SYMBOL_UP, LV_TXT_FLAG_NONE, NULL); /*Use a down arrow in ddlist, you can replace it with your custom symbol*/ + } + } + } + /*Draw the scrollbar in the ancestor page design function*/ + ancestor_design(ddlist, mask, mode); + } + + return true; +} + +/** + * Signal function of the drop down list + * @param ddlist pointer to a drop down list object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_ddlist_signal(lv_obj_t * ddlist, lv_signal_t sign, void * param) +{ + lv_res_t res; + /* Include the ancient signal function */ + res = ancestor_signal(ddlist, sign, param); + if(res != LV_RES_OK) return res; + + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + if(sign == LV_SIGNAL_STYLE_CHG) { + //! lv_ddlist_refr_size(ddlist, 0); // uncommented in OG + } else if(sign == LV_SIGNAL_CLEANUP) { + ext->label = NULL; + } else if(sign == LV_SIGNAL_FOCUS) { +#if USE_LV_GROUP + lv_group_t * g = lv_obj_get_group(ddlist); + bool editing = lv_group_get_editing(g); + lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + + /*Encoders need special handling*/ + if(indev_type == LV_INDEV_TYPE_ENCODER) { + /*Open the list if editing*/ + if(editing) { + ext->opened = true; + ext->sel_opt_id_ori = ext->sel_opt_id; + lv_ddlist_refr_size(ddlist, true); + } + /*Close the lift if navigating*/ + else { + ext->opened = false; + ext->sel_opt_id = ext->sel_opt_id_ori; + lv_ddlist_refr_size(ddlist, true); + + } + } else { + /*Open the list if closed*/ + if(!ext->opened) { + ext->opened = true; + ext->sel_opt_id_ori = ext->sel_opt_id; /*Save the current value. Used to revert this state if ENER wont't be pressed*/ + lv_ddlist_refr_size(ddlist, true); + } + } +#endif + } else if(sign == LV_SIGNAL_DEFOCUS) { + if(ext->opened) { + ext->opened = false; + ext->sel_opt_id = ext->sel_opt_id_ori; + lv_ddlist_refr_size(ddlist, true); + } + } else if(sign == LV_SIGNAL_CONTROLL) { + char c = *((char *)param); + if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_DOWN) { + if(!ext->opened) { + ext->opened = 1; + lv_ddlist_refr_size(ddlist, true); + } + + if(ext->sel_opt_id + 1 < ext->option_cnt) { + ext->sel_opt_id ++; + lv_ddlist_pos_current_option(ddlist); + lv_obj_invalidate(ddlist); + } + } else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_UP) { + if(!ext->opened) { + ext->opened = 1; + lv_ddlist_refr_size(ddlist, true); + } + if(ext->sel_opt_id > 0) { + ext->sel_opt_id --; + lv_ddlist_pos_current_option(ddlist); + lv_obj_invalidate(ddlist); + } + } else if(c == LV_GROUP_KEY_ENTER) { + if(ext->opened) { + ext->sel_opt_id_ori = ext->sel_opt_id; + ext->opened = 0; + if(ext->action) ext->action(ddlist); + +#if USE_LV_GROUP + lv_group_t * g = lv_obj_get_group(ddlist); + bool editing = lv_group_get_editing(g); + if(editing) lv_group_set_editing(g, false); /*In edit mode go to navigate mode if an option is selected*/ +#endif + } else { + ext->opened = 1; + } + + lv_ddlist_refr_size(ddlist, true); + } else if(c == LV_GROUP_KEY_ESC) { + if(ext->opened) { + ext->opened = 0; + ext->sel_opt_id = ext->sel_opt_id_ori; + lv_ddlist_refr_size(ddlist, true); + } + } + } else if(sign == LV_SIGNAL_GET_EDITABLE) { + bool * editable = (bool *)param; + *editable = true; + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_ddlist"; + } + + return res; +} + +/** + * Signal function of the drop down list's scrollable part + * @param scrl pointer to a drop down list's scrollable part + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_ddlist_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_scrl_signal(scrl, sign, param); + if(res != LV_RES_OK) return res; + + lv_obj_t * ddlist = lv_obj_get_parent(scrl); + + if(sign == LV_SIGNAL_REFR_EXT_SIZE) { + /* Because of the wider selected rectangle ext. size + * In this way by dragging the scrollable part the wider rectangle area can be redrawn too*/ + lv_style_t * style = lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BG); + if(scrl->ext_size < style->body.padding.hor) scrl->ext_size = style->body.padding.hor; + } else if(sign == LV_SIGNAL_CLEANUP) { + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + ext->label = NULL; /*The label is already deleted*/ + } + + return res; +} + +/** + * Called when a drop down list is released to open it or set new option + * @param ddlist pointer to a drop down list object + * @return LV_ACTION_RES_INV if the ddlist it deleted in the user callback else LV_ACTION_RES_OK + */ +static lv_res_t lv_ddlist_release_action(lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + if (!lv_obj_get_click(ddlist)) return LV_RES_OK; + + if(ext->opened == 0) { /*Open the list*/ + ext->opened = 1; + lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BGO)); + lv_obj_set_drag(lv_page_get_scrl(ddlist), true); + } else { + ext->opened = 0; + //lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_PR))); + lv_obj_set_drag(lv_page_get_scrl(ddlist), false); + + /*Search the clicked option*/ + lv_indev_t * indev = lv_indev_get_act(); + lv_point_t p; + lv_indev_get_point(indev, &p); + p.x -= ext->label->coords.x1; + p.y -= ext->label->coords.y1; + uint16_t letter_i; + letter_i = lv_label_get_letter_on(ext->label, &p); + + uint16_t new_opt = 0; + const char * txt = lv_label_get_text(ext->label); + uint32_t i = 0; + uint32_t line_cnt = 0; + uint32_t letter; + for(line_cnt = 0; line_cnt < letter_i; line_cnt++) { + letter = lv_txt_encoded_next(txt, &i); + if(letter == '\n') new_opt ++; + } + + ext->sel_opt_id = new_opt; + + if(ext->action != NULL) { + ext->action(ddlist); + } + } + lv_ddlist_refr_size(ddlist, true); + + return LV_RES_OK; +} + +static lv_res_t lv_ddlist_press_action(lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + + if (!lv_obj_get_click(ddlist)) return LV_RES_OK; + + if (ext->opened == 0) + { /*Open the list*/ + lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_PR)); + } + else + { + //lv_ddlist_set_style(ddlist, LV_DDLIST_STYLE_BG, lv_ddlist_get_style(ddlist, LV_DDLIST_STYLE_BGO)); + //lv_obj_set_drag(lv_page_get_scrl(ddlist), false); + + ///*Search the clicked option*/ + //lv_indev_t * indev = lv_indev_get_act(); + //lv_point_t p; + //lv_indev_get_point(indev, &p); + //p.x -= ext->label->coords.x1; + //p.y -= ext->label->coords.y1; + //uint16_t letter_i; + //letter_i = lv_label_get_letter_on(ext->label, &p); + + //uint16_t new_opt = 0; + //const char * txt = lv_label_get_text(ext->label); + //uint32_t i = 0; + //uint32_t line_cnt = 0; + //uint32_t letter; + //for (line_cnt = 0; line_cnt < letter_i; line_cnt++) + //{ + // letter = lv_txt_encoded_next(txt, &i); + // if (letter == '\n') new_opt++; + //} + + //ext->sel_opt_id = new_opt; + + //if (ext->action != NULL) + //{ + // ext->action(ddlist); + //} + } + + return LV_RES_OK; +} + +/** + * Refresh the size of drop down list according to its status (open or closed) + * @param ddlist pointer to a drop down list object + * @param anim_en Change the size (open/close) with or without animation (true/false) + */ +static void lv_ddlist_refr_size(lv_obj_t * ddlist, bool anim_en) +{ +#if USE_LV_ANIMATION == 0 + anim_en = false; +#endif + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + lv_style_t * style = lv_obj_get_style(ddlist); + lv_coord_t new_height, full_height; + bool current_state = 0; + + if(ext->opened) { /*Open the list*/ + if(ext->fix_height == 0) new_height = lv_obj_get_height(lv_page_get_scrl(ddlist)) + 2 * style->body.padding.ver; + else new_height = ext->fix_height; + current_state = 1; + + lv_page_set_sb_mode(ddlist, LV_SB_MODE_UNHIDE); + } else { /*Close the list*/ + const lv_font_t * font = style->text.font; + lv_style_t * label_style = lv_obj_get_style(ext->label); + lv_coord_t font_h = lv_font_get_height(font); + new_height = font_h + 2 * label_style->text.line_space; + //full_height = lv_obj_get_height(lv_page_get_scrl(ddlist)) + 2 * style->body.padding.ver; + current_state = 0; + + lv_page_set_sb_mode(ddlist, LV_SB_MODE_HIDE); + } + + if(anim_en == 0 || ext->direction_up) { + lv_obj_set_height(ddlist, new_height); + if (ext->direction_up) + { + full_height = lv_obj_get_height(lv_page_get_scrl(ddlist)) - lv_font_get_height(style->text.font); + if (current_state) + lv_obj_set_y(ddlist, lv_obj_get_y(ddlist) - full_height); + else + lv_obj_set_y(ddlist, lv_obj_get_y(ddlist) + full_height); + } + + lv_ddlist_pos_current_option(ddlist); +#if USE_LV_ANIMATION + lv_anim_del(ddlist, (lv_anim_fp_t)lv_obj_set_height); /*If an animation is in progress then it will overwrite this changes*/ + } else { + lv_anim_t a; + a.var = ddlist; + a.start = lv_obj_get_height(ddlist); + a.end = new_height; + a.fp = (lv_anim_fp_t)lv_obj_set_height; + a.path = lv_anim_path_linear; + a.end_cb = (lv_anim_cb_t)lv_ddlist_pos_current_option; + a.act_time = 0; + a.time = ext->anim_time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + + lv_anim_create(&a); +#endif + } +} + +/** + * Set the position of list when it is closed to show the selected item + * @param ddlist pointer to a drop down list + */ +static void lv_ddlist_pos_current_option(lv_obj_t * ddlist) +{ + lv_ddlist_ext_t * ext = lv_obj_get_ext_attr(ddlist); + lv_style_t * style = lv_obj_get_style(ddlist); + const lv_font_t * font = style->text.font; + lv_coord_t font_h = lv_font_get_height(font); + lv_style_t * label_style = lv_obj_get_style(ext->label); + lv_obj_t * scrl = lv_page_get_scrl(ddlist); + + lv_coord_t h = lv_obj_get_height(ddlist); + lv_coord_t line_y1 = ext->sel_opt_id * (font_h + label_style->text.line_space) + ext->label->coords.y1 - scrl->coords.y1; + + lv_obj_set_y(scrl, - line_y1 + (h - font_h) / 2); +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_ddlist.h b/bdk/libs/lvgl/lv_objx/lv_ddlist.h new file mode 100644 index 00000000..db000808 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_ddlist.h @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_ddlist.h + * + */ + +#ifndef LV_DDLIST_H +#define LV_DDLIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_DDLIST != 0 + +/*Testing of dependencies*/ +#if USE_LV_PAGE == 0 +#error "lv_ddlist: lv_page is required. Enable it in lv_conf.h (USE_LV_PAGE 1) " +#endif + +#if USE_LV_LABEL == 0 +#error "lv_ddlist: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "../lv_objx/lv_page.h" +#include "../lv_objx/lv_label.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of drop down list*/ +typedef struct +{ + lv_page_ext_t page; /*Ext. of ancestor*/ + /*New data for this type */ + lv_obj_t *label; /*Label for the options*/ + lv_style_t * sel_style; /*Style of the selected option*/ + lv_action_t action; /*Pointer to function to call when an option is selected*/ + uint16_t option_cnt; /*Number of options*/ + uint16_t sel_opt_id; /*Index of the current option*/ + uint16_t sel_opt_id_ori; /*Store the original index on focus*/ + uint16_t anim_time; /*Open/Close animation time [ms]*/ + uint8_t opened :1; /*1: The list is opened (handled by the library)*/ + uint8_t draw_arrow :1; /*1: Draw arrow*/ + uint8_t direction_up : 1; /*1: Open direction*/ + + lv_coord_t fix_height; /*Height of the ddlist when opened. (0: auto-size)*/ +} lv_ddlist_ext_t; + +enum { + LV_DDLIST_STYLE_BG, + LV_DDLIST_STYLE_BGO, + LV_DDLIST_STYLE_PR, + LV_DDLIST_STYLE_SEL, + LV_DDLIST_STYLE_SB, +}; +typedef uint8_t lv_ddlist_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ +/** + * Create a drop down list objects + * @param par pointer to an object, it will be the parent of the new drop down list + * @param copy pointer to a drop down list object, if not NULL then the new object will be copied from it + * @return pointer to the created drop down list + */ +lv_obj_t * lv_ddlist_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set arrow draw in a drop down list + * @param ddlist pointer to drop down list object + * @param en enable/disable a arrow draw. E.g. "true" for draw. + */ +void lv_ddlist_set_draw_arrow(lv_obj_t * ddlist, bool en); + +/** + * Set the options in a drop down list from a string + * @param ddlist pointer to drop down list object + * @param options a string with '\n' separated options. E.g. "One\nTwo\nThree" + */ +void lv_ddlist_set_options(lv_obj_t * ddlist, const char * options); + +/** + * Set the selected option + * @param ddlist pointer to drop down list object + * @param sel_opt id of the selected option (0 ... number of option - 1); + */ +void lv_ddlist_set_selected(lv_obj_t * ddlist, uint16_t sel_opt); + +/** + * Set a function to call when a new option is chosen + * @param ddlist pointer to a drop down list + * @param action pointer to a call back function + */ +void lv_ddlist_set_action(lv_obj_t * ddlist, lv_action_t action); + +/** + * Set the fix height for the drop down list + * If 0 then the opened ddlist will be auto. sized else the set height will be applied. + * @param ddlist pointer to a drop down list + * @param h the height when the list is opened (0: auto size) + */ +void lv_ddlist_set_fix_height(lv_obj_t * ddlist, lv_coord_t h); + +/** + * Enable or disable the horizontal fit to the content + * @param ddlist pointer to a drop down list + * @param en true: enable auto fit; false: disable auto fit + */ +void lv_ddlist_set_hor_fit(lv_obj_t * ddlist, bool en); + +/** + * Set the scroll bar mode of a drop down list + * @param ddlist pointer to a drop down list object + * @param sb_mode the new mode from 'lv_page_sb_mode_t' enum + */ +static inline void lv_ddlist_set_sb_mode(lv_obj_t * ddlist, lv_sb_mode_t mode) +{ + lv_page_set_sb_mode(ddlist, mode); +} + +/** + * Set the open/close animation time. + * @param ddlist pointer to a drop down list + * @param anim_time: open/close animation time [ms] + */ +void lv_ddlist_set_anim_time(lv_obj_t * ddlist, uint16_t anim_time); + + +/** + * Set a style of a drop down list + * @param ddlist pointer to a drop down list object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_ddlist_set_style(lv_obj_t *ddlist, lv_ddlist_style_t type, lv_style_t *style); + +/** + * Set the alignment of the labels in a drop down list + * @param ddlist pointer to a drop down list object + * @param align alignment of labels + */ +void lv_ddlist_set_align(lv_obj_t *ddlist, lv_label_align_t align); + +void lv_ddlist_set_direction_up(lv_obj_t *ddlist, bool enable); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get arrow draw in a drop down list + * @param ddlist pointer to drop down list object + */ +bool lv_ddlist_get_draw_arrow(lv_obj_t * ddlist); + +/** + * Get the options of a drop down list + * @param ddlist pointer to drop down list object + * @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3") + */ +const char * lv_ddlist_get_options(const lv_obj_t * ddlist); + +/** + * Get the selected option + * @param ddlist pointer to drop down list object + * @return id of the selected option (0 ... number of option - 1); + */ +uint16_t lv_ddlist_get_selected(const lv_obj_t * ddlist); + +/** + * Get the current selected option as a string + * @param ddlist pointer to ddlist object + * @param buf pointer to an array to store the string + */ +void lv_ddlist_get_selected_str(const lv_obj_t * ddlist, char * buf); + +/** + * Get the "option selected" callback function + * @param ddlist pointer to a drop down list + * @return pointer to the call back function + */ +lv_action_t lv_ddlist_get_action(const lv_obj_t * ddlist); + +/** + * Get the fix height value. + * @param ddlist pointer to a drop down list object + * @return the height if the ddlist is opened (0: auto size) + */ +lv_coord_t lv_ddlist_get_fix_height(const lv_obj_t * ddlist); + +/** + * Get the scroll bar mode of a drop down list + * @param ddlist pointer to a drop down list object + * @return scrollbar mode from 'lv_page_sb_mode_t' enum + */ +static inline lv_sb_mode_t lv_ddlist_get_sb_mode(const lv_obj_t * ddlist) +{ + return lv_page_get_sb_mode(ddlist); +} + +/** + * Get the open/close animation time. + * @param ddlist pointer to a drop down list + * @return open/close animation time [ms] + */ +uint16_t lv_ddlist_get_anim_time(const lv_obj_t * ddlist); + +/** + * Get a style of a drop down list + * @param ddlist pointer to a drop down list object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_ddlist_get_style(const lv_obj_t *ddlist, lv_ddlist_style_t type); + +/** + * Get the alignment of the labels in a drop down list + * @param ddlist pointer to a drop down list object + * @return alignment of labels + */ +lv_label_align_t lv_ddlist_get_align(const lv_obj_t *ddlist); + +/*===================== + * Other functions + *====================*/ + +/** + * Open the drop down list with or without animation + * @param ddlist pointer to drop down list object + * @param anim_en true: use animation; false: not use animations + */ +void lv_ddlist_open(lv_obj_t * ddlist, bool anim_en); + +/** + * Close (Collapse) the drop down list + * @param ddlist pointer to drop down list object + * @param anim_en true: use animation; false: not use animations + */ +void lv_ddlist_close(lv_obj_t * ddlist, bool anim_en); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_DDLIST*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_DDLIST_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_gauge.c b/bdk/libs/lvgl/lv_objx/lv_gauge.c new file mode 100644 index 00000000..ad2ef8d9 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_gauge.c @@ -0,0 +1,466 @@ +/** + * @file lv_gauge.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "lv_gauge.h" +#if USE_LV_GAUGE != 0 + +#include "../lv_draw/lv_draw.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_txt.h" +#include "../lv_misc/lv_math.h" +#include +#include + +/********************* + * DEFINES + *********************/ +#define LV_GAUGE_DEF_NEEDLE_COLOR LV_COLOR_RED +#define LV_GAUGE_DEF_LABEL_COUNT 6 +#define LV_GAUGE_DEF_LINE_COUNT 21 /*Should be: ((label_cnt - 1) * internal_lines) + 1*/ +#define LV_GAUGE_DEF_ANGLE 220 +#define LV_GAUGE_INTERPOLATE_SHIFT 5 /*Interpolate the needle drawing between to degrees*/ +#define LV_GAUGE_INTERPOLATE_MASK 0x1F + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_gauge_design(lv_obj_t * gauge, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_gauge_signal(lv_obj_t * gauge, lv_signal_t sign, void * param); +static void lv_gauge_draw_scale(lv_obj_t * gauge, const lv_area_t * mask); +static void lv_gauge_draw_needle(lv_obj_t * gauge, const lv_area_t * mask); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_design_func_t ancestor_design; +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a gauge objects + * @param par pointer to an object, it will be the parent of the new gauge + * @param copy pointer to a gauge object, if not NULL then the new object will be copied from it + * @return pointer to the created gauge + */ +lv_obj_t * lv_gauge_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("gauge create started"); + + /*Create the ancestor gauge*/ + lv_obj_t * new_gauge = lv_lmeter_create(par, copy); + lv_mem_assert(new_gauge); + if(new_gauge == NULL) return NULL; + + /*Allocate the gauge type specific extended data*/ + lv_gauge_ext_t * ext = lv_obj_allocate_ext_attr(new_gauge, sizeof(lv_gauge_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + /*Initialize the allocated 'ext' */ + ext->needle_count = 0; + ext->values = NULL; + ext->needle_colors = NULL; + ext->label_count = LV_GAUGE_DEF_LABEL_COUNT; + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_gauge); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_gauge); + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_gauge, lv_gauge_signal); + lv_obj_set_design_func(new_gauge, lv_gauge_design); + + /*Init the new gauge gauge*/ + if(copy == NULL) { + lv_gauge_set_scale(new_gauge, LV_GAUGE_DEF_ANGLE, LV_GAUGE_DEF_LINE_COUNT, LV_GAUGE_DEF_LABEL_COUNT); + lv_gauge_set_needle_count(new_gauge, 1, NULL); + lv_gauge_set_critical_value(new_gauge, 80); + lv_obj_set_size(new_gauge, 2 * LV_DPI, 2 * LV_DPI); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_gauge_set_style(new_gauge, th->gauge); + } else { + lv_gauge_set_style(new_gauge, &lv_style_pretty_color); + } + } + /*Copy an existing gauge*/ + else { + lv_gauge_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + lv_gauge_set_needle_count(new_gauge, copy_ext->needle_count, copy_ext->needle_colors); + + uint8_t i; + for(i = 0; i < ext->needle_count; i++) { + ext->values[i] = copy_ext->values[i]; + } + ext->label_count = copy_ext->label_count; + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_gauge); + } + + LV_LOG_INFO("gauge created"); + + return new_gauge; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the number of needles + * @param gauge pointer to gauge object + * @param needle_cnt new count of needles + * @param colors an array of colors for needles (with 'num' elements) + */ +void lv_gauge_set_needle_count(lv_obj_t * gauge, uint8_t needle_cnt, const lv_color_t * colors) +{ + lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge); + + if(ext->needle_count != needle_cnt) { + if(ext->values != NULL) { + lv_mem_free(ext->values); + ext->values = NULL; + } + + ext->values = lv_mem_realloc(ext->values, needle_cnt * sizeof(int16_t)); + lv_mem_assert(ext->values); + if(ext->values == NULL) return; + + int16_t min = lv_gauge_get_min_value(gauge); + uint8_t n; + for(n = ext->needle_count; n < needle_cnt; n++) { + ext->values[n] = min; + } + + ext->needle_count = needle_cnt; + } + + ext->needle_colors = colors; + lv_obj_invalidate(gauge); +} + +/** + * Set the value of a needle + * @param gauge pointer to a gauge + * @param needle_id the id of the needle + * @param value the new value + */ +void lv_gauge_set_value(lv_obj_t * gauge, uint8_t needle_id, int16_t value) +{ + lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge); + + if(needle_id >= ext->needle_count) return; + if(ext->values[needle_id] == value) return; + + + int16_t min = lv_gauge_get_min_value(gauge); + int16_t max = lv_gauge_get_max_value(gauge); + + if(value > max) value = max; + else if(value < min) value = min; + + ext->values[needle_id] = value; + + + lv_obj_invalidate(gauge); +} + + +/** + * Set the scale settings of a gauge + * @param gauge pointer to a gauge object + * @param angle angle of the scale (0..360) + * @param line_cnt count of scale lines. + * The get a given "subdivision" lines between label, `line_cnt` = (sub_div + 1) * (label_cnt - 1) + 1 + * @param label_cnt count of scale labels. + */ +void lv_gauge_set_scale(lv_obj_t * gauge, uint16_t angle, uint8_t line_cnt, uint8_t label_cnt) +{ + /*TODO v6.0: change `line_cnt` to `subdiv_cnt`*/ + + lv_lmeter_set_scale(gauge, angle, line_cnt); + + lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge); + ext->label_count = label_cnt; + lv_obj_invalidate(gauge); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the value of a needle + * @param gauge pointer to gauge object + * @param needle the id of the needle + * @return the value of the needle [min,max] + */ +int16_t lv_gauge_get_value(const lv_obj_t * gauge, uint8_t needle) +{ + lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge); + int16_t min = lv_gauge_get_min_value(gauge); + + if(needle >= ext->needle_count) return min; + + return ext->values[needle]; +} + +/** + * Get the count of needles on a gauge + * @param gauge pointer to gauge + * @return count of needles + */ +uint8_t lv_gauge_get_needle_count(const lv_obj_t * gauge) +{ + lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge); + return ext->needle_count; +} + +/** + * Set the number of labels (and the thicker lines too) + * @param gauge pointer to a gauge object + * @return count of labels + */ +uint8_t lv_gauge_get_label_count(const lv_obj_t * gauge) +{ + lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge); + return ext->label_count; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the gauges + * @param gauge pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_gauge_design(lv_obj_t * gauge, const lv_area_t * mask, lv_design_mode_t mode) +{ + + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + + /* Store the real pointer because of 'lv_group' + * If the object is in focus 'lv_obj_get_style()' will give a pointer to tmp style + * and to the real object style. It is important because of style change tricks below*/ + lv_style_t * style_ori_p = gauge->style_p; + lv_style_t * style = lv_obj_get_style(gauge); + lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge); + + lv_gauge_draw_scale(gauge, mask); + + /*Draw the ancestor line meter with max value to show the rainbow like line colors*/ + uint16_t line_cnt_tmp = ext->lmeter.line_cnt; + ancestor_design(gauge, mask, mode); /*To draw lines*/ + + /*Temporally modify the line meter to draw thicker and longer lines where labels are*/ + lv_style_t style_tmp; + lv_style_copy(&style_tmp, style); + ext->lmeter.line_cnt = ext->label_count; /*Only to labels*/ + style_tmp.line.width = style_tmp.line.width * 2; /*Ticker lines*/ + style_tmp.body.padding.hor = style_tmp.body.padding.hor * 2; /*Longer lines*/ + gauge->style_p = &style_tmp; + + ancestor_design(gauge, mask, mode); /*To draw lines*/ + + ext->lmeter.line_cnt = line_cnt_tmp; /*Restore the parameters*/ + gauge->style_p = style_ori_p; /*Restore the ORIGINAL style pointer*/ + + lv_gauge_draw_needle(gauge, mask); + + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + ancestor_design(gauge, mask, mode); + } + + return true; +} + +/** + * Signal function of the gauge + * @param gauge pointer to a gauge object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_gauge_signal(lv_obj_t * gauge, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(gauge, sign, param); + if(res != LV_RES_OK) return res; + + lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge); + if(sign == LV_SIGNAL_CLEANUP) { + lv_mem_free(ext->values); + ext->values = NULL; + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_gauge"; + } + + return res; +} + +/** + * Draw the scale on a gauge + * @param gauge pointer to gauge object + * @param mask mask of drawing + */ +static void lv_gauge_draw_scale(lv_obj_t * gauge, const lv_area_t * mask) +{ + char scale_txt[16]; + + lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge); + lv_style_t * style = lv_obj_get_style(gauge); + lv_opa_t opa_scale = lv_obj_get_opa_scale(gauge); + lv_coord_t r = lv_obj_get_width(gauge) / 2 - (3 * style->body.padding.hor) - style->body.padding.inner; + lv_coord_t x_ofs = lv_obj_get_width(gauge) / 2 + gauge->coords.x1; + lv_coord_t y_ofs = lv_obj_get_height(gauge) / 2 + gauge->coords.y1; + int16_t scale_angle = lv_lmeter_get_scale_angle(gauge); + uint16_t label_num = ext->label_count; + int16_t angle_ofs = 90 + (360 - scale_angle) / 2; + int16_t min = lv_gauge_get_min_value(gauge); + int16_t max = lv_gauge_get_max_value(gauge); + + uint8_t i; + for(i = 0; i < label_num; i++) { + /*Calculate the position a scale label*/ + int16_t angle = (i * scale_angle) / (label_num - 1) + angle_ofs; + + lv_coord_t y = (int32_t)((int32_t)lv_trigo_sin(angle) * r) / LV_TRIGO_SIN_MAX; + y += y_ofs; + + lv_coord_t x = (int32_t)((int32_t)lv_trigo_sin(angle + 90) * r) / LV_TRIGO_SIN_MAX; + x += x_ofs; + + int16_t scale_act = (int32_t)((int32_t)(max - min) * i) / (label_num - 1); + scale_act += min; + lv_math_num_to_str(scale_act, scale_txt); + + lv_area_t label_cord; + lv_point_t label_size; + lv_txt_get_size(&label_size, scale_txt, style->text.font, + style->text.letter_space, style->text.line_space, LV_COORD_MAX, LV_TXT_FLAG_NONE); + + /*Draw the label*/ + label_cord.x1 = x - label_size.x / 2; + label_cord.y1 = y - label_size.y / 2; + label_cord.x2 = label_cord.x1 + label_size.x; + label_cord.y2 = label_cord.y1 + label_size.y; + + lv_draw_label(&label_cord, mask, style, opa_scale, scale_txt, LV_TXT_FLAG_NONE, NULL); + } +} +/** + * Draw the needles of a gauge + * @param gauge pointer to gauge object + * @param mask mask of drawing + */ +static void lv_gauge_draw_needle(lv_obj_t * gauge, const lv_area_t * mask) +{ + lv_style_t style_needle; + lv_gauge_ext_t * ext = lv_obj_get_ext_attr(gauge); + lv_style_t * style = lv_gauge_get_style(gauge); + lv_opa_t opa_scale = lv_obj_get_opa_scale(gauge); + + lv_coord_t r = lv_obj_get_width(gauge) / 2 - style->body.padding.hor; + lv_coord_t x_ofs = lv_obj_get_width(gauge) / 2 + gauge->coords.x1; + lv_coord_t y_ofs = lv_obj_get_height(gauge) / 2 + gauge->coords.y1; + uint16_t angle = lv_lmeter_get_scale_angle(gauge); + int16_t angle_ofs = 90 + (360 - angle) / 2; + int16_t min = lv_gauge_get_min_value(gauge); + int16_t max = lv_gauge_get_max_value(gauge); + lv_point_t p_mid; + lv_point_t p_end; + lv_point_t p_end_low; + lv_point_t p_end_high; + uint8_t i; + + lv_style_copy(&style_needle, style); + + p_mid.x = x_ofs; + p_mid.y = y_ofs; + for(i = 0; i < ext->needle_count; i++) { + /*Calculate the end point of a needle*/ + int16_t needle_angle = (ext->values[i] - min) * angle * (1 << LV_GAUGE_INTERPOLATE_SHIFT) / (max - min); //+ angle_ofs; + + + int16_t needle_angle_low = (needle_angle >> LV_GAUGE_INTERPOLATE_SHIFT) + angle_ofs; + int16_t needle_angle_high = needle_angle_low + 1; + + + p_end_low.y = (lv_trigo_sin(needle_angle_low) * r) / LV_TRIGO_SIN_MAX + y_ofs; + p_end_low.x = (lv_trigo_sin(needle_angle_low + 90) * r) / LV_TRIGO_SIN_MAX + x_ofs; + + p_end_high.y = (lv_trigo_sin(needle_angle_high) * r) / LV_TRIGO_SIN_MAX + y_ofs; + p_end_high.x = (lv_trigo_sin(needle_angle_high + 90) * r) / LV_TRIGO_SIN_MAX + x_ofs; + + uint16_t rem = needle_angle & ((1 << LV_GAUGE_INTERPOLATE_SHIFT) - 1); + int16_t x_mod = ((LV_MATH_ABS(p_end_high.x - p_end_low.x)) * rem) >> LV_GAUGE_INTERPOLATE_SHIFT; + int16_t y_mod = ((LV_MATH_ABS(p_end_high.y - p_end_low.y)) * rem) >> LV_GAUGE_INTERPOLATE_SHIFT; + + if(p_end_high.x < p_end_low.x) x_mod = -x_mod; + if(p_end_high.y < p_end_low.y) y_mod = -y_mod; + + p_end.x = p_end_low.x + x_mod; + p_end.y = p_end_low.y + y_mod; + + /*Draw the needle with the corresponding color*/ + if(ext->needle_colors == NULL) style_needle.line.color = LV_GAUGE_DEF_NEEDLE_COLOR; + else style_needle.line.color = ext->needle_colors[i]; + + lv_draw_line(&p_mid, &p_end, mask, &style_needle, opa_scale); + } + + /*Draw the needle middle area*/ + lv_style_t style_neddle_mid; + lv_style_copy(&style_neddle_mid, &lv_style_plain); + style_neddle_mid.body.main_color = style->body.border.color; + style_neddle_mid.body.grad_color = style->body.border.color; + style_neddle_mid.body.radius = LV_RADIUS_CIRCLE; + + lv_area_t nm_cord; + nm_cord.x1 = x_ofs - style->body.padding.ver; + nm_cord.y1 = y_ofs - style->body.padding.ver; + nm_cord.x2 = x_ofs + style->body.padding.ver; + nm_cord.y2 = y_ofs + style->body.padding.ver; + + lv_draw_rect(&nm_cord, mask, &style_neddle_mid, lv_obj_get_opa_scale(gauge)); +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_gauge.h b/bdk/libs/lvgl/lv_objx/lv_gauge.h new file mode 100644 index 00000000..beef9dc5 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_gauge.h @@ -0,0 +1,222 @@ +/** + * @file lv_gauge.h + * + */ + +#ifndef LV_GAUGE_H +#define LV_GAUGE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_GAUGE != 0 + +/*Testing of dependencies*/ +#if USE_LV_LMETER == 0 +#error "lv_gauge: lv_lmeter is required. Enable it in lv_conf.h (USE_LV_LMETER 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_lmeter.h" +#include "lv_label.h" +#include "lv_line.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Data of gauge*/ +typedef struct +{ + lv_lmeter_ext_t lmeter; /*Ext. of ancestor*/ + /*New data for this type */ + int16_t * values; /*Array of the set values (for needles) */ + const lv_color_t * needle_colors; /*Color of the needles (lv_color_t my_colors[needle_num])*/ + uint8_t needle_count; /*Number of needles*/ + uint8_t label_count; /*Number of labels on the scale*/ +} lv_gauge_ext_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a gauge objects + * @param par pointer to an object, it will be the parent of the new gauge + * @param copy pointer to a gauge object, if not NULL then the new object will be copied from it + * @return pointer to the created gauge + */ +lv_obj_t * lv_gauge_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the number of needles + * @param gauge pointer to gauge object + * @param needle_cnt new count of needles + * @param colors an array of colors for needles (with 'num' elements) + */ +void lv_gauge_set_needle_count(lv_obj_t * gauge, uint8_t needle_cnt, const lv_color_t * colors); + +/** + * Set the value of a needle + * @param gauge pointer to a gauge + * @param needle_id the id of the needle + * @param value the new value + */ +void lv_gauge_set_value(lv_obj_t * gauge, uint8_t needle_id, int16_t value); + +/** + * Set minimum and the maximum values of a gauge + * @param gauge pointer to he gauge object + * @param min minimum value + * @param max maximum value + */ +static inline void lv_gauge_set_range(lv_obj_t *gauge, int16_t min, int16_t max) +{ + lv_lmeter_set_range(gauge, min, max); +} + +/** + * Set a critical value on the scale. After this value 'line.color' scale lines will be drawn + * @param gauge pointer to a gauge object + * @param value the critical value + */ +static inline void lv_gauge_set_critical_value(lv_obj_t * gauge, int16_t value) +{ + lv_lmeter_set_value(gauge, value); +} + +/** + * Set the scale settings of a gauge + * @param gauge pointer to a gauge object + * @param angle angle of the scale (0..360) + * @param line_cnt count of scale lines. + * The get a given "subdivision" lines between label, `line_cnt` = (sub_div + 1) * (label_cnt - 1) + 1 + * @param label_cnt count of scale labels. + */ +void lv_gauge_set_scale(lv_obj_t * gauge, uint16_t angle, uint8_t line_cnt, uint8_t label_cnt); + +/** + * Set the styles of a gauge + * @param gauge pointer to a gauge object + * @param bg set the style of the gauge + * */ +static inline void lv_gauge_set_style(lv_obj_t *gauge, lv_style_t *bg) +{ + lv_obj_set_style(gauge, bg); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the value of a needle + * @param gauge pointer to gauge object + * @param needle the id of the needle + * @return the value of the needle [min,max] + */ +int16_t lv_gauge_get_value(const lv_obj_t * gauge, uint8_t needle); + +/** + * Get the count of needles on a gauge + * @param gauge pointer to gauge + * @return count of needles + */ +uint8_t lv_gauge_get_needle_count(const lv_obj_t * gauge); + +/** + * Get the minimum value of a gauge + * @param gauge pointer to a gauge object + * @return the minimum value of the gauge + */ +static inline int16_t lv_gauge_get_min_value(const lv_obj_t * lmeter) +{ + return lv_lmeter_get_min_value(lmeter); +} + +/** + * Get the maximum value of a gauge + * @param gauge pointer to a gauge object + * @return the maximum value of the gauge + */ +static inline int16_t lv_gauge_get_max_value(const lv_obj_t * lmeter) +{ + return lv_lmeter_get_max_value(lmeter); +} + +/** + * Get a critical value on the scale. + * @param gauge pointer to a gauge object + * @return the critical value + */ +static inline int16_t lv_gauge_get_critical_value(const lv_obj_t * gauge) +{ + return lv_lmeter_get_value(gauge); +} + +/** + * Set the number of labels (and the thicker lines too) + * @param gauge pointer to a gauge object + * @return count of labels + */ +uint8_t lv_gauge_get_label_count(const lv_obj_t * gauge); + +/** + * Get the scale number of a gauge + * @param gauge pointer to a gauge object + * @return number of the scale units + */ +static inline uint8_t lv_gauge_get_line_count(const lv_obj_t * gauge) +{ + return lv_lmeter_get_line_count(gauge); +} + +/** + * Get the scale angle of a gauge + * @param gauge pointer to a gauge object + * @return angle of the scale + */ +static inline uint16_t lv_gauge_get_scale_angle(const lv_obj_t * gauge) +{ + return lv_lmeter_get_scale_angle(gauge); +} + +/** + * Get the style of a gauge + * @param gauge pointer to a gauge object + * @return pointer to the gauge's style + */ +static inline lv_style_t * lv_gauge_get_style(const lv_obj_t *gauge) +{ + return lv_obj_get_style(gauge); +} + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_GAUGE*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_GAUGE_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_img.c b/bdk/libs/lvgl/lv_objx/lv_img.c new file mode 100644 index 00000000..c9fb4287 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_img.c @@ -0,0 +1,408 @@ +/** + * @file lv_img.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_img.h" +#if USE_LV_IMG != 0 + +/*Testing of dependencies*/ +#if USE_LV_LABEL == 0 +#error "lv_img: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) " +#endif + +#include "../lv_core/lv_lang.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_fs.h" +#include "../lv_misc/lv_ufs.h" +#include "../lv_misc/lv_txt.h" +#include "../lv_misc/lv_log.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_img_design(lv_obj_t * img, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_img_signal(lv_obj_t * img, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create an image objects + * @param par pointer to an object, it will be the parent of the new button + * @param copy pointer to a image object, if not NULL then the new object will be copied from it + * @return pointer to the created image + */ +lv_obj_t * lv_img_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("image create started"); + + lv_obj_t * new_img = NULL; + + /*Create a basic object*/ + new_img = lv_obj_create(par, copy); + lv_mem_assert(new_img); + if(new_img == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_img); + + /*Extend the basic object to image object*/ + lv_img_ext_t * ext = lv_obj_allocate_ext_attr(new_img, sizeof(lv_img_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->src = NULL; + ext->src_type = LV_IMG_SRC_UNKNOWN; + ext->cf = LV_IMG_CF_UNKOWN; + ext->w = lv_obj_get_width(new_img); + ext->h = lv_obj_get_height(new_img); + ext->auto_size = 1; +#if USE_LV_MULTI_LANG + ext->lang_txt_id = LV_LANG_TXT_ID_NONE; +#endif + + /*Init the new object*/ + lv_obj_set_signal_func(new_img, lv_img_signal); + lv_obj_set_design_func(new_img, lv_img_design); + + if(copy == NULL) { + lv_obj_set_click(new_img, false); + /* Enable auto size for non screens + * because image screens are wallpapers + * and must be screen sized*/ + if(par != NULL) { + ext->auto_size = 1; + lv_obj_set_style(new_img, NULL); /*Inherit the style by default*/ + } else { + ext->auto_size = 0; + lv_obj_set_style(new_img, &lv_style_plain); /*Set a style for screens*/ + } + } else { + lv_img_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->auto_size = copy_ext->auto_size; + lv_img_set_src(new_img, copy_ext->src); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_img); + } + + + LV_LOG_INFO("image created"); + + return new_img; +} + + +/*===================== + * Setter functions + *====================*/ + + +/** + * Set the pixel map to display by the image + * @param img pointer to an image object + * @param data the image data + */ +void lv_img_set_src(lv_obj_t * img, const void * src_img) +{ + lv_img_src_t src_type = lv_img_src_get_type(src_img); + lv_img_ext_t * ext = lv_obj_get_ext_attr(img); + +#if LV_LOG_LEVEL >= LV_LOG_LEVEL_INFO + switch(src_type) { + case LV_IMG_SRC_FILE: + LV_LOG_TRACE("lv_img_set_src: `LV_IMG_SRC_FILE` type found"); + break; + case LV_IMG_SRC_VARIABLE: + LV_LOG_TRACE("lv_img_set_src: `LV_IMG_SRC_VARIABLE` type found"); + break; + case LV_IMG_SRC_SYMBOL: + LV_LOG_TRACE("lv_img_set_src: `LV_IMG_SRC_SYMBOL` type found"); + break; + default: + LV_LOG_WARN("lv_img_set_src: unknown type"); + } +#endif + + /*If the new source type is unknown free the memories of the old source*/ + if(src_type == LV_IMG_SRC_UNKNOWN) { + LV_LOG_WARN("lv_img_set_src: unknown image type"); + if(ext->src_type == LV_IMG_SRC_SYMBOL || ext->src_type == LV_IMG_SRC_FILE) { + lv_mem_free(ext->src); + } + ext->src = NULL; + ext->src_type = LV_IMG_SRC_UNKNOWN; + return; + } + + lv_img_header_t header; + lv_img_dsc_get_info(src_img, &header); + + + + /*Save the source*/ + if(src_type == LV_IMG_SRC_VARIABLE) { + LV_LOG_INFO("lv_img_set_src: `LV_IMG_SRC_VARIABLE` type found"); + + /*If memory was allocated because of the previous `src_type` then free it*/ + if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_SYMBOL) { + lv_mem_free(ext->src); + } + ext->src = src_img; + } else if(src_type == LV_IMG_SRC_FILE || src_type == LV_IMG_SRC_SYMBOL) { + /* If the new and the old src are the same then it was only a refresh.*/ + if(ext->src != src_img) { + /*If memory was allocated because of the previous `src_type` then free it*/ + if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_SYMBOL) { + lv_mem_free(ext->src); + } + char * new_str = lv_mem_alloc(strlen(src_img) + 1); + lv_mem_assert(new_str); + if(new_str == NULL) return; + strcpy(new_str, src_img); + ext->src = new_str; + } + } + + if(src_type == LV_IMG_SRC_SYMBOL) { + /*`lv_img_dsc_get_info` couldn't set the with and height of a font so set it here*/ + lv_style_t * style = lv_img_get_style(img); + lv_point_t size; + lv_txt_get_size(&size, src_img, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX, LV_TXT_FLAG_NONE); + header.w = size.x; + header.h = size.y; + } + + ext->src_type = src_type; + ext->w = header.w; + ext->h = header.h; + ext->cf = header.cf; + + if(lv_img_get_auto_size(img) != false) { + lv_obj_set_size(img, ext->w, ext->h); + } + + lv_obj_invalidate(img); +} + +#if USE_LV_MULTI_LANG +/** + * Set an ID which means a the same source but in different languages + * @param img pointer to an image object + * @param src_id ID of the source + */ +void lv_img_set_src_id(lv_obj_t * img, uint32_t src_id) +{ + lv_img_ext_t * ext = lv_obj_get_ext_attr(img); + ext->lang_txt_id = src_id; + + /*Apply the new language*/ + img->signal_func(img, LV_SIGNAL_LANG_CHG, NULL); +} +#endif + +/** + * Enable the auto size feature. + * If enabled the object size will be same as the picture size. + * @param img pointer to an image + * @param en true: auto size enable, false: auto size disable + */ +void lv_img_set_auto_size(lv_obj_t * img, bool en) +{ + lv_img_ext_t * ext = lv_obj_get_ext_attr(img); + + ext->auto_size = (en == false ? 0 : 1); +} + + +/*===================== + * Getter functions + *====================*/ + + +/** + * Get the source of the image + * @param img pointer to an image object + * @return the image source (symbol, file name or C array) + */ +const void * lv_img_get_src(lv_obj_t * img) +{ + lv_img_ext_t * ext = lv_obj_get_ext_attr(img); + + return ext->src; +} + +/** + * Get the name of the file set for an image + * @param img pointer to an image + * @return file name + */ +const char * lv_img_get_file_name(const lv_obj_t * img) +{ + lv_img_ext_t * ext = lv_obj_get_ext_attr(img); + + if(ext->src_type == LV_IMG_SRC_FILE) return ext->src; + else return ""; +} + +#if USE_LV_MULTI_LANG +/** + * Get the source ID of the image. (Used by the multi-language feature) + * @param img pointer to an image + * @return ID of the source + */ +uint16_t lv_img_get_src_id(lv_obj_t * img) +{ + lv_img_ext_t * ext = lv_obj_get_ext_attr(img); + return ext->lang_txt_id; +} +#endif + +/** + * Get the auto size enable attribute + * @param img pointer to an image + * @return true: auto size is enabled, false: auto size is disabled + */ +bool lv_img_get_auto_size(const lv_obj_t * img) +{ + lv_img_ext_t * ext = lv_obj_get_ext_attr(img); + + return ext->auto_size == 0 ? false : true; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the images + * @param img pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_img_design(lv_obj_t * img, const lv_area_t * mask, lv_design_mode_t mode) +{ + lv_style_t * style = lv_obj_get_style(img); + lv_img_ext_t * ext = lv_obj_get_ext_attr(img); + + if(mode == LV_DESIGN_COVER_CHK) { + bool cover = false; + if(ext->src_type == LV_IMG_SRC_UNKNOWN || ext->src_type == LV_IMG_SRC_SYMBOL) return false; + + if(ext->cf == LV_IMG_CF_TRUE_COLOR || ext->cf == LV_IMG_CF_RAW) cover = lv_area_is_in(mask, &img->coords); + + return cover; + } else if(mode == LV_DESIGN_DRAW_MAIN) { + if(ext->h == 0 || ext->w == 0) return true; + lv_area_t coords; + lv_opa_t opa_scale = lv_obj_get_opa_scale(img); + + lv_obj_get_coords(img, &coords); + + if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_VARIABLE) { + LV_LOG_TRACE("lv_img_design: start to draw image"); + lv_area_t cords_tmp; + cords_tmp.y1 = coords.y1; + cords_tmp.y2 = coords.y1 + ext->h - 1; + + for(; cords_tmp.y1 < coords.y2; cords_tmp.y1 += ext->h, cords_tmp.y2 += ext->h) { + cords_tmp.x1 = coords.x1; + cords_tmp.x2 = coords.x1 + ext->w - 1; + for(; cords_tmp.x1 < coords.x2; cords_tmp.x1 += ext->w, cords_tmp.x2 += ext->w) { + lv_draw_img(&cords_tmp, mask, ext->src, style, opa_scale); + } + } + } else if(ext->src_type == LV_IMG_SRC_SYMBOL) { + LV_LOG_TRACE("lv_img_design: start to draw symbol"); + lv_style_t style_mod; + lv_style_copy(&style_mod, style); + style_mod.text.color = style->image.color; + lv_draw_label(&coords, mask, &style_mod, opa_scale, ext->src, LV_TXT_FLAG_NONE, NULL); + } else { + /*Trigger the error handler of image drawer*/ + LV_LOG_WARN("lv_img_design: image source type is unknown"); + lv_draw_img(&img->coords, mask, NULL, style, opa_scale); + } + } + + return true; +} + + +/** + * Signal function of the image + * @param img pointer to an image object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_img_signal(lv_obj_t * img, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(img, sign, param); + if(res != LV_RES_OK) return res; + + lv_img_ext_t * ext = lv_obj_get_ext_attr(img); + if(sign == LV_SIGNAL_CLEANUP) { + if(ext->src_type == LV_IMG_SRC_FILE || ext->src_type == LV_IMG_SRC_SYMBOL) { + lv_mem_free(ext->src); + ext->src = NULL; + ext->src_type = LV_IMG_SRC_UNKNOWN; + } + } else if(sign == LV_SIGNAL_STYLE_CHG) { + /*Refresh the file name to refresh the symbol text size*/ + if(ext->src_type == LV_IMG_SRC_SYMBOL) { + lv_img_set_src(img, ext->src); + + } + } else if(sign == LV_SIGNAL_LANG_CHG) { +#if USE_LV_MULTI_LANG + if(ext->lang_txt_id != LV_LANG_TXT_ID_NONE) { + const char * lang_src = lv_lang_get_text(ext->lang_txt_id); + if(lang_src) { + lv_img_set_src(img, lang_src); + } else { + LV_LOG_WARN("lv_lang_get_text return NULL for an image's source"); + } + } +#endif + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_img"; + } + + return res; +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_img.h b/bdk/libs/lvgl/lv_objx/lv_img.h new file mode 100644 index 00000000..8ee86167 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_img.h @@ -0,0 +1,195 @@ +/** + * @file lv_img.h + * + */ + +#ifndef LV_IMG_H +#define LV_IMG_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_IMG != 0 + +#include "../lv_core/lv_obj.h" +#include "../lv_misc/lv_fs.h" +#include "../lv_misc/lv_symbol_def.h" +#include "lv_label.h" +#include "../lv_draw/lv_draw.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of image*/ +typedef struct +{ + /*No inherited ext. because inherited from the base object*/ /*Ext. of ancestor*/ + /*New data for this type */ + const void * src; /*Image source: Pointer to an array or a file or a symbol*/ + + lv_coord_t w; /*Width of the image (Handled by the library)*/ + lv_coord_t h; /*Height of the image (Handled by the library)*/ +#if USE_LV_MULTI_LANG + uint16_t lang_txt_id; /*The ID of the image to display. */ +#endif + uint8_t src_type :2; /*See: lv_img_src_t*/ + uint8_t auto_size :1; /*1: automatically set the object size to the image size*/ + uint8_t cf :5; /*Color format from `lv_img_color_format_t`*/ +} lv_img_ext_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create an image objects + * @param par pointer to an object, it will be the parent of the new button + * @param copy pointer to a image object, if not NULL then the new object will be copied from it + * @return pointer to the created image + */ +lv_obj_t * lv_img_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the pixel map to display by the image + * @param img pointer to an image object + * @param data the image data + */ +void lv_img_set_src(lv_obj_t * img, const void * src_img); + +#if USE_LV_MULTI_LANG +/** + * Set an ID which means a the same source but on different languages + * @param img pointer to an image object + * @param src_id ID of the source + */ +void lv_img_set_src_id(lv_obj_t * img, uint32_t txt_id); +#endif + +/** + * Obsolete since v5.1. Just for compatibility with v5.0. Will be removed in v6.0. + * Use 'lv_img_set_src()' instead. + * @param img - + * @param fn - + */ +static inline void lv_img_set_file(lv_obj_t * img, const char * fn) +{ + (void) img; + (void) fn; +} + +/** + * Enable the auto size feature. + * If enabled the object size will be same as the picture size. + * @param img pointer to an image + * @param en true: auto size enable, false: auto size disable + */ +void lv_img_set_auto_size(lv_obj_t * img, bool autosize_en); + +/** + * Set the style of an image + * @param img pointer to an image object + * @param style pointer to a style + */ +static inline void lv_img_set_style(lv_obj_t *img, lv_style_t *style) +{ + lv_obj_set_style(img, style); +} + +/** + * Obsolete since v5.1. Just for compatibility with v5.0. Will be removed in v6.0 + * @param img - + * @param upscale - + */ +static inline void lv_img_set_upscale(lv_obj_t * img, bool upcale) +{ + (void) img; + (void) upcale; +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the source of the image + * @param img pointer to an image object + * @return the image source (symbol, file name or C array) + */ +const void * lv_img_get_src(lv_obj_t * img); + +/** + * Get the name of the file set for an image + * @param img pointer to an image + * @return file name + */ +const char * lv_img_get_file_name(const lv_obj_t * img); + +#if USE_LV_MULTI_LANG +/** + * Get the source ID of the image. (Used by the multi-language feature) + * @param img pointer to an image + * @return ID of the source + */ +uint16_t lv_img_get_src_id(lv_obj_t * img); +#endif + +/** + * Get the auto size enable attribute + * @param img pointer to an image + * @return true: auto size is enabled, false: auto size is disabled + */ +bool lv_img_get_auto_size(const lv_obj_t * img); + +/** + * Get the style of an image object + * @param img pointer to an image object + * @return pointer to the image's style + */ +static inline lv_style_t* lv_img_get_style(const lv_obj_t *img) +{ + return lv_obj_get_style(img); +} + +/** + * Obsolete since v5.1. Just for compatibility with v5.0. Will be removed in v6.0 + * @param img - + * @return false + */ +static inline bool lv_img_get_upscale(const lv_obj_t * img) +{ + (void)img; + return false; +} + +/********************** + * MACROS + **********************/ + +/*Use this macro to declare an image in a c file*/ +#define LV_IMG_DECLARE(var_name) extern const lv_img_dsc_t var_name; + +#endif /*USE_LV_IMG*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_IMG_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_imgbtn.c b/bdk/libs/lvgl/lv_objx/lv_imgbtn.c new file mode 100644 index 00000000..ed1d72b4 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_imgbtn.c @@ -0,0 +1,391 @@ +/** + * @file lv_imgbtn.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_imgbtn.h" +#if USE_LV_IMGBTN != 0 + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_imgbtn_design(lv_obj_t * imgbtn, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_imgbtn_signal(lv_obj_t * imgbtn, lv_signal_t sign, void * param); +static void refr_img(lv_obj_t * imgbtn); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_design_func_t ancestor_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a image button object + * @param par pointer to an object, it will be the parent of the new image button + * @param copy pointer to a image button object, if not NULL then the new object will be copied from it + * @return pointer to the created image button + */ +lv_obj_t * lv_imgbtn_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("image button create started"); + + /*Create the ancestor of image button*/ + lv_obj_t * new_imgbtn = lv_btn_create(par, copy); + lv_mem_assert(new_imgbtn); + if(new_imgbtn == NULL) return NULL; + + /*Allocate the image button type specific extended data*/ + lv_imgbtn_ext_t * ext = lv_obj_allocate_ext_attr(new_imgbtn, sizeof(lv_imgbtn_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_imgbtn); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_imgbtn); + + /*Initialize the allocated 'ext' */ +#if LV_IMGBTN_TILED == 0 + memset(ext->img_src, 0, sizeof(ext->img_src)); +#else + memset(ext->img_src_left, 0, sizeof(ext->img_src_left)); + memset(ext->img_src_mid, 0, sizeof(ext->img_src_mid)); + memset(ext->img_src_right, 0, sizeof(ext->img_src_right)); +#endif + + ext->act_cf = LV_IMG_CF_UNKOWN; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_imgbtn, lv_imgbtn_signal); + lv_obj_set_design_func(new_imgbtn, lv_imgbtn_design); + + /*Init the new image button image button*/ + if(copy == NULL) { + + } + /*Copy an existing image button*/ + else { + lv_imgbtn_ext_t * copy_ext = lv_obj_get_ext_attr(copy); +#if LV_IMGBTN_TILED == 0 + memcpy(ext->img_src, copy_ext->img_src, sizeof(ext->img_src)); +#else + memcpy(ext->img_src_left, copy_ext->img_src_left, sizeof(ext->img_src_left)); + memcpy(ext->img_src_mid, copy_ext->img_src_mid, sizeof(ext->img_src_mid)); + memcpy(ext->img_src_right, copy_ext->img_src_right, sizeof(ext->img_src_right)); +#endif + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_imgbtn); + } + + LV_LOG_INFO("image button created"); + + return new_imgbtn; +} + +/*===================== + * Setter functions + *====================*/ + +#if LV_IMGBTN_TILED == 0 +/** + * Set images for a state of the image button + * @param imgbtn pointer to an image button object + * @param state for which state set the new image (from `lv_btn_state_t`) ` + * @param src pointer to an image source (a C array or path to a file) + */ +void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src) +{ + lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); + + ext->img_src[state] = src; + + refr_img(imgbtn); +} + +#else +/** + * Set images for a state of the image button + * @param imgbtn pointer to an image button object + * @param state for which state set the new image (from `lv_btn_state_t`) ` + * @param src_left pointer to an image source for the left side of the button (a C array or path to a file) + * @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C array or path to a file) + * @param src_right pointer to an image source for the right side of the button (a C array or path to a file) + */ +void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src_left, const void * src_mid, const void * src_right) +{ + lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); + + ext->img_src_left[state] = src_left; + ext->img_src_mid[state] = src_mid; + ext->img_src_right[state] = src_right; + + refr_img(imgbtn); +} + +#endif + +/** + * Set a style of a image button. + * @param imgbtn pointer to image button object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_imgbtn_set_style(lv_obj_t * imgbtn, lv_imgbtn_style_t type, lv_style_t * style) +{ + lv_btn_set_style(imgbtn, type, style); +} + +/*===================== + * Getter functions + *====================*/ + +#if LV_IMGBTN_TILED == 0 +/** + * Get the images in a given state + * @param imgbtn pointer to an image button object + * @param state the state where to get the image (from `lv_btn_state_t`) ` + * @return pointer to an image source (a C array or path to a file) + */ +const void * lv_imgbtn_get_src(lv_obj_t * imgbtn, lv_btn_state_t state) +{ + lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); + + return ext->img_src[state]; +} +#else + +/** + * Get the left image in a given state + * @param imgbtn pointer to an image button object + * @param state the state where to get the image (from `lv_btn_state_t`) ` + * @return pointer to the left image source (a C array or path to a file) + */ +const void * lv_imgbtn_get_src_left(lv_obj_t * imgbtn, lv_btn_state_t state) +{ + lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); + + return ext->img_src_left[state]; +} + +/** + * Get the middle image in a given state + * @param imgbtn pointer to an image button object + * @param state the state where to get the image (from `lv_btn_state_t`) ` + * @return pointer to the middle image source (a C array or path to a file) + */ +const void * lv_imgbtn_get_src_middle(lv_obj_t * imgbtn, lv_btn_state_t state) +{ + lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); + + return ext->img_src_mid[state]; +} + +/** + * Get the right image in a given state + * @param imgbtn pointer to an image button object + * @param state the state where to get the image (from `lv_btn_state_t`) ` + * @return pointer to the left image source (a C array or path to a file) + */ +const void * lv_imgbtn_get_src_right(lv_obj_t * imgbtn, lv_btn_state_t state) +{ + lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); + + return ext->img_src_right[state]; +} + +#endif + +/** + * Get style of a image button. + * @param imgbtn pointer to image button object + * @param type which style should be get + * @return style pointer to the style + */ +lv_style_t * lv_imgbtn_get_style(const lv_obj_t * imgbtn, lv_imgbtn_style_t type) +{ + return lv_btn_get_style(imgbtn, type); +} + +/*===================== + * Other functions + *====================*/ + +/* + * New object specific "other" functions come here + */ + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the image buttons + * @param imgbtn pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_imgbtn_design(lv_obj_t * imgbtn, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); + bool cover = false; + if(ext->act_cf == LV_IMG_CF_TRUE_COLOR || ext->act_cf == LV_IMG_CF_RAW) { + cover = lv_area_is_in(mask, &imgbtn->coords); + } + + return cover; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + /*Just draw an image*/ + lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); + lv_btn_state_t state = lv_imgbtn_get_state(imgbtn); + lv_style_t * style = lv_imgbtn_get_style(imgbtn, state); + lv_opa_t opa_scale = lv_obj_get_opa_scale(imgbtn); + +#if LV_IMGBTN_TILED == 0 + const void * src = ext->img_src[state]; + lv_draw_img(&imgbtn->coords, mask, src, style, opa_scale); +#else + const void * src; + lv_img_header_t header; + lv_area_t coords; + lv_coord_t left_w = 0; + lv_coord_t right_w = 0; + + src = ext->img_src_left[state]; + if(src) { + lv_img_dsc_get_info(src, &header); + left_w = header.w; + coords.x1 = imgbtn->coords.x1; + coords.y1 = imgbtn->coords.y1; + coords.x2 = coords.x1 + header.w - 1; + coords.y2 = coords.y1 + header.h - 1; + lv_draw_img(&coords, mask, src, style, opa_scale); + } + + src = ext->img_src_right[state]; + if(src) { + lv_img_dsc_get_info(src, &header); + right_w = header.w; + coords.x1 = imgbtn->coords.x2 - header.w + 1; + coords.y1 = imgbtn->coords.y1; + coords.x2 = imgbtn->coords.x2; + coords.y2 = imgbtn->coords.y1 + header.h - 1; + lv_draw_img(&coords, mask, src, style, opa_scale); + } + + src = ext->img_src_mid[state]; + if(src) { + lv_coord_t obj_w = lv_obj_get_width(imgbtn); + lv_coord_t i; + lv_img_dsc_get_info(src, &header); + + coords.x1 = imgbtn->coords.x1 + left_w ; + coords.y1 = imgbtn->coords.y1; + coords.x2 = coords.x1 + header.w - 1; + coords.y2 = imgbtn->coords.y1 + header.h - 1; + + for(i = 0; i < obj_w - right_w - left_w; i += header.w) { + lv_draw_img(&coords, mask, src, style, opa_scale); + coords.x1 = coords.x2 + 1; + coords.x2 += header.w; + } + } + + +#endif + + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + + } + + return true; +} + +/** + * Signal function of the image button + * @param imgbtn pointer to a image button object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_imgbtn_signal(lv_obj_t * imgbtn, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(imgbtn, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_STYLE_CHG) { + /* If the style changed then the button was clicked, released etc. so probably the state was changed as well + * Set the new image for the new state.*/ + refr_img(imgbtn); + } else if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_imgbtn"; + } + + return res; +} + + +static void refr_img(lv_obj_t * imgbtn) +{ + lv_imgbtn_ext_t * ext = lv_obj_get_ext_attr(imgbtn); + lv_btn_state_t state = lv_imgbtn_get_state(imgbtn); + lv_img_header_t header; + +#if LV_IMGBTN_TILED == 0 + const void * src = ext->img_src[state]; +#else + const void * src = ext->img_src_mid[state]; +#endif + + lv_res_t info_res; + info_res = lv_img_dsc_get_info(src, &header); + if(info_res == LV_RES_OK) { + ext->act_cf = header.cf; +#if LV_IMGBTN_TILED == 0 + lv_obj_set_size(imgbtn, header.w, header.h); +#else + lv_obj_set_height(imgbtn, header.h); +#endif + } else { + ext->act_cf = LV_IMG_CF_UNKOWN; + } + + lv_obj_invalidate(imgbtn); +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_imgbtn.h b/bdk/libs/lvgl/lv_objx/lv_imgbtn.h new file mode 100644 index 00000000..300e7b8e --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_imgbtn.h @@ -0,0 +1,249 @@ +/** + * @file lv_imgbtn.h + * + */ + +#ifndef LV_IMGBTN_H +#define LV_IMGBTN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_IMGBTN != 0 + +/*Testing of dependencies*/ +#if USE_LV_BTN == 0 +#error "lv_imgbtn: lv_btn is required. Enable it in lv_conf.h (USE_LV_BTN 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_btn.h" +#include "../lv_draw/lv_draw_img.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of image button*/ +typedef struct { + lv_btn_ext_t btn; /*Ext. of ancestor*/ + /*New data for this type */ + int idx; +#if LV_IMGBTN_TILED == 0 + const void * img_src[LV_BTN_STATE_NUM]; /*Store images to each state*/ +#else + const void * img_src_left[LV_BTN_STATE_NUM]; /*Store left side images to each state*/ + const void * img_src_mid[LV_BTN_STATE_NUM]; /*Store center images to each state*/ + const void * img_src_right[LV_BTN_STATE_NUM]; /*Store right side images to each state*/ +#endif + lv_img_cf_t act_cf; /*Color format of the currently active image*/ +} lv_imgbtn_ext_t; + + +/*Styles*/ +enum { + LV_IMGBTN_STYLE_REL, + LV_IMGBTN_STYLE_PR, + LV_IMGBTN_STYLE_TGL_REL, + LV_IMGBTN_STYLE_TGL_PR, + LV_IMGBTN_STYLE_INA, +}; +typedef uint8_t lv_imgbtn_style_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a image button objects + * @param par pointer to an object, it will be the parent of the new image button + * @param copy pointer to a image button object, if not NULL then the new object will be copied from it + * @return pointer to the created image button + */ +lv_obj_t * lv_imgbtn_create(lv_obj_t * par, const lv_obj_t * copy); + +/*====================== + * Add/remove functions + *=====================*/ + + +/*===================== + * Setter functions + *====================*/ + +#if LV_IMGBTN_TILED == 0 +/** + * Set images for a state of the image button + * @param imgbtn pointer to an image button object + * @param state for which state set the new image (from `lv_btn_state_t`) ` + * @param src pointer to an image source (a C array or path to a file) + */ +void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src); +#else +/** + * Set images for a state of the image button + * @param imgbtn pointer to an image button object + * @param state for which state set the new image (from `lv_btn_state_t`) ` + * @param src_left pointer to an image source for the left side of the button (a C array or path to a file) + * @param src_mid pointer to an image source for the middle of the button (ideally 1px wide) (a C array or path to a file) + * @param src_right pointer to an image source for the right side of the button (a C array or path to a file) + */ +void lv_imgbtn_set_src(lv_obj_t * imgbtn, lv_btn_state_t state, const void * src_left, const void * src_mid, const void * src_right); + +#endif + +/** + * Enable the toggled states. On release the button will change from/to toggled state. + * @param imgbtn pointer to an image button object + * @param tgl true: enable toggled states, false: disable + */ +static inline void lv_imgbtn_set_toggle(lv_obj_t * imgbtn, bool tgl) +{ + lv_btn_set_toggle(imgbtn, tgl); +} + +/** + * Set the state of the image button + * @param imgbtn pointer to an image button object + * @param state the new state of the button (from lv_btn_state_t enum) + */ +static inline void lv_imgbtn_set_state(lv_obj_t * imgbtn, lv_btn_state_t state) +{ + lv_btn_set_state(imgbtn, state); +} + +/** + * Toggle the state of the image button (ON->OFF, OFF->ON) + * @param imgbtn pointer to a image button object + */ +static inline void lv_imgbtn_toggle(lv_obj_t * imgbtn) +{ + lv_btn_toggle(imgbtn); +} + +/** + * Set a function to call when a button event happens + * @param imgbtn pointer to an image button object + * @param action type of event form 'lv_action_t' (press, release, long press, long press repeat) + */ +static inline void lv_imgbtn_set_action(lv_obj_t * imgbtn, lv_btn_action_t type, lv_action_t action) +{ + lv_btn_set_action(imgbtn, type, action); +} + +/** + * Set a style of a image button. + * @param imgbtn pointer to image button object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_imgbtn_set_style(lv_obj_t * imgbtn, lv_imgbtn_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + + +#if LV_IMGBTN_TILED == 0 +/** + * Get the images in a given state + * @param imgbtn pointer to an image button object + * @param state the state where to get the image (from `lv_btn_state_t`) ` + * @return pointer to an image source (a C array or path to a file) + */ +const void * lv_imgbtn_get_src(lv_obj_t * imgbtn, lv_btn_state_t state); + +#else + +/** + * Get the left image in a given state + * @param imgbtn pointer to an image button object + * @param state the state where to get the image (from `lv_btn_state_t`) ` + * @return pointer to the left image source (a C array or path to a file) + */ +const void * lv_imgbtn_get_src_left(lv_obj_t * imgbtn, lv_btn_state_t state); + +/** + * Get the middle image in a given state + * @param imgbtn pointer to an image button object + * @param state the state where to get the image (from `lv_btn_state_t`) ` + * @return pointer to the middle image source (a C array or path to a file) + */ +const void * lv_imgbtn_get_src_middle(lv_obj_t * imgbtn, lv_btn_state_t state); + +/** + * Get the right image in a given state + * @param imgbtn pointer to an image button object + * @param state the state where to get the image (from `lv_btn_state_t`) ` + * @return pointer to the left image source (a C array or path to a file) + */ +const void * lv_imgbtn_get_src_right(lv_obj_t * imgbtn, lv_btn_state_t state); + +#endif +/** + * Get the current state of the image button + * @param imgbtn pointer to a image button object + * @return the state of the button (from lv_btn_state_t enum) + */ +static inline lv_btn_state_t lv_imgbtn_get_state(const lv_obj_t * imgbtn) +{ + return lv_btn_get_state(imgbtn); +} + +/** + * Get the toggle enable attribute of the image button + * @param imgbtn pointer to a image button object + * @return ture: toggle enabled, false: disabled + */ +static inline bool lv_imgbtn_get_toggle(const lv_obj_t * imgbtn) +{ + return lv_btn_get_toggle(imgbtn); +} + +/** + * Get the release action of a image button + * @param imgbtn pointer to a image button object + * @return pointer to the release action function + */ +static inline lv_action_t lv_imgbtn_get_action(const lv_obj_t * imgbtn, lv_btn_action_t type) +{ + return lv_btn_get_action(imgbtn, type); +} + +/** + * Get style of a image button. + * @param imgbtn pointer to image button object + * @param type which style should be get + * @return style pointer to the style + */ +lv_style_t * lv_imgbtn_get_style(const lv_obj_t * imgbtn, lv_imgbtn_style_t type); + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_IMGBTN*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_IMGBTN_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_kb.c b/bdk/libs/lvgl/lv_objx/lv_kb.c new file mode 100644 index 00000000..1d1b3f4f --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_kb.c @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2019-2020 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_kb.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_kb.h" +#if USE_LV_KB != 0 + +#include "lv_ta.h" +#include "../lv_themes/lv_theme.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_kb_signal(lv_obj_t * kb, lv_signal_t sign, void * param); +static lv_res_t lv_kb_def_action(lv_obj_t * kb, const char * txt); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; + +static const char * kb_map_lc[] = { + "\2051#", "\204q", "\204w", "\204e", "\204r", "\204t", "\204y", "\204u", "\204i", "\204o", "\204p", "\207Bksp", "\n", + "\226ABC", "\203a", "\203s", "\203d", "\203f", "\203g", "\203h", "\203j", "\203k", "\203l", "\207Enter", "\n", + "_", "-", "z", "x", "c", "v", "b", "n", "m", ".", ",", ":", "\n", + "\202"SYMBOL_CLOSE, "\202"SYMBOL_LEFT, "\206 ", "\202"SYMBOL_RIGHT, "\202"SYMBOL_OK, "" +}; + +static const char * kb_map_uc[] = { + "\2051#", "\204Q", "\204W", "\204E", "\204R", "\204T", "\204Y", "\204U", "\204I", "\204O", "\204P", "\207Bksp", "\n", + "\226abc", "\203A", "\203S", "\203D", "\203F", "\203G", "\203H", "\203J", "\203K", "\203L", "\207Enter", "\n", + "_", "-", "Z", "X", "C", "V", "B", "N", "M", ".", ",", ":", "\n", + "\202"SYMBOL_CLOSE, "\202"SYMBOL_LEFT, "\206 ", "\202"SYMBOL_RIGHT, "\202"SYMBOL_OK, "" +}; + +static const char * kb_map_spec[] = { + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "\202Bksp", "\n", + "\222abc", "+", "-", "/", "*", "=", "%", "!", "?", "#", "<", ">", "\n", + "\\", "@", "$", "(", ")", "{", "}", "[", "]", ";", "\"", "'", "\n", + "\202"SYMBOL_CLOSE, "\202"SYMBOL_LEFT, "\206 ", "\202"SYMBOL_RIGHT, "\202"SYMBOL_OK, "" +}; + +static const char * kb_map_num[] = { + "1", "2", "3", "\202"SYMBOL_CLOSE, "\n", + "4", "5", "6", "\202"SYMBOL_OK, "\n", + "7", "8", "9", "\202Bksp", "\n", + "+/-", "0", ".", SYMBOL_LEFT, SYMBOL_RIGHT, "" +}; + +static const char * kb_map_hex[] = { + "1", "2", "3", "A", "D", "\212", "\n", + "4", "5", "6", "B", "E", "\202Bksp", "\n", + "7", "8", "9", "C", "F", "\202"SYMBOL_OK, "\n", + "\211", "0", "\213", SYMBOL_LEFT, SYMBOL_RIGHT, "" +}; +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a keyboard objects + * @param par pointer to an object, it will be the parent of the new keyboard + * @param copy pointer to a keyboard object, if not NULL then the new object will be copied from it + * @return pointer to the created keyboard + */ +lv_obj_t * lv_kb_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("keyboard create started"); + + /*Create the ancestor of keyboard*/ + lv_obj_t * new_kb = lv_btnm_create(par, copy); + lv_mem_assert(new_kb); + if(new_kb == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_kb); + + /*Allocate the keyboard type specific extended data*/ + lv_kb_ext_t * ext = lv_obj_allocate_ext_attr(new_kb, sizeof(lv_kb_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + /*Initialize the allocated 'ext' */ + + ext->ta = NULL; + ext->mode = LV_KB_MODE_TEXT; + ext->cursor_mng = 0; + ext->hide_action = NULL; + ext->ok_action = NULL; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_kb, lv_kb_signal); + + /*Init the new keyboard keyboard*/ + if(copy == NULL) { + lv_obj_set_size(new_kb, LV_HOR_RES, LV_VER_RES / 2); + lv_obj_align(new_kb, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, 0); + lv_btnm_set_action(new_kb, lv_kb_def_action); + lv_btnm_set_map(new_kb, kb_map_lc); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_kb_set_style(new_kb, LV_KB_STYLE_BG, th->kb.bg); + lv_kb_set_style(new_kb, LV_KB_STYLE_BTN_REL, th->kb.btn.rel); + lv_kb_set_style(new_kb, LV_KB_STYLE_BTN_PR, th->kb.btn.pr); + lv_kb_set_style(new_kb, LV_KB_STYLE_BTN_TGL_REL, th->kb.btn.tgl_rel); + lv_kb_set_style(new_kb, LV_KB_STYLE_BTN_TGL_PR, th->kb.btn.tgl_pr); + lv_kb_set_style(new_kb, LV_KB_STYLE_BTN_INA, th->kb.btn.ina); + } else { + /*Let the button matrix's styles*/ + } + } + /*Copy an existing keyboard*/ + else { + lv_kb_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->ta = NULL; + ext->ta = copy_ext->ta; + ext->mode = copy_ext->mode; + ext->cursor_mng = copy_ext->cursor_mng; + ext->hide_action = copy_ext->hide_action; + ext->ok_action = copy_ext->ok_action; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_kb); + } + + + LV_LOG_INFO("keyboard created"); + + + return new_kb; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Assign a Text Area to the Keyboard. The pressed characters will be put there. + * @param kb pointer to a Keyboard object + * @param ta pointer to a Text Area object to write there + */ +void lv_kb_set_ta(lv_obj_t * kb, lv_obj_t * ta) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + lv_cursor_type_t cur_type; + + /*Hide the cursor of the old Text area if cursor management is enabled*/ + if(ext->ta && ext->cursor_mng) { + cur_type = lv_ta_get_cursor_type(ext->ta); + lv_ta_set_cursor_type(ext->ta, cur_type | LV_CURSOR_HIDDEN); + } + + ext->ta = ta; + + /*Show the cursor of the new Text area if cursor management is enabled*/ + if(ext->ta && ext->cursor_mng) { + cur_type = lv_ta_get_cursor_type(ext->ta); + lv_ta_set_cursor_type(ext->ta, cur_type & (~LV_CURSOR_HIDDEN)); + } +} + +/** + * Set a new a mode (text or number map) + * @param kb pointer to a Keyboard object + * @param mode the mode from 'lv_kb_mode_t' + */ +void lv_kb_set_mode(lv_obj_t * kb, lv_kb_mode_t mode) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + if(ext->mode == mode) return; + + ext->mode = mode; + if(mode == LV_KB_MODE_TEXT) lv_btnm_set_map(kb, kb_map_lc); + else if(mode == LV_KB_MODE_NUM) lv_btnm_set_map(kb, kb_map_num); + else if (mode == LV_KB_MODE_HEX) lv_btnm_set_map(kb, kb_map_hex); +} + + +/** + * Automatically hide or show the cursor of Text Area + * @param kb pointer to a Keyboard object + * @param en true: show cursor on the current text area, false: hide cursor + */ +void lv_kb_set_cursor_manage(lv_obj_t * kb, bool en) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + if(ext->cursor_mng == en) return; + + ext->cursor_mng = en == false ? 0 : 1; + + if(ext->ta) { + lv_cursor_type_t cur_type; + cur_type = lv_ta_get_cursor_type(ext->ta); + + if(ext->cursor_mng) { + lv_ta_set_cursor_type(ext->ta, cur_type & (~LV_CURSOR_HIDDEN)); + } else { + lv_ta_set_cursor_type(ext->ta, cur_type | LV_CURSOR_HIDDEN); + } + } +} + +/** + * Set call back to call when the "Ok" button is pressed + * @param kb pointer to Keyboard object + * @param action a callback with 'lv_action_t' type + */ +void lv_kb_set_ok_action(lv_obj_t * kb, lv_action_t action) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + ext->ok_action = action; +} + +/** + * Set call back to call when the "Hide" button is pressed + * @param kb pointer to Keyboard object + * @param action a callback with 'lv_action_t' type + */ +void lv_kb_set_hide_action(lv_obj_t * kb, lv_action_t action) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + ext->hide_action = action; +} + +/** + * Set a style of a keyboard + * @param kb pointer to a keyboard object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_kb_set_style(lv_obj_t * kb, lv_kb_style_t type, lv_style_t * style) +{ + switch(type) { + case LV_KB_STYLE_BG: + lv_btnm_set_style(kb, LV_BTNM_STYLE_BG, style); + break; + case LV_KB_STYLE_BTN_REL: + lv_btnm_set_style(kb, LV_BTNM_STYLE_BTN_REL, style); + break; + case LV_KB_STYLE_BTN_PR: + lv_btnm_set_style(kb, LV_BTNM_STYLE_BTN_PR, style); + break; + case LV_KB_STYLE_BTN_TGL_REL: + lv_btnm_set_style(kb, LV_BTNM_STYLE_BTN_TGL_REL, style); + break; + case LV_KB_STYLE_BTN_TGL_PR: + lv_btnm_set_style(kb, LV_BTNM_STYLE_BTN_TGL_PR, style); + break; + case LV_KB_STYLE_BTN_INA: + lv_btnm_set_style(kb, LV_BTNM_STYLE_BTN_INA, style); + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Assign a Text Area to the Keyboard. The pressed characters will be put there. + * @param kb pointer to a Keyboard object + * @return pointer to the assigned Text Area object + */ +lv_obj_t * lv_kb_get_ta(const lv_obj_t * kb) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + return ext->ta; +} + +/** + * Set a new a mode (text or number map) + * @param kb pointer to a Keyboard object + * @return the current mode from 'lv_kb_mode_t' + */ +lv_kb_mode_t lv_kb_get_mode(const lv_obj_t * kb) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + return ext->mode; +} + + +/** + * Get the current cursor manage mode. + * @param kb pointer to a Keyboard object + * @return true: show cursor on the current text area, false: hide cursor + */ +bool lv_kb_get_cursor_manage(const lv_obj_t * kb) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + return ext->cursor_mng == 0 ? false : true; +} + +/** + * Get the callback to call when the "Ok" button is pressed + * @param kb pointer to Keyboard object + * @return the ok callback + */ +lv_action_t lv_kb_get_ok_action(const lv_obj_t * kb) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + return ext->ok_action; +} + +/** + * Get the callback to call when the "Hide" button is pressed + * @param kb pointer to Keyboard object + * @return the close callback + */ +lv_action_t lv_kb_get_hide_action(const lv_obj_t * kb) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + return ext->hide_action; +} + +/** + * Get a style of a keyboard + * @param kb pointer to a keyboard object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_kb_get_style(const lv_obj_t * kb, lv_kb_style_t type) +{ + lv_style_t * style = NULL; + + switch(type) { + case LV_KB_STYLE_BG: + style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BG); + break; + case LV_KB_STYLE_BTN_REL: + style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BTN_REL); + break; + case LV_KB_STYLE_BTN_PR: + style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BTN_PR); + break; + case LV_KB_STYLE_BTN_TGL_REL: + style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BTN_TGL_REL); + break; + case LV_KB_STYLE_BTN_TGL_PR: + style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BTN_TGL_PR); + break; + case LV_KB_STYLE_BTN_INA: + style = lv_btnm_get_style(kb, LV_BTNM_STYLE_BTN_INA); + break; + default: + style = NULL; + break; + } + + return style; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Signal function of the keyboard + * @param kb pointer to a keyboard object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_kb_signal(lv_obj_t * kb, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(kb, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_kb"; + } + + return res; +} + +/** + * Called when a button of 'kb_btnm' is released + * @param btnm pointer to 'kb_btnm' + * @param i the index of the released button from the current btnm map + * @return LV_ACTION_RES_INV if the btnm is deleted else LV_ACTION_RES_OK + */ +static lv_res_t lv_kb_def_action(lv_obj_t * kb, const char * txt) +{ + lv_kb_ext_t * ext = lv_obj_get_ext_attr(kb); + lv_res_t res = LV_RES_OK; + + /*Do the corresponding action according to the text of the button*/ + if(strcmp(txt, "abc") == 0) { + lv_btnm_set_map(kb, kb_map_lc); + return LV_RES_OK; + } else if(strcmp(txt, "ABC") == 0) { + lv_btnm_set_map(kb, kb_map_uc); + return LV_RES_OK; + } else if(strcmp(txt, "1#") == 0) { + lv_btnm_set_map(kb, kb_map_spec); + return LV_RES_OK; + } else if(strcmp(txt, SYMBOL_CLOSE) == 0) { + if(ext->hide_action) res = ext->hide_action(kb); + else { + lv_kb_set_ta(kb, NULL); /*De-assign the text area to hide it cursor if needed*/ + lv_obj_del(kb); + } + return res; + } else if(strcmp(txt, SYMBOL_OK) == 0) { + if(ext->ok_action) res = ext->ok_action(kb); + else { + lv_kb_set_ta(kb, NULL); /*De-assign the text area to hide it cursor if needed*/ + res = lv_obj_del(kb); + } + return res; + } + + if(res != LV_RES_OK) return res; /*The keyboard might be deleted in the actions*/ + + /*Add the characters to the text area if set*/ + if(ext->ta == NULL) return res; + + if(strcmp(txt, "Enter") == 0)lv_ta_add_char(ext->ta, '\n'); + else if(strcmp(txt, SYMBOL_LEFT) == 0) lv_ta_cursor_left(ext->ta); + else if(strcmp(txt, SYMBOL_RIGHT) == 0) lv_ta_cursor_right(ext->ta); + else if(strcmp(txt, "Bksp") == 0) lv_ta_del_char(ext->ta); + else if(strcmp(txt, "+/-") == 0) { + uint16_t cur = lv_ta_get_cursor_pos(ext->ta); + const char * ta_txt = lv_ta_get_text(ext->ta); + if(ta_txt[0] == '-') { + lv_ta_set_cursor_pos(ext->ta, 1); + lv_ta_del_char(ext->ta); + lv_ta_add_char(ext->ta, '+'); + lv_ta_set_cursor_pos(ext->ta, cur); + } else if(ta_txt[0] == '+') { + lv_ta_set_cursor_pos(ext->ta, 1); + lv_ta_del_char(ext->ta); + lv_ta_add_char(ext->ta, '-'); + lv_ta_set_cursor_pos(ext->ta, cur); + } else { + lv_ta_set_cursor_pos(ext->ta, 0); + lv_ta_add_char(ext->ta, '-'); + lv_ta_set_cursor_pos(ext->ta, cur + 1); + } + } else { + lv_ta_add_text(ext->ta, txt); + } + return LV_RES_OK; +} + + + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_kb.h b/bdk/libs/lvgl/lv_objx/lv_kb.h new file mode 100644 index 00000000..71a6f028 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_kb.h @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2019-2020 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_kb.h + * + */ + +#ifndef LV_KB_H +#define LV_KB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_KB != 0 + +/*Testing of dependencies*/ +#if USE_LV_BTNM == 0 +#error "lv_kb: lv_btnm is required. Enable it in lv_conf.h (USE_LV_BTNM 1) " +#endif + +#if USE_LV_TA == 0 +#error "lv_kb: lv_ta is required. Enable it in lv_conf.h (USE_LV_TA 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_btnm.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +enum { + LV_KB_MODE_TEXT, + LV_KB_MODE_NUM, + LV_KB_MODE_HEX +}; +typedef uint8_t lv_kb_mode_t; + +/*Data of keyboard*/ +typedef struct { + lv_btnm_ext_t btnm; /*Ext. of ancestor*/ + /*New data for this type */ + lv_obj_t *ta; /*Pointer to the assigned text area*/ + lv_kb_mode_t mode; /*Key map type*/ + uint8_t cursor_mng :1; /*1: automatically show/hide cursor when a text area is assigned or left*/ + lv_action_t ok_action; /*Called when the "Ok" button is clicked*/ + lv_action_t hide_action; /*Called when the "Hide" button is clicked*/ +} lv_kb_ext_t; + +enum { + LV_KB_STYLE_BG, + LV_KB_STYLE_BTN_REL, + LV_KB_STYLE_BTN_PR, + LV_KB_STYLE_BTN_TGL_REL, + LV_KB_STYLE_BTN_TGL_PR, + LV_KB_STYLE_BTN_INA, +}; +typedef uint8_t lv_kb_style_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a keyboard objects + * @param par pointer to an object, it will be the parent of the new keyboard + * @param copy pointer to a keyboard object, if not NULL then the new object will be copied from it + * @return pointer to the created keyboard + */ +lv_obj_t * lv_kb_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Assign a Text Area to the Keyboard. The pressed characters will be put there. + * @param kb pointer to a Keyboard object + * @param ta pointer to a Text Area object to write there + */ +void lv_kb_set_ta(lv_obj_t * kb, lv_obj_t * ta); + +/** + * Set a new a mode (text or number map) + * @param kb pointer to a Keyboard object + * @param mode the mode from 'lv_kb_mode_t' + */ +void lv_kb_set_mode(lv_obj_t * kb, lv_kb_mode_t mode); + +/** + * Automatically hide or show the cursor of the current Text Area + * @param kb pointer to a Keyboard object + * @param en true: show cursor on the current text area, false: hide cursor + */ +void lv_kb_set_cursor_manage(lv_obj_t * kb, bool en); + +/** + * Set call back to call when the "Ok" button is pressed + * @param kb pointer to Keyboard object + * @param action a callback with 'lv_action_t' type + */ +void lv_kb_set_ok_action(lv_obj_t * kb, lv_action_t action); + +/** + * Set call back to call when the "Hide" button is pressed + * @param kb pointer to Keyboard object + * @param action a callback with 'lv_action_t' type + */ +void lv_kb_set_hide_action(lv_obj_t * kb, lv_action_t action); + +/** + * Set a new map for the keyboard + * @param kb pointer to a Keyboard object + * @param map pointer to a string array to describe the map. + * See 'lv_btnm_set_map()' for more info. + */ +static inline void lv_kb_set_map(lv_obj_t *kb, const char ** map) +{ + lv_btnm_set_map(kb, map); +} + +/** + * Set a style of a keyboard + * @param kb pointer to a keyboard object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_kb_set_style(lv_obj_t *kb, lv_kb_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Assign a Text Area to the Keyboard. The pressed characters will be put there. + * @param kb pointer to a Keyboard object + * @return pointer to the assigned Text Area object + */ +lv_obj_t * lv_kb_get_ta(const lv_obj_t * kb); + +/** + * Set a new a mode (text or number map) + * @param kb pointer to a Keyboard object + * @return the current mode from 'lv_kb_mode_t' + */ +lv_kb_mode_t lv_kb_get_mode(const lv_obj_t * kb); + +/** + * Get the current cursor manage mode. + * @param kb pointer to a Keyboard object + * @return true: show cursor on the current text area, false: hide cursor + */ +bool lv_kb_get_cursor_manage(const lv_obj_t * kb); + +/** + * Get the callback to call when the "Ok" button is pressed + * @param kb pointer to Keyboard object + * @return the ok callback + */ +lv_action_t lv_kb_get_ok_action(const lv_obj_t * kb); + +/** + * Get the callback to call when the "Hide" button is pressed + * @param kb pointer to Keyboard object + * @return the close callback + */ +lv_action_t lv_kb_get_hide_action(const lv_obj_t * kb); + +/** + * Get a style of a keyboard + * @param kb pointer to a keyboard object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_kb_get_style(const lv_obj_t *kb, lv_kb_style_t type); + + + +/** + * Called when a button of 'kb_btnm' is released + * @param btnm pointer to 'kb_btnm' + * @param i the index of the released button from the current btnm map + * @return LV_ACTION_RES_INV if the btnm is deleted else LV_ACTION_RES_OK + */ +//static lv_res_t lv_kb_def_action(lv_obj_t* kb, const char* txt);//eingefügt TA springen + +//static lv_res_t lv_kb_signal(lv_obj_t* kb, lv_signal_t sign, void* param); +//static lv_res_t lv_kb_def_action(lv_obj_t* kb, const char* txt); + + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_KB*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_KB_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_label.c b/bdk/libs/lvgl/lv_objx/lv_label.c new file mode 100644 index 00000000..57ac2d0f --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_label.c @@ -0,0 +1,987 @@ +/** + * @file lv_rect.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_label.h" +#if USE_LV_LABEL != 0 + +#include "../lv_core/lv_obj.h" +#include "../lv_core/lv_group.h" +#include "../lv_core/lv_lang.h" +#include "../lv_draw/lv_draw.h" +#include "../lv_misc/lv_color.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ +/*Test configurations*/ +#ifndef LV_LABEL_SCROLL_SPEED +#define LV_LABEL_SCROLL_SPEED (25) /*Hor, or ver. scroll speed (px/sec) in 'LV_LABEL_LONG_SCROLL/ROLL' mode*/ +#endif + +#define ANIM_WAIT_CHAR_COUNT 3 + +#define LV_LABEL_DOT_END_INV 0xFFFF + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_label_signal(lv_obj_t * label, lv_signal_t sign, void * param); +static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_mode_t mode); +static void lv_label_refr_text(lv_obj_t * label); +static void lv_label_revert_dots(lv_obj_t * label); + +#if USE_LV_ANIMATION +static void lv_label_set_offset_x(lv_obj_t * label, lv_coord_t x); +static void lv_label_set_offset_y(lv_obj_t * label, lv_coord_t y); +#endif +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a label objects + * @param par pointer to an object, it will be the parent of the new label + * @param copy pointer to a button object, if not NULL then the new object will be copied from it + * @return pointer to the created button + */ +lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("label create started"); + + /*Create a basic object*/ + lv_obj_t * new_label = lv_obj_create(par, copy); + lv_mem_assert(new_label); + if(new_label == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_label); + + /*Extend the basic object to a label object*/ + lv_obj_allocate_ext_attr(new_label, sizeof(lv_label_ext_t)); + + lv_label_ext_t * ext = lv_obj_get_ext_attr(new_label); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->text = NULL; + ext->static_txt = 0; + ext->recolor = 0; + ext->body_draw = 0; + ext->align = LV_LABEL_ALIGN_LEFT; + ext->dot_end = LV_LABEL_DOT_END_INV; + ext->long_mode = LV_LABEL_LONG_EXPAND; + ext->anim_speed = LV_LABEL_SCROLL_SPEED; + ext->offset.x = 0; + ext->offset.y = 0; +#if USE_LV_MULTI_LANG + ext->lang_txt_id = LV_LANG_TXT_ID_NONE; +#endif + lv_obj_set_design_func(new_label, lv_label_design); + lv_obj_set_signal_func(new_label, lv_label_signal); + + /*Init the new label*/ + if(copy == NULL) { + lv_obj_set_click(new_label, false); + lv_label_set_long_mode(new_label, LV_LABEL_LONG_EXPAND); + lv_label_set_text(new_label, "Text"); + lv_label_set_style(new_label, NULL); /*Inherit parent's style*/ + } + /*Copy 'copy' if not NULL*/ + else { + lv_label_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + lv_label_set_long_mode(new_label, lv_label_get_long_mode(copy)); + lv_label_set_recolor(new_label, lv_label_get_recolor(copy)); + lv_label_set_body_draw(new_label, lv_label_get_body_draw(copy)); + lv_label_set_align(new_label, lv_label_get_align(copy)); + if(copy_ext->static_txt == 0) lv_label_set_text(new_label, lv_label_get_text(copy)); + else lv_label_set_static_text(new_label, lv_label_get_text(copy)); + + /*In DOT mode save the text byte-to-byte because a '\0' can be in the middle*/ + if(copy_ext->long_mode == LV_LABEL_LONG_DOT) { + ext->text = lv_mem_realloc(ext->text, lv_mem_get_size(copy_ext->text)); + lv_mem_assert(ext->text); + if(ext->text == NULL) return NULL; + memcpy(ext->text, copy_ext->text, lv_mem_get_size(copy_ext->text)); + } + + memcpy(ext->dot_tmp, copy_ext->dot_tmp, sizeof(ext->dot_tmp)); + ext->dot_end = copy_ext->dot_end; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_label); + } + + + LV_LOG_INFO("label created"); + + return new_label; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new text for a label. Memory will be allocated to store the text by the label. + * @param label pointer to a label object + * @param text '\0' terminated character string. NULL to refresh with the current text. + */ +void lv_label_set_text(lv_obj_t * label, const char * text) +{ + lv_obj_invalidate(label); + + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + + /*If text is NULL then refresh */ + if(text == NULL) { + lv_label_refr_text(label); + return; + } + + if(ext->text == text && ext->static_txt == 0) { + /*If set its own text then reallocate it (maybe its size changed)*/ + ext->text = lv_mem_realloc(ext->text, strlen(ext->text) + 1); + lv_mem_assert(ext->text); + if(ext->text == NULL) return; + } else { + /*Allocate space for the new text*/ + uint32_t len = strlen(text) + 1; + if(ext->text != NULL && ext->static_txt == 0) { + lv_mem_free(ext->text); + ext->text = NULL; + } + + ext->text = lv_mem_alloc(len); + lv_mem_assert(ext->text); + if(ext->text == NULL) return; + + strcpy(ext->text, text); + ext->static_txt = 0; /*Now the text is dynamically allocated*/ + } + + lv_label_refr_text(label); +} + +/** + * Set a new text for a label from a character array. The array don't has to be '\0' terminated. + * Memory will be allocated to store the array by the label. + * @param label pointer to a label object + * @param array array of characters or NULL to refresh the label + * @param size the size of 'array' in bytes + */ +void lv_label_set_array_text(lv_obj_t * label, const char * array, uint16_t size) +{ + lv_obj_invalidate(label); + + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + + /*If trying to set its own text or the array is NULL then refresh */ + if(array == ext->text || array == NULL) { + lv_label_refr_text(label); + return; + } + + /*Allocate space for the new text*/ + if(ext->text != NULL && ext->static_txt == 0) { + lv_mem_free(ext->text); + ext->text = NULL; + } + ext->text = lv_mem_alloc(size + 1); + lv_mem_assert(ext->text); + if(ext->text == NULL) return; + + memcpy(ext->text, array, size); + ext->text[size] = '\0'; + ext->static_txt = 0; /*Now the text is dynamically allocated*/ + + lv_label_refr_text(label); +} + +/** + * Set a static text. It will not be saved by the label so the 'text' variable + * has to be 'alive' while the label exist. + * @param label pointer to a label object + * @param text pointer to a text. NULL to refresh with the current text. + */ +void lv_label_set_static_text(lv_obj_t * label, const char * text) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + if(ext->static_txt == 0 && ext->text != NULL) { + lv_mem_free(ext->text); + ext->text = NULL; + } + + if(text != NULL) { + ext->static_txt = 1; + ext->text = (char *) text; + } + + lv_label_refr_text(label); +} + +#if USE_LV_MULTI_LANG +/** + *Set a text ID which refers a the same text but in a different languages + * @param label pointer to a label object + * @param txt_id ID of the text + */ +void lv_label_set_text_id(lv_obj_t * label, uint32_t txt_id) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + ext->lang_txt_id = txt_id; + + /*Apply the new language*/ + label->signal_func(label, LV_SIGNAL_LANG_CHG, NULL); +} +#endif + +/** + * Set the behavior of the label with longer text then the object size + * @param label pointer to a label object + * @param long_mode the new mode from 'lv_label_long_mode' enum. + * In LV_LONG_BREAK/LONG/ROLL the size of the label should be set AFTER this function + */ +void lv_label_set_long_mode(lv_obj_t * label, lv_label_long_mode_t long_mode) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + +#if USE_LV_ANIMATION + /*Delete the old animation (if exists)*/ + lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_x); + lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_y); + lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_x); + lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_y); +#endif + ext->offset.x = 0; + ext->offset.y = 0; + + if(long_mode == LV_LABEL_LONG_ROLL || long_mode == LV_LABEL_LONG_CROP) ext->expand = 1; + else ext->expand = 0; + + /*Restore the character under the dots*/ + if(ext->long_mode == LV_LABEL_LONG_DOT && ext->dot_end != LV_LABEL_DOT_END_INV) { + lv_label_revert_dots(label); + } + + ext->long_mode = long_mode; + lv_label_refr_text(label); +} + +/** + * Set the align of the label (left or center) + * @param label pointer to a label object + * @param align 'LV_LABEL_ALIGN_LEFT' or 'LV_LABEL_ALIGN_LEFT' + */ +void lv_label_set_align(lv_obj_t * label, lv_label_align_t align) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + if(ext->align == align) return; + + ext->align = align; + + lv_obj_invalidate(label); /*Enough to invalidate because alignment is only drawing related (lv_refr_label_text() not required)*/ +} + +/** + * Enable the recoloring by in-line commands + * @param label pointer to a label object + * @param en true: enable recoloring, false: disable + */ +void lv_label_set_recolor(lv_obj_t * label, bool en) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + if(ext->recolor == en) return; + + ext->recolor = en == false ? 0 : 1; + + lv_label_refr_text(label); /*Refresh the text because the potential colo codes in text needs to be hided or revealed*/ +} + +/** + * Set the label to draw (or not draw) background specified in its style's body + * @param label pointer to a label object + * @param en true: draw body; false: don't draw body + */ +void lv_label_set_body_draw(lv_obj_t * label, bool en) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + if(ext->body_draw == en) return; + + ext->body_draw = en == false ? 0 : 1; + + lv_obj_refresh_ext_size(label); + + lv_obj_invalidate(label); +} + +/** + * Set the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes + * @param label pointer to a label object + * @param anim_speed speed of animation in px/sec unit + */ +void lv_label_set_anim_speed(lv_obj_t * label, uint16_t anim_speed) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + if(ext->anim_speed == anim_speed) return; + + ext->anim_speed = anim_speed; + + if(ext->long_mode == LV_LABEL_LONG_ROLL || ext->long_mode == LV_LABEL_LONG_SCROLL) { + lv_label_refr_text(label); + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the text of a label + * @param label pointer to a label object + * @return the text of the label + */ +char * lv_label_get_text(const lv_obj_t * label) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + + return ext->text; +} + +#if USE_LV_MULTI_LANG +/** + * Get the text ID of the label. (Used by the multi-language feature) + * @param label pointer to a label object + * @return ID of the text + */ +uint16_t lv_label_get_text_id(lv_obj_t * label) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + return ext->lang_txt_id; +} +#endif + +/** + * Get the long mode of a label + * @param label pointer to a label object + * @return the long mode + */ +lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * label) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + return ext->long_mode; +} + +/** + * Get the align attribute + * @param label pointer to a label object + * @return LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER + */ +lv_label_align_t lv_label_get_align(const lv_obj_t * label) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + return ext->align; +} + +/** + * Get the recoloring attribute + * @param label pointer to a label object + * @return true: recoloring is enabled, false: disable + */ +bool lv_label_get_recolor(const lv_obj_t * label) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + return ext->recolor == 0 ? false : true; +} + +/** + * Get the body draw attribute + * @param label pointer to a label object + * @return true: draw body; false: don't draw body + */ +bool lv_label_get_body_draw(const lv_obj_t * label) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + return ext->body_draw == 0 ? false : true; +} + +/** + * Get the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes + * @param label pointer to a label object + * @return speed of animation in px/sec unit + */ +uint16_t lv_label_get_anim_speed(const lv_obj_t * label) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + return ext->anim_speed; +} + +/** + * Get the relative x and y coordinates of a letter + * @param label pointer to a label object + * @param index index of the letter [0 ... text length]. Expressed in character index, not byte index (different in UTF-8) + * @param pos store the result here (E.g. index = 0 gives 0;0 coordinates) + */ +void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t * pos) +{ + const char * txt = lv_label_get_text(label); + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + uint32_t line_start = 0; + uint32_t new_line_start = 0; + lv_coord_t max_w = lv_obj_get_width(label); + lv_style_t * style = lv_obj_get_style(label); + const lv_font_t * font = style->text.font; + uint8_t letter_height = lv_font_get_height(font); + lv_coord_t y = 0; + lv_txt_flag_t flag = LV_TXT_FLAG_NONE; + + if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR; + if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND; + if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER; + + /*If the width will be expanded the set the max length to very big */ + if(ext->long_mode == LV_LABEL_LONG_EXPAND || ext->long_mode == LV_LABEL_LONG_SCROLL) { + max_w = LV_COORD_MAX; + } + + index = lv_txt_encoded_get_byte_id(txt, index); + + /*Search the line of the index letter */; + while(txt[new_line_start] != '\0') { + new_line_start += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, max_w, flag); + if(index < new_line_start || txt[new_line_start] == '\0') break; /*The line of 'index' letter begins at 'line_start'*/ + + y += letter_height + style->text.line_space; + line_start = new_line_start; + } + + /*If the last character is line break then go to the next line*/ + if(index > 0) { + if((txt[index - 1] == '\n' || txt[index - 1] == '\r') && txt[index] == '\0') { + y += letter_height + style->text.line_space; + line_start = index; + } + } + + /*Calculate the x coordinate*/ + lv_coord_t x = lv_txt_get_width(&txt[line_start], index - line_start, + font, style->text.letter_space, flag); + + if(index != line_start) x += style->text.letter_space; + + if(ext->align == LV_LABEL_ALIGN_CENTER) { + lv_coord_t line_w; + line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start, + font, style->text.letter_space, flag); + x += lv_obj_get_width(label) / 2 - line_w / 2; + + } else if(ext->align == LV_LABEL_ALIGN_RIGHT) { + lv_coord_t line_w; + line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start, + font, style->text.letter_space, flag); + + x += lv_obj_get_width(label) - line_w; + } + pos->x = x; + pos->y = y; +} + +/** + * Get the index of letter on a relative point of a label + * @param label pointer to label object + * @param pos pointer to point with coordinates on a the label + * @return the index of the letter on the 'pos_p' point (E.g. on 0;0 is the 0. letter) + * Expressed in character index and not byte index (different in UTF-8) + */ +uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos) +{ + const char * txt = lv_label_get_text(label); + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + uint32_t line_start = 0; + uint32_t new_line_start = 0; + lv_coord_t max_w = lv_obj_get_width(label); + lv_style_t * style = lv_obj_get_style(label); + const lv_font_t * font = style->text.font; + uint8_t letter_height = lv_font_get_height(font); + lv_coord_t y = 0; + lv_txt_flag_t flag = LV_TXT_FLAG_NONE; + + if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR; + if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND; + if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER; + + /*If the width will be expanded set the max length to very big */ + if(ext->long_mode == LV_LABEL_LONG_EXPAND || ext->long_mode == LV_LABEL_LONG_SCROLL) { + max_w = LV_COORD_MAX; + } + + /*Search the line of the index letter */; + while(txt[line_start] != '\0') { + new_line_start += lv_txt_get_next_line(&txt[line_start], font, style->text.letter_space, max_w, flag); + + if(pos->y <= y + letter_height) break; /*The line is found (stored in 'line_start')*/ + y += letter_height + style->text.line_space; + + line_start = new_line_start; + } + + /*Calculate the x coordinate*/ + lv_coord_t x = 0; + if(ext->align == LV_LABEL_ALIGN_CENTER) { + lv_coord_t line_w; + line_w = lv_txt_get_width(&txt[line_start], new_line_start - line_start, + font, style->text.letter_space, flag); + x += lv_obj_get_width(label) / 2 - line_w / 2; + } + + lv_txt_cmd_state_t cmd_state = LV_TXT_CMD_STATE_WAIT; + uint32_t i = line_start; + uint32_t i_current = i; + uint32_t letter; + while(i < new_line_start - 1) { + letter = lv_txt_encoded_next(txt, &i); /*Be careful 'i' already points to the next character*/ + /*Handle the recolor command*/ + if((flag & LV_TXT_FLAG_RECOLOR) != 0) { + if(lv_txt_is_cmd(&cmd_state, txt[i]) != false) { + continue; /*Skip the letter is it is part of a command*/ + } + } + + x += lv_font_get_width(font, letter); + if(pos->x < x) { + i = i_current; + break; + } + x += style->text.letter_space; + i_current = i; + } + + return lv_encoded_get_char_id(txt, i); +} + + +/*===================== + * Other functions + *====================*/ + +/** + * Insert a text to the label. The label text can not be static. + * @param label pointer to a label object + * @param pos character index to insert. Expressed in character index and not byte index (Different in UTF-8) + * 0: before first char. + * LV_LABEL_POS_LAST: after last char. + * @param txt pointer to the text to insert + */ +void lv_label_ins_text(lv_obj_t * label, uint32_t pos, const char * txt) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + + /*Can not append to static text*/ + if(ext->static_txt != 0) return; + + lv_obj_invalidate(label); + + /*Allocate space for the new text*/ + uint32_t old_len = strlen(ext->text); + uint32_t ins_len = strlen(txt); + uint32_t new_len = ins_len + old_len; + ext->text = lv_mem_realloc(ext->text, new_len + 1); + lv_mem_assert(ext->text); + if(ext->text == NULL) return; + + if(pos == LV_LABEL_POS_LAST) { +#if LV_TXT_UTF8 == 0 + pos = old_len; +#else + pos = lv_txt_get_encoded_length(ext->text); +#endif + } + + lv_txt_ins(ext->text, pos, txt); + + lv_label_refr_text(label); +} + +/** + * Delete characters from a label. The label text can not be static. + * @param label pointer to a label object + * @param pos character index to insert. Expressed in character index and not byte index (Different in UTF-8) + * 0: before first char. + * @param cnt number of characters to cut + */ +void lv_label_cut_text(lv_obj_t * label, uint32_t pos, uint32_t cnt) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + + /*Can not append to static text*/ + if(ext->static_txt != 0) return; + + lv_obj_invalidate(label); + + char * label_txt = lv_label_get_text(label); + /*Delete the characters*/ + lv_txt_cut(label_txt, pos, cnt); + + /*Refresh the label*/ + lv_label_refr_text(label); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the labels + * @param label pointer to a label object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_label_design(lv_obj_t * label, const lv_area_t * mask, lv_design_mode_t mode) +{ + /* A label never covers an area */ + if(mode == LV_DESIGN_COVER_CHK) return false; + else if(mode == LV_DESIGN_DRAW_MAIN) { + lv_area_t coords; + lv_style_t * style = lv_obj_get_style(label); + lv_opa_t opa_scale = lv_obj_get_opa_scale(label); + lv_obj_get_coords(label, &coords); + +#if USE_LV_GROUP + lv_group_t * g = lv_obj_get_group(label); + if(lv_group_get_focused(g) == label) { + lv_draw_rect(&coords, mask, style, opa_scale); + } +#endif + + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + + if(ext->body_draw) { + lv_area_t bg; + lv_obj_get_coords(label, &bg); + bg.x1 -= style->body.padding.hor; + bg.x2 += style->body.padding.hor; + bg.y1 -= style->body.padding.ver; + bg.y2 += style->body.padding.ver; + + lv_draw_rect(&bg, mask, style, lv_obj_get_opa_scale(label)); + } + + /*TEST: draw a background for the label*/ + //lv_draw_rect(&label->coords, mask, &lv_style_plain_color, LV_OPA_COVER); + + lv_txt_flag_t flag = LV_TXT_FLAG_NONE; + if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR; + if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND; + if(ext->align == LV_LABEL_ALIGN_CENTER) flag |= LV_TXT_FLAG_CENTER; + if(ext->align == LV_LABEL_ALIGN_RIGHT) flag |= LV_TXT_FLAG_RIGHT; + + /* In ROLL mode the CENTER and RIGHT are pointless so remove them. + * (In addition they will result mis-alignment is this case)*/ + if((ext->long_mode == LV_LABEL_LONG_ROLL) && + (ext->align == LV_LABEL_ALIGN_CENTER || ext->align == LV_LABEL_ALIGN_RIGHT)) { + lv_point_t size; + lv_txt_get_size(&size, ext->text, style->text.font, style->text.letter_space, style->text.line_space, LV_COORD_MAX, flag); + if(size.x > lv_obj_get_width(label)) { + flag &= ~LV_TXT_FLAG_RIGHT; + flag &= ~LV_TXT_FLAG_CENTER; + } + } + + lv_draw_label(&coords, mask, style, opa_scale, ext->text, flag, &ext->offset); + } + return true; +} + + + +/** + * Signal function of the label + * @param label pointer to a label object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_label_signal(lv_obj_t * label, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(label, sign, param); + if(res != LV_RES_OK) return res; + + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + if(sign == LV_SIGNAL_CLEANUP) { + if(ext->static_txt == 0) { + lv_mem_free(ext->text); + ext->text = NULL; + } + } else if(sign == LV_SIGNAL_STYLE_CHG) { + /*Revert dots for proper refresh*/ + lv_label_revert_dots(label); + + lv_label_refr_text(label); + } else if(sign == LV_SIGNAL_CORD_CHG) { + if(lv_area_get_width(&label->coords) != lv_area_get_width(param) || + lv_area_get_height(&label->coords) != lv_area_get_height(param)) { + lv_label_revert_dots(label); + lv_label_refr_text(label); + } + } else if(sign == LV_SIGNAL_REFR_EXT_SIZE) { + if(ext->body_draw) { + lv_style_t * style = lv_label_get_style(label); + label->ext_size = LV_MATH_MAX(label->ext_size, style->body.padding.hor); + label->ext_size = LV_MATH_MAX(label->ext_size, style->body.padding.ver); + } + } else if(sign == LV_SIGNAL_LANG_CHG) { +#if USE_LV_MULTI_LANG + if(ext->lang_txt_id != LV_LANG_TXT_ID_NONE) { + const char * lang_txt = lv_lang_get_text(ext->lang_txt_id); + if(lang_txt) { + lv_label_set_text(label, lang_txt); + } else { + LV_LOG_WARN("lv_lang_get_text return NULL for a label's text"); + } + } +#endif + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_label"; + } + + return res; +} + +/** + * Refresh the label with its text stored in its extended data + * @param label pointer to a label object + */ +static void lv_label_refr_text(lv_obj_t * label) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + + if(ext->text == NULL) return; + + lv_coord_t max_w = lv_obj_get_width(label); + lv_style_t * style = lv_obj_get_style(label); + const lv_font_t * font = style->text.font; + + /*If the width will be expanded set the max length to very big */ + if(ext->long_mode == LV_LABEL_LONG_EXPAND || + ext->long_mode == LV_LABEL_LONG_SCROLL) { + max_w = LV_COORD_MAX; + } + + /*Calc. the height and longest line*/ + lv_point_t size; + lv_txt_flag_t flag = LV_TXT_FLAG_NONE; + if(ext->recolor != 0) flag |= LV_TXT_FLAG_RECOLOR; + if(ext->expand != 0) flag |= LV_TXT_FLAG_EXPAND; + lv_txt_get_size(&size, ext->text, font, style->text.letter_space, style->text.line_space, max_w, flag); + + /*Set the full size in expand mode*/ + if(ext->long_mode == LV_LABEL_LONG_EXPAND || ext->long_mode == LV_LABEL_LONG_SCROLL) { + lv_obj_set_size(label, size.x, size.y); + + /*Start scrolling if the label is greater then its parent*/ + if(ext->long_mode == LV_LABEL_LONG_SCROLL) { +#if USE_LV_ANIMATION + lv_obj_t * parent = lv_obj_get_parent(label); + + /*Delete the potential previous scroller animations*/ + lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_x); + lv_anim_del(label, (lv_anim_fp_t) lv_obj_set_y); + + lv_anim_t anim; + anim.var = label; + anim.repeat = 1; + anim.playback = 1; + anim.start = 0; + anim.act_time = 0; + anim.end_cb = NULL; + anim.path = lv_anim_path_linear; + + anim.playback_pause = (((lv_font_get_width(style->text.font, ' ') + + style->text.letter_space) * 1000) / ext->anim_speed) * ANIM_WAIT_CHAR_COUNT; + anim.repeat_pause = anim.playback_pause; + + if(lv_obj_get_width(label) > lv_obj_get_width(parent)) { + anim.end = lv_obj_get_width(parent) - lv_obj_get_width(label); + anim.fp = (lv_anim_fp_t) lv_obj_set_x; + anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end); + lv_anim_create(&anim); + } else if(lv_obj_get_height(label) > lv_obj_get_height(parent)) { + anim.end = lv_obj_get_height(parent) - lv_obj_get_height(label) - lv_font_get_height(font); + anim.fp = (lv_anim_fp_t)lv_obj_set_y; + anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end); + lv_anim_create(&anim); + } +#endif + } + } + /*In roll mode keep the size but start offset animations*/ + else if(ext->long_mode == LV_LABEL_LONG_ROLL) { +#if USE_LV_ANIMATION + lv_anim_t anim; + anim.var = label; + anim.repeat = 1; + anim.playback = 1; + anim.start = 0; + anim.act_time = 0; + anim.end_cb = NULL; + anim.path = lv_anim_path_linear; + anim.playback_pause = (((lv_font_get_width(style->text.font, ' ') + style->text.letter_space) * 1000) / ext->anim_speed) * ANIM_WAIT_CHAR_COUNT; + anim.repeat_pause = anim.playback_pause; + + bool hor_anim = false; + if(size.x > lv_obj_get_width(label)) { + anim.end = lv_obj_get_width(label) - size.x; + anim.fp = (lv_anim_fp_t) lv_label_set_offset_x; + anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end); + lv_anim_create(&anim); + hor_anim = true; + } else { + /*Delete the offset animation if not required*/ + lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_x); + ext->offset.x = 0; + } + + if(size.y > lv_obj_get_height(label) && hor_anim == false) { + anim.end = lv_obj_get_height(label) - size.y - (lv_font_get_height(font)); + anim.fp = (lv_anim_fp_t)lv_label_set_offset_y; + anim.time = lv_anim_speed_to_time(ext->anim_speed, anim.start, anim.end); + lv_anim_create(&anim); + } else { + /*Delete the offset animation if not required*/ + lv_anim_del(label, (lv_anim_fp_t) lv_label_set_offset_y); + ext->offset.y = 0; + } +#endif + } else if(ext->long_mode == LV_LABEL_LONG_DOT) { + if(size.y <= lv_obj_get_height(label)) { /*No dots are required, the text is short enough*/ + ext->dot_end = LV_LABEL_DOT_END_INV; + } else if(lv_txt_get_encoded_length(ext->text) <= LV_LABEL_DOT_NUM) { /*Don't turn to dots all the characters*/ + ext->dot_end = LV_LABEL_DOT_END_INV; + } else { + lv_point_t p; + p.x = lv_obj_get_width(label) - (lv_font_get_width(style->text.font, '.') + style->text.letter_space) * LV_LABEL_DOT_NUM; /*Shrink with dots*/ + p.y = lv_obj_get_height(label); + p.y -= p.y % (lv_font_get_height(style->text.font) + style->text.line_space); /*Round down to the last line*/ + p.y -= style->text.line_space; /*Trim the last line space*/ + uint32_t letter_id = lv_label_get_letter_on(label, &p); + + +#if LV_TXT_UTF8 == 0 + /*Save letters under the dots and replace them with dots*/ + uint8_t i; + for(i = 0; i < LV_LABEL_DOT_NUM; i++) { + ext->dot_tmp[i] = ext->text[letter_id + i]; + ext->text[letter_id + i] = '.'; + } + + ext->dot_tmp[LV_LABEL_DOT_NUM] = ext->text[letter_id + LV_LABEL_DOT_NUM]; + ext->text[letter_id + LV_LABEL_DOT_NUM] = '\0'; + + ext->dot_end = letter_id + LV_LABEL_DOT_NUM; +#else + /*Save letters under the dots and replace them with dots*/ + uint32_t i; + uint32_t byte_id = lv_txt_encoded_get_byte_id(ext->text, letter_id); + uint32_t byte_id_ori = byte_id; + uint8_t len = 0; + for(i = 0; i <= LV_LABEL_DOT_NUM; i++) { + len += lv_txt_encoded_size(&ext->text[byte_id]); + lv_txt_encoded_next(ext->text, &byte_id); + } + + memcpy(ext->dot_tmp, &ext->text[byte_id_ori], len); + ext->dot_tmp[len] = '\0'; /*Close with a zero*/ + + for(i = 0; i < LV_LABEL_DOT_NUM; i++) { + ext->text[byte_id_ori + i] = '.'; + } + ext->text[byte_id_ori + LV_LABEL_DOT_NUM] = '\0'; + + ext->dot_end = letter_id + LV_LABEL_DOT_NUM; +#endif + + } + } + /*In break mode only the height can change*/ + else if(ext->long_mode == LV_LABEL_LONG_BREAK) { + lv_obj_set_height(label, size.y); + } + /*Do not set the size in Clip mode*/ + else if(ext->long_mode == LV_LABEL_LONG_CROP) { + /*Do nothing*/ + } + + + lv_obj_invalidate(label); +} + +static void lv_label_revert_dots(lv_obj_t * label) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + if(ext->long_mode != LV_LABEL_LONG_DOT) return; + if(ext->dot_end == LV_LABEL_DOT_END_INV) return; +#if LV_TXT_UTF8 == 0 + uint32_t i; + for(i = 0; i <= LV_LABEL_DOT_NUM; i++) { + ext->text[ext->dot_end - i] = ext->dot_tmp[LV_LABEL_DOT_NUM - i]; + } +#else + uint32_t letter_i = ext->dot_end - LV_LABEL_DOT_NUM; + uint32_t byte_i = lv_txt_encoded_get_byte_id(ext->text, letter_i); + + /*Restore the characters*/ + uint8_t i = 0; + while(ext->dot_tmp[i] != '\0') { + ext->text[byte_i + i] = ext->dot_tmp[i]; + i++; + } +#endif + + ext->dot_end = LV_LABEL_DOT_END_INV; +} + +#if USE_LV_ANIMATION +static void lv_label_set_offset_x(lv_obj_t * label, lv_coord_t x) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + ext->offset.x = x; + lv_obj_invalidate(label); +} + +static void lv_label_set_offset_y(lv_obj_t * label, lv_coord_t y) +{ + lv_label_ext_t * ext = lv_obj_get_ext_attr(label); + ext->offset.y = y; + lv_obj_invalidate(label); +} +#endif +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_label.h b/bdk/libs/lvgl/lv_objx/lv_label.h new file mode 100644 index 00000000..35f07e86 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_label.h @@ -0,0 +1,313 @@ +/** + * @file lv_rect.h + * + */ + +#ifndef LV_LABEL_H +#define LV_LABEL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_LABEL != 0 + +#include "../lv_core/lv_obj.h" +#include "../lv_misc/lv_font.h" +#include "../lv_misc/lv_txt.h" +#include "../lv_misc/lv_symbol_def.h" + +/********************* + * DEFINES + *********************/ +#define LV_LABEL_DOT_NUM 3 +#define LV_LABEL_POS_LAST 0xFFFF + +/********************** + * TYPEDEFS + **********************/ + +/*Long mode behaviors. Used in 'lv_label_ext_t' */ +enum +{ + LV_LABEL_LONG_EXPAND, /*Expand the object size to the text size*/ + LV_LABEL_LONG_BREAK, /*Keep the object width, break the too long lines and expand the object height*/ + LV_LABEL_LONG_SCROLL, /*Expand the object size and scroll the text on the parent (move the label object)*/ + LV_LABEL_LONG_DOT, /*Keep the size and write dots at the end if the text is too long*/ + LV_LABEL_LONG_ROLL, /*Keep the size and roll the text infinitely*/ + LV_LABEL_LONG_CROP, /*Keep the size and crop the text out of it*/ +}; +typedef uint8_t lv_label_long_mode_t; + +/*Label align policy*/ +enum { + LV_LABEL_ALIGN_LEFT, + LV_LABEL_ALIGN_CENTER, + LV_LABEL_ALIGN_RIGHT, +}; +typedef uint8_t lv_label_align_t; + +/*Data of label*/ +typedef struct +{ + /*Inherited from 'base_obj' so no inherited ext.*/ /*Ext. of ancestor*/ + /*New data for this type */ + char * text; /*Text of the label*/ + lv_label_long_mode_t long_mode; /*Determinate what to do with the long texts*/ +#if LV_TXT_UTF8 == 0 + char dot_tmp[LV_LABEL_DOT_NUM + 1]; /*Store the character which are replaced by dots (Handled by the library)*/ +#else + char dot_tmp[LV_LABEL_DOT_NUM * 4 + 1]; /*Store the character which are replaced by dots (Handled by the library)*/ +#endif + +#if USE_LV_MULTI_LANG + uint16_t lang_txt_id; /*The ID of the text to display*/ +#endif + uint16_t dot_end; /*The text end position in dot mode (Handled by the library)*/ + uint16_t anim_speed; /*Speed of scroll and roll animation in px/sec unit*/ + lv_point_t offset; /*Text draw position offset*/ + uint8_t static_txt :1; /*Flag to indicate the text is static*/ + uint8_t align :2; /*Align type from 'lv_label_align_t'*/ + uint8_t recolor :1; /*Enable in-line letter re-coloring*/ + uint8_t expand :1; /*Ignore real width (used by the library with LV_LABEL_LONG_ROLL)*/ + uint8_t body_draw :1; /*Draw background body*/ +} lv_label_ext_t; + + + + + + +// Label styles hinzugefügt mein Style +enum { + LV_LABEL_STYLE_MAIN, +}; +typedef uint8_t lv_label_style_t; + + + + + + + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + + +/** + * Create a label objects + * @param par pointer to an object, it will be the parent of the new label + * @param copy pointer to a button object, if not NULL then the new object will be copied from it + * @return pointer to the created button + */ +lv_obj_t * lv_label_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new text for a label. Memory will be allocated to store the text by the label. + * @param label pointer to a label object + * @param text '\0' terminated character string. NULL to refresh with the current text. + */ +void lv_label_set_text(lv_obj_t * label, const char * text); + +/** + * Set a new text for a label from a character array. The array don't has to be '\0' terminated. + * Memory will be allocated to store the array by the label. + * @param label pointer to a label object + * @param array array of characters or NULL to refresh the label + * @param size the size of 'array' in bytes + */ +void lv_label_set_array_text(lv_obj_t * label, const char * array, uint16_t size); + +/** + * Set a static text. It will not be saved by the label so the 'text' variable + * has to be 'alive' while the label exist. + * @param label pointer to a label object + * @param text pointer to a text. NULL to refresh with the current text. + */ +void lv_label_set_static_text(lv_obj_t * label, const char * text); + +/** + *Set a text ID which means a the same text but on different languages + * @param label pointer to a label object + * @param txt_id ID of the text + */ +#if USE_LV_MULTI_LANG +void lv_label_set_text_id(lv_obj_t * label, uint32_t txt_id); +#endif + +/** + * Set the behavior of the label with longer text then the object size + * @param label pointer to a label object + * @param long_mode the new mode from 'lv_label_long_mode' enum. + * In LV_LONG_BREAK/LONG/ROLL the size of the label should be set AFTER this function + */ +void lv_label_set_long_mode(lv_obj_t * label, lv_label_long_mode_t long_mode); + +/** + * Set the align of the label (left or center) + * @param label pointer to a label object + * @param align 'LV_LABEL_ALIGN_LEFT' or 'LV_LABEL_ALIGN_LEFT' + */ +void lv_label_set_align(lv_obj_t *label, lv_label_align_t align); + +/** + * Enable the recoloring by in-line commands + * @param label pointer to a label object + * @param en true: enable recoloring, false: disable + */ +void lv_label_set_recolor(lv_obj_t * label, bool en); + +/** + * Set the label to draw (or not draw) background specified in its style's body + * @param label pointer to a label object + * @param en true: draw body; false: don't draw body + */ +void lv_label_set_body_draw(lv_obj_t *label, bool en); + +/** + * Set the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes + * @param label pointer to a label object + * @param anim_speed speed of animation in px/sec unit + */ +void lv_label_set_anim_speed(lv_obj_t *label, uint16_t anim_speed); + +/** + * Set the style of an label + * @param label pointer to an label object + * @param style pointer to a style + */ +static inline void lv_label_set_style(lv_obj_t *label, lv_style_t *style) +{ + lv_obj_set_style(label, style); +} +/*===================== + * Getter functions + *====================*/ + +/** + * Get the text of a label + * @param label pointer to a label object + * @return the text of the label + */ +char * lv_label_get_text(const lv_obj_t * label); + +#if USE_LV_MULTI_LANG +/** + * Get the text ID of the label. (Used by the multi-language feature) + * @param label pointer to a label object + * @return ID of the text + */ +uint16_t lv_label_get_text_id(lv_obj_t * label); +#endif + +/** + * Get the long mode of a label + * @param label pointer to a label object + * @return the long mode + */ +lv_label_long_mode_t lv_label_get_long_mode(const lv_obj_t * label); + +/** + * Get the align attribute + * @param label pointer to a label object + * @return LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER + */ +lv_label_align_t lv_label_get_align(const lv_obj_t * label); + +/** + * Get the recoloring attribute + * @param label pointer to a label object + * @return true: recoloring is enabled, false: disable + */ +bool lv_label_get_recolor(const lv_obj_t * label); + +/** + * Get the body draw attribute + * @param label pointer to a label object + * @return true: draw body; false: don't draw body + */ +bool lv_label_get_body_draw(const lv_obj_t *label); + +/** + * Get the label's animation speed in LV_LABEL_LONG_ROLL and SCROLL modes + * @param label pointer to a label object + * @return speed of animation in px/sec unit + */ +uint16_t lv_label_get_anim_speed(const lv_obj_t *label); + +/** + * Get the relative x and y coordinates of a letter + * @param label pointer to a label object + * @param index index of the letter [0 ... text length]. Expressed in character index, not byte index (different in UTF-8) + * @param pos store the result here (E.g. index = 0 gives 0;0 coordinates) + */ +void lv_label_get_letter_pos(const lv_obj_t * label, uint16_t index, lv_point_t * pos); + +/** + * Get the index of letter on a relative point of a label + * @param label pointer to label object + * @param pos pointer to point with coordinates on a the label + * @return the index of the letter on the 'pos_p' point (E.g. on 0;0 is the 0. letter) + * Expressed in character index and not byte index (different in UTF-8) + */ +uint16_t lv_label_get_letter_on(const lv_obj_t * label, lv_point_t * pos); + +/** + * Get the style of an label object + * @param label pointer to an label object + * @return pointer to the label's style + */ +static inline lv_style_t* lv_label_get_style(const lv_obj_t *label) +{ + return lv_obj_get_style(label); +} + +/*===================== + * Other functions + *====================*/ + +/** + * Insert a text to the label. The label text can not be static. + * @param label pointer to a label object + * @param pos character index to insert. Expressed in character index and not byte index (Different in UTF-8) + * 0: before first char. + * LV_LABEL_POS_LAST: after last char. + * @param txt pointer to the text to insert + */ +void lv_label_ins_text(lv_obj_t * label, uint32_t pos, const char * txt); + +/** + * Delete characters from a label. The label text can not be static. + * @param label pointer to a label object + * @param pos character index to insert. Expressed in character index and not byte index (Different in UTF-8) + * 0: before first char. + * @param cnt number of characters to cut + */ +void lv_label_cut_text(lv_obj_t * label, uint32_t pos, uint32_t cnt); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_LABEL*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_LABEL_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_led.c b/bdk/libs/lvgl/lv_objx/lv_led.c new file mode 100644 index 00000000..29fc967e --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_led.c @@ -0,0 +1,244 @@ +/** + * @file lv_led.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_led.h" +#if USE_LV_LED != 0 + +#include "../lv_themes/lv_theme.h" +#include "../lv_draw/lv_draw.h" + +/********************* + * DEFINES + *********************/ +#define LV_LED_WIDTH_DEF (LV_DPI / 3) +#define LV_LED_HEIGHT_DEF (LV_DPI / 3) +#define LV_LED_BRIGHT_OFF 100 +#define LV_LED_BRIGHT_ON 255 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_led_design(lv_obj_t * led, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_led_signal(lv_obj_t * led, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_design_func_t ancestor_design_f; +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a led objects + * @param par pointer to an object, it will be the parent of the new led + * @param copy pointer to a led object, if not NULL then the new object will be copied from it + * @return pointer to the created led + */ +lv_obj_t * lv_led_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("led create started"); + + /*Create the ancestor basic object*/ + lv_obj_t * new_led = lv_obj_create(par, copy); + lv_mem_assert(new_led); + if(new_led == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_led); + if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_led); + + /*Allocate the object type specific extended data*/ + lv_led_ext_t * ext = lv_obj_allocate_ext_attr(new_led, sizeof(lv_led_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->bright = LV_LED_BRIGHT_ON; + + lv_obj_set_signal_func(new_led, lv_led_signal); + lv_obj_set_design_func(new_led, lv_led_design); + + /*Init the new led object*/ + if(copy == NULL) { + lv_obj_set_size(new_led, LV_LED_WIDTH_DEF, LV_LED_HEIGHT_DEF); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_led_set_style(new_led, th->led); + } else { + lv_led_set_style(new_led, &lv_style_pretty_color); + } + } + /*Copy an existing object*/ + else { + lv_led_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->bright = copy_ext->bright; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_led); + } + + + LV_LOG_INFO("led created"); + + return new_led; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the brightness of a LED object + * @param led pointer to a LED object + * @param bright 0 (max. dark) ... 255 (max. light) + */ +void lv_led_set_bright(lv_obj_t * led, uint8_t bright) +{ + /*Set the brightness*/ + lv_led_ext_t * ext = lv_obj_get_ext_attr(led); + if(ext->bright == bright) return; + + ext->bright = bright; + + /*Invalidate the object there fore it will be redrawn*/ + lv_obj_invalidate(led); +} + +/** + * Light on a LED + * @param led pointer to a LED object + */ +void lv_led_on(lv_obj_t * led) +{ + lv_led_set_bright(led, LV_LED_BRIGHT_ON); +} + +/** + * Light off a LED + * @param led pointer to a LED object + */ +void lv_led_off(lv_obj_t * led) +{ + lv_led_set_bright(led, LV_LED_BRIGHT_OFF); +} + + +/** + * Toggle the state of a LED + * @param led pointer to a LED object + */ +void lv_led_toggle(lv_obj_t * led) +{ + uint8_t bright = lv_led_get_bright(led); + if(bright > (LV_LED_BRIGHT_OFF + LV_LED_BRIGHT_ON) >> 1) lv_led_off(led); + else lv_led_on(led); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the brightness of a LEd object + * @param led pointer to LED object + * @return bright 0 (max. dark) ... 255 (max. light) + */ +uint8_t lv_led_get_bright(const lv_obj_t * led) +{ + lv_led_ext_t * ext = lv_obj_get_ext_attr(led); + return ext->bright; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the leds + * @param led pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_led_design(lv_obj_t * led, const lv_area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + /*Return false if the object is not covers the mask area*/ + return ancestor_design_f(led, mask, mode); + } else if(mode == LV_DESIGN_DRAW_MAIN) { + /*Make darker colors in a temporary style according to the brightness*/ + lv_led_ext_t * ext = lv_obj_get_ext_attr(led); + lv_style_t * style = lv_obj_get_style(led); + + /* Store the real pointer because of 'lv_group' + * If the object is in focus 'lv_obj_get_style()' will give a pointer to tmp style + * and to the real object style. It is important because of style change tricks below*/ + lv_style_t * style_ori_p = led->style_p; + + /*Create a temporal style*/ + lv_style_t leds_tmp; + memcpy(&leds_tmp, style, sizeof(leds_tmp)); + + /*Mix. the color with black proportionally with brightness*/ + leds_tmp.body.main_color = lv_color_mix(leds_tmp.body.main_color, LV_COLOR_BLACK, ext->bright); + leds_tmp.body.grad_color = lv_color_mix(leds_tmp.body.grad_color, LV_COLOR_BLACK, ext->bright); + leds_tmp.body.border.color = lv_color_mix(leds_tmp.body.border.color, LV_COLOR_BLACK, ext->bright); + + /*Set the current swidth according to brightness proportionally between LV_LED_BRIGHT_OFF and LV_LED_BRIGHT_ON*/ + uint16_t bright_tmp = ext->bright; + leds_tmp.body.shadow.width = ((bright_tmp - LV_LED_BRIGHT_OFF) * style->body.shadow.width) / (LV_LED_BRIGHT_ON - LV_LED_BRIGHT_OFF); + + led->style_p = &leds_tmp; + ancestor_design_f(led, mask, mode); + led->style_p = style_ori_p; /*Restore the ORIGINAL style pointer*/ + } + return true; +} + +/** + * Signal function of the led + * @param led pointer to a led object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_led_signal(lv_obj_t * led, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(led, sign, param); + if(res != LV_RES_OK) return res; + + + if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_led"; + } + + return res; +} +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_led.h b/bdk/libs/lvgl/lv_objx/lv_led.h new file mode 100644 index 00000000..25e48a79 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_led.h @@ -0,0 +1,116 @@ +/** + * @file lv_led.h + * + */ + +#ifndef LV_LED_H +#define LV_LED_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_LED != 0 + +#include "../lv_core/lv_obj.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Data of led*/ +typedef struct +{ + /*No inherited ext.*/ + /*New data for this type */ + uint8_t bright; /*Current brightness of the LED (0..255)*/ +} lv_led_ext_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a led objects + * @param par pointer to an object, it will be the parent of the new led + * @param copy pointer to a led object, if not NULL then the new object will be copied from it + * @return pointer to the created led + */ +lv_obj_t * lv_led_create(lv_obj_t * par, const lv_obj_t * copy); + +/** + * Set the brightness of a LED object + * @param led pointer to a LED object + * @param bright 0 (max. dark) ... 255 (max. light) + */ +void lv_led_set_bright(lv_obj_t * led, uint8_t bright); + +/** + * Light on a LED + * @param led pointer to a LED object + */ +void lv_led_on(lv_obj_t * led); + +/** + * Light off a LED + * @param led pointer to a LED object + */ +void lv_led_off(lv_obj_t * led); + +/** + * Toggle the state of a LED + * @param led pointer to a LED object + */ +void lv_led_toggle(lv_obj_t * led); + +/** + * Set the style of a led + * @param led pointer to a led object + * @param style pointer to a style + */ +static inline void lv_led_set_style(lv_obj_t *led, lv_style_t *style) +{ + lv_obj_set_style(led, style); +} + +/** + * Get the brightness of a LEd object + * @param led pointer to LED object + * @return bright 0 (max. dark) ... 255 (max. light) + */ +uint8_t lv_led_get_bright(const lv_obj_t * led); + +/** + * Get the style of an led object + * @param led pointer to an led object + * @return pointer to the led's style + */ +static inline lv_style_t* lv_led_get_style(const lv_obj_t *led) +{ + return lv_obj_get_style(led); +} + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_LED*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_LED_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_line.c b/bdk/libs/lvgl/lv_objx/lv_line.c new file mode 100644 index 00000000..0e163371 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_line.c @@ -0,0 +1,301 @@ +/** + * @file lv_line.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_line.h" + +#if USE_LV_LINE != 0 +#include "../lv_draw/lv_draw.h" +#include "../lv_misc/lv_math.h" +#include +#include + + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_line_design(lv_obj_t * line, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_line_signal(lv_obj_t * line, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a line objects + * @param par pointer to an object, it will be the parent of the new line + * @return pointer to the created line + */ +lv_obj_t * lv_line_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("line create started"); + + /*Create a basic object*/ + lv_obj_t * new_line = lv_obj_create(par, copy); + lv_mem_assert(new_line); + if(new_line == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_line); + + /*Extend the basic object to line object*/ + lv_line_ext_t * ext = lv_obj_allocate_ext_attr(new_line, sizeof(lv_line_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->point_num = 0; + ext->point_array = NULL; + ext->auto_size = 1; + ext->y_inv = 0; + + lv_obj_set_design_func(new_line, lv_line_design); + lv_obj_set_signal_func(new_line, lv_line_signal); + + /*Init the new line*/ + if(copy == NULL) { + lv_obj_set_size(new_line, LV_DPI, LV_DPI); /*Auto size is enables, but set default size until no points are added*/ + lv_obj_set_style(new_line, NULL); /*Inherit parent's style*/ + lv_obj_set_click(new_line, false); + } + /*Copy an existing object*/ + else { + lv_line_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + lv_line_set_auto_size(new_line, lv_line_get_auto_size(copy)); + lv_line_set_y_invert(new_line, lv_line_get_y_invert(copy)); + lv_line_set_auto_size(new_line, lv_line_get_auto_size(copy)); + lv_line_set_points(new_line, copy_ext->point_array, copy_ext->point_num); + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_line); + } + + + LV_LOG_INFO("line created"); + + return new_line; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set an array of points. The line object will connect these points. + * @param line pointer to a line object + * @param point_a an array of points. Only the address is saved, + * so the array can NOT be a local variable which will be destroyed + * @param point_num number of points in 'point_a' + */ +void lv_line_set_points(lv_obj_t * line, const lv_point_t * point_a, uint16_t point_num) +{ + lv_line_ext_t * ext = lv_obj_get_ext_attr(line); + ext->point_array = point_a; + ext->point_num = point_num; + + if(point_num > 0 && ext->auto_size != 0) { + uint16_t i; + lv_coord_t xmax = LV_COORD_MIN; + lv_coord_t ymax = LV_COORD_MIN; + for(i = 0; i < point_num; i++) { + xmax = LV_MATH_MAX(point_a[i].x, xmax); + ymax = LV_MATH_MAX(point_a[i].y, ymax); + } + + lv_style_t * style = lv_line_get_style(line); + lv_obj_set_size(line, xmax + style->line.width, ymax + style->line.width); + } + + lv_obj_invalidate(line); +} + +/** + * Enable (or disable) the auto-size option. The size of the object will fit to its points. + * (set width to x max and height to y max) + * @param line pointer to a line object + * @param en true: auto size is enabled, false: auto size is disabled + */ +void lv_line_set_auto_size(lv_obj_t * line, bool en) +{ + lv_line_ext_t * ext = lv_obj_get_ext_attr(line); + if(ext->auto_size == en) return; + + ext->auto_size = en == false ? 0 : 1; + + /*Refresh the object*/ + if(en) lv_line_set_points(line, ext->point_array, ext->point_num); +} + +/** + * Enable (or disable) the y coordinate inversion. + * If enabled then y will be subtracted from the height of the object, + * therefore the y=0 coordinate will be on the bottom. + * @param line pointer to a line object + * @param en true: enable the y inversion, false:disable the y inversion + */ +void lv_line_set_y_invert(lv_obj_t * line, bool en) +{ + lv_line_ext_t * ext = lv_obj_get_ext_attr(line); + if(ext->y_inv == en) return; + + ext->y_inv = en == false ? 0 : 1; + + lv_obj_invalidate(line); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the auto size attribute + * @param line pointer to a line object + * @return true: auto size is enabled, false: disabled + */ +bool lv_line_get_auto_size(const lv_obj_t * line) +{ + lv_line_ext_t * ext = lv_obj_get_ext_attr(line); + + return ext->auto_size == 0 ? false : true; +} + +/** + * Get the y inversion attribute + * @param line pointer to a line object + * @return true: y inversion is enabled, false: disabled + */ +bool lv_line_get_y_invert(const lv_obj_t * line) +{ + lv_line_ext_t * ext = lv_obj_get_ext_attr(line); + + return ext->y_inv == 0 ? false : true; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the lines + * @param line pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_line_design(lv_obj_t * line, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*A line never covers an area*/ + if(mode == LV_DESIGN_COVER_CHK) return false; + else if(mode == LV_DESIGN_DRAW_MAIN) { + lv_line_ext_t * ext = lv_obj_get_ext_attr(line); + + if(ext->point_num == 0 || ext->point_array == NULL) return false; + + lv_style_t * style = lv_obj_get_style(line); + lv_opa_t opa_scale = lv_obj_get_opa_scale(line); + lv_area_t area; + lv_obj_get_coords(line, &area); + lv_coord_t x_ofs = area.x1; + lv_coord_t y_ofs = area.y1; + lv_point_t p1; + lv_point_t p2; + lv_coord_t h = lv_obj_get_height(line); + uint16_t i; + + lv_style_t circle_style; /*If rounded...*/ + lv_style_copy(&circle_style, style); + circle_style.body.radius = LV_RADIUS_CIRCLE; + circle_style.body.main_color = style->line.color; + circle_style.body.grad_color = style->line.color; + circle_style.body.opa = style->line.opa; + lv_area_t circle_area; + + /*Read all points and draw the lines*/ + for(i = 0; i < ext->point_num - 1; i++) { + + p1.x = ext->point_array[i].x + x_ofs; + p2.x = ext->point_array[i + 1].x + x_ofs; + + if(ext->y_inv == 0) { + p1.y = ext->point_array[i].y + y_ofs; + p2.y = ext->point_array[i + 1].y + y_ofs; + } else { + p1.y = h - ext->point_array[i].y + y_ofs; + p2.y = h - ext->point_array[i + 1].y + y_ofs; + } + lv_draw_line(&p1, &p2, mask, style, opa_scale); + + /*Draw circle on the joints if enabled*/ + if(style->line.rounded) { + circle_area.x1 = p1.x - ((style->line.width - 1) >> 1) - ((style->line.width - 1) & 0x1); + circle_area.y1 = p1.y - ((style->line.width - 1) >> 1) - ((style->line.width - 1) & 0x1); + circle_area.x2 = p1.x + ((style->line.width - 1) >> 1); + circle_area.y2 = p1.y + ((style->line.width - 1) >> 1); + lv_draw_rect(&circle_area, mask, &circle_style, opa_scale); + } + } + + /*Draw circle on the last point too if enabled*/ + if(style->line.rounded) { + circle_area.x1 = p2.x - ((style->line.width - 1) >> 1) - ((style->line.width - 1) & 0x1); + circle_area.y1 = p2.y - ((style->line.width - 1) >> 1) - ((style->line.width - 1) & 0x1); + circle_area.x2 = p2.x + ((style->line.width - 1) >> 1); + circle_area.y2 = p2.y + ((style->line.width - 1) >> 1); + lv_draw_rect(&circle_area, mask, &circle_style, opa_scale); + } + } + return true; +} + +/** + * Signal function of the line + * @param line pointer to a line object + * @param sign a signal type from lv_signal_t enum + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_line_signal(lv_obj_t * line, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(line, sign, param); + if(res != LV_RES_OK) return res; + + + if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_line"; + } else if(sign == LV_SIGNAL_REFR_EXT_SIZE) { + lv_style_t * style = lv_line_get_style(line); + if(line->ext_size < style->line.width) line->ext_size = style->line.width; + } + + + return res; +} +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_line.h b/bdk/libs/lvgl/lv_objx/lv_line.h new file mode 100644 index 00000000..377d3998 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_line.h @@ -0,0 +1,158 @@ +/** + * @file lv_line.h + * + */ + +#ifndef LV_LINE_H +#define LV_LINE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_LINE != 0 + +#include "../lv_core/lv_obj.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Data of line*/ +typedef struct +{ + /*Inherited from 'base_obj' so no inherited ext.*/ /*Ext. of ancestor*/ + const lv_point_t * point_array; /*Pointer to an array with the points of the line*/ + uint16_t point_num; /*Number of points in 'point_array' */ + uint8_t auto_size :1; /*1: set obj. width to x max and obj. height to y max */ + uint8_t y_inv :1; /*1: y == 0 will be on the bottom*/ +} lv_line_ext_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + + +/** + * Create a line objects + * @param par pointer to an object, it will be the parent of the new line + * @return pointer to the created line + */ +lv_obj_t * lv_line_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set an array of points. The line object will connect these points. + * @param line pointer to a line object + * @param point_a an array of points. Only the address is saved, + * so the array can NOT be a local variable which will be destroyed + * @param point_num number of points in 'point_a' + */ +void lv_line_set_points(lv_obj_t * line, const lv_point_t * point_a, uint16_t point_num); + +/** + * Enable (or disable) the auto-size option. The size of the object will fit to its points. + * (set width to x max and height to y max) + * @param line pointer to a line object + * @param en true: auto size is enabled, false: auto size is disabled + */ +void lv_line_set_auto_size(lv_obj_t * line, bool en); + +/** + * Enable (or disable) the y coordinate inversion. + * If enabled then y will be subtracted from the height of the object, + * therefore the y=0 coordinate will be on the bottom. + * @param line pointer to a line object + * @param en true: enable the y inversion, false:disable the y inversion + */ +void lv_line_set_y_invert(lv_obj_t * line, bool en); + +#define lv_line_set_y_inv lv_line_set_y_invert /*The name was inconsistent. In v.6.0 only `lv_line_set_y_invert`will work */ + +/** + * Set the style of a line + * @param line pointer to a line object + * @param style pointer to a style + */ +static inline void lv_line_set_style(lv_obj_t *line, lv_style_t *style) +{ + lv_obj_set_style(line, style); +} + +/** + * Obsolete since v5.1. Just for compatibility with v5.0. Will be removed in v6.0 + * @param line - + * @param upscale - + */ +static inline void lv_line_set_upscale(lv_obj_t * line, bool upcale) +{ + (void) line; + (void) upcale; +} +/*===================== + * Getter functions + *====================*/ + +/** + * Get the auto size attribute + * @param line pointer to a line object + * @return true: auto size is enabled, false: disabled + */ +bool lv_line_get_auto_size(const lv_obj_t * line); + +/** + * Get the y inversion attribute + * @param line pointer to a line object + * @return true: y inversion is enabled, false: disabled + */ +bool lv_line_get_y_invert(const lv_obj_t * line); + +/** + * Get the style of an line object + * @param line pointer to an line object + * @return pointer to the line's style + */ +static inline lv_style_t* lv_line_get_style(const lv_obj_t *line) +{ + return lv_obj_get_style(line); +} + +/** + * Obsolete since v5.1. Just for compatibility with v5.0. Will be removed in v6.0 + * @param line - + * @return false + */ +static inline bool lv_line_get_upscale(const lv_obj_t * line) +{ + (void) line; + return false; +} + + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_LINE*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_LINE_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_list.c b/bdk/libs/lvgl/lv_objx/lv_list.c new file mode 100644 index 00000000..cdb413ed --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_list.c @@ -0,0 +1,1056 @@ +/* + * Copyright (c) 2019 CTCaer + * Copyright (c) 2020 Storm + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_list.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_list.h" +#if USE_LV_LIST != 0 + +#include "../lv_core/lv_group.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_anim.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ +#define LV_LIST_LAYOUT_DEF LV_LAYOUT_COL_M + +#if USE_LV_ANIMATION +# ifndef LV_LIST_FOCUS_TIME +# define LV_LIST_FOCUS_TIME 100 /*Animation time of focusing to the a list element [ms] (0: no animation) */ +# endif +#else +# undef LV_LIST_FOCUS_TIME +# define LV_LIST_FOCUS_TIME 0 /*No animations*/ +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_list_signal(lv_obj_t * list, lv_signal_t sign, void * param); +static lv_res_t lv_list_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param); +static void refr_btn_width(lv_obj_t * list); +static void lv_list_btn_single_selected(lv_obj_t *btn); +static bool lv_list_is_list_btn(lv_obj_t * list_btn); +static bool lv_list_is_list_img(lv_obj_t * list_btn); +static bool lv_list_is_list_label(lv_obj_t * list_btn); + +/********************** + * STATIC VARIABLES + **********************/ +#if USE_LV_IMG +static lv_signal_func_t img_signal; +#endif +static lv_signal_func_t label_signal; +static lv_signal_func_t ancestor_page_signal; +static lv_signal_func_t ancestor_btn_signal; +#if USE_LV_GROUP +/*Used to make the last clicked button pressed (selected) when the list become focused and `click_focus == 1`*/ +static lv_obj_t * last_clicked_btn; +#endif + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a list objects + * @param par pointer to an object, it will be the parent of the new list + * @param copy pointer to a list object, if not NULL then the new object will be copied from it + * @return pointer to the created list + */ +lv_obj_t * lv_list_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("list create started"); + + /*Create the ancestor basic object*/ + lv_obj_t * new_list = lv_page_create(par, copy); + lv_mem_assert(new_list); + if(new_list == NULL) return NULL; + + if(ancestor_page_signal == NULL) ancestor_page_signal = lv_obj_get_signal_func(new_list); + + lv_list_ext_t * ext = lv_obj_allocate_ext_attr(new_list, sizeof(lv_list_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + // Important! + static lv_style_t img_btn_color; + lv_style_copy( &img_btn_color, &lv_style_plain); + img_btn_color.image.color = LV_COLOR_HEX(0xDDDDDD); + img_btn_color.image.intense = LV_OPA_50; + + img_btn_color.text.font = &interui_20;//Symbolgrösse in Liste img_btn !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ext->style_img = &img_btn_color; + ext->styles_btn[LV_BTN_STATE_REL] = &lv_style_btn_rel; + ext->styles_btn[LV_BTN_STATE_PR] = &lv_style_btn_pr; + ext->styles_btn[LV_BTN_STATE_TGL_REL] = &lv_style_btn_tgl_rel; + ext->styles_btn[LV_BTN_STATE_TGL_PR] = &lv_style_btn_tgl_pr; + ext->styles_btn[LV_BTN_STATE_INA] = &lv_style_btn_ina; + ext->anim_time = LV_LIST_FOCUS_TIME; + ext->single_mode = false; + ext->size = 0; + +#if USE_LV_GROUP + ext->last_sel = NULL; + ext->selected_btn = NULL; +#endif + + lv_obj_set_signal_func(new_list, lv_list_signal); + + /*Init the new list object*/ + if(copy == NULL) { + lv_obj_set_size(new_list, 2 * LV_DPI, 3 * LV_DPI); + lv_page_set_scrl_layout(new_list, LV_LIST_LAYOUT_DEF); + lv_list_set_sb_mode(new_list, LV_SB_MODE_DRAG); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_list_set_style(new_list, LV_LIST_STYLE_BG, th->list.bg); + lv_list_set_style(new_list, LV_LIST_STYLE_SCRL, th->list.scrl); + lv_list_set_style(new_list, LV_LIST_STYLE_SB, th->list.sb); + lv_list_set_style(new_list, LV_LIST_STYLE_BTN_REL, th->list.btn.rel); + lv_list_set_style(new_list, LV_LIST_STYLE_BTN_PR, th->list.btn.pr); + lv_list_set_style(new_list, LV_LIST_STYLE_BTN_TGL_REL, th->list.btn.tgl_rel); + lv_list_set_style(new_list, LV_LIST_STYLE_BTN_TGL_PR, th->list.btn.tgl_pr); + lv_list_set_style(new_list, LV_LIST_STYLE_BTN_INA, th->list.btn.ina); + } else { + lv_list_set_style(new_list, LV_LIST_STYLE_BG, &lv_style_transp_fit); + lv_list_set_style(new_list, LV_LIST_STYLE_SCRL, &lv_style_pretty); + } + } else { + lv_list_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + + lv_obj_t * copy_btn = lv_list_get_next_btn(copy, NULL); + while(copy_btn) { + const void * img_src = NULL; +#if USE_LV_IMG + lv_obj_t * copy_img = lv_list_get_btn_img(copy_btn); + if(copy_img) img_src = lv_img_get_src(copy_img); +#endif + lv_list_add(new_list, img_src, lv_list_get_btn_text(copy_btn), lv_btn_get_action(copy_btn, LV_BTN_ACTION_CLICK)); + copy_btn = lv_list_get_next_btn(copy, copy_btn); + } + + lv_list_set_style(new_list, LV_LIST_STYLE_BTN_REL, copy_ext->styles_btn[LV_BTN_STATE_REL]); + lv_list_set_style(new_list, LV_LIST_STYLE_BTN_PR, copy_ext->styles_btn[LV_BTN_STATE_PR]); + lv_list_set_style(new_list, LV_LIST_STYLE_BTN_TGL_REL, copy_ext->styles_btn[LV_BTN_STATE_TGL_REL]); + lv_list_set_style(new_list, LV_LIST_STYLE_BTN_TGL_PR, copy_ext->styles_btn[LV_BTN_STATE_TGL_REL]); + lv_list_set_style(new_list, LV_LIST_STYLE_BTN_INA, copy_ext->styles_btn[LV_BTN_STATE_INA]); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_list); + } + + + LV_LOG_INFO("list created"); + + + return new_list; +} + +/** + * Delete all children of the scrl object, without deleting scrl child. + * @param obj pointer to an object + */ +void lv_list_clean(lv_obj_t * obj) +{ + lv_obj_t * scrl = lv_page_get_scrl(obj); + lv_obj_clean(scrl); + lv_list_ext_t * ext = lv_obj_get_ext_attr(obj); + ext->size = 0; +} + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Add a list element to the list + * @param list pointer to list object + * @param img_fn file name of an image before the text (NULL if unused) + * @param txt text of the list element (NULL if unused) + * @param rel_action pointer to release action function (like with lv_btn) + * @return pointer to the new list element which can be customized (a button) + */ +lv_obj_t * lv_list_add(lv_obj_t * list, const void * img_src, const char * txt, lv_action_t rel_action) +{ + lv_style_t * style = lv_obj_get_style(list); + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + ext->size ++; + /*Create a list element with the image an the text*/ + lv_obj_t * liste; + liste = lv_btn_create(list, NULL); + + /*Save the original signal function because it will be required in `lv_list_btn_signal`*/ + if(ancestor_btn_signal == NULL) ancestor_btn_signal = lv_obj_get_signal_func(liste); + + /*Set the default styles*/ + lv_btn_set_style(liste, LV_BTN_STYLE_REL, ext->styles_btn[LV_BTN_STATE_REL]); + lv_btn_set_style(liste, LV_BTN_STYLE_PR, ext->styles_btn[LV_BTN_STATE_PR]); + lv_btn_set_style(liste, LV_BTN_STYLE_TGL_REL, ext->styles_btn[LV_BTN_STATE_TGL_REL]); + lv_btn_set_style(liste, LV_BTN_STYLE_TGL_PR, ext->styles_btn[LV_BTN_STATE_TGL_PR]); + lv_btn_set_style(liste, LV_BTN_STYLE_INA, ext->styles_btn[LV_BTN_STATE_INA]); + + lv_btn_set_action(liste, LV_BTN_ACTION_CLICK, rel_action); + lv_page_glue_obj(liste, true); + lv_btn_set_layout(liste, LV_LAYOUT_ROW_M); + lv_btn_set_fit(liste, false, true); + lv_obj_set_protect(liste, LV_PROTECT_PRESS_LOST); + lv_obj_set_signal_func(liste, lv_list_btn_signal); + + /*Make the size adjustment*/ + lv_coord_t w = lv_obj_get_width(list); + lv_style_t * style_scrl = lv_obj_get_style(lv_page_get_scrl(list)); + lv_coord_t pad_hor_tot = style->body.padding.hor + style_scrl->body.padding.hor; + w -= pad_hor_tot * 2; + + lv_obj_set_width(liste, w); +#if USE_LV_IMG != 0 + lv_obj_t * img = NULL; + if(img_src) { + img = lv_img_create(liste, NULL); + lv_img_set_src(img, img_src); + lv_obj_set_style(img, ext->style_img);//lv_obj_set_style(img, ext->style_img); + lv_obj_set_click(img, false); + if(img_signal == NULL) img_signal = lv_obj_get_signal_func(img); + } +#endif + if(txt != NULL) { + lv_coord_t btn_hor_pad = ext->styles_btn[LV_BTN_STYLE_REL]->body.padding.hor; + lv_obj_t * label = lv_label_create(liste, NULL); + lv_label_set_text(label, txt); + lv_obj_set_click(label, false); + lv_label_set_long_mode(label, LV_LABEL_LONG_ROLL); + lv_obj_set_width(label, liste->coords.x2 - label->coords.x1 - btn_hor_pad); + if(label_signal == NULL) label_signal = lv_obj_get_signal_func(label); + } +#if USE_LV_GROUP + /* If this is the first item to be added to the list and the list is + * focussed, select it */ + { + lv_group_t *g = lv_obj_get_group(list); + if(ext->size == 1 && lv_group_get_focused(g) == list) { + lv_list_set_btn_selected(list, liste); + } + } +#endif + + return liste; +} + +/** + * Remove the index of the button in the list + * @param list pointer to a list object + * @param index pointer to a the button's index in the list, index must be 0 <= index < lv_list_ext_t.size + * @return true: successfully deleted + */ +bool lv_list_remove(const lv_obj_t * list, uint32_t index) +{ + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + if(index >= ext->size) return false; + uint32_t count = 0; + lv_obj_t * e = lv_list_get_next_btn(list, NULL); + while(e != NULL) { + if(count == index) { + lv_obj_del(e); + ext->size --; + return true; + } + e = lv_list_get_next_btn(list, e); + count ++; + } + return false; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set single button selected mode, only one button will be selected if enabled. + * @param list pointer to the currently pressed list object + * @param mode, enable(true)/disable(false) single selected mode. + */ +void lv_list_set_single_mode(lv_obj_t *list, bool mode) +{ + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + + ext->single_mode = mode; +} + +#if USE_LV_GROUP + +/** + * Make a button selected + * @param list pointer to a list object + * @param btn pointer to a button to selectthe + */ +void lv_list_set_btn_selected(lv_obj_t * list, lv_obj_t * btn) +{ + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + + if(ext->selected_btn) { + lv_btn_state_t s = lv_btn_get_state(ext->selected_btn); + if(s == LV_BTN_STATE_PR) lv_btn_set_state(ext->selected_btn, LV_BTN_STATE_REL); + else if(s == LV_BTN_STATE_TGL_PR) lv_btn_set_state(ext->selected_btn, LV_BTN_STATE_TGL_REL); + } + + ext->selected_btn = btn; + if( btn != NULL ) { + ext->last_sel = btn; + } + + if(ext->selected_btn) { + lv_btn_state_t s = lv_btn_get_state(ext->selected_btn); + if(s == LV_BTN_STATE_REL) lv_btn_set_state(ext->selected_btn, LV_BTN_STATE_PR); + else if(s == LV_BTN_STATE_TGL_REL) lv_btn_set_state(ext->selected_btn, LV_BTN_STATE_TGL_PR); + + lv_page_focus(list, ext->selected_btn, ext->anim_time); + } +} + +#endif + +/** + * Set scroll animation duration on 'list_up()' 'list_down()' 'list_focus()' + * @param list pointer to a list object + * @param anim_time duration of animation [ms] + */ +void lv_list_set_anim_time(lv_obj_t * list, uint16_t anim_time) +{ + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); +#if USE_LV_ANIMATION == 0 + anim_time = 0; +#endif + + if(ext->anim_time == anim_time) return; + ext->anim_time = anim_time; +} + +/** + * Set a style of a list + * @param list pointer to a list object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_list_set_style(lv_obj_t * list, lv_list_style_t type, lv_style_t * style) +{ + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + lv_btn_style_t btn_style_refr = LV_BTN_STYLE_REL; + lv_obj_t * btn; + + switch(type) { + case LV_LIST_STYLE_BG: + lv_page_set_style(list, LV_PAGE_STYLE_BG, style); + /*style change signal will call 'refr_btn_width' */ + break; + case LV_LIST_STYLE_SCRL: + lv_page_set_style(list, LV_PAGE_STYLE_SCRL, style); + refr_btn_width(list); + break; + case LV_LIST_STYLE_SB: + lv_page_set_style(list, LV_PAGE_STYLE_SB, style); + break; + case LV_LIST_STYLE_EDGE_FLASH: + lv_page_set_style(list, LV_PAGE_STYLE_EDGE_FLASH, style); + break; + case LV_LIST_STYLE_BTN_REL: + ext->styles_btn[LV_BTN_STATE_REL] = style; + btn_style_refr = LV_BTN_STYLE_REL; + break; + case LV_LIST_STYLE_BTN_PR: + ext->styles_btn[LV_BTN_STATE_PR] = style; + btn_style_refr = LV_BTN_STYLE_PR; + break; + case LV_LIST_STYLE_BTN_TGL_REL: + ext->styles_btn[LV_BTN_STATE_TGL_REL] = style; + btn_style_refr = LV_BTN_STYLE_TGL_REL; + break; + case LV_LIST_STYLE_BTN_TGL_PR: + ext->styles_btn[LV_BTN_STATE_TGL_PR] = style; + btn_style_refr = LV_BTN_STYLE_TGL_PR; + break; + case LV_LIST_STYLE_BTN_INA: + ext->styles_btn[LV_BTN_STATE_INA] = style; + btn_style_refr = LV_BTN_STYLE_INA; + break; + } + + + /*Refresh existing buttons' style*/ + if(type == LV_LIST_STYLE_BTN_PR || type == LV_LIST_STYLE_BTN_REL || + type == LV_LIST_STYLE_BTN_TGL_REL || type == LV_LIST_STYLE_BTN_TGL_PR || + type == LV_LIST_STYLE_BTN_INA) { + btn = lv_list_get_prev_btn(list, NULL); + while(btn != NULL) { + lv_btn_set_style(btn, btn_style_refr, ext->styles_btn[btn_style_refr]); + btn = lv_list_get_prev_btn(list, btn); + } + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get single button selected mode. + * @param list pointer to the currently pressed list object. + */ +bool lv_list_get_single_mode(lv_obj_t *list) +{ + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + + return (ext->single_mode); +} + +/** + * Get the text of a list element + * @param btn pointer to list element + * @return pointer to the text + */ +const char * lv_list_get_btn_text(const lv_obj_t * btn) +{ + lv_obj_t * label = lv_list_get_btn_label(btn); + if(label == NULL) return ""; + return lv_label_get_text(label); +} + +/** + * Get the label object from a list element + * @param btn pointer to a list element (button) + * @return pointer to the label from the list element or NULL if not found + */ +lv_obj_t * lv_list_get_btn_label(const lv_obj_t * btn) +{ + lv_obj_t * label = lv_obj_get_child(btn, NULL); + if(label == NULL) return NULL; + + while(lv_list_is_list_label(label) == false) { + label = lv_obj_get_child(btn, label); + if(label == NULL) break; + } + + return label; +} + +/** + * Get the image object from a list element + * @param btn pointer to a list element (button) + * @return pointer to the image from the list element or NULL if not found + */ +lv_obj_t * lv_list_get_btn_img(const lv_obj_t * btn) +{ +#if USE_LV_IMG != 0 + lv_obj_t * img = lv_obj_get_child(btn, NULL); + if(img == NULL) return NULL; + + while(lv_list_is_list_img(img) == false) { + img = lv_obj_get_child(btn, img); + if(img == NULL) break; + } + + return img; +#else + return NULL; +#endif +} + +/** + * Get the previous button from list. (Starts from the top button) + * @param list pointer to a list object + * @param prev_btn pointer to button. Search the previous before it. + * @return pointer to the previous button or NULL when no more buttons + */ +lv_obj_t * lv_list_get_prev_btn(const lv_obj_t * list, lv_obj_t * prev_btn) +{ + /* Not a good practice but user can add/create objects to the lists manually. + * When getting the next button try to be sure that it is at least a button */ + + lv_obj_t * btn ; + lv_obj_t * scrl = lv_page_get_scrl(list); + + btn = lv_obj_get_child(scrl, prev_btn); + if(btn == NULL) return NULL; + + while(lv_list_is_list_btn(btn) == false) { + btn = lv_obj_get_child(scrl, btn); + if(btn == NULL) break; + } + + return btn; +} + + + + /** + * Get the next button from list. (Starts from the bottom button) + * @param list pointer to a list object + * @param prev_btn pointer to button. Search the next after it. + * @return pointer to the next button or NULL when no more buttons + */ +lv_obj_t * lv_list_get_next_btn(const lv_obj_t * list, lv_obj_t * prev_btn) +{ + /* Not a good practice but user can add/create objects to the lists manually. + * When getting the next button try to be sure that it is at least a button */ + + lv_obj_t * btn ; + lv_obj_t * scrl = lv_page_get_scrl(list); + + btn = lv_obj_get_child_back(scrl, prev_btn); + if(btn == NULL) return NULL; + + while(lv_list_is_list_btn(btn) == false) { + btn = lv_obj_get_child_back(scrl, btn); + if(btn == NULL) break; + } + + return btn; +} + +/** + * Get the index of the button in the list + * @param list pointer to a list object. If NULL, assumes btn is part of a list. + * @param btn pointer to a list element (button) + * @return the index of the button in the list, or -1 of the button not in this list + */ +int32_t lv_list_get_btn_index(const lv_obj_t * list, const lv_obj_t * btn) +{ + int index = 0; + if( list == NULL ){ + /* no list provided, assuming btn is part of a list */ + list = lv_obj_get_parent(lv_obj_get_parent(btn)); + } + lv_obj_t * e = lv_list_get_next_btn(list, NULL); + while(e != NULL) { + if(e == btn) { + return index; + } + index ++; + e = lv_list_get_next_btn(list, e); + } + return -1; +} + +/** + * Get the number of buttons in the list + * @param list pointer to a list object + * @return the number of buttons in the list + */ +uint32_t lv_list_get_size(const lv_obj_t * list) +{ + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + return ext->size; +} + +#if USE_LV_GROUP +/** + * Get the currently selected button + * @param list pointer to a list object + * @return pointer to the selected button + */ +lv_obj_t * lv_list_get_btn_selected(const lv_obj_t * list) +{ + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + return ext->selected_btn; +} + +#endif + +/** + * Get scroll animation duration + * @param list pointer to a list object + * @return duration of animation [ms] + */ +uint16_t lv_list_get_anim_time(const lv_obj_t * list) +{ + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + return ext->anim_time; +} + +/** + * Get a style of a list + * @param list pointer to a list object + * @param type which style should be get + * @return style pointer to a style + * */ +lv_style_t * lv_list_get_style(const lv_obj_t * list, lv_list_style_t type) +{ + lv_style_t * style = NULL; + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + + switch(type) { + case LV_LIST_STYLE_BG: + style = lv_page_get_style(list, LV_PAGE_STYLE_BG); + break; + case LV_LIST_STYLE_SCRL: + style = lv_page_get_style(list, LV_PAGE_STYLE_SB); + break; + case LV_LIST_STYLE_SB: + style = lv_page_get_style(list, LV_PAGE_STYLE_SCRL); + break; + case LV_LIST_STYLE_EDGE_FLASH: + style = lv_page_get_style(list, LV_PAGE_STYLE_EDGE_FLASH); + break; + case LV_LIST_STYLE_BTN_REL: + style = ext->styles_btn[LV_BTN_STATE_REL]; + break; + case LV_LIST_STYLE_BTN_PR: + style = ext->styles_btn[LV_BTN_STATE_PR]; + break; + case LV_LIST_STYLE_BTN_TGL_REL: + style = ext->styles_btn[LV_BTN_STATE_TGL_REL]; + break; + case LV_LIST_STYLE_BTN_TGL_PR: + style = ext->styles_btn[LV_BTN_STATE_TGL_PR]; + break; + case LV_LIST_STYLE_BTN_INA: + style = ext->styles_btn[LV_BTN_STATE_INA]; + break; + default: + style = NULL; + break; + } + + return style; +} +/*===================== + * Other functions + *====================*/ + +/** + * Move the list elements up by one + * @param list pointer a to list object + */ +void lv_list_up(const lv_obj_t * list) +{ + /*Search the first list element which 'y' coordinate is below the parent + * and position the list to show this element on the bottom*/ + lv_obj_t * scrl = lv_page_get_scrl(list); + lv_obj_t * e; + lv_obj_t * e_prev = NULL; + e = lv_list_get_prev_btn(list, NULL); + while(e != NULL) { + if(e->coords.y2 <= list->coords.y2) { + if(e_prev != NULL) { + lv_coord_t new_y = lv_obj_get_height(list) - (lv_obj_get_y(e_prev) + lv_obj_get_height(e_prev)); + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + if(ext->anim_time == 0) { + lv_obj_set_y(scrl, new_y); + } else { +#if USE_LV_ANIMATION + lv_anim_t a; + a.var = scrl; + a.start = lv_obj_get_y(scrl); + a.end = new_y; + a.fp = (lv_anim_fp_t)lv_obj_set_y; + a.path = lv_anim_path_linear; + a.end_cb = NULL; + a.act_time = 0; + a.time = LV_LIST_FOCUS_TIME; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); +#endif + } + } + break; + } + e_prev = e; + e = lv_list_get_prev_btn(list, e); + } +} + +/** + * Move the list elements down by one + * @param list pointer to a list object + */ +void lv_list_down(const lv_obj_t * list) +{ + /*Search the first list element which 'y' coordinate is above the parent + * and position the list to show this element on the top*/ + lv_obj_t * scrl = lv_page_get_scrl(list); + lv_obj_t * e; + e = lv_list_get_prev_btn(list, NULL); + while(e != NULL) { + if(e->coords.y1 < list->coords.y1) { + lv_coord_t new_y = -lv_obj_get_y(e); + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + if(ext->anim_time == 0) { + lv_obj_set_y(scrl, new_y); + } else { +#if USE_LV_ANIMATION + lv_anim_t a; + a.var = scrl; + a.start = lv_obj_get_y(scrl); + a.end = new_y; + a.fp = (lv_anim_fp_t)lv_obj_set_y; + a.path = lv_anim_path_linear; + a.end_cb = NULL; + a.act_time = 0; + a.time = LV_LIST_FOCUS_TIME; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); + +#endif + } + break; + } + e = lv_list_get_prev_btn(list, e); + } +} + +/** + * Focus on a list button. It ensures that the button will be visible on the list. + * @param btn pointer to a list button to focus + * @param anim_en true: scroll with animation, false: without animation + */ +void lv_list_focus(const lv_obj_t * btn, bool anim_en) +{ + +#if USE_LV_ANIMATION == 0 + anim_en = false; +#endif + + lv_obj_t * list = lv_obj_get_parent(lv_obj_get_parent(btn)); + + lv_page_focus(list, btn, anim_en == false ? 0 : lv_list_get_anim_time(list)); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Signal function of the list + * @param list pointer to a list object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_list_signal(lv_obj_t * list, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_page_signal(list, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_CORD_CHG) { + /*Be sure the width of the buttons are correct*/ + lv_coord_t w = lv_obj_get_width(list); + if(w != lv_area_get_width(param)) { /*Width changed*/ + refr_btn_width(list); + } + } else if(sign == LV_SIGNAL_STYLE_CHG) { + /*Because of the possible change of horizontal and vertical padding refresh buttons width */ + refr_btn_width(list); + } else if(sign == LV_SIGNAL_FOCUS) { + +#if USE_LV_GROUP + lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + /*With ENCODER select the first button only in edit mode*/ + if(indev_type == LV_INDEV_TYPE_ENCODER) { + lv_group_t * g = lv_obj_get_group(list); + if(lv_group_get_editing(g)) { + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + if(ext->last_sel) { + /* Select the last used button */ + lv_list_set_btn_selected(list, ext->last_sel); + } + else { + /*Get the first button and mark it as selected*/ + lv_list_set_btn_selected(list, lv_list_get_next_btn(list, NULL)); + } + } else { + lv_list_set_btn_selected(list, NULL); + } + } + /*Else select the clicked button*/ + else { + /*Mark the last clicked button (if any) as selected because it triggered the focus*/ + if(last_clicked_btn) { + lv_list_set_btn_selected(list, last_clicked_btn); + } else { + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + if(ext->last_sel) { + /* Select the last used button */ + lv_list_set_btn_selected(list, ext->last_sel); + } + else { + /*Get the first button and mark it as selected*/ + lv_list_set_btn_selected(list, lv_list_get_next_btn(list, NULL)); + } + } + } +#endif + } else if(sign == LV_SIGNAL_DEFOCUS) { + +#if USE_LV_GROUP + /*De-select the selected btn*/ + lv_list_set_btn_selected(list, NULL); + last_clicked_btn = NULL; /*button click will be set if click happens before focus*/ + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + ext->selected_btn = NULL; +#endif + } else if(sign == LV_SIGNAL_GET_EDITABLE) { + bool * editable = (bool *)param; + *editable = true; + } else if(sign == LV_SIGNAL_CONTROLL) { + +#if USE_LV_GROUP + char c = *((char *)param); + if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_DOWN) { + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + /*If there is a valid selected button the make the previous selected*/ + if(ext->selected_btn) { + lv_obj_t * btn_prev = lv_list_get_next_btn(list, ext->selected_btn); + if(btn_prev) lv_list_set_btn_selected(list, btn_prev); + } + /*If there is no selected button the make the first selected*/ + else { + lv_obj_t * btn = lv_list_get_next_btn(list, NULL); + if(btn) lv_list_set_btn_selected(list, btn); /*If there are no buttons on the list then there is no first button*/ + } + } else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_UP) { + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + /*If there is a valid selected button the make the next selected*/ + if(ext->selected_btn != NULL) { + lv_obj_t * btn_next = lv_list_get_prev_btn(list, ext->selected_btn); + if(btn_next) lv_list_set_btn_selected(list, btn_next); + } + /*If there is no selected button the make the first selected*/ + else { + lv_obj_t * btn = lv_list_get_next_btn(list, NULL); + if(btn) lv_list_set_btn_selected(list, btn); + } + } else if(c == LV_GROUP_KEY_ENTER) { + /*Get the 'pressed' button*/ + lv_obj_t * btn = NULL; + btn = lv_list_get_prev_btn(list, btn); + while(btn != NULL) { + if(lv_btn_get_state(btn) == LV_BTN_STATE_PR) break; + btn = lv_list_get_prev_btn(list, btn); + } + + if(btn != NULL) { + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + ext->last_sel = btn; + lv_action_t rel_action; + rel_action = lv_btn_get_action(btn, LV_BTN_ACTION_CLICK); + if(rel_action != NULL) rel_action(btn); + } + } +#endif + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_list"; + } + return res; +} + + +/** + * Signal function of the list buttons + * @param btn pointer to a button on the list + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_list_btn_signal(lv_obj_t * btn, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_btn_signal(btn, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_RELEASED) { + lv_obj_t * list = lv_obj_get_parent(lv_obj_get_parent(btn)); + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + ext->page.scroll_prop_ip = 0; + +#if USE_LV_GROUP + lv_group_t * g = lv_obj_get_group(list); + if(lv_group_get_focused(g) == list && lv_indev_is_dragging(lv_indev_get_act()) == false) { + /* Is the list is focused then be sure only the button being released + * has a pressed state to indicate the selected state on the list*/ + lv_obj_t * btn_i = lv_list_get_prev_btn(list, NULL); + while(btn_i) { + lv_btn_state_t s = lv_btn_get_state(btn_i); + if(s == LV_BTN_STATE_PR) lv_btn_set_state(btn_i, LV_BTN_STATE_REL); + else if(s == LV_BTN_STATE_TGL_PR) lv_btn_set_state(btn_i, LV_BTN_STATE_TGL_REL); + btn_i = lv_list_get_prev_btn(list, btn_i); + } + + /*Make the released button "selected"*/ + lv_list_set_btn_selected(list, btn); + } + + /* If `click_focus == 1` then LV_SIGNAL_FOCUS need to know which button triggered the focus + * to mark it as selected (pressed state)*/ + last_clicked_btn = btn; +#endif + if(lv_indev_is_dragging(lv_indev_get_act()) == false && ext->single_mode) + { + lv_list_btn_single_selected(btn); + } + } + else if(sign == LV_SIGNAL_PRESS_LOST) { + lv_obj_t * list = lv_obj_get_parent(lv_obj_get_parent(btn)); + lv_list_ext_t * ext = lv_obj_get_ext_attr(list); + ext->page.scroll_prop_ip = 0; + } + else if(sign == LV_SIGNAL_CLEANUP) { + +#if USE_LV_GROUP + lv_obj_t * list = lv_obj_get_parent(lv_obj_get_parent(btn)); + lv_obj_t * sel = lv_list_get_btn_selected(list); + if(sel == btn) lv_list_set_btn_selected(list, lv_list_get_next_btn(list, btn)); +#endif + } + + + return res; +} + +static void refr_btn_width(lv_obj_t * list) +{ + lv_style_t * style = lv_list_get_style(list, LV_LIST_STYLE_BG); + lv_style_t * style_scrl = lv_obj_get_style(lv_page_get_scrl(list)); + lv_coord_t w = lv_obj_get_width(list); + lv_coord_t btn_w = w - (style->body.padding.hor + style_scrl->body.padding.hor) * 2; + + lv_obj_t * btn = lv_list_get_prev_btn(list, NULL); + while(btn) { + /*Make the size adjustment for each buttons*/ + if(lv_obj_get_width(btn) != btn_w) { + lv_obj_set_width(btn, btn_w); + /*Set the label size to roll its text*/ + lv_obj_t * label = lv_list_get_btn_label(btn); + lv_obj_set_width(label, btn->coords.x2 - label->coords.x1); + lv_label_set_text(label, NULL); + } + btn = lv_list_get_prev_btn(list, btn); + } +} + +/** + * Make a single button selected in the list, deselect others, should be called in list btns call back. + * @param btn pointer to the currently pressed list btn object + */ +static void lv_list_btn_single_selected(lv_obj_t *btn) +{ + lv_obj_t *list = lv_obj_get_parent(lv_obj_get_parent(btn)); + + lv_obj_t * e = lv_list_get_next_btn(list, NULL); + do + { + if(e == btn) + { + lv_btn_set_state(e, LV_BTN_STATE_TGL_REL); + } + else + { + lv_btn_set_state(e, LV_BTN_STATE_REL); + } + e = lv_list_get_next_btn(list, e); + } while (e != NULL); +} + +/** + * Check if this is really a list button or another object. + * @param list_btn List button + */ +static bool lv_list_is_list_btn(lv_obj_t * list_btn) +{ + lv_obj_type_t type; + + lv_obj_get_type(list_btn, &type); + uint8_t cnt; + for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) { + if(type.type[cnt] == NULL) break; + if(!strcmp(type.type[cnt], "lv_btn")) + return true; + } + return false; +} + +/** + * Check if this is really a list label or another object. + * @param list_label List label + */ +static bool lv_list_is_list_label(lv_obj_t * list_label) +{ + lv_obj_type_t type; + + lv_obj_get_type(list_label, &type); + uint8_t cnt; + for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) { + if(type.type[cnt] == NULL) break; + if(!strcmp(type.type[cnt], "lv_label")) + return true; + } + return false; +} + +/** + * Check if this is really a list image or another object. + * @param list_image List image + */ +static bool lv_list_is_list_img(lv_obj_t * list_img) +{ + lv_obj_type_t type; + + lv_obj_get_type(list_img, &type); + uint8_t cnt; + for(cnt = 0; cnt < LV_MAX_ANCESTOR_NUM; cnt++) { + if(type.type[cnt] == NULL) break; + if(!strcmp(type.type[cnt], "lv_img")) + return true; + } + return false; +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_list.h b/bdk/libs/lvgl/lv_objx/lv_list.h new file mode 100644 index 00000000..01ebb194 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_list.h @@ -0,0 +1,336 @@ +/** + * @file lv_list.h + * + */ + +#ifndef LV_LIST_H +#define LV_LIST_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_LIST != 0 + +/*Testing of dependencies*/ +#if USE_LV_PAGE == 0 +#error "lv_list: lv_page is required. Enable it in lv_conf.h (USE_LV_PAGE 1) " +#endif + +#if USE_LV_BTN == 0 +#error "lv_list: lv_btn is required. Enable it in lv_conf.h (USE_LV_BTN 1) " +#endif + +#if USE_LV_LABEL == 0 +#error "lv_list: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) " +#endif + + +#include "../lv_core/lv_obj.h" +#include "lv_page.h" +#include "lv_btn.h" +#include "lv_label.h" +#include "lv_img.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of list*/ +typedef struct +{ + lv_page_ext_t page; /*Ext. of ancestor*/ + /*New data for this type */ + uint16_t anim_time; /*Scroll animation time*/ + lv_style_t *styles_btn[LV_BTN_STATE_NUM]; /*Styles of the list element buttons*/ + lv_style_t *style_img; /*Style of the list element images on buttons*/ + uint32_t size; /*the number of items(buttons) in the list*/ + bool single_mode; /* whether single selected mode is enabled */ +#if USE_LV_GROUP + lv_obj_t * last_sel; /* The last selected button. It will be reverted when the list is focused again */ + lv_obj_t * selected_btn; /* The button is currently being selected*/ +#endif +} lv_list_ext_t; + +enum { + LV_LIST_STYLE_BG, + LV_LIST_STYLE_SCRL, + LV_LIST_STYLE_SB, + LV_LIST_STYLE_EDGE_FLASH, + LV_LIST_STYLE_BTN_REL, + LV_LIST_STYLE_BTN_PR, + LV_LIST_STYLE_BTN_TGL_REL, + LV_LIST_STYLE_BTN_TGL_PR, + LV_LIST_STYLE_BTN_INA, +}; +typedef uint8_t lv_list_style_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a list objects + * @param par pointer to an object, it will be the parent of the new list + * @param copy pointer to a list object, if not NULL then the new object will be copied from it + * @return pointer to the created list + */ +lv_obj_t * lv_list_create(lv_obj_t * par, const lv_obj_t * copy); + +/** + * Delete all children of the scrl object, without deleting scrl child. + * @param obj pointer to an object + */ +void lv_list_clean(lv_obj_t *obj); + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Add a list element to the list + * @param list pointer to list object + * @param img_fn file name of an image before the text (NULL if unused) + * @param txt text of the list element (NULL if unused) + * @param rel_action pointer to release action function (like with lv_btn) + * @return pointer to the new list element which can be customized (a button) + */ +lv_obj_t * lv_list_add(lv_obj_t * list, const void * img_src, const char * txt, lv_action_t rel_action); + +/** + * Remove the index of the button in the list + * @param list pointer to a list object + * @param index pointer to a the button's index in the list, index must be 0 <= index < lv_list_ext_t.size + * @return true: successfully deleted + */ +bool lv_list_remove(const lv_obj_t * list, uint32_t index); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set single button selected mode, only one button will be selected if enabled. + * @param list pointer to the currently pressed list object + * @param mode, enable(true)/disable(false) single selected mode. + */ +void lv_list_set_single_mode(lv_obj_t *list, bool mode); + +#if USE_LV_GROUP + +/** + * Make a button selected. Can be used while navigating in the list with a keypad. + * @param list pointer to a list object + * @param btn pointer to a button to select + */ +void lv_list_set_btn_selected(lv_obj_t * list, lv_obj_t * btn); +#endif + +/** + * Set scroll animation duration on 'list_up()' 'list_down()' 'list_focus()' + * @param list pointer to a list object + * @param anim_time duration of animation [ms] + */ +void lv_list_set_anim_time(lv_obj_t *list, uint16_t anim_time); + +/** + * Set the scroll bar mode of a list + * @param list pointer to a list object + * @param sb_mode the new mode from 'lv_page_sb_mode_t' enum + */ +static inline void lv_list_set_sb_mode(lv_obj_t * list, lv_sb_mode_t mode) +{ + lv_page_set_sb_mode(list, mode); +} + +/** + * Enable the scroll propagation feature. If enabled then the List will move its parent if there is no more space to scroll. + * @param list pointer to a List + * @param en true or false to enable/disable scroll propagation + */ +static inline void lv_list_set_scroll_propagation(lv_obj_t * list, bool en) +{ + lv_page_set_scroll_propagation(list, en); +} + +/** + * Enable the edge flash effect. (Show an arc when the an edge is reached) + * @param list pointer to a List + * @param en true or false to enable/disable end flash + */ +static inline void lv_list_set_edge_flash(lv_obj_t * list, bool en) +{ + lv_page_set_edge_flash(list, en); +} + +/** + * Set a style of a list + * @param list pointer to a list object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_list_set_style(lv_obj_t *list, lv_list_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get single button selected mode. + * @param list pointer to the currently pressed list object. + */ +bool lv_list_get_single_mode(lv_obj_t *list); + +/** + * Get the text of a list element + * @param btn pointer to list element + * @return pointer to the text + */ +const char * lv_list_get_btn_text(const lv_obj_t * btn); +/** + * Get the label object from a list element + * @param btn pointer to a list element (button) + * @return pointer to the label from the list element or NULL if not found + */ +lv_obj_t * lv_list_get_btn_label(const lv_obj_t * btn); + +/** + * Get the image object from a list element + * @param btn pointer to a list element (button) + * @return pointer to the image from the list element or NULL if not found + */ +lv_obj_t * lv_list_get_btn_img(const lv_obj_t * btn); + +/** + * Get the next button from list. (Starts from the bottom button) + * @param list pointer to a list object + * @param prev_btn pointer to button. Search the next after it. + * @return pointer to the next button or NULL when no more buttons + */ +lv_obj_t * lv_list_get_prev_btn(const lv_obj_t * list, lv_obj_t * prev_btn); + +/** + * Get the previous button from list. (Starts from the top button) + * @param list pointer to a list object + * @param prev_btn pointer to button. Search the previous before it. + * @return pointer to the previous button or NULL when no more buttons + */ +lv_obj_t * lv_list_get_next_btn(const lv_obj_t * list, lv_obj_t * prev_btn); + +/** + * Get the index of the button in the list + * @param list pointer to a list object. If NULL, assumes btn is part of a list. + * @param btn pointer to a list element (button) + * @return the index of the button in the list, or -1 of the button not in this list + */ +int32_t lv_list_get_btn_index(const lv_obj_t * list, const lv_obj_t * btn); + +/** + * Get the number of buttons in the list + * @param list pointer to a list object + * @return the number of buttons in the list + */ +uint32_t lv_list_get_size(const lv_obj_t * list); + +#if USE_LV_GROUP +/** + * Get the currently selected button. Can be used while navigating in the list with a keypad. + * @param list pointer to a list object + * @return pointer to the selected button + */ +lv_obj_t * lv_list_get_btn_selected(const lv_obj_t * list); +#endif + + +/** + * Get scroll animation duration + * @param list pointer to a list object + * @return duration of animation [ms] + */ +uint16_t lv_list_get_anim_time(const lv_obj_t *list); + + +/** + * Get the scroll bar mode of a list + * @param list pointer to a list object + * @return scrollbar mode from 'lv_page_sb_mode_t' enum + */ +static inline lv_sb_mode_t lv_list_get_sb_mode(const lv_obj_t * list) +{ + return lv_page_get_sb_mode(list); +} + +/** + * Get the scroll propagation property + * @param list pointer to a List + * @return true or false + */ +static inline bool lv_list_get_scroll_propagation(lv_obj_t * list) +{ + return lv_page_get_scroll_propagation(list); +} + +/** + * Get the scroll propagation property + * @param list pointer to a List + * @return true or false + */ +static inline bool lv_list_get_edge_flash(lv_obj_t * list) +{ + return lv_page_get_edge_flash(list); +} + +/** + * Get a style of a list + * @param list pointer to a list object + * @param type which style should be get + * @return style pointer to a style + * */ +lv_style_t * lv_list_get_style(const lv_obj_t *list, lv_list_style_t type); + +/*===================== + * Other functions + *====================*/ + +/** + * Move the list elements up by one + * @param list pointer a to list object + */ +void lv_list_up(const lv_obj_t * list); +/** + * Move the list elements down by one + * @param list pointer to a list object + */ +void lv_list_down(const lv_obj_t * list); + +/** + * Focus on a list button. It ensures that the button will be visible on the list. + * @param btn pointer to a list button to focus + * @param anim_en true: scroll with animation, false: without animation + */ +void lv_list_focus(const lv_obj_t *btn, bool anim_en); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_LIST*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_LIST_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_lmeter.c b/bdk/libs/lvgl/lv_objx/lv_lmeter.c new file mode 100644 index 00000000..4d78d8ee --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_lmeter.c @@ -0,0 +1,382 @@ +/** + * @file lv_lmeter.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_lmeter.h" +#if USE_LV_LMETER != 0 + +#include "../lv_draw/lv_draw.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_core/lv_group.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ +#define LV_LMETER_LINE_UPSCALE 5 /*2^x upscale of line to make rounding*/ +#define LV_LMETER_LINE_UPSCALE_MASK ((1 << LV_LMETER_LINE_UPSCALE) - 1) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_lmeter_design(lv_obj_t * lmeter, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_lmeter_signal(lv_obj_t * lmeter, lv_signal_t sign, void * param); +static lv_coord_t lv_lmeter_coord_round(int32_t x); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a line meter objects + * @param par pointer to an object, it will be the parent of the new line meter + * @param copy pointer to a line meter object, if not NULL then the new object will be copied from it + * @return pointer to the created line meter + */ +lv_obj_t * lv_lmeter_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("line meter create started"); + + /*Create the ancestor of line meter*/ + lv_obj_t * new_lmeter = lv_obj_create(par, copy); + lv_mem_assert(new_lmeter); + if(new_lmeter == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_lmeter); + + /*Allocate the line meter type specific extended data*/ + lv_lmeter_ext_t * ext = lv_obj_allocate_ext_attr(new_lmeter, sizeof(lv_lmeter_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + /*Initialize the allocated 'ext' */ + ext->min_value = 0; + ext->max_value = 100; + ext->cur_value = 0; + ext->line_cnt = 21; /*Odd scale number looks better*/ + ext->scale_angle = 240; /*(scale_num - 1) * N looks better */ + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_lmeter, lv_lmeter_signal); + lv_obj_set_design_func(new_lmeter, lv_lmeter_design); + + /*Init the new line meter line meter*/ + if(copy == NULL) { + lv_obj_set_size(new_lmeter, LV_DPI, LV_DPI); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_lmeter_set_style(new_lmeter, th->lmeter); + } else { + lv_lmeter_set_style(new_lmeter, &lv_style_pretty_color); + } + } + /*Copy an existing line meter*/ + else { + lv_lmeter_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->scale_angle = copy_ext->scale_angle; + ext->line_cnt = copy_ext->line_cnt; + ext->min_value = copy_ext->min_value; + ext->max_value = copy_ext->max_value; + ext->cur_value = copy_ext->cur_value; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_lmeter); + } + + LV_LOG_INFO("line meter created"); + + return new_lmeter; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new value on the line meter + * @param lmeter pointer to a line meter object + * @param value new value + */ +void lv_lmeter_set_value(lv_obj_t * lmeter, int16_t value) +{ + lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter); + if(ext->cur_value == value) return; + + ext->cur_value = value > ext->max_value ? ext->max_value : value; + ext->cur_value = ext->cur_value < ext->min_value ? ext->min_value : ext->cur_value; + lv_obj_invalidate(lmeter); +} + +/** + * Set minimum and the maximum values of a line meter + * @param lmeter pointer to he line meter object + * @param min minimum value + * @param max maximum value + */ +void lv_lmeter_set_range(lv_obj_t * lmeter, int16_t min, int16_t max) +{ + lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter); + if(ext->min_value == min && ext->max_value == max) return; + + ext->max_value = max; + ext->min_value = min; + if(ext->cur_value > max) { + ext->cur_value = max; + lv_lmeter_set_value(lmeter, ext->cur_value); + } + if(ext->cur_value < min) { + ext->cur_value = min; + lv_lmeter_set_value(lmeter, ext->cur_value); + } + lv_obj_invalidate(lmeter); +} + +/** + * Set the scale settings of a line meter + * @param lmeter pointer to a line meter object + * @param angle angle of the scale (0..360) + * @param line_cnt number of lines + */ +void lv_lmeter_set_scale(lv_obj_t * lmeter, uint16_t angle, uint8_t line_cnt) +{ + lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter); + if(ext->scale_angle == angle && ext->line_cnt == line_cnt) return; + + ext->scale_angle = angle; + ext->line_cnt = line_cnt; + + lv_obj_invalidate(lmeter); +} + + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the value of a line meter + * @param lmeter pointer to a line meter object + * @return the value of the line meter + */ +int16_t lv_lmeter_get_value(const lv_obj_t * lmeter) +{ + lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter); + return ext->cur_value; +} + +/** + * Get the minimum value of a line meter + * @param lmeter pointer to a line meter object + * @return the minimum value of the line meter + */ +int16_t lv_lmeter_get_min_value(const lv_obj_t * lmeter) +{ + lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter); + return ext->min_value; +} + +/** + * Get the maximum value of a line meter + * @param lmeter pointer to a line meter object + * @return the maximum value of the line meter + */ +int16_t lv_lmeter_get_max_value(const lv_obj_t * lmeter) +{ + lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter); + return ext->max_value; +} + +/** + * Get the scale number of a line meter + * @param lmeter pointer to a line meter object + * @return number of the scale units + */ +uint8_t lv_lmeter_get_line_count(const lv_obj_t * lmeter) +{ + lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter); + return ext->line_cnt ; +} + +/** + * Get the scale angle of a line meter + * @param lmeter pointer to a line meter object + * @return angle of the scale + */ +uint16_t lv_lmeter_get_scale_angle(const lv_obj_t * lmeter) +{ + lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter); + return ext->scale_angle; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +/** + * Handle the drawing related tasks of the line meters + * @param lmeter pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_lmeter_design(lv_obj_t * lmeter, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + lv_lmeter_ext_t * ext = lv_obj_get_ext_attr(lmeter); + lv_style_t * style = lv_obj_get_style(lmeter); + lv_opa_t opa_scale = lv_obj_get_opa_scale(lmeter); + lv_style_t style_tmp; + memcpy(&style_tmp, style, sizeof(lv_style_t)); + + +#if USE_LV_GROUP + lv_group_t * g = lv_obj_get_group(lmeter); + if(lv_group_get_focused(g) == lmeter) { + style_tmp.line.width += 1; + } +#endif + + lv_coord_t r_out = lv_obj_get_width(lmeter) / 2; + lv_coord_t r_in = r_out - style->body.padding.hor; + if(r_in < 1) r_in = 1; + + lv_coord_t x_ofs = lv_obj_get_width(lmeter) / 2 + lmeter->coords.x1; + lv_coord_t y_ofs = lv_obj_get_height(lmeter) / 2 + lmeter->coords.y1; + int16_t angle_ofs = 90 + (360 - ext->scale_angle) / 2; + int16_t level = (int32_t)((int32_t)(ext->cur_value - ext->min_value) * ext->line_cnt) / (ext->max_value - ext->min_value); + uint8_t i; + + style_tmp.line.color = style->body.main_color; + + /*Calculate every coordinate in a bigger size to make rounding later*/ + r_out = r_out << LV_LMETER_LINE_UPSCALE; + r_in = r_in << LV_LMETER_LINE_UPSCALE; + + for(i = 0; i < ext->line_cnt; i++) { + /*Calculate the position a scale label*/ + int16_t angle = (i * ext->scale_angle) / (ext->line_cnt - 1) + angle_ofs; + + lv_coord_t y_out = (int32_t)((int32_t)lv_trigo_sin(angle) * r_out) >> LV_TRIGO_SHIFT; + lv_coord_t x_out = (int32_t)((int32_t)lv_trigo_sin(angle + 90) * r_out) >> LV_TRIGO_SHIFT; + lv_coord_t y_in = (int32_t)((int32_t)lv_trigo_sin(angle) * r_in) >> LV_TRIGO_SHIFT; + lv_coord_t x_in = (int32_t)((int32_t)lv_trigo_sin(angle + 90) * r_in) >> LV_TRIGO_SHIFT; + + /*Rounding*/ + x_out = lv_lmeter_coord_round(x_out); + x_in = lv_lmeter_coord_round(x_in); + y_out = lv_lmeter_coord_round(y_out); + y_in = lv_lmeter_coord_round(y_in); + + lv_point_t p1; + lv_point_t p2; + + p2.x = x_in + x_ofs; + p2.y = y_in + y_ofs; + + p1.x = x_out + x_ofs; + p1.y = y_out + y_ofs; + + if(i >= level) style_tmp.line.color = style->line.color; + else { + style_tmp.line.color = lv_color_mix(style->body.grad_color, style->body.main_color, (255 * i) / ext->line_cnt); + } + + lv_draw_line(&p1, &p2, mask, &style_tmp, opa_scale); + } + + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + + } + + return true; +} + +/** + * Signal function of the line meter + * @param lmeter pointer to a line meter object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_lmeter_signal(lv_obj_t * lmeter, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(lmeter, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } else if(sign == LV_SIGNAL_STYLE_CHG) { + lv_obj_refresh_ext_size(lmeter); + } else if(sign == LV_SIGNAL_REFR_EXT_SIZE) { + lv_style_t * style = lv_lmeter_get_style(lmeter); + lmeter->ext_size = LV_MATH_MAX(lmeter->ext_size, style->line.width); + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_lmeter"; + } + + return res; +} + +/** + * Round a coordinate which is upscaled (>=x.5 -> x + 1; x) + * @param x a coordinate which is greater then it should be + * @return the downscaled and rounded coordinate (+-1) + */ +static lv_coord_t lv_lmeter_coord_round(int32_t x) +{ +#if LV_LMETER_LINE_UPSCALE > 0 + bool was_negative = false; + if(x < 0) { + was_negative = true; + x = -x; + } + + x = (x >> LV_LMETER_LINE_UPSCALE) + ((x & LV_LMETER_LINE_UPSCALE_MASK) >> (LV_LMETER_LINE_UPSCALE - 1)); + + if(was_negative) x = -x; + + return x; +#else + return x; +#endif +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_lmeter.h b/bdk/libs/lvgl/lv_objx/lv_lmeter.h new file mode 100644 index 00000000..0235d34f --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_lmeter.h @@ -0,0 +1,153 @@ +/** + * @file lv_lmeter.h + * + */ + +#ifndef LV_LMETER_H +#define LV_LMETER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_LMETER != 0 + +#include "../lv_core/lv_obj.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of line meter*/ +typedef struct +{ + /*No inherited ext.*/ /*Ext. of ancestor*/ + /*New data for this type */ + uint16_t scale_angle; /*Angle of the scale in deg. (0..360)*/ + uint8_t line_cnt; /*Count of lines */ + int16_t cur_value; + int16_t min_value; + int16_t max_value; +} lv_lmeter_ext_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a line meter objects + * @param par pointer to an object, it will be the parent of the new line meter + * @param copy pointer to a line meter object, if not NULL then the new object will be copied from it + * @return pointer to the created line meter + */ +lv_obj_t * lv_lmeter_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new value on the line meter + * @param lmeter pointer to a line meter object + * @param value new value + */ +void lv_lmeter_set_value(lv_obj_t *lmeter, int16_t value); + +/** + * Set minimum and the maximum values of a line meter + * @param lmeter pointer to he line meter object + * @param min minimum value + * @param max maximum value + */ +void lv_lmeter_set_range(lv_obj_t *lmeter, int16_t min, int16_t max); + +/** + * Set the scale settings of a line meter + * @param lmeter pointer to a line meter object + * @param angle angle of the scale (0..360) + * @param line_cnt number of lines + */ +void lv_lmeter_set_scale(lv_obj_t * lmeter, uint16_t angle, uint8_t line_cnt); + +/** + * Set the styles of a line meter + * @param lmeter pointer to a line meter object + * @param bg set the style of the line meter + */ +static inline void lv_lmeter_set_style(lv_obj_t *lmeter, lv_style_t *bg) +{ + lv_obj_set_style(lmeter, bg); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the value of a line meter + * @param lmeter pointer to a line meter object + * @return the value of the line meter + */ +int16_t lv_lmeter_get_value(const lv_obj_t *lmeter); + +/** + * Get the minimum value of a line meter + * @param lmeter pointer to a line meter object + * @return the minimum value of the line meter + */ +int16_t lv_lmeter_get_min_value(const lv_obj_t * lmeter); + +/** + * Get the maximum value of a line meter + * @param lmeter pointer to a line meter object + * @return the maximum value of the line meter + */ +int16_t lv_lmeter_get_max_value(const lv_obj_t * lmeter); + +/** + * Get the scale number of a line meter + * @param lmeter pointer to a line meter object + * @return number of the scale units + */ +uint8_t lv_lmeter_get_line_count(const lv_obj_t * lmeter); + +/** + * Get the scale angle of a line meter + * @param lmeter pointer to a line meter object + * @return angle of the scale + */ +uint16_t lv_lmeter_get_scale_angle(const lv_obj_t * lmeter); + +/** + * Get the style of a line meter + * @param lmeter pointer to a line meter object + * @return pointer to the line meter's style + */ +static inline lv_style_t * lv_lmeter_get_style(const lv_obj_t * lmeter) +{ + return lv_obj_get_style(lmeter); +} + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_LMETER*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_LMETER_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_mbox.c b/bdk/libs/lvgl/lv_objx/lv_mbox.c new file mode 100644 index 00000000..4dcacddb --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_mbox.c @@ -0,0 +1,532 @@ +/* + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_mbox.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "lv_mbox.h" +#if USE_LV_MBOX != 0 + +#include "../lv_core/lv_group.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_anim.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ + +#if USE_LV_ANIMATION +# ifndef LV_MBOX_CLOSE_ANIM_TIME +# define LV_MBOX_CLOSE_ANIM_TIME 200 /*List close animation time) */ +# endif +#else +# undef LV_MBOX_CLOSE_ANIM_TIME +# define LV_MBOX_CLOSE_ANIM_TIME 0 /*No animations*/ +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_mbox_signal(lv_obj_t * mbox, lv_signal_t sign, void * param); +static void mbox_realign(lv_obj_t * mbox); +static lv_res_t lv_mbox_close_action(lv_obj_t * btn, const char * txt); +#if USE_LV_ANIMATION +static void lv_mbox_close_end_cb(lv_obj_t * mbox); +#endif + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a message box objects + * @param par pointer to an object, it will be the parent of the new message box + * @param copy pointer to a message box object, if not NULL then the new object will be copied from it + * @return pointer to the created message box + */ +lv_obj_t * lv_mbox_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("mesasge box create started"); + + /*Create the ancestor message box*/ + lv_obj_t * new_mbox = lv_cont_create(par, copy); + lv_mem_assert(new_mbox); + if(new_mbox == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_mbox); + + /*Allocate the message box type specific extended data*/ + lv_mbox_ext_t * ext = lv_obj_allocate_ext_attr(new_mbox, sizeof(lv_mbox_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->text = NULL; + ext->btnm = NULL; + ext->anim_time = LV_MBOX_CLOSE_ANIM_TIME; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_mbox, lv_mbox_signal); + + /*Init the new message box message box*/ + if(copy == NULL) { + ext->text = lv_label_create(new_mbox, NULL); + lv_label_set_align(ext->text, LV_LABEL_ALIGN_CENTER); + lv_label_set_long_mode(ext->text, LV_LABEL_LONG_BREAK); + lv_label_set_text(ext->text, "Message"); + + lv_cont_set_layout(new_mbox, LV_LAYOUT_COL_M); + lv_cont_set_fit(new_mbox, false, true); + lv_obj_set_width(new_mbox, LV_HOR_RES / 2); + lv_obj_align(new_mbox, NULL, LV_ALIGN_CENTER, 0, 0); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_mbox_set_style(new_mbox, LV_MBOX_STYLE_BG, th->mbox.bg); + } else { + lv_mbox_set_style(new_mbox, LV_MBOX_STYLE_BG, &lv_style_pretty); + } + + } + /*Copy an existing message box*/ + else { + lv_mbox_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + + ext->text = lv_label_create(new_mbox, copy_ext->text); + + /*Copy the buttons and the label on them*/ + if(copy_ext->btnm) ext->btnm = lv_btnm_create(new_mbox, copy_ext->btnm); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_mbox); + } + + + LV_LOG_INFO("mesasge box created"); + + return new_mbox; +} + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Add button to the message box + * @param mbox pointer to message box object + * @param btn_map button descriptor (button matrix map). + * E.g. a const char *txt[] = {"ok", "close", ""} (Can not be local variable) + * @param action a function which will be called when a button is released + */ +void lv_mbox_add_btns(lv_obj_t * mbox, const char ** btn_map, lv_btnm_action_t action) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + + /*Create a button matrix if not exists yet*/ + if(ext->btnm == NULL) { + ext->btnm = lv_btnm_create(mbox, NULL); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_mbox_set_style(mbox, LV_MBOX_STYLE_BTN_BG, th->mbox.btn.bg); + lv_mbox_set_style(mbox, LV_MBOX_STYLE_BTN_REL, th->mbox.btn.rel); + lv_mbox_set_style(mbox, LV_MBOX_STYLE_BTN_PR, th->mbox.btn.pr); + } else { + lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BG, &lv_style_transp_fit); + } + } + + lv_btnm_set_map(ext->btnm, btn_map); + if(action == NULL) lv_btnm_set_action(ext->btnm, lv_mbox_close_action); /*Set a default action anyway*/ + else lv_btnm_set_action(ext->btnm, action); + + mbox_realign(mbox); +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the text of the message box + * @param mbox pointer to a message box + * @param txt a '\0' terminated character string which will be the message box text + */ +void lv_mbox_set_text(lv_obj_t * mbox, const char * txt) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + lv_label_set_text(ext->text, txt); + + mbox_realign(mbox); +} + + +/** + * Stop the action to call when button is released + * @param pointer to an 'lv_btnm_action_t' action. In the action you need to use `lv_mbox_get_from_btn()` to get the `mbox`. + * @param pointer to an 'lv_btnm_action_t' action + */ +void lv_mbox_set_action(lv_obj_t * mbox, lv_btnm_action_t action) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + lv_btnm_set_action(ext->btnm, action); +} + + +/** + * Set animation duration + * @param mbox pointer to a message box object + * @param anim_time animation length in milliseconds (0: no animation) + */ +void lv_mbox_set_anim_time(lv_obj_t * mbox, uint16_t anim_time) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); +#if USE_LV_ANIMATION == 0 + anim_time = 0; +#endif + + ext->anim_time = anim_time; +} + +/** + * Automatically delete the message box after a given time + * @param mbox pointer to a message box object + * @param delay a time (in milliseconds) to wait before delete the message box + */ +void lv_mbox_start_auto_close(lv_obj_t * mbox, uint16_t delay) +{ +#if USE_LV_ANIMATION + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + + if(ext->anim_time != 0) { + /*Add shrinking animations*/ + lv_obj_animate(mbox, LV_ANIM_GROW_H | LV_ANIM_OUT, ext->anim_time, delay, NULL); + lv_obj_animate(mbox, LV_ANIM_GROW_V | LV_ANIM_OUT, ext->anim_time, delay, lv_mbox_close_end_cb); + + /*Disable fit to let shrinking work*/ + lv_cont_set_fit(mbox, false, false); + } else { + lv_obj_animate(mbox, LV_ANIM_NONE, ext->anim_time, delay, lv_mbox_close_end_cb); + } +#else + (void)delay; /*Unused*/ + lv_obj_del(mbox); +#endif +} + +/** + * Stop the auto. closing of message box + * @param mbox pointer to a message box object + */ +void lv_mbox_stop_auto_close(lv_obj_t * mbox) +{ +#if USE_LV_ANIMATION + lv_anim_del(mbox, NULL); +#else + (void)mbox; /*Unused*/ +#endif +} + +/** + * Set a style of a message box + * @param mbox pointer to a message box object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_mbox_set_style(lv_obj_t * mbox, lv_mbox_style_t type, lv_style_t * style) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + + switch(type) { + case LV_MBOX_STYLE_BG: + lv_obj_set_style(mbox, style); + break; + case LV_MBOX_STYLE_BTN_BG: + lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BG, style); + break; + case LV_MBOX_STYLE_BTN_REL: + lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_REL, style); + break; + case LV_MBOX_STYLE_BTN_PR: + lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_PR, style); + break; + case LV_MBOX_STYLE_BTN_TGL_REL: + lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_REL, style); + break; + case LV_MBOX_STYLE_BTN_TGL_PR: + lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_PR, style); + break; + case LV_MBOX_STYLE_BTN_INA: + lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_INA, style); + break; + } + + mbox_realign(mbox); + +} + +/** + * Set whether recoloring is enabled + * @param btnm pointer to button matrix object + * @param en whether recoloring is enabled + */ +void lv_mbox_set_recolor(lv_obj_t * mbox, bool en) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + + if(ext->btnm) + lv_btnm_set_recolor(ext->btnm, en); +} + +void lv_mbox_set_recolor_text(lv_obj_t * mbox, bool en) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + + if (ext->text) + lv_label_set_recolor(ext->text, en); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the text of the message box + * @param mbox pointer to a message box object + * @return pointer to the text of the message box + */ +const char * lv_mbox_get_text(const lv_obj_t * mbox) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + + return lv_label_get_text(ext->text); +} + +/** + * Get the message box object from one of its button. + * It is useful in the button release actions where only the button is known + * @param btn pointer to a button of a message box + * @return pointer to the button's message box + */ +lv_obj_t * lv_mbox_get_from_btn(const lv_obj_t * btn) +{ + lv_obj_t * mbox = lv_obj_get_parent(btn); + + return mbox; +} + +/** + * Get the animation duration (close animation time) + * @param mbox pointer to a message box object + * @return animation length in milliseconds (0: no animation) + */ +uint16_t lv_mbox_get_anim_time(const lv_obj_t * mbox) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + return ext->anim_time; +} + +/** + * Get a style of a message box + * @param mbox pointer to a message box object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_mbox_get_style(const lv_obj_t * mbox, lv_mbox_style_t type) +{ + lv_style_t * style = NULL; + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + + switch(type) { + case LV_MBOX_STYLE_BG: + style = lv_obj_get_style(mbox); + break; + case LV_MBOX_STYLE_BTN_BG: + style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BG); + break; + case LV_MBOX_STYLE_BTN_REL: + style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_REL); + break; + case LV_MBOX_STYLE_BTN_PR: + style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_PR); + break; + case LV_MBOX_STYLE_BTN_TGL_REL: + style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_REL); + break; + case LV_MBOX_STYLE_BTN_TGL_PR: + style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_PR); + break; + case LV_MBOX_STYLE_BTN_INA: + style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_INA); + break; + default: + style = NULL; + break; + } + + return style; +} + +/** + * Get whether recoloring is enabled + * @param btnm pointer to button matrix object + * @return whether recoloring is enabled + */ +bool lv_mbox_get_recolor(const lv_obj_t * mbox) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + + if(!ext->btnm) + return false; + + return lv_btnm_get_recolor(ext->btnm); +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Signal function of the message box + * @param mbox pointer to a message box object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_mbox_signal(lv_obj_t * mbox, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /*Translate LV_GROUP_KEY_UP/DOWN to LV_GROUP_KEY_LEFT/RIGHT */ + char c_trans = 0; + if(sign == LV_SIGNAL_CONTROLL) { + c_trans = *((char *)param); + if(c_trans == LV_GROUP_KEY_DOWN) c_trans = LV_GROUP_KEY_LEFT; + if(c_trans == LV_GROUP_KEY_UP) c_trans = LV_GROUP_KEY_RIGHT; + + param = &c_trans; + } + + /* Include the ancient signal function */ + res = ancestor_signal(mbox, sign, param); + if(res != LV_RES_OK) return res; + + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + if(sign == LV_SIGNAL_CORD_CHG) { + if(lv_obj_get_width(mbox) != lv_area_get_width(param)) { + mbox_realign(mbox); + } + } else if(sign == LV_SIGNAL_STYLE_CHG) { + mbox_realign(mbox); + + } else if(sign == LV_SIGNAL_FOCUS || sign == LV_SIGNAL_DEFOCUS || + sign == LV_SIGNAL_CONTROLL || sign == LV_SIGNAL_GET_EDITABLE) { + if(ext->btnm) { + ext->btnm->signal_func(ext->btnm, sign, param); + } + + /* The button matrix with ENCODER input supposes it's in a group but in this case it isn't (Only the message box's container) + * So so some actions here instead*/ + if(sign == LV_SIGNAL_FOCUS) { +#if USE_LV_GROUP + lv_indev_t * indev = lv_indev_get_act(); + lv_hal_indev_type_t indev_type = lv_indev_get_type(indev); + if(indev_type == LV_INDEV_TYPE_ENCODER) { + /*In navigation mode don't select any button but in edit mode select the fist*/ + lv_btnm_ext_t * btnm_ext = lv_obj_get_ext_attr(ext->btnm); + if(lv_group_get_editing(lv_obj_get_group(mbox))) btnm_ext->btn_id_pr = 0; + else btnm_ext->btn_id_pr = LV_BTNM_PR_NONE; + } +#endif + } + + + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_mbox"; + } + + return res; +} + +/** + * Resize the button holder to fit + * @param mbox pointer to message box object + */ +static void mbox_realign(lv_obj_t * mbox) +{ + lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox); + + lv_style_t * style = lv_mbox_get_style(mbox, LV_MBOX_STYLE_BG); + lv_coord_t w = lv_obj_get_width(mbox) - 2 * style->body.padding.hor; + + if(ext->text) { + lv_obj_set_width(ext->text, w); + } + + if(ext->btnm) { + lv_style_t * btn_bg_style = lv_mbox_get_style(mbox, LV_MBOX_STYLE_BTN_BG); + lv_style_t * btn_rel_style = lv_mbox_get_style(mbox, LV_MBOX_STYLE_BTN_REL); + lv_coord_t font_h = lv_font_get_height(btn_rel_style->text.font); + lv_obj_set_size(ext->btnm, w, font_h + 2 * btn_rel_style->body.padding.ver + 2 * btn_bg_style->body.padding.ver); + } +} + +static lv_res_t lv_mbox_close_action(lv_obj_t * btn, const char * txt) +{ + lv_obj_t * mbox = lv_mbox_get_from_btn(btn); + + if(txt[0] != '\0') { + lv_mbox_start_auto_close(mbox, 0); + return LV_RES_INV; + } + + return LV_RES_OK; +} + +#if USE_LV_ANIMATION +static void lv_mbox_close_end_cb(lv_obj_t * mbox) +{ + lv_obj_del(mbox); +} +#endif +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_mbox.h b/bdk/libs/lvgl/lv_objx/lv_mbox.h new file mode 100644 index 00000000..ae5c76a8 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_mbox.h @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_mbox.h + * + */ + +#ifndef LV_MBOX_H +#define LV_MBOX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_MBOX != 0 + +/*Testing of dependencies*/ +#if USE_LV_CONT == 0 +#error "lv_mbox: lv_cont is required. Enable it in lv_conf.h (USE_LV_CONT 1) " +#endif + +#if USE_LV_BTNM == 0 +#error "lv_mbox: lv_btnm is required. Enable it in lv_conf.h (USE_LV_BTNM 1) " +#endif + +#if USE_LV_LABEL == 0 +#error "lv_mbox: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) " +#endif + + +#include "../lv_core/lv_obj.h" +#include "lv_cont.h" +#include "lv_btnm.h" +#include "lv_label.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Data of message box*/ +typedef struct +{ + lv_cont_ext_t bg; /*Ext. of ancestor*/ + /*New data for this type */ + lv_obj_t *text; /*Text of the message box*/ + lv_obj_t *btnm; /*Button matrix for the buttons*/ + uint16_t anim_time; /*Duration of close animation [ms] (0: no animation)*/ +} lv_mbox_ext_t; + +enum { + LV_MBOX_STYLE_BG, + LV_MBOX_STYLE_BTN_BG, + LV_MBOX_STYLE_BTN_REL, + LV_MBOX_STYLE_BTN_PR, + LV_MBOX_STYLE_BTN_TGL_REL, + LV_MBOX_STYLE_BTN_TGL_PR, + LV_MBOX_STYLE_BTN_INA, +}; +typedef uint8_t lv_mbox_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a message box objects + * @param par pointer to an object, it will be the parent of the new message box + * @param copy pointer to a message box object, if not NULL then the new object will be copied from it + * @return pointer to the created message box + */ +lv_obj_t * lv_mbox_create(lv_obj_t * par, const lv_obj_t * copy); + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Add button to the message box + * @param mbox pointer to message box object + * @param btn_map button descriptor (button matrix map). + * E.g. a const char *txt[] = {"ok", "close", ""} (Can not be local variable) + * @param action a function which will be called when a button is released + */ +void lv_mbox_add_btns(lv_obj_t * mbox, const char **btn_map, lv_btnm_action_t action); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the text of the message box + * @param mbox pointer to a message box + * @param txt a '\0' terminated character string which will be the message box text + */ +void lv_mbox_set_text(lv_obj_t * mbox, const char * txt); + +/** + * Stop the action to call when button is released + * @param mbox pointer to a message box object + * @param pointer to an 'lv_btnm_action_t' action. In the action you need to use `lv_mbox_get_from_btn()` to get the `mbox`. + */ +void lv_mbox_set_action(lv_obj_t * mbox, lv_btnm_action_t action); + +/** + * Set animation duration + * @param mbox pointer to a message box object + * @param anim_time animation length in milliseconds (0: no animation) + */ +void lv_mbox_set_anim_time(lv_obj_t * mbox, uint16_t anim_time); + +/** + * Automatically delete the message box after a given time + * @param mbox pointer to a message box object + * @param delay a time (in milliseconds) to wait before delete the message box + */ +void lv_mbox_start_auto_close(lv_obj_t * mbox, uint16_t delay); + +/** + * Stop the auto. closing of message box + * @param mbox pointer to a message box object + */ +void lv_mbox_stop_auto_close(lv_obj_t * mbox); + +/** + * Set a style of a message box + * @param mbox pointer to a message box object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_mbox_set_style(lv_obj_t *mbox, lv_mbox_style_t type, lv_style_t *style); + +/** + * Set whether recoloring is enabled. Must be called after `lv_mbox_add_btns`. + * @param btnm pointer to button matrix object + * @param en whether recoloring is enabled + */ +void lv_mbox_set_recolor(lv_obj_t * mbox, bool en); + +void lv_mbox_set_recolor_text(lv_obj_t * mbox, bool en); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the text of the message box + * @param mbox pointer to a message box object + * @return pointer to the text of the message box + */ +const char * lv_mbox_get_text(const lv_obj_t * mbox); + +/** + * Get the message box object from one of its button. + * It is useful in the button release actions where only the button is known + * @param btn pointer to a button of a message box + * @return pointer to the button's message box + */ +lv_obj_t * lv_mbox_get_from_btn(const lv_obj_t * btn); + +/** + * Get the animation duration (close animation time) + * @param mbox pointer to a message box object + * @return animation length in milliseconds (0: no animation) + */ +uint16_t lv_mbox_get_anim_time(const lv_obj_t * mbox); + + +/** + * Get a style of a message box + * @param mbox pointer to a message box object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_mbox_get_style(const lv_obj_t *mbox, lv_mbox_style_t type); + +/** + * Get whether recoloring is enabled + * @param btnm pointer to button matrix object + * @return whether recoloring is enabled + */ +bool lv_mbox_get_recolor(const lv_obj_t * mbox); + +/********************** + * MACROS + **********************/ + + +#endif /*USE_LV_MBOX*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_MBOX_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_objx.mk b/bdk/libs/lvgl/lv_objx/lv_objx.mk new file mode 100644 index 00000000..d35252bc --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_objx.mk @@ -0,0 +1,36 @@ +CSRCS += lv_arc.c +CSRCS += lv_bar.c +CSRCS += lv_cb.c +CSRCS += lv_ddlist.c +CSRCS += lv_kb.c +CSRCS += lv_line.c +CSRCS += lv_mbox.c +CSRCS += lv_preload.c +CSRCS += lv_roller.c +CSRCS += lv_table.c +CSRCS += lv_tabview.c +CSRCS += lv_tileview.c +CSRCS += lv_btn.c +CSRCS += lv_calendar.c +CSRCS += lv_chart.c +CSRCS += lv_canvas.c +CSRCS += lv_gauge.c +CSRCS += lv_label.c +CSRCS += lv_list.c +CSRCS += lv_slider.c +CSRCS += lv_ta.c +CSRCS += lv_spinbox.c +CSRCS += lv_btnm.c +CSRCS += lv_cont.c +CSRCS += lv_img.c +CSRCS += lv_imgbtn.c +CSRCS += lv_led.c +CSRCS += lv_lmeter.c +CSRCS += lv_page.c +CSRCS += lv_sw.c +CSRCS += lv_win.c + +DEPPATH += --dep-path $(LVGL_DIR)/lvgl/lv_objx +VPATH += :$(LVGL_DIR)/lvgl/lv_objx + +CFLAGS += "-I$(LVGL_DIR)/lvgl/lv_objx" diff --git a/bdk/libs/lvgl/lv_objx/lv_objx_templ.c b/bdk/libs/lvgl/lv_objx/lv_objx_templ.c new file mode 100644 index 00000000..f605450c --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_objx_templ.c @@ -0,0 +1,231 @@ +/** + * @file lv_templ.c + * + */ + +/* TODO Remove these instructions + * Search an replace: template -> object normal name with lower case (e.g. button, label etc.) + * templ -> object short name with lower case(e.g. btn, label etc) + * TEMPL -> object short name with upper case (e.g. BTN, LABEL etc.) + * + */ + +/********************* + * INCLUDES + *********************/ +//#include "lv_templ.h" /*TODO uncomment this*/ +#if USE_LV_TEMPL != 0 + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_templ_design(lv_obj_t * templ, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_templ_signal(lv_obj_t * templ, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_design_func_t ancestor_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a template object + * @param par pointer to an object, it will be the parent of the new template + * @param copy pointer to a template object, if not NULL then the new object will be copied from it + * @return pointer to the created template + */ +lv_obj_t * lv_templ_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("template create started"); + + /*Create the ancestor of template*/ + /*TODO modify it to the ancestor create function */ + lv_obj_t * new_templ = lv_ANCESTOR_create(par, copy); + lv_mem_assert(new_templ); + if(new_templ == NULL) return NULL; + + /*Allocate the template type specific extended data*/ + lv_templ_ext_t * ext = lv_obj_allocate_ext_attr(new_templ, sizeof(lv_templ_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_templ); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_templ); + + /*Initialize the allocated 'ext' */ + ext->xyz = 0; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_templ, lv_templ_signal); + lv_obj_set_design_func(new_templ, lv_templ_design); + + /*Init the new template template*/ + if(copy == NULL) { + + } + /*Copy an existing template*/ + else { + lv_templ_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_templ); + } + + LV_LOG_INFO("template created"); + + return new_templ; +} + +/*====================== + * Add/remove functions + *=====================*/ + +/* + * New object specific "add" or "remove" functions come here + */ + + +/*===================== + * Setter functions + *====================*/ + +/* + * New object specific "set" functions come here + */ + + +/** + * Set a style of a template. + * @param templ pointer to template object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_templ_set_style(lv_obj_t * templ, lv_templ_style_t type, lv_style_t * style) +{ + lv_templ_ext_t * ext = lv_obj_get_ext_attr(templ); + + switch(type) { + case LV_TEMPL_STYLE_X: + break; + case LV_TEMPL_STYLE_Y: + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/* + * New object specific "get" functions come here + */ + +/** + * Get style of a template. + * @param templ pointer to template object + * @param type which style should be get + * @return style pointer to the style + */ +lv_style_t * lv_templ_get_style(const lv_obj_t * templ, lv_templ_style_t type) +{ + lv_templ_ext_t * ext = lv_obj_get_ext_attr(templ); + lv_style_t * style = NULL; + + switch(type) { + case LV_TEMPL_STYLE_X: + style = NULL; /*Replace NULL with a pointer to the style*/ + case LV_TEMPL_STYLE_Y: + style = NULL; /*Replace NULL with a pointer to the style*/ + default: + style = NULL; + } + + return style; +} + +/*===================== + * Other functions + *====================*/ + +/* + * New object specific "other" functions come here + */ + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the templates + * @param templ pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_templ_design(lv_obj_t * templ, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + + } + + return true; +} + +/** + * Signal function of the template + * @param templ pointer to a template object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_templ_signal(lv_obj_t * templ, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(templ, sign, param); + if(res != LV_RES_OK) return res; + + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_templ"; + } + + return res; +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_objx_templ.h b/bdk/libs/lvgl/lv_objx/lv_objx_templ.h new file mode 100644 index 00000000..ab6d0906 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_objx_templ.h @@ -0,0 +1,111 @@ +/** + * @file lv_templ.h + * + */ + + +/* TODO Remove these instructions + * Search an replace: template -> object normal name with lower case (e.g. button, label etc.) + * templ -> object short name with lower case(e.g. btn, label etc) + * TEMPL -> object short name with upper case (e.g. BTN, LABEL etc.) + * + */ + +#ifndef LV_TEMPL_H +#define LV_TEMPL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_TEMPL != 0 + +#include "../lv_core/lv_obj.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of template*/ +typedef struct { + lv_ANCESTOR_ext_t ANCESTOR; /*Ext. of ancestor*/ + /*New data for this type */ +} lv_templ_ext_t; + + +/*Styles*/ +enum { + LV_TEMPL_STYLE_X, + LV_TEMPL_STYLE_Y, +}; +typedef uint8_t lv_templ_style_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a template objects + * @param par pointer to an object, it will be the parent of the new template + * @param copy pointer to a template object, if not NULL then the new object will be copied from it + * @return pointer to the created template + */ +lv_obj_t * lv_templ_create(lv_obj_t * par, const lv_obj_t * copy); + +/*====================== + * Add/remove functions + *=====================*/ + + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a style of a template. + * @param templ pointer to template object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_templ_set_style(lv_obj_t * templ, lv_templ_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get style of a template. + * @param templ pointer to template object + * @param type which style should be get + * @return style pointer to the style + */ +lv_style_t * lv_templ_get_style(const lv_obj_t * templ, lv_templ_style_t type); + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_TEMPL*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_TEMPL_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_page.c b/bdk/libs/lvgl/lv_objx/lv_page.c new file mode 100644 index 00000000..65dcb359 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_page.c @@ -0,0 +1,1205 @@ +/* + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_page.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "../lv_objx/lv_page.h" +#if USE_LV_PAGE != 0 + +#include "../lv_core/lv_group.h" +#include "../lv_draw/lv_draw.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_core/lv_refr.h" +#include "../lv_misc/lv_anim.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ +#define LV_PAGE_SB_MIN_SIZE (LV_DPI / 8) +#define LV_PAGE_SCROLL_ANIM_TIME 200 /*[ms] Scroll anim time on `lv_page_scroll_up/down/left/rigth`*/ +#define LV_PAGE_END_FLASH_SIZE (LV_DPI / 4) +#define LV_PAGE_END_ANIM_TIME 300 +#define LV_PAGE_END_ANIM_WAIT_TIME 300 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void lv_page_sb_refresh(lv_obj_t * page); +static bool lv_page_design(lv_obj_t * page, const lv_area_t * mask, lv_design_mode_t mode); +static bool lv_scrl_design(lv_obj_t * scrl, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_page_signal(lv_obj_t * page, lv_signal_t sign, void * param); +static lv_res_t lv_page_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, void * param); +#if USE_LV_ANIMATION +static void edge_flash_anim(void * page, int32_t v); +static void edge_flash_anim_end(void * page); +#endif + +/********************** + * STATIC VARIABLES + **********************/ +static lv_design_func_t ancestor_design; +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a page objects + * @param par pointer to an object, it will be the parent of the new page + * @param copy pointer to a page object, if not NULL then the new object will be copied from it + * @return pointer to the created page + */ +lv_obj_t * lv_page_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("page create started"); + + /*Create the ancestor object*/ + lv_obj_t * new_page = lv_cont_create(par, copy); + lv_mem_assert(new_page); + if(new_page == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_page); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_page); + + /*Allocate the object type specific extended data*/ + lv_page_ext_t * ext = lv_obj_allocate_ext_attr(new_page, sizeof(lv_page_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->scrl = NULL; + ext->pr_action = NULL; + ext->rel_action = NULL; + ext->sb.hor_draw = 0; + ext->sb.ver_draw = 0; + ext->bgo = NULL; + ext->pr = NULL; + ext->sb.style = &lv_style_pretty; + ext->sb.mode = LV_SB_MODE_AUTO; + ext->edge_flash.enabled = 0; + ext->edge_flash.bottom_ip = 0; + ext->edge_flash.top_ip = 0; + ext->edge_flash.left_ip = 0; + ext->edge_flash.right_ip = 0; + ext->edge_flash.state = 0; + ext->edge_flash.style = &lv_style_plain_color; + ext->arrow_scroll = 0; + ext->scroll_prop = 0; + ext->scroll_prop_ip = 0; + + /*Init the new page object*/ + if(copy == NULL) { + ext->scrl = lv_cont_create(new_page, NULL); + lv_obj_set_signal_func(ext->scrl, lv_page_scrollable_signal); + lv_obj_set_design_func(ext->scrl, lv_scrl_design); + lv_obj_set_drag(ext->scrl, true); + lv_obj_set_drag_throw(ext->scrl, true); + lv_obj_set_protect(ext->scrl, LV_PROTECT_PARENT | LV_PROTECT_PRESS_LOST); + lv_cont_set_fit(ext->scrl, false, true); + + /* Add the signal function only if 'scrolling' is created + * because everything has to be ready before any signal is received*/ + lv_obj_set_signal_func(new_page, lv_page_signal); + lv_obj_set_design_func(new_page, lv_page_design); + + lv_page_set_sb_mode(new_page, ext->sb.mode); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + if(par == NULL) { /*Different styles if it is screen*/ + lv_page_set_style(new_page, LV_PAGE_STYLE_BG, th->bg); + lv_page_set_style(new_page, LV_PAGE_STYLE_SCRL, &lv_style_transp); + } else { + lv_page_set_style(new_page, LV_PAGE_STYLE_BG, th->page.bg); + lv_page_set_style(new_page, LV_PAGE_STYLE_SCRL, th->page.scrl); + + } + lv_page_set_style(new_page, LV_PAGE_STYLE_SB, th->page.sb); + } else { + lv_page_set_style(new_page, LV_PAGE_STYLE_BG, &lv_style_pretty_color); + lv_page_set_style(new_page, LV_PAGE_STYLE_SCRL, &lv_style_pretty); + lv_page_set_style(new_page, LV_PAGE_STYLE_SB, &lv_style_pretty_color); + } + + } else { + lv_page_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->scrl = lv_cont_create(new_page, copy_ext->scrl); + ext->bgo = lv_page_get_style(copy, LV_PAGE_STYLE_BGO); + ext->pr = lv_page_get_style(copy, LV_PAGE_STYLE_PR); + lv_obj_set_signal_func(ext->scrl, lv_page_scrollable_signal); + + lv_page_set_pr_action(new_page, copy_ext->pr_action); + lv_page_set_rel_action(new_page, copy_ext->rel_action); + lv_page_set_sb_mode(new_page, copy_ext->sb.mode); + lv_page_set_arrow_scroll(new_page, copy_ext->arrow_scroll); + + + lv_page_set_style(new_page, LV_PAGE_STYLE_BG, lv_page_get_style(copy, LV_PAGE_STYLE_BG)); + lv_page_set_style(new_page, LV_PAGE_STYLE_SCRL, lv_page_get_style(copy, LV_PAGE_STYLE_SCRL)); + lv_page_set_style(new_page, LV_PAGE_STYLE_SB, lv_page_get_style(copy, LV_PAGE_STYLE_SB)); + + /* Add the signal function only if 'scrolling' is created + * because everything has to be ready before any signal is received*/ + lv_obj_set_signal_func(new_page, lv_page_signal); + lv_obj_set_design_func(new_page, lv_page_design); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_page); + } + + lv_page_sb_refresh(new_page); + + LV_LOG_INFO("page created"); + + return new_page; +} + +/** + * Delete all children of the scrl object, without deleting scrl child. + * @param obj pointer to an object + */ +void lv_page_clean(lv_obj_t * obj) +{ + lv_obj_t * scrl = lv_page_get_scrl(obj); + lv_obj_clean(scrl); +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a release action for the page + * @param page pointer to a page object + * @param rel_action a function to call when the page is release + */ +void lv_page_set_rel_action(lv_obj_t * page, lv_action_t rel_action) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + ext->rel_action = rel_action; +} + +/** + * Set a press action for the page + * @param page pointer to a page object + * @param pr_action a function to call when the page is pressed + */ +void lv_page_set_pr_action(lv_obj_t * page, lv_action_t pr_action) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + ext->pr_action = pr_action; +} + +/** + * Set the scroll bar mode on a page + * @param page pointer to a page object + * @param sb_mode the new mode from 'lv_page_sb.mode_t' enum + */ +void lv_page_set_sb_mode(lv_obj_t * page, lv_sb_mode_t sb_mode) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + if(ext->sb.mode == sb_mode) return; + + if(sb_mode == LV_SB_MODE_HIDE) ext->sb.mode |= LV_SB_MODE_HIDE; /*Set the hidden flag*/ + else if(sb_mode == LV_SB_MODE_UNHIDE) ext->sb.mode &= (~LV_SB_MODE_HIDE); /*Clear the hidden flag*/ + else { + if(ext->sb.mode & LV_SB_MODE_HIDE) sb_mode |= LV_SB_MODE_HIDE; + ext->sb.mode = sb_mode; + } + + ext->sb.hor_draw = 0; + ext->sb.ver_draw = 0; + + lv_page_sb_refresh(page); + lv_obj_invalidate(page); +} + +/** + * Enable/Disable scrolling with arrows if the page is in group (arrows: LV_GROUP_KEY_LEFT/RIGHT/UP/DOWN) + * @param page pointer to a page object + * @param en true: enable scrolling with arrows + */ +void lv_page_set_arrow_scroll(lv_obj_t * page, bool en) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + ext->arrow_scroll = en ? 1 : 0; +} + +/** + * Enable the scroll propagation feature. If enabled then the page will move its parent if there is no more space to scroll. + * @param page pointer to a Page + * @param en true or false to enable/disable scroll propagation + */ +void lv_page_set_scroll_propagation(lv_obj_t * page, bool en) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + ext->scroll_prop = en ? 1 : 0; +} + +/** + * Enable the edge flash effect. (Show an arc when the an edge is reached) + * @param page pointer to a Page + * @param en true or false to enable/disable end flash + */ +void lv_page_set_edge_flash(lv_obj_t * page, bool en) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + ext->edge_flash.enabled = en ? 1 : 0; +} + +/** + * Set a style of a page + * @param page pointer to a page object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_page_set_style(lv_obj_t * page, lv_page_style_t type, lv_style_t * style) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + + switch(type) { + case LV_PAGE_STYLE_BG: + lv_obj_set_style(page, style); + break; + case LV_PAGE_STYLE_BGO: + ext->bgo = style; + break; + case LV_PAGE_STYLE_PR: + ext->pr = style; + break; + case LV_PAGE_STYLE_SCRL: + lv_obj_set_style(ext->scrl, style); + break; + case LV_PAGE_STYLE_SB: + ext->sb.style = style; + lv_area_set_height(&ext->sb.hor_area, ext->sb.style->body.padding.inner); + lv_area_set_width(&ext->sb.ver_area, ext->sb.style->body.padding.inner); + lv_page_sb_refresh(page); + lv_obj_refresh_ext_size(page); + lv_obj_invalidate(page); + break; + case LV_PAGE_STYLE_EDGE_FLASH: + ext->edge_flash.style = style; + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the scrollable object of a page + * @param page pointer to a page object + * @return pointer to a container which is the scrollable part of the page + */ +lv_obj_t * lv_page_get_scrl(const lv_obj_t * page) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + + return ext->scrl; +} + +/** + * Get the press action of the page + * @param page pointer to a page object + * @return a function to call when the page is pressed + */ +lv_action_t lv_page_get_pr_action(lv_obj_t * page) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + return ext->pr_action; +} + +/** + * Get the release action of the page + * @param page pointer to a page object + * @return a function to call when the page is released + */ +lv_action_t lv_page_get_rel_action(lv_obj_t * page) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + return ext->rel_action; +} + +/** + * Set the scroll bar mode on a page + * @param page pointer to a page object + * @return the mode from 'lv_page_sb.mode_t' enum + */ +lv_sb_mode_t lv_page_get_sb_mode(const lv_obj_t * page) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + return ext->sb.mode; +} + +/** + * Get the the scrolling with arrows (LV_GROUP_KEY_LEFT/RIGHT/UP/DOWN) is enabled or not + * @param page pointer to a page object + * @return true: scrolling with arrows is enabled + */ +bool lv_page_get_arrow_scroll(const lv_obj_t * page) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + return ext->arrow_scroll ? true : false; +} + +/** + * Get the scroll propagation property + * @param page pointer to a Page + * @return true or false + */ +bool lv_page_get_scroll_propagation(lv_obj_t * page) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + return ext->scroll_prop == 0 ? false : true; +} + +/** + * Get the edge flash effect property. + * @param page pointer to a Page + * return true or false + */ +bool lv_page_get_edge_flash(lv_obj_t * page) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + return ext->edge_flash.enabled == 0 ? false : true; +} + +/** + * Get that width which can be set to the children to still not cause overflow (show scrollbars) + * @param page pointer to a page object + * @return the width which still fits into the page + */ +lv_coord_t lv_page_get_fit_width(lv_obj_t * page) +{ + lv_style_t * bg_style = lv_page_get_style(page, LV_PAGE_STYLE_BG); + lv_style_t * scrl_style = lv_page_get_style(page, LV_PAGE_STYLE_SCRL); + + return lv_obj_get_width(page) - 2 * (bg_style->body.padding.hor + scrl_style->body.padding.hor); +} + +/** + * Get that height which can be set to the children to still not cause overflow (show scrollbars) + * @param page pointer to a page object + * @return the height which still fits into the page + */ +lv_coord_t lv_page_get_fit_height(lv_obj_t * page) +{ + lv_style_t * bg_style = lv_page_get_style(page, LV_PAGE_STYLE_BG); + lv_style_t * scrl_style = lv_page_get_style(page, LV_PAGE_STYLE_SCRL); + + return lv_obj_get_height(page) - 2 * (bg_style->body.padding.ver + scrl_style->body.padding.ver); +} + +/** + * Get a style of a page + * @param page pointer to page object + * @param type which style should be get + * @return style pointer to a style + * */ +lv_style_t * lv_page_get_style(const lv_obj_t * page, lv_page_style_t type) +{ + lv_style_t * style = NULL; + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + + switch(type) { + case LV_PAGE_STYLE_BG: + style = lv_obj_get_style(page); + break; + case LV_PAGE_STYLE_BGO: + style = ext->bgo; + break; + case LV_PAGE_STYLE_PR: + style = ext->pr; + break; + case LV_PAGE_STYLE_SCRL: + style = lv_obj_get_style(ext->scrl); + break; + case LV_PAGE_STYLE_SB: + style = ext->sb.style; + break; + case LV_PAGE_STYLE_EDGE_FLASH: + style = ext->edge_flash.style; + break; + default: + style = NULL; + break; + } + + return style; +} + +/*===================== + * Other functions + *====================*/ + +/** + * Glue the object to the page. After it the page can be moved (dragged) with this object too. + * @param obj pointer to an object on a page + * @param glue true: enable glue, false: disable glue + */ +void lv_page_glue_obj(lv_obj_t * obj, bool glue) +{ + lv_obj_set_drag_parent(obj, glue); + lv_obj_set_drag(obj, glue); +} + +/** + * Focus on an object. It ensures that the object will be visible on the page. + * @param page pointer to a page object + * @param obj pointer to an object to focus (must be on the page) + * @param anim_time scroll animation time in milliseconds (0: no animation) + */ +void lv_page_focus(lv_obj_t * page, const lv_obj_t * obj, uint16_t anim_time) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + +#if USE_LV_ANIMATION == 0 + anim_time = 0; +#else + /* Be sure there is no position changing animation in progress + * because it can overide the current changes*/ + lv_anim_del(page, (lv_anim_fp_t)lv_obj_set_y); + lv_anim_del(page, (lv_anim_fp_t)lv_obj_set_pos); + lv_anim_del(ext->scrl, (lv_anim_fp_t)lv_obj_set_y); + lv_anim_del(ext->scrl, (lv_anim_fp_t)lv_obj_set_pos); +#endif + + lv_style_t * style = lv_page_get_style(page, LV_PAGE_STYLE_BG); + lv_style_t * style_scrl = lv_page_get_style(page, LV_PAGE_STYLE_SCRL); + + lv_coord_t obj_y = obj->coords.y1 - ext->scrl->coords.y1; + lv_coord_t obj_h = lv_obj_get_height(obj); + lv_coord_t scrlable_y = lv_obj_get_y(ext->scrl); + lv_coord_t page_h = lv_obj_get_height(page); + + lv_coord_t top_err = -(scrlable_y + obj_y); + lv_coord_t bot_err = scrlable_y + obj_y + obj_h - page_h; + + /*If obj is higher then the page focus where the "error" is smaller*/ + + /*Out of the page on the top*/ + if((obj_h <= page_h && top_err > 0) || + (obj_h > page_h && top_err < bot_err)) { + /*Calculate a new position and let some space above*/ + scrlable_y = -(obj_y - style_scrl->body.padding.ver - style->body.padding.ver); + scrlable_y += style_scrl->body.padding.ver; + } + /*Out of the page on the bottom*/ + else if((obj_h <= page_h && bot_err > 0) || + (obj_h > page_h && top_err >= bot_err)) { + /*Calculate a new position and let some space below*/ + scrlable_y = -(obj_y + style_scrl->body.padding.ver + style->body.padding.ver); + scrlable_y -= style_scrl->body.padding.ver; + scrlable_y += page_h - obj_h; + } else { + /*Already in focus*/ + return; + } + + if(anim_time == 0) { + lv_obj_set_y(ext->scrl, scrlable_y); +#if USE_LV_ANIMATION + } else { + lv_anim_t a; + a.act_time = 0; + a.start = lv_obj_get_y(ext->scrl); + a.end = scrlable_y; + a.time = anim_time; + a.end_cb = NULL; + a.playback = 0; + a.repeat = 0; + a.var = ext->scrl; + a.path = lv_anim_path_linear; + a.fp = (lv_anim_fp_t) lv_obj_set_y; + lv_anim_create(&a); +#endif + } +} + +/** + * Scroll the page horizontally + * @param page pointer to a page object + * @param dist the distance to scroll (< 0: scroll right; > 0 scroll left) + */ +void lv_page_scroll_hor(lv_obj_t * page, lv_coord_t dist) +{ + lv_obj_t * scrl = lv_page_get_scrl(page); + +#if USE_LV_ANIMATION + lv_anim_t a; + a.var = scrl; + a.start = lv_obj_get_x(scrl); + a.end = a.start + dist; + a.fp = (lv_anim_fp_t)lv_obj_set_x; + a.path = lv_anim_path_linear; + a.end_cb = NULL; + a.act_time = 0; + a.time = LV_PAGE_SCROLL_ANIM_TIME; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); +#else + lv_obj_set_x(scrl, lv_obj_get_x(scrl) + dist); +#endif +} + +/** + * Scroll the page vertically + * @param page pointer to a page object + * @param dist the distance to scroll (< 0: scroll down; > 0 scroll up) + */ +void lv_page_scroll_ver(lv_obj_t * page, lv_coord_t dist) +{ + lv_obj_t * scrl = lv_page_get_scrl(page); + +#if USE_LV_ANIMATION + lv_anim_t a; + a.var = scrl; + a.start = lv_obj_get_y(scrl); + a.end = a.start + dist; + a.fp = (lv_anim_fp_t)lv_obj_set_y; + a.path = lv_anim_path_linear; + a.end_cb = NULL; + a.act_time = 0; + a.time = LV_PAGE_SCROLL_ANIM_TIME; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); +#else + lv_obj_set_y(scrl, lv_obj_get_y(scrl) + dist); +#endif +} + +/** + * Not intended to use directly by the user but by other object types internally. + * Start an edge flash animation. Exactly one `ext->edge_flash.xxx_ip` should be set + * @param page + */ +void lv_page_start_edge_flash(lv_obj_t * page) +{ +#if USE_LV_ANIMATION + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + if(ext->edge_flash.enabled) { + lv_anim_t a; + a.var = page; + a.start = 0; + a.end = LV_PAGE_END_FLASH_SIZE; + a.fp = (lv_anim_fp_t)edge_flash_anim; + a.path = lv_anim_path_linear; + a.end_cb = edge_flash_anim_end; + a.act_time = 0; + a.time = LV_PAGE_END_ANIM_TIME; + a.playback = 1; + a.playback_pause = LV_PAGE_END_ANIM_WAIT_TIME; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); + } +#endif +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the pages + * @param page pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_page_design(lv_obj_t * page, const lv_area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + return ancestor_design(page, mask, mode); + } else if(mode == LV_DESIGN_DRAW_MAIN) { + /*Draw without border*/ + lv_style_t * style = lv_page_get_style(page, LV_PAGE_STYLE_BG); + lv_coord_t border_width_tmp = style->body.border.width; + style->body.border.width = 0; + lv_draw_rect(&page->coords, mask, style, lv_obj_get_opa_scale(page)); + style->body.border.width = border_width_tmp; + + } else if(mode == LV_DESIGN_DRAW_POST) { /*Draw the scroll bars finally*/ + + /*Draw only a border*/ + lv_style_t * style = lv_page_get_style(page, LV_PAGE_STYLE_BG); + lv_coord_t shadow_width_tmp = style->body.shadow.width; + uint8_t empty_tmp = style->body.empty; + style->body.shadow.width = 0; + style->body.empty = 1; + lv_draw_rect(&page->coords, mask, style, lv_obj_get_opa_scale(page)); + style->body.shadow.width = shadow_width_tmp; + style->body.empty = empty_tmp; + + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + + /*Draw the scrollbars*/ + lv_area_t sb_area; + if(ext->sb.hor_draw && (ext->sb.mode & LV_SB_MODE_HIDE) == 0) { + /*Convert the relative coordinates to absolute*/ + lv_area_copy(&sb_area, &ext->sb.hor_area); + sb_area.x1 += page->coords.x1; + sb_area.y1 += page->coords.y1; + sb_area.x2 += page->coords.x1; + sb_area.y2 += page->coords.y1; + lv_draw_rect(&sb_area, mask, ext->sb.style, lv_obj_get_opa_scale(page)); + } + + if(ext->sb.ver_draw && (ext->sb.mode & LV_SB_MODE_HIDE) == 0) { + /*Convert the relative coordinates to absolute*/ + lv_area_copy(&sb_area, &ext->sb.ver_area); + sb_area.x1 += page->coords.x1; + sb_area.y1 += page->coords.y1; + sb_area.x2 += page->coords.x1; + sb_area.y2 += page->coords.y1; + lv_draw_rect(&sb_area, mask, ext->sb.style, lv_obj_get_opa_scale(page)); + } + + + lv_coord_t page_w = lv_obj_get_width(page); + lv_coord_t page_h = lv_obj_get_height(page); + lv_area_t flash_area; + + if(ext->edge_flash.top_ip) { + flash_area.x1 = page->coords.x1 - page_w; + flash_area.x2 = page->coords.x2 + page_w; + flash_area.y1 = page->coords.y1 - 3 * page_w + ext->edge_flash.state; + flash_area.y2 = page->coords.y1 + ext->edge_flash.state; + } + else if(ext->edge_flash.bottom_ip) { + flash_area.x1 = page->coords.x1 - page_w; + flash_area.x2 = page->coords.x2 + page_w; + flash_area.y1 = page->coords.y2 - ext->edge_flash.state; + flash_area.y2 = page->coords.y2 + 3 * page_w - ext->edge_flash.state; + } + else if(ext->edge_flash.right_ip) { + flash_area.x1 = page->coords.x2 - ext->edge_flash.state; + flash_area.x2 = page->coords.x2 + 3 * page_h - ext->edge_flash.state; + flash_area.y1 = page->coords.y1 - page_h; + flash_area.y2 = page->coords.y2 + page_h; + } + else if(ext->edge_flash.left_ip) { + flash_area.x1 = page->coords.x1 - 3 * page_h + ext->edge_flash.state; + flash_area.x2 = page->coords.x1 + ext->edge_flash.state; + flash_area.y1 = page->coords.y1 - page_h; + flash_area.y2 = page->coords.y2 + page_h; + } + + if(ext->edge_flash.left_ip || ext->edge_flash.right_ip || ext->edge_flash.top_ip || ext->edge_flash.bottom_ip) { + lv_style_t flash_style; + lv_style_copy(&flash_style, ext->edge_flash.style); + flash_style.body.radius = LV_RADIUS_CIRCLE; + uint32_t opa = (flash_style.body.opa * ext->edge_flash.state) / LV_PAGE_END_FLASH_SIZE; + flash_style.body.opa = opa; + lv_draw_rect(&flash_area, mask, &flash_style, lv_obj_get_opa_scale(page)); + } + + } + + return true; +} + +/** + * Handle the drawing related tasks of the scrollable object + * @param scrl pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_scrl_design(lv_obj_t * scrl, const lv_area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + return ancestor_design(scrl, mask, mode); + } else if(mode == LV_DESIGN_DRAW_MAIN) { +#if USE_LV_GROUP + /* If the page is focused in a group and + * the background object is not visible (transparent or empty) + * then "activate" the style of the scrollable*/ + lv_style_t * style_scrl_ori = lv_obj_get_style(scrl); + lv_obj_t * page = lv_obj_get_parent(scrl); + lv_style_t * style_page = lv_obj_get_style(page); + lv_group_t * g = lv_obj_get_group(page); + if((style_page->body.empty || style_page->body.opa == LV_OPA_TRANSP) && style_page->body.border.width == 0) { /*Is the background visible?*/ + if(lv_group_get_focused(g) == page) { + lv_style_t * style_mod; + style_mod = lv_group_mod_style(g, style_scrl_ori); + scrl->style_p = style_mod; /*Temporally change the style to the activated */ + } + } +#endif + ancestor_design(scrl, mask, mode); + +#if USE_LV_GROUP + scrl->style_p = style_scrl_ori; /*Revert the style*/ +#endif + } else if(mode == LV_DESIGN_DRAW_POST) { + ancestor_design(scrl, mask, mode); + } + + return true; +} + +/** + * Signal function of the page + * @param page pointer to a page object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_page_signal(lv_obj_t * page, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(page, sign, param); + if(res != LV_RES_OK) return res; + + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + lv_style_t * style = lv_obj_get_style(page); + lv_obj_t * child; + if(sign == LV_SIGNAL_CHILD_CHG) { /*Automatically move children to the scrollable object*/ + child = lv_obj_get_child(page, NULL); + while(child != NULL) { + if(lv_obj_is_protected(child, LV_PROTECT_PARENT) == false) { + lv_obj_t * tmp = child; + child = lv_obj_get_child(page, child); /*Get the next child before move this*/ + lv_obj_set_parent(tmp, ext->scrl); + } else { + child = lv_obj_get_child(page, child); + } + } + } else if(sign == LV_SIGNAL_STYLE_CHG) { + /*If no hor_fit enabled set the scrollable's width to the page's width*/ + if(lv_cont_get_hor_fit(ext->scrl) == false) { + lv_obj_set_width(ext->scrl, lv_obj_get_width(page) - 2 * style->body.padding.hor); + } else { + ext->scrl->signal_func(ext->scrl, LV_SIGNAL_CORD_CHG, &ext->scrl->coords); + } + + /*The scrollbars are important only if they are visible now*/ + if(ext->sb.hor_draw || ext->sb.ver_draw) lv_page_sb_refresh(page); + + /*Refresh the ext. size because the scrollbars might be positioned out of the page*/ + lv_obj_refresh_ext_size(page); + } else if(sign == LV_SIGNAL_CORD_CHG) { + /*Refresh the scrollbar and notify the scrl if the size is changed*/ + if(ext->scrl != NULL && (lv_obj_get_width(page) != lv_area_get_width(param) || + lv_obj_get_height(page) != lv_area_get_height(param))) { + /*If no hor_fit enabled set the scrollable's width to the page's width*/ + if(lv_cont_get_hor_fit(ext->scrl) == false) { + lv_obj_set_width(ext->scrl, lv_obj_get_width(page) - 2 * style->body.padding.hor); + } + + ext->scrl->signal_func(ext->scrl, LV_SIGNAL_CORD_CHG, &ext->scrl->coords); + + /*The scrollbars are important only if they are visible now*/ + if(ext->sb.hor_draw || ext->sb.ver_draw) lv_page_sb_refresh(page); + } + } else if(sign == LV_SIGNAL_PRESSED) { + if(ext->pr_action != NULL) { + res = ext->pr_action(page); + } + } else if(sign == LV_SIGNAL_RELEASED) { + if(lv_indev_is_dragging(lv_indev_get_act()) == false) { + if(ext->rel_action != NULL) { + res = ext->rel_action(page); + } + } + } else if(sign == LV_SIGNAL_REFR_EXT_SIZE) { + /*Ensure ext. size for the scrollbars if they are out of the page*/ + if(page->ext_size < (-ext->sb.style->body.padding.hor)) page->ext_size = -ext->sb.style->body.padding.hor; + if(page->ext_size < (-ext->sb.style->body.padding.ver)) page->ext_size = -ext->sb.style->body.padding.ver; + } else if(sign == LV_SIGNAL_CONTROLL) { + uint32_t c = *((uint32_t *) param); + + if((c == LV_GROUP_KEY_DOWN) && ext->arrow_scroll) { + lv_page_scroll_ver(page, - lv_obj_get_height(page) / 4); + } else if((c == LV_GROUP_KEY_UP) && ext->arrow_scroll) { + lv_page_scroll_ver(page, lv_obj_get_height(page) / 4); + } else if((c == LV_GROUP_KEY_RIGHT) && ext->arrow_scroll) { + /*If the page can be scrolled horizontally because it's not wide enough then scroll it vertically*/ + if(lv_page_get_scrl_width(page) < lv_obj_get_width(page)) lv_page_scroll_ver(page, - lv_obj_get_height(page) / 4); + else lv_page_scroll_hor(page, - lv_obj_get_width(page) / 4); + } else if((c == LV_GROUP_KEY_LEFT) && ext->arrow_scroll) { + /*If the page can be scrolled horizontally because it's not wide enough then scroll it vertically*/ + if(lv_page_get_scrl_width(page) < lv_obj_get_width(page)) lv_page_scroll_ver(page, lv_obj_get_height(page) / 4); + else lv_page_scroll_hor(page, lv_obj_get_width(page) / 4); + } + } else if(sign == LV_SIGNAL_GET_EDITABLE) { + bool * editable = (bool *)param; + *editable = lv_page_get_arrow_scroll(page); + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_page"; + } + + return res; +} + +/** + * Signal function of the scrollable part of a page + * @param scrl pointer to the scrollable object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_page_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(scrl, sign, param); + if(res != LV_RES_OK) return res; + + lv_obj_t * page = lv_obj_get_parent(scrl); + lv_style_t * page_style = lv_obj_get_style(page); + lv_page_ext_t * page_ext = lv_obj_get_ext_attr(page); + + if(sign == LV_SIGNAL_CORD_CHG) { + /*Limit the position of the scrollable object to be always visible + * (Do not let its edge inner then its parent respective edge)*/ + lv_coord_t new_x = lv_obj_get_x(scrl); + lv_coord_t new_y = lv_obj_get_y(scrl); + bool refr_x = false; + bool refr_y = false; + lv_area_t page_coords; + lv_area_t scrl_coords; + lv_obj_get_coords(scrl, &scrl_coords); + lv_obj_get_coords(page, &page_coords); + + lv_area_t * ori_coords = (lv_area_t *) param; + lv_coord_t diff_x = scrl->coords.x1 - ori_coords->x1; + lv_coord_t diff_y = scrl->coords.y1 - ori_coords->y1; + lv_coord_t hpad = page_style->body.padding.hor; + lv_coord_t vpad = page_style->body.padding.ver; + lv_obj_t * page_parent = lv_obj_get_parent(page); + + lv_indev_t * indev = lv_indev_get_act(); + lv_point_t drag_vect; + lv_indev_get_vect(indev, &drag_vect); + + + /* Start the scroll propagation if there is drag vector on the indev, but the drag is not started yet + * and the scrollable is in a corner. It will enable the scroll propagation only when a new scroll begins and not + * when the scrollable is already being scrolled.*/ + if(page_ext->scroll_prop && page_ext->scroll_prop_ip == 0 && lv_indev_is_dragging(indev) == false) { + if(((drag_vect.y > 0 && scrl_coords.y1 == page_coords.y1 + vpad) || + (drag_vect.y < 0 && scrl_coords.y2 == page_coords.y2 - vpad)) && + ((drag_vect.x > 0 && scrl_coords.x1 == page_coords.x1 + hpad) || + (drag_vect.x < 0 && scrl_coords.x2 == page_coords.x2 - hpad))) { + + if(lv_obj_get_parent(page_parent) != NULL) { /*Do not propagate the scroll to a screen*/ + page_ext->scroll_prop_ip = 1; + } + } + } + + /*scrollable width smaller then page width? -> align to left*/ + if(lv_area_get_width(&scrl_coords) + 2 * hpad <= lv_area_get_width(&page_coords)) { + if(scrl_coords.x1 != page_coords.x1 + hpad) { + new_x = hpad; + refr_x = true; + } + } else { + /*If the scroll propagation is in progress revert the original coordinates (don't let the page scroll)*/ + if(page_ext->scroll_prop_ip) { + if(drag_vect.x == diff_x) { /*`scrl` is bouncing: drag pos. it somewhere and here it is reverted. Handle only the pos. because of drag*/ + new_x = ori_coords->x1 - page_coords.x1; + refr_x = true; + } + } + /*The edges of the scrollable can not be in the page (minus hpad) */ + else if(scrl_coords.x2 < page_coords.x2 - hpad) { + new_x = lv_area_get_width(&page_coords) - lv_area_get_width(&scrl_coords) - hpad; /* Right align */ + refr_x = true; + if(page_ext->edge_flash.enabled && + page_ext->edge_flash.left_ip == 0 && page_ext->edge_flash.right_ip == 0 && + page_ext->edge_flash.top_ip == 0 && page_ext->edge_flash.bottom_ip == 0) { + lv_page_start_edge_flash(page); + page_ext->edge_flash.right_ip = 1; + } + } + else if(scrl_coords.x1 > page_coords.x1 + hpad) { + new_x = hpad; /*Left align*/ + refr_x = true; + if(page_ext->edge_flash.enabled && + page_ext->edge_flash.left_ip == 0 && page_ext->edge_flash.right_ip == 0 && + page_ext->edge_flash.top_ip == 0 && page_ext->edge_flash.bottom_ip == 0) { + lv_page_start_edge_flash(page); + page_ext->edge_flash.left_ip = 1; + } + } + } + + /*scrollable height smaller then page height? -> align to left*/ + if(lv_area_get_height(&scrl_coords) + 2 * vpad <= lv_area_get_height(&page_coords)) { + if(scrl_coords.y1 != page_coords.y1 + vpad) { + new_y = vpad; + refr_y = true; + } + } else { + /*If the scroll propagation is in progress revert the original coordinates (don't let the page scroll)*/ + if(page_ext->scroll_prop_ip) { + if(drag_vect.y == diff_y) { /*`scrl` is bouncing: drag pos. it somewhere and here it is reverted. Handle only the pos. because of drag*/ + new_y = ori_coords->y1 - page_coords.y1; + refr_y = true; + } + } + /*The edges of the scrollable can not be in the page (minus vpad) */ + else if(scrl_coords.y2 < page_coords.y2 - vpad) { + new_y = lv_area_get_height(&page_coords) - lv_area_get_height(&scrl_coords) - vpad; /* Bottom align */ + refr_y = true; + if(page_ext->edge_flash.enabled && + page_ext->edge_flash.left_ip == 0 && page_ext->edge_flash.right_ip == 0 && + page_ext->edge_flash.top_ip == 0 && page_ext->edge_flash.bottom_ip == 0) { + lv_page_start_edge_flash(page); + page_ext->edge_flash.bottom_ip = 1; + } + } + else if(scrl_coords.y1 > page_coords.y1 + vpad) { + new_y = vpad; /*Top align*/ + refr_y = true; + if(page_ext->edge_flash.enabled && + page_ext->edge_flash.left_ip == 0 && page_ext->edge_flash.right_ip == 0 && + page_ext->edge_flash.top_ip == 0 && page_ext->edge_flash.bottom_ip == 0) { + lv_page_start_edge_flash(page); + page_ext->edge_flash.top_ip = 1; + } + } + } + + if(refr_x || refr_y) { + lv_obj_set_pos(scrl, new_x, new_y); + + if(page_ext->scroll_prop_ip) { + if(refr_y) lv_obj_set_y(page_parent, lv_obj_get_y(page_parent) + diff_y); + if(refr_x) lv_obj_set_x(page_parent, lv_obj_get_x(page_parent) + diff_x); + } + } + + lv_page_sb_refresh(page); + } + else if(sign == LV_SIGNAL_DRAG_END) { + + /*Scroll propagation is finished on drag end*/ + page_ext->scroll_prop_ip = 0; + + /*Hide scrollbars if required*/ + if(page_ext->sb.mode == LV_SB_MODE_DRAG) { + lv_area_t sb_area_tmp; + if(page_ext->sb.hor_draw) { + lv_area_copy(&sb_area_tmp, &page_ext->sb.hor_area); + sb_area_tmp.x1 += page->coords.x1; + sb_area_tmp.y1 += page->coords.y1; + sb_area_tmp.x2 += page->coords.x1; + sb_area_tmp.y2 += page->coords.y1; + lv_inv_area(&sb_area_tmp); + page_ext->sb.hor_draw = 0; + } + if(page_ext->sb.ver_draw) { + lv_area_copy(&sb_area_tmp, &page_ext->sb.ver_area); + sb_area_tmp.x1 += page->coords.x1; + sb_area_tmp.y1 += page->coords.y1; + sb_area_tmp.x2 += page->coords.x1; + sb_area_tmp.y2 += page->coords.y1; + lv_inv_area(&sb_area_tmp); + page_ext->sb.ver_draw = 0; + } + } + } else if(sign == LV_SIGNAL_PRESSED) { + if(page_ext->pr_action != NULL) { + res = page_ext->pr_action(page); + } + } else if(sign == LV_SIGNAL_RELEASED) { + if(lv_indev_is_dragging(lv_indev_get_act()) == false) { + if(page_ext->rel_action != NULL) { + res = page_ext->rel_action(page); + } + } + } + + return res; +} + + +/** + * Refresh the position and size of the scroll bars. + * @param page pointer to a page object + */ +static void lv_page_sb_refresh(lv_obj_t * page) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + lv_style_t * style = lv_obj_get_style(page); + lv_obj_t * scrl = ext->scrl; + lv_coord_t size_tmp; + lv_coord_t scrl_w = lv_obj_get_width(scrl); + lv_coord_t scrl_h = lv_obj_get_height(scrl); + lv_coord_t hpad = style->body.padding.hor; + lv_coord_t vpad = style->body.padding.ver; + lv_coord_t obj_w = lv_obj_get_width(page); + lv_coord_t obj_h = lv_obj_get_height(page); + + /*Always let 'scrollbar width' padding above, under, left and right to the scrollbars + * else: + * - horizontal and vertical scrollbars can overlap on the corners + * - if the page has radius the scrollbar can be out of the radius */ + lv_coord_t sb_hor_pad = LV_MATH_MAX(ext->sb.style->body.padding.inner, style->body.padding.hor); + lv_coord_t sb_ver_pad = LV_MATH_MAX(ext->sb.style->body.padding.inner, style->body.padding.ver); + + if(ext->sb.mode == LV_SB_MODE_OFF) return; + + if(ext->sb.mode == LV_SB_MODE_ON) { + ext->sb.hor_draw = 1; + ext->sb.ver_draw = 1; + } + + /*Invalidate the current (old) scrollbar areas*/ + lv_area_t sb_area_tmp; + if(ext->sb.hor_draw != 0) { + lv_area_copy(&sb_area_tmp, &ext->sb.hor_area); + sb_area_tmp.x1 += page->coords.x1; + sb_area_tmp.y1 += page->coords.y1; + sb_area_tmp.x2 += page->coords.x1; + sb_area_tmp.y2 += page->coords.y1; + lv_inv_area(&sb_area_tmp); + } + if(ext->sb.ver_draw != 0) { + lv_area_copy(&sb_area_tmp, &ext->sb.ver_area); + sb_area_tmp.x1 += page->coords.x1; + sb_area_tmp.y1 += page->coords.y1; + sb_area_tmp.x2 += page->coords.x1; + sb_area_tmp.y2 += page->coords.y1; + lv_inv_area(&sb_area_tmp); + } + + + if(ext->sb.mode == LV_SB_MODE_DRAG && lv_indev_is_dragging(lv_indev_get_act()) == false) { + ext->sb.hor_draw = 0; + ext->sb.ver_draw = 0; + return; + + } + + /*Horizontal scrollbar*/ + if(scrl_w <= obj_w - 2 * hpad) { /*Full sized scroll bar*/ + lv_area_set_width(&ext->sb.hor_area, obj_w - 2 * sb_hor_pad); + lv_area_set_pos(&ext->sb.hor_area, sb_hor_pad, obj_h - ext->sb.style->body.padding.inner - ext->sb.style->body.padding.ver); + if(ext->sb.mode == LV_SB_MODE_AUTO || ext->sb.mode == LV_SB_MODE_DRAG) ext->sb.hor_draw = 0; + } else { + size_tmp = (obj_w * (obj_w - (2 * sb_hor_pad))) / (scrl_w + 2 * hpad); + if(size_tmp < LV_PAGE_SB_MIN_SIZE) size_tmp = LV_PAGE_SB_MIN_SIZE; + lv_area_set_width(&ext->sb.hor_area, size_tmp); + + lv_area_set_pos(&ext->sb.hor_area, sb_hor_pad + + (-(lv_obj_get_x(scrl) - hpad) * (obj_w - size_tmp - 2 * sb_hor_pad)) / + (scrl_w + 2 * hpad - obj_w), + obj_h - ext->sb.style->body.padding.inner - ext->sb.style->body.padding.ver); + + if(ext->sb.mode == LV_SB_MODE_AUTO || ext->sb.mode == LV_SB_MODE_DRAG) ext->sb.hor_draw = 1; + } + + /*Vertical scrollbar*/ + if(scrl_h <= obj_h - 2 * vpad) { /*Full sized scroll bar*/ + lv_area_set_height(&ext->sb.ver_area, obj_h - 2 * sb_ver_pad); + lv_area_set_pos(&ext->sb.ver_area, obj_w - ext->sb.style->body.padding.inner - ext->sb.style->body.padding.hor, sb_ver_pad); + if(ext->sb.mode == LV_SB_MODE_AUTO || ext->sb.mode == LV_SB_MODE_DRAG) ext->sb.ver_draw = 0; + } else { + size_tmp = (obj_h * (obj_h - (2 * sb_ver_pad))) / (scrl_h + 2 * vpad); + if(size_tmp < LV_PAGE_SB_MIN_SIZE) size_tmp = LV_PAGE_SB_MIN_SIZE; + lv_area_set_height(&ext->sb.ver_area, size_tmp); + + lv_area_set_pos(&ext->sb.ver_area, obj_w - ext->sb.style->body.padding.inner - ext->sb.style->body.padding.hor, + sb_ver_pad + + (-(lv_obj_get_y(scrl) - vpad) * (obj_h - size_tmp - 2 * sb_ver_pad)) / + (scrl_h + 2 * vpad - obj_h)); + + if(ext->sb.mode == LV_SB_MODE_AUTO || ext->sb.mode == LV_SB_MODE_DRAG) ext->sb.ver_draw = 1; + } + + /*Invalidate the new scrollbar areas*/ + if(ext->sb.hor_draw != 0) { + lv_area_copy(&sb_area_tmp, &ext->sb.hor_area); + sb_area_tmp.x1 += page->coords.x1; + sb_area_tmp.y1 += page->coords.y1; + sb_area_tmp.x2 += page->coords.x1; + sb_area_tmp.y2 += page->coords.y1; + lv_inv_area(&sb_area_tmp); + } + if(ext->sb.ver_draw != 0) { + lv_area_copy(&sb_area_tmp, &ext->sb.ver_area); + sb_area_tmp.x1 += page->coords.x1; + sb_area_tmp.y1 += page->coords.y1; + sb_area_tmp.x2 += page->coords.x1; + sb_area_tmp.y2 += page->coords.y1; + lv_inv_area(&sb_area_tmp); + } +} + +#if USE_LV_ANIMATION +static void edge_flash_anim(void * page, int32_t v) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + ext->edge_flash.state = v; + lv_obj_invalidate(page); +} + +static void edge_flash_anim_end(void * page) +{ + lv_page_ext_t * ext = lv_obj_get_ext_attr(page); + ext->edge_flash.top_ip = 0; + ext->edge_flash.bottom_ip = 0; + ext->edge_flash.left_ip = 0; + ext->edge_flash.right_ip = 0; + lv_obj_invalidate(page); +} +#endif + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_page.h b/bdk/libs/lvgl/lv_objx/lv_page.h new file mode 100644 index 00000000..caed34e9 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_page.h @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_page.h + * + */ + +#ifndef LV_PAGE_H +#define LV_PAGE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_PAGE != 0 + +/*Testing of dependencies*/ +#if USE_LV_CONT == 0 +#error "lv_page: lv_cont is required. Enable it in lv_conf.h (USE_LV_CONT 1) " +#endif + +#include "lv_cont.h" +#include "../lv_core/lv_indev.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Scrollbar modes: shows when should the scrollbars be visible*/ +enum +{ + LV_SB_MODE_OFF = 0x0, /*Never show scrollbars*/ + LV_SB_MODE_ON = 0x1, /*Always show scrollbars*/ + LV_SB_MODE_DRAG = 0x2, /*Show scrollbars when page is being dragged*/ + LV_SB_MODE_AUTO = 0x3, /*Show scrollbars when the scrollable container is large enough to be scrolled*/ + LV_SB_MODE_HIDE = 0x4, /*Hide the scroll bar temporally*/ + LV_SB_MODE_UNHIDE = 0x5, /*Unhide the previously hidden scrollbar. Recover it's type too*/ +}; +typedef uint8_t lv_sb_mode_t; + +/*Data of page*/ +typedef struct +{ + lv_cont_ext_t bg; /*Ext. of ancestor*/ + /*New data for this type */ + lv_obj_t * scrl; /*The scrollable object on the background*/ + lv_style_t *bgo; /*The scrollable object on the background*/ + lv_style_t *pr; /*The scrollable object on the background*/ + lv_action_t rel_action; /*Function to call when the page is released*/ + lv_action_t pr_action; /*Function to call when the page is pressed*/ + struct { + lv_style_t *style; /*Style of scrollbars*/ + lv_area_t hor_area; /*Horizontal scrollbar area relative to the page. (Handled by the library) */ + lv_area_t ver_area; /*Vertical scrollbar area relative to the page (Handled by the library)*/ + uint8_t hor_draw :1; /*1: horizontal scrollbar is visible now (Handled by the library)*/ + uint8_t ver_draw :1; /*1: vertical scrollbar is visible now (Handled by the library)*/ + lv_sb_mode_t mode:3; /*Scrollbar visibility from 'lv_page_sb_mode_t'*/ + } sb; + struct { + uint16_t state; /*Store the current size of the edge flash effect*/ + lv_style_t *style; /*Style of edge flash effect (usually homogeneous circle)*/ + uint8_t enabled :1; /*1: Show a flash animation on the edge*/ + uint8_t top_ip :1; /*Used internally to show that top most position is reached (flash is In Progress)*/ + uint8_t bottom_ip :1; /*Used internally to show that bottom most position is reached (flash is In Progress)*/ + uint8_t right_ip :1; /*Used internally to show that right most position is reached (flash is In Progress)*/ + uint8_t left_ip :1; /*Used internally to show that left most position is reached (flash is In Progress)*/ + }edge_flash; + + uint8_t arrow_scroll :1; /*1: Enable scrolling with LV_GROUP_KEY_LEFT/RIGHT/UP/DOWN*/ + uint8_t scroll_prop :1; /*1: Propagate the scrolling the the parent if the edge is reached*/ + uint8_t scroll_prop_ip :1; /*1: Scroll propagation is in progress (used by the library)*/ +} lv_page_ext_t; + +enum { + LV_PAGE_STYLE_BG, + LV_PAGE_STYLE_BGO, + LV_PAGE_STYLE_PR, + LV_PAGE_STYLE_SCRL, + LV_PAGE_STYLE_SB, + LV_PAGE_STYLE_EDGE_FLASH, +}; +typedef uint8_t lv_page_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a page objects + * @param par pointer to an object, it will be the parent of the new page + * @param copy pointer to a page object, if not NULL then the new object will be copied from it + * @return pointer to the created page + */ +lv_obj_t * lv_page_create(lv_obj_t * par, const lv_obj_t * copy); + +/** + * Delete all children of the scrl object, without deleting scrl child. + * @param obj pointer to an object + */ +void lv_page_clean(lv_obj_t *obj); + +/** + * Get the press action of the page + * @param page pointer to a page object + * @return a function to call when the page is pressed + */ +lv_action_t lv_page_get_pr_action(lv_obj_t * page); + +/** + * Get the release action of the page + * @param page pointer to a page object + * @return a function to call when the page is released + */ +lv_action_t lv_page_get_rel_action(lv_obj_t * page); + +/** + * Get the scrollable object of a page + * @param page pointer to a page object + * @return pointer to a container which is the scrollable part of the page + */ +lv_obj_t * lv_page_get_scrl(const lv_obj_t * page); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a release action for the page + * @param page pointer to a page object + * @param rel_action a function to call when the page is released + */ +void lv_page_set_rel_action(lv_obj_t * page, lv_action_t rel_action); + +/** + * Set a press action for the page + * @param page pointer to a page object + * @param pr_action a function to call when the page is pressed + */ +void lv_page_set_pr_action(lv_obj_t * page, lv_action_t pr_action); + +/** + * Set the scroll bar mode on a page + * @param page pointer to a page object + * @param sb_mode the new mode from 'lv_page_sb.mode_t' enum + */ +void lv_page_set_sb_mode(lv_obj_t * page, lv_sb_mode_t sb_mode); + +/** + * Enable/Disable scrolling with arrows if the page is in group (arrows: LV_GROUP_KEY_LEFT/RIGHT/UP/DOWN) + * @param page pointer to a page object + * @param en true: enable scrolling with arrows + */ +void lv_page_set_arrow_scroll(lv_obj_t * page, bool en); + +/** + * Enable the scroll propagation feature. If enabled then the page will move its parent if there is no more space to scroll. + * @param page pointer to a Page + * @param en true or false to enable/disable scroll propagation + */ +void lv_page_set_scroll_propagation(lv_obj_t * page, bool en); + +/** + * Enable the edge flash effect. (Show an arc when the an edge is reached) + * @param page pointer to a Page + * @param en true or false to enable/disable end flash + */ +void lv_page_set_edge_flash(lv_obj_t * page, bool en); + +/** + * Set the fit attribute of the scrollable part of a page. + * It means it can set its size automatically to involve all children. + * (Can be set separately horizontally and vertically) + * @param page pointer to a page object + * @param hor_en true: enable horizontal fit + * @param ver_en true: enable vertical fit + */ +static inline void lv_page_set_scrl_fit(lv_obj_t *page, bool hor_en, bool ver_en) +{ + lv_cont_set_fit(lv_page_get_scrl(page), hor_en, ver_en); +} + +/** + * Set width of the scrollable part of a page + * @param page pointer to a page object + * @param w the new width of the scrollable (it ha no effect is horizontal fit is enabled) + */ +static inline void lv_page_set_scrl_width(lv_obj_t *page, lv_coord_t w) +{ + lv_obj_set_width(lv_page_get_scrl(page), w); +} + +/** + * Set height of the scrollable part of a page + * @param page pointer to a page object + * @param h the new height of the scrollable (it ha no effect is vertical fit is enabled) + */ +static inline void lv_page_set_scrl_height(lv_obj_t *page, lv_coord_t h) +{ + lv_obj_set_height(lv_page_get_scrl(page), h); + +} + +/** +* Set the layout of the scrollable part of the page +* @param page pointer to a page object +* @param layout a layout from 'lv_cont_layout_t' +*/ +static inline void lv_page_set_scrl_layout(lv_obj_t * page, lv_layout_t layout) +{ + lv_cont_set_layout(lv_page_get_scrl(page), layout); +} + +/** + * Set a style of a page + * @param page pointer to a page object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_page_set_style(lv_obj_t *page, lv_page_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Set the scroll bar mode on a page + * @param page pointer to a page object + * @return the mode from 'lv_page_sb.mode_t' enum + */ +lv_sb_mode_t lv_page_get_sb_mode(const lv_obj_t * page); + + +/** + * Get the the scrolling with arrows (LV_GROUP_KEY_LEFT/RIGHT/UP/DOWN) is enabled or not + * @param page pointer to a page object + * @return true: scrolling with arrows is enabled + */ +bool lv_page_get_arrow_scroll(const lv_obj_t * page); + +/** + * Get the scroll propagation property + * @param page pointer to a Page + * @return true or false + */ +bool lv_page_get_scroll_propagation(lv_obj_t * page); + +/** + * Get the edge flash effect property. + * @param page pointer to a Page + * return true or false + */ +bool lv_page_get_edge_flash(lv_obj_t * page); + +/** + * Get that width which can be set to the children to still not cause overflow (show scrollbars) + * @param page pointer to a page object + * @return the width which still fits into the page + */ +lv_coord_t lv_page_get_fit_width(lv_obj_t * page); + +/** + * Get that height which can be set to the children to still not cause overflow (show scrollbars) + * @param page pointer to a page object + * @return the height which still fits into the page + */ +lv_coord_t lv_page_get_fit_height(lv_obj_t * page); + +/** + * Get width of the scrollable part of a page + * @param page pointer to a page object + * @return the width of the scrollable + */ +static inline lv_coord_t lv_page_get_scrl_width(const lv_obj_t *page) +{ + return lv_obj_get_width(lv_page_get_scrl(page)); +} + +/** + * Get height of the scrollable part of a page + * @param page pointer to a page object + * @return the height of the scrollable + */ +static inline lv_coord_t lv_page_get_scrl_height(const lv_obj_t *page) +{ + return lv_obj_get_height(lv_page_get_scrl(page)); +} + +/** +* Get the layout of the scrollable part of a page +* @param page pointer to page object +* @return the layout from 'lv_cont_layout_t' +*/ +static inline lv_layout_t lv_page_get_scrl_layout(const lv_obj_t * page) +{ + return lv_cont_get_layout(lv_page_get_scrl(page)); +} + +/** +* Get horizontal fit attribute of the scrollable part of a page +* @param page pointer to a page object +* @return true: horizontal fit is enabled; false: disabled +*/ +static inline bool lv_page_get_scrl_hor_fit(const lv_obj_t * page) +{ + return lv_cont_get_hor_fit(lv_page_get_scrl(page)); +} + +/** +* Get vertical fit attribute of the scrollable part of a page +* @param page pointer to a page object +* @return true: vertical fit is enabled; false: disabled +*/ +static inline bool lv_page_get_scrl_fit_ver(const lv_obj_t * page) +{ + return lv_cont_get_ver_fit(lv_page_get_scrl(page)); +} + +/** + * Get a style of a page + * @param page pointer to page object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_page_get_style(const lv_obj_t *page, lv_page_style_t type); + +/*===================== + * Other functions + *====================*/ + +/** + * Glue the object to the page. After it the page can be moved (dragged) with this object too. + * @param obj pointer to an object on a page + * @param glue true: enable glue, false: disable glue + */ +void lv_page_glue_obj(lv_obj_t * obj, bool glue); + +/** + * Focus on an object. It ensures that the object will be visible on the page. + * @param page pointer to a page object + * @param obj pointer to an object to focus (must be on the page) + * @param anim_time scroll animation time in milliseconds (0: no animation) + */ +void lv_page_focus(lv_obj_t * page, const lv_obj_t * obj, uint16_t anim_time); + +/** + * Scroll the page horizontally + * @param page pointer to a page object + * @param dist the distance to scroll (< 0: scroll left; > 0 scroll right) + */ +void lv_page_scroll_hor(lv_obj_t * page, lv_coord_t dist); + +/** + * Scroll the page vertically + * @param page pointer to a page object + * @param dist the distance to scroll (< 0: scroll down; > 0 scroll up) + */ +void lv_page_scroll_ver(lv_obj_t * page, lv_coord_t dist); + +/** + * Not intended to use directly by the user but by other object types internally. + * Start an edge flash animation. Exactly one `ext->edge_flash.xxx_ip` should be set + * @param page + */ +void lv_page_start_edge_flash(lv_obj_t * page); +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_PAGE*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_PAGE_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_preload.c b/bdk/libs/lvgl/lv_objx/lv_preload.c new file mode 100644 index 00000000..1700a4da --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_preload.c @@ -0,0 +1,411 @@ +/** + * @file lv_preload.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_preload.h" +#if USE_LV_PRELOAD != 0 + +#include "../lv_misc/lv_math.h" +#include "../lv_draw/lv_draw_rect.h" +#include "../lv_draw/lv_draw_arc.h" +#include "../lv_themes/lv_theme.h" + +/********************* + * DEFINES + *********************/ +#ifndef LV_PRELOAD_DEF_ARC_LENGTH +# define LV_PRELOAD_DEF_ARC_LENGTH 60 /*[deg]*/ +#endif + +#ifndef LV_PRELOAD_DEF_SPIN_TIME +# define LV_PRELOAD_DEF_SPIN_TIME 1000 /*[ms]*/ +#endif + +#ifndef LV_PRELOAD_DEF_ANIM +# define LV_PRELOAD_DEF_ANIM LV_PRELOAD_TYPE_SPINNING_ARC /*animation type*/ +#endif + + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_preload_design(lv_obj_t * preload, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_preload_signal(lv_obj_t * preload, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_design_func_t ancestor_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a pre loader object + * @param par pointer to an object, it will be the parent of the new pre loader + * @param copy pointer to a pre loader object, if not NULL then the new object will be copied from it + * @return pointer to the created pre loader + */ +lv_obj_t * lv_preload_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("preload create started"); + + /*Create the ancestor of pre loader*/ + lv_obj_t * new_preload = lv_arc_create(par, copy); + lv_mem_assert(new_preload); + if(new_preload == NULL) return NULL; + + /*Allocate the pre loader type specific extended data*/ + lv_preload_ext_t * ext = lv_obj_allocate_ext_attr(new_preload, sizeof(lv_preload_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_preload); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_preload); + + /*Initialize the allocated 'ext' */ + ext->arc_length = LV_PRELOAD_DEF_ARC_LENGTH; + ext->anim_type = LV_PRELOAD_DEF_ANIM; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_preload, lv_preload_signal); + lv_obj_set_design_func(new_preload, lv_preload_design); + + + /*Init the new pre loader pre loader*/ + if(copy == NULL) { + lv_obj_set_size(new_preload, LV_DPI / 2, LV_DPI / 2); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_preload_set_style(new_preload, LV_PRELOAD_STYLE_MAIN, th->preload); + } else { + lv_obj_set_style(new_preload, &lv_style_pretty_color); + } + + ext->time = LV_PRELOAD_DEF_SPIN_TIME; + + } + /*Copy an existing pre loader*/ + else { + lv_preload_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->arc_length = copy_ext->arc_length; + ext->time = copy_ext->time; + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_preload); + } + + lv_preload_set_animation_type(new_preload, ext->anim_type); + + + LV_LOG_INFO("preload created"); + + return new_preload; +} + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Set the length of the spinning arc in degrees + * @param preload pointer to a preload object + * @param deg length of the arc + */ +void lv_preload_set_arc_length(lv_obj_t * preload, uint16_t deg) +{ + lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload); + + ext->arc_length = deg; +} + +/** + * Set the spin time of the arc + * @param preload pointer to a preload object + * @param time time of one round in milliseconds + */ +void lv_preload_set_spin_time(lv_obj_t * preload, uint16_t time) +{ + lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload); + + ext->time = time; + lv_preload_set_animation_type(preload, ext->anim_type); +} +/*===================== + * Setter functions + *====================*/ + +/** + * Set a style of a pre loader. + * @param preload pointer to pre loader object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_preload_set_style(lv_obj_t * preload, lv_preload_style_t type, lv_style_t * style) +{ + switch(type) { + case LV_PRELOAD_STYLE_MAIN: + lv_arc_set_style(preload, LV_ARC_STYLE_MAIN, style); + break; + } +} + +/** + * Set the animation type of a preloadeer. + * @param preload pointer to pre loader object + * @param type animation type of the preload + * */ +void lv_preload_set_animation_type(lv_obj_t * preload, lv_preloader_type_t type) +{ +#if USE_LV_ANIMATION + lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload); + + /*delete previous animation*/ + //lv_anim_del(preload, NULL); + switch(type) + { + case LV_PRELOAD_TYPE_FILLSPIN_ARC: + { + ext->anim_type = LV_PRELOAD_TYPE_FILLSPIN_ARC; + lv_anim_t a; + a.var = preload; + a.start = 0; + a.end = 360; + a.fp = (lv_anim_fp_t)lv_preload_spinner_animation; + a.path = lv_anim_path_ease_in_out; + a.end_cb = NULL; + a.act_time = 0; + a.time = ext->time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 1; + a.repeat_pause = 0; + lv_anim_create(&a); + + lv_anim_t b; + b.var = preload; + b.start = ext->arc_length; + b.end = 360 - ext->arc_length; + b.fp = (lv_anim_fp_t)lv_preload_set_arc_length; + b.path = lv_anim_path_ease_in_out; + b.end_cb = NULL; + b.act_time = 0; + b.time = ext->time; + b.playback = 1; + b.playback_pause = 0; + b.repeat = 1; + b.repeat_pause = 0; + lv_anim_create(&b); + break; + } + case LV_PRELOAD_TYPE_SPINNING_ARC: + default: + { + ext->anim_type = LV_PRELOAD_TYPE_SPINNING_ARC; + lv_anim_t a; + a.var = preload; + a.start = 0; + a.end = 360; + a.fp = (lv_anim_fp_t)lv_preload_spinner_animation; + a.path = lv_anim_path_ease_in_out; + a.end_cb = NULL; + a.act_time = 0; + a.time = ext->time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 1; + a.repeat_pause = 0; + lv_anim_create(&a); + break; + } + } + +#endif //USE_LV_ANIMATION +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the arc length [degree] of the a pre loader + * @param preload pointer to a pre loader object + */ +uint16_t lv_preload_get_arc_length(const lv_obj_t * preload) +{ + lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload); + return ext->arc_length; + +} + +/** + * Get the spin time of the arc + * @param preload pointer to a pre loader object [milliseconds] + */ +uint16_t lv_preload_get_spin_time(const lv_obj_t * preload) +{ + lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload); + return ext->time; +} + +/** + * Get style of a pre loader. + * @param preload pointer to pre loader object + * @param type which style should be get + * @return style pointer to the style + * */ +lv_style_t * lv_preload_get_style(const lv_obj_t * preload, lv_preload_style_t type) +{ + lv_style_t * style = NULL; + + switch(type) { + case LV_PRELOAD_STYLE_MAIN: + style = lv_arc_get_style(preload, LV_ARC_STYLE_MAIN); + break; + default: + style = NULL; + break; + } + + return style; +} + +/** + * Get the animation type of a preloadeer. + * @param preload pointer to pre loader object + * @return animation type + * */ +lv_preloader_type_t lv_preload_get_animation_type(lv_obj_t * preload) +{ + lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload); + return ext->anim_type; +} + +/*===================== + * Other functions + *====================*/ + +/** + * Automatically in an animation to rotate the arc of spinner. + * @param ptr pointer to preloader + * @param val the current desired value [0..360] + */ +void lv_preload_spinner_animation(void * ptr, int32_t val) +{ + lv_obj_t * preload = ptr; + lv_preload_ext_t * ext = lv_obj_get_ext_attr(preload); + int16_t angle_start = val - ext->arc_length / 2 + 180; + int16_t angle_end = angle_start + ext->arc_length; + + angle_start = angle_start % 360; + angle_end = angle_end % 360; + + lv_arc_set_angles(preload, angle_start, angle_end); + +} + + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the pre loaders + * @param preload pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_preload_design(lv_obj_t * preload, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + + /*Draw a circle as background*/ + lv_style_t * style = lv_arc_get_style(preload, LV_ARC_STYLE_MAIN); + if(style->body.border.width > 0) { + lv_coord_t r = (LV_MATH_MIN(lv_obj_get_width(preload), lv_obj_get_height(preload))) / 2; + r -= LV_MATH_MIN(style->body.padding.hor, style->body.padding.ver); + + lv_coord_t x = preload->coords.x1 + lv_obj_get_width(preload) / 2; + lv_coord_t y = preload->coords.y1 + lv_obj_get_height(preload) / 2; + + lv_style_t bg_style; + lv_style_copy(&bg_style, &lv_style_plain); + bg_style.body.empty = 1; + bg_style.body.radius = LV_RADIUS_CIRCLE; + bg_style.body.border.color = style->body.border.color; + bg_style.body.border.width = style->body.border.width; + + lv_area_t bg_area; + bg_area.x1 = x - r; + bg_area.y1 = y - r; + bg_area.x2 = x + r; + bg_area.y2 = y + r; + + lv_draw_rect(&bg_area, mask, &bg_style, lv_obj_get_opa_scale(preload)); + } + /*Draw the arc above the background circle */ + ancestor_design(preload, mask, mode); + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + + } + + return true; +} + +/** + * Signal function of the pre loader + * @param preload pointer to a pre loader object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_preload_signal(lv_obj_t * preload, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(preload, sign, param); + if(res != LV_RES_OK) return res; + + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_preload"; + } + + return res; +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_preload.h b/bdk/libs/lvgl/lv_objx/lv_preload.h new file mode 100644 index 00000000..4f12f022 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_preload.h @@ -0,0 +1,168 @@ +/** + * @file lv_preload.h + * + */ + +#ifndef LV_PRELOAD_H +#define LV_PRELOAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_PRELOAD != 0 + +/*Testing of dependencies*/ +#if USE_LV_ARC == 0 +#error "lv_preload: lv_arc is required. Enable it in lv_conf.h (USE_LV_ARC 1) " +#endif + +#if USE_LV_ANIMATION == 0 +#error "lv_preload: animations are required. Enable it in lv_conf.h (USE_LV_ANIMATION 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_arc.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +enum { + LV_PRELOAD_TYPE_SPINNING_ARC, + LV_PRELOAD_TYPE_FILLSPIN_ARC, +}; +typedef uint8_t lv_preloader_type_t; + +/*Data of pre loader*/ +typedef struct { + lv_arc_ext_t arc; /*Ext. of ancestor*/ + /*New data for this type */ + uint16_t arc_length; /*Length of the spinning indicator in degree*/ + uint16_t time; /*Time of one round*/ + lv_preloader_type_t anim_type; /*Type of the arc animation*/ +} lv_preload_ext_t; + + +/*Styles*/ +enum { + LV_PRELOAD_STYLE_MAIN, +}; +typedef uint8_t lv_preload_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a pre loader objects + * @param par pointer to an object, it will be the parent of the new pre loader + * @param copy pointer to a pre loader object, if not NULL then the new object will be copied from it + * @return pointer to the created pre loader + */ +lv_obj_t * lv_preload_create(lv_obj_t * par, const lv_obj_t * copy); + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Set the length of the spinning arc in degrees + * @param preload pointer to a preload object + * @param deg length of the arc + */ +void lv_preload_set_arc_length(lv_obj_t * preload, uint16_t deg); + +/** + * Set the spin time of the arc + * @param preload pointer to a preload object + * @param time time of one round in milliseconds + */ +void lv_preload_set_spin_time(lv_obj_t * preload, uint16_t time); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a style of a pre loader. + * @param preload pointer to pre loader object + * @param type which style should be set + * @param style pointer to a style + * */ +void lv_preload_set_style(lv_obj_t * preload, lv_preload_style_t type, lv_style_t *style); + +/** + * Set the animation type of a preloadeer. + * @param preload pointer to pre loader object + * @param type animation type of the preload + * */ +void lv_preload_set_animation_type(lv_obj_t * preload, lv_preloader_type_t type); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the arc length [degree] of the a pre loader + * @param preload pointer to a pre loader object + */ +uint16_t lv_preload_get_arc_length(const lv_obj_t * preload); + +/** + * Get the spin time of the arc + * @param preload pointer to a pre loader object [milliseconds] + */ +uint16_t lv_preload_get_spin_time(const lv_obj_t * preload); + +/** + * Get style of a pre loader. + * @param preload pointer to pre loader object + * @param type which style should be get + * @return style pointer to the style + * */ +lv_style_t * lv_preload_get_style(const lv_obj_t * preload, lv_preload_style_t type); + +/** + * Get the animation type of a preloadeer. + * @param preload pointer to pre loader object + * @return animation type + * */ +lv_preloader_type_t lv_preload_get_animation_type(lv_obj_t * preload); + +/*===================== + * Other functions + *====================*/ + +/** + * Get style of a pre loader. + * @param preload pointer to pre loader object + * @param type which style should be get + * @return style pointer to the style + * */ +void lv_preload_spinner_animation(void * ptr, int32_t val); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_PRELOAD*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_PRELOAD_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_roller.c b/bdk/libs/lvgl/lv_objx/lv_roller.c new file mode 100644 index 00000000..28c8b801 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_roller.c @@ -0,0 +1,582 @@ +/** + * @file lv_roller.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_roller.h" +#if USE_LV_ROLLER != 0 + +#include "../lv_draw/lv_draw.h" +#include "../lv_core/lv_group.h" +#include "../lv_themes/lv_theme.h" + +/********************* + * DEFINES + *********************/ +#if USE_LV_ANIMATION +# ifndef LV_ROLLER_ANIM_TIME +# define LV_ROLLER_ANIM_TIME 200 /*ms*/ +# endif +#else +# undef LV_ROLLER_ANIM_TIME +# define LV_ROLLER_ANIM_TIME 0 /*No animation*/ +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_roller_design(lv_obj_t * roller, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_roller_scrl_signal(lv_obj_t * roller_scrl, lv_signal_t sign, void * param); +static lv_res_t lv_roller_signal(lv_obj_t * roller, lv_signal_t sign, void * param); +static void refr_position(lv_obj_t * roller, bool anim_en); +static void draw_bg(lv_obj_t * roller, const lv_area_t * mask); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_signal_func_t ancestor_scrl_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a roller object + * @param par pointer to an object, it will be the parent of the new roller + * @param copy pointer to a roller object, if not NULL then the new object will be copied from it + * @return pointer to the created roller + */ +lv_obj_t * lv_roller_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("roller create started"); + + /*Create the ancestor of roller*/ + lv_obj_t * new_roller = lv_ddlist_create(par, copy); + lv_mem_assert(new_roller); + if(new_roller == NULL) return NULL; + + if(ancestor_scrl_signal == NULL) ancestor_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(new_roller)); + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_roller); + + /*Allocate the roller type specific extended data*/ + lv_roller_ext_t * ext = lv_obj_allocate_ext_attr(new_roller, sizeof(lv_roller_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + ext->ddlist.draw_arrow = 0; /*Do not draw arrow by default*/ + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_roller, lv_roller_signal); + lv_obj_set_design_func(new_roller, lv_roller_design); + + /*Init the new roller roller*/ + if(copy == NULL) { + lv_obj_t * scrl = lv_page_get_scrl(new_roller); + lv_obj_set_drag(scrl, true); /*In ddlist is might be disabled*/ + lv_page_set_rel_action(new_roller, NULL); /*Roller don't uses it (like ddlist)*/ + lv_page_set_scrl_fit(new_roller, true, false); /*Height is specified directly*/ + lv_ddlist_open(new_roller, false); + lv_ddlist_set_anim_time(new_roller, LV_ROLLER_ANIM_TIME); + lv_roller_set_visible_row_count(new_roller, 3); + lv_label_set_align(ext->ddlist.label, LV_LABEL_ALIGN_CENTER); + + lv_obj_set_signal_func(scrl, lv_roller_scrl_signal); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_roller_set_style(new_roller, LV_ROLLER_STYLE_BG, th->roller.bg); + lv_roller_set_style(new_roller, LV_ROLLER_STYLE_SEL, th->roller.sel); + } else { + /*Let the ddlist's style*/ + lv_obj_refresh_style(new_roller); /*To set scrollable size automatically*/ + } + } + /*Copy an existing roller*/ + else { + lv_obj_t * scrl = lv_page_get_scrl(new_roller); + lv_ddlist_open(new_roller, false); + lv_obj_set_signal_func(scrl, lv_roller_scrl_signal); + + lv_obj_refresh_style(new_roller); /*Refresh the style with new signal function*/ + } + + + LV_LOG_INFO("roller created"); + + + return new_roller; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the align of the roller's options (left or center) + * @param roller - pointer to a roller object + * @param align - one of lv_label_align_t values (left, right, center) + */ +void lv_roller_set_align(lv_obj_t * roller, lv_label_align_t align) +{ + lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller); + lv_mem_assert(ext); + if(ext->ddlist.label == NULL) return; /*Probably the roller is being deleted if the label is NULL.*/ + lv_label_set_align(ext->ddlist.label, align); +} + +/** + * Set the selected option + * @param roller pointer to a roller object + * @param sel_opt id of the selected option (0 ... number of option - 1); + * @param anim_en true: set with animation; false set immediately + */ +void lv_roller_set_selected(lv_obj_t * roller, uint16_t sel_opt, bool anim_en) +{ +#if USE_LV_ANIMATION == 0 + anim_en = false; +#endif + + if(lv_roller_get_selected(roller) == sel_opt) return; + + lv_ddlist_set_selected(roller, sel_opt); + refr_position(roller, anim_en); +} + +/** + * Set the height to show the given number of rows (options) + * @param roller pointer to a roller object + * @param row_cnt number of desired visible rows + */ +void lv_roller_set_visible_row_count(lv_obj_t * roller, uint8_t row_cnt) +{ + lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller); + lv_style_t * style_label = lv_obj_get_style(ext->ddlist.label); + uint8_t n_line_space = (row_cnt > 1) ? row_cnt - 1 : 1; + lv_ddlist_set_fix_height(roller, lv_font_get_height(style_label->text.font) * row_cnt + style_label->text.line_space * n_line_space); +} + +/** + * Set a style of a roller + * @param roller pointer to a roller object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_roller_set_style(lv_obj_t * roller, lv_roller_style_t type, lv_style_t * style) +{ + switch(type) { + case LV_ROLLER_STYLE_BG: + lv_obj_set_style(roller, style); + break; + case LV_ROLLER_STYLE_SEL: + lv_ddlist_set_style(roller, LV_DDLIST_STYLE_SEL, style); + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the align attribute. Default alignment after _create is LV_LABEL_ALIGN_CENTER + * @param roller pointer to a roller object + * @return LV_LABEL_ALIGN_LEFT, LV_LABEL_ALIGN_RIGHT or LV_LABEL_ALIGN_CENTER + */ +lv_label_align_t lv_roller_get_align(const lv_obj_t * roller) +{ + lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller); + lv_mem_assert(ext); + lv_mem_assert(ext->ddlist.label); + return lv_label_get_align(ext->ddlist.label); +} + +/** + * Get the auto width set attribute + * @param roller pointer to a roller object + * @return true: auto size enabled; false: manual width settings enabled + */ +bool lv_roller_get_hor_fit(const lv_obj_t * roller) +{ + return lv_page_get_scrl_hor_fit(roller); +} + +/** + * Get a style of a roller + * @param roller pointer to a roller object + * @param type which style should be get + * @return style pointer to a style + * */ +lv_style_t * lv_roller_get_style(const lv_obj_t * roller, lv_roller_style_t type) +{ + switch(type) { + case LV_ROLLER_STYLE_BG: + return lv_obj_get_style(roller); + case LV_ROLLER_STYLE_SEL: + return lv_ddlist_get_style(roller, LV_DDLIST_STYLE_SEL); + default: + return NULL; + } + + /*To avoid warning*/ + return NULL; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the rollers + * @param roller pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_roller_design(lv_obj_t * roller, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + draw_bg(roller, mask); + + lv_style_t * style = lv_roller_get_style(roller, LV_ROLLER_STYLE_BG); + lv_opa_t opa_scale = lv_obj_get_opa_scale(roller); + const lv_font_t * font = style->text.font; + lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller); + lv_coord_t font_h = lv_font_get_height(font); + lv_area_t rect_area; + rect_area.y1 = roller->coords.y1 + lv_obj_get_height(roller) / 2 - font_h / 2 - style->text.line_space / 2; + if((font_h & 0x1) && (style->text.line_space & 0x1)) rect_area.y1 --; /*Compensate the two rounding error*/ + rect_area.y2 = rect_area.y1 + font_h + style->text.line_space - 1; + rect_area.x1 = roller->coords.x1; + rect_area.x2 = roller->coords.x2; + + lv_draw_rect(&rect_area, mask, ext->ddlist.sel_style, opa_scale); + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + lv_style_t * style = lv_roller_get_style(roller, LV_ROLLER_STYLE_BG); + lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller); + const lv_font_t * font = style->text.font; + lv_coord_t font_h = lv_font_get_height(font); + lv_opa_t opa_scale = lv_obj_get_opa_scale(roller); + + /*Redraw the text on the selected area with a different color*/ + lv_area_t rect_area; + rect_area.y1 = roller->coords.y1 + lv_obj_get_height(roller) / 2 - font_h / 2 - style->text.line_space / 2; + if((font_h & 0x1) && (style->text.line_space & 0x1)) rect_area.y1 --; /*Compensate the two rounding error*/ + rect_area.y2 = rect_area.y1 + font_h + style->text.line_space - 1; + rect_area.x1 = roller->coords.x1; + rect_area.x2 = roller->coords.x2; + lv_area_t mask_sel; + bool area_ok; + area_ok = lv_area_intersect(&mask_sel, mask, &rect_area); + if(area_ok) { + lv_style_t * sel_style = lv_roller_get_style(roller, LV_ROLLER_STYLE_SEL); + lv_style_t new_style; + lv_txt_flag_t txt_align = LV_TXT_FLAG_NONE; + + { + lv_label_align_t label_align = lv_label_get_align(ext->ddlist.label); + + if(LV_LABEL_ALIGN_CENTER == label_align) { + txt_align |= LV_TXT_FLAG_CENTER; + } else if(LV_LABEL_ALIGN_RIGHT == label_align) { + txt_align |= LV_TXT_FLAG_RIGHT; + } + } + + lv_style_copy(&new_style, style); + new_style.text.color = sel_style->text.color; + new_style.text.opa = sel_style->text.opa; + lv_draw_label(&ext->ddlist.label->coords, &mask_sel, &new_style, opa_scale, + lv_label_get_text(ext->ddlist.label), txt_align, NULL); + } + } + + return true; +} + +/** + * Signal function of the roller + * @param roller pointer to a roller object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_roller_signal(lv_obj_t * roller, lv_signal_t sign, void * param) +{ + lv_res_t res = LV_RES_OK; + + /*Don't let the drop down list to handle the control signals. It works differently*/ + if(sign != LV_SIGNAL_CONTROLL && sign != LV_SIGNAL_FOCUS && sign != LV_SIGNAL_DEFOCUS) { + /* Include the ancient signal function */ + res = ancestor_signal(roller, sign, param); + if(res != LV_RES_OK) return res; + } + + lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller); + lv_align_t obj_align = LV_ALIGN_IN_LEFT_MID; + if(ext->ddlist.label) { + lv_label_align_t label_align = lv_label_get_align(ext->ddlist.label); + if(LV_LABEL_ALIGN_CENTER == label_align) obj_align = LV_ALIGN_CENTER; + else if(LV_LABEL_ALIGN_RIGHT == label_align) obj_align = LV_ALIGN_IN_RIGHT_MID; + } + + if(sign == LV_SIGNAL_STYLE_CHG) { + lv_obj_set_height(lv_page_get_scrl(roller), + lv_obj_get_height(ext->ddlist.label) + lv_obj_get_height(roller)); + lv_obj_align(ext->ddlist.label, NULL, obj_align, 0, 0); + lv_ddlist_set_selected(roller, ext->ddlist.sel_opt_id); + refr_position(roller, false); + } else if(sign == LV_SIGNAL_CORD_CHG) { + + if(lv_obj_get_width(roller) != lv_area_get_width(param) || + lv_obj_get_height(roller) != lv_area_get_height(param)) { + + lv_ddlist_set_fix_height(roller, lv_obj_get_height(roller)); + lv_obj_set_height(lv_page_get_scrl(roller), + lv_obj_get_height(ext->ddlist.label) + lv_obj_get_height(roller)); + + lv_obj_align(ext->ddlist.label, NULL, obj_align, 0, 0); + lv_ddlist_set_selected(roller, ext->ddlist.sel_opt_id); + refr_position(roller, false); + } + } else if(sign == LV_SIGNAL_FOCUS) { +#if USE_LV_GROUP + lv_group_t * g = lv_obj_get_group(roller); + bool editing = lv_group_get_editing(g); + lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + + /*Encoders need special handling*/ + if(indev_type == LV_INDEV_TYPE_ENCODER) { + /*In navigate mode revert the original value*/ + if(!editing) { + if(ext->ddlist.sel_opt_id != ext->ddlist.sel_opt_id_ori) { + ext->ddlist.sel_opt_id = ext->ddlist.sel_opt_id_ori; + refr_position(roller, true); + } + } + /*Save the current state when entered to edit mode*/ + else { + ext->ddlist.sel_opt_id_ori = ext->ddlist.sel_opt_id; + } + } else { + ext->ddlist.sel_opt_id_ori = ext->ddlist.sel_opt_id; /*Save the current value. Used to revert this state if ENER wont't be pressed*/ + + } +#endif + } else if(sign == LV_SIGNAL_DEFOCUS) { +#if USE_LV_GROUP + /*Revert the original state*/ + if(ext->ddlist.sel_opt_id != ext->ddlist.sel_opt_id_ori) { + ext->ddlist.sel_opt_id = ext->ddlist.sel_opt_id_ori; + refr_position(roller, true); + } +#endif + } else if(sign == LV_SIGNAL_CONTROLL) { + char c = *((char *)param); + if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_DOWN) { + if(ext->ddlist.sel_opt_id + 1 < ext->ddlist.option_cnt) { + lv_roller_set_selected(roller, ext->ddlist.sel_opt_id + 1, true); + } + } else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_UP) { + if(ext->ddlist.sel_opt_id > 0) { + lv_roller_set_selected(roller, ext->ddlist.sel_opt_id - 1, true); + } + } else if(c == LV_GROUP_KEY_ENTER) { + ext->ddlist.sel_opt_id_ori = ext->ddlist.sel_opt_id; /*Set the entered value as default*/ + if(ext->ddlist.action) ext->ddlist.action(roller); + +#if USE_LV_GROUP + lv_group_t * g = lv_obj_get_group(roller); + bool editing = lv_group_get_editing(g); + if(editing) lv_group_set_editing(g, false); /*In edit mode go to navigate mode if an option is selected*/ +#endif + } + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_roller"; + } + + return res; +} + +/** + * Signal function of the scrollable part of the roller. + * @param roller_scrl ointer to the scrollable part of roller (page) + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_roller_scrl_signal(lv_obj_t * roller_scrl, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_scrl_signal(roller_scrl, sign, param); + if(res != LV_RES_OK) return res; + + lv_indev_t * indev = lv_indev_get_act(); + int32_t id = -1; + lv_obj_t * roller = lv_obj_get_parent(roller_scrl); + lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller); + + if(ext->ddlist.label == NULL) return LV_RES_INV; /*On delete the ddlist signal deletes the label so nothing left to do here*/ + + lv_style_t * style_label = lv_obj_get_style(ext->ddlist.label); + const lv_font_t * font = style_label->text.font; + lv_coord_t font_h = lv_font_get_height(font); + + if(sign == LV_SIGNAL_DRAG_END) { + /*If dragged then align the list to there be an element in the middle*/ + lv_coord_t label_y1 = ext->ddlist.label->coords.y1 - roller->coords.y1; + lv_coord_t label_unit = font_h + style_label->text.line_space; + lv_coord_t mid = (roller->coords.y2 - roller->coords.y1) / 2; + id = (mid - label_y1 + style_label->text.line_space / 2) / label_unit; + if(id < 0) id = 0; + if(id >= ext->ddlist.option_cnt) id = ext->ddlist.option_cnt - 1; + ext->ddlist.sel_opt_id = id; + if(ext->ddlist.action) ext->ddlist.action(roller); + } else if(sign == LV_SIGNAL_RELEASED) { + /*If picked an option by clicking then set it*/ + if(!lv_indev_is_dragging(indev)) { + lv_point_t p; + lv_indev_get_point(indev, &p); + p.y = p.y - ext->ddlist.label->coords.y1; + id = p.y / (font_h + style_label->text.line_space); + if(id < 0) id = 0; + if(id >= ext->ddlist.option_cnt) id = ext->ddlist.option_cnt - 1; + ext->ddlist.sel_opt_id = id; + if(ext->ddlist.action) ext->ddlist.action(roller); + } + } + + /*Position the scrollable according to the new selected option*/ + if(id != -1) { + refr_position(roller, true); + } + + return res; +} + +/** + * Draw a rectangle which has gradient on its top and bottom + * @param roller pointer to a roller object + * @param mask pointer to the current mask (from the design function) + */ +static void draw_bg(lv_obj_t * roller, const lv_area_t * mask) +{ + lv_style_t * style = lv_roller_get_style(roller, LV_ROLLER_STYLE_BG); + lv_area_t half_mask; + lv_area_t half_roller; + lv_coord_t h = lv_obj_get_height(roller); + bool union_ok; + lv_area_copy(&half_roller, &roller->coords); + + half_roller.x1 -= roller->ext_size; /*Add ext size too (e.g. because of shadow draw) */ + half_roller.x2 += roller->ext_size; + half_roller.y1 -= roller->ext_size; + half_roller.y2 = roller->coords.y1 + h / 2; + + union_ok = lv_area_intersect(&half_mask, &half_roller, mask); + + half_roller.x1 += roller->ext_size; /*Revert ext. size adding*/ + half_roller.x2 -= roller->ext_size; + half_roller.y1 += roller->ext_size; + half_roller.y2 += style->body.radius; + + if(union_ok) { + lv_draw_rect(&half_roller, &half_mask, style, lv_obj_get_opa_scale(roller)); + } + + half_roller.x1 -= roller->ext_size; /*Add ext size too (e.g. because of shadow draw) */ + half_roller.x2 += roller->ext_size; + half_roller.y2 = roller->coords.y2 + roller->ext_size; + half_roller.y1 = roller->coords.y1 + h / 2; + if((h & 0x1) == 0) half_roller.y1++; /*With even height the pixels in the middle would be drawn twice*/ + + union_ok = lv_area_intersect(&half_mask, &half_roller, mask); + + half_roller.x1 += roller->ext_size; /*Revert ext. size adding*/ + half_roller.x2 -= roller->ext_size; + half_roller.y2 -= roller->ext_size; + half_roller.y1 -= style->body.radius; + + if(union_ok) { + lv_color_t main_tmp = style->body.main_color; + lv_color_t grad_tmp = style->body.grad_color; + + style->body.main_color = grad_tmp; + style->body.grad_color = main_tmp; + lv_draw_rect(&half_roller, &half_mask, style, lv_obj_get_opa_scale(roller)); + style->body.main_color = main_tmp; + style->body.grad_color = grad_tmp; + } +} + +/** + * Refresh the position of the roller. It uses the id stored in: ext->ddlist.selected_option_id + * @param roller pointer to a roller object + * @param anim_en true: refresh with animation; false: without animation + */ +static void refr_position(lv_obj_t * roller, bool anim_en) +{ +#if USE_LV_ANIMATION == 0 + anim_en = false; +#endif + lv_obj_t * roller_scrl = lv_page_get_scrl(roller); + lv_roller_ext_t * ext = lv_obj_get_ext_attr(roller); + lv_style_t * style_label = lv_obj_get_style(ext->ddlist.label); + const lv_font_t * font = style_label->text.font; + lv_coord_t font_h = lv_font_get_height(font); + lv_coord_t h = lv_obj_get_height(roller); + int32_t id = ext->ddlist.sel_opt_id; + lv_coord_t line_y1 = id * (font_h + style_label->text.line_space) + ext->ddlist.label->coords.y1 - roller_scrl->coords.y1; + lv_coord_t new_y = - line_y1 + (h - font_h) / 2; + + if(ext->ddlist.anim_time == 0 || anim_en == false) { + lv_obj_set_y(roller_scrl, new_y); + } else { +#if USE_LV_ANIMATION + lv_anim_t a; + a.var = roller_scrl; + a.start = lv_obj_get_y(roller_scrl); + a.end = new_y; + a.fp = (lv_anim_fp_t)lv_obj_set_y; + a.path = lv_anim_path_linear; + a.end_cb = NULL; + a.act_time = 0; + a.time = ext->ddlist.anim_time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); +#endif + } +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_roller.h b/bdk/libs/lvgl/lv_objx/lv_roller.h new file mode 100644 index 00000000..2f1b21c7 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_roller.h @@ -0,0 +1,224 @@ +/** + * @file lv_roller.h + * + */ + +#ifndef LV_ROLLER_H +#define LV_ROLLER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_ROLLER != 0 + +/*Testing of dependencies*/ +#if USE_LV_DDLIST == 0 +#error "lv_roller: lv_ddlist is required. Enable it in lv_conf.h (USE_LV_DDLIST 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_ddlist.h" +#include "lv_label.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of roller*/ +typedef struct { + lv_ddlist_ext_t ddlist; /*Ext. of ancestor*/ + /*New data for this type */ +} lv_roller_ext_t; + +enum { + LV_ROLLER_STYLE_BG, + LV_ROLLER_STYLE_SEL, +}; +typedef uint8_t lv_roller_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a roller object + * @param par pointer to an object, it will be the parent of the new roller + * @param copy pointer to a roller object, if not NULL then the new object will be copied from it + * @return pointer to the created roller + */ +lv_obj_t * lv_roller_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the align of the roller's options (left, right or center[default]) + * @param roller - pointer to a roller object + * @param align - one of lv_label_align_t values (left, right, center) + */ +void lv_roller_set_align(lv_obj_t * roller, lv_label_align_t align); + +/** + * Set the options on a roller + * @param roller pointer to roller object + * @param options a string with '\n' separated options. E.g. "One\nTwo\nThree" + */ +static inline void lv_roller_set_options(lv_obj_t * roller, const char * options) +{ + lv_ddlist_set_options(roller, options); +} + +/** + * Set the selected option + * @param roller pointer to a roller object + * @param sel_opt id of the selected option (0 ... number of option - 1); + * @param anim_en true: set with animation; false set immediately + */ +void lv_roller_set_selected(lv_obj_t *roller, uint16_t sel_opt, bool anim_en); + +/** + * Set a function to call when a new option is chosen + * @param roller pointer to a roller + * @param action pointer to a callback function + */ +static inline void lv_roller_set_action(lv_obj_t * roller, lv_action_t action) +{ + lv_ddlist_set_action(roller, action); +} + +/** + * Set the height to show the given number of rows (options) + * @param roller pointer to a roller object + * @param row_cnt number of desired visible rows + */ +void lv_roller_set_visible_row_count(lv_obj_t *roller, uint8_t row_cnt); + +/** + * Enable or disable the horizontal fit to the content + * @param roller pointer to a roller + * @param en true: enable auto fit; false: disable auto fit + */ +static inline void lv_roller_set_hor_fit(lv_obj_t * roller, bool en) +{ + lv_ddlist_set_hor_fit(roller, en); +} + +/** + * Set the open/close animation time. + * @param roller pointer to a roller object + * @param anim_time: open/close animation time [ms] + */ +static inline void lv_roller_set_anim_time(lv_obj_t *roller, uint16_t anim_time) +{ + lv_ddlist_set_anim_time(roller, anim_time); +} + +/** + * Set a style of a roller + * @param roller pointer to a roller object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_roller_set_style(lv_obj_t *roller, lv_roller_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the align attribute. Default alignment after _create is LV_LABEL_ALIGN_CENTER + * @param roller pointer to a roller object + * @return LV_LABEL_ALIGN_LEFT, LV_LABEL_ALIGN_RIGHT or LV_LABEL_ALIGN_CENTER + */ +lv_label_align_t lv_roller_get_align(const lv_obj_t * roller); + +/** + * Get the options of a roller + * @param roller pointer to roller object + * @return the options separated by '\n'-s (E.g. "Option1\nOption2\nOption3") + */ +static inline const char * lv_roller_get_options(const lv_obj_t *roller) +{ + return lv_ddlist_get_options(roller); +} + +/** + * Get the id of the selected option + * @param roller pointer to a roller object + * @return id of the selected option (0 ... number of option - 1); + */ +static inline uint16_t lv_roller_get_selected(const lv_obj_t *roller) +{ + return lv_ddlist_get_selected(roller); +} + +/** + * Get the current selected option as a string + * @param roller pointer to roller object + * @param buf pointer to an array to store the string + */ +static inline void lv_roller_get_selected_str(const lv_obj_t * roller, char * buf) +{ + lv_ddlist_get_selected_str(roller, buf); +} + +/** + * Get the "option selected" callback function + * @param roller pointer to a roller + * @return pointer to the call back function + */ +static inline lv_action_t lv_roller_get_action(const lv_obj_t * roller) +{ + return lv_ddlist_get_action(roller); +} + +/** + * Get the open/close animation time. + * @param roller pointer to a roller + * @return open/close animation time [ms] + */ +static inline uint16_t lv_roller_get_anim_time(const lv_obj_t * roller) +{ + return lv_ddlist_get_anim_time(roller); +} + +/** + * Get the auto width set attribute + * @param roller pointer to a roller object + * @return true: auto size enabled; false: manual width settings enabled + */ +bool lv_roller_get_hor_fit(const lv_obj_t *roller); + +/** + * Get a style of a roller + * @param roller pointer to a roller object + * @param type which style should be get + * @return style pointer to a style + * */ +lv_style_t * lv_roller_get_style(const lv_obj_t *roller, lv_roller_style_t type); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_ROLLER*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_ROLLER_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_slider.c b/bdk/libs/lvgl/lv_objx/lv_slider.c new file mode 100644 index 00000000..eaf04e8c --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_slider.c @@ -0,0 +1,520 @@ + +/** + * @file lv_slider.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_slider.h" +#if USE_LV_SLIDER != 0 + +#include "../lv_core/lv_group.h" +#include "../lv_draw/lv_draw.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ +#define LV_SLIDER_SIZE_MIN 4 /*hor. pad and ver. pad cannot make the bar or indicator smaller then this [px]*/ +#define LV_SLIDER_NOT_PRESSED INT16_MIN + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_slider_design(lv_obj_t * slider, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_slider_signal(lv_obj_t * slider, lv_signal_t sign, void * param); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_design_func_t ancestor_design_f; +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a slider objects + * @param par pointer to an object, it will be the parent of the new slider + * @param copy pointer to a slider object, if not NULL then the new object will be copied from it + * @return pointer to the created slider + */ +lv_obj_t * lv_slider_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("slider create started"); + + /*Create the ancestor slider*/ + lv_obj_t * new_slider = lv_bar_create(par, copy); + lv_mem_assert(new_slider); + if(new_slider == NULL) return NULL; + + if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_slider); + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_slider); + + /*Allocate the slider type specific extended data*/ + lv_slider_ext_t * ext = lv_obj_allocate_ext_attr(new_slider, sizeof(lv_slider_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + /*Initialize the allocated 'ext' */ + ext->action = NULL; + ext->drag_value = LV_SLIDER_NOT_PRESSED; + ext->style_knob = &lv_style_pretty; + ext->knob_in = 0; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_slider, lv_slider_signal); + lv_obj_set_design_func(new_slider, lv_slider_design); + + /*Init the new slider slider*/ + if(copy == NULL) { + lv_obj_set_click(new_slider, true); + lv_obj_set_protect(new_slider, LV_PROTECT_PRESS_LOST); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_slider_set_style(new_slider, LV_SLIDER_STYLE_BG, th->slider.bg); + lv_slider_set_style(new_slider, LV_SLIDER_STYLE_INDIC, th->slider.indic); + lv_slider_set_style(new_slider, LV_SLIDER_STYLE_KNOB, th->slider.knob); + } else { + lv_slider_set_style(new_slider, LV_SLIDER_STYLE_KNOB, ext->style_knob); + } + } + /*Copy an existing slider*/ + else { + lv_slider_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->style_knob = copy_ext->style_knob; + ext->action = copy_ext->action; + ext->knob_in = copy_ext->knob_in; + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_slider); + } + + + LV_LOG_INFO("slider created"); + + + return new_slider; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a function which will be called when a new value is set on the slider + * @param slider pointer to slider object + * @param action a callback function + */ +void lv_slider_set_action(lv_obj_t * slider, lv_action_t action) +{ + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + ext->action = action; +} + +/** + * Set the 'knob in' attribute of a slider + * @param slider pointer to slider object + * @param in true: the knob is drawn always in the slider; + * false: the knob can be out on the edges + */ +void lv_slider_set_knob_in(lv_obj_t * slider, bool in) +{ + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + if(ext->knob_in == in) return; + + ext->knob_in = in == false ? 0 : 1; + lv_obj_invalidate(slider); +} + +/** + * Set a style of a slider + * @param slider pointer to a slider object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_slider_set_style(lv_obj_t * slider, lv_slider_style_t type, lv_style_t * style) +{ + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + + switch(type) { + case LV_SLIDER_STYLE_BG: + lv_bar_set_style(slider, LV_BAR_STYLE_BG, style); + break; + case LV_SLIDER_STYLE_INDIC: + lv_bar_set_style(slider, LV_BAR_STYLE_INDIC, style); + break; + case LV_SLIDER_STYLE_KNOB: + ext->style_knob = style; + lv_obj_refresh_ext_size(slider); + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the value of a slider + * @param slider pointer to a slider object + * @return the value of the slider + */ +int16_t lv_slider_get_value(const lv_obj_t * slider) +{ + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + + if(ext->drag_value != LV_SLIDER_NOT_PRESSED) return ext->drag_value; + else return lv_bar_get_value(slider); +} + +/** + * Get the slider action function + * @param slider pointer to slider object + * @return the callback function + */ +lv_action_t lv_slider_get_action(const lv_obj_t * slider) +{ + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + return ext->action; +} + +/** + * Give the slider is being dragged or not + * @param slider pointer to a slider object + * @return true: drag in progress false: not dragged + */ +bool lv_slider_is_dragged(const lv_obj_t * slider) +{ + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + return ext->drag_value == LV_SLIDER_NOT_PRESSED ? false : true; +} + +/** + * Get the 'knob in' attribute of a slider + * @param slider pointer to slider object + * @return true: the knob is drawn always in the slider; + * false: the knob can be out on the edges + */ +bool lv_slider_get_knob_in(const lv_obj_t * slider) +{ + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + return ext->knob_in == 0 ? false : true; +} + +/** + * Get a style of a slider + * @param slider pointer to a slider object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_slider_get_style(const lv_obj_t * slider, lv_slider_style_t type) +{ + lv_style_t * style = NULL; + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + + switch(type) { + case LV_SLIDER_STYLE_BG: + style = lv_bar_get_style(slider, LV_BAR_STYLE_BG); + break; + case LV_SLIDER_STYLE_INDIC: + style = lv_bar_get_style(slider, LV_BAR_STYLE_INDIC); + break; + case LV_SLIDER_STYLE_KNOB: + style = ext->style_knob; + break; + default: + style = NULL; + break; + } + + return style; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + + +/** + * Handle the drawing related tasks of the sliders + * @param slider pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_slider_design(lv_obj_t * slider, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + + lv_style_t * style_bg = lv_slider_get_style(slider, LV_SLIDER_STYLE_BG); + lv_style_t * style_knob = lv_slider_get_style(slider, LV_SLIDER_STYLE_KNOB); + lv_style_t * style_indic = lv_slider_get_style(slider, LV_SLIDER_STYLE_INDIC); + + lv_opa_t opa_scale = lv_obj_get_opa_scale(slider); + + lv_coord_t slider_w = lv_area_get_width(&slider->coords); + lv_coord_t slider_h = lv_area_get_height(&slider->coords); + + /*Draw the bar*/ + lv_area_t area_bg; + lv_area_copy(&area_bg, &slider->coords); + + /*Be sure at least LV_SLIDER_SIZE_MIN size will remain*/ + lv_coord_t pad_ver_bg = style_bg->body.padding.ver; + lv_coord_t pad_hor_bg = style_bg->body.padding.hor; + if(pad_ver_bg * 2 + LV_SLIDER_SIZE_MIN > lv_area_get_height(&area_bg)) { + pad_ver_bg = (lv_area_get_height(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1; + } + if(pad_hor_bg * 2 + LV_SLIDER_SIZE_MIN > lv_area_get_width(&area_bg)) { + pad_hor_bg = (lv_area_get_width(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1; + } + + if(ext->knob_in) { /*Enable extra size if the knob is inside */ + area_bg.x1 += pad_hor_bg; + area_bg.x2 -= pad_hor_bg; + area_bg.y1 += pad_hor_bg; + area_bg.y2 -= pad_hor_bg; + } else { /*Let space only in the perpendicular directions*/ + area_bg.x1 += slider_w < slider_h ? pad_hor_bg : 0; /*Pad only for vertical slider*/ + area_bg.x2 -= slider_w < slider_h ? pad_hor_bg : 0; /*Pad only for vertical slider*/ + area_bg.y1 += slider_w > slider_h ? pad_ver_bg : 0; /*Pad only for horizontal slider*/ + area_bg.y2 -= slider_w > slider_h ? pad_ver_bg : 0; /*Pad only for horizontal slider*/ + } + + +#if USE_LV_GROUP == 0 + lv_draw_rect(&area_bg, mask, style_bg, lv_obj_get_opa_scale(slider)); +#else + /* Draw the borders later if the bar is focused. + * At value = 100% the indicator can cover to whole background and the focused style won't be visible*/ + if(lv_obj_is_focused(slider)) { + lv_style_t style_tmp; + lv_style_copy(&style_tmp, style_bg); + style_tmp.body.border.width = 0; + lv_draw_rect(&area_bg, mask, &style_tmp, opa_scale); + } else { + lv_draw_rect(&area_bg, mask, style_bg, opa_scale); + } +#endif + + + /*Draw the indicator*/ + lv_area_t area_indic; + lv_area_copy(&area_indic, &area_bg); + + /*Be sure at least ver pad/hor pad width indicator will remain*/ + lv_coord_t pad_ver_indic = style_indic->body.padding.ver; + lv_coord_t pad_hor_indic = style_indic->body.padding.hor; + if(pad_ver_indic * 2 + LV_SLIDER_SIZE_MIN > lv_area_get_height(&area_bg)) { + pad_ver_indic = (lv_area_get_height(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1; + } + if(pad_hor_indic * 2 + LV_SLIDER_SIZE_MIN > lv_area_get_width(&area_bg)) { + pad_hor_indic = (lv_area_get_width(&area_bg) - LV_SLIDER_SIZE_MIN) >> 1; + } + + area_indic.x1 += pad_hor_indic; + area_indic.x2 -= pad_hor_indic; + area_indic.y1 += pad_ver_indic; + area_indic.y2 -= pad_ver_indic; + + + lv_coord_t cur_value = lv_slider_get_value(slider); + lv_coord_t min_value = lv_slider_get_min_value(slider); + lv_coord_t max_value = lv_slider_get_max_value(slider); + + /*If dragged draw to the drag position*/ + if(ext->drag_value != LV_SLIDER_NOT_PRESSED) cur_value = ext->drag_value; + + if(slider_w >= slider_h) { + area_indic.x2 = (int32_t)((int32_t)(lv_area_get_width(&area_indic)) * (cur_value - min_value)) / (max_value - min_value); + area_indic.x2 = area_indic.x1 + area_indic.x2 - 1; + + } else { + area_indic.y1 = (int32_t)((int32_t)(lv_area_get_height(&area_indic)) * (cur_value - min_value)) / (max_value - min_value); + area_indic.y1 = area_indic.y2 - area_indic.y1 + 1; + } + + if(cur_value != min_value) lv_draw_rect(&area_indic, mask, style_indic, opa_scale); + + /*Before the knob add the border if required*/ +#if USE_LV_GROUP + /* Draw the borders later if the bar is focused. + * At value = 100% the indicator can cover to whole background and the focused style won't be visible*/ + if(lv_obj_is_focused(slider)) { + lv_style_t style_tmp; + lv_style_copy(&style_tmp, style_bg); + style_tmp.body.empty = 1; + style_tmp.body.shadow.width = 0; + lv_draw_rect(&area_bg, mask, &style_tmp, opa_scale); + } +#endif + + /*Draw the knob*/ + lv_area_t knob_area; + lv_area_copy(&knob_area, &slider->coords); + + if(slider_w >= slider_h) { + if(ext->knob_in == 0) { + knob_area.x1 = area_indic.x2 - slider_h / 2; + knob_area.x2 = knob_area.x1 + slider_h - 1; + } else { + knob_area.x1 = (int32_t)((int32_t)(slider_w - slider_h - 1) * (cur_value - min_value)) / (max_value - min_value); + knob_area.x1 += slider->coords.x1; + knob_area.x2 = knob_area.x1 + slider_h - 1; + } + + knob_area.y1 = slider->coords.y1; + knob_area.y2 = slider->coords.y2; + } else { + if(ext->knob_in == 0) { + knob_area.y1 = area_indic.y1 - slider_w / 2; + knob_area.y2 = knob_area.y1 + slider_w - 1; + } else { + knob_area.y2 = (int32_t)((int32_t)(slider_h - slider_w - 1) * (cur_value - min_value)) / (max_value - min_value); + knob_area.y2 = slider->coords.y2 - knob_area.y2; + knob_area.y1 = knob_area.y2 - slider_w - 1; + } + knob_area.x1 = slider->coords.x1; + knob_area.x2 = slider->coords.x2; + + } + lv_draw_rect(&knob_area, mask, style_knob, opa_scale); + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + + } + + return true; +} + +/** + * Signal function of the slider + * @param slider pointer to a slider object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_slider_signal(lv_obj_t * slider, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(slider, sign, param); + if(res != LV_RES_OK) return res; + + lv_slider_ext_t * ext = lv_obj_get_ext_attr(slider); + lv_point_t p; + lv_coord_t w = lv_obj_get_width(slider); + lv_coord_t h = lv_obj_get_height(slider); + + if(sign == LV_SIGNAL_PRESSED) { + ext->drag_value = lv_slider_get_value(slider); + } else if(sign == LV_SIGNAL_PRESSING) { + lv_indev_get_point(param, &p); + int16_t tmp = 0; + if(w > h) { + lv_coord_t knob_w = h; + p.x -= slider->coords.x1 + h / 2; /*Modify the point to shift with half knob (important on the start and end)*/ + tmp = (int32_t)((int32_t) p.x * (ext->bar.max_value - ext->bar.min_value + 1)) / (w - knob_w); + tmp += ext->bar.min_value; + } else { + lv_coord_t knob_h = w; + p.y -= slider->coords.y1 + w / 2; /*Modify the point to shift with half knob (important on the start and end)*/ + tmp = (int32_t)((int32_t) p.y * (ext->bar.max_value - ext->bar.min_value + 1)) / (h - knob_h); + tmp = ext->bar.max_value - tmp; /*Invert the value: smaller value means higher y*/ + } + + if(tmp < ext->bar.min_value) tmp = ext->bar.min_value; + else if(tmp > ext->bar.max_value) tmp = ext->bar.max_value; + + if(tmp != ext->drag_value) { + ext->drag_value = tmp; + lv_obj_invalidate(slider); + if(ext->action != NULL) res = ext->action(slider); + } + } else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) { + lv_slider_set_value(slider, ext->drag_value); + ext->drag_value = LV_SLIDER_NOT_PRESSED; + if(ext->action != NULL) res = ext->action(slider); + } else if(sign == LV_SIGNAL_CORD_CHG) { + /* The knob size depends on slider size. + * During the drawing method the ext. size is used by the knob so refresh the ext. size.*/ + if(lv_obj_get_width(slider) != lv_area_get_width(param) || + lv_obj_get_height(slider) != lv_area_get_height(param)) { + slider->signal_func(slider, LV_SIGNAL_REFR_EXT_SIZE, NULL); + } + } else if(sign == LV_SIGNAL_REFR_EXT_SIZE) { + lv_style_t * style = lv_slider_get_style(slider, LV_SLIDER_STYLE_BG); + lv_style_t * knob_style = lv_slider_get_style(slider, LV_SLIDER_STYLE_KNOB); + lv_coord_t shadow_w = knob_style->body.shadow.width; + if(ext->knob_in == 0) { + /* The smaller size is the knob diameter*/ + lv_coord_t x = LV_MATH_MIN(w / 2 + 1 + shadow_w, h / 2 + 1 + shadow_w); + if(slider->ext_size < x) slider->ext_size = x; + } else { + lv_coord_t pad = LV_MATH_MIN(style->body.padding.hor, style->body.padding.ver); + if(pad < 0) pad = -pad; + if(slider->ext_size < pad) slider->ext_size = pad; + + if(slider->ext_size < shadow_w) slider->ext_size = shadow_w; + } + } else if(sign == LV_SIGNAL_CONTROLL) { + char c = *((char *)param); + + ext->drag_value = LV_SLIDER_NOT_PRESSED; + +#if USE_LV_GROUP + lv_group_t * g = lv_obj_get_group(slider); + bool editing = lv_group_get_editing(g); + lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + /*Encoders need special handling*/ + if(indev_type == LV_INDEV_TYPE_ENCODER && c == LV_GROUP_KEY_ENTER) { + if(editing) lv_group_set_editing(g, false); + } +#endif + if(c == LV_GROUP_KEY_RIGHT || c == LV_GROUP_KEY_UP) { + lv_slider_set_value(slider, lv_slider_get_value(slider) + 1); + if(ext->action != NULL) res = ext->action(slider); + } else if(c == LV_GROUP_KEY_LEFT || c == LV_GROUP_KEY_DOWN) { + lv_slider_set_value(slider, lv_slider_get_value(slider) - 1); + if(ext->action != NULL) res = ext->action(slider); + } + } else if(sign == LV_SIGNAL_GET_EDITABLE) { + bool * editable = (bool *)param; + *editable = true; + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_slider"; + } + + return res; +} +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_slider.h b/bdk/libs/lvgl/lv_objx/lv_slider.h new file mode 100644 index 00000000..6336ae8a --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_slider.h @@ -0,0 +1,202 @@ +/** + * @file lv_slider.h + * + */ + +#ifndef LV_SLIDER_H +#define LV_SLIDER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_SLIDER != 0 + +/*Testing of dependencies*/ +#if USE_LV_BAR == 0 +#error "lv_slider: lv_bar is required. Enable it in lv_conf.h (USE_LV_BAR 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_bar.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ +/*Data of slider*/ +typedef struct +{ + lv_bar_ext_t bar; /*Ext. of ancestor*/ + /*New data for this type */ + lv_action_t action; /*Function to call when a new value is set*/ + lv_style_t *style_knob; /*Style of the knob*/ + int16_t drag_value; /*Store a temporal value during press until release (Handled by the library)*/ + uint8_t knob_in :1; /*1: Draw the knob inside the bar*/ +} lv_slider_ext_t; + +/*Built-in styles of slider*/ +enum +{ + LV_SLIDER_STYLE_BG, + LV_SLIDER_STYLE_INDIC, + LV_SLIDER_STYLE_KNOB, +}; +typedef uint8_t lv_slider_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a slider objects + * @param par pointer to an object, it will be the parent of the new slider + * @param copy pointer to a slider object, if not NULL then the new object will be copied from it + * @return pointer to the created slider + */ +lv_obj_t * lv_slider_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new value on the slider + * @param slider pointer to a slider object + * @param value new value + */ +static inline void lv_slider_set_value(lv_obj_t * slider, int16_t value) +{ + lv_bar_set_value(slider, value); +} + +/** + * Set a new value with animation on a slider + * @param slider pointer to a slider object + * @param value new value + * @param anim_time animation time in milliseconds + */ +static inline void lv_slider_set_value_anim(lv_obj_t * slider, int16_t value, uint16_t anim_time) +{ + lv_bar_set_value_anim(slider, value, anim_time); +} + +/** + * Set minimum and the maximum values of a bar + * @param slider pointer to the slider object + * @param min minimum value + * @param max maximum value + */ +static inline void lv_slider_set_range(lv_obj_t *slider, int16_t min, int16_t max) +{ + lv_bar_set_range(slider, min, max); +} + +/** + * Set a function which will be called when a new value is set on the slider + * @param slider pointer to slider object + * @param action a callback function + */ +void lv_slider_set_action(lv_obj_t * slider, lv_action_t action); + +/** + * Set the 'knob in' attribute of a slider + * @param slider pointer to slider object + * @param in true: the knob is drawn always in the slider; + * false: the knob can be out on the edges + */ +void lv_slider_set_knob_in(lv_obj_t * slider, bool in); + +/** + * Set a style of a slider + * @param slider pointer to a slider object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_slider_set_style(lv_obj_t *slider, lv_slider_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the value of a slider + * @param slider pointer to a slider object + * @return the value of the slider + */ +int16_t lv_slider_get_value(const lv_obj_t * slider); + +/** + * Get the minimum value of a slider + * @param slider pointer to a slider object + * @return the minimum value of the slider + */ +static inline int16_t lv_slider_get_min_value(const lv_obj_t * slider) +{ + return lv_bar_get_min_value(slider); +} + +/** + * Get the maximum value of a slider + * @param slider pointer to a slider object + * @return the maximum value of the slider + */ +static inline int16_t lv_slider_get_max_value(const lv_obj_t * slider) +{ + return lv_bar_get_max_value(slider); +} + +/** + * Get the slider action function + * @param slider pointer to slider object + * @return the callback function + */ +lv_action_t lv_slider_get_action(const lv_obj_t * slider); + +/** + * Give the slider is being dragged or not + * @param slider pointer to a slider object + * @return true: drag in progress false: not dragged + */ +bool lv_slider_is_dragged(const lv_obj_t * slider); + +/** + * Get the 'knob in' attribute of a slider + * @param slider pointer to slider object + * @return true: the knob is drawn always in the slider; + * false: the knob can be out on the edges + */ +bool lv_slider_get_knob_in(const lv_obj_t * slider); + + +/** + * Get a style of a slider + * @param slider pointer to a slider object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_slider_get_style(const lv_obj_t *slider, lv_slider_style_t type); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_SLIDER*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_SLIDER_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_spinbox.c b/bdk/libs/lvgl/lv_objx/lv_spinbox.c new file mode 100644 index 00000000..70fac33c --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_spinbox.c @@ -0,0 +1,471 @@ +/** + * @file lv_spinbox.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_spinbox.h" + +#if USE_LV_SPINBOX != 0 +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_spinbox_signal(lv_obj_t * spinbox, lv_signal_t sign, void * param); +static void lv_spinbox_updatevalue(lv_obj_t * spinbox); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_design_func_t ancestor_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a spinbox object + * @param par pointer to an object, it will be the parent of the new spinbox + * @param copy pointer to a spinbox object, if not NULL then the new object will be copied from it + * @return pointer to the created spinbox + */ +lv_obj_t * lv_spinbox_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("spinbox create started"); + + /*Create the ancestor of spinbox*/ + lv_obj_t * new_spinbox = lv_ta_create(par, copy); + lv_mem_assert(new_spinbox); + if(new_spinbox == NULL) return NULL; + + /*Allocate the spinbox type specific extended data*/ + lv_spinbox_ext_t * ext = lv_obj_allocate_ext_attr(new_spinbox, sizeof(lv_spinbox_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_spinbox); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_spinbox); + + /*Initialize the allocated 'ext'*/ + ext->ta.one_line = 1; + ext->ta.pwd_mode = 0; + ext->ta.accapted_chars = "1234567890+-. "; + + ext->value = 0; + ext->dec_point_pos = 0; + ext->digit_count = 5; + ext->digit_padding_left = 0; + ext->step = 1; + ext->range_max = 99999; + ext->range_min = -99999; + ext->value_changed_cb = NULL; + + lv_ta_set_cursor_type(new_spinbox, LV_CURSOR_BLOCK | LV_CURSOR_HIDDEN); /*hidden by default*/ + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_spinbox, lv_spinbox_signal); + lv_obj_set_design_func(new_spinbox, ancestor_design); /*Leave the Text area's design function*/ + + /*Init the new spinbox spinbox*/ + if(copy == NULL) { + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_spinbox_set_style(new_spinbox, LV_SPINBOX_STYLE_BG, th->spinbox.bg); + lv_spinbox_set_style(new_spinbox, LV_SPINBOX_STYLE_CURSOR, th->spinbox.cursor); + lv_spinbox_set_style(new_spinbox, LV_SPINBOX_STYLE_SB, th->spinbox.sb); + } + } + /*Copy an existing spinbox*/ + else { + lv_spinbox_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + + lv_spinbox_set_value(new_spinbox, copy_ext->value); + lv_spinbox_set_digit_format(new_spinbox, copy_ext->digit_count, copy_ext->dec_point_pos); + lv_spinbox_set_range(new_spinbox, copy_ext->range_min, copy_ext->range_max); + lv_spinbox_set_step(new_spinbox, copy_ext->step); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_spinbox); + } + + lv_spinbox_updatevalue(new_spinbox); + + LV_LOG_INFO("spinbox created"); + + return new_spinbox; +} + + +/*===================== + * Setter functions + *====================*/ + +/** + * Set spinbox value + * @param spinbox pointer to spinbox + * @param i value to be set + */ +void lv_spinbox_set_value(lv_obj_t * spinbox, int32_t i) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + if(ext == NULL) + return; + + if(i > ext->range_max) + i = ext->range_max; + if(i < ext->range_min) + i = ext->range_min; + + ext->value = i; + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Set spinbox digit format (digit count and decimal format) + * @param spinbox pointer to spinbox + * @param digit_count number of digit excluding the decimal separator and the sign + * @param separator_position number of digit before the decimal point. If 0, decimal point is not shown + */ +void lv_spinbox_set_digit_format(lv_obj_t * spinbox, uint8_t digit_count, uint8_t separator_position) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + if(ext == NULL) + return; + + if(digit_count > LV_SPINBOX_MAX_DIGIT_COUNT) + digit_count = LV_SPINBOX_MAX_DIGIT_COUNT; + + if(separator_position > LV_SPINBOX_MAX_DIGIT_COUNT) + separator_position = LV_SPINBOX_MAX_DIGIT_COUNT; + + ext->digit_count = digit_count; + ext->dec_point_pos = separator_position; + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Set spinbox step + * @param spinbox pointer to spinbox + * @param step steps on increment/decrement + */ +void lv_spinbox_set_step(lv_obj_t * spinbox, uint32_t step) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + if(ext == NULL) return; + + ext->step = step; +} + +/** + * Set spinbox value range + * @param spinbox pointer to spinbox + * @param range_min maximum value, inclusive + * @param range_max minimum value, inclusive + */ +void lv_spinbox_set_range(lv_obj_t * spinbox, int32_t range_min, int32_t range_max) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + if(ext == NULL) return; + + ext->range_max = range_max; + ext->range_min = range_min; + + if(ext->value > ext->range_max) { + ext->value = ext->range_max; + lv_obj_invalidate(spinbox); + } + if(ext->value < ext->range_min) { + ext->value = ext->range_min; + lv_obj_invalidate(spinbox); + } +} + +/** + * Set spinbox callback on calue change + * @param spinbox pointer to spinbox + * @param cb Callback function called on value change event + */ +void lv_spinbox_set_value_changed_cb(lv_obj_t * spinbox, lv_spinbox_value_changed_cb_t cb) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + ext->value_changed_cb = cb; +} + +/** + * Set spinbox left padding in digits count (added between sign and first digit) + * @param spinbox pointer to spinbox + * @param cb Callback function called on value change event + */ +void lv_spinbox_set_padding_left(lv_obj_t * spinbox, uint8_t padding) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + ext->digit_padding_left = padding; + lv_spinbox_updatevalue(spinbox); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the spinbox numeral value (user has to convert to float according to its digit format) + * @param spinbox pointer to spinbox + * @return value integer value of the spinbox + */ +int32_t lv_spinbox_get_value(lv_obj_t * spinbox) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + return ext->value; +} + +/*===================== + * Other functions + *====================*/ + +/** + * Select next lower digit for edition by dividing the step by 10 + * @param spinbox pointer to spinbox + */ +void lv_spinbox_step_next(lv_obj_t * spinbox) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + int32_t new_step = ext->step / 10; + if((new_step) > 0) ext->step = new_step; + else ext->step = 1; + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Select next higher digit for edition by multiplying the step by 10 + * @param spinbox pointer to spinbox + */ +void lv_spinbox_step_previous(lv_obj_t * spinbox) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + int32_t step_limit; + step_limit = LV_MATH_MAX(ext->range_max, (ext->range_min < 0 ? (-ext->range_min) : ext->range_min)); + int32_t new_step = ext->step * 10; + if(new_step <= step_limit) ext->step = new_step; + + lv_spinbox_updatevalue(spinbox); +} + +/** + * Increment spinbox value by one step + * @param spinbox pointer to spinbox + */ +void lv_spinbox_increment(lv_obj_t * spinbox) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + if(ext->value + ext->step <= ext->range_max) { + /*Special mode when zero crossing*/ + if((ext->value + ext->step) > 0 && ext->value < 0) ext->value = -ext->value; + ext->value += ext->step; + + } else { + ext->value = ext->range_max; + } + + if(ext->value_changed_cb != NULL) ext->value_changed_cb(spinbox, ext->value); + lv_spinbox_updatevalue(spinbox); +} + +/** + * Decrement spinbox value by one step + * @param spinbox pointer to spinbox + */ +void lv_spinbox_decrement(lv_obj_t * spinbox) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + if(ext->value - ext->step >= ext->range_min) { + /*Special mode when zero crossing*/ + if((ext->value - ext->step) < 0 && ext->value > 0) ext->value = -ext->value; + ext->value -= ext->step; + } else { + ext->value = ext->range_min; + } + + if(ext->value_changed_cb != NULL) ext->value_changed_cb(spinbox, ext->value); + lv_spinbox_updatevalue(spinbox); +} + + + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Signal function of the spinbox + * @param spinbox pointer to a spinbox object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_spinbox_signal(lv_obj_t * spinbox, lv_signal_t sign, void * param) +{ + + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + lv_res_t res = LV_RES_OK; + + /* Include the ancient signal function */ + if(sign != LV_SIGNAL_CONTROLL) + { + res = ancestor_signal(spinbox, sign, param); + if(res != LV_RES_OK) return res; + } + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } else if(sign == LV_SIGNAL_GET_TYPE) + { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) + { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_spinbox"; + } + else if(sign == LV_SIGNAL_CONTROLL) { + lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + + uint32_t c = *((uint32_t *)param); /*uint32_t because can be UTF-8*/ + if(c == LV_GROUP_KEY_RIGHT) { + if(indev_type == LV_INDEV_TYPE_ENCODER) lv_spinbox_increment(spinbox); + else lv_spinbox_step_next(spinbox); + } + else if(c == LV_GROUP_KEY_LEFT) { + if(indev_type == LV_INDEV_TYPE_ENCODER) lv_spinbox_decrement(spinbox); + else lv_spinbox_step_previous(spinbox); + } + else if(c == LV_GROUP_KEY_UP) { + lv_spinbox_increment(spinbox); + } + else if(c == LV_GROUP_KEY_DOWN) { + lv_spinbox_decrement(spinbox); + } + else if(c == LV_GROUP_KEY_ENTER) { + + if(ext->step > 1) { + lv_spinbox_step_next(spinbox); + } else { + /*Restart from the MSB*/ + ext->step = 1; + uint32_t i; + for(i = 0; i < ext->digit_count; i++) { + int32_t new_step = ext->step * 10; + if(new_step >= ext->range_max) break; + ext->step = new_step; + } + lv_spinbox_step_previous(spinbox); + } + } + else { + lv_ta_add_char(spinbox, c); + } + } + + return res; +} + +static void lv_spinbox_updatevalue(lv_obj_t * spinbox) +{ + lv_spinbox_ext_t * ext = lv_obj_get_ext_attr(spinbox); + + char buf[LV_SPINBOX_MAX_DIGIT_COUNT + 8]; + memset(buf, 0, sizeof(buf)); + char * buf_p = buf; + + /*Add the sign*/ + (*buf_p) = ext->value >= 0 ? '+' : '-'; + buf_p++; + + int i; + /*padding left*/ + for(i = 0; i < ext->digit_padding_left; i++) { + (*buf_p) = ' '; + buf_p++; + } + + char digits[64]; + /*Convert the numbers to string (the sign is already handled so always covert positive number)*/ + lv_math_num_to_str(ext->value < 0 ? -ext->value : ext->value, digits); + + /*Add leading zeros*/ + int lz_cnt = ext->digit_count - (int)strlen(digits); + if(lz_cnt > 0) { + for(i = strlen(digits); i >= 0; i--) { + digits[i + lz_cnt] = digits[i]; + } + for(i = 0; i < lz_cnt; i++) { + digits[i] = '0'; + } + } + + int32_t intDigits; + intDigits = (ext->dec_point_pos == 0) ? ext->digit_count : ext->dec_point_pos; + + /*Add the decimal part*/ + for(i = 0; i < intDigits && digits[i] != '\0'; i++) { + (*buf_p) = digits[i]; + buf_p++; + } + + if(ext->dec_point_pos != 0) { + /*Insert the decimal point*/ + (*buf_p) = '.'; + buf_p++; + + for(/*Leave i*/ ;i < ext->digit_count && digits[i] != '\0'; i++) { + (*buf_p) = digits[i]; + buf_p++; + } + } + + /*Refresh the text*/ + lv_ta_set_text(spinbox, (char*)buf); + + + /*Set the cursor position*/ + int32_t step = ext->step; + uint8_t cur_pos = ext->digit_count; + while(step >= 10) + { + step /= 10; + cur_pos--; + } + + if(cur_pos > intDigits ) cur_pos ++; /*Skip teh decimal point*/ + + cur_pos += ext->digit_padding_left; + + lv_ta_set_cursor_pos(spinbox, cur_pos); +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_spinbox.h b/bdk/libs/lvgl/lv_objx/lv_spinbox.h new file mode 100644 index 00000000..ca576148 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_spinbox.h @@ -0,0 +1,201 @@ +/** + * @file lv_spinbox.h + * + */ + + +#ifndef LV_SPINBOX_H +#define LV_SPINBOX_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_SPINBOX != 0 + +/*Testing of dependencies*/ +#if USE_LV_TA == 0 +#error "lv_spinbox: lv_ta is required. Enable it in lv_conf.h (USE_LV_TA 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "../lv_objx/lv_ta.h" + +/********************* + * DEFINES + *********************/ +#define LV_SPINBOX_MAX_DIGIT_COUNT 16 + +/********************** + * TYPEDEFS + **********************/ + +/*callback on value change*/ +typedef void (*lv_spinbox_value_changed_cb_t)(lv_obj_t * spinbox, int32_t new_value); + +/*Data of spinbox*/ +typedef struct { + lv_ta_ext_t ta; /*Ext. of ancestor*/ + /*New data for this type */ + int32_t value; + int32_t range_max; + int32_t range_min; + int32_t step; + uint16_t digit_count:4; + uint16_t dec_point_pos:4; /*if 0, there is no separator and the number is an integer*/ + uint16_t digit_padding_left:4; + lv_spinbox_value_changed_cb_t value_changed_cb; +} lv_spinbox_ext_t; + + +/*Styles*/ +enum { + LV_SPINBOX_STYLE_BG, + LV_SPINBOX_STYLE_SB, + LV_SPINBOX_STYLE_CURSOR, +}; +typedef uint8_t lv_spinbox_style_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a spinbox objects + * @param par pointer to an object, it will be the parent of the new spinbox + * @param copy pointer to a spinbox object, if not NULL then the new object will be copied from it + * @return pointer to the created spinbox + */ +lv_obj_t * lv_spinbox_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a style of a spinbox. + * @param templ pointer to template object + * @param type which style should be set + * @param style pointer to a style + */ +static inline void lv_spinbox_set_style(lv_obj_t * spinbox, lv_spinbox_style_t type, lv_style_t *style) +{ + lv_ta_set_style(spinbox, type, style); +} + +/** + * Set spinbox value + * @param spinbox pointer to spinbox + * @param i value to be set + */ +void lv_spinbox_set_value(lv_obj_t * spinbox, int32_t i); + +/** + * Set spinbox digit format (digit count and decimal format) + * @param spinbox pointer to spinbox + * @param digit_count number of digit excluding the decimal separator and the sign + * @param separator_position number of digit before the decimal point. If 0, decimal point is not shown + */ +void lv_spinbox_set_digit_format(lv_obj_t * spinbox, uint8_t digit_count, uint8_t separator_position); + +/** + * Set spinbox step + * @param spinbox pointer to spinbox + * @param step steps on increment/decrement + */ +void lv_spinbox_set_step(lv_obj_t * spinbox, uint32_t step); + +/** + * Set spinbox value range + * @param spinbox pointer to spinbox + * @param range_min maximum value, inclusive + * @param range_max minimum value, inclusive + */ +void lv_spinbox_set_range(lv_obj_t * spinbox, int32_t range_min, int32_t range_max); + +/** + * Set spinbox callback on calue change + * @param spinbox pointer to spinbox + * @param cb Callback function called on value change event + */ +void lv_spinbox_set_value_changed_cb(lv_obj_t * spinbox, lv_spinbox_value_changed_cb_t cb); + +/** + * Set spinbox left padding in digits count (added between sign and first digit) + * @param spinbox pointer to spinbox + * @param cb Callback function called on value change event + */ +void lv_spinbox_set_padding_left(lv_obj_t * spinbox, uint8_t padding); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get style of a spinbox. + * @param templ pointer to template object + * @param type which style should be get + * @return style pointer to the style + */ +static inline lv_style_t * lv_spinbox_get_style(lv_obj_t * spinbox, lv_spinbox_style_t type) +{ + return lv_ta_get_style(spinbox, type); +} + +/** + * Get the spinbox numeral value (user has to convert to float according to its digit format) + * @param spinbox pointer to spinbox + * @return value integer value of the spinbox + */ +int32_t lv_spinbox_get_value(lv_obj_t * spinbox); + +/*===================== + * Other functions + *====================*/ + +/** + * Select next lower digit for edition by dividing the step by 10 + * @param spinbox pointer to spinbox + */ +void lv_spinbox_step_next(lv_obj_t * spinbox); + +/** + * Select next higher digit for edition by multiplying the step by 10 + * @param spinbox pointer to spinbox + */ +void lv_spinbox_step_previous(lv_obj_t * spinbox); + +/** + * Increment spinbox value by one step + * @param spinbox pointer to spinbox + */ +void lv_spinbox_increment(lv_obj_t * spinbox); + +/** + * Decrement spinbox value by one step + * @param spinbox pointer to spinbox + */ +void lv_spinbox_decrement(lv_obj_t * spinbox); + + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_SPINBOX*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_SPINBOX_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_sw.c b/bdk/libs/lvgl/lv_objx/lv_sw.c new file mode 100644 index 00000000..2b09354a --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_sw.c @@ -0,0 +1,445 @@ +/** + * @file lv_sw.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_sw.h" + +#if USE_LV_SW != 0 + +/*Testing of dependencies*/ +#if USE_LV_SLIDER == 0 +#error "lv_sw: lv_slider is required. Enable it in lv_conf.h (USE_LV_SLIDER 1) " +#endif + +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param); +static void lv_sw_anim_to_value(lv_obj_t * sw, int16_t value); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a switch objects + * @param par pointer to an object, it will be the parent of the new switch + * @param copy pointer to a switch object, if not NULL then the new object will be copied from it + * @return pointer to the created switch + */ +lv_obj_t * lv_sw_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("switch create started"); + + /*Create the ancestor of switch*/ + lv_obj_t * new_sw = lv_slider_create(par, copy); + lv_mem_assert(new_sw); + if(new_sw == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_sw); + + /*Allocate the switch type specific extended data*/ + lv_sw_ext_t * ext = lv_obj_allocate_ext_attr(new_sw, sizeof(lv_sw_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + /*Initialize the allocated 'ext' */ + ext->changed = 0; +#if USE_LV_ANIMATION + ext->anim_time = 0; +#endif + ext->style_knob_off = ext->slider.style_knob; + ext->style_knob_on = ext->slider.style_knob; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_sw, lv_sw_signal); + + /*Init the new switch switch*/ + if(copy == NULL) { + lv_slider_set_range(new_sw, 0, 1); + lv_obj_set_size(new_sw, 2 * LV_DPI / 3, LV_DPI / 3); + lv_slider_set_knob_in(new_sw, true); + lv_slider_set_range(new_sw, 0, LV_SWITCH_SLIDER_ANIM_MAX); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_sw_set_style(new_sw, LV_SW_STYLE_BG, th->sw.bg); + lv_sw_set_style(new_sw, LV_SW_STYLE_INDIC, th->sw.indic); + lv_sw_set_style(new_sw, LV_SW_STYLE_KNOB_OFF, th->sw.knob_off); + lv_sw_set_style(new_sw, LV_SW_STYLE_KNOB_ON, th->sw.knob_on); + } else { + /*Let the slider' style*/ + } + + } + /*Copy an existing switch*/ + else { + lv_sw_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->style_knob_off = copy_ext->style_knob_off; + ext->style_knob_on = copy_ext->style_knob_on; +#if USE_LV_ANIMATION + ext->anim_time = copy_ext->anim_time; +#endif + + if(lv_sw_get_state(new_sw)) lv_slider_set_style(new_sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on); + else lv_slider_set_style(new_sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off); + + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_sw); + } + + LV_LOG_INFO("switch created"); + + return new_sw; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Turn ON the switch + * @param sw pointer to a switch object + */ +void lv_sw_on(lv_obj_t * sw) +{ + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + lv_slider_set_value(sw, LV_SWITCH_SLIDER_ANIM_MAX); + + lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on); +} + +/** + * Turn OFF the switch + * @param sw pointer to a switch object + */ +void lv_sw_off(lv_obj_t * sw) +{ + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + lv_slider_set_value(sw, 0); + + lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off); +} + +/** + * Toggle the position of the switch + * @param sw pointer to a switch object + * @return resulting state of the switch. + */ +bool lv_sw_toggle(lv_obj_t *sw) { + bool state = lv_sw_get_state(sw); + if(state) { + lv_sw_off(sw); + } + else { + lv_sw_on(sw); + } + return !state; +} + +/** + * Turn ON the switch with an animation + * @param sw pointer to a switch object + */ +void lv_sw_on_anim(lv_obj_t * sw) +{ + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + if(lv_sw_get_anim_time(sw) > 0)lv_sw_anim_to_value(sw, LV_SWITCH_SLIDER_ANIM_MAX); + else lv_slider_set_value(sw, LV_SWITCH_SLIDER_ANIM_MAX); + + lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on); +} + +/** + * Turn OFF the switch with an animation + * @param sw pointer to a switch object + */ +void lv_sw_off_anim(lv_obj_t * sw) +{ + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + if(lv_sw_get_anim_time(sw) > 0) lv_sw_anim_to_value(sw, 0); + else lv_slider_set_value(sw, 0); + + lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off); +} + +/** + * Toggle the position of the switch with an animation + * @param sw pointer to a switch object + * @return resulting state of the switch. + */ +bool lv_sw_toggle_anim(lv_obj_t *sw) { + bool state = lv_sw_get_state(sw); + if(state) { + lv_sw_off_anim(sw); + } + else { + lv_sw_on_anim(sw); + } + return !state; +} + +/** + * Set a style of a switch + * @param sw pointer to a switch object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_sw_set_style(lv_obj_t * sw, lv_sw_style_t type, lv_style_t * style) +{ + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + + switch(type) { + case LV_SLIDER_STYLE_BG: + lv_slider_set_style(sw, LV_SLIDER_STYLE_BG, style); + break; + case LV_SLIDER_STYLE_INDIC: + lv_bar_set_style(sw, LV_SLIDER_STYLE_INDIC, style); + break; + case LV_SW_STYLE_KNOB_OFF: + ext->style_knob_off = style; + if(lv_sw_get_state(sw) == 0) lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, style); + break; + case LV_SW_STYLE_KNOB_ON: + ext->style_knob_on = style; + if(lv_sw_get_state(sw) != 0) lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, style); + break; + } +} + +void lv_sw_set_anim_time(lv_obj_t *sw, uint16_t anim_time) +{ +#if USE_LV_ANIMATION + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + ext->anim_time = anim_time; +#endif +} + + +/*===================== + * Getter functions + *====================*/ + +/** + * Get a style of a switch + * @param sw pointer to a switch object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_sw_get_style(const lv_obj_t * sw, lv_sw_style_t type) +{ + lv_style_t * style = NULL; + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + + switch(type) { + case LV_SW_STYLE_BG: + style = lv_slider_get_style(sw, LV_SLIDER_STYLE_BG); + break; + case LV_SW_STYLE_INDIC: + style = lv_slider_get_style(sw, LV_SLIDER_STYLE_INDIC); + break; + case LV_SW_STYLE_KNOB_OFF: + style = ext->style_knob_off; + break; + case LV_SW_STYLE_KNOB_ON: + style = ext->style_knob_on; + break; + default: + style = NULL; + break; + } + + return style; +} + +uint16_t lv_sw_get_anim_time(const lv_obj_t *sw) +{ + +#if USE_LV_ANIMATION + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + return ext->anim_time; +#else + return 0; +#endif +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Signal function of the switch + * @param sw pointer to a switch object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_sw_signal(lv_obj_t * sw, lv_signal_t sign, void * param) +{ + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + + /*Save the current (old) value before slider signal modifies it*/ + int16_t old_val; + + if(sign == LV_SIGNAL_PRESSING) old_val = ext->slider.drag_value; + else old_val = lv_slider_get_value(sw); + + /*Do not let the slider to call the callback. The Switch will do it if required*/ + lv_action_t slider_action = ext->slider.action; + ext->slider.action = NULL; + + lv_res_t res; + /* Include the ancient signal function */ + res = ancestor_signal(sw, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } + else if(sign == LV_SIGNAL_PRESSED) { + + /*Save the x coordinate of the pressed point to see if the switch was slid*/ + lv_indev_t * indev = lv_indev_get_act(); + if(indev) { + lv_point_t p; + lv_indev_get_point(indev, &p); + ext->start_x = p.x; + } + ext->slided = 0; + ext->changed = 0; + } + else if(sign == LV_SIGNAL_PRESSING) { + /*See if the switch was slid*/ + lv_indev_t * indev = lv_indev_get_act(); + if(indev) { + lv_point_t p = {0,0}; + lv_indev_get_point(indev, &p); + if(LV_MATH_ABS(p.x - ext->start_x) > LV_INDEV_DRAG_LIMIT) ext->slided = 1; + } + + /*If didn't slide then revert the min/max value. So click without slide won't move the switch as a slider*/ + if(ext->slided == 0) { + if(lv_sw_get_state(sw)) ext->slider.drag_value = LV_SWITCH_SLIDER_ANIM_MAX; + else ext->slider.drag_value = 0; + } + + /*If explicitly changed (by slide) don't need to be toggled on release*/ + int16_t threshold = LV_SWITCH_SLIDER_ANIM_MAX / 2; + if((old_val < threshold && ext->slider.drag_value > threshold) || + (old_val > threshold && ext->slider.drag_value < threshold)) + { + ext->changed = 1; + } + } + else if(sign == LV_SIGNAL_PRESS_LOST) { + if(lv_sw_get_state(sw)) { + lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_on); +#if USE_LV_ANIMATION + lv_sw_anim_to_value(sw, LV_SWITCH_SLIDER_ANIM_MAX); +#endif + } + else { + lv_slider_set_style(sw, LV_SLIDER_STYLE_KNOB, ext->style_knob_off); +#if USE_LV_ANIMATION + lv_sw_anim_to_value(sw, 0); +#endif + } + } + else if(sign == LV_SIGNAL_RELEASED) { + /*If not dragged then toggle the switch*/ + if(ext->changed == 0) { + if(lv_sw_get_state(sw)) lv_sw_off_anim(sw); + else lv_sw_on_anim(sw); + + if(slider_action != NULL) res = slider_action(sw); + } + /*If the switch was dragged then calculate the new state based on the current position*/ + else { + int16_t v = lv_slider_get_value(sw); + if(v > LV_SWITCH_SLIDER_ANIM_MAX / 2) lv_sw_on_anim(sw); + else lv_sw_off_anim(sw); + + if(slider_action != NULL) res = slider_action(sw); + } + + } else if(sign == LV_SIGNAL_CONTROLL) { + + char c = *((char *)param); + if(c == LV_GROUP_KEY_ENTER) { + if(old_val) lv_sw_off_anim(sw); + else lv_sw_on_anim(sw); + + if(slider_action) res = slider_action(sw); + } else if(c == LV_GROUP_KEY_UP || c == LV_GROUP_KEY_RIGHT) { + lv_sw_on_anim(sw); + if(slider_action) res = slider_action(sw); + } else if(c == LV_GROUP_KEY_DOWN || c == LV_GROUP_KEY_LEFT) { + lv_sw_off_anim(sw); + if(slider_action) res = slider_action(sw); + } + } else if(sign == LV_SIGNAL_GET_EDITABLE) { + bool * editable = (bool *)param; + *editable = false; /*The ancestor slider is editable the switch is not*/ + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_sw"; + } + + /*Restore the callback*/ + if(res == LV_RES_OK) ext->slider.action = slider_action; + + return res; +} + +static void lv_sw_anim_to_value(lv_obj_t * sw, int16_t value) +{ +#if USE_LV_ANIMATION + lv_anim_t a; + lv_sw_ext_t * ext = lv_obj_get_ext_attr(sw); + a.var = sw; + a.start = ext->slider.bar.cur_value; + a.end = value; + a.fp = (lv_anim_fp_t)lv_slider_set_value; + a.path = lv_anim_path_linear; + a.end_cb = NULL; + a.act_time = 0; + a.time = lv_sw_get_anim_time(sw); + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); +#endif +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_sw.h b/bdk/libs/lvgl/lv_objx/lv_sw.h new file mode 100644 index 00000000..28b22f73 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_sw.h @@ -0,0 +1,194 @@ +/** + * @file lv_sw.h + * + */ + +#ifndef LV_SW_H +#define LV_SW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_SW != 0 + +/*Testing of dependencies*/ +#if USE_LV_SLIDER == 0 +#error "lv_sw: lv_slider is required. Enable it in lv_conf.h (USE_LV_SLIDER 1)" +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_slider.h" + +/********************* + * DEFINES + *********************/ +#define LV_SWITCH_SLIDER_ANIM_MAX 1000 + +/********************** + * TYPEDEFS + **********************/ +/*Data of switch*/ +typedef struct +{ + lv_slider_ext_t slider; /*Ext. of ancestor*/ + /*New data for this type */ + lv_style_t *style_knob_off; /*Style of the knob when the switch is OFF*/ + lv_style_t *style_knob_on; /*Style of the knob when the switch is ON (NULL to use the same as OFF)*/ + lv_coord_t start_x; + uint8_t changed :1; /*Indicates the switch state explicitly changed by drag*/ + uint8_t slided :1; +#if USE_LV_ANIMATION + uint16_t anim_time; /*switch animation time */ +#endif +} lv_sw_ext_t; + +enum { + LV_SW_STYLE_BG, + LV_SW_STYLE_INDIC, + LV_SW_STYLE_KNOB_OFF, + LV_SW_STYLE_KNOB_ON, +}; +typedef uint8_t lv_sw_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a switch objects + * @param par pointer to an object, it will be the parent of the new switch + * @param copy pointer to a switch object, if not NULL then the new object will be copied from it + * @return pointer to the created switch + */ +lv_obj_t * lv_sw_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Turn ON the switch + * @param sw pointer to a switch object + */ +void lv_sw_on(lv_obj_t *sw); + +/** + * Turn OFF the switch + * @param sw pointer to a switch object + */ +void lv_sw_off(lv_obj_t *sw); + +/** + * Toggle the position of the switch + * @param sw pointer to a switch object + * @return resulting state of the switch. + */ +bool lv_sw_toggle(lv_obj_t *sw); + +/** + * Turn ON the switch with an animation + * @param sw pointer to a switch object + */ +void lv_sw_on_anim(lv_obj_t * sw); + +/** + * Turn OFF the switch with an animation + * @param sw pointer to a switch object + */ +void lv_sw_off_anim(lv_obj_t * sw); + +/** + * Toggle the position of the switch with an animation + * @param sw pointer to a switch object + * @return resulting state of the switch. + */ +bool lv_sw_toggle_anim(lv_obj_t *sw); + +/** + * Set a function which will be called when the switch is toggled by the user + * @param sw pointer to switch object + * @param action a callback function + */ +static inline void lv_sw_set_action(lv_obj_t * sw, lv_action_t action) +{ + lv_slider_set_action(sw, action); +} + +/** + * Set a style of a switch + * @param sw pointer to a switch object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_sw_set_style(lv_obj_t *sw, lv_sw_style_t type, lv_style_t *style); + +#if USE_LV_ANIMATION +/** + * Set the animation time of the switch + * @param sw pointer to a switch object + * @param anim_time animation time + * @return style pointer to a style + */ +void lv_sw_set_anim_time(lv_obj_t *sw, uint16_t anim_time); +#endif + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the state of a switch + * @param sw pointer to a switch object + * @return false: OFF; true: ON + */ +static inline bool lv_sw_get_state(const lv_obj_t *sw) +{ + return lv_bar_get_value(sw) < LV_SWITCH_SLIDER_ANIM_MAX / 2 ? false : true; +} + +/** + * Get the switch action function + * @param slider pointer to a switch object + * @return the callback function + */ +static inline lv_action_t lv_sw_get_action(const lv_obj_t * slider) +{ + return lv_slider_get_action(slider); +} + +/** + * Get a style of a switch + * @param sw pointer to a switch object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_sw_get_style(const lv_obj_t *sw, lv_sw_style_t type); + +/** + * Get the animation time of the switch + * @param sw pointer to a switch object + * @return style pointer to a style + */ +uint16_t lv_sw_get_anim_time(const lv_obj_t *sw); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_SW*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_SW_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_ta.c b/bdk/libs/lvgl/lv_objx/lv_ta.c new file mode 100644 index 00000000..e3492a03 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_ta.c @@ -0,0 +1,1365 @@ +/** + * @file lv_ta.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "lv_ta.h" +#if USE_LV_TA != 0 + +#include "../lv_core/lv_group.h" +#include "../lv_core/lv_refr.h" +#include "../lv_draw/lv_draw.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_anim.h" +#include "../lv_misc/lv_txt.h" +#include "../lv_misc/lv_math.h" + +/********************* + * DEFINES + *********************/ +/*Test configuration*/ + +#ifndef LV_TA_CURSOR_BLINK_TIME +#define LV_TA_CURSOR_BLINK_TIME 400 /*ms*/ +#endif + +#ifndef LV_TA_PWD_SHOW_TIME +#define LV_TA_PWD_SHOW_TIME 1500 /*ms*/ +#endif + +#define LV_TA_DEF_WIDTH (2 * LV_DPI) +#define LV_TA_DEF_HEIGHT (1 * LV_DPI) + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_ta_design(lv_obj_t * ta, const lv_area_t * mask, lv_design_mode_t mode); +static bool lv_ta_scrollable_design(lv_obj_t * scrl, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_ta_signal(lv_obj_t * ta, lv_signal_t sign, void * param); +static lv_res_t lv_ta_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, void * param); +#if USE_LV_ANIMATION +static void cursor_blink_anim(lv_obj_t * ta, uint8_t show); +static void pwd_char_hider_anim(lv_obj_t * ta, int32_t x); +#endif +static void pwd_char_hider(lv_obj_t * ta); +static bool char_is_accepted(lv_obj_t * ta, uint32_t c); +static void get_cursor_style(lv_obj_t * ta, lv_style_t * style_res); +static void refr_cursor_area(lv_obj_t * ta); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_design_func_t ancestor_design; +static lv_design_func_t scrl_design; +static lv_signal_func_t ancestor_signal; +static lv_signal_func_t scrl_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a text area objects + * @param par pointer to an object, it will be the parent of the new text area + * @param copy pointer to a text area object, if not NULL then the new object will be copied from it + * @return pointer to the created text area + */ +lv_obj_t * lv_ta_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("text area create started"); + + /*Create the ancestor object*/ + lv_obj_t * new_ta = lv_page_create(par, copy); + lv_mem_assert(new_ta); + if(new_ta == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_ta); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_ta); + if(scrl_signal == NULL) scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(new_ta)); + if(scrl_design == NULL) scrl_design = lv_obj_get_design_func(lv_page_get_scrl(new_ta)); + + /*Allocate the object type specific extended data*/ + lv_ta_ext_t * ext = lv_obj_allocate_ext_attr(new_ta, sizeof(lv_ta_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->cursor.state = 1; + ext->pwd_mode = 0; + ext->pwd_tmp = NULL; + ext->accapted_chars = NULL; + ext->max_length = 0; + ext->cursor.style = NULL; + ext->cursor.pos = 0; + ext->cursor.type = LV_CURSOR_LINE; + ext->cursor.valid_x = 0; + ext->one_line = 0; + ext->label = NULL; + + lv_obj_set_signal_func(new_ta, lv_ta_signal); + lv_obj_set_signal_func(lv_page_get_scrl(new_ta), lv_ta_scrollable_signal); + lv_obj_set_design_func(new_ta, lv_ta_design); + + /*Init the new text area object*/ + if(copy == NULL) { + ext->label = lv_label_create(new_ta, NULL); + + lv_obj_set_design_func(ext->page.scrl, lv_ta_scrollable_design); + + lv_label_set_long_mode(ext->label, LV_LABEL_LONG_BREAK); + lv_label_set_text(ext->label, "Text area"); + lv_obj_set_click(ext->label, false); + lv_obj_set_size(new_ta, LV_TA_DEF_WIDTH, LV_TA_DEF_HEIGHT); + lv_ta_set_sb_mode(new_ta, LV_SB_MODE_DRAG); + lv_page_set_style(new_ta, LV_PAGE_STYLE_SCRL, &lv_style_transp_tight); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_ta_set_style(new_ta, LV_TA_STYLE_BG, th->ta.area); + lv_ta_set_style(new_ta, LV_TA_STYLE_SB, th->ta.sb); + } else { + lv_ta_set_style(new_ta, LV_TA_STYLE_BG, &lv_style_pretty); + } + } + /*Copy an existing object*/ + else { + lv_obj_set_design_func(ext->page.scrl, lv_ta_scrollable_design); + lv_ta_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->label = lv_label_create(new_ta, copy_ext->label); + ext->pwd_mode = copy_ext->pwd_mode; + ext->accapted_chars = copy_ext->accapted_chars; + ext->max_length = copy_ext->max_length; + ext->cursor.style = copy_ext->cursor.style; + ext->cursor.pos = copy_ext->cursor.pos; + ext->cursor.valid_x = copy_ext->cursor.valid_x; + ext->cursor.type = copy_ext->cursor.type; + if(copy_ext->one_line) lv_ta_set_one_line(new_ta, true); + + lv_ta_set_style(new_ta, LV_TA_STYLE_CURSOR, lv_ta_get_style(copy, LV_TA_STYLE_CURSOR)); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_ta); + } + +#if USE_LV_ANIMATION + /*Create a cursor blinker animation*/ + lv_anim_t a; + a.var = new_ta; + a.fp = (lv_anim_fp_t)cursor_blink_anim; + a.time = LV_TA_CURSOR_BLINK_TIME; + a.act_time = 0; + a.end_cb = NULL; + a.start = 1; + a.end = 0; + a.repeat = 1; + a.repeat_pause = 0; + a.playback = 1; + a.playback_pause = 0; + a.path = lv_anim_path_step; + lv_anim_create(&a); +#endif + + LV_LOG_INFO("text area created"); + + return new_ta; +} + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Insert a character to the current cursor position. + * To add a wide char, e.g. 'Á' use `lv_txt_encoded_conv_wc('Á')` + * @param ta pointer to a text area object + * @param c a character (e.g. 'a') + */ +void lv_ta_add_char(lv_obj_t * ta, uint32_t c) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + if(ext->one_line && (c == '\n' || c == '\r')) { + LV_LOG_INFO("Text area: line break ignored in one-line mode"); + return; + } + + uint32_t c_uni = lv_txt_encoded_next((const char *)&c, NULL); + + if(char_is_accepted(ta, c_uni) == false) { + LV_LOG_INFO("Character is no accepted by the text area (too long text or not in the accepted list)"); + return; + } + + /*Disable edge flash. If a new line was added it could show edge flash effect*/ + bool edge_flash_en = lv_ta_get_edge_flash(ta); + lv_ta_set_edge_flash(ta, false); + + if(ext->pwd_mode != 0) pwd_char_hider(ta); /*Make sure all the current text contains only '*'*/ + uint32_t letter_buf[2]; + letter_buf[0] = c; + letter_buf[1] = '\0'; + + lv_label_ins_text(ext->label, ext->cursor.pos, (const char *)letter_buf); /*Insert the character*/ + + if(ext->pwd_mode != 0) { + + ext->pwd_tmp = lv_mem_realloc(ext->pwd_tmp, strlen(ext->pwd_tmp) + 2); /*+2: the new char + \0 */ + lv_mem_assert(ext->pwd_tmp); + if(ext->pwd_tmp == NULL) return; + + lv_txt_ins(ext->pwd_tmp, ext->cursor.pos, (const char *)letter_buf); + +#if USE_LV_ANIMATION && LV_TA_PWD_SHOW_TIME > 0 + /*Auto hide characters*/ + lv_anim_t a; + a.var = ta; + a.fp = (lv_anim_fp_t)pwd_char_hider_anim; + a.time = LV_TA_PWD_SHOW_TIME; + a.act_time = 0; + a.end_cb = (lv_anim_cb_t)pwd_char_hider; + a.start = 0; + a.end = 1; + a.repeat = 0; + a.repeat_pause = 0; + a.playback = 0; + a.playback_pause = 0; + a.path = lv_anim_path_step; + lv_anim_create(&a); +#else + pwd_char_hider(ta); +#endif + } + + /*Move the cursor after the new character*/ + lv_ta_set_cursor_pos(ta, lv_ta_get_cursor_pos(ta) + 1); + + /*Revert the original edge flash state*/ + lv_ta_set_edge_flash(ta, edge_flash_en); +} + +/** + * Insert a text to the current cursor position + * @param ta pointer to a text area object + * @param txt a '\0' terminated string to insert + */ +void lv_ta_add_text(lv_obj_t * ta, const char * txt) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + if(ext->pwd_mode != 0) pwd_char_hider(ta); /*Make sure all the current text contains only '*'*/ + + /*Add the character one-by-one if not all characters are accepted or there is character limit.*/ + if(lv_ta_get_accepted_chars(ta) || lv_ta_get_max_length(ta)) { + uint32_t i = 0; + while(txt[i] != '\0') { + uint32_t c = lv_txt_encoded_next(txt, &i); + lv_ta_add_char(ta, lv_txt_unicode_to_encoded(c)); + } + return; + } + + /*Disable edge flash. If a new line was added it could show edge flash effect*/ + bool edge_flash_en = lv_ta_get_edge_flash(ta); + lv_ta_set_edge_flash(ta, false); + + /*Insert the text*/ + lv_label_ins_text(ext->label, ext->cursor.pos, txt); + + if(ext->pwd_mode != 0) { + ext->pwd_tmp = lv_mem_realloc(ext->pwd_tmp, strlen(ext->pwd_tmp) + strlen(txt) + 1); + lv_mem_assert(ext->pwd_tmp); + if(ext->pwd_tmp == NULL) return; + + lv_txt_ins(ext->pwd_tmp, ext->cursor.pos, txt); + +#if USE_LV_ANIMATION && LV_TA_PWD_SHOW_TIME > 0 + /*Auto hide characters*/ + lv_anim_t a; + a.var = ta; + a.fp = (lv_anim_fp_t)pwd_char_hider_anim; + a.time = LV_TA_PWD_SHOW_TIME; + a.act_time = 0; + a.end_cb = (lv_anim_cb_t)pwd_char_hider; + a.start = 0; + a.end = 1; + a.repeat = 0; + a.repeat_pause = 0; + a.playback = 0; + a.playback_pause = 0; + a.path = lv_anim_path_step; + lv_anim_create(&a); +#else + pwd_char_hider(ta); +#endif + } + + /*Move the cursor after the new text*/ + lv_ta_set_cursor_pos(ta, lv_ta_get_cursor_pos(ta) + lv_txt_get_encoded_length(txt)); + + /*Revert the original edge flash state*/ + lv_ta_set_edge_flash(ta, edge_flash_en); +} + +/** + * Delete a the left character from the current cursor position + * @param ta pointer to a text area object + */ +void lv_ta_del_char(lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + uint16_t cur_pos = ext->cursor.pos; + + if(cur_pos == 0) return; + + char * label_txt = lv_label_get_text(ext->label); + /*Delete a character*/ + lv_txt_cut(label_txt, ext->cursor.pos - 1, 1); + /*Refresh the label*/ + lv_label_set_text(ext->label, label_txt); + + /*Don't let 'width == 0' because cursor will not be visible*/ + if(lv_obj_get_width(ext->label) == 0) { + lv_style_t * style = lv_obj_get_style(ext->label); + lv_obj_set_width(ext->label, style->line.width); + } + + if(ext->pwd_mode != 0) { +#if LV_TXT_UTF8 == 0 + lv_txt_cut(ext->pwd_tmp, ext->cursor.pos - 1, 1); +#else + uint32_t byte_pos = lv_txt_encoded_get_byte_id(ext->pwd_tmp, ext->cursor.pos - 1); + lv_txt_cut(ext->pwd_tmp, ext->cursor.pos - 1, lv_txt_encoded_size(&label_txt[byte_pos])); +#endif + ext->pwd_tmp = lv_mem_realloc(ext->pwd_tmp, strlen(ext->pwd_tmp) + 1); + lv_mem_assert(ext->pwd_tmp); + if(ext->pwd_tmp == NULL) return; + } + + /*Move the cursor to the place of the deleted character*/ + lv_ta_set_cursor_pos(ta, ext->cursor.pos - 1); +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the text of a text area + * @param ta pointer to a text area + * @param txt pointer to the text + */ +void lv_ta_set_text(lv_obj_t * ta, const char * txt) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + /*Add the character one-by-one if not all characters are accepted or there is character limit.*/ + if(lv_ta_get_accepted_chars(ta) || lv_ta_get_max_length(ta)) { + lv_label_set_text(ext->label, ""); + lv_ta_set_cursor_pos(ta, LV_TA_CURSOR_LAST); + + uint32_t i = 0; + while(txt[i] != '\0') { + uint32_t c = lv_txt_encoded_next(txt, &i); + lv_ta_add_char(ta, lv_txt_unicode_to_encoded(c)); + } + } else { + lv_label_set_text(ext->label, txt); + lv_ta_set_cursor_pos(ta, LV_TA_CURSOR_LAST); + } + + /*Don't let 'width == 0' because the cursor will not be visible*/ + if(lv_obj_get_width(ext->label) == 0) { + lv_style_t * style = lv_obj_get_style(ext->label); + lv_obj_set_width(ext->label, lv_font_get_width(style->text.font, ' ')); + } + + if(ext->pwd_mode != 0) { + ext->pwd_tmp = lv_mem_realloc(ext->pwd_tmp, strlen(txt) + 1); + lv_mem_assert(ext->pwd_tmp); + if(ext->pwd_tmp == NULL) return; + strcpy(ext->pwd_tmp, txt); + +#if USE_LV_ANIMATION && LV_TA_PWD_SHOW_TIME > 0 + /*Auto hide characters*/ + lv_anim_t a; + a.var = ta; + a.fp = (lv_anim_fp_t)pwd_char_hider_anim; + a.time = LV_TA_PWD_SHOW_TIME; + a.act_time = 0; + a.end_cb = (lv_anim_cb_t)pwd_char_hider; + a.start = 0; + a.end = 1; + a.repeat = 0; + a.repeat_pause = 0; + a.playback = 0; + a.playback_pause = 0; + a.path = lv_anim_path_step; + lv_anim_create(&a); +#else + pwd_char_hider(ta); +#endif + } +} + +/** + * Set the cursor position + * @param obj pointer to a text area object + * @param pos the new cursor position in character index + * < 0 : index from the end of the text + * LV_TA_CURSOR_LAST: go after the last character + */ +void lv_ta_set_cursor_pos(lv_obj_t * ta, int16_t pos) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + if(ext->cursor.pos == pos) return; + + uint16_t len = lv_txt_get_encoded_length(lv_label_get_text(ext->label)); + + if(pos < 0) pos = len + pos; + + if(pos > len || pos == LV_TA_CURSOR_LAST) pos = len; + + ext->cursor.pos = pos; + + /*Position the label to make the cursor visible*/ + lv_obj_t * label_par = lv_obj_get_parent(ext->label); + lv_point_t cur_pos; + lv_style_t * style = lv_obj_get_style(ta); + const lv_font_t * font_p = style->text.font; + lv_area_t label_cords; + lv_area_t ta_cords; + lv_label_get_letter_pos(ext->label, pos, &cur_pos); + lv_obj_get_coords(ta, &ta_cords); + lv_obj_get_coords(ext->label, &label_cords); + + /*Check the top*/ + lv_coord_t font_h = lv_font_get_height(font_p); + if(lv_obj_get_y(label_par) + cur_pos.y < 0) { + lv_obj_set_y(label_par, - cur_pos.y + style->body.padding.ver); + } + + /*Check the bottom*/ + if(label_cords.y1 + cur_pos.y + font_h + style->body.padding.ver > ta_cords.y2) { + lv_obj_set_y(label_par, -(cur_pos.y - lv_obj_get_height(ta) + + font_h + 2 * style->body.padding.ver)); + } + /*Check the left (use the font_h as general unit)*/ + if(lv_obj_get_x(label_par) + cur_pos.x < font_h) { + lv_obj_set_x(label_par, - cur_pos.x + font_h); + } + + /*Check the right (use the font_h as general unit)*/ + if(label_cords.x1 + cur_pos.x + font_h + style->body.padding.hor > ta_cords.x2) { + lv_obj_set_x(label_par, -(cur_pos.x - lv_obj_get_width(ta) + + font_h + 2 * style->body.padding.hor)); + } + + ext->cursor.valid_x = cur_pos.x; + +#if USE_LV_ANIMATION + /*Reset cursor blink animation*/ + lv_anim_t a; + a.var = ta; + a.fp = (lv_anim_fp_t)cursor_blink_anim; + a.time = LV_TA_CURSOR_BLINK_TIME; + a.act_time = 0; + a.end_cb = NULL; + a.start = 1; + a.end = 0; + a.repeat = 1; + a.repeat_pause = 0; + a.playback = 1; + a.playback_pause = 0; + a.path = lv_anim_path_step; + lv_anim_create(&a); +#endif + + refr_cursor_area(ta); +} + +/** + * Set the cursor type. + * @param ta pointer to a text area object + * @param cur_type: element of 'lv_ta_cursor_type_t' + */ +void lv_ta_set_cursor_type(lv_obj_t * ta, lv_cursor_type_t cur_type) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + if(ext->cursor.type == cur_type) return; + + ext->cursor.type = cur_type; + + refr_cursor_area(ta); +} + +/** + * Enable/Disable password mode + * @param ta pointer to a text area object + * @param en true: enable, false: disable + */ +void lv_ta_set_pwd_mode(lv_obj_t * ta, bool en) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + if(ext->pwd_mode == en) return; + + /*Pwd mode is now enabled*/ + if(ext->pwd_mode == 0 && en != false) { + char * txt = lv_label_get_text(ext->label); + uint16_t len = strlen(txt); + ext->pwd_tmp = lv_mem_alloc(len + 1); + lv_mem_assert(ext->pwd_tmp); + if(ext->pwd_tmp == NULL) return; + + strcpy(ext->pwd_tmp, txt); + + uint16_t i; + for(i = 0; i < len; i++) { + txt[i] = '*'; /*All char to '*'*/ + } + txt[i] = '\0'; + + lv_label_set_text(ext->label, NULL); + } + /*Pwd mode is now disabled*/ + else if(ext->pwd_mode == 1 && en == false) { + lv_label_set_text(ext->label, ext->pwd_tmp); + lv_mem_free(ext->pwd_tmp); + ext->pwd_tmp = NULL; + } + + ext->pwd_mode = en == false ? 0 : 1; + + refr_cursor_area(ta); +} + +/** + * Configure the text area to one line or back to normal + * @param ta pointer to a Text area object + * @param en true: one line, false: normal + */ +void lv_ta_set_one_line(lv_obj_t * ta, bool en) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + if(ext->one_line == en) return; + + if(en) { + lv_style_t * style_ta = lv_obj_get_style(ta); + lv_style_t * style_scrl = lv_obj_get_style(lv_page_get_scrl(ta)); + lv_style_t * style_label = lv_obj_get_style(ext->label); + lv_coord_t font_h = lv_font_get_height(style_label->text.font); + + ext->one_line = 1; + lv_page_set_scrl_fit(ta, true, true); + lv_obj_set_height(ta, font_h + (style_ta->body.padding.ver + style_scrl->body.padding.ver) * 2); + lv_label_set_long_mode(ext->label, LV_LABEL_LONG_EXPAND); + lv_obj_set_pos(lv_page_get_scrl(ta), style_ta->body.padding.hor, style_ta->body.padding.ver); + } else { + lv_style_t * style_ta = lv_obj_get_style(ta); + + ext->one_line = 0; + lv_page_set_scrl_fit(ta, false, true); + lv_label_set_long_mode(ext->label, LV_LABEL_LONG_BREAK); + lv_obj_set_height(ta, LV_TA_DEF_HEIGHT); + lv_obj_set_pos(lv_page_get_scrl(ta), style_ta->body.padding.hor, style_ta->body.padding.ver); + } + + refr_cursor_area(ta); +} + +/** + * Set the alignment of the text area. + * In one line mode the text can be scrolled only with `LV_LABEL_ALIGN_LEFT`. + * This function should be called if the size of text area changes. + * @param ta pointer to a text are object + * @param align the desired alignment from `lv_label_align_t`. (LV_LABEL_ALIGN_LEFT/CENTER/RIGHT) + */ +void lv_ta_set_text_align(lv_obj_t * ta, lv_label_align_t align) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + lv_obj_t * label = lv_ta_get_label(ta); + if(!ext->one_line) { + lv_label_set_align(label, align); + } else { + /*Normal left align. Just let the text expand*/ + if(align == LV_LABEL_ALIGN_LEFT) { + lv_label_set_long_mode(label, LV_LABEL_LONG_EXPAND); + lv_page_set_scrl_fit(ta, true, false); + lv_label_set_align(label, align); + + } + /*Else use fix label width equal to the Text area width*/ + else { + lv_label_set_long_mode(label, LV_LABEL_LONG_CROP); + lv_page_set_scrl_fit(ta, false, false); + lv_page_set_scrl_width(ta, 1); /*To refresh the scrollable's width*/ + lv_label_set_align(label, align); + + lv_style_t * bg_style = lv_ta_get_style(ta, LV_TA_STYLE_BG); + lv_obj_set_width(label, lv_obj_get_width(ta) - 2 * bg_style->body.padding.hor); + } + } + + refr_cursor_area(ta); +} + +/** + * Set a list of characters. Only these characters will be accepted by the text area + * @param ta pointer to Text Area + * @param list list of characters. Only the pointer is saved. E.g. "+-.,0123456789" + */ +void lv_ta_set_accepted_chars(lv_obj_t * ta, const char * list) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + ext->accapted_chars = list; +} + +/** + * Set max length of a Text Area. + * @param ta pointer to Text Area + * @param num the maximal number of characters can be added (`lv_ta_set_text` ignores it) + */ +void lv_ta_set_max_length(lv_obj_t * ta, uint16_t num) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + ext->max_length = num; +} + +/** + * Set a style of a text area + * @param ta pointer to a text area object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_ta_set_style(lv_obj_t * ta, lv_ta_style_t type, lv_style_t * style) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + switch(type) { + case LV_TA_STYLE_BG: + lv_page_set_style(ta, LV_PAGE_STYLE_BG, style); + break; + case LV_TA_STYLE_SB: + lv_page_set_style(ta, LV_PAGE_STYLE_SB, style); + break; + case LV_TA_STYLE_EDGE_FLASH: + lv_page_set_style(ta, LV_PAGE_STYLE_EDGE_FLASH, style); + break; + case LV_TA_STYLE_CURSOR: + ext->cursor.style = style; + lv_obj_refresh_ext_size(lv_page_get_scrl(ta)); /*Refresh ext. size because of cursor drawing*/ + refr_cursor_area(ta); + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the text of a text area. In password mode it gives the real text (not '*'s). + * @param ta pointer to a text area object + * @return pointer to the text + */ +const char * lv_ta_get_text(const lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + const char * txt; + if(ext->pwd_mode == 0) { + txt = lv_label_get_text(ext->label); + } else { + txt = ext->pwd_tmp; + } + + return txt; +} + + +/** + * Get the label of a text area + * @param ta pointer to a text area object + * @return pointer to the label object + */ +lv_obj_t * lv_ta_get_label(const lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + return ext->label; +} + + +/** + * Get the current cursor position in character index + * @param ta pointer to a text area object + * @return the cursor position + */ +uint16_t lv_ta_get_cursor_pos(const lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + return ext->cursor.pos; +} + +/** + * Get the current cursor type. + * @param ta pointer to a text area object + * @return element of 'lv_ta_cursor_type_t' + */ +lv_cursor_type_t lv_ta_get_cursor_type(const lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + return ext->cursor.type; +} + +/** + * Get the password mode attribute + * @param ta pointer to a text area object + * @return true: password mode is enabled, false: disabled + */ +bool lv_ta_get_pwd_mode(const lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + return ext->pwd_mode == 0 ? false : true; +} + +/** + * Get the one line configuration attribute + * @param ta pointer to a text area object + * @return true: one line configuration is enabled, false: disabled + */ +bool lv_ta_get_one_line(const lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + return ext->one_line == 0 ? false : true; +} + +/** + * Get a list of accepted characters. + * @param ta pointer to Text Area + * @return list of accented characters. + */ +const char * lv_ta_get_accepted_chars(lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + return ext->accapted_chars; +} + +/** + * Set max length of a Text Area. + * @param ta pointer to Text Area + * @return the maximal number of characters to be add + */ +uint16_t lv_ta_get_max_length(lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + return ext->max_length; +} + +/** + * Get a style of a text area + * @param ta pointer to a text area object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_ta_get_style(const lv_obj_t * ta, lv_ta_style_t type) +{ + lv_style_t * style = NULL; + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + switch(type) { + case LV_TA_STYLE_BG: + style = lv_page_get_style(ta, LV_PAGE_STYLE_BG); + break; + case LV_TA_STYLE_SB: + style = lv_page_get_style(ta, LV_PAGE_STYLE_SB); + break; + case LV_TA_STYLE_EDGE_FLASH: + style = lv_page_get_style(ta, LV_PAGE_STYLE_EDGE_FLASH); + break; + case LV_TA_STYLE_CURSOR: + style = ext->cursor.style; + break; + default: + style = NULL; + break; + } + + return style; +} + +/*===================== + * Other functions + *====================*/ + +/** + * Move the cursor one character right + * @param ta pointer to a text area object + */ +void lv_ta_cursor_right(lv_obj_t * ta) +{ + uint16_t cp = lv_ta_get_cursor_pos(ta); + cp++; + lv_ta_set_cursor_pos(ta, cp); +} + +/** + * Move the cursor one character left + * @param ta pointer to a text area object + */ +void lv_ta_cursor_left(lv_obj_t * ta) +{ + uint16_t cp = lv_ta_get_cursor_pos(ta); + if(cp > 0) { + cp--; + lv_ta_set_cursor_pos(ta, cp); + } +} + +/** + * Move the cursor one line down + * @param ta pointer to a text area object + */ +void lv_ta_cursor_down(lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + lv_point_t pos; + + /*Get the position of the current letter*/ + lv_label_get_letter_pos(ext->label, lv_ta_get_cursor_pos(ta), &pos); + + /*Increment the y with one line and keep the valid x*/ + lv_style_t * label_style = lv_obj_get_style(ext->label); + const lv_font_t * font_p = label_style->text.font; + lv_coord_t font_h = lv_font_get_height(font_p); + pos.y += font_h + label_style->text.line_space + 1; + pos.x = ext->cursor.valid_x; + + /*Do not go below the last line*/ + if(pos.y < lv_obj_get_height(ext->label)) { + /*Get the letter index on the new cursor position and set it*/ + uint16_t new_cur_pos = lv_label_get_letter_on(ext->label, &pos); + + lv_coord_t cur_valid_x_tmp = ext->cursor.valid_x; /*Cursor position set overwrites the valid positon */ + lv_ta_set_cursor_pos(ta, new_cur_pos); + ext->cursor.valid_x = cur_valid_x_tmp; + } +} + +/** + * Move the cursor one line up + * @param ta pointer to a text area object + */ +void lv_ta_cursor_up(lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + lv_point_t pos; + + /*Get the position of the current letter*/ + lv_label_get_letter_pos(ext->label, lv_ta_get_cursor_pos(ta), &pos); + + /*Decrement the y with one line and keep the valid x*/ + lv_style_t * label_style = lv_obj_get_style(ext->label); + const lv_font_t * font = label_style->text.font; + lv_coord_t font_h = lv_font_get_height(font); + pos.y -= font_h + label_style->text.line_space - 1; + pos.x = ext->cursor.valid_x; + + + /*Get the letter index on the new cursor position and set it*/ + uint16_t new_cur_pos = lv_label_get_letter_on(ext->label, &pos); + lv_coord_t cur_valid_x_tmp = ext->cursor.valid_x; /*Cursor position set overwrites the valid positon */ + lv_ta_set_cursor_pos(ta, new_cur_pos); + ext->cursor.valid_x = cur_valid_x_tmp; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the text areas + * @param ta pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW_MAIN: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_ta_design(lv_obj_t * ta, const lv_area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + /*Return false if the object is not covers the mask_p area*/ + return ancestor_design(ta, mask, mode); + } else if(mode == LV_DESIGN_DRAW_MAIN) { + /*Draw the object*/ + ancestor_design(ta, mask, mode); + + } else if(mode == LV_DESIGN_DRAW_POST) { + ancestor_design(ta, mask, mode); + } + return true; +} + + +/** + * An extended scrollable design of the page. Calls the normal design function and draws a cursor. + * @param scrl pointer to the scrollable part of the Text area + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW_MAIN: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @return return true/false, depends on 'mode' + */ +static bool lv_ta_scrollable_design(lv_obj_t * scrl, const lv_area_t * mask, lv_design_mode_t mode) +{ + if(mode == LV_DESIGN_COVER_CHK) { + /*Return false if the object is not covers the mask_p area*/ + return scrl_design(scrl, mask, mode); + } else if(mode == LV_DESIGN_DRAW_MAIN) { + /*Draw the object*/ + scrl_design(scrl, mask, mode); + } else if(mode == LV_DESIGN_DRAW_POST) { + scrl_design(scrl, mask, mode); + + /*Draw the cursor*/ + lv_obj_t * ta = lv_obj_get_parent(scrl); + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + if(ext->cursor.type == LV_CURSOR_NONE || + (ext->cursor.type & LV_CURSOR_HIDDEN) || + ext->cursor.state == 0) { + return true; /*The cursor is not visible now*/ + } + + lv_style_t cur_style; + get_cursor_style(ta, &cur_style); + + const char * txt = lv_label_get_text(ext->label); + + /*Draw he cursor according to the type*/ + lv_area_t cur_area; + lv_area_copy(&cur_area, &ext->cursor.area); + + cur_area.x1 += ext->label->coords.x1; + cur_area.y1 += ext->label->coords.y1; + cur_area.x2 += ext->label->coords.x1; + cur_area.y2 += ext->label->coords.y1; + + lv_opa_t opa_scale = lv_obj_get_opa_scale(ta); + + if(ext->cursor.type == LV_CURSOR_LINE) { + lv_draw_rect(&cur_area, mask, &cur_style, opa_scale); + } else if(ext->cursor.type == LV_CURSOR_BLOCK) { + lv_draw_rect(&cur_area, mask, &cur_style, opa_scale); + +#if LV_TXT_UTF8 == 0 + char letter_buf[2]; + letter_buf[0] = txt[ext->cursor.txt_byte_pos]; + letter_buf[1] = '\0'; +#else + char letter_buf[8] = {0}; + memcpy(letter_buf, &txt[ext->cursor.txt_byte_pos], lv_txt_encoded_size(&txt[ext->cursor.txt_byte_pos])); +#endif + cur_area.x1 += cur_style.body.padding.hor; + cur_area.y1 += cur_style.body.padding.ver; + lv_draw_label(&cur_area, mask, &cur_style, opa_scale, letter_buf, LV_TXT_FLAG_NONE, 0); + + } else if(ext->cursor.type == LV_CURSOR_OUTLINE) { + cur_style.body.empty = 1; + if(cur_style.body.border.width == 0) cur_style.body.border.width = 1; /*Be sure the border will be drawn*/ + lv_draw_rect(&cur_area, mask, &cur_style, opa_scale); + } else if(ext->cursor.type == LV_CURSOR_UNDERLINE) { + lv_draw_rect(&cur_area, mask, &cur_style, opa_scale); + } + } + + return true; +} + +/** + * Signal function of the text area + * @param ta pointer to a text area object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_ta_signal(lv_obj_t * ta, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(ta, sign, param); + if(res != LV_RES_OK) return res; + + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + if(sign == LV_SIGNAL_CLEANUP) { + if(ext->pwd_tmp != NULL) lv_mem_free(ext->pwd_tmp); + + /* (The created label will be deleted automatically) */ + } else if(sign == LV_SIGNAL_STYLE_CHG) { + if(ext->label) { + lv_obj_t * scrl = lv_page_get_scrl(ta); + lv_style_t * style_ta = lv_obj_get_style(ta); + lv_style_t * style_scrl = lv_obj_get_style(scrl); + if(ext->one_line) { + /*In one line mode refresh the Text Area height because 'vpad' can modify it*/ + lv_style_t * style_label = lv_obj_get_style(ext->label); + lv_coord_t font_h = lv_font_get_height(style_label->text.font); + lv_obj_set_height(ta, font_h + (style_ta->body.padding.ver + style_scrl->body.padding.ver) * 2); + } else { + /*In not one line mode refresh the Label width because 'hpad' can modify it*/ + lv_obj_set_width(ext->label, lv_obj_get_width(scrl) - 2 * style_scrl->body.padding.hor); + lv_obj_set_pos(ext->label, style_scrl->body.padding.hor, style_scrl->body.padding.ver); /*Be sure the Label is in the correct position*/ + } + lv_label_set_text(ext->label, NULL); + + } + } else if(sign == LV_SIGNAL_CORD_CHG) { + /*Set the label width according to the text area width*/ + if(ext->label) { + if(lv_obj_get_width(ta) != lv_area_get_width(param) || + lv_obj_get_height(ta) != lv_area_get_height(param)) { + lv_obj_t * scrl = lv_page_get_scrl(ta); + lv_style_t * style_scrl = lv_obj_get_style(scrl); + lv_obj_set_width(ext->label, lv_obj_get_width(scrl) - 2 * style_scrl->body.padding.hor); + lv_obj_set_pos(ext->label, style_scrl->body.padding.hor, style_scrl->body.padding.ver); + lv_label_set_text(ext->label, NULL); /*Refresh the label*/ + + refr_cursor_area(ta); + } + } + } else if(sign == LV_SIGNAL_CONTROLL) { + uint32_t c = *((uint32_t *)param); /*uint32_t because can be UTF-8*/ + if(c == LV_GROUP_KEY_RIGHT) lv_ta_cursor_right(ta); + else if(c == LV_GROUP_KEY_LEFT) lv_ta_cursor_left(ta); + else if(c == LV_GROUP_KEY_UP) lv_ta_cursor_up(ta); + else if(c == LV_GROUP_KEY_DOWN) lv_ta_cursor_down(ta); + else if(c == LV_GROUP_KEY_BACKSPACE) lv_ta_del_char(ta); + else if(c == LV_GROUP_KEY_DEL) { + uint16_t cp = lv_ta_get_cursor_pos(ta); + lv_ta_set_cursor_pos(ta, cp + 1); + if(cp != lv_ta_get_cursor_pos(ta)) lv_ta_del_char(ta); + } + else { + lv_ta_add_char(ta, c); + } + } else if(sign == LV_SIGNAL_GET_EDITABLE) { + bool * editable = (bool *)param; + *editable = true; + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_ta"; + } else if(sign == LV_SIGNAL_DEFOCUS) { + lv_cursor_type_t cur_type; + cur_type = lv_ta_get_cursor_type(ta); + lv_ta_set_cursor_type(ta, cur_type | LV_CURSOR_HIDDEN); + } else if(sign == LV_SIGNAL_FOCUS) { +#if USE_LV_GROUP + lv_cursor_type_t cur_type; + cur_type = lv_ta_get_cursor_type(ta); + lv_group_t * g = lv_obj_get_group(ta); + bool editing = lv_group_get_editing(g); + lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + + /*Encoders need special handling*/ + if(indev_type == LV_INDEV_TYPE_ENCODER) { + if(editing) lv_ta_set_cursor_type(ta, cur_type & (~LV_CURSOR_HIDDEN)); + else lv_ta_set_cursor_type(ta, cur_type | LV_CURSOR_HIDDEN); + } + else { + lv_ta_set_cursor_type(ta, cur_type & (~LV_CURSOR_HIDDEN)); + } +#endif + } + return res; +} + +/** + * Signal function of the scrollable part of the text area + * @param scrl pointer to scrollable part of a text area object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_ta_scrollable_signal(lv_obj_t * scrl, lv_signal_t sign, void * param) +{ + lv_res_t res; + lv_obj_t * ta = lv_obj_get_parent(scrl); + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + /* Include the ancient signal function */ + res = scrl_signal(scrl, sign, param); + if(res != LV_RES_OK) return res; + + if(sign == LV_SIGNAL_REFR_EXT_SIZE) { + /*Set ext. size because the cursor might be out of this object*/ + lv_style_t * style_label = lv_obj_get_style(ext->label); + lv_coord_t font_h = lv_font_get_height(style_label->text.font); + scrl->ext_size = LV_MATH_MAX(scrl->ext_size, style_label->text.line_space + font_h); + } +#if 0 + else if(sign == LV_SIGNAL_CORD_CHG) { + /*Set the label width according to the text area width*/ + if(ext->label) { + if(lv_obj_get_width(ta) != lv_area_get_width(param) || + lv_obj_get_height(ta) != lv_area_get_height(param)) { + lv_obj_t * scrl = lv_page_get_scrl(ta); + lv_style_t * style_scrl = lv_obj_get_style(scrl); + lv_obj_set_width(ext->label, lv_obj_get_width(scrl) - 2 * style_scrl->body.padding.hor); + lv_obj_set_pos(ext->label, style_scrl->body.padding.hor, style_scrl->body.padding.ver); + lv_label_set_text(ext->label, NULL); /*Refresh the label*/ + + refr_cursor_area(ta); + } + } + } +#endif + + return res; +} + +#if USE_LV_ANIMATION + +/** + * Called to blink the cursor + * @param ta pointer to a text area + * @param hide 1: hide the cursor, 0: show it + */ +static void cursor_blink_anim(lv_obj_t * ta, uint8_t show) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + if(show != ext->cursor.state) { + ext->cursor.state = show == 0 ? 0 : 1; + if(ext->cursor.type != LV_CURSOR_NONE && + (ext->cursor.type & LV_CURSOR_HIDDEN) == 0) + { + lv_area_t area_tmp; + lv_area_copy(&area_tmp, &ext->cursor.area); + area_tmp.x1 += ext->label->coords.x1; + area_tmp.y1 += ext->label->coords.y1; + area_tmp.x2 += ext->label->coords.x1; + area_tmp.y2 += ext->label->coords.y1; + lv_inv_area(&area_tmp); + } + } +} + + +/** + * Dummy function to animate char hiding in pwd mode. + * Does nothing, but a function is required in car hiding anim. + * (pwd_char_hider callback do the real job) + * @param ta unused + * @param x unused + */ +static void pwd_char_hider_anim(lv_obj_t * ta, int32_t x) +{ + (void)ta; + (void)x; +} + +#endif + +/** + * Hide all characters (convert them to '*') + * @param ta: pointer to text area object + */ +static void pwd_char_hider(lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + if(ext->pwd_mode != 0) { + char * txt = lv_label_get_text(ext->label); + int16_t len = lv_txt_get_encoded_length(txt); + bool refr = false; + uint16_t i; + for(i = 0; i < len; i++) { + txt[i] = '*'; + refr = true; + } + + txt[i] = '\0'; + + if(refr != false) lv_label_set_text(ext->label, txt); + } +} + +/** + * Test an unicode character if it is accepted or not. Checks max length and accepted char list. + * @param ta pointer to a test area object + * @param c an unicode character + * @return true: accapted; false: rejected + */ +static bool char_is_accepted(lv_obj_t * ta, uint32_t c) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + + /*If no restriction accept it*/ + if(ext->accapted_chars == NULL && ext->max_length == 0) return true; + + /*Too many characters?*/ + if(ext->max_length > 0 && + lv_txt_get_encoded_length(lv_ta_get_text(ta)) >= ext->max_length) { + return false; + } + + /*Accepted character?*/ + if(ext->accapted_chars) { + uint32_t i = 0; + uint32_t a; + while(ext->accapted_chars[i] != '\0') { + a = lv_txt_encoded_next(ext->accapted_chars, &i); + if(a == c) return true; /*Accepted*/ + } + + return false; /*The character wasn't in the list*/ + } else { + return true; /*If the accepted char list in not specified the accept the character*/ + } + +} + +static void get_cursor_style(lv_obj_t * ta, lv_style_t * style_res) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + lv_style_t * label_style = lv_obj_get_style(ext->label); + + if(ext->cursor.style) { + lv_style_copy(style_res, ext->cursor.style); + } else { + /*If cursor style is not specified then use the modified label style */ + lv_style_copy(style_res, label_style); + lv_color_t clv_color_tmp = style_res->text.color; /*Make letter color to cursor color*/ + style_res->text.color = style_res->body.main_color; /*In block mode the letter color will be current background color*/ + style_res->body.main_color = clv_color_tmp; + style_res->body.grad_color = clv_color_tmp; + style_res->body.border.color = clv_color_tmp; + style_res->body.border.opa = LV_OPA_COVER; + style_res->body.border.width = 1; + style_res->body.shadow.width = 0; + style_res->body.radius = 0; + style_res->body.empty = 0; + style_res->body.padding.hor = 0; + style_res->body.padding.ver = 0; + style_res->line.width = 1; + style_res->body.opa = LV_OPA_COVER; + } + +} + +static void refr_cursor_area(lv_obj_t * ta) +{ + lv_ta_ext_t * ext = lv_obj_get_ext_attr(ta); + lv_style_t * label_style = lv_obj_get_style(ext->label); + + lv_style_t cur_style; + get_cursor_style(ta, &cur_style); + + uint16_t cur_pos = lv_ta_get_cursor_pos(ta); + const char * txt = lv_label_get_text(ext->label); + uint32_t byte_pos; +#if LV_TXT_UTF8 != 0 + byte_pos = lv_txt_encoded_get_byte_id(txt, cur_pos); + uint32_t letter = lv_txt_encoded_next(&txt[byte_pos], NULL); +#else + byte_pos = cur_pos; + uint32_t letter = txt[byte_pos]; +#endif + + lv_coord_t letter_h = lv_font_get_height(label_style->text.font); + /*Set letter_w (set not 0 on non printable but valid chars)*/ + lv_coord_t letter_w; + if(letter == '\0' || letter == '\n' || letter == '\r') { + letter_w = lv_font_get_width(label_style->text.font, ' '); + } else { + letter_w = lv_font_get_width(label_style->text.font, letter); + } + + lv_point_t letter_pos; + lv_label_get_letter_pos(ext->label, cur_pos, &letter_pos); + + /*If the cursor is out of the text (most right) draw it to the next line*/ + if(letter_pos.x + ext->label->coords.x1 + letter_w > ext->label->coords.x2 && ext->one_line == 0 && lv_label_get_align(ext->label) != LV_LABEL_ALIGN_RIGHT) { + letter_pos.x = 0; + letter_pos.y += letter_h + label_style->text.line_space; + + if(letter != '\0') { + byte_pos += lv_txt_encoded_size(&txt[byte_pos]); + letter = lv_txt_encoded_next(&txt[byte_pos], NULL); + } + + if(letter == '\0' || letter == '\n' || letter == '\r') { + letter_w = lv_font_get_width(label_style->text.font, ' '); + } else { + letter_w = lv_font_get_width(label_style->text.font, letter); + } + } + + /*Save the byte position. It is required to draw `LV_CURSOR_BLOCK`*/ + ext->cursor.txt_byte_pos = byte_pos; + + /*Draw he cursor according to the type*/ + lv_area_t cur_area; + + if(ext->cursor.type == LV_CURSOR_LINE) { + cur_area.x1 = letter_pos.x + cur_style.body.padding.hor - (cur_style.line.width >> 1) - (cur_style.line.width & 0x1); + cur_area.y1 = letter_pos.y + cur_style.body.padding.ver; + cur_area.x2 = letter_pos.x + cur_style.body.padding.hor + (cur_style.line.width >> 1); + cur_area.y2 = letter_pos.y + cur_style.body.padding.ver + letter_h; + } else if(ext->cursor.type == LV_CURSOR_BLOCK) { + cur_area.x1 = letter_pos.x - cur_style.body.padding.hor; + cur_area.y1 = letter_pos.y - cur_style.body.padding.ver; + cur_area.x2 = letter_pos.x + cur_style.body.padding.hor + letter_w; + cur_area.y2 = letter_pos.y + cur_style.body.padding.ver + letter_h; + + } else if(ext->cursor.type == LV_CURSOR_OUTLINE) { + cur_area.x1 = letter_pos.x - cur_style.body.padding.hor; + cur_area.y1 = letter_pos.y - cur_style.body.padding.ver; + cur_area.x2 = letter_pos.x + cur_style.body.padding.hor + letter_w; + cur_area.y2 = letter_pos.y + cur_style.body.padding.ver + letter_h; + } else if(ext->cursor.type == LV_CURSOR_UNDERLINE) { + cur_area.x1 = letter_pos.x + cur_style.body.padding.hor; + cur_area.y1 = letter_pos.y + cur_style.body.padding.ver + letter_h - (cur_style.line.width >> 1); + cur_area.x2 = letter_pos.x + cur_style.body.padding.hor + letter_w; + cur_area.y2 = letter_pos.y + cur_style.body.padding.ver + letter_h + (cur_style.line.width >> 1) + (cur_style.line.width & 0x1); + } + + /*Save the new area*/ + lv_area_t area_tmp; + lv_area_copy(&area_tmp, &ext->cursor.area); + area_tmp.x1 += ext->label->coords.x1; + area_tmp.y1 += ext->label->coords.y1; + area_tmp.x2 += ext->label->coords.x1; + area_tmp.y2 += ext->label->coords.y1; + lv_inv_area(&area_tmp); + + lv_area_copy(&ext->cursor.area, &cur_area); + + lv_area_copy(&area_tmp, &ext->cursor.area); + area_tmp.x1 += ext->label->coords.x1; + area_tmp.y1 += ext->label->coords.y1; + area_tmp.x2 += ext->label->coords.x1; + area_tmp.y2 += ext->label->coords.y1; + lv_inv_area(&area_tmp); +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_ta.h b/bdk/libs/lvgl/lv_objx/lv_ta.h new file mode 100644 index 00000000..3227873a --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_ta.h @@ -0,0 +1,390 @@ +/** + * @file lv_ta.h + * + */ + +#ifndef LV_TA_H +#define LV_TA_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_TA != 0 + +/*Testing of dependencies*/ +#if USE_LV_PAGE == 0 +#error "lv_ta: lv_page is required. Enable it in lv_conf.h (USE_LV_PAGE 1) " +#endif + +#if USE_LV_LABEL == 0 +#error "lv_ta: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_page.h" +#include "lv_label.h" + +/********************* + * DEFINES + *********************/ +#define LV_TA_CURSOR_LAST (0x7FFF) /*Put the cursor after the last character*/ + +/********************** + * TYPEDEFS + **********************/ + +enum { + LV_CURSOR_NONE, + LV_CURSOR_LINE, + LV_CURSOR_BLOCK, + LV_CURSOR_OUTLINE, + LV_CURSOR_UNDERLINE, + LV_CURSOR_HIDDEN = 0x08, /*Or it to any value to hide the cursor temporally*/ +}; +typedef uint8_t lv_cursor_type_t; + +/*Data of text area*/ +typedef struct +{ + lv_page_ext_t page; /*Ext. of ancestor*/ + /*New data for this type */ + lv_obj_t * label; /*Label of the text area*/ + char * pwd_tmp; /*Used to store the original text in password mode*/ + const char * accapted_chars;/*Only these characters will be accepted. NULL: accept all*/ + uint16_t max_length; /*The max. number of characters. 0: no limit*/ + uint8_t pwd_mode :1; /*Replace characters with '*' */ + uint8_t one_line :1; /*One line mode (ignore line breaks)*/ + struct { + lv_style_t *style; /*Style of the cursor (NULL to use label's style)*/ + lv_coord_t valid_x; /*Used when stepping up/down in text area when stepping to a shorter line. (Handled by the library)*/ + uint16_t pos; /*The current cursor position (0: before 1. letter; 1: before 2. letter etc.)*/ + lv_area_t area; /*Cursor area relative to the Text Area*/ + uint16_t txt_byte_pos; /*Byte index of the letter after (on) the cursor*/ + lv_cursor_type_t type:4; /*Shape of the cursor*/ + uint8_t state :1; /*Indicates that the cursor is visible now or not (Handled by the library)*/ + } cursor; +} lv_ta_ext_t; + +enum { + LV_TA_STYLE_BG, + LV_TA_STYLE_SB, + LV_TA_STYLE_EDGE_FLASH, + LV_TA_STYLE_CURSOR, +}; +typedef uint8_t lv_ta_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + + +/** + * Create a text area objects + * @param par pointer to an object, it will be the parent of the new text area + * @param copy pointer to a text area object, if not NULL then the new object will be copied from it + * @return pointer to the created text area + */ +lv_obj_t * lv_ta_create(lv_obj_t * par, const lv_obj_t * copy); + + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Insert a character to the current cursor position. + * To add a wide char, e.g. 'Á' use `lv_txt_encoded_conv_wc('Á')` + * @param ta pointer to a text area object + * @param c a character (e.g. 'a') + */ +void lv_ta_add_char(lv_obj_t * ta, uint32_t c); + +/** + * Insert a text to the current cursor position + * @param ta pointer to a text area object + * @param txt a '\0' terminated string to insert + */ +void lv_ta_add_text(lv_obj_t * ta, const char * txt); + +/** + * Delete a the left character from the current cursor position + * @param ta pointer to a text area object + */ +void lv_ta_del_char(lv_obj_t * ta); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the text of a text area + * @param ta pointer to a text area + * @param txt pointer to the text + */ +void lv_ta_set_text(lv_obj_t * ta, const char * txt); + +/** + * Set the cursor position + * @param obj pointer to a text area object + * @param pos the new cursor position in character index + * < 0 : index from the end of the text + * LV_TA_CURSOR_LAST: go after the last character + */ +void lv_ta_set_cursor_pos(lv_obj_t * ta, int16_t pos); + +/** + * Set the cursor type. + * @param ta pointer to a text area object + * @param cur_type: element of 'lv_cursor_type_t' + */ +void lv_ta_set_cursor_type(lv_obj_t * ta, lv_cursor_type_t cur_type); + +/** + * Enable/Disable password mode + * @param ta pointer to a text area object + * @param en true: enable, false: disable + */ +void lv_ta_set_pwd_mode(lv_obj_t * ta, bool en); + +/** + * Configure the text area to one line or back to normal + * @param ta pointer to a Text area object + * @param en true: one line, false: normal + */ +void lv_ta_set_one_line(lv_obj_t * ta, bool en); + +/** + * Set the alignment of the text area. + * In one line mode the text can be scrolled only with `LV_LABEL_ALIGN_LEFT`. + * This function should be called if the size of text area changes. + * @param ta pointer to a text are object + * @param align the desired alignment from `lv_label_align_t`. (LV_LABEL_ALIGN_LEFT/CENTER/RIGHT) + */ +void lv_ta_set_text_align(lv_obj_t * ta, lv_label_align_t align); + +/** + * Set a list of characters. Only these characters will be accepted by the text area + * @param ta pointer to Text Area + * @param list list of characters. Only the pointer is saved. E.g. "+-.,0123456789" + */ +void lv_ta_set_accepted_chars(lv_obj_t * ta, const char * list); + +/** + * Set max length of a Text Area. + * @param ta pointer to Text Area + * @param num the maximal number of characters can be added (`lv_ta_set_text` ignores it) + */ +void lv_ta_set_max_length(lv_obj_t * ta, uint16_t num); + +/** + * Set an action to call when the Text area is clicked + * @param ta pointer to a Text area + * @param action a function pointer + */ +static inline void lv_ta_set_action(lv_obj_t * ta, lv_action_t action) +{ + lv_page_set_rel_action(ta, action); +} + +/** + * Set the scroll bar mode of a text area + * @param ta pointer to a text area object + * @param sb_mode the new mode from 'lv_page_sb_mode_t' enum + */ +static inline void lv_ta_set_sb_mode(lv_obj_t * ta, lv_sb_mode_t mode) +{ + lv_page_set_sb_mode(ta, mode); +} + +/** + * Enable the scroll propagation feature. If enabled then the Text area will move its parent if there is no more space to scroll. + * @param ta pointer to a Text area + * @param en true or false to enable/disable scroll propagation + */ +static inline void lv_ta_set_scroll_propagation(lv_obj_t * ta, bool en) +{ + lv_page_set_scroll_propagation(ta, en); +} + +/** + * Enable the edge flash effect. (Show an arc when the an edge is reached) + * @param page pointer to a Text Area + * @param en true or false to enable/disable end flash + */ +static inline void lv_ta_set_edge_flash(lv_obj_t * ta, bool en) +{ + lv_page_set_edge_flash(ta, en); +} + +/** + * Set a style of a text area + * @param ta pointer to a text area object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_ta_set_style(lv_obj_t *ta, lv_ta_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the text of a text area. In password mode it gives the real text (not '*'s). + * @param ta pointer to a text area object + * @return pointer to the text + */ +const char * lv_ta_get_text(const lv_obj_t * ta); + +/** + * Get the label of a text area + * @param ta pointer to a text area object + * @return pointer to the label object + */ +lv_obj_t * lv_ta_get_label(const lv_obj_t * ta); + +/** + * Get the current cursor position in character index + * @param ta pointer to a text area object + * @return the cursor position + */ +uint16_t lv_ta_get_cursor_pos(const lv_obj_t * ta); + +/** + * Get the current cursor visibility. + * @param ta pointer to a text area object + * @return true: the cursor is drawn, false: the cursor is hidden + */ +//bool lv_ta_get_cursor_show(const lv_obj_t * ta); + +/** + * Get the current cursor type. + * @param ta pointer to a text area object + * @return element of 'lv_cursor_type_t' + */ +lv_cursor_type_t lv_ta_get_cursor_type(const lv_obj_t * ta); + +/** + * Get the password mode attribute + * @param ta pointer to a text area object + * @return true: password mode is enabled, false: disabled + */ +bool lv_ta_get_pwd_mode(const lv_obj_t * ta); + +/** + * Get the one line configuration attribute + * @param ta pointer to a text area object + * @return true: one line configuration is enabled, false: disabled + */ +bool lv_ta_get_one_line(const lv_obj_t * ta); + +/** + * Get a list of accepted characters. + * @param ta pointer to Text Area + * @return list of accented characters. + */ +const char * lv_ta_get_accepted_chars(lv_obj_t * ta); + +/** + * Set max length of a Text Area. + * @param ta pointer to Text Area + * @return the maximal number of characters to be add + */ +uint16_t lv_ta_get_max_length(lv_obj_t * ta); + +/** + * Set an action to call when the Text area is clicked + * @param ta pointer to a Text area + * @param action a function pointer + */ +static inline lv_action_t lv_ta_get_action(lv_obj_t * ta) +{ + return lv_page_get_rel_action(ta); +} + +/** + * Get the scroll bar mode of a text area + * @param ta pointer to a text area object + * @return scrollbar mode from 'lv_page_sb_mode_t' enum + */ +static inline lv_sb_mode_t lv_ta_get_sb_mode(const lv_obj_t * ta) +{ + return lv_page_get_sb_mode(ta); +} + +/** + * Get the scroll propagation property + * @param ta pointer to a Text area + * @return true or false + */ +static inline bool lv_ta_get_scroll_propagation(lv_obj_t * ta) +{ + return lv_page_get_scroll_propagation(ta); +} + +/** + * Get the scroll propagation property + * @param ta pointer to a Text area + * @return true or false + */ +static inline bool lv_ta_get_edge_flash(lv_obj_t * ta) +{ + return lv_page_get_edge_flash(ta); +} + +/** + * Get a style of a text area + * @param ta pointer to a text area object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_ta_get_style(const lv_obj_t *ta, lv_ta_style_t type); + +/*===================== + * Other functions + *====================*/ + +/** + * Move the cursor one character right + * @param ta pointer to a text area object + */ +void lv_ta_cursor_right(lv_obj_t * ta); + +/** + * Move the cursor one character left + * @param ta pointer to a text area object + */ +void lv_ta_cursor_left(lv_obj_t * ta); + +/** + * Move the cursor one line down + * @param ta pointer to a text area object + */ +void lv_ta_cursor_down(lv_obj_t * ta); + +/** + * Move the cursor one line up + * @param ta pointer to a text area object + */ +void lv_ta_cursor_up(lv_obj_t * ta); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_TA_H*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_TA_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_table.c b/bdk/libs/lvgl/lv_objx/lv_table.c new file mode 100644 index 00000000..c6177ed1 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_table.c @@ -0,0 +1,855 @@ +/** + * @file lv_table.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_table.h" +#if USE_LV_TABLE != 0 + +#include "../lv_misc/lv_txt.h" +#include "../lv_misc/lv_math.h" +#include "../lv_draw/lv_draw_label.h" +#include "../lv_themes/lv_theme.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static bool lv_table_design(lv_obj_t * table, const lv_area_t * mask, lv_design_mode_t mode); +static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param); +static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id); +static void refr_size(lv_obj_t * table); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_design_func_t ancestor_scrl_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a table object + * @param par pointer to an object, it will be the parent of the new table + * @param copy pointer to a table object, if not NULL then the new object will be copied from it + * @return pointer to the created table + */ +lv_obj_t * lv_table_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("table create started"); + + /*Create the ancestor of table*/ + lv_obj_t * new_table = lv_obj_create(par, copy); + lv_mem_assert(new_table); + if(new_table == NULL) return NULL; + + /*Allocate the table type specific extended data*/ + lv_table_ext_t * ext = lv_obj_allocate_ext_attr(new_table, sizeof(lv_table_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_table); + if(ancestor_scrl_design == NULL) ancestor_scrl_design = lv_obj_get_design_func(new_table); + + /*Initialize the allocated 'ext' */ + ext->cell_data = NULL; + ext->cell_style[0] = &lv_style_plain; + ext->cell_style[1] = &lv_style_plain; + ext->cell_style[2] = &lv_style_plain; + ext->cell_style[3] = &lv_style_plain; + ext->col_cnt = 0; + ext->row_cnt = 0; + + uint16_t i; + for(i = 0; i < LV_TABLE_COL_MAX; i++) { + ext->col_w[i] = LV_DPI; + } + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_table, lv_table_signal); + lv_obj_set_design_func(new_table, lv_table_design); + + /*Init the new table table*/ + if(copy == NULL) { + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_table_set_style(new_table, LV_TABLE_STYLE_BG, th->table.bg); + lv_table_set_style(new_table, LV_TABLE_STYLE_CELL1, th->table.cell); + lv_table_set_style(new_table, LV_TABLE_STYLE_CELL2, th->table.cell); + lv_table_set_style(new_table, LV_TABLE_STYLE_CELL3, th->table.cell); + lv_table_set_style(new_table, LV_TABLE_STYLE_CELL4, th->table.cell); + } else { + lv_table_set_style(new_table, LV_TABLE_STYLE_BG, &lv_style_plain_color); + } + } + /*Copy an existing table*/ + else { + lv_table_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->cell_style[0] = copy_ext->cell_style[0]; + ext->cell_style[1] = copy_ext->cell_style[1]; + ext->cell_style[2] = copy_ext->cell_style[2]; + ext->cell_style[3] = copy_ext->cell_style[3]; + ext->col_cnt = copy_ext->col_cnt; + ext->row_cnt = copy_ext->row_cnt; + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_table); + } + + LV_LOG_INFO("table created"); + + return new_table; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the value of a cell. + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param txt text to display in the cell. It will be copied and saved so this variable is not required after this function call. + */ +void lv_table_set_cell_value(lv_obj_t * table, uint16_t row, uint16_t col, const char * txt) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + if(row >= ext->row_cnt || col >= ext->col_cnt) { + LV_LOG_WARN("lv_table_set_cell_value: invalid row or column"); + return; + } + uint32_t cell = row * ext->col_cnt + col; + lv_table_cell_format_t format; + + /*Save the format byte*/ + if(ext->cell_data[cell]) { + format.format_byte = ext->cell_data[cell][0]; + } + /*Initialize the format byte*/ + else { + format.align = LV_LABEL_ALIGN_LEFT; + format.right_merge = 0; + format.type = 0; + format.crop = 0; + } + + + ext->cell_data[cell] = lv_mem_realloc(ext->cell_data[cell], strlen(txt) + 2); /*+1: trailing '\0; +1: format byte*/ + strcpy(ext->cell_data[cell] + 1, txt); /*Leave the format byte*/ + ext->cell_data[cell][0] = format.format_byte; + refr_size(table); +} + +/** + * Set the number of rows + * @param table table pointer to a Table object + * @param row_cnt number of rows + */ +void lv_table_set_row_cnt(lv_obj_t * table, uint16_t row_cnt) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + uint16_t old_row_cnt = ext->row_cnt; + ext->row_cnt = row_cnt; + + if(ext->row_cnt > 0 && ext->col_cnt > 0) { + ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char*)); + + /*Initilize the new fields*/ + if(old_row_cnt < row_cnt) { + uint16_t old_cell_cnt = old_row_cnt * ext->col_cnt; + uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt; + memset(&ext->cell_data[old_cell_cnt], 0, (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0])); + } + } + else { + lv_mem_free(ext->cell_data); + ext->cell_data = NULL; + } + + refr_size(table); +} + +/** + * Set the number of columns + * @param table table pointer to a Table object + * @param col_cnt number of columns. Must be < LV_TABLE_COL_MAX + */ +void lv_table_set_col_cnt(lv_obj_t * table, uint16_t col_cnt) +{ + + if(col_cnt >= LV_TABLE_COL_MAX) { + LV_LOG_WARN("lv_table_set_col_cnt: too many columns. Must be < LV_TABLE_COL_MAX."); + return; + } + + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + uint16_t old_col_cnt = ext->col_cnt; + ext->col_cnt = col_cnt; + + if(ext->row_cnt > 0 && ext->col_cnt > 0) { + ext->cell_data = lv_mem_realloc(ext->cell_data, ext->row_cnt * ext->col_cnt * sizeof(char*)); + /*Initilize the new fields*/ + if(old_col_cnt < col_cnt) { + uint16_t old_cell_cnt = old_col_cnt * ext->row_cnt; + uint32_t new_cell_cnt = ext->col_cnt * ext->row_cnt; + memset(&ext->cell_data[old_cell_cnt], 0, (new_cell_cnt - old_cell_cnt) * sizeof(ext->cell_data[0])); + } + + } + else { + lv_mem_free(ext->cell_data); + ext->cell_data = NULL; + } + refr_size(table); +} + +/** + * Set the width of a column + * @param table table pointer to a Table object + * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1] + * @param w width of the column + */ +void lv_table_set_col_width(lv_obj_t * table, uint16_t col_id, lv_coord_t w) +{ + if(col_id >= LV_TABLE_COL_MAX) { + LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX."); + return; + } + + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + ext->col_w[col_id] = w; + refr_size(table); +} + +/** + * Set the text align in a cell + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param align LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT + */ +void lv_table_set_cell_align(lv_obj_t * table, uint16_t row, uint16_t col, lv_label_align_t align) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + if(row >= ext->row_cnt || col >= ext->col_cnt) { + LV_LOG_WARN("lv_table_set_cell_align: invalid row or column"); + return; + } + uint32_t cell = row * ext->col_cnt + col; + + if(ext->cell_data[cell] == NULL) { + ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/ + ext->cell_data[cell][0] = 0; + ext->cell_data[cell][1] = '\0'; + } + + lv_table_cell_format_t format; + format.format_byte = ext->cell_data[cell][0]; + format.align = align; + ext->cell_data[cell][0] = format.format_byte; +} + +/** + * Set the type of a cell. + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param type 1,2,3 or 4. The cell style will be chosen accordingly. + */ +void lv_table_set_cell_type(lv_obj_t * table, uint16_t row, uint16_t col, uint8_t type) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + if(row >= ext->row_cnt || col >= ext->col_cnt) { + LV_LOG_WARN("lv_table_set_cell_type: invalid row or column"); + return; + } + uint32_t cell = row * ext->col_cnt + col; + + if(ext->cell_data[cell] == NULL) { + ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/ + ext->cell_data[cell][0] = 0; + ext->cell_data[cell][1] = '\0'; + } + + if(type > 0) type--; /*User gives 1,2,3,4 but easier to handle 0, 1, 2, 3*/ + if(type >= LV_TABLE_CELL_STYLE_CNT) type = LV_TABLE_CELL_STYLE_CNT - 1; + + lv_table_cell_format_t format; + format.format_byte = ext->cell_data[cell][0]; + format.type = type; + ext->cell_data[cell][0] = format.format_byte; +} + +/** + * Set the cell crop. (Don't adjust the height of the cell according to its content) + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param crop true: crop the cell content; false: set the cell height to the content. + */ +void lv_table_set_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col, bool crop) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + if(row >= ext->row_cnt || col >= ext->col_cnt) { + LV_LOG_WARN("lv_table_set_cell_crop: invalid row or column"); + return; + } + uint32_t cell = row * ext->col_cnt + col; + + if(ext->cell_data[cell] == NULL) { + ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/ + ext->cell_data[cell][0] = 0; + ext->cell_data[cell][1] = '\0'; + } + + lv_table_cell_format_t format; + format.format_byte = ext->cell_data[cell][0]; + format.crop = crop; + ext->cell_data[cell][0] = format.format_byte; +} + + +/** + * Merge a cell with the right neighbor. The value of the cell to the right won't be displayed. + * @param table table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param en true: merge right; false: don't merge right + */ +void lv_table_set_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col, bool en) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + if(row >= ext->row_cnt || col >= ext->col_cnt) { + LV_LOG_WARN("lv_table_set_cell_merge_right: invalid row or column"); + return; + } + + uint32_t cell = row * ext->col_cnt + col; + + if(ext->cell_data[cell] == NULL) { + ext->cell_data[cell] = lv_mem_alloc(2); /*+1: trailing '\0; +1: format byte*/ + ext->cell_data[cell][0] = 0; + ext->cell_data[cell][1] = '\0'; + } + + lv_table_cell_format_t format; + format.format_byte = ext->cell_data[cell][0]; + format.right_merge = en ? 1 : 0; + ext->cell_data[cell][0] = format.format_byte; + refr_size(table); +} + +/** + * Set a style of a table. + * @param table pointer to table object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_table_set_style(lv_obj_t * table, lv_table_style_t type, lv_style_t * style) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + + switch(type) { + case LV_TABLE_STYLE_BG: + lv_obj_set_style(table, style); + refr_size(table); + break; + case LV_TABLE_STYLE_CELL1: + ext->cell_style[0] = style; + refr_size(table); + break; + case LV_TABLE_STYLE_CELL2: + ext->cell_style[1] = style; + refr_size(table); + break; + case LV_TABLE_STYLE_CELL3: + ext->cell_style[2] = style; + refr_size(table); + break; + case LV_TABLE_STYLE_CELL4: + ext->cell_style[3] = style; + refr_size(table); + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the value of a cell. + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @return text in the cell + */ +const char * lv_table_get_cell_value(lv_obj_t * table, uint16_t row, uint16_t col) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + if(row >= ext->row_cnt || col >= ext->col_cnt) { + LV_LOG_WARN("lv_table_set_cell_value: invalid row or column"); + return ""; + } + uint32_t cell = row * ext->col_cnt + col; + + if(ext->cell_data[cell] == NULL) return ""; + + return &ext->cell_data[cell][1]; /*Skip the format byte*/ +} + +/** + * Get the number of rows. + * @param table table pointer to a Table object + * @return number of rows. + */ +uint16_t lv_table_get_row_cnt(lv_obj_t * table) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + return ext->row_cnt; +} + +/** + * Get the number of columns. + * @param table table pointer to a Table object + * @return number of columns. + */ +uint16_t lv_table_get_col_cnt(lv_obj_t * table) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + return ext->col_cnt; +} + +/** + * Get the width of a column + * @param table table pointer to a Table object + * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1] + * @return width of the column + */ +lv_coord_t lv_table_get_col_width(lv_obj_t * table, uint16_t col_id) +{ + if(col_id >= LV_TABLE_COL_MAX) { + LV_LOG_WARN("lv_table_set_col_width: too big 'col_id'. Must be < LV_TABLE_COL_MAX."); + return 0; + } + + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + return ext->col_w[col_id]; +} + +/** + * Get the text align of a cell + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @return LV_LABEL_ALIGN_LEFT (default in case of error) or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT + */ +lv_label_align_t lv_table_get_cell_align(lv_obj_t * table, uint16_t row, uint16_t col) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + if(row >= ext->row_cnt || col >= ext->col_cnt) { + LV_LOG_WARN("lv_table_set_cell_align: invalid row or column"); + return LV_LABEL_ALIGN_LEFT; /*Just return with something*/ + } + uint32_t cell = row * ext->col_cnt + col; + + if(ext->cell_data[cell] == NULL) return LV_LABEL_ALIGN_LEFT; /*Just return with something*/ + else { + lv_table_cell_format_t format; + format.format_byte = ext->cell_data[cell][0]; + return format.align; + } +} + +/** + * Get the type of a cell + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @return 1,2,3 or 4 + */ +lv_label_align_t lv_table_get_cell_type(lv_obj_t * table, uint16_t row, uint16_t col) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + if(row >= ext->row_cnt || col >= ext->col_cnt) { + LV_LOG_WARN("lv_table_get_cell_type: invalid row or column"); + return 1; /*Just return with something*/ + } + uint32_t cell = row * ext->col_cnt + col; + + if(ext->cell_data[cell] == NULL) return 1; /*Just return with something*/ + else { + lv_table_cell_format_t format; + format.format_byte = ext->cell_data[cell][0]; + return format.type + 1; /*0,1,2,3 is stored but user sees 1,2,3,4*/ + } +} + +/** + * Get the crop property of a cell + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @return true: text crop enabled; false: disabled + */ +lv_label_align_t lv_table_get_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + if(row >= ext->row_cnt || col >= ext->col_cnt) { + LV_LOG_WARN("lv_table_get_cell_crop: invalid row or column"); + return false; /*Just return with something*/ + } + uint32_t cell = row * ext->col_cnt + col; + + if(ext->cell_data[cell] == NULL) return false; /*Just return with something*/ + else { + lv_table_cell_format_t format; + format.format_byte = ext->cell_data[cell][0]; + return format.crop; + } +} + +/** + * Get the cell merge attribute. + * @param table table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @return true: merge right; false: don't merge right + */ +bool lv_table_get_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + if(row >= ext->row_cnt || col >= ext->col_cnt) { + LV_LOG_WARN("lv_table_get_cell_merge_right: invalid row or column"); + return false; + } + + uint32_t cell = row * ext->col_cnt + col; + + if(ext->cell_data[cell] == NULL) return false; + else { + lv_table_cell_format_t format; + format.format_byte = ext->cell_data[cell][0]; + return format.right_merge ? true : false; + } +} + +/** + * Get style of a table. + * @param table pointer to table object + * @param type which style should be get + * @return style pointer to the style + */ +lv_style_t * lv_table_get_style(const lv_obj_t * table, lv_table_style_t type) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + lv_style_t * style = NULL; + + switch(type) { + case LV_TABLE_STYLE_BG: + style = lv_obj_get_style(table); + break; + case LV_TABLE_STYLE_CELL1: + style = ext->cell_style[0]; + break; + case LV_TABLE_STYLE_CELL2: + style = ext->cell_style[1]; + break; + case LV_TABLE_STYLE_CELL3: + style = ext->cell_style[2]; + break; + case LV_TABLE_STYLE_CELL4: + style = ext->cell_style[3]; + break; + default: + return NULL; + } + + return style; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Handle the drawing related tasks of the tables + * @param table pointer to an object + * @param mask the object will be drawn only in this area + * @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area + * (return 'true' if yes) + * LV_DESIGN_DRAW: draw the object (always return 'true') + * LV_DESIGN_DRAW_POST: drawing after every children are drawn + * @param return true/false, depends on 'mode' + */ +static bool lv_table_design(lv_obj_t * table, const lv_area_t * mask, lv_design_mode_t mode) +{ + /*Return false if the object is not covers the mask_p area*/ + if(mode == LV_DESIGN_COVER_CHK) { + return false; + } + /*Draw the object*/ + else if(mode == LV_DESIGN_DRAW_MAIN) { + ancestor_scrl_design(table, mask, mode); + + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + lv_style_t * bg_style = lv_obj_get_style(table); + lv_style_t * cell_style; + lv_coord_t h_row; + lv_point_t txt_size; + lv_area_t cell_area; + lv_area_t txt_area; + lv_txt_flag_t txt_flags; + lv_opa_t opa_scale = lv_obj_get_opa_scale(table); + + uint16_t col; + uint16_t row; + uint16_t cell = 0; + + cell_area.y2 = table->coords.y1 + bg_style->body.padding.ver; + for(row = 0; row < ext->row_cnt; row++) { + h_row = get_row_height(table, row); + + cell_area.y1 = cell_area.y2; + cell_area.y2 = cell_area.y1 + h_row; + + cell_area.x2 = table->coords.x1 + bg_style->body.padding.hor; + + for(col = 0; col < ext->col_cnt; col++) { + + lv_table_cell_format_t format; + if(ext->cell_data[cell]) { + format.format_byte = ext->cell_data[cell][0]; + } else { + format.right_merge = 0; + format.align = LV_LABEL_ALIGN_LEFT; + format.type = 0; + format.crop = 1; + } + + cell_style = ext->cell_style[format.type]; + cell_area.x1 = cell_area.x2; + cell_area.x2 = cell_area.x1 + ext->col_w[col]; + + uint16_t col_merge = 0; + for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge ++) { + + if(ext->cell_data[cell + col_merge] != NULL) { + format.format_byte = ext->cell_data[cell + col_merge][0]; + if(format.right_merge) cell_area.x2 += ext->col_w[col + col_merge + 1]; + else break; + } else { + break; + } + } + + lv_draw_rect(&cell_area, mask, cell_style, opa_scale); + + if(ext->cell_data[cell]) { + txt_area.x1 = cell_area.x1 + cell_style->body.padding.hor; + txt_area.x2 = cell_area.x2 - cell_style->body.padding.hor; + txt_area.y1 = cell_area.y1 + cell_style->body.padding.ver; + txt_area.y2 = cell_area.y2 - cell_style->body.padding.ver; + /*Align the content to the middle if not cropped*/ + if(format.crop == 0) { + txt_flags = LV_TXT_FLAG_NONE; + } else { + txt_flags = LV_TXT_FLAG_EXPAND; + } + + lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font, + cell_style->text.letter_space, cell_style->text.line_space, lv_area_get_width(&txt_area), txt_flags); + + /*Align the content to the middle if not cropped*/ + if(format.crop == 0) { + txt_area.y1 = cell_area.y1 + h_row / 2 - txt_size.y / 2; + txt_area.y2 = cell_area.y1 + h_row / 2 + txt_size.y / 2; + } + + switch(format.align) { + default: + case LV_LABEL_ALIGN_LEFT: + txt_flags |= LV_TXT_FLAG_NONE; + break; + case LV_LABEL_ALIGN_RIGHT: + txt_flags |= LV_TXT_FLAG_RIGHT; + break; + case LV_LABEL_ALIGN_CENTER: + txt_flags |= LV_TXT_FLAG_CENTER; + break; + } + + lv_area_t label_mask; + bool label_mask_ok; + label_mask_ok = lv_area_intersect(&label_mask, mask, &cell_area); + if(label_mask_ok) { + lv_draw_label(&txt_area, &label_mask, cell_style, opa_scale, ext->cell_data[cell] + 1, txt_flags, NULL); + } + /*Draw lines after '\n's*/ + lv_point_t p1; + lv_point_t p2; + p1.x = cell_area.x1; + p2.x = cell_area.x2; + uint16_t i; + for(i = 1; ext->cell_data[cell][i] != '\0'; i++) { + if(ext->cell_data[cell][i] == '\n') { + ext->cell_data[cell][i] = '\0'; + lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font, + cell_style->text.letter_space, cell_style->text.line_space, lv_area_get_width(&txt_area), txt_flags); + + p1.y = txt_area.y1 + txt_size.y + cell_style->text.line_space / 2; + p2.y = txt_area.y1 + txt_size.y + cell_style->text.line_space / 2; + lv_draw_line(&p1, &p2, mask, cell_style, opa_scale); + + ext->cell_data[cell][i] = '\n'; + } + } + } + + cell += col_merge + 1; + col += col_merge; + } + } + } + /*Post draw when the children are drawn*/ + else if(mode == LV_DESIGN_DRAW_POST) { + + } + + return true; +} + +/** + * Signal function of the table + * @param table pointer to a table object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_table_signal(lv_obj_t * table, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(table, sign, param); + if(res != LV_RES_OK) return res; + + + if(sign == LV_SIGNAL_CLEANUP) { + /*Free the cell texts*/ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + uint16_t cell; + for(cell = 0; cell < ext->col_cnt * ext->row_cnt; cell++) { + if(ext->cell_data[cell]) { + lv_mem_free(ext->cell_data[cell]); + ext->cell_data[cell] = NULL; + } + } + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_table"; + } + + return res; +} + +static void refr_size(lv_obj_t * table) +{ + lv_coord_t h = 0; + lv_coord_t w = 0; + + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + + uint16_t i; + for(i= 0; i < ext->col_cnt; i++) { + w += ext->col_w[i]; + } + for(i= 0; i < ext->row_cnt; i++) { + h += get_row_height(table, i); + } + + lv_style_t * bg_style = lv_obj_get_style(table); + + w += bg_style->body.padding.hor * 2; + h += bg_style->body.padding.ver * 2; + + lv_obj_set_size(table, w + 1, h + 1); + lv_obj_invalidate(table); +} + +static lv_coord_t get_row_height(lv_obj_t * table, uint16_t row_id) +{ + lv_table_ext_t * ext = lv_obj_get_ext_attr(table); + lv_point_t txt_size; + lv_coord_t txt_w; + lv_style_t * cell_style; + + uint16_t row_start = row_id * ext->col_cnt; + uint16_t cell; + uint16_t col; + lv_coord_t h_max = lv_font_get_height(ext->cell_style[0]->text.font) + 2 * ext->cell_style[0]->body.padding.ver; + + for(cell = row_start, col = 0; cell < row_start + ext->col_cnt; cell++, col ++) { + if(ext->cell_data[cell] != NULL) { + + txt_w = ext->col_w[col]; + uint16_t col_merge = 0; + for(col_merge = 0; col_merge + col < ext->col_cnt - 1; col_merge ++) { + + if(ext->cell_data[cell + col_merge] != NULL) { + lv_table_cell_format_t format; + format.format_byte = ext->cell_data[cell + col_merge][0]; + if(format.right_merge) txt_w += ext->col_w[col + col_merge + 1]; + else break; + } else { + break; + } + } + + lv_table_cell_format_t format; + format.format_byte = ext->cell_data[cell][0]; + cell_style = ext->cell_style[format.type]; + + /*With text crop assume 1 line*/ + if(format.crop) { + h_max = LV_MATH_MAX(lv_font_get_height(cell_style->text.font) + 2 * cell_style->body.padding.ver, h_max); + } + /*Without text crop calculate the height of the text in the cell*/ + else { + txt_w -= 2 * cell_style->body.padding.hor; + + lv_txt_get_size(&txt_size, ext->cell_data[cell] + 1, cell_style->text.font, + cell_style->text.letter_space, cell_style->text.line_space, txt_w, LV_TXT_FLAG_NONE); + + h_max = LV_MATH_MAX(txt_size.y + 2 * cell_style->body.padding.ver, h_max); + cell += col_merge; + col += col_merge; + } + } + } + + return h_max; +} + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_table.h b/bdk/libs/lvgl/lv_objx/lv_table.h new file mode 100644 index 00000000..94c3575a --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_table.h @@ -0,0 +1,261 @@ +/** + * @file lv_table.h + * + */ + +#ifndef LV_TABLE_H +#define LV_TABLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_TABLE != 0 + +/*Testing of dependencies*/ +#if USE_LV_LABEL == 0 +#error "lv_table: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_label.h" + +/********************* + * DEFINES + *********************/ +#ifndef LV_TABLE_COL_MAX +#define LV_TABLE_COL_MAX 12 +#endif + +#define LV_TABLE_CELL_STYLE_CNT 4 +/********************** + * TYPEDEFS + **********************/ + +typedef union { + struct { + uint8_t align:2; + uint8_t right_merge:1; + uint8_t type:2; + uint8_t crop:1; + }; + uint8_t format_byte; +}lv_table_cell_format_t; + +/*Data of table*/ +typedef struct { + /*New data for this type */ + uint16_t col_cnt; + uint16_t row_cnt; + char ** cell_data; + lv_style_t * cell_style[LV_TABLE_CELL_STYLE_CNT]; + lv_coord_t col_w[LV_TABLE_COL_MAX]; +} lv_table_ext_t; + + +/*Styles*/ +enum { + LV_TABLE_STYLE_BG, + LV_TABLE_STYLE_CELL1, + LV_TABLE_STYLE_CELL2, + LV_TABLE_STYLE_CELL3, + LV_TABLE_STYLE_CELL4, +}; +typedef uint8_t lv_table_style_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a table object + * @param par pointer to an object, it will be the parent of the new table + * @param copy pointer to a table object, if not NULL then the new object will be copied from it + * @return pointer to the created table + */ +lv_obj_t * lv_table_create(lv_obj_t * par, const lv_obj_t * copy); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the value of a cell. + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param txt text to display in the cell. It will be copied and saved so this variable is not required after this function call. + */ +void lv_table_set_cell_value(lv_obj_t * table, uint16_t row, uint16_t col, const char * txt); + +/** + * Set the number of rows + * @param table table pointer to a Table object + * @param row_cnt number of rows + */ +void lv_table_set_row_cnt(lv_obj_t * table, uint16_t row_cnt); + +/** + * Set the number of columns + * @param table table pointer to a Table object + * @param col_cnt number of columns. Must be < LV_TABLE_COL_MAX + */ +void lv_table_set_col_cnt(lv_obj_t * table, uint16_t col_cnt); + +/** + * Set the width of a column + * @param table table pointer to a Table object + * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1] + * @param w width of the column + */ +void lv_table_set_col_width(lv_obj_t * table, uint16_t col_id, lv_coord_t w); + +/** + * Set the text align in a cell + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param align LV_LABEL_ALIGN_LEFT or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT + */ +void lv_table_set_cell_align(lv_obj_t * table, uint16_t row, uint16_t col, lv_label_align_t align); + +/** + * Set the type of a cell. + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param type 1,2,3 or 4. The cell style will be chosen accordingly. + */ +void lv_table_set_cell_type(lv_obj_t * table, uint16_t row, uint16_t col, uint8_t type); + +/** + * Set the cell crop. (Don't adjust the height of the cell according to its content) + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param crop true: crop the cell content; false: set the cell height to the content. + */ +void lv_table_set_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col, bool crop); + +/** + * Merge a cell with the right neighbor. The value of the cell to the right won't be displayed. + * @param table table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @param en true: merge right; false: don't merge right + */ +void lv_table_set_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col, bool en); + +/** + * Set a style of a table. + * @param table pointer to table object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_table_set_style(lv_obj_t * table, lv_table_style_t type, lv_style_t * style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the value of a cell. + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @return text in the cell + */ +const char * lv_table_get_cell_value(lv_obj_t * table, uint16_t row, uint16_t col); + +/** + * Get the number of rows. + * @param table table pointer to a Table object + * @return number of rows. + */ +uint16_t lv_table_get_row_cnt(lv_obj_t * table); + +/** + * Get the number of columns. + * @param table table pointer to a Table object + * @return number of columns. + */ +uint16_t lv_table_get_col_cnt(lv_obj_t * table); + +/** + * Get the width of a column + * @param table table pointer to a Table object + * @param col_id id of the column [0 .. LV_TABLE_COL_MAX -1] + * @return width of the column + */ +lv_coord_t lv_table_get_col_width(lv_obj_t * table, uint16_t col_id); + +/** + * Get the text align of a cell + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @return LV_LABEL_ALIGN_LEFT (default in case of error) or LV_LABEL_ALIGN_CENTER or LV_LABEL_ALIGN_RIGHT + */ +lv_label_align_t lv_table_get_cell_align(lv_obj_t * table, uint16_t row, uint16_t col); + +/** + * Get the type of a cell + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @return 1,2,3 or 4 + */ +lv_label_align_t lv_table_get_cell_type(lv_obj_t * table, uint16_t row, uint16_t col); + + +/** + * Get the crop property of a cell + * @param table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @return true: text crop enabled; false: disabled + */ +lv_label_align_t lv_table_get_cell_crop(lv_obj_t * table, uint16_t row, uint16_t col); + +/** + * Get the cell merge attribute. + * @param table table pointer to a Table object + * @param row id of the row [0 .. row_cnt -1] + * @param col id of the column [0 .. col_cnt -1] + * @return true: merge right; false: don't merge right + */ +bool lv_table_get_cell_merge_right(lv_obj_t * table, uint16_t row, uint16_t col); + +/** + * Get style of a table. + * @param table pointer to table object + * @param type which style should be get + * @return style pointer to the style + */ +lv_style_t * lv_table_get_style(const lv_obj_t * table, lv_table_style_t type); + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_TABLE*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_TABLE_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_tabview.c b/bdk/libs/lvgl/lv_objx/lv_tabview.c new file mode 100644 index 00000000..e7dacc33 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_tabview.c @@ -0,0 +1,910 @@ +/* + * Copyright (c) 2019 CTCaer + * Copyright (c) 2020 Storm + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_tab.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_tabview.h" +#if USE_LV_TABVIEW != 0 + +#include "lv_btnm.h" +#include "../lv_themes/lv_theme.h" +#include "../lv_misc/lv_anim.h" + +/********************* + * DEFINES + *********************/ +#if USE_LV_ANIMATION +# ifndef LV_TABVIEW_ANIM_TIME +# define LV_TABVIEW_ANIM_TIME 300 /*Animation time of focusing to the a list element [ms] (0: no animation) */ +# endif +#else +# undef LV_TABVIEW_ANIM_TIME +# define LV_TABVIEW_ANIM_TIME 0 /*No animations*/ +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_tabview_signal(lv_obj_t * tabview, lv_signal_t sign, void * param); +static lv_res_t tabpage_signal(lv_obj_t * tab_page, lv_signal_t sign, void * param); +static lv_res_t tabpage_scrl_signal(lv_obj_t * tab_scrl, lv_signal_t sign, void * param); + +static void tabpage_pressed_handler(lv_obj_t * tabview, lv_obj_t * tabpage); +static void tabpage_pressing_handler(lv_obj_t * tabview, lv_obj_t * tabpage); +static void tabpage_press_lost_handler(lv_obj_t * tabview, lv_obj_t * tabpage); +static lv_res_t tab_btnm_action(lv_obj_t * tab_btnm, const char * tab_name); +static void tabview_realign(lv_obj_t * tabview); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_signal_func_t page_signal; +static lv_signal_func_t page_scrl_signal; +static const char * tab_def[] = {""}; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a Tab view object + * @param par pointer to an object, it will be the parent of the new tab + * @param copy pointer to a tab object, if not NULL then the new object will be copied from it + * @return pointer to the created tab + */ +lv_obj_t * lv_tabview_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("tab view create started"); + + /*Create the ancestor of tab*/ + lv_obj_t * new_tabview = lv_obj_create(par, copy); + lv_mem_assert(new_tabview); + if(new_tabview == NULL) return NULL; + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_tabview); + + /*Allocate the tab type specific extended data*/ + lv_tabview_ext_t * ext = lv_obj_allocate_ext_attr(new_tabview, sizeof(lv_tabview_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + /*Initialize the allocated 'ext' */ + ext->drag_hor = 0; + ext->draging = 0; + ext->slide_enable = 1; + ext->tab_cur = 0; + ext->point_last.x = 0; + ext->point_last.y = 0; + ext->content = NULL; + ext->indic = NULL; + ext->btns = NULL; + ext->tab_load_action = NULL; + ext->btns_pos = LV_TABVIEW_BTNS_POS_TOP; + ext->anim_time = LV_TABVIEW_ANIM_TIME; + ext->btns_hide = 0; + + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_tabview, lv_tabview_signal); + + /*Init the new tab tab*/ + if(copy == NULL) { + ext->tab_name_ptr = lv_mem_alloc(sizeof(char *)); + lv_mem_assert(ext->tab_name_ptr); + if(ext->tab_name_ptr == NULL) return NULL; + ext->tab_name_ptr[0] = ""; + ext->tab_cnt = 0; + + lv_obj_set_size(new_tabview, LV_HOR_RES, LV_VER_RES); + + ext->btns = lv_btnm_create(new_tabview, NULL); + lv_obj_set_height(ext->btns, 3 * LV_DPI / 4); + lv_btnm_set_map(ext->btns, tab_def); + lv_btnm_set_action(ext->btns, tab_btnm_action); + lv_btnm_set_toggle(ext->btns, true, 0); + + ext->indic = lv_obj_create(ext->btns, NULL); + lv_obj_set_width(ext->indic, LV_DPI); + lv_obj_align(ext->indic, ext->btns, LV_ALIGN_IN_BOTTOM_LEFT, 0, 0); + lv_obj_set_click(ext->indic, false); + + ext->content = lv_cont_create(new_tabview, NULL); + lv_cont_set_fit(ext->content, true, false); + lv_cont_set_layout(ext->content, LV_LAYOUT_ROW_T); + lv_cont_set_style(ext->content, &lv_style_transp_tight); + lv_obj_set_height(ext->content, LV_VER_RES - lv_obj_get_height(ext->btns)); + lv_obj_align(ext->content, ext->btns, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BG, th->tabview.bg); + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_INDIC, th->tabview.indic); + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_BG, th->tabview.btn.bg); + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_REL, th->tabview.btn.rel); + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_PR, th->tabview.btn.pr); + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_TGL_REL, th->tabview.btn.tgl_rel); + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_TGL_PR, th->tabview.btn.tgl_pr); + } else { + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BG, &lv_style_plain); + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_BTN_BG, &lv_style_transp); + lv_tabview_set_style(new_tabview, LV_TABVIEW_STYLE_INDIC, &lv_style_plain_color); + } + } + /*Copy an existing tab view*/ + else { + lv_tabview_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->point_last.x = 0; + ext->point_last.y = 0; + ext->btns = lv_btnm_create(new_tabview, copy_ext->btns); + ext->indic = lv_obj_create(ext->btns, copy_ext->indic); + ext->content = lv_cont_create(new_tabview, copy_ext->content); + ext->anim_time = copy_ext->anim_time; + ext->tab_load_action = copy_ext->tab_load_action; + + ext->tab_name_ptr = lv_mem_alloc(sizeof(char *)); + lv_mem_assert(ext->tab_name_ptr); + if(ext->tab_name_ptr == NULL) return NULL; + ext->tab_name_ptr[0] = ""; + lv_btnm_set_map(ext->btns, ext->tab_name_ptr); + + uint16_t i; + lv_obj_t * new_tab; + lv_obj_t * copy_tab; + for(i = 0; i < copy_ext->tab_cnt; i++) { + new_tab = lv_tabview_add_tab(new_tabview, copy_ext->tab_name_ptr[i]); + copy_tab = lv_tabview_get_tab(copy, i); + lv_page_set_style(new_tab, LV_PAGE_STYLE_BG, lv_page_get_style(copy_tab, LV_PAGE_STYLE_BG)); + lv_page_set_style(new_tab, LV_PAGE_STYLE_SCRL, lv_page_get_style(copy_tab, LV_PAGE_STYLE_SCRL)); + lv_page_set_style(new_tab, LV_PAGE_STYLE_SB, lv_page_get_style(copy_tab, LV_PAGE_STYLE_SB)); + } + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_tabview); + } + + + LV_LOG_INFO("tab view created"); + + return new_tabview; +} + +/** + * Delete all children of the scrl object, without deleting scrl child. + * @param obj pointer to an object + */ +void lv_tabview_clean(lv_obj_t * obj) +{ + lv_obj_t * scrl = lv_page_get_scrl(obj); + lv_obj_clean(scrl); +} + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Add a new tab with the given name + * @param tabview pointer to Tab view object where to ass the new tab + * @param name the text on the tab button + * @return pointer to the created page object (lv_page). You can create your content here + */ +lv_obj_t * lv_tabview_add_tab(lv_obj_t * tabview, const char * name) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + + /*Create the container page*/ + lv_obj_t * h = lv_page_create(ext->content, NULL); + lv_obj_set_size(h, lv_obj_get_width(tabview), lv_obj_get_height(ext->content)); + lv_page_set_sb_mode(h, LV_SB_MODE_OFF); // Important! + lv_page_set_style(h, LV_PAGE_STYLE_BG, &lv_style_transp); + lv_page_set_style(h, LV_PAGE_STYLE_SCRL, &lv_style_transp); + + if(page_signal == NULL) page_signal = lv_obj_get_signal_func(h); + if(page_scrl_signal == NULL) page_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(h)); + lv_obj_set_signal_func(h, tabpage_signal); + lv_obj_set_signal_func(lv_page_get_scrl(h), tabpage_scrl_signal); + + /*Extend the button matrix map with the new name*/ + char * name_dm; + if((name[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) { /*If control byte presented let is*/ + name_dm = lv_mem_alloc(strlen(name) + 1); /*+1 for the the closing '\0' */ + lv_mem_assert(name_dm); + if(name_dm == NULL) return NULL; + strcpy(name_dm, name); + } else { /*Set a no long press control byte is not presented*/ + name_dm = lv_mem_alloc(strlen(name) + 2); /*+1 for the the closing '\0' and +1 for the control byte */ + lv_mem_assert(name_dm); + if(name_dm == NULL) return NULL; + name_dm[0] = '\221'; + strcpy(&name_dm[1], name); + } + + ext->tab_cnt++; + ext->tab_name_ptr = lv_mem_realloc(ext->tab_name_ptr, sizeof(char *) * (ext->tab_cnt + 1)); + lv_mem_assert(ext->tab_name_ptr); + if(ext->tab_name_ptr == NULL) return NULL; + + ext->tab_name_ptr[ext->tab_cnt - 1] = name_dm; + ext->tab_name_ptr[ext->tab_cnt] = ""; + + lv_btnm_set_map(ext->btns, ext->tab_name_ptr); + + /*Modify the indicator size*/ + lv_style_t * style_tabs = lv_obj_get_style(ext->btns); + lv_coord_t indic_width = (lv_obj_get_width(tabview) - style_tabs->body.padding.inner * (ext->tab_cnt - 1) - 2 * style_tabs->body.padding.hor) / ext->tab_cnt; + lv_obj_set_width(ext->indic, indic_width); + lv_obj_set_x(ext->indic, indic_width * ext->tab_cur + style_tabs->body.padding.inner * ext->tab_cur + style_tabs->body.padding.hor); + + /*Set the first btn as active*/ + if(ext->tab_cnt == 1) { + ext->tab_cur = 0; + lv_tabview_set_tab_act(tabview, 0, false); + tabview_realign(tabview); /*To set the proper btns height*/ + } + + return h; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new tab + * @param tabview pointer to Tab view object + * @param id index of a tab to load + * @param anim_en true: set with sliding animation; false: set immediately + */ +void lv_tabview_set_tab_act(lv_obj_t * tabview, uint16_t id, bool anim_en) +{ +#if USE_LV_ANIMATION == 0 + anim_en = false; +#endif + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + + lv_style_t * style = lv_obj_get_style(ext->content); + + lv_res_t res = LV_RES_OK; + if(id >= ext->tab_cnt) id = ext->tab_cnt - 1; + if(ext->tab_load_action && id != ext->tab_cur) res = ext->tab_load_action(tabview, id); + if(res != LV_RES_OK) return; /*Prevent the tab loading*/ + + ext->tab_cur = id; + + lv_coord_t cont_x = -(lv_obj_get_width(tabview) * id + style->body.padding.inner * id + style->body.padding.hor); + if(ext->anim_time == 0 || anim_en == false) { + lv_obj_set_x(ext->content, cont_x); + } else { +#if USE_LV_ANIMATION + lv_anim_t a; + a.var = ext->content; + a.start = lv_obj_get_x(ext->content); + a.end = cont_x; + a.fp = (lv_anim_fp_t)lv_obj_set_x; + a.path = lv_anim_path_linear; + a.end_cb = NULL; + a.act_time = 0; + a.time = ext->anim_time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); +#endif + } + + /*Move the indicator*/ + lv_coord_t indic_width = lv_obj_get_width(ext->indic); + lv_style_t * tabs_style = lv_obj_get_style(ext->btns); + lv_coord_t indic_x = indic_width * id + tabs_style->body.padding.inner * id + tabs_style->body.padding.hor; + + if(ext->anim_time == 0 || anim_en == false) { + lv_obj_set_x(ext->indic, indic_x); + } else { +#if USE_LV_ANIMATION + lv_anim_t a; + a.var = ext->indic; + a.start = lv_obj_get_x(ext->indic); + a.end = indic_x; + a.fp = (lv_anim_fp_t)lv_obj_set_x; + a.path = lv_anim_path_linear; + a.end_cb = NULL; + a.act_time = 0; + a.time = ext->anim_time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + lv_anim_create(&a); +#endif + } + + lv_btnm_set_toggle(ext->btns, true, ext->tab_cur); +} + +/** + * Set an action to call when a tab is loaded (Good to create content only if required) + * lv_tabview_get_act() still gives the current (old) tab (to remove content from here) + * @param tabview pointer to a tabview object + * @param action pointer to a function to call when a btn is loaded + */ +void lv_tabview_set_tab_load_action(lv_obj_t * tabview, lv_tabview_action_t action) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + ext->tab_load_action = action; +} + +/** + * Enable horizontal sliding with touch pad + * @param tabview pointer to Tab view object + * @param en true: enable sliding; false: disable sliding + */ +void lv_tabview_set_sliding(lv_obj_t * tabview, bool en) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + ext->slide_enable = en == false ? 0 : 1; +} + +/** + * Set the animation time of tab view when a new tab is loaded + * @param tabview pointer to Tab view object + * @param anim_time_ms time of animation in milliseconds + */ +void lv_tabview_set_anim_time(lv_obj_t * tabview, uint16_t anim_time) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); +#if USE_LV_ANIMATION == 0 + anim_time = 0; +#endif + ext->anim_time = anim_time; +} + +/** + * Set the style of a tab view + * @param tabview pointer to a tan view object + * @param type which style should be set + * @param style pointer to the new style + */ +void lv_tabview_set_style(lv_obj_t * tabview, lv_tabview_style_t type, lv_style_t * style) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + + switch(type) { + case LV_TABVIEW_STYLE_BG: + lv_obj_set_style(tabview, style); + break; + case LV_TABVIEW_STYLE_BTN_BG: + lv_btnm_set_style(ext->btns, LV_BTNM_STYLE_BG, style); + tabview_realign(tabview); + break; + case LV_TABVIEW_STYLE_BTN_REL: + lv_btnm_set_style(ext->btns, LV_BTNM_STYLE_BTN_REL, style); + tabview_realign(tabview); + break; + case LV_TABVIEW_STYLE_BTN_PR: + lv_btnm_set_style(ext->btns, LV_BTNM_STYLE_BTN_PR, style); + break; + case LV_TABVIEW_STYLE_BTN_TGL_REL: + lv_btnm_set_style(ext->btns, LV_BTNM_STYLE_BTN_TGL_REL, style); + break; + case LV_TABVIEW_STYLE_BTN_TGL_PR: + lv_btnm_set_style(ext->btns, LV_BTNM_STYLE_BTN_TGL_PR, style); + break; + case LV_TABVIEW_STYLE_INDIC: + lv_obj_set_style(ext->indic, style); + lv_obj_set_height(ext->indic, style->body.padding.inner); + tabview_realign(tabview); + break; + } +} + +/** + * Set the position of tab select buttons + * @param tabview pointer to a tan view object + * @param btns_pos which button position + */ +void lv_tabview_set_btns_pos(lv_obj_t * tabview, lv_tabview_btns_pos_t btns_pos) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + + ext->btns_pos = btns_pos; + tabview_realign(tabview); +} + +/** + * Set whether tab buttons are hidden + * @param tabview pointer to a tab view object + * @param en whether tab buttons are hidden + */ +void lv_tabview_set_btns_hidden(lv_obj_t *tabview, bool en) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + + ext->btns_hide = en; + tabview_realign(tabview); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the index of the currently active tab + * @param tabview pointer to Tab view object + * @return the active btn index + */ +uint16_t lv_tabview_get_tab_act(const lv_obj_t * tabview) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + return ext->tab_cur; +} + +/** + * Get the number of tabs + * @param tabview pointer to Tab view object + * @return btn count + */ +uint16_t lv_tabview_get_tab_count(const lv_obj_t * tabview) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + return ext->tab_cnt; +} + +/** + * Get the page (content area) of a tab + * @param tabview pointer to Tab view object + * @param id index of the btn (>= 0) + * @return pointer to page (lv_page) object + */ +lv_obj_t * lv_tabview_get_tab(const lv_obj_t * tabview, uint16_t id) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + uint16_t i = 0; + lv_obj_t * page = lv_obj_get_child_back(ext->content, NULL); + + while(page != NULL && i != id) { + i++; + page = lv_obj_get_child_back(ext->content, page); + } + + if(i == id) return page; + + return NULL; +} + +/** + * Get the tab load action + * @param tabview pointer to a tabview object + * @param return the current btn load action + */ +lv_tabview_action_t lv_tabview_get_tab_load_action(const lv_obj_t * tabview) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + return ext->tab_load_action; +} + +/** + * Get horizontal sliding is enabled or not + * @param tabview pointer to Tab view object + * @return true: enable sliding; false: disable sliding + */ +bool lv_tabview_get_sliding(const lv_obj_t * tabview) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + return ext->slide_enable ? true : false; +} + +/** + * Get the animation time of tab view when a new tab is loaded + * @param tabview pointer to Tab view object + * @return time of animation in milliseconds + */ +uint16_t lv_tabview_get_anim_time(const lv_obj_t * tabview) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + return ext->anim_time; +} + +/** + * Get a style of a tab view + * @param tabview pointer to a ab view object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_tabview_get_style(const lv_obj_t * tabview, lv_tabview_style_t type) +{ + lv_style_t * style = NULL; + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + + switch(type) { + case LV_TABVIEW_STYLE_BG: + style = lv_obj_get_style(tabview); + break; + case LV_TABVIEW_STYLE_BTN_BG: + style = lv_btnm_get_style(ext->btns, LV_BTNM_STYLE_BG); + break; + case LV_TABVIEW_STYLE_BTN_REL: + style = lv_btnm_get_style(ext->btns, LV_BTNM_STYLE_BTN_REL); + break; + case LV_TABVIEW_STYLE_BTN_PR: + style = lv_btnm_get_style(ext->btns, LV_BTNM_STYLE_BTN_PR); + break; + case LV_TABVIEW_STYLE_BTN_TGL_REL: + style = lv_btnm_get_style(ext->btns, LV_BTNM_STYLE_BTN_TGL_REL); + break; + case LV_TABVIEW_STYLE_BTN_TGL_PR: + style = lv_btnm_get_style(ext->btns, LV_BTNM_STYLE_BTN_TGL_PR); + break; + default: + style = NULL; + break; + } + + return style; +} + +/** + * Get position of tab select buttons + * @param tabview pointer to a ab view object + */ +lv_tabview_btns_pos_t lv_tabview_get_btns_pos(const lv_obj_t * tabview) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + return ext->btns_pos; +} + +/** + * Get whether tab buttons are hidden + * @param tabview pointer to a tab view object + * @return whether tab buttons are hidden + */ +bool lv_tabview_get_btns_hidden(const lv_obj_t *tabview) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + + return ext->btns_hide; +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Signal function of the Tab view + * @param tabview pointer to a Tab view object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_tabview_signal(lv_obj_t * tabview, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(tabview, sign, param); + if(res != LV_RES_OK) return res; + + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + if(sign == LV_SIGNAL_CLEANUP) { + uint8_t i; + for(i = 0; ext->tab_name_ptr[i][0] != '\0'; i++) lv_mem_free(ext->tab_name_ptr[i]); + + lv_mem_free(ext->tab_name_ptr); + ext->tab_name_ptr = NULL; + ext->btns = NULL; /*These objects were children so they are already invalid*/ + ext->content = NULL; + } else if(sign == LV_SIGNAL_CORD_CHG) { + if(ext->content != NULL && + (lv_obj_get_width(tabview) != lv_area_get_width(param) || + lv_obj_get_height(tabview) != lv_area_get_height(param))) { + tabview_realign(tabview); + } + } else if(sign == LV_SIGNAL_FOCUS || sign == LV_SIGNAL_DEFOCUS || sign == LV_SIGNAL_CONTROLL) { + /* The button matrix is not in a group (the tab view is in it) but it should handle the group signals. + * So propagate the related signals to the button matrix manually*/ + if(ext->btns) { + ext->btns->signal_func(ext->btns, sign, param); + } + if(sign == LV_SIGNAL_FOCUS) { + lv_hal_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + /*With ENCODER select the first button only in edit mode*/ + if(indev_type == LV_INDEV_TYPE_ENCODER) { +#if USE_LV_GROUP + lv_group_t * g = lv_obj_get_group(tabview); + if(lv_group_get_editing(g)) { + lv_btnm_ext_t * btnm_ext = lv_obj_get_ext_attr(ext->btns); + btnm_ext->btn_id_pr = 0; + lv_obj_invalidate(ext->btns); + } +#endif + } else { + lv_btnm_ext_t * btnm_ext = lv_obj_get_ext_attr(ext->btns); + btnm_ext->btn_id_pr = 0; + lv_obj_invalidate(ext->btns); + } + } + } else if(sign == LV_SIGNAL_GET_EDITABLE) { + bool * editable = (bool *)param; + *editable = true; + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_tabview"; + } + + return res; +} + + +/** + * Signal function of a tab's page + * @param tab pointer to a tab page object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t tabpage_signal(lv_obj_t * tab_page, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = page_signal(tab_page, sign, param); + if(res != LV_RES_OK) return res; + + lv_obj_t * cont = lv_obj_get_parent(tab_page); + lv_obj_t * tabview = lv_obj_get_parent(cont); + + if(lv_tabview_get_sliding(tabview) == false) return res; + + if(sign == LV_SIGNAL_PRESSED) { + tabpage_pressed_handler(tabview, tab_page); + } else if(sign == LV_SIGNAL_PRESSING) { + tabpage_pressing_handler(tabview, tab_page); + } else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) { + tabpage_press_lost_handler(tabview, tab_page); + } + + return res; +} +/** + * Signal function of the tab page's scrollable object + * @param tab_scrl pointer to a tab page's scrollable object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t tabpage_scrl_signal(lv_obj_t * tab_scrl, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = page_scrl_signal(tab_scrl, sign, param); + if(res != LV_RES_OK) return res; + + lv_obj_t * tab_page = lv_obj_get_parent(tab_scrl); + lv_obj_t * cont = lv_obj_get_parent(tab_page); + lv_obj_t * tabview = lv_obj_get_parent(cont); + + if(lv_tabview_get_sliding(tabview) == false) return res; + + if(sign == LV_SIGNAL_PRESSED) { + tabpage_pressed_handler(tabview, tab_page); + } else if(sign == LV_SIGNAL_PRESSING) { + tabpage_pressing_handler(tabview, tab_page); + } else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) { + tabpage_press_lost_handler(tabview, tab_page); + } + + return res; +} + +/** + * Called when a tab's page or scrollable object is pressed + * @param tabview pointer to the btn view object + * @param tabpage pointer to the page of a btn + */ +static void tabpage_pressed_handler(lv_obj_t * tabview, lv_obj_t * tabpage) +{ + (void)tabpage; + + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + lv_indev_t * indev = lv_indev_get_act(); + lv_indev_get_point(indev, &ext->point_last); +} + +/** + * Called when a tab's page or scrollable object is being pressed + * @param tabview pointer to the btn view object + * @param tabpage pointer to the page of a btn + */ +static void tabpage_pressing_handler(lv_obj_t * tabview, lv_obj_t * tabpage) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + lv_indev_t * indev = lv_indev_get_act(); + lv_point_t point_act; + lv_indev_get_point(indev, &point_act); + lv_coord_t x_diff = point_act.x - ext->point_last.x; + lv_coord_t y_diff = point_act.y - ext->point_last.y; + + if(ext->draging == 0) { + if(x_diff >= LV_INDEV_DRAG_LIMIT || x_diff <= -LV_INDEV_DRAG_LIMIT) { + ext->drag_hor = 1; + ext->draging = 1; + lv_obj_set_drag(lv_page_get_scrl(tabpage), false); + } else if(y_diff >= LV_INDEV_DRAG_LIMIT || y_diff <= -LV_INDEV_DRAG_LIMIT) { + ext->drag_hor = 0; + ext->draging = 1; + } + } + if(ext->drag_hor) { + lv_obj_set_x(ext->content, lv_obj_get_x(ext->content) + point_act.x - ext->point_last.x); + ext->point_last.x = point_act.x; + ext->point_last.y = point_act.y; + + /*Move the indicator*/ + lv_coord_t indic_width = lv_obj_get_width(ext->indic); + lv_style_t * tabs_style = lv_obj_get_style(ext->btns); + lv_style_t * indic_style = lv_obj_get_style(ext->indic); + lv_coord_t p = ((tabpage->coords.x1 - tabview->coords.x1) * (indic_width + tabs_style->body.padding.inner)) / lv_obj_get_width(tabview); + + lv_obj_set_x(ext->indic, indic_width * ext->tab_cur + tabs_style->body.padding.inner * ext->tab_cur + indic_style->body.padding.hor - p); + } +} + +/** + * Called when a tab's page or scrollable object is released or the press id lost + * @param tabview pointer to the btn view object + * @param tabpage pointer to the page of a btn + */ +static void tabpage_press_lost_handler(lv_obj_t * tabview, lv_obj_t * tabpage) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + ext->drag_hor = 0; + ext->draging = 0; + + lv_obj_set_drag(lv_page_get_scrl(tabpage), true); + + lv_indev_t * indev = lv_indev_get_act(); + lv_point_t point_act; + lv_indev_get_point(indev, &point_act); + lv_point_t vect; + lv_indev_get_vect(indev, &vect); + lv_coord_t x_predict = 0; + + while(vect.x != 0) { + x_predict += vect.x; + vect.x = vect.x * (100 - LV_INDEV_DRAG_THROW) / 100; + } + + lv_coord_t page_x1 = tabpage->coords.x1 - tabview->coords.x1 + x_predict; + lv_coord_t page_x2 = page_x1 + lv_obj_get_width(tabpage); + lv_coord_t treshold = lv_obj_get_width(tabview) / 2; + + uint16_t tab_cur = ext->tab_cur; + if(page_x1 > treshold) { + if(tab_cur != 0) tab_cur--; + } else if(page_x2 < treshold) { + if(tab_cur < ext->tab_cnt - 1) tab_cur++; + } + + lv_tabview_set_tab_act(tabview, tab_cur, true); +} + +/** + * Called when a tab button is released + * @param tab_btnm pointer to the tab's button matrix object + * @param id the id of the tab (>= 0) + * @return LV_ACTION_RES_OK because the button matrix in not deleted in the function + */ +static lv_res_t tab_btnm_action(lv_obj_t * tab_btnm, const char * tab_name) +{ + lv_obj_t * tab = lv_obj_get_parent(tab_btnm); + const char ** tabs_map = lv_btnm_get_map(tab_btnm); + + uint8_t i = 0; + + while(tabs_map[i][0] != '\0') { + if(strcmp(&tabs_map[i][1], tab_name) == 0) break; /*[1] to skip the control byte*/ + i++; + } + + lv_tabview_set_tab_act(tab, i, true); + + return LV_RES_OK; +} + +/** + * Realign and resize the elements of Tab view + * @param tabview pointer to a Tab view object + */ +static void tabview_realign(lv_obj_t * tabview) +{ + lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview); + + lv_obj_set_width(ext->btns, lv_obj_get_width(tabview)); + + if(ext->btns_hide) { + lv_obj_set_hidden(ext->btns, true); + lv_obj_set_hidden(ext->indic, true); + lv_obj_set_height(ext->content, lv_obj_get_height(tabview)); + lv_obj_align(ext->content, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); + } + else if(ext->tab_cnt != 0) { + lv_obj_set_hidden(ext->btns, false); + lv_obj_set_hidden(ext->indic, false); + + lv_style_t * style_btn_bg = lv_tabview_get_style(tabview, LV_TABVIEW_STYLE_BTN_BG); + lv_style_t * style_btn_rel = lv_tabview_get_style(tabview, LV_TABVIEW_STYLE_BTN_REL); + + /*Set the indicator widths*/ + lv_coord_t indic_width = (lv_obj_get_width(tabview) - style_btn_bg->body.padding.inner * (ext->tab_cnt - 1) - + 2 * style_btn_bg->body.padding.hor) / ext->tab_cnt; + lv_obj_set_width(ext->indic, indic_width); + + /*Set the tabs height*/ + lv_coord_t btns_height = lv_font_get_height(style_btn_rel->text.font) + + 2 * style_btn_rel->body.padding.ver + + 2 * style_btn_bg->body.padding.ver; + lv_obj_set_height(ext->btns, btns_height); + + lv_obj_set_height(ext->content, lv_obj_get_height(tabview) - lv_obj_get_height(ext->btns)); + + switch(ext->btns_pos) { + case LV_TABVIEW_BTNS_POS_TOP: + lv_obj_align(ext->btns, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); + lv_obj_align(ext->content, ext->btns, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0); + break; + case LV_TABVIEW_BTNS_POS_BOTTOM: + lv_obj_align(ext->content, NULL, LV_ALIGN_IN_TOP_LEFT, 0, 0); + lv_obj_align(ext->btns, ext->content, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0); + break; + } + } + + lv_obj_t * pages = lv_obj_get_child(ext->content, NULL); + while(pages != NULL) { + if(lv_obj_get_signal_func(pages) == tabpage_signal) { /*Be sure adjust only the pages (user can other things)*/ + lv_obj_set_size(pages, lv_obj_get_width(tabview), lv_obj_get_height(ext->content)); + } + pages = lv_obj_get_child(ext->content, pages); + } + + if(!ext->btns_hide) { + lv_obj_align(ext->indic, ext->btns, LV_ALIGN_IN_TOP_LEFT, 0, 0); + } + + lv_tabview_set_tab_act(tabview, ext->tab_cur, false); +} +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_tabview.h b/bdk/libs/lvgl/lv_objx/lv_tabview.h new file mode 100644 index 00000000..2d60c3c8 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_tabview.h @@ -0,0 +1,252 @@ +/** + * @file lv_tabview.h + * + */ + +#ifndef LV_TABVIEW_H +#define LV_TABVIEW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_TABVIEW != 0 + +/*Testing of dependencies*/ +#if USE_LV_BTNM == 0 +#error "lv_tabview: lv_btnm is required. Enable it in lv_conf.h (USE_LV_BTNM 1) " +#endif + +#if USE_LV_PAGE == 0 +#error "lv_tabview: lv_page is required. Enable it in lv_conf.h (USE_LV_PAGE 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "../lv_objx/lv_win.h" +#include "../lv_objx/lv_page.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/* parametes: pointer to a tabview object, tab_id + * return: LV_RES_INV: to prevent the loading of the tab; LV_RES_OK: if everything is fine*/ +typedef lv_res_t (*lv_tabview_action_t)(lv_obj_t *, uint16_t); + + +enum { + LV_TABVIEW_BTNS_POS_TOP, + LV_TABVIEW_BTNS_POS_BOTTOM, +}; +typedef uint8_t lv_tabview_btns_pos_t; + +/*Data of tab*/ +typedef struct +{ + /*Ext. of ancestor*/ + /*New data for this type */ + lv_obj_t * btns; + lv_obj_t * indic; + lv_obj_t * content; /*A rectangle to show the current tab*/ + const char ** tab_name_ptr; + lv_point_t point_last; + uint16_t tab_cur; + uint16_t tab_cnt; + uint16_t anim_time; + uint8_t slide_enable :1; /*1: enable horizontal sliding by touch pad*/ + uint8_t draging :1; + uint8_t drag_hor :1; + uint8_t btns_hide :1; + lv_tabview_btns_pos_t btns_pos :1; + lv_tabview_action_t tab_load_action; +} lv_tabview_ext_t; + +enum { + LV_TABVIEW_STYLE_BG, + LV_TABVIEW_STYLE_INDIC, + LV_TABVIEW_STYLE_BTN_BG, + LV_TABVIEW_STYLE_BTN_REL, + LV_TABVIEW_STYLE_BTN_PR, + LV_TABVIEW_STYLE_BTN_TGL_REL, + LV_TABVIEW_STYLE_BTN_TGL_PR, +}; +typedef uint8_t lv_tabview_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + + +/** + * Create a Tab view object + * @param par pointer to an object, it will be the parent of the new tab + * @param copy pointer to a tab object, if not NULL then the new object will be copied from it + * @return pointer to the created tab + */ +lv_obj_t * lv_tabview_create(lv_obj_t * par, const lv_obj_t * copy); + +/** + * Delete all children of the scrl object, without deleting scrl child. + * @param obj pointer to an object + */ +void lv_tabview_clean(lv_obj_t *obj); + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Add a new tab with the given name + * @param tabview pointer to Tab view object where to ass the new tab + * @param name the text on the tab button + * @return pointer to the created page object (lv_page). You can create your content here + */ +lv_obj_t * lv_tabview_add_tab(lv_obj_t * tabview, const char * name); + +/*===================== + * Setter functions + *====================*/ + +/** + * Set a new tab + * @param tabview pointer to Tab view object + * @param id index of a tab to load + * @param anim_en true: set with sliding animation; false: set immediately + */ +void lv_tabview_set_tab_act(lv_obj_t * tabview, uint16_t id, bool anim_en); + +/** + * Set an action to call when a tab is loaded (Good to create content only if required) + * lv_tabview_get_act() still gives the current (old) tab (to remove content from here) + * @param tabview pointer to a tabview object + * @param action pointer to a function to call when a tab is loaded + */ +void lv_tabview_set_tab_load_action(lv_obj_t *tabview, lv_tabview_action_t action); + +/** + * Enable horizontal sliding with touch pad + * @param tabview pointer to Tab view object + * @param en true: enable sliding; false: disable sliding + */ +void lv_tabview_set_sliding(lv_obj_t * tabview, bool en); + +/** + * Set the animation time of tab view when a new tab is loaded + * @param tabview pointer to Tab view object + * @param anim_time time of animation in milliseconds + */ +void lv_tabview_set_anim_time(lv_obj_t * tabview, uint16_t anim_time); + +/** + * Set the style of a tab view + * @param tabview pointer to a tan view object + * @param type which style should be set + * @param style pointer to the new style + */ +void lv_tabview_set_style(lv_obj_t *tabview, lv_tabview_style_t type, lv_style_t *style); + +/** + * Set the position of tab select buttons + * @param tabview pointer to a tab view object + * @param btns_pos which button position + */ +void lv_tabview_set_btns_pos(lv_obj_t *tabview, lv_tabview_btns_pos_t btns_pos); + +/** + * Set whether tab buttons are hidden + * @param tabview pointer to a tab view object + * @param en whether tab buttons are hidden + */ +void lv_tabview_set_btns_hidden(lv_obj_t *tabview, bool en); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the index of the currently active tab + * @param tabview pointer to Tab view object + * @return the active tab index + */ +uint16_t lv_tabview_get_tab_act(const lv_obj_t * tabview); + +/** + * Get the number of tabs + * @param tabview pointer to Tab view object + * @return tab count + */ +uint16_t lv_tabview_get_tab_count(const lv_obj_t * tabview); +/** + * Get the page (content area) of a tab + * @param tabview pointer to Tab view object + * @param id index of the tab (>= 0) + * @return pointer to page (lv_page) object + */ +lv_obj_t * lv_tabview_get_tab(const lv_obj_t * tabview, uint16_t id); + +/** + * Get the tab load action + * @param tabview pointer to a tabview object + * @param return the current tab load action + */ +lv_tabview_action_t lv_tabview_get_tab_load_action(const lv_obj_t *tabview); + +/** + * Get horizontal sliding is enabled or not + * @param tabview pointer to Tab view object + * @return true: enable sliding; false: disable sliding + */ +bool lv_tabview_get_sliding(const lv_obj_t * tabview); + +/** + * Get the animation time of tab view when a new tab is loaded + * @param tabview pointer to Tab view object + * @return time of animation in milliseconds + */ +uint16_t lv_tabview_get_anim_time(const lv_obj_t * tabview); + +/** + * Get a style of a tab view + * @param tabview pointer to a ab view object + * @param type which style should be get + * @return style pointer to a style + */ +lv_style_t * lv_tabview_get_style(const lv_obj_t *tabview, lv_tabview_style_t type); + +/** + * Get position of tab select buttons + * @param tabview pointer to a ab view object + */ +lv_tabview_btns_pos_t lv_tabview_get_btns_pos(const lv_obj_t *tabview); + +/** + * Get whether tab buttons are hidden + * @param tabview pointer to a tab view object + * @return whether tab buttons are hidden + */ +bool lv_tabview_get_btns_hidden(const lv_obj_t *tabview); + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_TABVIEW*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_TABVIEW_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_tileview.c b/bdk/libs/lvgl/lv_objx/lv_tileview.c new file mode 100644 index 00000000..7435a9b3 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_tileview.c @@ -0,0 +1,578 @@ +/** + * @file lv_tileview.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_tileview.h" +#if USE_LV_TILEVIEW != 0 + +#include "lv_cont.h" +#include "../lv_themes/lv_theme.h" + +/********************* + * DEFINES + *********************/ +#if USE_LV_ANIMATION +# ifndef LV_TILEVIEW_ANIM_TIME +# define LV_TILEVIEW_ANIM_TIME 300 /*Animation time loading a tile [ms] (0: no animation) */ +# endif +#else +# undef LV_TILEVIEW_ANIM_TIME +# define LV_TILEVIEW_ANIM_TIME 0 /*No animations*/ +#endif + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_tileview_signal(lv_obj_t * tileview, lv_signal_t sign, void * param); +static lv_res_t lv_tileview_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param); +static lv_res_t element_signal_func(lv_obj_t * element, lv_signal_t sign, void * param); +static void drag_end_handler(lv_obj_t * tileview); +static bool set_valid_drag_dirs(lv_obj_t * tileview); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; +static lv_signal_func_t ancestor_scrl_signal; +static lv_design_func_t ancestor_design; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a tileview object + * @param par pointer to an object, it will be the parent of the new tileview + * @param copy pointer to a tileview object, if not NULL then the new object will be copied from it + * @return pointer to the created tileview + */ +lv_obj_t * lv_tileview_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("tileview create started"); + + /*Create the ancestor of tileview*/ + lv_obj_t * new_tileview = lv_page_create(par, copy); + lv_mem_assert(new_tileview); + if(new_tileview == NULL) return NULL; + + /*Allocate the tileview type specific extended data*/ + lv_tileview_ext_t * ext = lv_obj_allocate_ext_attr(new_tileview, sizeof(lv_tileview_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_tileview); + if(ancestor_scrl_signal == NULL) ancestor_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(new_tileview)); + if(ancestor_design == NULL) ancestor_design = lv_obj_get_design_func(new_tileview); + + /*Initialize the allocated 'ext' */ + ext->anim_time = LV_TILEVIEW_ANIM_TIME; + ext->action = NULL; + ext->act_id.x = 0; + ext->act_id.y = 0; + ext->valid_pos = NULL; + + /*The signal and design functions are not copied so set them here*/ + lv_obj_set_signal_func(new_tileview, lv_tileview_signal); + lv_obj_set_signal_func(lv_page_get_scrl(new_tileview), lv_tileview_scrl_signal); + + /*Init the new tileview*/ + if(copy == NULL) { + lv_obj_set_size(new_tileview, LV_HOR_RES, LV_VER_RES); + lv_obj_set_drag_throw(lv_page_get_scrl(new_tileview), false); + lv_page_set_scrl_fit(new_tileview, true, true); + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_page_set_style(new_tileview, LV_PAGE_STYLE_BG, th->tileview.bg); + lv_page_set_style(new_tileview, LV_PAGE_STYLE_SCRL, th->tileview.scrl); + lv_page_set_style(new_tileview, LV_PAGE_STYLE_SB, th->tileview.sb); + } else { + lv_page_set_style(new_tileview, LV_PAGE_STYLE_BG, &lv_style_transp_tight); + lv_page_set_style(new_tileview, LV_PAGE_STYLE_SCRL, &lv_style_transp_tight); + } + } + /*Copy an existing tileview*/ + else { + lv_tileview_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + ext->act_id.x = copy_ext->act_id.x; + ext->act_id.y = copy_ext->act_id.y; + ext->action = copy_ext->action; + ext->anim_time = copy_ext->anim_time; + + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_tileview); + } + + LV_LOG_INFO("tileview created"); + + return new_tileview; +} + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Register an object on the tileview. The register object will able to slide the tileview + * @param element pointer to an object + */ +void lv_tileview_add_element(lv_obj_t * element) +{ + lv_obj_set_free_ptr(element, lv_obj_get_signal_func(element)); + lv_obj_set_signal_func(element, element_signal_func); + lv_obj_set_drag_parent(element, true); +} + + +/*===================== + * Setter functions + *====================*/ + +/** + * Set the valid position's indices. The scrolling will be possible only to these positions. + * @param tileview pointer to a Tileview object + * @param valid_pos array width the indices. E.g. `lv_point_t p[] = {{0,0}, {1,0}, {1,1}, {LV_COORD_MIN, LV_COORD_MIN}};` + * Must be closed with `{LV_COORD_MIN, LV_COORD_MIN}`. Only the pointer is saved so can't be a local variable. + */ +void lv_tileview_set_valid_positions(lv_obj_t * tileview, const lv_point_t * valid_pos) +{ + lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview); + ext->valid_pos = valid_pos; +} + +/** + * Set the tile to be shown + * @param tileview pointer to a tileview object + * @param x column id (0, 1, 2...) + * @param y line id (0, 1, 2...) + * @param anim_en true: move with animation + */ +void lv_tileview_set_tile_act(lv_obj_t * tileview, lv_coord_t x, lv_coord_t y, bool anim_en) +{ +#if USE_LV_ANIMATION == 0 + anim_en = false; +#endif + + lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview); + + + uint16_t i; + bool valid = false; + for(i = 0; ext->valid_pos[i].x != LV_COORD_MIN; i++) { + if(ext->valid_pos[i].x == x && ext->valid_pos[i].y == y) { + valid = true; + } + } + + if(valid == false) return; /*Don't load not valid tiles*/ + + lv_res_t res = LV_RES_OK; + if(ext->action) res = ext->action(tileview, x, y); + if(res != LV_RES_OK) return; /*Prevent the tile loading*/ + + ext->act_id.x = x; + ext->act_id.y = y; + + lv_coord_t x_coord = -x * lv_obj_get_width(tileview); + lv_coord_t y_coord = -y * lv_obj_get_height(tileview); + lv_obj_t * scrl = lv_page_get_scrl(tileview); + if(anim_en) { +#if USE_LV_ANIMATION + lv_coord_t x_act = lv_obj_get_x(scrl); + lv_coord_t y_act = lv_obj_get_y(scrl); + + lv_anim_t a; + a.var = scrl; + a.fp = (lv_anim_fp_t)lv_obj_set_x; + a.path = lv_anim_path_linear; + a.end_cb = NULL; + a.act_time = 0; + a.time = ext->anim_time; + a.playback = 0; + a.playback_pause = 0; + a.repeat = 0; + a.repeat_pause = 0; + + if(x_coord != x_act) { + a.start = x_act; + a.end = x_coord; + lv_anim_create(&a); + } + + if(y_coord != y_act) { + a.start = y_act; + a.end = y_coord; + a.fp = (lv_anim_fp_t)lv_obj_set_y; + lv_anim_create(&a); + } +#endif + } else { + lv_obj_set_pos(scrl, x_coord, y_coord); + } +} + +void lv_tileview_set_tile_load_action(lv_obj_t * tileview, lv_tileview_action_t action) +{ + lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview); + ext->action = action; + +} + +/** + * Set a style of a tileview. + * @param tileview pointer to tileview object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_tileview_set_style(lv_obj_t * tileview, lv_tileview_style_t type, lv_style_t * style) +{ + + switch(type) { + case LV_TILEVIEW_STYLE_BG: + lv_obj_set_style(tileview, style); + break; + } +} + +/*===================== + * Getter functions + *====================*/ + +/* + * New object specific "get" functions come here + */ + +/** + * Get style of a tileview. + * @param tileview pointer to tileview object + * @param type which style should be get + * @return style pointer to the style + */ +lv_style_t * lv_tileview_get_style(const lv_obj_t * tileview, lv_tileview_style_t type) +{ + lv_style_t * style = NULL; + switch(type) { + case LV_TILEVIEW_STYLE_BG: + style = lv_obj_get_style(tileview); + break; + default: + style = NULL; + } + + return style; +} + +/*===================== + * Other functions + *====================*/ + +/* + * New object specific "other" functions come here + */ + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Signal function of the tileview + * @param tileview pointer to a tileview object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_tileview_signal(lv_obj_t * tileview, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(tileview, sign, param); + if(res != LV_RES_OK) return res; + + + if(sign == LV_SIGNAL_CLEANUP) { + /*Nothing to cleanup. (No dynamically allocated memory in 'ext')*/ + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_tileview"; + } + + return res; +} + +/** + * Signal function of the tileview scrollable + * @param tileview pointer to the scrollable part of the tileview object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_tileview_scrl_signal(lv_obj_t * scrl, lv_signal_t sign, void * param) +{ + + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_scrl_signal(scrl, sign, param); + if(res != LV_RES_OK) return res; + + lv_obj_t * tileview = lv_obj_get_parent(scrl); + lv_style_t * style_bg = lv_tileview_get_style(tileview, LV_TILEVIEW_STYLE_BG); + + + /*Apply constraint on moving of the tileview*/ + if(sign == LV_SIGNAL_CORD_CHG) { + lv_indev_t * indev = lv_indev_get_act(); + if(indev) { + lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview); + + /*Set horizontal drag constraint if no vertical constraint an dragged to valid x direction */ + if(ext->drag_ver == 0 && + ((ext->drag_right_en && indev->proc.drag_sum.x <= -LV_INDEV_DRAG_LIMIT) || + (ext->drag_left_en && indev->proc.drag_sum.x >= LV_INDEV_DRAG_LIMIT))) { + ext->drag_hor = 1; + } + /*Set vertical drag constraint if no horizontal constraint an dragged to valid y direction */ + if(ext->drag_hor == 0 && + ((ext->drag_bottom_en && indev->proc.drag_sum.y <= -LV_INDEV_DRAG_LIMIT) || + (ext->drag_top_en && indev->proc.drag_sum.y >= LV_INDEV_DRAG_LIMIT))) { + ext->drag_ver = 1; + } + + if(ext->drag_hor) { + ext->page.edge_flash.top_ip = 0; + ext->page.edge_flash.bottom_ip = 0; + } + + if(ext->drag_ver) { + ext->page.edge_flash.right_ip = 0; + ext->page.edge_flash.left_ip = 0; + } + + lv_coord_t x = lv_obj_get_x(scrl); + lv_coord_t y = lv_obj_get_y(scrl); + lv_coord_t h = lv_obj_get_height(tileview); + lv_coord_t w = lv_obj_get_width(tileview); + if(ext->drag_top_en == 0) { + if(y > -(ext->act_id.y * h) && indev->proc.vect.y > 0 && ext->drag_hor == 0) { + if(ext->page.edge_flash.enabled && + ext->page.edge_flash.left_ip == 0 && ext->page.edge_flash.right_ip == 0 && + ext->page.edge_flash.top_ip == 0 && ext->page.edge_flash.bottom_ip == 0) { + ext->page.edge_flash.top_ip = 1; + lv_page_start_edge_flash(tileview); + } + + lv_obj_set_y(scrl, -ext->act_id.y * h + style_bg->body.padding.ver); + } + } + if(ext->drag_bottom_en == 0 && indev->proc.vect.y < 0 && ext->drag_hor == 0) { + if(y < -(ext->act_id.y * h)) { + if(ext->page.edge_flash.enabled && + ext->page.edge_flash.left_ip == 0 && ext->page.edge_flash.right_ip == 0 && + ext->page.edge_flash.top_ip == 0 && ext->page.edge_flash.bottom_ip == 0) { + ext->page.edge_flash.bottom_ip = 1; + lv_page_start_edge_flash(tileview); + } + } + + lv_obj_set_y(scrl, -ext->act_id.y * h + style_bg->body.padding.ver); + } + if(ext->drag_left_en == 0) { + if(x > -(ext->act_id.x * w) && indev->proc.vect.x > 0 && ext->drag_ver == 0) { + if(ext->page.edge_flash.enabled && + ext->page.edge_flash.left_ip == 0 && ext->page.edge_flash.right_ip == 0 && + ext->page.edge_flash.top_ip == 0 && ext->page.edge_flash.bottom_ip == 0) { + ext->page.edge_flash.left_ip = 1; + lv_page_start_edge_flash(tileview); + } + + lv_obj_set_x(scrl, -ext->act_id.x * w + style_bg->body.padding.hor); + } + } + if(ext->drag_right_en == 0 && indev->proc.vect.x < 0 && ext->drag_ver == 0) { + if(x < -(ext->act_id.x * w)) { + if(ext->page.edge_flash.enabled && + ext->page.edge_flash.left_ip == 0 && ext->page.edge_flash.right_ip == 0 && + ext->page.edge_flash.top_ip == 0 && ext->page.edge_flash.bottom_ip == 0) { + ext->page.edge_flash.right_ip = 1; + lv_page_start_edge_flash(tileview); + } + } + + lv_obj_set_x(scrl, -ext->act_id.x * w + style_bg->body.padding.hor); + } + + /*Apply the drag constraints*/ + if(ext->drag_ver == 0) lv_obj_set_y(scrl, - ext->act_id.y * lv_obj_get_height(tileview) + style_bg->body.padding.ver); + if(ext->drag_hor == 0) lv_obj_set_x(scrl, - ext->act_id.x * lv_obj_get_width(tileview) + style_bg->body.padding.hor); + } + } + + return res; + +} + +/** + * This function is applied called for the elements of the tileview. Used when the element is + * @param element + * @param sign + * @param param + * @return + */ +static lv_res_t element_signal_func(lv_obj_t * element, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + lv_signal_func_t sign_func = lv_obj_get_free_ptr(element); + res = sign_func(element, sign, param); + if(res != LV_RES_OK) return res; + + /*Initialize some variables on PRESS*/ + if(sign == LV_SIGNAL_PRESSED) { + /*Get the tileview from the element*/ + lv_obj_t * tileview = lv_obj_get_parent(element); + while(tileview) { + if(lv_obj_get_signal_func(tileview) != lv_tileview_signal) tileview = lv_obj_get_parent(tileview); + else break; + } + + if(tileview) { + lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview); + ext->drag_hor = 0; + ext->drag_ver = 0; + set_valid_drag_dirs(tileview); + } + } + + /*Animate the tabview to the correct location on RELEASE*/ + else if(sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_RELEASED) { + + /*Get the tileview from the element*/ + lv_obj_t * tileview = lv_obj_get_parent(element); + while(tileview) { + if(lv_obj_get_signal_func(tileview) != lv_tileview_signal) tileview = lv_obj_get_parent(tileview); + else break; + } + + if(tileview) { + /* If the element was dragged and it moved the tileview finish the drag manually to + * let the tileview to finish the move.*/ + lv_indev_t * indev = lv_indev_get_act(); + lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview); + if(indev->proc.drag_in_prog && (ext->drag_hor || ext->drag_ver)) { + + lv_obj_t * drag_obj = element; + while(lv_obj_get_drag_parent(drag_obj)) { + drag_obj = lv_obj_get_parent(drag_obj); + if(drag_obj == NULL) break; + } + indev->proc.drag_in_prog = 0; + if(drag_obj) drag_obj->signal_func(drag_obj, LV_SIGNAL_DRAG_END, NULL); + } + + drag_end_handler(tileview); + } + } + + return res; +} + +/** + * Called when the user releases an element of the tileview after dragging it. + * @param tileview pointer to a tileview object + */ +static void drag_end_handler(lv_obj_t * tileview) +{ + lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview); + lv_indev_t * indev = lv_indev_get_act(); + lv_point_t point_act; + lv_indev_get_point(indev, &point_act); + lv_obj_t * scrl = lv_page_get_scrl(tileview); + lv_point_t p; + + p.x = - (scrl->coords.x1 - LV_HOR_RES / 2); + p.y = - (scrl->coords.y1 - LV_VER_RES / 2); + + /*From the drag vector (drag throw) predict the end position*/ + if(ext->drag_hor) { + lv_point_t vect; + lv_indev_get_vect(indev, &vect); + lv_coord_t predict = 0; + + while(vect.x != 0) { + predict += vect.x; + vect.x = vect.x * (100 - LV_INDEV_DRAG_THROW) / 100; + } + + p.x -= predict; + } + else if(ext->drag_ver) { + lv_point_t vect; + lv_indev_get_vect(indev, &vect); + lv_coord_t predict = 0; + + while(vect.y != 0) { + predict += vect.y; + vect.y = vect.y * (100 - LV_INDEV_DRAG_THROW) / 100; + } + + p.y -= predict; + } + + /*Get the index of the tile*/ + p.x = p.x / lv_obj_get_width(tileview); + p.y = p.y / lv_obj_get_height(tileview); + + /*Max +- move*/ + lv_coord_t x_move = p.x - ext->act_id.x; + lv_coord_t y_move = p.y - ext->act_id.y; + if(x_move < -1) x_move = -1; + if(x_move > 1) x_move = 1; + if(y_move < -1) y_move = -1; + if(y_move > 1) y_move = 1; + + /*Set the new tile*/ + lv_tileview_set_tile_act(tileview, ext->act_id.x + x_move, ext->act_id.y + y_move,true); +} + +static bool set_valid_drag_dirs(lv_obj_t * tileview) +{ + + lv_tileview_ext_t * ext = lv_obj_get_ext_attr(tileview); + if(ext->valid_pos == NULL) return false; + + ext->drag_bottom_en = 0; + ext->drag_top_en = 0; + ext->drag_left_en = 0; + ext->drag_right_en = 0; + + uint16_t i; + for(i = 0; ext->valid_pos[i].x != LV_COORD_MIN; i++) { + if(ext->valid_pos[i].x == ext->act_id.x && ext->valid_pos[i].y == ext->act_id.y - 1) ext->drag_top_en = 1; + if(ext->valid_pos[i].x == ext->act_id.x && ext->valid_pos[i].y == ext->act_id.y + 1) ext->drag_bottom_en = 1; + if(ext->valid_pos[i].x == ext->act_id.x - 1 && ext->valid_pos[i].y == ext->act_id.y) ext->drag_left_en = 1; + if(ext->valid_pos[i].x == ext->act_id.x + 1 && ext->valid_pos[i].y == ext->act_id.y) ext->drag_right_en = 1; + } + + return true; +} + + +#endif diff --git a/bdk/libs/lvgl/lv_objx/lv_tileview.h b/bdk/libs/lvgl/lv_objx/lv_tileview.h new file mode 100644 index 00000000..d276feaa --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_tileview.h @@ -0,0 +1,163 @@ +/** + * @file lv_tileview.h + * + */ + + +#ifndef LV_TILEVIEW_H +#define LV_TILEVIEW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_TILEVIEW != 0 + +#include "../lv_objx/lv_page.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + + + +/* parametes: pointer to a tileview object, x, y (tile coordinates to load) + * return: LV_RES_INV: to prevent the loading of the tab; LV_RES_OK: if everything is fine*/ +typedef lv_res_t (*lv_tileview_action_t)(lv_obj_t *, lv_coord_t, lv_coord_t); + +/*Data of tileview*/ +typedef struct { + lv_page_ext_t page; + /*New data for this type */ + const lv_point_t * valid_pos; + uint16_t anim_time; + lv_tileview_action_t action; + lv_point_t act_id; + uint8_t drag_top_en :1; + uint8_t drag_bottom_en :1; + uint8_t drag_left_en :1; + uint8_t drag_right_en :1; + uint8_t drag_hor :1; + uint8_t drag_ver :1; +} lv_tileview_ext_t; + + +/*Styles*/ +enum { + LV_TILEVIEW_STYLE_BG, +}; +typedef uint8_t lv_tileview_style_t; + + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a tileview objects + * @param par pointer to an object, it will be the parent of the new tileview + * @param copy pointer to a tileview object, if not NULL then the new object will be copied from it + * @return pointer to the created tileview + */ +lv_obj_t * lv_tileview_create(lv_obj_t * par, const lv_obj_t * copy); + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Register an object on the tileview. The register object will able to slide the tileview + * @param element pointer to an object + */ +void lv_tileview_add_element(lv_obj_t * element); + +/*===================== + * Setter functions + *====================*/ + + +/** + * Set the valid position's indices. The scrolling will be possible only to these positions. + * @param tileview pointer to a Tileview object + * @param valid_pos array width the indices. E.g. `lv_point_t p[] = {{0,0}, {1,0}, {1,1}, {LV_COORD_MIN, LV_COORD_MIN}};` + * Must be closed with `{LV_COORD_MIN, LV_COORD_MIN}`. Only the pointer is saved so can't be a local variable. + */ +void lv_tileview_set_valid_positions(lv_obj_t * tileview, const lv_point_t * valid_pos); + +/** + * Set the tile to be shown + * @param tileview pointer to a tileview object + * @param x column id (0, 1, 2...) + * @param y line id (0, 1, 2...) + * @param anim_en true: move with animation + */ +void lv_tileview_set_tile_act(lv_obj_t * tileview, lv_coord_t x, lv_coord_t y, bool anim_en); + +/** + * Enable the edge flash effect. (Show an arc when the an edge is reached) + * @param tileview pointer to a Tileview + * @param en true or false to enable/disable end flash + */ +static inline void lv_tileview_set_edge_flash(lv_obj_t * tileview, bool en) +{ + lv_page_set_edge_flash(tileview, en); +} + +/** + * Set a style of a tileview. + * @param tileview pointer to tileview object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_tileview_set_style(lv_obj_t * tileview, lv_tileview_style_t type, lv_style_t *style); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the scroll propagation property + * @param tileview pointer to a Tileview + * @return true or false + */ +static inline bool lv_tileview_get_edge_flash(lv_obj_t * tileview) +{ + return lv_page_get_edge_flash(tileview); +} + +/** + * Get style of a tileview. + * @param tileview pointer to tileview object + * @param type which style should be get + * @return style pointer to the style + */ +lv_style_t * lv_tileview_get_style(const lv_obj_t * tileview, lv_tileview_style_t type); + +/*===================== + * Other functions + *====================*/ + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_TILEVIEW*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_TILEVIEW_H*/ diff --git a/bdk/libs/lvgl/lv_objx/lv_win.c b/bdk/libs/lvgl/lv_objx/lv_win.c new file mode 100644 index 00000000..af81e281 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_win.c @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2019 CTCaer + * Copyright (c) 2020 Storm + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_win.c + * + */ + +/********************* + * INCLUDES + *********************/ +#include "lv_win.h" +#if USE_LV_WIN != 0 + +#include "../lv_themes/lv_theme.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static lv_res_t lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param); +static void lv_win_realign(lv_obj_t * win); + +/********************** + * STATIC VARIABLES + **********************/ +static lv_signal_func_t ancestor_signal; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +/** + * Create a window objects + * @param par pointer to an object, it will be the parent of the new window + * @param copy pointer to a window object, if not NULL then the new object will be copied from it + * @return pointer to the created window + */ +lv_obj_t * lv_win_create(lv_obj_t * par, const lv_obj_t * copy) +{ + LV_LOG_TRACE("window create started"); + + /*Create the ancestor object*/ + lv_obj_t * new_win = lv_obj_create(par, copy); + lv_mem_assert(new_win); + if(new_win == NULL) return NULL; + + if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_win); + + /*Allocate the object type specific extended data*/ + lv_win_ext_t * ext = lv_obj_allocate_ext_attr(new_win, sizeof(lv_win_ext_t)); + lv_mem_assert(ext); + if(ext == NULL) return NULL; + + ext->page = NULL; + ext->header = NULL; + ext->title = NULL; + ext->style_header = &lv_style_plain_color; + ext->style_btn_rel = &lv_style_btn_rel; + ext->style_btn_pr = &lv_style_btn_pr; + ext->btn_size = (LV_DPI) / 2; + + /*Init the new window object*/ + if(copy == NULL) { + lv_obj_set_size(new_win, LV_HOR_RES, LV_VER_RES); + lv_obj_set_pos(new_win, 0, 0); + lv_obj_set_style(new_win, &lv_style_pretty); + + ext->page = lv_page_create(new_win, NULL); + lv_obj_set_protect(ext->page, LV_PROTECT_PARENT); + lv_page_set_sb_mode(ext->page, LV_SB_MODE_AUTO); + lv_page_set_arrow_scroll(ext->page, true); + + /*Create a holder for the header*/ + ext->header = lv_obj_create(new_win, NULL); + /*Move back the header because it is automatically moved to the scrollable */ + lv_obj_set_protect(ext->header, LV_PROTECT_PARENT); + lv_obj_set_parent(ext->header, new_win); + lv_obj_set_width(ext->header, LV_HOR_RES - 62);//// + ext->btn_size = lv_obj_get_height(ext->header) - 3;//// + + /*Create a title on the header*/ + ext->title = lv_label_create(ext->header, NULL); + lv_label_set_text(ext->title, "My title"); + + /*Set the default styles*/ + lv_theme_t * th = lv_theme_get_current(); + if(th) { + lv_win_set_style(new_win, LV_WIN_STYLE_BG, th->win.bg); + lv_win_set_style(new_win, LV_WIN_STYLE_SB, th->win.sb); + lv_win_set_style(new_win, LV_WIN_STYLE_HEADER, th->win.header); + lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT_BG, th->win.content.bg); + lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT_SCRL, th->win.content.scrl); + lv_win_set_style(new_win, LV_WIN_STYLE_BTN_REL, th->win.btn.rel); + lv_win_set_style(new_win, LV_WIN_STYLE_BTN_PR, th->win.btn.pr); + } else { + lv_win_set_style(new_win, LV_WIN_STYLE_BG, &lv_style_plain); + lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT_BG, &lv_style_plain); + lv_win_set_style(new_win, LV_WIN_STYLE_CONTENT_SCRL, &lv_style_transp); + lv_win_set_style(new_win, LV_WIN_STYLE_HEADER, &lv_style_plain_color); + } + + lv_obj_set_signal_func(new_win, lv_win_signal); + lv_obj_set_size(new_win, LV_HOR_RES, LV_VER_RES); + } + /*Copy an existing object*/ + else { + lv_win_ext_t * copy_ext = lv_obj_get_ext_attr(copy); + /*Create the objects*/ + ext->header = lv_obj_create(new_win, copy_ext->header); + ext->title = lv_label_create(ext->header, copy_ext->title); + ext->page = lv_page_create(new_win, copy_ext->page); + ext->btn_size = copy_ext->btn_size; + + /*Copy the control buttons*/ + lv_obj_t * child; + lv_obj_t * cbtn; + child = lv_obj_get_child_back(copy_ext->header, NULL); + child = lv_obj_get_child_back(copy_ext->header, child); /*Sip the title*/ + while(child != NULL) { + cbtn = lv_btn_create(ext->header, child); + lv_img_create(cbtn, lv_obj_get_child(child, NULL)); + child = lv_obj_get_child_back(copy_ext->header, child); + } + + lv_obj_set_signal_func(new_win, lv_win_signal); + + /*Refresh the style with new signal function*/ + lv_obj_refresh_style(new_win); + } + + lv_win_realign(new_win); + + LV_LOG_INFO("window created"); + + return new_win; +} + +/** + * Delete all children of the scrl object, without deleting scrl child. + * @param obj pointer to an object + */ +void lv_win_clean(lv_obj_t * obj) +{ + lv_obj_t * scrl = lv_page_get_scrl(obj); + lv_obj_clean(scrl); +} + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Add control button to the header of the window + * @param win pointer to a window object + * @param img_src an image source ('lv_img_t' variable, path to file or a symbol) + * @param rel_action a function pointer to call when the button is released + * @return pointer to the created button object + */ +lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * img_src, const char * label_src, lv_action_t rel_action) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + + lv_obj_t * btn = lv_btn_create(ext->header, NULL); + lv_btn_set_style(btn, LV_BTN_STYLE_REL, ext->style_btn_rel); + lv_btn_set_style(btn, LV_BTN_STYLE_PR, ext->style_btn_pr); + lv_obj_set_size(btn, lv_obj_get_width(btn), ext->btn_size); + lv_btn_set_fit(btn, true, false); + lv_btn_set_action(btn, LV_BTN_ACTION_CLICK, rel_action); + + if (img_src) + { + lv_obj_t * img = lv_img_create(btn, NULL); + lv_obj_set_click(img, false); + lv_img_set_src(img, img_src); + } + else if (label_src) + { + lv_obj_t *label = lv_label_create(btn, NULL); + lv_label_set_recolor(label, true); + lv_label_set_text(label, label_src); + } + + lv_win_realign(win); + + return btn; +} + +/*===================== + * Setter functions + *====================*/ + +/** + * A release action which can be assigned to a window control button to close it + * @param btn pointer to the released button + * @return always LV_ACTION_RES_INV because the button is deleted with the window + */ +lv_res_t lv_win_close_action(lv_obj_t * btn) +{ + lv_obj_t * win = lv_win_get_from_btn(btn); + + lv_obj_del(win); + + return LV_RES_INV; +} + +/** + * Set the title of a window + * @param win pointer to a window object + * @param title string of the new title + */ +void lv_win_set_title(lv_obj_t * win, const char * title) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + + lv_label_set_text(ext->title, title); + lv_win_realign(win); +} + +/** + * Set the control button size of a window + * @param win pointer to a window object + * @param size control button size + */ +void lv_win_set_btn_size(lv_obj_t * win, lv_coord_t size) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + if(ext->btn_size == size) return; + + ext->btn_size = size; + + lv_win_realign(win); +} + +/** + * Set the layout of the window + * @param win pointer to a window object + * @param layout the layout from 'lv_layout_t' + */ +void lv_win_set_layout(lv_obj_t * win, lv_layout_t layout) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + lv_page_set_scrl_layout(ext->page, layout); +} + +/** + * Set the scroll bar mode of a window + * @param win pointer to a window object + * @param sb_mode the new scroll bar mode from 'lv_sb_mode_t' + */ +void lv_win_set_sb_mode(lv_obj_t * win, lv_sb_mode_t sb_mode) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + lv_page_set_sb_mode(ext->page, sb_mode); +} + +/** + * Set a style of a window + * @param win pointer to a window object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_win_set_style(lv_obj_t * win, lv_win_style_t type, lv_style_t * style) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + + switch(type) { + case LV_WIN_STYLE_BG: + lv_obj_set_style(win, style); + lv_win_realign(win); + break; + case LV_WIN_STYLE_CONTENT_BG: + lv_page_set_style(ext->page, LV_PAGE_STYLE_BG, style); + break; + case LV_WIN_STYLE_CONTENT_SCRL: + lv_page_set_style(ext->page, LV_PAGE_STYLE_SCRL, style); + break; + case LV_WIN_STYLE_SB: + lv_page_set_style(ext->page, LV_PAGE_STYLE_SB, style); + break; + case LV_WIN_STYLE_HEADER: + lv_obj_set_style(ext->header, style); + lv_win_realign(win); + break; + case LV_WIN_STYLE_BTN_REL: + ext->style_btn_rel = style; + break; + case LV_WIN_STYLE_BTN_PR: + ext->style_btn_pr = style; + break; + } + + /*Refresh the existing buttons*/ + if(type == LV_WIN_STYLE_BTN_REL || type == LV_WIN_STYLE_BTN_PR) { + lv_obj_t * btn; + btn = lv_obj_get_child_back(ext->header, NULL); + btn = lv_obj_get_child_back(ext->header, btn); /*Skip the title*/ + while(btn != NULL) { + if(type == LV_WIN_STYLE_BTN_REL) lv_btn_set_style(btn, LV_BTN_STYLE_REL, style); + else lv_btn_set_style(btn, LV_BTN_STYLE_PR, style); + btn = lv_obj_get_child_back(ext->header, btn); + } + } +} + +/** + * Set drag status of a window. If set to 'true' window can be dragged like on a PC. + * @param win pointer to a window object + * @param en whether dragging is enabled + */ +void lv_win_set_drag(lv_obj_t *win, bool en) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + lv_obj_t * win_header = ext->header; + lv_obj_set_drag_parent(win_header, en); + lv_obj_set_drag(win, en); +} + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the title of a window + * @param win pointer to a window object + * @return title string of the window + */ +const char * lv_win_get_title(const lv_obj_t * win) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + return lv_label_get_text(ext->title); +} + +/** +* Get the content holder object of window (`lv_page`) to allow additional customization +* @param win pointer to a window object +* @return the Page object where the window's content is +*/ +lv_obj_t * lv_win_get_content(const lv_obj_t * win) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + return ext->page; +} + +/** + * Get the control button size of a window + * @param win pointer to a window object + * @return control button size + */ +lv_coord_t lv_win_get_btn_size(const lv_obj_t * win) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + return ext->btn_size; +} + +/** + * Get the pointer of a widow from one of its control button. + * It is useful in the action of the control buttons where only button is known. + * @param ctrl_btn pointer to a control button of a window + * @return pointer to the window of 'ctrl_btn' + */ +lv_obj_t * lv_win_get_from_btn(const lv_obj_t * ctrl_btn) +{ + lv_obj_t * header = lv_obj_get_parent(ctrl_btn); + lv_obj_t * win = lv_obj_get_parent(header); + + return win; +} + +/** + * Get the layout of a window + * @param win pointer to a window object + * @return the layout of the window (from 'lv_layout_t') + */ +lv_layout_t lv_win_get_layout(lv_obj_t * win) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + return lv_page_get_scrl_layout(ext->page); +} + +/** + * Get the scroll bar mode of a window + * @param win pointer to a window object + * @return the scroll bar mode of the window (from 'lv_sb_mode_t') + */ +lv_sb_mode_t lv_win_get_sb_mode(lv_obj_t * win) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + return lv_page_get_sb_mode(ext->page); +} + +/** + * Get width of the content area (page scrollable) of the window + * @param win pointer to a window object + * @return the width of the content_bg area + */ +lv_coord_t lv_win_get_width(lv_obj_t * win) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + lv_obj_t * scrl = lv_page_get_scrl(ext->page); + lv_style_t * style_scrl = lv_obj_get_style(scrl); + + return lv_obj_get_width(scrl) - 2 * style_scrl->body.padding.hor; +} + +/** + * Get a style of a window + * @param win pointer to a button object + * @param type which style window be get + * @return style pointer to a style + */ +lv_style_t * lv_win_get_style(const lv_obj_t * win, lv_win_style_t type) +{ + lv_style_t * style = NULL; + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + + switch(type) { + case LV_WIN_STYLE_BG: + style = lv_obj_get_style(win); + break; + case LV_WIN_STYLE_CONTENT_BG: + style = lv_page_get_style(ext->page, LV_PAGE_STYLE_BG); + break; + case LV_WIN_STYLE_CONTENT_SCRL: + style = lv_page_get_style(ext->page, LV_PAGE_STYLE_SCRL); + break; + case LV_WIN_STYLE_SB: + style = lv_page_get_style(ext->page, LV_PAGE_STYLE_SB); + break; + case LV_WIN_STYLE_HEADER: + style = lv_obj_get_style(ext->header); + break; + case LV_WIN_STYLE_BTN_REL: + style = ext->style_btn_rel; + break; + case LV_WIN_STYLE_BTN_PR: + style = ext->style_btn_pr; + break; + default: + style = NULL; + break; + } + + return style; +} + +/*===================== + * Other functions + *====================*/ + +/** + * Focus on an object. It ensures that the object will be visible in the window. + * @param win pointer to a window object + * @param obj pointer to an object to focus (must be in the window) + * @param anim_time scroll animation time in milliseconds (0: no animation) + */ +void lv_win_focus(lv_obj_t * win, lv_obj_t * obj, uint16_t anim_time) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + lv_page_focus(ext->page, obj, anim_time); +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/** + * Signal function of the window + * @param win pointer to a window object + * @param sign a signal type from lv_signal_t enum + * @param param pointer to a signal specific variable + * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted + */ +static lv_res_t lv_win_signal(lv_obj_t * win, lv_signal_t sign, void * param) +{ + lv_res_t res; + + /* Include the ancient signal function */ + res = ancestor_signal(win, sign, param); + if(res != LV_RES_OK) return res; + + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + if(sign == LV_SIGNAL_CHILD_CHG) { /*Move children to the page*/ + lv_obj_t * page = ext->page; + if(page != NULL) { + lv_obj_t * child; + child = lv_obj_get_child(win, NULL); + while(child != NULL) { + if(lv_obj_is_protected(child, LV_PROTECT_PARENT) == false) { + lv_obj_t * tmp = child; + child = lv_obj_get_child(win, child); /*Get the next child before move this*/ + lv_obj_set_parent(tmp, page); + } else { + child = lv_obj_get_child(win, child); + } + } + } + } else if(sign == LV_SIGNAL_STYLE_CHG) { + lv_win_realign(win); + } else if(sign == LV_SIGNAL_CORD_CHG) { + /*If the size is changed refresh the window*/ + if(lv_area_get_width(param) != lv_obj_get_width(win) || + lv_area_get_height(param) != lv_obj_get_height(win)) { + lv_win_realign(win); + } + } else if(sign == LV_SIGNAL_CLEANUP) { + ext->header = NULL; /*These objects were children so they are already invalid*/ + ext->page = NULL; + ext->title = NULL; + } else if(sign == LV_SIGNAL_CONTROLL) { + /*Forward all the control signals to the page*/ + ext->page->signal_func(ext->page, sign, param); + } else if(sign == LV_SIGNAL_GET_TYPE) { + lv_obj_type_t * buf = param; + uint8_t i; + for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/ + if(buf->type[i] == NULL) break; + } + buf->type[i] = "lv_win"; + } + + + return res; +} + +/** + * Realign the building elements of a window + * @param win pointer to window objectker + */ +static void lv_win_realign(lv_obj_t * win) +{ + lv_win_ext_t * ext = lv_obj_get_ext_attr(win); + + if(ext->page == NULL || ext->header == NULL || ext->title == NULL) return; + + lv_style_t * header_style = lv_win_get_style(win, LV_WIN_STYLE_HEADER); + lv_obj_set_size(ext->header, lv_obj_get_width(win) - 0, ext->btn_size + 2 * header_style->body.padding.ver); + + bool first_btn = true; + lv_obj_t * btn; + lv_obj_t * btn_prev = NULL; + /*Refresh the size of all control buttons*/ + btn = lv_obj_get_child_back(ext->header, NULL); + btn = lv_obj_get_child_back(ext->header, btn); /*Skip the title*/ + while(btn != NULL) { + lv_obj_set_size(btn, lv_obj_get_width(btn), ext->btn_size); + if(first_btn) { + lv_obj_align(btn, ext->header, LV_ALIGN_IN_RIGHT_MID, - header_style->body.padding.hor, 0); + first_btn = false; + } else { + lv_obj_align(btn, btn_prev, LV_ALIGN_OUT_LEFT_MID, - header_style->body.padding.inner, 0); + } + btn_prev = btn; + btn = lv_obj_get_child_back(ext->header, btn); + } + + + lv_obj_align(ext->title, NULL, LV_ALIGN_IN_LEFT_MID, ext->style_header->body.padding.hor, 0); + + lv_obj_set_pos(ext->header, 0, 0); + + lv_obj_set_size(ext->page, lv_obj_get_width(win), lv_obj_get_height(win) - lv_obj_get_height(ext->header)); + lv_obj_align(ext->page, ext->header, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 0); +} + +#endif + diff --git a/bdk/libs/lvgl/lv_objx/lv_win.h b/bdk/libs/lvgl/lv_objx/lv_win.h new file mode 100644 index 00000000..87debcf1 --- /dev/null +++ b/bdk/libs/lvgl/lv_objx/lv_win.h @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2019 CTCaer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file lv_win.h + * + */ + +#ifndef LV_WIN_H +#define LV_WIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#ifdef LV_CONF_INCLUDE_SIMPLE +#include "lv_conf.h" +#else +#include "../../lv_conf.h" +#endif + +#if USE_LV_WIN != 0 + +/*Testing of dependencies*/ +#if USE_LV_BTN == 0 +#error "lv_win: lv_btn is required. Enable it in lv_conf.h (USE_LV_BTN 1) " +#endif + +#if USE_LV_LABEL == 0 +#error "lv_win: lv_label is required. Enable it in lv_conf.h (USE_LV_LABEL 1) " +#endif + +#if USE_LV_IMG == 0 +#error "lv_win: lv_img is required. Enable it in lv_conf.h (USE_LV_IMG 1) " +#endif + + +#if USE_LV_PAGE == 0 +#error "lv_win: lv_page is required. Enable it in lv_conf.h (USE_LV_PAGE 1) " +#endif + +#include "../lv_core/lv_obj.h" +#include "lv_cont.h" +#include "lv_btn.h" +#include "lv_label.h" +#include "lv_img.h" +#include "lv_page.h" + +/********************* + * DEFINES + *********************/ + +/********************** + * TYPEDEFS + **********************/ + +/*Data of window*/ +typedef struct +{ + /*Ext. of ancestor*/ + /*New data for this type */ + lv_obj_t * page; /*Pointer to a page which holds the content*/ + lv_obj_t * header; /*Pointer to the header container of the window*/ + lv_obj_t * title; /*Pointer to the title label of the window*/ + lv_style_t * style_header; /*Style of the header container*/ + lv_style_t * style_btn_rel; /*Control button releases style*/ + lv_style_t * style_btn_pr; /*Control button pressed style*/ + lv_coord_t btn_size; /*Size of the control buttons (square)*/ +} lv_win_ext_t; + +enum { + LV_WIN_STYLE_BG, + LV_WIN_STYLE_CONTENT_BG, + LV_WIN_STYLE_CONTENT_SCRL, + LV_WIN_STYLE_SB, + LV_WIN_STYLE_HEADER, + LV_WIN_STYLE_BTN_REL, + LV_WIN_STYLE_BTN_PR, + LV_WIN_STYLE_CONTENT, //Window content style. hinzugefügt Fenster Style +}; +typedef uint8_t lv_win_style_t; + +/********************** + * GLOBAL PROTOTYPES + **********************/ + +/** + * Create a window objects + * @param par pointer to an object, it will be the parent of the new window + * @param copy pointer to a window object, if not NULL then the new object will be copied from it + * @return pointer to the created window + */ +lv_obj_t * lv_win_create(lv_obj_t * par, const lv_obj_t * copy); + +/** + * Delete all children of the scrl object, without deleting scrl child. + * @param obj pointer to an object + */ +void lv_win_clean(lv_obj_t *obj); + +/*====================== + * Add/remove functions + *=====================*/ + +/** + * Add control button to the header of the window + * @param win pointer to a window object + * @param img_src an image source ('lv_img_t' variable, path to file or a symbol) + * @param rel_action a function pointer to call when the button is released + * @return pointer to the created button object + */ +lv_obj_t * lv_win_add_btn(lv_obj_t * win, const void * img_src, const char * label_src, lv_action_t rel_action); + +/*===================== + * Setter functions + *====================*/ + +/** + * A release action which can be assigned to a window control button to close it + * @param btn pointer to the released button + * @return always LV_ACTION_RES_INV because the button is deleted with the window + */ +lv_res_t lv_win_close_action(lv_obj_t * btn); + +/** + * Set the title of a window + * @param win pointer to a window object + * @param title string of the new title + */ +void lv_win_set_title(lv_obj_t * win, const char * title); + +/** + * Set the control button size of a window + * @param win pointer to a window object + * @return control button size + */ +void lv_win_set_btn_size(lv_obj_t * win, lv_coord_t size); + +/** + * Set the layout of the window + * @param win pointer to a window object + * @param layout the layout from 'lv_layout_t' + */ +void lv_win_set_layout(lv_obj_t *win, lv_layout_t layout); + +/** + * Set the scroll bar mode of a window + * @param win pointer to a window object + * @param sb_mode the new scroll bar mode from 'lv_sb_mode_t' + */ +void lv_win_set_sb_mode(lv_obj_t *win, lv_sb_mode_t sb_mode); + +/** + * Set a style of a window + * @param win pointer to a window object + * @param type which style should be set + * @param style pointer to a style + */ +void lv_win_set_style(lv_obj_t *win, lv_win_style_t type, lv_style_t *style); + +/** + * Set drag status of a window. If set to 'true' window can be dragged like on a PC. + * @param win pointer to a window object + * @param en whether dragging is enabled + */ +void lv_win_set_drag(lv_obj_t *win, bool en); + +/*===================== + * Getter functions + *====================*/ + +/** + * Get the title of a window + * @param win pointer to a window object + * @return title string of the window + */ +const char * lv_win_get_title(const lv_obj_t * win); + +/** +* Get the content holder object of window (`lv_page`) to allow additional customization +* @param win pointer to a window object +* @return the Page object where the window's content is +*/ +lv_obj_t * lv_win_get_content(const lv_obj_t * win); + +/** + * Get the control button size of a window + * @param win pointer to a window object + * @return control button size + */ +lv_coord_t lv_win_get_btn_size(const lv_obj_t * win); + +/** + * Get the pointer of a widow from one of its control button. + * It is useful in the action of the control buttons where only button is known. + * @param ctrl_btn pointer to a control button of a window + * @return pointer to the window of 'ctrl_btn' + */ +lv_obj_t * lv_win_get_from_btn(const lv_obj_t * ctrl_btn); + +/** + * Get the layout of a window + * @param win pointer to a window object + * @return the layout of the window (from 'lv_layout_t') + */ +lv_layout_t lv_win_get_layout(lv_obj_t *win); + +/** + * Get the scroll bar mode of a window + * @param win pointer to a window object + * @return the scroll bar mode of the window (from 'lv_sb_mode_t') + */ +lv_sb_mode_t lv_win_get_sb_mode(lv_obj_t *win); + +/** + * Get width of the content area (page scrollable) of the window + * @param win pointer to a window object + * @return the width of the content area + */ +lv_coord_t lv_win_get_width(lv_obj_t * win); + +/** + * Get a style of a window + * @param win pointer to a button object + * @param type which style window be get + * @return style pointer to a style + */ +lv_style_t * lv_win_get_style(const lv_obj_t *win, lv_win_style_t type); + +/** + * Get drag status of a window. If set to 'true' window can be dragged like on a PC. + * @param win pointer to a window object + * @return whether window is draggable + */ +static inline bool lv_win_get_drag(const lv_obj_t *win) +{ + return lv_obj_get_drag(win); +} + +/*===================== + * Other functions + *====================*/ + +/** + * Focus on an object. It ensures that the object will be visible in the window. + * @param win pointer to a window object + * @param obj pointer to an object to focus (must be in the window) + * @param anim_time scroll animation time in milliseconds (0: no animation) + */ +void lv_win_focus(lv_obj_t * win, lv_obj_t * obj, uint16_t anim_time); + +/** + * Scroll the window horizontally + * @param win pointer to a window object + * @param dist the distance to scroll (< 0: scroll right; > 0 scroll left) + */ +static inline void lv_win_scroll_hor(lv_obj_t * win, lv_coord_t dist) +{ + lv_win_ext_t * ext = (lv_win_ext_t *)lv_obj_get_ext_attr(win); + lv_page_scroll_hor(ext->page, dist); +} +/** + * Scroll the window vertically + * @param win pointer to a window object + * @param dist the distance to scroll (< 0: scroll down; > 0 scroll up) + */ +static inline void lv_win_scroll_ver(lv_obj_t * win, lv_coord_t dist) +{ + lv_win_ext_t * ext = (lv_win_ext_t *)lv_obj_get_ext_attr(win); + lv_page_scroll_ver(ext->page, dist); +} + +/********************** + * MACROS + **********************/ + +#endif /*USE_LV_WIN*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /*LV_WIN_H*/