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