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