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
16#ifdef HAVE_BOLOS
17#include "localization.h"
18#endif
19#include "nbgl_debug.h"
20#include "nbgl_front.h"
22#include "nbgl_obj.h"
23#include "nbgl_draw.h"
24#include "nbgl_screen.h"
25#include "nbgl_touch.h"
26#include "glyphs.h"
27#include "os_io_seph_ux.h"
28#include "os_pic.h"
29#include "os_helpers.h"
30#include "lcx_rng.h"
31
32/*********************
33 * DEFINES
34 *********************/
35// half internal margin, between items
36#define INTERNAL_SPACE 16
37// inner margin, between buttons
38#define INNER_MARGIN 12
39
40#define NB_MAX_LAYOUTS 3
41
42// used by container
43#define NB_MAX_CONTAINER_CHILDREN 20
44
45#define TAG_VALUE_ICON_WIDTH 32
46
47#if defined(TARGET_STAX)
48#define RADIO_CHOICE_HEIGHT 96
49#define BAR_INTERVALE 12
50#define FOOTER_BUTTON_HEIGHT 128
51#define FOOTER_IN_PAIR_HEIGHT 80
52#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 192
53#define FOOTER_TEXT_AND_NAV_WIDTH 160
54#define TAP_TO_CONTINUE_MARGIN 24
55#define SUB_HEADER_MARGIN 24
56#define PRE_FIRST_TEXT_MARGIN 24
57#define INTER_PARAGRAPHS_MARGIN 40
58#define PRE_TITLE_MARGIN 24
59#define PRE_DESCRIPTION_MARGIN 16
60#define PRE_FIRST_ROW_MARGIN 32
61#define INTER_ROWS_MARGIN 16
62#define QR_PRE_TEXT_MARGIN 24
63#define QR_INTER_TEXTS_MARGIN 40
64#define SPINNER_TEXT_MARGIN 20
65#define SPINNER_INTER_TEXTS_MARGIN 20
66#define BAR_TEXT_MARGIN 24
67#define BAR_INTER_TEXTS_MARGIN 16
68#define LEFT_CONTENT_TEXT_PADDING 0
69#define BUTTON_FROM_BOTTOM_MARGIN 4
70#define TOP_BUTTON_MARGIN VERTICAL_BORDER_MARGIN
71#define TOP_BUTTON_MARGIN_WITH_ACTION VERTICAL_BORDER_MARGIN
72#define SINGLE_BUTTON_MARGIN 24
73#define LONG_PRESS_PROGRESS_HEIGHT 8
74#define LONG_PRESS_PROGRESS_ALIGN 1
75#define LEFT_CONTENT_ICON_TEXT_X 16
76#define TIP_BOX_MARGIN_Y 24
77#define TIP_BOX_TEXT_ICON_MARGIN 24
78#elif defined(TARGET_FLEX)
79#define RADIO_CHOICE_HEIGHT 92
80#define BAR_INTERVALE 16
81#define FOOTER_BUTTON_HEIGHT 136
82#define FOOTER_IN_PAIR_HEIGHT 88
83#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 208
84#define FOOTER_TEXT_AND_NAV_WIDTH 192
85#define TAP_TO_CONTINUE_MARGIN 30
86#define SUB_HEADER_MARGIN 28
87#define PRE_FIRST_TEXT_MARGIN 0
88#define INTER_PARAGRAPHS_MARGIN 24
89#define PRE_TITLE_MARGIN 16
90#define PRE_DESCRIPTION_MARGIN 24
91#define PRE_FIRST_ROW_MARGIN 32
92#define INTER_ROWS_MARGIN 24
93#define QR_PRE_TEXT_MARGIN 24
94#define QR_INTER_TEXTS_MARGIN 28
95#define SPINNER_TEXT_MARGIN 24
96#define SPINNER_INTER_TEXTS_MARGIN 16
97#define BAR_TEXT_MARGIN 24
98#define BAR_INTER_TEXTS_MARGIN 16
99#define LEFT_CONTENT_TEXT_PADDING 4
100#define BUTTON_FROM_BOTTOM_MARGIN 4
101#define TOP_BUTTON_MARGIN VERTICAL_BORDER_MARGIN
102#define TOP_BUTTON_MARGIN_WITH_ACTION VERTICAL_BORDER_MARGIN
103#define SINGLE_BUTTON_MARGIN 24
104#define LONG_PRESS_PROGRESS_HEIGHT 8
105#define LONG_PRESS_PROGRESS_ALIGN 1
106#define LEFT_CONTENT_ICON_TEXT_X 16
107#define TIP_BOX_MARGIN_Y 24
108#define TIP_BOX_TEXT_ICON_MARGIN 32
109#elif defined(TARGET_APEX)
110#define RADIO_CHOICE_HEIGHT 68
111#define BAR_INTERVALE 8
112#define FOOTER_BUTTON_HEIGHT 88
113#define FOOTER_IN_PAIR_HEIGHT 60
114#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 128
115#define FOOTER_TEXT_AND_NAV_WIDTH 120
116#define TAP_TO_CONTINUE_MARGIN 30
117#define SUB_HEADER_MARGIN 16
118#define PRE_FIRST_TEXT_MARGIN 0
119#define INTER_PARAGRAPHS_MARGIN 16
120#define PRE_TITLE_MARGIN 16
121#define PRE_DESCRIPTION_MARGIN 12
122#define PRE_FIRST_ROW_MARGIN 24
123#define INTER_ROWS_MARGIN 12
124#define QR_PRE_TEXT_MARGIN 16
125#define QR_INTER_TEXTS_MARGIN 20
126#define SPINNER_TEXT_MARGIN 16
127#define SPINNER_INTER_TEXTS_MARGIN 16
128#define BAR_TEXT_MARGIN 16
129#define BAR_INTER_TEXTS_MARGIN 12
130#define LEFT_CONTENT_TEXT_PADDING 4
131#define BUTTON_FROM_BOTTOM_MARGIN 0
132#define TOP_BUTTON_MARGIN 12
133#define TOP_BUTTON_MARGIN_WITH_ACTION 0
134#define SINGLE_BUTTON_MARGIN 16
135#define LONG_PRESS_PROGRESS_HEIGHT 4
136#define LONG_PRESS_PROGRESS_ALIGN 0
137#define LEFT_CONTENT_ICON_TEXT_X 8
138#define TIP_BOX_MARGIN_Y 12
139#define TIP_BOX_TEXT_ICON_MARGIN 20
140#else // TARGETS
141#error Undefined target
142#endif // TARGETS
143
144// refresh period of the spinner, in ms
145#define SPINNER_REFRESH_PERIOD 400
146
147/**********************
148 * MACROS
149 **********************/
150
151/**********************
152 * TYPEDEFS
153 **********************/
154
161
162// used to build either a touchable bar or a switch
163typedef struct {
165 const nbgl_icon_details_t *iconLeft; // a buffer containing the 1BPP icon for icon on
166 // left (can be NULL)
167 const nbgl_icon_details_t *iconRight; // a buffer containing the 1BPP icon for icon 2 (can be
168 // NULL). Dimensions must be the same as iconLeft
169 const char *text; // text (can be NULL)
170 const char *subText; // sub text (can be NULL)
171 uint8_t token; // the token that will be used as argument of the callback
172 nbgl_state_t state; // state of the item
173 bool large; // set to true only for the main level of OS settings
174 uint8_t index; // index of obj (for callback)
175 tune_index_e tuneId; // if not @ref NBGL_NO_TUNE, a tune will be played
176} listItem_t;
177
178/**********************
179 * VARIABLES
180 **********************/
181
186static nbgl_layoutInternal_t gLayout[NB_MAX_LAYOUTS] = {0};
187
188// current top of stack
189static nbgl_layoutInternal_t *topLayout;
190
191// numbers of touchable controls for the whole page
192static uint8_t nbTouchableControls = 0;
193
194/**********************
195 * STATIC PROTOTYPES
196 **********************/
197// extern const char *get_ux_loc_string(UX_LOC_STRINGS_INDEX index);
198
199// Hold-to-approve parameters
200#if defined(TARGET_FLEX)
201// Percent of a single "hold-to-approve" bar
202#define HOLD_TO_APPROVE_STEP_PERCENT (7)
203// Duration between two bars
204#define HOLD_TO_APPROVE_STEP_DURATION_MS (100)
205// Number of bars displayed when the user start the "hold-to-approve" mechanism
206#define HOLD_TO_APPROVE_FIRST_STEP (0)
207#elif defined(TARGET_STAX)
208#define HOLD_TO_APPROVE_STEP_PERCENT (25)
209#define HOLD_TO_APPROVE_STEP_DURATION_MS (400)
210#define HOLD_TO_APPROVE_FIRST_STEP (1)
211#elif defined(TARGET_APEX)
212#define HOLD_TO_APPROVE_STEP_PERCENT (20)
213#define HOLD_TO_APPROVE_STEP_DURATION_MS (300)
214#define HOLD_TO_APPROVE_FIRST_STEP (1)
215#endif // TARGETS
216
217static inline uint8_t get_hold_to_approve_percent(uint32_t touch_duration)
218{
219 uint8_t current_step_nb
220 = (touch_duration / HOLD_TO_APPROVE_STEP_DURATION_MS) + HOLD_TO_APPROVE_FIRST_STEP;
221 return (current_step_nb * HOLD_TO_APPROVE_STEP_PERCENT);
222}
223
224// function used to retrieve the concerned layout and layout obj matching the given touched obj
225static bool getLayoutAndLayoutObj(nbgl_obj_t *obj,
226 nbgl_layoutInternal_t **layout,
227 layoutObj_t **layoutObj)
228{
229 // only try to find the object on top layout on the stack (the other cannot receive touch
230 // events)
231 *layout = NULL;
232 if ((topLayout) && (topLayout->isUsed)) {
233 uint8_t j;
234
235 // search index of obj in this layout
236 for (j = 0; j < topLayout->nbUsedCallbackObjs; j++) {
237 if (obj == topLayout->callbackObjPool[j].obj) {
239 "getLayoutAndLayoutObj(): obj found in layout %p "
240 "nbUsedCallbackObjs = %d\n",
241 topLayout,
242 topLayout->nbUsedCallbackObjs);
243 *layout = topLayout;
244 *layoutObj = &(topLayout->callbackObjPool[j]);
245 return true;
246 }
247 }
248 }
249 // not found
250 return false;
251}
252
253static void radioTouchCallback(nbgl_obj_t *obj,
254 nbgl_touchType_t eventType,
255 nbgl_layoutInternal_t *layout);
256static void longTouchCallback(nbgl_obj_t *obj,
257 nbgl_touchType_t eventType,
258 nbgl_layoutInternal_t *layout,
259 layoutObj_t *layoutObj);
260
261// callback for most touched object
262static void touchCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType)
263{
264 nbgl_layoutInternal_t *layout;
265 layoutObj_t *layoutObj;
266 bool needRefresh = false;
267
268 if (obj == NULL) {
269 return;
270 }
271 LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): eventType = %d, obj = %p\n", eventType, obj);
272 if (getLayoutAndLayoutObj(obj, &layout, &layoutObj) == false) {
273 // try with parent, if existing
274 if (getLayoutAndLayoutObj(obj->parent, &layout, &layoutObj) == false) {
275 LOG_WARN(
277 "touchCallback(): eventType = %d, obj = %p, no active layout or obj not found\n",
278 eventType,
279 obj);
280 return;
281 }
282 }
283
284 // case of swipe
285 if (((eventType == SWIPED_UP) || (eventType == SWIPED_DOWN) || (eventType == SWIPED_LEFT)
286 || (eventType == SWIPED_RIGHT))
287 && (obj->type == CONTAINER)) {
288#ifdef NBGL_KEYBOARD
289 if (layout->swipeUsage == SWIPE_USAGE_SUGGESTIONS) {
290 keyboardSwipeCallback(obj, eventType);
291 return;
292 }
293#endif // NBGL_KEYBOARD
294 if (layout->swipeUsage == SWIPE_USAGE_CUSTOM) {
295 layoutObj->index = eventType;
296 }
297 else if ((layout->swipeUsage == SWIPE_USAGE_NAVIGATION)
298 && ((nbgl_obj_t *) layout->container == obj)) {
299 nbgl_container_t *navContainer;
300 if (layout->footerType == FOOTER_NAV) {
301 navContainer = (nbgl_container_t *) layout->footerContainer;
302 }
303 else if (layout->footerType == FOOTER_TEXT_AND_NAV) {
304 navContainer = (nbgl_container_t *) layout->footerContainer->children[1];
305 }
306 else {
307 return;
308 }
309
311 (nbgl_obj_t *) navContainer, eventType, layout->nbPages, &layout->activePage)
312 == false) {
313 // navigation was impossible
314 return;
315 }
316 layoutObj->index = layout->activePage;
317 }
318 }
319
320 // case of navigation bar
321 if (((obj->parent == (nbgl_obj_t *) layout->footerContainer)
322 && (layout->footerType == FOOTER_NAV))
323 || ((obj->parent->type == CONTAINER)
324 && (obj->parent->parent == (nbgl_obj_t *) layout->footerContainer)
325 && (layout->footerType == FOOTER_TEXT_AND_NAV))) {
326 if (layoutNavigationCallback(obj, eventType, layout->nbPages, &layout->activePage)
327 == false) {
328 // navigation was impossible
329 return;
330 }
331 layoutObj->index = layout->activePage;
332 }
333
334 // case of switch
335 if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren >= 2)
336 && (((nbgl_container_t *) obj)->children[1] != NULL)
337 && (((nbgl_container_t *) obj)->children[1]->type == SWITCH)) {
338 nbgl_switch_t *lSwitch = (nbgl_switch_t *) ((nbgl_container_t *) obj)->children[1];
339 lSwitch->state = (lSwitch->state == ON_STATE) ? OFF_STATE : ON_STATE;
340 nbgl_objDraw((nbgl_obj_t *) lSwitch);
341 // refresh will be done after tune playback
342 needRefresh = true;
343 // index is used for state
344 layoutObj->index = lSwitch->state;
345 }
346 // case of radio
347 else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 2)
348 && (((nbgl_container_t *) obj)->children[1] != NULL)
349 && (((nbgl_container_t *) obj)->children[1]->type == RADIO_BUTTON)) {
350 radioTouchCallback(obj, eventType, layout);
351 return;
352 }
353 // case of long press
354 else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 4)
355 && (((nbgl_container_t *) obj)->children[3] != NULL)
356 && (((nbgl_container_t *) obj)->children[3]->type == PROGRESS_BAR)) {
357 longTouchCallback(obj, eventType, layout, layoutObj);
358 return;
359 }
360 LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): layout->callback = %p\n", layout->callback);
361 if ((layout->callback != NULL) && (layoutObj->token != NBGL_INVALID_TOKEN)) {
362#ifdef HAVE_PIEZO_SOUND
363 if (layoutObj->tuneId < NBGL_NO_TUNE) {
364 os_io_seph_cmd_piezo_play_tune(layoutObj->tuneId);
365 }
366#endif // HAVE_PIEZO_SOUND
367 if (needRefresh) {
369 }
370 layout->callback(layoutObj->token, layoutObj->index);
371 }
372}
373
374// callback for long press button
375static void longTouchCallback(nbgl_obj_t *obj,
376 nbgl_touchType_t eventType,
377 nbgl_layoutInternal_t *layout,
378 layoutObj_t *layoutObj)
379{
380 nbgl_container_t *container = (nbgl_container_t *) obj;
381 // 4th child of container is the progress bar
382 nbgl_progress_bar_t *progressBar = (nbgl_progress_bar_t *) container->children[3];
383
384 LOG_DEBUG(LAYOUT_LOGGER, "longTouchCallback(): eventType = %d, obj = %p\n", eventType, obj);
385
386 // case of pressing a long press button
387 if (eventType == TOUCHING) {
388 uint32_t touchDuration = nbgl_touchGetTouchDuration(obj);
389
390 // Compute the new progress bar state in %
391 uint8_t new_state = get_hold_to_approve_percent(touchDuration);
392
393 // Ensure the callback is triggered once,
394 // when the progress bar state reaches 100%
395 bool trigger_callback = (new_state >= 100) && (progressBar->state < 100);
396
397 // Cap progress bar state at 100%
398 if (new_state >= 100) {
399 new_state = 100;
400 }
401
402 // Update progress bar state
403 if (new_state != progressBar->state) {
404 progressBar->partialRedraw = true;
405 progressBar->state = new_state;
406
407 nbgl_objDraw((nbgl_obj_t *) progressBar);
408 // Ensure progress bar is fully drawn
409 // before calling the callback.
412 }
413
414 if (trigger_callback) {
415 // End of progress bar reached: trigger callback
416 if (layout->callback != NULL) {
417 layout->callback(layoutObj->token, layoutObj->index);
418 }
419 }
420 }
421 // case of releasing a long press button (or getting out of it)
422 else if ((eventType == TOUCH_RELEASED) || (eventType == OUT_OF_TOUCH)
423 || (eventType == SWIPED_LEFT) || (eventType == SWIPED_RIGHT)) {
425 progressBar->partialRedraw = true;
426 progressBar->state = 0;
427 nbgl_objDraw((nbgl_obj_t *) progressBar);
428 // It's necessary to redraw the grey/dotted line, that has been partially wiped
429 nbgl_line_t *line = (nbgl_line_t *) container->children[2];
430 nbgl_objDraw((nbgl_obj_t *) line);
432 }
433}
434
435// callback for radio button touch
436static void radioTouchCallback(nbgl_obj_t *obj,
437 nbgl_touchType_t eventType,
438 nbgl_layoutInternal_t *layout)
439{
440 uint8_t i = NB_MAX_LAYOUTS, radioIndex = 0, foundRadio = 0xFF, foundRadioIndex;
441
442 if (eventType != TOUCHED) {
443 return;
444 }
445
446 i = 0;
447 // parse all objs to find all containers of radio buttons
448 while (i < layout->nbUsedCallbackObjs) {
449 if ((obj == (nbgl_obj_t *) layout->callbackObjPool[i].obj)
450 && (layout->callbackObjPool[i].obj->type == CONTAINER)) {
451 nbgl_radio_t *radio
452 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
453 ->children[1];
454 nbgl_text_area_t *textArea
456 ->children[0];
457 foundRadio = i;
458 foundRadioIndex = radioIndex;
459 // set text as active (black and bold)
460 textArea->textColor = BLACK;
461 textArea->fontId = SMALL_BOLD_FONT;
462 // ensure that radio button is ON
463 radio->state = ON_STATE;
464 // redraw container
465 nbgl_objDraw((nbgl_obj_t *) obj);
466 }
467 else if ((layout->callbackObjPool[i].obj->type == CONTAINER)
468 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->nbChildren == 2)
469 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->children[1]->type
470 == RADIO_BUTTON)) {
471 nbgl_radio_t *radio
472 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
473 ->children[1];
474 nbgl_text_area_t *textArea
476 ->children[0];
477 radioIndex++;
478 // set to OFF the one that was in ON
479 if (radio->state == ON_STATE) {
480 radio->state = OFF_STATE;
481 // set text it as inactive (gray and normal)
482 textArea->textColor = LIGHT_TEXT_COLOR;
483 textArea->fontId = SMALL_REGULAR_FONT;
484 // redraw container
486 }
487 }
488 i++;
489 }
490 // call callback after redraw to avoid asynchronicity
491 if (foundRadio != 0xFF) {
492 if (layout->callback != NULL) {
493#ifdef HAVE_PIEZO_SOUND
494 if (layout->callbackObjPool[foundRadio].tuneId < NBGL_NO_TUNE) {
495 os_io_seph_cmd_piezo_play_tune(layout->callbackObjPool[foundRadio].tuneId);
496 }
497#endif // HAVE_PIEZO_SOUND
499 layout->callback(layout->callbackObjPool[foundRadio].token, foundRadioIndex);
500 }
501 }
502}
503
504// callback for spinner ticker
505static void spinnerTickerCallback(void)
506{
507 nbgl_spinner_t *spinner;
508 uint8_t i = 0;
509
510 if (!topLayout || !topLayout->isUsed) {
511 return;
512 }
513
514 // get index of obj
515 while (i < topLayout->container->nbChildren) {
516 if (topLayout->container->children[i]->type == CONTAINER) {
517 nbgl_container_t *container = (nbgl_container_t *) topLayout->container->children[i];
518 if (container->nbChildren && (container->children[0]->type == SPINNER)) {
519 spinner = (nbgl_spinner_t *) container->children[0];
520 spinner->position++;
521 // there are only NB_SPINNER_POSITIONS positions
522 if (spinner->position == NB_SPINNER_POSITIONS) {
523 spinner->position = 0;
524 }
525 nbgl_objDraw((nbgl_obj_t *) spinner);
527 return;
528 }
529 }
530 i++;
531 }
532}
533
534// callback for animation ticker
535static void animTickerCallback(void)
536{
537 nbgl_image_t *image;
538 uint8_t i = 0;
539 nbgl_layoutInternal_t *layout = topLayout;
540
541 if (!layout || !layout->isUsed || (layout->animation == NULL)) {
542 return;
543 }
544
545 // get index of image obj
546 while (i < layout->container->nbChildren) {
547 if (layout->container->children[i]->type == CONTAINER) {
548 nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
549 if (container->children[1]->type == IMAGE) {
550 image = (nbgl_image_t *) container->children[1];
551 if (layout->animation->parsing == LOOP_PARSING) {
552 if (layout->iconIdxInAnim == (layout->animation->nbIcons - 1)) {
553 layout->iconIdxInAnim = 0;
554 }
555 else {
556 layout->iconIdxInAnim++;
557 }
558 }
559 else {
560 // Flip incrementAnim when reaching upper or lower limit
561 if ((layout->incrementAnim)
562 && (layout->iconIdxInAnim >= layout->animation->nbIcons - 1)) {
563 layout->incrementAnim = false;
564 }
565 else if (layout->iconIdxInAnim == 0) {
566 layout->incrementAnim = true;
567 }
568
569 // Increase / Decrease index according to incrementAnim
570 if (layout->incrementAnim) {
571 layout->iconIdxInAnim++;
572 }
573 else {
574 layout->iconIdxInAnim--;
575 }
576 }
577 image->buffer = layout->animation->icons[layout->iconIdxInAnim];
578 nbgl_objDraw((nbgl_obj_t *) image);
581 return;
582 }
583 }
584 i++;
585 }
586}
587
588static nbgl_line_t *createHorizontalLine(uint8_t layer)
589{
590 nbgl_line_t *line;
591
592 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
593 line->lineColor = LIGHT_GRAY;
594 line->obj.area.width = SCREEN_WIDTH;
595 line->obj.area.height = 1;
596 line->direction = HORIZONTAL;
597 line->thickness = 1;
598 return line;
599}
600
601#ifdef TARGET_STAX
602static nbgl_line_t *createLeftVerticalLine(uint8_t layer)
603{
604 nbgl_line_t *line;
605
606 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
607 line->lineColor = LIGHT_GRAY;
608 line->obj.area.width = 1;
609 line->obj.area.height = SCREEN_HEIGHT;
610 line->direction = VERTICAL;
611 line->thickness = 1;
612 line->obj.alignment = MID_LEFT;
613 return line;
614}
615#endif // TARGET_STAX
616
617// function adding a layout object in the callbackObjPool array for the given layout, and
618// configuring it
620 nbgl_obj_t *obj,
621 uint8_t token,
622 tune_index_e tuneId)
623{
624 layoutObj_t *layoutObj = NULL;
625
626 if (layout->nbUsedCallbackObjs < (LAYOUT_OBJ_POOL_LEN - 1)) {
627 layoutObj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
628 layout->nbUsedCallbackObjs++;
629 layoutObj->obj = obj;
630 layoutObj->token = token;
631 layoutObj->tuneId = tuneId;
632 }
633 else {
634 LOG_FATAL(LAYOUT_LOGGER, "layoutAddCallbackObj: no more callback obj\n");
635 }
636
637 return layoutObj;
638}
639
647{
648 if (layout->container->nbChildren == NB_MAX_CONTAINER_CHILDREN) {
649 LOG_FATAL(LAYOUT_LOGGER, "layoutAddObject(): No more object\n");
650 }
651 layout->container->children[layout->container->nbChildren] = obj;
652 layout->container->nbChildren++;
653}
654
664static int addSwipeInternal(nbgl_layoutInternal_t *layoutInt,
665 uint16_t swipesMask,
666 nbgl_swipe_usage_t usage,
667 uint8_t token,
668 tune_index_e tuneId)
669{
670 layoutObj_t *obj;
671
672 if ((swipesMask & SWIPE_MASK) == 0) {
673 return -1;
674 }
675
676 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) layoutInt->container, token, tuneId);
677 if (obj == NULL) {
678 return -1;
679 }
680 layoutInt->container->obj.touchMask = swipesMask;
681 layoutInt->swipeUsage = usage;
682
683 return 0;
684}
685
693static nbgl_container_t *addListItem(nbgl_layoutInternal_t *layoutInt, const listItem_t *itemDesc)
694{
695 layoutObj_t *obj;
696 nbgl_text_area_t *textArea = NULL;
697 nbgl_container_t *container;
698 color_t color = ((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == OFF_STATE))
700 : BLACK;
701 nbgl_font_id_e fontId
702 = ((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == OFF_STATE))
704 : SMALL_BOLD_FONT;
705
706 LOG_DEBUG(LAYOUT_LOGGER, "addListItem():\n");
707
708 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
710 layoutInt, (nbgl_obj_t *) container, itemDesc->token, itemDesc->tuneId);
711 if (obj == NULL) {
712 return NULL;
713 }
714 obj->index = itemDesc->index;
715
716 // get container children (up to 4: text + left+right icons + sub text)
717 container->children = nbgl_containerPoolGet(4, layoutInt->layer);
718 container->nbChildren = 0;
719
720 container->obj.area.width = AVAILABLE_WIDTH;
721 container->obj.area.height
722 = LIST_ITEM_MIN_TEXT_HEIGHT
723 + 2 * (itemDesc->large ? LIST_ITEM_PRE_HEADING_LARGE : LIST_ITEM_PRE_HEADING);
724 container->layout = HORIZONTAL;
725 container->obj.alignmentMarginX = BORDER_MARGIN;
726 container->obj.alignment = NO_ALIGNMENT;
727 container->obj.alignTo = NULL;
728 // the bar can only be touched if not inactive AND if one of the icon is present
729 // otherwise it is seen as a title
730 if (((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == ON_STATE))
731 || (itemDesc->type == SWITCH_ITEM)) {
732 container->obj.touchMask = (1 << TOUCHED);
733 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
734 nbTouchableControls++;
735 }
736
737 // allocate main text if not NULL
738 if (itemDesc->text != NULL) {
739 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
740 textArea->textColor = color;
741 textArea->text = PIC(itemDesc->text);
742 textArea->onDrawCallback = NULL;
743 textArea->fontId = fontId;
744 textArea->wrapping = true;
745 textArea->obj.area.width = container->obj.area.width;
746 if (itemDesc->iconLeft != NULL) {
747 // reduce text width accordingly
748 textArea->obj.area.width
749 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE;
750 }
751 if (itemDesc->iconRight != NULL) {
752 // reduce text width accordingly
753 textArea->obj.area.width
754 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconRight))->width + BAR_INTERVALE;
755 }
756 else if (itemDesc->type == SWITCH_ITEM) {
757 textArea->obj.area.width -= SWITCH_ICON.width + BAR_INTERVALE;
758 }
759 textArea->obj.area.height = MAX(
760 LIST_ITEM_MIN_TEXT_HEIGHT,
762 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping));
763 textArea->style = NO_STYLE;
764 textArea->obj.alignment = TOP_LEFT;
765 textArea->obj.alignmentMarginY
766 = itemDesc->large ? LIST_ITEM_PRE_HEADING_LARGE : LIST_ITEM_PRE_HEADING;
767 if (textArea->obj.area.height > LIST_ITEM_MIN_TEXT_HEIGHT) {
768 textArea->obj.alignmentMarginY
769 -= (textArea->obj.area.height - LIST_ITEM_MIN_TEXT_HEIGHT) / 2;
770 }
771 textArea->textAlignment = MID_LEFT;
772 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
773 container->nbChildren++;
774 }
775
776 // allocate left icon if present
777 if (itemDesc->iconLeft != NULL) {
778 nbgl_image_t *imageLeft = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
779 imageLeft->foregroundColor = color;
780 imageLeft->buffer = PIC(itemDesc->iconLeft);
781 // align at the left of text
782 imageLeft->obj.alignment = MID_LEFT;
783 imageLeft->obj.alignTo = (nbgl_obj_t *) textArea;
784 imageLeft->obj.alignmentMarginX = BAR_INTERVALE;
785 container->children[container->nbChildren] = (nbgl_obj_t *) imageLeft;
786 container->nbChildren++;
787
788 if (textArea != NULL) {
789 textArea->obj.alignmentMarginX = imageLeft->buffer->width + BAR_INTERVALE;
790 }
791 }
792 // allocate right icon if present
793 if (itemDesc->iconRight != NULL) {
794 nbgl_image_t *imageRight = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
795 imageRight->foregroundColor = color;
796 imageRight->buffer = PIC(itemDesc->iconRight);
797 // align at the right of text
798 imageRight->obj.alignment = MID_RIGHT;
799 imageRight->obj.alignmentMarginX = BAR_INTERVALE;
800 imageRight->obj.alignTo = (nbgl_obj_t *) textArea;
801
802 container->children[container->nbChildren] = (nbgl_obj_t *) imageRight;
803 container->nbChildren++;
804 }
805 else if (itemDesc->type == SWITCH_ITEM) {
806 nbgl_switch_t *switchObj = (nbgl_switch_t *) nbgl_objPoolGet(SWITCH, layoutInt->layer);
807 switchObj->onColor = BLACK;
808 switchObj->offColor = LIGHT_GRAY;
809 switchObj->state = itemDesc->state;
810 switchObj->obj.alignment = MID_RIGHT;
811 switchObj->obj.alignmentMarginX = BAR_INTERVALE;
812 switchObj->obj.alignTo = (nbgl_obj_t *) textArea;
813
814 container->children[container->nbChildren] = (nbgl_obj_t *) switchObj;
815 container->nbChildren++;
816 }
817
818 if (itemDesc->subText != NULL) {
819 nbgl_text_area_t *subTextArea
821
822 subTextArea->textColor = color;
823 subTextArea->text = PIC(itemDesc->subText);
824 subTextArea->textAlignment = MID_LEFT;
825 subTextArea->fontId = SMALL_REGULAR_FONT;
826 subTextArea->style = NO_STYLE;
827 subTextArea->wrapping = true;
828 if (itemDesc->text != NULL) {
829 subTextArea->obj.alignment = BOTTOM_LEFT;
830 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
831 subTextArea->obj.alignmentMarginY = LIST_ITEM_HEADING_SUB_TEXT;
832 }
833 else {
834 subTextArea->obj.alignment = TOP_LEFT;
835 subTextArea->obj.alignmentMarginY = SUB_HEADER_MARGIN;
836 container->obj.area.height = SUB_HEADER_MARGIN;
837 }
838 if (itemDesc->iconLeft != NULL) {
839 subTextArea->obj.alignmentMarginX
840 = -(((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE);
841 }
842 subTextArea->obj.area.width = container->obj.area.width;
843 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
844 subTextArea->text,
845 subTextArea->obj.area.width,
846 subTextArea->wrapping);
847 container->children[container->nbChildren] = (nbgl_obj_t *) subTextArea;
848 container->nbChildren++;
849 container->obj.area.height
850 += subTextArea->obj.area.height + subTextArea->obj.alignmentMarginY;
851 }
852 // set this new container as child of main container
853 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
854
855 return container;
856}
857
866static nbgl_container_t *addContentCenter(nbgl_layoutInternal_t *layoutInt,
867 const nbgl_contentCenter_t *info)
868{
869 nbgl_container_t *container;
870 nbgl_text_area_t *textArea = NULL;
871 nbgl_image_t *image = NULL;
872 uint16_t fullHeight = 0;
873
874 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
875
876 // get container children
877 container->nbChildren = 0;
878 // the max number is: icon + anim + title + small title + description + sub-text
879 container->children = nbgl_containerPoolGet(6, layoutInt->layer);
880
881 // add icon or animation if present
882 if (info->icon != NULL) {
883 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
884 image->foregroundColor = (layoutInt->invertedColors) ? WHITE : BLACK;
885 image->buffer = PIC(info->icon);
886 image->obj.alignment = TOP_MIDDLE;
887 image->obj.alignmentMarginY = info->iconHug;
888
889 fullHeight += image->buffer->height + info->iconHug;
890 container->children[container->nbChildren] = (nbgl_obj_t *) image;
891 container->nbChildren++;
892
893 if (info->illustrType == ANIM_ILLUSTRATION) {
894 nbgl_image_t *anim;
895 anim = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
896 anim->foregroundColor = (layoutInt->invertedColors) ? WHITE : BLACK;
897 anim->buffer = PIC(info->animation->icons[0]);
898 anim->obj.alignment = TOP_MIDDLE;
899 anim->obj.alignmentMarginY = info->iconHug + info->animOffsetY;
900 anim->obj.alignmentMarginX = info->animOffsetX;
901
902 container->children[container->nbChildren] = (nbgl_obj_t *) anim;
903 container->nbChildren++;
904
905 layoutInt->animation = info->animation;
906 layoutInt->incrementAnim = true;
907 layoutInt->iconIdxInAnim = 0;
908
909 // update ticker to update the animation periodically
911
912 tickerCfg.tickerIntervale = info->animation->delayMs; // ms
913 tickerCfg.tickerValue = info->animation->delayMs; // ms
914 tickerCfg.tickerCallback = &animTickerCallback;
915 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
916 }
917 }
918 // add title if present
919 if (info->title != NULL) {
920 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
921 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
922 textArea->text = PIC(info->title);
923 textArea->textAlignment = CENTER;
924 textArea->fontId = LARGE_MEDIUM_FONT;
925 textArea->wrapping = true;
926 textArea->obj.area.width = AVAILABLE_WIDTH;
927 textArea->obj.area.height = nbgl_getTextHeightInWidth(
928 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
929
930 // if not the first child, put on bottom of the previous, with a margin
931 if (container->nbChildren > 0) {
932 textArea->obj.alignment = BOTTOM_MIDDLE;
933 textArea->obj.alignTo = (nbgl_obj_t *) image;
934 textArea->obj.alignmentMarginY = ICON_TITLE_MARGIN + info->iconHug;
935 }
936 else {
937 textArea->obj.alignment = TOP_MIDDLE;
938 }
939
940 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
941
942 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
943 container->nbChildren++;
944 }
945 // add small title if present
946 if (info->smallTitle != NULL) {
947 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
948 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
949 textArea->text = PIC(info->smallTitle);
950 textArea->textAlignment = CENTER;
951 textArea->fontId = SMALL_BOLD_FONT;
952 textArea->wrapping = true;
953 textArea->obj.area.width = AVAILABLE_WIDTH;
954 textArea->obj.area.height = nbgl_getTextHeightInWidth(
955 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
956
957 // if not the first child, put on bottom of the previous, with a margin
958 if (container->nbChildren > 0) {
959 textArea->obj.alignment = BOTTOM_MIDDLE;
960 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
961 textArea->obj.alignmentMarginY = VERTICAL_BORDER_MARGIN;
962 if (container->children[container->nbChildren - 1]->type == IMAGE) {
963 textArea->obj.alignmentMarginY = VERTICAL_BORDER_MARGIN + info->iconHug;
964 }
965 else {
966 textArea->obj.alignmentMarginY = TITLE_DESC_MARGIN;
967 }
968 }
969 else {
970 textArea->obj.alignment = TOP_MIDDLE;
971 }
972
973 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
974
975 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
976 container->nbChildren++;
977 }
978 // add description if present
979 if (info->description != NULL) {
980 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
981 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
982 textArea->text = PIC(info->description);
983 textArea->textAlignment = CENTER;
984 textArea->fontId = SMALL_REGULAR_FONT;
985 textArea->wrapping = true;
986 textArea->obj.area.width = AVAILABLE_WIDTH;
987 textArea->obj.area.height = nbgl_getTextHeightInWidth(
988 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
989
990 // if not the first child, put on bottom of the previous, with a margin
991 if (container->nbChildren > 0) {
992 textArea->obj.alignment = BOTTOM_MIDDLE;
993 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
994 if (container->children[container->nbChildren - 1]->type == TEXT_AREA) {
995 // if previous element is text, only space of 16 px
996 textArea->obj.alignmentMarginY = TITLE_DESC_MARGIN;
997 }
998 else {
999 textArea->obj.alignmentMarginY = ICON_TITLE_MARGIN + info->iconHug;
1000 }
1001 }
1002 else {
1003 textArea->obj.alignment = TOP_MIDDLE;
1004 }
1005
1006 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1007
1008 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1009 container->nbChildren++;
1010 }
1011 // add sub-text if present
1012 if (info->subText != NULL) {
1013 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1014 textArea->textColor = LIGHT_TEXT_COLOR;
1015 textArea->text = PIC(info->subText);
1016 textArea->textAlignment = CENTER;
1017 textArea->fontId = SMALL_REGULAR_FONT;
1018 textArea->wrapping = true;
1019 textArea->obj.area.width = AVAILABLE_WIDTH;
1020 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1021 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1022 // sub-text is included in a hug of 8px
1023 textArea->obj.area.height += 2 * 8;
1024 // if not the first child, put on bottom of the previous, with a margin
1025 if (container->nbChildren > 0) {
1026 textArea->obj.alignment = BOTTOM_MIDDLE;
1027 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1028 textArea->obj.alignmentMarginY = 16;
1029 if (container->children[container->nbChildren - 1]->type == IMAGE) {
1030 textArea->obj.alignmentMarginY += info->iconHug;
1031 }
1032 }
1033 else {
1034 textArea->obj.alignment = TOP_MIDDLE;
1035 }
1036
1037 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1038
1039 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1040 container->nbChildren++;
1041 }
1042 container->layout = VERTICAL;
1043 container->obj.alignment = CENTER;
1044 container->obj.area.width = AVAILABLE_WIDTH;
1045 container->obj.area.height = fullHeight;
1046 if (info->padding) {
1047 container->obj.area.height += 40;
1048 }
1049
1050 // set this new container as child of main container
1051 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1052
1053 return container;
1054}
1055
1056/**********************
1057 * GLOBAL FUNCTIONS
1058 **********************/
1059
1067{
1068 nbgl_layoutInternal_t *layout = NULL;
1069
1070 if (description->modal) {
1071 int i;
1072 // find an empty layout in the array of layouts (0 is reserved for background)
1073 for (i = 1; i < NB_MAX_LAYOUTS; i++) {
1074 if (!gLayout[i].isUsed) {
1075 layout = &gLayout[i];
1076 }
1077 }
1078 }
1079 else {
1080 // always use layout 0 for background
1081 layout = &gLayout[0];
1082 if (topLayout == NULL) {
1083 topLayout = layout;
1084 }
1085 }
1086 if (layout == NULL) {
1087 LOG_WARN(LAYOUT_LOGGER, "nbgl_layoutGet(): impossible to get a layout!\n");
1088 return NULL;
1089 }
1090
1091 // reset globals
1092 nbgl_layoutInternal_t *backgroundTop = gLayout[0].top;
1093 memset(layout, 0, sizeof(nbgl_layoutInternal_t));
1094 // link layout to other ones
1095 if (description->modal) {
1096 if (topLayout != NULL) {
1097 // if topLayout already existing, push this new one on top of it
1098 topLayout->top = layout;
1099 layout->bottom = topLayout;
1100 }
1101 else {
1102 // otherwise put it on top of background layout
1103 layout->bottom = &gLayout[0];
1104 gLayout[0].top = layout;
1105 }
1106 topLayout = layout;
1107 }
1108 else {
1109 // restore potentially valid background top layer
1110 gLayout[0].top = backgroundTop;
1111 }
1112
1113 nbTouchableControls = 0;
1114
1115 layout->callback = (nbgl_layoutTouchCallback_t) PIC(description->onActionCallback);
1116 layout->modal = description->modal;
1117 layout->withLeftBorder = description->withLeftBorder;
1118 if (description->modal) {
1119 layout->layer = nbgl_screenPush(&layout->children,
1121 &description->ticker,
1122 (nbgl_touchCallback_t) touchCallback);
1123 }
1124 else {
1125 nbgl_screenSet(&layout->children,
1127 &description->ticker,
1128 (nbgl_touchCallback_t) touchCallback);
1129 layout->layer = 0;
1130 }
1132 layout->container->obj.area.width = SCREEN_WIDTH;
1133 layout->container->obj.area.height = SCREEN_HEIGHT;
1134 layout->container->layout = VERTICAL;
1135 layout->container->children = nbgl_containerPoolGet(NB_MAX_CONTAINER_CHILDREN, layout->layer);
1136 // by default, if no header, main container is aligned on top-left
1137 layout->container->obj.alignment = TOP_LEFT;
1138 // main container is always the second object, leaving space for header
1139 layout->children[MAIN_CONTAINER_INDEX] = (nbgl_obj_t *) layout->container;
1140 layout->isUsed = true;
1141
1142 // if a tap text is defined, make the container tapable and display this text in gray
1143 if (description->tapActionText != NULL) {
1144 layoutObj_t *obj;
1145 const char *tapActionText = PIC(description->tapActionText);
1146
1147 obj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
1148 layout->nbUsedCallbackObjs++;
1149 obj->obj = (nbgl_obj_t *) layout->container;
1150 obj->token = description->tapActionToken;
1151 obj->tuneId = description->tapTuneId;
1152 layout->container->obj.touchMask = (1 << TOUCHED);
1153 layout->container->obj.touchId = WHOLE_SCREEN_ID;
1154
1155 if (strlen(tapActionText) > 0) {
1156 nbgl_layoutUpFooter_t footerDesc;
1157 footerDesc.type = UP_FOOTER_TEXT;
1158 footerDesc.text.text = tapActionText;
1159 footerDesc.text.token = description->tapActionToken;
1160 footerDesc.text.tuneId = description->tapTuneId;
1161 nbgl_layoutAddUpFooter((nbgl_layout_t *) layout, &footerDesc);
1162 }
1163 }
1164 return (nbgl_layout_t *) layout;
1165}
1166
1179 uint16_t swipesMask,
1180 const char *text,
1181 uint8_t token,
1182 tune_index_e tuneId)
1183{
1184 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1185
1186 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwipe():\n");
1187 if (layout == NULL) {
1188 return -1;
1189 }
1190
1191 if (text) {
1192 // create 'tap to continue' text area
1194 layoutInt->tapText->text = PIC(text);
1195 layoutInt->tapText->textColor = LIGHT_TEXT_COLOR;
1196 layoutInt->tapText->fontId = SMALL_REGULAR_FONT;
1197 layoutInt->tapText->obj.area.width = AVAILABLE_WIDTH;
1198 layoutInt->tapText->obj.area.height = nbgl_getFontLineHeight(layoutInt->tapText->fontId);
1199 layoutInt->tapText->textAlignment = CENTER;
1200 layoutInt->tapText->obj.alignmentMarginY = TAP_TO_CONTINUE_MARGIN;
1201 layoutInt->tapText->obj.alignment = BOTTOM_MIDDLE;
1202 }
1203 return addSwipeInternal(layoutInt, swipesMask, SWIPE_USAGE_CUSTOM, token, tuneId);
1204}
1205
1214{
1215 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1216
1217 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutInvertBackground():\n");
1218 if (layout == NULL) {
1219 return -1;
1220 }
1221 layoutInt->invertedColors = true;
1223
1224 // invert potential "Tap to continue"
1225 if ((layoutInt->upFooterContainer) && (layoutInt->upFooterContainer->nbChildren == 1)
1226 && (layoutInt->upFooterContainer->children[0]->type == TEXT_AREA)) {
1227 nbgl_text_area_t *textArea = (nbgl_text_area_t *) layoutInt->upFooterContainer->children[0];
1228 if (textArea->textColor == LIGHT_TEXT_COLOR) {
1229 textArea->textColor = INACTIVE_COLOR;
1230 }
1231 }
1232
1233 return 0;
1234}
1235
1246 const nbgl_icon_details_t *icon,
1247 uint8_t token,
1248 tune_index_e tuneId)
1249{
1250 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1251 layoutObj_t *obj;
1252 nbgl_button_t *button;
1253
1254 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTopRightButton():\n");
1255 if (layout == NULL) {
1256 return -1;
1257 }
1258 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
1259 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) button, token, tuneId);
1260 if (obj == NULL) {
1261 return -1;
1262 }
1263
1264 button->obj.area.width = BUTTON_WIDTH;
1265 button->obj.area.height = BUTTON_DIAMETER;
1266 button->radius = BUTTON_RADIUS;
1267 button->obj.alignmentMarginX = BORDER_MARGIN;
1268 button->obj.alignmentMarginY = BORDER_MARGIN;
1269 button->foregroundColor = BLACK;
1270 button->innerColor = WHITE;
1271 button->borderColor = LIGHT_GRAY;
1272 button->obj.touchMask = (1 << TOUCHED);
1273 button->obj.touchId = TOP_RIGHT_BUTTON_ID;
1274 button->icon = PIC(icon);
1275 button->obj.alignment = TOP_RIGHT;
1276
1277 // add to screen
1278 layoutInt->children[TOP_RIGHT_BUTTON_INDEX] = (nbgl_obj_t *) button;
1279
1280 return 0;
1281}
1282
1291{
1292 nbgl_layoutFooter_t footerDesc;
1293 footerDesc.type = FOOTER_NAV;
1294 footerDesc.separationLine = info->withSeparationLine;
1295 footerDesc.navigation.activePage = info->activePage;
1296 footerDesc.navigation.nbPages = info->nbPages;
1297 footerDesc.navigation.withExitKey = info->withExitKey;
1298 footerDesc.navigation.withBackKey = info->withBackKey;
1299 footerDesc.navigation.withPageIndicator = false;
1300 footerDesc.navigation.token = info->token;
1301 footerDesc.navigation.tuneId = info->tuneId;
1302 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1303}
1304
1317 const nbgl_icon_details_t *icon,
1318 uint8_t token,
1319 bool separationLine,
1320 tune_index_e tuneId)
1321{
1322 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddBottomButton():\n");
1323 nbgl_layoutFooter_t footerDesc;
1324 footerDesc.type = FOOTER_SIMPLE_BUTTON;
1325 footerDesc.separationLine = separationLine;
1326 footerDesc.button.fittingContent = false;
1327 footerDesc.button.icon = PIC(icon);
1328 footerDesc.button.text = NULL;
1329 footerDesc.button.token = token;
1330 footerDesc.button.tuneId = tuneId;
1331 footerDesc.button.style = WHITE_BACKGROUND;
1332 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1333}
1334
1343{
1344 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1345 nbgl_container_t *container;
1346 listItem_t itemDesc = {0};
1347
1348 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar():\n");
1349 if (layout == NULL) {
1350 return -1;
1351 }
1352 // main text is mandatory
1353 if (barLayout->text == NULL) {
1354 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar(): main text is mandatory\n");
1355 }
1356
1357 itemDesc.iconLeft = barLayout->iconLeft;
1358 itemDesc.iconRight = barLayout->iconRight;
1359 itemDesc.text = barLayout->text;
1360 itemDesc.subText = barLayout->subText;
1361 itemDesc.token = barLayout->token;
1362 itemDesc.tuneId = barLayout->tuneId;
1363 itemDesc.state = (barLayout->inactive) ? OFF_STATE : ON_STATE;
1364 itemDesc.large = barLayout->large;
1365 itemDesc.type = TOUCHABLE_BAR_ITEM;
1366 container = addListItem(layoutInt, &itemDesc);
1367
1368 if (container == NULL) {
1369 return -1;
1370 }
1371 return container->obj.area.height;
1372}
1373
1382{
1383 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1384 nbgl_container_t *container;
1385 listItem_t itemDesc = {0};
1386
1387 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwitch():\n");
1388 if (layout == NULL) {
1389 return -1;
1390 }
1391 // main text is mandatory
1392 if (switchLayout->text == NULL) {
1393 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddSwitch(): main text is mandatory\n");
1394 }
1395
1396 itemDesc.text = switchLayout->text;
1397 itemDesc.subText = switchLayout->subText;
1398 itemDesc.token = switchLayout->token;
1399 itemDesc.tuneId = switchLayout->tuneId;
1400 itemDesc.state = switchLayout->initState;
1401 itemDesc.large = false;
1402 itemDesc.type = SWITCH_ITEM;
1403 container = addListItem(layoutInt, &itemDesc);
1404
1405 if (container == NULL) {
1406 return -1;
1407 }
1408 return container->obj.area.height;
1409}
1410
1419int nbgl_layoutAddText(nbgl_layout_t *layout, const char *text, const char *subText)
1420{
1421 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1422 nbgl_container_t *container;
1423 listItem_t itemDesc = {0};
1424
1425 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddText():\n");
1426 if (layout == NULL) {
1427 return -1;
1428 }
1429
1430 itemDesc.text = text;
1431 itemDesc.subText = subText;
1432 itemDesc.token = NBGL_INVALID_TOKEN;
1433 itemDesc.tuneId = NBGL_NO_TUNE;
1434 itemDesc.type = TEXT_ITEM;
1435 container = addListItem(layoutInt, &itemDesc);
1436
1437 if (container == NULL) {
1438 return -1;
1439 }
1440 return container->obj.area.height;
1441}
1442
1455 const char *text,
1456 const char *subText,
1457 uint8_t token,
1458 uint8_t index)
1459{
1460 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1461 nbgl_container_t *container;
1462 listItem_t itemDesc = {0};
1463 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextWithAlias():\n");
1464 if (layout == NULL) {
1465 return -1;
1466 }
1467
1468 itemDesc.text = text;
1469 itemDesc.subText = subText;
1470 itemDesc.iconRight = &MINI_PUSH_ICON;
1471 itemDesc.token = token;
1472 itemDesc.tuneId = NBGL_NO_TUNE;
1473 itemDesc.type = TOUCHABLE_BAR_ITEM;
1474 itemDesc.index = index;
1475 itemDesc.state = ON_STATE;
1476 container = addListItem(layoutInt, &itemDesc);
1477
1478 if (container == NULL) {
1479 return -1;
1480 }
1481 return container->obj.area.height;
1482}
1483
1492int nbgl_layoutAddLargeCaseText(nbgl_layout_t *layout, const char *text, bool grayedOut)
1493{
1494 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1495 nbgl_text_area_t *textArea;
1496
1497 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLargeCaseText():\n");
1498 if (layout == NULL) {
1499 return -1;
1500 }
1501 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1502
1503 textArea->textColor = grayedOut ? INACTIVE_TEXT_COLOR : BLACK;
1504 textArea->text = PIC(text);
1505 textArea->textAlignment = MID_LEFT;
1506 textArea->fontId = LARGE_MEDIUM_FONT;
1507 textArea->obj.area.width = AVAILABLE_WIDTH;
1508 textArea->wrapping = true;
1509 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1510 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1511 textArea->style = NO_STYLE;
1512 textArea->obj.alignment = NO_ALIGNMENT;
1513 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1514 if (layoutInt->container->nbChildren == 0) {
1515 // if first object of container, increase the margin from top
1516 textArea->obj.alignmentMarginY += PRE_FIRST_TEXT_MARGIN;
1517 }
1518 else {
1519 textArea->obj.alignmentMarginY = INTER_PARAGRAPHS_MARGIN; // between paragraphs
1520 }
1521
1522 // set this new obj as child of main container
1523 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1524
1525 return 0;
1526}
1527
1541 const char *title,
1542 const char *description,
1543 const char *info)
1544{
1545 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1546 nbgl_text_area_t *textArea;
1547
1548 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextContent():\n");
1549 if (layout == NULL) {
1550 return -1;
1551 }
1552
1553 // create title
1554 if (title != NULL) {
1555 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1556 textArea->textColor = BLACK;
1557 textArea->text = PIC(title);
1558 textArea->textAlignment = MID_LEFT;
1559 textArea->fontId = LARGE_MEDIUM_FONT;
1560 textArea->style = NO_STYLE;
1561 textArea->wrapping = true;
1562 textArea->obj.alignment = NO_ALIGNMENT;
1563 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1564 textArea->obj.alignmentMarginY = PRE_TITLE_MARGIN;
1565 textArea->obj.area.width = AVAILABLE_WIDTH;
1566 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1567 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1568 // set this new obj as child of main container
1569 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1570 }
1571
1572 // create description
1573 if (description != NULL) {
1574 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1575 textArea->textColor = BLACK;
1576 textArea->text = PIC(description);
1577 textArea->fontId = SMALL_REGULAR_FONT;
1578 textArea->style = NO_STYLE;
1579 textArea->wrapping = true;
1580 textArea->obj.area.width = AVAILABLE_WIDTH;
1581 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1582 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1583 textArea->textAlignment = MID_LEFT;
1584 textArea->obj.alignment = NO_ALIGNMENT;
1585 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1586 textArea->obj.alignmentMarginY = PRE_DESCRIPTION_MARGIN;
1587 // set this new obj as child of main container
1588 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1589 }
1590
1591 // create info on the bottom
1592 if (info != NULL) {
1593 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1594 textArea->textColor = LIGHT_TEXT_COLOR;
1595 textArea->text = PIC(info);
1596 textArea->fontId = SMALL_REGULAR_FONT;
1597 textArea->style = NO_STYLE;
1598 textArea->wrapping = true;
1599 textArea->obj.area.width = AVAILABLE_WIDTH;
1600 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1601 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1602 textArea->textAlignment = MID_LEFT;
1603 textArea->obj.alignment = BOTTOM_LEFT;
1604 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1605 textArea->obj.alignmentMarginY = 40;
1606 // set this new obj as child of main container
1607 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1608 }
1609
1610 return layoutInt->container->obj.area.height;
1611}
1612
1621{
1622 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1623 layoutObj_t *obj;
1624 uint8_t i;
1625
1626 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddRadioChoice():\n");
1627 if (layout == NULL) {
1628 return -1;
1629 }
1630 for (i = 0; i < choices->nbChoices; i++) {
1631 nbgl_container_t *container;
1632 nbgl_text_area_t *textArea;
1633 nbgl_radio_t *button;
1634 nbgl_line_t *line;
1635
1636 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1637 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1638 button = (nbgl_radio_t *) nbgl_objPoolGet(RADIO_BUTTON, layoutInt->layer);
1639
1641 layoutInt, (nbgl_obj_t *) container, choices->token, choices->tuneId);
1642 if (obj == NULL) {
1643 return -1;
1644 }
1645
1646 // get container children (max 2)
1647 container->nbChildren = 2;
1648 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1649 container->obj.area.width = AVAILABLE_WIDTH;
1650 container->obj.area.height = RADIO_CHOICE_HEIGHT;
1651 container->obj.alignment = NO_ALIGNMENT;
1652 container->obj.alignmentMarginX = BORDER_MARGIN;
1653 container->obj.alignTo = (nbgl_obj_t *) NULL;
1654
1655 // init button for this choice
1656 button->activeColor = BLACK;
1657 button->borderColor = LIGHT_GRAY;
1658 button->obj.alignTo = (nbgl_obj_t *) container;
1659 button->obj.alignment = MID_RIGHT;
1660 button->state = OFF_STATE;
1661 container->children[1] = (nbgl_obj_t *) button;
1662
1663 // init text area for this choice
1664 if (choices->localized == true) {
1665#ifdef HAVE_LANGUAGE_PACK
1666 textArea->text = get_ux_loc_string(choices->nameIds[i]);
1667#endif // HAVE_LANGUAGE_PACK
1668 }
1669 else {
1670 textArea->text = PIC(choices->names[i]);
1671 }
1672 textArea->textAlignment = MID_LEFT;
1673 textArea->obj.area.width = container->obj.area.width - RADIO_WIDTH;
1674 textArea->style = NO_STYLE;
1675 textArea->obj.alignment = MID_LEFT;
1676 textArea->obj.alignTo = (nbgl_obj_t *) container;
1677 container->children[0] = (nbgl_obj_t *) textArea;
1678
1679 // whole container should be touchable
1680 container->obj.touchMask = (1 << TOUCHED);
1681 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
1682 nbTouchableControls++;
1683
1684 // highlight init choice
1685 if (i == choices->initChoice) {
1686 button->state = ON_STATE;
1687 textArea->textColor = BLACK;
1688 textArea->fontId = SMALL_BOLD_FONT;
1689 }
1690 else {
1691 button->state = OFF_STATE;
1692 textArea->textColor = LIGHT_TEXT_COLOR;
1693 textArea->fontId = SMALL_REGULAR_FONT;
1694 }
1695 textArea->obj.area.height = nbgl_getFontHeight(textArea->fontId);
1696
1697 line = createHorizontalLine(layoutInt->layer);
1698 line->obj.alignmentMarginY = -1;
1699
1700 // set these new objs as child of main container
1701 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1702 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
1703 }
1704
1705 return 0;
1706}
1707
1717{
1718 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1719 nbgl_container_t *container;
1720 nbgl_contentCenter_t centeredInfo = {0};
1721
1722 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddCenteredInfo():\n");
1723 if (layout == NULL) {
1724 return -1;
1725 }
1726
1727 centeredInfo.icon = info->icon;
1728 centeredInfo.illustrType = ICON_ILLUSTRATION;
1729
1730 if (info->text1 != NULL) {
1731 if (info->style != NORMAL_INFO) {
1732 centeredInfo.title = info->text1;
1733 }
1734 else {
1735 centeredInfo.smallTitle = info->text1;
1736 }
1737 }
1738 if (info->text2 != NULL) {
1739 if (info->style != LARGE_CASE_BOLD_INFO) {
1740 centeredInfo.description = info->text2;
1741 }
1742 else {
1743 centeredInfo.smallTitle = info->text2;
1744 }
1745 }
1746 if (info->text3 != NULL) {
1747 if (info->style == LARGE_CASE_GRAY_INFO) {
1748 centeredInfo.subText = info->text3;
1749 }
1750 else {
1751 centeredInfo.description = info->text3;
1752 }
1753 }
1754 container = addContentCenter(layoutInt, &centeredInfo);
1755
1756 if (info->onTop) {
1757 container->obj.alignmentMarginX = BORDER_MARGIN;
1758 container->obj.alignmentMarginY = BORDER_MARGIN + info->offsetY;
1759 container->obj.alignment = NO_ALIGNMENT;
1760 }
1761 else {
1762 container->obj.alignmentMarginY = info->offsetY;
1763 }
1764
1765 return container->obj.area.height;
1766}
1767
1777{
1778 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1779 nbgl_container_t *container;
1780
1781 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddContentCenter():\n");
1782 if (layout == NULL) {
1783 return -1;
1784 }
1785
1786 container = addContentCenter(layoutInt, info);
1787
1788 return container->obj.area.height;
1789}
1790
1799{
1800 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1801 nbgl_container_t *container;
1802 nbgl_text_area_t *textArea;
1803 uint8_t row;
1804
1805 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLeftContent():\n");
1806 if (layout == NULL) {
1807 return -1;
1808 }
1809 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1810
1811 // get container children
1812 container->nbChildren = info->nbRows + 1;
1813 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1814 container->layout = VERTICAL;
1815 container->obj.area.width = AVAILABLE_WIDTH;
1816 container->obj.alignmentMarginX = BORDER_MARGIN;
1817
1818 // create title
1819 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1820 textArea->textColor = BLACK;
1821 textArea->text = PIC(info->title);
1822 textArea->textAlignment = MID_LEFT;
1823 textArea->fontId = LARGE_MEDIUM_FONT;
1824 textArea->wrapping = true;
1825#ifdef TARGET_STAX
1826 textArea->obj.alignmentMarginY = 24;
1827#endif
1828 textArea->obj.area.width = AVAILABLE_WIDTH;
1829 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1830 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1831
1832 container->obj.area.height += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1833
1834 container->children[0] = (nbgl_obj_t *) textArea;
1835
1836 for (row = 0; row < info->nbRows; row++) {
1837 nbgl_container_t *rowContainer;
1838 nbgl_image_t *image;
1839
1840 // create a grid with 1 icon on the left column and 1 text on the right one
1841 rowContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, 0);
1842 rowContainer->nbChildren = 2; // 1 icon + 1 text
1843 rowContainer->children = nbgl_containerPoolGet(rowContainer->nbChildren, 0);
1844 rowContainer->obj.area.width = AVAILABLE_WIDTH;
1845
1846 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, 0);
1847 image->foregroundColor = BLACK;
1848 image->buffer = PIC(info->rowIcons[row]);
1849 rowContainer->children[0] = (nbgl_obj_t *) image;
1850
1851 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, 0);
1852 textArea->textColor = BLACK;
1853 textArea->text = PIC(info->rowTexts[row]);
1854 textArea->textAlignment = MID_LEFT;
1855 textArea->fontId = SMALL_REGULAR_FONT;
1856 textArea->wrapping = true;
1857 textArea->obj.area.width
1858 = AVAILABLE_WIDTH - image->buffer->width - LEFT_CONTENT_ICON_TEXT_X;
1859 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1860 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1861 textArea->obj.alignment = MID_RIGHT;
1862 rowContainer->children[1] = (nbgl_obj_t *) textArea;
1863 rowContainer->obj.area.height
1864 = MAX(image->buffer->height, textArea->obj.area.height + LEFT_CONTENT_TEXT_PADDING);
1865
1866 if (row == 0) {
1867 rowContainer->obj.alignmentMarginY = PRE_FIRST_ROW_MARGIN;
1868 }
1869 else {
1870 rowContainer->obj.alignmentMarginY = INTER_ROWS_MARGIN;
1871 }
1872 container->children[1 + row] = (nbgl_obj_t *) rowContainer;
1873 container->obj.area.height
1874 += rowContainer->obj.area.height + rowContainer->obj.alignmentMarginY;
1875 }
1876 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1877
1878 return container->obj.area.height;
1879}
1880
1881#ifdef NBGL_QRCODE
1891{
1892 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1893 nbgl_container_t *container;
1894 nbgl_text_area_t *textArea = NULL;
1895 nbgl_qrcode_t *qrcode;
1896 uint16_t fullHeight = 0;
1897
1898 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddQRCode():\n");
1899 if (layout == NULL) {
1900 return -1;
1901 }
1902
1903 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1904
1905 // get container children (max 2 (QRCode + text1 + text2))
1906 container->children = nbgl_containerPoolGet(3, layoutInt->layer);
1907 container->nbChildren = 0;
1908
1909 qrcode = (nbgl_qrcode_t *) nbgl_objPoolGet(QR_CODE, layoutInt->layer);
1910 // version is forced to V10 if url is longer than 62 characters
1911 if (strlen(PIC(info->url)) > 62) {
1912 qrcode->version = QRCODE_V10;
1913 }
1914 else {
1915 qrcode->version = QRCODE_V4;
1916 }
1917 qrcode->foregroundColor = BLACK;
1918 // in QR V4, we use:
1919 // - 8*8 screen pixels for one QR pixel on Stax/Flex
1920 // - 5*5 screen pixels for one QR pixel on Apex
1921 // in QR V10, we use 4*4 screen pixels for one QR pixel
1922#ifndef TARGET_APEX
1923 qrcode->obj.area.width
1924 = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 8) : (QR_V10_NB_PIX_SIZE * 4);
1925#else // TARGET_APEX
1926 qrcode->obj.area.width
1927 = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 5) : (QR_V10_NB_PIX_SIZE * 4);
1928#endif // TARGET_APEX
1929 qrcode->obj.area.height = qrcode->obj.area.width;
1930 qrcode->text = PIC(info->url);
1931 qrcode->obj.area.bpp = NBGL_BPP_1;
1932 qrcode->obj.alignment = TOP_MIDDLE;
1933
1934 fullHeight += qrcode->obj.area.height;
1935 container->children[container->nbChildren] = (nbgl_obj_t *) qrcode;
1936 container->nbChildren++;
1937
1938 if (info->text1 != NULL) {
1939 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1940 textArea->textColor = BLACK;
1941 textArea->text = PIC(info->text1);
1942 textArea->textAlignment = CENTER;
1943 textArea->fontId = (info->largeText1 == true) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
1944 textArea->wrapping = true;
1945 textArea->obj.area.width = AVAILABLE_WIDTH;
1946 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1947 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1948 textArea->obj.alignment = BOTTOM_MIDDLE;
1949 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1950 textArea->obj.alignmentMarginY = QR_PRE_TEXT_MARGIN;
1951
1952 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1953
1954 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1955 container->nbChildren++;
1956 }
1957 if (info->text2 != NULL) {
1958 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1959 textArea->textColor = LIGHT_TEXT_COLOR;
1960 textArea->text = PIC(info->text2);
1961 textArea->textAlignment = CENTER;
1962 textArea->fontId = SMALL_REGULAR_FONT;
1963 textArea->wrapping = true;
1964 textArea->obj.area.width = AVAILABLE_WIDTH;
1965 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1966 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1967 textArea->obj.alignment = BOTTOM_MIDDLE;
1968 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1969 if (info->text1 != NULL) {
1970 textArea->obj.alignmentMarginY = QR_INTER_TEXTS_MARGIN;
1971 }
1972 else {
1973 textArea->obj.alignmentMarginY = QR_PRE_TEXT_MARGIN + 8;
1974 }
1975
1976 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY + 8;
1977
1978 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1979 container->nbChildren++;
1980 }
1981 // ensure that fullHeight is fitting in main container height (with 16 px margin)
1982 if ((fullHeight >= (layoutInt->container->obj.area.height - 16))
1983 && (qrcode->version == QRCODE_V4)) {
1984 qrcode->version = QRCODE_V4_SMALL;
1985 // in QR V4 small, we use 4*4 screen pixels for one QR pixel
1986 qrcode->obj.area.width = QR_V4_NB_PIX_SIZE * 4;
1987 qrcode->obj.area.height = qrcode->obj.area.width;
1988 fullHeight -= QR_V4_NB_PIX_SIZE * 4;
1989 }
1990 container->obj.area.height = fullHeight;
1991 container->layout = VERTICAL;
1992 if (info->centered) {
1993 container->obj.alignment = CENTER;
1994 }
1995 else {
1996 container->obj.alignment = BOTTOM_MIDDLE;
1997 container->obj.alignTo
1998 = layoutInt->container->children[layoutInt->container->nbChildren - 1];
1999 }
2000 container->obj.alignmentMarginY = info->offsetY;
2001
2002 container->obj.area.width = AVAILABLE_WIDTH;
2003
2004 // set this new container as child of main container
2005 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2006
2007 return fullHeight;
2008}
2009#endif // NBGL_QRCODE
2010
2020{
2021 nbgl_layoutFooter_t footerDesc;
2022 footerDesc.type = FOOTER_CHOICE_BUTTONS;
2023 footerDesc.separationLine = false;
2024 footerDesc.choiceButtons.bottomText = info->bottomText;
2025 footerDesc.choiceButtons.token = info->token;
2026 footerDesc.choiceButtons.topText = info->topText;
2027 footerDesc.choiceButtons.topIcon = info->topIcon;
2028 footerDesc.choiceButtons.style = info->style;
2029 footerDesc.choiceButtons.tuneId = info->tuneId;
2030 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2031}
2032
2044{
2046 .horizontalButtons.leftIcon = info->leftIcon,
2047 .horizontalButtons.leftToken = info->leftToken,
2048 .horizontalButtons.rightText = info->rightText,
2049 .horizontalButtons.rightToken = info->rightToken,
2050 .horizontalButtons.tuneId = info->tuneId};
2051
2052 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHorizontalButtons():\n");
2053 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2054}
2055
2064{
2065 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2066 nbgl_text_area_t *itemTextArea;
2067 nbgl_text_area_t *valueTextArea;
2068 nbgl_container_t *container;
2069 uint8_t i;
2070
2071 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTagValueList():\n");
2072 if (layout == NULL) {
2073 return -1;
2074 }
2075
2076 for (i = 0; i < list->nbPairs; i++) {
2077 const nbgl_layoutTagValue_t *pair;
2078 uint16_t fullHeight = 0;
2079 const nbgl_icon_details_t *valueIcon = NULL;
2080
2081 if (list->pairs != NULL) {
2082 pair = &list->pairs[i];
2083 }
2084 else {
2085 pair = list->callback(list->startIndex + i);
2086 }
2087
2088 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2089
2090 // get container children (max 3 if a valueIcon, otherwise 2)
2091 container->children
2092 = nbgl_containerPoolGet((pair->valueIcon != NULL) ? 3 : 2, layoutInt->layer);
2093
2094 itemTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2095 valueTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2096
2097 // init text area for this choice
2098 itemTextArea->textColor = LIGHT_TEXT_COLOR;
2099 itemTextArea->text = PIC(pair->item);
2100 itemTextArea->textAlignment = MID_LEFT;
2101 itemTextArea->fontId = SMALL_REGULAR_FONT;
2102 itemTextArea->wrapping = true;
2103 itemTextArea->obj.area.width = AVAILABLE_WIDTH;
2104 itemTextArea->obj.area.height = nbgl_getTextHeightInWidth(
2105 itemTextArea->fontId, itemTextArea->text, AVAILABLE_WIDTH, itemTextArea->wrapping);
2106 itemTextArea->style = NO_STYLE;
2107 itemTextArea->obj.alignment = NO_ALIGNMENT;
2108 itemTextArea->obj.alignmentMarginX = 0;
2109 itemTextArea->obj.alignmentMarginY = 0;
2110 itemTextArea->obj.alignTo = NULL;
2111 container->children[container->nbChildren] = (nbgl_obj_t *) itemTextArea;
2112 container->nbChildren++;
2113
2114 fullHeight += itemTextArea->obj.area.height;
2115
2116 // init button for this choice
2117 valueTextArea->textColor = BLACK;
2118 valueTextArea->text = PIC(pair->value);
2119 valueTextArea->textAlignment = MID_LEFT;
2120 if (list->smallCaseForValue) {
2121 valueTextArea->fontId = SMALL_BOLD_FONT;
2122 }
2123 else {
2124 valueTextArea->fontId = LARGE_MEDIUM_FONT;
2125 }
2126 if ((pair->aliasValue == 0) && (pair->valueIcon == NULL)) {
2127 valueTextArea->obj.area.width = AVAILABLE_WIDTH;
2128 }
2129 else {
2130 if (pair->aliasValue) {
2131 // if the value is an alias, we automatically display a (>) icon
2132 valueIcon = &MINI_PUSH_ICON;
2133 }
2134 else {
2135 // otherwise use the provided icon
2136 valueIcon = PIC(pair->valueIcon);
2137 }
2138 // decrease the available width for value text
2139 valueTextArea->obj.area.width = AVAILABLE_WIDTH - valueIcon->width - 12;
2140 }
2141
2142 // handle the nbMaxLinesForValue parameter, used to automatically keep only
2143 // nbMaxLinesForValue lines
2144 uint16_t nbLines = nbgl_getTextNbLinesInWidth(valueTextArea->fontId,
2145 valueTextArea->text,
2146 valueTextArea->obj.area.width,
2147 list->wrapping);
2148 // use this nbMaxLinesForValue parameter only if >0
2149 if ((list->nbMaxLinesForValue > 0) && (nbLines > list->nbMaxLinesForValue)) {
2150 nbLines = list->nbMaxLinesForValue;
2151 valueTextArea->nbMaxLines = list->nbMaxLinesForValue;
2152 valueTextArea->hideEndOfLastLine = list->hideEndOfLastLine;
2153 }
2154 const nbgl_font_t *font = nbgl_getFont(valueTextArea->fontId);
2155 valueTextArea->obj.area.height = nbLines * font->line_height;
2156 valueTextArea->style = NO_STYLE;
2157 valueTextArea->obj.alignment = BOTTOM_LEFT;
2158 valueTextArea->obj.alignmentMarginY = 4;
2159 valueTextArea->obj.alignTo = (nbgl_obj_t *) itemTextArea;
2160 valueTextArea->wrapping = list->wrapping;
2161 container->children[container->nbChildren] = (nbgl_obj_t *) valueTextArea;
2162 container->nbChildren++;
2163
2164 fullHeight += valueTextArea->obj.area.height + valueTextArea->obj.alignmentMarginY;
2165 if (valueIcon != NULL) {
2166 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2167 // set the container as touchable, not only the image
2169 layoutInt, (nbgl_obj_t *) container, list->token, TUNE_TAP_CASUAL);
2170 obj->index = i;
2171 image->foregroundColor = BLACK;
2172 image->buffer = valueIcon;
2173 image->obj.alignment = RIGHT_TOP;
2174 image->obj.alignmentMarginX = 12;
2175 image->obj.alignTo = (nbgl_obj_t *) valueTextArea;
2176 // set the container as touchable, not only the image
2177 container->obj.touchMask = (1 << TOUCHED);
2178 container->obj.touchId = VALUE_BUTTON_1_ID + i;
2179
2180 container->children[container->nbChildren] = (nbgl_obj_t *) image;
2181 container->nbChildren++;
2182 }
2183
2184 container->obj.area.width = AVAILABLE_WIDTH;
2185 container->obj.area.height = fullHeight;
2186 container->layout = VERTICAL;
2187 container->obj.alignmentMarginX = BORDER_MARGIN;
2188 if (i > 0) {
2189 container->obj.alignmentMarginY = INTER_TAG_VALUE_MARGIN;
2190 }
2191 else {
2192 // if there is a header with a separation line, add a margin
2193 if (layoutInt->headerContainer && (layoutInt->headerContainer->nbChildren > 0)
2194 && (layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren - 1]
2195 ->type
2196 == LINE)) {
2197 container->obj.alignmentMarginY = INTER_TAG_VALUE_MARGIN;
2198 }
2199 else {
2200 container->obj.alignmentMarginY = PRE_TAG_VALUE_MARGIN;
2201 }
2202 }
2203 container->obj.alignment = NO_ALIGNMENT;
2204
2205 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2206 }
2207
2208 return 0;
2209}
2210
2224 const char *text,
2225 const char *subText,
2226 uint8_t percentage)
2227{
2228 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2229 nbgl_container_t *container;
2230 nbgl_text_area_t *textArea;
2231 nbgl_progress_bar_t *progress;
2232
2233 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressBar():\n");
2234 if (layout == NULL) {
2235 return -1;
2236 }
2237
2238 // First Create Container :
2239 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2240 // progressbar + text + subText
2241 container->nbChildren = (subText != NULL) ? 3 : 2;
2242 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
2243
2244 container->obj.area.width = AVAILABLE_WIDTH;
2245 container->layout = VERTICAL;
2246 container->obj.alignment = CENTER;
2247
2248 // Create progressbar
2249 progress = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
2250 progress->foregroundColor = BLACK;
2251 progress->withBorder = true;
2252 progress->state = percentage;
2253 progress->obj.area.width = PROGRESSBAR_WIDTH;
2254 progress->obj.area.height = PROGRESSBAR_HEIGHT;
2255 progress->obj.alignment = TOP_MIDDLE;
2256
2257 // set this new progressbar as child of the container
2258 container->children[0] = (nbgl_obj_t *) progress;
2259
2260 // update container height
2261 container->obj.area.height = progress->obj.alignmentMarginY + progress->obj.area.height;
2262
2263 // create text area
2264 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2265 textArea->textColor = BLACK;
2266 textArea->text = PIC(text);
2267 textArea->textAlignment = CENTER;
2268 textArea->fontId = LARGE_MEDIUM_FONT;
2269 textArea->wrapping = true;
2270 textArea->obj.alignmentMarginY = BAR_TEXT_MARGIN;
2271 textArea->obj.alignTo = (nbgl_obj_t *) progress;
2272 textArea->obj.alignment = BOTTOM_MIDDLE;
2273 textArea->obj.area.width = AVAILABLE_WIDTH;
2274 textArea->obj.area.height = nbgl_getTextHeightInWidth(
2275 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
2276 textArea->style = NO_STYLE;
2277
2278 // update container height
2279 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
2280
2281 // set this text as child of the container
2282 container->children[1] = (nbgl_obj_t *) textArea;
2283
2284 if (subText != NULL) {
2285 nbgl_text_area_t *subTextArea;
2286 // create sub-text area
2287 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2288 subTextArea->textColor = BLACK;
2289 subTextArea->text = PIC(subText);
2290 subTextArea->textAlignment = CENTER;
2291 subTextArea->fontId = SMALL_REGULAR_FONT;
2292 subTextArea->wrapping = true;
2293 subTextArea->obj.alignmentMarginY = BAR_INTER_TEXTS_MARGIN;
2294 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
2295 subTextArea->obj.alignment = BOTTOM_MIDDLE;
2296 subTextArea->obj.area.width = AVAILABLE_WIDTH;
2297 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
2298 subTextArea->text,
2299 subTextArea->obj.area.width,
2300 subTextArea->wrapping);
2301 subTextArea->style = NO_STYLE;
2302
2303 // update container height
2304 container->obj.area.height
2305 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
2306
2307 // set thissub-text as child of the container
2308 container->children[2] = (nbgl_obj_t *) subTextArea;
2309 }
2310 // The height of containers must be a multiple of eight
2311 container->obj.area.height = (container->obj.area.height + 7) & 0xFFF8;
2312
2313 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2314
2315 return 2;
2316}
2317
2325{
2326 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2327 nbgl_line_t *line;
2328
2329 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSeparationLine():\n");
2330 line = createHorizontalLine(layoutInt->layer);
2331 line->obj.alignmentMarginY = -1;
2332 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
2333 return 0;
2334}
2335
2344{
2345 layoutObj_t *obj;
2346 nbgl_button_t *button;
2347 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2348
2349 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddButton():\n");
2350 if (layout == NULL) {
2351 return -1;
2352 }
2353
2354 // Add in footer if matching
2355 if ((buttonInfo->onBottom) && (!buttonInfo->fittingContent)) {
2356 if (layoutInt->footerContainer == NULL) {
2357 nbgl_layoutFooter_t footerDesc;
2358 footerDesc.type = FOOTER_SIMPLE_BUTTON;
2359 footerDesc.separationLine = false;
2360 footerDesc.button.text = buttonInfo->text;
2361 footerDesc.button.token = buttonInfo->token;
2362 footerDesc.button.tuneId = buttonInfo->tuneId;
2363 footerDesc.button.icon = buttonInfo->icon;
2364 footerDesc.button.style = buttonInfo->style;
2365 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2366 }
2367 else {
2368 nbgl_layoutUpFooter_t upFooterDesc;
2369 upFooterDesc.type = UP_FOOTER_BUTTON;
2370 upFooterDesc.button.text = buttonInfo->text;
2371 upFooterDesc.button.token = buttonInfo->token;
2372 upFooterDesc.button.tuneId = buttonInfo->tuneId;
2373 upFooterDesc.button.icon = buttonInfo->icon;
2374 upFooterDesc.button.style = buttonInfo->style;
2375 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2376 }
2377 }
2378
2379 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2381 layoutInt, (nbgl_obj_t *) button, buttonInfo->token, buttonInfo->tuneId);
2382 if (obj == NULL) {
2383 return -1;
2384 }
2385
2386 button->obj.alignmentMarginX = BORDER_MARGIN;
2387 button->obj.alignmentMarginY = 12;
2388 button->obj.alignment = NO_ALIGNMENT;
2389 if (buttonInfo->style == BLACK_BACKGROUND) {
2390 button->innerColor = BLACK;
2391 button->foregroundColor = WHITE;
2392 }
2393 else {
2394 button->innerColor = WHITE;
2395 button->foregroundColor = BLACK;
2396 }
2397 if (buttonInfo->style == NO_BORDER) {
2398 button->borderColor = WHITE;
2399 }
2400 else {
2401 if (buttonInfo->style == BLACK_BACKGROUND) {
2402 button->borderColor = BLACK;
2403 }
2404 else {
2405 button->borderColor = LIGHT_GRAY;
2406 }
2407 }
2408 button->text = PIC(buttonInfo->text);
2409 button->fontId = SMALL_BOLD_FONT;
2410 button->icon = PIC(buttonInfo->icon);
2411 if (buttonInfo->fittingContent == true) {
2412 button->obj.area.width = nbgl_getTextWidth(button->fontId, button->text)
2413 + SMALL_BUTTON_HEIGHT
2414 + ((button->icon) ? (button->icon->width + 12) : 0);
2415 button->obj.area.height = SMALL_BUTTON_HEIGHT;
2416 button->radius = SMALL_BUTTON_RADIUS_INDEX;
2417 if (buttonInfo->onBottom != true) {
2418 button->obj.alignmentMarginX += (AVAILABLE_WIDTH - button->obj.area.width) / 2;
2419 }
2420 }
2421 else {
2422 button->obj.area.width = AVAILABLE_WIDTH;
2423 button->obj.area.height = BUTTON_DIAMETER;
2424 button->radius = BUTTON_RADIUS;
2425 }
2426 button->obj.alignTo = NULL;
2427 button->obj.touchMask = (1 << TOUCHED);
2428 button->obj.touchId = (buttonInfo->fittingContent) ? EXTRA_BUTTON_ID : SINGLE_BUTTON_ID;
2429 // set this new button as child of the container
2430 layoutAddObject(layoutInt, (nbgl_obj_t *) button);
2431
2432 return 0;
2433}
2434
2445 const char *text,
2446 uint8_t token,
2447 tune_index_e tuneId)
2448{
2450 .longPress.text = text,
2451 .longPress.token = token,
2452 .longPress.tuneId = tuneId};
2453
2454 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLongPressButton():\n");
2455 if (layout == NULL) {
2456 return -1;
2457 }
2458
2459 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2460}
2461
2473 const char *text,
2474 uint8_t token,
2475 tune_index_e tuneId)
2476{
2477 nbgl_layoutFooter_t footerDesc;
2478 footerDesc.type = FOOTER_SIMPLE_TEXT;
2479 footerDesc.separationLine = true;
2480 footerDesc.simpleText.text = text;
2481 footerDesc.simpleText.mutedOut = false;
2482 footerDesc.simpleText.token = token;
2483 footerDesc.simpleText.tuneId = tuneId;
2484 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2485}
2486
2500 const char *leftText,
2501 uint8_t leftToken,
2502 const char *rightText,
2503 uint8_t rightToken,
2504 tune_index_e tuneId)
2505{
2506 nbgl_layoutFooter_t footerDesc;
2507 footerDesc.type = FOOTER_DOUBLE_TEXT;
2508 footerDesc.separationLine = true;
2509 footerDesc.doubleText.leftText = leftText;
2510 footerDesc.doubleText.leftToken = leftToken;
2511 footerDesc.doubleText.rightText = rightText;
2512 footerDesc.doubleText.rightToken = rightToken;
2513 footerDesc.doubleText.tuneId = tuneId;
2514 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2515}
2516
2526{
2527 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2528 layoutObj_t *obj;
2529 nbgl_text_area_t *textArea = NULL;
2530 nbgl_line_t *line = NULL;
2531 nbgl_image_t *image = NULL;
2532 nbgl_button_t *button = NULL;
2533
2534 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHeader(): type = %d\n", headerDesc->type);
2535 if (layout == NULL) {
2536 return -1;
2537 }
2538 if ((headerDesc == NULL) || (headerDesc->type >= NB_HEADER_TYPES)) {
2539 return -2;
2540 }
2541
2542 layoutInt->headerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2543 layoutInt->headerContainer->obj.area.width = SCREEN_WIDTH;
2544 layoutInt->headerContainer->layout = VERTICAL;
2545 layoutInt->headerContainer->children
2546 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2547 layoutInt->headerContainer->obj.alignment = TOP_MIDDLE;
2548
2549 switch (headerDesc->type) {
2550 case HEADER_EMPTY: {
2551 layoutInt->headerContainer->obj.area.height = headerDesc->emptySpace.height;
2552 break;
2553 }
2556 case HEADER_EXTENDED_BACK: {
2557 const char *text = (headerDesc->type == HEADER_EXTENDED_BACK)
2558 ? PIC(headerDesc->extendedBack.text)
2559 : PIC(headerDesc->backAndText.text);
2560 uint8_t backToken = (headerDesc->type == HEADER_EXTENDED_BACK)
2561 ? headerDesc->extendedBack.backToken
2562 : headerDesc->backAndText.token;
2563 nbgl_button_t *actionButton = NULL;
2564 // add back button
2565 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2566 // only make it active if valid token
2567 if (backToken != NBGL_INVALID_TOKEN) {
2568 obj = layoutAddCallbackObj(layoutInt,
2569 (nbgl_obj_t *) button,
2570 backToken,
2571 (headerDesc->type == HEADER_EXTENDED_BACK)
2572 ? headerDesc->extendedBack.tuneId
2573 : headerDesc->backAndText.tuneId);
2574 if (obj == NULL) {
2575 return -1;
2576 }
2577 }
2578
2579 button->obj.alignment = MID_LEFT;
2580 button->innerColor = WHITE;
2581 button->foregroundColor = (backToken != NBGL_INVALID_TOKEN) ? BLACK : WHITE;
2582 button->borderColor = WHITE;
2583 button->obj.area.width = BACK_KEY_WIDTH;
2584 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2585 button->text = NULL;
2586 button->icon = PIC(&LEFT_ARROW_ICON);
2587 button->obj.touchMask = (backToken != NBGL_INVALID_TOKEN) ? (1 << TOUCHED) : 0;
2588 button->obj.touchId = BACK_BUTTON_ID;
2589 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2590 = (nbgl_obj_t *) button;
2591 layoutInt->headerContainer->nbChildren++;
2592
2593 // add optional text if needed
2594 if (text != NULL) {
2595 // add optional icon if type is HEADER_BACK_ICON_AND_TEXT
2596 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2597 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2598 image->buffer = PIC(headerDesc->backAndText.icon);
2599 image->foregroundColor = BLACK;
2600 image->obj.alignment = CENTER;
2601 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2602 = (nbgl_obj_t *) image;
2603 layoutInt->headerContainer->nbChildren++;
2604 }
2605 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2606 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2607 && (headerDesc->extendedBack.textToken != NBGL_INVALID_TOKEN)) {
2608 obj = layoutAddCallbackObj(layoutInt,
2609 (nbgl_obj_t *) textArea,
2610 headerDesc->extendedBack.textToken,
2611 headerDesc->extendedBack.tuneId);
2612 if (obj == NULL) {
2613 return -1;
2614 }
2615 textArea->obj.touchMask = (1 << TOUCHED);
2616 }
2617 textArea->obj.alignment = CENTER;
2618 textArea->textColor = BLACK;
2619 textArea->obj.area.width
2620 = layoutInt->headerContainer->obj.area.width - 2 * BACK_KEY_WIDTH;
2621 // if icon, reduce to 1 line and fit text width
2622 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2623 textArea->obj.area.width -= 16 + image->buffer->width;
2624 }
2625 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2626 textArea->text = text;
2627 textArea->fontId = SMALL_BOLD_FONT;
2628 textArea->textAlignment = CENTER;
2629 textArea->wrapping = true;
2630 uint8_t nbMaxLines = (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) ? 1 : 2;
2631 // ensure that text fits on 2 lines maximum
2632 if (nbgl_getTextNbLinesInWidth(textArea->fontId,
2633 textArea->text,
2634 textArea->obj.area.width,
2635 textArea->wrapping)
2636 > nbMaxLines) {
2637 textArea->obj.area.height
2638 = nbMaxLines * nbgl_getFontLineHeight(textArea->fontId);
2639#ifndef BUILD_SCREENSHOTS
2641 "nbgl_layoutAddHeader: text [%s] is too long for header\n",
2642 text);
2643#endif // BUILD_SCREENSHOTS
2644 }
2645 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2646 textArea->obj.area.width = nbgl_getTextWidth(textArea->fontId, textArea->text);
2647 }
2648 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2649 = (nbgl_obj_t *) textArea;
2650 layoutInt->headerContainer->nbChildren++;
2651 // if icon, realign icon & text
2652 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2653 textArea->obj.alignmentMarginX = 8 + image->buffer->width / 2;
2654 image->obj.alignmentMarginX = -8 - textArea->obj.area.width / 2;
2655 }
2656 }
2657 // add action key if the type is HEADER_EXTENDED_BACK
2658 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2659 && (headerDesc->extendedBack.actionIcon)) {
2660 actionButton = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2661 // if token is valid
2662 if (headerDesc->extendedBack.actionToken != NBGL_INVALID_TOKEN) {
2663 obj = layoutAddCallbackObj(layoutInt,
2664 (nbgl_obj_t *) actionButton,
2665 headerDesc->extendedBack.actionToken,
2666 headerDesc->extendedBack.tuneId);
2667 if (obj == NULL) {
2668 return -1;
2669 }
2670 actionButton->obj.touchMask = (1 << TOUCHED);
2671 }
2672
2673 actionButton->obj.alignment = MID_RIGHT;
2674 actionButton->innerColor = WHITE;
2675 button->foregroundColor
2677 : LIGHT_GRAY;
2678 actionButton->borderColor = WHITE;
2679 actionButton->obj.area.width = BACK_KEY_WIDTH;
2680 actionButton->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2681 actionButton->text = NULL;
2682 actionButton->icon = PIC(headerDesc->extendedBack.actionIcon);
2683 actionButton->obj.touchId = EXTRA_BUTTON_ID;
2684 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2685 = (nbgl_obj_t *) actionButton;
2686 layoutInt->headerContainer->nbChildren++;
2687 }
2688
2689 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2690 // add potential text under the line if the type is HEADER_EXTENDED_BACK
2691 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2692 && (headerDesc->extendedBack.subText != NULL)) {
2693 nbgl_text_area_t *subTextArea;
2694
2695 line = createHorizontalLine(layoutInt->layer);
2696 line->obj.alignment = TOP_MIDDLE;
2697 line->obj.alignmentMarginY = TOUCHABLE_HEADER_BAR_HEIGHT;
2698 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2699 = (nbgl_obj_t *) line;
2700 layoutInt->headerContainer->nbChildren++;
2701
2702 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2703 subTextArea->textColor = BLACK;
2704 subTextArea->text = PIC(headerDesc->extendedBack.subText);
2705 subTextArea->textAlignment = MID_LEFT;
2706 subTextArea->fontId = SMALL_REGULAR_FONT;
2707 subTextArea->wrapping = true;
2708 subTextArea->obj.alignment = BOTTOM_MIDDLE;
2709 subTextArea->obj.alignmentMarginY = SUB_HEADER_MARGIN;
2710 subTextArea->obj.area.width = AVAILABLE_WIDTH;
2711 subTextArea->obj.area.height
2712 = nbgl_getTextHeightInWidth(subTextArea->fontId,
2713 subTextArea->text,
2714 subTextArea->obj.area.width,
2715 subTextArea->wrapping);
2716 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2717 = (nbgl_obj_t *) subTextArea;
2718 layoutInt->headerContainer->nbChildren++;
2719 layoutInt->headerContainer->obj.area.height
2720 += subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN;
2722 if (button != NULL) {
2723 button->obj.alignmentMarginY
2724 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2725 }
2726 if (textArea != NULL) {
2727 textArea->obj.alignmentMarginY
2728 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2729 }
2730 if (actionButton != NULL) {
2731 actionButton->obj.alignmentMarginY
2732 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2733 }
2734 }
2735 break;
2736 }
2738#ifdef TARGET_STAX
2739 // add optional back button
2740 if (headerDesc->progressAndBack.withBack) {
2741 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2742 obj = layoutAddCallbackObj(layoutInt,
2743 (nbgl_obj_t *) button,
2744 headerDesc->progressAndBack.token,
2745 headerDesc->progressAndBack.tuneId);
2746 if (obj == NULL) {
2747 return -1;
2748 }
2749
2750 button->obj.alignment = MID_LEFT;
2751 button->innerColor = WHITE;
2752 button->foregroundColor = BLACK;
2753 button->borderColor = WHITE;
2754 button->obj.area.width = BACK_KEY_WIDTH;
2755 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2756 button->text = NULL;
2757 button->icon = PIC(&LEFT_ARROW_ICON);
2758 button->obj.touchMask = (1 << TOUCHED);
2759 button->obj.touchId = BACK_BUTTON_ID;
2760 // add to container
2761 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2762 = (nbgl_obj_t *) button;
2763 layoutInt->headerContainer->nbChildren++;
2764 }
2765
2766 // add progress indicator
2767 if (headerDesc->progressAndBack.nbPages > 1
2769 nbgl_page_indicator_t *progress;
2770
2771 progress
2773 progress->activePage = headerDesc->progressAndBack.activePage;
2774 progress->nbPages = headerDesc->progressAndBack.nbPages;
2775 progress->obj.area.width = 224;
2776 progress->obj.alignment = CENTER;
2777 // add to container
2778 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2779 = (nbgl_obj_t *) progress;
2780 layoutInt->headerContainer->nbChildren++;
2781 }
2782 layoutInt->activePage = headerDesc->progressAndBack.activePage;
2783 layoutInt->nbPages = headerDesc->progressAndBack.nbPages;
2784 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2785#endif // TARGET_STAX
2786 break;
2787 }
2788 case HEADER_TITLE: {
2789 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2790 textArea->textColor = BLACK;
2791 textArea->obj.area.width = AVAILABLE_WIDTH;
2792 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2793 textArea->text = PIC(headerDesc->title.text);
2794 textArea->fontId = SMALL_BOLD_FONT;
2795 textArea->textAlignment = CENTER;
2796 textArea->wrapping = true;
2797 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2798 = (nbgl_obj_t *) textArea;
2799 layoutInt->headerContainer->nbChildren++;
2800 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2801 break;
2802 }
2803 case HEADER_RIGHT_TEXT: {
2804 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2805 obj = layoutAddCallbackObj(layoutInt,
2806 (nbgl_obj_t *) textArea,
2807 headerDesc->rightText.token,
2808 headerDesc->rightText.tuneId);
2809 if (obj == NULL) {
2810 return -1;
2811 }
2812 textArea->obj.alignment = MID_RIGHT;
2813 textArea->obj.alignmentMarginX = BORDER_MARGIN;
2814 textArea->textColor = BLACK;
2815 textArea->obj.area.width = AVAILABLE_WIDTH;
2816 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2817 textArea->text = PIC(headerDesc->rightText.text);
2818 textArea->fontId = SMALL_BOLD_FONT;
2819 textArea->textAlignment = MID_RIGHT;
2820 textArea->obj.touchMask = (1 << TOUCHED);
2821 textArea->obj.touchId = TOP_RIGHT_BUTTON_ID;
2822 // add to bottom container
2823 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2824 = (nbgl_obj_t *) textArea;
2825 layoutInt->headerContainer->nbChildren++;
2826 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2827 break;
2828 }
2829 default:
2830 return -2;
2831 }
2832 // draw separation line at bottom of container
2833 if (headerDesc->separationLine) {
2834 line = createHorizontalLine(layoutInt->layer);
2835 line->obj.alignment = BOTTOM_MIDDLE;
2836 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2837 = (nbgl_obj_t *) line;
2838 layoutInt->headerContainer->nbChildren++;
2839 }
2840 // header must be the first child
2841 layoutInt->children[HEADER_INDEX] = (nbgl_obj_t *) layoutInt->headerContainer;
2842
2843 // subtract header height from main container height
2844 layoutInt->container->obj.area.height -= layoutInt->headerContainer->obj.area.height;
2845 layoutInt->container->obj.alignTo = (nbgl_obj_t *) layoutInt->headerContainer;
2846 layoutInt->container->obj.alignment = BOTTOM_LEFT;
2847
2848 layoutInt->headerType = headerDesc->type;
2849
2850 return layoutInt->headerContainer->obj.area.height;
2851}
2852
2862{
2863 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2864 layoutObj_t *obj;
2865 nbgl_text_area_t *textArea;
2866 nbgl_line_t *line, *separationLine = NULL;
2867 nbgl_button_t *button;
2868
2869 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddExtendedFooter():\n");
2870 if (layout == NULL) {
2871 return -1;
2872 }
2873 if ((footerDesc == NULL) || (footerDesc->type >= NB_FOOTER_TYPES)) {
2874 return -2;
2875 }
2876
2877 layoutInt->footerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2878 layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
2879 layoutInt->footerContainer->layout = VERTICAL;
2880 layoutInt->footerContainer->children
2881 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2882 layoutInt->footerContainer->obj.alignment = BOTTOM_MIDDLE;
2883
2884 switch (footerDesc->type) {
2885 case FOOTER_EMPTY: {
2886 layoutInt->footerContainer->obj.area.height = footerDesc->emptySpace.height;
2887 break;
2888 }
2889 case FOOTER_SIMPLE_TEXT: {
2890 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2891 obj = layoutAddCallbackObj(layoutInt,
2892 (nbgl_obj_t *) textArea,
2893 footerDesc->simpleText.token,
2894 footerDesc->simpleText.tuneId);
2895 if (obj == NULL) {
2896 return -1;
2897 }
2898
2899 textArea->obj.alignment = BOTTOM_MIDDLE;
2900 textArea->textColor = (footerDesc->simpleText.mutedOut) ? LIGHT_TEXT_COLOR : BLACK;
2901 textArea->obj.area.width = AVAILABLE_WIDTH;
2902 textArea->obj.area.height
2903 = (footerDesc->simpleText.mutedOut) ? SMALL_FOOTER_HEIGHT : SIMPLE_FOOTER_HEIGHT;
2904 textArea->text = PIC(footerDesc->simpleText.text);
2905 textArea->fontId
2906 = (footerDesc->simpleText.mutedOut) ? SMALL_REGULAR_FONT : SMALL_BOLD_FONT;
2907 textArea->textAlignment = CENTER;
2908 textArea->obj.touchMask = (1 << TOUCHED);
2909 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2910 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2911 = (nbgl_obj_t *) textArea;
2912 layoutInt->footerContainer->nbChildren++;
2913 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2914 break;
2915 }
2916 case FOOTER_DOUBLE_TEXT: {
2917 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2918 obj = layoutAddCallbackObj(layoutInt,
2919 (nbgl_obj_t *) textArea,
2920 footerDesc->doubleText.leftToken,
2921 footerDesc->doubleText.tuneId);
2922 if (obj == NULL) {
2923 return -1;
2924 }
2925 textArea->obj.alignment = BOTTOM_LEFT;
2926 textArea->textColor = BLACK;
2927 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2928 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2929 textArea->text = PIC(footerDesc->doubleText.leftText);
2930 textArea->fontId = SMALL_BOLD_FONT;
2931 textArea->textAlignment = CENTER;
2932 textArea->obj.touchMask = (1 << TOUCHED);
2933 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2934 // add to bottom container
2935 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2936 = (nbgl_obj_t *) textArea;
2937 layoutInt->footerContainer->nbChildren++;
2938
2939 // create right touchable text
2940 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2941 obj = layoutAddCallbackObj(layoutInt,
2942 (nbgl_obj_t *) textArea,
2943 footerDesc->doubleText.rightToken,
2944 footerDesc->doubleText.tuneId);
2945 if (obj == NULL) {
2946 return -1;
2947 }
2948
2949 textArea->obj.alignment = BOTTOM_RIGHT;
2950 textArea->textColor = BLACK;
2951 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2952 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2953 textArea->text = PIC(footerDesc->doubleText.rightText);
2954 textArea->fontId = SMALL_BOLD_FONT;
2955 textArea->textAlignment = CENTER;
2956 textArea->obj.touchMask = (1 << TOUCHED);
2957 textArea->obj.touchId = RIGHT_BUTTON_ID;
2958 // add to bottom container
2959 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2960 = (nbgl_obj_t *) textArea;
2961 layoutInt->footerContainer->nbChildren++;
2962 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2963
2964 // create vertical line separating texts
2965 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
2966 separationLine->lineColor = LIGHT_GRAY;
2967 separationLine->obj.area.width = 1;
2968 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
2969 separationLine->direction = VERTICAL;
2970 separationLine->thickness = 1;
2971 separationLine->obj.alignment = MID_LEFT;
2972 separationLine->obj.alignTo = (nbgl_obj_t *) textArea;
2973 separationLine->obj.alignmentMarginX = -1;
2974 break;
2975 }
2976 case FOOTER_TEXT_AND_NAV: {
2977 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2978 // add touchable text on the left
2979 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2980 obj = layoutAddCallbackObj(layoutInt,
2981 (nbgl_obj_t *) textArea,
2982 footerDesc->textAndNav.token,
2983 footerDesc->textAndNav.tuneId);
2984 if (obj == NULL) {
2985 return -1;
2986 }
2987 textArea->obj.alignment = BOTTOM_LEFT;
2988 textArea->textColor = BLACK;
2989 textArea->obj.area.width = FOOTER_TEXT_AND_NAV_WIDTH;
2990 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2991 textArea->text = PIC(footerDesc->textAndNav.text);
2992 textArea->fontId = SMALL_BOLD_FONT;
2993 textArea->textAlignment = CENTER;
2994 textArea->obj.touchMask = (1 << TOUCHED);
2995 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2996 // add to bottom container
2997 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2998 = (nbgl_obj_t *) textArea;
2999 layoutInt->footerContainer->nbChildren++;
3000
3001 // add navigation on the right
3002 nbgl_container_t *navContainer
3004 navContainer->obj.area.width = AVAILABLE_WIDTH;
3005 navContainer->layout = VERTICAL;
3006 navContainer->nbChildren = 4;
3007 navContainer->children
3008 = (nbgl_obj_t **) nbgl_containerPoolGet(navContainer->nbChildren, layoutInt->layer);
3009 navContainer->obj.alignment = BOTTOM_RIGHT;
3010 navContainer->obj.area.width = SCREEN_WIDTH - textArea->obj.area.width;
3011 navContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3012 layoutNavigationPopulate(navContainer, &footerDesc->navigation, layoutInt->layer);
3013 obj = layoutAddCallbackObj(layoutInt,
3014 (nbgl_obj_t *) navContainer,
3015 footerDesc->textAndNav.navigation.token,
3016 footerDesc->textAndNav.navigation.tuneId);
3017 if (obj == NULL) {
3018 return -1;
3019 }
3020
3021 // create vertical line separating text from nav
3022 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
3023 separationLine->lineColor = LIGHT_GRAY;
3024 separationLine->obj.area.width = 1;
3025 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
3026 separationLine->direction = VERTICAL;
3027 separationLine->thickness = 1;
3028 separationLine->obj.alignment = MID_LEFT;
3029 separationLine->obj.alignTo = (nbgl_obj_t *) navContainer;
3030 separationLine->obj.alignmentMarginX = -1;
3031
3032 layoutInt->activePage = footerDesc->textAndNav.navigation.activePage;
3033 layoutInt->nbPages = footerDesc->textAndNav.navigation.nbPages;
3034 // add to bottom container
3035 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3036 = (nbgl_obj_t *) navContainer;
3037 layoutInt->footerContainer->nbChildren++;
3038 break;
3039 }
3040 case FOOTER_NAV: {
3041 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3043 layoutInt->footerContainer, &footerDesc->navigation, layoutInt->layer);
3044 layoutInt->footerContainer->nbChildren = 4;
3045 obj = layoutAddCallbackObj(layoutInt,
3046 (nbgl_obj_t *) layoutInt->footerContainer,
3047 footerDesc->navigation.token,
3048 footerDesc->navigation.tuneId);
3049 if (obj == NULL) {
3050 return -1;
3051 }
3052
3053 layoutInt->activePage = footerDesc->navigation.activePage;
3054 layoutInt->nbPages = footerDesc->navigation.nbPages;
3055 break;
3056 }
3057 case FOOTER_SIMPLE_BUTTON: {
3058 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3059 obj = layoutAddCallbackObj(layoutInt,
3060 (nbgl_obj_t *) button,
3061 footerDesc->button.token,
3062 footerDesc->button.tuneId);
3063 if (obj == NULL) {
3064 return -1;
3065 }
3066
3067 button->obj.alignment = BOTTOM_MIDDLE;
3068 button->obj.alignmentMarginY = SINGLE_BUTTON_MARGIN;
3069 if (footerDesc->button.style == BLACK_BACKGROUND) {
3070 button->innerColor = BLACK;
3071 button->foregroundColor = WHITE;
3072 }
3073 else {
3074 button->innerColor = WHITE;
3075 button->foregroundColor = BLACK;
3076 }
3077
3078 if (footerDesc->button.style == NO_BORDER) {
3079 button->borderColor = WHITE;
3080 }
3081 else {
3082 if (footerDesc->button.style == BLACK_BACKGROUND) {
3083 button->borderColor = BLACK;
3084 }
3085 else {
3086 button->borderColor = LIGHT_GRAY;
3087 }
3088 }
3089 button->text = PIC(footerDesc->button.text);
3090 button->fontId = SMALL_BOLD_FONT;
3091 button->icon = PIC(footerDesc->button.icon);
3092 button->radius = BUTTON_RADIUS;
3093 button->obj.area.height = BUTTON_DIAMETER;
3094 layoutInt->footerContainer->obj.area.height = FOOTER_BUTTON_HEIGHT;
3095 if (footerDesc->button.text == NULL) {
3096 button->obj.area.width = BUTTON_DIAMETER;
3097 }
3098 else {
3099 button->obj.area.width = AVAILABLE_WIDTH;
3100 }
3101 button->obj.touchMask = (1 << TOUCHED);
3102 button->obj.touchId = button->text ? SINGLE_BUTTON_ID : BOTTOM_BUTTON_ID;
3103 // add to bottom container
3104 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3105 = (nbgl_obj_t *) button;
3106 layoutInt->footerContainer->nbChildren++;
3107 break;
3108 }
3109 case FOOTER_CHOICE_BUTTONS: {
3110 // texts cannot be NULL
3111 if ((footerDesc->choiceButtons.bottomText == NULL)
3112 || (footerDesc->choiceButtons.topText == NULL)) {
3113 return -1;
3114 }
3115
3116 // create bottom button (footer) at first
3117 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3118 obj = layoutAddCallbackObj(layoutInt,
3119 (nbgl_obj_t *) button,
3120 footerDesc->choiceButtons.token,
3121 footerDesc->choiceButtons.tuneId);
3122 if (obj == NULL) {
3123 return -1;
3124 }
3125 // associate with with index 1
3126 obj->index = 1;
3127 // put at the bottom of the container
3128 button->obj.alignment = BOTTOM_MIDDLE;
3129 button->innerColor = WHITE;
3130 if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3131 button->obj.alignmentMarginY
3132 = SINGLE_BUTTON_MARGIN; // pixels from bottom of container
3133 button->borderColor = LIGHT_GRAY;
3134 button->obj.area.height = BUTTON_DIAMETER;
3135 }
3136 else {
3137 button->obj.alignmentMarginY
3138 = BUTTON_FROM_BOTTOM_MARGIN; // pixels from screen bottom
3139 button->borderColor = WHITE;
3140 button->obj.area.height = FOOTER_IN_PAIR_HEIGHT;
3141 }
3142 button->foregroundColor = BLACK;
3143 button->obj.area.width = AVAILABLE_WIDTH;
3144 button->radius = BUTTON_RADIUS;
3145 button->text = PIC(footerDesc->choiceButtons.bottomText);
3146 button->fontId = SMALL_BOLD_FONT;
3147 button->obj.touchMask = (1 << TOUCHED);
3148 button->obj.touchId = CHOICE_2_ID;
3149 // add to bottom container
3150 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3151 = (nbgl_obj_t *) button;
3152 layoutInt->footerContainer->nbChildren++;
3153
3154 // add line if needed
3155 if ((footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3156 && (footerDesc->choiceButtons.style != BOTH_ROUNDED_STYLE)) {
3157 line = createHorizontalLine(layoutInt->layer);
3158 line->obj.alignment = TOP_MIDDLE;
3159 line->obj.alignTo = (nbgl_obj_t *) button;
3160 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3161 = (nbgl_obj_t *) line;
3162 layoutInt->footerContainer->nbChildren++;
3163 }
3164
3165 // then top button, on top of it
3166 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3167 obj = layoutAddCallbackObj(layoutInt,
3168 (nbgl_obj_t *) button,
3169 footerDesc->choiceButtons.token,
3170 footerDesc->choiceButtons.tuneId);
3171 if (obj == NULL) {
3172 return -1;
3173 }
3174 // associate with with index 0
3175 obj->index = 0;
3176 button->obj.alignment = TOP_MIDDLE;
3177 if (footerDesc->choiceButtons.style == ROUNDED_AND_FOOTER_STYLE) {
3178 button->obj.alignmentMarginY = TOP_BUTTON_MARGIN; // pixels from top of container
3179 }
3180 else if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3181 button->obj.alignmentMarginY
3182 = SINGLE_BUTTON_MARGIN; // pixels from top of container
3183 }
3184 else {
3185 button->obj.alignmentMarginY
3186 = TOP_BUTTON_MARGIN_WITH_ACTION; // pixels from top of container
3187 }
3189 button->innerColor = WHITE;
3190 button->borderColor = LIGHT_GRAY;
3191 button->foregroundColor = BLACK;
3192 }
3193 else {
3194 button->innerColor = BLACK;
3195 button->borderColor = BLACK;
3196 button->foregroundColor = WHITE;
3197 }
3198 button->obj.area.width = AVAILABLE_WIDTH;
3199 button->obj.area.height = BUTTON_DIAMETER;
3200 button->radius = BUTTON_RADIUS;
3201 button->text = PIC(footerDesc->choiceButtons.topText);
3202 button->icon = (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3203 ? PIC(footerDesc->choiceButtons.topIcon)
3204 : NULL;
3205 button->fontId = SMALL_BOLD_FONT;
3206 button->obj.touchMask = (1 << TOUCHED);
3207 button->obj.touchId = CHOICE_1_ID;
3208 // add to bottom container
3209 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3210 = (nbgl_obj_t *) button;
3211 layoutInt->footerContainer->nbChildren++;
3212
3213 if (footerDesc->choiceButtons.style == ROUNDED_AND_FOOTER_STYLE) {
3214 layoutInt->footerContainer->obj.area.height = ROUNDED_AND_FOOTER_FOOTER_HEIGHT;
3215 }
3216 else if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3217 layoutInt->footerContainer->obj.area.height = BOTH_ROUNDED_FOOTER_HEIGHT;
3218 }
3219 else {
3220 layoutInt->footerContainer->obj.area.height = ACTION_AND_FOOTER_FOOTER_HEIGHT;
3221 }
3222
3223 break;
3224 }
3225 default:
3226 return -2;
3227 }
3228
3229 // add swipable feature for navigation
3230 if ((footerDesc->type == FOOTER_NAV) || (footerDesc->type == FOOTER_TEXT_AND_NAV)) {
3231 addSwipeInternal(layoutInt,
3232 ((1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT)),
3234 (footerDesc->type == FOOTER_NAV) ? footerDesc->navigation.token
3235 : footerDesc->textAndNav.navigation.token,
3236 (footerDesc->type == FOOTER_NAV)
3237 ? footerDesc->navigation.tuneId
3238 : footerDesc->textAndNav.navigation.tuneId);
3239 }
3240
3241 if (footerDesc->separationLine) {
3242 line = createHorizontalLine(layoutInt->layer);
3243 line->obj.alignment = TOP_MIDDLE;
3244 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3245 = (nbgl_obj_t *) line;
3246 layoutInt->footerContainer->nbChildren++;
3247 }
3248 if (separationLine != NULL) {
3249 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3250 = (nbgl_obj_t *) separationLine;
3251 layoutInt->footerContainer->nbChildren++;
3252 }
3253
3254 layoutInt->children[FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->footerContainer;
3255
3256 // subtract footer height from main container height
3257 layoutInt->container->obj.area.height -= layoutInt->footerContainer->obj.area.height;
3258
3259 layoutInt->footerType = footerDesc->type;
3260
3261 return layoutInt->footerContainer->obj.area.height;
3262}
3263
3273{
3274 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3275 layoutObj_t *obj;
3276 nbgl_text_area_t *textArea;
3277 nbgl_line_t *line;
3278 nbgl_button_t *button;
3279
3280 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddUpFooter():\n");
3281 if (layout == NULL) {
3282 return -1;
3283 }
3284 if ((upFooterDesc == NULL) || (upFooterDesc->type >= NB_UP_FOOTER_TYPES)) {
3285 return -2;
3286 }
3287
3288 layoutInt->upFooterContainer
3290 layoutInt->upFooterContainer->obj.area.width = SCREEN_WIDTH;
3291 layoutInt->upFooterContainer->layout = VERTICAL;
3292 // maximum 4 children for long press button
3293 layoutInt->upFooterContainer->children
3294 = (nbgl_obj_t **) nbgl_containerPoolGet(4, layoutInt->layer);
3295 layoutInt->upFooterContainer->obj.alignTo = (nbgl_obj_t *) layoutInt->container;
3296 layoutInt->upFooterContainer->obj.alignment = BOTTOM_MIDDLE;
3297
3298 switch (upFooterDesc->type) {
3299 case UP_FOOTER_LONG_PRESS: {
3300 nbgl_progress_bar_t *progressBar;
3301
3302 obj = layoutAddCallbackObj(layoutInt,
3303 (nbgl_obj_t *) layoutInt->upFooterContainer,
3304 upFooterDesc->longPress.token,
3305 upFooterDesc->longPress.tuneId);
3306 if (obj == NULL) {
3307 return -1;
3308 }
3309 layoutInt->upFooterContainer->nbChildren = 4;
3310 layoutInt->upFooterContainer->obj.area.height = LONG_PRESS_BUTTON_HEIGHT;
3311 layoutInt->upFooterContainer->obj.touchId = LONG_PRESS_BUTTON_ID;
3312 layoutInt->upFooterContainer->obj.touchMask
3313 = ((1 << TOUCHING) | (1 << TOUCH_RELEASED) | (1 << OUT_OF_TOUCH)
3314 | (1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT));
3315
3316 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3317 button->obj.alignmentMarginX = BORDER_MARGIN;
3318 button->obj.alignment = MID_RIGHT;
3319 button->innerColor = BLACK;
3320 button->foregroundColor = WHITE;
3321 button->borderColor = BLACK;
3322 button->obj.area.width = BUTTON_DIAMETER;
3323 button->obj.area.height = BUTTON_DIAMETER;
3324 button->radius = BUTTON_RADIUS;
3325 button->icon = PIC(&VALIDATE_ICON);
3326 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3327
3328 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3329 textArea->textColor = BLACK;
3330 textArea->text = PIC(upFooterDesc->longPress.text);
3331 textArea->textAlignment = MID_LEFT;
3332 textArea->fontId = LARGE_MEDIUM_FONT;
3333 textArea->wrapping = true;
3334 textArea->obj.area.width = SCREEN_WIDTH - 3 * BORDER_MARGIN - button->obj.area.width;
3335 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3336 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3337 textArea->style = NO_STYLE;
3338 textArea->obj.alignment = MID_LEFT;
3339 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3340 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) textArea;
3341
3342 line = createHorizontalLine(layoutInt->layer);
3343 line->obj.alignment = TOP_MIDDLE;
3344 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) line;
3345
3346 progressBar = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
3347 progressBar->obj.area.width = SCREEN_WIDTH;
3348 progressBar->obj.area.height = LONG_PRESS_PROGRESS_HEIGHT;
3349 progressBar->obj.alignment = TOP_MIDDLE;
3350 progressBar->obj.alignmentMarginY = LONG_PRESS_PROGRESS_ALIGN;
3351 progressBar->resetIfOverriden = true;
3352 progressBar->partialRedraw = true;
3353 layoutInt->upFooterContainer->children[3] = (nbgl_obj_t *) progressBar;
3354 break;
3355 }
3356 case UP_FOOTER_BUTTON: {
3357 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3358 obj = layoutAddCallbackObj(layoutInt,
3359 (nbgl_obj_t *) button,
3360 upFooterDesc->button.token,
3361 upFooterDesc->button.tuneId);
3362 if (obj == NULL) {
3363 return -1;
3364 }
3365
3366 layoutInt->upFooterContainer->nbChildren = 1;
3367 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3368 button->obj.alignment = CENTER;
3369
3370 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3371 button->innerColor = BLACK;
3372 button->foregroundColor = WHITE;
3373 }
3374 else {
3375 button->innerColor = WHITE;
3376 button->foregroundColor = BLACK;
3377 }
3378 if (upFooterDesc->button.style == NO_BORDER) {
3379 button->borderColor = WHITE;
3380 }
3381 else {
3382 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3383 button->borderColor = BLACK;
3384 }
3385 else {
3386 button->borderColor = LIGHT_GRAY;
3387 }
3388 }
3389 button->text = PIC(upFooterDesc->button.text);
3390 button->fontId = SMALL_BOLD_FONT;
3391 button->icon = PIC(upFooterDesc->button.icon);
3392 button->obj.area.width = AVAILABLE_WIDTH;
3393 button->obj.area.height = BUTTON_DIAMETER;
3394 button->radius = BUTTON_RADIUS;
3395
3396 button->obj.alignTo = NULL;
3397 button->obj.touchMask = (1 << TOUCHED);
3398 button->obj.touchId = SINGLE_BUTTON_ID;
3399 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3400 break;
3401 }
3403 // icon & text cannot be NULL
3404 if ((upFooterDesc->horizontalButtons.leftIcon == NULL)
3405 || (upFooterDesc->horizontalButtons.rightText == NULL)) {
3406 return -1;
3407 }
3408
3409 layoutInt->upFooterContainer->nbChildren = 2;
3410 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3411
3412 // create left button (in white) at first
3413 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3414 obj = layoutAddCallbackObj(layoutInt,
3415 (nbgl_obj_t *) button,
3416 upFooterDesc->horizontalButtons.leftToken,
3417 upFooterDesc->horizontalButtons.tuneId);
3418 if (obj == NULL) {
3419 return -1;
3420 }
3421 // associate with with index 1
3422 obj->index = 1;
3423 button->obj.alignment = MID_LEFT;
3424 button->obj.alignmentMarginX = BORDER_MARGIN;
3425 button->borderColor = LIGHT_GRAY;
3426 button->innerColor = WHITE;
3427 button->foregroundColor = BLACK;
3428 button->obj.area.width = BUTTON_WIDTH;
3429 button->obj.area.height = BUTTON_DIAMETER;
3430 button->radius = BUTTON_RADIUS;
3431 button->icon = PIC(upFooterDesc->horizontalButtons.leftIcon);
3432 button->fontId = SMALL_BOLD_FONT;
3433 button->obj.touchMask = (1 << TOUCHED);
3434 button->obj.touchId = CHOICE_2_ID;
3435 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3436
3437 // then black button, on right
3438 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3439 obj = layoutAddCallbackObj(layoutInt,
3440 (nbgl_obj_t *) button,
3441 upFooterDesc->horizontalButtons.rightToken,
3442 upFooterDesc->horizontalButtons.tuneId);
3443 if (obj == NULL) {
3444 return -1;
3445 }
3446 // associate with with index 0
3447 obj->index = 0;
3448 button->obj.alignment = MID_RIGHT;
3449 button->obj.alignmentMarginX = BORDER_MARGIN;
3450 button->innerColor = BLACK;
3451 button->borderColor = BLACK;
3452 button->foregroundColor = WHITE;
3453 button->obj.area.width = AVAILABLE_WIDTH - BUTTON_WIDTH - LEFT_CONTENT_ICON_TEXT_X;
3454 button->obj.area.height = BUTTON_DIAMETER;
3455 button->radius = BUTTON_RADIUS;
3456 button->text = PIC(upFooterDesc->horizontalButtons.rightText);
3457 button->fontId = SMALL_BOLD_FONT;
3458 button->obj.touchMask = (1 << TOUCHED);
3459 button->obj.touchId = CHOICE_1_ID;
3460 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) button;
3461 break;
3462 }
3463 case UP_FOOTER_TIP_BOX: {
3464 // text cannot be NULL
3465 if (upFooterDesc->tipBox.text == NULL) {
3466 return -1;
3467 }
3468 obj = layoutAddCallbackObj(layoutInt,
3469 (nbgl_obj_t *) layoutInt->upFooterContainer,
3470 upFooterDesc->tipBox.token,
3471 upFooterDesc->tipBox.tuneId);
3472 if (obj == NULL) {
3473 return -1;
3474 }
3475 layoutInt->upFooterContainer->nbChildren = 3;
3476 layoutInt->upFooterContainer->obj.touchId = TIP_BOX_ID;
3477 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3478
3479 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3480 textArea->textColor = BLACK;
3481 textArea->text = PIC(upFooterDesc->tipBox.text);
3482 textArea->textAlignment = MID_LEFT;
3483 textArea->fontId = SMALL_REGULAR_FONT;
3484 textArea->wrapping = true;
3485 textArea->obj.area.width = AVAILABLE_WIDTH;
3486 if (upFooterDesc->tipBox.icon != NULL) {
3487 textArea->obj.area.width
3488 -= ((nbgl_icon_details_t *) PIC(upFooterDesc->tipBox.icon))->width
3489 + TIP_BOX_TEXT_ICON_MARGIN;
3490 }
3491 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3492 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3493 textArea->obj.alignment = MID_LEFT;
3494 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3495 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3496 layoutInt->upFooterContainer->obj.area.height = textArea->obj.area.height;
3497
3498 line = createHorizontalLine(layoutInt->layer);
3499 line->obj.alignment = TOP_MIDDLE;
3500 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) line;
3501
3502 if (upFooterDesc->tipBox.icon != NULL) {
3503 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
3504 image->obj.alignmentMarginX = BORDER_MARGIN;
3505 image->obj.alignment = MID_RIGHT;
3506 image->foregroundColor = BLACK;
3507 image->buffer = PIC(upFooterDesc->tipBox.icon);
3508 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) image;
3509 if (layoutInt->upFooterContainer->obj.area.height < image->buffer->height) {
3510 layoutInt->upFooterContainer->obj.area.height = image->buffer->height;
3511 }
3512 }
3513 layoutInt->upFooterContainer->obj.area.height += 2 * TIP_BOX_MARGIN_Y;
3514
3515 break;
3516 }
3517 case UP_FOOTER_TEXT: {
3518 obj = layoutAddCallbackObj(layoutInt,
3519 (nbgl_obj_t *) layoutInt->upFooterContainer,
3520 upFooterDesc->text.token,
3521 upFooterDesc->text.tuneId);
3522 if (obj == NULL) {
3523 return -1;
3524 }
3525 layoutInt->upFooterContainer->nbChildren = 1;
3526 layoutInt->upFooterContainer->obj.area.height = SMALL_FOOTER_HEIGHT;
3527 layoutInt->upFooterContainer->obj.touchId = WHOLE_SCREEN_ID;
3528 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3529
3530 // only create text_area if text is not empty
3531 if (strlen(PIC(upFooterDesc->text.text))) {
3532 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3533 textArea->textColor = LIGHT_TEXT_COLOR;
3534 textArea->text = PIC(upFooterDesc->text.text);
3535 textArea->textAlignment = CENTER;
3536 textArea->fontId = SMALL_REGULAR_FONT;
3537 textArea->wrapping = true;
3538 textArea->obj.area.width = AVAILABLE_WIDTH;
3539 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3540 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3541 textArea->obj.alignment = CENTER;
3542 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3543 }
3544 break;
3545 }
3546 default:
3547 return -2;
3548 }
3549
3550 // subtract up footer height from main container height
3551 layoutInt->container->obj.area.height -= layoutInt->upFooterContainer->obj.area.height;
3552
3553 layoutInt->children[UP_FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->upFooterContainer;
3554
3555 layoutInt->upFooterType = upFooterDesc->type;
3556
3557 return layoutInt->upFooterContainer->obj.area.height;
3558}
3559
3574 uint8_t activePage,
3575 uint8_t nbPages,
3576 bool withBack,
3577 uint8_t backToken,
3578 tune_index_e tuneId)
3579{
3581 .separationLine = false,
3582 .progressAndBack.activePage = activePage,
3583 .progressAndBack.nbPages = nbPages,
3584 .progressAndBack.token = backToken,
3585 .progressAndBack.tuneId = tuneId,
3586 .progressAndBack.withBack = withBack,
3587 .progressAndBack.actionIcon = NULL,
3588 .progressAndBack.actionToken = NBGL_INVALID_TOKEN};
3589 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressIndicator():\n");
3590
3591 return nbgl_layoutAddHeader(layout, &headerDesc);
3592}
3593
3605 const char *text,
3606 const char *subText,
3607 uint8_t initPosition)
3608{
3609 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3610 nbgl_container_t *container;
3611 nbgl_text_area_t *textArea;
3612 nbgl_spinner_t *spinner;
3613
3614 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSpinner():\n");
3615 if (layout == NULL) {
3616 return -1;
3617 }
3618
3619 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
3620 // spinner + text + subText
3621 container->nbChildren = 3;
3622 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
3623
3624 container->obj.area.width = AVAILABLE_WIDTH;
3625 container->layout = VERTICAL;
3626 container->obj.alignment = CENTER;
3627
3628 // create spinner
3629 spinner = (nbgl_spinner_t *) nbgl_objPoolGet(SPINNER, layoutInt->layer);
3630 spinner->position = initPosition;
3631 spinner->obj.alignment = TOP_MIDDLE;
3632 // set this new spinner as child of the container
3633 container->children[0] = (nbgl_obj_t *) spinner;
3634
3635 // update container height
3636 container->obj.area.height += SPINNER_HEIGHT;
3637
3638 // create text area
3639 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3640 textArea->textColor = BLACK;
3641 textArea->text = PIC(text);
3642 textArea->textAlignment = CENTER;
3643 textArea->fontId = (subText != NULL) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
3644 textArea->wrapping = true;
3645 textArea->obj.alignmentMarginY = SPINNER_TEXT_MARGIN;
3646 textArea->obj.alignTo = (nbgl_obj_t *) spinner;
3647 textArea->obj.alignment = BOTTOM_MIDDLE;
3648 textArea->obj.area.width = AVAILABLE_WIDTH;
3649 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3650 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3651 textArea->style = NO_STYLE;
3652
3653 // update container height
3654 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
3655
3656 // set this text as child of the container
3657 container->children[1] = (nbgl_obj_t *) textArea;
3658
3659 if (subText != NULL) {
3660 nbgl_text_area_t *subTextArea;
3661 // create sub-text area
3662 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3663 subTextArea->textColor = BLACK;
3664 subTextArea->text = PIC(subText);
3665 subTextArea->textAlignment = CENTER;
3666 subTextArea->fontId = SMALL_REGULAR_FONT;
3667 subTextArea->wrapping = true;
3668 subTextArea->obj.alignmentMarginY = SPINNER_INTER_TEXTS_MARGIN;
3669 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
3670 subTextArea->obj.alignment = BOTTOM_MIDDLE;
3671 subTextArea->obj.area.width = AVAILABLE_WIDTH;
3672 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
3673 subTextArea->text,
3674 subTextArea->obj.area.width,
3675 subTextArea->wrapping);
3676 subTextArea->style = NO_STYLE;
3677
3678 // update container height
3679 container->obj.area.height
3680 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
3681
3682 // set thissub-text as child of the container
3683 container->children[2] = (nbgl_obj_t *) subTextArea;
3684 }
3685 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
3686
3687 if (initPosition != SPINNER_FIXED) {
3688 // update ticker to update the spinner periodically
3690
3691 tickerCfg.tickerIntervale = SPINNER_REFRESH_PERIOD; // ms
3692 tickerCfg.tickerValue = SPINNER_REFRESH_PERIOD; // ms
3693 tickerCfg.tickerCallback = &spinnerTickerCallback;
3694 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
3695 }
3696
3697 return 0;
3698}
3699
3712 const char *text,
3713 const char *subText,
3714 uint8_t position)
3715{
3716 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3717 nbgl_container_t *container;
3718 nbgl_text_area_t *textArea;
3719 nbgl_spinner_t *spinner;
3720 int ret = 0;
3721
3722 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutUpdateSpinner():\n");
3723 if ((layout == NULL) || (layoutInt->container->nbChildren == 0)) {
3724 return -1;
3725 }
3726
3727 container = (nbgl_container_t *) layoutInt->container->children[0];
3728 if ((container->obj.type != CONTAINER) || (container->nbChildren < 2)) {
3729 return -1;
3730 }
3731
3732 spinner = (nbgl_spinner_t *) container->children[0];
3733 if (spinner->obj.type != SPINNER) {
3734 return -1;
3735 }
3736 // if position is different, redraw
3737 if (spinner->position != position) {
3738 spinner->position = position;
3739 nbgl_objDraw((nbgl_obj_t *) spinner);
3740 ret = 1;
3741 }
3742
3743 // update text area if necessary
3744 textArea = (nbgl_text_area_t *) container->children[1];
3745 if (textArea->obj.type != TEXT_AREA) {
3746 return -1;
3747 }
3748 const char *newText = PIC(text);
3749 size_t newTextLen = strlen(newText);
3750 // if text is different, redraw (don't use strcmp because it crashes with Rust SDK)
3751 if ((newTextLen != strlen(textArea->text)) || memcmp(textArea->text, newText, newTextLen)) {
3752 textArea->text = newText;
3753 nbgl_objDraw((nbgl_obj_t *) textArea);
3754 ret = 2;
3755 }
3756
3757 if (subText != NULL) {
3758 nbgl_text_area_t *subTextArea;
3759
3760 if (container->nbChildren != 3) {
3761 return -1;
3762 }
3763 subTextArea = (nbgl_text_area_t *) container->children[2];
3764 if (subTextArea->obj.type != TEXT_AREA) {
3765 return -1;
3766 }
3767 const char *newSubText = PIC(subText);
3768 size_t newSubTextLen = strlen(newSubText);
3769 // if text is different, redraw
3770 if ((newSubTextLen != strlen(subTextArea->text))
3771 || memcmp(subTextArea->text, newSubText, newSubTextLen)) {
3772 subTextArea->text = newSubText;
3773 nbgl_objDraw((nbgl_obj_t *) subTextArea);
3774 ret = 2;
3775 }
3776 }
3777
3778 return ret;
3779}
3780
3788{
3789 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3790
3791 if (layout == NULL) {
3792 return -1;
3793 }
3794 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutDraw(): layout->isUsed = %d\n", layout->isUsed);
3795 if (layout->tapText) {
3796 // set this new container as child of main container
3797 layoutAddObject(layout, (nbgl_obj_t *) layout->tapText);
3798 }
3799#ifdef TARGET_STAX
3800 if (layout->withLeftBorder == true) {
3801 // draw now the line
3802 nbgl_line_t *line = createLeftVerticalLine(layout->layer);
3803 layout->children[LEFT_BORDER_INDEX] = (nbgl_obj_t *) line;
3804 }
3805#endif // TARGET_STAX
3807
3808 return 0;
3809}
3810
3818{
3819 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3820 LOG_DEBUG(PAGE_LOGGER, "nbgl_layoutRelease(): \n");
3821 if ((layout == NULL) || (!layout->isUsed)) {
3822 return -1;
3823 }
3824 // if modal
3825 if (layout->modal) {
3826 nbgl_screenPop(layout->layer);
3827 // if this layout was on top, use its bottom layout as top
3828 if (layout == topLayout) {
3829 topLayout = layout->bottom;
3830 topLayout->top = NULL;
3831 }
3832 else {
3833 // otherwise connect top to bottom
3834 layout->bottom->top = layout->top;
3835 layout->top->bottom = layout->bottom;
3836 }
3837 }
3838 layout->isUsed = false;
3839 return 0;
3840}
3841
3842#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
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
nbgl_font_id_e
Definition nbgl_fonts.h:132
uint8_t nbgl_getFontHeight(nbgl_font_id_e fontId)
uint16_t nbgl_getTextWidth(nbgl_font_id_e fontId, const char *text)
uint16_t nbgl_getTextHeightInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
uint16_t nbgl_getTextNbLinesInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
const nbgl_font_t * nbgl_getFont(nbgl_font_id_e fontId)
uint8_t nbgl_getFontLineHeight(nbgl_font_id_e fontId)
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.
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:43
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_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
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_layoutInvertBackground(nbgl_layout_t *layout)
Inverts the background color (black instead of white)
int nbgl_layoutAddSwipe(nbgl_layout_t *layout, uint16_t swipesMask, const char *text, uint8_t token, tune_index_e tuneId)
Creates a swipe interaction on the main container.
int nbgl_layoutAddSwitch(nbgl_layout_t *layout, const nbgl_layoutSwitch_t *switchLayout)
Creates a switch with the given text and its state.
int nbgl_layoutAddHorizontalButtons(nbgl_layout_t *layout, const nbgl_layoutHorizontalButtons_t *info)
Creates two buttons to make a choice. Both buttons are mandatory The left one contains only an icon a...
listItemType_t
@ NB_ITEM_TYPES
@ TOUCHABLE_BAR_ITEM
@ TEXT_ITEM
@ SWITCH_ITEM
int nbgl_layoutAddButton(nbgl_layout_t *layout, const nbgl_layoutButton_t *buttonInfo)
Creates a rounded button in the main container.
nbgl_layout_t * nbgl_layoutGet(const nbgl_layoutDescription_t *description)
returns a layout of the given type. The layout is reset
int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_t *footerDesc)
Creates a touchable area at the footer of the screen, containing various controls,...
int nbgl_layoutAddLeftContent(nbgl_layout_t *layout, const nbgl_layoutLeftContent_t *info)
Creates an area with a title, and rows of icon + text, left aligned.
#define NB_MAX_LAYOUTS
Definition nbgl_layout.c:40
int nbgl_layoutAddChoiceButtons(nbgl_layout_t *layout, const nbgl_layoutChoiceButtons_t *info)
Creates two buttons to make a choice. Both buttons are mandatory. Both buttons are full width,...
int nbgl_layoutRelease(nbgl_layout_t *layoutParam)
Release the layout obtained with nbgl_layoutGet()
int nbgl_layoutAddSpinner(nbgl_layout_t *layout, const char *text, const char *subText, uint8_t initPosition)
Creates a centered (vertically & horizontally) spinner with a text under it.
int nbgl_layoutUpdateSpinner(nbgl_layout_t *layout, const char *text, const char *subText, uint8_t position)
Update an existing spinner (must be the only object of the layout)
int nbgl_layoutAddLongPressButton(nbgl_layout_t *layout, const char *text, uint8_t token, tune_index_e tuneId)
Creates a long press button in the main container.
void layoutAddObject(nbgl_layoutInternal_t *layout, nbgl_obj_t *obj)
adds the given obj to the main container
int nbgl_layoutAddProgressBar(nbgl_layout_t *layout, const char *text, const char *subText, uint8_t percentage)
Creates an area in main panel to display a progress bar, with a title text and a subtext if needed.
int nbgl_layoutAddHeader(nbgl_layout_t *layout, const nbgl_layoutHeader_t *headerDesc)
Creates a touchable (or not) area at the header of the screen, containing various controls,...
int nbgl_layoutAddBottomButton(nbgl_layout_t *layout, const nbgl_icon_details_t *icon, uint8_t token, bool separationLine, tune_index_e tuneId)
Creates a centered button at bottom of main container.
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
@ 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:30
#define SPINNER_FIXED
position to use for a "fixed" spinner
@ SOFT_ACTION_AND_FOOTER_STYLE
A white button on top of a footer, with a separation line.
@ BOTH_ROUNDED_STYLE
A black button on top of a white button.
@ ROUNDED_AND_FOOTER_STYLE
A black background button on top of a footer.
#define NBGL_NO_PROGRESS_INDICATOR
To be used when a control token shall not be used.
Definition nbgl_layout.h:27
#define NBGL_NO_TUNE
Definition nbgl_layout.h:26
@ 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
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
@ UP_FOOTER_INDEX
@ FOOTER_INDEX
@ HEADER_INDEX
@ TOP_RIGHT_BUTTON_INDEX
@ MAIN_CONTAINER_INDEX
@ LEFT_BORDER_INDEX
@ NB_MAX_SCREEN_CHILDREN
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:264
#define LIGHT_TEXT_COLOR
Definition nbgl_obj.h:282
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:346
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)
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)
#define SWIPE_MASK
Definition nbgl_obj.h:292
nbgl_obj_t * nbgl_objPoolGet(nbgl_obj_type_t type, uint8_t layer)
void nbgl_refreshSpecial(nbgl_refresh_mode_t mode)
struct PACKED__ nbgl_image_s nbgl_image_t
struct to represent an image (IMAGE type)
#define INACTIVE_SMALL_FONT
Definition nbgl_obj.h:281
struct PACKED__ nbgl_button_s nbgl_button_t
struct to represent a button (BUTTON type) that can contain a text and/or an icon
#define INACTIVE_COLOR
Definition nbgl_obj.h:279
#define INACTIVE_TEXT_COLOR
Definition nbgl_obj.h:280
void nbgl_refreshSpecialWithPostRefresh(nbgl_refresh_mode_t mode, nbgl_post_refresh_t post_refresh)
@ CHOICE_1_ID
Definition nbgl_obj.h:680
@ TOP_RIGHT_BUTTON_ID
Definition nbgl_obj.h:676
@ LONG_PRESS_BUTTON_ID
Definition nbgl_obj.h:689
@ CONTROLS_ID
Definition nbgl_obj.h:691
@ RIGHT_BUTTON_ID
Definition nbgl_obj.h:674
@ WHOLE_SCREEN_ID
Definition nbgl_obj.h:675
@ BOTTOM_BUTTON_ID
Definition nbgl_obj.h:672
@ SINGLE_BUTTON_ID
Definition nbgl_obj.h:678
@ BACK_BUTTON_ID
Definition nbgl_obj.h:677
@ VALUE_BUTTON_1_ID
Definition nbgl_obj.h:686
@ TIP_BOX_ID
Definition nbgl_obj.h:690
@ EXTRA_BUTTON_ID
Definition nbgl_obj.h:679
@ CHOICE_2_ID
Definition nbgl_obj.h:681
#define BUTTON_DIAMETER
Definition nbgl_obj.h:99
struct PACKED__ nbgl_container_s nbgl_container_t
struct to represent a container (CONTAINER type)
#define BUTTON_WIDTH
Definition nbgl_obj.h:104
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)
int nbgl_screenPush(nbgl_obj_t ***elements, uint8_t nbElements, const nbgl_screenTickerConfiguration_t *ticker, nbgl_touchCallback_t touchCallback)
void nbgl_wait_pipeline(void)
int nbgl_screenUpdateTicker(uint8_t screenIndex, const nbgl_screenTickerConfiguration_t *ticker)
int nbgl_screenPop(uint8_t screenIndex)
struct PACKED__ nbgl_screenTickerConfiguration_s nbgl_screenTickerConfiguration_t
struct to configure a screen layer
int nbgl_screenUpdateBackgroundColor(uint8_t screenIndex, color_t color)
void nbgl_screenRedraw(void)
uint32_t nbgl_touchGetTouchDuration(nbgl_obj_t *obj)
color_t
Definition nbgl_types.h:140
@ WHITE
Definition nbgl_types.h:144
@ LIGHT_GRAY
Definition nbgl_types.h:143
@ BLACK
Definition nbgl_types.h:141
nbgl_state_t
to represent a boolean state.
Definition nbgl_types.h:199
@ ON_STATE
Definition nbgl_types.h:201
@ OFF_STATE
Definition nbgl_types.h:200
@ POST_REFRESH_FORCE_POWER_OFF
Force screen power off after refresh.
Definition nbgl_types.h:352
@ POST_REFRESH_FORCE_POWER_ON
Force screen power on after refresh.
Definition nbgl_types.h:353
@ POST_REFRESH_FORCE_POWER_ON_WITH_PIPELINE
Force screen power on and enable pipeline.
Definition nbgl_types.h:354
nbgl_touchType_t
The different types of Touchscreen events.
Definition nbgl_types.h:259
@ SWIPED_LEFT
Definition nbgl_types.h:274
@ SWIPED_UP
Definition nbgl_types.h:271
@ SWIPED_DOWN
Definition nbgl_types.h:272
@ SWIPED_RIGHT
Definition nbgl_types.h:273
@ TOUCH_RELEASED
Definition nbgl_types.h:269
@ TOUCHED
Definition nbgl_types.h:260
@ TOUCHING
corresponding to an object that is currently touched
Definition nbgl_types.h:264
@ OUT_OF_TOUCH
Definition nbgl_types.h:265
@ QRCODE_V10
QRCode V10, can encode text len up to 1500 chars, display size = 228*228.
Definition nbgl_types.h:231
@ QRCODE_V4_SMALL
QRCode V4, can encode text len up to 1500 chars, display size = 132*132.
Definition nbgl_types.h:232
@ QRCODE_V4
QRCode V4, can encode text len up to 62 chars, display size = 264*264.
Definition nbgl_types.h:230
@ VERTICAL
from top to bottom
Definition nbgl_types.h:209
@ HORIZONTAL
from left to right
Definition nbgl_types.h:210
@ LOOP_PARSING
0, 1, 2, 0, 1, 2, ...
Definition nbgl_types.h:408
struct PACKED__ nbgl_icon_details_s nbgl_icon_details_t
Represents all information about an icon.
@ TOP_MIDDLE
Definition nbgl_types.h:182
@ CENTER
Definition nbgl_types.h:185
@ BOTTOM_RIGHT
Definition nbgl_types.h:189
@ TOP_LEFT
Definition nbgl_types.h:181
@ NO_ALIGNMENT
used when parent container layout is used
Definition nbgl_types.h:180
@ BOTTOM_LEFT
Definition nbgl_types.h:187
@ MID_RIGHT
Definition nbgl_types.h:186
@ RIGHT_TOP
on outside right
Definition nbgl_types.h:192
@ TOP_RIGHT
Definition nbgl_types.h:183
@ MID_LEFT
Definition nbgl_types.h:184
@ BOTTOM_MIDDLE
Definition nbgl_types.h:188
@ IMAGE
Bitmap (y and height must be multiple of 4 on Stax)
Definition nbgl_types.h:157
@ SWITCH
Switch to turn on/off something.
Definition nbgl_types.h:161
@ RADIO_BUTTON
Indicator to inform whether something is on or off.
Definition nbgl_types.h:164
@ SPINNER
Spinner.
Definition nbgl_types.h:168
@ BUTTON
Rounded rectangle button with icon and/or text.
Definition nbgl_types.h:160
@ PROGRESS_BAR
horizontal bar to indicate progression of something (between 0% and 100%)
Definition nbgl_types.h:163
@ QR_CODE
QR Code.
Definition nbgl_types.h:165
@ PAGE_INDICATOR
horizontal bar to indicate position within pages
Definition nbgl_types.h:162
@ LINE
Vertical or Horizontal line.
Definition nbgl_types.h:158
@ CONTAINER
Empty container.
Definition nbgl_types.h:156
@ TEXT_AREA
Area to contain text line(s)
Definition nbgl_types.h:159
#define MAX(x, y)
Definition nbgl_types.h:121
@ NBGL_BPP_1
1 bit per pixel
Definition nbgl_types.h:283
@ NO_STYLE
no border
Definition nbgl_types.h:218
@ BLACK_AND_WHITE_REFRESH
to be used for pure B&W area, when contrast is important
Definition nbgl_types.h:329
@ BLACK_AND_WHITE_FAST_REFRESH
to be used for pure B&W area, when contrast is not priority
Definition nbgl_types.h:330
@ FULL_COLOR_PARTIAL_REFRESH
to be used for small partial refresh (radio buttons, switches)
Definition nbgl_types.h:327
tune_index_e tuneId
uint8_t index
nbgl_state_t state
const char * text
const nbgl_icon_details_t * iconRight
tune_index_e tuneId
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:420
uint8_t nbIcons
number of icons in icons array
Definition nbgl_types.h:418
const nbgl_icon_details_t ** icons
array of nbIcons pointers on icons
Definition nbgl_types.h:417
nbgl_parsingType_t parsing
Definition nbgl_types.h:419
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...
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played when selecting a radio button)
uint8_t token
the token that will be used as argument of the callback
bool localized
if set to true, use nameIds and not names
uint8_t initChoice
index of the current choice
const char *const * names
array of strings giving the choices (nbChoices)
uint8_t nbChoices
number of choices
This structure contains info to build a switch (on the right) with a description (on the left),...
const char * text
main text for the switch
uint8_t token
the token that will be used as argument of the callback (unused on Nano)
nbgl_state_t initState
initial state of the switch
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played
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
nbgl_contentTagValueCallback_t callback
function to call to retrieve a given pair
uint8_t nbMaxLinesForValue
if > 0, set the max number of lines for value field.
bool hideEndOfLastLine
if set to true, replace 3 last chars of last line by "..."
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
This structure contains a [tag,value] pair and possible extensions.
const nbgl_icon_details_t * valueIcon
const char * value
string giving the value name
const char * item
string giving the tag name
const char * text
text of the tip-box
const nbgl_icon_details_t * icon
icon of the tip-box
uint8_t token
token used when tip-box is tapped
tune_index_e tuneId
tune played when tip-box is tapped
structure defining an ASCII font
Definition nbgl_fonts.h:77
uint8_t line_height
height of a line for all characters in pixels
Definition nbgl_fonts.h:82
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)
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played
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.
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played
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)
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played
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::@19::@24 textAndNav
if type is FOOTER_TEXT_AND_NAV
bool separationLine
if true, a separation line is added at the top of this control
const char * leftText
struct nbgl_layoutFooter_t::@19::@22 simpleText
if type is FOOTER_SIMPLE_TEXT
nbgl_layoutButton_t button
if type is FOOTER_SIMPLE_BUTTON
struct nbgl_layoutFooter_t::@19::@23 doubleText
if type is FOOTER_DOUBLE_TEXT
bool mutedOut
if true, text is displayed in gray
struct nbgl_layoutFooter_t::@19::@21 emptySpace
if type is FOOTER_EMPTY
nbgl_layoutFooterType_t type
type of footer
nbgl_layoutNavigationBar_t navigation
if type is FOOTER_NAV
const char * rightText
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::@11::@16 title
if type is HEADER_TITLE
struct nbgl_layoutHeader_t::@11::@18 rightText
if type is HEADER_RIGHT_TEXT
const char * subText
text under the line (can be NULL)
const nbgl_icon_details_t * actionIcon
right button icon
uint8_t actionToken
when optional right button is pressed
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::@11::@13 emptySpace
if type is HEADER_EMPTY
struct nbgl_layoutHeader_t::@11::@17 extendedBack
if type is HEADER_EXTENDED_BACK
uint8_t textToken
when text is touched
struct nbgl_layoutHeader_t::@11::@14 backAndText
if type is HEADER_BACK_ICON_AND_TEXT or HEADER_BACK_AND_TEXT
uint8_t token
when back key is pressed
struct nbgl_layoutHeader_t::@11::@15 progressAndBack
if type is HEADER_BACK_AND_PROGRESS
This structure contains info to build a pair of buttons, the small one, with icon,...
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played
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....)
uint8_t activePage
index of active page for navigation bar
uint8_t layer
layer in screen stack
struct nbgl_layoutInternal_s * top
layout above this one, in stack
nbgl_swipe_usage_t swipeUsage
nbgl_layoutTouchCallback_t callback
user callback for all controls
struct nbgl_layoutInternal_s * bottom
layout under this one, in stack
uint8_t invertedColors
if true, means that background is black
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...)
uint8_t isUsed
if true, means this layout is in use
nbgl_layoutFooterType_t footerType
type of footer
nbgl_layoutHeaderType_t headerType
type of header
nbgl_container_t * upFooterContainer
uint8_t incrementAnim
if true, means that animation index is currently incrementing
uint8_t nbPages
number of pages for navigation bar
nbgl_layoutUpFooterType_t upFooterType
type of up-footer
uint8_t modal
if true, means the screen is a modal
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).
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played when pressing keys)
bool withBackKey
if set to true, the "back" key is drawn
bool withExitKey
if set to true, an exit button is drawn (X on the left)
uint8_t token
the token that will be used as argument of the callback
uint8_t nbPages
number of pages. (if 0, no navigation)
This structure contains info to build a centered (vertically and horizontally) area,...
const char * text2
second text (can be null)
const char * text1
first text (can be null)
const char * url
URL for QR code.
bool largeText1
if set to true, use 32px font for text1
int16_t offsetY
vertical shift to apply to this info (if > 0, shift to bottom)
bool centered
if set to true, center vertically
This structure contains info to build an up-footer (area on top of footer).
nbgl_layoutButton_t button
if type is UP_FOOTER_BUTTON
tune_index_e tuneId
tune played when button is long-pressed
nbgl_contentTipBox_t tipBox
if type is UP_FOOTER_TIP_BOX
const char * text
text in the long-press button
nbgl_layoutUpFooterType_t type
type of up-footer
struct nbgl_layoutUpFooter_t::@25::@27 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