Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
nbgl_layout.c
Go to the documentation of this file.
1
7#include "app_config.h"
8
9#ifdef HAVE_SE_TOUCH
10/*********************
11 * INCLUDES
12 *********************/
13#include <string.h>
14#include <stdlib.h>
15#include "nbgl_debug.h"
16#include "nbgl_front.h"
18#include "nbgl_obj.h"
19#include "nbgl_draw.h"
20#include "nbgl_screen.h"
21#include "nbgl_touch.h"
22#include "glyphs.h"
23#include "os_pic.h"
24#include "os_helpers.h"
25#include "lcx_rng.h"
26
27/*********************
28 * DEFINES
29 *********************/
30// half internal margin, between items
31#define INTERNAL_SPACE 16
32// inner margin, between buttons
33#define INNER_MARGIN 12
34
35#define NB_MAX_LAYOUTS 3
36
37// used by container
38#define NB_MAX_CONTAINER_CHILDREN 20
39
40#define TAG_VALUE_ICON_WIDTH 32
41
42#if defined(TARGET_STAX)
43#define RADIO_CHOICE_HEIGHT 96
44#define FOOTER_HEIGHT 80
45#define BAR_INTERVALE 12
46#define FOOTER_BUTTON_HEIGHT 128
47#define UP_FOOTER_BUTTON_HEIGHT 120
48#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 192
49#define ACTION_AND_FOOTER_FOOTER_HEIGHT 216
50#define FOOTER_TEXT_AND_NAV_WIDTH 160
51#define TAP_TO_CONTINUE_MARGIN 24
52#define SUB_HEADER_MARGIN (2 * 24)
53#define PRE_FIRST_TEXT_MARGIN 24
54#define INTER_PARAGRAPHS_MARGIN 40
55#define PRE_TITLE_MARGIN 24
56#define PRE_DESCRIPTION_MARGIN 16
57#define INTER_ROWS_MARGIN 16
58#define QR_INTER_TEXTS_MARGIN 40
59#define SPINNER_TEXT_MARGIN 20
60#define SPINNER_INTER_TEXTS_MARGIN 20
61#define BAR_TEXT_MARGIN 24
62#define BAR_INTER_TEXTS_MARGIN 16
63#define PROGRESSBAR_ALIGNMENT_MARGIN_Y 28
64#define LEFT_CONTENT_TEXT_PADDING 0
65#elif defined(TARGET_FLEX)
66#define RADIO_CHOICE_HEIGHT 92
67#define FOOTER_HEIGHT 80
68#define BAR_INTERVALE 16
69#define FOOTER_BUTTON_HEIGHT 136
70#define UP_FOOTER_BUTTON_HEIGHT 136
71#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 208
72#define ACTION_AND_FOOTER_FOOTER_HEIGHT 232
73#define FOOTER_TEXT_AND_NAV_WIDTH 192
74#define TAP_TO_CONTINUE_MARGIN 30
75#define SUB_HEADER_MARGIN (2 * 28)
76#define PRE_FIRST_TEXT_MARGIN 0
77#define INTER_PARAGRAPHS_MARGIN 24
78#define PRE_TITLE_MARGIN 16
79#define PRE_DESCRIPTION_MARGIN 24
80#define INTER_ROWS_MARGIN 26
81#define QR_INTER_TEXTS_MARGIN 28
82#define SPINNER_TEXT_MARGIN 24
83#define SPINNER_INTER_TEXTS_MARGIN 16
84#define BAR_TEXT_MARGIN 24
85#define BAR_INTER_TEXTS_MARGIN 16
86#define PROGRESSBAR_ALIGNMENT_MARGIN_Y 32
87#define LEFT_CONTENT_TEXT_PADDING 4
88#else // TARGETS
89#error Undefined target
90#endif // TARGETS
91
92// refresh period of the spinner, in ms
93#define SPINNER_REFRESH_PERIOD 400
94
95/**********************
96 * MACROS
97 **********************/
98
99/**********************
100 * TYPEDEFS
101 **********************/
102
108
109// used to build either a touchable bar or a switch
110typedef struct {
112 const nbgl_icon_details_t *iconLeft; // a buffer containing the 1BPP icon for icon on
113 // left (can be NULL)
114 const nbgl_icon_details_t *iconRight; // a buffer containing the 1BPP icon for icon 2 (can be
115 // NULL). Dimensions must be the same as iconLeft
116 const char *text; // text (can be NULL)
117 const char *subText; // sub text (can be NULL)
118 uint8_t token; // the token that will be used as argument of the callback
119 nbgl_state_t state; // state of the item
120 bool large; // set to true only for the main level of OS settings
121#ifdef HAVE_PIEZO_SOUND
122 tune_index_e tuneId; // if not @ref NBGL_NO_TUNE, a tune will be played
123#endif // HAVE_PIEZO_SOUND
124} listItem_t;
125
126/**********************
127 * VARIABLES
128 **********************/
129
134static nbgl_layoutInternal_t gLayout[NB_MAX_LAYOUTS] = {0};
135
136// numbers of touchable controls for the whole page
137static uint8_t nbTouchableControls = 0;
138
139/**********************
140 * STATIC PROTOTYPES
141 **********************/
142extern const char *get_ux_loc_string(uint32_t index);
143
144#ifdef HAVE_FAST_HOLD_TO_APPROVE
145// Unit step in % of touchable progress bar
146#define HOLD_TO_APPROVE_STEP_PERCENT (7)
147// Duration in ms the user must hold the progress bar
148// to make it progress HOLD_TO_APPROVE_STEP_PERCENT %.
149// This duration must be higher than the screen refresh duration.
150#define HOLD_TO_APPROVE_STEP_DURATION_MS (100)
151#else
152#define HOLD_TO_APPROVE_STEP_PERCENT (25)
153#define HOLD_TO_APPROVE_STEP_DURATION_MS (400)
154#endif // HAVE_FAST_HOLD_TO_APPROVE
155
156static inline uint8_t get_hold_to_approve_percent(uint32_t touch_duration)
157{
158#ifdef HAVE_FAST_HOLD_TO_APPROVE
159 uint8_t current_step_nb = (touch_duration / HOLD_TO_APPROVE_STEP_DURATION_MS);
160#else
161 uint8_t current_step_nb = (touch_duration / HOLD_TO_APPROVE_STEP_DURATION_MS) + 1;
162#endif
163 return (current_step_nb * HOLD_TO_APPROVE_STEP_PERCENT);
164}
165
166// function used to retrieve the concerned layout and layout obj matching the given touched obj
167static bool getLayoutAndLayoutObj(nbgl_obj_t *obj,
168 nbgl_layoutInternal_t **layout,
169 layoutObj_t **layoutObj)
170{
172
173 // parse all layouts (starting with modals) to find the object
174 *layout = NULL;
175 while (i > 0) {
176 i--;
177 if (gLayout[i].nbChildren > 0) {
178 uint8_t j;
179
180 // search index of obj in this layout
181 for (j = 0; j < gLayout[i].nbUsedCallbackObjs; j++) {
182 if (obj == gLayout[i].callbackObjPool[j].obj) {
184 "getLayoutAndLayoutObj(): obj found in layout[%d], index = %d, "
185 "nbUsedCallbackObjs = %d\n",
186 i,
187 j,
188 gLayout[i].nbUsedCallbackObjs);
189 *layout = &gLayout[i];
190 *layoutObj = &(gLayout[i].callbackObjPool[j]);
191 return true;
192 }
193 }
194 }
195 }
196 // not found
197 return false;
198}
199
200static void radioTouchCallback(nbgl_obj_t *obj,
201 nbgl_touchType_t eventType,
202 nbgl_layoutInternal_t *layout);
203static void longTouchCallback(nbgl_obj_t *obj,
204 nbgl_touchType_t eventType,
205 nbgl_layoutInternal_t *layout,
206 layoutObj_t *layoutObj);
207
208// callback for most touched object
209static void touchCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType)
210{
211 nbgl_layoutInternal_t *layout;
212 layoutObj_t *layoutObj;
213 bool needRefresh = false;
214
215 if (obj == NULL) {
216 return;
217 }
218 LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): eventType = %d, obj = %p\n", eventType, obj);
219 if (getLayoutAndLayoutObj(obj, &layout, &layoutObj) == false) {
220 // try with parent, if existing
221 if (getLayoutAndLayoutObj(obj->parent, &layout, &layoutObj) == false) {
222 LOG_WARN(
224 "touchCallback(): eventType = %d, obj = %p, no active layout or obj not found\n",
225 eventType,
226 obj);
227 return;
228 }
229 }
230
231 // case of swipe
232 if (((eventType == SWIPED_UP) || (eventType == SWIPED_DOWN) || (eventType == SWIPED_LEFT)
233 || (eventType == SWIPED_RIGHT))
234 && (obj->type == CONTAINER)) {
235#ifdef NBGL_KEYBOARD
236 if (layout->swipeUsage == SWIPE_USAGE_SUGGESTIONS) {
237 keyboardSwipeCallback(obj, eventType);
238 return;
239 }
240#endif // NBGL_KEYBOARD
241 if (layout->swipeUsage == SWIPE_USAGE_CUSTOM) {
242 layoutObj->index = eventType;
243 }
244 else if ((layout->swipeUsage == SWIPE_USAGE_NAVIGATION)
245 && ((nbgl_obj_t *) layout->container == obj)) {
246 nbgl_container_t *navContainer;
247 if (layout->footerType == FOOTER_NAV) {
248 navContainer = (nbgl_container_t *) layout->footerContainer;
249 }
250 else if (layout->footerType == FOOTER_TEXT_AND_NAV) {
251 navContainer = (nbgl_container_t *) layout->footerContainer->children[1];
252 }
253 else {
254 return;
255 }
256
258 (nbgl_obj_t *) navContainer, eventType, layout->nbPages, &layout->activePage)
259 == false) {
260 // navigation was impossible
261 return;
262 }
263 layoutObj->index = layout->activePage;
264 }
265 }
266
267 // case of navigation bar
268 if (((obj->parent == (nbgl_obj_t *) layout->footerContainer)
269 && (layout->footerType == FOOTER_NAV))
270 || ((obj->parent->type == CONTAINER)
271 && (obj->parent->parent == (nbgl_obj_t *) layout->footerContainer)
272 && (layout->footerType == FOOTER_TEXT_AND_NAV))) {
273 if (layoutNavigationCallback(obj, eventType, layout->nbPages, &layout->activePage)
274 == false) {
275 // navigation was impossible
276 return;
277 }
278 layoutObj->index = layout->activePage;
279 }
280
281 // case of switch
282 if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren >= 2)
283 && (((nbgl_container_t *) obj)->children[1] != NULL)
284 && (((nbgl_container_t *) obj)->children[1]->type == SWITCH)) {
285 nbgl_switch_t *lSwitch = (nbgl_switch_t *) ((nbgl_container_t *) obj)->children[1];
286 lSwitch->state = (lSwitch->state == ON_STATE) ? OFF_STATE : ON_STATE;
287 nbgl_objDraw((nbgl_obj_t *) lSwitch);
288 // refresh will be done after tune playback
289 needRefresh = true;
290 // index is used for state
291 layoutObj->index = lSwitch->state;
292 }
293 // case of radio
294 else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 2)
295 && (((nbgl_container_t *) obj)->children[1] != NULL)
296 && (((nbgl_container_t *) obj)->children[1]->type == RADIO_BUTTON)) {
297 radioTouchCallback(obj, eventType, layout);
298 return;
299 }
300 // case of long press
301 else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 4)
302 && (((nbgl_container_t *) obj)->children[3] != NULL)
303 && (((nbgl_container_t *) obj)->children[3]->type == PROGRESS_BAR)) {
304 longTouchCallback(obj, eventType, layout, layoutObj);
305 return;
306 }
307 LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): layout->callback = %p\n", layout->callback);
308 if ((layout->callback != NULL) && (layoutObj->token != NBGL_INVALID_TOKEN)) {
309#ifdef HAVE_PIEZO_SOUND
310 if (layoutObj->tuneId < NBGL_NO_TUNE) {
311 io_seproxyhal_play_tune(layoutObj->tuneId);
312 }
313#endif // HAVE_PIEZO_SOUND
314 if (needRefresh) {
316 }
317 layout->callback(layoutObj->token, layoutObj->index);
318 }
319}
320
321// callback for long press button
322static void longTouchCallback(nbgl_obj_t *obj,
323 nbgl_touchType_t eventType,
324 nbgl_layoutInternal_t *layout,
325 layoutObj_t *layoutObj)
326{
327 nbgl_container_t *container = (nbgl_container_t *) obj;
328 // 4th child of container is the progress bar
329 nbgl_progress_bar_t *progressBar = (nbgl_progress_bar_t *) container->children[3];
330
332 "longTouchCallback(): eventType = %d, obj = %p, gLayout[1].nbChildren = %d\n",
333 eventType,
334 obj,
335 gLayout[1].nbChildren);
336
337 // case of pressing a long press button
338 if (eventType == TOUCHING) {
339 uint32_t touchDuration = nbgl_touchGetTouchDuration(obj);
340
341 // Compute the new progress bar state in %
342 uint8_t new_state = get_hold_to_approve_percent(touchDuration);
343
344 // Ensure the callback is triggered once,
345 // when the progress bar state reaches 100%
346 bool trigger_callback = (new_state >= 100) && (progressBar->state < 100);
347
348 // Cap progress bar state at 100%
349 if (new_state >= 100) {
350 new_state = 100;
351 }
352
353 // Update progress bar state
354 if (new_state != progressBar->state) {
355 progressBar->partialRedraw = true;
356 progressBar->state = new_state;
357
358 nbgl_objDraw((nbgl_obj_t *) progressBar);
359 // Ensure progress bar is fully drawn
360 // before calling the callback.
363 }
364
365 if (trigger_callback) {
366 // End of progress bar reached: trigger callback
367 if (layout->callback != NULL) {
368 layout->callback(layoutObj->token, layoutObj->index);
369 }
370 }
371 }
372 // case of releasing a long press button (or getting out of it)
373 else if ((eventType == TOUCH_RELEASED) || (eventType == OUT_OF_TOUCH)
374 || (eventType == SWIPED_LEFT) || (eventType == SWIPED_RIGHT)) {
376 progressBar->partialRedraw = true;
377 progressBar->state = 0;
378 nbgl_objDraw((nbgl_obj_t *) progressBar);
380 }
381}
382
383// callback for radio button touch
384static void radioTouchCallback(nbgl_obj_t *obj,
385 nbgl_touchType_t eventType,
386 nbgl_layoutInternal_t *layout)
387{
388 uint8_t i = NB_MAX_LAYOUTS, radioIndex = 0, foundRadio = 0xFF, foundRadioIndex;
389
390 if (eventType != TOUCHED) {
391 return;
392 }
393
394 i = 0;
395 // parse all objs to find all containers of radio buttons
396 while (i < layout->nbUsedCallbackObjs) {
397 if ((obj == (nbgl_obj_t *) layout->callbackObjPool[i].obj)
398 && (layout->callbackObjPool[i].obj->type == CONTAINER)) {
399 nbgl_radio_t *radio
400 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
401 ->children[1];
402 nbgl_text_area_t *textArea
404 ->children[0];
405 foundRadio = i;
406 foundRadioIndex = radioIndex;
407 // set text as active (black and bold)
408 textArea->textColor = BLACK;
409 textArea->fontId = SMALL_BOLD_FONT;
410 // ensure that radio button is ON
411 radio->state = ON_STATE;
412 // redraw container
413 nbgl_objDraw((nbgl_obj_t *) obj);
414 }
415 else if ((layout->callbackObjPool[i].obj->type == CONTAINER)
416 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->nbChildren == 2)
417 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->children[1]->type
418 == RADIO_BUTTON)) {
419 nbgl_radio_t *radio
420 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
421 ->children[1];
422 nbgl_text_area_t *textArea
424 ->children[0];
425 radioIndex++;
426 // set to OFF the one that was in ON
427 if (radio->state == ON_STATE) {
428 radio->state = OFF_STATE;
429 // set text it as inactive (gray and normal)
430 textArea->textColor = DARK_GRAY;
431 textArea->fontId = SMALL_REGULAR_FONT;
432 // redraw container
434 }
435 }
436 i++;
437 }
438 // call callback after redraw to avoid asynchronicity
439 if (foundRadio != 0xFF) {
440 if (layout->callback != NULL) {
441#ifdef HAVE_PIEZO_SOUND
442 if (layout->callbackObjPool[foundRadio].tuneId < NBGL_NO_TUNE) {
443 io_seproxyhal_play_tune(layout->callbackObjPool[foundRadio].tuneId);
444 }
446#endif // HAVE_PIEZO_SOUND
447 layout->callback(layout->callbackObjPool[foundRadio].token, foundRadioIndex);
448 }
449 }
450}
451
452// callback for spinner ticker
453static void spinnerTickerCallback(void)
454{
455 nbgl_spinner_t *spinner;
456 uint8_t i = 0;
457 nbgl_layoutInternal_t *layout;
458
459 // gLayout[1] is on top of gLayout[0] so if gLayout[1] is active, it must catch the event
460 if (gLayout[1].nbChildren > 0) {
461 layout = &gLayout[1];
462 }
463 else {
464 layout = &gLayout[0];
465 }
466
467 // get index of obj
468 while (i < layout->container->nbChildren) {
469 if (layout->container->children[i]->type == CONTAINER) {
470 nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
471 if (container->nbChildren && (container->children[0]->type == SPINNER)) {
472 spinner = (nbgl_spinner_t *) container->children[0];
473 spinner->position++;
474 // there are only NB_SPINNER_POSITIONS positions
475 if (spinner->position == NB_SPINNER_POSITIONS) {
476 spinner->position = 0;
477 }
478 nbgl_objDraw((nbgl_obj_t *) spinner);
480 return;
481 }
482 }
483 i++;
484 }
485}
486
487// callback for animation ticker
488static void animTickerCallback(void)
489{
490 nbgl_image_t *image;
491 uint8_t i = 0;
492 nbgl_layoutInternal_t *layout;
493
494 // gLayout[1] is on top of gLayout[0] so if gLayout[1] is active, it must catch the event
495 if (gLayout[1].nbChildren > 0) {
496 layout = &gLayout[1];
497 }
498 else {
499 layout = &gLayout[0];
500 }
501
502 if (layout->animation == NULL) {
503 return;
504 }
505
506 // get index of image obj
507 while (i < layout->container->nbChildren) {
508 if (layout->container->children[i]->type == CONTAINER) {
509 nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
510 if (container->children[1]->type == IMAGE) {
511 image = (nbgl_image_t *) container->children[1];
512 if (layout->animation->parsing == LOOP_PARSING) {
513 if (layout->iconIdxInAnim == (layout->animation->nbIcons - 1)) {
514 layout->iconIdxInAnim = 0;
515 }
516 else {
517 layout->iconIdxInAnim++;
518 }
519 }
520 else {
521 // Flip incrementAnim when reaching upper or lower limit
522 if ((layout->incrementAnim)
523 && (layout->iconIdxInAnim >= layout->animation->nbIcons - 1)) {
524 layout->incrementAnim = false;
525 }
526 else if (layout->iconIdxInAnim == 0) {
527 layout->incrementAnim = true;
528 }
529
530 // Increase / Decrease index according to incrementAnim
531 if (layout->incrementAnim) {
532 layout->iconIdxInAnim++;
533 }
534 else {
535 layout->iconIdxInAnim--;
536 }
537 }
538 image->buffer = layout->animation->icons[layout->iconIdxInAnim];
539 nbgl_objDraw((nbgl_obj_t *) image);
542 return;
543 }
544 }
545 i++;
546 }
547}
548
549static nbgl_line_t *createHorizontalLine(uint8_t layer)
550{
551 nbgl_line_t *line;
552
553 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
554 line->lineColor = LIGHT_GRAY;
555 line->obj.area.width = SCREEN_WIDTH;
556 line->obj.area.height = 4;
557 line->direction = HORIZONTAL;
558 line->thickness = 1;
559 return line;
560}
561
562static nbgl_line_t *createLeftVerticalLine(uint8_t layer)
563{
564 nbgl_line_t *line;
565
566 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
567 line->lineColor = LIGHT_GRAY;
568 line->obj.area.width = 1;
569 line->obj.area.height = SCREEN_HEIGHT;
570 line->direction = VERTICAL;
571 line->thickness = 1;
572 line->obj.alignment = MID_LEFT;
573 return line;
574}
575
576// function adding a layout object in the callbackObjPool array for the given layout, and
577// configuring it
579 nbgl_obj_t *obj,
580 uint8_t token,
581 tune_index_e tuneId)
582{
583 layoutObj_t *layoutObj = NULL;
584
585 if (layout->nbUsedCallbackObjs < (LAYOUT_OBJ_POOL_LEN - 1)) {
586 layoutObj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
587 layout->nbUsedCallbackObjs++;
588 layoutObj->obj = obj;
589 layoutObj->token = token;
590 layoutObj->tuneId = tuneId;
591 }
592 else {
593 LOG_FATAL(LAYOUT_LOGGER, "layoutAddCallbackObj: no more callback obj\n");
594 }
595
596 return layoutObj;
597}
598
606{
607 if (layout->container->nbChildren == NB_MAX_CONTAINER_CHILDREN) {
608 LOG_FATAL(LAYOUT_LOGGER, "layoutAddObject(): No more object\n");
609 }
610 layout->container->children[layout->container->nbChildren] = obj;
611 layout->container->nbChildren++;
612}
613
623static int addSwipeInternal(nbgl_layoutInternal_t *layoutInt,
624 uint16_t swipesMask,
625 nbgl_swipe_usage_t usage,
626 uint8_t token,
627 tune_index_e tuneId)
628{
629 layoutObj_t *obj;
630
631 if ((swipesMask & SWIPE_MASK) == 0) {
632 return -1;
633 }
634
635 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) layoutInt->container, token, tuneId);
636 if (obj == NULL) {
637 return -1;
638 }
639 layoutInt->container->obj.touchMask = swipesMask;
640 layoutInt->swipeUsage = usage;
641
642 return 0;
643}
644
652static nbgl_container_t *addListItem(nbgl_layoutInternal_t *layoutInt, const listItem_t *itemDesc)
653{
654 layoutObj_t *obj;
655 nbgl_text_area_t *textArea;
656 nbgl_container_t *container;
657 color_t color = ((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == OFF_STATE))
658 ? LIGHT_GRAY
659 : BLACK;
660 int16_t usedHeight = 40;
661
662 LOG_DEBUG(LAYOUT_LOGGER, "addListItem():\n");
663
664 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
666 layoutInt, (nbgl_obj_t *) container, itemDesc->token, itemDesc->tuneId);
667 if (obj == NULL) {
668 return NULL;
669 }
670
671 // get container children (up to 4: text + left+right icons + sub text)
672 container->children = nbgl_containerPoolGet(4, layoutInt->layer);
673 container->nbChildren = 0;
674
675 container->obj.area.width = AVAILABLE_WIDTH;
676 container->obj.area.height = itemDesc->large ? TOUCHABLE_MAIN_BAR_HEIGHT : TOUCHABLE_BAR_HEIGHT;
677 container->layout = HORIZONTAL;
678 container->obj.alignmentMarginX = BORDER_MARGIN;
679 container->obj.alignment = NO_ALIGNMENT;
680 container->obj.alignTo = NULL;
681 // the bar can only be touched if not inactive AND if one of the icon is present
682 // otherwise it is seen as a title
683 if (((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == ON_STATE))
684 || (itemDesc->type == SWITCH_ITEM)) {
685 container->obj.touchMask = (1 << TOUCHED);
686 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
687 nbTouchableControls++;
688 }
689
690 // allocate main text because always present
691 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
692 textArea->textColor = color;
693 textArea->text = PIC(itemDesc->text);
694 textArea->onDrawCallback = NULL;
695 textArea->fontId = SMALL_BOLD_FONT;
696 textArea->wrapping = true;
697 textArea->obj.area.width = container->obj.area.width;
698 if (itemDesc->iconLeft != NULL) {
699 // reduce text width accordingly
700 textArea->obj.area.width
701 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE;
702 }
703 if (itemDesc->iconRight != NULL) {
704 // reduce text width accordingly
705 textArea->obj.area.width
706 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconRight))->width + BAR_INTERVALE;
707 }
708 else if (itemDesc->type == SWITCH_ITEM) {
709 textArea->obj.area.width -= 60 + BAR_INTERVALE;
710 }
711 textArea->obj.area.height = nbgl_getTextHeightInWidth(
712 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
713 usedHeight = MAX(usedHeight, textArea->obj.area.height);
714 textArea->style = NO_STYLE;
715 textArea->obj.alignment = MID_LEFT;
716 textArea->textAlignment = MID_LEFT;
717 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
718 container->nbChildren++;
719
720 // allocate left icon if present
721 if (itemDesc->iconLeft != NULL) {
722 nbgl_image_t *imageLeft = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
723 imageLeft->foregroundColor = color;
724 imageLeft->buffer = PIC(itemDesc->iconLeft);
725 // align at the left of text
726 imageLeft->obj.alignment = MID_LEFT;
727 imageLeft->obj.alignTo = (nbgl_obj_t *) textArea;
728 imageLeft->obj.alignmentMarginX = BAR_INTERVALE;
729 container->children[container->nbChildren] = (nbgl_obj_t *) imageLeft;
730 container->nbChildren++;
731
732 textArea->obj.alignmentMarginX = imageLeft->buffer->width + BAR_INTERVALE;
733
734 usedHeight = MAX(usedHeight, imageLeft->buffer->height);
735 }
736 // allocate right icon if present
737 if (itemDesc->iconRight != NULL) {
738 nbgl_image_t *imageRight = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
739 imageRight->foregroundColor = color;
740 imageRight->buffer = PIC(itemDesc->iconRight);
741 // align at the right of text
742 imageRight->obj.alignment = MID_RIGHT;
743 imageRight->obj.alignmentMarginX = BAR_INTERVALE;
744 imageRight->obj.alignTo = (nbgl_obj_t *) textArea;
745
746 container->children[container->nbChildren] = (nbgl_obj_t *) imageRight;
747 container->nbChildren++;
748
749 usedHeight = MAX(usedHeight, imageRight->buffer->height);
750 }
751 else if (itemDesc->type == SWITCH_ITEM) {
752 nbgl_switch_t *switchObj = (nbgl_switch_t *) nbgl_objPoolGet(SWITCH, layoutInt->layer);
753 switchObj->onColor = BLACK;
754 switchObj->offColor = LIGHT_GRAY;
755 switchObj->state = itemDesc->state;
756 switchObj->obj.alignment = MID_RIGHT;
757 switchObj->obj.alignmentMarginX = BAR_INTERVALE;
758 switchObj->obj.alignTo = (nbgl_obj_t *) textArea;
759
760 container->children[container->nbChildren] = (nbgl_obj_t *) switchObj;
761 container->nbChildren++;
762 }
763
764 if (itemDesc->subText != NULL) {
765 nbgl_text_area_t *subTextArea
767
768 subTextArea->textColor = color;
769 subTextArea->text = PIC(itemDesc->subText);
770 subTextArea->textAlignment = MID_LEFT;
771 subTextArea->fontId = SMALL_REGULAR_FONT;
772 subTextArea->style = NO_STYLE;
773 subTextArea->wrapping = true;
774 subTextArea->obj.alignment = MID_LEFT;
775 subTextArea->obj.area.width = container->obj.area.width;
776 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
777 subTextArea->text,
778 subTextArea->obj.area.width,
779 subTextArea->wrapping);
780 container->children[container->nbChildren] = (nbgl_obj_t *) subTextArea;
781 container->nbChildren++;
782 container->obj.area.height += subTextArea->obj.area.height + 12;
783
784 // modify alignments to have sub-text under (icon left - text - icon right)
785 textArea->obj.alignmentMarginY = -(subTextArea->obj.area.height + 12) / 2;
786 subTextArea->obj.alignmentMarginY = (usedHeight + 12) / 2;
787 }
788
789 // set this new container as child of main container
790 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
791
792 return container;
793}
794
803static nbgl_container_t *addContentCenter(nbgl_layoutInternal_t *layoutInt,
804 const nbgl_contentCenter_t *info)
805{
806 nbgl_container_t *container;
807 nbgl_text_area_t *textArea = NULL;
808 nbgl_image_t *image = NULL;
809 uint16_t fullHeight = 0;
810
811 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
812
813 // get container children
814 container->nbChildren = 0;
815 // the max number is: icon + anim + title + small title + description + sub-text
816 container->children = nbgl_containerPoolGet(6, layoutInt->layer);
817
818 // add icon or animation if present
819 if (info->icon != NULL) {
820 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
821 image->foregroundColor = BLACK;
822 image->buffer = PIC(info->icon);
823 image->obj.alignment = TOP_MIDDLE;
824 image->obj.alignmentMarginY = info->iconHug;
825
826 fullHeight += image->buffer->height + info->iconHug;
827 container->children[container->nbChildren] = (nbgl_obj_t *) image;
828 container->nbChildren++;
829
830 if (info->illustrType == ANIM_ILLUSTRATION) {
831 nbgl_image_t *anim;
832 anim = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
833 anim->foregroundColor = BLACK;
834 anim->buffer = PIC(info->animation->icons[0]);
835 anim->obj.alignment = TOP_MIDDLE;
836 anim->obj.alignmentMarginY = info->iconHug + info->animOffsetY;
837 anim->obj.alignmentMarginX = info->animOffsetX;
838
839 container->children[container->nbChildren] = (nbgl_obj_t *) anim;
840 container->nbChildren++;
841
842 layoutInt->animation = info->animation;
843 layoutInt->incrementAnim = true;
844 layoutInt->iconIdxInAnim = 0;
845
846 // update ticker to update the animation periodically
848
849 tickerCfg.tickerIntervale = info->animation->delayMs; // ms
850 tickerCfg.tickerValue = info->animation->delayMs; // ms
851 tickerCfg.tickerCallback = &animTickerCallback;
852 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
853 }
854 }
855 // add title if present
856 if (info->title != NULL) {
857 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
858 textArea->textColor = BLACK;
859 textArea->text = PIC(info->title);
860 textArea->textAlignment = CENTER;
861 textArea->fontId = LARGE_MEDIUM_FONT;
862 textArea->wrapping = true;
863 textArea->obj.area.width = AVAILABLE_WIDTH;
864 textArea->obj.area.height = nbgl_getTextHeightInWidth(
865 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
866
867 // if not the first child, put on bottom of the previous, with a margin
868 if (container->nbChildren > 0) {
869 textArea->obj.alignment = BOTTOM_MIDDLE;
870 textArea->obj.alignTo = (nbgl_obj_t *) image;
871 textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN + info->iconHug;
872 }
873 else {
874 textArea->obj.alignment = TOP_MIDDLE;
875 }
876
877 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
878
879 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
880 container->nbChildren++;
881 }
882 // add small title if present
883 if (info->smallTitle != NULL) {
884 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
885 textArea->textColor = BLACK;
886 textArea->text = PIC(info->smallTitle);
887 textArea->textAlignment = CENTER;
888 textArea->fontId = SMALL_BOLD_FONT;
889 textArea->wrapping = true;
890 textArea->obj.area.width = AVAILABLE_WIDTH;
891 textArea->obj.area.height = nbgl_getTextHeightInWidth(
892 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
893
894 // if not the first child, put on bottom of the previous, with a margin
895 if (container->nbChildren > 0) {
896 textArea->obj.alignment = BOTTOM_MIDDLE;
897 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
898 textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN;
899 if (container->children[container->nbChildren - 1]->type == IMAGE) {
900 textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN + info->iconHug;
901 }
902 else {
903 textArea->obj.alignmentMarginY = 16;
904 }
905 }
906 else {
907 textArea->obj.alignment = TOP_MIDDLE;
908 }
909
910 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
911
912 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
913 container->nbChildren++;
914 }
915 // add description if present
916 if (info->description != NULL) {
917 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
918 textArea->textColor = BLACK;
919 textArea->text = PIC(info->description);
920 textArea->textAlignment = CENTER;
921 textArea->fontId = SMALL_REGULAR_FONT;
922 textArea->wrapping = true;
923 textArea->obj.area.width = AVAILABLE_WIDTH;
924 textArea->obj.area.height = nbgl_getTextHeightInWidth(
925 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
926
927 // if not the first child, put on bottom of the previous, with a margin
928 if (container->nbChildren > 0) {
929 textArea->obj.alignment = BOTTOM_MIDDLE;
930 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
931 if (container->children[container->nbChildren - 1]->type == TEXT_AREA) {
932 // if previous element is text, only space of 16 px
933 textArea->obj.alignmentMarginY = 16;
934 }
935 else {
936 textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN + info->iconHug;
937 }
938 }
939 else {
940 textArea->obj.alignment = TOP_MIDDLE;
941 }
942
943 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
944
945 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
946 container->nbChildren++;
947 }
948 // add sub-text if present
949 if (info->subText != NULL) {
950 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
951 textArea->textColor = DARK_GRAY;
952 textArea->text = PIC(info->subText);
953 textArea->textAlignment = CENTER;
954 textArea->fontId = SMALL_REGULAR_FONT;
955 textArea->wrapping = true;
956 textArea->obj.area.width = AVAILABLE_WIDTH;
957 textArea->obj.area.height = nbgl_getTextHeightInWidth(
958 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
959 // sub-text is included in a hug of 8px
960 textArea->obj.area.height += 2 * 8;
961 // if not the first child, put on bottom of the previous, with a margin
962 if (container->nbChildren > 0) {
963 textArea->obj.alignment = BOTTOM_MIDDLE;
964 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
965 textArea->obj.alignmentMarginY = 16;
966 if (container->children[container->nbChildren - 1]->type == IMAGE) {
967 textArea->obj.alignmentMarginY += info->iconHug;
968 }
969 }
970 else {
971 textArea->obj.alignment = TOP_MIDDLE;
972 }
973
974 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
975
976 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
977 container->nbChildren++;
978 }
979 container->layout = VERTICAL;
980 container->obj.alignment = CENTER;
981 container->obj.area.width = AVAILABLE_WIDTH;
982 container->obj.area.height = fullHeight;
983 if (info->padding) {
984 container->obj.area.height += 40;
985 }
986
987 // set this new container as child of main container
988 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
989
990 return container;
991}
992
993static int addText(nbgl_layout_t *layout,
994 const char *text,
995 const char *subText,
996 uint8_t token,
997 uint8_t index,
998 bool withAlias)
999{
1000 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1001 nbgl_container_t *container;
1002 nbgl_text_area_t *textArea;
1003 nbgl_text_area_t *subTextArea;
1004 uint16_t fullHeight = 0;
1005
1006 if (layout == NULL) {
1007 return -1;
1008 }
1009 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1010
1011 // get container children
1012 container->children = nbgl_containerPoolGet(withAlias ? 3 : 2, layoutInt->layer);
1013 container->obj.area.width = AVAILABLE_WIDTH;
1014
1015 if (text != NULL) {
1016 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1017
1018 textArea->textColor = BLACK;
1019 textArea->text = PIC(text);
1020 textArea->textAlignment = MID_LEFT;
1021 textArea->fontId = SMALL_BOLD_FONT;
1022 textArea->style = NO_STYLE;
1023 textArea->wrapping = true;
1024 textArea->obj.alignment = NO_ALIGNMENT;
1025 textArea->obj.alignmentMarginY = PRE_TEXT_MARGIN;
1026 textArea->obj.area.width = container->obj.area.width;
1027 if (withAlias == true) {
1028 textArea->obj.area.width -= 12 + MINI_PUSH_ICON.width;
1029 }
1030 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1031 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1032 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1033 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1034 container->nbChildren++;
1035 if (withAlias == true) {
1036 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
1037 // the whole container is touchable
1038 layoutObj_t *obj
1039 = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) container, token, TUNE_TAP_CASUAL);
1040 obj->index = index;
1041 image->foregroundColor = BLACK;
1042 image->buffer = &PUSH_ICON;
1043 image->obj.alignment = RIGHT_TOP;
1044 image->obj.alignmentMarginX = 12;
1045 image->obj.alignTo = (nbgl_obj_t *) textArea;
1046 container->obj.touchMask = (1 << TOUCHED);
1047 container->obj.touchId = VALUE_BUTTON_1_ID + index;
1048
1049 container->children[container->nbChildren] = (nbgl_obj_t *) image;
1050 container->nbChildren++;
1051 }
1052 }
1053 if (subText != NULL) {
1054 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1055 subTextArea->textColor = BLACK;
1056 subTextArea->text = PIC(subText);
1057 subTextArea->fontId = SMALL_REGULAR_FONT;
1058 subTextArea->style = NO_STYLE;
1059 subTextArea->wrapping = true;
1060 subTextArea->obj.area.width = container->obj.area.width;
1061 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
1062 subTextArea->text,
1063 subTextArea->obj.area.width,
1064 subTextArea->wrapping);
1065 subTextArea->textAlignment = MID_LEFT;
1066 subTextArea->obj.alignment = NO_ALIGNMENT;
1067 if (text != NULL) {
1068 subTextArea->obj.alignmentMarginY = TEXT_SUBTEXT_MARGIN;
1069 }
1070 else {
1071 subTextArea->obj.alignmentMarginY = PRE_SUBTEXT_MARGIN;
1072 }
1073 fullHeight += subTextArea->obj.alignmentMarginY;
1074 container->children[container->nbChildren] = (nbgl_obj_t *) subTextArea;
1075 container->nbChildren++;
1076 fullHeight += subTextArea->obj.area.height + subTextArea->obj.alignmentMarginY;
1077 }
1078 else {
1079 fullHeight += PRE_TEXT_MARGIN;
1080 }
1081 container->obj.area.height = fullHeight;
1082 container->layout = VERTICAL;
1083 container->obj.alignmentMarginX = BORDER_MARGIN;
1084 container->obj.alignment = NO_ALIGNMENT;
1085 // set this new obj as child of main container
1086 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1087
1088 return container->obj.area.height;
1089}
1090
1091/**********************
1092 * GLOBAL FUNCTIONS
1093 **********************/
1094
1102{
1103 nbgl_layoutInternal_t *layout = NULL;
1104
1105 // find an empty layout in the proper "layer"
1106 if (description->modal) {
1107 if (gLayout[1].nbChildren == 0) {
1108 layout = &gLayout[1];
1109 }
1110 else if (gLayout[2].nbChildren == 0) {
1111 layout = &gLayout[2];
1112 }
1113 }
1114 else {
1115 // automatically "release" a potentially opened non-modal layout
1116 gLayout[0].nbChildren = 0;
1117 layout = &gLayout[0];
1118 }
1119 if (layout == NULL) {
1120 LOG_WARN(LAYOUT_LOGGER, "nbgl_layoutGet(): impossible to get a layout!\n");
1121 return NULL;
1122 }
1123
1124 // reset globals
1125 memset(layout, 0, sizeof(nbgl_layoutInternal_t));
1126
1127 nbTouchableControls = 0;
1128
1129 layout->callback = (nbgl_layoutTouchCallback_t) PIC(description->onActionCallback);
1130 layout->modal = description->modal;
1131 layout->withLeftBorder = description->withLeftBorder;
1132 if (description->modal) {
1133 layout->layer = nbgl_screenPush(&layout->children,
1135 &description->ticker,
1136 (nbgl_touchCallback_t) touchCallback);
1137 }
1138 else {
1139 nbgl_screenSet(&layout->children,
1141 &description->ticker,
1142 (nbgl_touchCallback_t) touchCallback);
1143 layout->layer = 0;
1144 }
1146 layout->container->obj.area.width = SCREEN_WIDTH;
1147 layout->container->obj.area.height = SCREEN_HEIGHT;
1148 layout->container->layout = VERTICAL;
1149 layout->container->children = nbgl_containerPoolGet(NB_MAX_CONTAINER_CHILDREN, layout->layer);
1150 // by default, if no header, main container is aligned on top-left
1151 layout->container->obj.alignment = TOP_LEFT;
1152 // main container is always the second object, leaving space for header
1153 layout->children[MAIN_CONTAINER_INDEX] = (nbgl_obj_t *) layout->container;
1155
1156 // if a tap text is defined, make the container tapable and display this text in gray
1157 if (description->tapActionText != NULL) {
1158 layoutObj_t *obj;
1159
1160 obj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
1161 layout->nbUsedCallbackObjs++;
1162 obj->obj = (nbgl_obj_t *) layout->container;
1163 obj->token = description->tapActionToken;
1164 obj->tuneId = description->tapTuneId;
1165 layout->container->obj.touchMask = (1 << TOUCHED);
1166 layout->container->obj.touchId = WHOLE_SCREEN_ID;
1167
1168 nbgl_layoutUpFooter_t footerDesc;
1169 footerDesc.type = UP_FOOTER_TEXT;
1170 footerDesc.text.text = PIC(description->tapActionText);
1171 footerDesc.text.token = description->tapActionToken;
1172 footerDesc.text.tuneId = description->tapTuneId;
1173 nbgl_layoutAddUpFooter((nbgl_layout_t *) layout, &footerDesc);
1174 }
1175
1176 return (nbgl_layout_t *) layout;
1177}
1178
1191 uint16_t swipesMask,
1192 const char *text,
1193 uint8_t token,
1194 tune_index_e tuneId)
1195{
1196 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1197
1198 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwipe():\n");
1199 if (layout == NULL) {
1200 return -1;
1201 }
1202
1203 if (text) {
1204 // create 'tap to continue' text area
1206 layoutInt->tapText->text = PIC(text);
1207 layoutInt->tapText->textColor = DARK_GRAY;
1208 layoutInt->tapText->fontId = SMALL_REGULAR_FONT;
1209 layoutInt->tapText->obj.area.width = AVAILABLE_WIDTH;
1210 layoutInt->tapText->obj.area.height = nbgl_getFontLineHeight(layoutInt->tapText->fontId);
1211 layoutInt->tapText->textAlignment = CENTER;
1212 layoutInt->tapText->obj.alignmentMarginY = TAP_TO_CONTINUE_MARGIN;
1213 layoutInt->tapText->obj.alignment = BOTTOM_MIDDLE;
1214 }
1215 return addSwipeInternal(layoutInt, swipesMask, SWIPE_USAGE_CUSTOM, token, tuneId);
1216}
1217
1228 const nbgl_icon_details_t *icon,
1229 uint8_t token,
1230 tune_index_e tuneId)
1231{
1232 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1233 layoutObj_t *obj;
1234 nbgl_button_t *button;
1235
1236 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTopRightButton():\n");
1237 if (layout == NULL) {
1238 return -1;
1239 }
1240 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
1241 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) button, token, tuneId);
1242 if (obj == NULL) {
1243 return -1;
1244 }
1245
1246 button->obj.area.width = BUTTON_DIAMETER;
1247 button->obj.area.height = BUTTON_DIAMETER;
1248 button->radius = BUTTON_RADIUS;
1249 button->obj.alignmentMarginX = BORDER_MARGIN;
1250 button->obj.alignmentMarginY = BORDER_MARGIN;
1251 button->foregroundColor = BLACK;
1252 button->innerColor = WHITE;
1253 button->borderColor = LIGHT_GRAY;
1254 button->obj.touchMask = (1 << TOUCHED);
1255 button->obj.touchId = TOP_RIGHT_BUTTON_ID;
1256 button->icon = PIC(icon);
1257 button->obj.alignment = TOP_RIGHT;
1258
1259 // add to screen
1260 layoutInt->children[TOP_RIGHT_BUTTON_INDEX] = (nbgl_obj_t *) button;
1261
1262 return 0;
1263}
1264
1273{
1274 nbgl_layoutFooter_t footerDesc;
1275 footerDesc.type = FOOTER_NAV;
1276 footerDesc.separationLine = info->withSeparationLine;
1277 footerDesc.navigation.activePage = info->activePage;
1278 footerDesc.navigation.nbPages = info->nbPages;
1279 footerDesc.navigation.withExitKey = info->withExitKey;
1280 footerDesc.navigation.withBackKey = info->withBackKey;
1281 footerDesc.navigation.withPageIndicator = false;
1282 footerDesc.navigation.token = info->token;
1283 footerDesc.navigation.tuneId = info->tuneId;
1284 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1285}
1286
1299 const nbgl_icon_details_t *icon,
1300 uint8_t token,
1301 bool separationLine,
1302 tune_index_e tuneId)
1303{
1304 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddBottomButton():\n");
1305 nbgl_layoutFooter_t footerDesc;
1306 footerDesc.type = FOOTER_SIMPLE_BUTTON;
1307 footerDesc.separationLine = separationLine;
1308 footerDesc.button.fittingContent = false;
1309 footerDesc.button.icon = PIC(icon);
1310 footerDesc.button.text = NULL;
1311 footerDesc.button.token = token;
1312 footerDesc.button.tuneId = tuneId;
1313 footerDesc.button.style = WHITE_BACKGROUND;
1314 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1315}
1316
1325{
1326 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1327 nbgl_container_t *container;
1328 listItem_t itemDesc;
1329
1330 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar():\n");
1331 if (layout == NULL) {
1332 return -1;
1333 }
1334 // main text is mandatory
1335 if (barLayout->text == NULL) {
1336 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar(): main text is mandatory\n");
1337 }
1338
1339 itemDesc.iconLeft = barLayout->iconLeft;
1340 itemDesc.iconRight = barLayout->iconRight;
1341 itemDesc.text = barLayout->text;
1342 itemDesc.subText = barLayout->subText;
1343 itemDesc.token = barLayout->token;
1344 itemDesc.tuneId = barLayout->tuneId;
1345 itemDesc.state = (barLayout->inactive) ? OFF_STATE : ON_STATE;
1346 itemDesc.large = barLayout->large;
1347 itemDesc.type = TOUCHABLE_BAR_ITEM;
1348 container = addListItem(layoutInt, &itemDesc);
1349
1350 if (container == NULL) {
1351 return -1;
1352 }
1353 return container->obj.area.height;
1354}
1355
1364{
1365 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1366 nbgl_container_t *container;
1367 listItem_t itemDesc;
1368
1369 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwitch():\n");
1370 if (layout == NULL) {
1371 return -1;
1372 }
1373 // main text is mandatory
1374 if (switchLayout->text == NULL) {
1375 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddSwitch(): main text is mandatory\n");
1376 }
1377
1378 itemDesc.iconLeft = NULL;
1379 itemDesc.iconRight = NULL;
1380 itemDesc.text = switchLayout->text;
1381 itemDesc.subText = switchLayout->subText;
1382 itemDesc.token = switchLayout->token;
1383 itemDesc.tuneId = switchLayout->tuneId;
1384 itemDesc.state = switchLayout->initState;
1385 itemDesc.large = false;
1386 itemDesc.type = SWITCH_ITEM;
1387 container = addListItem(layoutInt, &itemDesc);
1388
1389 if (container == NULL) {
1390 return -1;
1391 }
1392 return container->obj.area.height;
1393}
1394
1403int nbgl_layoutAddText(nbgl_layout_t *layout, const char *text, const char *subText)
1404{
1405 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddText():\n");
1406 return addText(layout, text, subText, 0, 0, false);
1407}
1408
1421 const char *text,
1422 const char *subText,
1423 uint8_t token,
1424 uint8_t index)
1425{
1426 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextWithAlias():\n");
1427 return addText(layout, text, subText, token, index, true);
1428}
1429
1437int nbgl_layoutAddSubHeaderText(nbgl_layout_t *layout, const char *text)
1438{
1439 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1440 nbgl_text_area_t *textArea;
1441
1442 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSubHeaderText():\n");
1443 if (layout == NULL) {
1444 return -1;
1445 }
1446 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1447
1448 textArea->textColor = BLACK;
1449 textArea->text = PIC(text);
1450 textArea->textAlignment = MID_LEFT;
1451 textArea->fontId = SMALL_REGULAR_FONT;
1452 textArea->style = NO_STYLE;
1453 textArea->wrapping = true;
1454 textArea->obj.alignment = NO_ALIGNMENT;
1455 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1456 textArea->obj.area.width = AVAILABLE_WIDTH;
1457 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1458 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1459 textArea->obj.area.height += SUB_HEADER_MARGIN;
1460
1461 // set this new obj as child of main container
1462 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1463
1464 return textArea->obj.area.height;
1465}
1466
1475int nbgl_layoutAddLargeCaseText(nbgl_layout_t *layout, const char *text, bool grayedOut)
1476{
1477 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1478 nbgl_text_area_t *textArea;
1479
1480 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLargeCaseText():\n");
1481 if (layout == NULL) {
1482 return -1;
1483 }
1484 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1485
1486 textArea->textColor = grayedOut ? LIGHT_GRAY : BLACK;
1487 textArea->text = PIC(text);
1488 textArea->textAlignment = MID_LEFT;
1489 textArea->fontId = LARGE_MEDIUM_FONT;
1490 textArea->obj.area.width = AVAILABLE_WIDTH;
1491 textArea->wrapping = true;
1492 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1493 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1494 textArea->style = NO_STYLE;
1495 textArea->obj.alignment = NO_ALIGNMENT;
1496 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1497 if (layoutInt->container->nbChildren == 0) {
1498 // if first object of container, increase the margin from top
1499 textArea->obj.alignmentMarginY += PRE_FIRST_TEXT_MARGIN;
1500 }
1501 else {
1502 textArea->obj.alignmentMarginY = INTER_PARAGRAPHS_MARGIN; // between paragraphs
1503 }
1504
1505 // set this new obj as child of main container
1506 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1507
1508 return 0;
1509}
1510
1524 const char *title,
1525 const char *description,
1526 const char *info)
1527{
1528 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1529 nbgl_text_area_t *textArea;
1530
1531 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextContent():\n");
1532 if (layout == NULL) {
1533 return -1;
1534 }
1535
1536 // create title
1537 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1538 textArea->textColor = BLACK;
1539 textArea->text = PIC(title);
1540 textArea->textAlignment = MID_LEFT;
1541 textArea->fontId = LARGE_MEDIUM_FONT;
1542 textArea->style = NO_STYLE;
1543 textArea->wrapping = true;
1544 textArea->obj.alignment = NO_ALIGNMENT;
1545 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1546 textArea->obj.alignmentMarginY = PRE_TITLE_MARGIN;
1547 textArea->obj.area.width = AVAILABLE_WIDTH;
1548 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1549 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1550 // set this new obj as child of main container
1551 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1552
1553 // create description
1554 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1555 textArea->textColor = BLACK;
1556 textArea->text = PIC(description);
1557 textArea->fontId = SMALL_REGULAR_FONT;
1558 textArea->style = NO_STYLE;
1559 textArea->wrapping = true;
1560 textArea->obj.area.width = AVAILABLE_WIDTH;
1561 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1562 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1563 textArea->textAlignment = MID_LEFT;
1564 textArea->obj.alignment = NO_ALIGNMENT;
1565 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1566 textArea->obj.alignmentMarginY = PRE_DESCRIPTION_MARGIN;
1567 // set this new obj as child of main container
1568 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1569
1570 // create info on the bottom
1571 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1572 textArea->textColor = DARK_GRAY;
1573 textArea->text = PIC(info);
1574 textArea->fontId = SMALL_REGULAR_FONT;
1575 textArea->style = NO_STYLE;
1576 textArea->wrapping = true;
1577 textArea->obj.area.width = AVAILABLE_WIDTH;
1578 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1579 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1580 textArea->textAlignment = MID_LEFT;
1581 textArea->obj.alignment = BOTTOM_LEFT;
1582 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1583 textArea->obj.alignmentMarginY = 40;
1584 // set this new obj as child of main container
1585 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1586
1587 return layoutInt->container->obj.area.height;
1588}
1589
1598{
1599 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1600 layoutObj_t *obj;
1601 uint8_t i;
1602
1603 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddRadioChoice():\n");
1604 if (layout == NULL) {
1605 return -1;
1606 }
1607 for (i = 0; i < choices->nbChoices; i++) {
1608 nbgl_container_t *container;
1609 nbgl_text_area_t *textArea;
1610 nbgl_radio_t *button;
1611 nbgl_line_t *line;
1612
1613 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1614 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1615 button = (nbgl_radio_t *) nbgl_objPoolGet(RADIO_BUTTON, layoutInt->layer);
1616
1618 layoutInt, (nbgl_obj_t *) container, choices->token, choices->tuneId);
1619 if (obj == NULL) {
1620 return -1;
1621 }
1622
1623 // get container children (max 2)
1624 container->nbChildren = 2;
1625 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1626 container->obj.area.width = AVAILABLE_WIDTH;
1627 container->obj.area.height = RADIO_CHOICE_HEIGHT;
1628 container->obj.alignment = NO_ALIGNMENT;
1629 container->obj.alignmentMarginX = BORDER_MARGIN;
1630 container->obj.alignTo = (nbgl_obj_t *) NULL;
1631
1632 // init button for this choice
1633 button->activeColor = BLACK;
1634 button->borderColor = LIGHT_GRAY;
1635 button->obj.alignTo = (nbgl_obj_t *) container;
1636 button->obj.alignment = MID_RIGHT;
1637 button->state = OFF_STATE;
1638 container->children[1] = (nbgl_obj_t *) button;
1639
1640 // init text area for this choice
1641 if (choices->localized == true) {
1642#ifdef HAVE_LANGUAGE_PACK
1643 textArea->text = get_ux_loc_string(choices->nameIds[i]);
1644#endif // HAVE_LANGUAGE_PACK
1645 }
1646 else {
1647 textArea->text = PIC(choices->names[i]);
1648 }
1649 textArea->textAlignment = MID_LEFT;
1650 textArea->obj.area.width = container->obj.area.width - RADIO_WIDTH;
1651 textArea->style = NO_STYLE;
1652 textArea->obj.alignment = MID_LEFT;
1653 textArea->obj.alignTo = (nbgl_obj_t *) container;
1654 container->children[0] = (nbgl_obj_t *) textArea;
1655
1656 // whole container should be touchable
1657 container->obj.touchMask = (1 << TOUCHED);
1658 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
1659 nbTouchableControls++;
1660
1661 // highlight init choice
1662 if (i == choices->initChoice) {
1663 button->state = ON_STATE;
1664 textArea->textColor = BLACK;
1665 textArea->fontId = SMALL_BOLD_FONT;
1666 }
1667 else {
1668 button->state = OFF_STATE;
1669 textArea->textColor = DARK_GRAY;
1670 textArea->fontId = SMALL_REGULAR_FONT;
1671 }
1672 textArea->obj.area.height = nbgl_getFontHeight(textArea->fontId);
1673
1674 line = createHorizontalLine(layoutInt->layer);
1675 line->obj.alignmentMarginY = -4;
1676 line->offset = 3;
1677
1678 // set these new objs as child of main container
1679 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1680 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
1681 }
1682
1683 return 0;
1684}
1685
1695{
1696 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1697 nbgl_container_t *container;
1698 nbgl_contentCenter_t centeredInfo = {0};
1699
1700 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddCenteredInfo():\n");
1701 if (layout == NULL) {
1702 return -1;
1703 }
1704
1705 centeredInfo.icon = info->icon;
1706 centeredInfo.illustrType = ICON_ILLUSTRATION;
1707
1708 if (info->text1 != NULL) {
1709 if (info->style != NORMAL_INFO) {
1710 centeredInfo.title = info->text1;
1711 }
1712 else {
1713 centeredInfo.smallTitle = info->text1;
1714 }
1715 }
1716 if (info->text2 != NULL) {
1717 if (info->style != LARGE_CASE_BOLD_INFO) {
1718 centeredInfo.description = info->text2;
1719 }
1720 else {
1721 centeredInfo.smallTitle = info->text2;
1722 }
1723 }
1724 if (info->text3 != NULL) {
1725 if (info->style == LARGE_CASE_GRAY_INFO) {
1726 centeredInfo.subText = info->text3;
1727 }
1728 else {
1729 centeredInfo.description = info->text3;
1730 }
1731 }
1732 container = addContentCenter(layoutInt, &centeredInfo);
1733
1734 if (info->onTop) {
1735 container->obj.alignmentMarginX = BORDER_MARGIN;
1736 container->obj.alignmentMarginY = BORDER_MARGIN + info->offsetY;
1737 container->obj.alignment = NO_ALIGNMENT;
1738 }
1739 else {
1740 container->obj.alignmentMarginY = info->offsetY;
1741 }
1742
1743 return container->obj.area.height;
1744}
1745
1755{
1756 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1757 nbgl_container_t *container;
1758
1759 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddContentCenter():\n");
1760 if (layout == NULL) {
1761 return -1;
1762 }
1763
1764 container = addContentCenter(layoutInt, info);
1765
1766 return container->obj.area.height;
1767}
1768
1777{
1778 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1779 nbgl_container_t *container;
1780 nbgl_text_area_t *textArea;
1781 uint8_t row;
1782
1783 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLeftContent():\n");
1784 if (layout == NULL) {
1785 return -1;
1786 }
1787 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1788
1789 // get container children
1790 container->nbChildren = info->nbRows + 1;
1791 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1792 container->layout = VERTICAL;
1793 container->obj.area.width = AVAILABLE_WIDTH;
1794 container->obj.alignmentMarginX = BORDER_MARGIN;
1795
1796 // create title
1797 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1798 textArea->textColor = BLACK;
1799 textArea->text = PIC(info->title);
1800 textArea->textAlignment = MID_LEFT;
1801 textArea->fontId = LARGE_MEDIUM_FONT;
1802 textArea->wrapping = true;
1803#ifdef TARGET_STAX
1804 textArea->obj.alignmentMarginY = 24;
1805#endif
1806 textArea->obj.area.width = AVAILABLE_WIDTH;
1807 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1808 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1809
1810 container->obj.area.height += textArea->obj.area.height;
1811
1812 container->children[0] = (nbgl_obj_t *) textArea;
1813
1814 for (row = 0; row < info->nbRows; row++) {
1815 nbgl_container_t *rowContainer;
1816 nbgl_image_t *image;
1817
1818 // create a grid with 1 icon on the left column and 1 text on the right one
1819 rowContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, 0);
1820 rowContainer->nbChildren = 2; // 1 icon + 1 text
1821 rowContainer->children = nbgl_containerPoolGet(rowContainer->nbChildren, 0);
1822 rowContainer->obj.area.width = AVAILABLE_WIDTH;
1823
1824 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, 0);
1825 image->foregroundColor = BLACK;
1826 image->buffer = info->rowIcons[row];
1827 rowContainer->children[0] = (nbgl_obj_t *) image;
1828
1829 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, 0);
1830 textArea->textColor = BLACK;
1831 textArea->text = info->rowTexts[row];
1832 textArea->textAlignment = MID_LEFT;
1833 textArea->fontId = SMALL_REGULAR_FONT;
1834 textArea->wrapping = true;
1835 textArea->obj.area.width = AVAILABLE_WIDTH - image->buffer->width - 16;
1836 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1837 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1838 textArea->obj.alignment = MID_RIGHT;
1839 rowContainer->children[1] = (nbgl_obj_t *) textArea;
1840 rowContainer->obj.area.height
1841 = MAX(image->buffer->height, textArea->obj.area.height + LEFT_CONTENT_TEXT_PADDING);
1842
1843 if (row == 0) {
1844 rowContainer->obj.alignmentMarginY = 32;
1845 }
1846 else {
1847 rowContainer->obj.alignmentMarginY = INTER_ROWS_MARGIN;
1848 }
1849 container->children[1 + row] = (nbgl_obj_t *) rowContainer;
1850 container->obj.area.height
1851 += rowContainer->obj.area.height + rowContainer->obj.alignmentMarginY;
1852 }
1853 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1854
1855 return container->obj.area.height;
1856}
1857
1858#ifdef NBGL_QRCODE
1868{
1869 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1870 nbgl_container_t *container;
1871 nbgl_text_area_t *textArea = NULL;
1873 uint16_t fullHeight = 0;
1874
1875 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddQRCode():\n");
1876 if (layout == NULL) {
1877 return -1;
1878 }
1879
1880 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1881
1882 // get container children (max 2 (QRCode + text1 + text2))
1883 container->children = nbgl_containerPoolGet(3, layoutInt->layer);
1884 container->nbChildren = 0;
1885
1887 // version is forced to V10 if url is longer than 62 characters
1888 if (strlen(PIC(info->url)) > 62) {
1889 qrcode->version = QRCODE_V10;
1890 }
1891 else {
1892 qrcode->version = QRCODE_V4;
1893 }
1894 qrcode->foregroundColor = BLACK;
1895 // in QR V4, we use 8*8 screen pixels for one QR pixel
1896 // in QR V10, we use 4*4 screen pixels for one QR pixel
1897 qrcode->obj.area.width
1898 = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 8) : (QR_V10_NB_PIX_SIZE * 4);
1899 qrcode->obj.area.height = qrcode->obj.area.width;
1900 qrcode->text = PIC(info->url);
1901 qrcode->obj.area.bpp = NBGL_BPP_1;
1902 qrcode->obj.alignment = TOP_MIDDLE;
1903
1904 fullHeight += qrcode->obj.area.height;
1905 container->children[container->nbChildren] = (nbgl_obj_t *) qrcode;
1906 container->nbChildren++;
1907
1908 if (info->text1 != NULL) {
1909 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1910 textArea->textColor = BLACK;
1911 textArea->text = PIC(info->text1);
1912 textArea->textAlignment = CENTER;
1913 textArea->fontId = (info->largeText1 == true) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
1914 textArea->wrapping = true;
1915 textArea->obj.area.width = AVAILABLE_WIDTH;
1916 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1917 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1918 textArea->obj.alignment = BOTTOM_MIDDLE;
1919 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1920 textArea->obj.alignmentMarginY = 24;
1921
1922 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1923
1924 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1925 container->nbChildren++;
1926 }
1927 if (info->text2 != NULL) {
1928 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1929 textArea->textColor = DARK_GRAY;
1930 textArea->text = PIC(info->text2);
1931 textArea->textAlignment = CENTER;
1932 textArea->fontId = SMALL_REGULAR_FONT;
1933 textArea->wrapping = true;
1934 textArea->obj.area.width = AVAILABLE_WIDTH;
1935 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1936 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1937 textArea->obj.alignment = BOTTOM_MIDDLE;
1938 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1939 if (info->text1 != NULL) {
1940 textArea->obj.alignmentMarginY = QR_INTER_TEXTS_MARGIN;
1941 }
1942 else {
1943 textArea->obj.alignmentMarginY = 32;
1944 fullHeight += 8;
1945 }
1946
1947 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1948
1949 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1950 container->nbChildren++;
1951 }
1952 // ensure that fullHeight is fitting in main container height (with 16 px margin)
1953 if ((fullHeight >= (layoutInt->container->obj.area.height - 16))
1954 && (qrcode->version == QRCODE_V4)) {
1955 qrcode->version = QRCODE_V4_SMALL;
1956 // in QR V4 small, we use 4*4 screen pixels for one QR pixel
1957 qrcode->obj.area.width = QR_V4_NB_PIX_SIZE * 4;
1958 qrcode->obj.area.height = qrcode->obj.area.width;
1959 fullHeight -= QR_V4_NB_PIX_SIZE * 4;
1960 }
1961 container->obj.area.height = fullHeight;
1962 container->layout = VERTICAL;
1963 if (info->centered) {
1964 container->obj.alignment = CENTER;
1965 }
1966 else {
1967 container->obj.alignment = BOTTOM_MIDDLE;
1968 container->obj.alignTo
1969 = layoutInt->container->children[layoutInt->container->nbChildren - 1];
1970 }
1971 container->obj.alignmentMarginY = info->offsetY;
1972
1973 container->obj.area.width = AVAILABLE_WIDTH;
1974
1975 // set this new container as child of main container
1976 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1977
1978 return fullHeight;
1979}
1980#endif // NBGL_QRCODE
1981
1991{
1992 nbgl_layoutFooter_t footerDesc;
1993 footerDesc.type = FOOTER_CHOICE_BUTTONS;
1994 footerDesc.separationLine = false;
1995 footerDesc.choiceButtons.bottomText = info->bottomText;
1996 footerDesc.choiceButtons.token = info->token;
1997 footerDesc.choiceButtons.topText = info->topText;
1998 footerDesc.choiceButtons.topIcon = info->topIcon;
1999 footerDesc.choiceButtons.style = info->style;
2000 footerDesc.choiceButtons.tuneId = info->tuneId;
2001 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2002}
2003
2015{
2017 .horizontalButtons.leftIcon = info->leftIcon,
2018 .horizontalButtons.leftToken = info->leftToken,
2019 .horizontalButtons.rightText = info->rightText,
2020 .horizontalButtons.rightToken = info->rightToken,
2021 .horizontalButtons.tuneId = info->tuneId};
2022
2023 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHorizontalButtons():\n");
2024 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2025}
2026
2035{
2036 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2037 nbgl_text_area_t *itemTextArea;
2038 nbgl_text_area_t *valueTextArea;
2039 nbgl_container_t *container;
2040 uint8_t i;
2041
2042 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTagValueList():\n");
2043 if (layout == NULL) {
2044 return -1;
2045 }
2046
2047 for (i = 0; i < list->nbPairs; i++) {
2048 const nbgl_layoutTagValue_t *pair;
2049 uint16_t fullHeight = 0;
2050 const nbgl_icon_details_t *valueIcon = NULL;
2051
2052 if (list->pairs != NULL) {
2053 pair = &list->pairs[i];
2054 }
2055 else {
2056 pair = list->callback(list->startIndex + i);
2057 }
2058
2059 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2060
2061 // get container children (max 3 if a valueIcon, otherwise 2)
2062 container->children
2063 = nbgl_containerPoolGet((pair->valueIcon != NULL) ? 3 : 2, layoutInt->layer);
2064
2065 itemTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2066 valueTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2067
2068 // init text area for this choice
2069 itemTextArea->textColor = DARK_GRAY;
2070 itemTextArea->text = PIC(pair->item);
2071 itemTextArea->textAlignment = MID_LEFT;
2072 itemTextArea->fontId = SMALL_REGULAR_FONT;
2073 itemTextArea->wrapping = true;
2074 itemTextArea->obj.area.width = AVAILABLE_WIDTH;
2075 itemTextArea->obj.area.height = nbgl_getTextHeightInWidth(
2076 itemTextArea->fontId, itemTextArea->text, AVAILABLE_WIDTH, itemTextArea->wrapping);
2077 itemTextArea->style = NO_STYLE;
2078 itemTextArea->obj.alignment = NO_ALIGNMENT;
2079 itemTextArea->obj.alignmentMarginX = 0;
2080 itemTextArea->obj.alignmentMarginY = 0;
2081 itemTextArea->obj.alignTo = NULL;
2082 container->children[container->nbChildren] = (nbgl_obj_t *) itemTextArea;
2083 container->nbChildren++;
2084
2085 fullHeight += itemTextArea->obj.area.height;
2086
2087 // init button for this choice
2088 valueTextArea->textColor = BLACK;
2089 valueTextArea->text = PIC(pair->value);
2090 valueTextArea->textAlignment = MID_LEFT;
2091 if (list->smallCaseForValue) {
2092 valueTextArea->fontId = SMALL_BOLD_FONT;
2093 }
2094 else {
2095 valueTextArea->fontId = LARGE_MEDIUM_FONT;
2096 }
2097 if ((pair->aliasValue == 0) && (pair->valueIcon == NULL)) {
2098 valueTextArea->obj.area.width = AVAILABLE_WIDTH;
2099 }
2100 else {
2101 if (pair->aliasValue) {
2102 // if the value is an alias, we automatically display a (>) icon
2103 valueIcon = &MINI_PUSH_ICON;
2104 }
2105 else {
2106 // otherwise use the provided icon
2107 valueIcon = PIC(pair->valueIcon);
2108 }
2109 // decrease the available width for value text
2110 valueTextArea->obj.area.width = AVAILABLE_WIDTH - valueIcon->width - 12;
2111 }
2112
2113 // handle the nbMaxLinesForValue parameter, used to automatically keep only
2114 // nbMaxLinesForValue lines
2115 uint16_t nbLines = nbgl_getTextNbLinesInWidth(valueTextArea->fontId,
2116 valueTextArea->text,
2117 valueTextArea->obj.area.width,
2118 list->wrapping);
2119 // use this nbMaxLinesForValue parameter only if >0
2120 if ((list->nbMaxLinesForValue > 0) && (nbLines > list->nbMaxLinesForValue)) {
2121 nbLines = list->nbMaxLinesForValue;
2122 valueTextArea->nbMaxLines = list->nbMaxLinesForValue;
2123 }
2124 const nbgl_font_t *font = nbgl_getFont(valueTextArea->fontId);
2125 valueTextArea->obj.area.height = nbLines * font->line_height;
2126 valueTextArea->style = NO_STYLE;
2127 valueTextArea->obj.alignment = BOTTOM_LEFT;
2128 valueTextArea->obj.alignmentMarginY = 4;
2129 valueTextArea->obj.alignTo = (nbgl_obj_t *) itemTextArea;
2130 valueTextArea->wrapping = list->wrapping;
2131 container->children[container->nbChildren] = (nbgl_obj_t *) valueTextArea;
2132 container->nbChildren++;
2133
2134 fullHeight += valueTextArea->obj.area.height + valueTextArea->obj.alignmentMarginY;
2135 if (valueIcon != NULL) {
2136 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2138 layoutInt, (nbgl_obj_t *) image, list->token, TUNE_TAP_CASUAL);
2139 obj->index = i;
2140 image->foregroundColor = BLACK;
2141 image->buffer = valueIcon;
2142 image->obj.alignment = RIGHT_TOP;
2143 image->obj.alignmentMarginX = 12;
2144 image->obj.alignTo = (nbgl_obj_t *) valueTextArea;
2145 image->obj.touchMask = (1 << TOUCHED);
2146 image->obj.touchId = VALUE_BUTTON_1_ID + i;
2147
2148 container->children[container->nbChildren] = (nbgl_obj_t *) image;
2149 container->nbChildren++;
2150 }
2151
2152 container->obj.area.width = AVAILABLE_WIDTH;
2153 container->obj.area.height = fullHeight;
2154 container->layout = VERTICAL;
2155 container->obj.alignmentMarginX = BORDER_MARGIN;
2156 if (i > 0) {
2157 container->obj.alignmentMarginY = INTER_TAG_VALUE_MARGIN;
2158 }
2159 else {
2160 container->obj.alignmentMarginY = PRE_TAG_VALUE_MARGIN;
2161 }
2162 container->obj.alignment = NO_ALIGNMENT;
2163
2164 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2165 }
2166
2167 return 0;
2168}
2169
2183 const char *text,
2184 const char *subText,
2185 uint8_t percentage)
2186{
2187 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2188 nbgl_container_t *container;
2189 nbgl_text_area_t *textArea;
2190 nbgl_progress_bar_t *progress;
2191
2192 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressBar():\n");
2193 if (layout == NULL) {
2194 return -1;
2195 }
2196
2197 // First Create Container :
2198 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2199 // progressbar + text + subText
2200 container->nbChildren = (subText != NULL) ? 3 : 2;
2201 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
2202
2203 container->obj.area.width = AVAILABLE_WIDTH;
2204 container->layout = VERTICAL;
2205 container->obj.alignment = CENTER;
2206
2207 // Create progressbar
2208 progress = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
2209 progress->foregroundColor = BLACK;
2210 progress->withBorder = true;
2211 progress->state = percentage;
2212 progress->obj.area.width = PROGRESSBAR_WIDTH;
2213 progress->obj.area.height = PROGRESSBAR_HEIGHT;
2214 progress->obj.alignment = TOP_MIDDLE;
2215
2216 // set this new progressbar as child of the container
2217 container->children[0] = (nbgl_obj_t *) progress;
2218
2219 // update container height
2220 container->obj.area.height = progress->obj.alignmentMarginY + progress->obj.area.height;
2221
2222 // create text area
2223 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2224 textArea->textColor = BLACK;
2225 textArea->text = PIC(text);
2226 textArea->textAlignment = CENTER;
2227 textArea->fontId = LARGE_MEDIUM_FONT;
2228 textArea->wrapping = true;
2229 textArea->obj.alignmentMarginY = BAR_TEXT_MARGIN;
2230 textArea->obj.alignTo = (nbgl_obj_t *) progress;
2231 textArea->obj.alignment = BOTTOM_MIDDLE;
2232 textArea->obj.area.width = AVAILABLE_WIDTH;
2233 textArea->obj.area.height = nbgl_getTextHeightInWidth(
2234 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
2235 textArea->style = NO_STYLE;
2236
2237 // update container height
2238 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
2239
2240 // set this text as child of the container
2241 container->children[1] = (nbgl_obj_t *) textArea;
2242
2243 if (subText != NULL) {
2244 nbgl_text_area_t *subTextArea;
2245 // create sub-text area
2246 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2247 subTextArea->textColor = BLACK;
2248 subTextArea->text = PIC(subText);
2249 subTextArea->textAlignment = CENTER;
2250 subTextArea->fontId = SMALL_REGULAR_FONT;
2251 subTextArea->wrapping = true;
2252 subTextArea->obj.alignmentMarginY = BAR_INTER_TEXTS_MARGIN;
2253 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
2254 subTextArea->obj.alignment = BOTTOM_MIDDLE;
2255 subTextArea->obj.area.width = AVAILABLE_WIDTH;
2256 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
2257 subTextArea->text,
2258 subTextArea->obj.area.width,
2259 subTextArea->wrapping);
2260 subTextArea->style = NO_STYLE;
2261
2262 // update container height
2263 container->obj.area.height
2264 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
2265
2266 // set thissub-text as child of the container
2267 container->children[2] = (nbgl_obj_t *) subTextArea;
2268 }
2269 // The height of containers must be a multiple of eight
2270 container->obj.area.height = (container->obj.area.height + 7) & 0xFFF8;
2271
2272 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2273
2274 return 2;
2275}
2276
2284{
2285 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2286 nbgl_line_t *line;
2287
2288 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSeparationLine():\n");
2289 line = createHorizontalLine(layoutInt->layer);
2290 line->obj.alignmentMarginY = -4;
2291 line->offset = 3;
2292 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
2293 return 0;
2294}
2295
2304{
2305 layoutObj_t *obj;
2306 nbgl_button_t *button;
2307 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2308
2309 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddButton():\n");
2310 if (layout == NULL) {
2311 return -1;
2312 }
2313
2314 // Add in footer if matching
2315 if ((buttonInfo->onBottom) && (!buttonInfo->fittingContent)) {
2316 if (layoutInt->footerContainer == NULL) {
2317 nbgl_layoutFooter_t footerDesc;
2318 footerDesc.type = FOOTER_SIMPLE_BUTTON;
2319 footerDesc.separationLine = false;
2320 footerDesc.button.text = buttonInfo->text;
2321 footerDesc.button.token = buttonInfo->token;
2322 footerDesc.button.tuneId = buttonInfo->tuneId;
2323 footerDesc.button.icon = buttonInfo->icon;
2324 footerDesc.button.style = buttonInfo->style;
2325 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2326 }
2327 else {
2328 nbgl_layoutUpFooter_t upFooterDesc;
2329 upFooterDesc.type = UP_FOOTER_BUTTON;
2330 upFooterDesc.button.text = buttonInfo->text;
2331 upFooterDesc.button.token = buttonInfo->token;
2332 upFooterDesc.button.tuneId = buttonInfo->tuneId;
2333 upFooterDesc.button.icon = buttonInfo->icon;
2334 upFooterDesc.button.style = buttonInfo->style;
2335 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2336 }
2337 }
2338
2339 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2341 layoutInt, (nbgl_obj_t *) button, buttonInfo->token, buttonInfo->tuneId);
2342 if (obj == NULL) {
2343 return -1;
2344 }
2345
2346 button->obj.alignmentMarginX = BORDER_MARGIN;
2347 button->obj.alignmentMarginY = 12;
2348 button->obj.alignment = NO_ALIGNMENT;
2349 if (buttonInfo->style == BLACK_BACKGROUND) {
2350 button->innerColor = BLACK;
2351 button->foregroundColor = WHITE;
2352 }
2353 else {
2354 button->innerColor = WHITE;
2355 button->foregroundColor = BLACK;
2356 }
2357 if (buttonInfo->style == NO_BORDER) {
2358 button->borderColor = WHITE;
2359 }
2360 else {
2361 if (buttonInfo->style == BLACK_BACKGROUND) {
2362 button->borderColor = BLACK;
2363 }
2364 else {
2365 button->borderColor = LIGHT_GRAY;
2366 }
2367 }
2368 button->text = PIC(buttonInfo->text);
2369 button->fontId = SMALL_BOLD_FONT;
2370 button->icon = PIC(buttonInfo->icon);
2371 if (buttonInfo->fittingContent == true) {
2372 button->obj.area.width = nbgl_getTextWidth(button->fontId, button->text)
2373 + SMALL_BUTTON_HEIGHT
2374 + ((button->icon) ? (button->icon->width + 12) : 0);
2375 button->obj.area.height = SMALL_BUTTON_HEIGHT;
2376 button->radius = SMALL_BUTTON_RADIUS_INDEX;
2377 if (buttonInfo->onBottom != true) {
2378 button->obj.alignmentMarginX
2379 += (SCREEN_WIDTH - 2 * BORDER_MARGIN - button->obj.area.width) / 2;
2380 }
2381 }
2382 else {
2383 button->obj.area.width = AVAILABLE_WIDTH;
2384 button->obj.area.height = BUTTON_DIAMETER;
2385 button->radius = BUTTON_RADIUS;
2386 }
2387 button->obj.alignTo = NULL;
2388 button->obj.touchMask = (1 << TOUCHED);
2389 button->obj.touchId = (buttonInfo->fittingContent) ? EXTRA_BUTTON_ID : SINGLE_BUTTON_ID;
2390 // set this new button as child of the container
2391 layoutAddObject(layoutInt, (nbgl_obj_t *) button);
2392
2393 return 0;
2394}
2395
2406 const char *text,
2407 uint8_t token,
2408 tune_index_e tuneId)
2409{
2411 .longPress.text = text,
2412 .longPress.token = token,
2413 .longPress.tuneId = tuneId};
2414
2415 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLongPressButton():\n");
2416 if (layout == NULL) {
2417 return -1;
2418 }
2419
2420 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2421}
2422
2434 const char *text,
2435 uint8_t token,
2436 tune_index_e tuneId)
2437{
2438 nbgl_layoutFooter_t footerDesc;
2439 footerDesc.type = FOOTER_SIMPLE_TEXT;
2440 footerDesc.separationLine = true;
2441 footerDesc.simpleText.text = text;
2442 footerDesc.simpleText.mutedOut = false;
2443 footerDesc.simpleText.token = token;
2444 footerDesc.simpleText.tuneId = tuneId;
2445 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2446}
2447
2461 const char *leftText,
2462 uint8_t leftToken,
2463 const char *rightText,
2464 uint8_t rightToken,
2465 tune_index_e tuneId)
2466{
2467 nbgl_layoutFooter_t footerDesc;
2468 footerDesc.type = FOOTER_DOUBLE_TEXT;
2469 footerDesc.separationLine = true;
2470 footerDesc.doubleText.leftText = leftText;
2471 footerDesc.doubleText.leftToken = leftToken;
2472 footerDesc.doubleText.rightText = rightText;
2473 footerDesc.doubleText.rightToken = rightToken;
2474 footerDesc.doubleText.tuneId = tuneId;
2475 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2476}
2477
2487{
2488 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2489 layoutObj_t *obj;
2490 nbgl_text_area_t *textArea;
2491 nbgl_line_t *line, *separationLine = NULL;
2492 nbgl_image_t *image = NULL;
2493 nbgl_button_t *button;
2494
2495 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHeader(): type = %d\n", headerDesc->type);
2496 if (layout == NULL) {
2497 return -1;
2498 }
2499 if ((headerDesc == NULL) || (headerDesc->type >= NB_HEADER_TYPES)) {
2500 return -2;
2501 }
2502
2503 layoutInt->headerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2504 layoutInt->headerContainer->obj.area.width = SCREEN_WIDTH;
2505 layoutInt->headerContainer->layout = VERTICAL;
2506 layoutInt->headerContainer->children
2507 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2508 layoutInt->headerContainer->obj.alignment = TOP_MIDDLE;
2509
2510 switch (headerDesc->type) {
2511 case HEADER_EMPTY: {
2512 layoutInt->headerContainer->obj.area.height = headerDesc->emptySpace.height;
2513 break;
2514 }
2517 case HEADER_EXTENDED_BACK: {
2518 const char *text = (headerDesc->type == HEADER_EXTENDED_BACK)
2519 ? PIC(headerDesc->extendedBack.text)
2520 : PIC(headerDesc->backAndText.text);
2521 uint8_t backToken = (headerDesc->type == HEADER_EXTENDED_BACK)
2522 ? headerDesc->extendedBack.backToken
2523 : headerDesc->backAndText.token;
2524 // add back button
2525 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2526 // only make it active if valid token
2527 if (backToken != NBGL_INVALID_TOKEN) {
2528 obj = layoutAddCallbackObj(layoutInt,
2529 (nbgl_obj_t *) button,
2530 backToken,
2531 (headerDesc->type == HEADER_EXTENDED_BACK)
2532 ? headerDesc->extendedBack.tuneId
2533 : headerDesc->backAndText.tuneId);
2534 if (obj == NULL) {
2535 return -1;
2536 }
2537 }
2538
2539 button->obj.alignment = MID_LEFT;
2540 button->innerColor = WHITE;
2541 button->foregroundColor = (backToken != NBGL_INVALID_TOKEN) ? BLACK : WHITE;
2542 button->borderColor = WHITE;
2543 button->obj.area.width = BACK_KEY_WIDTH;
2544 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2545 button->text = NULL;
2546 button->icon = PIC(&LEFT_ARROW_ICON);
2547 button->obj.touchMask = (backToken != NBGL_INVALID_TOKEN) ? (1 << TOUCHED) : 0;
2548 button->obj.touchId = BACK_BUTTON_ID;
2549 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2550 = (nbgl_obj_t *) button;
2551 layoutInt->headerContainer->nbChildren++;
2552
2553 // add optional text if needed
2554 if (text != NULL) {
2555 // add optional icon if type is HEADER_BACK_ICON_AND_TEXT
2556 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2557 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2558 image->buffer = PIC(headerDesc->backAndText.icon);
2559 image->foregroundColor = BLACK;
2560 image->obj.alignment = CENTER;
2561 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2562 = (nbgl_obj_t *) image;
2563 layoutInt->headerContainer->nbChildren++;
2564 }
2565 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2566 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2567 && (headerDesc->extendedBack.textToken != NBGL_INVALID_TOKEN)) {
2568 obj = layoutAddCallbackObj(layoutInt,
2569 (nbgl_obj_t *) textArea,
2570 headerDesc->extendedBack.textToken,
2571 headerDesc->extendedBack.tuneId);
2572 if (obj == NULL) {
2573 return -1;
2574 }
2575 textArea->obj.touchMask = (1 << TOUCHED);
2576 }
2577 textArea->obj.alignment = CENTER;
2578 textArea->textColor = BLACK;
2579 textArea->obj.area.width
2580 = layoutInt->headerContainer->obj.area.width - 2 * BACK_KEY_WIDTH;
2581 // if icon, reduce to 1 line and fit text width
2582 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2583 textArea->obj.area.width -= 16 + image->buffer->width;
2584 }
2585 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2586 textArea->text = text;
2587 textArea->fontId = SMALL_BOLD_FONT;
2588 textArea->textAlignment = CENTER;
2589 textArea->wrapping = true;
2590 uint8_t nbMaxLines = (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) ? 1 : 2;
2591 // ensure that text fits on 2 lines maximum
2592 if (nbgl_getTextNbLinesInWidth(textArea->fontId,
2593 textArea->text,
2594 textArea->obj.area.width,
2595 textArea->wrapping)
2596 > nbMaxLines) {
2598 "nbgl_layoutAddHeader: text [%s] is too long for header\n",
2599 text);
2600 }
2601 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2602 textArea->obj.area.width = nbgl_getTextWidth(textArea->fontId, textArea->text);
2603 }
2604 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2605 = (nbgl_obj_t *) textArea;
2606 layoutInt->headerContainer->nbChildren++;
2607 // if icon, realign icon & text
2608 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2609 textArea->obj.alignmentMarginX = 8 + image->buffer->width / 2;
2610 image->obj.alignmentMarginX = -8 - textArea->obj.area.width / 2;
2611 }
2612 }
2613 // add action key if the type is HEADER_EXTENDED_BACK
2614 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2615 && (headerDesc->extendedBack.actionIcon)) {
2616 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2617 // if token is valid
2618 if (headerDesc->extendedBack.actionToken != NBGL_INVALID_TOKEN) {
2619 obj = layoutAddCallbackObj(layoutInt,
2620 (nbgl_obj_t *) button,
2621 headerDesc->extendedBack.actionToken,
2622 headerDesc->extendedBack.tuneId);
2623 if (obj == NULL) {
2624 return -1;
2625 }
2626 button->obj.touchMask = (1 << TOUCHED);
2627 }
2628
2629 button->obj.alignment = MID_RIGHT;
2630 button->innerColor = WHITE;
2631 button->foregroundColor
2633 : LIGHT_GRAY;
2634 button->borderColor = WHITE;
2635 button->obj.area.width = BACK_KEY_WIDTH;
2636 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2637 button->text = NULL;
2638 button->icon = PIC(headerDesc->extendedBack.actionIcon);
2639 button->obj.touchId = EXTRA_BUTTON_ID;
2640 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2641 = (nbgl_obj_t *) button;
2642 layoutInt->headerContainer->nbChildren++;
2643 }
2644
2645 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2646 break;
2647 }
2649#ifdef TARGET_STAX
2650 // add optional back button
2651 if (headerDesc->progressAndBack.withBack) {
2652 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2653 obj = layoutAddCallbackObj(layoutInt,
2654 (nbgl_obj_t *) button,
2655 headerDesc->progressAndBack.token,
2656 headerDesc->progressAndBack.tuneId);
2657 if (obj == NULL) {
2658 return -1;
2659 }
2660
2661 button->obj.alignment = MID_LEFT;
2662 button->innerColor = WHITE;
2663 button->foregroundColor = BLACK;
2664 button->borderColor = WHITE;
2665 button->obj.area.width = BACK_KEY_WIDTH;
2666 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2667 button->text = NULL;
2668 button->icon = PIC(&LEFT_ARROW_ICON);
2669 button->obj.touchMask = (1 << TOUCHED);
2670 button->obj.touchId = BACK_BUTTON_ID;
2671 // add to container
2672 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2673 = (nbgl_obj_t *) button;
2674 layoutInt->headerContainer->nbChildren++;
2675 }
2676
2677 // add progress indicator
2678 if (headerDesc->progressAndBack.nbPages > 1
2680 nbgl_page_indicator_t *progress;
2681
2682 progress
2684 progress->activePage = headerDesc->progressAndBack.activePage;
2685 progress->nbPages = headerDesc->progressAndBack.nbPages;
2686 progress->obj.area.width = 224;
2687 progress->obj.alignment = CENTER;
2688 // add to container
2689 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2690 = (nbgl_obj_t *) progress;
2691 layoutInt->headerContainer->nbChildren++;
2692 }
2693 layoutInt->activePage = headerDesc->progressAndBack.activePage;
2694 layoutInt->nbPages = headerDesc->progressAndBack.nbPages;
2695 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2696#endif // TARGET_STAX
2697 break;
2698 }
2699 case HEADER_TITLE: {
2700 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2701 textArea->textColor = BLACK;
2702 textArea->obj.area.width = AVAILABLE_WIDTH;
2703 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2704 textArea->text = PIC(headerDesc->title.text);
2705 textArea->fontId = SMALL_BOLD_FONT;
2706 textArea->textAlignment = CENTER;
2707 textArea->wrapping = true;
2708 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2709 = (nbgl_obj_t *) textArea;
2710 layoutInt->headerContainer->nbChildren++;
2711 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2712 break;
2713 }
2714 case HEADER_RIGHT_TEXT: {
2715 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2716 obj = layoutAddCallbackObj(layoutInt,
2717 (nbgl_obj_t *) textArea,
2718 headerDesc->rightText.token,
2719 headerDesc->rightText.tuneId);
2720 if (obj == NULL) {
2721 return -1;
2722 }
2723 textArea->obj.alignment = MID_RIGHT;
2724 textArea->obj.alignmentMarginX = BORDER_MARGIN;
2725 textArea->textColor = BLACK;
2726 textArea->obj.area.width = AVAILABLE_WIDTH;
2727 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2728 textArea->text = PIC(headerDesc->rightText.text);
2729 textArea->fontId = SMALL_BOLD_FONT;
2730 textArea->textAlignment = MID_RIGHT;
2731 textArea->obj.touchMask = (1 << TOUCHED);
2732 textArea->obj.touchId = TOP_RIGHT_BUTTON_ID;
2733 // add to bottom container
2734 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2735 = (nbgl_obj_t *) textArea;
2736 layoutInt->headerContainer->nbChildren++;
2737 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2738 break;
2739 }
2740 default:
2741 return -2;
2742 }
2743
2744 if (headerDesc->separationLine) {
2745 line = createHorizontalLine(layoutInt->layer);
2746 line->obj.alignment = BOTTOM_MIDDLE;
2747 line->offset = 3;
2748 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2749 = (nbgl_obj_t *) line;
2750 layoutInt->headerContainer->nbChildren++;
2751 }
2752 if (separationLine != NULL) {
2753 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2754 = (nbgl_obj_t *) separationLine;
2755 layoutInt->headerContainer->nbChildren++;
2756 }
2757 // header must be the first child
2758 layoutInt->children[HEADER_INDEX] = (nbgl_obj_t *) layoutInt->headerContainer;
2759
2760 // subtract header height from main container height
2761 layoutInt->container->obj.area.height -= layoutInt->headerContainer->obj.area.height;
2762 layoutInt->container->obj.alignTo = (nbgl_obj_t *) layoutInt->headerContainer;
2763 layoutInt->container->obj.alignment = BOTTOM_LEFT;
2764
2765 layoutInt->headerType = headerDesc->type;
2766
2767 return layoutInt->headerContainer->obj.area.height;
2768}
2769
2779{
2780 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2781 layoutObj_t *obj;
2782 nbgl_text_area_t *textArea;
2783 nbgl_line_t *line, *separationLine = NULL;
2784 nbgl_button_t *button;
2785
2786 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddExtendedFooter():\n");
2787 if (layout == NULL) {
2788 return -1;
2789 }
2790 if ((footerDesc == NULL) || (footerDesc->type >= NB_FOOTER_TYPES)) {
2791 return -2;
2792 }
2793
2794 layoutInt->footerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2795 layoutInt->footerContainer->obj.area.width = AVAILABLE_WIDTH;
2796 layoutInt->footerContainer->layout = VERTICAL;
2797 layoutInt->footerContainer->children
2798 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2799 layoutInt->footerContainer->obj.alignment = BOTTOM_MIDDLE;
2800
2801 switch (footerDesc->type) {
2802 case FOOTER_EMPTY: {
2803 layoutInt->footerContainer->obj.area.height = footerDesc->emptySpace.height;
2804 break;
2805 }
2806 case FOOTER_SIMPLE_TEXT: {
2807 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2808 obj = layoutAddCallbackObj(layoutInt,
2809 (nbgl_obj_t *) textArea,
2810 footerDesc->simpleText.token,
2811 footerDesc->simpleText.tuneId);
2812 if (obj == NULL) {
2813 return -1;
2814 }
2815
2816 textArea->obj.alignment = BOTTOM_MIDDLE;
2817 textArea->textColor = (footerDesc->simpleText.mutedOut) ? DARK_GRAY : BLACK;
2818 textArea->obj.area.width = AVAILABLE_WIDTH;
2819 textArea->obj.area.height
2820 = (footerDesc->simpleText.mutedOut) ? SMALL_FOOTER_HEIGHT : SIMPLE_FOOTER_HEIGHT;
2821 textArea->text = PIC(footerDesc->simpleText.text);
2822 textArea->fontId
2823 = (footerDesc->simpleText.mutedOut) ? SMALL_REGULAR_FONT : SMALL_BOLD_FONT;
2824 textArea->textAlignment = CENTER;
2825 textArea->obj.touchMask = (1 << TOUCHED);
2826 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2827 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2828 = (nbgl_obj_t *) textArea;
2829 layoutInt->footerContainer->nbChildren++;
2830 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2831 break;
2832 }
2833 case FOOTER_DOUBLE_TEXT: {
2834 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2835 obj = layoutAddCallbackObj(layoutInt,
2836 (nbgl_obj_t *) textArea,
2837 footerDesc->doubleText.leftToken,
2838 footerDesc->doubleText.tuneId);
2839 if (obj == NULL) {
2840 return -1;
2841 }
2842 textArea->obj.alignment = BOTTOM_LEFT;
2843 textArea->textColor = BLACK;
2844 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2845 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2846 textArea->text = PIC(footerDesc->doubleText.leftText);
2847 textArea->fontId = SMALL_BOLD_FONT;
2848 textArea->textAlignment = CENTER;
2849 textArea->obj.touchMask = (1 << TOUCHED);
2850 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2851 // add to bottom container
2852 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2853 = (nbgl_obj_t *) textArea;
2854 layoutInt->footerContainer->nbChildren++;
2855
2856 // create right touchable text
2857 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2858 obj = layoutAddCallbackObj(layoutInt,
2859 (nbgl_obj_t *) textArea,
2860 footerDesc->doubleText.rightToken,
2861 footerDesc->doubleText.tuneId);
2862 if (obj == NULL) {
2863 return -1;
2864 }
2865
2866 textArea->obj.alignment = BOTTOM_RIGHT;
2867 textArea->textColor = BLACK;
2868 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2869 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2870 textArea->text = PIC(footerDesc->doubleText.rightText);
2871 textArea->fontId = SMALL_BOLD_FONT;
2872 textArea->textAlignment = CENTER;
2873 textArea->obj.touchMask = (1 << TOUCHED);
2874 textArea->obj.touchId = RIGHT_BUTTON_ID;
2875 // add to bottom container
2876 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2877 = (nbgl_obj_t *) textArea;
2878 layoutInt->footerContainer->nbChildren++;
2879 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2880
2881 // create vertical line separating texts
2882 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
2883 separationLine->lineColor = LIGHT_GRAY;
2884 separationLine->obj.area.width = 1;
2885 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
2886 separationLine->direction = VERTICAL;
2887 separationLine->thickness = 1;
2888 separationLine->obj.alignment = MID_LEFT;
2889 separationLine->obj.alignTo = (nbgl_obj_t *) textArea;
2890 separationLine->obj.alignmentMarginX = -1;
2891 break;
2892 }
2893 case FOOTER_TEXT_AND_NAV: {
2894 layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
2895 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2896 // add touchable text on the left
2897 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2898 obj = layoutAddCallbackObj(layoutInt,
2899 (nbgl_obj_t *) textArea,
2900 footerDesc->textAndNav.token,
2901 footerDesc->textAndNav.tuneId);
2902 if (obj == NULL) {
2903 return -1;
2904 }
2905 textArea->obj.alignment = BOTTOM_LEFT;
2906 textArea->textColor = BLACK;
2907 textArea->obj.area.width = FOOTER_TEXT_AND_NAV_WIDTH;
2908 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2909 textArea->text = PIC(footerDesc->textAndNav.text);
2910 textArea->fontId = SMALL_BOLD_FONT;
2911 textArea->textAlignment = CENTER;
2912 textArea->obj.touchMask = (1 << TOUCHED);
2913 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2914 // add to bottom container
2915 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2916 = (nbgl_obj_t *) textArea;
2917 layoutInt->footerContainer->nbChildren++;
2918
2919 // add navigation on the right
2920 nbgl_container_t *navContainer
2922 navContainer->obj.area.width = AVAILABLE_WIDTH;
2923 navContainer->layout = VERTICAL;
2924 navContainer->nbChildren = 4;
2925 navContainer->children
2926 = (nbgl_obj_t **) nbgl_containerPoolGet(navContainer->nbChildren, layoutInt->layer);
2927 navContainer->obj.alignment = BOTTOM_RIGHT;
2928 navContainer->obj.area.width = SCREEN_WIDTH - textArea->obj.area.width;
2929 navContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2930 layoutNavigationPopulate(navContainer, &footerDesc->navigation, layoutInt->layer);
2931 obj = layoutAddCallbackObj(layoutInt,
2932 (nbgl_obj_t *) navContainer,
2933 footerDesc->textAndNav.navigation.token,
2934 footerDesc->textAndNav.navigation.tuneId);
2935 if (obj == NULL) {
2936 return -1;
2937 }
2938
2939 // create vertical line separating text from nav
2940 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
2941 separationLine->lineColor = LIGHT_GRAY;
2942 separationLine->obj.area.width = 1;
2943 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
2944 separationLine->direction = VERTICAL;
2945 separationLine->thickness = 1;
2946 separationLine->obj.alignment = MID_LEFT;
2947 separationLine->obj.alignTo = (nbgl_obj_t *) navContainer;
2948 separationLine->obj.alignmentMarginX = -1;
2949
2950 layoutInt->activePage = footerDesc->textAndNav.navigation.activePage;
2951 layoutInt->nbPages = footerDesc->textAndNav.navigation.nbPages;
2952 // add to bottom container
2953 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2954 = (nbgl_obj_t *) navContainer;
2955 layoutInt->footerContainer->nbChildren++;
2956 break;
2957 }
2958 case FOOTER_NAV: {
2959 layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
2960 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2962 layoutInt->footerContainer, &footerDesc->navigation, layoutInt->layer);
2963 layoutInt->footerContainer->nbChildren = 4;
2964 obj = layoutAddCallbackObj(layoutInt,
2965 (nbgl_obj_t *) layoutInt->footerContainer,
2966 footerDesc->navigation.token,
2967 footerDesc->navigation.tuneId);
2968 if (obj == NULL) {
2969 return -1;
2970 }
2971
2972 layoutInt->activePage = footerDesc->navigation.activePage;
2973 layoutInt->nbPages = footerDesc->navigation.nbPages;
2974 break;
2975 }
2976 case FOOTER_SIMPLE_BUTTON: {
2977 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2978 obj = layoutAddCallbackObj(layoutInt,
2979 (nbgl_obj_t *) button,
2980 footerDesc->button.token,
2981 footerDesc->button.tuneId);
2982 if (obj == NULL) {
2983 return -1;
2984 }
2985
2986 button->obj.alignment = CENTER;
2987 if (footerDesc->button.style == BLACK_BACKGROUND) {
2988 button->innerColor = BLACK;
2989 button->foregroundColor = WHITE;
2990 }
2991 else {
2992 button->innerColor = WHITE;
2993 button->foregroundColor = BLACK;
2994 }
2995
2996 if (footerDesc->button.style == NO_BORDER) {
2997 button->borderColor = WHITE;
2998 }
2999 else {
3000 if (footerDesc->button.style == BLACK_BACKGROUND) {
3001 button->borderColor = BLACK;
3002 }
3003 else {
3004 button->borderColor = LIGHT_GRAY;
3005 }
3006 }
3007 button->text = PIC(footerDesc->button.text);
3008 button->fontId = SMALL_BOLD_FONT;
3009 button->icon = PIC(footerDesc->button.icon);
3010 button->radius = BUTTON_RADIUS;
3011 button->obj.area.height = BUTTON_DIAMETER;
3012 layoutInt->footerContainer->obj.area.height = FOOTER_BUTTON_HEIGHT;
3013 if (footerDesc->button.text == NULL) {
3014 button->obj.area.width = BUTTON_DIAMETER;
3015 }
3016 else {
3017 button->obj.area.width = AVAILABLE_WIDTH;
3018 }
3019 button->obj.touchMask = (1 << TOUCHED);
3020 button->obj.touchId = button->text ? SINGLE_BUTTON_ID : BOTTOM_BUTTON_ID;
3021 // add to bottom container
3022 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3023 = (nbgl_obj_t *) button;
3024 layoutInt->footerContainer->nbChildren++;
3025 break;
3026 }
3027 case FOOTER_CHOICE_BUTTONS: {
3028 // texts cannot be NULL
3029 if ((footerDesc->choiceButtons.bottomText == NULL)
3030 || (footerDesc->choiceButtons.topText == NULL)) {
3031 return -1;
3032 }
3033
3034 // create bottom button (footer) at first
3035 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3036 obj = layoutAddCallbackObj(layoutInt,
3037 (nbgl_obj_t *) button,
3038 footerDesc->choiceButtons.token,
3039 footerDesc->choiceButtons.tuneId);
3040 if (obj == NULL) {
3041 return -1;
3042 }
3043 // associate with with index 1
3044 obj->index = 1;
3045 // put at the bottom of the container
3046 button->obj.alignment = BOTTOM_MIDDLE;
3047 button->innerColor = WHITE;
3048 if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3049 button->obj.alignmentMarginY
3050 = BOTTOM_BORDER_MARGIN; // 24 pixels from bottom of container
3051 button->borderColor = LIGHT_GRAY;
3052 }
3053 else {
3054 button->obj.alignmentMarginY = 4; // 4 pixels from screen bottom
3055 button->borderColor = WHITE;
3056 }
3057 button->foregroundColor = BLACK;
3058 button->obj.area.width = AVAILABLE_WIDTH;
3059 button->obj.area.height = BUTTON_DIAMETER;
3060 button->radius = BUTTON_RADIUS;
3061 button->text = PIC(footerDesc->choiceButtons.bottomText);
3062 button->fontId = SMALL_BOLD_FONT;
3063 button->obj.touchMask = (1 << TOUCHED);
3064 button->obj.touchId = CHOICE_2_ID;
3065 // add to bottom container
3066 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3067 = (nbgl_obj_t *) button;
3068 layoutInt->footerContainer->nbChildren++;
3069
3070 // add line if needed
3071 if ((footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3072 && (footerDesc->choiceButtons.style != BOTH_ROUNDED_STYLE)) {
3073 line = createHorizontalLine(layoutInt->layer);
3074 line->obj.alignment = TOP_MIDDLE;
3075 line->obj.alignmentMarginY = 4;
3076 line->obj.alignTo = (nbgl_obj_t *) button;
3077 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3078 = (nbgl_obj_t *) line;
3079 layoutInt->footerContainer->nbChildren++;
3080 }
3081
3082 // then top button, on top of it
3083 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3084 obj = layoutAddCallbackObj(layoutInt,
3085 (nbgl_obj_t *) button,
3086 footerDesc->choiceButtons.token,
3087 footerDesc->choiceButtons.tuneId);
3088 if (obj == NULL) {
3089 return -1;
3090 }
3091 // associate with with index 0
3092 obj->index = 0;
3093 button->obj.alignment = TOP_MIDDLE;
3094 button->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN; // 24 pixels from top of container
3096 button->innerColor = WHITE;
3097 button->borderColor = LIGHT_GRAY;
3098 button->foregroundColor = BLACK;
3099 }
3100 else {
3101 button->innerColor = BLACK;
3102 button->borderColor = BLACK;
3103 button->foregroundColor = WHITE;
3104 }
3105 button->obj.area.width = AVAILABLE_WIDTH;
3106 button->obj.area.height = BUTTON_DIAMETER;
3107 button->radius = BUTTON_RADIUS;
3108 button->text = PIC(footerDesc->choiceButtons.topText);
3109 button->icon = (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3110 ? PIC(footerDesc->choiceButtons.topIcon)
3111 : NULL;
3112 button->fontId = SMALL_BOLD_FONT;
3113 button->obj.touchMask = (1 << TOUCHED);
3114 button->obj.touchId = CHOICE_1_ID;
3115 // add to bottom container
3116 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3117 = (nbgl_obj_t *) button;
3118 layoutInt->footerContainer->nbChildren++;
3119
3120 if (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE) {
3121 layoutInt->footerContainer->obj.area.height = ACTION_AND_FOOTER_FOOTER_HEIGHT;
3122 }
3123 else {
3124 layoutInt->footerContainer->obj.area.height = ROUNDED_AND_FOOTER_FOOTER_HEIGHT;
3125 }
3126
3127 break;
3128 }
3129 default:
3130 return -2;
3131 }
3132
3133 // add swipable feature for navigation
3134 if ((footerDesc->type == FOOTER_NAV) || (footerDesc->type == FOOTER_TEXT_AND_NAV)) {
3135 addSwipeInternal(layoutInt,
3136 ((1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT)),
3138 (footerDesc->type == FOOTER_NAV) ? footerDesc->navigation.token
3139 : footerDesc->textAndNav.navigation.token,
3140 (footerDesc->type == FOOTER_NAV)
3141 ? footerDesc->navigation.tuneId
3142 : footerDesc->textAndNav.navigation.tuneId);
3143 }
3144
3145 if (footerDesc->separationLine) {
3146 line = createHorizontalLine(layoutInt->layer);
3147 line->obj.alignment = TOP_MIDDLE;
3148 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3149 = (nbgl_obj_t *) line;
3150 layoutInt->footerContainer->nbChildren++;
3151 }
3152 if (separationLine != NULL) {
3153 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3154 = (nbgl_obj_t *) separationLine;
3155 layoutInt->footerContainer->nbChildren++;
3156 }
3157
3158 layoutInt->children[FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->footerContainer;
3159
3160 // subtract footer height from main container height
3161 layoutInt->container->obj.area.height -= layoutInt->footerContainer->obj.area.height;
3162
3163 layoutInt->footerType = footerDesc->type;
3164
3165 return layoutInt->footerContainer->obj.area.height;
3166}
3167
3177{
3178 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3179 layoutObj_t *obj;
3180 nbgl_text_area_t *textArea;
3181 nbgl_line_t *line;
3182 nbgl_button_t *button;
3183
3184 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddUpFooter():\n");
3185 if (layout == NULL) {
3186 return -1;
3187 }
3188 if ((upFooterDesc == NULL) || (upFooterDesc->type >= NB_UP_FOOTER_TYPES)) {
3189 return -2;
3190 }
3191
3192 layoutInt->upFooterContainer
3194 layoutInt->upFooterContainer->obj.area.width = SCREEN_WIDTH;
3195 layoutInt->upFooterContainer->layout = VERTICAL;
3196 // maximum 4 children for long press button
3197 layoutInt->upFooterContainer->children
3198 = (nbgl_obj_t **) nbgl_containerPoolGet(4, layoutInt->layer);
3199 layoutInt->upFooterContainer->obj.alignTo = (nbgl_obj_t *) layoutInt->container;
3200 layoutInt->upFooterContainer->obj.alignment = BOTTOM_MIDDLE;
3201
3202 switch (upFooterDesc->type) {
3203 case UP_FOOTER_LONG_PRESS: {
3204 nbgl_progress_bar_t *progressBar;
3205
3206 obj = layoutAddCallbackObj(layoutInt,
3207 (nbgl_obj_t *) layoutInt->upFooterContainer,
3208 upFooterDesc->longPress.token,
3209 upFooterDesc->longPress.tuneId);
3210 if (obj == NULL) {
3211 return -1;
3212 }
3213 layoutInt->upFooterContainer->nbChildren = 4;
3214 layoutInt->upFooterContainer->obj.area.height = LONG_PRESS_BUTTON_HEIGHT;
3215 layoutInt->upFooterContainer->obj.touchId = LONG_PRESS_BUTTON_ID;
3216 layoutInt->upFooterContainer->obj.touchMask
3217 = ((1 << TOUCHING) | (1 << TOUCH_RELEASED) | (1 << OUT_OF_TOUCH)
3218 | (1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT));
3219
3220 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3221 button->obj.alignmentMarginX = BORDER_MARGIN;
3222 button->obj.alignment = MID_RIGHT;
3223 button->innerColor = BLACK;
3224 button->foregroundColor = WHITE;
3225 button->borderColor = BLACK;
3226 button->obj.area.width = BUTTON_DIAMETER;
3227 button->obj.area.height = BUTTON_DIAMETER;
3228 button->radius = BUTTON_RADIUS;
3229 button->icon = PIC(&VALIDATE_ICON);
3230 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3231
3232 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3233 textArea->textColor = BLACK;
3234 textArea->text = PIC(upFooterDesc->longPress.text);
3235 textArea->textAlignment = MID_LEFT;
3236 textArea->fontId = LARGE_MEDIUM_FONT;
3237 textArea->wrapping = true;
3238 textArea->obj.area.width = SCREEN_WIDTH - 3 * BORDER_MARGIN - button->obj.area.width;
3239 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3240 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3241 textArea->style = NO_STYLE;
3242 textArea->obj.alignment = MID_LEFT;
3243 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3244 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) textArea;
3245
3246 line = createHorizontalLine(layoutInt->layer);
3247 line->offset = 3;
3248 line->obj.alignment = TOP_MIDDLE;
3249 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) line;
3250
3251 progressBar = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
3252 progressBar->withBorder = false;
3253 progressBar->obj.area.width = SCREEN_WIDTH;
3254 progressBar->obj.area.height = 8;
3255 progressBar->obj.alignment = TOP_MIDDLE;
3256 progressBar->obj.alignmentMarginY = 4;
3257 progressBar->obj.alignTo = NULL;
3258 layoutInt->upFooterContainer->children[3] = (nbgl_obj_t *) progressBar;
3259 break;
3260 }
3261 case UP_FOOTER_BUTTON: {
3262 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3263 obj = layoutAddCallbackObj(layoutInt,
3264 (nbgl_obj_t *) button,
3265 upFooterDesc->button.token,
3266 upFooterDesc->button.tuneId);
3267 if (obj == NULL) {
3268 return -1;
3269 }
3270
3271 layoutInt->upFooterContainer->nbChildren = 1;
3272 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3273 button->obj.alignment = CENTER;
3274
3275 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3276 button->innerColor = BLACK;
3277 button->foregroundColor = WHITE;
3278 }
3279 else {
3280 button->innerColor = WHITE;
3281 button->foregroundColor = BLACK;
3282 }
3283 if (upFooterDesc->button.style == NO_BORDER) {
3284 button->borderColor = WHITE;
3285 }
3286 else {
3287 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3288 button->borderColor = BLACK;
3289 }
3290 else {
3291 button->borderColor = LIGHT_GRAY;
3292 }
3293 }
3294 button->text = PIC(upFooterDesc->button.text);
3295 button->fontId = SMALL_BOLD_FONT;
3296 button->icon = PIC(upFooterDesc->button.icon);
3297 button->obj.area.width = AVAILABLE_WIDTH;
3298 button->obj.area.height = BUTTON_DIAMETER;
3299 button->radius = BUTTON_RADIUS;
3300
3301 button->obj.alignTo = NULL;
3302 button->obj.touchMask = (1 << TOUCHED);
3303 button->obj.touchId = SINGLE_BUTTON_ID;
3304 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3305 break;
3306 }
3308 // icon & text cannot be NULL
3309 if ((upFooterDesc->horizontalButtons.leftIcon == NULL)
3310 || (upFooterDesc->horizontalButtons.rightText == NULL)) {
3311 return -1;
3312 }
3313
3314 layoutInt->upFooterContainer->nbChildren = 2;
3315 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3316
3317 // create left button (in white) at first
3318 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3319 obj = layoutAddCallbackObj(layoutInt,
3320 (nbgl_obj_t *) button,
3321 upFooterDesc->horizontalButtons.leftToken,
3322 upFooterDesc->horizontalButtons.tuneId);
3323 if (obj == NULL) {
3324 return -1;
3325 }
3326 // associate with with index 1
3327 obj->index = 1;
3328 button->obj.alignment = MID_LEFT;
3329 button->obj.alignmentMarginX = BORDER_MARGIN;
3330 button->borderColor = LIGHT_GRAY;
3331 button->innerColor = WHITE;
3332 button->foregroundColor = BLACK;
3333 button->obj.area.width = BUTTON_DIAMETER;
3334 button->obj.area.height = BUTTON_DIAMETER;
3335 button->radius = BUTTON_RADIUS;
3336 button->icon = PIC(upFooterDesc->horizontalButtons.leftIcon);
3337 button->fontId = SMALL_BOLD_FONT;
3338 button->obj.touchMask = (1 << TOUCHED);
3339 button->obj.touchId = CHOICE_2_ID;
3340 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3341
3342 // then black button, on right
3343 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3344 obj = layoutAddCallbackObj(layoutInt,
3345 (nbgl_obj_t *) button,
3346 upFooterDesc->horizontalButtons.rightToken,
3347 upFooterDesc->horizontalButtons.tuneId);
3348 if (obj == NULL) {
3349 return -1;
3350 }
3351 // associate with with index 0
3352 obj->index = 0;
3353 button->obj.alignment = MID_RIGHT;
3354 button->obj.alignmentMarginX = BORDER_MARGIN;
3355 button->innerColor = BLACK;
3356 button->borderColor = BLACK;
3357 button->foregroundColor = WHITE;
3358 button->obj.area.width = AVAILABLE_WIDTH - BUTTON_DIAMETER - 16;
3359 button->obj.area.height = BUTTON_DIAMETER;
3360 button->radius = BUTTON_RADIUS;
3361 button->text = PIC(upFooterDesc->horizontalButtons.rightText);
3362 button->fontId = SMALL_BOLD_FONT;
3363 button->obj.touchMask = (1 << TOUCHED);
3364 button->obj.touchId = CHOICE_1_ID;
3365 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) button;
3366 break;
3367 }
3368 case UP_FOOTER_TIP_BOX: {
3369 // text cannot be NULL
3370 if (upFooterDesc->tipBox.text == NULL) {
3371 return -1;
3372 }
3373 obj = layoutAddCallbackObj(layoutInt,
3374 (nbgl_obj_t *) layoutInt->upFooterContainer,
3375 upFooterDesc->tipBox.token,
3376 upFooterDesc->tipBox.tuneId);
3377 if (obj == NULL) {
3378 return -1;
3379 }
3380 layoutInt->upFooterContainer->nbChildren = 3;
3381 layoutInt->upFooterContainer->obj.touchId = TIP_BOX_ID;
3382 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3383
3384 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3385 textArea->textColor = BLACK;
3386 textArea->text = PIC(upFooterDesc->tipBox.text);
3387 textArea->textAlignment = MID_LEFT;
3388 textArea->fontId = SMALL_REGULAR_FONT;
3389 textArea->wrapping = true;
3390 textArea->obj.area.width = AVAILABLE_WIDTH;
3391 if (upFooterDesc->tipBox.icon != NULL) {
3392 textArea->obj.area.width
3393 -= ((nbgl_icon_details_t *) PIC(upFooterDesc->tipBox.icon))->width
3394 + BORDER_MARGIN;
3395 }
3396 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3397 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3398 textArea->obj.alignment = MID_LEFT;
3399 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3400 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3401 layoutInt->upFooterContainer->obj.area.height = textArea->obj.area.height;
3402
3403 line = createHorizontalLine(layoutInt->layer);
3404 line->offset = 3;
3405 line->obj.alignment = TOP_MIDDLE;
3406 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) line;
3407
3408 if (upFooterDesc->tipBox.icon != NULL) {
3409 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
3410 image->obj.alignmentMarginX = BORDER_MARGIN;
3411 image->obj.alignment = MID_RIGHT;
3412 image->foregroundColor = BLACK;
3413 image->buffer = PIC(upFooterDesc->tipBox.icon);
3414 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) image;
3415 if (layoutInt->upFooterContainer->obj.area.height < image->buffer->height) {
3416 layoutInt->upFooterContainer->obj.area.height = image->buffer->height;
3417 }
3418 }
3419 layoutInt->upFooterContainer->obj.area.height += 2 * BOTTOM_BORDER_MARGIN;
3420
3421 break;
3422 }
3423 case UP_FOOTER_TEXT: {
3424 obj = layoutAddCallbackObj(layoutInt,
3425 (nbgl_obj_t *) layoutInt->upFooterContainer,
3426 upFooterDesc->text.token,
3427 upFooterDesc->text.tuneId);
3428 if (obj == NULL) {
3429 return -1;
3430 }
3431 layoutInt->upFooterContainer->nbChildren = 1;
3432 layoutInt->upFooterContainer->obj.area.height = SMALL_FOOTER_HEIGHT;
3433 layoutInt->upFooterContainer->obj.touchId = WHOLE_SCREEN_ID;
3434 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3435
3436 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3437 textArea->textColor = DARK_GRAY;
3438 textArea->text = PIC(upFooterDesc->text.text);
3439 textArea->textAlignment = CENTER;
3440 textArea->fontId = SMALL_REGULAR_FONT;
3441 textArea->wrapping = true;
3442 textArea->obj.area.width = AVAILABLE_WIDTH;
3443 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3444 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3445 textArea->obj.alignment = CENTER;
3446 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3447 break;
3448 }
3449 default:
3450 return -2;
3451 }
3452
3453 // subtract up footer height from main container height
3454 layoutInt->container->obj.area.height -= layoutInt->upFooterContainer->obj.area.height;
3455
3456 layoutInt->children[UP_FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->upFooterContainer;
3457
3458 layoutInt->upFooterType = upFooterDesc->type;
3459
3460 return layoutInt->upFooterContainer->obj.area.height;
3461}
3462
3477 uint8_t activePage,
3478 uint8_t nbPages,
3479 bool withBack,
3480 uint8_t backToken,
3481 tune_index_e tuneId)
3482{
3484 .separationLine = false,
3485 .progressAndBack.activePage = activePage,
3486 .progressAndBack.nbPages = nbPages,
3487 .progressAndBack.token = backToken,
3488 .progressAndBack.tuneId = tuneId,
3489 .progressAndBack.withBack = withBack,
3490 .progressAndBack.actionIcon = NULL,
3491 .progressAndBack.actionToken = NBGL_INVALID_TOKEN};
3492 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressIndicator():\n");
3493
3494 return nbgl_layoutAddHeader(layout, &headerDesc);
3495}
3496
3508 const char *text,
3509 const char *subText,
3510 uint8_t initPosition)
3511{
3512 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3513 nbgl_container_t *container;
3514 nbgl_text_area_t *textArea;
3515 nbgl_spinner_t *spinner;
3516
3517 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSpinner():\n");
3518 if (layout == NULL) {
3519 return -1;
3520 }
3521
3522 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
3523 // spinner + text + subText
3524 container->nbChildren = 3;
3525 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
3526
3527 container->obj.area.width = AVAILABLE_WIDTH;
3528 container->layout = VERTICAL;
3529 container->obj.alignment = CENTER;
3530
3531 // create spinner
3532 spinner = (nbgl_spinner_t *) nbgl_objPoolGet(SPINNER, layoutInt->layer);
3533 spinner->position = initPosition;
3534 spinner->obj.alignment = TOP_MIDDLE;
3535 // set this new spinner as child of the container
3536 container->children[0] = (nbgl_obj_t *) spinner;
3537
3538 // update container height
3539 container->obj.area.height += SPINNER_HEIGHT;
3540
3541 // create text area
3542 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3543 textArea->textColor = BLACK;
3544 textArea->text = PIC(text);
3545 textArea->textAlignment = CENTER;
3546 textArea->fontId = (subText != NULL) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
3547 textArea->wrapping = true;
3548 textArea->obj.alignmentMarginY = SPINNER_TEXT_MARGIN;
3549 textArea->obj.alignTo = (nbgl_obj_t *) spinner;
3550 textArea->obj.alignment = BOTTOM_MIDDLE;
3551 textArea->obj.area.width = AVAILABLE_WIDTH;
3552 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3553 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3554 textArea->style = NO_STYLE;
3555
3556 // update container height
3557 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
3558
3559 // set this text as child of the container
3560 container->children[1] = (nbgl_obj_t *) textArea;
3561
3562 if (subText != NULL) {
3563 nbgl_text_area_t *subTextArea;
3564 // create sub-text area
3565 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3566 subTextArea->textColor = BLACK;
3567 subTextArea->text = PIC(subText);
3568 subTextArea->textAlignment = CENTER;
3569 subTextArea->fontId = SMALL_REGULAR_FONT;
3570 subTextArea->wrapping = true;
3571 subTextArea->obj.alignmentMarginY = SPINNER_INTER_TEXTS_MARGIN;
3572 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
3573 subTextArea->obj.alignment = BOTTOM_MIDDLE;
3574 subTextArea->obj.area.width = AVAILABLE_WIDTH;
3575 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
3576 subTextArea->text,
3577 subTextArea->obj.area.width,
3578 subTextArea->wrapping);
3579 subTextArea->style = NO_STYLE;
3580
3581 // update container height
3582 container->obj.area.height
3583 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
3584
3585 // set thissub-text as child of the container
3586 container->children[2] = (nbgl_obj_t *) subTextArea;
3587 }
3588 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
3589
3590 if (initPosition != SPINNER_FIXED) {
3591 // update ticker to update the spinner periodically
3593
3594 tickerCfg.tickerIntervale = SPINNER_REFRESH_PERIOD; // ms
3595 tickerCfg.tickerValue = SPINNER_REFRESH_PERIOD; // ms
3596 tickerCfg.tickerCallback = &spinnerTickerCallback;
3597 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
3598 }
3599
3600 return 0;
3601}
3602
3615 const char *text,
3616 const char *subText,
3617 uint8_t position)
3618{
3619 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3620 nbgl_container_t *container;
3621 nbgl_text_area_t *textArea;
3622 nbgl_spinner_t *spinner;
3623 int ret = 0;
3624
3625 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutUpdateSpinner():\n");
3626 if ((layout == NULL) || (layoutInt->container->nbChildren == 0)) {
3627 return -1;
3628 }
3629
3630 container = (nbgl_container_t *) layoutInt->container->children[0];
3631 if ((container->obj.type != CONTAINER) || (container->nbChildren < 2)) {
3632 return -1;
3633 }
3634
3635 spinner = (nbgl_spinner_t *) container->children[0];
3636 if (spinner->obj.type != SPINNER) {
3637 return -1;
3638 }
3639 // if position is different, redraw
3640 if (spinner->position != position) {
3641 spinner->position = position;
3642 nbgl_objDraw((nbgl_obj_t *) spinner);
3643 ret = 1;
3644 }
3645
3646 // update text area if necessary
3647 textArea = (nbgl_text_area_t *) container->children[1];
3648 if (textArea->obj.type != TEXT_AREA) {
3649 return -1;
3650 }
3651 const char *newText = PIC(text);
3652 size_t newTextLen = strlen(newText);
3653 // if text is different, redraw (don't use strcmp because it crashes with Rust SDK)
3654 if ((newTextLen != strlen(textArea->text)) || memcmp(textArea->text, newText, newTextLen)) {
3655 textArea->text = newText;
3656 nbgl_objDraw((nbgl_obj_t *) textArea);
3657 ret = 2;
3658 }
3659
3660 if (subText != NULL) {
3661 nbgl_text_area_t *subTextArea;
3662
3663 if (container->nbChildren != 3) {
3664 return -1;
3665 }
3666 subTextArea = (nbgl_text_area_t *) container->children[2];
3667 if (subTextArea->obj.type != TEXT_AREA) {
3668 return -1;
3669 }
3670 const char *newSubText = PIC(subText);
3671 size_t newSubTextLen = strlen(newSubText);
3672 // if text is different, redraw
3673 if ((newSubTextLen != strlen(subTextArea->text))
3674 || memcmp(subTextArea->text, newSubText, newSubTextLen)) {
3675 subTextArea->text = newSubText;
3676 nbgl_objDraw((nbgl_obj_t *) subTextArea);
3677 ret = 2;
3678 }
3679 }
3680
3681 return ret;
3682}
3683
3691{
3692 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3693
3694 if (layout == NULL) {
3695 return -1;
3696 }
3698 "nbgl_layoutDraw(): container.nbChildren =%d, layout->nbChildren = %d\n",
3699 layout->container->nbChildren,
3700 layout->nbChildren);
3701 if (layout->tapText) {
3702 // set this new container as child of main container
3703 layoutAddObject(layout, (nbgl_obj_t *) layout->tapText);
3704 }
3705 if (layout->withLeftBorder == true) {
3706 // draw now the line
3707 nbgl_line_t *line = createLeftVerticalLine(layout->layer);
3708 layout->children[LEFT_BORDER_INDEX] = (nbgl_obj_t *) line;
3709 }
3711
3712 return 0;
3713}
3714
3722{
3723 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3724 LOG_DEBUG(PAGE_LOGGER, "nbgl_layoutRelease(): \n");
3725 if (layout == NULL) {
3726 return -1;
3727 }
3728 // if modal
3729 if (layout->modal) {
3730 nbgl_screenPop(layout->layer);
3731 }
3732 layout->nbChildren = 0;
3733 return 0;
3734}
3735
3736#endif // HAVE_SE_TOUCH
Random Number Generation.
@ ANIM_ILLUSTRATION
animation
@ ICON_ILLUSTRATION
simple icon
@ LARGE_CASE_BOLD_INFO
@ NORMAL_INFO
@ LARGE_CASE_GRAY_INFO
debug traces management
#define LOG_WARN(__logger,...)
Definition nbgl_debug.h:87
#define LOG_DEBUG(__logger,...)
Definition nbgl_debug.h:86
#define LOG_FATAL(__logger,...)
Definition nbgl_debug.h:88
@ LAYOUT_LOGGER
Definition nbgl_debug.h:33
@ PAGE_LOGGER
Definition nbgl_debug.h:34
#define qrcode
Definition nbgl_draw.c:46
Middle Level API of the new BOLOS Graphical Library.
#define QR_V10_NB_PIX_SIZE
Definition nbgl_draw.h:24
#define QR_V4_NB_PIX_SIZE
Definition nbgl_draw.h:23
uint8_t nbgl_getFontHeight(nbgl_font_id_e fontId)
return the height in pixels of the font with the given font ID
Definition nbgl_fonts.c:388
uint16_t nbgl_getTextWidth(nbgl_font_id_e fontId, const char *text)
return the max width in pixels of the given text (can be multiline)
Definition nbgl_fonts.c:350
uint16_t nbgl_getTextHeightInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
return the height of the given multiline text, with the given font.
uint16_t nbgl_getTextNbLinesInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
compute the number of lines of the given text fitting in the given maxWidth
Definition nbgl_fonts.c:721
const nbgl_font_t * nbgl_getFont(nbgl_font_id_e fontId)
uint8_t nbgl_getFontLineHeight(nbgl_font_id_e fontId)
return the height in pixels of the line of font with the given font ID
Definition nbgl_fonts.c:400
Font screen low-Level driver API, to draw elementary forms.
int nbgl_layoutAddTagValueList(nbgl_layout_t *layout, const nbgl_layoutTagValueList_t *list)
Creates a list of [tag,value] pairs.
int nbgl_layoutAddContentCenter(nbgl_layout_t *layout, const nbgl_contentCenter_t *info)
Creates an area on the center of the main panel, with a possible icon, and possible texts under it.
#define HOLD_TO_APPROVE_STEP_PERCENT
int nbgl_layoutAddUpFooter(nbgl_layout_t *layout, const nbgl_layoutUpFooter_t *upFooterDesc)
Creates a touchable area on top of the footer of the screen, containing various controls,...
int nbgl_layoutDraw(nbgl_layout_t *layoutParam)
Applies given layout. The screen will be redrawn.
#define NB_MAX_CONTAINER_CHILDREN
Definition nbgl_layout.c:38
int nbgl_layoutAddTextContent(nbgl_layout_t *layout, const char *title, const char *description, const char *info)
Creates in the main container three text areas:
int nbgl_layoutAddText(nbgl_layout_t *layout, const char *text, const char *subText)
Creates an area with given text (in bold) and sub text (in regular)
int nbgl_layoutAddProgressIndicator(nbgl_layout_t *layout, uint8_t activePage, uint8_t nbPages, bool withBack, uint8_t backToken, tune_index_e tuneId)
Creates a kind of navigation bar with an optional <- arrow on the left. This widget is placed on top ...
int nbgl_layoutAddNavigationBar(nbgl_layout_t *layout, const nbgl_layoutNavigationBar_t *info)
Creates a navigation bar on bottom of main container.
int nbgl_layoutAddSeparationLine(nbgl_layout_t *layout)
adds a separation line on bottom of the last added item
int nbgl_layoutAddQRCode(nbgl_layout_t *layout, const nbgl_layoutQRCode_t *info)
Creates an area on the center of the main panel, with a QRCode, a possible text in black (bold) under...
int nbgl_layoutAddRadioChoice(nbgl_layout_t *layout, const nbgl_layoutRadioChoice_t *choices)
Creates a list of radio buttons (on the right)
int nbgl_layoutAddCenteredInfo(nbgl_layout_t *layout, const nbgl_layoutCenteredInfo_t *info)
Creates an area on the center of the main panel, with a possible icon/image, a possible text in black...
int nbgl_layoutAddSplitFooter(nbgl_layout_t *layout, const char *leftText, uint8_t leftToken, const char *rightText, uint8_t rightToken, tune_index_e tuneId)
Creates 2 touchable texts at the footer of the screen, separated with a thin line from the rest of th...
int nbgl_layoutAddSubHeaderText(nbgl_layout_t *layout, const char *text)
Creates an area with given text in small regular font, under the header.
int nbgl_layoutAddTextWithAlias(nbgl_layout_t *layout, const char *text, const char *subText, uint8_t token, uint8_t index)
Creates an area with given text (in bold) and sub text (in regular), with a ‍icon on right of text to...
int nbgl_layoutAddTouchableBar(nbgl_layout_t *layout, const nbgl_layoutBar_t *barLayout)
Creates a touchable bar in main panel.
#define SPINNER_REFRESH_PERIOD
Definition nbgl_layout.c:93
int nbgl_layoutAddTopRightButton(nbgl_layout_t *layout, const nbgl_icon_details_t *icon, uint8_t token, tune_index_e tuneId)
Creates a Top-right button in the top right corner of the top panel.
int nbgl_layoutAddSwipe(nbgl_layout_t *layout, uint16_t swipesMask, const char *text, uint8_t token, tune_index_e tuneId)
Creates a swipe interaction on the main container.
int nbgl_layoutAddSwitch(nbgl_layout_t *layout, const nbgl_layoutSwitch_t *switchLayout)
Creates a switch with the given text and its state.
int nbgl_layoutAddHorizontalButtons(nbgl_layout_t *layout, const nbgl_layoutHorizontalButtons_t *info)
Creates two buttons to make a choice. Both buttons are mandatory The left one contains only an icon a...
listItemType_t
@ NB_ITEM_TYPES
@ TOUCHABLE_BAR_ITEM
@ SWITCH_ITEM
int nbgl_layoutAddButton(nbgl_layout_t *layout, const nbgl_layoutButton_t *buttonInfo)
Creates a rounded button in the main container.
nbgl_layout_t * nbgl_layoutGet(const nbgl_layoutDescription_t *description)
returns a layout of the given type. The layout is reset
int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_t *footerDesc)
Creates a touchable area at the footer of the screen, containing various controls,...
int nbgl_layoutAddLeftContent(nbgl_layout_t *layout, const nbgl_layoutLeftContent_t *info)
Creates an area with a title, and rows of icon + text, left aligned.
const char * get_ux_loc_string(uint32_t index)
#define NB_MAX_LAYOUTS
Definition nbgl_layout.c:35
int nbgl_layoutAddChoiceButtons(nbgl_layout_t *layout, const nbgl_layoutChoiceButtons_t *info)
Creates two buttons to make a choice. Both buttons are mandatory. Both buttons are full width,...
int nbgl_layoutRelease(nbgl_layout_t *layoutParam)
Release the layout obtained with nbgl_layoutGet()
int nbgl_layoutAddSpinner(nbgl_layout_t *layout, const char *text, const char *subText, uint8_t initPosition)
Creates a centered (vertically & horizontally) spinner with a text under it.
int nbgl_layoutUpdateSpinner(nbgl_layout_t *layout, const char *text, const char *subText, uint8_t position)
Update an existing spinner (must be the only object of the layout)
int nbgl_layoutAddLongPressButton(nbgl_layout_t *layout, const char *text, uint8_t token, tune_index_e tuneId)
Creates a long press button in the main container.
void layoutAddObject(nbgl_layoutInternal_t *layout, nbgl_obj_t *obj)
adds the given obj to the main container
int nbgl_layoutAddProgressBar(nbgl_layout_t *layout, const char *text, const char *subText, uint8_t percentage)
Creates an area in main panel to display a progress bar, with a title text and a subtext if needed.
int nbgl_layoutAddHeader(nbgl_layout_t *layout, const nbgl_layoutHeader_t *headerDesc)
Creates a touchable (or not) area at the header of the screen, containing various controls,...
int nbgl_layoutAddBottomButton(nbgl_layout_t *layout, const nbgl_icon_details_t *icon, uint8_t token, bool separationLine, tune_index_e tuneId)
Creates a centered button at bottom of main container.
#define HOLD_TO_APPROVE_STEP_DURATION_MS
int nbgl_layoutAddFooter(nbgl_layout_t *layout, const char *text, uint8_t token, tune_index_e tuneId)
Creates a touchable text at the footer of the screen, separated with a thin line from the rest of the...
int nbgl_layoutAddLargeCaseText(nbgl_layout_t *layout, const char *text, bool grayedOut)
Creates an area with given text in 32px font (in Black or Light Gray)
layoutObj_t * layoutAddCallbackObj(nbgl_layoutInternal_t *layout, nbgl_obj_t *obj, uint8_t token, tune_index_e tuneId)
void(* nbgl_layoutTouchCallback_t)(int token, uint8_t index)
prototype of function to be called when an object is touched
@ WHITE_BACKGROUND
rounded bordered button, with text/icon in black, on white background
@ NO_BORDER
simple clickable text, in black
@ BLACK_BACKGROUND
rounded bordered button, with text/icon in white, on black background
#define AVAILABLE_WIDTH
Definition nbgl_layout.h:94
@ UP_FOOTER_TEXT
grayed-out text, for example "Tap to continue"
@ NB_UP_FOOTER_TYPES
@ UP_FOOTER_BUTTON
simple button
@ UP_FOOTER_LONG_PRESS
long-press button
@ UP_FOOTER_TIP_BOX
Tip-box.
@ UP_FOOTER_HORIZONTAL_BUTTONS
2 buttons, on the same line
void * nbgl_layout_t
type shared externally
@ HEADER_TITLE
simple centered text
@ HEADER_BACK_AND_PROGRESS
optional back key and progress indicator (only on Stax)
@ HEADER_EMPTY
empty space, to have a better vertical centering of centered info
@ NB_HEADER_TYPES
@ HEADER_EXTENDED_BACK
back key, centered text and touchable key on the right
@ HEADER_BACK_AND_TEXT
back key and optional text
@ HEADER_RIGHT_TEXT
touchable text on the right, with a vertical separation line
@ HEADER_BACK_ICON_AND_TEXT
back key and optional icon and text
#define NBGL_INVALID_TOKEN
Definition nbgl_layout.h:33
#define SPINNER_FIXED
position to use for a "fixed" spinner
@ SOFT_ACTION_AND_FOOTER_STYLE
A white button on top of a footer, with a separation line.
@ BOTH_ROUNDED_STYLE
A black button on top of a white button.
@ ROUNDED_AND_FOOTER_STYLE
A black background button on top of a footer.
#define NBGL_NO_PROGRESS_INDICATOR
To be used when a control token shall not be used.
Definition nbgl_layout.h:30
#define NBGL_NO_TUNE
Definition nbgl_layout.h:29
@ FOOTER_SIMPLE_TEXT
simple touchable text in bold
@ FOOTER_NAV
navigation bar
@ FOOTER_SIMPLE_BUTTON
simple black or white button (see nbgl_layoutButtonStyle_t)
@ FOOTER_TEXT_AND_NAV
@ FOOTER_DOUBLE_TEXT
2 touchable texts in bold, separated by a vertical line (only on Stax)
@ NB_FOOTER_TYPES
@ FOOTER_EMPTY
empty space, to have a better vertical centering of centered info
@ FOOTER_CHOICE_BUTTONS
double buttons (see nbgl_layoutChoiceButtonsStyle_t)
Internal functions/constants of NBGL layout layer.
nbgl_swipe_usage_t
@ SWIPE_USAGE_CUSTOM
@ SWIPE_USAGE_SUGGESTIONS
@ SWIPE_USAGE_NAVIGATION
@ UP_FOOTER_INDEX
@ FOOTER_INDEX
@ HEADER_INDEX
@ TOP_RIGHT_BUTTON_INDEX
@ MAIN_CONTAINER_INDEX
@ LEFT_BORDER_INDEX
@ NB_MAX_SCREEN_CHILDREN
bool keyboardSwipeCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType)
void layoutNavigationPopulate(nbgl_container_t *navContainer, const nbgl_layoutNavigationBar_t *navConfig, uint8_t layer)
This function creates a full navigation bar "object", with buttons and returns it as a container.
#define LAYOUT_OBJ_POOL_LEN
Max number of complex objects with callback retrievable from pool.
bool layoutNavigationCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType, uint8_t nbPages, uint8_t *activePage)
function to be called when any of the controls in navigation bar is touched
API to draw all basic graphic objects.
struct PACKED__ nbgl_line_s nbgl_line_t
struct to represent a vertical or horizontal line
struct PACKED__ nbgl_navigation_bar_s nbgl_page_indicator_t
struct to represent a navigation bar (PAGE_INDICATOR type) There can be up to 5 page indicators,...
struct PACKED__ nbgl_radio_s nbgl_radio_t
struct to represent a radio button (RADIO_BUTTON type)
#define NB_SPINNER_POSITIONS
Definition nbgl_obj.h:185
void(* nbgl_touchCallback_t)(void *obj, nbgl_touchType_t eventType)
prototype of function to be called when a touch event is received by an object
Definition nbgl_obj.h:246
struct PACKED__ nbgl_text_area_s nbgl_text_area_t
struct to represent a text area (TEXT_AREA type)
void nbgl_objDraw(nbgl_obj_t *obj)
This function draws or redraws the given object and its children (recursive version)
Definition nbgl_obj.c:1622
struct PACKED__ nbgl_progress_bar_s nbgl_progress_bar_t
struct to represent a progress bar (PROGRESS_BAR type)
nbgl_obj_t ** nbgl_containerPoolGet(uint8_t nbObjs, uint8_t layer)
Gets a new container from the pool, with the given number of obj pointers.
#define SWIPE_MASK
Definition nbgl_obj.h:192
nbgl_obj_t * nbgl_objPoolGet(nbgl_obj_type_t type, uint8_t layer)
Gets a new graphic object from the pool, with the given type. The type field of the object is set.
void nbgl_refreshSpecial(nbgl_refresh_mode_t mode)
This functions refreshes the actual screen on display with what has changed since the last refresh,...
Definition nbgl_obj.c:1671
struct PACKED__ nbgl_image_s nbgl_image_t
struct to represent an image (IMAGE type)
struct PACKED__ nbgl_button_s nbgl_button_t
struct to represent a button (BUTTON type) that can contain a text and/or an icon
void nbgl_refreshSpecialWithPostRefresh(nbgl_refresh_mode_t mode, nbgl_post_refresh_t post_refresh)
Definition nbgl_obj.c:1687
@ CHOICE_1_ID
Definition nbgl_obj.h:578
@ TOP_RIGHT_BUTTON_ID
Definition nbgl_obj.h:574
@ LONG_PRESS_BUTTON_ID
Definition nbgl_obj.h:587
@ CONTROLS_ID
Definition nbgl_obj.h:589
@ RIGHT_BUTTON_ID
Definition nbgl_obj.h:572
@ WHOLE_SCREEN_ID
Definition nbgl_obj.h:573
@ BOTTOM_BUTTON_ID
Definition nbgl_obj.h:570
@ SINGLE_BUTTON_ID
Definition nbgl_obj.h:576
@ BACK_BUTTON_ID
Definition nbgl_obj.h:575
@ VALUE_BUTTON_1_ID
Definition nbgl_obj.h:584
@ TIP_BOX_ID
Definition nbgl_obj.h:588
@ EXTRA_BUTTON_ID
Definition nbgl_obj.h:577
@ CHOICE_2_ID
Definition nbgl_obj.h:579
struct PACKED__ nbgl_container_s nbgl_container_t
struct to represent a container (CONTAINER type)
struct PACKED__ nbgl_switch_s nbgl_switch_t
struct to represent a switch (size is fixed) (SWITCH type)
struct PACKED__ nbgl_obj_s nbgl_obj_t
Common structure for all graphical objects.
struct PACKED__ nbgl_spinner_s nbgl_spinner_t
struct to represent a "spinner", represented by the Ledger corners, in gray, with one of the corners ...
struct PACKED__ nbgl_qrcode_s nbgl_qrcode_t
struct to represent a QR code (QR_CODE type), whose size is fixed
API to manage screens.
int nbgl_screenSet(nbgl_obj_t ***elements, uint8_t nbElements, const nbgl_screenTickerConfiguration_t *ticker, nbgl_touchCallback_t touchCallback)
Configures the lowest layer screen. To be used by applications A nbgl_screenRedraw() can be called af...
int nbgl_screenPush(nbgl_obj_t ***elements, uint8_t nbElements, const nbgl_screenTickerConfiguration_t *ticker, nbgl_touchCallback_t touchCallback)
Pushes a screen on top of the stack, with the given number of elements, if possible....
void nbgl_wait_pipeline(void)
int nbgl_screenUpdateTicker(uint8_t screenIndex, const nbgl_screenTickerConfiguration_t *ticker)
Updates the ticker configuration of the screen at the given screenIndex, always set at WHITE in.
int nbgl_screenPop(uint8_t screenIndex)
Release the screen at the given index in screen array (index returned by nbgl_screenPush())....
struct PACKED__ nbgl_screenTickerConfiguration_s nbgl_screenTickerConfiguration_t
struct to configure a screen layer
void nbgl_screenRedraw(void)
This function redraws the whole screen on top of stack and its children.
Definition nbgl_screen.c:66
uint32_t nbgl_touchGetTouchDuration(nbgl_obj_t *obj)
Definition nbgl_touch.c:404
color_t
Definition nbgl_types.h:122
@ WHITE
Definition nbgl_types.h:126
@ DARK_GRAY
Definition nbgl_types.h:124
@ LIGHT_GRAY
Definition nbgl_types.h:125
@ BLACK
Definition nbgl_types.h:123
nbgl_state_t
to represent a boolean state.
Definition nbgl_types.h:181
@ ON_STATE
Definition nbgl_types.h:183
@ OFF_STATE
Definition nbgl_types.h:182
@ POST_REFRESH_FORCE_POWER_OFF
Force screen power off after refresh.
Definition nbgl_types.h:335
@ POST_REFRESH_FORCE_POWER_ON
Force screen power on after refresh.
Definition nbgl_types.h:336
@ POST_REFRESH_FORCE_POWER_ON_WITH_PIPELINE
Force screen power on and enable pipeline.
Definition nbgl_types.h:337
nbgl_touchType_t
The different types of Touchscreen events.
Definition nbgl_types.h:241
@ SWIPED_LEFT
Definition nbgl_types.h:257
@ SWIPED_UP
Definition nbgl_types.h:254
@ SWIPED_DOWN
Definition nbgl_types.h:255
@ SWIPED_RIGHT
Definition nbgl_types.h:256
@ TOUCH_RELEASED
Definition nbgl_types.h:251
@ TOUCHED
Definition nbgl_types.h:242
@ TOUCHING
corresponding to an object that is currently touched
Definition nbgl_types.h:246
@ OUT_OF_TOUCH
Definition nbgl_types.h:247
@ QRCODE_V10
QRCode V10, can encode text len up to 1500 chars, display size = 228*228.
Definition nbgl_types.h:213
@ QRCODE_V4_SMALL
QRCode V4, can encode text len up to 1500 chars, display size = 132*132.
Definition nbgl_types.h:214
@ QRCODE_V4
QRCode V4, can encode text len up to 62 chars, display size = 264*264.
Definition nbgl_types.h:212
@ VERTICAL
from top to bottom
Definition nbgl_types.h:191
@ HORIZONTAL
from left to right
Definition nbgl_types.h:192
@ LOOP_PARSING
0, 1, 2, 0, 1, 2, ...
Definition nbgl_types.h:390
struct PACKED__ nbgl_icon_details_s nbgl_icon_details_t
Represents all information about an icon.
@ TOP_MIDDLE
Definition nbgl_types.h:164
@ CENTER
Definition nbgl_types.h:167
@ BOTTOM_RIGHT
Definition nbgl_types.h:171
@ TOP_LEFT
Definition nbgl_types.h:163
@ NO_ALIGNMENT
used when parent container layout is used
Definition nbgl_types.h:162
@ BOTTOM_LEFT
Definition nbgl_types.h:169
@ MID_RIGHT
Definition nbgl_types.h:168
@ RIGHT_TOP
on outside right
Definition nbgl_types.h:174
@ TOP_RIGHT
Definition nbgl_types.h:165
@ MID_LEFT
Definition nbgl_types.h:166
@ BOTTOM_MIDDLE
Definition nbgl_types.h:170
@ IMAGE
Bitmap (y and height must be multiple of 4 on Stax)
Definition nbgl_types.h:139
@ SWITCH
Switch to turn on/off something.
Definition nbgl_types.h:143
@ RADIO_BUTTON
Indicator to inform whether something is on or off.
Definition nbgl_types.h:146
@ SPINNER
Spinner.
Definition nbgl_types.h:150
@ BUTTON
Rounded rectangle button with icon and/or text.
Definition nbgl_types.h:142
@ PROGRESS_BAR
horizontal bar to indicate progression of something (between 0% and 100%)
Definition nbgl_types.h:145
@ QR_CODE
QR Code.
Definition nbgl_types.h:147
@ PAGE_INDICATOR
horizontal bar to indicate position within pages
Definition nbgl_types.h:144
@ LINE
Vertical or Horizontal line.
Definition nbgl_types.h:140
@ CONTAINER
Empty container.
Definition nbgl_types.h:138
@ TEXT_AREA
Area to contain text line(s)
Definition nbgl_types.h:141
#define MAX(x, y)
Definition nbgl_types.h:103
@ NBGL_BPP_1
1 bit per pixel
Definition nbgl_types.h:266
@ NO_STYLE
no border
Definition nbgl_types.h:200
@ BLACK_AND_WHITE_REFRESH
to be used for pure B&W area, when contrast is important
Definition nbgl_types.h:312
@ BLACK_AND_WHITE_FAST_REFRESH
to be used for pure B&W area, when contrast is not priority
Definition nbgl_types.h:313
@ FULL_COLOR_PARTIAL_REFRESH
to be used for small partial refresh (radio buttons, switches)
Definition nbgl_types.h:310
tune_index_e tuneId
nbgl_state_t state
const char * text
const nbgl_icon_details_t * iconRight
const char * subText
listItemType_t type
uint8_t token
const nbgl_icon_details_t * iconLeft
uint16_t delayMs
delay between 2 drawings
Definition nbgl_types.h:402
uint8_t nbIcons
number of icons in icons array
Definition nbgl_types.h:400
const nbgl_icon_details_t ** icons
array of nbIcons pointers on icons
Definition nbgl_types.h:399
nbgl_parsingType_t parsing
Definition nbgl_types.h:401
This structure contains info to build a centered (vertically and horizontally) area,...
uint16_t iconHug
vertical margin to apply on top and bottom of the icon
const nbgl_icon_details_t * icon
the icon (can be null)
const char * title
title in black large (can be null)
const char * description
description in black small regular case (can be null)
const char * subText
sub-text in dark gray regular small case
uint16_t animOffsetY
vertical offset of animation in icon if integrated (but usually 0)
bool padding
if true, apply a padding of 40px at the bottom
const nbgl_animation_t * animation
the animation (can be null), used if illustrType is ANIM_ILLUSTRATION
const char * smallTitle
sub-title in black small bold case (can be null)
uint16_t animOffsetX
horizontal offset of animation in icon if integrated (but usually 0)
nbgl_contentIllustrationType_t illustrType
This structure contains info to build a centered (vertically and horizontally) area,...
const char * text2
second text (can be null)
const char * text1
first text (can be null)
bool onTop
if set to true, align only horizontally
nbgl_contentCenteredInfoStyle_t style
style to apply to this info
int16_t offsetY
vertical shift to apply to this info (if >0, shift to bottom)
const char * text3
third text (can be null)
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
This structure contains a list of names to build a list of radio buttons (on the right part of screen...
uint8_t token
the token that will be used as argument of the callback
bool localized
if set to true, use nameIds and not names
uint8_t initChoice
index of the current choice
const char *const * names
array of strings giving the choices (nbChoices)
uint8_t nbChoices
number of choices
This structure contains info to build a switch (on the right) with a description (on the left),...
const char * text
main text for the switch
uint8_t token
the token that will be used as argument of the callback (unused on Nano)
nbgl_state_t initState
initial state of the switch
const char * subText
description under main text (NULL terminated, single line, may be null)
This structure contains a list of [tag,value] pairs.
const nbgl_contentTagValue_t * pairs
array of [tag,value] pairs (nbPairs items). If NULL, callback is used instead
bool wrapping
if set to true, value text will be wrapped on ' ' to avoid cutting words
uint8_t startIndex
index of the first pair to get with callback
nbgl_contentTagValueCallback_t callback
function to call to retrieve a given pair
This structure contains a [tag,value] pair and possible extensions.
const nbgl_icon_details_t * valueIcon
const char * value
string giving the value name
const char * item
string giving the tag name
const char * text
text of the tip-box
const nbgl_icon_details_t * icon
icon of the tip-box
uint8_t token
token used when tip-box is tapped
structure defining an ASCII font
Definition nbgl_fonts.h:76
uint8_t line_height
height of a line for all characters in pixels
Definition nbgl_fonts.h:81
This structure contains info to build a clickable "bar" with a text and an icon.
bool inactive
if set to true, the bar is grayed-out and cannot be touched
const char * text
text (can be NULL)
uint8_t token
the token that will be used as argument of the callback
bool large
set to true only for the main level of OS settings
const char * subText
sub text (can be NULL)
const nbgl_icon_details_t * iconLeft
a buffer containing the 1BPP icon for icon on left (can be NULL)
const nbgl_icon_details_t * iconRight
This structure contains info to build a single button.
const char * text
button text
uint8_t token
the token that will be used as argument of the callback
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon for button
nbgl_layoutButtonStyle_t style
bool fittingContent
if set to true, fit the width of button to text, otherwise full width
This structure contains info to build a pair of buttons, one on top of the other.
nbgl_layoutChoiceButtonsStyle_t style
the style of the pair
const nbgl_icon_details_t * topIcon
icon of top button
uint8_t token
the token that will be used as argument of the callback
const char * topText
up-button text (index 0)
const char * bottomText
bottom-button text (index 1)
Structure containing all information when creating a layout. This structure must be passed as argumen...
nbgl_screenTickerConfiguration_t ticker
const char * tapActionText
Light gray text used when main container is "tapable".
nbgl_layoutTouchCallback_t onActionCallback
the callback to be called on any action on the layout
This structure contains info to build an extended footer.
nbgl_layoutChoiceButtons_t choiceButtons
if type is FOOTER_CHOICE_BUTTONS
struct nbgl_layoutFooter_t::@17::@19 emptySpace
if type is FOOTER_EMPTY
struct nbgl_layoutFooter_t::@17::@20 simpleText
if type is FOOTER_SIMPLE_TEXT
bool separationLine
if true, a separation line is added at the top of this control
const char * leftText
nbgl_layoutButton_t button
if type is FOOTER_SIMPLE_BUTTON
bool mutedOut
if true, text is displayed in gray
nbgl_layoutFooterType_t type
type of footer
nbgl_layoutNavigationBar_t navigation
if type is FOOTER_NAV
struct nbgl_layoutFooter_t::@17::@22 textAndNav
if type is FOOTER_TEXT_AND_NAV
const char * rightText
struct nbgl_layoutFooter_t::@17::@21 doubleText
if type is FOOTER_DOUBLE_TEXT
const char * text
tune_index_e tuneId
This structure contains info to build a header.
nbgl_layoutHeaderType_t type
type of header
const nbgl_icon_details_t * icon
icon on left of text (only if HEADER_BACK_ICON_AND_TEXT)
uint8_t backToken
when back key is pressed
struct nbgl_layoutHeader_t::@9::@14 title
if type is HEADER_TITLE
struct nbgl_layoutHeader_t::@9::@11 emptySpace
if type is HEADER_EMPTY
const nbgl_icon_details_t * actionIcon
right button icon
uint8_t actionToken
when optional right button is pressed
struct nbgl_layoutHeader_t::@9::@12 backAndText
if type is HEADER_BACK_ICON_AND_TEXT or HEADER_BACK_AND_TEXT
bool separationLine
if true, a separation line is added at the bottom of this control
tune_index_e tuneId
when back key is pressed
const char * text
can be NULL if no text
struct nbgl_layoutHeader_t::@9::@13 progressAndBack
if type is HEADER_BACK_AND_PROGRESS
struct nbgl_layoutHeader_t::@9::@15 extendedBack
if type is HEADER_EXTENDED_BACK
uint8_t textToken
when text is touched
struct nbgl_layoutHeader_t::@9::@16 rightText
if type is HEADER_RIGHT_TEXT
uint8_t token
when back key is pressed
This structure contains info to build a pair of buttons, the small one, with icon,...
const nbgl_icon_details_t * leftIcon
a buffer containing the 1BPP icon for left button
uint8_t leftToken
the token used when left button is pressed
uint8_t rightToken
the token used when right button is pressed
const char * rightText
right-button text
Structure containing all information about the current layout.
nbgl_container_t * footerContainer
container used to store footer (buttons, nav....)
bool incrementAnim
if true, means that animation index is currently incrementing
uint8_t activePage
index of active page for navigation bar
nbgl_swipe_usage_t swipeUsage
nbgl_layoutTouchCallback_t callback
nbgl_container_t * container
uint8_t iconIdxInAnim
current icon index in animation
nbgl_container_t * headerContainer
container used to store header (progress, back, empty space...)
nbgl_layoutFooterType_t footerType
type of footer
bool modal
if true, means the screen is a modal
uint8_t nbChildren
number of children in above array
nbgl_layoutHeaderType_t headerType
type of header
nbgl_container_t * upFooterContainer
uint8_t nbPages
number of pages for navigation bar
nbgl_layoutUpFooterType_t upFooterType
type of up-footer
layoutObj_t callbackObjPool[LAYOUT_OBJ_POOL_LEN]
nbgl_text_area_t * tapText
nbgl_obj_t ** children
children for main screen
const nbgl_animation_t * animation
current animation (if not NULL)
This structure contains info to build a left content area.
uint8_t nbRows
number of rows in the area
const char * title
title of area in bold
const nbgl_icon_details_t ** rowIcons
array of nbRows icon
const char ** rowTexts
array of nbRows texts (displayed in regular)
This structure contains info to build a navigation bar at the bottom of the screen.
uint8_t activePage
index of active page (from 0 to nbPages-1).
bool withBackKey
if set to true, the "back" key is drawn
bool withExitKey
if set to true, an exit button is drawn (X on the left)
uint8_t token
the token that will be used as argument of the callback
uint8_t nbPages
number of pages. (if 0, no navigation)
This structure contains info to build a centered (vertically and horizontally) area,...
const char * text2
second text (can be null)
const char * text1
first text (can be null)
const char * url
URL for QR code.
bool largeText1
if set to true, use 32px font for text1
int16_t offsetY
vertical shift to apply to this info (if > 0, shift to bottom)
bool centered
if set to true, center vertically
This structure contains info to build an up-footer (area on top of footer).
nbgl_layoutButton_t button
if type is UP_FOOTER_BUTTON
tune_index_e tuneId
tune played when button is long-pressed
nbgl_contentTipBox_t tipBox
if type is UP_FOOTER_TIP_BOX
const char * text
text in the long-press button
nbgl_layoutUpFooterType_t type
type of up-footer
struct nbgl_layoutUpFooter_t::@23::@25 longPress
if type is UP_FOOTER_LONG_PRESS
uint8_t token
token used when button is long-pressed
nbgl_layoutHorizontalButtons_t horizontalButtons
if type is UP_FOOTER_HORIZONTAL_BUTTONS
signed short int16_t
Definition usbd_conf.h:50
unsigned short uint16_t
Definition usbd_conf.h:54
unsigned char uint8_t
Definition usbd_conf.h:53