Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/libs/lvgl/lv_objx/lv_btnm.c
1476 views
1
/**
2
* @file lv_btnm.c
3
*
4
*/
5
6
/*********************
7
* INCLUDES
8
*********************/
9
#include "lv_btnm.h"
10
#if USE_LV_BTNM != 0
11
12
#include "../lv_core/lv_group.h"
13
#include "../lv_draw/lv_draw.h"
14
#include "../lv_core/lv_refr.h"
15
#include "../lv_themes/lv_theme.h"
16
#include "../lv_misc/lv_txt.h"
17
18
/*********************
19
* DEFINES
20
*********************/
21
22
/**********************
23
* TYPEDEFS
24
**********************/
25
26
/**********************
27
* STATIC PROTOTYPES
28
**********************/
29
static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param);
30
static bool lv_btnm_design(lv_obj_t * btnm, const lv_area_t * mask, lv_design_mode_t mode);
31
static uint8_t get_button_width(const char * btn_str);
32
static bool button_is_hidden(const char * btn_str);
33
static bool button_is_repeat_disabled(const char * btn_str);
34
static bool button_is_inactive(const char * btn_str);
35
const char * cut_ctrl_byte(const char * btn_str);
36
static uint16_t get_button_from_point(lv_obj_t * btnm, lv_point_t * p);
37
static uint16_t get_button_text(lv_obj_t * btnm, uint16_t btn_id);
38
static void allocate_btn_areas(lv_obj_t * btnm, const char ** map);
39
40
/**********************
41
* STATIC VARIABLES
42
**********************/
43
static const char * lv_btnm_def_map[] = {"Btn1", "Btn2", "Btn3", "\n",
44
"\002Btn4", "Btn5", ""
45
};
46
47
static lv_design_func_t ancestor_design_f;
48
static lv_signal_func_t ancestor_signal;
49
50
/**********************
51
* MACROS
52
**********************/
53
54
/**********************
55
* GLOBAL FUNCTIONS
56
**********************/
57
58
/**
59
* Create a button matrix objects
60
* @param par pointer to an object, it will be the parent of the new button matrix
61
* @param copy pointer to a button matrix object, if not NULL then the new object will be copied from it
62
* @return pointer to the created button matrix
63
*/
64
lv_obj_t * lv_btnm_create(lv_obj_t * par, const lv_obj_t * copy)
65
{
66
LV_LOG_TRACE("button matrix create started");
67
68
/*Create the ancestor object*/
69
lv_obj_t * new_btnm = lv_obj_create(par, copy);
70
lv_mem_assert(new_btnm);
71
if(new_btnm == NULL) return NULL;
72
73
if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_func(new_btnm);
74
75
/*Allocate the object type specific extended data*/
76
lv_btnm_ext_t * ext = lv_obj_allocate_ext_attr(new_btnm, sizeof(lv_btnm_ext_t));
77
lv_mem_assert(ext);
78
if(ext == NULL) return NULL;
79
80
ext->btn_cnt = 0;
81
ext->btn_id_pr = LV_BTNM_PR_NONE;
82
ext->btn_id_tgl = LV_BTNM_PR_NONE;
83
ext->button_areas = NULL;
84
ext->action = NULL;
85
ext->map_p = NULL;
86
ext->toggle = 0;
87
ext->recolor = 0;
88
ext->styles_btn[LV_BTN_STATE_REL] = &lv_style_btn_rel;
89
ext->styles_btn[LV_BTN_STATE_PR] = &lv_style_btn_pr;
90
ext->styles_btn[LV_BTN_STATE_TGL_REL] = &lv_style_btn_tgl_rel;
91
ext->styles_btn[LV_BTN_STATE_TGL_PR] = &lv_style_btn_tgl_pr;
92
ext->styles_btn[LV_BTN_STATE_INA] = &lv_style_btn_ina;
93
94
if(ancestor_design_f == NULL) ancestor_design_f = lv_obj_get_design_func(new_btnm);
95
96
lv_obj_set_signal_func(new_btnm, lv_btnm_signal);
97
lv_obj_set_design_func(new_btnm, lv_btnm_design);
98
99
/*Init the new button matrix object*/
100
if(copy == NULL) {
101
lv_obj_set_size(new_btnm, LV_HOR_RES / 2, LV_VER_RES / 4);
102
lv_btnm_set_map(new_btnm, lv_btnm_def_map);
103
104
/*Set the default styles*/
105
lv_theme_t * th = lv_theme_get_current();
106
if(th) {
107
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BG, th->btnm.bg);
108
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_REL, th->btnm.btn.rel);
109
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_PR, th->btnm.btn.pr);
110
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_TGL_REL, th->btnm.btn.tgl_rel);
111
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_TGL_PR, th->btnm.btn.tgl_pr);
112
lv_btnm_set_style(new_btnm, LV_BTNM_STYLE_BTN_INA, th->btnm.btn.ina);
113
} else {
114
lv_obj_set_style(new_btnm, &lv_style_pretty);
115
}
116
}
117
/*Copy an existing object*/
118
else {
119
lv_btnm_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
120
memcpy(ext->styles_btn, copy_ext->styles_btn, sizeof(ext->styles_btn));
121
ext->action = copy_ext->action;
122
ext->toggle = copy_ext->toggle;
123
ext->btn_id_tgl = copy_ext->btn_id_tgl;
124
lv_btnm_set_map(new_btnm, lv_btnm_get_map(copy));
125
}
126
127
LV_LOG_INFO("button matrix created");
128
129
return new_btnm;
130
}
131
132
/*=====================
133
* Setter functions
134
*====================*/
135
136
/**
137
* Set a new map. Buttons will be created/deleted according to the map.
138
* @param btnm pointer to a button matrix object
139
* @param map pointer a string array. The last string has to be: "".
140
* Use "\n" to begin a new line.
141
* The first byte can be a control data:
142
* - bit 7: always 1
143
* - bit 6: always 0
144
* - bit 5: inactive (disabled) (\24x)
145
* - bit 4: no repeat (on long press) (\22x)
146
* - bit 3: hidden (\21x)
147
* - bit 2..0: button relative width
148
* Example (practically use octal numbers): "\224abc": "abc" text with 4 width and no long press
149
*/
150
void lv_btnm_set_map(lv_obj_t * btnm, const char ** map)
151
{
152
if(map == NULL) return;
153
154
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
155
ext->map_p = map;
156
157
/*Analyze the map and create the required number of buttons*/
158
allocate_btn_areas(btnm, map);
159
160
/*Set size and positions of the buttons*/
161
lv_style_t * style_bg = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BG);
162
lv_coord_t max_w = lv_obj_get_width(btnm) - 2 * style_bg->body.padding.hor;
163
lv_coord_t max_h = lv_obj_get_height(btnm) - 2 * style_bg->body.padding.ver;
164
lv_coord_t act_y = style_bg->body.padding.ver;
165
166
/*Count the lines to calculate button height*/
167
uint8_t line_cnt = 1;
168
uint8_t li;
169
for(li = 0; strlen(map[li]) != 0; li++) {
170
if(strcmp(map[li], "\n") == 0) line_cnt ++;
171
}
172
173
lv_coord_t btn_h = max_h - ((line_cnt - 1) * style_bg->body.padding.inner);
174
btn_h = btn_h / line_cnt;
175
btn_h --; /*-1 because e.g. height = 100 means 101 pixels (0..100)*/
176
177
/* Count the units and the buttons in a line
178
* (A button can be 1,2,3... unit wide)*/
179
uint16_t unit_cnt; /*Number of units in a row*/
180
uint16_t unit_act_cnt; /*Number of units currently put in a row*/
181
uint16_t btn_cnt; /*Number of buttons in a row*/
182
uint16_t i_tot = 0; /*Act. index in the str map*/
183
uint16_t btn_i = 0; /*Act. index of button areas*/
184
const char ** map_p_tmp = map;
185
186
/*Count the units and the buttons in a line*/
187
while(1) {
188
unit_cnt = 0;
189
btn_cnt = 0;
190
/*Count the buttons in a line*/
191
while(strcmp(map_p_tmp[btn_cnt], "\n") != 0 &&
192
strlen(map_p_tmp[btn_cnt]) != 0) { /*Check a line*/
193
unit_cnt += get_button_width(map_p_tmp[btn_cnt]);
194
btn_cnt ++;
195
}
196
197
/*Make sure the last row is at the bottom of 'btnm'*/
198
if(map_p_tmp[btn_cnt][0] == '\0') { /*Last row?*/
199
btn_h = max_h - act_y + style_bg->body.padding.ver - 1;
200
}
201
202
/*Only deal with the non empty lines*/
203
if(btn_cnt != 0) {
204
/*Calculate the width of all units*/
205
lv_coord_t all_unit_w = max_w - ((btn_cnt - 1) * style_bg->body.padding.inner);
206
207
/*Set the button size and positions and set the texts*/
208
uint16_t i;
209
lv_coord_t act_x = style_bg->body.padding.hor;
210
lv_coord_t act_unit_w;
211
unit_act_cnt = 0;
212
for(i = 0; i < btn_cnt; i++) {
213
/* one_unit_w = all_unit_w / unit_cnt
214
* act_unit_w = one_unit_w * button_width
215
* do this two operations but the multiply first to divide a greater number */
216
act_unit_w = (all_unit_w * get_button_width(map_p_tmp[i])) / unit_cnt;
217
act_unit_w --; /*-1 because e.g. width = 100 means 101 pixels (0..100)*/
218
219
/*Always recalculate act_x because of rounding errors */
220
act_x = (unit_act_cnt * all_unit_w) / unit_cnt + i * style_bg->body.padding.inner + style_bg->body.padding.hor;
221
222
/* Set the button's area.
223
* If inner padding is zero then use the prev. button x2 as x1 to avoid rounding errors*/
224
if(style_bg->body.padding.inner == 0 && act_x != style_bg->body.padding.hor) {
225
lv_area_set(&ext->button_areas[btn_i], ext->button_areas[btn_i - 1].x2, act_y,
226
act_x + act_unit_w, act_y + btn_h);
227
} else {
228
lv_area_set(&ext->button_areas[btn_i], act_x, act_y,
229
act_x + act_unit_w, act_y + btn_h);
230
}
231
232
unit_act_cnt += get_button_width(map_p_tmp[i]);
233
234
i_tot ++;
235
btn_i ++;
236
}
237
}
238
act_y += btn_h + style_bg->body.padding.inner;
239
240
241
if(strlen(map_p_tmp[btn_cnt]) == 0) break; /*Break on end of map*/
242
map_p_tmp = &map_p_tmp[btn_cnt + 1]; /*Set the map to the next line*/
243
i_tot ++; /*Skip the '\n'*/
244
}
245
246
lv_obj_invalidate(btnm);
247
}
248
249
/**
250
* Set a new callback function for the buttons (It will be called when a button is released)
251
* @param btnm: pointer to button matrix object
252
* @param cb pointer to a callback function
253
*/
254
void lv_btnm_set_action(lv_obj_t * btnm, lv_btnm_action_t action)
255
{
256
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
257
ext->action = action;
258
}
259
260
/**
261
* Enable or disable button toggling
262
* @param btnm pointer to button matrix object
263
* @param en true: enable toggling; false: disable toggling
264
* @param id index of the currently toggled button (ignored if 'en' == false)
265
*/
266
void lv_btnm_set_toggle(lv_obj_t * btnm, bool en, uint16_t id)
267
{
268
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
269
270
ext->toggle = en == false ? 0 : 1;
271
if(ext->toggle != 0) {
272
if(id >= ext->btn_cnt) id = ext->btn_cnt - 1;
273
ext->btn_id_tgl = id;
274
} else {
275
ext->btn_id_tgl = LV_BTNM_PR_NONE;
276
}
277
278
lv_obj_invalidate(btnm);
279
}
280
281
/**
282
* Set a style of a button matrix
283
* @param btnm pointer to a button matrix object
284
* @param type which style should be set
285
* @param style pointer to a style
286
*/
287
void lv_btnm_set_style(lv_obj_t * btnm, lv_btnm_style_t type, lv_style_t * style)
288
{
289
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
290
291
switch(type) {
292
case LV_BTNM_STYLE_BG:
293
lv_obj_set_style(btnm, style);
294
break;
295
case LV_BTNM_STYLE_BTN_REL:
296
ext->styles_btn[LV_BTN_STATE_REL] = style;
297
lv_obj_invalidate(btnm);
298
break;
299
case LV_BTNM_STYLE_BTN_PR:
300
ext->styles_btn[LV_BTN_STATE_PR] = style;
301
lv_obj_invalidate(btnm);
302
break;
303
case LV_BTNM_STYLE_BTN_TGL_REL:
304
ext->styles_btn[LV_BTN_STATE_TGL_REL] = style;
305
lv_obj_invalidate(btnm);
306
break;
307
case LV_BTNM_STYLE_BTN_TGL_PR:
308
ext->styles_btn[LV_BTN_STATE_TGL_PR] = style;
309
lv_obj_invalidate(btnm);
310
break;
311
case LV_BTNM_STYLE_BTN_INA:
312
ext->styles_btn[LV_BTN_STATE_INA] = style;
313
lv_obj_invalidate(btnm);
314
break;
315
}
316
}
317
318
void lv_btnm_set_recolor(const lv_obj_t * btnm, bool en)
319
{
320
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
321
322
ext->recolor = en;
323
lv_obj_invalidate(btnm);
324
}
325
326
/*=====================
327
* Getter functions
328
*====================*/
329
330
/**
331
* Get the current map of a button matrix
332
* @param btnm pointer to a button matrix object
333
* @return the current map
334
*/
335
const char ** lv_btnm_get_map(const lv_obj_t * btnm)
336
{
337
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
338
return ext->map_p;
339
}
340
341
/**
342
* Get a the callback function of the buttons on a button matrix
343
* @param btnm: pointer to button matrix object
344
* @return pointer to the callback function
345
*/
346
lv_btnm_action_t lv_btnm_get_action(const lv_obj_t * btnm)
347
{
348
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
349
return ext->action;
350
}
351
352
/**
353
* Get the pressed button
354
* @param btnm pointer to button matrix object
355
* @return index of the currently pressed button (LV_BTNM_PR_NONE: if unset)
356
*/
357
uint16_t lv_btnm_get_pressed(const lv_obj_t * btnm)
358
{
359
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
360
return ext->btn_id_pr;
361
}
362
363
/**
364
* Get the toggled button
365
* @param btnm pointer to button matrix object
366
* @return index of the currently toggled button (LV_BTNM_PR_NONE: if unset)
367
*/
368
uint16_t lv_btnm_get_toggled(const lv_obj_t * btnm)
369
{
370
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
371
372
if(ext->toggle == 0) return LV_BTNM_PR_NONE;
373
else return ext->btn_id_tgl;
374
}
375
376
/**
377
* Get a style of a button matrix
378
* @param btnm pointer to a button matrix object
379
* @param type which style should be get
380
* @return style pointer to a style
381
*/
382
lv_style_t * lv_btnm_get_style(const lv_obj_t * btnm, lv_btnm_style_t type)
383
{
384
lv_style_t * style = NULL;
385
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
386
387
switch(type) {
388
case LV_BTNM_STYLE_BG:
389
style = lv_obj_get_style(btnm);
390
break;
391
case LV_BTNM_STYLE_BTN_REL:
392
style = ext->styles_btn[LV_BTN_STATE_REL];
393
break;
394
case LV_BTNM_STYLE_BTN_PR:
395
style = ext->styles_btn[LV_BTN_STATE_PR];
396
break;
397
case LV_BTNM_STYLE_BTN_TGL_REL:
398
style = ext->styles_btn[LV_BTN_STATE_TGL_REL];
399
break;
400
case LV_BTNM_STYLE_BTN_TGL_PR:
401
style = ext->styles_btn[LV_BTN_STATE_TGL_PR];
402
break;
403
case LV_BTNM_STYLE_BTN_INA:
404
style = ext->styles_btn[LV_BTN_STATE_INA];
405
break;
406
default:
407
style = NULL;
408
break;
409
}
410
411
return style;
412
}
413
414
bool lv_btnm_get_recolor(const lv_obj_t * btnm)
415
{
416
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
417
418
return ext->recolor;
419
}
420
421
/**********************
422
* STATIC FUNCTIONS
423
**********************/
424
425
/**
426
* Handle the drawing related tasks of the button matrixs
427
* @param btnm pointer to a button matrix object
428
* @param mask the object will be drawn only in this area
429
* @param mode LV_DESIGN_COVER_CHK: only check if the object fully covers the 'mask_p' area
430
* (return 'true' if yes)
431
* LV_DESIGN_DRAW: draw the object (always return 'true')
432
* LV_DESIGN_DRAW_POST: drawing after every children are drawn
433
* @param return true/false, depends on 'mode'
434
*/
435
static bool lv_btnm_design(lv_obj_t * btnm, const lv_area_t * mask, lv_design_mode_t mode)
436
{
437
if(mode == LV_DESIGN_COVER_CHK) {
438
return ancestor_design_f(btnm, mask, mode);
439
/*Return false if the object is not covers the mask_p area*/
440
}
441
/*Draw the object*/
442
else if(mode == LV_DESIGN_DRAW_MAIN) {
443
444
ancestor_design_f(btnm, mask, mode);
445
446
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
447
lv_style_t * bg_style = lv_obj_get_style(btnm);
448
lv_style_t * btn_style;
449
lv_opa_t opa_scale = lv_obj_get_opa_scale(btnm);
450
451
lv_area_t area_btnm;
452
lv_obj_get_coords(btnm, &area_btnm);
453
454
lv_area_t area_tmp;
455
lv_coord_t btn_w;
456
lv_coord_t btn_h;
457
458
uint16_t btn_i = 0;
459
uint16_t txt_i = 0;
460
lv_style_t style_tmp;
461
lv_txt_flag_t txt_flag = LV_TXT_FLAG_NONE;
462
463
if(ext->recolor) txt_flag = LV_TXT_FLAG_RECOLOR;
464
465
for(btn_i = 0; btn_i < ext->btn_cnt; btn_i ++, txt_i ++) {
466
/*Search the next valid text in the map*/
467
while(strcmp(ext->map_p[txt_i], "\n") == 0) {
468
txt_i ++;
469
}
470
471
/*Skip hidden buttons*/
472
if(button_is_hidden(ext->map_p[txt_i])) continue;
473
474
lv_area_copy(&area_tmp, &ext->button_areas[btn_i]);
475
area_tmp.x1 += area_btnm.x1;
476
area_tmp.y1 += area_btnm.y1;
477
area_tmp.x2 += area_btnm.x1;
478
area_tmp.y2 += area_btnm.y1;
479
480
btn_w = lv_area_get_width(&area_tmp);
481
btn_h = lv_area_get_height(&area_tmp);
482
483
/*Load the style*/
484
if(button_is_inactive(ext->map_p[txt_i])) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_INA);
485
else if(btn_i != ext->btn_id_pr && btn_i != ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_REL);
486
else if(btn_i == ext->btn_id_pr && btn_i != ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_PR);
487
else if(btn_i != ext->btn_id_pr && btn_i == ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_TGL_REL);
488
else if(btn_i == ext->btn_id_pr && btn_i == ext->btn_id_tgl) btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_TGL_PR);
489
else btn_style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BTN_REL); /*Not possible option, just to be sure*/
490
491
lv_style_copy(&style_tmp, btn_style);
492
493
/*Remove borders on the edges if `LV_BORDER_INTERNAL`*/
494
if(style_tmp.body.border.part & LV_BORDER_INTERNAL) {
495
if(area_tmp.y1 == btnm->coords.y1 + bg_style->body.padding.ver) {
496
style_tmp.body.border.part &= ~LV_BORDER_TOP;
497
}
498
if(area_tmp.y2 == btnm->coords.y2 - bg_style->body.padding.ver) {
499
style_tmp.body.border.part &= ~LV_BORDER_BOTTOM;
500
}
501
502
if(txt_i == 0) {
503
style_tmp.body.border.part &= ~LV_BORDER_LEFT;
504
}
505
else if(strcmp(ext->map_p[txt_i - 1],"\n") == 0) {
506
style_tmp.body.border.part &= ~LV_BORDER_LEFT;
507
}
508
509
if(ext->map_p[txt_i + 1][0] == '\0' || strcmp(ext->map_p[txt_i + 1], "\n") == 0) {
510
style_tmp.body.border.part &= ~LV_BORDER_RIGHT;
511
}
512
}
513
lv_draw_rect(&area_tmp, mask, &style_tmp, opa_scale);
514
515
/*Calculate the size of the text*/
516
if(btn_style->glass) btn_style = bg_style;
517
const lv_font_t * font = btn_style->text.font;
518
lv_point_t txt_size;
519
lv_txt_get_size(&txt_size, ext->map_p[txt_i], font,
520
btn_style->text.letter_space, btn_style->text.line_space,
521
lv_area_get_width(&area_btnm), txt_flag);
522
523
area_tmp.x1 += (btn_w - txt_size.x) / 2;
524
area_tmp.y1 += (btn_h - txt_size.y) / 2;
525
area_tmp.x2 = area_tmp.x1 + txt_size.x;
526
area_tmp.y2 = area_tmp.y1 + txt_size.y;
527
528
lv_draw_label(&area_tmp, mask, btn_style, opa_scale, ext->map_p[txt_i], txt_flag, NULL);
529
}
530
}
531
return true;
532
}
533
534
/**
535
* Signal function of the button matrix
536
* @param btnm pointer to a button matrix object
537
* @param sign a signal type from lv_signal_t enum
538
* @param param pointer to a signal specific variable
539
* @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
540
*/
541
static lv_res_t lv_btnm_signal(lv_obj_t * btnm, lv_signal_t sign, void * param)
542
{
543
lv_res_t res;
544
545
/* Include the ancient signal function */
546
res = ancestor_signal(btnm, sign, param);
547
if(res != LV_RES_OK) return res;
548
549
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
550
lv_area_t btnm_area;
551
lv_area_t btn_area;
552
lv_point_t p;
553
if(sign == LV_SIGNAL_CLEANUP) {
554
lv_mem_free(ext->button_areas);
555
} else if(sign == LV_SIGNAL_STYLE_CHG || sign == LV_SIGNAL_CORD_CHG) {
556
lv_btnm_set_map(btnm, ext->map_p);
557
} else if(sign == LV_SIGNAL_PRESSING) {
558
uint16_t btn_pr;
559
/*Search the pressed area*/
560
lv_indev_get_point(param, &p);
561
btn_pr = get_button_from_point(btnm, &p);
562
/*Invalidate to old and the new areas*/;
563
lv_obj_get_coords(btnm, &btnm_area);
564
if(btn_pr != ext->btn_id_pr) {
565
lv_indev_reset_lpr(param);
566
if(ext->btn_id_pr != LV_BTNM_PR_NONE) {
567
lv_area_copy(&btn_area, &ext->button_areas[ext->btn_id_pr]);
568
btn_area.x1 += btnm_area.x1;
569
btn_area.y1 += btnm_area.y1;
570
btn_area.x2 += btnm_area.x1;
571
btn_area.y2 += btnm_area.y1;
572
lv_inv_area(&btn_area);
573
}
574
if(btn_pr != LV_BTNM_PR_NONE) {
575
lv_area_copy(&btn_area, &ext->button_areas[btn_pr]);
576
btn_area.x1 += btnm_area.x1;
577
btn_area.y1 += btnm_area.y1;
578
btn_area.x2 += btnm_area.x1;
579
btn_area.y2 += btnm_area.y1;
580
lv_inv_area(&btn_area);
581
}
582
}
583
584
ext->btn_id_pr = btn_pr;
585
}
586
587
else if(sign == LV_SIGNAL_LONG_PRESS_REP) {
588
if(ext->action && ext->btn_id_pr != LV_BTNM_PR_NONE) {
589
uint16_t txt_i = get_button_text(btnm, ext->btn_id_pr);
590
if(txt_i != LV_BTNM_PR_NONE) {
591
if(button_is_repeat_disabled(ext->map_p[txt_i]) == false &&
592
button_is_inactive(ext->map_p[txt_i]) == false) {
593
res = ext->action(btnm, cut_ctrl_byte(ext->map_p[txt_i]));
594
}
595
}
596
}
597
} else if(sign == LV_SIGNAL_RELEASED) {
598
if(ext->btn_id_pr != LV_BTNM_PR_NONE) {
599
uint16_t txt_i = get_button_text(btnm, ext->btn_id_pr);
600
if(button_is_inactive(ext->map_p[txt_i]) == false && txt_i != LV_BTNM_PR_NONE) { /*Ignore the inactive buttons anf click between the buttons*/
601
if(ext->action) res = ext->action(btnm, cut_ctrl_byte(ext->map_p[txt_i]));
602
if(res == LV_RES_OK) {
603
604
/*Invalidate to old pressed area*/;
605
lv_obj_get_coords(btnm, &btnm_area);
606
lv_area_copy(&btn_area, &ext->button_areas[ext->btn_id_pr]);
607
btn_area.x1 += btnm_area.x1;
608
btn_area.y1 += btnm_area.y1;
609
btn_area.x2 += btnm_area.x1;
610
btn_area.y2 += btnm_area.y1;
611
lv_inv_area(&btn_area);
612
613
if(ext->toggle != 0) {
614
/*Invalidate to old toggled area*/;
615
lv_area_copy(&btn_area, &ext->button_areas[ext->btn_id_tgl]);
616
btn_area.x1 += btnm_area.x1;
617
btn_area.y1 += btnm_area.y1;
618
btn_area.x2 += btnm_area.x1;
619
btn_area.y2 += btnm_area.y1;
620
lv_inv_area(&btn_area);
621
ext->btn_id_tgl = ext->btn_id_pr;
622
623
}
624
625
#if USE_LV_GROUP
626
/*Leave the clicked button when releases if this not the focused object in a group*/
627
lv_group_t * g = lv_obj_get_group(btnm);
628
if(lv_group_get_focused(g) != btnm) {
629
ext->btn_id_pr = LV_BTNM_PR_NONE;
630
}
631
#else
632
ext->btn_id_pr = LV_BTNM_PR_NONE;
633
#endif
634
635
}
636
}
637
}
638
} else if(sign == LV_SIGNAL_PRESS_LOST || sign == LV_SIGNAL_DEFOCUS) {
639
ext->btn_id_pr = LV_BTNM_PR_NONE;
640
lv_obj_invalidate(btnm);
641
} else if(sign == LV_SIGNAL_FOCUS) {
642
#if USE_LV_GROUP
643
lv_indev_t * indev = lv_indev_get_act();
644
lv_hal_indev_type_t indev_type = lv_indev_get_type(indev);
645
if(indev_type == LV_INDEV_TYPE_POINTER) {
646
/*Select the clicked button*/
647
lv_point_t p1;
648
lv_indev_get_point(indev, &p1);
649
uint16_t btn_i = get_button_from_point(btnm, &p1);
650
ext->btn_id_pr = btn_i;
651
} else if(indev_type == LV_INDEV_TYPE_ENCODER) {
652
/*In navigation mode don't select any button but in edit mode select the fist*/
653
if(lv_group_get_editing(lv_obj_get_group(btnm))) ext->btn_id_pr = 0;
654
else ext->btn_id_pr = LV_BTNM_PR_NONE;
655
} else {
656
ext->btn_id_pr = 0;
657
}
658
#else
659
ext->btn_id_pr = 0;
660
#endif
661
lv_obj_invalidate(btnm);
662
} else if(sign == LV_SIGNAL_CONTROLL) {
663
char c = *((char *)param);
664
if(c == LV_GROUP_KEY_RIGHT) {
665
if(ext->btn_id_pr == LV_BTNM_PR_NONE) ext->btn_id_pr = 0;
666
else ext->btn_id_pr++;
667
if(ext->btn_id_pr >= ext->btn_cnt - 1) ext->btn_id_pr = ext->btn_cnt - 1;
668
lv_obj_invalidate(btnm);
669
} else if(c == LV_GROUP_KEY_LEFT) {
670
if(ext->btn_id_pr == LV_BTNM_PR_NONE) ext->btn_id_pr = 0;
671
if(ext->btn_id_pr > 0) ext->btn_id_pr--;
672
lv_obj_invalidate(btnm);
673
} else if(c == LV_GROUP_KEY_DOWN) {
674
lv_style_t * style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BG);
675
/*Find the area below the the current*/
676
if(ext->btn_id_pr == LV_BTNM_PR_NONE) {
677
ext->btn_id_pr = 0;
678
} else {
679
uint16_t area_below;
680
lv_coord_t pr_center = ext->button_areas[ext->btn_id_pr].x1 + (lv_area_get_width(&ext->button_areas[ext->btn_id_pr]) >> 1);
681
682
for(area_below = ext->btn_id_pr; area_below < ext->btn_cnt; area_below ++) {
683
if(ext->button_areas[area_below].y1 > ext->button_areas[ext->btn_id_pr].y1 &&
684
pr_center >= ext->button_areas[area_below].x1 &&
685
pr_center <= ext->button_areas[area_below].x2 + style->body.padding.hor) {
686
break;
687
}
688
}
689
690
if(area_below < ext->btn_cnt) ext->btn_id_pr = area_below;
691
}
692
lv_obj_invalidate(btnm);
693
} else if(c == LV_GROUP_KEY_UP) {
694
lv_style_t * style = lv_btnm_get_style(btnm, LV_BTNM_STYLE_BG);
695
/*Find the area below the the current*/
696
if(ext->btn_id_pr == LV_BTNM_PR_NONE) {
697
ext->btn_id_pr = 0;
698
} else {
699
int16_t area_above;
700
lv_coord_t pr_center = ext->button_areas[ext->btn_id_pr].x1 + (lv_area_get_width(&ext->button_areas[ext->btn_id_pr]) >> 1);
701
702
for(area_above = ext->btn_id_pr; area_above >= 0; area_above --) {
703
if(ext->button_areas[area_above].y1 < ext->button_areas[ext->btn_id_pr].y1 &&
704
pr_center >= ext->button_areas[area_above].x1 - style->body.padding.hor &&
705
pr_center <= ext->button_areas[area_above].x2) {
706
break;
707
}
708
}
709
if(area_above >= 0) ext->btn_id_pr = area_above;
710
711
}
712
lv_obj_invalidate(btnm);
713
} else if(c == LV_GROUP_KEY_ENTER) {
714
if(ext->action != NULL) {
715
uint16_t txt_i = get_button_text(btnm, ext->btn_id_pr);
716
if(txt_i != LV_BTNM_PR_NONE) {
717
res = ext->action(btnm, cut_ctrl_byte(ext->map_p[txt_i]));
718
}
719
}
720
}
721
} else if(sign == LV_SIGNAL_GET_EDITABLE) {
722
bool * editable = (bool *)param;
723
*editable = true;
724
} else if(sign == LV_SIGNAL_GET_TYPE) {
725
lv_obj_type_t * buf = param;
726
uint8_t i;
727
for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
728
if(buf->type[i] == NULL) break;
729
}
730
buf->type[i] = "lv_btnm";
731
}
732
733
734
return res;
735
}
736
737
/**
738
* Create the required number of buttons according to a map
739
* @param btnm pointer to button matrix object
740
* @param map_p pointer to a string array
741
*/
742
static void allocate_btn_areas(lv_obj_t * btnm, const char ** map)
743
{
744
/*Count the buttons in the map*/
745
uint16_t btn_cnt = 0;
746
uint16_t i = 0;
747
while(strlen(map[i]) != 0) {
748
if(strcmp(map[i], "\n") != 0) { /*Do not count line breaks*/
749
btn_cnt ++;
750
}
751
i++;
752
}
753
754
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
755
756
if(ext->button_areas != NULL) {
757
lv_mem_free(ext->button_areas);
758
ext->button_areas = NULL;
759
}
760
761
ext->button_areas = lv_mem_alloc(sizeof(lv_area_t) * btn_cnt);
762
lv_mem_assert(ext->button_areas);
763
if(ext->button_areas == NULL) btn_cnt = 0;
764
765
ext->btn_cnt = btn_cnt;
766
}
767
768
/**
769
* Get the width of a button in units. It comes from the first "letter".
770
* @param btn_str The descriptor string of a button. E.g. "apple" or "\004banana"
771
* @return the width of the button in units
772
*/
773
static uint8_t get_button_width(const char * btn_str)
774
{
775
if((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) {
776
return btn_str[0] & LV_BTNM_WIDTH_MASK;
777
}
778
779
return 1; /*Default width is 1*/
780
}
781
782
static bool button_is_hidden(const char * btn_str)
783
{
784
/*If control byte presents and hidden bit is '1' then the button is hidden*/
785
if(((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) &&
786
(btn_str[0] & LV_BTNM_HIDE_MASK)) {
787
return true;
788
}
789
790
return false;
791
}
792
793
static bool button_is_repeat_disabled(const char * btn_str)
794
{
795
/*If control byte presents and hidden bit is '1' then the button is hidden*/
796
if(((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) &&
797
(btn_str[0] & LV_BTNM_REPEAT_DISABLE_MASK)) {
798
return true;
799
}
800
801
return false;
802
}
803
804
static bool button_is_inactive(const char * btn_str)
805
{
806
/*If control byte presents and hidden bit is '1' then the button is hidden*/
807
if(((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) &&
808
(btn_str[0] & LV_BTNM_INACTIVE_MASK)) {
809
return true;
810
}
811
812
return false;
813
}
814
815
816
const char * cut_ctrl_byte(const char * btn_str)
817
{
818
/*Cut the control byte if present*/
819
if((btn_str[0] & LV_BTNM_CTRL_MASK) == LV_BTNM_CTRL_CODE) return &btn_str[1];
820
else return btn_str;
821
}
822
823
/**
824
* Gives the button id of a button under a given point
825
* @param btnm pointer to a button matrix object
826
* @param p a point with absolute coordinates
827
* @return the id of the button or LV_BTNM_PR_NONE.
828
*/
829
static uint16_t get_button_from_point(lv_obj_t * btnm, lv_point_t * p)
830
{
831
lv_area_t btnm_cords;
832
lv_area_t btn_area;
833
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
834
uint16_t i;
835
lv_obj_get_coords(btnm, &btnm_cords);
836
837
for(i = 0; i < ext->btn_cnt; i++) {
838
lv_area_copy(&btn_area, &ext->button_areas[i]);
839
btn_area.x1 += btnm_cords.x1;
840
btn_area.y1 += btnm_cords.y1;
841
btn_area.x2 += btnm_cords.x1;
842
btn_area.y2 += btnm_cords.y1;
843
if(lv_area_is_point_on(&btn_area, p) != false) {
844
break;
845
}
846
}
847
848
if(i == ext->btn_cnt) i = LV_BTNM_PR_NONE;
849
850
return i;
851
}
852
853
/**
854
* Get the text of a button
855
* @param btnm pointer to a button matrix object
856
* @param btn_id button id
857
* @return text id in ext->map_p or LV_BTNM_PR_NONE if 'btn_id' was invalid
858
*/
859
static uint16_t get_button_text(lv_obj_t * btnm, uint16_t btn_id)
860
{
861
lv_btnm_ext_t * ext = lv_obj_get_ext_attr(btnm);
862
if(btn_id > ext->btn_cnt) return LV_BTNM_PR_NONE;
863
864
uint16_t txt_i = 0;
865
uint16_t btn_i = 0;
866
867
/* Search the text of ext->btn_pr the buttons text in the map
868
* Skip "\n"-s*/
869
while(btn_i != btn_id) {
870
btn_i ++;
871
txt_i ++;
872
if(strcmp(ext->map_p[txt_i], "\n") == 0) txt_i ++;
873
}
874
875
if(btn_i == ext->btn_cnt) return LV_BTNM_PR_NONE;
876
877
return txt_i;
878
}
879
880
881
#endif
882
883