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
lv_btnmatrix_create(lv_obj_t * parent)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
lv_btnmatrix_set_map(lv_obj_t * obj,const char * map[])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
lv_btnmatrix_set_ctrl_map(lv_obj_t * obj,const lv_btnmatrix_ctrl_t ctrl_map[])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
lv_btnmatrix_set_selected_btn(lv_obj_t * obj,uint16_t btn_id)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
lv_btnmatrix_set_btn_ctrl(lv_obj_t * obj,uint16_t btn_id,lv_btnmatrix_ctrl_t ctrl)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
lv_btnmatrix_clear_btn_ctrl(lv_obj_t * obj,uint16_t btn_id,lv_btnmatrix_ctrl_t ctrl)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
lv_btnmatrix_set_btn_ctrl_all(lv_obj_t * obj,lv_btnmatrix_ctrl_t ctrl)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
lv_btnmatrix_clear_btn_ctrl_all(lv_obj_t * obj,lv_btnmatrix_ctrl_t ctrl)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
lv_btnmatrix_set_btn_width(lv_obj_t * obj,uint16_t btn_id,uint8_t width)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
lv_btnmatrix_set_one_checked(lv_obj_t * obj,bool en)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
lv_btnmatrix_get_map(const lv_obj_t * obj)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
lv_btnmatrix_get_selected_btn(const lv_obj_t * obj)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
lv_btnmatrix_get_btn_text(const lv_obj_t * obj,uint16_t btn_id)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
lv_btnmatrix_has_btn_ctrl(lv_obj_t * obj,uint16_t btn_id,lv_btnmatrix_ctrl_t ctrl)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
lv_btnmatrix_get_one_checked(const lv_obj_t * obj)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
lv_btnmatrix_constructor(const lv_obj_class_t * class_p,lv_obj_t * obj)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
lv_btnmatrix_destructor(const lv_obj_class_t * class_p,lv_obj_t * obj)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
lv_btnmatrix_event(const lv_obj_class_t * class_p,lv_event_t * e)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
draw_main(lv_event_t * e)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 */
allocate_btn_areas_and_controls(const lv_obj_t * obj,const char ** map)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 */
get_button_width(lv_btnmatrix_ctrl_t ctrl_bits)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
button_is_hidden(lv_btnmatrix_ctrl_t ctrl_bits)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
button_is_checked(lv_btnmatrix_ctrl_t ctrl_bits)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
button_is_repeat_disabled(lv_btnmatrix_ctrl_t ctrl_bits)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
button_is_inactive(lv_btnmatrix_ctrl_t ctrl_bits)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
button_is_click_trig(lv_btnmatrix_ctrl_t ctrl_bits)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
button_is_popover(lv_btnmatrix_ctrl_t ctrl_bits)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
button_is_checkable(lv_btnmatrix_ctrl_t ctrl_bits)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
button_get_checked(lv_btnmatrix_ctrl_t ctrl_bits)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
button_is_recolor(lv_btnmatrix_ctrl_t ctrl_bits)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 */
get_button_from_point(lv_obj_t * obj,lv_point_t * p)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
invalidate_button_area(const lv_obj_t * obj,uint16_t btn_idx)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 */
make_one_button_checked(lv_obj_t * obj,uint16_t btn_idx)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 */
has_popovers_in_top_row(lv_obj_t * obj)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
1052