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