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_spinbox.c (14841B)

      1 /**
      2  * @file lv_spinbox.c
      3  *
      4  */
      5 
      6 /*********************
      7  *      INCLUDES
      8  *********************/
      9 #include "lv_spinbox.h"
     10 #if LV_USE_SPINBOX
     11 
     12 #include "../../../misc/lv_assert.h"
     13 
     14 /*********************
     15  *      DEFINES
     16  *********************/
     17 #define MY_CLASS    &lv_spinbox_class
     18 
     19 /**********************
     20  *      TYPEDEFS
     21  **********************/
     22 
     23 /**********************
     24  *  STATIC PROTOTYPES
     25  **********************/
     26 
     27 static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj);
     28 static void lv_spinbox_event(const lv_obj_class_t * class_p, lv_event_t * e);
     29 static void lv_spinbox_updatevalue(lv_obj_t * obj);
     30 
     31 /**********************
     32  *  STATIC VARIABLES
     33  **********************/
     34 const lv_obj_class_t lv_spinbox_class = {
     35     .constructor_cb = lv_spinbox_constructor,
     36     .event_cb = lv_spinbox_event,
     37     .width_def = LV_DPI_DEF,
     38     .instance_size = sizeof(lv_spinbox_t),
     39     .editable = LV_OBJ_CLASS_EDITABLE_TRUE,
     40     .base_class = &lv_textarea_class
     41 };
     42 /**********************
     43  *      MACROS
     44  **********************/
     45 
     46 /**********************
     47  *   GLOBAL FUNCTIONS
     48  **********************/
     49 
     50 lv_obj_t * lv_spinbox_create(lv_obj_t * parent)
     51 {
     52     LV_LOG_INFO("begin");
     53     lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent);
     54     lv_obj_class_init_obj(obj);
     55     return obj;
     56 }
     57 
     58 /*=====================
     59  * Setter functions
     60  *====================*/
     61 
     62 /**
     63  * Set spinbox value
     64  * @param obj pointer to spinbox
     65  * @param i value to be set
     66  */
     67 void lv_spinbox_set_value(lv_obj_t * obj, int32_t i)
     68 {
     69     LV_ASSERT_OBJ(obj, MY_CLASS);
     70     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
     71 
     72     if(i > spinbox->range_max) i = spinbox->range_max;
     73     if(i < spinbox->range_min) i = spinbox->range_min;
     74 
     75     spinbox->value = i;
     76 
     77     lv_spinbox_updatevalue(obj);
     78 }
     79 
     80 /**
     81  * Set spinbox rollover function
     82  * @param spinbox pointer to spinbox
     83  * @param b true or false to enable or disable (default)
     84  */
     85 void lv_spinbox_set_rollover(lv_obj_t * obj, bool b)
     86 {
     87     LV_ASSERT_OBJ(obj, MY_CLASS);
     88     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
     89 
     90     spinbox->rollover = b;
     91 }
     92 
     93 /**
     94  * Set spinbox digit format (digit count and decimal format)
     95  * @param spinbox pointer to spinbox
     96  * @param digit_count number of digit excluding the decimal separator and the sign
     97  * @param separator_position number of digit before the decimal point. If 0, decimal point is not
     98  * shown
     99  */
    100 void lv_spinbox_set_digit_format(lv_obj_t * obj, uint8_t digit_count, uint8_t separator_position)
    101 {
    102     LV_ASSERT_OBJ(obj, MY_CLASS);
    103     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    104 
    105     if(digit_count > LV_SPINBOX_MAX_DIGIT_COUNT) digit_count = LV_SPINBOX_MAX_DIGIT_COUNT;
    106 
    107     if(separator_position >= digit_count) separator_position = 0;
    108 
    109     if(digit_count < LV_SPINBOX_MAX_DIGIT_COUNT) {
    110         int64_t max_val = lv_pow(10, digit_count);
    111         if(spinbox->range_max > max_val - 1) spinbox->range_max = max_val - 1;
    112         if(spinbox->range_min < - max_val  + 1) spinbox->range_min = - max_val  + 1;
    113     }
    114 
    115     spinbox->digit_count   = digit_count;
    116     spinbox->dec_point_pos = separator_position;
    117 
    118     lv_spinbox_updatevalue(obj);
    119 }
    120 
    121 /**
    122  * Set spinbox step
    123  * @param spinbox pointer to spinbox
    124  * @param step steps on increment/decrement
    125  */
    126 void lv_spinbox_set_step(lv_obj_t * obj, uint32_t step)
    127 {
    128     LV_ASSERT_OBJ(obj, MY_CLASS);
    129     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    130 
    131     spinbox->step = step;
    132     lv_spinbox_updatevalue(obj);
    133 }
    134 
    135 /**
    136  * Set spinbox value range
    137  * @param spinbox pointer to spinbox
    138  * @param range_min maximum value, inclusive
    139  * @param range_max minimum value, inclusive
    140  */
    141 void lv_spinbox_set_range(lv_obj_t * obj, int32_t range_min, int32_t range_max)
    142 {
    143     LV_ASSERT_OBJ(obj, MY_CLASS);
    144     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    145 
    146     spinbox->range_max = range_max;
    147     spinbox->range_min = range_min;
    148 
    149     if(spinbox->value > spinbox->range_max) spinbox->value = spinbox->range_max;
    150     if(spinbox->value < spinbox->range_min) spinbox->value = spinbox->range_min;
    151 
    152     lv_spinbox_updatevalue(obj);
    153 }
    154 
    155 /**
    156  * Set cursor position to a specific digit for edition
    157  * @param spinbox pointer to spinbox
    158  * @param pos selected position in spinbox
    159  */
    160 void lv_spinbox_set_pos(lv_obj_t * obj, uint8_t pos)
    161 {
    162     LV_ASSERT_OBJ(obj, MY_CLASS);
    163     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    164     int32_t step_limit;
    165     step_limit       = LV_MAX(spinbox->range_max, (spinbox->range_min < 0 ? (-spinbox->range_min) : spinbox->range_min));
    166     int32_t new_step = spinbox->step * lv_pow(10, pos);
    167     if(pos <= 0) spinbox->step = 1;
    168     else if(new_step <= step_limit) spinbox->step = new_step;
    169 
    170     lv_spinbox_updatevalue(obj);
    171 }
    172 
    173 /**
    174  * Set direction of digit step when clicking an encoder button while in editing mode
    175  * @param spinbox pointer to spinbox
    176  * @param direction the direction (LV_DIR_RIGHT or LV_DIR_LEFT)
    177  */
    178 void lv_spinbox_set_digit_step_direction(lv_obj_t * obj, lv_dir_t direction)
    179 {
    180     LV_ASSERT_OBJ(obj, MY_CLASS);
    181     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    182     spinbox->digit_step_dir = direction;
    183 
    184     lv_spinbox_updatevalue(obj);
    185 }
    186 /*=====================
    187  * Getter functions
    188  *====================*/
    189 
    190 /**
    191  * Get the spinbox numeral value (user has to convert to float according to its digit format)
    192  * @param obj pointer to spinbox
    193  * @return value integer value of the spinbox
    194  */
    195 int32_t lv_spinbox_get_value(lv_obj_t * obj)
    196 {
    197     LV_ASSERT_OBJ(obj, MY_CLASS);
    198     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    199 
    200     return spinbox->value;
    201 }
    202 /**
    203  * Get the spinbox step value (user has to convert to float according to its digit format)
    204  * @param obj pointer to spinbox
    205  * @return value integer step value of the spinbox
    206  */
    207 int32_t lv_spinbox_get_step(lv_obj_t * obj)
    208 {
    209     LV_ASSERT_OBJ(obj, MY_CLASS);
    210     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    211 
    212     return spinbox->step;
    213 }
    214 
    215 /*=====================
    216  * Other functions
    217  *====================*/
    218 
    219 /**
    220  * Select next lower digit for edition
    221  * @param obj pointer to spinbox
    222  */
    223 void lv_spinbox_step_next(lv_obj_t * obj)
    224 {
    225     LV_ASSERT_OBJ(obj, MY_CLASS);
    226     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    227 
    228     int32_t new_step = spinbox->step / 10;
    229     if((new_step) > 0)
    230         spinbox->step = new_step;
    231     else
    232         spinbox->step = 1;
    233 
    234     lv_spinbox_updatevalue(obj);
    235 }
    236 
    237 /**
    238  * Select next higher digit for edition
    239  * @param obj pointer to spinbox
    240  */
    241 void lv_spinbox_step_prev(lv_obj_t * obj)
    242 {
    243     LV_ASSERT_OBJ(obj, MY_CLASS);
    244     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    245     int32_t step_limit;
    246     step_limit       = LV_MAX(spinbox->range_max, (spinbox->range_min < 0 ? (-spinbox->range_min) : spinbox->range_min));
    247     int32_t new_step = spinbox->step * 10;
    248     if(new_step <= step_limit) spinbox->step = new_step;
    249 
    250     lv_spinbox_updatevalue(obj);
    251 }
    252 
    253 /**
    254  * Get spinbox rollover function status
    255  * @param obj pointer to spinbox
    256  */
    257 bool lv_spinbox_get_rollover(lv_obj_t * obj)
    258 {
    259     LV_ASSERT_OBJ(obj, MY_CLASS);
    260     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    261 
    262     return spinbox->rollover;
    263 }
    264 
    265 /**
    266  * Increment spinbox value by one step
    267  * @param obj pointer to spinbox
    268  */
    269 void lv_spinbox_increment(lv_obj_t * obj)
    270 {
    271     LV_ASSERT_OBJ(obj, MY_CLASS);
    272     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    273 
    274     if(spinbox->value + spinbox->step <= spinbox->range_max) {
    275         /*Special mode when zero crossing*/
    276         if((spinbox->value + spinbox->step) > 0 && spinbox->value < 0) spinbox->value = -spinbox->value;
    277         spinbox->value += spinbox->step;
    278 
    279     }
    280     else {
    281         // Rollover?
    282         if((spinbox->rollover) && (spinbox->value == spinbox->range_max))
    283             spinbox->value = spinbox->range_min;
    284         else
    285             spinbox->value = spinbox->range_max;
    286     }
    287 
    288     lv_spinbox_updatevalue(obj);
    289 }
    290 
    291 /**
    292  * Decrement spinbox value by one step
    293  * @param obj pointer to spinbox
    294  */
    295 void lv_spinbox_decrement(lv_obj_t * obj)
    296 {
    297     LV_ASSERT_OBJ(obj, MY_CLASS);
    298     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    299 
    300     if(spinbox->value - spinbox->step >= spinbox->range_min) {
    301         /*Special mode when zero crossing*/
    302         if((spinbox->value - spinbox->step) < 0 && spinbox->value > 0) spinbox->value = -spinbox->value;
    303         spinbox->value -= spinbox->step;
    304     }
    305     else {
    306         /*Rollover?*/
    307         if((spinbox->rollover) && (spinbox->value == spinbox->range_min))
    308             spinbox->value = spinbox->range_max;
    309         else
    310             spinbox->value = spinbox->range_min;
    311     }
    312 
    313     lv_spinbox_updatevalue(obj);
    314 }
    315 
    316 /**********************
    317  *   STATIC FUNCTIONS
    318  **********************/
    319 
    320 static void lv_spinbox_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj)
    321 {
    322     LV_UNUSED(class_p);
    323     LV_LOG_TRACE("begin");
    324 
    325     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    326 
    327     /*Initialize the allocated 'ext'*/
    328     spinbox->value              = 0;
    329     spinbox->dec_point_pos      = 0;
    330     spinbox->digit_count        = 5;
    331     spinbox->step               = 1;
    332     spinbox->range_max          = 99999;
    333     spinbox->range_min          = -99999;
    334     spinbox->rollover           = false;
    335     spinbox->digit_step_dir     = LV_DIR_RIGHT;
    336 
    337     lv_textarea_set_one_line(obj, true);
    338     lv_textarea_set_cursor_click_pos(obj, true);
    339 
    340     lv_spinbox_updatevalue(obj);
    341 
    342     LV_LOG_TRACE("Spinbox constructor finished");
    343 }
    344 
    345 static void lv_spinbox_event(const lv_obj_class_t * class_p, lv_event_t * e)
    346 {
    347     LV_UNUSED(class_p);
    348 
    349     /*Call the ancestor's event handler*/
    350     lv_res_t res = LV_RES_OK;
    351     res = lv_obj_event_base(MY_CLASS, e);
    352     if(res != LV_RES_OK) return;
    353 
    354     lv_event_code_t code = lv_event_get_code(e);
    355     lv_obj_t * obj = lv_event_get_target(e);
    356     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    357     if(code == LV_EVENT_RELEASED) {
    358         /*If released with an ENCODER then move to the next digit*/
    359         lv_indev_t * indev = lv_indev_get_act();
    360         if(lv_indev_get_type(indev) == LV_INDEV_TYPE_ENCODER) {
    361             if(lv_group_get_editing(lv_obj_get_group(obj))) {
    362                 if(spinbox->digit_count > 1) {
    363                     if(spinbox->digit_step_dir == LV_DIR_RIGHT) {
    364                         if(spinbox->step > 1) {
    365                             lv_spinbox_step_next(obj);
    366                         }
    367                         else {
    368                             /*Restart from the MSB*/
    369                             spinbox->step = lv_pow(10, spinbox->digit_count - 2);
    370                             lv_spinbox_step_prev(obj);
    371                         }
    372                     }
    373                     else {
    374                         if(spinbox->step < lv_pow(10, spinbox->digit_count - 1)) {
    375                             lv_spinbox_step_prev(obj);
    376                         }
    377                         else {
    378                             /*Restart from the LSB*/
    379                             spinbox->step = 10;
    380                             lv_spinbox_step_next(obj);
    381                         }
    382                     }
    383                 }
    384             }
    385         }
    386         /*The cursor has been positioned to a digit.
    387          * Set `step` accordingly*/
    388         else {
    389             const char * txt = lv_textarea_get_text(obj);
    390             size_t txt_len = strlen(txt);
    391 
    392             if(txt[spinbox->ta.cursor.pos] == '.') {
    393                 lv_textarea_cursor_left(obj);
    394             }
    395             else if(spinbox->ta.cursor.pos == (uint32_t)txt_len) {
    396                 lv_textarea_set_cursor_pos(obj, txt_len - 1);
    397             }
    398             else if(spinbox->ta.cursor.pos == 0 && spinbox->range_min < 0) {
    399                 lv_textarea_set_cursor_pos(obj, 1);
    400             }
    401 
    402             size_t len = spinbox->digit_count - 1;
    403             uint16_t cp = spinbox->ta.cursor.pos;
    404 
    405             if(spinbox->ta.cursor.pos > spinbox->dec_point_pos && spinbox->dec_point_pos != 0) cp--;
    406             uint32_t pos = len - cp;
    407 
    408             if(spinbox->range_min < 0) pos++;
    409 
    410             spinbox->step = 1;
    411             uint16_t i;
    412             for(i = 0; i < pos; i++) spinbox->step *= 10;
    413         }
    414     }
    415     else if(code == LV_EVENT_KEY) {
    416         lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act());
    417 
    418         uint32_t c = *((uint32_t *)lv_event_get_param(e)); /*uint32_t because can be UTF-8*/
    419         if(c == LV_KEY_RIGHT) {
    420             if(indev_type == LV_INDEV_TYPE_ENCODER)
    421                 lv_spinbox_increment(obj);
    422             else
    423                 lv_spinbox_step_next(obj);
    424         }
    425         else if(c == LV_KEY_LEFT) {
    426             if(indev_type == LV_INDEV_TYPE_ENCODER)
    427                 lv_spinbox_decrement(obj);
    428             else
    429                 lv_spinbox_step_prev(obj);
    430         }
    431         else if(c == LV_KEY_UP) {
    432             lv_spinbox_increment(obj);
    433         }
    434         else if(c == LV_KEY_DOWN) {
    435             lv_spinbox_decrement(obj);
    436         }
    437         else {
    438             lv_textarea_add_char(obj, c);
    439         }
    440     }
    441 }
    442 
    443 static void lv_spinbox_updatevalue(lv_obj_t * obj)
    444 {
    445     lv_spinbox_t * spinbox = (lv_spinbox_t *)obj;
    446 
    447     char buf[LV_SPINBOX_MAX_DIGIT_COUNT + 8];
    448     lv_memset_00(buf, sizeof(buf));
    449     char * buf_p = buf;
    450     uint8_t cur_shift_left = 0;
    451 
    452     if(spinbox->range_min < 0) {  // hide sign if there are only positive values
    453         /*Add the sign*/
    454         (*buf_p) = spinbox->value >= 0 ? '+' : '-';
    455         buf_p++;
    456     }
    457     else {
    458         /*Cursor need shift to left*/
    459         cur_shift_left++;
    460     }
    461 
    462     int32_t i;
    463     char digits[LV_SPINBOX_MAX_DIGIT_COUNT + 4];
    464     /*Convert the numbers to string (the sign is already handled so always covert positive number)*/
    465     lv_snprintf(digits, sizeof(digits), "%" LV_PRId32, LV_ABS(spinbox->value));
    466 
    467     /*Add leading zeros*/
    468     int lz_cnt = spinbox->digit_count - (int)strlen(digits);
    469     if(lz_cnt > 0) {
    470         for(i = (uint16_t)strlen(digits); i >= 0; i--) {
    471             digits[i + lz_cnt] = digits[i];
    472         }
    473         for(i = 0; i < lz_cnt; i++) {
    474             digits[i] = '0';
    475         }
    476     }
    477 
    478     int32_t intDigits;
    479     intDigits = (spinbox->dec_point_pos == 0) ? spinbox->digit_count : spinbox->dec_point_pos;
    480 
    481     /*Add the decimal part*/
    482     for(i = 0; i < intDigits && digits[i] != '\0'; i++) {
    483         (*buf_p) = digits[i];
    484         buf_p++;
    485     }
    486 
    487     if(spinbox->dec_point_pos != 0) {
    488         /*Insert the decimal point*/
    489         (*buf_p) = '.';
    490         buf_p++;
    491 
    492         for(/*Leave i*/; i < spinbox->digit_count && digits[i] != '\0'; i++) {
    493             (*buf_p) = digits[i];
    494             buf_p++;
    495         }
    496     }
    497 
    498     /*Refresh the text*/
    499     lv_textarea_set_text(obj, (char *)buf);
    500 
    501     /*Set the cursor position*/
    502     int32_t step    = spinbox->step;
    503     uint8_t cur_pos = (uint8_t)spinbox->digit_count;
    504     while(step >= 10) {
    505         step /= 10;
    506         cur_pos--;
    507     }
    508 
    509     if(cur_pos > intDigits) cur_pos++; /*Skip the decimal point*/
    510 
    511     cur_pos -= cur_shift_left;
    512 
    513     lv_textarea_set_cursor_pos(obj, cur_pos);
    514 }
    515 
    516 #endif /*LV_USE_SPINBOX*/