1 /**
2 * @file lv_mbox.c
3 *
4 */
5
6 /*********************
7 * INCLUDES
8 *********************/
9 #include "lv_mbox.h"
10 #if LV_USE_MBOX != 0
11
12 #include "../lv_core/lv_group.h"
13 #include "../lv_themes/lv_theme.h"
14 #include "../lv_misc/lv_anim.h"
15 #include "../lv_misc/lv_math.h"
16
17 /*********************
18 * DEFINES
19 *********************/
20
21 #if LV_USE_ANIMATION
22 #ifndef LV_MBOX_CLOSE_ANIM_TIME
23 #define LV_MBOX_CLOSE_ANIM_TIME 200 /*List close animation time) */
24 #endif
25 #else
26 #undef LV_MBOX_CLOSE_ANIM_TIME
27 #define LV_MBOX_CLOSE_ANIM_TIME 0 /*No animations*/
28 #endif
29
30 /**********************
31 * TYPEDEFS
32 **********************/
33
34 /**********************
35 * STATIC PROTOTYPES
36 **********************/
37 static lv_res_t lv_mbox_signal(lv_obj_t * mbox, lv_signal_t sign, void * param);
38 static void mbox_realign(lv_obj_t * mbox);
39 #if LV_USE_ANIMATION
40 static void lv_mbox_close_ready_cb(lv_anim_t * a);
41 #endif
42 static void lv_mbox_default_event_cb(lv_obj_t * mbox, lv_event_t event);
43 static void lv_mbox_btnm_event_cb(lv_obj_t * btnm, lv_event_t event);
44
45 /**********************
46 * STATIC VARIABLES
47 **********************/
48 static lv_signal_cb_t ancestor_signal;
49
50 /**********************
51 * MACROS
52 **********************/
53
54 /**********************
55 * GLOBAL FUNCTIONS
56 **********************/
57
58 /**
59 * Create a message box objects
60 * @param par pointer to an object, it will be the parent of the new message box
61 * @param copy pointer to a message box object, if not NULL then the new object will be copied from
62 * it
63 * @return pointer to the created message box
64 */
lv_mbox_create(lv_obj_t * par,const lv_obj_t * copy)65 lv_obj_t * lv_mbox_create(lv_obj_t * par, const lv_obj_t * copy)
66 {
67 LV_LOG_TRACE("mesasge box create started");
68
69 /*Create the ancestor message box*/
70 lv_obj_t * new_mbox = lv_cont_create(par, copy);
71 lv_mem_assert(new_mbox);
72 if(new_mbox == NULL) return NULL;
73
74 if(ancestor_signal == NULL) ancestor_signal = lv_obj_get_signal_cb(new_mbox);
75
76 /*Allocate the message box type specific extended data*/
77 lv_mbox_ext_t * ext = lv_obj_allocate_ext_attr(new_mbox, sizeof(lv_mbox_ext_t));
78 lv_mem_assert(ext);
79 if(ext == NULL) return NULL;
80
81 ext->text = NULL;
82 ext->btnm = NULL;
83 #if LV_USE_ANIMATION
84 ext->anim_time = LV_MBOX_CLOSE_ANIM_TIME;
85 #endif
86
87 /*The signal and design functions are not copied so set them here*/
88 lv_obj_set_signal_cb(new_mbox, lv_mbox_signal);
89
90 /*Init the new message box message box*/
91 if(copy == NULL) {
92 ext->text = lv_label_create(new_mbox, NULL);
93 lv_label_set_align(ext->text, LV_LABEL_ALIGN_CENTER);
94 lv_label_set_long_mode(ext->text, LV_LABEL_LONG_BREAK);
95 lv_label_set_text(ext->text, "Message");
96
97 lv_cont_set_layout(new_mbox, LV_LAYOUT_COL_M);
98 lv_cont_set_fit2(new_mbox, LV_FIT_NONE, LV_FIT_TIGHT);
99 lv_obj_set_width(new_mbox, LV_DPI * 2);
100 lv_obj_align(new_mbox, NULL, LV_ALIGN_CENTER, 0, 0);
101 lv_obj_set_event_cb(new_mbox, lv_mbox_default_event_cb);
102
103 /*Set the default styles*/
104 lv_theme_t * th = lv_theme_get_current();
105 if(th) {
106 lv_mbox_set_style(new_mbox, LV_MBOX_STYLE_BG, th->style.mbox.bg);
107 } else {
108 lv_mbox_set_style(new_mbox, LV_MBOX_STYLE_BG, &lv_style_pretty);
109 }
110
111 }
112 /*Copy an existing message box*/
113 else {
114 lv_mbox_ext_t * copy_ext = lv_obj_get_ext_attr(copy);
115
116 ext->text = lv_label_create(new_mbox, copy_ext->text);
117
118 /*Copy the buttons and the label on them*/
119 if(copy_ext->btnm) ext->btnm = lv_btnm_create(new_mbox, copy_ext->btnm);
120
121 /*Refresh the style with new signal function*/
122 lv_obj_refresh_style(new_mbox);
123 }
124
125 LV_LOG_INFO("mesasge box created");
126
127 return new_mbox;
128 }
129
130 /*======================
131 * Add/remove functions
132 *=====================*/
133
134 /**
135 * Add button to the message box
136 * @param mbox pointer to message box object
137 * @param btn_map button descriptor (button matrix map).
138 * E.g. a const char *txt[] = {"ok", "close", ""} (Can not be local variable)
139 */
lv_mbox_add_btns(lv_obj_t * mbox,const char ** btn_map)140 void lv_mbox_add_btns(lv_obj_t * mbox, const char ** btn_map)
141 {
142 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
143
144 /*Create a button matrix if not exists yet*/
145 if(ext->btnm == NULL) {
146 ext->btnm = lv_btnm_create(mbox, NULL);
147
148 /*Set the default styles*/
149 lv_theme_t * th = lv_theme_get_current();
150 if(th) {
151 lv_mbox_set_style(mbox, LV_MBOX_STYLE_BTN_BG, th->style.mbox.btn.bg);
152 lv_mbox_set_style(mbox, LV_MBOX_STYLE_BTN_REL, th->style.mbox.btn.rel);
153 lv_mbox_set_style(mbox, LV_MBOX_STYLE_BTN_PR, th->style.mbox.btn.pr);
154 } else {
155 lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BG, &lv_style_transp_fit);
156 }
157 }
158
159 lv_btnm_set_map(ext->btnm, btn_map);
160 lv_btnm_set_btn_ctrl_all(ext->btnm, LV_BTNM_CTRL_CLICK_TRIG | LV_BTNM_CTRL_NO_REPEAT);
161 lv_obj_set_event_cb(ext->btnm, lv_mbox_btnm_event_cb);
162
163 mbox_realign(mbox);
164 }
165
166 /*=====================
167 * Setter functions
168 *====================*/
169
170 /**
171 * Set the text of the message box
172 * @param mbox pointer to a message box
173 * @param txt a '\0' terminated character string which will be the message box text
174 */
lv_mbox_set_text(lv_obj_t * mbox,const char * txt)175 void lv_mbox_set_text(lv_obj_t * mbox, const char * txt)
176 {
177 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
178 lv_label_set_text(ext->text, txt);
179
180 mbox_realign(mbox);
181 }
182
183 /**
184 * Set animation duration
185 * @param mbox pointer to a message box object
186 * @param anim_time animation length in milliseconds (0: no animation)
187 */
lv_mbox_set_anim_time(lv_obj_t * mbox,uint16_t anim_time)188 void lv_mbox_set_anim_time(lv_obj_t * mbox, uint16_t anim_time)
189 {
190 #if LV_USE_ANIMATION
191 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
192 anim_time = 0;
193 ext->anim_time = anim_time;
194 #else
195 (void)mbox;
196 (void)anim_time;
197 #endif
198 }
199
200 /**
201 * Automatically delete the message box after a given time
202 * @param mbox pointer to a message box object
203 * @param delay a time (in milliseconds) to wait before delete the message box
204 */
lv_mbox_start_auto_close(lv_obj_t * mbox,uint16_t delay)205 void lv_mbox_start_auto_close(lv_obj_t * mbox, uint16_t delay)
206 {
207 #if LV_USE_ANIMATION
208 if(lv_mbox_get_anim_time(mbox) != 0) {
209 /*Add shrinking animations*/
210 lv_anim_t a;
211 a.var = mbox;
212 a.start = lv_obj_get_height(mbox);
213 a.end = 0;
214 a.exec_cb = (lv_anim_exec_xcb_t)lv_obj_set_height;
215 a.path_cb = lv_anim_path_linear;
216 a.ready_cb = NULL;
217 a.act_time = -delay;
218 a.time = lv_mbox_get_anim_time(mbox);
219 a.playback = 0;
220 a.playback_pause = 0;
221 a.repeat = 0;
222 a.repeat_pause = 0;
223 lv_anim_create(&a);
224
225 a.start = lv_obj_get_width(mbox);
226 a.exec_cb = (lv_anim_exec_xcb_t)lv_obj_set_width;
227 a.ready_cb = lv_mbox_close_ready_cb;
228 lv_anim_create(&a);
229
230 /*Disable fit to let shrinking work*/
231 lv_cont_set_fit(mbox, LV_FIT_NONE);
232 } else {
233 /*Create an animation to delete the mbox `delay` ms later*/
234 lv_anim_t a;
235 a.var = mbox;
236 a.start = 0;
237 a.end = 1;
238 a.exec_cb = (lv_anim_exec_xcb_t)NULL;
239 a.path_cb = lv_anim_path_linear;
240 a.ready_cb = lv_mbox_close_ready_cb;
241 a.act_time = -delay;
242 a.time = 0;
243 a.playback = 0;
244 a.playback_pause = 0;
245 a.repeat = 0;
246 a.repeat_pause = 0;
247 lv_anim_create(&a);
248 }
249 #else
250 (void)delay; /*Unused*/
251 lv_obj_del(mbox);
252 #endif
253 }
254
255 /**
256 * Stop the auto. closing of message box
257 * @param mbox pointer to a message box object
258 */
lv_mbox_stop_auto_close(lv_obj_t * mbox)259 void lv_mbox_stop_auto_close(lv_obj_t * mbox)
260 {
261 #if LV_USE_ANIMATION
262 lv_anim_del(mbox, NULL);
263 #else
264 (void)mbox; /*Unused*/
265 #endif
266 }
267
268 /**
269 * Set a style of a message box
270 * @param mbox pointer to a message box object
271 * @param type which style should be set
272 * @param style pointer to a style
273 */
lv_mbox_set_style(lv_obj_t * mbox,lv_mbox_style_t type,const lv_style_t * style)274 void lv_mbox_set_style(lv_obj_t * mbox, lv_mbox_style_t type, const lv_style_t * style)
275 {
276 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
277
278 switch(type) {
279 case LV_MBOX_STYLE_BG: lv_obj_set_style(mbox, style); break;
280 case LV_MBOX_STYLE_BTN_BG: lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BG, style); break;
281 case LV_MBOX_STYLE_BTN_REL: lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_REL, style); break;
282 case LV_MBOX_STYLE_BTN_PR: lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_PR, style); break;
283 case LV_MBOX_STYLE_BTN_TGL_REL: lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_REL, style); break;
284 case LV_MBOX_STYLE_BTN_TGL_PR: lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_PR, style); break;
285 case LV_MBOX_STYLE_BTN_INA: lv_btnm_set_style(ext->btnm, LV_BTNM_STYLE_BTN_INA, style); break;
286 }
287
288 mbox_realign(mbox);
289 }
290
291 /**
292 * Set whether recoloring is enabled
293 * @param btnm pointer to button matrix object
294 * @param en whether recoloring is enabled
295 */
lv_mbox_set_recolor(lv_obj_t * mbox,bool en)296 void lv_mbox_set_recolor(lv_obj_t * mbox, bool en)
297 {
298 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
299
300 if(ext->btnm) lv_btnm_set_recolor(ext->btnm, en);
301 }
302
303 /*=====================
304 * Getter functions
305 *====================*/
306
307 /**
308 * Get the text of the message box
309 * @param mbox pointer to a message box object
310 * @return pointer to the text of the message box
311 */
lv_mbox_get_text(const lv_obj_t * mbox)312 const char * lv_mbox_get_text(const lv_obj_t * mbox)
313 {
314 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
315
316 return lv_label_get_text(ext->text);
317 }
318
319 /**
320 * Get the index of the lastly "activated" button by the user (pressed, released etc)
321 * Useful in the the `event_cb`.
322 * @param btnm pointer to button matrix object
323 * @return index of the last released button (LV_BTNM_BTN_NONE: if unset)
324 */
lv_mbox_get_active_btn(lv_obj_t * mbox)325 uint16_t lv_mbox_get_active_btn(lv_obj_t * mbox)
326 {
327 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
328 if(ext->btnm)
329 return lv_btnm_get_active_btn(ext->btnm);
330 else
331 return LV_BTNM_BTN_NONE;
332 }
333
334 /**
335 * Get the text of the lastly "activated" button by the user (pressed, released etc)
336 * Useful in the the `event_cb`.
337 * @param btnm pointer to button matrix object
338 * @return text of the last released button (NULL: if unset)
339 */
lv_mbox_get_active_btn_text(lv_obj_t * mbox)340 const char * lv_mbox_get_active_btn_text(lv_obj_t * mbox)
341 {
342 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
343 if(ext->btnm)
344 return lv_btnm_get_active_btn_text(ext->btnm);
345 else
346 return NULL;
347 }
348
349 /**
350 * Get the animation duration (close animation time)
351 * @param mbox pointer to a message box object
352 * @return animation length in milliseconds (0: no animation)
353 */
lv_mbox_get_anim_time(const lv_obj_t * mbox)354 uint16_t lv_mbox_get_anim_time(const lv_obj_t * mbox)
355 {
356 #if LV_USE_ANIMATION
357 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
358 return ext->anim_time;
359 #else
360 (void)mbox;
361 return 0;
362 #endif
363 }
364
365 /**
366 * Get a style of a message box
367 * @param mbox pointer to a message box object
368 * @param type which style should be get
369 * @return style pointer to a style
370 */
lv_mbox_get_style(const lv_obj_t * mbox,lv_mbox_style_t type)371 const lv_style_t * lv_mbox_get_style(const lv_obj_t * mbox, lv_mbox_style_t type)
372 {
373 const lv_style_t * style = NULL;
374 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
375
376 switch(type) {
377 case LV_MBOX_STYLE_BG: style = lv_obj_get_style(mbox); break;
378 case LV_MBOX_STYLE_BTN_BG: style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BG); break;
379 case LV_MBOX_STYLE_BTN_REL: style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_REL); break;
380 case LV_MBOX_STYLE_BTN_PR: style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_PR); break;
381 case LV_MBOX_STYLE_BTN_TGL_REL: style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_REL); break;
382 case LV_MBOX_STYLE_BTN_TGL_PR: style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_TGL_PR); break;
383 case LV_MBOX_STYLE_BTN_INA: style = lv_btnm_get_style(ext->btnm, LV_BTNM_STYLE_BTN_INA); break;
384 default: style = NULL; break;
385 }
386
387 return style;
388 }
389
390 /**
391 * Get whether recoloring is enabled
392 * @param mbox pointer to a message box object
393 * @return whether recoloring is enabled
394 */
lv_mbox_get_recolor(const lv_obj_t * mbox)395 bool lv_mbox_get_recolor(const lv_obj_t * mbox)
396 {
397 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
398
399 if(!ext->btnm) return false;
400
401 return lv_btnm_get_recolor(ext->btnm);
402 }
403
404 /**
405 * Get message box button matrix
406 * @param mbox pointer to a message box object
407 * @return pointer to button matrix object
408 * @remarks return value will be NULL unless `lv_mbox_add_btns` has been already called
409 */
lv_mbox_get_btnm(lv_obj_t * mbox)410 lv_obj_t * lv_mbox_get_btnm(lv_obj_t * mbox)
411 {
412 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
413 return ext->btnm;
414 }
415
416 /**********************
417 * STATIC FUNCTIONS
418 **********************/
419
420 /**
421 * Signal function of the message box
422 * @param mbox pointer to a message box object
423 * @param sign a signal type from lv_signal_t enum
424 * @param param pointer to a signal specific variable
425 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
426 */
lv_mbox_signal(lv_obj_t * mbox,lv_signal_t sign,void * param)427 static lv_res_t lv_mbox_signal(lv_obj_t * mbox, lv_signal_t sign, void * param)
428 {
429 lv_res_t res;
430
431 /*Translate LV_KEY_UP/DOWN to LV_KEY_LEFT/RIGHT */
432 char c_trans = 0;
433 if(sign == LV_SIGNAL_CONTROL) {
434 c_trans = *((char *)param);
435 if(c_trans == LV_KEY_DOWN) c_trans = LV_KEY_LEFT;
436 if(c_trans == LV_KEY_UP) c_trans = LV_KEY_RIGHT;
437
438 param = &c_trans;
439 }
440
441 /* Include the ancient signal function */
442 res = ancestor_signal(mbox, sign, param);
443 if(res != LV_RES_OK) return res;
444
445 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
446 if(sign == LV_SIGNAL_CORD_CHG) {
447 if(lv_obj_get_width(mbox) != lv_area_get_width(param)) {
448 mbox_realign(mbox);
449 }
450 } else if(sign == LV_SIGNAL_STYLE_CHG) {
451 mbox_realign(mbox);
452 } else if(sign == LV_SIGNAL_RELEASED) {
453 if(ext->btnm) {
454 uint32_t btn_id = lv_btnm_get_active_btn(ext->btnm);
455 if(btn_id != LV_BTNM_BTN_NONE) lv_event_send(mbox, LV_EVENT_VALUE_CHANGED, &btn_id);
456 }
457 } else if(sign == LV_SIGNAL_FOCUS || sign == LV_SIGNAL_DEFOCUS || sign == LV_SIGNAL_CONTROL ||
458 sign == LV_SIGNAL_GET_EDITABLE) {
459 if(ext->btnm) {
460 ext->btnm->signal_cb(ext->btnm, sign, param);
461 }
462
463 /* The button matrix with ENCODER input supposes it's in a group but in this case it isn't
464 * (Only the message box's container) So so some actions here instead*/
465 if(sign == LV_SIGNAL_FOCUS) {
466 #if LV_USE_GROUP
467 lv_indev_t * indev = lv_indev_get_act();
468 lv_indev_type_t indev_type = lv_indev_get_type(indev);
469 if(indev_type == LV_INDEV_TYPE_ENCODER) {
470 /*In navigation mode don't select any button but in edit mode select the fist*/
471 lv_btnm_ext_t * btnm_ext = lv_obj_get_ext_attr(ext->btnm);
472 if(lv_group_get_editing(lv_obj_get_group(mbox)))
473 btnm_ext->btn_id_pr = 0;
474 else
475 btnm_ext->btn_id_pr = LV_BTNM_BTN_NONE;
476 }
477 #endif
478 }
479 } else if(sign == LV_SIGNAL_GET_TYPE) {
480 lv_obj_type_t * buf = param;
481 uint8_t i;
482 for(i = 0; i < LV_MAX_ANCESTOR_NUM - 1; i++) { /*Find the last set data*/
483 if(buf->type[i] == NULL) break;
484 }
485 buf->type[i] = "lv_mbox";
486 }
487
488 return res;
489 }
490
491 /**
492 * Resize the button holder to fit
493 * @param mbox pointer to message box object
494 */
mbox_realign(lv_obj_t * mbox)495 static void mbox_realign(lv_obj_t * mbox)
496 {
497 lv_mbox_ext_t * ext = lv_obj_get_ext_attr(mbox);
498
499 const lv_style_t * style = lv_mbox_get_style(mbox, LV_MBOX_STYLE_BG);
500 lv_coord_t w = lv_obj_get_width(mbox) - style->body.padding.left - style->body.padding.right;
501
502 if(ext->text) {
503 lv_obj_set_width(ext->text, w);
504 }
505
506 if(ext->btnm) {
507 const lv_style_t * btn_bg_style = lv_mbox_get_style(mbox, LV_MBOX_STYLE_BTN_BG);
508 const lv_style_t * btn_rel_style = lv_mbox_get_style(mbox, LV_MBOX_STYLE_BTN_REL);
509 lv_coord_t font_h = lv_font_get_line_height(btn_rel_style->text.font);
510 lv_obj_set_size(ext->btnm, w,
511 font_h + btn_rel_style->body.padding.top + btn_rel_style->body.padding.bottom +
512 btn_bg_style->body.padding.top + btn_bg_style->body.padding.bottom);
513 }
514 }
515
516 #if LV_USE_ANIMATION
lv_mbox_close_ready_cb(lv_anim_t * a)517 static void lv_mbox_close_ready_cb(lv_anim_t * a)
518 {
519 lv_obj_del(a->var);
520 }
521 #endif
522
lv_mbox_default_event_cb(lv_obj_t * mbox,lv_event_t event)523 static void lv_mbox_default_event_cb(lv_obj_t * mbox, lv_event_t event)
524 {
525 if(event != LV_EVENT_VALUE_CHANGED) return;
526
527 uint32_t btn_id = lv_mbox_get_active_btn(mbox);
528 if(btn_id == LV_BTNM_BTN_NONE) return;
529
530 lv_mbox_start_auto_close(mbox, 0);
531 }
532
lv_mbox_btnm_event_cb(lv_obj_t * btnm,lv_event_t event)533 static void lv_mbox_btnm_event_cb(lv_obj_t * btnm, lv_event_t event)
534 {
535 lv_obj_t * mbox = lv_obj_get_parent(btnm);
536
537 /*clang-format off*/
538 if(event == LV_EVENT_PRESSED || event == LV_EVENT_PRESSING || event == LV_EVENT_PRESS_LOST ||
539 event == LV_EVENT_RELEASED || event == LV_EVENT_SHORT_CLICKED || event == LV_EVENT_CLICKED ||
540 event == LV_EVENT_LONG_PRESSED || event == LV_EVENT_LONG_PRESSED_REPEAT ||
541 event == LV_EVENT_VALUE_CHANGED) {
542 lv_event_send(mbox, event, lv_event_get_data());
543 }
544 /*clang-format on*/
545 }
546
547 #endif
548