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 BACK_KEY_WIDTH 88
47#define FOOTER_BUTTON_HEIGHT 128
48#define UP_FOOTER_BUTTON_HEIGHT 120
49#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 192
50#define ACTION_AND_FOOTER_FOOTER_HEIGHT 216
51#define FOOTER_TEXT_AND_NAV_WIDTH 160
52#define TAP_TO_CONTINUE_MARGIN 24
53#define SUB_HEADER_MARGIN (2 * 24)
54#define PRE_FIRST_TEXT_MARGIN 24
55#define INTER_PARAGRAPHS_MARGIN 40
56#define PRE_TITLE_MARGIN 24
57#define PRE_DESCRIPTION_MARGIN 16
58#define INTER_ROWS_MARGIN 16
59#define QR_INTER_TEXTS_MARGIN 40
60#define SPINNER_TEXT_MARGIN 20
61#define SPINNER_INTER_TEXTS_MARGIN 20
62#define BAR_TEXT_MARGIN 24
63#define BAR_INTER_TEXTS_MARGIN 16
64#define PROGRESSBAR_ALIGNMENT_MARGIN_Y 28
65#define LEFT_CONTENT_TEXT_PADDING 0
66#elif defined(TARGET_FLEX)
67#define RADIO_CHOICE_HEIGHT 92
68#define FOOTER_HEIGHT 80
69#define BAR_INTERVALE 16
70#define BACK_KEY_WIDTH 104
71#define FOOTER_BUTTON_HEIGHT 136
72#define UP_FOOTER_BUTTON_HEIGHT 136
73#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 208
74#define ACTION_AND_FOOTER_FOOTER_HEIGHT 232
75#define FOOTER_TEXT_AND_NAV_WIDTH 192
76#define TAP_TO_CONTINUE_MARGIN 30
77#define SUB_HEADER_MARGIN (2 * 28)
78#define PRE_FIRST_TEXT_MARGIN 0
79#define INTER_PARAGRAPHS_MARGIN 24
80#define PRE_TITLE_MARGIN 16
81#define PRE_DESCRIPTION_MARGIN 24
82#define INTER_ROWS_MARGIN 26
83#define QR_INTER_TEXTS_MARGIN 28
84#define SPINNER_TEXT_MARGIN 24
85#define SPINNER_INTER_TEXTS_MARGIN 16
86#define BAR_TEXT_MARGIN 24
87#define BAR_INTER_TEXTS_MARGIN 16
88#define PROGRESSBAR_ALIGNMENT_MARGIN_Y 32
89#define LEFT_CONTENT_TEXT_PADDING 4
90#else // TARGETS
91#error Undefined target
92#endif // TARGETS
93
94// width & height for progress bar
95#define PROGRESSBAR_WIDTH 120
96#define PROGRESSBAR_HEIGHT 12
97
98// refresh period of the spinner, in ms
99#define SPINNER_REFRESH_PERIOD 400
100
101/**********************
102 * MACROS
103 **********************/
104
105/**********************
106 * TYPEDEFS
107 **********************/
108
114
115// used to build either a touchable bar or a switch
116typedef struct {
118 const nbgl_icon_details_t *iconLeft; // a buffer containing the 1BPP icon for icon on
119 // left (can be NULL)
120 const nbgl_icon_details_t *iconRight; // a buffer containing the 1BPP icon for icon 2 (can be
121 // NULL). Dimensions must be the same as iconLeft
122 const char *text; // text (can be NULL)
123 const char *subText; // sub text (can be NULL)
124 uint8_t token; // the token that will be used as argument of the callback
125 nbgl_state_t state; // state of the item
126 bool large; // set to true only for the main level of OS settings
127#ifdef HAVE_PIEZO_SOUND
128 tune_index_e tuneId; // if not @ref NBGL_NO_TUNE, a tune will be played
129#endif // HAVE_PIEZO_SOUND
130} listItem_t;
131
132/**********************
133 * VARIABLES
134 **********************/
135
140static nbgl_layoutInternal_t gLayout[NB_MAX_LAYOUTS] = {0};
141
142// numbers of touchable controls for the whole page
143static uint8_t nbTouchableControls = 0;
144
145/**********************
146 * STATIC PROTOTYPES
147 **********************/
148extern const char *get_ux_loc_string(uint32_t index);
149
150#ifdef HAVE_FAST_HOLD_TO_APPROVE
151// Unit step in % of touchable progress bar
152#define HOLD_TO_APPROVE_STEP_PERCENT (7)
153// Duration in ms the user must hold the progress bar
154// to make it progress HOLD_TO_APPROVE_STEP_PERCENT %.
155// This duration must be higher than the screen refresh duration.
156#define HOLD_TO_APPROVE_STEP_DURATION_MS (100)
157#else
158#define HOLD_TO_APPROVE_STEP_PERCENT (25)
159#define HOLD_TO_APPROVE_STEP_DURATION_MS (400)
160#endif // HAVE_FAST_HOLD_TO_APPROVE
161
162static inline uint8_t get_hold_to_approve_percent(uint32_t touch_duration)
163{
164#ifdef HAVE_FAST_HOLD_TO_APPROVE
165 uint8_t current_step_nb = (touch_duration / HOLD_TO_APPROVE_STEP_DURATION_MS);
166#else
167 uint8_t current_step_nb = (touch_duration / HOLD_TO_APPROVE_STEP_DURATION_MS) + 1;
168#endif
169 return (current_step_nb * HOLD_TO_APPROVE_STEP_PERCENT);
170}
171
172// function used to retrieve the concerned layout and layout obj matching the given touched obj
173static bool getLayoutAndLayoutObj(nbgl_obj_t *obj,
174 nbgl_layoutInternal_t **layout,
175 layoutObj_t **layoutObj)
176{
178
179 // parse all layouts (starting with modals) to find the object
180 *layout = NULL;
181 while (i > 0) {
182 i--;
183 if (gLayout[i].nbChildren > 0) {
184 uint8_t j;
185
186 // search index of obj in this layout
187 for (j = 0; j < gLayout[i].nbUsedCallbackObjs; j++) {
188 if (obj == gLayout[i].callbackObjPool[j].obj) {
190 "getLayoutAndLayoutObj(): obj found in layout[%d], index = %d, "
191 "nbUsedCallbackObjs = %d\n",
192 i,
193 j,
194 gLayout[i].nbUsedCallbackObjs);
195 *layout = &gLayout[i];
196 *layoutObj = &(gLayout[i].callbackObjPool[j]);
197 return true;
198 }
199 }
200 }
201 }
202 // not found
203 return false;
204}
205
206static void radioTouchCallback(nbgl_obj_t *obj,
207 nbgl_touchType_t eventType,
208 nbgl_layoutInternal_t *layout);
209static void longTouchCallback(nbgl_obj_t *obj,
210 nbgl_touchType_t eventType,
211 nbgl_layoutInternal_t *layout,
212 layoutObj_t *layoutObj);
213
214// callback for most touched object
215static void touchCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType)
216{
217 nbgl_layoutInternal_t *layout;
218 layoutObj_t *layoutObj;
219 bool needRefresh = false;
220
221 if (obj == NULL) {
222 return;
223 }
224 LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): eventType = %d, obj = %p\n", eventType, obj);
225 if (getLayoutAndLayoutObj(obj, &layout, &layoutObj) == false) {
226 // try with parent, if existing
227 if (getLayoutAndLayoutObj(obj->parent, &layout, &layoutObj) == false) {
228 LOG_WARN(
230 "touchCallback(): eventType = %d, obj = %p, no active layout or obj not found\n",
231 eventType,
232 obj);
233 return;
234 }
235 }
236
237 // case of swipe
238 if (((eventType == SWIPED_UP) || (eventType == SWIPED_DOWN) || (eventType == SWIPED_LEFT)
239 || (eventType == SWIPED_RIGHT))
240 && (obj->type == CONTAINER)) {
241#ifdef NBGL_KEYBOARD
242 if (layout->swipeUsage == SWIPE_USAGE_SUGGESTIONS) {
243 keyboardSwipeCallback(obj, eventType);
244 return;
245 }
246#endif // NBGL_KEYBOARD
247 if (layout->swipeUsage == SWIPE_USAGE_CUSTOM) {
248 layoutObj->index = eventType;
249 }
250 else if ((layout->swipeUsage == SWIPE_USAGE_NAVIGATION)
251 && ((nbgl_obj_t *) layout->container == obj)) {
252 nbgl_container_t *navContainer;
253 if (layout->footerType == FOOTER_NAV) {
254 navContainer = (nbgl_container_t *) layout->footerContainer;
255 }
256 else if (layout->footerType == FOOTER_TEXT_AND_NAV) {
257 navContainer = (nbgl_container_t *) layout->footerContainer->children[1];
258 }
259 else {
260 return;
261 }
262
264 (nbgl_obj_t *) navContainer, eventType, layout->nbPages, &layout->activePage)
265 == false) {
266 // navigation was impossible
267 return;
268 }
269 layoutObj->index = layout->activePage;
270 }
271 }
272
273 // case of navigation bar
274 if (((obj->parent == (nbgl_obj_t *) layout->footerContainer)
275 && (layout->footerType == FOOTER_NAV))
276 || ((obj->parent->type == CONTAINER)
277 && (obj->parent->parent == (nbgl_obj_t *) layout->footerContainer)
278 && (layout->footerType == FOOTER_TEXT_AND_NAV))) {
279 if (layoutNavigationCallback(obj, eventType, layout->nbPages, &layout->activePage)
280 == false) {
281 // navigation was impossible
282 return;
283 }
284 layoutObj->index = layout->activePage;
285 }
286
287 // case of switch
288 if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren >= 2)
289 && (((nbgl_container_t *) obj)->children[1] != NULL)
290 && (((nbgl_container_t *) obj)->children[1]->type == SWITCH)) {
291 nbgl_switch_t *lSwitch = (nbgl_switch_t *) ((nbgl_container_t *) obj)->children[1];
292 lSwitch->state = (lSwitch->state == ON_STATE) ? OFF_STATE : ON_STATE;
293 nbgl_objDraw((nbgl_obj_t *) lSwitch);
294 // refresh will be done after tune playback
295 needRefresh = true;
296 // index is used for state
297 layoutObj->index = lSwitch->state;
298 }
299 // case of radio
300 else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 2)
301 && (((nbgl_container_t *) obj)->children[1] != NULL)
302 && (((nbgl_container_t *) obj)->children[1]->type == RADIO_BUTTON)) {
303 radioTouchCallback(obj, eventType, layout);
304 return;
305 }
306 // case of long press
307 else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 4)
308 && (((nbgl_container_t *) obj)->children[3] != NULL)
309 && (((nbgl_container_t *) obj)->children[3]->type == PROGRESS_BAR)) {
310 longTouchCallback(obj, eventType, layout, layoutObj);
311 return;
312 }
313 LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): layout->callback = %p\n", layout->callback);
314 if ((layout->callback != NULL) && (layoutObj->token != NBGL_INVALID_TOKEN)) {
315#ifdef HAVE_PIEZO_SOUND
316 if (layoutObj->tuneId < NBGL_NO_TUNE) {
317 io_seproxyhal_play_tune(layoutObj->tuneId);
318 }
319#endif // HAVE_PIEZO_SOUND
320 if (needRefresh) {
322 }
323 layout->callback(layoutObj->token, layoutObj->index);
324 }
325}
326
327// callback for long press button
328static void longTouchCallback(nbgl_obj_t *obj,
329 nbgl_touchType_t eventType,
330 nbgl_layoutInternal_t *layout,
331 layoutObj_t *layoutObj)
332{
333 nbgl_container_t *container = (nbgl_container_t *) obj;
334 // 4th child of container is the progress bar
335 nbgl_progress_bar_t *progressBar = (nbgl_progress_bar_t *) container->children[3];
336
338 "longTouchCallback(): eventType = %d, obj = %p, gLayout[1].nbChildren = %d\n",
339 eventType,
340 obj,
341 gLayout[1].nbChildren);
342
343 // case of pressing a long press button
344 if (eventType == TOUCHING) {
345 uint32_t touchDuration = nbgl_touchGetTouchDuration(obj);
346
347 // Compute the new progress bar state in %
348 uint8_t new_state = get_hold_to_approve_percent(touchDuration);
349
350 // Ensure the callback is triggered once,
351 // when the progress bar state reaches 100%
352 bool trigger_callback = (new_state >= 100) && (progressBar->state < 100);
353
354 // Cap progress bar state at 100%
355 if (new_state >= 100) {
356 new_state = 100;
357 }
358
359 // Update progress bar state
360 if (new_state != progressBar->state) {
361 progressBar->partialRedraw = true;
362 progressBar->state = new_state;
363
364 nbgl_objDraw((nbgl_obj_t *) progressBar);
365 // Ensure progress bar is fully drawn
366 // before calling the callback.
369 }
370
371 if (trigger_callback) {
372 // End of progress bar reached: trigger callback
373 if (layout->callback != NULL) {
374 layout->callback(layoutObj->token, layoutObj->index);
375 }
376 }
377 }
378 // case of releasing a long press button (or getting out of it)
379 else if ((eventType == TOUCH_RELEASED) || (eventType == OUT_OF_TOUCH)
380 || (eventType == SWIPED_LEFT) || (eventType == SWIPED_RIGHT)) {
382 progressBar->partialRedraw = true;
383 progressBar->state = 0;
384 nbgl_objDraw((nbgl_obj_t *) progressBar);
386 }
387}
388
389// callback for radio button touch
390static void radioTouchCallback(nbgl_obj_t *obj,
391 nbgl_touchType_t eventType,
392 nbgl_layoutInternal_t *layout)
393{
394 uint8_t i = NB_MAX_LAYOUTS, radioIndex = 0, foundRadio = 0xFF, foundRadioIndex;
395
396 if (eventType != TOUCHED) {
397 return;
398 }
399
400 i = 0;
401 // parse all objs to find all containers of radio buttons
402 while (i < layout->nbUsedCallbackObjs) {
403 if ((obj == (nbgl_obj_t *) layout->callbackObjPool[i].obj)
404 && (layout->callbackObjPool[i].obj->type == CONTAINER)) {
405 nbgl_radio_t *radio
406 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
407 ->children[1];
408 nbgl_text_area_t *textArea
410 ->children[0];
411 foundRadio = i;
412 foundRadioIndex = radioIndex;
413 // set text as active (black and bold)
414 textArea->textColor = BLACK;
415 textArea->fontId = SMALL_BOLD_FONT;
416 // ensure that radio button is ON
417 radio->state = ON_STATE;
418 // redraw container
419 nbgl_objDraw((nbgl_obj_t *) obj);
420 }
421 else if ((layout->callbackObjPool[i].obj->type == CONTAINER)
422 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->nbChildren == 2)
423 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->children[1]->type
424 == RADIO_BUTTON)) {
425 nbgl_radio_t *radio
426 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
427 ->children[1];
428 nbgl_text_area_t *textArea
430 ->children[0];
431 radioIndex++;
432 // set to OFF the one that was in ON
433 if (radio->state == ON_STATE) {
434 radio->state = OFF_STATE;
435 // set text it as inactive (gray and normal)
436 textArea->textColor = DARK_GRAY;
437 textArea->fontId = SMALL_REGULAR_FONT;
438 // redraw container
440 }
441 }
442 i++;
443 }
444 // call callback after redraw to avoid asynchronicity
445 if (foundRadio != 0xFF) {
446 if (layout->callback != NULL) {
447#ifdef HAVE_PIEZO_SOUND
448 if (layout->callbackObjPool[foundRadio].tuneId < NBGL_NO_TUNE) {
449 io_seproxyhal_play_tune(layout->callbackObjPool[foundRadio].tuneId);
450 }
452#endif // HAVE_PIEZO_SOUND
453 layout->callback(layout->callbackObjPool[foundRadio].token, foundRadioIndex);
454 }
455 }
456}
457
458// callback for spinner ticker
459static void spinnerTickerCallback(void)
460{
461 nbgl_spinner_t *spinner;
462 uint8_t i = 0;
463 nbgl_layoutInternal_t *layout;
464
465 // gLayout[1] is on top of gLayout[0] so if gLayout[1] is active, it must catch the event
466 if (gLayout[1].nbChildren > 0) {
467 layout = &gLayout[1];
468 }
469 else {
470 layout = &gLayout[0];
471 }
472
473 // get index of obj
474 while (i < layout->container->nbChildren) {
475 if (layout->container->children[i]->type == CONTAINER) {
476 nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
477 if (container->nbChildren && (container->children[0]->type == SPINNER)) {
478 spinner = (nbgl_spinner_t *) container->children[0];
479 spinner->position++;
480 // there are only NB_SPINNER_POSITIONS positions
481 if (spinner->position == NB_SPINNER_POSITIONS) {
482 spinner->position = 0;
483 }
484 nbgl_objDraw((nbgl_obj_t *) spinner);
486 return;
487 }
488 }
489 i++;
490 }
491}
492
493// callback for animation ticker
494static void animTickerCallback(void)
495{
496 nbgl_image_t *image;
497 uint8_t i = 0;
498 nbgl_layoutInternal_t *layout;
499
500 // gLayout[1] is on top of gLayout[0] so if gLayout[1] is active, it must catch the event
501 if (gLayout[1].nbChildren > 0) {
502 layout = &gLayout[1];
503 }
504 else {
505 layout = &gLayout[0];
506 }
507
508 if (layout->animation == NULL) {
509 return;
510 }
511
512 // get index of image obj
513 while (i < layout->container->nbChildren) {
514 if (layout->container->children[i]->type == CONTAINER) {
515 nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
516 if (container->children[1]->type == IMAGE) {
517 image = (nbgl_image_t *) container->children[1];
518 if (layout->animation->parsing == LOOP_PARSING) {
519 if (layout->iconIdxInAnim == (layout->animation->nbIcons - 1)) {
520 layout->iconIdxInAnim = 0;
521 }
522 else {
523 layout->iconIdxInAnim++;
524 }
525 }
526 else {
527 // Flip incrementAnim when reaching upper or lower limit
528 if ((layout->incrementAnim)
529 && (layout->iconIdxInAnim >= layout->animation->nbIcons - 1)) {
530 layout->incrementAnim = false;
531 }
532 else if (layout->iconIdxInAnim == 0) {
533 layout->incrementAnim = true;
534 }
535
536 // Increase / Decrease index according to incrementAnim
537 if (layout->incrementAnim) {
538 layout->iconIdxInAnim++;
539 }
540 else {
541 layout->iconIdxInAnim--;
542 }
543 }
544 image->buffer = layout->animation->icons[layout->iconIdxInAnim];
545 nbgl_objDraw((nbgl_obj_t *) image);
548 return;
549 }
550 }
551 i++;
552 }
553}
554
555static nbgl_line_t *createHorizontalLine(uint8_t layer)
556{
557 nbgl_line_t *line;
558
559 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
560 line->lineColor = LIGHT_GRAY;
561 line->obj.area.width = SCREEN_WIDTH;
562 line->obj.area.height = 4;
563 line->direction = HORIZONTAL;
564 line->thickness = 1;
565 return line;
566}
567
568static nbgl_line_t *createLeftVerticalLine(uint8_t layer)
569{
570 nbgl_line_t *line;
571
572 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
573 line->lineColor = LIGHT_GRAY;
574 line->obj.area.width = 1;
575 line->obj.area.height = SCREEN_HEIGHT;
576 line->direction = VERTICAL;
577 line->thickness = 1;
578 line->obj.alignment = MID_LEFT;
579 return line;
580}
581
582// function adding a layout object in the callbackObjPool array for the given layout, and
583// configuring it
585 nbgl_obj_t *obj,
586 uint8_t token,
587 tune_index_e tuneId)
588{
589 layoutObj_t *layoutObj = NULL;
590
591 if (layout->nbUsedCallbackObjs < (LAYOUT_OBJ_POOL_LEN - 1)) {
592 layoutObj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
593 layout->nbUsedCallbackObjs++;
594 layoutObj->obj = obj;
595 layoutObj->token = token;
596 layoutObj->tuneId = tuneId;
597 }
598 else {
599 LOG_FATAL(LAYOUT_LOGGER, "layoutAddCallbackObj: no more callback obj\n");
600 }
601
602 return layoutObj;
603}
604
612{
613 if (layout->container->nbChildren == NB_MAX_CONTAINER_CHILDREN) {
614 LOG_FATAL(LAYOUT_LOGGER, "layoutAddObject(): No more object\n");
615 }
616 layout->container->children[layout->container->nbChildren] = obj;
617 layout->container->nbChildren++;
618}
619
629static int addSwipeInternal(nbgl_layoutInternal_t *layoutInt,
630 uint16_t swipesMask,
631 nbgl_swipe_usage_t usage,
632 uint8_t token,
633 tune_index_e tuneId)
634{
635 layoutObj_t *obj;
636
637 if ((swipesMask & SWIPE_MASK) == 0) {
638 return -1;
639 }
640
641 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) layoutInt->container, token, tuneId);
642 if (obj == NULL) {
643 return -1;
644 }
645 layoutInt->container->obj.touchMask = swipesMask;
646 layoutInt->swipeUsage = usage;
647
648 return 0;
649}
650
658static nbgl_container_t *addListItem(nbgl_layoutInternal_t *layoutInt, const listItem_t *itemDesc)
659{
660 layoutObj_t *obj;
661 nbgl_text_area_t *textArea;
662 nbgl_container_t *container;
663 color_t color = ((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == OFF_STATE))
664 ? LIGHT_GRAY
665 : BLACK;
666 int16_t usedHeight = 40;
667
668 LOG_DEBUG(LAYOUT_LOGGER, "addListItem():\n");
669
670 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
672 layoutInt, (nbgl_obj_t *) container, itemDesc->token, itemDesc->tuneId);
673 if (obj == NULL) {
674 return NULL;
675 }
676
677 // get container children (up to 4: text + left+right icons + sub text)
678 container->children = nbgl_containerPoolGet(4, layoutInt->layer);
679 container->nbChildren = 0;
680
681 container->obj.area.width = AVAILABLE_WIDTH;
682 container->obj.area.height = itemDesc->large ? TOUCHABLE_MAIN_BAR_HEIGHT : TOUCHABLE_BAR_HEIGHT;
683 container->layout = HORIZONTAL;
684 container->obj.alignmentMarginX = BORDER_MARGIN;
685 container->obj.alignment = NO_ALIGNMENT;
686 container->obj.alignTo = NULL;
687 // the bar can only be touched if not inactive AND if one of the icon is present
688 // otherwise it is seen as a title
689 if (((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == ON_STATE))
690 || (itemDesc->type == SWITCH_ITEM)) {
691 container->obj.touchMask = (1 << TOUCHED);
692 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
693 nbTouchableControls++;
694 }
695
696 // allocate main text because always present
697 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
698 textArea->textColor = color;
699 textArea->text = PIC(itemDesc->text);
700 textArea->onDrawCallback = NULL;
701 textArea->fontId = SMALL_BOLD_FONT;
702 textArea->wrapping = true;
703 textArea->obj.area.width = container->obj.area.width;
704 if (itemDesc->iconLeft != NULL) {
705 // reduce text width accordingly
706 textArea->obj.area.width
707 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE;
708 }
709 if (itemDesc->iconRight != NULL) {
710 // reduce text width accordingly
711 textArea->obj.area.width
712 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconRight))->width + BAR_INTERVALE;
713 }
714 else if (itemDesc->type == SWITCH_ITEM) {
715 textArea->obj.area.width -= 60 + BAR_INTERVALE;
716 }
717 textArea->obj.area.height = nbgl_getTextHeightInWidth(
718 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
719 usedHeight = MAX(usedHeight, textArea->obj.area.height);
720 textArea->style = NO_STYLE;
721 textArea->obj.alignment = MID_LEFT;
722 textArea->textAlignment = MID_LEFT;
723 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
724 container->nbChildren++;
725
726 // allocate left icon if present
727 if (itemDesc->iconLeft != NULL) {
728 nbgl_image_t *imageLeft = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
729 imageLeft->foregroundColor = color;
730 imageLeft->buffer = PIC(itemDesc->iconLeft);
731 // align at the left of text
732 imageLeft->obj.alignment = MID_LEFT;
733 imageLeft->obj.alignTo = (nbgl_obj_t *) textArea;
734 imageLeft->obj.alignmentMarginX = BAR_INTERVALE;
735 container->children[container->nbChildren] = (nbgl_obj_t *) imageLeft;
736 container->nbChildren++;
737
738 textArea->obj.alignmentMarginX = imageLeft->buffer->width + BAR_INTERVALE;
739
740 usedHeight = MAX(usedHeight, imageLeft->buffer->height);
741 }
742 // allocate right icon if present
743 if (itemDesc->iconRight != NULL) {
744 nbgl_image_t *imageRight = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
745 imageRight->foregroundColor = color;
746 imageRight->buffer = PIC(itemDesc->iconRight);
747 // align at the right of text
748 imageRight->obj.alignment = MID_RIGHT;
749 imageRight->obj.alignmentMarginX = BAR_INTERVALE;
750 imageRight->obj.alignTo = (nbgl_obj_t *) textArea;
751
752 container->children[container->nbChildren] = (nbgl_obj_t *) imageRight;
753 container->nbChildren++;
754
755 usedHeight = MAX(usedHeight, imageRight->buffer->height);
756 }
757 else if (itemDesc->type == SWITCH_ITEM) {
758 nbgl_switch_t *switchObj = (nbgl_switch_t *) nbgl_objPoolGet(SWITCH, layoutInt->layer);
759 switchObj->onColor = BLACK;
760 switchObj->offColor = LIGHT_GRAY;
761 switchObj->state = itemDesc->state;
762 switchObj->obj.alignment = MID_RIGHT;
763 switchObj->obj.alignmentMarginX = BAR_INTERVALE;
764 switchObj->obj.alignTo = (nbgl_obj_t *) textArea;
765
766 container->children[container->nbChildren] = (nbgl_obj_t *) switchObj;
767 container->nbChildren++;
768 }
769
770 if (itemDesc->subText != NULL) {
771 nbgl_text_area_t *subTextArea
773
774 subTextArea->textColor = color;
775 subTextArea->text = PIC(itemDesc->subText);
776 subTextArea->textAlignment = MID_LEFT;
777 subTextArea->fontId = SMALL_REGULAR_FONT;
778 subTextArea->style = NO_STYLE;
779 subTextArea->wrapping = true;
780 subTextArea->obj.alignment = MID_LEFT;
781 subTextArea->obj.area.width = container->obj.area.width;
782 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
783 subTextArea->text,
784 subTextArea->obj.area.width,
785 subTextArea->wrapping);
786 container->children[container->nbChildren] = (nbgl_obj_t *) subTextArea;
787 container->nbChildren++;
788 container->obj.area.height += subTextArea->obj.area.height + 12;
789
790 // modify alignments to have sub-text under (icon left - text - icon right)
791 textArea->obj.alignmentMarginY = -(subTextArea->obj.area.height + 12) / 2;
792 subTextArea->obj.alignmentMarginY = (usedHeight + 12) / 2;
793 }
794
795 // set this new container as child of main container
796 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
797
798 return container;
799}
800
809static nbgl_container_t *addContentCenter(nbgl_layoutInternal_t *layoutInt,
810 const nbgl_contentCenter_t *info)
811{
812 nbgl_container_t *container;
813 nbgl_text_area_t *textArea = NULL;
814 nbgl_image_t *image = NULL;
815 uint16_t fullHeight = 0;
816
817 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
818
819 // get container children
820 container->nbChildren = 0;
821 // the max number is: icon + anim + title + small title + description + sub-text
822 container->children = nbgl_containerPoolGet(6, layoutInt->layer);
823
824 // add icon or animation if present
825 if (info->icon != NULL) {
826 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
827 image->foregroundColor = BLACK;
828 image->buffer = PIC(info->icon);
829 image->obj.alignment = TOP_MIDDLE;
830 image->obj.alignmentMarginY = info->iconHug;
831
832 fullHeight += image->buffer->height + info->iconHug;
833 container->children[container->nbChildren] = (nbgl_obj_t *) image;
834 container->nbChildren++;
835
836 if (info->illustrType == ANIM_ILLUSTRATION) {
837 nbgl_image_t *anim;
838 anim = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
839 anim->foregroundColor = BLACK;
840 anim->buffer = PIC(info->animation->icons[0]);
841 anim->obj.alignment = TOP_MIDDLE;
842 anim->obj.alignmentMarginY = info->iconHug + info->animOffsetY;
843 anim->obj.alignmentMarginX = info->animOffsetX;
844
845 container->children[container->nbChildren] = (nbgl_obj_t *) anim;
846 container->nbChildren++;
847
848 layoutInt->animation = info->animation;
849 layoutInt->incrementAnim = true;
850 layoutInt->iconIdxInAnim = 0;
851
852 // update ticker to update the animation periodically
854
855 tickerCfg.tickerIntervale = info->animation->delayMs; // ms
856 tickerCfg.tickerValue = info->animation->delayMs; // ms
857 tickerCfg.tickerCallback = &animTickerCallback;
858 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
859 }
860 }
861 // add title if present
862 if (info->title != NULL) {
863 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
864 textArea->textColor = BLACK;
865 textArea->text = PIC(info->title);
866 textArea->textAlignment = CENTER;
867 textArea->fontId = LARGE_MEDIUM_FONT;
868 textArea->wrapping = true;
869 textArea->obj.area.width = AVAILABLE_WIDTH;
870 textArea->obj.area.height = nbgl_getTextHeightInWidth(
871 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
872
873 // if not the first child, put on bottom of the previous, with a margin
874 if (container->nbChildren > 0) {
875 textArea->obj.alignment = BOTTOM_MIDDLE;
876 textArea->obj.alignTo = (nbgl_obj_t *) image;
877 textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN + info->iconHug;
878 }
879 else {
880 textArea->obj.alignment = TOP_MIDDLE;
881 }
882
883 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
884
885 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
886 container->nbChildren++;
887 }
888 // add small title if present
889 if (info->smallTitle != NULL) {
890 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
891 textArea->textColor = BLACK;
892 textArea->text = PIC(info->smallTitle);
893 textArea->textAlignment = CENTER;
894 textArea->fontId = SMALL_BOLD_FONT;
895 textArea->wrapping = true;
896 textArea->obj.area.width = AVAILABLE_WIDTH;
897 textArea->obj.area.height = nbgl_getTextHeightInWidth(
898 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
899
900 // if not the first child, put on bottom of the previous, with a margin
901 if (container->nbChildren > 0) {
902 textArea->obj.alignment = BOTTOM_MIDDLE;
903 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
904 textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN;
905 if (container->children[container->nbChildren - 1]->type == IMAGE) {
906 textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN + info->iconHug;
907 }
908 else {
909 textArea->obj.alignmentMarginY = 16;
910 }
911 }
912 else {
913 textArea->obj.alignment = TOP_MIDDLE;
914 }
915
916 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
917
918 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
919 container->nbChildren++;
920 }
921 // add description if present
922 if (info->description != NULL) {
923 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
924 textArea->textColor = BLACK;
925 textArea->text = PIC(info->description);
926 textArea->textAlignment = CENTER;
927 textArea->fontId = SMALL_REGULAR_FONT;
928 textArea->wrapping = true;
929 textArea->obj.area.width = AVAILABLE_WIDTH;
930 textArea->obj.area.height = nbgl_getTextHeightInWidth(
931 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
932
933 // if not the first child, put on bottom of the previous, with a margin
934 if (container->nbChildren > 0) {
935 textArea->obj.alignment = BOTTOM_MIDDLE;
936 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
937 if (container->children[container->nbChildren - 1]->type == TEXT_AREA) {
938 // if previous element is text, only space of 16 px
939 textArea->obj.alignmentMarginY = 16;
940 }
941 else {
942 textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN + info->iconHug;
943 }
944 }
945 else {
946 textArea->obj.alignment = TOP_MIDDLE;
947 }
948
949 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
950
951 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
952 container->nbChildren++;
953 }
954 // add sub-text if present
955 if (info->subText != NULL) {
956 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
957 textArea->textColor = DARK_GRAY;
958 textArea->text = PIC(info->subText);
959 textArea->textAlignment = CENTER;
960 textArea->fontId = SMALL_REGULAR_FONT;
961 textArea->wrapping = true;
962 textArea->obj.area.width = AVAILABLE_WIDTH;
963 textArea->obj.area.height = nbgl_getTextHeightInWidth(
964 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
965 // sub-text is included in a hug of 8px
966 textArea->obj.area.height += 2 * 8;
967 // if not the first child, put on bottom of the previous, with a margin
968 if (container->nbChildren > 0) {
969 textArea->obj.alignment = BOTTOM_MIDDLE;
970 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
971 textArea->obj.alignmentMarginY = 16;
972 if (container->children[container->nbChildren - 1]->type == IMAGE) {
973 textArea->obj.alignmentMarginY += info->iconHug;
974 }
975 }
976 else {
977 textArea->obj.alignment = TOP_MIDDLE;
978 }
979
980 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
981
982 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
983 container->nbChildren++;
984 }
985 container->layout = VERTICAL;
986 container->obj.alignment = CENTER;
987 container->obj.area.width = AVAILABLE_WIDTH;
988 container->obj.area.height = fullHeight;
989 if (info->padding) {
990 container->obj.area.height += 40;
991 }
992
993 // set this new container as child of main container
994 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
995
996 return container;
997}
998
999static int addText(nbgl_layout_t *layout,
1000 const char *text,
1001 const char *subText,
1002 uint8_t token,
1003 uint8_t index,
1004 bool withAlias)
1005{
1006 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1007 nbgl_container_t *container;
1008 nbgl_text_area_t *textArea;
1009 nbgl_text_area_t *subTextArea;
1010 uint16_t fullHeight = 0;
1011
1012 if (layout == NULL) {
1013 return -1;
1014 }
1015 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1016
1017 // get container children
1018 container->children = nbgl_containerPoolGet(withAlias ? 3 : 2, layoutInt->layer);
1019 container->obj.area.width = AVAILABLE_WIDTH;
1020
1021 if (text != NULL) {
1022 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1023
1024 textArea->textColor = BLACK;
1025 textArea->text = PIC(text);
1026 textArea->textAlignment = MID_LEFT;
1027 textArea->fontId = SMALL_BOLD_FONT;
1028 textArea->style = NO_STYLE;
1029 textArea->wrapping = true;
1030 textArea->obj.alignment = NO_ALIGNMENT;
1031 textArea->obj.alignmentMarginY = PRE_TEXT_MARGIN;
1032 textArea->obj.area.width = container->obj.area.width;
1033 if (withAlias == true) {
1034 textArea->obj.area.width -= 12 + MINI_PUSH_ICON.width;
1035 }
1036 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1037 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1038 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1039 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1040 container->nbChildren++;
1041 if (withAlias == true) {
1042 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
1043 // the whole container is touchable
1044 layoutObj_t *obj
1045 = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) container, token, TUNE_TAP_CASUAL);
1046 obj->index = index;
1047 image->foregroundColor = BLACK;
1048 image->buffer = &PUSH_ICON;
1049 image->obj.alignment = RIGHT_TOP;
1050 image->obj.alignmentMarginX = 12;
1051 image->obj.alignTo = (nbgl_obj_t *) textArea;
1052 container->obj.touchMask = (1 << TOUCHED);
1053 container->obj.touchId = VALUE_BUTTON_1_ID + index;
1054
1055 container->children[container->nbChildren] = (nbgl_obj_t *) image;
1056 container->nbChildren++;
1057 }
1058 }
1059 if (subText != NULL) {
1060 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1061 subTextArea->textColor = BLACK;
1062 subTextArea->text = PIC(subText);
1063 subTextArea->fontId = SMALL_REGULAR_FONT;
1064 subTextArea->style = NO_STYLE;
1065 subTextArea->wrapping = true;
1066 subTextArea->obj.area.width = container->obj.area.width;
1067 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
1068 subTextArea->text,
1069 subTextArea->obj.area.width,
1070 subTextArea->wrapping);
1071 subTextArea->textAlignment = MID_LEFT;
1072 subTextArea->obj.alignment = NO_ALIGNMENT;
1073 if (text != NULL) {
1074 subTextArea->obj.alignmentMarginY = TEXT_SUBTEXT_MARGIN;
1075 }
1076 else {
1077 subTextArea->obj.alignmentMarginY = PRE_SUBTEXT_MARGIN;
1078 }
1079 fullHeight += subTextArea->obj.alignmentMarginY;
1080 container->children[container->nbChildren] = (nbgl_obj_t *) subTextArea;
1081 container->nbChildren++;
1082 fullHeight += subTextArea->obj.area.height + subTextArea->obj.alignmentMarginY;
1083 }
1084 else {
1085 fullHeight += PRE_TEXT_MARGIN;
1086 }
1087 container->obj.area.height = fullHeight;
1088 container->layout = VERTICAL;
1089 container->obj.alignmentMarginX = BORDER_MARGIN;
1090 container->obj.alignment = NO_ALIGNMENT;
1091 // set this new obj as child of main container
1092 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1093
1094 return container->obj.area.height;
1095}
1096
1097/**********************
1098 * GLOBAL FUNCTIONS
1099 **********************/
1100
1108{
1109 nbgl_layoutInternal_t *layout = NULL;
1110
1111 // find an empty layout in the proper "layer"
1112 if (description->modal) {
1113 if (gLayout[1].nbChildren == 0) {
1114 layout = &gLayout[1];
1115 }
1116 else if (gLayout[2].nbChildren == 0) {
1117 layout = &gLayout[2];
1118 }
1119 }
1120 else {
1121 // automatically "release" a potentially opened non-modal layout
1122 gLayout[0].nbChildren = 0;
1123 layout = &gLayout[0];
1124 }
1125 if (layout == NULL) {
1126 LOG_WARN(LAYOUT_LOGGER, "nbgl_layoutGet(): impossible to get a layout!\n");
1127 return NULL;
1128 }
1129
1130 // reset globals
1131 memset(layout, 0, sizeof(nbgl_layoutInternal_t));
1132
1133 nbTouchableControls = 0;
1134
1135 layout->callback = (nbgl_layoutTouchCallback_t) PIC(description->onActionCallback);
1136 layout->modal = description->modal;
1137 layout->withLeftBorder = description->withLeftBorder;
1138 if (description->modal) {
1139 layout->layer = nbgl_screenPush(&layout->children,
1141 &description->ticker,
1142 (nbgl_touchCallback_t) touchCallback);
1143 }
1144 else {
1145 nbgl_screenSet(&layout->children,
1147 &description->ticker,
1148 (nbgl_touchCallback_t) touchCallback);
1149 layout->layer = 0;
1150 }
1152 layout->container->obj.area.width = SCREEN_WIDTH;
1153 layout->container->obj.area.height = SCREEN_HEIGHT;
1154 layout->container->layout = VERTICAL;
1155 layout->container->children = nbgl_containerPoolGet(NB_MAX_CONTAINER_CHILDREN, layout->layer);
1156 // by default, if no header, main container is aligned on top-left
1157 layout->container->obj.alignment = TOP_LEFT;
1158 // main container is always the second object, leaving space for header
1159 layout->children[MAIN_CONTAINER_INDEX] = (nbgl_obj_t *) layout->container;
1161
1162 // if a tap text is defined, make the container tapable and display this text in gray
1163 if (description->tapActionText != NULL) {
1164 layoutObj_t *obj;
1165
1166 obj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
1167 layout->nbUsedCallbackObjs++;
1168 obj->obj = (nbgl_obj_t *) layout->container;
1169 obj->token = description->tapActionToken;
1170 obj->tuneId = description->tapTuneId;
1171 layout->container->obj.touchMask = (1 << TOUCHED);
1172 layout->container->obj.touchId = WHOLE_SCREEN_ID;
1173
1174 nbgl_layoutUpFooter_t footerDesc;
1175 footerDesc.type = UP_FOOTER_TEXT;
1176 footerDesc.text.text = PIC(description->tapActionText);
1177 footerDesc.text.token = description->tapActionToken;
1178 footerDesc.text.tuneId = description->tapTuneId;
1179 nbgl_layoutAddUpFooter((nbgl_layout_t *) layout, &footerDesc);
1180 }
1181
1182 return (nbgl_layout_t *) layout;
1183}
1184
1197 uint16_t swipesMask,
1198 const char *text,
1199 uint8_t token,
1200 tune_index_e tuneId)
1201{
1202 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1203
1204 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwipe():\n");
1205 if (layout == NULL) {
1206 return -1;
1207 }
1208
1209 if (text) {
1210 // create 'tap to continue' text area
1212 layoutInt->tapText->text = PIC(text);
1213 layoutInt->tapText->textColor = DARK_GRAY;
1214 layoutInt->tapText->fontId = SMALL_REGULAR_FONT;
1215 layoutInt->tapText->obj.area.width = AVAILABLE_WIDTH;
1216 layoutInt->tapText->obj.area.height = nbgl_getFontLineHeight(layoutInt->tapText->fontId);
1217 layoutInt->tapText->textAlignment = CENTER;
1218 layoutInt->tapText->obj.alignmentMarginY = TAP_TO_CONTINUE_MARGIN;
1219 layoutInt->tapText->obj.alignment = BOTTOM_MIDDLE;
1220 }
1221 return addSwipeInternal(layoutInt, swipesMask, SWIPE_USAGE_CUSTOM, token, tuneId);
1222}
1223
1234 const nbgl_icon_details_t *icon,
1235 uint8_t token,
1236 tune_index_e tuneId)
1237{
1238 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1239 layoutObj_t *obj;
1240 nbgl_button_t *button;
1241
1242 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTopRightButton():\n");
1243 if (layout == NULL) {
1244 return -1;
1245 }
1246 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
1247 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) button, token, tuneId);
1248 if (obj == NULL) {
1249 return -1;
1250 }
1251
1252 button->obj.area.width = BUTTON_DIAMETER;
1253 button->obj.area.height = BUTTON_DIAMETER;
1254 button->radius = BUTTON_RADIUS;
1255 button->obj.alignmentMarginX = BORDER_MARGIN;
1256 button->obj.alignmentMarginY = BORDER_MARGIN;
1257 button->foregroundColor = BLACK;
1258 button->innerColor = WHITE;
1259 button->borderColor = LIGHT_GRAY;
1260 button->obj.touchMask = (1 << TOUCHED);
1261 button->obj.touchId = TOP_RIGHT_BUTTON_ID;
1262 button->icon = PIC(icon);
1263 button->obj.alignment = TOP_RIGHT;
1264
1265 // add to screen
1266 layoutInt->children[TOP_RIGHT_BUTTON_INDEX] = (nbgl_obj_t *) button;
1267
1268 return 0;
1269}
1270
1279{
1280 nbgl_layoutFooter_t footerDesc;
1281 footerDesc.type = FOOTER_NAV;
1282 footerDesc.separationLine = info->withSeparationLine;
1283 footerDesc.navigation.activePage = info->activePage;
1284 footerDesc.navigation.nbPages = info->nbPages;
1285 footerDesc.navigation.withExitKey = info->withExitKey;
1286 footerDesc.navigation.withBackKey = info->withBackKey;
1287 footerDesc.navigation.withPageIndicator = false;
1288 footerDesc.navigation.token = info->token;
1289 footerDesc.navigation.tuneId = info->tuneId;
1290 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1291}
1292
1305 const nbgl_icon_details_t *icon,
1306 uint8_t token,
1307 bool separationLine,
1308 tune_index_e tuneId)
1309{
1310 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddBottomButton():\n");
1311 nbgl_layoutFooter_t footerDesc;
1312 footerDesc.type = FOOTER_SIMPLE_BUTTON;
1313 footerDesc.separationLine = separationLine;
1314 footerDesc.button.fittingContent = false;
1315 footerDesc.button.icon = PIC(icon);
1316 footerDesc.button.text = NULL;
1317 footerDesc.button.token = token;
1318 footerDesc.button.tuneId = tuneId;
1319 footerDesc.button.style = WHITE_BACKGROUND;
1320 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1321}
1322
1331{
1332 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1333 nbgl_container_t *container;
1334 listItem_t itemDesc;
1335
1336 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar():\n");
1337 if (layout == NULL) {
1338 return -1;
1339 }
1340 // main text is mandatory
1341 if (barLayout->text == NULL) {
1342 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar(): main text is mandatory\n");
1343 }
1344
1345 itemDesc.iconLeft = barLayout->iconLeft;
1346 itemDesc.iconRight = barLayout->iconRight;
1347 itemDesc.text = barLayout->text;
1348 itemDesc.subText = barLayout->subText;
1349 itemDesc.token = barLayout->token;
1350 itemDesc.tuneId = barLayout->tuneId;
1351 itemDesc.state = (barLayout->inactive) ? OFF_STATE : ON_STATE;
1352 itemDesc.large = barLayout->large;
1353 itemDesc.type = TOUCHABLE_BAR_ITEM;
1354 container = addListItem(layoutInt, &itemDesc);
1355
1356 if (container == NULL) {
1357 return -1;
1358 }
1359 return container->obj.area.height;
1360}
1361
1370{
1371 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1372 nbgl_container_t *container;
1373 listItem_t itemDesc;
1374
1375 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwitch():\n");
1376 if (layout == NULL) {
1377 return -1;
1378 }
1379 // main text is mandatory
1380 if (switchLayout->text == NULL) {
1381 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddSwitch(): main text is mandatory\n");
1382 }
1383
1384 itemDesc.iconLeft = NULL;
1385 itemDesc.iconRight = NULL;
1386 itemDesc.text = switchLayout->text;
1387 itemDesc.subText = switchLayout->subText;
1388 itemDesc.token = switchLayout->token;
1389 itemDesc.tuneId = switchLayout->tuneId;
1390 itemDesc.state = switchLayout->initState;
1391 itemDesc.large = false;
1392 itemDesc.type = SWITCH_ITEM;
1393 container = addListItem(layoutInt, &itemDesc);
1394
1395 if (container == NULL) {
1396 return -1;
1397 }
1398 return container->obj.area.height;
1399}
1400
1409int nbgl_layoutAddText(nbgl_layout_t *layout, const char *text, const char *subText)
1410{
1411 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddText():\n");
1412 return addText(layout, text, subText, 0, 0, false);
1413}
1414
1427 const char *text,
1428 const char *subText,
1429 uint8_t token,
1430 uint8_t index)
1431{
1432 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextWithAlias():\n");
1433 return addText(layout, text, subText, token, index, true);
1434}
1435
1443int nbgl_layoutAddSubHeaderText(nbgl_layout_t *layout, const char *text)
1444{
1445 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1446 nbgl_text_area_t *textArea;
1447
1448 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSubHeaderText():\n");
1449 if (layout == NULL) {
1450 return -1;
1451 }
1452 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1453
1454 textArea->textColor = BLACK;
1455 textArea->text = PIC(text);
1456 textArea->textAlignment = MID_LEFT;
1457 textArea->fontId = SMALL_REGULAR_FONT;
1458 textArea->style = NO_STYLE;
1459 textArea->wrapping = true;
1460 textArea->obj.alignment = NO_ALIGNMENT;
1461 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1462 textArea->obj.area.width = AVAILABLE_WIDTH;
1463 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1464 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1465 textArea->obj.area.height += SUB_HEADER_MARGIN;
1466
1467 // set this new obj as child of main container
1468 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1469
1470 return textArea->obj.area.height;
1471}
1472
1481int nbgl_layoutAddLargeCaseText(nbgl_layout_t *layout, const char *text, bool grayedOut)
1482{
1483 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1484 nbgl_text_area_t *textArea;
1485
1486 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLargeCaseText():\n");
1487 if (layout == NULL) {
1488 return -1;
1489 }
1490 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1491
1492 textArea->textColor = grayedOut ? LIGHT_GRAY : BLACK;
1493 textArea->text = PIC(text);
1494 textArea->textAlignment = MID_LEFT;
1495 textArea->fontId = LARGE_MEDIUM_FONT;
1496 textArea->obj.area.width = AVAILABLE_WIDTH;
1497 textArea->wrapping = true;
1498 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1499 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1500 textArea->style = NO_STYLE;
1501 textArea->obj.alignment = NO_ALIGNMENT;
1502 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1503 if (layoutInt->container->nbChildren == 0) {
1504 // if first object of container, increase the margin from top
1505 textArea->obj.alignmentMarginY += PRE_FIRST_TEXT_MARGIN;
1506 }
1507 else {
1508 textArea->obj.alignmentMarginY = INTER_PARAGRAPHS_MARGIN; // between paragraphs
1509 }
1510
1511 // set this new obj as child of main container
1512 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1513
1514 return 0;
1515}
1516
1530 const char *title,
1531 const char *description,
1532 const char *info)
1533{
1534 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1535 nbgl_text_area_t *textArea;
1536
1537 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextContent():\n");
1538 if (layout == NULL) {
1539 return -1;
1540 }
1541
1542 // create title
1543 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1544 textArea->textColor = BLACK;
1545 textArea->text = PIC(title);
1546 textArea->textAlignment = MID_LEFT;
1547 textArea->fontId = LARGE_MEDIUM_FONT;
1548 textArea->style = NO_STYLE;
1549 textArea->wrapping = true;
1550 textArea->obj.alignment = NO_ALIGNMENT;
1551 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1552 textArea->obj.alignmentMarginY = PRE_TITLE_MARGIN;
1553 textArea->obj.area.width = AVAILABLE_WIDTH;
1554 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1555 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1556 // set this new obj as child of main container
1557 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1558
1559 // create description
1560 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1561 textArea->textColor = BLACK;
1562 textArea->text = PIC(description);
1563 textArea->fontId = SMALL_REGULAR_FONT;
1564 textArea->style = NO_STYLE;
1565 textArea->wrapping = true;
1566 textArea->obj.area.width = AVAILABLE_WIDTH;
1567 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1568 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1569 textArea->textAlignment = MID_LEFT;
1570 textArea->obj.alignment = NO_ALIGNMENT;
1571 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1572 textArea->obj.alignmentMarginY = PRE_DESCRIPTION_MARGIN;
1573 // set this new obj as child of main container
1574 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1575
1576 // create info on the bottom
1577 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1578 textArea->textColor = DARK_GRAY;
1579 textArea->text = PIC(info);
1580 textArea->fontId = SMALL_REGULAR_FONT;
1581 textArea->style = NO_STYLE;
1582 textArea->wrapping = true;
1583 textArea->obj.area.width = AVAILABLE_WIDTH;
1584 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1585 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1586 textArea->textAlignment = MID_LEFT;
1587 textArea->obj.alignment = BOTTOM_LEFT;
1588 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1589 textArea->obj.alignmentMarginY = 40;
1590 // set this new obj as child of main container
1591 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1592
1593 return layoutInt->container->obj.area.height;
1594}
1595
1604{
1605 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1606 layoutObj_t *obj;
1607 uint8_t i;
1608
1609 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddRadioChoice():\n");
1610 if (layout == NULL) {
1611 return -1;
1612 }
1613 for (i = 0; i < choices->nbChoices; i++) {
1614 nbgl_container_t *container;
1615 nbgl_text_area_t *textArea;
1616 nbgl_radio_t *button;
1617 nbgl_line_t *line;
1618
1619 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1620 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1621 button = (nbgl_radio_t *) nbgl_objPoolGet(RADIO_BUTTON, layoutInt->layer);
1622
1624 layoutInt, (nbgl_obj_t *) container, choices->token, choices->tuneId);
1625 if (obj == NULL) {
1626 return -1;
1627 }
1628
1629 // get container children (max 2)
1630 container->nbChildren = 2;
1631 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1632 container->obj.area.width = AVAILABLE_WIDTH;
1633 container->obj.area.height = RADIO_CHOICE_HEIGHT;
1634 container->obj.alignment = NO_ALIGNMENT;
1635 container->obj.alignmentMarginX = BORDER_MARGIN;
1636 container->obj.alignTo = (nbgl_obj_t *) NULL;
1637
1638 // init button for this choice
1639 button->activeColor = BLACK;
1640 button->borderColor = LIGHT_GRAY;
1641 button->obj.alignTo = (nbgl_obj_t *) container;
1642 button->obj.alignment = MID_RIGHT;
1643 button->state = OFF_STATE;
1644 container->children[1] = (nbgl_obj_t *) button;
1645
1646 // init text area for this choice
1647 if (choices->localized == true) {
1648#ifdef HAVE_LANGUAGE_PACK
1649 textArea->text = get_ux_loc_string(choices->nameIds[i]);
1650#endif // HAVE_LANGUAGE_PACK
1651 }
1652 else {
1653 textArea->text = PIC(choices->names[i]);
1654 }
1655 textArea->textAlignment = MID_LEFT;
1656 textArea->obj.area.width = container->obj.area.width - RADIO_WIDTH;
1657 textArea->style = NO_STYLE;
1658 textArea->obj.alignment = MID_LEFT;
1659 textArea->obj.alignTo = (nbgl_obj_t *) container;
1660 container->children[0] = (nbgl_obj_t *) textArea;
1661
1662 // whole container should be touchable
1663 container->obj.touchMask = (1 << TOUCHED);
1664 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
1665 nbTouchableControls++;
1666
1667 // highlight init choice
1668 if (i == choices->initChoice) {
1669 button->state = ON_STATE;
1670 textArea->textColor = BLACK;
1671 textArea->fontId = SMALL_BOLD_FONT;
1672 }
1673 else {
1674 button->state = OFF_STATE;
1675 textArea->textColor = DARK_GRAY;
1676 textArea->fontId = SMALL_REGULAR_FONT;
1677 }
1678 textArea->obj.area.height = nbgl_getFontHeight(textArea->fontId);
1679
1680 line = createHorizontalLine(layoutInt->layer);
1681 line->obj.alignmentMarginY = -4;
1682 line->offset = 3;
1683
1684 // set these new objs as child of main container
1685 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1686 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
1687 }
1688
1689 return 0;
1690}
1691
1701{
1702 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1703 nbgl_container_t *container;
1704 nbgl_contentCenter_t centeredInfo = {0};
1705
1706 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddCenteredInfo():\n");
1707 if (layout == NULL) {
1708 return -1;
1709 }
1710
1711 centeredInfo.icon = info->icon;
1712 centeredInfo.illustrType = ICON_ILLUSTRATION;
1713
1714 if (info->text1 != NULL) {
1715 if (info->style != NORMAL_INFO) {
1716 centeredInfo.title = info->text1;
1717 }
1718 else {
1719 centeredInfo.smallTitle = info->text1;
1720 }
1721 }
1722 if (info->text2 != NULL) {
1723 if (info->style != LARGE_CASE_BOLD_INFO) {
1724 centeredInfo.description = info->text2;
1725 }
1726 else {
1727 centeredInfo.smallTitle = info->text2;
1728 }
1729 }
1730 if (info->text3 != NULL) {
1731 if (info->style == LARGE_CASE_GRAY_INFO) {
1732 centeredInfo.subText = info->text3;
1733 }
1734 else {
1735 centeredInfo.description = info->text3;
1736 }
1737 }
1738 container = addContentCenter(layoutInt, &centeredInfo);
1739
1740 if (info->onTop) {
1741 container->obj.alignmentMarginX = BORDER_MARGIN;
1742 container->obj.alignmentMarginY = BORDER_MARGIN + info->offsetY;
1743 container->obj.alignment = NO_ALIGNMENT;
1744 }
1745 else {
1746 container->obj.alignmentMarginY = info->offsetY;
1747 }
1748
1749 return container->obj.area.height;
1750}
1751
1761{
1762 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1763 nbgl_container_t *container;
1764
1765 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddContentCenter():\n");
1766 if (layout == NULL) {
1767 return -1;
1768 }
1769
1770 container = addContentCenter(layoutInt, info);
1771
1772 return container->obj.area.height;
1773}
1774
1783{
1784 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1785 nbgl_container_t *container;
1786 nbgl_text_area_t *textArea;
1787 uint8_t row;
1788
1789 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLeftContent():\n");
1790 if (layout == NULL) {
1791 return -1;
1792 }
1793 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1794
1795 // get container children
1796 container->nbChildren = info->nbRows + 1;
1797 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1798 container->layout = VERTICAL;
1799 container->obj.area.width = AVAILABLE_WIDTH;
1800 container->obj.alignmentMarginX = BORDER_MARGIN;
1801
1802 // create title
1803 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1804 textArea->textColor = BLACK;
1805 textArea->text = PIC(info->title);
1806 textArea->textAlignment = MID_LEFT;
1807 textArea->fontId = LARGE_MEDIUM_FONT;
1808 textArea->wrapping = true;
1809 textArea->obj.area.width = AVAILABLE_WIDTH;
1810 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1811 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1812
1813 container->obj.area.height += textArea->obj.area.height;
1814
1815 container->children[0] = (nbgl_obj_t *) textArea;
1816
1817 for (row = 0; row < info->nbRows; row++) {
1818 nbgl_container_t *rowContainer;
1819 nbgl_image_t *image;
1820
1821 // create a grid with 1 icon on the left column and 1 text on the right one
1822 rowContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, 0);
1823 rowContainer->nbChildren = 2; // 1 icon + 1 text
1824 rowContainer->children = nbgl_containerPoolGet(rowContainer->nbChildren, 0);
1825 rowContainer->obj.area.width = AVAILABLE_WIDTH;
1826
1827 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, 0);
1828 image->foregroundColor = BLACK;
1829 image->buffer = info->rowIcons[row];
1830 rowContainer->children[0] = (nbgl_obj_t *) image;
1831
1832 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, 0);
1833 textArea->textColor = BLACK;
1834 textArea->text = info->rowTexts[row];
1835 textArea->textAlignment = MID_LEFT;
1836 textArea->fontId = SMALL_REGULAR_FONT;
1837 textArea->wrapping = true;
1838 textArea->obj.area.width = AVAILABLE_WIDTH - image->buffer->width - 16;
1839 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1840 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1841 textArea->obj.alignment = MID_RIGHT;
1842 rowContainer->children[1] = (nbgl_obj_t *) textArea;
1843 rowContainer->obj.area.height
1844 = MAX(image->buffer->height, textArea->obj.area.height + LEFT_CONTENT_TEXT_PADDING);
1845
1846 if (row == 0) {
1847 rowContainer->obj.alignmentMarginY = 32;
1848 }
1849 else {
1850 rowContainer->obj.alignmentMarginY = INTER_ROWS_MARGIN;
1851 }
1852 container->children[1 + row] = (nbgl_obj_t *) rowContainer;
1853 container->obj.area.height
1854 += rowContainer->obj.area.height + rowContainer->obj.alignmentMarginY;
1855 }
1856 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1857
1858 return container->obj.area.height;
1859}
1860
1861#ifdef NBGL_QRCODE
1871{
1872 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1873 nbgl_container_t *container;
1874 nbgl_text_area_t *textArea = NULL;
1876 uint16_t fullHeight = 0;
1877
1878 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddQRCode():\n");
1879 if (layout == NULL) {
1880 return -1;
1881 }
1882
1883 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1884
1885 // get container children (max 2 (QRCode + text1 + text2))
1886 container->children = nbgl_containerPoolGet(3, layoutInt->layer);
1887 container->nbChildren = 0;
1888
1890 // version is forced to V10 if url is longer than 62 characters
1891 if (strlen(PIC(info->url)) > 62) {
1892 qrcode->version = QRCODE_V10;
1893 }
1894 else {
1895 qrcode->version = QRCODE_V4;
1896 }
1897 qrcode->foregroundColor = BLACK;
1898 // in QR V4, we use 8*8 screen pixels for one QR pixel
1899 // in QR V10, we use 4*4 screen pixels for one QR pixel
1900 qrcode->obj.area.width
1901 = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 8) : (QR_V10_NB_PIX_SIZE * 4);
1902 qrcode->obj.area.height = qrcode->obj.area.width;
1903 qrcode->text = PIC(info->url);
1904 qrcode->obj.area.bpp = NBGL_BPP_1;
1905 qrcode->obj.alignment = TOP_MIDDLE;
1906
1907 fullHeight += qrcode->obj.area.height;
1908 container->children[container->nbChildren] = (nbgl_obj_t *) qrcode;
1909 container->nbChildren++;
1910
1911 if (info->text1 != NULL) {
1912 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1913 textArea->textColor = BLACK;
1914 textArea->text = PIC(info->text1);
1915 textArea->textAlignment = CENTER;
1916 textArea->fontId = (info->largeText1 == true) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
1917 textArea->wrapping = true;
1918 textArea->obj.area.width = AVAILABLE_WIDTH;
1919 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1920 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1921 textArea->obj.alignment = BOTTOM_MIDDLE;
1922 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1923 textArea->obj.alignmentMarginY = 24;
1924
1925 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1926
1927 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1928 container->nbChildren++;
1929 }
1930 if (info->text2 != NULL) {
1931 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1932 textArea->textColor = DARK_GRAY;
1933 textArea->text = PIC(info->text2);
1934 textArea->textAlignment = CENTER;
1935 textArea->fontId = SMALL_REGULAR_FONT;
1936 textArea->wrapping = true;
1937 textArea->obj.area.width = AVAILABLE_WIDTH;
1938 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1939 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1940 textArea->obj.alignment = BOTTOM_MIDDLE;
1941 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1942 if (info->text1 != NULL) {
1943 textArea->obj.alignmentMarginY = QR_INTER_TEXTS_MARGIN;
1944 }
1945 else {
1946 textArea->obj.alignmentMarginY = 32;
1947 fullHeight += 8;
1948 }
1949
1950 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1951
1952 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1953 container->nbChildren++;
1954 }
1955 // ensure that fullHeight is fitting in main container height (with 16 px margin)
1956 if ((fullHeight >= (layoutInt->container->obj.area.height - 16))
1957 && (qrcode->version == QRCODE_V4)) {
1958 qrcode->version = QRCODE_V4_SMALL;
1959 // in QR V4 small, we use 4*4 screen pixels for one QR pixel
1960 qrcode->obj.area.width = QR_V4_NB_PIX_SIZE * 4;
1961 qrcode->obj.area.height = qrcode->obj.area.width;
1962 fullHeight -= QR_V4_NB_PIX_SIZE * 4;
1963 }
1964 container->obj.area.height = fullHeight;
1965 container->layout = VERTICAL;
1966 if (info->centered) {
1967 container->obj.alignment = CENTER;
1968 }
1969 else {
1970 container->obj.alignment = BOTTOM_MIDDLE;
1971 container->obj.alignTo
1972 = layoutInt->container->children[layoutInt->container->nbChildren - 1];
1973 }
1974 container->obj.alignmentMarginY = info->offsetY;
1975
1976 container->obj.area.width = AVAILABLE_WIDTH;
1977
1978 // set this new container as child of main container
1979 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1980
1981 return fullHeight;
1982}
1983#endif // NBGL_QRCODE
1984
1994{
1995 nbgl_layoutFooter_t footerDesc;
1996 footerDesc.type = FOOTER_CHOICE_BUTTONS;
1997 footerDesc.separationLine = false;
1998 footerDesc.choiceButtons.bottomText = info->bottomText;
1999 footerDesc.choiceButtons.token = info->token;
2000 footerDesc.choiceButtons.topText = info->topText;
2001 footerDesc.choiceButtons.topIcon = info->topIcon;
2002 footerDesc.choiceButtons.style = info->style;
2003 footerDesc.choiceButtons.tuneId = info->tuneId;
2004 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2005}
2006
2018{
2020 .horizontalButtons.leftIcon = info->leftIcon,
2021 .horizontalButtons.leftToken = info->leftToken,
2022 .horizontalButtons.rightText = info->rightText,
2023 .horizontalButtons.rightToken = info->rightToken,
2024 .horizontalButtons.tuneId = info->tuneId};
2025
2026 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHorizontalButtons():\n");
2027 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2028}
2029
2038{
2039 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2040 nbgl_text_area_t *itemTextArea;
2041 nbgl_text_area_t *valueTextArea;
2042 nbgl_container_t *container;
2043 uint8_t i;
2044
2045 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTagValueList():\n");
2046 if (layout == NULL) {
2047 return -1;
2048 }
2049
2050 for (i = 0; i < list->nbPairs; i++) {
2051 const nbgl_layoutTagValue_t *pair;
2052 uint16_t fullHeight = 0;
2053 const nbgl_icon_details_t *valueIcon = NULL;
2054
2055 if (list->pairs != NULL) {
2056 pair = &list->pairs[i];
2057 }
2058 else {
2059 pair = list->callback(list->startIndex + i);
2060 }
2061
2062 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2063
2064 // get container children (max 3 if a valueIcon, otherwise 2)
2065 container->children
2066 = nbgl_containerPoolGet((pair->valueIcon != NULL) ? 3 : 2, layoutInt->layer);
2067
2068 itemTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2069 valueTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2070
2071 // init text area for this choice
2072 itemTextArea->textColor = DARK_GRAY;
2073 itemTextArea->text = PIC(pair->item);
2074 itemTextArea->textAlignment = MID_LEFT;
2075 itemTextArea->fontId = SMALL_REGULAR_FONT;
2076 itemTextArea->wrapping = true;
2077 itemTextArea->obj.area.width = AVAILABLE_WIDTH;
2078 itemTextArea->obj.area.height = nbgl_getTextHeightInWidth(
2079 itemTextArea->fontId, itemTextArea->text, AVAILABLE_WIDTH, itemTextArea->wrapping);
2080 itemTextArea->style = NO_STYLE;
2081 itemTextArea->obj.alignment = NO_ALIGNMENT;
2082 itemTextArea->obj.alignmentMarginX = 0;
2083 itemTextArea->obj.alignmentMarginY = 0;
2084 itemTextArea->obj.alignTo = NULL;
2085 container->children[container->nbChildren] = (nbgl_obj_t *) itemTextArea;
2086 container->nbChildren++;
2087
2088 fullHeight += itemTextArea->obj.area.height;
2089
2090 // init button for this choice
2091 valueTextArea->textColor = BLACK;
2092 valueTextArea->text = PIC(pair->value);
2093 valueTextArea->textAlignment = MID_LEFT;
2094 if (list->smallCaseForValue) {
2095 valueTextArea->fontId = SMALL_BOLD_FONT;
2096 }
2097 else {
2098 valueTextArea->fontId = LARGE_MEDIUM_FONT;
2099 }
2100 if ((pair->aliasValue == 0) && (pair->valueIcon == NULL)) {
2101 valueTextArea->obj.area.width = AVAILABLE_WIDTH;
2102 }
2103 else {
2104 if (pair->aliasValue) {
2105 // if the value is an alias, we automatically display a (>) icon
2106 valueIcon = &MINI_PUSH_ICON;
2107 }
2108 else {
2109 // otherwise use the provided icon
2110 valueIcon = PIC(pair->valueIcon);
2111 }
2112 // decrease the available width for value text
2113 valueTextArea->obj.area.width = AVAILABLE_WIDTH - valueIcon->width - 12;
2114 }
2115
2116 // handle the nbMaxLinesForValue parameter, used to automatically keep only
2117 // nbMaxLinesForValue lines
2118 uint16_t nbLines = nbgl_getTextNbLinesInWidth(valueTextArea->fontId,
2119 valueTextArea->text,
2120 valueTextArea->obj.area.width,
2121 list->wrapping);
2122 // use this nbMaxLinesForValue parameter only if >0
2123 if ((list->nbMaxLinesForValue > 0) && (nbLines > list->nbMaxLinesForValue)) {
2124 nbLines = list->nbMaxLinesForValue;
2125 valueTextArea->nbMaxLines = list->nbMaxLinesForValue;
2126 }
2127 const nbgl_font_t *font = nbgl_getFont(valueTextArea->fontId);
2128 valueTextArea->obj.area.height = nbLines * font->line_height;
2129 valueTextArea->style = NO_STYLE;
2130 valueTextArea->obj.alignment = BOTTOM_LEFT;
2131 valueTextArea->obj.alignmentMarginY = 4;
2132 valueTextArea->obj.alignTo = (nbgl_obj_t *) itemTextArea;
2133 valueTextArea->wrapping = list->wrapping;
2134 container->children[container->nbChildren] = (nbgl_obj_t *) valueTextArea;
2135 container->nbChildren++;
2136
2137 fullHeight += valueTextArea->obj.area.height + valueTextArea->obj.alignmentMarginY;
2138 if (valueIcon != NULL) {
2139 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2141 layoutInt, (nbgl_obj_t *) image, list->token, TUNE_TAP_CASUAL);
2142 obj->index = i;
2143 image->foregroundColor = BLACK;
2144 image->buffer = valueIcon;
2145 image->obj.alignment = RIGHT_TOP;
2146 image->obj.alignmentMarginX = 12;
2147 image->obj.alignTo = (nbgl_obj_t *) valueTextArea;
2148 image->obj.touchMask = (1 << TOUCHED);
2149 image->obj.touchId = VALUE_BUTTON_1_ID + i;
2150
2151 container->children[container->nbChildren] = (nbgl_obj_t *) image;
2152 container->nbChildren++;
2153 }
2154
2155 container->obj.area.width = AVAILABLE_WIDTH;
2156 container->obj.area.height = fullHeight;
2157 container->layout = VERTICAL;
2158 container->obj.alignmentMarginX = BORDER_MARGIN;
2159 if (i > 0) {
2160 container->obj.alignmentMarginY = INTER_TAG_VALUE_MARGIN;
2161 }
2162 else {
2163 container->obj.alignmentMarginY = PRE_TAG_VALUE_MARGIN;
2164 }
2165 container->obj.alignment = NO_ALIGNMENT;
2166
2167 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2168 }
2169
2170 return 0;
2171}
2172
2186 const char *text,
2187 const char *subText,
2188 uint8_t percentage)
2189{
2190 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2191 nbgl_container_t *container;
2192 nbgl_text_area_t *textArea;
2193 nbgl_progress_bar_t *progress;
2194
2195 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressBar():\n");
2196 if (layout == NULL) {
2197 return -1;
2198 }
2199
2200 // First Create Container :
2201 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2202 // progressbar + text + subText
2203 container->nbChildren = (subText != NULL) ? 3 : 2;
2204 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
2205
2206 container->obj.area.width = AVAILABLE_WIDTH;
2207 container->layout = VERTICAL;
2208 container->obj.alignment = CENTER;
2209
2210 // Create progressbar
2211 progress = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
2212 progress->foregroundColor = BLACK;
2213 progress->withBorder = true;
2214 progress->state = percentage;
2215 progress->obj.area.width = PROGRESSBAR_WIDTH;
2216 progress->obj.area.height = PROGRESSBAR_HEIGHT;
2217 progress->obj.alignment = TOP_MIDDLE;
2218
2219 // set this new progressbar as child of the container
2220 container->children[0] = (nbgl_obj_t *) progress;
2221
2222 // update container height
2223 container->obj.area.height = progress->obj.alignmentMarginY + progress->obj.area.height;
2224
2225 // create text area
2226 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2227 textArea->textColor = BLACK;
2228 textArea->text = PIC(text);
2229 textArea->textAlignment = CENTER;
2230 textArea->fontId = LARGE_MEDIUM_FONT;
2231 textArea->wrapping = true;
2232 textArea->obj.alignmentMarginY = BAR_TEXT_MARGIN;
2233 textArea->obj.alignTo = (nbgl_obj_t *) progress;
2234 textArea->obj.alignment = BOTTOM_MIDDLE;
2235 textArea->obj.area.width = AVAILABLE_WIDTH;
2236 textArea->obj.area.height = nbgl_getTextHeightInWidth(
2237 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
2238 textArea->style = NO_STYLE;
2239
2240 // update container height
2241 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
2242
2243 // set this text as child of the container
2244 container->children[1] = (nbgl_obj_t *) textArea;
2245
2246 if (subText != NULL) {
2247 nbgl_text_area_t *subTextArea;
2248 // create sub-text area
2249 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2250 subTextArea->textColor = BLACK;
2251 subTextArea->text = PIC(subText);
2252 subTextArea->textAlignment = CENTER;
2253 subTextArea->fontId = SMALL_REGULAR_FONT;
2254 subTextArea->wrapping = true;
2255 subTextArea->obj.alignmentMarginY = BAR_INTER_TEXTS_MARGIN;
2256 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
2257 subTextArea->obj.alignment = BOTTOM_MIDDLE;
2258 subTextArea->obj.area.width = AVAILABLE_WIDTH;
2259 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
2260 subTextArea->text,
2261 subTextArea->obj.area.width,
2262 subTextArea->wrapping);
2263 subTextArea->style = NO_STYLE;
2264
2265 // update container height
2266 container->obj.area.height
2267 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
2268
2269 // set thissub-text as child of the container
2270 container->children[2] = (nbgl_obj_t *) subTextArea;
2271 }
2272 // The height of containers must be a multiple of eight
2273 container->obj.area.height = (container->obj.area.height + 7) & 0xFFF8;
2274
2275 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2276
2277 return 2;
2278}
2279
2293 const char *text,
2294 const char *subText,
2295 uint8_t percentage)
2296{
2297 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2298 nbgl_container_t *container;
2299 nbgl_text_area_t *textArea;
2300 nbgl_progress_bar_t *progress;
2301 int ret = 0;
2302
2303 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutUpdateProgressBar():\n");
2304 if ((layout == NULL) || (layoutInt->container->nbChildren == 0)) {
2305 return -1;
2306 }
2307
2308 container = (nbgl_container_t *) layoutInt->container->children[0];
2309 if ((container->obj.type != CONTAINER) || (container->nbChildren < 2)) {
2310 return -1;
2311 }
2312
2313 progress = (nbgl_progress_bar_t *) container->children[0];
2314 if (progress->obj.type != PROGRESS_BAR) {
2315 return -1;
2316 }
2317
2318 // if percentage is different, redraw
2319 if (progress->state != percentage) {
2320 progress->partialRedraw = true;
2321 progress->state = percentage;
2322 nbgl_objDraw((nbgl_obj_t *) progress);
2323 ret = 1;
2324 }
2325
2326 // update text area if necessary
2327 textArea = (nbgl_text_area_t *) container->children[1];
2328 if (textArea->obj.type != TEXT_AREA) {
2329 return -1;
2330 }
2331
2332 const char *newText = PIC(text);
2333 size_t newTextLen = strlen(newText);
2334 // if text is different, redraw (don't use strcmp because it crashes with Rust SDK)
2335 if ((newTextLen != strlen(textArea->text)) || memcmp(textArea->text, newText, newTextLen)) {
2336 textArea->text = newText;
2337 nbgl_objDraw((nbgl_obj_t *) textArea);
2338 ret = 2;
2339 }
2340
2341 if (subText != NULL) {
2342 nbgl_text_area_t *subTextArea;
2343
2344 if (container->nbChildren != 3) {
2345 return -1;
2346 }
2347
2348 subTextArea = (nbgl_text_area_t *) container->children[2];
2349 if (subTextArea->obj.type != TEXT_AREA) {
2350 return -1;
2351 }
2352 const char *newSubText = PIC(subText);
2353 size_t newSubTextLen = strlen(newSubText);
2354 // if text is different, redraw
2355 if ((newSubTextLen != strlen(subTextArea->text))
2356 || memcmp(subTextArea->text, newSubText, newSubTextLen)) {
2357 subTextArea->text = newSubText;
2358 nbgl_objDraw((nbgl_obj_t *) subTextArea);
2359 ret = 2;
2360 }
2361 }
2362
2363 return ret;
2364}
2365
2373{
2374 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2375 nbgl_line_t *line;
2376
2377 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSeparationLine():\n");
2378 line = createHorizontalLine(layoutInt->layer);
2379 line->obj.alignmentMarginY = -4;
2380 line->offset = 3;
2381 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
2382 return 0;
2383}
2384
2393{
2394 layoutObj_t *obj;
2395 nbgl_button_t *button;
2396 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2397
2398 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddButton():\n");
2399 if (layout == NULL) {
2400 return -1;
2401 }
2402
2403 // Add in footer if matching
2404 if ((buttonInfo->onBottom) && (!buttonInfo->fittingContent)) {
2405 if (layoutInt->footerContainer == NULL) {
2406 nbgl_layoutFooter_t footerDesc;
2407 footerDesc.type = FOOTER_SIMPLE_BUTTON;
2408 footerDesc.separationLine = false;
2409 footerDesc.button.text = buttonInfo->text;
2410 footerDesc.button.token = buttonInfo->token;
2411 footerDesc.button.tuneId = buttonInfo->tuneId;
2412 footerDesc.button.icon = buttonInfo->icon;
2413 footerDesc.button.style = buttonInfo->style;
2414 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2415 }
2416 else {
2417 nbgl_layoutUpFooter_t upFooterDesc;
2418 upFooterDesc.type = UP_FOOTER_BUTTON;
2419 upFooterDesc.button.text = buttonInfo->text;
2420 upFooterDesc.button.token = buttonInfo->token;
2421 upFooterDesc.button.tuneId = buttonInfo->tuneId;
2422 upFooterDesc.button.icon = buttonInfo->icon;
2423 upFooterDesc.button.style = buttonInfo->style;
2424 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2425 }
2426 }
2427
2428 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2430 layoutInt, (nbgl_obj_t *) button, buttonInfo->token, buttonInfo->tuneId);
2431 if (obj == NULL) {
2432 return -1;
2433 }
2434
2435 button->obj.alignmentMarginX = BORDER_MARGIN;
2436 button->obj.alignmentMarginY = 12;
2437 button->obj.alignment = NO_ALIGNMENT;
2438 if (buttonInfo->style == BLACK_BACKGROUND) {
2439 button->innerColor = BLACK;
2440 button->foregroundColor = WHITE;
2441 }
2442 else {
2443 button->innerColor = WHITE;
2444 button->foregroundColor = BLACK;
2445 }
2446 if (buttonInfo->style == NO_BORDER) {
2447 button->borderColor = WHITE;
2448 }
2449 else {
2450 if (buttonInfo->style == BLACK_BACKGROUND) {
2451 button->borderColor = BLACK;
2452 }
2453 else {
2454 button->borderColor = LIGHT_GRAY;
2455 }
2456 }
2457 button->text = PIC(buttonInfo->text);
2458 button->fontId = SMALL_BOLD_FONT;
2459 button->icon = PIC(buttonInfo->icon);
2460 if (buttonInfo->fittingContent == true) {
2461 button->obj.area.width = nbgl_getTextWidth(button->fontId, button->text)
2462 + SMALL_BUTTON_HEIGHT
2463 + ((button->icon) ? (button->icon->width + 12) : 0);
2464 button->obj.area.height = SMALL_BUTTON_HEIGHT;
2465 button->radius = SMALL_BUTTON_RADIUS_INDEX;
2466 if (buttonInfo->onBottom != true) {
2467 button->obj.alignmentMarginX
2468 += (SCREEN_WIDTH - 2 * BORDER_MARGIN - button->obj.area.width) / 2;
2469 }
2470 }
2471 else {
2472 button->obj.area.width = AVAILABLE_WIDTH;
2473 button->obj.area.height = BUTTON_DIAMETER;
2474 button->radius = BUTTON_RADIUS;
2475 }
2476 button->obj.alignTo = NULL;
2477 button->obj.touchMask = (1 << TOUCHED);
2478 button->obj.touchId = (buttonInfo->fittingContent) ? EXTRA_BUTTON_ID : SINGLE_BUTTON_ID;
2479 // set this new button as child of the container
2480 layoutAddObject(layoutInt, (nbgl_obj_t *) button);
2481
2482 return 0;
2483}
2484
2495 const char *text,
2496 uint8_t token,
2497 tune_index_e tuneId)
2498{
2500 .longPress.text = text,
2501 .longPress.token = token,
2502 .longPress.tuneId = tuneId};
2503
2504 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLongPressButton():\n");
2505 if (layout == NULL) {
2506 return -1;
2507 }
2508
2509 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2510}
2511
2523 const char *text,
2524 uint8_t token,
2525 tune_index_e tuneId)
2526{
2527 nbgl_layoutFooter_t footerDesc;
2528 footerDesc.type = FOOTER_SIMPLE_TEXT;
2529 footerDesc.separationLine = true;
2530 footerDesc.simpleText.text = text;
2531 footerDesc.simpleText.mutedOut = false;
2532 footerDesc.simpleText.token = token;
2533 footerDesc.simpleText.tuneId = tuneId;
2534 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2535}
2536
2550 const char *leftText,
2551 uint8_t leftToken,
2552 const char *rightText,
2553 uint8_t rightToken,
2554 tune_index_e tuneId)
2555{
2556 nbgl_layoutFooter_t footerDesc;
2557 footerDesc.type = FOOTER_DOUBLE_TEXT;
2558 footerDesc.separationLine = true;
2559 footerDesc.doubleText.leftText = leftText;
2560 footerDesc.doubleText.leftToken = leftToken;
2561 footerDesc.doubleText.rightText = rightText;
2562 footerDesc.doubleText.rightToken = rightToken;
2563 footerDesc.doubleText.tuneId = tuneId;
2564 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2565}
2566
2576{
2577 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2578 layoutObj_t *obj;
2579 nbgl_text_area_t *textArea;
2580 nbgl_line_t *line, *separationLine = NULL;
2581 nbgl_image_t *image = NULL;
2582 nbgl_button_t *button;
2583
2584 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHeader(): type = %d\n", headerDesc->type);
2585 if (layout == NULL) {
2586 return -1;
2587 }
2588 if ((headerDesc == NULL) || (headerDesc->type >= NB_HEADER_TYPES)) {
2589 return -2;
2590 }
2591
2592 layoutInt->headerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2593 layoutInt->headerContainer->obj.area.width = SCREEN_WIDTH;
2594 layoutInt->headerContainer->layout = VERTICAL;
2595 layoutInt->headerContainer->children
2596 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2597 layoutInt->headerContainer->obj.alignment = TOP_MIDDLE;
2598
2599 switch (headerDesc->type) {
2600 case HEADER_EMPTY: {
2601 layoutInt->headerContainer->obj.area.height = headerDesc->emptySpace.height;
2602 break;
2603 }
2606 case HEADER_EXTENDED_BACK: {
2607 const char *text = (headerDesc->type == HEADER_EXTENDED_BACK)
2608 ? PIC(headerDesc->extendedBack.text)
2609 : PIC(headerDesc->backAndText.text);
2610 uint8_t backToken = (headerDesc->type == HEADER_EXTENDED_BACK)
2611 ? headerDesc->extendedBack.backToken
2612 : headerDesc->backAndText.token;
2613 // add back button
2614 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2615 // only make it active if valid token
2616 if (backToken != NBGL_INVALID_TOKEN) {
2617 obj = layoutAddCallbackObj(layoutInt,
2618 (nbgl_obj_t *) button,
2619 backToken,
2620 (headerDesc->type == HEADER_EXTENDED_BACK)
2621 ? headerDesc->extendedBack.tuneId
2622 : headerDesc->backAndText.tuneId);
2623 if (obj == NULL) {
2624 return -1;
2625 }
2626 }
2627
2628 button->obj.alignment = MID_LEFT;
2629 button->innerColor = WHITE;
2630 button->foregroundColor = (backToken != NBGL_INVALID_TOKEN) ? BLACK : WHITE;
2631 button->borderColor = WHITE;
2632 button->obj.area.width = BACK_KEY_WIDTH;
2633 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2634 button->text = NULL;
2635 button->icon = PIC(&LEFT_ARROW_ICON);
2636 button->obj.touchMask = (backToken != NBGL_INVALID_TOKEN) ? (1 << TOUCHED) : 0;
2637 button->obj.touchId = BACK_BUTTON_ID;
2638 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2639 = (nbgl_obj_t *) button;
2640 layoutInt->headerContainer->nbChildren++;
2641
2642 // add optional text if needed
2643 if (text != NULL) {
2644 // add optional icon if type is HEADER_BACK_ICON_AND_TEXT
2645 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2646 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2647 image->buffer = PIC(headerDesc->backAndText.icon);
2648 image->foregroundColor = BLACK;
2649 image->obj.alignment = CENTER;
2650 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2651 = (nbgl_obj_t *) image;
2652 layoutInt->headerContainer->nbChildren++;
2653 }
2654 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2655 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2656 && (headerDesc->extendedBack.textToken != NBGL_INVALID_TOKEN)) {
2657 obj = layoutAddCallbackObj(layoutInt,
2658 (nbgl_obj_t *) textArea,
2659 headerDesc->extendedBack.textToken,
2660 headerDesc->extendedBack.tuneId);
2661 if (obj == NULL) {
2662 return -1;
2663 }
2664 textArea->obj.touchMask = (1 << TOUCHED);
2665 }
2666 textArea->obj.alignment = CENTER;
2667 textArea->textColor = BLACK;
2668 textArea->obj.area.width
2669 = layoutInt->headerContainer->obj.area.width - 2 * BACK_KEY_WIDTH;
2670 // if icon, reduce to 1 line and fit text width
2671 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2672 textArea->obj.area.width -= 16 + image->buffer->width;
2673 }
2674 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2675 textArea->text = text;
2676 textArea->fontId = SMALL_BOLD_FONT;
2677 textArea->textAlignment = CENTER;
2678 textArea->wrapping = true;
2679 uint8_t nbMaxLines = (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) ? 1 : 2;
2680 // ensure that text fits on 2 lines maximum
2681 if (nbgl_getTextNbLinesInWidth(textArea->fontId,
2682 textArea->text,
2683 textArea->obj.area.width,
2684 textArea->wrapping)
2685 > nbMaxLines) {
2687 "nbgl_layoutAddHeader: text [%s] is too long for header\n",
2688 text);
2689 }
2690 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2691 textArea->obj.area.width = nbgl_getTextWidth(textArea->fontId, textArea->text);
2692 }
2693 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2694 = (nbgl_obj_t *) textArea;
2695 layoutInt->headerContainer->nbChildren++;
2696 // if icon, realign icon & text
2697 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2698 textArea->obj.alignmentMarginX = 8 + image->buffer->width / 2;
2699 image->obj.alignmentMarginX = -8 - textArea->obj.area.width / 2;
2700 }
2701 }
2702 // add action key if the type is HEADER_EXTENDED_BACK
2703 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2704 && (headerDesc->extendedBack.actionIcon)) {
2705 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2706 // if token is valid
2707 if (headerDesc->extendedBack.actionToken != NBGL_INVALID_TOKEN) {
2708 obj = layoutAddCallbackObj(layoutInt,
2709 (nbgl_obj_t *) button,
2710 headerDesc->extendedBack.actionToken,
2711 headerDesc->extendedBack.tuneId);
2712 if (obj == NULL) {
2713 return -1;
2714 }
2715 button->obj.touchMask = (1 << TOUCHED);
2716 }
2717
2718 button->obj.alignment = MID_RIGHT;
2719 button->innerColor = WHITE;
2720 button->foregroundColor
2722 : LIGHT_GRAY;
2723 button->borderColor = WHITE;
2724 button->obj.area.width = BACK_KEY_WIDTH;
2725 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2726 button->text = NULL;
2727 button->icon = PIC(headerDesc->extendedBack.actionIcon);
2728 button->obj.touchId = EXTRA_BUTTON_ID;
2729 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2730 = (nbgl_obj_t *) button;
2731 layoutInt->headerContainer->nbChildren++;
2732 }
2733
2734 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2735 break;
2736 }
2738#ifdef TARGET_STAX
2739 // add optional back button
2740 if (headerDesc->progressAndBack.withBack) {
2741 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2742 obj = layoutAddCallbackObj(layoutInt,
2743 (nbgl_obj_t *) button,
2744 headerDesc->progressAndBack.token,
2745 headerDesc->progressAndBack.tuneId);
2746 if (obj == NULL) {
2747 return -1;
2748 }
2749
2750 button->obj.alignment = MID_LEFT;
2751 button->innerColor = WHITE;
2752 button->foregroundColor = BLACK;
2753 button->borderColor = WHITE;
2754 button->obj.area.width = BACK_KEY_WIDTH;
2755 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2756 button->text = NULL;
2757 button->icon = PIC(&LEFT_ARROW_ICON);
2758 button->obj.touchMask = (1 << TOUCHED);
2759 button->obj.touchId = BACK_BUTTON_ID;
2760 // add to container
2761 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2762 = (nbgl_obj_t *) button;
2763 layoutInt->headerContainer->nbChildren++;
2764 }
2765
2766 // add progress indicator
2767 if (headerDesc->progressAndBack.nbPages > 1
2769 nbgl_page_indicator_t *progress;
2770
2771 progress
2773 progress->activePage = headerDesc->progressAndBack.activePage;
2774 progress->nbPages = headerDesc->progressAndBack.nbPages;
2775 progress->obj.area.width = 224;
2776 progress->obj.alignment = CENTER;
2777 // add to container
2778 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2779 = (nbgl_obj_t *) progress;
2780 layoutInt->headerContainer->nbChildren++;
2781 }
2782 layoutInt->activePage = headerDesc->progressAndBack.activePage;
2783 layoutInt->nbPages = headerDesc->progressAndBack.nbPages;
2784 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2785#endif // TARGET_STAX
2786 break;
2787 }
2788 case HEADER_TITLE: {
2789 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2790 textArea->textColor = BLACK;
2791 textArea->obj.area.width = AVAILABLE_WIDTH;
2792 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2793 textArea->text = PIC(headerDesc->title.text);
2794 textArea->fontId = SMALL_BOLD_FONT;
2795 textArea->textAlignment = CENTER;
2796 textArea->wrapping = true;
2797 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2798 = (nbgl_obj_t *) textArea;
2799 layoutInt->headerContainer->nbChildren++;
2800 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2801 break;
2802 }
2803 case HEADER_RIGHT_TEXT: {
2804 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2805 obj = layoutAddCallbackObj(layoutInt,
2806 (nbgl_obj_t *) textArea,
2807 headerDesc->rightText.token,
2808 headerDesc->rightText.tuneId);
2809 if (obj == NULL) {
2810 return -1;
2811 }
2812 textArea->obj.alignment = MID_RIGHT;
2813 textArea->obj.alignmentMarginX = BORDER_MARGIN;
2814 textArea->textColor = BLACK;
2815 textArea->obj.area.width = AVAILABLE_WIDTH;
2816 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2817 textArea->text = PIC(headerDesc->rightText.text);
2818 textArea->fontId = SMALL_BOLD_FONT;
2819 textArea->textAlignment = MID_RIGHT;
2820 textArea->obj.touchMask = (1 << TOUCHED);
2821 textArea->obj.touchId = TOP_RIGHT_BUTTON_ID;
2822 // add to bottom container
2823 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2824 = (nbgl_obj_t *) textArea;
2825 layoutInt->headerContainer->nbChildren++;
2826 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2827 break;
2828 }
2829 default:
2830 return -2;
2831 }
2832
2833 if (headerDesc->separationLine) {
2834 line = createHorizontalLine(layoutInt->layer);
2835 line->obj.alignment = BOTTOM_MIDDLE;
2836 line->offset = 3;
2837 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2838 = (nbgl_obj_t *) line;
2839 layoutInt->headerContainer->nbChildren++;
2840 }
2841 if (separationLine != NULL) {
2842 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2843 = (nbgl_obj_t *) separationLine;
2844 layoutInt->headerContainer->nbChildren++;
2845 }
2846 // header must be the first child
2847 layoutInt->children[HEADER_INDEX] = (nbgl_obj_t *) layoutInt->headerContainer;
2848
2849 // subtract header height from main container height
2850 layoutInt->container->obj.area.height -= layoutInt->headerContainer->obj.area.height;
2851 layoutInt->container->obj.alignTo = (nbgl_obj_t *) layoutInt->headerContainer;
2852 layoutInt->container->obj.alignment = BOTTOM_LEFT;
2853
2854 layoutInt->headerType = headerDesc->type;
2855
2856 return layoutInt->headerContainer->obj.area.height;
2857}
2858
2868{
2869 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2870 layoutObj_t *obj;
2871 nbgl_text_area_t *textArea;
2872 nbgl_line_t *line, *separationLine = NULL;
2873 nbgl_button_t *button;
2874
2875 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddExtendedFooter():\n");
2876 if (layout == NULL) {
2877 return -1;
2878 }
2879 if ((footerDesc == NULL) || (footerDesc->type >= NB_FOOTER_TYPES)) {
2880 return -2;
2881 }
2882
2883 layoutInt->footerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2884 layoutInt->footerContainer->obj.area.width = AVAILABLE_WIDTH;
2885 layoutInt->footerContainer->layout = VERTICAL;
2886 layoutInt->footerContainer->children
2887 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2888 layoutInt->footerContainer->obj.alignment = BOTTOM_MIDDLE;
2889
2890 switch (footerDesc->type) {
2891 case FOOTER_EMPTY: {
2892 layoutInt->footerContainer->obj.area.height = footerDesc->emptySpace.height;
2893 break;
2894 }
2895 case FOOTER_SIMPLE_TEXT: {
2896 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2897 obj = layoutAddCallbackObj(layoutInt,
2898 (nbgl_obj_t *) textArea,
2899 footerDesc->simpleText.token,
2900 footerDesc->simpleText.tuneId);
2901 if (obj == NULL) {
2902 return -1;
2903 }
2904
2905 textArea->obj.alignment = BOTTOM_MIDDLE;
2906 textArea->textColor = (footerDesc->simpleText.mutedOut) ? DARK_GRAY : BLACK;
2907 textArea->obj.area.width = AVAILABLE_WIDTH;
2908 textArea->obj.area.height
2909 = (footerDesc->simpleText.mutedOut) ? SMALL_FOOTER_HEIGHT : SIMPLE_FOOTER_HEIGHT;
2910 textArea->text = PIC(footerDesc->simpleText.text);
2911 textArea->fontId
2912 = (footerDesc->simpleText.mutedOut) ? SMALL_REGULAR_FONT : SMALL_BOLD_FONT;
2913 textArea->textAlignment = CENTER;
2914 textArea->obj.touchMask = (1 << TOUCHED);
2915 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2916 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2917 = (nbgl_obj_t *) textArea;
2918 layoutInt->footerContainer->nbChildren++;
2919 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2920 break;
2921 }
2922 case FOOTER_DOUBLE_TEXT: {
2923 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2924 obj = layoutAddCallbackObj(layoutInt,
2925 (nbgl_obj_t *) textArea,
2926 footerDesc->doubleText.leftToken,
2927 footerDesc->doubleText.tuneId);
2928 if (obj == NULL) {
2929 return -1;
2930 }
2931 textArea->obj.alignment = BOTTOM_LEFT;
2932 textArea->textColor = BLACK;
2933 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2934 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2935 textArea->text = PIC(footerDesc->doubleText.leftText);
2936 textArea->fontId = SMALL_BOLD_FONT;
2937 textArea->textAlignment = CENTER;
2938 textArea->obj.touchMask = (1 << TOUCHED);
2939 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2940 // add to bottom container
2941 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2942 = (nbgl_obj_t *) textArea;
2943 layoutInt->footerContainer->nbChildren++;
2944
2945 // create right touchable text
2946 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2947 obj = layoutAddCallbackObj(layoutInt,
2948 (nbgl_obj_t *) textArea,
2949 footerDesc->doubleText.rightToken,
2950 footerDesc->doubleText.tuneId);
2951 if (obj == NULL) {
2952 return -1;
2953 }
2954
2955 textArea->obj.alignment = BOTTOM_RIGHT;
2956 textArea->textColor = BLACK;
2957 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2958 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2959 textArea->text = PIC(footerDesc->doubleText.rightText);
2960 textArea->fontId = SMALL_BOLD_FONT;
2961 textArea->textAlignment = CENTER;
2962 textArea->obj.touchMask = (1 << TOUCHED);
2963 textArea->obj.touchId = RIGHT_BUTTON_ID;
2964 // add to bottom container
2965 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2966 = (nbgl_obj_t *) textArea;
2967 layoutInt->footerContainer->nbChildren++;
2968 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2969
2970 // create vertical line separating texts
2971 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
2972 separationLine->lineColor = LIGHT_GRAY;
2973 separationLine->obj.area.width = 1;
2974 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
2975 separationLine->direction = VERTICAL;
2976 separationLine->thickness = 1;
2977 separationLine->obj.alignment = MID_LEFT;
2978 separationLine->obj.alignTo = (nbgl_obj_t *) textArea;
2979 separationLine->obj.alignmentMarginX = -1;
2980 break;
2981 }
2982 case FOOTER_TEXT_AND_NAV: {
2983 layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
2984 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2985 // add touchable text on the left
2986 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2987 obj = layoutAddCallbackObj(layoutInt,
2988 (nbgl_obj_t *) textArea,
2989 footerDesc->textAndNav.token,
2990 footerDesc->textAndNav.tuneId);
2991 if (obj == NULL) {
2992 return -1;
2993 }
2994 textArea->obj.alignment = BOTTOM_LEFT;
2995 textArea->textColor = BLACK;
2996 textArea->obj.area.width = FOOTER_TEXT_AND_NAV_WIDTH;
2997 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2998 textArea->text = PIC(footerDesc->textAndNav.text);
2999 textArea->fontId = SMALL_BOLD_FONT;
3000 textArea->textAlignment = CENTER;
3001 textArea->obj.touchMask = (1 << TOUCHED);
3002 textArea->obj.touchId = BOTTOM_BUTTON_ID;
3003 // add to bottom container
3004 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3005 = (nbgl_obj_t *) textArea;
3006 layoutInt->footerContainer->nbChildren++;
3007
3008 // add navigation on the right
3009 nbgl_container_t *navContainer
3011 navContainer->obj.area.width = AVAILABLE_WIDTH;
3012 navContainer->layout = VERTICAL;
3013 navContainer->nbChildren = 4;
3014 navContainer->children
3015 = (nbgl_obj_t **) nbgl_containerPoolGet(navContainer->nbChildren, layoutInt->layer);
3016 navContainer->obj.alignment = BOTTOM_RIGHT;
3017 navContainer->obj.area.width = SCREEN_WIDTH - textArea->obj.area.width;
3018 navContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3019 layoutNavigationPopulate(navContainer, &footerDesc->navigation, layoutInt->layer);
3020 obj = layoutAddCallbackObj(layoutInt,
3021 (nbgl_obj_t *) navContainer,
3022 footerDesc->textAndNav.navigation.token,
3023 footerDesc->textAndNav.navigation.tuneId);
3024 if (obj == NULL) {
3025 return -1;
3026 }
3027
3028 // create vertical line separating text from nav
3029 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
3030 separationLine->lineColor = LIGHT_GRAY;
3031 separationLine->obj.area.width = 1;
3032 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
3033 separationLine->direction = VERTICAL;
3034 separationLine->thickness = 1;
3035 separationLine->obj.alignment = MID_LEFT;
3036 separationLine->obj.alignTo = (nbgl_obj_t *) navContainer;
3037 separationLine->obj.alignmentMarginX = -1;
3038
3039 layoutInt->activePage = footerDesc->textAndNav.navigation.activePage;
3040 layoutInt->nbPages = footerDesc->textAndNav.navigation.nbPages;
3041 // add to bottom container
3042 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3043 = (nbgl_obj_t *) navContainer;
3044 layoutInt->footerContainer->nbChildren++;
3045 break;
3046 }
3047 case FOOTER_NAV: {
3048 layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
3049 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3051 layoutInt->footerContainer, &footerDesc->navigation, layoutInt->layer);
3052 layoutInt->footerContainer->nbChildren = 4;
3053 obj = layoutAddCallbackObj(layoutInt,
3054 (nbgl_obj_t *) layoutInt->footerContainer,
3055 footerDesc->navigation.token,
3056 footerDesc->navigation.tuneId);
3057 if (obj == NULL) {
3058 return -1;
3059 }
3060
3061 layoutInt->activePage = footerDesc->navigation.activePage;
3062 layoutInt->nbPages = footerDesc->navigation.nbPages;
3063 break;
3064 }
3065 case FOOTER_SIMPLE_BUTTON: {
3066 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3067 obj = layoutAddCallbackObj(layoutInt,
3068 (nbgl_obj_t *) button,
3069 footerDesc->button.token,
3070 footerDesc->button.tuneId);
3071 if (obj == NULL) {
3072 return -1;
3073 }
3074
3075 button->obj.alignment = CENTER;
3076 if (footerDesc->button.style == BLACK_BACKGROUND) {
3077 button->innerColor = BLACK;
3078 button->foregroundColor = WHITE;
3079 }
3080 else {
3081 button->innerColor = WHITE;
3082 button->foregroundColor = BLACK;
3083 }
3084
3085 if (footerDesc->button.style == NO_BORDER) {
3086 button->borderColor = WHITE;
3087 }
3088 else {
3089 if (footerDesc->button.style == BLACK_BACKGROUND) {
3090 button->borderColor = BLACK;
3091 }
3092 else {
3093 button->borderColor = LIGHT_GRAY;
3094 }
3095 }
3096 button->text = PIC(footerDesc->button.text);
3097 button->fontId = SMALL_BOLD_FONT;
3098 button->icon = PIC(footerDesc->button.icon);
3099 button->radius = BUTTON_RADIUS;
3100 button->obj.area.height = BUTTON_DIAMETER;
3101 layoutInt->footerContainer->obj.area.height = FOOTER_BUTTON_HEIGHT;
3102 if (footerDesc->button.text == NULL) {
3103 button->obj.area.width = BUTTON_DIAMETER;
3104 }
3105 else {
3106 button->obj.area.width = AVAILABLE_WIDTH;
3107 }
3108 button->obj.touchMask = (1 << TOUCHED);
3109 button->obj.touchId = button->text ? SINGLE_BUTTON_ID : BOTTOM_BUTTON_ID;
3110 // add to bottom container
3111 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3112 = (nbgl_obj_t *) button;
3113 layoutInt->footerContainer->nbChildren++;
3114 break;
3115 }
3116 case FOOTER_CHOICE_BUTTONS: {
3117 // texts cannot be NULL
3118 if ((footerDesc->choiceButtons.bottomText == NULL)
3119 || (footerDesc->choiceButtons.topText == NULL)) {
3120 return -1;
3121 }
3122
3123 // create bottom button (footer) at first
3124 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3125 obj = layoutAddCallbackObj(layoutInt,
3126 (nbgl_obj_t *) button,
3127 footerDesc->choiceButtons.token,
3128 footerDesc->choiceButtons.tuneId);
3129 if (obj == NULL) {
3130 return -1;
3131 }
3132 // associate with with index 1
3133 obj->index = 1;
3134 // put at the bottom of the container
3135 button->obj.alignment = BOTTOM_MIDDLE;
3136 button->innerColor = WHITE;
3137 if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3138 button->obj.alignmentMarginY
3139 = BOTTOM_BORDER_MARGIN; // 24 pixels from bottom of container
3140 button->borderColor = LIGHT_GRAY;
3141 }
3142 else {
3143 button->obj.alignmentMarginY = 4; // 4 pixels from screen bottom
3144 button->borderColor = WHITE;
3145 }
3146 button->foregroundColor = BLACK;
3147 button->obj.area.width = AVAILABLE_WIDTH;
3148 button->obj.area.height = BUTTON_DIAMETER;
3149 button->radius = BUTTON_RADIUS;
3150 button->text = PIC(footerDesc->choiceButtons.bottomText);
3151 button->fontId = SMALL_BOLD_FONT;
3152 button->obj.touchMask = (1 << TOUCHED);
3153 button->obj.touchId = CHOICE_2_ID;
3154 // add to bottom container
3155 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3156 = (nbgl_obj_t *) button;
3157 layoutInt->footerContainer->nbChildren++;
3158
3159 // add line if needed
3160 if ((footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3161 && (footerDesc->choiceButtons.style != BOTH_ROUNDED_STYLE)) {
3162 line = createHorizontalLine(layoutInt->layer);
3163 line->obj.alignment = TOP_MIDDLE;
3164 line->obj.alignmentMarginY = 4;
3165 line->obj.alignTo = (nbgl_obj_t *) button;
3166 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3167 = (nbgl_obj_t *) line;
3168 layoutInt->footerContainer->nbChildren++;
3169 }
3170
3171 // then top button, on top of it
3172 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3173 obj = layoutAddCallbackObj(layoutInt,
3174 (nbgl_obj_t *) button,
3175 footerDesc->choiceButtons.token,
3176 footerDesc->choiceButtons.tuneId);
3177 if (obj == NULL) {
3178 return -1;
3179 }
3180 // associate with with index 0
3181 obj->index = 0;
3182 button->obj.alignment = TOP_MIDDLE;
3183 button->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN; // 24 pixels from top of container
3185 button->innerColor = WHITE;
3186 button->borderColor = LIGHT_GRAY;
3187 button->foregroundColor = BLACK;
3188 }
3189 else {
3190 button->innerColor = BLACK;
3191 button->borderColor = BLACK;
3192 button->foregroundColor = WHITE;
3193 }
3194 button->obj.area.width = AVAILABLE_WIDTH;
3195 button->obj.area.height = BUTTON_DIAMETER;
3196 button->radius = BUTTON_RADIUS;
3197 button->text = PIC(footerDesc->choiceButtons.topText);
3198 button->icon = (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3199 ? PIC(footerDesc->choiceButtons.topIcon)
3200 : NULL;
3201 button->fontId = SMALL_BOLD_FONT;
3202 button->obj.touchMask = (1 << TOUCHED);
3203 button->obj.touchId = CHOICE_1_ID;
3204 // add to bottom container
3205 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3206 = (nbgl_obj_t *) button;
3207 layoutInt->footerContainer->nbChildren++;
3208
3209 if (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE) {
3210 layoutInt->footerContainer->obj.area.height = ACTION_AND_FOOTER_FOOTER_HEIGHT;
3211 }
3212 else {
3213 layoutInt->footerContainer->obj.area.height = ROUNDED_AND_FOOTER_FOOTER_HEIGHT;
3214 }
3215
3216 break;
3217 }
3218 default:
3219 return -2;
3220 }
3221
3222 // add swipable feature for navigation
3223 if ((footerDesc->type == FOOTER_NAV) || (footerDesc->type == FOOTER_TEXT_AND_NAV)) {
3224 addSwipeInternal(layoutInt,
3225 ((1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT)),
3227 (footerDesc->type == FOOTER_NAV) ? footerDesc->navigation.token
3228 : footerDesc->textAndNav.navigation.token,
3229 (footerDesc->type == FOOTER_NAV)
3230 ? footerDesc->navigation.tuneId
3231 : footerDesc->textAndNav.navigation.tuneId);
3232 }
3233
3234 if (footerDesc->separationLine) {
3235 line = createHorizontalLine(layoutInt->layer);
3236 line->obj.alignment = TOP_MIDDLE;
3237 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3238 = (nbgl_obj_t *) line;
3239 layoutInt->footerContainer->nbChildren++;
3240 }
3241 if (separationLine != NULL) {
3242 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3243 = (nbgl_obj_t *) separationLine;
3244 layoutInt->footerContainer->nbChildren++;
3245 }
3246
3247 layoutInt->children[FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->footerContainer;
3248
3249 // subtract footer height from main container height
3250 layoutInt->container->obj.area.height -= layoutInt->footerContainer->obj.area.height;
3251
3252 layoutInt->footerType = footerDesc->type;
3253
3254 return layoutInt->footerContainer->obj.area.height;
3255}
3256
3266{
3267 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3268 layoutObj_t *obj;
3269 nbgl_text_area_t *textArea;
3270 nbgl_line_t *line;
3271 nbgl_button_t *button;
3272
3273 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddUpFooter():\n");
3274 if (layout == NULL) {
3275 return -1;
3276 }
3277 if ((upFooterDesc == NULL) || (upFooterDesc->type >= NB_UP_FOOTER_TYPES)) {
3278 return -2;
3279 }
3280
3281 layoutInt->upFooterContainer
3283 layoutInt->upFooterContainer->obj.area.width = SCREEN_WIDTH;
3284 layoutInt->upFooterContainer->layout = VERTICAL;
3285 // maximum 4 children for long press button
3286 layoutInt->upFooterContainer->children
3287 = (nbgl_obj_t **) nbgl_containerPoolGet(4, layoutInt->layer);
3288 layoutInt->upFooterContainer->obj.alignTo = (nbgl_obj_t *) layoutInt->container;
3289 layoutInt->upFooterContainer->obj.alignment = BOTTOM_MIDDLE;
3290
3291 switch (upFooterDesc->type) {
3292 case UP_FOOTER_LONG_PRESS: {
3293 nbgl_progress_bar_t *progressBar;
3294
3295 obj = layoutAddCallbackObj(layoutInt,
3296 (nbgl_obj_t *) layoutInt->upFooterContainer,
3297 upFooterDesc->longPress.token,
3298 upFooterDesc->longPress.tuneId);
3299 if (obj == NULL) {
3300 return -1;
3301 }
3302 layoutInt->upFooterContainer->nbChildren = 4;
3303 layoutInt->upFooterContainer->obj.area.height = LONG_PRESS_BUTTON_HEIGHT;
3304 layoutInt->upFooterContainer->obj.touchId = LONG_PRESS_BUTTON_ID;
3305 layoutInt->upFooterContainer->obj.touchMask
3306 = ((1 << TOUCHING) | (1 << TOUCH_RELEASED) | (1 << OUT_OF_TOUCH)
3307 | (1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT));
3308
3309 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3310 button->obj.alignmentMarginX = BORDER_MARGIN;
3311 button->obj.alignment = MID_RIGHT;
3312 button->innerColor = BLACK;
3313 button->foregroundColor = WHITE;
3314 button->borderColor = BLACK;
3315 button->obj.area.width = BUTTON_DIAMETER;
3316 button->obj.area.height = BUTTON_DIAMETER;
3317 button->radius = BUTTON_RADIUS;
3318 button->icon = PIC(&VALIDATE_ICON);
3319 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3320
3321 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3322 textArea->textColor = BLACK;
3323 textArea->text = PIC(upFooterDesc->longPress.text);
3324 textArea->textAlignment = MID_LEFT;
3325 textArea->fontId = LARGE_MEDIUM_FONT;
3326 textArea->wrapping = true;
3327 textArea->obj.area.width = SCREEN_WIDTH - 3 * BORDER_MARGIN - button->obj.area.width;
3328 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3329 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3330 textArea->style = NO_STYLE;
3331 textArea->obj.alignment = MID_LEFT;
3332 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3333 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) textArea;
3334
3335 line = createHorizontalLine(layoutInt->layer);
3336 line->offset = 3;
3337 line->obj.alignment = TOP_MIDDLE;
3338 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) line;
3339
3340 progressBar = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
3341 progressBar->withBorder = false;
3342 progressBar->obj.area.width = SCREEN_WIDTH;
3343 progressBar->obj.area.height = 8;
3344 progressBar->obj.alignment = TOP_MIDDLE;
3345 progressBar->obj.alignmentMarginY = 4;
3346 progressBar->obj.alignTo = NULL;
3347 layoutInt->upFooterContainer->children[3] = (nbgl_obj_t *) progressBar;
3348 break;
3349 }
3350 case UP_FOOTER_BUTTON: {
3351 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3352 obj = layoutAddCallbackObj(layoutInt,
3353 (nbgl_obj_t *) button,
3354 upFooterDesc->button.token,
3355 upFooterDesc->button.tuneId);
3356 if (obj == NULL) {
3357 return -1;
3358 }
3359
3360 layoutInt->upFooterContainer->nbChildren = 1;
3361 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3362 button->obj.alignment = CENTER;
3363
3364 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3365 button->innerColor = BLACK;
3366 button->foregroundColor = WHITE;
3367 }
3368 else {
3369 button->innerColor = WHITE;
3370 button->foregroundColor = BLACK;
3371 }
3372 if (upFooterDesc->button.style == NO_BORDER) {
3373 button->borderColor = WHITE;
3374 }
3375 else {
3376 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3377 button->borderColor = BLACK;
3378 }
3379 else {
3380 button->borderColor = LIGHT_GRAY;
3381 }
3382 }
3383 button->text = PIC(upFooterDesc->button.text);
3384 button->fontId = SMALL_BOLD_FONT;
3385 button->icon = PIC(upFooterDesc->button.icon);
3386 button->obj.area.width = AVAILABLE_WIDTH;
3387 button->obj.area.height = BUTTON_DIAMETER;
3388 button->radius = BUTTON_RADIUS;
3389
3390 button->obj.alignTo = NULL;
3391 button->obj.touchMask = (1 << TOUCHED);
3392 button->obj.touchId = SINGLE_BUTTON_ID;
3393 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3394 break;
3395 }
3397 // icon & text cannot be NULL
3398 if ((upFooterDesc->horizontalButtons.leftIcon == NULL)
3399 || (upFooterDesc->horizontalButtons.rightText == NULL)) {
3400 return -1;
3401 }
3402
3403 layoutInt->upFooterContainer->nbChildren = 2;
3404 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3405
3406 // create left button (in white) at first
3407 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3408 obj = layoutAddCallbackObj(layoutInt,
3409 (nbgl_obj_t *) button,
3410 upFooterDesc->horizontalButtons.leftToken,
3411 upFooterDesc->horizontalButtons.tuneId);
3412 if (obj == NULL) {
3413 return -1;
3414 }
3415 // associate with with index 1
3416 obj->index = 1;
3417 button->obj.alignment = MID_LEFT;
3418 button->obj.alignmentMarginX = BORDER_MARGIN;
3419 button->borderColor = LIGHT_GRAY;
3420 button->innerColor = WHITE;
3421 button->foregroundColor = BLACK;
3422 button->obj.area.width = BUTTON_DIAMETER;
3423 button->obj.area.height = BUTTON_DIAMETER;
3424 button->radius = BUTTON_RADIUS;
3425 button->icon = PIC(upFooterDesc->horizontalButtons.leftIcon);
3426 button->fontId = SMALL_BOLD_FONT;
3427 button->obj.touchMask = (1 << TOUCHED);
3428 button->obj.touchId = CHOICE_2_ID;
3429 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3430
3431 // then black button, on right
3432 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3433 obj = layoutAddCallbackObj(layoutInt,
3434 (nbgl_obj_t *) button,
3435 upFooterDesc->horizontalButtons.rightToken,
3436 upFooterDesc->horizontalButtons.tuneId);
3437 if (obj == NULL) {
3438 return -1;
3439 }
3440 // associate with with index 0
3441 obj->index = 0;
3442 button->obj.alignment = MID_RIGHT;
3443 button->obj.alignmentMarginX = BORDER_MARGIN;
3444 button->innerColor = BLACK;
3445 button->borderColor = BLACK;
3446 button->foregroundColor = WHITE;
3447 button->obj.area.width = AVAILABLE_WIDTH - BUTTON_DIAMETER - 16;
3448 button->obj.area.height = BUTTON_DIAMETER;
3449 button->radius = BUTTON_RADIUS;
3450 button->text = PIC(upFooterDesc->horizontalButtons.rightText);
3451 button->fontId = SMALL_BOLD_FONT;
3452 button->obj.touchMask = (1 << TOUCHED);
3453 button->obj.touchId = CHOICE_1_ID;
3454 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) button;
3455 break;
3456 }
3457 case UP_FOOTER_TIP_BOX: {
3458 // text cannot be NULL
3459 if (upFooterDesc->tipBox.text == NULL) {
3460 return -1;
3461 }
3462 obj = layoutAddCallbackObj(layoutInt,
3463 (nbgl_obj_t *) layoutInt->upFooterContainer,
3464 upFooterDesc->tipBox.token,
3465 upFooterDesc->tipBox.tuneId);
3466 if (obj == NULL) {
3467 return -1;
3468 }
3469 layoutInt->upFooterContainer->nbChildren = 3;
3470 layoutInt->upFooterContainer->obj.touchId = TIP_BOX_ID;
3471 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3472
3473 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3474 textArea->textColor = BLACK;
3475 textArea->text = PIC(upFooterDesc->tipBox.text);
3476 textArea->textAlignment = MID_LEFT;
3477 textArea->fontId = SMALL_REGULAR_FONT;
3478 textArea->wrapping = true;
3479 textArea->obj.area.width = AVAILABLE_WIDTH;
3480 if (upFooterDesc->tipBox.icon != NULL) {
3481 textArea->obj.area.width
3482 -= ((nbgl_icon_details_t *) PIC(upFooterDesc->tipBox.icon))->width
3483 + BORDER_MARGIN;
3484 }
3485 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3486 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3487 textArea->obj.alignment = MID_LEFT;
3488 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3489 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3490 layoutInt->upFooterContainer->obj.area.height = textArea->obj.area.height;
3491
3492 line = createHorizontalLine(layoutInt->layer);
3493 line->offset = 3;
3494 line->obj.alignment = TOP_MIDDLE;
3495 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) line;
3496
3497 if (upFooterDesc->tipBox.icon != NULL) {
3498 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
3499 image->obj.alignmentMarginX = BORDER_MARGIN;
3500 image->obj.alignment = MID_RIGHT;
3501 image->foregroundColor = BLACK;
3502 image->buffer = PIC(upFooterDesc->tipBox.icon);
3503 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) image;
3504 if (layoutInt->upFooterContainer->obj.area.height < image->buffer->height) {
3505 layoutInt->upFooterContainer->obj.area.height = image->buffer->height;
3506 }
3507 }
3508 layoutInt->upFooterContainer->obj.area.height += 2 * BOTTOM_BORDER_MARGIN;
3509
3510 break;
3511 }
3512 case UP_FOOTER_TEXT: {
3513 obj = layoutAddCallbackObj(layoutInt,
3514 (nbgl_obj_t *) layoutInt->upFooterContainer,
3515 upFooterDesc->text.token,
3516 upFooterDesc->text.tuneId);
3517 if (obj == NULL) {
3518 return -1;
3519 }
3520 layoutInt->upFooterContainer->nbChildren = 1;
3521 layoutInt->upFooterContainer->obj.area.height = SMALL_FOOTER_HEIGHT;
3522 layoutInt->upFooterContainer->obj.touchId = WHOLE_SCREEN_ID;
3523 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3524
3525 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3526 textArea->textColor = DARK_GRAY;
3527 textArea->text = PIC(upFooterDesc->text.text);
3528 textArea->textAlignment = CENTER;
3529 textArea->fontId = SMALL_REGULAR_FONT;
3530 textArea->wrapping = true;
3531 textArea->obj.area.width = AVAILABLE_WIDTH;
3532 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3533 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3534 textArea->obj.alignment = CENTER;
3535 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3536 break;
3537 }
3538 default:
3539 return -2;
3540 }
3541
3542 // subtract up footer height from main container height
3543 layoutInt->container->obj.area.height -= layoutInt->upFooterContainer->obj.area.height;
3544
3545 layoutInt->children[UP_FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->upFooterContainer;
3546
3547 layoutInt->upFooterType = upFooterDesc->type;
3548
3549 return layoutInt->upFooterContainer->obj.area.height;
3550}
3551
3566 uint8_t activePage,
3567 uint8_t nbPages,
3568 bool withBack,
3569 uint8_t backToken,
3570 tune_index_e tuneId)
3571{
3573 .separationLine = false,
3574 .progressAndBack.activePage = activePage,
3575 .progressAndBack.nbPages = nbPages,
3576 .progressAndBack.token = backToken,
3577 .progressAndBack.tuneId = tuneId,
3578 .progressAndBack.withBack = withBack,
3579 .progressAndBack.actionIcon = NULL,
3580 .progressAndBack.actionToken = NBGL_INVALID_TOKEN};
3581 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressIndicator():\n");
3582
3583 return nbgl_layoutAddHeader(layout, &headerDesc);
3584}
3585
3597 const char *text,
3598 const char *subText,
3599 uint8_t initPosition)
3600{
3601 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3602 nbgl_container_t *container;
3603 nbgl_text_area_t *textArea;
3604 nbgl_spinner_t *spinner;
3605
3606 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSpinner():\n");
3607 if (layout == NULL) {
3608 return -1;
3609 }
3610
3611 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
3612 // spinner + text + subText
3613 container->nbChildren = 3;
3614 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
3615
3616 container->obj.area.width = AVAILABLE_WIDTH;
3617 container->layout = VERTICAL;
3618 container->obj.alignment = CENTER;
3619
3620 // create spinner
3621 spinner = (nbgl_spinner_t *) nbgl_objPoolGet(SPINNER, layoutInt->layer);
3622 spinner->position = initPosition;
3623 spinner->obj.alignment = TOP_MIDDLE;
3624 // set this new spinner as child of the container
3625 container->children[0] = (nbgl_obj_t *) spinner;
3626
3627 // update container height
3628 container->obj.area.height += SPINNER_HEIGHT;
3629
3630 // create text area
3631 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3632 textArea->textColor = BLACK;
3633 textArea->text = PIC(text);
3634 textArea->textAlignment = CENTER;
3635 textArea->fontId = (subText != NULL) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
3636 textArea->wrapping = true;
3637 textArea->obj.alignmentMarginY = SPINNER_TEXT_MARGIN;
3638 textArea->obj.alignTo = (nbgl_obj_t *) spinner;
3639 textArea->obj.alignment = BOTTOM_MIDDLE;
3640 textArea->obj.area.width = AVAILABLE_WIDTH;
3641 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3642 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3643 textArea->style = NO_STYLE;
3644
3645 // update container height
3646 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
3647
3648 // set this text as child of the container
3649 container->children[1] = (nbgl_obj_t *) textArea;
3650
3651 if (subText != NULL) {
3652 nbgl_text_area_t *subTextArea;
3653 // create sub-text area
3654 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3655 subTextArea->textColor = BLACK;
3656 subTextArea->text = PIC(subText);
3657 subTextArea->textAlignment = CENTER;
3658 subTextArea->fontId = SMALL_REGULAR_FONT;
3659 subTextArea->wrapping = true;
3660 subTextArea->obj.alignmentMarginY = SPINNER_INTER_TEXTS_MARGIN;
3661 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
3662 subTextArea->obj.alignment = BOTTOM_MIDDLE;
3663 subTextArea->obj.area.width = AVAILABLE_WIDTH;
3664 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
3665 subTextArea->text,
3666 subTextArea->obj.area.width,
3667 subTextArea->wrapping);
3668 subTextArea->style = NO_STYLE;
3669
3670 // update container height
3671 container->obj.area.height
3672 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
3673
3674 // set thissub-text as child of the container
3675 container->children[2] = (nbgl_obj_t *) subTextArea;
3676 }
3677 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
3678
3679 if (initPosition != SPINNER_FIXED) {
3680 // update ticker to update the spinner periodically
3682
3683 tickerCfg.tickerIntervale = SPINNER_REFRESH_PERIOD; // ms
3684 tickerCfg.tickerValue = SPINNER_REFRESH_PERIOD; // ms
3685 tickerCfg.tickerCallback = &spinnerTickerCallback;
3686 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
3687 }
3688
3689 return 0;
3690}
3691
3704 const char *text,
3705 const char *subText,
3706 uint8_t position)
3707{
3708 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3709 nbgl_container_t *container;
3710 nbgl_text_area_t *textArea;
3711 nbgl_spinner_t *spinner;
3712 int ret = 0;
3713
3714 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutUpdateSpinner():\n");
3715 if ((layout == NULL) || (layoutInt->container->nbChildren == 0)) {
3716 return -1;
3717 }
3718
3719 container = (nbgl_container_t *) layoutInt->container->children[0];
3720 if ((container->obj.type != CONTAINER) || (container->nbChildren < 2)) {
3721 return -1;
3722 }
3723
3724 spinner = (nbgl_spinner_t *) container->children[0];
3725 if (spinner->obj.type != SPINNER) {
3726 return -1;
3727 }
3728 // if position is different, redraw
3729 if (spinner->position != position) {
3730 spinner->position = position;
3731 nbgl_objDraw((nbgl_obj_t *) spinner);
3732 ret = 1;
3733 }
3734
3735 // update text area if necessary
3736 textArea = (nbgl_text_area_t *) container->children[1];
3737 if (textArea->obj.type != TEXT_AREA) {
3738 return -1;
3739 }
3740 const char *newText = PIC(text);
3741 size_t newTextLen = strlen(newText);
3742 // if text is different, redraw (don't use strcmp because it crashes with Rust SDK)
3743 if ((newTextLen != strlen(textArea->text)) || memcmp(textArea->text, newText, newTextLen)) {
3744 textArea->text = newText;
3745 nbgl_objDraw((nbgl_obj_t *) textArea);
3746 ret = 2;
3747 }
3748
3749 if (subText != NULL) {
3750 nbgl_text_area_t *subTextArea;
3751
3752 if (container->nbChildren != 3) {
3753 return -1;
3754 }
3755 subTextArea = (nbgl_text_area_t *) container->children[2];
3756 if (subTextArea->obj.type != TEXT_AREA) {
3757 return -1;
3758 }
3759 const char *newSubText = PIC(subText);
3760 size_t newSubTextLen = strlen(newSubText);
3761 // if text is different, redraw
3762 if ((newSubTextLen != strlen(subTextArea->text))
3763 || memcmp(subTextArea->text, newSubText, newSubTextLen)) {
3764 subTextArea->text = newSubText;
3765 nbgl_objDraw((nbgl_obj_t *) subTextArea);
3766 ret = 2;
3767 }
3768 }
3769
3770 return ret;
3771}
3772
3780{
3781 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3782
3783 if (layout == NULL) {
3784 return -1;
3785 }
3787 "nbgl_layoutDraw(): container.nbChildren =%d, layout->nbChildren = %d\n",
3788 layout->container->nbChildren,
3789 layout->nbChildren);
3790 if (layout->tapText) {
3791 // set this new container as child of main container
3792 layoutAddObject(layout, (nbgl_obj_t *) layout->tapText);
3793 }
3794 if (layout->withLeftBorder == true) {
3795 // draw now the line
3796 nbgl_line_t *line = createLeftVerticalLine(layout->layer);
3797 layout->children[LEFT_BORDER_INDEX] = (nbgl_obj_t *) line;
3798 }
3800
3801 return 0;
3802}
3803
3811{
3812 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3813 LOG_DEBUG(PAGE_LOGGER, "nbgl_layoutRelease(): \n");
3814 if (layout == NULL) {
3815 return -1;
3816 }
3817 // if modal
3818 if (layout->modal) {
3819 nbgl_screenPop(layout->layer);
3820 }
3821 layout->nbChildren = 0;
3822 return 0;
3823}
3824
3825#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 PROGRESSBAR_HEIGHT
Definition nbgl_layout.c:96
#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_layoutUpdateProgressBar(nbgl_layout_t *layout, const char *text, const char *subText, uint8_t percentage)
Update an existing progress Bar (must be the only object of the layout)
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:99
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,...
#define PROGRESSBAR_WIDTH
Definition nbgl_layout.c:95
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:86
@ 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
Definition nbgl_layout.h:98
@ 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:1611
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:1660
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:1676
@ 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