acid-drop

- Hacking the planet from a LilyGo T-Deck using custom firmware
git clone git://git.acid.vegas/acid-drop.git
Log | Files | Refs | Archive | README | LICENSE

lv_obj.c (31366B)

      1 /**
      2  * @file lv_obj.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 #include "lv_obj.h"
     10 #include "lv_indev.h"
     11 #include "lv_refr.h"
     12 #include "lv_group.h"
     13 #include "lv_disp.h"
     14 #include "lv_theme.h"
     15 #include "../misc/lv_assert.h"
     16 #include "../draw/lv_draw.h"
     17 #include "../misc/lv_anim.h"
     18 #include "../misc/lv_timer.h"
     19 #include "../misc/lv_async.h"
     20 #include "../misc/lv_fs.h"
     21 #include "../misc/lv_gc.h"
     22 #include "../misc/lv_math.h"
     23 #include "../misc/lv_log.h"
     24 #include "../hal/lv_hal.h"
     25 #include "../extra/lv_extra.h"
     26 #include <stdint.h>
     27 #include <string.h>
     28 
     29 #if LV_USE_GPU_STM32_DMA2D
     30     #include "../draw/stm32_dma2d/lv_gpu_stm32_dma2d.h"
     31 #endif
     32 
     33 #if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
     34     #include "../gpu/lv_gpu_nxp_pxp.h"
     35     #include "../gpu/lv_gpu_nxp_pxp_osa.h"
     36 #endif
     37 
     38 /*********************
     39  *      DEFINES
     40  *********************/
     41 #define MY_CLASS &lv_obj_class
     42 #define LV_OBJ_DEF_WIDTH    (LV_DPX(100))
     43 #define LV_OBJ_DEF_HEIGHT   (LV_DPX(50))
     44 #define STYLE_TRANSITION_MAX 32
     45 
     46 /**********************
     47  *      TYPEDEFS
     48  **********************/
     49 
     50 /**********************
     51  *  STATIC PROTOTYPES
     52  **********************/
     53 static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     54 static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     55 static void lv_obj_draw(lv_event_t * e);
     56 static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e);
     57 static void draw_scrollbar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx);
     58 static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc);
     59 static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find);
     60 static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state);
     61 
     62 /**********************
     63  *  STATIC VARIABLES
     64  **********************/
     65 static bool lv_initialized = false;
     66 const lv_obj_class_t lv_obj_class = {
     67     .constructor_cb = lv_obj_constructor,
     68     .destructor_cb = lv_obj_destructor,
     69     .event_cb = lv_obj_event,
     70     .width_def = LV_DPI_DEF,
     71     .height_def = LV_DPI_DEF,
     72     .editable = LV_OBJ_CLASS_EDITABLE_FALSE,
     73     .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE,
     74     .instance_size = (sizeof(lv_obj_t)),
     75     .base_class = NULL,
     76 };
     77 
     78 /**********************
     79  *      MACROS
     80  **********************/
     81 
     82 /**********************
     83  *   GLOBAL FUNCTIONS
     84  **********************/
     85 
     86 bool lv_is_initialized(void)
     87 {
     88     return lv_initialized;
     89 }
     90 
     91 void lv_init(void)
     92 {
     93     /*Do nothing if already initialized*/
     94     if(lv_initialized) {
     95         LV_LOG_WARN("lv_init: already inited");
     96         return;
     97     }
     98 
     99     LV_LOG_INFO("begin");
    100 
    101     /*Initialize the misc modules*/
    102     lv_mem_init();
    103 
    104     _lv_timer_core_init();
    105 
    106     _lv_fs_init();
    107 
    108     _lv_anim_core_init();
    109 
    110     _lv_group_init();
    111 
    112     lv_draw_init();
    113 
    114 #if LV_USE_GPU_STM32_DMA2D
    115     /*Initialize DMA2D GPU*/
    116     lv_draw_stm32_dma2d_init();
    117 #endif
    118 
    119 #if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT
    120     if(lv_gpu_nxp_pxp_init(&pxp_default_cfg) != LV_RES_OK) {
    121         LV_LOG_ERROR("PXP init error. STOP.\n");
    122         for(; ;) ;
    123     }
    124 #endif
    125 
    126     _lv_obj_style_init();
    127     _lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t));
    128     _lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t));
    129 
    130     /*Initialize the screen refresh system*/
    131     _lv_refr_init();
    132 
    133     _lv_img_decoder_init();
    134 #if LV_IMG_CACHE_DEF_SIZE
    135     lv_img_cache_set_size(LV_IMG_CACHE_DEF_SIZE);
    136 #endif
    137     /*Test if the IDE has UTF-8 encoding*/
    138     char * txt = "Á";
    139 
    140     uint8_t * txt_u8 = (uint8_t *)txt;
    141     if(txt_u8[0] != 0xc3 || txt_u8[1] != 0x81 || txt_u8[2] != 0x00) {
    142         LV_LOG_WARN("The strings have no UTF-8 encoding. Non-ASCII characters won't be displayed.");
    143     }
    144 
    145     uint32_t endianess_test = 0x11223344;
    146     uint8_t * endianess_test_p = (uint8_t *) &endianess_test;
    147     bool big_endian = endianess_test_p[0] == 0x11 ? true : false;
    148 
    149     if(big_endian) {
    150         LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 1,
    151                       "It's a big endian system but LV_BIG_ENDIAN_SYSTEM is not enabled in lv_conf.h");
    152     }
    153     else {
    154         LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 0,
    155                       "It's a little endian system but LV_BIG_ENDIAN_SYSTEM is enabled in lv_conf.h");
    156     }
    157 
    158 #if LV_USE_ASSERT_MEM_INTEGRITY
    159     LV_LOG_WARN("Memory integrity checks are enabled via LV_USE_ASSERT_MEM_INTEGRITY which makes LVGL much slower");
    160 #endif
    161 
    162 #if LV_USE_ASSERT_OBJ
    163     LV_LOG_WARN("Object sanity checks are enabled via LV_USE_ASSERT_OBJ which makes LVGL much slower");
    164 #endif
    165 
    166 #if LV_USE_ASSERT_STYLE
    167     LV_LOG_WARN("Style sanity checks are enabled that uses more RAM");
    168 #endif
    169 
    170 #if LV_LOG_LEVEL == LV_LOG_LEVEL_TRACE
    171     LV_LOG_WARN("Log level is set to 'Trace' which makes LVGL much slower");
    172 #endif
    173 
    174     lv_extra_init();
    175 
    176     lv_initialized = true;
    177 
    178     LV_LOG_TRACE("finished");
    179 }
    180 
    181 #if LV_ENABLE_GC || !LV_MEM_CUSTOM
    182 
    183 void lv_deinit(void)
    184 {
    185     _lv_gc_clear_roots();
    186 
    187     lv_disp_set_default(NULL);
    188     lv_mem_deinit();
    189     lv_initialized = false;
    190 
    191     LV_LOG_INFO("lv_deinit done");
    192 
    193 #if LV_USE_LOG
    194     lv_log_register_print_cb(NULL);
    195 #endif
    196 }
    197 #endif
    198 
    199 lv_obj_t * lv_obj_create(lv_obj_t * parent)
    200 {
    201     LV_LOG_INFO("begin");
    202     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
    203     lv_obj_class_init_obj(obj);
    204     return obj;
    205 }
    206 
    207 /*=====================
    208  * Setter functions
    209  *====================*/
    210 
    211 /*-----------------
    212  * Attribute set
    213  *----------------*/
    214 
    215 void lv_obj_add_flag(lv_obj_t * obj, lv_obj_flag_t f)
    216 {
    217     LV_ASSERT_OBJ(obj, MY_CLASS);
    218 
    219     bool was_on_layout = lv_obj_is_layout_positioned(obj);
    220 
    221     if(f & LV_OBJ_FLAG_HIDDEN) lv_obj_invalidate(obj);
    222 
    223     obj->flags |= f;
    224 
    225     if(f & LV_OBJ_FLAG_HIDDEN) {
    226         lv_obj_invalidate(obj);
    227     }
    228 
    229     if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 |  LV_OBJ_FLAG_LAYOUT_2))) {
    230         lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
    231         lv_obj_mark_layout_as_dirty(obj);
    232     }
    233 
    234     if(f & LV_OBJ_FLAG_SCROLLABLE) {
    235         lv_area_t hor_area, ver_area;
    236         lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
    237         lv_obj_invalidate_area(obj, &hor_area);
    238         lv_obj_invalidate_area(obj, &ver_area);
    239     }
    240 }
    241 
    242 void lv_obj_clear_flag(lv_obj_t * obj, lv_obj_flag_t f)
    243 {
    244     LV_ASSERT_OBJ(obj, MY_CLASS);
    245 
    246     bool was_on_layout = lv_obj_is_layout_positioned(obj);
    247     if(f & LV_OBJ_FLAG_SCROLLABLE) {
    248         lv_area_t hor_area, ver_area;
    249         lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
    250         lv_obj_invalidate_area(obj, &hor_area);
    251         lv_obj_invalidate_area(obj, &ver_area);
    252     }
    253 
    254     obj->flags &= (~f);
    255 
    256     if(f & LV_OBJ_FLAG_HIDDEN) {
    257         lv_obj_invalidate(obj);
    258         if(lv_obj_is_layout_positioned(obj)) {
    259             lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
    260             lv_obj_mark_layout_as_dirty(obj);
    261         }
    262     }
    263 
    264     if((was_on_layout != lv_obj_is_layout_positioned(obj)) || (f & (LV_OBJ_FLAG_LAYOUT_1 |  LV_OBJ_FLAG_LAYOUT_2))) {
    265         lv_obj_mark_layout_as_dirty(lv_obj_get_parent(obj));
    266     }
    267 }
    268 
    269 void lv_obj_add_state(lv_obj_t * obj, lv_state_t state)
    270 {
    271     LV_ASSERT_OBJ(obj, MY_CLASS);
    272 
    273     lv_state_t new_state = obj->state | state;
    274     if(obj->state != new_state) {
    275         lv_obj_set_state(obj, new_state);
    276     }
    277 }
    278 
    279 void lv_obj_clear_state(lv_obj_t * obj, lv_state_t state)
    280 {
    281     LV_ASSERT_OBJ(obj, MY_CLASS);
    282 
    283     lv_state_t new_state = obj->state & (~state);
    284     if(obj->state != new_state) {
    285         lv_obj_set_state(obj, new_state);
    286     }
    287 }
    288 
    289 /*=======================
    290  * Getter functions
    291  *======================*/
    292 
    293 bool lv_obj_has_flag(const lv_obj_t * obj, lv_obj_flag_t f)
    294 {
    295     LV_ASSERT_OBJ(obj, MY_CLASS);
    296 
    297     return (obj->flags & f)  == f ? true : false;
    298 }
    299 
    300 bool lv_obj_has_flag_any(const lv_obj_t * obj, lv_obj_flag_t f)
    301 {
    302     LV_ASSERT_OBJ(obj, MY_CLASS);
    303 
    304     return (obj->flags & f) ? true : false;
    305 }
    306 
    307 lv_state_t lv_obj_get_state(const lv_obj_t * obj)
    308 {
    309     LV_ASSERT_OBJ(obj, MY_CLASS);
    310 
    311     return obj->state;
    312 }
    313 
    314 bool lv_obj_has_state(const lv_obj_t * obj, lv_state_t state)
    315 {
    316     LV_ASSERT_OBJ(obj, MY_CLASS);
    317 
    318     return obj->state & state ? true : false;
    319 }
    320 
    321 void * lv_obj_get_group(const lv_obj_t * obj)
    322 {
    323     LV_ASSERT_OBJ(obj, MY_CLASS);
    324 
    325     if(obj->spec_attr) return obj->spec_attr->group_p;
    326     else return NULL;
    327 }
    328 
    329 /*-------------------
    330  * OTHER FUNCTIONS
    331  *------------------*/
    332 
    333 void lv_obj_allocate_spec_attr(lv_obj_t * obj)
    334 {
    335     LV_ASSERT_OBJ(obj, MY_CLASS);
    336 
    337     if(obj->spec_attr == NULL) {
    338         static uint32_t x = 0;
    339         x++;
    340         obj->spec_attr = lv_mem_alloc(sizeof(_lv_obj_spec_attr_t));
    341         LV_ASSERT_MALLOC(obj->spec_attr);
    342         if(obj->spec_attr == NULL) return;
    343 
    344         lv_memset_00(obj->spec_attr, sizeof(_lv_obj_spec_attr_t));
    345 
    346         obj->spec_attr->scroll_dir = LV_DIR_ALL;
    347         obj->spec_attr->scrollbar_mode = LV_SCROLLBAR_MODE_AUTO;
    348     }
    349 }
    350 
    351 bool lv_obj_check_type(const lv_obj_t * obj, const lv_obj_class_t * class_p)
    352 {
    353     if(obj == NULL) return false;
    354     return obj->class_p == class_p ? true : false;
    355 }
    356 
    357 bool lv_obj_has_class(const lv_obj_t * obj, const lv_obj_class_t * class_p)
    358 {
    359     const lv_obj_class_t * obj_class = obj->class_p;
    360     while(obj_class) {
    361         if(obj_class == class_p) return true;
    362         obj_class = obj_class->base_class;
    363     }
    364 
    365     return false;
    366 }
    367 
    368 const lv_obj_class_t * lv_obj_get_class(const lv_obj_t * obj)
    369 {
    370     return obj->class_p;
    371 }
    372 
    373 bool lv_obj_is_valid(const lv_obj_t * obj)
    374 {
    375     lv_disp_t * disp = lv_disp_get_next(NULL);
    376     while(disp) {
    377         uint32_t i;
    378         for(i = 0; i < disp->screen_cnt; i++) {
    379             if(disp->screens[i] == obj) return true;
    380             bool found = obj_valid_child(disp->screens[i], obj);
    381             if(found) return true;
    382         }
    383 
    384         disp = lv_disp_get_next(disp);
    385     }
    386 
    387     return false;
    388 }
    389 
    390 /**********************
    391  *   STATIC FUNCTIONS
    392  **********************/
    393 
    394 static void lv_obj_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
    395 {
    396     LV_UNUSED(class_p);
    397     LV_TRACE_OBJ_CREATE("begin");
    398 
    399     lv_obj_t * parent = obj->parent;
    400     if(parent) {
    401         lv_coord_t sl = lv_obj_get_scroll_left(parent);
    402         lv_coord_t st = lv_obj_get_scroll_top(parent);
    403 
    404         obj->coords.y1 = parent->coords.y1 + lv_obj_get_style_pad_top(parent, LV_PART_MAIN) - st;
    405         obj->coords.y2 = obj->coords.y1 - 1;
    406         obj->coords.x1  = parent->coords.x1 + lv_obj_get_style_pad_left(parent, LV_PART_MAIN) - sl;
    407         obj->coords.x2  = obj->coords.x1 - 1;
    408     }
    409 
    410     /*Set attributes*/
    411     obj->flags = LV_OBJ_FLAG_CLICKABLE;
    412     obj->flags |= LV_OBJ_FLAG_SNAPPABLE;
    413     if(parent) obj->flags |= LV_OBJ_FLAG_PRESS_LOCK;
    414     if(parent) obj->flags |= LV_OBJ_FLAG_SCROLL_CHAIN;
    415     obj->flags |= LV_OBJ_FLAG_CLICK_FOCUSABLE;
    416     obj->flags |= LV_OBJ_FLAG_SCROLLABLE;
    417     obj->flags |= LV_OBJ_FLAG_SCROLL_ELASTIC;
    418     obj->flags |= LV_OBJ_FLAG_SCROLL_MOMENTUM;
    419     obj->flags |= LV_OBJ_FLAG_SCROLL_WITH_ARROW;
    420     if(parent) obj->flags |= LV_OBJ_FLAG_GESTURE_BUBBLE;
    421 
    422     LV_TRACE_OBJ_CREATE("finished");
    423 }
    424 
    425 static void lv_obj_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
    426 {
    427     LV_UNUSED(class_p);
    428 
    429     _lv_event_mark_deleted(obj);
    430 
    431     /*Remove all style*/
    432     lv_obj_enable_style_refresh(false); /*No need to refresh the style because the object will be deleted*/
    433     lv_obj_remove_style_all(obj);
    434     lv_obj_enable_style_refresh(true);
    435 
    436     /*Remove the animations from this object*/
    437     lv_anim_del(obj, NULL);
    438 
    439     /*Delete from the group*/
    440     lv_group_t * group = lv_obj_get_group(obj);
    441     if(group) lv_group_remove_obj(obj);
    442 
    443     if(obj->spec_attr) {
    444         if(obj->spec_attr->children) {
    445             lv_mem_free(obj->spec_attr->children);
    446             obj->spec_attr->children = NULL;
    447         }
    448         if(obj->spec_attr->event_dsc) {
    449             lv_mem_free(obj->spec_attr->event_dsc);
    450             obj->spec_attr->event_dsc = NULL;
    451         }
    452 
    453         lv_mem_free(obj->spec_attr);
    454         obj->spec_attr = NULL;
    455     }
    456 }
    457 
    458 static void lv_obj_draw(lv_event_t * e)
    459 {
    460     lv_event_code_t code = lv_event_get_code(e);
    461     lv_obj_t * obj = lv_event_get_target(e);
    462     if(code == LV_EVENT_COVER_CHECK) {
    463         lv_cover_check_info_t * info = lv_event_get_param(e);
    464         if(info->res == LV_COVER_RES_MASKED) return;
    465         if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
    466             info->res = LV_COVER_RES_MASKED;
    467             return;
    468         }
    469 
    470         /*Most trivial test. Is the mask fully IN the object? If no it surely doesn't cover it*/
    471         lv_coord_t r = lv_obj_get_style_radius(obj, LV_PART_MAIN);
    472         lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
    473         lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
    474         lv_area_t coords;
    475         lv_area_copy(&coords, &obj->coords);
    476         coords.x1 -= w;
    477         coords.x2 += w;
    478         coords.y1 -= h;
    479         coords.y2 += h;
    480 
    481         if(_lv_area_is_in(info->area, &coords, r) == false) {
    482             info->res = LV_COVER_RES_NOT_COVER;
    483             return;
    484         }
    485 
    486         if(lv_obj_get_style_bg_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
    487             info->res = LV_COVER_RES_NOT_COVER;
    488             return;
    489         }
    490 
    491 #if LV_DRAW_COMPLEX
    492         if(lv_obj_get_style_blend_mode(obj, LV_PART_MAIN) != LV_BLEND_MODE_NORMAL) {
    493             info->res = LV_COVER_RES_NOT_COVER;
    494             return;
    495         }
    496 #endif
    497         if(lv_obj_get_style_opa(obj, LV_PART_MAIN) < LV_OPA_MAX) {
    498             info->res = LV_COVER_RES_NOT_COVER;
    499             return;
    500         }
    501 
    502         info->res = LV_COVER_RES_COVER;
    503 
    504     }
    505     else if(code == LV_EVENT_DRAW_MAIN) {
    506         lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
    507         lv_draw_rect_dsc_t draw_dsc;
    508         lv_draw_rect_dsc_init(&draw_dsc);
    509         /*If the border is drawn later disable loading its properties*/
    510         if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
    511             draw_dsc.border_post = 1;
    512         }
    513 
    514         lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
    515         lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
    516         lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
    517         lv_area_t coords;
    518         lv_area_copy(&coords, &obj->coords);
    519         coords.x1 -= w;
    520         coords.x2 += w;
    521         coords.y1 -= h;
    522         coords.y2 += h;
    523 
    524         lv_obj_draw_part_dsc_t part_dsc;
    525         lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
    526         part_dsc.class_p = MY_CLASS;
    527         part_dsc.type = LV_OBJ_DRAW_PART_RECTANGLE;
    528         part_dsc.rect_dsc = &draw_dsc;
    529         part_dsc.draw_area = &coords;
    530         part_dsc.part = LV_PART_MAIN;
    531         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
    532 
    533 
    534 #if LV_DRAW_COMPLEX
    535         /*With clip corner enabled draw the bg img separately to make it clipped*/
    536         bool clip_corner = (lv_obj_get_style_clip_corner(obj, LV_PART_MAIN) && draw_dsc.radius != 0) ? true : false;
    537         const void * bg_img_src = draw_dsc.bg_img_src;
    538         if(clip_corner) {
    539             draw_dsc.bg_img_src = NULL;
    540         }
    541 #endif
    542 
    543         lv_draw_rect(draw_ctx, &draw_dsc, &coords);
    544 
    545 
    546 #if LV_DRAW_COMPLEX
    547         if(clip_corner) {
    548             lv_draw_mask_radius_param_t * mp = lv_mem_buf_get(sizeof(lv_draw_mask_radius_param_t));
    549             lv_draw_mask_radius_init(mp, &obj->coords, draw_dsc.radius, false);
    550             /*Add the mask and use `obj+8` as custom id. Don't use `obj` directly because it might be used by the user*/
    551             lv_draw_mask_add(mp, obj + 8);
    552 
    553             if(bg_img_src) {
    554                 draw_dsc.bg_opa = LV_OPA_TRANSP;
    555                 draw_dsc.border_opa = LV_OPA_TRANSP;
    556                 draw_dsc.outline_opa = LV_OPA_TRANSP;
    557                 draw_dsc.shadow_opa = LV_OPA_TRANSP;
    558                 draw_dsc.bg_img_src = bg_img_src;
    559                 lv_draw_rect(draw_ctx, &draw_dsc, &coords);
    560             }
    561 
    562         }
    563 #endif
    564         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
    565     }
    566     else if(code == LV_EVENT_DRAW_POST) {
    567         lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
    568         draw_scrollbar(obj, draw_ctx);
    569 
    570 #if LV_DRAW_COMPLEX
    571         if(lv_obj_get_style_clip_corner(obj, LV_PART_MAIN)) {
    572             lv_draw_mask_radius_param_t * param = lv_draw_mask_remove_custom(obj + 8);
    573             if(param) {
    574                 lv_draw_mask_free_param(param);
    575                 lv_mem_buf_release(param);
    576             }
    577         }
    578 #endif
    579 
    580         /*If the border is drawn later disable loading other properties*/
    581         if(lv_obj_get_style_border_post(obj, LV_PART_MAIN)) {
    582             lv_draw_rect_dsc_t draw_dsc;
    583             lv_draw_rect_dsc_init(&draw_dsc);
    584             draw_dsc.bg_opa = LV_OPA_TRANSP;
    585             draw_dsc.bg_img_opa = LV_OPA_TRANSP;
    586             draw_dsc.outline_opa = LV_OPA_TRANSP;
    587             draw_dsc.shadow_opa = LV_OPA_TRANSP;
    588             lv_obj_init_draw_rect_dsc(obj, LV_PART_MAIN, &draw_dsc);
    589 
    590             lv_coord_t w = lv_obj_get_style_transform_width(obj, LV_PART_MAIN);
    591             lv_coord_t h = lv_obj_get_style_transform_height(obj, LV_PART_MAIN);
    592             lv_area_t coords;
    593             lv_area_copy(&coords, &obj->coords);
    594             coords.x1 -= w;
    595             coords.x2 += w;
    596             coords.y1 -= h;
    597             coords.y2 += h;
    598 
    599             lv_obj_draw_part_dsc_t part_dsc;
    600             lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
    601             part_dsc.class_p = MY_CLASS;
    602             part_dsc.type = LV_OBJ_DRAW_PART_BORDER_POST;
    603             part_dsc.rect_dsc = &draw_dsc;
    604             part_dsc.draw_area = &coords;
    605             part_dsc.part = LV_PART_MAIN;
    606             lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
    607 
    608             lv_draw_rect(draw_ctx, &draw_dsc, &coords);
    609             lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
    610         }
    611     }
    612 }
    613 
    614 static void draw_scrollbar(lv_obj_t * obj, lv_draw_ctx_t * draw_ctx)
    615 {
    616 
    617     lv_area_t hor_area;
    618     lv_area_t ver_area;
    619     lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
    620 
    621     if(lv_area_get_size(&hor_area) <= 0 && lv_area_get_size(&ver_area) <= 0) return;
    622 
    623     lv_draw_rect_dsc_t draw_dsc;
    624     lv_res_t sb_res = scrollbar_init_draw_dsc(obj, &draw_dsc);
    625     if(sb_res != LV_RES_OK) return;
    626 
    627     lv_obj_draw_part_dsc_t part_dsc;
    628     lv_obj_draw_dsc_init(&part_dsc, draw_ctx);
    629     part_dsc.class_p = MY_CLASS;
    630     part_dsc.type = LV_OBJ_DRAW_PART_SCROLLBAR;
    631     part_dsc.rect_dsc = &draw_dsc;
    632     part_dsc.part = LV_PART_SCROLLBAR;
    633 
    634     if(lv_area_get_size(&hor_area) > 0) {
    635         part_dsc.draw_area = &hor_area;
    636         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
    637         lv_draw_rect(draw_ctx, &draw_dsc, &hor_area);
    638         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
    639     }
    640     if(lv_area_get_size(&ver_area) > 0) {
    641         part_dsc.draw_area = &ver_area;
    642         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_dsc);
    643         part_dsc.draw_area = &ver_area;
    644         lv_draw_rect(draw_ctx, &draw_dsc, &ver_area);
    645         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_dsc);
    646     }
    647 }
    648 
    649 /**
    650  * Initialize the draw descriptor for the scrollbar
    651  * @param obj pointer to an object
    652  * @param dsc the draw descriptor to initialize
    653  * @return LV_RES_OK: the scrollbar is visible; LV_RES_INV: the scrollbar is not visible
    654  */
    655 static lv_res_t scrollbar_init_draw_dsc(lv_obj_t * obj, lv_draw_rect_dsc_t * dsc)
    656 {
    657     lv_draw_rect_dsc_init(dsc);
    658     dsc->bg_opa = lv_obj_get_style_bg_opa(obj, LV_PART_SCROLLBAR);
    659     if(dsc->bg_opa > LV_OPA_MIN) {
    660         dsc->bg_color = lv_obj_get_style_bg_color(obj, LV_PART_SCROLLBAR);
    661     }
    662 
    663     dsc->border_opa = lv_obj_get_style_border_opa(obj, LV_PART_SCROLLBAR);
    664     if(dsc->border_opa > LV_OPA_MIN) {
    665         dsc->border_width = lv_obj_get_style_border_width(obj, LV_PART_SCROLLBAR);
    666         if(dsc->border_width > 0) {
    667             dsc->border_color = lv_obj_get_style_border_color(obj, LV_PART_SCROLLBAR);
    668         }
    669         else {
    670             dsc->border_opa = LV_OPA_TRANSP;
    671         }
    672     }
    673 
    674 #if LV_DRAW_COMPLEX
    675     dsc->shadow_opa = lv_obj_get_style_shadow_opa(obj, LV_PART_SCROLLBAR);
    676     if(dsc->shadow_opa > LV_OPA_MIN) {
    677         dsc->shadow_width = lv_obj_get_style_shadow_width(obj, LV_PART_SCROLLBAR);
    678         if(dsc->shadow_width > 0) {
    679             dsc->shadow_spread = lv_obj_get_style_shadow_spread(obj, LV_PART_SCROLLBAR);
    680             dsc->shadow_color = lv_obj_get_style_shadow_color(obj, LV_PART_SCROLLBAR);
    681         }
    682         else {
    683             dsc->shadow_opa = LV_OPA_TRANSP;
    684         }
    685     }
    686 
    687     lv_opa_t opa = lv_obj_get_style_opa(obj, LV_PART_SCROLLBAR);
    688     if(opa < LV_OPA_MAX) {
    689         dsc->bg_opa = (dsc->bg_opa * opa) >> 8;
    690         dsc->border_opa = (dsc->bg_opa * opa) >> 8;
    691         dsc->shadow_opa = (dsc->bg_opa * opa) >> 8;
    692     }
    693 
    694     if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP || dsc->shadow_opa != LV_OPA_TRANSP) {
    695         dsc->radius = lv_obj_get_style_radius(obj, LV_PART_SCROLLBAR);
    696         return LV_RES_OK;
    697     }
    698     else {
    699         return LV_RES_INV;
    700     }
    701 #else
    702     if(dsc->bg_opa != LV_OPA_TRANSP || dsc->border_opa != LV_OPA_TRANSP) return LV_RES_OK;
    703     else return LV_RES_INV;
    704 #endif
    705 }
    706 
    707 static void lv_obj_event(const lv_obj_class_t * class_p, lv_event_t * e)
    708 {
    709     LV_UNUSED(class_p);
    710 
    711     lv_event_code_t code = lv_event_get_code(e);
    712     lv_obj_t * obj = lv_event_get_current_target(e);
    713     if(code == LV_EVENT_PRESSED) {
    714         lv_obj_add_state(obj, LV_STATE_PRESSED);
    715     }
    716     else if(code == LV_EVENT_RELEASED) {
    717         lv_obj_clear_state(obj, LV_STATE_PRESSED);
    718         void * param = lv_event_get_param(e);
    719         /*Go the checked state if enabled*/
    720         if(lv_indev_get_scroll_obj(param) == NULL && lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
    721             if(!(lv_obj_get_state(obj) & LV_STATE_CHECKED)) lv_obj_add_state(obj, LV_STATE_CHECKED);
    722             else lv_obj_clear_state(obj, LV_STATE_CHECKED);
    723 
    724             lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
    725             if(res != LV_RES_OK) return;
    726         }
    727     }
    728     else if(code == LV_EVENT_PRESS_LOST) {
    729         lv_obj_clear_state(obj, LV_STATE_PRESSED);
    730     }
    731     else if(code == LV_EVENT_STYLE_CHANGED) {
    732         uint32_t child_cnt = lv_obj_get_child_cnt(obj);
    733         for(uint32_t i = 0; i < child_cnt; i++) {
    734             lv_obj_t * child = obj->spec_attr->children[i];
    735             lv_obj_mark_layout_as_dirty(child);
    736         }
    737     }
    738     else if(code == LV_EVENT_KEY) {
    739         if(lv_obj_has_flag(obj, LV_OBJ_FLAG_CHECKABLE)) {
    740             char c = *((char *)lv_event_get_param(e));
    741             if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
    742                 lv_obj_add_state(obj, LV_STATE_CHECKED);
    743             }
    744             else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
    745                 lv_obj_clear_state(obj, LV_STATE_CHECKED);
    746             }
    747 
    748             /*With Enter LV_EVENT_RELEASED will send VALUE_CHANGE event*/
    749             if(c != LV_KEY_ENTER) {
    750                 lv_res_t res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
    751                 if(res != LV_RES_OK) return;
    752             }
    753         }
    754         else if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLLABLE | LV_OBJ_FLAG_SCROLL_WITH_ARROW) && !lv_obj_is_editable(obj)) {
    755             /*scroll by keypad or encoder*/
    756             lv_anim_enable_t anim_enable = LV_ANIM_OFF;
    757             lv_coord_t sl = lv_obj_get_scroll_left(obj);
    758             lv_coord_t sr = lv_obj_get_scroll_right(obj);
    759             char c = *((char *)lv_event_get_param(e));
    760             if(c == LV_KEY_DOWN) {
    761                 /*use scroll_to_x/y functions to enforce scroll limits*/
    762                 lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
    763             }
    764             else if(c == LV_KEY_UP) {
    765                 lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
    766             }
    767             else if(c == LV_KEY_RIGHT) {
    768                 /*If the object can't be scrolled horizontally then scroll it vertically*/
    769                 if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
    770                     lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) + lv_obj_get_height(obj) / 4, anim_enable);
    771                 else
    772                     lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) + lv_obj_get_width(obj) / 4, anim_enable);
    773             }
    774             else if(c == LV_KEY_LEFT) {
    775                 /*If the object can't be scrolled horizontally then scroll it vertically*/
    776                 if(!((lv_obj_get_scroll_dir(obj) & LV_DIR_HOR) && (sl > 0 || sr > 0)))
    777                     lv_obj_scroll_to_y(obj, lv_obj_get_scroll_y(obj) - lv_obj_get_height(obj) / 4, anim_enable);
    778                 else
    779                     lv_obj_scroll_to_x(obj, lv_obj_get_scroll_x(obj) - lv_obj_get_width(obj) / 4, anim_enable);
    780             }
    781         }
    782     }
    783     else if(code == LV_EVENT_FOCUSED) {
    784         if(lv_obj_has_flag(obj, LV_OBJ_FLAG_SCROLL_ON_FOCUS)) {
    785             lv_obj_scroll_to_view_recursive(obj, LV_ANIM_ON);
    786         }
    787 
    788         bool editing = false;
    789         editing = lv_group_get_editing(lv_obj_get_group(obj));
    790         lv_state_t state = LV_STATE_FOCUSED;
    791 
    792         /* Use the indev for then indev handler.
    793          * But if the obj was focused manually it returns NULL so try to
    794          * use the indev from the event*/
    795         lv_indev_t * indev = lv_indev_get_act();
    796         if(indev == NULL) indev = lv_event_get_indev(e);
    797 
    798         lv_indev_type_t indev_type = lv_indev_get_type(indev);
    799         if(indev_type == LV_INDEV_TYPE_KEYPAD || indev_type == LV_INDEV_TYPE_ENCODER) state |= LV_STATE_FOCUS_KEY;
    800         if(editing) {
    801             state |= LV_STATE_EDITED;
    802             lv_obj_add_state(obj, state);
    803         }
    804         else {
    805             lv_obj_add_state(obj, state);
    806             lv_obj_clear_state(obj, LV_STATE_EDITED);
    807         }
    808     }
    809     else if(code == LV_EVENT_SCROLL_BEGIN) {
    810         lv_obj_add_state(obj, LV_STATE_SCROLLED);
    811     }
    812     else if(code == LV_EVENT_SCROLL_END) {
    813         lv_obj_clear_state(obj, LV_STATE_SCROLLED);
    814         if(lv_obj_get_scrollbar_mode(obj) == LV_SCROLLBAR_MODE_ACTIVE) {
    815             lv_area_t hor_area, ver_area;
    816             lv_obj_get_scrollbar_area(obj, &hor_area, &ver_area);
    817             lv_obj_invalidate_area(obj, &hor_area);
    818             lv_obj_invalidate_area(obj, &ver_area);
    819         }
    820     }
    821     else if(code == LV_EVENT_DEFOCUSED) {
    822         lv_obj_clear_state(obj, LV_STATE_FOCUSED | LV_STATE_EDITED | LV_STATE_FOCUS_KEY);
    823     }
    824     else if(code == LV_EVENT_SIZE_CHANGED) {
    825         lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
    826         uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
    827         if(layout || align) {
    828             lv_obj_mark_layout_as_dirty(obj);
    829         }
    830 
    831         uint32_t i;
    832         uint32_t child_cnt = lv_obj_get_child_cnt(obj);
    833         for(i = 0; i < child_cnt; i++) {
    834             lv_obj_t * child = obj->spec_attr->children[i];
    835             lv_obj_mark_layout_as_dirty(child);
    836         }
    837     }
    838     else if(code == LV_EVENT_CHILD_CHANGED) {
    839         lv_coord_t w = lv_obj_get_style_width(obj, LV_PART_MAIN);
    840         lv_coord_t h = lv_obj_get_style_height(obj, LV_PART_MAIN);
    841         lv_coord_t align = lv_obj_get_style_align(obj, LV_PART_MAIN);
    842         uint16_t layout = lv_obj_get_style_layout(obj, LV_PART_MAIN);
    843         if(layout || align || w == LV_SIZE_CONTENT || h == LV_SIZE_CONTENT) {
    844             lv_obj_mark_layout_as_dirty(obj);
    845         }
    846     }
    847     else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
    848         lv_coord_t * s = lv_event_get_param(e);
    849         lv_coord_t d = lv_obj_calculate_ext_draw_size(obj, LV_PART_MAIN);
    850         *s = LV_MAX(*s, d);
    851     }
    852     else if(code == LV_EVENT_DRAW_MAIN || code == LV_EVENT_DRAW_POST || code == LV_EVENT_COVER_CHECK) {
    853         lv_obj_draw(e);
    854     }
    855 }
    856 
    857 /**
    858  * Set the state (fully overwrite) of an object.
    859  * If specified in the styles, transition animations will be started from the previous state to the current.
    860  * @param obj       pointer to an object
    861  * @param state     the new state
    862  */
    863 static void lv_obj_set_state(lv_obj_t * obj, lv_state_t new_state)
    864 {
    865     if(obj->state == new_state) return;
    866 
    867     LV_ASSERT_OBJ(obj, MY_CLASS);
    868 
    869     lv_state_t prev_state = obj->state;
    870     obj->state = new_state;
    871 
    872     _lv_style_state_cmp_t cmp_res = _lv_obj_style_state_compare(obj, prev_state, new_state);
    873     /*If there is no difference in styles there is nothing else to do*/
    874     if(cmp_res == _LV_STYLE_STATE_CMP_SAME) return;
    875 
    876     _lv_obj_style_transition_dsc_t * ts = lv_mem_buf_get(sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
    877     lv_memset_00(ts, sizeof(_lv_obj_style_transition_dsc_t) * STYLE_TRANSITION_MAX);
    878     uint32_t tsi = 0;
    879     uint32_t i;
    880     for(i = 0; i < obj->style_cnt && tsi < STYLE_TRANSITION_MAX; i++) {
    881         _lv_obj_style_t * obj_style = &obj->styles[i];
    882         lv_state_t state_act = lv_obj_style_get_selector_state(obj->styles[i].selector);
    883         lv_part_t part_act = lv_obj_style_get_selector_part(obj->styles[i].selector);
    884         if(state_act & (~new_state)) continue; /*Skip unrelated styles*/
    885         if(obj_style->is_trans) continue;
    886 
    887         lv_style_value_t v;
    888         if(lv_style_get_prop_inlined(obj_style->style, LV_STYLE_TRANSITION, &v) == false) continue;
    889         const lv_style_transition_dsc_t * tr = v.ptr;
    890 
    891         /*Add the props to the set if not added yet or added but with smaller weight*/
    892         uint32_t j;
    893         for(j = 0; tr->props[j] != 0 && tsi < STYLE_TRANSITION_MAX; j++) {
    894             uint32_t t;
    895             for(t = 0; t < tsi; t++) {
    896                 lv_style_selector_t selector = ts[t].selector;
    897                 lv_state_t state_ts = lv_obj_style_get_selector_state(selector);
    898                 lv_part_t part_ts = lv_obj_style_get_selector_part(selector);
    899                 if(ts[t].prop == tr->props[j] && part_ts == part_act && state_ts >= state_act) break;
    900             }
    901 
    902             /*If not found  add it*/
    903             if(t == tsi) {
    904                 ts[tsi].time = tr->time;
    905                 ts[tsi].delay = tr->delay;
    906                 ts[tsi].path_cb = tr->path_xcb;
    907                 ts[tsi].prop = tr->props[j];
    908 #if LV_USE_USER_DATA
    909                 ts[tsi].user_data = tr->user_data;
    910 #endif
    911                 ts[tsi].selector = obj_style->selector;
    912                 tsi++;
    913             }
    914         }
    915     }
    916 
    917     for(i = 0; i < tsi; i++) {
    918         lv_part_t part_act = lv_obj_style_get_selector_part(ts[i].selector);
    919         _lv_obj_style_create_transition(obj, part_act, prev_state, new_state, &ts[i]);
    920     }
    921 
    922     lv_mem_buf_release(ts);
    923 
    924     if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_REDRAW) {
    925         lv_obj_invalidate(obj);
    926     }
    927     else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_LAYOUT) {
    928         lv_obj_refresh_style(obj, LV_PART_ANY, LV_STYLE_PROP_ANY);
    929     }
    930     else if(cmp_res == _LV_STYLE_STATE_CMP_DIFF_DRAW_PAD) {
    931         lv_obj_invalidate(obj);
    932         lv_obj_refresh_ext_draw_size(obj);
    933     }
    934 }
    935 
    936 static bool obj_valid_child(const lv_obj_t * parent, const lv_obj_t * obj_to_find)
    937 {
    938     /*Check all children of `parent`*/
    939     uint32_t child_cnt = 0;
    940     if(parent->spec_attr) child_cnt = parent->spec_attr->child_cnt;
    941     uint32_t i;
    942     for(i = 0; i < child_cnt; i++) {
    943         lv_obj_t * child = parent->spec_attr->children[i];
    944         if(child == obj_to_find) {
    945             return true;
    946         }
    947 
    948         /*Check the children*/
    949         bool found = obj_valid_child(child, obj_to_find);
    950         if(found) {
    951             return true;
    952         }
    953     }
    954     return false;
    955 }