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 TOP_BUTTON_MARGIN_WITH_ACTION VERTICAL_BORDER_MARGIN
68#define SINGLE_BUTTON_MARGIN 24
69#define LONG_PRESS_PROGRESS_HEIGHT 8
70#define LONG_PRESS_PROGRESS_ALIGN 4
71#define TITLE_DESC_MARGIN 16
72#define LEFT_CONTENT_ICON_TEXT_X 16
73#define TIP_BOX_MARGIN_Y 24
74#elif defined(TARGET_FLEX)
75#define RADIO_CHOICE_HEIGHT 92
76#define BAR_INTERVALE 16
77#define FOOTER_BUTTON_HEIGHT 136
78#define FOOTER_IN_PAIR_HEIGHT 88
79#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 208
80#define FOOTER_TEXT_AND_NAV_WIDTH 192
81#define TAP_TO_CONTINUE_MARGIN 30
82#define SUB_HEADER_MARGIN 28
83#define PRE_FIRST_TEXT_MARGIN 0
84#define INTER_PARAGRAPHS_MARGIN 24
85#define PRE_TITLE_MARGIN 16
86#define PRE_DESCRIPTION_MARGIN 24
87#define PRE_FIRST_ROW_MARGIN 32
88#define INTER_ROWS_MARGIN 24
89#define QR_PRE_TEXT_MARGIN 24
90#define QR_INTER_TEXTS_MARGIN 28
91#define SPINNER_TEXT_MARGIN 24
92#define SPINNER_INTER_TEXTS_MARGIN 16
93#define BAR_TEXT_MARGIN 24
94#define BAR_INTER_TEXTS_MARGIN 16
95#define LEFT_CONTENT_TEXT_PADDING 4
96#define BUTTON_FROM_BOTTOM_MARGIN 4
97#define TOP_BUTTON_MARGIN VERTICAL_BORDER_MARGIN
98#define TOP_BUTTON_MARGIN_WITH_ACTION VERTICAL_BORDER_MARGIN
99#define SINGLE_BUTTON_MARGIN 24
100#define LONG_PRESS_PROGRESS_HEIGHT 8
101#define LONG_PRESS_PROGRESS_ALIGN 4
102#define TITLE_DESC_MARGIN 16
103#define LEFT_CONTENT_ICON_TEXT_X 16
104#define TIP_BOX_MARGIN_Y 24
105#elif defined(TARGET_APEX)
106#define RADIO_CHOICE_HEIGHT 68
107#define BAR_INTERVALE 8
108#define FOOTER_BUTTON_HEIGHT 72
109#define FOOTER_IN_PAIR_HEIGHT 60
110#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 128
111#define FOOTER_TEXT_AND_NAV_WIDTH 120
112#define TAP_TO_CONTINUE_MARGIN 30
113#define SUB_HEADER_MARGIN 16
114#define PRE_FIRST_TEXT_MARGIN 0
115#define INTER_PARAGRAPHS_MARGIN 16
116#define PRE_TITLE_MARGIN 16
117#define PRE_DESCRIPTION_MARGIN 12
118#define PRE_FIRST_ROW_MARGIN 24
119#define INTER_ROWS_MARGIN 12
120#define QR_PRE_TEXT_MARGIN 16
121#define QR_INTER_TEXTS_MARGIN 20
122#define SPINNER_TEXT_MARGIN 16
123#define SPINNER_INTER_TEXTS_MARGIN 16
124#define BAR_TEXT_MARGIN 16
125#define BAR_INTER_TEXTS_MARGIN 12
126#define LEFT_CONTENT_TEXT_PADDING 4
127#define BUTTON_FROM_BOTTOM_MARGIN 0
128#define TOP_BUTTON_MARGIN 12
129#define TOP_BUTTON_MARGIN_WITH_ACTION 0
130#define SINGLE_BUTTON_MARGIN 16
131#define LONG_PRESS_PROGRESS_HEIGHT 4
132#define LONG_PRESS_PROGRESS_ALIGN 4
133#define TITLE_DESC_MARGIN 12
134#define LEFT_CONTENT_ICON_TEXT_X 8
135#define TIP_BOX_MARGIN_Y 12
136#else // TARGETS
137#error Undefined target
138#endif // TARGETS
139
140// refresh period of the spinner, in ms
141#define SPINNER_REFRESH_PERIOD 400
142
143/**********************
144 * MACROS
145 **********************/
146
147/**********************
148 * TYPEDEFS
149 **********************/
150
157
158// used to build either a touchable bar or a switch
159typedef struct {
161 const nbgl_icon_details_t *iconLeft; // a buffer containing the 1BPP icon for icon on
162 // left (can be NULL)
163 const nbgl_icon_details_t *iconRight; // a buffer containing the 1BPP icon for icon 2 (can be
164 // NULL). Dimensions must be the same as iconLeft
165 const char *text; // text (can be NULL)
166 const char *subText; // sub text (can be NULL)
167 uint8_t token; // the token that will be used as argument of the callback
168 nbgl_state_t state; // state of the item
169 bool large; // set to true only for the main level of OS settings
170 uint8_t index; // index of obj (for callback)
171 tune_index_e tuneId; // if not @ref NBGL_NO_TUNE, a tune will be played
172} listItem_t;
173
174/**********************
175 * VARIABLES
176 **********************/
177
182static nbgl_layoutInternal_t gLayout[NB_MAX_LAYOUTS] = {0};
183
184// numbers of touchable controls for the whole page
185static uint8_t nbTouchableControls = 0;
186
187/**********************
188 * STATIC PROTOTYPES
189 **********************/
190// extern const char *get_ux_loc_string(UX_LOC_STRINGS_INDEX index);
191
192#ifdef HAVE_FAST_HOLD_TO_APPROVE
193// Unit step in % of touchable progress bar
194#define HOLD_TO_APPROVE_STEP_PERCENT (7)
195// Duration in ms the user must hold the progress bar
196// to make it progress HOLD_TO_APPROVE_STEP_PERCENT %.
197// This duration must be higher than the screen refresh duration.
198#define HOLD_TO_APPROVE_STEP_DURATION_MS (100)
199#else
200#define HOLD_TO_APPROVE_STEP_PERCENT (25)
201#define HOLD_TO_APPROVE_STEP_DURATION_MS (400)
202#endif // HAVE_FAST_HOLD_TO_APPROVE
203
204static inline uint8_t get_hold_to_approve_percent(uint32_t touch_duration)
205{
206#ifdef HAVE_FAST_HOLD_TO_APPROVE
207 uint8_t current_step_nb = (touch_duration / HOLD_TO_APPROVE_STEP_DURATION_MS);
208#else
209 uint8_t current_step_nb = (touch_duration / HOLD_TO_APPROVE_STEP_DURATION_MS) + 1;
210#endif
211 return (current_step_nb * HOLD_TO_APPROVE_STEP_PERCENT);
212}
213
214// function used to retrieve the concerned layout and layout obj matching the given touched obj
215static bool getLayoutAndLayoutObj(nbgl_obj_t *obj,
216 nbgl_layoutInternal_t **layout,
217 layoutObj_t **layoutObj)
218{
219 uint8_t i = NB_MAX_LAYOUTS;
220
221 // parse all layouts (starting with modals) to find the object
222 *layout = NULL;
223 while (i > 0) {
224 i--;
225 if (gLayout[i].nbChildren > 0) {
226 uint8_t j;
227
228 // search index of obj in this layout
229 for (j = 0; j < gLayout[i].nbUsedCallbackObjs; j++) {
230 if (obj == gLayout[i].callbackObjPool[j].obj) {
232 "getLayoutAndLayoutObj(): obj found in layout[%d], index = %d, "
233 "nbUsedCallbackObjs = %d\n",
234 i,
235 j,
236 gLayout[i].nbUsedCallbackObjs);
237 *layout = &gLayout[i];
238 *layoutObj = &(gLayout[i].callbackObjPool[j]);
239 return true;
240 }
241 }
242 }
243 }
244 // not found
245 return false;
246}
247
248static void radioTouchCallback(nbgl_obj_t *obj,
249 nbgl_touchType_t eventType,
250 nbgl_layoutInternal_t *layout);
251static void longTouchCallback(nbgl_obj_t *obj,
252 nbgl_touchType_t eventType,
253 nbgl_layoutInternal_t *layout,
254 layoutObj_t *layoutObj);
255
256// callback for most touched object
257static void touchCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType)
258{
259 nbgl_layoutInternal_t *layout;
260 layoutObj_t *layoutObj;
261 bool needRefresh = false;
262
263 if (obj == NULL) {
264 return;
265 }
266 LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): eventType = %d, obj = %p\n", eventType, obj);
267 if (getLayoutAndLayoutObj(obj, &layout, &layoutObj) == false) {
268 // try with parent, if existing
269 if (getLayoutAndLayoutObj(obj->parent, &layout, &layoutObj) == false) {
270 LOG_WARN(
272 "touchCallback(): eventType = %d, obj = %p, no active layout or obj not found\n",
273 eventType,
274 obj);
275 return;
276 }
277 }
278
279 // case of swipe
280 if (((eventType == SWIPED_UP) || (eventType == SWIPED_DOWN) || (eventType == SWIPED_LEFT)
281 || (eventType == SWIPED_RIGHT))
282 && (obj->type == CONTAINER)) {
283#ifdef NBGL_KEYBOARD
284 if (layout->swipeUsage == SWIPE_USAGE_SUGGESTIONS) {
285 keyboardSwipeCallback(obj, eventType);
286 return;
287 }
288#endif // NBGL_KEYBOARD
289 if (layout->swipeUsage == SWIPE_USAGE_CUSTOM) {
290 layoutObj->index = eventType;
291 }
292 else if ((layout->swipeUsage == SWIPE_USAGE_NAVIGATION)
293 && ((nbgl_obj_t *) layout->container == obj)) {
294 nbgl_container_t *navContainer;
295 if (layout->footerType == FOOTER_NAV) {
296 navContainer = (nbgl_container_t *) layout->footerContainer;
297 }
298 else if (layout->footerType == FOOTER_TEXT_AND_NAV) {
299 navContainer = (nbgl_container_t *) layout->footerContainer->children[1];
300 }
301 else {
302 return;
303 }
304
306 (nbgl_obj_t *) navContainer, eventType, layout->nbPages, &layout->activePage)
307 == false) {
308 // navigation was impossible
309 return;
310 }
311 layoutObj->index = layout->activePage;
312 }
313 }
314
315 // case of navigation bar
316 if (((obj->parent == (nbgl_obj_t *) layout->footerContainer)
317 && (layout->footerType == FOOTER_NAV))
318 || ((obj->parent->type == CONTAINER)
319 && (obj->parent->parent == (nbgl_obj_t *) layout->footerContainer)
320 && (layout->footerType == FOOTER_TEXT_AND_NAV))) {
321 if (layoutNavigationCallback(obj, eventType, layout->nbPages, &layout->activePage)
322 == false) {
323 // navigation was impossible
324 return;
325 }
326 layoutObj->index = layout->activePage;
327 }
328
329 // case of switch
330 if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren >= 2)
331 && (((nbgl_container_t *) obj)->children[1] != NULL)
332 && (((nbgl_container_t *) obj)->children[1]->type == SWITCH)) {
333 nbgl_switch_t *lSwitch = (nbgl_switch_t *) ((nbgl_container_t *) obj)->children[1];
334 lSwitch->state = (lSwitch->state == ON_STATE) ? OFF_STATE : ON_STATE;
335 nbgl_objDraw((nbgl_obj_t *) lSwitch);
336 // refresh will be done after tune playback
337 needRefresh = true;
338 // index is used for state
339 layoutObj->index = lSwitch->state;
340 }
341 // case of radio
342 else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 2)
343 && (((nbgl_container_t *) obj)->children[1] != NULL)
344 && (((nbgl_container_t *) obj)->children[1]->type == RADIO_BUTTON)) {
345 radioTouchCallback(obj, eventType, layout);
346 return;
347 }
348 // case of long press
349 else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 4)
350 && (((nbgl_container_t *) obj)->children[3] != NULL)
351 && (((nbgl_container_t *) obj)->children[3]->type == PROGRESS_BAR)) {
352 longTouchCallback(obj, eventType, layout, layoutObj);
353 return;
354 }
355 LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): layout->callback = %p\n", layout->callback);
356 if ((layout->callback != NULL) && (layoutObj->token != NBGL_INVALID_TOKEN)) {
357#ifdef HAVE_PIEZO_SOUND
358 if (layoutObj->tuneId < NBGL_NO_TUNE) {
359 os_io_seph_cmd_piezo_play_tune(layoutObj->tuneId);
360 }
361#endif // HAVE_PIEZO_SOUND
362 if (needRefresh) {
364 }
365 layout->callback(layoutObj->token, layoutObj->index);
366 }
367}
368
369// callback for long press button
370static void longTouchCallback(nbgl_obj_t *obj,
371 nbgl_touchType_t eventType,
372 nbgl_layoutInternal_t *layout,
373 layoutObj_t *layoutObj)
374{
375 nbgl_container_t *container = (nbgl_container_t *) obj;
376 // 4th child of container is the progress bar
377 nbgl_progress_bar_t *progressBar = (nbgl_progress_bar_t *) container->children[3];
378
380 "longTouchCallback(): eventType = %d, obj = %p, gLayout[1].nbChildren = %d\n",
381 eventType,
382 obj,
383 gLayout[1].nbChildren);
384
385 // case of pressing a long press button
386 if (eventType == TOUCHING) {
387 uint32_t touchDuration = nbgl_touchGetTouchDuration(obj);
388
389 // Compute the new progress bar state in %
390 uint8_t new_state = get_hold_to_approve_percent(touchDuration);
391
392 // Ensure the callback is triggered once,
393 // when the progress bar state reaches 100%
394 bool trigger_callback = (new_state >= 100) && (progressBar->state < 100);
395
396 // Cap progress bar state at 100%
397 if (new_state >= 100) {
398 new_state = 100;
399 }
400
401 // Update progress bar state
402 if (new_state != progressBar->state) {
403 progressBar->partialRedraw = true;
404 progressBar->state = new_state;
405
406 nbgl_objDraw((nbgl_obj_t *) progressBar);
407 // Ensure progress bar is fully drawn
408 // before calling the callback.
411 }
412
413 if (trigger_callback) {
414 // End of progress bar reached: trigger callback
415 if (layout->callback != NULL) {
416 layout->callback(layoutObj->token, layoutObj->index);
417 }
418 }
419 }
420 // case of releasing a long press button (or getting out of it)
421 else if ((eventType == TOUCH_RELEASED) || (eventType == OUT_OF_TOUCH)
422 || (eventType == SWIPED_LEFT) || (eventType == SWIPED_RIGHT)) {
424 progressBar->partialRedraw = true;
425 progressBar->state = 0;
426 nbgl_objDraw((nbgl_obj_t *) progressBar);
427#ifdef TARGET_APEX
428 // for Apex, it's necessary to redraw the dotted line, that has been partially wiped
429 nbgl_line_t *line = (nbgl_line_t *) container->children[2];
430 nbgl_objDraw((nbgl_obj_t *) line);
431#endif // TARGET_APEX
433 }
434}
435
436// callback for radio button touch
437static void radioTouchCallback(nbgl_obj_t *obj,
438 nbgl_touchType_t eventType,
439 nbgl_layoutInternal_t *layout)
440{
441 uint8_t i = NB_MAX_LAYOUTS, radioIndex = 0, foundRadio = 0xFF, foundRadioIndex;
442
443 if (eventType != TOUCHED) {
444 return;
445 }
446
447 i = 0;
448 // parse all objs to find all containers of radio buttons
449 while (i < layout->nbUsedCallbackObjs) {
450 if ((obj == (nbgl_obj_t *) layout->callbackObjPool[i].obj)
451 && (layout->callbackObjPool[i].obj->type == CONTAINER)) {
452 nbgl_radio_t *radio
453 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
454 ->children[1];
455 nbgl_text_area_t *textArea
457 ->children[0];
458 foundRadio = i;
459 foundRadioIndex = radioIndex;
460 // set text as active (black and bold)
461 textArea->textColor = BLACK;
462 textArea->fontId = SMALL_BOLD_FONT;
463 // ensure that radio button is ON
464 radio->state = ON_STATE;
465 // redraw container
466 nbgl_objDraw((nbgl_obj_t *) obj);
467 }
468 else if ((layout->callbackObjPool[i].obj->type == CONTAINER)
469 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->nbChildren == 2)
470 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->children[1]->type
471 == RADIO_BUTTON)) {
472 nbgl_radio_t *radio
473 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
474 ->children[1];
475 nbgl_text_area_t *textArea
477 ->children[0];
478 radioIndex++;
479 // set to OFF the one that was in ON
480 if (radio->state == ON_STATE) {
481 radio->state = OFF_STATE;
482 // set text it as inactive (gray and normal)
483 textArea->textColor = LIGHT_TEXT_COLOR;
484 textArea->fontId = SMALL_REGULAR_FONT;
485 // redraw container
487 }
488 }
489 i++;
490 }
491 // call callback after redraw to avoid asynchronicity
492 if (foundRadio != 0xFF) {
493 if (layout->callback != NULL) {
494#ifdef HAVE_PIEZO_SOUND
495 if (layout->callbackObjPool[foundRadio].tuneId < NBGL_NO_TUNE) {
496 os_io_seph_cmd_piezo_play_tune(layout->callbackObjPool[foundRadio].tuneId);
497 }
499#endif // HAVE_PIEZO_SOUND
500 layout->callback(layout->callbackObjPool[foundRadio].token, foundRadioIndex);
501 }
502 }
503}
504
505// callback for spinner ticker
506static void spinnerTickerCallback(void)
507{
508 nbgl_spinner_t *spinner;
509 uint8_t i = 0;
510 nbgl_layoutInternal_t *layout;
511
512 // gLayout[1] is on top of gLayout[0] so if gLayout[1] is active, it must catch the event
513 if (gLayout[1].nbChildren > 0) {
514 layout = &gLayout[1];
515 }
516 else {
517 layout = &gLayout[0];
518 }
519
520 // get index of obj
521 while (i < layout->container->nbChildren) {
522 if (layout->container->children[i]->type == CONTAINER) {
523 nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
524 if (container->nbChildren && (container->children[0]->type == SPINNER)) {
525 spinner = (nbgl_spinner_t *) container->children[0];
526 spinner->position++;
527 // there are only NB_SPINNER_POSITIONS positions
528 if (spinner->position == NB_SPINNER_POSITIONS) {
529 spinner->position = 0;
530 }
531 nbgl_objDraw((nbgl_obj_t *) spinner);
533 return;
534 }
535 }
536 i++;
537 }
538}
539
540// callback for animation ticker
541static void animTickerCallback(void)
542{
543 nbgl_image_t *image;
544 uint8_t i = 0;
545 nbgl_layoutInternal_t *layout;
546
547 // gLayout[1] is on top of gLayout[0] so if gLayout[1] is active, it must catch the event
548 if (gLayout[1].nbChildren > 0) {
549 layout = &gLayout[1];
550 }
551 else {
552 layout = &gLayout[0];
553 }
554
555 if (layout->animation == NULL) {
556 return;
557 }
558
559 // get index of image obj
560 while (i < layout->container->nbChildren) {
561 if (layout->container->children[i]->type == CONTAINER) {
562 nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
563 if (container->children[1]->type == IMAGE) {
564 image = (nbgl_image_t *) container->children[1];
565 if (layout->animation->parsing == LOOP_PARSING) {
566 if (layout->iconIdxInAnim == (layout->animation->nbIcons - 1)) {
567 layout->iconIdxInAnim = 0;
568 }
569 else {
570 layout->iconIdxInAnim++;
571 }
572 }
573 else {
574 // Flip incrementAnim when reaching upper or lower limit
575 if ((layout->incrementAnim)
576 && (layout->iconIdxInAnim >= layout->animation->nbIcons - 1)) {
577 layout->incrementAnim = false;
578 }
579 else if (layout->iconIdxInAnim == 0) {
580 layout->incrementAnim = true;
581 }
582
583 // Increase / Decrease index according to incrementAnim
584 if (layout->incrementAnim) {
585 layout->iconIdxInAnim++;
586 }
587 else {
588 layout->iconIdxInAnim--;
589 }
590 }
591 image->buffer = layout->animation->icons[layout->iconIdxInAnim];
592 nbgl_objDraw((nbgl_obj_t *) image);
595 return;
596 }
597 }
598 i++;
599 }
600}
601
602static nbgl_line_t *createHorizontalLine(uint8_t layer)
603{
604 nbgl_line_t *line;
605
606 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
607 line->lineColor = LIGHT_GRAY;
608 line->obj.area.width = SCREEN_WIDTH;
609 line->obj.area.height = 1;
610 line->direction = HORIZONTAL;
611 line->thickness = 1;
612 return line;
613}
614
615#ifdef TARGET_STAX
616static nbgl_line_t *createLeftVerticalLine(uint8_t layer)
617{
618 nbgl_line_t *line;
619
620 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
621 line->lineColor = LIGHT_GRAY;
622 line->obj.area.width = 1;
623 line->obj.area.height = SCREEN_HEIGHT;
624 line->direction = VERTICAL;
625 line->thickness = 1;
626 line->obj.alignment = MID_LEFT;
627 return line;
628}
629#endif // TARGET_STAX
630
631// function adding a layout object in the callbackObjPool array for the given layout, and
632// configuring it
634 nbgl_obj_t *obj,
635 uint8_t token,
636 tune_index_e tuneId)
637{
638 layoutObj_t *layoutObj = NULL;
639
640 if (layout->nbUsedCallbackObjs < (LAYOUT_OBJ_POOL_LEN - 1)) {
641 layoutObj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
642 layout->nbUsedCallbackObjs++;
643 layoutObj->obj = obj;
644 layoutObj->token = token;
645 layoutObj->tuneId = tuneId;
646 }
647 else {
648 LOG_FATAL(LAYOUT_LOGGER, "layoutAddCallbackObj: no more callback obj\n");
649 }
650
651 return layoutObj;
652}
653
661{
662 if (layout->container->nbChildren == NB_MAX_CONTAINER_CHILDREN) {
663 LOG_FATAL(LAYOUT_LOGGER, "layoutAddObject(): No more object\n");
664 }
665 layout->container->children[layout->container->nbChildren] = obj;
666 layout->container->nbChildren++;
667}
668
678static int addSwipeInternal(nbgl_layoutInternal_t *layoutInt,
679 uint16_t swipesMask,
680 nbgl_swipe_usage_t usage,
681 uint8_t token,
682 tune_index_e tuneId)
683{
684 layoutObj_t *obj;
685
686 if ((swipesMask & SWIPE_MASK) == 0) {
687 return -1;
688 }
689
690 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) layoutInt->container, token, tuneId);
691 if (obj == NULL) {
692 return -1;
693 }
694 layoutInt->container->obj.touchMask = swipesMask;
695 layoutInt->swipeUsage = usage;
696
697 return 0;
698}
699
707static nbgl_container_t *addListItem(nbgl_layoutInternal_t *layoutInt, const listItem_t *itemDesc)
708{
709 layoutObj_t *obj;
710 nbgl_text_area_t *textArea = NULL;
711 nbgl_container_t *container;
712 color_t color = ((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == OFF_STATE))
714 : BLACK;
715 nbgl_font_id_e fontId
716 = ((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == OFF_STATE))
718 : SMALL_BOLD_FONT;
719
720 LOG_DEBUG(LAYOUT_LOGGER, "addListItem():\n");
721
722 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
724 layoutInt, (nbgl_obj_t *) container, itemDesc->token, itemDesc->tuneId);
725 if (obj == NULL) {
726 return NULL;
727 }
728 obj->index = itemDesc->index;
729
730 // get container children (up to 4: text + left+right icons + sub text)
731 container->children = nbgl_containerPoolGet(4, layoutInt->layer);
732 container->nbChildren = 0;
733
734 container->obj.area.width = AVAILABLE_WIDTH;
735 container->obj.area.height
736 = LIST_ITEM_MIN_TEXT_HEIGHT
737 + 2 * (itemDesc->large ? LIST_ITEM_PRE_HEADING_LARGE : LIST_ITEM_PRE_HEADING);
738 container->layout = HORIZONTAL;
739 container->obj.alignmentMarginX = BORDER_MARGIN;
740 container->obj.alignment = NO_ALIGNMENT;
741 container->obj.alignTo = NULL;
742 // the bar can only be touched if not inactive AND if one of the icon is present
743 // otherwise it is seen as a title
744 if (((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == ON_STATE))
745 || (itemDesc->type == SWITCH_ITEM)) {
746 container->obj.touchMask = (1 << TOUCHED);
747 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
748 nbTouchableControls++;
749 }
750
751 // allocate main text if not NULL
752 if (itemDesc->text != NULL) {
753 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
754 textArea->textColor = color;
755 textArea->text = PIC(itemDesc->text);
756 textArea->onDrawCallback = NULL;
757 textArea->fontId = fontId;
758 textArea->wrapping = true;
759 textArea->obj.area.width = container->obj.area.width;
760 if (itemDesc->iconLeft != NULL) {
761 // reduce text width accordingly
762 textArea->obj.area.width
763 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE;
764 }
765 if (itemDesc->iconRight != NULL) {
766 // reduce text width accordingly
767 textArea->obj.area.width
768 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconRight))->width + BAR_INTERVALE;
769 }
770 else if (itemDesc->type == SWITCH_ITEM) {
771 textArea->obj.area.width -= SWITCH_ICON.width + BAR_INTERVALE;
772 }
773 textArea->obj.area.height = MAX(
774 LIST_ITEM_MIN_TEXT_HEIGHT,
776 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping));
777 textArea->style = NO_STYLE;
778 textArea->obj.alignment = TOP_LEFT;
779 textArea->obj.alignmentMarginY
780 = itemDesc->large ? LIST_ITEM_PRE_HEADING_LARGE : LIST_ITEM_PRE_HEADING;
781 if (textArea->obj.area.height > LIST_ITEM_MIN_TEXT_HEIGHT) {
782 textArea->obj.alignmentMarginY
783 -= (textArea->obj.area.height - LIST_ITEM_MIN_TEXT_HEIGHT) / 2;
784 }
785 textArea->textAlignment = MID_LEFT;
786 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
787 container->nbChildren++;
788 }
789
790 // allocate left icon if present
791 if (itemDesc->iconLeft != NULL) {
792 nbgl_image_t *imageLeft = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
793 imageLeft->foregroundColor = color;
794 imageLeft->buffer = PIC(itemDesc->iconLeft);
795 // align at the left of text
796 imageLeft->obj.alignment = MID_LEFT;
797 imageLeft->obj.alignTo = (nbgl_obj_t *) textArea;
798 imageLeft->obj.alignmentMarginX = BAR_INTERVALE;
799 container->children[container->nbChildren] = (nbgl_obj_t *) imageLeft;
800 container->nbChildren++;
801
802 if (textArea != NULL) {
803 textArea->obj.alignmentMarginX = imageLeft->buffer->width + BAR_INTERVALE;
804 }
805 }
806 // allocate right icon if present
807 if (itemDesc->iconRight != NULL) {
808 nbgl_image_t *imageRight = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
809 imageRight->foregroundColor = color;
810 imageRight->buffer = PIC(itemDesc->iconRight);
811 // align at the right of text
812 imageRight->obj.alignment = MID_RIGHT;
813 imageRight->obj.alignmentMarginX = BAR_INTERVALE;
814 imageRight->obj.alignTo = (nbgl_obj_t *) textArea;
815
816 container->children[container->nbChildren] = (nbgl_obj_t *) imageRight;
817 container->nbChildren++;
818 }
819 else if (itemDesc->type == SWITCH_ITEM) {
820 nbgl_switch_t *switchObj = (nbgl_switch_t *) nbgl_objPoolGet(SWITCH, layoutInt->layer);
821 switchObj->onColor = BLACK;
822 switchObj->offColor = LIGHT_GRAY;
823 switchObj->state = itemDesc->state;
824 switchObj->obj.alignment = MID_RIGHT;
825 switchObj->obj.alignmentMarginX = BAR_INTERVALE;
826 switchObj->obj.alignTo = (nbgl_obj_t *) textArea;
827
828 container->children[container->nbChildren] = (nbgl_obj_t *) switchObj;
829 container->nbChildren++;
830 }
831
832 if (itemDesc->subText != NULL) {
833 nbgl_text_area_t *subTextArea
835
836 subTextArea->textColor = color;
837 subTextArea->text = PIC(itemDesc->subText);
838 subTextArea->textAlignment = MID_LEFT;
839 subTextArea->fontId = SMALL_REGULAR_FONT;
840 subTextArea->style = NO_STYLE;
841 subTextArea->wrapping = true;
842 if (itemDesc->text != NULL) {
843 subTextArea->obj.alignment = BOTTOM_LEFT;
844 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
845 subTextArea->obj.alignmentMarginY = LIST_ITEM_HEADING_SUB_TEXT;
846 }
847 else {
848 subTextArea->obj.alignment = TOP_LEFT;
849 subTextArea->obj.alignmentMarginY = SUB_HEADER_MARGIN;
850 container->obj.area.height = SUB_HEADER_MARGIN;
851 }
852 if (itemDesc->iconLeft != NULL) {
853 subTextArea->obj.alignmentMarginX
854 = -(((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE);
855 }
856 subTextArea->obj.area.width = container->obj.area.width;
857 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
858 subTextArea->text,
859 subTextArea->obj.area.width,
860 subTextArea->wrapping);
861 container->children[container->nbChildren] = (nbgl_obj_t *) subTextArea;
862 container->nbChildren++;
863 container->obj.area.height
864 += subTextArea->obj.area.height + subTextArea->obj.alignmentMarginY;
865 }
866 // set this new container as child of main container
867 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
868
869 return container;
870}
871
880static nbgl_container_t *addContentCenter(nbgl_layoutInternal_t *layoutInt,
881 const nbgl_contentCenter_t *info)
882{
883 nbgl_container_t *container;
884 nbgl_text_area_t *textArea = NULL;
885 nbgl_image_t *image = NULL;
886 uint16_t fullHeight = 0;
887
888 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
889
890 // get container children
891 container->nbChildren = 0;
892 // the max number is: icon + anim + title + small title + description + sub-text
893 container->children = nbgl_containerPoolGet(6, layoutInt->layer);
894
895 // add icon or animation if present
896 if (info->icon != NULL) {
897 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
898 image->foregroundColor = (layoutInt->invertedColors) ? WHITE : BLACK;
899 image->buffer = PIC(info->icon);
900 image->obj.alignment = TOP_MIDDLE;
901 image->obj.alignmentMarginY = info->iconHug;
902
903 fullHeight += image->buffer->height + info->iconHug;
904 container->children[container->nbChildren] = (nbgl_obj_t *) image;
905 container->nbChildren++;
906
907 if (info->illustrType == ANIM_ILLUSTRATION) {
908 nbgl_image_t *anim;
909 anim = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
910 anim->foregroundColor = (layoutInt->invertedColors) ? WHITE : BLACK;
911 anim->buffer = PIC(info->animation->icons[0]);
912 anim->obj.alignment = TOP_MIDDLE;
913 anim->obj.alignmentMarginY = info->iconHug + info->animOffsetY;
914 anim->obj.alignmentMarginX = info->animOffsetX;
915
916 container->children[container->nbChildren] = (nbgl_obj_t *) anim;
917 container->nbChildren++;
918
919 layoutInt->animation = info->animation;
920 layoutInt->incrementAnim = true;
921 layoutInt->iconIdxInAnim = 0;
922
923 // update ticker to update the animation periodically
925
926 tickerCfg.tickerIntervale = info->animation->delayMs; // ms
927 tickerCfg.tickerValue = info->animation->delayMs; // ms
928 tickerCfg.tickerCallback = &animTickerCallback;
929 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
930 }
931 }
932 // add title if present
933 if (info->title != NULL) {
934 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
935 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
936 textArea->text = PIC(info->title);
937 textArea->textAlignment = CENTER;
938 textArea->fontId = LARGE_MEDIUM_FONT;
939 textArea->wrapping = true;
940 textArea->obj.area.width = AVAILABLE_WIDTH;
941 textArea->obj.area.height = nbgl_getTextHeightInWidth(
942 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
943
944 // if not the first child, put on bottom of the previous, with a margin
945 if (container->nbChildren > 0) {
946 textArea->obj.alignment = BOTTOM_MIDDLE;
947 textArea->obj.alignTo = (nbgl_obj_t *) image;
948 textArea->obj.alignmentMarginY = ICON_TITLE_MARGIN + info->iconHug;
949 }
950 else {
951 textArea->obj.alignment = TOP_MIDDLE;
952 }
953
954 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
955
956 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
957 container->nbChildren++;
958 }
959 // add small title if present
960 if (info->smallTitle != NULL) {
961 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
962 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
963 textArea->text = PIC(info->smallTitle);
964 textArea->textAlignment = CENTER;
965 textArea->fontId = SMALL_BOLD_FONT;
966 textArea->wrapping = true;
967 textArea->obj.area.width = AVAILABLE_WIDTH;
968 textArea->obj.area.height = nbgl_getTextHeightInWidth(
969 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
970
971 // if not the first child, put on bottom of the previous, with a margin
972 if (container->nbChildren > 0) {
973 textArea->obj.alignment = BOTTOM_MIDDLE;
974 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
975 textArea->obj.alignmentMarginY = VERTICAL_BORDER_MARGIN;
976 if (container->children[container->nbChildren - 1]->type == IMAGE) {
977 textArea->obj.alignmentMarginY = VERTICAL_BORDER_MARGIN + info->iconHug;
978 }
979 else {
980 textArea->obj.alignmentMarginY = 16;
981 }
982 }
983 else {
984 textArea->obj.alignment = TOP_MIDDLE;
985 }
986
987 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
988
989 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
990 container->nbChildren++;
991 }
992 // add description if present
993 if (info->description != NULL) {
994 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
995 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
996 textArea->text = PIC(info->description);
997 textArea->textAlignment = CENTER;
998 textArea->fontId = SMALL_REGULAR_FONT;
999 textArea->wrapping = true;
1000 textArea->obj.area.width = AVAILABLE_WIDTH;
1001 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1002 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1003
1004 // if not the first child, put on bottom of the previous, with a margin
1005 if (container->nbChildren > 0) {
1006 textArea->obj.alignment = BOTTOM_MIDDLE;
1007 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1008 if (container->children[container->nbChildren - 1]->type == TEXT_AREA) {
1009 // if previous element is text, only space of 16 px
1010 textArea->obj.alignmentMarginY = TITLE_DESC_MARGIN;
1011 }
1012 else {
1013 textArea->obj.alignmentMarginY = VERTICAL_BORDER_MARGIN + info->iconHug;
1014 }
1015 }
1016 else {
1017 textArea->obj.alignment = TOP_MIDDLE;
1018 }
1019
1020 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1021
1022 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1023 container->nbChildren++;
1024 }
1025 // add sub-text if present
1026 if (info->subText != NULL) {
1027 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1028 textArea->textColor = LIGHT_TEXT_COLOR;
1029 textArea->text = PIC(info->subText);
1030 textArea->textAlignment = CENTER;
1031 textArea->fontId = SMALL_REGULAR_FONT;
1032 textArea->wrapping = true;
1033 textArea->obj.area.width = AVAILABLE_WIDTH;
1034 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1035 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1036 // sub-text is included in a hug of 8px
1037 textArea->obj.area.height += 2 * 8;
1038 // if not the first child, put on bottom of the previous, with a margin
1039 if (container->nbChildren > 0) {
1040 textArea->obj.alignment = BOTTOM_MIDDLE;
1041 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1042 textArea->obj.alignmentMarginY = 16;
1043 if (container->children[container->nbChildren - 1]->type == IMAGE) {
1044 textArea->obj.alignmentMarginY += info->iconHug;
1045 }
1046 }
1047 else {
1048 textArea->obj.alignment = TOP_MIDDLE;
1049 }
1050
1051 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1052
1053 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1054 container->nbChildren++;
1055 }
1056 container->layout = VERTICAL;
1057 container->obj.alignment = CENTER;
1058 container->obj.area.width = AVAILABLE_WIDTH;
1059 container->obj.area.height = fullHeight;
1060 if (info->padding) {
1061 container->obj.area.height += 40;
1062 }
1063
1064 // set this new container as child of main container
1065 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1066
1067 return container;
1068}
1069
1070/**********************
1071 * GLOBAL FUNCTIONS
1072 **********************/
1073
1081{
1082 nbgl_layoutInternal_t *layout = NULL;
1083
1084 // find an empty layout in the proper "layer"
1085 if (description->modal) {
1086 if (gLayout[1].nbChildren == 0) {
1087 layout = &gLayout[1];
1088 }
1089 else if (gLayout[2].nbChildren == 0) {
1090 layout = &gLayout[2];
1091 }
1092 }
1093 else {
1094 // automatically "release" a potentially opened non-modal layout
1095 gLayout[0].nbChildren = 0;
1096 layout = &gLayout[0];
1097 }
1098 if (layout == NULL) {
1099 LOG_WARN(LAYOUT_LOGGER, "nbgl_layoutGet(): impossible to get a layout!\n");
1100 return NULL;
1101 }
1102
1103 // reset globals
1104 memset(layout, 0, sizeof(nbgl_layoutInternal_t));
1105
1106 nbTouchableControls = 0;
1107
1108 layout->callback = (nbgl_layoutTouchCallback_t) PIC(description->onActionCallback);
1109 layout->modal = description->modal;
1110 layout->withLeftBorder = description->withLeftBorder;
1111 if (description->modal) {
1112 layout->layer = nbgl_screenPush(&layout->children,
1114 &description->ticker,
1115 (nbgl_touchCallback_t) touchCallback);
1116 }
1117 else {
1118 nbgl_screenSet(&layout->children,
1120 &description->ticker,
1121 (nbgl_touchCallback_t) touchCallback);
1122 layout->layer = 0;
1123 }
1125 layout->container->obj.area.width = SCREEN_WIDTH;
1126 layout->container->obj.area.height = SCREEN_HEIGHT;
1127 layout->container->layout = VERTICAL;
1128 layout->container->children = nbgl_containerPoolGet(NB_MAX_CONTAINER_CHILDREN, layout->layer);
1129 // by default, if no header, main container is aligned on top-left
1130 layout->container->obj.alignment = TOP_LEFT;
1131 // main container is always the second object, leaving space for header
1132 layout->children[MAIN_CONTAINER_INDEX] = (nbgl_obj_t *) layout->container;
1134
1135 // if a tap text is defined, make the container tapable and display this text in gray
1136 if (description->tapActionText != NULL) {
1137 layoutObj_t *obj;
1138
1139 obj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
1140 layout->nbUsedCallbackObjs++;
1141 obj->obj = (nbgl_obj_t *) layout->container;
1142 obj->token = description->tapActionToken;
1143 obj->tuneId = description->tapTuneId;
1144 layout->container->obj.touchMask = (1 << TOUCHED);
1145 layout->container->obj.touchId = WHOLE_SCREEN_ID;
1146
1147 nbgl_layoutUpFooter_t footerDesc;
1148 footerDesc.type = UP_FOOTER_TEXT;
1149 footerDesc.text.text = PIC(description->tapActionText);
1150 footerDesc.text.token = description->tapActionToken;
1151 footerDesc.text.tuneId = description->tapTuneId;
1152 nbgl_layoutAddUpFooter((nbgl_layout_t *) layout, &footerDesc);
1153 }
1154
1155 return (nbgl_layout_t *) layout;
1156}
1157
1170 uint16_t swipesMask,
1171 const char *text,
1172 uint8_t token,
1173 tune_index_e tuneId)
1174{
1175 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1176
1177 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwipe():\n");
1178 if (layout == NULL) {
1179 return -1;
1180 }
1181
1182 if (text) {
1183 // create 'tap to continue' text area
1185 layoutInt->tapText->text = PIC(text);
1186 layoutInt->tapText->textColor = LIGHT_TEXT_COLOR;
1187 layoutInt->tapText->fontId = SMALL_REGULAR_FONT;
1188 layoutInt->tapText->obj.area.width = AVAILABLE_WIDTH;
1189 layoutInt->tapText->obj.area.height = nbgl_getFontLineHeight(layoutInt->tapText->fontId);
1190 layoutInt->tapText->textAlignment = CENTER;
1191 layoutInt->tapText->obj.alignmentMarginY = TAP_TO_CONTINUE_MARGIN;
1192 layoutInt->tapText->obj.alignment = BOTTOM_MIDDLE;
1193 }
1194 return addSwipeInternal(layoutInt, swipesMask, SWIPE_USAGE_CUSTOM, token, tuneId);
1195}
1196
1205{
1206 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1207
1208 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutInvertBackground():\n");
1209 if (layout == NULL) {
1210 return -1;
1211 }
1212 layoutInt->invertedColors = true;
1214
1215 // invert potential "Tap to continue"
1216 if ((layoutInt->upFooterContainer) && (layoutInt->upFooterContainer->nbChildren == 1)
1217 && (layoutInt->upFooterContainer->children[0]->type == TEXT_AREA)) {
1218 nbgl_text_area_t *textArea = (nbgl_text_area_t *) layoutInt->upFooterContainer->children[0];
1219 if (textArea->textColor == LIGHT_TEXT_COLOR) {
1220 textArea->textColor = INACTIVE_COLOR;
1221 }
1222 }
1223
1224 return 0;
1225}
1226
1237 const nbgl_icon_details_t *icon,
1238 uint8_t token,
1239 tune_index_e tuneId)
1240{
1241 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1242 layoutObj_t *obj;
1243 nbgl_button_t *button;
1244
1245 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTopRightButton():\n");
1246 if (layout == NULL) {
1247 return -1;
1248 }
1249 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
1250 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) button, token, tuneId);
1251 if (obj == NULL) {
1252 return -1;
1253 }
1254
1255 button->obj.area.width = BUTTON_WIDTH;
1256 button->obj.area.height = BUTTON_DIAMETER;
1257 button->radius = BUTTON_RADIUS;
1258 button->obj.alignmentMarginX = BORDER_MARGIN;
1259 button->obj.alignmentMarginY = BORDER_MARGIN;
1260 button->foregroundColor = BLACK;
1261 button->innerColor = WHITE;
1262 button->borderColor = LIGHT_GRAY;
1263 button->obj.touchMask = (1 << TOUCHED);
1264 button->obj.touchId = TOP_RIGHT_BUTTON_ID;
1265 button->icon = PIC(icon);
1266 button->obj.alignment = TOP_RIGHT;
1267
1268 // add to screen
1269 layoutInt->children[TOP_RIGHT_BUTTON_INDEX] = (nbgl_obj_t *) button;
1270
1271 return 0;
1272}
1273
1282{
1283 nbgl_layoutFooter_t footerDesc;
1284 footerDesc.type = FOOTER_NAV;
1285 footerDesc.separationLine = info->withSeparationLine;
1286 footerDesc.navigation.activePage = info->activePage;
1287 footerDesc.navigation.nbPages = info->nbPages;
1288 footerDesc.navigation.withExitKey = info->withExitKey;
1289 footerDesc.navigation.withBackKey = info->withBackKey;
1290 footerDesc.navigation.withPageIndicator = false;
1291 footerDesc.navigation.token = info->token;
1292 footerDesc.navigation.tuneId = info->tuneId;
1293 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1294}
1295
1308 const nbgl_icon_details_t *icon,
1309 uint8_t token,
1310 bool separationLine,
1311 tune_index_e tuneId)
1312{
1313 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddBottomButton():\n");
1314 nbgl_layoutFooter_t footerDesc;
1315 footerDesc.type = FOOTER_SIMPLE_BUTTON;
1316 footerDesc.separationLine = separationLine;
1317 footerDesc.button.fittingContent = false;
1318 footerDesc.button.icon = PIC(icon);
1319 footerDesc.button.text = NULL;
1320 footerDesc.button.token = token;
1321 footerDesc.button.tuneId = tuneId;
1322 footerDesc.button.style = WHITE_BACKGROUND;
1323 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1324}
1325
1334{
1335 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1336 nbgl_container_t *container;
1337 listItem_t itemDesc = {0};
1338
1339 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar():\n");
1340 if (layout == NULL) {
1341 return -1;
1342 }
1343 // main text is mandatory
1344 if (barLayout->text == NULL) {
1345 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar(): main text is mandatory\n");
1346 }
1347
1348 itemDesc.iconLeft = barLayout->iconLeft;
1349 itemDesc.iconRight = barLayout->iconRight;
1350 itemDesc.text = barLayout->text;
1351 itemDesc.subText = barLayout->subText;
1352 itemDesc.token = barLayout->token;
1353 itemDesc.tuneId = barLayout->tuneId;
1354 itemDesc.state = (barLayout->inactive) ? OFF_STATE : ON_STATE;
1355 itemDesc.large = barLayout->large;
1356 itemDesc.type = TOUCHABLE_BAR_ITEM;
1357 container = addListItem(layoutInt, &itemDesc);
1358
1359 if (container == NULL) {
1360 return -1;
1361 }
1362 return container->obj.area.height;
1363}
1364
1373{
1374 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1375 nbgl_container_t *container;
1376 listItem_t itemDesc = {0};
1377
1378 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwitch():\n");
1379 if (layout == NULL) {
1380 return -1;
1381 }
1382 // main text is mandatory
1383 if (switchLayout->text == NULL) {
1384 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddSwitch(): main text is mandatory\n");
1385 }
1386
1387 itemDesc.text = switchLayout->text;
1388 itemDesc.subText = switchLayout->subText;
1389 itemDesc.token = switchLayout->token;
1390 itemDesc.tuneId = switchLayout->tuneId;
1391 itemDesc.state = switchLayout->initState;
1392 itemDesc.large = false;
1393 itemDesc.type = SWITCH_ITEM;
1394 container = addListItem(layoutInt, &itemDesc);
1395
1396 if (container == NULL) {
1397 return -1;
1398 }
1399 return container->obj.area.height;
1400}
1401
1410int nbgl_layoutAddText(nbgl_layout_t *layout, const char *text, const char *subText)
1411{
1412 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1413 nbgl_container_t *container;
1414 listItem_t itemDesc = {0};
1415
1416 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddText():\n");
1417 if (layout == NULL) {
1418 return -1;
1419 }
1420
1421 itemDesc.text = text;
1422 itemDesc.subText = subText;
1423 itemDesc.token = NBGL_INVALID_TOKEN;
1424 itemDesc.tuneId = NBGL_NO_TUNE;
1425 itemDesc.type = TEXT_ITEM;
1426 container = addListItem(layoutInt, &itemDesc);
1427
1428 if (container == NULL) {
1429 return -1;
1430 }
1431 return container->obj.area.height;
1432}
1433
1446 const char *text,
1447 const char *subText,
1448 uint8_t token,
1449 uint8_t index)
1450{
1451 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1452 nbgl_container_t *container;
1453 listItem_t itemDesc = {0};
1454 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextWithAlias():\n");
1455 if (layout == NULL) {
1456 return -1;
1457 }
1458
1459 itemDesc.text = text;
1460 itemDesc.subText = subText;
1461 itemDesc.iconRight = &MINI_PUSH_ICON;
1462 itemDesc.token = token;
1463 itemDesc.tuneId = NBGL_NO_TUNE;
1464 itemDesc.type = TOUCHABLE_BAR_ITEM;
1465 itemDesc.index = index;
1466 itemDesc.state = ON_STATE;
1467 container = addListItem(layoutInt, &itemDesc);
1468
1469 if (container == NULL) {
1470 return -1;
1471 }
1472 return container->obj.area.height;
1473}
1474
1483int nbgl_layoutAddLargeCaseText(nbgl_layout_t *layout, const char *text, bool grayedOut)
1484{
1485 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1486 nbgl_text_area_t *textArea;
1487
1488 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLargeCaseText():\n");
1489 if (layout == NULL) {
1490 return -1;
1491 }
1492 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1493
1494 textArea->textColor = grayedOut ? INACTIVE_TEXT_COLOR : BLACK;
1495 textArea->text = PIC(text);
1496 textArea->textAlignment = MID_LEFT;
1497 textArea->fontId = LARGE_MEDIUM_FONT;
1498 textArea->obj.area.width = AVAILABLE_WIDTH;
1499 textArea->wrapping = true;
1500 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1501 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1502 textArea->style = NO_STYLE;
1503 textArea->obj.alignment = NO_ALIGNMENT;
1504 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1505 if (layoutInt->container->nbChildren == 0) {
1506 // if first object of container, increase the margin from top
1507 textArea->obj.alignmentMarginY += PRE_FIRST_TEXT_MARGIN;
1508 }
1509 else {
1510 textArea->obj.alignmentMarginY = INTER_PARAGRAPHS_MARGIN; // between paragraphs
1511 }
1512
1513 // set this new obj as child of main container
1514 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1515
1516 return 0;
1517}
1518
1532 const char *title,
1533 const char *description,
1534 const char *info)
1535{
1536 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1537 nbgl_text_area_t *textArea;
1538
1539 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextContent():\n");
1540 if (layout == NULL) {
1541 return -1;
1542 }
1543
1544 // create title
1545 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1546 textArea->textColor = BLACK;
1547 textArea->text = PIC(title);
1548 textArea->textAlignment = MID_LEFT;
1549 textArea->fontId = LARGE_MEDIUM_FONT;
1550 textArea->style = NO_STYLE;
1551 textArea->wrapping = true;
1552 textArea->obj.alignment = NO_ALIGNMENT;
1553 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1554 textArea->obj.alignmentMarginY = PRE_TITLE_MARGIN;
1555 textArea->obj.area.width = AVAILABLE_WIDTH;
1556 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1557 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1558 // set this new obj as child of main container
1559 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1560
1561 // create description
1562 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1563 textArea->textColor = BLACK;
1564 textArea->text = PIC(description);
1565 textArea->fontId = SMALL_REGULAR_FONT;
1566 textArea->style = NO_STYLE;
1567 textArea->wrapping = true;
1568 textArea->obj.area.width = AVAILABLE_WIDTH;
1569 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1570 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1571 textArea->textAlignment = MID_LEFT;
1572 textArea->obj.alignment = NO_ALIGNMENT;
1573 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1574 textArea->obj.alignmentMarginY = PRE_DESCRIPTION_MARGIN;
1575 // set this new obj as child of main container
1576 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1577
1578 // create info on the bottom
1579 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1580 textArea->textColor = LIGHT_TEXT_COLOR;
1581 textArea->text = PIC(info);
1582 textArea->fontId = SMALL_REGULAR_FONT;
1583 textArea->style = NO_STYLE;
1584 textArea->wrapping = true;
1585 textArea->obj.area.width = AVAILABLE_WIDTH;
1586 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1587 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1588 textArea->textAlignment = MID_LEFT;
1589 textArea->obj.alignment = BOTTOM_LEFT;
1590 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1591 textArea->obj.alignmentMarginY = 40;
1592 // set this new obj as child of main container
1593 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1594
1595 return layoutInt->container->obj.area.height;
1596}
1597
1606{
1607 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1608 layoutObj_t *obj;
1609 uint8_t i;
1610
1611 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddRadioChoice():\n");
1612 if (layout == NULL) {
1613 return -1;
1614 }
1615 for (i = 0; i < choices->nbChoices; i++) {
1616 nbgl_container_t *container;
1617 nbgl_text_area_t *textArea;
1618 nbgl_radio_t *button;
1619 nbgl_line_t *line;
1620
1621 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1622 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1623 button = (nbgl_radio_t *) nbgl_objPoolGet(RADIO_BUTTON, layoutInt->layer);
1624
1626 layoutInt, (nbgl_obj_t *) container, choices->token, choices->tuneId);
1627 if (obj == NULL) {
1628 return -1;
1629 }
1630
1631 // get container children (max 2)
1632 container->nbChildren = 2;
1633 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1634 container->obj.area.width = AVAILABLE_WIDTH;
1635 container->obj.area.height = RADIO_CHOICE_HEIGHT;
1636 container->obj.alignment = NO_ALIGNMENT;
1637 container->obj.alignmentMarginX = BORDER_MARGIN;
1638 container->obj.alignTo = (nbgl_obj_t *) NULL;
1639
1640 // init button for this choice
1641 button->activeColor = BLACK;
1642 button->borderColor = LIGHT_GRAY;
1643 button->obj.alignTo = (nbgl_obj_t *) container;
1644 button->obj.alignment = MID_RIGHT;
1645 button->state = OFF_STATE;
1646 container->children[1] = (nbgl_obj_t *) button;
1647
1648 // init text area for this choice
1649 if (choices->localized == true) {
1650#ifdef HAVE_LANGUAGE_PACK
1651 textArea->text = get_ux_loc_string(choices->nameIds[i]);
1652#endif // HAVE_LANGUAGE_PACK
1653 }
1654 else {
1655 textArea->text = PIC(choices->names[i]);
1656 }
1657 textArea->textAlignment = MID_LEFT;
1658 textArea->obj.area.width = container->obj.area.width - RADIO_WIDTH;
1659 textArea->style = NO_STYLE;
1660 textArea->obj.alignment = MID_LEFT;
1661 textArea->obj.alignTo = (nbgl_obj_t *) container;
1662 container->children[0] = (nbgl_obj_t *) textArea;
1663
1664 // whole container should be touchable
1665 container->obj.touchMask = (1 << TOUCHED);
1666 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
1667 nbTouchableControls++;
1668
1669 // highlight init choice
1670 if (i == choices->initChoice) {
1671 button->state = ON_STATE;
1672 textArea->textColor = BLACK;
1673 textArea->fontId = SMALL_BOLD_FONT;
1674 }
1675 else {
1676 button->state = OFF_STATE;
1677 textArea->textColor = LIGHT_TEXT_COLOR;
1678 textArea->fontId = SMALL_REGULAR_FONT;
1679 }
1680 textArea->obj.area.height = nbgl_getFontHeight(textArea->fontId);
1681
1682 line = createHorizontalLine(layoutInt->layer);
1683 line->obj.alignmentMarginY = -1;
1684
1685 // set these new objs as child of main container
1686 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1687 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
1688 }
1689
1690 return 0;
1691}
1692
1702{
1703 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1704 nbgl_container_t *container;
1705 nbgl_contentCenter_t centeredInfo = {0};
1706
1707 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddCenteredInfo():\n");
1708 if (layout == NULL) {
1709 return -1;
1710 }
1711
1712 centeredInfo.icon = info->icon;
1713 centeredInfo.illustrType = ICON_ILLUSTRATION;
1714
1715 if (info->text1 != NULL) {
1716 if (info->style != NORMAL_INFO) {
1717 centeredInfo.title = info->text1;
1718 }
1719 else {
1720 centeredInfo.smallTitle = info->text1;
1721 }
1722 }
1723 if (info->text2 != NULL) {
1724 if (info->style != LARGE_CASE_BOLD_INFO) {
1725 centeredInfo.description = info->text2;
1726 }
1727 else {
1728 centeredInfo.smallTitle = info->text2;
1729 }
1730 }
1731 if (info->text3 != NULL) {
1732 if (info->style == LARGE_CASE_GRAY_INFO) {
1733 centeredInfo.subText = info->text3;
1734 }
1735 else {
1736 centeredInfo.description = info->text3;
1737 }
1738 }
1739 container = addContentCenter(layoutInt, &centeredInfo);
1740
1741 if (info->onTop) {
1742 container->obj.alignmentMarginX = BORDER_MARGIN;
1743 container->obj.alignmentMarginY = BORDER_MARGIN + info->offsetY;
1744 container->obj.alignment = NO_ALIGNMENT;
1745 }
1746 else {
1747 container->obj.alignmentMarginY = info->offsetY;
1748 }
1749
1750 return container->obj.area.height;
1751}
1752
1762{
1763 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1764 nbgl_container_t *container;
1765
1766 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddContentCenter():\n");
1767 if (layout == NULL) {
1768 return -1;
1769 }
1770
1771 container = addContentCenter(layoutInt, info);
1772
1773 return container->obj.area.height;
1774}
1775
1784{
1785 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1786 nbgl_container_t *container;
1787 nbgl_text_area_t *textArea;
1788 uint8_t row;
1789
1790 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLeftContent():\n");
1791 if (layout == NULL) {
1792 return -1;
1793 }
1794 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1795
1796 // get container children
1797 container->nbChildren = info->nbRows + 1;
1798 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1799 container->layout = VERTICAL;
1800 container->obj.area.width = AVAILABLE_WIDTH;
1801 container->obj.alignmentMarginX = BORDER_MARGIN;
1802
1803 // create title
1804 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1805 textArea->textColor = BLACK;
1806 textArea->text = PIC(info->title);
1807 textArea->textAlignment = MID_LEFT;
1808 textArea->fontId = LARGE_MEDIUM_FONT;
1809 textArea->wrapping = true;
1810#ifdef TARGET_STAX
1811 textArea->obj.alignmentMarginY = 24;
1812#endif
1813 textArea->obj.area.width = AVAILABLE_WIDTH;
1814 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1815 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1816
1817 container->obj.area.height += textArea->obj.area.height;
1818
1819 container->children[0] = (nbgl_obj_t *) textArea;
1820
1821 for (row = 0; row < info->nbRows; row++) {
1822 nbgl_container_t *rowContainer;
1823 nbgl_image_t *image;
1824
1825 // create a grid with 1 icon on the left column and 1 text on the right one
1826 rowContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, 0);
1827 rowContainer->nbChildren = 2; // 1 icon + 1 text
1828 rowContainer->children = nbgl_containerPoolGet(rowContainer->nbChildren, 0);
1829 rowContainer->obj.area.width = AVAILABLE_WIDTH;
1830
1831 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, 0);
1832 image->foregroundColor = BLACK;
1833 image->buffer = info->rowIcons[row];
1834 rowContainer->children[0] = (nbgl_obj_t *) image;
1835
1836 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, 0);
1837 textArea->textColor = BLACK;
1838 textArea->text = info->rowTexts[row];
1839 textArea->textAlignment = MID_LEFT;
1840 textArea->fontId = SMALL_REGULAR_FONT;
1841 textArea->wrapping = true;
1842 textArea->obj.area.width
1843 = AVAILABLE_WIDTH - image->buffer->width - LEFT_CONTENT_ICON_TEXT_X;
1844 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1845 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1846 textArea->obj.alignment = MID_RIGHT;
1847 rowContainer->children[1] = (nbgl_obj_t *) textArea;
1848 rowContainer->obj.area.height
1849 = MAX(image->buffer->height, textArea->obj.area.height + LEFT_CONTENT_TEXT_PADDING);
1850
1851 if (row == 0) {
1852 rowContainer->obj.alignmentMarginY = PRE_FIRST_ROW_MARGIN;
1853 }
1854 else {
1855 rowContainer->obj.alignmentMarginY = INTER_ROWS_MARGIN;
1856 }
1857 container->children[1 + row] = (nbgl_obj_t *) rowContainer;
1858 container->obj.area.height
1859 += rowContainer->obj.area.height + rowContainer->obj.alignmentMarginY;
1860 }
1861 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1862
1863 return container->obj.area.height;
1864}
1865
1866#ifdef NBGL_QRCODE
1876{
1877 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1878 nbgl_container_t *container;
1879 nbgl_text_area_t *textArea = NULL;
1881 uint16_t fullHeight = 0;
1882
1883 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddQRCode():\n");
1884 if (layout == NULL) {
1885 return -1;
1886 }
1887
1888 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1889
1890 // get container children (max 2 (QRCode + text1 + text2))
1891 container->children = nbgl_containerPoolGet(3, layoutInt->layer);
1892 container->nbChildren = 0;
1893
1895 // version is forced to V10 if url is longer than 62 characters
1896 if (strlen(PIC(info->url)) > 62) {
1897 qrcode->version = QRCODE_V10;
1898 }
1899 else {
1900 qrcode->version = QRCODE_V4;
1901 }
1902 qrcode->foregroundColor = BLACK;
1903 // in QR V4, we use:
1904 // - 8*8 screen pixels for one QR pixel on Stax/Flex
1905 // - 5*5 screen pixels for one QR pixel on Apex
1906 // in QR V10, we use 4*4 screen pixels for one QR pixel
1907#ifndef TARGET_APEX
1908 qrcode->obj.area.width
1909 = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 8) : (QR_V10_NB_PIX_SIZE * 4);
1910#else // TARGET_APEX
1911 qrcode->obj.area.width
1912 = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 5) : (QR_V10_NB_PIX_SIZE * 4);
1913#endif // TARGET_APEX
1914 qrcode->obj.area.height = qrcode->obj.area.width;
1915 qrcode->text = PIC(info->url);
1916 qrcode->obj.area.bpp = NBGL_BPP_1;
1917 qrcode->obj.alignment = TOP_MIDDLE;
1918
1919 fullHeight += qrcode->obj.area.height;
1920 container->children[container->nbChildren] = (nbgl_obj_t *) qrcode;
1921 container->nbChildren++;
1922
1923 if (info->text1 != NULL) {
1924 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1925 textArea->textColor = BLACK;
1926 textArea->text = PIC(info->text1);
1927 textArea->textAlignment = CENTER;
1928 textArea->fontId = (info->largeText1 == true) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
1929 textArea->wrapping = true;
1930 textArea->obj.area.width = AVAILABLE_WIDTH;
1931 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1932 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1933 textArea->obj.alignment = BOTTOM_MIDDLE;
1934 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1935 textArea->obj.alignmentMarginY = QR_PRE_TEXT_MARGIN;
1936
1937 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1938
1939 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1940 container->nbChildren++;
1941 }
1942 if (info->text2 != NULL) {
1943 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1944 textArea->textColor = LIGHT_TEXT_COLOR;
1945 textArea->text = PIC(info->text2);
1946 textArea->textAlignment = CENTER;
1947 textArea->fontId = SMALL_REGULAR_FONT;
1948 textArea->wrapping = true;
1949 textArea->obj.area.width = AVAILABLE_WIDTH;
1950 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1951 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1952 textArea->obj.alignment = BOTTOM_MIDDLE;
1953 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1954 if (info->text1 != NULL) {
1955 textArea->obj.alignmentMarginY = QR_INTER_TEXTS_MARGIN;
1956 }
1957 else {
1958 textArea->obj.alignmentMarginY = 32;
1959 }
1960
1961 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY + 8;
1962
1963 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1964 container->nbChildren++;
1965 }
1966 // ensure that fullHeight is fitting in main container height (with 16 px margin)
1967 if ((fullHeight >= (layoutInt->container->obj.area.height - 16))
1968 && (qrcode->version == QRCODE_V4)) {
1969 qrcode->version = QRCODE_V4_SMALL;
1970 // in QR V4 small, we use 4*4 screen pixels for one QR pixel
1971 qrcode->obj.area.width = QR_V4_NB_PIX_SIZE * 4;
1972 qrcode->obj.area.height = qrcode->obj.area.width;
1973 fullHeight -= QR_V4_NB_PIX_SIZE * 4;
1974 }
1975 container->obj.area.height = fullHeight;
1976 container->layout = VERTICAL;
1977 if (info->centered) {
1978 container->obj.alignment = CENTER;
1979 }
1980 else {
1981 container->obj.alignment = BOTTOM_MIDDLE;
1982 container->obj.alignTo
1983 = layoutInt->container->children[layoutInt->container->nbChildren - 1];
1984 }
1985 container->obj.alignmentMarginY = info->offsetY;
1986
1987 container->obj.area.width = AVAILABLE_WIDTH;
1988
1989 // set this new container as child of main container
1990 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1991
1992 return fullHeight;
1993}
1994#endif // NBGL_QRCODE
1995
2005{
2006 nbgl_layoutFooter_t footerDesc;
2007 footerDesc.type = FOOTER_CHOICE_BUTTONS;
2008 footerDesc.separationLine = false;
2009 footerDesc.choiceButtons.bottomText = info->bottomText;
2010 footerDesc.choiceButtons.token = info->token;
2011 footerDesc.choiceButtons.topText = info->topText;
2012 footerDesc.choiceButtons.topIcon = info->topIcon;
2013 footerDesc.choiceButtons.style = info->style;
2014 footerDesc.choiceButtons.tuneId = info->tuneId;
2015 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2016}
2017
2029{
2031 .horizontalButtons.leftIcon = info->leftIcon,
2032 .horizontalButtons.leftToken = info->leftToken,
2033 .horizontalButtons.rightText = info->rightText,
2034 .horizontalButtons.rightToken = info->rightToken,
2035 .horizontalButtons.tuneId = info->tuneId};
2036
2037 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHorizontalButtons():\n");
2038 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2039}
2040
2049{
2050 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2051 nbgl_text_area_t *itemTextArea;
2052 nbgl_text_area_t *valueTextArea;
2053 nbgl_container_t *container;
2054 uint8_t i;
2055
2056 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTagValueList():\n");
2057 if (layout == NULL) {
2058 return -1;
2059 }
2060
2061 for (i = 0; i < list->nbPairs; i++) {
2062 const nbgl_layoutTagValue_t *pair;
2063 uint16_t fullHeight = 0;
2064 const nbgl_icon_details_t *valueIcon = NULL;
2065
2066 if (list->pairs != NULL) {
2067 pair = &list->pairs[i];
2068 }
2069 else {
2070 pair = list->callback(list->startIndex + i);
2071 }
2072
2073 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2074
2075 // get container children (max 3 if a valueIcon, otherwise 2)
2076 container->children
2077 = nbgl_containerPoolGet((pair->valueIcon != NULL) ? 3 : 2, layoutInt->layer);
2078
2079 itemTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2080 valueTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2081
2082 // init text area for this choice
2083 itemTextArea->textColor = LIGHT_TEXT_COLOR;
2084 itemTextArea->text = PIC(pair->item);
2085 itemTextArea->textAlignment = MID_LEFT;
2086 itemTextArea->fontId = SMALL_REGULAR_FONT;
2087 itemTextArea->wrapping = true;
2088 itemTextArea->obj.area.width = AVAILABLE_WIDTH;
2089 itemTextArea->obj.area.height = nbgl_getTextHeightInWidth(
2090 itemTextArea->fontId, itemTextArea->text, AVAILABLE_WIDTH, itemTextArea->wrapping);
2091 itemTextArea->style = NO_STYLE;
2092 itemTextArea->obj.alignment = NO_ALIGNMENT;
2093 itemTextArea->obj.alignmentMarginX = 0;
2094 itemTextArea->obj.alignmentMarginY = 0;
2095 itemTextArea->obj.alignTo = NULL;
2096 container->children[container->nbChildren] = (nbgl_obj_t *) itemTextArea;
2097 container->nbChildren++;
2098
2099 fullHeight += itemTextArea->obj.area.height;
2100
2101 // init button for this choice
2102 valueTextArea->textColor = BLACK;
2103 valueTextArea->text = PIC(pair->value);
2104 valueTextArea->textAlignment = MID_LEFT;
2105 if (list->smallCaseForValue) {
2106 valueTextArea->fontId = SMALL_BOLD_FONT;
2107 }
2108 else {
2109 valueTextArea->fontId = LARGE_MEDIUM_FONT;
2110 }
2111 if ((pair->aliasValue == 0) && (pair->valueIcon == NULL)) {
2112 valueTextArea->obj.area.width = AVAILABLE_WIDTH;
2113 }
2114 else {
2115 if (pair->aliasValue) {
2116 // if the value is an alias, we automatically display a (>) icon
2117 valueIcon = &MINI_PUSH_ICON;
2118 }
2119 else {
2120 // otherwise use the provided icon
2121 valueIcon = PIC(pair->valueIcon);
2122 }
2123 // decrease the available width for value text
2124 valueTextArea->obj.area.width = AVAILABLE_WIDTH - valueIcon->width - 12;
2125 }
2126
2127 // handle the nbMaxLinesForValue parameter, used to automatically keep only
2128 // nbMaxLinesForValue lines
2129 uint16_t nbLines = nbgl_getTextNbLinesInWidth(valueTextArea->fontId,
2130 valueTextArea->text,
2131 valueTextArea->obj.area.width,
2132 list->wrapping);
2133 // use this nbMaxLinesForValue parameter only if >0
2134 if ((list->nbMaxLinesForValue > 0) && (nbLines > list->nbMaxLinesForValue)) {
2135 nbLines = list->nbMaxLinesForValue;
2136 valueTextArea->nbMaxLines = list->nbMaxLinesForValue;
2137 }
2138 const nbgl_font_t *font = nbgl_getFont(valueTextArea->fontId);
2139 valueTextArea->obj.area.height = nbLines * font->line_height;
2140 valueTextArea->style = NO_STYLE;
2141 valueTextArea->obj.alignment = BOTTOM_LEFT;
2142 valueTextArea->obj.alignmentMarginY = 4;
2143 valueTextArea->obj.alignTo = (nbgl_obj_t *) itemTextArea;
2144 valueTextArea->wrapping = list->wrapping;
2145 container->children[container->nbChildren] = (nbgl_obj_t *) valueTextArea;
2146 container->nbChildren++;
2147
2148 fullHeight += valueTextArea->obj.area.height + valueTextArea->obj.alignmentMarginY;
2149 if (valueIcon != NULL) {
2150 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2151 // set the container as touchable, not only the image
2153 layoutInt, (nbgl_obj_t *) container, list->token, TUNE_TAP_CASUAL);
2154 obj->index = i;
2155 image->foregroundColor = BLACK;
2156 image->buffer = valueIcon;
2157 image->obj.alignment = RIGHT_TOP;
2158 image->obj.alignmentMarginX = 12;
2159 image->obj.alignTo = (nbgl_obj_t *) valueTextArea;
2160 // set the container as touchable, not only the image
2161 container->obj.touchMask = (1 << TOUCHED);
2162 container->obj.touchId = VALUE_BUTTON_1_ID + i;
2163
2164 container->children[container->nbChildren] = (nbgl_obj_t *) image;
2165 container->nbChildren++;
2166 }
2167
2168 container->obj.area.width = AVAILABLE_WIDTH;
2169 container->obj.area.height = fullHeight;
2170 container->layout = VERTICAL;
2171 container->obj.alignmentMarginX = BORDER_MARGIN;
2172 if (i > 0) {
2173 container->obj.alignmentMarginY = INTER_TAG_VALUE_MARGIN;
2174 }
2175 else {
2176 container->obj.alignmentMarginY = PRE_TAG_VALUE_MARGIN;
2177 }
2178 container->obj.alignment = NO_ALIGNMENT;
2179
2180 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2181 }
2182
2183 return 0;
2184}
2185
2199 const char *text,
2200 const char *subText,
2201 uint8_t percentage)
2202{
2203 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2204 nbgl_container_t *container;
2205 nbgl_text_area_t *textArea;
2206 nbgl_progress_bar_t *progress;
2207
2208 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressBar():\n");
2209 if (layout == NULL) {
2210 return -1;
2211 }
2212
2213 // First Create Container :
2214 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2215 // progressbar + text + subText
2216 container->nbChildren = (subText != NULL) ? 3 : 2;
2217 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
2218
2219 container->obj.area.width = AVAILABLE_WIDTH;
2220 container->layout = VERTICAL;
2221 container->obj.alignment = CENTER;
2222
2223 // Create progressbar
2224 progress = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
2225 progress->foregroundColor = BLACK;
2226 progress->withBorder = true;
2227 progress->state = percentage;
2228 progress->obj.area.width = PROGRESSBAR_WIDTH;
2229 progress->obj.area.height = PROGRESSBAR_HEIGHT;
2230 progress->obj.alignment = TOP_MIDDLE;
2231
2232 // set this new progressbar as child of the container
2233 container->children[0] = (nbgl_obj_t *) progress;
2234
2235 // update container height
2236 container->obj.area.height = progress->obj.alignmentMarginY + progress->obj.area.height;
2237
2238 // create text area
2239 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2240 textArea->textColor = BLACK;
2241 textArea->text = PIC(text);
2242 textArea->textAlignment = CENTER;
2243 textArea->fontId = LARGE_MEDIUM_FONT;
2244 textArea->wrapping = true;
2245 textArea->obj.alignmentMarginY = BAR_TEXT_MARGIN;
2246 textArea->obj.alignTo = (nbgl_obj_t *) progress;
2247 textArea->obj.alignment = BOTTOM_MIDDLE;
2248 textArea->obj.area.width = AVAILABLE_WIDTH;
2249 textArea->obj.area.height = nbgl_getTextHeightInWidth(
2250 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
2251 textArea->style = NO_STYLE;
2252
2253 // update container height
2254 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
2255
2256 // set this text as child of the container
2257 container->children[1] = (nbgl_obj_t *) textArea;
2258
2259 if (subText != NULL) {
2260 nbgl_text_area_t *subTextArea;
2261 // create sub-text area
2262 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2263 subTextArea->textColor = BLACK;
2264 subTextArea->text = PIC(subText);
2265 subTextArea->textAlignment = CENTER;
2266 subTextArea->fontId = SMALL_REGULAR_FONT;
2267 subTextArea->wrapping = true;
2268 subTextArea->obj.alignmentMarginY = BAR_INTER_TEXTS_MARGIN;
2269 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
2270 subTextArea->obj.alignment = BOTTOM_MIDDLE;
2271 subTextArea->obj.area.width = AVAILABLE_WIDTH;
2272 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
2273 subTextArea->text,
2274 subTextArea->obj.area.width,
2275 subTextArea->wrapping);
2276 subTextArea->style = NO_STYLE;
2277
2278 // update container height
2279 container->obj.area.height
2280 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
2281
2282 // set thissub-text as child of the container
2283 container->children[2] = (nbgl_obj_t *) subTextArea;
2284 }
2285 // The height of containers must be a multiple of eight
2286 container->obj.area.height = (container->obj.area.height + 7) & 0xFFF8;
2287
2288 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2289
2290 return 2;
2291}
2292
2300{
2301 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2302 nbgl_line_t *line;
2303
2304 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSeparationLine():\n");
2305 line = createHorizontalLine(layoutInt->layer);
2306 line->obj.alignmentMarginY = -1;
2307 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
2308 return 0;
2309}
2310
2319{
2320 layoutObj_t *obj;
2321 nbgl_button_t *button;
2322 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2323
2324 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddButton():\n");
2325 if (layout == NULL) {
2326 return -1;
2327 }
2328
2329 // Add in footer if matching
2330 if ((buttonInfo->onBottom) && (!buttonInfo->fittingContent)) {
2331 if (layoutInt->footerContainer == NULL) {
2332 nbgl_layoutFooter_t footerDesc;
2333 footerDesc.type = FOOTER_SIMPLE_BUTTON;
2334 footerDesc.separationLine = false;
2335 footerDesc.button.text = buttonInfo->text;
2336 footerDesc.button.token = buttonInfo->token;
2337 footerDesc.button.tuneId = buttonInfo->tuneId;
2338 footerDesc.button.icon = buttonInfo->icon;
2339 footerDesc.button.style = buttonInfo->style;
2340 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2341 }
2342 else {
2343 nbgl_layoutUpFooter_t upFooterDesc;
2344 upFooterDesc.type = UP_FOOTER_BUTTON;
2345 upFooterDesc.button.text = buttonInfo->text;
2346 upFooterDesc.button.token = buttonInfo->token;
2347 upFooterDesc.button.tuneId = buttonInfo->tuneId;
2348 upFooterDesc.button.icon = buttonInfo->icon;
2349 upFooterDesc.button.style = buttonInfo->style;
2350 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2351 }
2352 }
2353
2354 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2356 layoutInt, (nbgl_obj_t *) button, buttonInfo->token, buttonInfo->tuneId);
2357 if (obj == NULL) {
2358 return -1;
2359 }
2360
2361 button->obj.alignmentMarginX = BORDER_MARGIN;
2362 button->obj.alignmentMarginY = 12;
2363 button->obj.alignment = NO_ALIGNMENT;
2364 if (buttonInfo->style == BLACK_BACKGROUND) {
2365 button->innerColor = BLACK;
2366 button->foregroundColor = WHITE;
2367 }
2368 else {
2369 button->innerColor = WHITE;
2370 button->foregroundColor = BLACK;
2371 }
2372 if (buttonInfo->style == NO_BORDER) {
2373 button->borderColor = WHITE;
2374 }
2375 else {
2376 if (buttonInfo->style == BLACK_BACKGROUND) {
2377 button->borderColor = BLACK;
2378 }
2379 else {
2380 button->borderColor = INACTIVE_COLOR;
2381 }
2382 }
2383 button->text = PIC(buttonInfo->text);
2384 button->fontId = SMALL_BOLD_FONT;
2385 button->icon = PIC(buttonInfo->icon);
2386 if (buttonInfo->fittingContent == true) {
2387 button->obj.area.width = nbgl_getTextWidth(button->fontId, button->text)
2388 + SMALL_BUTTON_HEIGHT
2389 + ((button->icon) ? (button->icon->width + 12) : 0);
2390 button->obj.area.height = SMALL_BUTTON_HEIGHT;
2391 button->radius = SMALL_BUTTON_RADIUS_INDEX;
2392 if (buttonInfo->onBottom != true) {
2393 button->obj.alignmentMarginX += (AVAILABLE_WIDTH - button->obj.area.width) / 2;
2394 }
2395 }
2396 else {
2397 button->obj.area.width = AVAILABLE_WIDTH;
2398 button->obj.area.height = BUTTON_DIAMETER;
2399 button->radius = BUTTON_RADIUS;
2400 }
2401 button->obj.alignTo = NULL;
2402 button->obj.touchMask = (1 << TOUCHED);
2403 button->obj.touchId = (buttonInfo->fittingContent) ? EXTRA_BUTTON_ID : SINGLE_BUTTON_ID;
2404 // set this new button as child of the container
2405 layoutAddObject(layoutInt, (nbgl_obj_t *) button);
2406
2407 return 0;
2408}
2409
2420 const char *text,
2421 uint8_t token,
2422 tune_index_e tuneId)
2423{
2425 .longPress.text = text,
2426 .longPress.token = token,
2427 .longPress.tuneId = tuneId};
2428
2429 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLongPressButton():\n");
2430 if (layout == NULL) {
2431 return -1;
2432 }
2433
2434 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2435}
2436
2448 const char *text,
2449 uint8_t token,
2450 tune_index_e tuneId)
2451{
2452 nbgl_layoutFooter_t footerDesc;
2453 footerDesc.type = FOOTER_SIMPLE_TEXT;
2454 footerDesc.separationLine = true;
2455 footerDesc.simpleText.text = text;
2456 footerDesc.simpleText.mutedOut = false;
2457 footerDesc.simpleText.token = token;
2458 footerDesc.simpleText.tuneId = tuneId;
2459 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2460}
2461
2475 const char *leftText,
2476 uint8_t leftToken,
2477 const char *rightText,
2478 uint8_t rightToken,
2479 tune_index_e tuneId)
2480{
2481 nbgl_layoutFooter_t footerDesc;
2482 footerDesc.type = FOOTER_DOUBLE_TEXT;
2483 footerDesc.separationLine = true;
2484 footerDesc.doubleText.leftText = leftText;
2485 footerDesc.doubleText.leftToken = leftToken;
2486 footerDesc.doubleText.rightText = rightText;
2487 footerDesc.doubleText.rightToken = rightToken;
2488 footerDesc.doubleText.tuneId = tuneId;
2489 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2490}
2491
2501{
2502 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2503 layoutObj_t *obj;
2504 nbgl_text_area_t *textArea = NULL;
2505 nbgl_line_t *line = NULL;
2506 nbgl_image_t *image = NULL;
2507 nbgl_button_t *button = NULL;
2508
2509 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHeader(): type = %d\n", headerDesc->type);
2510 if (layout == NULL) {
2511 return -1;
2512 }
2513 if ((headerDesc == NULL) || (headerDesc->type >= NB_HEADER_TYPES)) {
2514 return -2;
2515 }
2516
2517 layoutInt->headerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2518 layoutInt->headerContainer->obj.area.width = SCREEN_WIDTH;
2519 layoutInt->headerContainer->layout = VERTICAL;
2520 layoutInt->headerContainer->children
2521 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2522 layoutInt->headerContainer->obj.alignment = TOP_MIDDLE;
2523
2524 switch (headerDesc->type) {
2525 case HEADER_EMPTY: {
2526 layoutInt->headerContainer->obj.area.height = headerDesc->emptySpace.height;
2527 break;
2528 }
2531 case HEADER_EXTENDED_BACK: {
2532 const char *text = (headerDesc->type == HEADER_EXTENDED_BACK)
2533 ? PIC(headerDesc->extendedBack.text)
2534 : PIC(headerDesc->backAndText.text);
2535 uint8_t backToken = (headerDesc->type == HEADER_EXTENDED_BACK)
2536 ? headerDesc->extendedBack.backToken
2537 : headerDesc->backAndText.token;
2538 nbgl_button_t *actionButton = NULL;
2539 // add back button
2540 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2541 // only make it active if valid token
2542 if (backToken != NBGL_INVALID_TOKEN) {
2543 obj = layoutAddCallbackObj(layoutInt,
2544 (nbgl_obj_t *) button,
2545 backToken,
2546 (headerDesc->type == HEADER_EXTENDED_BACK)
2547 ? headerDesc->extendedBack.tuneId
2548 : headerDesc->backAndText.tuneId);
2549 if (obj == NULL) {
2550 return -1;
2551 }
2552 }
2553
2554 button->obj.alignment = MID_LEFT;
2555 button->innerColor = WHITE;
2556 button->foregroundColor = (backToken != NBGL_INVALID_TOKEN) ? BLACK : WHITE;
2557 button->borderColor = WHITE;
2558 button->obj.area.width = BACK_KEY_WIDTH;
2559 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2560 button->text = NULL;
2561 button->icon = PIC(&LEFT_ARROW_ICON);
2562 button->obj.touchMask = (backToken != NBGL_INVALID_TOKEN) ? (1 << TOUCHED) : 0;
2563 button->obj.touchId = BACK_BUTTON_ID;
2564 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2565 = (nbgl_obj_t *) button;
2566 layoutInt->headerContainer->nbChildren++;
2567
2568 // add optional text if needed
2569 if (text != NULL) {
2570 // add optional icon if type is HEADER_BACK_ICON_AND_TEXT
2571 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2572 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2573 image->buffer = PIC(headerDesc->backAndText.icon);
2574 image->foregroundColor = BLACK;
2575 image->obj.alignment = CENTER;
2576 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2577 = (nbgl_obj_t *) image;
2578 layoutInt->headerContainer->nbChildren++;
2579 }
2580 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2581 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2582 && (headerDesc->extendedBack.textToken != NBGL_INVALID_TOKEN)) {
2583 obj = layoutAddCallbackObj(layoutInt,
2584 (nbgl_obj_t *) textArea,
2585 headerDesc->extendedBack.textToken,
2586 headerDesc->extendedBack.tuneId);
2587 if (obj == NULL) {
2588 return -1;
2589 }
2590 textArea->obj.touchMask = (1 << TOUCHED);
2591 }
2592 textArea->obj.alignment = CENTER;
2593 textArea->textColor = BLACK;
2594 textArea->obj.area.width
2595 = layoutInt->headerContainer->obj.area.width - 2 * BACK_KEY_WIDTH;
2596 // if icon, reduce to 1 line and fit text width
2597 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2598 textArea->obj.area.width -= 16 + image->buffer->width;
2599 }
2600 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2601 textArea->text = text;
2602 textArea->fontId = SMALL_BOLD_FONT;
2603 textArea->textAlignment = CENTER;
2604 textArea->wrapping = true;
2605 uint8_t nbMaxLines = (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) ? 1 : 2;
2606 // ensure that text fits on 2 lines maximum
2607 if (nbgl_getTextNbLinesInWidth(textArea->fontId,
2608 textArea->text,
2609 textArea->obj.area.width,
2610 textArea->wrapping)
2611 > nbMaxLines) {
2613 "nbgl_layoutAddHeader: text [%s] is too long for header\n",
2614 text);
2615 }
2616 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2617 textArea->obj.area.width = nbgl_getTextWidth(textArea->fontId, textArea->text);
2618 }
2619 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2620 = (nbgl_obj_t *) textArea;
2621 layoutInt->headerContainer->nbChildren++;
2622 // if icon, realign icon & text
2623 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2624 textArea->obj.alignmentMarginX = 8 + image->buffer->width / 2;
2625 image->obj.alignmentMarginX = -8 - textArea->obj.area.width / 2;
2626 }
2627 }
2628 // add action key if the type is HEADER_EXTENDED_BACK
2629 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2630 && (headerDesc->extendedBack.actionIcon)) {
2631 actionButton = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2632 // if token is valid
2633 if (headerDesc->extendedBack.actionToken != NBGL_INVALID_TOKEN) {
2634 obj = layoutAddCallbackObj(layoutInt,
2635 (nbgl_obj_t *) actionButton,
2636 headerDesc->extendedBack.actionToken,
2637 headerDesc->extendedBack.tuneId);
2638 if (obj == NULL) {
2639 return -1;
2640 }
2641 actionButton->obj.touchMask = (1 << TOUCHED);
2642 }
2643
2644 actionButton->obj.alignment = MID_RIGHT;
2645 actionButton->innerColor = WHITE;
2646 button->foregroundColor
2648 : LIGHT_GRAY;
2649 actionButton->borderColor = WHITE;
2650 actionButton->obj.area.width = BACK_KEY_WIDTH;
2651 actionButton->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2652 actionButton->text = NULL;
2653 actionButton->icon = PIC(headerDesc->extendedBack.actionIcon);
2654 actionButton->obj.touchId = EXTRA_BUTTON_ID;
2655 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2656 = (nbgl_obj_t *) actionButton;
2657 layoutInt->headerContainer->nbChildren++;
2658 }
2659
2660 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2661 // add potential text under the line if the type is HEADER_EXTENDED_BACK
2662 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2663 && (headerDesc->extendedBack.subText != NULL)) {
2664 nbgl_text_area_t *subTextArea;
2665
2666 line = createHorizontalLine(layoutInt->layer);
2667 line->obj.alignment = TOP_MIDDLE;
2668 line->obj.alignmentMarginY = TOUCHABLE_HEADER_BAR_HEIGHT;
2669 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2670 = (nbgl_obj_t *) line;
2671 layoutInt->headerContainer->nbChildren++;
2672
2673 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2674 subTextArea->textColor = BLACK;
2675 subTextArea->text = PIC(headerDesc->extendedBack.subText);
2676 subTextArea->textAlignment = MID_LEFT;
2677 subTextArea->fontId = SMALL_REGULAR_FONT;
2678 subTextArea->wrapping = true;
2679 subTextArea->obj.alignment = BOTTOM_MIDDLE;
2680 subTextArea->obj.alignmentMarginY = SUB_HEADER_MARGIN;
2681 subTextArea->obj.area.width = AVAILABLE_WIDTH;
2682 subTextArea->obj.area.height
2683 = nbgl_getTextHeightInWidth(subTextArea->fontId,
2684 subTextArea->text,
2685 subTextArea->obj.area.width,
2686 subTextArea->wrapping);
2687 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2688 = (nbgl_obj_t *) subTextArea;
2689 layoutInt->headerContainer->nbChildren++;
2690 layoutInt->headerContainer->obj.area.height
2691 += subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN;
2693 if (button != NULL) {
2694 button->obj.alignmentMarginY
2695 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2696 }
2697 if (textArea != NULL) {
2698 textArea->obj.alignmentMarginY
2699 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2700 }
2701 if (actionButton != NULL) {
2702 actionButton->obj.alignmentMarginY
2703 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2704 }
2705 }
2706 break;
2707 }
2709#ifdef TARGET_STAX
2710 // add optional back button
2711 if (headerDesc->progressAndBack.withBack) {
2712 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2713 obj = layoutAddCallbackObj(layoutInt,
2714 (nbgl_obj_t *) button,
2715 headerDesc->progressAndBack.token,
2716 headerDesc->progressAndBack.tuneId);
2717 if (obj == NULL) {
2718 return -1;
2719 }
2720
2721 button->obj.alignment = MID_LEFT;
2722 button->innerColor = WHITE;
2723 button->foregroundColor = BLACK;
2724 button->borderColor = WHITE;
2725 button->obj.area.width = BACK_KEY_WIDTH;
2726 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2727 button->text = NULL;
2728 button->icon = PIC(&LEFT_ARROW_ICON);
2729 button->obj.touchMask = (1 << TOUCHED);
2730 button->obj.touchId = BACK_BUTTON_ID;
2731 // add to container
2732 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2733 = (nbgl_obj_t *) button;
2734 layoutInt->headerContainer->nbChildren++;
2735 }
2736
2737 // add progress indicator
2738 if (headerDesc->progressAndBack.nbPages > 1
2740 nbgl_page_indicator_t *progress;
2741
2742 progress
2744 progress->activePage = headerDesc->progressAndBack.activePage;
2745 progress->nbPages = headerDesc->progressAndBack.nbPages;
2746 progress->obj.area.width = 224;
2747 progress->obj.alignment = CENTER;
2748 // add to container
2749 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2750 = (nbgl_obj_t *) progress;
2751 layoutInt->headerContainer->nbChildren++;
2752 }
2753 layoutInt->activePage = headerDesc->progressAndBack.activePage;
2754 layoutInt->nbPages = headerDesc->progressAndBack.nbPages;
2755 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2756#endif // TARGET_STAX
2757 break;
2758 }
2759 case HEADER_TITLE: {
2760 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2761 textArea->textColor = BLACK;
2762 textArea->obj.area.width = AVAILABLE_WIDTH;
2763 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2764 textArea->text = PIC(headerDesc->title.text);
2765 textArea->fontId = SMALL_BOLD_FONT;
2766 textArea->textAlignment = CENTER;
2767 textArea->wrapping = true;
2768 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2769 = (nbgl_obj_t *) textArea;
2770 layoutInt->headerContainer->nbChildren++;
2771 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2772 break;
2773 }
2774 case HEADER_RIGHT_TEXT: {
2775 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2776 obj = layoutAddCallbackObj(layoutInt,
2777 (nbgl_obj_t *) textArea,
2778 headerDesc->rightText.token,
2779 headerDesc->rightText.tuneId);
2780 if (obj == NULL) {
2781 return -1;
2782 }
2783 textArea->obj.alignment = MID_RIGHT;
2784 textArea->obj.alignmentMarginX = BORDER_MARGIN;
2785 textArea->textColor = BLACK;
2786 textArea->obj.area.width = AVAILABLE_WIDTH;
2787 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2788 textArea->text = PIC(headerDesc->rightText.text);
2789 textArea->fontId = SMALL_BOLD_FONT;
2790 textArea->textAlignment = MID_RIGHT;
2791 textArea->obj.touchMask = (1 << TOUCHED);
2792 textArea->obj.touchId = TOP_RIGHT_BUTTON_ID;
2793 // add to bottom container
2794 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2795 = (nbgl_obj_t *) textArea;
2796 layoutInt->headerContainer->nbChildren++;
2797 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2798 break;
2799 }
2800 default:
2801 return -2;
2802 }
2803 // draw separation line at bottom of container
2804 if (headerDesc->separationLine) {
2805 line = createHorizontalLine(layoutInt->layer);
2806 line->obj.alignment = BOTTOM_MIDDLE;
2807 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2808 = (nbgl_obj_t *) line;
2809 layoutInt->headerContainer->nbChildren++;
2810 }
2811 // header must be the first child
2812 layoutInt->children[HEADER_INDEX] = (nbgl_obj_t *) layoutInt->headerContainer;
2813
2814 // subtract header height from main container height
2815 layoutInt->container->obj.area.height -= layoutInt->headerContainer->obj.area.height;
2816 layoutInt->container->obj.alignTo = (nbgl_obj_t *) layoutInt->headerContainer;
2817 layoutInt->container->obj.alignment = BOTTOM_LEFT;
2818
2819 layoutInt->headerType = headerDesc->type;
2820
2821 return layoutInt->headerContainer->obj.area.height;
2822}
2823
2833{
2834 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2835 layoutObj_t *obj;
2836 nbgl_text_area_t *textArea;
2837 nbgl_line_t *line, *separationLine = NULL;
2838 nbgl_button_t *button;
2839
2840 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddExtendedFooter():\n");
2841 if (layout == NULL) {
2842 return -1;
2843 }
2844 if ((footerDesc == NULL) || (footerDesc->type >= NB_FOOTER_TYPES)) {
2845 return -2;
2846 }
2847
2848 layoutInt->footerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2849 layoutInt->footerContainer->obj.area.width = AVAILABLE_WIDTH;
2850 layoutInt->footerContainer->layout = VERTICAL;
2851 layoutInt->footerContainer->children
2852 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2853 layoutInt->footerContainer->obj.alignment = BOTTOM_MIDDLE;
2854
2855 switch (footerDesc->type) {
2856 case FOOTER_EMPTY: {
2857 layoutInt->footerContainer->obj.area.height = footerDesc->emptySpace.height;
2858 break;
2859 }
2860 case FOOTER_SIMPLE_TEXT: {
2861 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2862 obj = layoutAddCallbackObj(layoutInt,
2863 (nbgl_obj_t *) textArea,
2864 footerDesc->simpleText.token,
2865 footerDesc->simpleText.tuneId);
2866 if (obj == NULL) {
2867 return -1;
2868 }
2869
2870 textArea->obj.alignment = BOTTOM_MIDDLE;
2871 textArea->textColor = (footerDesc->simpleText.mutedOut) ? LIGHT_TEXT_COLOR : BLACK;
2872 textArea->obj.area.width = AVAILABLE_WIDTH;
2873 textArea->obj.area.height
2874 = (footerDesc->simpleText.mutedOut) ? SMALL_FOOTER_HEIGHT : SIMPLE_FOOTER_HEIGHT;
2875 textArea->text = PIC(footerDesc->simpleText.text);
2876 textArea->fontId
2877 = (footerDesc->simpleText.mutedOut) ? SMALL_REGULAR_FONT : SMALL_BOLD_FONT;
2878 textArea->textAlignment = CENTER;
2879 textArea->obj.touchMask = (1 << TOUCHED);
2880 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2881 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2882 = (nbgl_obj_t *) textArea;
2883 layoutInt->footerContainer->nbChildren++;
2884 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2885 break;
2886 }
2887 case FOOTER_DOUBLE_TEXT: {
2888 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2889 obj = layoutAddCallbackObj(layoutInt,
2890 (nbgl_obj_t *) textArea,
2891 footerDesc->doubleText.leftToken,
2892 footerDesc->doubleText.tuneId);
2893 if (obj == NULL) {
2894 return -1;
2895 }
2896 textArea->obj.alignment = BOTTOM_LEFT;
2897 textArea->textColor = BLACK;
2898 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2899 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2900 textArea->text = PIC(footerDesc->doubleText.leftText);
2901 textArea->fontId = SMALL_BOLD_FONT;
2902 textArea->textAlignment = CENTER;
2903 textArea->obj.touchMask = (1 << TOUCHED);
2904 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2905 // add to bottom container
2906 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2907 = (nbgl_obj_t *) textArea;
2908 layoutInt->footerContainer->nbChildren++;
2909
2910 // create right touchable text
2911 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2912 obj = layoutAddCallbackObj(layoutInt,
2913 (nbgl_obj_t *) textArea,
2914 footerDesc->doubleText.rightToken,
2915 footerDesc->doubleText.tuneId);
2916 if (obj == NULL) {
2917 return -1;
2918 }
2919
2920 textArea->obj.alignment = BOTTOM_RIGHT;
2921 textArea->textColor = BLACK;
2922 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2923 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2924 textArea->text = PIC(footerDesc->doubleText.rightText);
2925 textArea->fontId = SMALL_BOLD_FONT;
2926 textArea->textAlignment = CENTER;
2927 textArea->obj.touchMask = (1 << TOUCHED);
2928 textArea->obj.touchId = RIGHT_BUTTON_ID;
2929 // add to bottom container
2930 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2931 = (nbgl_obj_t *) textArea;
2932 layoutInt->footerContainer->nbChildren++;
2933 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2934
2935 // create vertical line separating texts
2936 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
2937 separationLine->lineColor = LIGHT_GRAY;
2938 separationLine->obj.area.width = 1;
2939 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
2940 separationLine->direction = VERTICAL;
2941 separationLine->thickness = 1;
2942 separationLine->obj.alignment = MID_LEFT;
2943 separationLine->obj.alignTo = (nbgl_obj_t *) textArea;
2944 separationLine->obj.alignmentMarginX = -1;
2945 break;
2946 }
2947 case FOOTER_TEXT_AND_NAV: {
2948 layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
2949 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2950 // add touchable text on the left
2951 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2952 obj = layoutAddCallbackObj(layoutInt,
2953 (nbgl_obj_t *) textArea,
2954 footerDesc->textAndNav.token,
2955 footerDesc->textAndNav.tuneId);
2956 if (obj == NULL) {
2957 return -1;
2958 }
2959 textArea->obj.alignment = BOTTOM_LEFT;
2960 textArea->textColor = BLACK;
2961 textArea->obj.area.width = FOOTER_TEXT_AND_NAV_WIDTH;
2962 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2963 textArea->text = PIC(footerDesc->textAndNav.text);
2964 textArea->fontId = SMALL_BOLD_FONT;
2965 textArea->textAlignment = CENTER;
2966 textArea->obj.touchMask = (1 << TOUCHED);
2967 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2968 // add to bottom container
2969 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2970 = (nbgl_obj_t *) textArea;
2971 layoutInt->footerContainer->nbChildren++;
2972
2973 // add navigation on the right
2974 nbgl_container_t *navContainer
2976 navContainer->obj.area.width = AVAILABLE_WIDTH;
2977 navContainer->layout = VERTICAL;
2978 navContainer->nbChildren = 4;
2979 navContainer->children
2980 = (nbgl_obj_t **) nbgl_containerPoolGet(navContainer->nbChildren, layoutInt->layer);
2981 navContainer->obj.alignment = BOTTOM_RIGHT;
2982 navContainer->obj.area.width = SCREEN_WIDTH - textArea->obj.area.width;
2983 navContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2984 layoutNavigationPopulate(navContainer, &footerDesc->navigation, layoutInt->layer);
2985 obj = layoutAddCallbackObj(layoutInt,
2986 (nbgl_obj_t *) navContainer,
2987 footerDesc->textAndNav.navigation.token,
2988 footerDesc->textAndNav.navigation.tuneId);
2989 if (obj == NULL) {
2990 return -1;
2991 }
2992
2993 // create vertical line separating text from nav
2994 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
2995 separationLine->lineColor = LIGHT_GRAY;
2996 separationLine->obj.area.width = 1;
2997 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
2998 separationLine->direction = VERTICAL;
2999 separationLine->thickness = 1;
3000 separationLine->obj.alignment = MID_LEFT;
3001 separationLine->obj.alignTo = (nbgl_obj_t *) navContainer;
3002 separationLine->obj.alignmentMarginX = -1;
3003
3004 layoutInt->activePage = footerDesc->textAndNav.navigation.activePage;
3005 layoutInt->nbPages = footerDesc->textAndNav.navigation.nbPages;
3006 // add to bottom container
3007 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3008 = (nbgl_obj_t *) navContainer;
3009 layoutInt->footerContainer->nbChildren++;
3010 break;
3011 }
3012 case FOOTER_NAV: {
3013 layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
3014 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3016 layoutInt->footerContainer, &footerDesc->navigation, layoutInt->layer);
3017 layoutInt->footerContainer->nbChildren = 4;
3018 obj = layoutAddCallbackObj(layoutInt,
3019 (nbgl_obj_t *) layoutInt->footerContainer,
3020 footerDesc->navigation.token,
3021 footerDesc->navigation.tuneId);
3022 if (obj == NULL) {
3023 return -1;
3024 }
3025
3026 layoutInt->activePage = footerDesc->navigation.activePage;
3027 layoutInt->nbPages = footerDesc->navigation.nbPages;
3028 break;
3029 }
3030 case FOOTER_SIMPLE_BUTTON: {
3031 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3032 obj = layoutAddCallbackObj(layoutInt,
3033 (nbgl_obj_t *) button,
3034 footerDesc->button.token,
3035 footerDesc->button.tuneId);
3036 if (obj == NULL) {
3037 return -1;
3038 }
3039
3040 button->obj.alignment = BOTTOM_MIDDLE;
3041 button->obj.alignmentMarginY = SINGLE_BUTTON_MARGIN;
3042 if (footerDesc->button.style == BLACK_BACKGROUND) {
3043 button->innerColor = BLACK;
3044 button->foregroundColor = WHITE;
3045 }
3046 else {
3047 button->innerColor = WHITE;
3048 button->foregroundColor = BLACK;
3049 }
3050
3051 if (footerDesc->button.style == NO_BORDER) {
3052 button->borderColor = WHITE;
3053 }
3054 else {
3055 if (footerDesc->button.style == BLACK_BACKGROUND) {
3056 button->borderColor = BLACK;
3057 }
3058 else {
3059 button->borderColor = LIGHT_GRAY;
3060 }
3061 }
3062 button->text = PIC(footerDesc->button.text);
3063 button->fontId = SMALL_BOLD_FONT;
3064 button->icon = PIC(footerDesc->button.icon);
3065 button->radius = BUTTON_RADIUS;
3066 button->obj.area.height = BUTTON_DIAMETER;
3067 layoutInt->footerContainer->obj.area.height = FOOTER_BUTTON_HEIGHT;
3068 if (footerDesc->button.text == NULL) {
3069 button->obj.area.width = BUTTON_DIAMETER;
3070 }
3071 else {
3072 button->obj.area.width = AVAILABLE_WIDTH;
3073 }
3074 button->obj.touchMask = (1 << TOUCHED);
3075 button->obj.touchId = button->text ? SINGLE_BUTTON_ID : BOTTOM_BUTTON_ID;
3076 // add to bottom container
3077 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3078 = (nbgl_obj_t *) button;
3079 layoutInt->footerContainer->nbChildren++;
3080 break;
3081 }
3082 case FOOTER_CHOICE_BUTTONS: {
3083 // texts cannot be NULL
3084 if ((footerDesc->choiceButtons.bottomText == NULL)
3085 || (footerDesc->choiceButtons.topText == NULL)) {
3086 return -1;
3087 }
3088
3089 // create bottom button (footer) at first
3090 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3091 obj = layoutAddCallbackObj(layoutInt,
3092 (nbgl_obj_t *) button,
3093 footerDesc->choiceButtons.token,
3094 footerDesc->choiceButtons.tuneId);
3095 if (obj == NULL) {
3096 return -1;
3097 }
3098 // associate with with index 1
3099 obj->index = 1;
3100 // put at the bottom of the container
3101 button->obj.alignment = BOTTOM_MIDDLE;
3102 button->innerColor = WHITE;
3103 if (footerDesc->choiceButtons.style
3104 == BOTH_ROUNDED_STYLE) { // TODO: remove BOTH_ROUNDED_STYLE support
3105 button->obj.alignmentMarginY
3106 = VERTICAL_BORDER_MARGIN; // pixels from bottom of container
3107 button->borderColor = LIGHT_GRAY;
3108 }
3109 else {
3110 button->obj.alignmentMarginY
3111 = BUTTON_FROM_BOTTOM_MARGIN; // pixels from screen bottom
3112 button->borderColor = WHITE;
3113 }
3114 button->foregroundColor = BLACK;
3115 button->obj.area.width = AVAILABLE_WIDTH;
3116 button->obj.area.height = FOOTER_IN_PAIR_HEIGHT;
3117 button->radius = BUTTON_RADIUS;
3118 button->text = PIC(footerDesc->choiceButtons.bottomText);
3119 button->fontId = SMALL_BOLD_FONT;
3120 button->obj.touchMask = (1 << TOUCHED);
3121 button->obj.touchId = CHOICE_2_ID;
3122 // add to bottom container
3123 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3124 = (nbgl_obj_t *) button;
3125 layoutInt->footerContainer->nbChildren++;
3126
3127 // add line if needed
3128 if ((footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3129 && (footerDesc->choiceButtons.style != BOTH_ROUNDED_STYLE)) {
3130 line = createHorizontalLine(layoutInt->layer);
3131 line->obj.alignment = TOP_MIDDLE;
3132 line->obj.alignTo = (nbgl_obj_t *) button;
3133 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3134 = (nbgl_obj_t *) line;
3135 layoutInt->footerContainer->nbChildren++;
3136 }
3137
3138 // then top button, on top of it
3139 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3140 obj = layoutAddCallbackObj(layoutInt,
3141 (nbgl_obj_t *) button,
3142 footerDesc->choiceButtons.token,
3143 footerDesc->choiceButtons.tuneId);
3144 if (obj == NULL) {
3145 return -1;
3146 }
3147 // associate with with index 0
3148 obj->index = 0;
3149 button->obj.alignment = TOP_MIDDLE;
3150 if ((footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3151 && (footerDesc->choiceButtons.style != BOTH_ROUNDED_STYLE)) {
3152 button->obj.alignmentMarginY
3153 = TOP_BUTTON_MARGIN_WITH_ACTION; // pixels from top of container
3154 }
3155 else {
3156 button->obj.alignmentMarginY = TOP_BUTTON_MARGIN; // pixels from top of container
3157 }
3159 button->innerColor = WHITE;
3160 button->borderColor = LIGHT_GRAY;
3161 button->foregroundColor = BLACK;
3162 }
3163 else {
3164 button->innerColor = BLACK;
3165 button->borderColor = BLACK;
3166 button->foregroundColor = WHITE;
3167 }
3168 button->obj.area.width = AVAILABLE_WIDTH;
3169 button->obj.area.height = BUTTON_DIAMETER;
3170 button->radius = BUTTON_RADIUS;
3171 button->text = PIC(footerDesc->choiceButtons.topText);
3172 button->icon = (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3173 ? PIC(footerDesc->choiceButtons.topIcon)
3174 : NULL;
3175 button->fontId = SMALL_BOLD_FONT;
3176 button->obj.touchMask = (1 << TOUCHED);
3177 button->obj.touchId = CHOICE_1_ID;
3178 // add to bottom container
3179 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3180 = (nbgl_obj_t *) button;
3181 layoutInt->footerContainer->nbChildren++;
3182
3183 if (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE) {
3184 layoutInt->footerContainer->obj.area.height = ACTION_AND_FOOTER_FOOTER_HEIGHT;
3185 }
3186 else {
3187 layoutInt->footerContainer->obj.area.height = ROUNDED_AND_FOOTER_FOOTER_HEIGHT;
3188 }
3189
3190 break;
3191 }
3192 default:
3193 return -2;
3194 }
3195
3196 // add swipable feature for navigation
3197 if ((footerDesc->type == FOOTER_NAV) || (footerDesc->type == FOOTER_TEXT_AND_NAV)) {
3198 addSwipeInternal(layoutInt,
3199 ((1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT)),
3201 (footerDesc->type == FOOTER_NAV) ? footerDesc->navigation.token
3202 : footerDesc->textAndNav.navigation.token,
3203 (footerDesc->type == FOOTER_NAV)
3204 ? footerDesc->navigation.tuneId
3205 : footerDesc->textAndNav.navigation.tuneId);
3206 }
3207
3208 if (footerDesc->separationLine) {
3209 line = createHorizontalLine(layoutInt->layer);
3210 line->obj.alignment = TOP_MIDDLE;
3211 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3212 = (nbgl_obj_t *) line;
3213 layoutInt->footerContainer->nbChildren++;
3214 }
3215 if (separationLine != NULL) {
3216 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3217 = (nbgl_obj_t *) separationLine;
3218 layoutInt->footerContainer->nbChildren++;
3219 }
3220
3221 layoutInt->children[FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->footerContainer;
3222
3223 // subtract footer height from main container height
3224 layoutInt->container->obj.area.height -= layoutInt->footerContainer->obj.area.height;
3225
3226 layoutInt->footerType = footerDesc->type;
3227
3228 return layoutInt->footerContainer->obj.area.height;
3229}
3230
3240{
3241 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3242 layoutObj_t *obj;
3243 nbgl_text_area_t *textArea;
3244 nbgl_line_t *line;
3245 nbgl_button_t *button;
3246
3247 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddUpFooter():\n");
3248 if (layout == NULL) {
3249 return -1;
3250 }
3251 if ((upFooterDesc == NULL) || (upFooterDesc->type >= NB_UP_FOOTER_TYPES)) {
3252 return -2;
3253 }
3254
3255 layoutInt->upFooterContainer
3257 layoutInt->upFooterContainer->obj.area.width = SCREEN_WIDTH;
3258 layoutInt->upFooterContainer->layout = VERTICAL;
3259 // maximum 4 children for long press button
3260 layoutInt->upFooterContainer->children
3261 = (nbgl_obj_t **) nbgl_containerPoolGet(4, layoutInt->layer);
3262 layoutInt->upFooterContainer->obj.alignTo = (nbgl_obj_t *) layoutInt->container;
3263 layoutInt->upFooterContainer->obj.alignment = BOTTOM_MIDDLE;
3264
3265 switch (upFooterDesc->type) {
3266 case UP_FOOTER_LONG_PRESS: {
3267 nbgl_progress_bar_t *progressBar;
3268
3269 obj = layoutAddCallbackObj(layoutInt,
3270 (nbgl_obj_t *) layoutInt->upFooterContainer,
3271 upFooterDesc->longPress.token,
3272 upFooterDesc->longPress.tuneId);
3273 if (obj == NULL) {
3274 return -1;
3275 }
3276 layoutInt->upFooterContainer->nbChildren = 4;
3277 layoutInt->upFooterContainer->obj.area.height = LONG_PRESS_BUTTON_HEIGHT;
3278 layoutInt->upFooterContainer->obj.touchId = LONG_PRESS_BUTTON_ID;
3279 layoutInt->upFooterContainer->obj.touchMask
3280 = ((1 << TOUCHING) | (1 << TOUCH_RELEASED) | (1 << OUT_OF_TOUCH)
3281 | (1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT));
3282
3283 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3284 button->obj.alignmentMarginX = BORDER_MARGIN;
3285 button->obj.alignment = MID_RIGHT;
3286 button->innerColor = BLACK;
3287 button->foregroundColor = WHITE;
3288 button->borderColor = BLACK;
3289 button->obj.area.width = BUTTON_DIAMETER;
3290 button->obj.area.height = BUTTON_DIAMETER;
3291 button->radius = BUTTON_RADIUS;
3292 button->icon = PIC(&VALIDATE_ICON);
3293 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3294
3295 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3296 textArea->textColor = BLACK;
3297 textArea->text = PIC(upFooterDesc->longPress.text);
3298 textArea->textAlignment = MID_LEFT;
3299 textArea->fontId = LARGE_MEDIUM_FONT;
3300 textArea->wrapping = true;
3301 textArea->obj.area.width = SCREEN_WIDTH - 3 * BORDER_MARGIN - button->obj.area.width;
3302 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3303 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3304 textArea->style = NO_STYLE;
3305 textArea->obj.alignment = MID_LEFT;
3306 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3307 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) textArea;
3308
3309 line = createHorizontalLine(layoutInt->layer);
3310 line->obj.alignment = TOP_MIDDLE;
3311 line->obj.alignmentMarginY = VERTICAL_ALIGNMENT - 1;
3312 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) line;
3313
3314 progressBar = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
3315 progressBar->obj.area.width = SCREEN_WIDTH;
3316 progressBar->obj.area.height = LONG_PRESS_PROGRESS_HEIGHT;
3317 progressBar->obj.alignment = TOP_MIDDLE;
3318 progressBar->obj.alignmentMarginY = LONG_PRESS_PROGRESS_ALIGN;
3319 progressBar->resetIfOverriden = true;
3320 progressBar->partialRedraw = true;
3321 layoutInt->upFooterContainer->children[3] = (nbgl_obj_t *) progressBar;
3322 break;
3323 }
3324 case UP_FOOTER_BUTTON: {
3325 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3326 obj = layoutAddCallbackObj(layoutInt,
3327 (nbgl_obj_t *) button,
3328 upFooterDesc->button.token,
3329 upFooterDesc->button.tuneId);
3330 if (obj == NULL) {
3331 return -1;
3332 }
3333
3334 layoutInt->upFooterContainer->nbChildren = 1;
3335 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3336 button->obj.alignment = CENTER;
3337
3338 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3339 button->innerColor = BLACK;
3340 button->foregroundColor = WHITE;
3341 }
3342 else {
3343 button->innerColor = WHITE;
3344 button->foregroundColor = BLACK;
3345 }
3346 if (upFooterDesc->button.style == NO_BORDER) {
3347 button->borderColor = WHITE;
3348 }
3349 else {
3350 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3351 button->borderColor = BLACK;
3352 }
3353 else {
3354 button->borderColor = LIGHT_GRAY;
3355 }
3356 }
3357 button->text = PIC(upFooterDesc->button.text);
3358 button->fontId = SMALL_BOLD_FONT;
3359 button->icon = PIC(upFooterDesc->button.icon);
3360 button->obj.area.width = AVAILABLE_WIDTH;
3361 button->obj.area.height = BUTTON_DIAMETER;
3362 button->radius = BUTTON_RADIUS;
3363
3364 button->obj.alignTo = NULL;
3365 button->obj.touchMask = (1 << TOUCHED);
3366 button->obj.touchId = SINGLE_BUTTON_ID;
3367 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3368 break;
3369 }
3371 // icon & text cannot be NULL
3372 if ((upFooterDesc->horizontalButtons.leftIcon == NULL)
3373 || (upFooterDesc->horizontalButtons.rightText == NULL)) {
3374 return -1;
3375 }
3376
3377 layoutInt->upFooterContainer->nbChildren = 2;
3378 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3379
3380 // create left button (in white) at first
3381 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3382 obj = layoutAddCallbackObj(layoutInt,
3383 (nbgl_obj_t *) button,
3384 upFooterDesc->horizontalButtons.leftToken,
3385 upFooterDesc->horizontalButtons.tuneId);
3386 if (obj == NULL) {
3387 return -1;
3388 }
3389 // associate with with index 1
3390 obj->index = 1;
3391 button->obj.alignment = MID_LEFT;
3392 button->obj.alignmentMarginX = BORDER_MARGIN;
3393 button->borderColor = LIGHT_GRAY;
3394 button->innerColor = WHITE;
3395 button->foregroundColor = BLACK;
3396 button->obj.area.width = BUTTON_WIDTH;
3397 button->obj.area.height = BUTTON_DIAMETER;
3398 button->radius = BUTTON_RADIUS;
3399 button->icon = PIC(upFooterDesc->horizontalButtons.leftIcon);
3400 button->fontId = SMALL_BOLD_FONT;
3401 button->obj.touchMask = (1 << TOUCHED);
3402 button->obj.touchId = CHOICE_2_ID;
3403 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3404
3405 // then black button, on right
3406 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3407 obj = layoutAddCallbackObj(layoutInt,
3408 (nbgl_obj_t *) button,
3409 upFooterDesc->horizontalButtons.rightToken,
3410 upFooterDesc->horizontalButtons.tuneId);
3411 if (obj == NULL) {
3412 return -1;
3413 }
3414 // associate with with index 0
3415 obj->index = 0;
3416 button->obj.alignment = MID_RIGHT;
3417 button->obj.alignmentMarginX = BORDER_MARGIN;
3418 button->innerColor = BLACK;
3419 button->borderColor = BLACK;
3420 button->foregroundColor = WHITE;
3421 button->obj.area.width = AVAILABLE_WIDTH - BUTTON_WIDTH - LEFT_CONTENT_ICON_TEXT_X;
3422 button->obj.area.height = BUTTON_DIAMETER;
3423 button->radius = BUTTON_RADIUS;
3424 button->text = PIC(upFooterDesc->horizontalButtons.rightText);
3425 button->fontId = SMALL_BOLD_FONT;
3426 button->obj.touchMask = (1 << TOUCHED);
3427 button->obj.touchId = CHOICE_1_ID;
3428 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) button;
3429 break;
3430 }
3431 case UP_FOOTER_TIP_BOX: {
3432 // text cannot be NULL
3433 if (upFooterDesc->tipBox.text == NULL) {
3434 return -1;
3435 }
3436 obj = layoutAddCallbackObj(layoutInt,
3437 (nbgl_obj_t *) layoutInt->upFooterContainer,
3438 upFooterDesc->tipBox.token,
3439 upFooterDesc->tipBox.tuneId);
3440 if (obj == NULL) {
3441 return -1;
3442 }
3443 layoutInt->upFooterContainer->nbChildren = 3;
3444 layoutInt->upFooterContainer->obj.touchId = TIP_BOX_ID;
3445 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3446
3447 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3448 textArea->textColor = BLACK;
3449 textArea->text = PIC(upFooterDesc->tipBox.text);
3450 textArea->textAlignment = MID_LEFT;
3451 textArea->fontId = SMALL_REGULAR_FONT;
3452 textArea->wrapping = true;
3453 textArea->obj.area.width = AVAILABLE_WIDTH;
3454 if (upFooterDesc->tipBox.icon != NULL) {
3455 textArea->obj.area.width
3456 -= ((nbgl_icon_details_t *) PIC(upFooterDesc->tipBox.icon))->width
3457 + BORDER_MARGIN;
3458 }
3459 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3460 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3461 textArea->obj.alignment = MID_LEFT;
3462 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3463 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3464 layoutInt->upFooterContainer->obj.area.height = textArea->obj.area.height;
3465
3466 line = createHorizontalLine(layoutInt->layer);
3467 line->obj.alignment = TOP_MIDDLE;
3468 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) line;
3469
3470 if (upFooterDesc->tipBox.icon != NULL) {
3471 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
3472 image->obj.alignmentMarginX = BORDER_MARGIN;
3473 image->obj.alignment = MID_RIGHT;
3474 image->foregroundColor = BLACK;
3475 image->buffer = PIC(upFooterDesc->tipBox.icon);
3476 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) image;
3477 if (layoutInt->upFooterContainer->obj.area.height < image->buffer->height) {
3478 layoutInt->upFooterContainer->obj.area.height = image->buffer->height;
3479 }
3480 }
3481 layoutInt->upFooterContainer->obj.area.height += 2 * TIP_BOX_MARGIN_Y;
3482
3483 break;
3484 }
3485 case UP_FOOTER_TEXT: {
3486 obj = layoutAddCallbackObj(layoutInt,
3487 (nbgl_obj_t *) layoutInt->upFooterContainer,
3488 upFooterDesc->text.token,
3489 upFooterDesc->text.tuneId);
3490 if (obj == NULL) {
3491 return -1;
3492 }
3493 layoutInt->upFooterContainer->nbChildren = 1;
3494 layoutInt->upFooterContainer->obj.area.height = SMALL_FOOTER_HEIGHT;
3495 layoutInt->upFooterContainer->obj.touchId = WHOLE_SCREEN_ID;
3496 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3497
3498 // only create text_area if text is not empty
3499 if (strlen(PIC(upFooterDesc->text.text))) {
3500 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3501 textArea->textColor = LIGHT_TEXT_COLOR;
3502 textArea->text = PIC(upFooterDesc->text.text);
3503 textArea->textAlignment = CENTER;
3504 textArea->fontId = SMALL_REGULAR_FONT;
3505 textArea->wrapping = true;
3506 textArea->obj.area.width = AVAILABLE_WIDTH;
3507 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3508 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3509 textArea->obj.alignment = CENTER;
3510 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3511 }
3512 break;
3513 }
3514 default:
3515 return -2;
3516 }
3517
3518 // subtract up footer height from main container height
3519 layoutInt->container->obj.area.height -= layoutInt->upFooterContainer->obj.area.height;
3520
3521 layoutInt->children[UP_FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->upFooterContainer;
3522
3523 layoutInt->upFooterType = upFooterDesc->type;
3524
3525 return layoutInt->upFooterContainer->obj.area.height;
3526}
3527
3542 uint8_t activePage,
3543 uint8_t nbPages,
3544 bool withBack,
3545 uint8_t backToken,
3546 tune_index_e tuneId)
3547{
3549 .separationLine = false,
3550 .progressAndBack.activePage = activePage,
3551 .progressAndBack.nbPages = nbPages,
3552 .progressAndBack.token = backToken,
3553 .progressAndBack.tuneId = tuneId,
3554 .progressAndBack.withBack = withBack,
3555 .progressAndBack.actionIcon = NULL,
3556 .progressAndBack.actionToken = NBGL_INVALID_TOKEN};
3557 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressIndicator():\n");
3558
3559 return nbgl_layoutAddHeader(layout, &headerDesc);
3560}
3561
3573 const char *text,
3574 const char *subText,
3575 uint8_t initPosition)
3576{
3577 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3578 nbgl_container_t *container;
3579 nbgl_text_area_t *textArea;
3580 nbgl_spinner_t *spinner;
3581
3582 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSpinner():\n");
3583 if (layout == NULL) {
3584 return -1;
3585 }
3586
3587 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
3588 // spinner + text + subText
3589 container->nbChildren = 3;
3590 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
3591
3592 container->obj.area.width = AVAILABLE_WIDTH;
3593 container->layout = VERTICAL;
3594 container->obj.alignment = CENTER;
3595
3596 // create spinner
3597 spinner = (nbgl_spinner_t *) nbgl_objPoolGet(SPINNER, layoutInt->layer);
3598 spinner->position = initPosition;
3599 spinner->obj.alignment = TOP_MIDDLE;
3600 // set this new spinner as child of the container
3601 container->children[0] = (nbgl_obj_t *) spinner;
3602
3603 // update container height
3604 container->obj.area.height += SPINNER_HEIGHT;
3605
3606 // create text area
3607 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3608 textArea->textColor = BLACK;
3609 textArea->text = PIC(text);
3610 textArea->textAlignment = CENTER;
3611 textArea->fontId = (subText != NULL) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
3612 textArea->wrapping = true;
3613 textArea->obj.alignmentMarginY = SPINNER_TEXT_MARGIN;
3614 textArea->obj.alignTo = (nbgl_obj_t *) spinner;
3615 textArea->obj.alignment = BOTTOM_MIDDLE;
3616 textArea->obj.area.width = AVAILABLE_WIDTH;
3617 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3618 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3619 textArea->style = NO_STYLE;
3620
3621 // update container height
3622 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
3623
3624 // set this text as child of the container
3625 container->children[1] = (nbgl_obj_t *) textArea;
3626
3627 if (subText != NULL) {
3628 nbgl_text_area_t *subTextArea;
3629 // create sub-text area
3630 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3631 subTextArea->textColor = BLACK;
3632 subTextArea->text = PIC(subText);
3633 subTextArea->textAlignment = CENTER;
3634 subTextArea->fontId = SMALL_REGULAR_FONT;
3635 subTextArea->wrapping = true;
3636 subTextArea->obj.alignmentMarginY = SPINNER_INTER_TEXTS_MARGIN;
3637 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
3638 subTextArea->obj.alignment = BOTTOM_MIDDLE;
3639 subTextArea->obj.area.width = AVAILABLE_WIDTH;
3640 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
3641 subTextArea->text,
3642 subTextArea->obj.area.width,
3643 subTextArea->wrapping);
3644 subTextArea->style = NO_STYLE;
3645
3646 // update container height
3647 container->obj.area.height
3648 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
3649
3650 // set thissub-text as child of the container
3651 container->children[2] = (nbgl_obj_t *) subTextArea;
3652 }
3653 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
3654
3655 if (initPosition != SPINNER_FIXED) {
3656 // update ticker to update the spinner periodically
3658
3659 tickerCfg.tickerIntervale = SPINNER_REFRESH_PERIOD; // ms
3660 tickerCfg.tickerValue = SPINNER_REFRESH_PERIOD; // ms
3661 tickerCfg.tickerCallback = &spinnerTickerCallback;
3662 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
3663 }
3664
3665 return 0;
3666}
3667
3680 const char *text,
3681 const char *subText,
3682 uint8_t position)
3683{
3684 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3685 nbgl_container_t *container;
3686 nbgl_text_area_t *textArea;
3687 nbgl_spinner_t *spinner;
3688 int ret = 0;
3689
3690 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutUpdateSpinner():\n");
3691 if ((layout == NULL) || (layoutInt->container->nbChildren == 0)) {
3692 return -1;
3693 }
3694
3695 container = (nbgl_container_t *) layoutInt->container->children[0];
3696 if ((container->obj.type != CONTAINER) || (container->nbChildren < 2)) {
3697 return -1;
3698 }
3699
3700 spinner = (nbgl_spinner_t *) container->children[0];
3701 if (spinner->obj.type != SPINNER) {
3702 return -1;
3703 }
3704 // if position is different, redraw
3705 if (spinner->position != position) {
3706 spinner->position = position;
3707 nbgl_objDraw((nbgl_obj_t *) spinner);
3708 ret = 1;
3709 }
3710
3711 // update text area if necessary
3712 textArea = (nbgl_text_area_t *) container->children[1];
3713 if (textArea->obj.type != TEXT_AREA) {
3714 return -1;
3715 }
3716 const char *newText = PIC(text);
3717 size_t newTextLen = strlen(newText);
3718 // if text is different, redraw (don't use strcmp because it crashes with Rust SDK)
3719 if ((newTextLen != strlen(textArea->text)) || memcmp(textArea->text, newText, newTextLen)) {
3720 textArea->text = newText;
3721 nbgl_objDraw((nbgl_obj_t *) textArea);
3722 ret = 2;
3723 }
3724
3725 if (subText != NULL) {
3726 nbgl_text_area_t *subTextArea;
3727
3728 if (container->nbChildren != 3) {
3729 return -1;
3730 }
3731 subTextArea = (nbgl_text_area_t *) container->children[2];
3732 if (subTextArea->obj.type != TEXT_AREA) {
3733 return -1;
3734 }
3735 const char *newSubText = PIC(subText);
3736 size_t newSubTextLen = strlen(newSubText);
3737 // if text is different, redraw
3738 if ((newSubTextLen != strlen(subTextArea->text))
3739 || memcmp(subTextArea->text, newSubText, newSubTextLen)) {
3740 subTextArea->text = newSubText;
3741 nbgl_objDraw((nbgl_obj_t *) subTextArea);
3742 ret = 2;
3743 }
3744 }
3745
3746 return ret;
3747}
3748
3756{
3757 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3758
3759 if (layout == NULL) {
3760 return -1;
3761 }
3763 "nbgl_layoutDraw(): container.nbChildren =%d, layout->nbChildren = %d\n",
3764 layout->container->nbChildren,
3765 layout->nbChildren);
3766 if (layout->tapText) {
3767 // set this new container as child of main container
3768 layoutAddObject(layout, (nbgl_obj_t *) layout->tapText);
3769 }
3770#ifdef TARGET_STAX
3771 if (layout->withLeftBorder == true) {
3772 // draw now the line
3773 nbgl_line_t *line = createLeftVerticalLine(layout->layer);
3774 layout->children[LEFT_BORDER_INDEX] = (nbgl_obj_t *) line;
3775 }
3776#endif // TARGET_STAX
3778
3779 return 0;
3780}
3781
3789{
3790 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3791 LOG_DEBUG(PAGE_LOGGER, "nbgl_layoutRelease(): \n");
3792 if (layout == NULL) {
3793 return -1;
3794 }
3795 // if modal
3796 if (layout->modal) {
3797 nbgl_screenPop(layout->layer);
3798 }
3799 layout->nbChildren = 0;
3800 return 0;
3801}
3802
3803#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:264
#define LIGHT_TEXT_COLOR
Definition nbgl_obj.h:282
void(* nbgl_touchCallback_t)(void *obj, nbgl_touchType_t eventType)
prototype of function to be called when a touch event is received by an object
Definition nbgl_obj.h:346
struct PACKED__ nbgl_text_area_s nbgl_text_area_t
struct to represent a text area (TEXT_AREA type)
void nbgl_objDraw(nbgl_obj_t *obj)
This function draws or redraws the given object and its children (recursive version)
Definition nbgl_obj.c:1626
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:292
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:1679
struct PACKED__ nbgl_image_s nbgl_image_t
struct to represent an image (IMAGE type)
#define INACTIVE_SMALL_FONT
Definition nbgl_obj.h:281
struct PACKED__ nbgl_button_s nbgl_button_t
struct to represent a button (BUTTON type) that can contain a text and/or an icon
#define INACTIVE_COLOR
Definition nbgl_obj.h:279
#define INACTIVE_TEXT_COLOR
Definition nbgl_obj.h:280
void nbgl_refreshSpecialWithPostRefresh(nbgl_refresh_mode_t mode, nbgl_post_refresh_t post_refresh)
Definition nbgl_obj.c:1695
@ CHOICE_1_ID
Definition nbgl_obj.h:683
@ TOP_RIGHT_BUTTON_ID
Definition nbgl_obj.h:679
@ LONG_PRESS_BUTTON_ID
Definition nbgl_obj.h:692
@ CONTROLS_ID
Definition nbgl_obj.h:694
@ RIGHT_BUTTON_ID
Definition nbgl_obj.h:677
@ WHOLE_SCREEN_ID
Definition nbgl_obj.h:678
@ BOTTOM_BUTTON_ID
Definition nbgl_obj.h:675
@ SINGLE_BUTTON_ID
Definition nbgl_obj.h:681
@ BACK_BUTTON_ID
Definition nbgl_obj.h:680
@ VALUE_BUTTON_1_ID
Definition nbgl_obj.h:689
@ TIP_BOX_ID
Definition nbgl_obj.h:693
@ EXTRA_BUTTON_ID
Definition nbgl_obj.h:682
@ CHOICE_2_ID
Definition nbgl_obj.h:684
#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