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_btnmatrix.c (38154B)
1 /** 2 * @file lv_btnmatrix.c 3 * 4 */ 5 6 /********************* 7 * INCLUDES 8 *********************/ 9 #include "lv_btnmatrix.h" 10 #if LV_USE_BTNMATRIX != 0 11 12 #include "../misc/lv_assert.h" 13 #include "../core/lv_indev.h" 14 #include "../core/lv_group.h" 15 #include "../draw/lv_draw.h" 16 #include "../core/lv_refr.h" 17 #include "../misc/lv_txt.h" 18 #include "../misc/lv_txt_ap.h" 19 20 /********************* 21 * DEFINES 22 *********************/ 23 #define MY_CLASS &lv_btnmatrix_class 24 25 #define BTN_EXTRA_CLICK_AREA_MAX (LV_DPI_DEF / 10) 26 #define LV_BTNMATRIX_WIDTH_MASK 0x0007 27 28 /********************** 29 * TYPEDEFS 30 **********************/ 31 32 /********************** 33 * STATIC PROTOTYPES 34 **********************/ 35 static void lv_btnmatrix_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj); 36 static void lv_btnmatrix_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj); 37 static void lv_btnmatrix_event(const lv_obj_class_t * class_p, lv_event_t * e); 38 static void draw_main(lv_event_t * e); 39 40 static uint8_t get_button_width(lv_btnmatrix_ctrl_t ctrl_bits); 41 static bool button_is_hidden(lv_btnmatrix_ctrl_t ctrl_bits); 42 static bool button_is_checked(lv_btnmatrix_ctrl_t ctrl_bits); 43 static bool button_is_repeat_disabled(lv_btnmatrix_ctrl_t ctrl_bits); 44 static bool button_is_inactive(lv_btnmatrix_ctrl_t ctrl_bits); 45 static bool button_is_click_trig(lv_btnmatrix_ctrl_t ctrl_bits); 46 static bool button_is_popover(lv_btnmatrix_ctrl_t ctrl_bits); 47 static bool button_is_checkable(lv_btnmatrix_ctrl_t ctrl_bits); 48 static bool button_is_recolor(lv_btnmatrix_ctrl_t ctrl_bits); 49 static bool button_get_checked(lv_btnmatrix_ctrl_t ctrl_bits); 50 static uint16_t get_button_from_point(lv_obj_t * obj, lv_point_t * p); 51 static void allocate_btn_areas_and_controls(const lv_obj_t * obj, const char ** map); 52 static void invalidate_button_area(const lv_obj_t * obj, uint16_t btn_idx); 53 static void make_one_button_checked(lv_obj_t * obj, uint16_t btn_idx); 54 static bool has_popovers_in_top_row(lv_obj_t * obj); 55 56 /********************** 57 * STATIC VARIABLES 58 **********************/ 59 static const char * lv_btnmatrix_def_map[] = {"Btn1", "Btn2", "Btn3", "\n", "Btn4", "Btn5", ""}; 60 61 const lv_obj_class_t lv_btnmatrix_class = { 62 .constructor_cb = lv_btnmatrix_constructor, 63 .destructor_cb = lv_btnmatrix_destructor, 64 .event_cb = lv_btnmatrix_event, 65 .width_def = LV_DPI_DEF * 2, 66 .height_def = LV_DPI_DEF, 67 .instance_size = sizeof(lv_btnmatrix_t), 68 .editable = LV_OBJ_CLASS_EDITABLE_TRUE, 69 .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, 70 .base_class = &lv_obj_class 71 }; 72 73 /********************** 74 * MACROS 75 **********************/ 76 77 /********************** 78 * GLOBAL FUNCTIONS 79 **********************/ 80 81 lv_obj_t * lv_btnmatrix_create(lv_obj_t * parent) 82 { 83 LV_LOG_INFO("begin"); 84 lv_obj_t * obj = lv_obj_class_create_obj(MY_CLASS, parent); 85 lv_obj_class_init_obj(obj); 86 return obj; 87 } 88 89 /*===================== 90 * Setter functions 91 *====================*/ 92 93 void lv_btnmatrix_set_map(lv_obj_t * obj, const char * map[]) 94 { 95 LV_ASSERT_OBJ(obj, MY_CLASS); 96 if(map == NULL) return; 97 98 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj; 99 100 /*Analyze the map and create the required number of buttons*/ 101 allocate_btn_areas_and_controls(obj, map); 102 btnm->map_p = map; 103 104 lv_base_dir_t base_dir = lv_obj_get_style_base_dir(obj, LV_PART_MAIN); 105 106 /*Set size and positions of the buttons*/ 107 lv_coord_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); 108 lv_coord_t ptop = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); 109 lv_coord_t prow = lv_obj_get_style_pad_row(obj, LV_PART_MAIN); 110 lv_coord_t pcol = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); 111 112 lv_coord_t max_w = lv_obj_get_content_width(obj); 113 lv_coord_t max_h = lv_obj_get_content_height(obj); 114 115 /*Calculate the position of each row*/ 116 lv_coord_t max_h_no_gap = max_h - (prow * (btnm->row_cnt - 1)); 117 118 /*Count the units and the buttons in a line 119 *(A button can be 1,2,3... unit wide)*/ 120 uint32_t txt_tot_i = 0; /*Act. index in the str map*/ 121 uint32_t btn_tot_i = 0; /*Act. index of button areas*/ 122 const char ** map_row = map; 123 124 /*Count the units and the buttons in a line*/ 125 uint32_t row; 126 for(row = 0; row < btnm->row_cnt; row++) { 127 uint16_t unit_cnt = 0; /*Number of units in a row*/ 128 uint16_t btn_cnt = 0; /*Number of buttons in a row*/ 129 /*Count the buttons and units in this row*/ 130 while(map_row[btn_cnt] && strcmp(map_row[btn_cnt], "\n") != 0 && map_row[btn_cnt][0] != '\0') { 131 unit_cnt += get_button_width(btnm->ctrl_bits[btn_tot_i + btn_cnt]); 132 btn_cnt++; 133 } 134 135 /*Only deal with the non empty lines*/ 136 if(btn_cnt == 0) { 137 map_row = &map_row[btn_cnt + 1]; /*Set the map to the next row*/ 138 continue; 139 } 140 141 lv_coord_t row_y1 = ptop + (max_h_no_gap * row) / btnm->row_cnt + row * prow; 142 lv_coord_t row_y2 = ptop + (max_h_no_gap * (row + 1)) / btnm->row_cnt + row * prow - 1; 143 144 /*Set the button size and positions*/ 145 lv_coord_t max_w_no_gap = max_w - (pcol * (btn_cnt - 1)); 146 if(max_w_no_gap < 0) max_w_no_gap = 0; 147 148 uint32_t row_unit_cnt = 0; /*The current unit position in the row*/ 149 uint32_t btn; 150 for(btn = 0; btn < btn_cnt; btn++, btn_tot_i++, txt_tot_i++) { 151 uint32_t btn_u = get_button_width(btnm->ctrl_bits[btn_tot_i]); 152 153 lv_coord_t btn_x1 = (max_w_no_gap * row_unit_cnt) / unit_cnt + btn * pcol; 154 lv_coord_t btn_x2 = (max_w_no_gap * (row_unit_cnt + btn_u)) / unit_cnt + btn * pcol - 1; 155 156 /*If RTL start from the right*/ 157 if(base_dir == LV_BASE_DIR_RTL) { 158 lv_coord_t tmp = btn_x1; 159 btn_x1 = btn_x2; 160 btn_x2 = tmp; 161 162 btn_x1 = max_w - btn_x1; 163 btn_x2 = max_w - btn_x2; 164 } 165 166 btn_x1 += pleft; 167 btn_x2 += pleft; 168 169 lv_area_set(&btnm->button_areas[btn_tot_i], btn_x1, row_y1, btn_x2, row_y2); 170 171 row_unit_cnt += btn_u; 172 } 173 174 map_row = &map_row[btn_cnt + 1]; /*Set the map to the next line*/ 175 } 176 177 /*Popovers in the top row will draw outside the widget and the extended draw size depends on 178 *the row height which may have changed when setting the new map*/ 179 lv_obj_refresh_ext_draw_size(obj); 180 181 lv_obj_invalidate(obj); 182 } 183 184 void lv_btnmatrix_set_ctrl_map(lv_obj_t * obj, const lv_btnmatrix_ctrl_t ctrl_map[]) 185 { 186 LV_ASSERT_OBJ(obj, MY_CLASS); 187 188 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj; 189 lv_memcpy(btnm->ctrl_bits, ctrl_map, sizeof(lv_btnmatrix_ctrl_t) * btnm->btn_cnt); 190 191 lv_btnmatrix_set_map(obj, btnm->map_p); 192 } 193 194 void lv_btnmatrix_set_selected_btn(lv_obj_t * obj, uint16_t btn_id) 195 { 196 LV_ASSERT_OBJ(obj, MY_CLASS); 197 198 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj; 199 200 if(btn_id >= btnm->btn_cnt && btn_id != LV_BTNMATRIX_BTN_NONE) return; 201 202 invalidate_button_area(obj, btnm->btn_id_sel); 203 btnm->btn_id_sel = btn_id; 204 invalidate_button_area(obj, btn_id); 205 } 206 207 void lv_btnmatrix_set_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctrl_t ctrl) 208 { 209 LV_ASSERT_OBJ(obj, MY_CLASS); 210 211 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 212 213 if(btn_id >= btnm->btn_cnt) return; 214 215 if(btnm->one_check && (ctrl & LV_BTNMATRIX_CTRL_CHECKED)) { 216 lv_btnmatrix_clear_btn_ctrl_all(obj, LV_BTNMATRIX_CTRL_CHECKED); 217 } 218 219 btnm->ctrl_bits[btn_id] |= ctrl; 220 invalidate_button_area(obj, btn_id); 221 222 if(ctrl & LV_BTNMATRIX_CTRL_POPOVER) { 223 lv_obj_refresh_ext_draw_size(obj); 224 } 225 } 226 227 void lv_btnmatrix_clear_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctrl_t ctrl) 228 { 229 LV_ASSERT_OBJ(obj, MY_CLASS); 230 231 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 232 233 if(btn_id >= btnm->btn_cnt) return; 234 235 btnm->ctrl_bits[btn_id] &= (~ctrl); 236 invalidate_button_area(obj, btn_id); 237 238 if(ctrl & LV_BTNMATRIX_CTRL_POPOVER) { 239 lv_obj_refresh_ext_draw_size(obj); 240 } 241 } 242 243 void lv_btnmatrix_set_btn_ctrl_all(lv_obj_t * obj, lv_btnmatrix_ctrl_t ctrl) 244 { 245 LV_ASSERT_OBJ(obj, MY_CLASS); 246 247 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 248 uint16_t i; 249 for(i = 0; i < btnm->btn_cnt; i++) { 250 lv_btnmatrix_set_btn_ctrl(obj, i, ctrl); 251 } 252 } 253 254 void lv_btnmatrix_clear_btn_ctrl_all(lv_obj_t * obj, lv_btnmatrix_ctrl_t ctrl) 255 { 256 LV_ASSERT_OBJ(obj, MY_CLASS); 257 258 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 259 uint16_t i; 260 for(i = 0; i < btnm->btn_cnt; i++) { 261 lv_btnmatrix_clear_btn_ctrl(obj, i, ctrl); 262 } 263 } 264 265 void lv_btnmatrix_set_btn_width(lv_obj_t * obj, uint16_t btn_id, uint8_t width) 266 { 267 LV_ASSERT_OBJ(obj, MY_CLASS); 268 269 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 270 if(btn_id >= btnm->btn_cnt) return; 271 btnm->ctrl_bits[btn_id] &= (~LV_BTNMATRIX_WIDTH_MASK); 272 btnm->ctrl_bits[btn_id] |= (LV_BTNMATRIX_WIDTH_MASK & width); 273 274 lv_btnmatrix_set_map(obj, btnm->map_p); 275 } 276 277 void lv_btnmatrix_set_one_checked(lv_obj_t * obj, bool en) 278 { 279 LV_ASSERT_OBJ(obj, MY_CLASS); 280 281 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 282 btnm->one_check = en; 283 284 /*If more than one button is toggled only the first one should be*/ 285 make_one_button_checked(obj, 0); 286 } 287 288 /*===================== 289 * Getter functions 290 *====================*/ 291 292 const char ** lv_btnmatrix_get_map(const lv_obj_t * obj) 293 { 294 LV_ASSERT_OBJ(obj, MY_CLASS); 295 296 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 297 return btnm->map_p; 298 } 299 300 uint16_t lv_btnmatrix_get_selected_btn(const lv_obj_t * obj) 301 { 302 LV_ASSERT_OBJ(obj, MY_CLASS); 303 304 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 305 return btnm->btn_id_sel; 306 } 307 308 const char * lv_btnmatrix_get_btn_text(const lv_obj_t * obj, uint16_t btn_id) 309 { 310 LV_ASSERT_OBJ(obj, MY_CLASS); 311 312 if(btn_id == LV_BTNMATRIX_BTN_NONE) return NULL; 313 314 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj; 315 if(btn_id > btnm->btn_cnt) return NULL; 316 317 uint16_t txt_i = 0; 318 uint16_t btn_i = 0; 319 320 /*Search the text of btnm->btn_pr the buttons text in the map 321 *Skip "\n"-s*/ 322 while(btn_i != btn_id) { 323 btn_i++; 324 txt_i++; 325 if(strcmp(btnm->map_p[txt_i], "\n") == 0) txt_i++; 326 } 327 328 if(btn_i == btnm->btn_cnt) return NULL; 329 330 return btnm->map_p[txt_i]; 331 } 332 333 bool lv_btnmatrix_has_btn_ctrl(lv_obj_t * obj, uint16_t btn_id, lv_btnmatrix_ctrl_t ctrl) 334 { 335 LV_ASSERT_OBJ(obj, MY_CLASS); 336 337 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 338 if(btn_id >= btnm->btn_cnt) return false; 339 340 return ((btnm->ctrl_bits[btn_id] & ctrl) == ctrl) ? true : false; 341 } 342 343 bool lv_btnmatrix_get_one_checked(const lv_obj_t * obj) 344 { 345 LV_ASSERT_OBJ(obj, MY_CLASS); 346 347 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 348 349 return btnm->one_check; 350 } 351 352 /********************** 353 * STATIC FUNCTIONS 354 **********************/ 355 356 static void lv_btnmatrix_constructor(const lv_obj_class_t * class_p, lv_obj_t * obj) 357 { 358 LV_UNUSED(class_p); 359 LV_TRACE_OBJ_CREATE("begin"); 360 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj; 361 btnm->btn_cnt = 0; 362 btnm->row_cnt = 0; 363 btnm->btn_id_sel = LV_BTNMATRIX_BTN_NONE; 364 btnm->button_areas = NULL; 365 btnm->ctrl_bits = NULL; 366 btnm->map_p = NULL; 367 btnm->one_check = 0; 368 369 lv_btnmatrix_set_map(obj, lv_btnmatrix_def_map); 370 371 LV_TRACE_OBJ_CREATE("finished"); 372 } 373 374 static void lv_btnmatrix_destructor(const lv_obj_class_t * class_p, lv_obj_t * obj) 375 { 376 LV_TRACE_OBJ_CREATE("begin"); 377 LV_UNUSED(class_p); 378 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj; 379 lv_mem_free(btnm->button_areas); 380 lv_mem_free(btnm->ctrl_bits); 381 btnm->button_areas = NULL; 382 btnm->ctrl_bits = NULL; 383 LV_TRACE_OBJ_CREATE("finished"); 384 } 385 386 static void lv_btnmatrix_event(const lv_obj_class_t * class_p, lv_event_t * e) 387 { 388 LV_UNUSED(class_p); 389 390 lv_res_t res; 391 392 /*Call the ancestor's event handler*/ 393 res = lv_obj_event_base(MY_CLASS, e); 394 if(res != LV_RES_OK) return; 395 396 lv_event_code_t code = lv_event_get_code(e); 397 lv_obj_t * obj = lv_event_get_target(e); 398 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj; 399 lv_point_t p; 400 401 if(code == LV_EVENT_REFR_EXT_DRAW_SIZE) { 402 lv_coord_t * s = lv_event_get_param(e); 403 if(has_popovers_in_top_row(obj)) { 404 /*reserve one row worth of extra space to account for popovers in the top row*/ 405 *s = btnm->row_cnt > 0 ? lv_obj_get_content_height(obj) / btnm->row_cnt : 0; 406 } 407 else { 408 *s = 0; 409 } 410 } 411 if(code == LV_EVENT_STYLE_CHANGED) { 412 lv_btnmatrix_set_map(obj, btnm->map_p); 413 } 414 else if(code == LV_EVENT_SIZE_CHANGED) { 415 lv_btnmatrix_set_map(obj, btnm->map_p); 416 } 417 else if(code == LV_EVENT_PRESSED) { 418 void * param = lv_event_get_param(e); 419 invalidate_button_area(obj, btnm->btn_id_sel); 420 421 lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); 422 if(indev_type == LV_INDEV_TYPE_POINTER || indev_type == LV_INDEV_TYPE_BUTTON) { 423 uint16_t btn_pr; 424 /*Search the pressed area*/ 425 lv_indev_get_point(param, &p); 426 btn_pr = get_button_from_point(obj, &p); 427 /*Handle the case where there is no button there*/ 428 if(btn_pr != LV_BTNMATRIX_BTN_NONE) { 429 if(button_is_inactive(btnm->ctrl_bits[btn_pr]) == false && 430 button_is_hidden(btnm->ctrl_bits[btn_pr]) == false) { 431 btnm->btn_id_sel = btn_pr; 432 invalidate_button_area(obj, btnm->btn_id_sel); /*Invalidate the new area*/ 433 } 434 } 435 } 436 437 if(btnm->btn_id_sel != LV_BTNMATRIX_BTN_NONE) { 438 if(button_is_click_trig(btnm->ctrl_bits[btnm->btn_id_sel]) == false && 439 button_is_popover(btnm->ctrl_bits[btnm->btn_id_sel]) == false && 440 button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel]) == false && 441 button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) == false) { 442 uint32_t b = btnm->btn_id_sel; 443 res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, &b); 444 if(res != LV_RES_OK) return; 445 } 446 } 447 } 448 else if(code == LV_EVENT_PRESSING) { 449 void * param = lv_event_get_param(e); 450 uint16_t btn_pr = LV_BTNMATRIX_BTN_NONE; 451 /*Search the pressed area*/ 452 lv_indev_t * indev = lv_indev_get_act(); 453 lv_indev_type_t indev_type = lv_indev_get_type(indev); 454 if(indev_type == LV_INDEV_TYPE_ENCODER || indev_type == LV_INDEV_TYPE_KEYPAD) return; 455 456 lv_indev_get_point(indev, &p); 457 btn_pr = get_button_from_point(obj, &p); 458 /*Invalidate to old and the new areas*/ 459 if(btn_pr != btnm->btn_id_sel) { 460 if(btnm->btn_id_sel != LV_BTNMATRIX_BTN_NONE) { 461 invalidate_button_area(obj, btnm->btn_id_sel); 462 } 463 464 btnm->btn_id_sel = btn_pr; 465 466 lv_indev_reset_long_press(param); /*Start the log press time again on the new button*/ 467 if(btn_pr != LV_BTNMATRIX_BTN_NONE && 468 button_is_inactive(btnm->ctrl_bits[btn_pr]) == false && 469 button_is_hidden(btnm->ctrl_bits[btn_pr]) == false) { 470 invalidate_button_area(obj, btn_pr); 471 /*Send VALUE_CHANGED for the newly pressed button*/ 472 if(button_is_click_trig(btnm->ctrl_bits[btn_pr]) == false && 473 button_is_popover(btnm->ctrl_bits[btnm->btn_id_sel]) == false) { 474 uint32_t b = btn_pr; 475 res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, &b); 476 if(res != LV_RES_OK) return; 477 } 478 } 479 } 480 } 481 else if(code == LV_EVENT_RELEASED) { 482 if(btnm->btn_id_sel != LV_BTNMATRIX_BTN_NONE) { 483 /*Toggle the button if enabled*/ 484 if(button_is_checkable(btnm->ctrl_bits[btnm->btn_id_sel]) && 485 !button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel])) { 486 if(button_get_checked(btnm->ctrl_bits[btnm->btn_id_sel]) && !btnm->one_check) { 487 btnm->ctrl_bits[btnm->btn_id_sel] &= (~LV_BTNMATRIX_CTRL_CHECKED); 488 } 489 else { 490 btnm->ctrl_bits[btnm->btn_id_sel] |= LV_BTNMATRIX_CTRL_CHECKED; 491 } 492 if(btnm->one_check) make_one_button_checked(obj, btnm->btn_id_sel); 493 } 494 495 496 if((button_is_click_trig(btnm->ctrl_bits[btnm->btn_id_sel]) == true || 497 button_is_popover(btnm->ctrl_bits[btnm->btn_id_sel]) == true) && 498 button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel]) == false && 499 button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) == false) { 500 uint32_t b = btnm->btn_id_sel; 501 res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, &b); 502 if(res != LV_RES_OK) return; 503 } 504 } 505 506 /*Invalidate to old pressed area*/; 507 invalidate_button_area(obj, btnm->btn_id_sel); 508 509 } 510 else if(code == LV_EVENT_LONG_PRESSED_REPEAT) { 511 if(btnm->btn_id_sel != LV_BTNMATRIX_BTN_NONE) { 512 if(button_is_repeat_disabled(btnm->ctrl_bits[btnm->btn_id_sel]) == false && 513 button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel]) == false && 514 button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) == false) { 515 uint32_t b = btnm->btn_id_sel; 516 res = lv_event_send(obj, LV_EVENT_VALUE_CHANGED, &b); 517 if(res != LV_RES_OK) return; 518 } 519 } 520 } 521 else if(code == LV_EVENT_PRESS_LOST) { 522 invalidate_button_area(obj, btnm->btn_id_sel); 523 btnm->btn_id_sel = LV_BTNMATRIX_BTN_NONE; 524 } 525 else if(code == LV_EVENT_FOCUSED) { 526 lv_indev_t * indev = lv_event_get_param(e); 527 lv_indev_type_t indev_type = lv_indev_get_type(indev); 528 529 /*If not focused by an input device assume the last input device*/ 530 if(indev == NULL) { 531 indev = lv_indev_get_next(NULL); 532 indev_type = lv_indev_get_type(indev); 533 } 534 535 bool editing = lv_group_get_editing(lv_obj_get_group(obj)); 536 /*Focus the first button if there is not selected button*/ 537 if(btnm->btn_id_sel == LV_BTNMATRIX_BTN_NONE) { 538 if(indev_type == LV_INDEV_TYPE_KEYPAD || (indev_type == LV_INDEV_TYPE_ENCODER && editing)) { 539 uint32_t b = 0; 540 if(btnm->one_check) { 541 while(button_is_hidden(btnm->ctrl_bits[b]) || button_is_inactive(btnm->ctrl_bits[b]) || 542 button_is_checked(btnm->ctrl_bits[b]) == false) b++; 543 } 544 else { 545 while(button_is_hidden(btnm->ctrl_bits[b]) || button_is_inactive(btnm->ctrl_bits[b])) b++; 546 } 547 548 btnm->btn_id_sel = b; 549 } 550 else { 551 btnm->btn_id_sel = LV_BTNMATRIX_BTN_NONE; 552 } 553 } 554 } 555 else if(code == LV_EVENT_DEFOCUSED || code == LV_EVENT_LEAVE) { 556 if(btnm->btn_id_sel != LV_BTNMATRIX_BTN_NONE) invalidate_button_area(obj, btnm->btn_id_sel); 557 btnm->btn_id_sel = LV_BTNMATRIX_BTN_NONE; 558 } 559 else if(code == LV_EVENT_KEY) { 560 561 invalidate_button_area(obj, btnm->btn_id_sel); 562 563 char c = *((char *)lv_event_get_param(e)); 564 if(c == LV_KEY_RIGHT) { 565 if(btnm->btn_id_sel == LV_BTNMATRIX_BTN_NONE) btnm->btn_id_sel = 0; 566 else btnm->btn_id_sel++; 567 if(btnm->btn_id_sel >= btnm->btn_cnt) btnm->btn_id_sel = 0; 568 569 while(button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) || button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel])) { 570 btnm->btn_id_sel++; 571 if(btnm->btn_id_sel >= btnm->btn_cnt) btnm->btn_id_sel = 0; 572 } 573 } 574 else if(c == LV_KEY_LEFT) { 575 if(btnm->btn_id_sel == LV_BTNMATRIX_BTN_NONE) btnm->btn_id_sel = 0; 576 577 if(btnm->btn_id_sel == 0) btnm->btn_id_sel = btnm->btn_cnt - 1; 578 else if(btnm->btn_id_sel > 0) btnm->btn_id_sel--; 579 580 while(button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) || button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel])) { 581 if(btnm->btn_id_sel > 0) btnm->btn_id_sel--; 582 else btnm->btn_id_sel = btnm->btn_cnt - 1; 583 } 584 } 585 else if(c == LV_KEY_DOWN) { 586 lv_coord_t col_gap = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); 587 /*Find the area below the current*/ 588 if(btnm->btn_id_sel == LV_BTNMATRIX_BTN_NONE) { 589 btnm->btn_id_sel = 0; 590 while(button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) || button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel])) { 591 btnm->btn_id_sel++; 592 if(btnm->btn_id_sel >= btnm->btn_cnt) btnm->btn_id_sel = 0; 593 } 594 } 595 else { 596 uint16_t area_below; 597 lv_coord_t pr_center = 598 btnm->button_areas[btnm->btn_id_sel].x1 + (lv_area_get_width(&btnm->button_areas[btnm->btn_id_sel]) >> 1); 599 600 for(area_below = btnm->btn_id_sel; area_below < btnm->btn_cnt; area_below++) { 601 if(btnm->button_areas[area_below].y1 > btnm->button_areas[btnm->btn_id_sel].y1 && 602 pr_center >= btnm->button_areas[area_below].x1 && 603 pr_center <= btnm->button_areas[area_below].x2 + col_gap && 604 button_is_inactive(btnm->ctrl_bits[area_below]) == false && 605 button_is_hidden(btnm->ctrl_bits[area_below]) == false) { 606 break; 607 } 608 } 609 610 if(area_below < btnm->btn_cnt) btnm->btn_id_sel = area_below; 611 } 612 } 613 else if(c == LV_KEY_UP) { 614 lv_coord_t col_gap = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); 615 /*Find the area below the current*/ 616 if(btnm->btn_id_sel == LV_BTNMATRIX_BTN_NONE) { 617 btnm->btn_id_sel = 0; 618 while(button_is_hidden(btnm->ctrl_bits[btnm->btn_id_sel]) || button_is_inactive(btnm->ctrl_bits[btnm->btn_id_sel])) { 619 btnm->btn_id_sel++; 620 if(btnm->btn_id_sel >= btnm->btn_cnt) btnm->btn_id_sel = 0; 621 } 622 } 623 else { 624 int16_t area_above; 625 lv_coord_t pr_center = 626 btnm->button_areas[btnm->btn_id_sel].x1 + (lv_area_get_width(&btnm->button_areas[btnm->btn_id_sel]) >> 1); 627 628 for(area_above = btnm->btn_id_sel; area_above >= 0; area_above--) { 629 if(btnm->button_areas[area_above].y1 < btnm->button_areas[btnm->btn_id_sel].y1 && 630 pr_center >= btnm->button_areas[area_above].x1 - col_gap && 631 pr_center <= btnm->button_areas[area_above].x2 && 632 button_is_inactive(btnm->ctrl_bits[area_above]) == false && 633 button_is_hidden(btnm->ctrl_bits[area_above]) == false) { 634 break; 635 } 636 } 637 if(area_above >= 0) btnm->btn_id_sel = area_above; 638 } 639 } 640 641 invalidate_button_area(obj, btnm->btn_id_sel); 642 } 643 else if(code == LV_EVENT_DRAW_MAIN) { 644 draw_main(e); 645 } 646 647 } 648 649 static void draw_main(lv_event_t * e) 650 { 651 lv_obj_t * obj = lv_event_get_target(e); 652 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj; 653 if(btnm->btn_cnt == 0) return; 654 655 lv_draw_ctx_t * draw_ctx = lv_event_get_draw_ctx(e); 656 obj->skip_trans = 1; 657 658 lv_area_t area_obj; 659 lv_obj_get_coords(obj, &area_obj); 660 661 lv_area_t btn_area; 662 663 uint16_t btn_i = 0; 664 uint16_t txt_i = 0; 665 666 lv_draw_rect_dsc_t draw_rect_dsc_act; 667 lv_draw_label_dsc_t draw_label_dsc_act; 668 669 lv_draw_rect_dsc_t draw_rect_dsc_def; 670 lv_draw_label_dsc_t draw_label_dsc_def; 671 672 lv_state_t state_ori = obj->state; 673 obj->state = LV_STATE_DEFAULT; 674 obj->skip_trans = 1; 675 lv_draw_rect_dsc_init(&draw_rect_dsc_def); 676 lv_draw_label_dsc_init(&draw_label_dsc_def); 677 lv_obj_init_draw_rect_dsc(obj, LV_PART_ITEMS, &draw_rect_dsc_def); 678 lv_obj_init_draw_label_dsc(obj, LV_PART_ITEMS, &draw_label_dsc_def); 679 obj->skip_trans = 0; 680 obj->state = state_ori; 681 682 lv_coord_t ptop = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); 683 lv_coord_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); 684 lv_coord_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); 685 lv_coord_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); 686 687 #if LV_USE_ARABIC_PERSIAN_CHARS 688 const size_t txt_ap_size = 256 ; 689 char * txt_ap = lv_mem_buf_get(txt_ap_size); 690 #endif 691 692 lv_obj_draw_part_dsc_t part_draw_dsc; 693 lv_obj_draw_dsc_init(&part_draw_dsc, draw_ctx); 694 part_draw_dsc.part = LV_PART_ITEMS; 695 part_draw_dsc.class_p = MY_CLASS; 696 part_draw_dsc.type = LV_BTNMATRIX_DRAW_PART_BTN; 697 part_draw_dsc.rect_dsc = &draw_rect_dsc_act; 698 part_draw_dsc.label_dsc = &draw_label_dsc_act; 699 700 for(btn_i = 0; btn_i < btnm->btn_cnt; btn_i++, txt_i++) { 701 /*Search the next valid text in the map*/ 702 while(strcmp(btnm->map_p[txt_i], "\n") == 0) { 703 txt_i++; 704 } 705 706 /*Skip hidden buttons*/ 707 if(button_is_hidden(btnm->ctrl_bits[btn_i])) continue; 708 709 /*Get the state of the button*/ 710 lv_state_t btn_state = LV_STATE_DEFAULT; 711 if(button_get_checked(btnm->ctrl_bits[btn_i])) btn_state |= LV_STATE_CHECKED; 712 713 if(button_is_inactive(btnm->ctrl_bits[btn_i])) btn_state |= LV_STATE_DISABLED; 714 else if(btn_i == btnm->btn_id_sel) { 715 if(state_ori & LV_STATE_PRESSED) btn_state |= LV_STATE_PRESSED; 716 if(state_ori & LV_STATE_FOCUSED) btn_state |= LV_STATE_FOCUSED; 717 if(state_ori & LV_STATE_FOCUS_KEY) btn_state |= LV_STATE_FOCUS_KEY; 718 if(state_ori & LV_STATE_EDITED) btn_state |= LV_STATE_EDITED; 719 } 720 721 /*Get the button's area*/ 722 lv_area_copy(&btn_area, &btnm->button_areas[btn_i]); 723 btn_area.x1 += area_obj.x1; 724 btn_area.y1 += area_obj.y1; 725 btn_area.x2 += area_obj.x1; 726 btn_area.y2 += area_obj.y1; 727 728 /*Set up the draw descriptors*/ 729 if(btn_state == LV_STATE_DEFAULT) { 730 lv_memcpy(&draw_rect_dsc_act, &draw_rect_dsc_def, sizeof(lv_draw_rect_dsc_t)); 731 lv_memcpy(&draw_label_dsc_act, &draw_label_dsc_def, sizeof(lv_draw_label_dsc_t)); 732 } 733 /*In other cases get the styles directly without caching them*/ 734 else { 735 obj->state = btn_state; 736 obj->skip_trans = 1; 737 lv_draw_rect_dsc_init(&draw_rect_dsc_act); 738 lv_draw_label_dsc_init(&draw_label_dsc_act); 739 lv_obj_init_draw_rect_dsc(obj, LV_PART_ITEMS, &draw_rect_dsc_act); 740 lv_obj_init_draw_label_dsc(obj, LV_PART_ITEMS, &draw_label_dsc_act); 741 obj->state = state_ori; 742 obj->skip_trans = 0; 743 } 744 745 bool recolor = button_is_recolor(btnm->ctrl_bits[btn_i]); 746 if(recolor) draw_label_dsc_act.flag |= LV_TEXT_FLAG_RECOLOR; 747 else draw_label_dsc_act.flag &= ~LV_TEXT_FLAG_RECOLOR; 748 749 750 part_draw_dsc.draw_area = &btn_area; 751 part_draw_dsc.id = btn_i; 752 lv_event_send(obj, LV_EVENT_DRAW_PART_BEGIN, &part_draw_dsc); 753 754 /*Remove borders on the edges if `LV_BORDER_SIDE_INTERNAL`*/ 755 if(draw_rect_dsc_act.border_side & LV_BORDER_SIDE_INTERNAL) { 756 draw_rect_dsc_act.border_side = LV_BORDER_SIDE_FULL; 757 if(btn_area.x1 == obj->coords.x1 + pleft) draw_rect_dsc_act.border_side &= ~LV_BORDER_SIDE_LEFT; 758 if(btn_area.x2 == obj->coords.x2 - pright) draw_rect_dsc_act.border_side &= ~LV_BORDER_SIDE_RIGHT; 759 if(btn_area.y1 == obj->coords.y1 + ptop) draw_rect_dsc_act.border_side &= ~LV_BORDER_SIDE_TOP; 760 if(btn_area.y2 == obj->coords.y2 - pbottom) draw_rect_dsc_act.border_side &= ~LV_BORDER_SIDE_BOTTOM; 761 } 762 763 lv_coord_t btn_height = lv_area_get_height(&btn_area); 764 765 if((btn_state & LV_STATE_PRESSED) && (btnm->ctrl_bits[btn_i] & LV_BTNMATRIX_CTRL_POPOVER)) { 766 /*Push up the upper boundary of the btn area to create the popover*/ 767 btn_area.y1 -= btn_height; 768 } 769 770 /*Draw the background*/ 771 lv_draw_rect(draw_ctx, &draw_rect_dsc_act, &btn_area); 772 773 /*Calculate the size of the text*/ 774 const lv_font_t * font = draw_label_dsc_act.font; 775 lv_coord_t letter_space = draw_label_dsc_act.letter_space; 776 lv_coord_t line_space = draw_label_dsc_act.line_space; 777 const char * txt = btnm->map_p[txt_i]; 778 779 #if LV_USE_ARABIC_PERSIAN_CHARS 780 /*Get the size of the Arabic text and process it*/ 781 size_t len_ap = _lv_txt_ap_calc_bytes_cnt(txt); 782 if(len_ap < txt_ap_size) { 783 _lv_txt_ap_proc(txt, txt_ap); 784 txt = txt_ap; 785 } 786 #endif 787 lv_point_t txt_size; 788 lv_txt_get_size(&txt_size, txt, font, letter_space, 789 line_space, lv_area_get_width(&area_obj), draw_label_dsc_act.flag); 790 791 btn_area.x1 += (lv_area_get_width(&btn_area) - txt_size.x) / 2; 792 btn_area.y1 += (lv_area_get_height(&btn_area) - txt_size.y) / 2; 793 btn_area.x2 = btn_area.x1 + txt_size.x; 794 btn_area.y2 = btn_area.y1 + txt_size.y; 795 796 if((btn_state & LV_STATE_PRESSED) && (btnm->ctrl_bits[btn_i] & LV_BTNMATRIX_CTRL_POPOVER)) { 797 /*Push up the button text into the popover*/ 798 btn_area.y1 -= btn_height / 2; 799 btn_area.y2 -= btn_height / 2; 800 } 801 802 /*Draw the text*/ 803 lv_draw_label(draw_ctx, &draw_label_dsc_act, &btn_area, txt, NULL); 804 805 lv_event_send(obj, LV_EVENT_DRAW_PART_END, &part_draw_dsc); 806 } 807 808 obj->skip_trans = 0; 809 #if LV_USE_ARABIC_PERSIAN_CHARS 810 lv_mem_buf_release(txt_ap); 811 #endif 812 } 813 /** 814 * Create the required number of buttons and control bytes according to a map 815 * @param obj pointer to button matrix object 816 * @param map_p pointer to a string array 817 */ 818 static void allocate_btn_areas_and_controls(const lv_obj_t * obj, const char ** map) 819 { 820 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj; 821 btnm->row_cnt = 1; 822 /*Count the buttons in the map*/ 823 uint16_t btn_cnt = 0; 824 uint16_t i = 0; 825 while(map[i] && map[i][0] != '\0') { 826 if(strcmp(map[i], "\n") != 0) { /*Do not count line breaks*/ 827 btn_cnt++; 828 } 829 else { 830 btnm->row_cnt++; 831 } 832 i++; 833 } 834 835 /*Do not allocate memory for the same amount of buttons*/ 836 if(btn_cnt == btnm->btn_cnt) return; 837 838 if(btnm->button_areas != NULL) { 839 lv_mem_free(btnm->button_areas); 840 btnm->button_areas = NULL; 841 } 842 if(btnm->ctrl_bits != NULL) { 843 lv_mem_free(btnm->ctrl_bits); 844 btnm->ctrl_bits = NULL; 845 } 846 847 btnm->button_areas = lv_mem_alloc(sizeof(lv_area_t) * btn_cnt); 848 LV_ASSERT_MALLOC(btnm->button_areas); 849 btnm->ctrl_bits = lv_mem_alloc(sizeof(lv_btnmatrix_ctrl_t) * btn_cnt); 850 LV_ASSERT_MALLOC(btnm->ctrl_bits); 851 if(btnm->button_areas == NULL || btnm->ctrl_bits == NULL) btn_cnt = 0; 852 853 lv_memset_00(btnm->ctrl_bits, sizeof(lv_btnmatrix_ctrl_t) * btn_cnt); 854 855 btnm->btn_cnt = btn_cnt; 856 } 857 858 /** 859 * Get the width of a button in units (default is 1). 860 * @param ctrl_bits least significant 3 bits used (1..7 valid values) 861 * @return the width of the button in units 862 */ 863 static uint8_t get_button_width(lv_btnmatrix_ctrl_t ctrl_bits) 864 { 865 uint8_t w = ctrl_bits & LV_BTNMATRIX_WIDTH_MASK; 866 return w != 0 ? w : 1; 867 } 868 869 static bool button_is_hidden(lv_btnmatrix_ctrl_t ctrl_bits) 870 { 871 return (ctrl_bits & LV_BTNMATRIX_CTRL_HIDDEN) ? true : false; 872 } 873 874 static bool button_is_checked(lv_btnmatrix_ctrl_t ctrl_bits) 875 { 876 return (ctrl_bits & LV_BTNMATRIX_CTRL_CHECKED) ? true : false; 877 } 878 879 static bool button_is_repeat_disabled(lv_btnmatrix_ctrl_t ctrl_bits) 880 { 881 return (ctrl_bits & LV_BTNMATRIX_CTRL_NO_REPEAT) ? true : false; 882 } 883 884 static bool button_is_inactive(lv_btnmatrix_ctrl_t ctrl_bits) 885 { 886 return (ctrl_bits & LV_BTNMATRIX_CTRL_DISABLED) ? true : false; 887 } 888 889 static bool button_is_click_trig(lv_btnmatrix_ctrl_t ctrl_bits) 890 { 891 return (ctrl_bits & LV_BTNMATRIX_CTRL_CLICK_TRIG) ? true : false; 892 } 893 894 static bool button_is_popover(lv_btnmatrix_ctrl_t ctrl_bits) 895 { 896 return (ctrl_bits & LV_BTNMATRIX_CTRL_POPOVER) ? true : false; 897 } 898 899 static bool button_is_checkable(lv_btnmatrix_ctrl_t ctrl_bits) 900 { 901 return (ctrl_bits & LV_BTNMATRIX_CTRL_CHECKABLE) ? true : false; 902 } 903 904 static bool button_get_checked(lv_btnmatrix_ctrl_t ctrl_bits) 905 { 906 return (ctrl_bits & LV_BTNMATRIX_CTRL_CHECKED) ? true : false; 907 } 908 909 static bool button_is_recolor(lv_btnmatrix_ctrl_t ctrl_bits) 910 { 911 return (ctrl_bits & LV_BTNMATRIX_CTRL_RECOLOR) ? true : false; 912 } 913 /** 914 * Gives the button id of a button under a given point 915 * @param obj pointer to a button matrix object 916 * @param p a point with absolute coordinates 917 * @return the id of the button or LV_BTNMATRIX_BTN_NONE. 918 */ 919 static uint16_t get_button_from_point(lv_obj_t * obj, lv_point_t * p) 920 { 921 lv_area_t obj_cords; 922 lv_area_t btn_area; 923 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 924 uint16_t i; 925 lv_obj_get_coords(obj, &obj_cords); 926 927 lv_coord_t w = lv_obj_get_width(obj); 928 lv_coord_t h = lv_obj_get_height(obj); 929 lv_coord_t pleft = lv_obj_get_style_pad_left(obj, LV_PART_MAIN); 930 lv_coord_t pright = lv_obj_get_style_pad_right(obj, LV_PART_MAIN); 931 lv_coord_t ptop = lv_obj_get_style_pad_top(obj, LV_PART_MAIN); 932 lv_coord_t pbottom = lv_obj_get_style_pad_bottom(obj, LV_PART_MAIN); 933 lv_coord_t prow = lv_obj_get_style_pad_row(obj, LV_PART_MAIN); 934 lv_coord_t pcol = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); 935 936 /*Get the half gap. Button look larger with this value. (+1 for rounding error)*/ 937 prow = (prow / 2) + 1 + (prow & 1); 938 pcol = (pcol / 2) + 1 + (pcol & 1); 939 940 prow = LV_MIN(prow, BTN_EXTRA_CLICK_AREA_MAX); 941 pcol = LV_MIN(pcol, BTN_EXTRA_CLICK_AREA_MAX); 942 pright = LV_MIN(pright, BTN_EXTRA_CLICK_AREA_MAX); 943 ptop = LV_MIN(ptop, BTN_EXTRA_CLICK_AREA_MAX); 944 pbottom = LV_MIN(pbottom, BTN_EXTRA_CLICK_AREA_MAX); 945 946 for(i = 0; i < btnm->btn_cnt; i++) { 947 lv_area_copy(&btn_area, &btnm->button_areas[i]); 948 if(btn_area.x1 <= pleft) btn_area.x1 += obj_cords.x1 - LV_MIN(pleft, BTN_EXTRA_CLICK_AREA_MAX); 949 else btn_area.x1 += obj_cords.x1 - pcol; 950 951 if(btn_area.y1 <= ptop) btn_area.y1 += obj_cords.y1 - LV_MIN(ptop, BTN_EXTRA_CLICK_AREA_MAX); 952 else btn_area.y1 += obj_cords.y1 - prow; 953 954 if(btn_area.x2 >= w - pright - 2) btn_area.x2 += obj_cords.x1 + LV_MIN(pright, 955 BTN_EXTRA_CLICK_AREA_MAX); /*-2 for rounding error*/ 956 else btn_area.x2 += obj_cords.x1 + pcol; 957 958 if(btn_area.y2 >= h - pbottom - 2) btn_area.y2 += obj_cords.y1 + LV_MIN(pbottom, 959 BTN_EXTRA_CLICK_AREA_MAX); /*-2 for rounding error*/ 960 else btn_area.y2 += obj_cords.y1 + prow; 961 962 if(_lv_area_is_point_on(&btn_area, p, 0) != false) { 963 break; 964 } 965 } 966 967 if(i == btnm->btn_cnt) i = LV_BTNMATRIX_BTN_NONE; 968 969 return i; 970 } 971 972 static void invalidate_button_area(const lv_obj_t * obj, uint16_t btn_idx) 973 { 974 if(btn_idx == LV_BTNMATRIX_BTN_NONE) return; 975 976 lv_area_t btn_area; 977 lv_area_t obj_area; 978 979 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj;; 980 if(btn_idx >= btnm->btn_cnt) return; 981 982 lv_area_copy(&btn_area, &btnm->button_areas[btn_idx]); 983 lv_obj_get_coords(obj, &obj_area); 984 985 /*The buttons might have outline and shadow so make the invalidation larger with the gaps between the buttons. 986 *It assumes that the outline or shadow is smaller than the gaps*/ 987 lv_coord_t row_gap = lv_obj_get_style_pad_row(obj, LV_PART_MAIN); 988 lv_coord_t col_gap = lv_obj_get_style_pad_column(obj, LV_PART_MAIN); 989 990 /*Be sure to have a minimal extra space if row/col_gap is small*/ 991 lv_coord_t dpi = lv_disp_get_dpi(lv_obj_get_disp(obj)); 992 row_gap = LV_MAX(row_gap, dpi / 10); 993 col_gap = LV_MAX(col_gap, dpi / 10); 994 995 /*Convert relative coordinates to absolute*/ 996 btn_area.x1 += obj_area.x1 - row_gap; 997 btn_area.y1 += obj_area.y1 - col_gap; 998 btn_area.x2 += obj_area.x1 + row_gap; 999 btn_area.y2 += obj_area.y1 + col_gap; 1000 1001 if((btn_idx == btnm->btn_id_sel) && (btnm->ctrl_bits[btn_idx] & LV_BTNMATRIX_CTRL_POPOVER)) { 1002 /*Push up the upper boundary of the btn area to also invalidate the popover*/ 1003 btn_area.y1 -= lv_area_get_height(&btn_area); 1004 } 1005 1006 lv_obj_invalidate_area(obj, &btn_area); 1007 } 1008 1009 /** 1010 * Enforces a single button being toggled on the button matrix. 1011 * It simply clears the toggle flag on other buttons. 1012 * @param obj Button matrix object 1013 * @param btn_idx Button that should remain toggled 1014 */ 1015 static void make_one_button_checked(lv_obj_t * obj, uint16_t btn_idx) 1016 { 1017 /*Save whether the button was toggled*/ 1018 bool was_toggled = lv_btnmatrix_has_btn_ctrl(obj, btn_idx, LV_BTNMATRIX_CTRL_CHECKED); 1019 1020 lv_btnmatrix_clear_btn_ctrl_all(obj, LV_BTNMATRIX_CTRL_CHECKED); 1021 1022 if(was_toggled) lv_btnmatrix_set_btn_ctrl(obj, btn_idx, LV_BTNMATRIX_CTRL_CHECKED); 1023 } 1024 1025 /** 1026 * Check if any of the buttons in the first row has the LV_BTNMATRIX_CTRL_POPOVER control flag set. 1027 * @param obj Button matrix object 1028 * @return true if at least one button has the flag, false otherwise 1029 */ 1030 static bool has_popovers_in_top_row(lv_obj_t * obj) 1031 { 1032 lv_btnmatrix_t * btnm = (lv_btnmatrix_t *)obj; 1033 1034 if(btnm->row_cnt <= 0) { 1035 return false; 1036 } 1037 1038 const char ** map_row = btnm->map_p; 1039 uint16_t btn_cnt = 0; 1040 1041 while(map_row[btn_cnt] && strcmp(map_row[btn_cnt], "\n") != 0 && map_row[btn_cnt][0] != '\0') { 1042 if(button_is_popover(btnm->ctrl_bits[btn_cnt])) { 1043 return true; 1044 } 1045 btn_cnt++; 1046 } 1047 1048 return false; 1049 } 1050 1051 #endif