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