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*/