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