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