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_slider.c (17305B)

      1 /**
      2  * @file lv_slider.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 #include "lv_slider.h"
     10 #if LV_USE_SLIDER != 0
     11 
     12 #include "../misc/lv_assert.h"
     13 #include "../core/lv_group.h"
     14 #include "../core/lv_indev.h"
     15 #include "../draw/lv_draw.h"
     16 #include "../misc/lv_math.h"
     17 #include "../core/lv_disp.h"
     18 #include "lv_img.h"
     19 
     20 /*********************
     21  *      DEFINES
     22  *********************/
     23 #define MY_CLASS &lv_slider_class
     24 
     25 #define LV_SLIDER_KNOB_COORD(is_rtl, area) (is_rtl ? area.x1 : area.x2)
     26 
     27 /**********************
     28  *      TYPEDEFS
     29  **********************/
     30 
     31 /**********************
     32  *  STATIC PROTOTYPES
     33  **********************/
     34 static void lv_slider_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     35 static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e);
     36 static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, const lv_coord_t knob_size, const bool hor);
     37 static void draw_knob(lv_event_t * e);
     38 static bool is_slider_horizontal(lv_obj_t * obj);
     39 
     40 /**********************
     41  *  STATIC VARIABLES
     42  **********************/
     43 const lv_obj_class_t lv_slider_class = {
     44     .constructor_cb = lv_slider_constructor,
     45     .event_cb = lv_slider_event,
     46     .editable = LV_OBJ_CLASS_EDITABLE_TRUE,
     47     .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE,
     48     .instance_size = sizeof(lv_slider_t),
     49     .base_class = &lv_bar_class
     50 };
     51 
     52 /**********************
     53  *      MACROS
     54  **********************/
     55 
     56 /**********************
     57  *   GLOBAL FUNCTIONS
     58  **********************/
     59 
     60 lv_obj_t * lv_slider_create(lv_obj_t * parent)
     61 {
     62     LV_LOG_INFO("begin");
     63     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
     64     lv_obj_class_init_obj(obj);
     65     return obj;
     66 }
     67 
     68 bool lv_slider_is_dragged(const lv_obj_t * obj)
     69 {
     70     LV_ASSERT_OBJ(obj, MY_CLASS);
     71     lv_slider_t * slider = (lv_slider_t *)obj;
     72 
     73     return slider->dragging ? true : false;
     74 }
     75 
     76 /**********************
     77  *   STATIC FUNCTIONS
     78  **********************/
     79 
     80 static void lv_slider_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
     81 {
     82     LV_UNUSED(class_p);
     83     lv_slider_t * slider = (lv_slider_t *)obj;
     84 
     85     /*Initialize the allocated 'slider'*/
     86     slider->value_to_set = NULL;
     87     slider->dragging = 0U;
     88     slider->left_knob_focus = 0U;
     89 
     90     lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLL_CHAIN);
     91     lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
     92     lv_obj_set_ext_click_area(obj, LV_DPX(8));
     93 }
     94 
     95 static void lv_slider_event(const lv_obj_class_t * class_p, lv_event_t * e)
     96 {
     97     LV_UNUSED(class_p);
     98 
     99     lv_res_t res;
    100 
    101     /*Call the ancestor's event handler*/
    102     res = lv_obj_event_base(MY_CLASS, e);
    103     if(res != LV_RES_OK) return;
    104 
    105     lv_event_code_t code = lv_event_get_code(e);
    106     lv_obj_t * obj = lv_event_get_target(e);
    107     lv_slider_t * slider = (lv_slider_t *)obj;
    108     lv_slider_mode_t type = lv_slider_get_mode(obj);
    109 
    110     /*Advanced hit testing: react only on dragging the knob(s)*/
    111     if(code == LV_EVENT_HIT_TEST) {
    112         lv_hit_test_info_t * info = lv_event_get_param(e);
    113         lv_coord_t ext_click_area = obj->spec_attr ? obj->spec_attr->ext_click_pad : 0;
    114 
    115         /*Ordinary slider: was the knob area hit?*/
    116         lv_area_t a;
    117         lv_area_copy(&a, &slider->right_knob_area);
    118         lv_area_increase(&a, ext_click_area, ext_click_area);
    119         info->res = _lv_area_is_point_on(&a, info->point, 0);
    120 
    121         /*There's still a chance that there is a hit if there is another knob*/
    122         if((info->res == false) && (type == LV_SLIDER_MODE_RANGE)) {
    123             lv_area_copy(&a, &slider->left_knob_area);
    124             lv_area_increase(&a, ext_click_area, ext_click_area);
    125             info->res = _lv_area_is_point_on(&a, info->point, 0);
    126         }
    127     }
    128     else if(code == LV_EVENT_PRESSED) {
    129         lv_obj_invalidate(obj);
    130 
    131         lv_point_t p;
    132         slider->dragging = true;
    133         if(type == LV_SLIDER_MODE_NORMAL || type == LV_SLIDER_MODE_SYMMETRICAL) {
    134             slider->value_to_set = &slider->bar.cur_value;
    135         }
    136         else if(type == LV_SLIDER_MODE_RANGE) {
    137             lv_indev_get_point(lv_indev_get_act(), &p);
    138             bool hor = lv_obj_get_width(obj) >= lv_obj_get_height(obj);
    139             lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
    140 
    141             lv_coord_t dist_left, dist_right;
    142             if(hor) {
    143                 if((base_dir != LV_BASE_DIR_RTL && p.x > slider->right_knob_area.x2) || (base_dir == LV_BASE_DIR_RTL &&
    144                                                                                          p.x < slider->right_knob_area.x1)) {
    145                     slider->value_to_set = &slider->bar.cur_value;
    146                 }
    147                 else if((base_dir != LV_BASE_DIR_RTL && p.x < slider->left_knob_area.x1) || (base_dir == LV_BASE_DIR_RTL &&
    148                                                                                              p.x > slider->left_knob_area.x2)) {
    149                     slider->value_to_set = &slider->bar.start_value;
    150                 }
    151                 else {
    152                     /*Calculate the distance from each knob*/
    153                     dist_left = LV_ABS((slider->left_knob_area.x1 + (slider->left_knob_area.x2 - slider->left_knob_area.x1) / 2) - p.x);
    154                     dist_right = LV_ABS((slider->right_knob_area.x1 + (slider->right_knob_area.x2 - slider->right_knob_area.x1) / 2) - p.x);
    155 
    156                     /*Use whichever one is closer*/
    157                     if(dist_right < dist_left) {
    158                         slider->value_to_set = &slider->bar.cur_value;
    159                         slider->left_knob_focus = 0;
    160                     }
    161                     else {
    162                         slider->value_to_set = &slider->bar.start_value;
    163                         slider->left_knob_focus = 1;
    164                     }
    165                 }
    166             }
    167             else {
    168                 if(p.y < slider->right_knob_area.y1) {
    169                     slider->value_to_set = &slider->bar.cur_value;
    170                 }
    171                 else if(p.y > slider->left_knob_area.y2) {
    172                     slider->value_to_set = &slider->bar.start_value;
    173                 }
    174                 else {
    175                     /*Calculate the distance from each knob*/
    176                     dist_left = LV_ABS((slider->left_knob_area.y1 + (slider->left_knob_area.y2 - slider->left_knob_area.y1) / 2) - p.y);
    177                     dist_right = LV_ABS((slider->right_knob_area.y1 + (slider->right_knob_area.y2 - slider->right_knob_area.y1) / 2) - p.y);
    178 
    179                     /*Use whichever one is closer*/
    180                     if(dist_right < dist_left) {
    181                         slider->value_to_set = &slider->bar.cur_value;
    182                         slider->left_knob_focus = 0;
    183                     }
    184                     else {
    185                         slider->value_to_set = &slider->bar.start_value;
    186                         slider->left_knob_focus = 1;
    187                     }
    188                 }
    189             }
    190         }
    191     }
    192     else if(code == LV_EVENT_PRESSING && slider->value_to_set != NULL) {
    193         lv_indev_t * indev = lv_indev_get_act();
    194         if(lv_indev_get_type(indev) != LV_INDEV_TYPE_POINTER) return;
    195 
    196         lv_point_t p;
    197         lv_indev_get_point(indev, &p);
    198         int32_t new_value = 0;
    199 
    200         const int32_t range = slider->bar.max_value - slider->bar.min_value;
    201         if(is_slider_horizontal(obj)) {
    202             const lv_coord_t bg_left = lv_obj_get_style_pad_left(obj, LV_PART_MAIN);
    203             const lv_coord_t bg_right = lv_obj_get_style_pad_right(obj, LV_PART_MAIN);
    204             const lv_coord_t w = lv_obj_get_width(obj);
    205             const lv_coord_t indic_w = w - bg_left - bg_right;
    206 
    207             if(lv_obj_get_style_base_dir(obj, LV_PART_MAIN) == LV_BASE_DIR_RTL) {
    208                 /*Make the point relative to the indicator*/
    209                 new_value = (obj->coords.x2 - bg_right) - p.x;
    210             }
    211             else {
    212                 /*Make the point relative to the indicator*/
    213                 new_value = p.x - (obj->coords.x1 + bg_left);
    214             }
    215             new_value = (new_value * range) / indic_w;
    216             new_value += slider->bar.min_value;
    217         }
    218         else {
    219             const lv_coord_t bg_top = lv_obj_get_style_pad_top(obj, LV_PART_MAIN);
    220             const lv_coord_t bg_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN);
    221             const lv_coord_t h = lv_obj_get_height(obj);
    222             const lv_coord_t indic_h = h - bg_bottom - bg_top;
    223 
    224             /*Make the point relative to the indicator*/
    225             new_value = p.y - (obj->coords.y2 + bg_bottom);
    226             new_value = (-new_value * range) / indic_h;
    227             new_value += slider->bar.min_value;
    228         }
    229 
    230         int32_t real_max_value = slider->bar.max_value;
    231         int32_t real_min_value = slider->bar.min_value;
    232         /*Figure out the min. and max. for this mode*/
    233         if(slider->value_to_set == &slider->bar.start_value) {
    234             real_max_value = slider->bar.cur_value;
    235         }
    236         else {
    237             real_min_value = slider->bar.start_value;
    238         }
    239 
    240         new_value = LV_CLAMP(real_min_value, new_value, real_max_value);
    241 
    242         if(*slider->value_to_set != new_value) {
    243             *slider->value_to_set = new_value;
    244             lv_obj_invalidate(obj);
    245             res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
    246             if(res != LV_RES_OK) return;
    247         }
    248 
    249     }
    250     else if(code == LV_EVENT_RELEASED || code == LV_EVENT_PRESS_LOST) {
    251         slider->dragging = false;
    252         slider->value_to_set = NULL;
    253 
    254         lv_obj_invalidate(obj);
    255 
    256         /*Leave edit mode if released. (No need to wait for LONG_PRESS)*/
    257         lv_group_t * g   = lv_obj_get_group(obj);
    258         bool editing     = lv_group_get_editing(g);
    259         lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
    260         if(indev_type == LV_INDEV_TYPE_ENCODER) {
    261             if(editing) {
    262                 if(lv_slider_get_mode(obj) == LV_SLIDER_MODE_RANGE) {
    263                     if(slider->left_knob_focus == 0) slider->left_knob_focus = 1;
    264                     else {
    265                         slider->left_knob_focus = 0;
    266                         lv_group_set_editing(g, false);
    267                     }
    268                 }
    269                 else {
    270                     lv_group_set_editing(g, false);
    271                 }
    272             }
    273         }
    274 
    275     }
    276     else if(code == LV_EVENT_FOCUSED) {
    277         lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
    278         if(indev_type == LV_INDEV_TYPE_ENCODER || indev_type == LV_INDEV_TYPE_KEYPAD) {
    279             slider->left_knob_focus = 0;
    280         }
    281     }
    282     else if(code == LV_EVENT_SIZE_CHANGED) {
    283         lv_obj_refresh_ext_draw_size(obj);
    284     }
    285     else if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) {
    286         lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
    287         lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
    288         lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
    289         lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
    290 
    291         /*The smaller size is the knob diameter*/
    292         lv_coord_t zoom = lv_obj_get_style_transform_zoom(obj, LV_PART_KNOB);
    293         lv_coord_t trans_w = lv_obj_get_style_transform_width(obj, LV_PART_KNOB);
    294         lv_coord_t trans_h = lv_obj_get_style_transform_height(obj, LV_PART_KNOB);
    295         lv_coord_t knob_size = LV_MIN(lv_obj_get_width(obj) + 2 * trans_w, lv_obj_get_height(obj) + 2 * trans_h) >> 1;
    296         knob_size = (knob_size * zoom) >> 8;
    297         knob_size += LV_MAX(LV_MAX(knob_left, knob_right), LV_MAX(knob_bottom, knob_top));
    298         knob_size += 2;         /*For rounding error*/
    299         knob_size += lv_obj_calculate_ext_draw_size(obj, LV_PART_KNOB);
    300 
    301         /*Indic. size is handled by bar*/
    302         lv_coord_t * s = lv_event_get_param(e);
    303         *s  = LV_MAX(*s, knob_size);
    304 
    305     }
    306     else if(code == LV_EVENT_KEY) {
    307         char c = *((char *)lv_event_get_param(e));
    308 
    309         if(c == LV_KEY_RIGHT || c == LV_KEY_UP) {
    310             if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) + 1, LV_ANIM_ON);
    311             else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) + 1, LV_ANIM_ON);
    312         }
    313         else if(c == LV_KEY_LEFT || c == LV_KEY_DOWN) {
    314             if(!slider->left_knob_focus) lv_slider_set_value(obj, lv_slider_get_value(obj) - 1, LV_ANIM_ON);
    315             else lv_slider_set_left_value(obj, lv_slider_get_left_value(obj) - 1, LV_ANIM_ON);
    316         }
    317         else {
    318             return;
    319         }
    320 
    321         res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, NULL);
    322         if(res != LV_RES_OK) return;
    323     }
    324     else if(code == LV_EVENT_DRAW_MAIN) {
    325         draw_knob(e);
    326     }
    327 }
    328 
    329 static void draw_knob(lv_event_t * e)
    330 {
    331     lv_obj_t * obj = lv_event_get_target(e);
    332     lv_slider_t * slider = (lv_slider_t *)obj;
    333     lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e);
    334 
    335     const bool is_rtl = LV_BASE_DIR_RTL == lv_obj_get_style_base_dir(obj, LV_PART_MAIN);
    336     const bool is_horizontal = is_slider_horizontal(obj);
    337 
    338     lv_area_t knob_area;
    339     lv_coord_t knob_size;
    340     if(is_horizontal) {
    341         knob_size = lv_obj_get_height(obj);
    342         knob_area.x1 = LV_SLIDER_KNOB_COORD(is_rtl, slider->bar.indic_area);
    343     }
    344     else {
    345         bool is_symmetrical = false;
    346         if(slider->bar.mode == LV_BAR_MODE_SYMMETRICAL && slider->bar.min_value < 0 &&
    347            slider->bar.max_value > 0) is_symmetrical = true;
    348 
    349         knob_size = lv_obj_get_width(obj);
    350         if(is_symmetrical && slider->bar.cur_value < 0) knob_area.y1 = slider->bar.indic_area.y2;
    351         else knob_area.y1 = slider->bar.indic_area.y1;
    352     }
    353 
    354     lv_draw_rect_dsc_t knob_rect_dsc;
    355     lv_draw_rect_dsc_init(&knob_rect_dsc);
    356     lv_obj_init_draw_rect_dsc(obj, LV_PART_KNOB, &knob_rect_dsc);
    357     /* Update knob area with knob style */
    358     position_knob(obj, &knob_area, knob_size, is_horizontal);
    359     /* Update right knob area with calculated knob area */
    360     lv_area_copy(&slider->right_knob_area, &knob_area);
    361 
    362     lv_obj_draw_part_dsc_t part_draw_dsc;
    363     lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx);
    364     part_draw_dsc.part = LV_PART_KNOB;
    365     part_draw_dsc.class_p = MY_CLASS;
    366     part_draw_dsc.type = LV_SLIDER_DRAW_PART_KNOB;
    367     part_draw_dsc.id = 0;
    368     part_draw_dsc.draw_area = &slider->right_knob_area;
    369     part_draw_dsc.rect_dsc = &knob_rect_dsc;
    370 
    371     if(lv_slider_get_mode(obj) != LV_SLIDER_MODE_RANGE) {
    372         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
    373         lv_draw_rect(draw_ctx, &knob_rect_dsc, &slider->right_knob_area);
    374         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
    375     }
    376     else {
    377         /*Save the draw part_draw_dsc. because it can be modified in the event*/
    378         lv_draw_rect_dsc_t knob_rect_dsc_tmp;
    379         lv_memcpy(&knob_rect_dsc_tmp, &knob_rect_dsc, sizeof(lv_draw_rect_dsc_t));
    380         /* Draw the right knob */
    381         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
    382         lv_draw_rect(draw_ctx, &knob_rect_dsc, &slider->right_knob_area);
    383         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
    384 
    385         /*Calculate the second knob area*/
    386         if(is_horizontal) {
    387             /*use !is_rtl to get the other knob*/
    388             knob_area.x1 = LV_SLIDER_KNOB_COORD(!is_rtl, slider->bar.indic_area);
    389         }
    390         else {
    391             knob_area.y1 = slider->bar.indic_area.y2;
    392         }
    393         position_knob(obj, &knob_area, knob_size, is_horizontal);
    394         lv_area_copy(&slider->left_knob_area, &knob_area);
    395 
    396         lv_memcpy(&knob_rect_dsc, &knob_rect_dsc_tmp, sizeof(lv_draw_rect_dsc_t));
    397         part_draw_dsc.type = LV_SLIDER_DRAW_PART_KNOB_LEFT;
    398         part_draw_dsc.draw_area = &slider->left_knob_area;
    399         part_draw_dsc.rect_dsc = &knob_rect_dsc;
    400         part_draw_dsc.id = 1;
    401 
    402         lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc);
    403         lv_draw_rect(draw_ctx, &knob_rect_dsc, &slider->left_knob_area);
    404         lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc);
    405     }
    406 }
    407 
    408 static void position_knob(lv_obj_t * obj, lv_area_t * knob_area, const lv_coord_t knob_size, const bool hor)
    409 {
    410     if(hor) {
    411         knob_area->x1 -= (knob_size >> 1);
    412         knob_area->x2 = knob_area->x1 + knob_size - 1;
    413         knob_area->y1 = obj->coords.y1;
    414         knob_area->y2 = obj->coords.y2;
    415     }
    416     else {
    417         knob_area->y1 -= (knob_size >> 1);
    418         knob_area->y2 = knob_area->y1 + knob_size - 1;
    419         knob_area->x1 = obj->coords.x1;
    420         knob_area->x2 = obj->coords.x2;
    421     }
    422 
    423     lv_coord_t knob_left = lv_obj_get_style_pad_left(obj, LV_PART_KNOB);
    424     lv_coord_t knob_right = lv_obj_get_style_pad_right(obj, LV_PART_KNOB);
    425     lv_coord_t knob_top = lv_obj_get_style_pad_top(obj, LV_PART_KNOB);
    426     lv_coord_t knob_bottom = lv_obj_get_style_pad_bottom(obj, LV_PART_KNOB);
    427 
    428     lv_coord_t transf_w = lv_obj_get_style_transform_width(obj, LV_PART_KNOB);
    429     lv_coord_t transf_h = lv_obj_get_style_transform_height(obj, LV_PART_KNOB);
    430 
    431     /*Apply the paddings on the knob area*/
    432     knob_area->x1 -= knob_left + transf_w;
    433     knob_area->x2 += knob_right + transf_w;
    434     knob_area->y1 -= knob_top + transf_h;
    435     knob_area->y2 += knob_bottom + transf_h;
    436 }
    437 
    438 static bool is_slider_horizontal(lv_obj_t * obj)
    439 {
    440     return lv_obj_get_width(obj) >= lv_obj_get_height(obj);
    441 }
    442 
    443 #endif