Embedded SDK
Embedded SDK
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"
17 #include "nbgl_layout_internal.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_pic.h"
24 #include "os_helpers.h"
25 #include "lcx_rng.h"
26 
27 /*********************
28  * DEFINES
29  *********************/
30 // half internal margin, between items
31 #define INTERNAL_SPACE 16
32 // inner margin, between buttons
33 #define INNER_MARGIN 12
34 
35 #define NB_MAX_LAYOUTS 3
36 
37 // used by container
38 #define NB_MAX_CONTAINER_CHILDREN 20
39 
40 #define TAG_VALUE_ICON_WIDTH 32
41 
42 #ifdef TARGET_STAX
43 #define RADIO_CHOICE_HEIGHT 96
44 #define FOOTER_HEIGHT 80
45 #define BAR_INTERVALE 12
46 #define BACK_KEY_WIDTH 88
47 #define FOOTER_BUTTON_HEIGHT 128
48 #define UP_FOOTER_BUTTON_HEIGHT 120
49 #define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 192
50 #define ACTION_AND_FOOTER_FOOTER_HEIGHT 216
51 #else // TARGET_STAX
52 #define RADIO_CHOICE_HEIGHT 92
53 #define FOOTER_HEIGHT 80
54 #define BAR_INTERVALE 16
55 #define BACK_KEY_WIDTH 104
56 #define FOOTER_BUTTON_HEIGHT 136
57 #define UP_FOOTER_BUTTON_HEIGHT 136
58 #define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 208
59 #define ACTION_AND_FOOTER_FOOTER_HEIGHT 232
60 #endif // TARGET_STAX
61 
62 // refresh period of the spinner, in ms
63 #define SPINNER_REFRESH_PERIOD 400
64 
65 #ifdef TARGET_STAX
66 #define FIRST_BUTTON_INDEX 0
67 #else // TARGET_STAX
68 // for suggestion buttons, on Flex there are other objects than buttons
69 enum {
71  LEFT_HALF_INDEX, // half disc displayed on the bottom left
72  RIGHT_HALF_INDEX, // half disc displayed on the bottom right
74 };
75 #endif // TARGET_STAX
76 
77 /**********************
78  * MACROS
79  **********************/
80 
81 /**********************
82  * TYPEDEFS
83  **********************/
84 
85 typedef enum {
90 
91 // used to build either a touchable bar or a switch
92 typedef struct {
94  const nbgl_icon_details_t *iconLeft; // a buffer containing the 1BPP icon for icon on
95  // left (can be NULL)
96  const nbgl_icon_details_t *iconRight; // a buffer containing the 1BPP icon for icon 2 (can be
97  // NULL). Dimensions must be the same as iconLeft
98  const char *text; // text (can be NULL)
99  const char *subText; // sub text (can be NULL)
100  uint8_t token; // the token that will be used as argument of the callback
101  nbgl_state_t state; // state of the item
102  bool large; // set to true only for the main level of OS settings
103 #ifdef HAVE_PIEZO_SOUND
104  tune_index_e tuneId; // if not @ref NBGL_NO_TUNE, a tune will be played
105 #endif // HAVE_PIEZO_SOUND
106 } listItem_t;
107 
108 /**********************
109  * VARIABLES
110  **********************/
111 
116 static nbgl_layoutInternal_t gLayout[NB_MAX_LAYOUTS] = {0};
117 
118 // numbers of touchable controls for the whole page
119 static uint8_t nbTouchableControls = 0;
120 
121 /**********************
122  * STATIC PROTOTYPES
123  **********************/
124 extern const char *get_ux_loc_string(uint32_t index);
125 
126 #ifdef HAVE_FAST_HOLD_TO_APPROVE
127 // Unit step in % of touchable progress bar
128 #define HOLD_TO_APPROVE_STEP_PERCENT (7)
129 // Duration in ms the user must hold the progress bar
130 // to make it progress HOLD_TO_APPROVE_STEP_PERCENT %.
131 // This duration must be higher than the screen refresh duration.
132 #define HOLD_TO_APPROVE_STEP_DURATION_MS (100)
133 #else
134 #define HOLD_TO_APPROVE_STEP_PERCENT (25)
135 #define HOLD_TO_APPROVE_STEP_DURATION_MS (400)
136 #endif // HAVE_FAST_HOLD_TO_APPROVE
137 
138 static inline uint8_t get_hold_to_approve_percent(uint32_t touch_duration)
139 {
140 #ifdef HAVE_FAST_HOLD_TO_APPROVE
141  uint8_t current_step_nb = (touch_duration / HOLD_TO_APPROVE_STEP_DURATION_MS);
142 #else
143  uint8_t current_step_nb = (touch_duration / HOLD_TO_APPROVE_STEP_DURATION_MS) + 1;
144 #endif
145  return (current_step_nb * HOLD_TO_APPROVE_STEP_PERCENT);
146 }
147 
148 // function used to retrieve the concerned layout and layout obj matching the given touched obj
149 static bool getLayoutAndLayoutObj(nbgl_obj_t *obj,
150  nbgl_layoutInternal_t **layout,
151  layoutObj_t **layoutObj)
152 {
154 
155  // parse all layouts (starting with modals) to find the object
156  *layout = NULL;
157  while (i > 0) {
158  i--;
159  if (gLayout[i].nbChildren > 0) {
160  uint8_t j;
161 
162  // search index of obj in this layout
163  for (j = 0; j < gLayout[i].nbUsedCallbackObjs; j++) {
164  if (obj == gLayout[i].callbackObjPool[j].obj) {
166  "getLayoutAndLayoutObj(): obj found in layout[%d], index = %d, "
167  "nbUsedCallbackObjs = %d\n",
168  i,
169  j,
170  gLayout[i].nbUsedCallbackObjs);
171  *layout = &gLayout[i];
172  *layoutObj = &(gLayout[i].callbackObjPool[j]);
173  return true;
174  }
175  }
176  }
177  }
178  // not found
179  return false;
180 }
181 
182 static void radioTouchCallback(nbgl_obj_t *obj,
183  nbgl_touchType_t eventType,
184  nbgl_layoutInternal_t *layout);
185 static void longTouchCallback(nbgl_obj_t *obj,
186  nbgl_touchType_t eventType,
187  nbgl_layoutInternal_t *layout,
188  layoutObj_t *layoutObj);
189 
190 // callback for most touched object
191 static void touchCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType)
192 {
193  nbgl_layoutInternal_t *layout;
194  layoutObj_t *layoutObj;
195  bool needRefresh = false;
196 
197  if (obj == NULL) {
198  return;
199  }
200  LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): eventType = %d, obj = %p\n", eventType, obj);
201  if (getLayoutAndLayoutObj(obj, &layout, &layoutObj) == false) {
202  // try with parent, if existing
203  if (getLayoutAndLayoutObj(obj->parent, &layout, &layoutObj) == false) {
204  LOG_WARN(
206  "touchCallback(): eventType = %d, obj = %p, no active layout or obj not found\n",
207  eventType,
208  obj);
209  return;
210  }
211  }
212 
213  // case of swipe
214  if (((eventType == SWIPED_UP) || (eventType == SWIPED_DOWN) || (eventType == SWIPED_LEFT)
215  || (eventType == SWIPED_RIGHT))
216  && (obj->type == CONTAINER)) {
217 #ifdef NBGL_KEYBOARD
218  if (layout->swipeUsage == SWIPE_USAGE_SUGGESTIONS) {
219  keyboardSwipeCallback(obj, eventType);
220  return;
221  }
222 #endif // NBGL_KEYBOARD
223  if (layout->swipeUsage == SWIPE_USAGE_CUSTOM) {
224  layoutObj->index = eventType;
225  }
226  else if ((layout->swipeUsage == SWIPE_USAGE_NAVIGATION)
227  && ((nbgl_obj_t *) layout->container == obj)) {
228  nbgl_container_t *navContainer;
229  if (layout->footerType == FOOTER_NAV) {
230  navContainer = (nbgl_container_t *) layout->footerContainer;
231  }
232  else if (layout->footerType == FOOTER_TEXT_AND_NAV) {
233  navContainer = (nbgl_container_t *) layout->footerContainer->children[1];
234  }
235  else {
236  return;
237  }
238 
240  (nbgl_obj_t *) navContainer, eventType, layout->nbPages, &layout->activePage)
241  == false) {
242  // navigation was impossible
243  return;
244  }
245  layoutObj->index = layout->activePage;
246  }
247  }
248 
249  // case of navigation bar
250  if (((obj->parent == (nbgl_obj_t *) layout->footerContainer)
251  && (layout->footerType == FOOTER_NAV))
252  || ((obj->parent->type == CONTAINER)
253  && (obj->parent->parent == (nbgl_obj_t *) layout->footerContainer)
254  && (layout->footerType == FOOTER_TEXT_AND_NAV))) {
255  if (layoutNavigationCallback(obj, eventType, layout->nbPages, &layout->activePage)
256  == false) {
257  // navigation was impossible
258  return;
259  }
260  layoutObj->index = layout->activePage;
261  }
262 
263  // case of switch
264  if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren >= 2)
265  && (((nbgl_container_t *) obj)->children[1] != NULL)
266  && (((nbgl_container_t *) obj)->children[1]->type == SWITCH)) {
267  nbgl_switch_t *lSwitch = (nbgl_switch_t *) ((nbgl_container_t *) obj)->children[1];
268  lSwitch->state = (lSwitch->state == ON_STATE) ? OFF_STATE : ON_STATE;
269  nbgl_objDraw((nbgl_obj_t *) lSwitch);
270  // refresh will be done after tune playback
271  needRefresh = true;
272  // index is used for state
273  layoutObj->index = lSwitch->state;
274  }
275  // case of radio
276  else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 2)
277  && (((nbgl_container_t *) obj)->children[1] != NULL)
278  && (((nbgl_container_t *) obj)->children[1]->type == RADIO_BUTTON)) {
279  radioTouchCallback(obj, eventType, layout);
280  return;
281  }
282  // case of long press
283  else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 4)
284  && (((nbgl_container_t *) obj)->children[3] != NULL)
285  && (((nbgl_container_t *) obj)->children[3]->type == PROGRESS_BAR)) {
286  longTouchCallback(obj, eventType, layout, layoutObj);
287  return;
288  }
289  LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): layout->callback = %p\n", layout->callback);
290  if (layout->callback != NULL) {
291 #ifdef HAVE_PIEZO_SOUND
292  if (layoutObj->tuneId < NBGL_NO_TUNE) {
293  io_seproxyhal_play_tune(layoutObj->tuneId);
294  }
295 #endif // HAVE_PIEZO_SOUND
296  if (needRefresh) {
298  }
299  layout->callback(layoutObj->token, layoutObj->index);
300  }
301 }
302 
303 // callback for long press button
304 static void longTouchCallback(nbgl_obj_t *obj,
305  nbgl_touchType_t eventType,
306  nbgl_layoutInternal_t *layout,
307  layoutObj_t *layoutObj)
308 {
309  nbgl_container_t *container = (nbgl_container_t *) obj;
310  // 4th child of container is the progress bar
311  nbgl_progress_bar_t *progressBar = (nbgl_progress_bar_t *) container->children[3];
312 
314  "longTouchCallback(): eventType = %d, obj = %p, gLayout[1].nbChildren = %d\n",
315  eventType,
316  obj,
317  gLayout[1].nbChildren);
318 
319  // case of pressing a long press button
320  if (eventType == TOUCHING) {
321  uint32_t touchDuration = nbgl_touchGetTouchDuration(obj);
322 
323  // Compute the new progress bar state in %
324  uint8_t new_state = get_hold_to_approve_percent(touchDuration);
325 
326  // Ensure the callback is triggered once,
327  // when the progress bar state reaches 100%
328  bool trigger_callback = (new_state >= 100) && (progressBar->state < 100);
329 
330  // Cap progress bar state at 100%
331  if (new_state >= 100) {
332  new_state = 100;
333  }
334 
335  // Update progress bar state
336  if (new_state != progressBar->state) {
337  progressBar->partialRedraw = true;
338  progressBar->state = new_state;
339 
340  nbgl_objDraw((nbgl_obj_t *) progressBar);
341  // Ensure progress bar is fully drawn
342  // before calling the callback.
345  }
346 
347  if (trigger_callback) {
348  // End of progress bar reached: trigger callback
349  if (layout->callback != NULL) {
350  layout->callback(layoutObj->token, layoutObj->index);
351  }
352  }
353  }
354  // case of releasing a long press button (or getting out of it)
355  else if ((eventType == TOUCH_RELEASED) || (eventType == OUT_OF_TOUCH)
356  || (eventType == SWIPED_LEFT) || (eventType == SWIPED_RIGHT)) {
358  progressBar->partialRedraw = true;
359  progressBar->state = 0;
360  nbgl_objDraw((nbgl_obj_t *) progressBar);
362  }
363 }
364 
365 // callback for radio button touch
366 static void radioTouchCallback(nbgl_obj_t *obj,
367  nbgl_touchType_t eventType,
368  nbgl_layoutInternal_t *layout)
369 {
370  uint8_t i = NB_MAX_LAYOUTS, radioIndex = 0, foundRadio = 0xFF, foundRadioIndex;
371 
372  if (eventType != TOUCHED) {
373  return;
374  }
375 
376  i = 0;
377  // parse all objs to find all containers of radio buttons
378  while (i < layout->nbUsedCallbackObjs) {
379  if ((obj == (nbgl_obj_t *) layout->callbackObjPool[i].obj)
380  && (layout->callbackObjPool[i].obj->type == CONTAINER)) {
381  nbgl_radio_t *radio
382  = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
383  ->children[1];
384  nbgl_text_area_t *textArea
386  ->children[0];
387  foundRadio = i;
388  foundRadioIndex = radioIndex;
389  // set text as active (black and bold)
390  textArea->textColor = BLACK;
391  textArea->fontId = SMALL_BOLD_FONT;
392  // ensure that radio button is ON
393  radio->state = ON_STATE;
394  // redraw container
395  nbgl_objDraw((nbgl_obj_t *) obj);
396  }
397  else if ((layout->callbackObjPool[i].obj->type == CONTAINER)
398  && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->nbChildren == 2)
399  && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->children[1]->type
400  == RADIO_BUTTON)) {
401  nbgl_radio_t *radio
402  = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
403  ->children[1];
404  nbgl_text_area_t *textArea
406  ->children[0];
407  radioIndex++;
408  // set to OFF the one that was in ON
409  if (radio->state == ON_STATE) {
410  radio->state = OFF_STATE;
411  // set text it as inactive (gray and normal)
412  textArea->textColor = DARK_GRAY;
413  textArea->fontId = SMALL_REGULAR_FONT;
414  // redraw container
415  nbgl_objDraw((nbgl_obj_t *) layout->callbackObjPool[i].obj);
416  }
417  }
418  i++;
419  }
420  // call callback after redraw to avoid asynchronicity
421  if (foundRadio != 0xFF) {
422  if (layout->callback != NULL) {
423 #ifdef HAVE_PIEZO_SOUND
424  if (layout->callbackObjPool[foundRadio].tuneId < NBGL_NO_TUNE) {
425  io_seproxyhal_play_tune(layout->callbackObjPool[foundRadio].tuneId);
426  }
428 #endif // HAVE_PIEZO_SOUND
429  layout->callback(layout->callbackObjPool[foundRadio].token, foundRadioIndex);
430  }
431  }
432 }
433 
434 // callback for spinner ticker
435 static void spinnerTickerCallback(void)
436 {
437  nbgl_spinner_t *spinner;
438  uint8_t i = 0;
439  nbgl_layoutInternal_t *layout;
440 
441  // gLayout[1] is on top of gLayout[0] so if gLayout[1] is active, it must catch the event
442  if (gLayout[1].nbChildren > 0) {
443  layout = &gLayout[1];
444  }
445  else {
446  layout = &gLayout[0];
447  }
448 
449  // get index of obj
450  while (i < layout->container->nbChildren) {
451  if (layout->container->children[i]->type == CONTAINER) {
452  nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
453  if (container->children[0]->type == SPINNER) {
454  spinner = (nbgl_spinner_t *) container->children[0];
455  spinner->position++;
456  spinner->position &= 3; // modulo 4
457  nbgl_objDraw((nbgl_obj_t *) spinner);
459  return;
460  }
461  }
462  i++;
463  }
464 }
465 
466 // callback for animation ticker
467 static void animTickerCallback(void)
468 {
469  nbgl_image_t *image;
470  uint8_t i = 0;
471  nbgl_layoutInternal_t *layout;
472 
473  // gLayout[1] is on top of gLayout[0] so if gLayout[1] is active, it must catch the event
474  if (gLayout[1].nbChildren > 0) {
475  layout = &gLayout[1];
476  }
477  else {
478  layout = &gLayout[0];
479  }
480 
481  if (layout->animation == NULL) {
482  return;
483  }
484 
485  // get index of image obj
486  while (i < layout->container->nbChildren) {
487  if (layout->container->children[i]->type == CONTAINER) {
488  nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
489  if (container->children[1]->type == IMAGE) {
490  image = (nbgl_image_t *) container->children[1];
491  if (layout->animation->parsing == LOOP_PARSING) {
492  if (layout->iconIdxInAnim == (layout->animation->nbIcons - 1)) {
493  layout->iconIdxInAnim = 0;
494  }
495  else {
496  layout->iconIdxInAnim++;
497  }
498  }
499  else {
500  // Flip incrementAnim when reaching upper or lower limit
501  if ((layout->incrementAnim)
502  && (layout->iconIdxInAnim >= layout->animation->nbIcons - 1)) {
503  layout->incrementAnim = false;
504  }
505  else if (layout->iconIdxInAnim == 0) {
506  layout->incrementAnim = true;
507  }
508 
509  // Increase / Decrease index according to incrementAnim
510  if (layout->incrementAnim) {
511  layout->iconIdxInAnim++;
512  }
513  else {
514  layout->iconIdxInAnim--;
515  }
516  }
517  image->buffer = layout->animation->icons[layout->iconIdxInAnim];
518  nbgl_objDraw((nbgl_obj_t *) image);
521  return;
522  }
523  }
524  i++;
525  }
526 }
527 
528 static nbgl_line_t *createHorizontalLine(uint8_t layer)
529 {
530  nbgl_line_t *line;
531 
532  line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
533  line->lineColor = LIGHT_GRAY;
534  line->obj.area.width = SCREEN_WIDTH;
535  line->obj.area.height = 4;
536  line->direction = HORIZONTAL;
537  line->thickness = 1;
538  return line;
539 }
540 
541 static nbgl_line_t *createLeftVerticalLine(uint8_t layer)
542 {
543  nbgl_line_t *line;
544 
545  line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
546  line->lineColor = LIGHT_GRAY;
547  line->obj.area.width = 1;
548  line->obj.area.height = SCREEN_HEIGHT;
549  line->direction = VERTICAL;
550  line->thickness = 1;
551  line->obj.alignment = MID_LEFT;
552  return line;
553 }
554 
555 // function adding a layout object in the callbackObjPool array for the given layout, and
556 // configuring it
558  nbgl_obj_t *obj,
559  uint8_t token,
560  tune_index_e tuneId)
561 {
562  layoutObj_t *layoutObj = NULL;
563 
564  if (layout->nbUsedCallbackObjs < (LAYOUT_OBJ_POOL_LEN - 1)) {
565  layoutObj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
566  layout->nbUsedCallbackObjs++;
567  layoutObj->obj = obj;
568  layoutObj->token = token;
569  layoutObj->tuneId = tuneId;
570  }
571  else {
572  LOG_FATAL(LAYOUT_LOGGER, "layoutAddCallbackObj: no more callback obj\n");
573  }
574 
575  return layoutObj;
576 }
577 
585 {
586  if (layout->container->nbChildren == NB_MAX_CONTAINER_CHILDREN) {
587  LOG_FATAL(LAYOUT_LOGGER, "layoutAddObject(): No more object\n");
588  }
589  layout->container->children[layout->container->nbChildren] = obj;
590  layout->container->nbChildren++;
591 }
592 
602 static int addSwipeInternal(nbgl_layoutInternal_t *layoutInt,
603  uint16_t swipesMask,
604  nbgl_swipe_usage_t usage,
605  uint8_t token,
606  tune_index_e tuneId)
607 {
608  layoutObj_t *obj;
609 
610  if ((swipesMask & SWIPE_MASK) == 0) {
611  return -1;
612  }
613 
614  obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) layoutInt->container, token, tuneId);
615  if (obj == NULL) {
616  return -1;
617  }
618  layoutInt->container->obj.touchMask = swipesMask;
619  layoutInt->swipeUsage = usage;
620 
621  return 0;
622 }
623 
631 static nbgl_container_t *addListItem(nbgl_layoutInternal_t *layoutInt, const listItem_t *itemDesc)
632 {
633  layoutObj_t *obj;
634  nbgl_text_area_t *textArea;
635  nbgl_container_t *container;
636  color_t color = ((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == OFF_STATE))
637  ? LIGHT_GRAY
638  : BLACK;
639  int16_t usedHeight = 40;
640 
641  LOG_DEBUG(LAYOUT_LOGGER, "addListItem():\n");
642 
643  container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
644  obj = layoutAddCallbackObj(
645  layoutInt, (nbgl_obj_t *) container, itemDesc->token, itemDesc->tuneId);
646  if (obj == NULL) {
647  return NULL;
648  }
649 
650  // get container children (up to 4: text + left+right icons + sub text)
651  container->children = nbgl_containerPoolGet(4, layoutInt->layer);
652  container->nbChildren = 0;
653 
654  container->obj.area.width = AVAILABLE_WIDTH;
655  container->obj.area.height = itemDesc->large ? TOUCHABLE_MAIN_BAR_HEIGHT : TOUCHABLE_BAR_HEIGHT;
656  container->layout = HORIZONTAL;
657  container->obj.alignmentMarginX = BORDER_MARGIN;
658  container->obj.alignment = NO_ALIGNMENT;
659  container->obj.alignTo = NULL;
660  // the bar can only be touched if not inactive AND if one of the icon is present
661  // otherwise it is seen as a title
662  if (((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == ON_STATE))
663  || (itemDesc->type == SWITCH_ITEM)) {
664  container->obj.touchMask = (1 << TOUCHED);
665  container->obj.touchId = CONTROLS_ID + nbTouchableControls;
666  nbTouchableControls++;
667  }
668 
669  // allocate main text because always present
670  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
671  textArea->textColor = color;
672  textArea->text = PIC(itemDesc->text);
673  textArea->onDrawCallback = NULL;
674  textArea->fontId = SMALL_BOLD_FONT;
675  textArea->wrapping = true;
676  textArea->obj.area.width = container->obj.area.width;
677  if (itemDesc->iconLeft != NULL) {
678  // reduce text width accordingly
679  textArea->obj.area.width
680  -= ((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE;
681  }
682  if (itemDesc->iconRight != NULL) {
683  // reduce text width accordingly
684  textArea->obj.area.width
685  -= ((nbgl_icon_details_t *) PIC(itemDesc->iconRight))->width + BAR_INTERVALE;
686  }
687  else if (itemDesc->type == SWITCH_ITEM) {
688  textArea->obj.area.width -= 60 + BAR_INTERVALE;
689  }
690  textArea->obj.area.height = nbgl_getTextHeightInWidth(
691  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
692  usedHeight = MAX(usedHeight, textArea->obj.area.height);
693  textArea->style = NO_STYLE;
694  textArea->obj.alignment = MID_LEFT;
695  textArea->textAlignment = MID_LEFT;
696  container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
697  container->nbChildren++;
698 
699  // allocate left icon if present
700  if (itemDesc->iconLeft != NULL) {
701  nbgl_image_t *imageLeft = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
702  imageLeft->foregroundColor = color;
703  imageLeft->buffer = PIC(itemDesc->iconLeft);
704  // align at the left of text
705  imageLeft->obj.alignment = MID_LEFT;
706  imageLeft->obj.alignTo = (nbgl_obj_t *) textArea;
707  imageLeft->obj.alignmentMarginX = BAR_INTERVALE;
708  container->children[container->nbChildren] = (nbgl_obj_t *) imageLeft;
709  container->nbChildren++;
710 
711  textArea->obj.alignmentMarginX = imageLeft->buffer->width + BAR_INTERVALE;
712 
713  usedHeight = MAX(usedHeight, imageLeft->buffer->height);
714  }
715  // allocate right icon if present
716  if (itemDesc->iconRight != NULL) {
717  nbgl_image_t *imageRight = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
718  imageRight->foregroundColor = color;
719  imageRight->buffer = PIC(itemDesc->iconRight);
720  // align at the right of text
721  imageRight->obj.alignment = MID_RIGHT;
722  imageRight->obj.alignmentMarginX = BAR_INTERVALE;
723  imageRight->obj.alignTo = (nbgl_obj_t *) textArea;
724 
725  container->children[container->nbChildren] = (nbgl_obj_t *) imageRight;
726  container->nbChildren++;
727 
728  usedHeight = MAX(usedHeight, imageRight->buffer->height);
729  }
730  else if (itemDesc->type == SWITCH_ITEM) {
731  nbgl_switch_t *switchObj = (nbgl_switch_t *) nbgl_objPoolGet(SWITCH, layoutInt->layer);
732  switchObj->onColor = BLACK;
733  switchObj->offColor = LIGHT_GRAY;
734  switchObj->state = itemDesc->state;
735  switchObj->obj.alignment = MID_RIGHT;
736  switchObj->obj.alignmentMarginX = BAR_INTERVALE;
737  switchObj->obj.alignTo = (nbgl_obj_t *) textArea;
738 
739  container->children[container->nbChildren] = (nbgl_obj_t *) switchObj;
740  container->nbChildren++;
741  }
742 
743  if (itemDesc->subText != NULL) {
744  nbgl_text_area_t *subTextArea
745  = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
746 
747  subTextArea->textColor = color;
748  subTextArea->text = PIC(itemDesc->subText);
749  subTextArea->textAlignment = MID_LEFT;
750  subTextArea->fontId = SMALL_REGULAR_FONT;
751  subTextArea->style = NO_STYLE;
752  subTextArea->wrapping = true;
753  subTextArea->obj.alignment = MID_LEFT;
754  subTextArea->obj.area.width = container->obj.area.width;
755  subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
756  subTextArea->text,
757  subTextArea->obj.area.width,
758  subTextArea->wrapping);
759  container->children[container->nbChildren] = (nbgl_obj_t *) subTextArea;
760  container->nbChildren++;
761  container->obj.area.height += subTextArea->obj.area.height + 12;
762 
763  // modify alignments to have sub-text under (icon left - text - icon right)
764  textArea->obj.alignmentMarginY = -(subTextArea->obj.area.height + 12) / 2;
765  subTextArea->obj.alignmentMarginY = (usedHeight + 12) / 2;
766  }
767 
768  // set this new container as child of main container
769  layoutAddObject(layoutInt, (nbgl_obj_t *) container);
770 
771  return container;
772 }
773 
782 static nbgl_container_t *addContentCenter(nbgl_layoutInternal_t *layoutInt,
783  const nbgl_contentCenter_t *info)
784 {
785  nbgl_container_t *container;
786  nbgl_text_area_t *textArea = NULL;
787  nbgl_image_t *image = NULL;
788  uint16_t fullHeight = 0;
789 
790  container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
791 
792  // get container children
793  container->nbChildren = 0;
794  // the max number is: icon + anim + title + small title + description + sub-text
795  container->children = nbgl_containerPoolGet(6, layoutInt->layer);
796 
797  // add icon or animation if present
798  if (info->icon != NULL) {
799  image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
800  image->foregroundColor = BLACK;
801  image->buffer = PIC(info->icon);
802  image->obj.alignment = TOP_MIDDLE;
803  image->obj.alignmentMarginY = info->iconHug;
804 
805  fullHeight += image->buffer->height + info->iconHug;
806  container->children[container->nbChildren] = (nbgl_obj_t *) image;
807  container->nbChildren++;
808 
809  if (info->illustrType == ANIM_ILLUSTRATION) {
810  nbgl_image_t *anim;
811  anim = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
812  anim->foregroundColor = BLACK;
813  anim->buffer = PIC(info->animation->icons[0]);
814  anim->obj.alignment = TOP_MIDDLE;
815  anim->obj.alignmentMarginY = info->iconHug + info->animOffsetY;
816  anim->obj.alignmentMarginX = info->animOffsetX;
817 
818  container->children[container->nbChildren] = (nbgl_obj_t *) anim;
819  container->nbChildren++;
820 
821  layoutInt->animation = info->animation;
822  layoutInt->incrementAnim = true;
823  layoutInt->iconIdxInAnim = 0;
824 
825  // update ticker to update the animation periodically
827 
828  tickerCfg.tickerIntervale = info->animation->delayMs; // ms
829  tickerCfg.tickerValue = info->animation->delayMs; // ms
830  tickerCfg.tickerCallback = &animTickerCallback;
831  nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
832  }
833  }
834  // add title if present
835  if (info->title != NULL) {
836  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
837  textArea->textColor = BLACK;
838  textArea->text = PIC(info->title);
839  textArea->textAlignment = CENTER;
840  textArea->fontId = LARGE_MEDIUM_FONT;
841  textArea->wrapping = true;
842  textArea->obj.area.width = AVAILABLE_WIDTH;
843  textArea->obj.area.height = nbgl_getTextHeightInWidth(
844  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
845 
846  // if not the first child, put on bottom of the previous, with a margin
847  if (container->nbChildren > 0) {
848  textArea->obj.alignment = BOTTOM_MIDDLE;
849  textArea->obj.alignTo = (nbgl_obj_t *) image;
850  textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN + info->iconHug;
851  }
852  else {
853  textArea->obj.alignment = TOP_MIDDLE;
854  }
855 
856  fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
857 
858  container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
859  container->nbChildren++;
860  }
861  // add small title if present
862  if (info->smallTitle != NULL) {
863  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
864  textArea->textColor = BLACK;
865  textArea->text = PIC(info->smallTitle);
866  textArea->textAlignment = CENTER;
867  textArea->fontId = SMALL_BOLD_FONT;
868  textArea->wrapping = true;
869  textArea->obj.area.width = AVAILABLE_WIDTH;
870  textArea->obj.area.height = nbgl_getTextHeightInWidth(
871  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
872 
873  // if not the first child, put on bottom of the previous, with a margin
874  if (container->nbChildren > 0) {
875  textArea->obj.alignment = BOTTOM_MIDDLE;
876  textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
877  textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN;
878  if (container->children[container->nbChildren - 1]->type == IMAGE) {
879  textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN + info->iconHug;
880  }
881  else {
882  textArea->obj.alignmentMarginY = 16;
883  }
884  }
885  else {
886  textArea->obj.alignment = TOP_MIDDLE;
887  }
888 
889  fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
890 
891  container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
892  container->nbChildren++;
893  }
894  // add description if present
895  if (info->description != NULL) {
896  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
897  textArea->textColor = BLACK;
898  textArea->text = PIC(info->description);
899  textArea->textAlignment = CENTER;
900  textArea->fontId = SMALL_REGULAR_FONT;
901  textArea->wrapping = true;
902  textArea->obj.area.width = AVAILABLE_WIDTH;
903  textArea->obj.area.height = nbgl_getTextHeightInWidth(
904  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
905 
906  // if not the first child, put on bottom of the previous, with a margin
907  if (container->nbChildren > 0) {
908  textArea->obj.alignment = BOTTOM_MIDDLE;
909  textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
910  if (container->children[container->nbChildren - 1]->type == TEXT_AREA) {
911  // if previous element is text, only space of 16 px
912  textArea->obj.alignmentMarginY = 16;
913  }
914  else {
915  textArea->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN + info->iconHug;
916  }
917  }
918  else {
919  textArea->obj.alignment = TOP_MIDDLE;
920  }
921 
922  fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
923 
924  container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
925  container->nbChildren++;
926  }
927  // add sub-text if present
928  if (info->subText != NULL) {
929  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
930  textArea->textColor = DARK_GRAY;
931  textArea->text = PIC(info->subText);
932  textArea->textAlignment = CENTER;
933  textArea->fontId = SMALL_REGULAR_FONT;
934  textArea->wrapping = true;
935  textArea->obj.area.width = AVAILABLE_WIDTH;
936  textArea->obj.area.height = nbgl_getTextHeightInWidth(
937  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
938  // sub-text is included in a hug of 8px
939  textArea->obj.area.height += 2 * 8;
940  // if not the first child, put on bottom of the previous, with a margin
941  if (container->nbChildren > 0) {
942  textArea->obj.alignment = BOTTOM_MIDDLE;
943  textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
944  textArea->obj.alignmentMarginY = 16;
945  if (container->children[container->nbChildren - 1]->type == IMAGE) {
946  textArea->obj.alignmentMarginY += info->iconHug;
947  }
948  }
949  else {
950  textArea->obj.alignment = TOP_MIDDLE;
951  }
952 
953  fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
954 
955  container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
956  container->nbChildren++;
957  }
958  container->layout = VERTICAL;
959  container->obj.alignment = CENTER;
960  container->obj.area.width = AVAILABLE_WIDTH;
961  container->obj.area.height = fullHeight;
962  if (info->padding) {
963  container->obj.area.height += 40;
964  }
965 
966  // set this new container as child of main container
967  layoutAddObject(layoutInt, (nbgl_obj_t *) container);
968 
969  return container;
970 }
971 
972 static int addText(nbgl_layout_t *layout,
973  const char *text,
974  const char *subText,
975  uint8_t token,
976  uint8_t index,
977  bool withAlias)
978 {
979  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
980  nbgl_container_t *container;
981  nbgl_text_area_t *textArea;
982  nbgl_text_area_t *subTextArea;
983  uint16_t fullHeight = 0;
984 
985  if (layout == NULL) {
986  return -1;
987  }
988  container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
989 
990  // get container children
991  container->children = nbgl_containerPoolGet(withAlias ? 3 : 2, layoutInt->layer);
992  container->obj.area.width = AVAILABLE_WIDTH;
993 
994  if (text != NULL) {
995  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
996 
997  textArea->textColor = BLACK;
998  textArea->text = PIC(text);
999  textArea->textAlignment = MID_LEFT;
1000  textArea->fontId = SMALL_BOLD_FONT;
1001  textArea->style = NO_STYLE;
1002  textArea->wrapping = true;
1003  textArea->obj.alignment = NO_ALIGNMENT;
1004  textArea->obj.alignmentMarginY = PRE_TEXT_MARGIN;
1005  textArea->obj.area.width = container->obj.area.width;
1006  if (withAlias == true) {
1007  textArea->obj.area.width -= 12 + MINI_PUSH_ICON.width;
1008  }
1009  textArea->obj.area.height = nbgl_getTextHeightInWidth(
1010  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1011  fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1012  container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1013  container->nbChildren++;
1014  if (withAlias == true) {
1015  nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
1016  // the whole container is touchable
1017  layoutObj_t *obj
1018  = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) container, token, TUNE_TAP_CASUAL);
1019  obj->index = index;
1020  image->foregroundColor = BLACK;
1021  image->buffer = &PUSH_ICON;
1022  image->obj.alignment = RIGHT_TOP;
1023  image->obj.alignmentMarginX = 12;
1024  image->obj.alignTo = (nbgl_obj_t *) textArea;
1025  container->obj.touchMask = (1 << TOUCHED);
1026  container->obj.touchId = VALUE_BUTTON_1_ID + index;
1027 
1028  container->children[container->nbChildren] = (nbgl_obj_t *) image;
1029  container->nbChildren++;
1030  }
1031  }
1032  if (subText != NULL) {
1033  subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1034  subTextArea->textColor = BLACK;
1035  subTextArea->text = PIC(subText);
1036  subTextArea->fontId = SMALL_REGULAR_FONT;
1037  subTextArea->style = NO_STYLE;
1038  subTextArea->wrapping = true;
1039  subTextArea->obj.area.width = container->obj.area.width;
1040  subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
1041  subTextArea->text,
1042  subTextArea->obj.area.width,
1043  subTextArea->wrapping);
1044  subTextArea->textAlignment = MID_LEFT;
1045  subTextArea->obj.alignment = NO_ALIGNMENT;
1046  if (text != NULL) {
1047  subTextArea->obj.alignmentMarginY = TEXT_SUBTEXT_MARGIN;
1048  fullHeight += POST_SUBTEXT_MARGIN; // under the subText
1049  }
1050  else {
1051 #ifdef TARGET_STAX
1052  subTextArea->obj.alignmentMarginY = BORDER_MARGIN;
1053  fullHeight += BORDER_MARGIN;
1054 #else // TARGET_STAX
1055  subTextArea->obj.alignmentMarginY = 26;
1056  fullHeight += 26; // under the subText
1057 #endif // TARGET_STAX
1058  }
1059  container->children[container->nbChildren] = (nbgl_obj_t *) subTextArea;
1060  container->nbChildren++;
1061  fullHeight += subTextArea->obj.area.height + subTextArea->obj.alignmentMarginY;
1062  }
1063  else {
1064  fullHeight += PRE_TEXT_MARGIN;
1065  }
1066  container->obj.area.height = fullHeight;
1067  container->layout = VERTICAL;
1068  container->obj.alignmentMarginX = BORDER_MARGIN;
1069  container->obj.alignment = NO_ALIGNMENT;
1070  // set this new obj as child of main container
1071  layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1072 
1073  return container->obj.area.height;
1074 }
1075 
1076 /**********************
1077  * GLOBAL FUNCTIONS
1078  **********************/
1079 
1087 {
1088  nbgl_layoutInternal_t *layout = NULL;
1089 
1090  // find an empty layout in the proper "layer"
1091  if (description->modal) {
1092  if (gLayout[1].nbChildren == 0) {
1093  layout = &gLayout[1];
1094  }
1095  else if (gLayout[2].nbChildren == 0) {
1096  layout = &gLayout[2];
1097  }
1098  }
1099  else {
1100  // automatically "release" a potentially opened non-modal layout
1101  gLayout[0].nbChildren = 0;
1102  layout = &gLayout[0];
1103  }
1104  if (layout == NULL) {
1105  LOG_WARN(LAYOUT_LOGGER, "nbgl_layoutGet(): impossible to get a layout!\n");
1106  return NULL;
1107  }
1108 
1109  // reset globals
1110  memset(layout, 0, sizeof(nbgl_layoutInternal_t));
1111 
1112  nbTouchableControls = 0;
1113 
1114  layout->callback = (nbgl_layoutTouchCallback_t) PIC(description->onActionCallback);
1115  layout->modal = description->modal;
1116  layout->withLeftBorder = description->withLeftBorder;
1117  if (description->modal) {
1118  layout->layer = nbgl_screenPush(&layout->children,
1120  &description->ticker,
1121  (nbgl_touchCallback_t) touchCallback);
1122  }
1123  else {
1124  nbgl_screenSet(&layout->children,
1126  &description->ticker,
1127  (nbgl_touchCallback_t) touchCallback);
1128  layout->layer = 0;
1129  }
1130  layout->container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layout->layer);
1131  layout->container->obj.area.width = SCREEN_WIDTH;
1132  layout->container->obj.area.height = SCREEN_HEIGHT;
1133  layout->container->layout = VERTICAL;
1134  layout->container->children = nbgl_containerPoolGet(NB_MAX_CONTAINER_CHILDREN, layout->layer);
1135  // by default, if no header, main container is aligned on top-left
1136  layout->container->obj.alignment = TOP_LEFT;
1137  // main container is always the second object, leaving space for header
1138  layout->children[MAIN_CONTAINER_INDEX] = (nbgl_obj_t *) layout->container;
1140 
1141  // if a tap text is defined, make the container tapable and display this text in gray
1142  if (description->tapActionText != NULL) {
1143  layoutObj_t *obj;
1144 
1145  obj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
1146  layout->nbUsedCallbackObjs++;
1147  obj->obj = (nbgl_obj_t *) layout->container;
1148  obj->token = description->tapActionToken;
1149  obj->tuneId = description->tapTuneId;
1150  layout->container->obj.touchMask = (1 << TOUCHED);
1151  layout->container->obj.touchId = WHOLE_SCREEN_ID;
1152 
1153  nbgl_layoutUpFooter_t footerDesc;
1154  footerDesc.type = UP_FOOTER_TEXT;
1155  footerDesc.text.text = PIC(description->tapActionText);
1156  footerDesc.text.token = description->tapActionToken;
1157  footerDesc.text.tuneId = description->tapTuneId;
1158  nbgl_layoutAddUpFooter((nbgl_layout_t *) layout, &footerDesc);
1159  }
1160 
1161  return (nbgl_layout_t *) layout;
1162 }
1163 
1176  uint16_t swipesMask,
1177  const char *text,
1178  uint8_t token,
1179  tune_index_e tuneId)
1180 {
1181  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1182 
1183  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwipe():\n");
1184  if (layout == NULL) {
1185  return -1;
1186  }
1187 
1188  if (text) {
1189  // create 'tap to continue' text area
1190  layoutInt->tapText = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, 0);
1191  layoutInt->tapText->text = PIC(text);
1192  layoutInt->tapText->textColor = DARK_GRAY;
1193  layoutInt->tapText->fontId = SMALL_REGULAR_FONT;
1194  layoutInt->tapText->obj.area.width = AVAILABLE_WIDTH;
1195  layoutInt->tapText->obj.area.height = nbgl_getFontLineHeight(layoutInt->tapText->fontId);
1196  layoutInt->tapText->textAlignment = CENTER;
1197 #ifdef TARGET_STAX
1198  layoutInt->tapText->obj.alignmentMarginY = BORDER_MARGIN;
1199 #else // TARGET_STAX
1200  layoutInt->tapText->obj.alignmentMarginY = 30;
1201 #endif // TARGET_STAX
1202  layoutInt->tapText->obj.alignment = BOTTOM_MIDDLE;
1203  }
1204  return addSwipeInternal(layoutInt, swipesMask, SWIPE_USAGE_CUSTOM, token, tuneId);
1205 }
1206 
1217  const nbgl_icon_details_t *icon,
1218  uint8_t token,
1219  tune_index_e tuneId)
1220 {
1221  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1222  layoutObj_t *obj;
1223  nbgl_button_t *button;
1224 
1225  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTopRightButton():\n");
1226  if (layout == NULL) {
1227  return -1;
1228  }
1229  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
1230  obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) button, token, tuneId);
1231  if (obj == NULL) {
1232  return -1;
1233  }
1234 
1235  button->obj.area.width = BUTTON_DIAMETER;
1236  button->obj.area.height = BUTTON_DIAMETER;
1237  button->radius = BUTTON_RADIUS;
1238  button->obj.alignmentMarginX = BORDER_MARGIN;
1239  button->obj.alignmentMarginY = BORDER_MARGIN;
1240  button->foregroundColor = BLACK;
1241  button->innerColor = WHITE;
1242  button->borderColor = LIGHT_GRAY;
1243  button->obj.touchMask = (1 << TOUCHED);
1244  button->obj.touchId = TOP_RIGHT_BUTTON_ID;
1245  button->icon = PIC(icon);
1246  button->obj.alignment = TOP_RIGHT;
1247 
1248  // add to screen
1249  layoutInt->children[TOP_RIGHT_BUTTON_INDEX] = (nbgl_obj_t *) button;
1250 
1251  return 0;
1252 }
1253 
1262 {
1263  nbgl_layoutFooter_t footerDesc;
1264  footerDesc.type = FOOTER_NAV;
1265  footerDesc.separationLine = info->withSeparationLine;
1266  footerDesc.navigation.activePage = info->activePage;
1267  footerDesc.navigation.nbPages = info->nbPages;
1268  footerDesc.navigation.withExitKey = info->withExitKey;
1269  footerDesc.navigation.withBackKey = info->withBackKey;
1270  footerDesc.navigation.withPageIndicator = false;
1271  footerDesc.navigation.token = info->token;
1272  footerDesc.navigation.tuneId = info->tuneId;
1273  return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1274 }
1275 
1288  const nbgl_icon_details_t *icon,
1289  uint8_t token,
1290  bool separationLine,
1291  tune_index_e tuneId)
1292 {
1293  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddBottomButton():\n");
1294  nbgl_layoutFooter_t footerDesc;
1295  footerDesc.type = FOOTER_SIMPLE_BUTTON;
1296  footerDesc.separationLine = separationLine;
1297  footerDesc.button.fittingContent = false;
1298  footerDesc.button.icon = PIC(icon);
1299  footerDesc.button.text = NULL;
1300  footerDesc.button.token = token;
1301  footerDesc.button.tuneId = tuneId;
1302  footerDesc.button.style = WHITE_BACKGROUND;
1303  return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1304 }
1305 
1314 {
1315  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1316  nbgl_container_t *container;
1317  listItem_t itemDesc;
1318 
1319  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar():\n");
1320  if (layout == NULL) {
1321  return -1;
1322  }
1323  // main text is mandatory
1324  if (barLayout->text == NULL) {
1325  LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar(): main text is mandatory\n");
1326  }
1327 
1328  itemDesc.iconLeft = barLayout->iconLeft;
1329  itemDesc.iconRight = barLayout->iconRight;
1330  itemDesc.text = barLayout->text;
1331  itemDesc.subText = barLayout->subText;
1332  itemDesc.token = barLayout->token;
1333  itemDesc.tuneId = barLayout->tuneId;
1334  itemDesc.state = (barLayout->inactive) ? OFF_STATE : ON_STATE;
1335  itemDesc.large = barLayout->large;
1336  itemDesc.type = TOUCHABLE_BAR_ITEM;
1337  container = addListItem(layoutInt, &itemDesc);
1338 
1339  if (container == NULL) {
1340  return -1;
1341  }
1342  return container->obj.area.height;
1343 }
1344 
1352 int nbgl_layoutAddSwitch(nbgl_layout_t *layout, const nbgl_layoutSwitch_t *switchLayout)
1353 {
1354  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1355  nbgl_container_t *container;
1356  listItem_t itemDesc;
1357 
1358  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwitch():\n");
1359  if (layout == NULL) {
1360  return -1;
1361  }
1362  // main text is mandatory
1363  if (switchLayout->text == NULL) {
1364  LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddSwitch(): main text is mandatory\n");
1365  }
1366 
1367  itemDesc.iconLeft = NULL;
1368  itemDesc.iconRight = NULL;
1369  itemDesc.text = switchLayout->text;
1370  itemDesc.subText = switchLayout->subText;
1371  itemDesc.token = switchLayout->token;
1372  itemDesc.tuneId = switchLayout->tuneId;
1373  itemDesc.state = switchLayout->initState;
1374  itemDesc.large = false;
1375  itemDesc.type = SWITCH_ITEM;
1376  container = addListItem(layoutInt, &itemDesc);
1377 
1378  if (container == NULL) {
1379  return -1;
1380  }
1381  return container->obj.area.height;
1382 }
1383 
1392 int nbgl_layoutAddText(nbgl_layout_t *layout, const char *text, const char *subText)
1393 {
1394  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddText():\n");
1395  return addText(layout, text, subText, 0, 0, false);
1396 }
1397 
1410  const char *text,
1411  const char *subText,
1412  uint8_t token,
1413  uint8_t index)
1414 {
1415  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextWithAlias():\n");
1416  return addText(layout, text, subText, token, index, true);
1417 }
1418 
1426 int nbgl_layoutAddSubHeaderText(nbgl_layout_t *layout, const char *text)
1427 {
1428  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1429  nbgl_text_area_t *textArea;
1430 
1431  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSubHeaderText():\n");
1432  if (layout == NULL) {
1433  return -1;
1434  }
1435  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1436 
1437  textArea->textColor = BLACK;
1438  textArea->text = PIC(text);
1439  textArea->textAlignment = MID_LEFT;
1440  textArea->fontId = SMALL_REGULAR_FONT;
1441  textArea->style = NO_STYLE;
1442  textArea->wrapping = true;
1443  textArea->obj.alignment = NO_ALIGNMENT;
1444  textArea->obj.alignmentMarginX = BORDER_MARGIN;
1445  textArea->obj.area.width = AVAILABLE_WIDTH;
1446  textArea->obj.area.height = nbgl_getTextHeightInWidth(
1447  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1448 #ifdef TARGET_STAX
1449  textArea->obj.area.height += 2 * 24;
1450 #else // TARGET_STAX
1451  textArea->obj.area.height += 2 * 28;
1452 #endif // TARGET_STAX
1453 
1454  // set this new obj as child of main container
1455  layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1456 
1457  return textArea->obj.area.height;
1458 }
1459 
1468 int nbgl_layoutAddLargeCaseText(nbgl_layout_t *layout, const char *text, bool grayedOut)
1469 {
1470  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1471  nbgl_text_area_t *textArea;
1472 
1473  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLargeCaseText():\n");
1474  if (layout == NULL) {
1475  return -1;
1476  }
1477  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1478 
1479  textArea->textColor = grayedOut ? LIGHT_GRAY : BLACK;
1480  textArea->text = PIC(text);
1481  textArea->textAlignment = MID_LEFT;
1482  textArea->fontId = LARGE_MEDIUM_FONT;
1483  textArea->obj.area.width = AVAILABLE_WIDTH;
1484  textArea->wrapping = true;
1485  textArea->obj.area.height = nbgl_getTextHeightInWidth(
1486  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1487  textArea->style = NO_STYLE;
1488  textArea->obj.alignment = NO_ALIGNMENT;
1489  textArea->obj.alignmentMarginX = BORDER_MARGIN;
1490  if (layoutInt->container->nbChildren == 0) {
1491 #ifdef TARGET_STAX
1492  // if first object of container, increase the margin from top
1493  textArea->obj.alignmentMarginY += BORDER_MARGIN;
1494 #endif // TARGET_STAX
1495  }
1496  else {
1497 #ifdef TARGET_STAX
1498  textArea->obj.alignmentMarginY = 40; // 40px between paragraphs
1499 #else // TARGET_STAX
1500  textArea->obj.alignmentMarginY = 24; // 24px between paragraphs
1501 #endif // TARGET_STAX
1502  }
1503 
1504  // set this new obj as child of main container
1505  layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1506 
1507  return 0;
1508 }
1509 
1523  const char *title,
1524  const char *description,
1525  const char *info)
1526 {
1527  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1528  nbgl_text_area_t *textArea;
1529 
1530  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextContent():\n");
1531  if (layout == NULL) {
1532  return -1;
1533  }
1534 
1535  // create title
1536  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1537  textArea->textColor = BLACK;
1538  textArea->text = PIC(title);
1539  textArea->textAlignment = MID_LEFT;
1540  textArea->fontId = LARGE_MEDIUM_FONT;
1541  textArea->style = NO_STYLE;
1542  textArea->wrapping = true;
1543  textArea->obj.alignment = NO_ALIGNMENT;
1544  textArea->obj.alignmentMarginX = BORDER_MARGIN;
1545 #ifdef TARGET_STAX
1546  textArea->obj.alignmentMarginY = 24;
1547 #else // TARGET_STAX
1548  textArea->obj.alignmentMarginY = 16;
1549 #endif // TARGET_STAX
1550  textArea->obj.area.width = AVAILABLE_WIDTH;
1551  textArea->obj.area.height = nbgl_getTextHeightInWidth(
1552  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1553  // set this new obj as child of main container
1554  layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1555 
1556  // create description
1557  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1558  textArea->textColor = BLACK;
1559  textArea->text = PIC(description);
1560  textArea->fontId = SMALL_REGULAR_FONT;
1561  textArea->style = NO_STYLE;
1562  textArea->wrapping = true;
1563  textArea->obj.area.width = AVAILABLE_WIDTH;
1564  textArea->obj.area.height = nbgl_getTextHeightInWidth(
1565  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1566  textArea->textAlignment = MID_LEFT;
1567  textArea->obj.alignment = NO_ALIGNMENT;
1568  textArea->obj.alignmentMarginX = BORDER_MARGIN;
1569 #ifdef TARGET_STAX
1570  textArea->obj.alignmentMarginY = 16;
1571 #else // TARGET_STAX
1572  textArea->obj.alignmentMarginY = 24;
1573 #endif // TARGET_STAX
1574  // set this new obj as child of main container
1575  layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1576 
1577  // create info on the bottom
1578  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1579  textArea->textColor = DARK_GRAY;
1580  textArea->text = PIC(info);
1581  textArea->fontId = SMALL_REGULAR_FONT;
1582  textArea->style = NO_STYLE;
1583  textArea->wrapping = true;
1584  textArea->obj.area.width = AVAILABLE_WIDTH;
1585  textArea->obj.area.height = nbgl_getTextHeightInWidth(
1586  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1587  textArea->textAlignment = MID_LEFT;
1588  textArea->obj.alignment = BOTTOM_LEFT;
1589  textArea->obj.alignmentMarginX = BORDER_MARGIN;
1590  textArea->obj.alignmentMarginY = 40;
1591  // set this new obj as child of main container
1592  layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1593 
1594  return layoutInt->container->obj.area.height;
1595 }
1596 
1605 {
1606  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1607  layoutObj_t *obj;
1608  uint8_t i;
1609 
1610  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddRadioChoice():\n");
1611  if (layout == NULL) {
1612  return -1;
1613  }
1614  for (i = 0; i < choices->nbChoices; i++) {
1615  nbgl_container_t *container;
1616  nbgl_text_area_t *textArea;
1617  nbgl_radio_t *button;
1618  nbgl_line_t *line;
1619 
1620  container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1621  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1622  button = (nbgl_radio_t *) nbgl_objPoolGet(RADIO_BUTTON, layoutInt->layer);
1623 
1624  obj = layoutAddCallbackObj(
1625  layoutInt, (nbgl_obj_t *) container, choices->token, choices->tuneId);
1626  if (obj == NULL) {
1627  return -1;
1628  }
1629 
1630  // get container children (max 2)
1631  container->nbChildren = 2;
1632  container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1633  container->obj.area.width = AVAILABLE_WIDTH;
1634  container->obj.area.height = RADIO_CHOICE_HEIGHT;
1635  container->obj.alignment = NO_ALIGNMENT;
1636  container->obj.alignmentMarginX = BORDER_MARGIN;
1637  container->obj.alignTo = (nbgl_obj_t *) NULL;
1638 
1639  // init button for this choice
1640  button->activeColor = BLACK;
1641  button->borderColor = LIGHT_GRAY;
1642  button->obj.alignTo = (nbgl_obj_t *) container;
1643  button->obj.alignment = MID_RIGHT;
1644  button->state = OFF_STATE;
1645  container->children[1] = (nbgl_obj_t *) button;
1646 
1647  // init text area for this choice
1648  if (choices->localized == true) {
1649 #ifdef HAVE_LANGUAGE_PACK
1650  textArea->text = get_ux_loc_string(choices->nameIds[i]);
1651 #endif // HAVE_LANGUAGE_PACK
1652  }
1653  else {
1654  textArea->text = PIC(choices->names[i]);
1655  }
1656  textArea->textAlignment = MID_LEFT;
1657  textArea->obj.area.width = container->obj.area.width - RADIO_WIDTH;
1658  textArea->style = NO_STYLE;
1659  textArea->obj.alignment = MID_LEFT;
1660  textArea->obj.alignTo = (nbgl_obj_t *) container;
1661  container->children[0] = (nbgl_obj_t *) textArea;
1662 
1663  // whole container should be touchable
1664  container->obj.touchMask = (1 << TOUCHED);
1665  container->obj.touchId = CONTROLS_ID + nbTouchableControls;
1666  nbTouchableControls++;
1667 
1668  // highlight init choice
1669  if (i == choices->initChoice) {
1670  button->state = ON_STATE;
1671  textArea->textColor = BLACK;
1672  textArea->fontId = SMALL_BOLD_FONT;
1673  }
1674  else {
1675  button->state = OFF_STATE;
1676  textArea->textColor = DARK_GRAY;
1677  textArea->fontId = SMALL_REGULAR_FONT;
1678  }
1679  textArea->obj.area.height = nbgl_getFontHeight(textArea->fontId);
1680 
1681  line = createHorizontalLine(layoutInt->layer);
1682  line->obj.alignmentMarginY = -4;
1683  line->offset = 3;
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  textArea->obj.area.width = AVAILABLE_WIDTH;
1811  textArea->obj.area.height = nbgl_getTextHeightInWidth(
1812  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1813 
1814  container->obj.area.height += textArea->obj.area.height;
1815 
1816  container->children[0] = (nbgl_obj_t *) textArea;
1817 
1818  for (row = 0; row < info->nbRows; row++) {
1819  nbgl_container_t *rowContainer;
1820  nbgl_image_t *image;
1821 
1822  // create a grid with 1 icon on the left column and 1 text on the right one
1823  rowContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, 0);
1824  rowContainer->nbChildren = 2; // 1 icon + 1 text
1825  rowContainer->children = nbgl_containerPoolGet(rowContainer->nbChildren, 0);
1826  rowContainer->obj.area.width = AVAILABLE_WIDTH;
1827 
1828  image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, 0);
1829  image->foregroundColor = BLACK;
1830  image->buffer = info->rowIcons[row];
1831  rowContainer->children[0] = (nbgl_obj_t *) image;
1832 
1833  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, 0);
1834  textArea->textColor = BLACK;
1835  textArea->text = info->rowTexts[row];
1836  textArea->textAlignment = MID_LEFT;
1837  textArea->fontId = SMALL_REGULAR_FONT;
1838  textArea->wrapping = true;
1839  textArea->obj.alignmentMarginX = 16;
1840  textArea->obj.area.width
1841  = AVAILABLE_WIDTH - image->buffer->width - textArea->obj.alignmentMarginX;
1842  textArea->obj.area.height = nbgl_getTextHeightInWidth(
1843  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1844  textArea->obj.alignment = RIGHT_TOP;
1845  textArea->obj.alignTo = (nbgl_obj_t *) image;
1846  rowContainer->children[1] = (nbgl_obj_t *) textArea;
1847  rowContainer->obj.area.height = MAX(image->buffer->height, textArea->obj.area.height);
1848 
1849  if (row == 0) {
1850  rowContainer->obj.alignmentMarginY = 32;
1851  }
1852  else {
1853 #ifdef TARGET_STAX
1854  rowContainer->obj.alignmentMarginY = 16;
1855 #else // TARGET_STAX
1856  rowContainer->obj.alignmentMarginY = 26;
1857 #endif // TARGET_STAX
1858  }
1859  container->children[1 + row] = (nbgl_obj_t *) rowContainer;
1860  container->obj.area.height
1861  += rowContainer->obj.area.height + rowContainer->obj.alignmentMarginY;
1862  }
1863  layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1864 
1865  return container->obj.area.height;
1866 }
1867 
1868 #ifdef NBGL_QRCODE
1878 {
1879  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1880  nbgl_container_t *container;
1881  nbgl_text_area_t *textArea = NULL;
1883  uint16_t fullHeight = 0;
1884 
1885  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddQRCode():\n");
1886  if (layout == NULL) {
1887  return -1;
1888  }
1889 
1890  container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1891 
1892  // get container children (max 2 (QRCode + text1 + text2))
1893  container->children = nbgl_containerPoolGet(3, layoutInt->layer);
1894  container->nbChildren = 0;
1895 
1896  qrcode = (nbgl_qrcode_t *) nbgl_objPoolGet(QR_CODE, layoutInt->layer);
1897  // version is forced to V10 if url is longer than 62 characters
1898  if (strlen(PIC(info->url)) > 62) {
1899  qrcode->version = QRCODE_V10;
1900  }
1901  else {
1902  qrcode->version = QRCODE_V4;
1903  }
1904  qrcode->foregroundColor = BLACK;
1905  // in QR V4, we use 8*8 screen pixels for one QR pixel
1906  // in QR V10, we use 4*4 screen pixels for one QR pixel
1907  qrcode->obj.area.width
1908  = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 8) : (QR_V10_NB_PIX_SIZE * 4);
1909  qrcode->obj.area.height = qrcode->obj.area.width;
1910  qrcode->text = PIC(info->url);
1911  qrcode->obj.area.bpp = NBGL_BPP_1;
1912  qrcode->obj.alignment = TOP_MIDDLE;
1913 
1914  fullHeight += qrcode->obj.area.height;
1915  container->children[container->nbChildren] = (nbgl_obj_t *) qrcode;
1916  container->nbChildren++;
1917 
1918  if (info->text1 != NULL) {
1919  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1920  textArea->textColor = BLACK;
1921  textArea->text = PIC(info->text1);
1922  textArea->textAlignment = CENTER;
1923  textArea->fontId = (info->largeText1 == true) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
1924  textArea->wrapping = true;
1925  textArea->obj.area.width = AVAILABLE_WIDTH;
1926  textArea->obj.area.height = nbgl_getTextHeightInWidth(
1927  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1928  textArea->obj.alignment = BOTTOM_MIDDLE;
1929  textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1930  textArea->obj.alignmentMarginY = 24;
1931 
1932  fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1933 
1934  container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1935  container->nbChildren++;
1936  }
1937  if (info->text2 != NULL) {
1938  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1939  textArea->textColor = DARK_GRAY;
1940  textArea->text = PIC(info->text2);
1941  textArea->textAlignment = CENTER;
1942  textArea->fontId = SMALL_REGULAR_FONT;
1943  textArea->wrapping = true;
1944  textArea->obj.area.width = AVAILABLE_WIDTH;
1945  textArea->obj.area.height = nbgl_getTextHeightInWidth(
1946  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1947  textArea->obj.alignment = BOTTOM_MIDDLE;
1948  textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1949  if (info->text1 != NULL) {
1950 #ifdef TARGET_STAX
1951  textArea->obj.alignmentMarginY = 40;
1952 #else // TARGET_STAX
1953  textArea->obj.alignmentMarginY = 28;
1954 #endif // TARGET_STAX
1955  }
1956  else {
1957  textArea->obj.alignmentMarginY = 32;
1958  fullHeight += 8;
1959  }
1960 
1961  fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
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 
2028  const nbgl_layoutHorizontalButtons_t *info)
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 = DARK_GRAY;
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);
2152  layoutInt, (nbgl_obj_t *) image, list->token, TUNE_TAP_CASUAL);
2153  obj->index = i;
2154  image->foregroundColor = BLACK;
2155  image->buffer = valueIcon;
2156  image->obj.alignment = RIGHT_TOP;
2157  image->obj.alignmentMarginX = 12;
2158  image->obj.alignTo = (nbgl_obj_t *) valueTextArea;
2159  image->obj.touchMask = (1 << TOUCHED);
2160  image->obj.touchId = VALUE_BUTTON_1_ID + i;
2161 
2162  container->children[container->nbChildren] = (nbgl_obj_t *) image;
2163  container->nbChildren++;
2164  }
2165 
2166  container->obj.area.width = AVAILABLE_WIDTH;
2167  container->obj.area.height = fullHeight;
2168  container->layout = VERTICAL;
2169  container->obj.alignmentMarginX = BORDER_MARGIN;
2170 #ifdef TARGET_STAX
2171  // On Stax, 12 px between each tag/value pair
2172  if (i > 0) {
2173  container->obj.alignmentMarginY = 12;
2174  }
2175  else {
2176  container->obj.alignmentMarginY = 24;
2177  }
2178 #else // TARGET_STAX
2179  // On Flex, 24 px between each tag/value pair
2180  if (i > 0) {
2181  container->obj.alignmentMarginY = 24;
2182  }
2183 #endif // TARGET_STAX
2184  container->obj.alignment = NO_ALIGNMENT;
2185 
2186  layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2187  }
2188 
2189  return 0;
2190 }
2191 
2201 {
2202  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2203  nbgl_progress_bar_t *progress;
2204 
2205  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressBar():\n");
2206  if (layout == NULL) {
2207  return -1;
2208  }
2209  if (barLayout->text != NULL) {
2210  nbgl_text_area_t *textArea;
2211 
2213  ((nbgl_layoutInternal_t *) layout)->layer);
2214  textArea->textColor = BLACK;
2215  textArea->text = PIC(barLayout->text);
2216  textArea->textAlignment = MID_LEFT;
2217  textArea->fontId = SMALL_REGULAR_FONT;
2218  textArea->wrapping = true;
2219  textArea->obj.area.width = AVAILABLE_WIDTH;
2220  textArea->obj.area.height = nbgl_getTextHeightInWidth(
2221  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
2222  textArea->style = NO_STYLE;
2223  textArea->obj.alignment = NO_ALIGNMENT;
2224  textArea->obj.alignmentMarginX = BORDER_MARGIN;
2225  textArea->obj.alignmentMarginY = BORDER_MARGIN;
2226  layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
2227  }
2229  ((nbgl_layoutInternal_t *) layout)->layer);
2230  progress->foregroundColor = BLACK;
2231  progress->withBorder = true;
2232  progress->state = barLayout->percentage;
2233  progress->obj.area.width = 120;
2234  progress->obj.area.height = 12;
2235  progress->obj.alignment = NO_ALIGNMENT;
2236  progress->obj.alignmentMarginX = (AVAILABLE_WIDTH - progress->obj.area.width) / 2;
2237  progress->obj.alignmentMarginY = BORDER_MARGIN;
2238  layoutAddObject(layoutInt, (nbgl_obj_t *) progress);
2239 
2240  if (barLayout->subText != NULL) {
2241  nbgl_text_area_t *subTextArea;
2242 
2243  subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(
2244  TEXT_AREA, ((nbgl_layoutInternal_t *) layout)->layer);
2245  subTextArea->textColor = LIGHT_GRAY;
2246  subTextArea->text = PIC(barLayout->subText);
2247  subTextArea->textAlignment = MID_LEFT;
2248  subTextArea->fontId = SMALL_REGULAR_FONT;
2249  subTextArea->wrapping = true;
2250  subTextArea->obj.area.width = AVAILABLE_WIDTH;
2251  subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
2252  subTextArea->text,
2253  subTextArea->obj.area.width,
2254  subTextArea->wrapping);
2255  subTextArea->style = NO_STYLE;
2256  subTextArea->obj.alignment = NO_ALIGNMENT;
2257  subTextArea->obj.alignmentMarginX = BORDER_MARGIN;
2258  subTextArea->obj.alignmentMarginY = BORDER_MARGIN;
2259  layoutAddObject(layoutInt, (nbgl_obj_t *) subTextArea);
2260  }
2261 
2262  return 0;
2263 }
2264 
2272 {
2273  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2274  nbgl_line_t *line;
2275 
2276  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSeparationLine():\n");
2277  line = createHorizontalLine(layoutInt->layer);
2278  line->obj.alignmentMarginY = -4;
2279  line->offset = 3;
2280  layoutAddObject(layoutInt, (nbgl_obj_t *) line);
2281  return 0;
2282 }
2283 
2292 {
2293  layoutObj_t *obj;
2294  nbgl_button_t *button;
2295  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2296 
2297  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddButton():\n");
2298  if (layout == NULL) {
2299  return -1;
2300  }
2301 
2302  // Add in footer if matching
2303  if ((buttonInfo->onBottom) && (!buttonInfo->fittingContent)) {
2304  if (layoutInt->footerContainer == NULL) {
2305  nbgl_layoutFooter_t footerDesc;
2306  footerDesc.type = FOOTER_SIMPLE_BUTTON;
2307  footerDesc.separationLine = false;
2308  footerDesc.button.text = buttonInfo->text;
2309  footerDesc.button.token = buttonInfo->token;
2310  footerDesc.button.tuneId = buttonInfo->tuneId;
2311  footerDesc.button.icon = buttonInfo->icon;
2312  footerDesc.button.style = buttonInfo->style;
2313  return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2314  }
2315  else {
2316  nbgl_layoutUpFooter_t upFooterDesc;
2317  upFooterDesc.type = UP_FOOTER_BUTTON;
2318  upFooterDesc.button.text = buttonInfo->text;
2319  upFooterDesc.button.token = buttonInfo->token;
2320  upFooterDesc.button.tuneId = buttonInfo->tuneId;
2321  upFooterDesc.button.icon = buttonInfo->icon;
2322  upFooterDesc.button.style = buttonInfo->style;
2323  return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2324  }
2325  }
2326 
2327  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2328  obj = layoutAddCallbackObj(
2329  layoutInt, (nbgl_obj_t *) button, buttonInfo->token, buttonInfo->tuneId);
2330  if (obj == NULL) {
2331  return -1;
2332  }
2333 
2334  button->obj.alignmentMarginX = BORDER_MARGIN;
2335  button->obj.alignmentMarginY = 12;
2336  button->obj.alignment = NO_ALIGNMENT;
2337  if (buttonInfo->style == BLACK_BACKGROUND) {
2338  button->innerColor = BLACK;
2339  button->foregroundColor = WHITE;
2340  }
2341  else {
2342  button->innerColor = WHITE;
2343  button->foregroundColor = BLACK;
2344  }
2345  if (buttonInfo->style == NO_BORDER) {
2346  button->borderColor = WHITE;
2347  }
2348  else {
2349  if (buttonInfo->style == BLACK_BACKGROUND) {
2350  button->borderColor = BLACK;
2351  }
2352  else {
2353  button->borderColor = LIGHT_GRAY;
2354  }
2355  }
2356  button->text = PIC(buttonInfo->text);
2357  button->fontId = SMALL_BOLD_FONT;
2358  button->icon = PIC(buttonInfo->icon);
2359  if (buttonInfo->fittingContent == true) {
2360  button->obj.area.width = nbgl_getTextWidth(button->fontId, button->text)
2362  + ((button->icon) ? (button->icon->width + 12) : 0);
2363  button->obj.area.height = SMALL_BUTTON_HEIGHT;
2364  button->radius = RADIUS_32_PIXELS;
2365  if (buttonInfo->onBottom != true) {
2366  button->obj.alignmentMarginX
2367  += (SCREEN_WIDTH - 2 * BORDER_MARGIN - button->obj.area.width) / 2;
2368  }
2369  }
2370  else {
2371  button->obj.area.width = AVAILABLE_WIDTH;
2372  button->obj.area.height = BUTTON_DIAMETER;
2373  button->radius = BUTTON_RADIUS;
2374  }
2375  button->obj.alignTo = NULL;
2376  button->obj.touchMask = (1 << TOUCHED);
2377  button->obj.touchId = (buttonInfo->fittingContent) ? EXTRA_BUTTON_ID : SINGLE_BUTTON_ID;
2378  // set this new button as child of the container
2379  layoutAddObject(layoutInt, (nbgl_obj_t *) button);
2380 
2381  return 0;
2382 }
2383 
2394  const char *text,
2395  uint8_t token,
2396  tune_index_e tuneId)
2397 {
2398  nbgl_layoutUpFooter_t upFooterDesc = {.type = UP_FOOTER_LONG_PRESS,
2399  .longPress.text = text,
2400  .longPress.token = token,
2401  .longPress.tuneId = tuneId};
2402 
2403  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLongPressButton():\n");
2404  if (layout == NULL) {
2405  return -1;
2406  }
2407 
2408  return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2409 }
2410 
2422  const char *text,
2423  uint8_t token,
2424  tune_index_e tuneId)
2425 {
2426  nbgl_layoutFooter_t footerDesc;
2427  footerDesc.type = FOOTER_SIMPLE_TEXT;
2428  footerDesc.separationLine = true;
2429  footerDesc.simpleText.text = text;
2430  footerDesc.simpleText.mutedOut = false;
2431  footerDesc.simpleText.token = token;
2432  footerDesc.simpleText.tuneId = tuneId;
2433  return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2434 }
2435 
2449  const char *leftText,
2450  uint8_t leftToken,
2451  const char *rightText,
2452  uint8_t rightToken,
2453  tune_index_e tuneId)
2454 {
2455  nbgl_layoutFooter_t footerDesc;
2456  footerDesc.type = FOOTER_DOUBLE_TEXT;
2457  footerDesc.separationLine = true;
2458  footerDesc.doubleText.leftText = leftText;
2459  footerDesc.doubleText.leftToken = leftToken;
2460  footerDesc.doubleText.rightText = rightText;
2461  footerDesc.doubleText.rightToken = rightToken;
2462  footerDesc.doubleText.tuneId = tuneId;
2463  return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2464 }
2465 
2475 {
2476  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2477  layoutObj_t *obj;
2478  nbgl_text_area_t *textArea;
2479  nbgl_line_t *line, *separationLine = NULL;
2480  nbgl_image_t *image = NULL;
2481  nbgl_button_t *button;
2482 
2483  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHeader():\n");
2484  if (layout == NULL) {
2485  return -1;
2486  }
2487  if ((headerDesc == NULL) || (headerDesc->type >= NB_HEADER_TYPES)) {
2488  return -2;
2489  }
2490 
2491  layoutInt->headerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2492  layoutInt->headerContainer->obj.area.width = SCREEN_WIDTH;
2493  layoutInt->headerContainer->layout = VERTICAL;
2494  layoutInt->headerContainer->children
2495  = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2496  layoutInt->headerContainer->obj.alignment = TOP_MIDDLE;
2497 
2498  switch (headerDesc->type) {
2499  case HEADER_EMPTY: {
2500  layoutInt->headerContainer->obj.area.height = headerDesc->emptySpace.height;
2501  break;
2502  }
2503  case HEADER_BACK_AND_TEXT:
2505  case HEADER_EXTENDED_BACK: {
2506  const char *text = (headerDesc->type == HEADER_EXTENDED_BACK)
2507  ? PIC(headerDesc->extendedBack.text)
2508  : PIC(headerDesc->backAndText.text);
2509  // add back button
2510  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2511  obj = layoutAddCallbackObj(
2512  layoutInt,
2513  (nbgl_obj_t *) button,
2514  (headerDesc->type == HEADER_EXTENDED_BACK) ? headerDesc->extendedBack.backToken
2515  : headerDesc->backAndText.token,
2516  (headerDesc->type == HEADER_EXTENDED_BACK) ? headerDesc->extendedBack.tuneId
2517  : headerDesc->backAndText.tuneId);
2518  if (obj == NULL) {
2519  return -1;
2520  }
2521 
2522  button->obj.alignment = MID_LEFT;
2523  button->innerColor = WHITE;
2524  button->foregroundColor = BLACK;
2525  button->borderColor = WHITE;
2526  button->obj.area.width = BACK_KEY_WIDTH;
2527  button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2528  button->text = NULL;
2529  button->icon = PIC(&LEFT_ARROW_ICON);
2530  button->obj.touchMask = (1 << TOUCHED);
2531  button->obj.touchId = BACK_BUTTON_ID;
2532  layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2533  = (nbgl_obj_t *) button;
2534  layoutInt->headerContainer->nbChildren++;
2535 
2536  // add optional text if needed
2537  if (text != NULL) {
2538  // add optional icon if type is HEADER_BACK_ICON_AND_TEXT
2539  if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2540  image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2541  image->buffer = PIC(headerDesc->backAndText.icon);
2542  image->foregroundColor = BLACK;
2543  image->obj.alignment = CENTER;
2544  layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2545  = (nbgl_obj_t *) image;
2546  layoutInt->headerContainer->nbChildren++;
2547  }
2548  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2549  if ((headerDesc->type == HEADER_EXTENDED_BACK)
2550  && (headerDesc->extendedBack.textToken != NBGL_INVALID_TOKEN)) {
2551  obj = layoutAddCallbackObj(layoutInt,
2552  (nbgl_obj_t *) textArea,
2553  headerDesc->extendedBack.textToken,
2554  headerDesc->extendedBack.tuneId);
2555  if (obj == NULL) {
2556  return -1;
2557  }
2558  textArea->obj.touchMask = (1 << TOUCHED);
2559  }
2560  textArea->obj.alignment = CENTER;
2561  textArea->textColor = BLACK;
2562  textArea->obj.area.width
2563  = layoutInt->headerContainer->obj.area.width - 2 * BACK_KEY_WIDTH;
2564  // if icon, reduce to 1 line and fit text width
2565  if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2566  textArea->obj.area.width -= 16 + image->buffer->width;
2567  }
2568  textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2569  textArea->text = text;
2570  textArea->fontId = SMALL_BOLD_FONT;
2571  textArea->textAlignment = CENTER;
2572  textArea->wrapping = true;
2573  uint8_t nbMaxLines = (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) ? 1 : 2;
2574  // ensure that text fits on 2 lines maximum
2575  if (nbgl_getTextNbLinesInWidth(textArea->fontId,
2576  textArea->text,
2577  textArea->obj.area.width,
2578  textArea->wrapping)
2579  > nbMaxLines) {
2581  "nbgl_layoutAddHeader: text [%s] is too long for header\n",
2582  text);
2583  }
2584  if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2585  textArea->obj.area.width = nbgl_getTextWidth(textArea->fontId, textArea->text);
2586  }
2587  layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2588  = (nbgl_obj_t *) textArea;
2589  layoutInt->headerContainer->nbChildren++;
2590  // if icon, realign icon & text
2591  if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2592  textArea->obj.alignmentMarginX = 8 + image->buffer->width / 2;
2593  image->obj.alignmentMarginX = -8 - textArea->obj.area.width / 2;
2594  }
2595  }
2596  // add action key if the type is HEADER_EXTENDED_BACK
2597  if ((headerDesc->type == HEADER_EXTENDED_BACK)
2598  && (headerDesc->extendedBack.actionIcon)) {
2599  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2600  // if token is valid
2601  if (headerDesc->extendedBack.actionToken != NBGL_INVALID_TOKEN) {
2602  obj = layoutAddCallbackObj(layoutInt,
2603  (nbgl_obj_t *) button,
2604  headerDesc->extendedBack.actionToken,
2605  headerDesc->extendedBack.tuneId);
2606  if (obj == NULL) {
2607  return -1;
2608  }
2609  button->obj.touchMask = (1 << TOUCHED);
2610  }
2611 
2612  button->obj.alignment = MID_RIGHT;
2613  button->innerColor = WHITE;
2614  button->foregroundColor
2615  = (headerDesc->extendedBack.actionToken != NBGL_INVALID_TOKEN) ? BLACK
2616  : LIGHT_GRAY;
2617  button->borderColor = WHITE;
2618  button->obj.area.width = BACK_KEY_WIDTH;
2619  button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2620  button->text = NULL;
2621  button->icon = PIC(headerDesc->extendedBack.actionIcon);
2622  button->obj.touchId = EXTRA_BUTTON_ID;
2623  layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2624  = (nbgl_obj_t *) button;
2625  layoutInt->headerContainer->nbChildren++;
2626  }
2627 
2628  layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2629  break;
2630  }
2631  case HEADER_BACK_AND_PROGRESS: {
2632 #ifdef TARGET_STAX
2633  // add optional back button
2634  if (headerDesc->progressAndBack.withBack) {
2635  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2636  obj = layoutAddCallbackObj(layoutInt,
2637  (nbgl_obj_t *) button,
2638  headerDesc->progressAndBack.token,
2639  headerDesc->progressAndBack.tuneId);
2640  if (obj == NULL) {
2641  return -1;
2642  }
2643 
2644  button->obj.alignment = MID_LEFT;
2645  button->innerColor = WHITE;
2646  button->foregroundColor = BLACK;
2647  button->borderColor = WHITE;
2648  button->obj.area.width = BACK_KEY_WIDTH;
2649  button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2650  button->text = NULL;
2651  button->icon = PIC(&LEFT_ARROW_ICON);
2652  button->obj.touchMask = (1 << TOUCHED);
2653  button->obj.touchId = BACK_BUTTON_ID;
2654  // add to container
2655  layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2656  = (nbgl_obj_t *) button;
2657  layoutInt->headerContainer->nbChildren++;
2658  }
2659 
2660  // add progress indicator
2661  if (headerDesc->progressAndBack.nbPages > 1
2662  && headerDesc->progressAndBack.nbPages != NBGL_NO_PROGRESS_INDICATOR) {
2663  nbgl_page_indicator_t *progress;
2664 
2665  progress
2667  progress->activePage = headerDesc->progressAndBack.activePage;
2668  progress->nbPages = headerDesc->progressAndBack.nbPages;
2669  progress->obj.area.width = 224;
2670  progress->obj.alignment = CENTER;
2671  // add to container
2672  layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2673  = (nbgl_obj_t *) progress;
2674  layoutInt->headerContainer->nbChildren++;
2675  }
2676  layoutInt->activePage = headerDesc->progressAndBack.activePage;
2677  layoutInt->nbPages = headerDesc->progressAndBack.nbPages;
2678  layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2679 #endif // TARGET_STAX
2680  break;
2681  }
2682  case HEADER_TITLE: {
2683  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2684  textArea->textColor = BLACK;
2685  textArea->obj.area.width = AVAILABLE_WIDTH;
2686  textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2687  textArea->text = PIC(headerDesc->title.text);
2688  textArea->fontId = SMALL_BOLD_FONT;
2689  textArea->textAlignment = CENTER;
2690  textArea->wrapping = true;
2691  layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2692  = (nbgl_obj_t *) textArea;
2693  layoutInt->headerContainer->nbChildren++;
2694  layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2695  break;
2696  }
2697  case HEADER_RIGHT_TEXT: {
2698  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2699  obj = layoutAddCallbackObj(layoutInt,
2700  (nbgl_obj_t *) textArea,
2701  headerDesc->rightText.token,
2702  headerDesc->rightText.tuneId);
2703  if (obj == NULL) {
2704  return -1;
2705  }
2706  textArea->obj.alignment = MID_RIGHT;
2707  textArea->obj.alignmentMarginX = BORDER_MARGIN;
2708  textArea->textColor = BLACK;
2709  textArea->obj.area.width = AVAILABLE_WIDTH;
2710  textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2711  textArea->text = PIC(headerDesc->rightText.text);
2712  textArea->fontId = SMALL_BOLD_FONT;
2713  textArea->textAlignment = MID_RIGHT;
2714  textArea->obj.touchMask = (1 << TOUCHED);
2715  textArea->obj.touchId = TOP_RIGHT_BUTTON_ID;
2716  // add to bottom container
2717  layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2718  = (nbgl_obj_t *) textArea;
2719  layoutInt->headerContainer->nbChildren++;
2720  layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2721  break;
2722  }
2723  default:
2724  return -2;
2725  }
2726 
2727  if (headerDesc->separationLine) {
2728  line = createHorizontalLine(layoutInt->layer);
2729  line->obj.alignment = BOTTOM_MIDDLE;
2730  line->offset = 3;
2731  layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2732  = (nbgl_obj_t *) line;
2733  layoutInt->headerContainer->nbChildren++;
2734  }
2735  if (separationLine != NULL) {
2736  layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2737  = (nbgl_obj_t *) separationLine;
2738  layoutInt->headerContainer->nbChildren++;
2739  }
2740  // header must be the first child
2741  layoutInt->children[HEADER_INDEX] = (nbgl_obj_t *) layoutInt->headerContainer;
2742 
2743  // subtract header height from main container height
2744  layoutInt->container->obj.area.height -= layoutInt->headerContainer->obj.area.height;
2745  layoutInt->container->obj.alignTo = (nbgl_obj_t *) layoutInt->headerContainer;
2746  layoutInt->container->obj.alignment = BOTTOM_LEFT;
2747 
2748  layoutInt->headerType = headerDesc->type;
2749 
2750  return layoutInt->headerContainer->obj.area.height;
2751 }
2752 
2762 {
2763  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2764  layoutObj_t *obj;
2765  nbgl_text_area_t *textArea;
2766  nbgl_line_t *line, *separationLine = NULL;
2767  nbgl_button_t *button;
2768 
2769  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddExtendedFooter():\n");
2770  if (layout == NULL) {
2771  return -1;
2772  }
2773  if ((footerDesc == NULL) || (footerDesc->type >= NB_FOOTER_TYPES)) {
2774  return -2;
2775  }
2776 
2777  layoutInt->footerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2778  layoutInt->footerContainer->obj.area.width = AVAILABLE_WIDTH;
2779  layoutInt->footerContainer->layout = VERTICAL;
2780  layoutInt->footerContainer->children
2781  = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2782  layoutInt->footerContainer->obj.alignment = BOTTOM_MIDDLE;
2783 
2784  switch (footerDesc->type) {
2785  case FOOTER_EMPTY: {
2786  layoutInt->footerContainer->obj.area.height = footerDesc->emptySpace.height;
2787  break;
2788  }
2789  case FOOTER_SIMPLE_TEXT: {
2790  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2791  obj = layoutAddCallbackObj(layoutInt,
2792  (nbgl_obj_t *) textArea,
2793  footerDesc->simpleText.token,
2794  footerDesc->simpleText.tuneId);
2795  if (obj == NULL) {
2796  return -1;
2797  }
2798 
2799  textArea->obj.alignment = BOTTOM_MIDDLE;
2800  textArea->textColor = (footerDesc->simpleText.mutedOut) ? DARK_GRAY : BLACK;
2801  textArea->obj.area.width = AVAILABLE_WIDTH;
2802  textArea->obj.area.height
2803  = (footerDesc->simpleText.mutedOut) ? SMALL_FOOTER_HEIGHT : SIMPLE_FOOTER_HEIGHT;
2804  textArea->text = PIC(footerDesc->simpleText.text);
2805  textArea->fontId
2806  = (footerDesc->simpleText.mutedOut) ? SMALL_REGULAR_FONT : SMALL_BOLD_FONT;
2807  textArea->textAlignment = CENTER;
2808  textArea->obj.touchMask = (1 << TOUCHED);
2809  textArea->obj.touchId = BOTTOM_BUTTON_ID;
2810  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2811  = (nbgl_obj_t *) textArea;
2812  layoutInt->footerContainer->nbChildren++;
2813  layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2814  break;
2815  }
2816  case FOOTER_DOUBLE_TEXT: {
2817  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2818  obj = layoutAddCallbackObj(layoutInt,
2819  (nbgl_obj_t *) textArea,
2820  footerDesc->doubleText.leftToken,
2821  footerDesc->doubleText.tuneId);
2822  if (obj == NULL) {
2823  return -1;
2824  }
2825  textArea->obj.alignment = BOTTOM_LEFT;
2826  textArea->textColor = BLACK;
2827  textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2828  textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2829  textArea->text = PIC(footerDesc->doubleText.leftText);
2830  textArea->fontId = SMALL_BOLD_FONT;
2831  textArea->textAlignment = CENTER;
2832  textArea->obj.touchMask = (1 << TOUCHED);
2833  textArea->obj.touchId = BOTTOM_BUTTON_ID;
2834  // add to bottom container
2835  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2836  = (nbgl_obj_t *) textArea;
2837  layoutInt->footerContainer->nbChildren++;
2838 
2839  // create right touchable text
2840  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2841  obj = layoutAddCallbackObj(layoutInt,
2842  (nbgl_obj_t *) textArea,
2843  footerDesc->doubleText.rightToken,
2844  footerDesc->doubleText.tuneId);
2845  if (obj == NULL) {
2846  return -1;
2847  }
2848 
2849  textArea->obj.alignment = BOTTOM_RIGHT;
2850  textArea->textColor = BLACK;
2851  textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2852  textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2853  textArea->text = PIC(footerDesc->doubleText.rightText);
2854  textArea->fontId = SMALL_BOLD_FONT;
2855  textArea->textAlignment = CENTER;
2856  textArea->obj.touchMask = (1 << TOUCHED);
2857  textArea->obj.touchId = RIGHT_BUTTON_ID;
2858  // add to bottom container
2859  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2860  = (nbgl_obj_t *) textArea;
2861  layoutInt->footerContainer->nbChildren++;
2862  layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2863 
2864  // create vertical line separating texts
2865  separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
2866  separationLine->lineColor = LIGHT_GRAY;
2867  separationLine->obj.area.width = 1;
2868  separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
2869  separationLine->direction = VERTICAL;
2870  separationLine->thickness = 1;
2871  separationLine->obj.alignment = MID_LEFT;
2872  separationLine->obj.alignTo = (nbgl_obj_t *) textArea;
2873  separationLine->obj.alignmentMarginX = -1;
2874  break;
2875  }
2876  case FOOTER_TEXT_AND_NAV: {
2877  layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
2878  layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2879  // add touchable text on the left
2880  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2881  obj = layoutAddCallbackObj(layoutInt,
2882  (nbgl_obj_t *) textArea,
2883  footerDesc->textAndNav.token,
2884  footerDesc->textAndNav.tuneId);
2885  if (obj == NULL) {
2886  return -1;
2887  }
2888  textArea->obj.alignment = BOTTOM_LEFT;
2889  textArea->textColor = BLACK;
2890 #ifdef TARGET_STAX
2891  textArea->obj.area.width = 160;
2892 #else // TARGET_STAX
2893  textArea->obj.area.width = 192;
2894 #endif // TARGET_STAX
2895  textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2896  textArea->text = PIC(footerDesc->textAndNav.text);
2897  textArea->fontId = SMALL_BOLD_FONT;
2898  textArea->textAlignment = CENTER;
2899  textArea->obj.touchMask = (1 << TOUCHED);
2900  textArea->obj.touchId = BOTTOM_BUTTON_ID;
2901  // add to bottom container
2902  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2903  = (nbgl_obj_t *) textArea;
2904  layoutInt->footerContainer->nbChildren++;
2905 
2906  // add navigation on the right
2907  nbgl_container_t *navContainer
2908  = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2909  navContainer->obj.area.width = AVAILABLE_WIDTH;
2910  navContainer->layout = VERTICAL;
2911  navContainer->nbChildren = 4;
2912  navContainer->children
2913  = (nbgl_obj_t **) nbgl_containerPoolGet(navContainer->nbChildren, layoutInt->layer);
2914  navContainer->obj.alignment = BOTTOM_RIGHT;
2915  navContainer->obj.area.width = SCREEN_WIDTH - textArea->obj.area.width;
2916  navContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2917  layoutNavigationPopulate(navContainer, &footerDesc->navigation, layoutInt->layer);
2918  obj = layoutAddCallbackObj(layoutInt,
2919  (nbgl_obj_t *) navContainer,
2920  footerDesc->textAndNav.navigation.token,
2921  footerDesc->textAndNav.navigation.tuneId);
2922  if (obj == NULL) {
2923  return -1;
2924  }
2925 
2926  // create vertical line separating text from nav
2927  separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
2928  separationLine->lineColor = LIGHT_GRAY;
2929  separationLine->obj.area.width = 1;
2930  separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
2931  separationLine->direction = VERTICAL;
2932  separationLine->thickness = 1;
2933  separationLine->obj.alignment = MID_LEFT;
2934  separationLine->obj.alignTo = (nbgl_obj_t *) navContainer;
2935  separationLine->obj.alignmentMarginX = -1;
2936 
2937  layoutInt->activePage = footerDesc->textAndNav.navigation.activePage;
2938  layoutInt->nbPages = footerDesc->textAndNav.navigation.nbPages;
2939  // add to bottom container
2940  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2941  = (nbgl_obj_t *) navContainer;
2942  layoutInt->footerContainer->nbChildren++;
2943  break;
2944  }
2945  case FOOTER_NAV: {
2946  layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
2947  layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2949  layoutInt->footerContainer, &footerDesc->navigation, layoutInt->layer);
2950  layoutInt->footerContainer->nbChildren = 4;
2951  obj = layoutAddCallbackObj(layoutInt,
2952  (nbgl_obj_t *) layoutInt->footerContainer,
2953  footerDesc->navigation.token,
2954  footerDesc->navigation.tuneId);
2955  if (obj == NULL) {
2956  return -1;
2957  }
2958 
2959  layoutInt->activePage = footerDesc->navigation.activePage;
2960  layoutInt->nbPages = footerDesc->navigation.nbPages;
2961  break;
2962  }
2963  case FOOTER_SIMPLE_BUTTON: {
2964  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2965  obj = layoutAddCallbackObj(layoutInt,
2966  (nbgl_obj_t *) button,
2967  footerDesc->button.token,
2968  footerDesc->button.tuneId);
2969  if (obj == NULL) {
2970  return -1;
2971  }
2972 
2973  button->obj.alignment = CENTER;
2974  if (footerDesc->button.style == BLACK_BACKGROUND) {
2975  button->innerColor = BLACK;
2976  button->foregroundColor = WHITE;
2977  }
2978  else {
2979  button->innerColor = WHITE;
2980  button->foregroundColor = BLACK;
2981  }
2982 
2983  if (footerDesc->button.style == NO_BORDER) {
2984  button->borderColor = WHITE;
2985  }
2986  else {
2987  if (footerDesc->button.style == BLACK_BACKGROUND) {
2988  button->borderColor = BLACK;
2989  }
2990  else {
2991  button->borderColor = LIGHT_GRAY;
2992  }
2993  }
2994  button->text = PIC(footerDesc->button.text);
2995  button->fontId = SMALL_BOLD_FONT;
2996  button->icon = PIC(footerDesc->button.icon);
2997  button->radius = BUTTON_RADIUS;
2998  button->obj.area.height = BUTTON_DIAMETER;
2999  layoutInt->footerContainer->obj.area.height = FOOTER_BUTTON_HEIGHT;
3000  if (footerDesc->button.text == NULL) {
3001  button->obj.area.width = BUTTON_DIAMETER;
3002  }
3003  else {
3004  button->obj.area.width = AVAILABLE_WIDTH;
3005  }
3006  button->obj.touchMask = (1 << TOUCHED);
3007  button->obj.touchId = button->text ? SINGLE_BUTTON_ID : BOTTOM_BUTTON_ID;
3008  // add to bottom container
3009  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3010  = (nbgl_obj_t *) button;
3011  layoutInt->footerContainer->nbChildren++;
3012  break;
3013  }
3014  case FOOTER_CHOICE_BUTTONS: {
3015  // texts cannot be NULL
3016  if ((footerDesc->choiceButtons.bottomText == NULL)
3017  || (footerDesc->choiceButtons.topText == NULL)) {
3018  return -1;
3019  }
3020 
3021  // create bottom button (footer) at first
3022  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3023  obj = layoutAddCallbackObj(layoutInt,
3024  (nbgl_obj_t *) button,
3025  footerDesc->choiceButtons.token,
3026  footerDesc->choiceButtons.tuneId);
3027  if (obj == NULL) {
3028  return -1;
3029  }
3030  // associate with with index 1
3031  obj->index = 1;
3032  // put at the bottom of the container
3033  button->obj.alignment = BOTTOM_MIDDLE;
3034  button->innerColor = WHITE;
3035  if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3036  button->obj.alignmentMarginY
3037  = BOTTOM_BORDER_MARGIN; // 24 pixels from bottom of container
3038  button->borderColor = LIGHT_GRAY;
3039  }
3040  else {
3041  button->obj.alignmentMarginY = 4; // 4 pixels from screen bottom
3042  button->borderColor = WHITE;
3043  }
3044  button->foregroundColor = BLACK;
3045  button->obj.area.width = AVAILABLE_WIDTH;
3046  button->obj.area.height = BUTTON_DIAMETER;
3047  button->radius = BUTTON_RADIUS;
3048  button->text = PIC(footerDesc->choiceButtons.bottomText);
3049  button->fontId = SMALL_BOLD_FONT;
3050  button->obj.touchMask = (1 << TOUCHED);
3051  button->obj.touchId = CHOICE_2_ID;
3052  // add to bottom container
3053  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3054  = (nbgl_obj_t *) button;
3055  layoutInt->footerContainer->nbChildren++;
3056 
3057  // add line if needed
3058  if ((footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3059  && (footerDesc->choiceButtons.style != BOTH_ROUNDED_STYLE)) {
3060  line = createHorizontalLine(layoutInt->layer);
3061  line->obj.alignment = TOP_MIDDLE;
3062  line->obj.alignmentMarginY = 4;
3063  line->obj.alignTo = (nbgl_obj_t *) button;
3064  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3065  = (nbgl_obj_t *) line;
3066  layoutInt->footerContainer->nbChildren++;
3067  }
3068 
3069  // then top button, on top of it
3070  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3071  obj = layoutAddCallbackObj(layoutInt,
3072  (nbgl_obj_t *) button,
3073  footerDesc->choiceButtons.token,
3074  footerDesc->choiceButtons.tuneId);
3075  if (obj == NULL) {
3076  return -1;
3077  }
3078  // associate with with index 0
3079  obj->index = 0;
3080  button->obj.alignment = TOP_MIDDLE;
3081  button->obj.alignmentMarginY = BOTTOM_BORDER_MARGIN; // 24 pixels from top of container
3082  if (footerDesc->choiceButtons.style == SOFT_ACTION_AND_FOOTER_STYLE) {
3083  button->innerColor = WHITE;
3084  button->borderColor = LIGHT_GRAY;
3085  button->foregroundColor = BLACK;
3086  }
3087  else {
3088  button->innerColor = BLACK;
3089  button->borderColor = BLACK;
3090  button->foregroundColor = WHITE;
3091  }
3092  button->obj.area.width = AVAILABLE_WIDTH;
3093  button->obj.area.height = BUTTON_DIAMETER;
3094  button->radius = BUTTON_RADIUS;
3095  button->text = PIC(footerDesc->choiceButtons.topText);
3096  button->icon = (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3097  ? PIC(footerDesc->choiceButtons.topIcon)
3098  : NULL;
3099  button->fontId = SMALL_BOLD_FONT;
3100  button->obj.touchMask = (1 << TOUCHED);
3101  button->obj.touchId = CHOICE_1_ID;
3102  // add to bottom container
3103  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3104  = (nbgl_obj_t *) button;
3105  layoutInt->footerContainer->nbChildren++;
3106 
3107  if (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE) {
3108  layoutInt->footerContainer->obj.area.height = ACTION_AND_FOOTER_FOOTER_HEIGHT;
3109  }
3110  else {
3111  layoutInt->footerContainer->obj.area.height = ROUNDED_AND_FOOTER_FOOTER_HEIGHT;
3112  }
3113 
3114  break;
3115  }
3116  default:
3117  return -2;
3118  }
3119 
3120  // add swipable feature for navigation
3121  if ((footerDesc->type == FOOTER_NAV) || (footerDesc->type == FOOTER_TEXT_AND_NAV)) {
3122  addSwipeInternal(layoutInt,
3123  ((1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT)),
3125  (footerDesc->type == FOOTER_NAV) ? footerDesc->navigation.token
3126  : footerDesc->textAndNav.navigation.token,
3127  (footerDesc->type == FOOTER_NAV)
3128  ? footerDesc->navigation.tuneId
3129  : footerDesc->textAndNav.navigation.tuneId);
3130  }
3131 
3132  if (footerDesc->separationLine) {
3133  line = createHorizontalLine(layoutInt->layer);
3134  line->obj.alignment = TOP_MIDDLE;
3135  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3136  = (nbgl_obj_t *) line;
3137  layoutInt->footerContainer->nbChildren++;
3138  }
3139  if (separationLine != NULL) {
3140  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3141  = (nbgl_obj_t *) separationLine;
3142  layoutInt->footerContainer->nbChildren++;
3143  }
3144 
3145  layoutInt->children[FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->footerContainer;
3146 
3147  // subtract footer height from main container height
3148  layoutInt->container->obj.area.height -= layoutInt->footerContainer->obj.area.height;
3149 
3150  layoutInt->footerType = footerDesc->type;
3151 
3152  return layoutInt->footerContainer->obj.area.height;
3153 }
3154 
3164 {
3165  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3166  layoutObj_t *obj;
3167  nbgl_text_area_t *textArea;
3168  nbgl_line_t *line;
3169  nbgl_button_t *button;
3170 
3171  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddUpFooter():\n");
3172  if (layout == NULL) {
3173  return -1;
3174  }
3175  if ((upFooterDesc == NULL) || (upFooterDesc->type >= NB_UP_FOOTER_TYPES)) {
3176  return -2;
3177  }
3178 
3179  layoutInt->upFooterContainer
3180  = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
3181  layoutInt->upFooterContainer->obj.area.width = SCREEN_WIDTH;
3182  layoutInt->upFooterContainer->layout = VERTICAL;
3183  // maximum 4 children for long press button
3184  layoutInt->upFooterContainer->children
3185  = (nbgl_obj_t **) nbgl_containerPoolGet(4, layoutInt->layer);
3186  layoutInt->upFooterContainer->obj.alignTo = (nbgl_obj_t *) layoutInt->container;
3187  layoutInt->upFooterContainer->obj.alignment = BOTTOM_MIDDLE;
3188 
3189  switch (upFooterDesc->type) {
3190  case UP_FOOTER_LONG_PRESS: {
3191  nbgl_progress_bar_t *progressBar;
3192 
3193  obj = layoutAddCallbackObj(layoutInt,
3194  (nbgl_obj_t *) layoutInt->upFooterContainer,
3195  upFooterDesc->longPress.token,
3196  upFooterDesc->longPress.tuneId);
3197  if (obj == NULL) {
3198  return -1;
3199  }
3200  layoutInt->upFooterContainer->nbChildren = 4;
3201  layoutInt->upFooterContainer->obj.area.height = LONG_PRESS_BUTTON_HEIGHT;
3202  layoutInt->upFooterContainer->obj.touchId = LONG_PRESS_BUTTON_ID;
3203  layoutInt->upFooterContainer->obj.touchMask
3204  = ((1 << TOUCHING) | (1 << TOUCH_RELEASED) | (1 << OUT_OF_TOUCH)
3205  | (1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT));
3206 
3207  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3208  button->obj.alignmentMarginX = BORDER_MARGIN;
3209  button->obj.alignment = MID_RIGHT;
3210  button->innerColor = BLACK;
3211  button->foregroundColor = WHITE;
3212  button->borderColor = BLACK;
3213  button->obj.area.width = BUTTON_DIAMETER;
3214  button->obj.area.height = BUTTON_DIAMETER;
3215  button->radius = BUTTON_RADIUS;
3216  button->icon = PIC(&VALIDATE_ICON);
3217  layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3218 
3219  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3220  textArea->textColor = BLACK;
3221  textArea->text = PIC(upFooterDesc->longPress.text);
3222  textArea->textAlignment = MID_LEFT;
3223  textArea->fontId = LARGE_MEDIUM_FONT;
3224  textArea->wrapping = true;
3225  textArea->obj.area.width = SCREEN_WIDTH - 3 * BORDER_MARGIN - button->obj.area.width;
3226  textArea->obj.area.height = nbgl_getTextHeightInWidth(
3227  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3228  textArea->style = NO_STYLE;
3229  textArea->obj.alignment = MID_LEFT;
3230  textArea->obj.alignmentMarginX = BORDER_MARGIN;
3231  layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) textArea;
3232 
3233  line = createHorizontalLine(layoutInt->layer);
3234  line->offset = 3;
3235  line->obj.alignment = TOP_MIDDLE;
3236  layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) line;
3237 
3238  progressBar = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
3239  progressBar->withBorder = false;
3240  progressBar->obj.area.width = SCREEN_WIDTH;
3241  progressBar->obj.area.height = 8;
3242  progressBar->obj.alignment = TOP_MIDDLE;
3243  progressBar->obj.alignmentMarginY = 4;
3244  progressBar->obj.alignTo = NULL;
3245  layoutInt->upFooterContainer->children[3] = (nbgl_obj_t *) progressBar;
3246  break;
3247  }
3248  case UP_FOOTER_BUTTON: {
3249  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3250  obj = layoutAddCallbackObj(layoutInt,
3251  (nbgl_obj_t *) button,
3252  upFooterDesc->button.token,
3253  upFooterDesc->button.tuneId);
3254  if (obj == NULL) {
3255  return -1;
3256  }
3257 
3258  layoutInt->upFooterContainer->nbChildren = 1;
3259  layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3260  button->obj.alignment = CENTER;
3261 
3262  if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3263  button->innerColor = BLACK;
3264  button->foregroundColor = WHITE;
3265  }
3266  else {
3267  button->innerColor = WHITE;
3268  button->foregroundColor = BLACK;
3269  }
3270  if (upFooterDesc->button.style == NO_BORDER) {
3271  button->borderColor = WHITE;
3272  }
3273  else {
3274  if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3275  button->borderColor = BLACK;
3276  }
3277  else {
3278  button->borderColor = LIGHT_GRAY;
3279  }
3280  }
3281  button->text = PIC(upFooterDesc->button.text);
3282  button->fontId = SMALL_BOLD_FONT;
3283  button->icon = PIC(upFooterDesc->button.icon);
3284  button->obj.area.width = AVAILABLE_WIDTH;
3285  button->obj.area.height = BUTTON_DIAMETER;
3286  button->radius = BUTTON_RADIUS;
3287 
3288  button->obj.alignTo = NULL;
3289  button->obj.touchMask = (1 << TOUCHED);
3290  button->obj.touchId = SINGLE_BUTTON_ID;
3291  layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3292  break;
3293  }
3295  // icon & text cannot be NULL
3296  if ((upFooterDesc->horizontalButtons.leftIcon == NULL)
3297  || (upFooterDesc->horizontalButtons.rightText == NULL)) {
3298  return -1;
3299  }
3300 
3301  layoutInt->upFooterContainer->nbChildren = 2;
3302  layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3303 
3304  // create left button (in white) at first
3305  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3306  obj = layoutAddCallbackObj(layoutInt,
3307  (nbgl_obj_t *) button,
3308  upFooterDesc->horizontalButtons.leftToken,
3309  upFooterDesc->horizontalButtons.tuneId);
3310  if (obj == NULL) {
3311  return -1;
3312  }
3313  // associate with with index 1
3314  obj->index = 1;
3315  button->obj.alignment = MID_LEFT;
3316  button->obj.alignmentMarginX = BORDER_MARGIN;
3317  button->borderColor = LIGHT_GRAY;
3318  button->innerColor = WHITE;
3319  button->foregroundColor = BLACK;
3320  button->obj.area.width = BUTTON_DIAMETER;
3321  button->obj.area.height = BUTTON_DIAMETER;
3322  button->radius = BUTTON_RADIUS;
3323  button->icon = PIC(upFooterDesc->horizontalButtons.leftIcon);
3324  button->fontId = SMALL_BOLD_FONT;
3325  button->obj.touchMask = (1 << TOUCHED);
3326  button->obj.touchId = CHOICE_2_ID;
3327  layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3328 
3329  // then black button, on right
3330  button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3331  obj = layoutAddCallbackObj(layoutInt,
3332  (nbgl_obj_t *) button,
3333  upFooterDesc->horizontalButtons.rightToken,
3334  upFooterDesc->horizontalButtons.tuneId);
3335  if (obj == NULL) {
3336  return -1;
3337  }
3338  // associate with with index 0
3339  obj->index = 0;
3340  button->obj.alignment = MID_RIGHT;
3341  button->obj.alignmentMarginX = BORDER_MARGIN;
3342  button->innerColor = BLACK;
3343  button->borderColor = BLACK;
3344  button->foregroundColor = WHITE;
3345  button->obj.area.width = AVAILABLE_WIDTH - BUTTON_DIAMETER - 16;
3346  button->obj.area.height = BUTTON_DIAMETER;
3347  button->radius = BUTTON_RADIUS;
3348  button->text = PIC(upFooterDesc->horizontalButtons.rightText);
3349  button->fontId = SMALL_BOLD_FONT;
3350  button->obj.touchMask = (1 << TOUCHED);
3351  button->obj.touchId = CHOICE_1_ID;
3352  layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) button;
3353  break;
3354  }
3355  case UP_FOOTER_TIP_BOX: {
3356  // text cannot be NULL
3357  if (upFooterDesc->tipBox.text == NULL) {
3358  return -1;
3359  }
3360  obj = layoutAddCallbackObj(layoutInt,
3361  (nbgl_obj_t *) layoutInt->upFooterContainer,
3362  upFooterDesc->tipBox.token,
3363  upFooterDesc->tipBox.tuneId);
3364  if (obj == NULL) {
3365  return -1;
3366  }
3367  layoutInt->upFooterContainer->nbChildren = 3;
3368  layoutInt->upFooterContainer->obj.touchId = TIP_BOX_ID;
3369  layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3370 
3371  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3372  textArea->textColor = BLACK;
3373  textArea->text = PIC(upFooterDesc->tipBox.text);
3374  textArea->textAlignment = MID_LEFT;
3375  textArea->fontId = SMALL_REGULAR_FONT;
3376  textArea->wrapping = true;
3377  textArea->obj.area.width = AVAILABLE_WIDTH;
3378  if (upFooterDesc->tipBox.icon != NULL) {
3379  textArea->obj.area.width
3380  -= ((nbgl_icon_details_t *) PIC(upFooterDesc->tipBox.icon))->width
3381  + BORDER_MARGIN;
3382  }
3383  textArea->obj.area.height = nbgl_getTextHeightInWidth(
3384  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3385  textArea->obj.alignment = MID_LEFT;
3386  textArea->obj.alignmentMarginX = BORDER_MARGIN;
3387  layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3388  layoutInt->upFooterContainer->obj.area.height = textArea->obj.area.height;
3389 
3390  line = createHorizontalLine(layoutInt->layer);
3391  line->offset = 3;
3392  line->obj.alignment = TOP_MIDDLE;
3393  layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) line;
3394 
3395  if (upFooterDesc->tipBox.icon != NULL) {
3396  nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
3397  image->obj.alignmentMarginX = BORDER_MARGIN;
3398  image->obj.alignment = MID_RIGHT;
3399  image->foregroundColor = BLACK;
3400  image->buffer = PIC(upFooterDesc->tipBox.icon);
3401  layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) image;
3402  if (layoutInt->upFooterContainer->obj.area.height < image->buffer->height) {
3403  layoutInt->upFooterContainer->obj.area.height = image->buffer->height;
3404  }
3405  }
3406  layoutInt->upFooterContainer->obj.area.height += 2 * BOTTOM_BORDER_MARGIN;
3407 
3408  break;
3409  }
3410  case UP_FOOTER_TEXT: {
3411  obj = layoutAddCallbackObj(layoutInt,
3412  (nbgl_obj_t *) layoutInt->upFooterContainer,
3413  upFooterDesc->text.token,
3414  upFooterDesc->text.tuneId);
3415  if (obj == NULL) {
3416  return -1;
3417  }
3418  layoutInt->upFooterContainer->nbChildren = 1;
3419  layoutInt->upFooterContainer->obj.area.height = SMALL_FOOTER_HEIGHT;
3420  layoutInt->upFooterContainer->obj.touchId = WHOLE_SCREEN_ID;
3421  layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3422 
3423  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3424  textArea->textColor = DARK_GRAY;
3425  textArea->text = PIC(upFooterDesc->text.text);
3426  textArea->textAlignment = CENTER;
3427  textArea->fontId = SMALL_REGULAR_FONT;
3428  textArea->wrapping = true;
3429  textArea->obj.area.width = AVAILABLE_WIDTH;
3430  textArea->obj.area.height = nbgl_getTextHeightInWidth(
3431  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3432  textArea->obj.alignment = CENTER;
3433  layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3434  break;
3435  }
3436  default:
3437  return -2;
3438  }
3439 
3440  // subtract up footer height from main container height
3441  layoutInt->container->obj.area.height -= layoutInt->upFooterContainer->obj.area.height;
3442 
3443  layoutInt->children[UP_FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->upFooterContainer;
3444 
3445  layoutInt->upFooterType = upFooterDesc->type;
3446 
3447  return layoutInt->upFooterContainer->obj.area.height;
3448 }
3449 
3464  uint8_t activePage,
3465  uint8_t nbPages,
3466  bool withBack,
3467  uint8_t backToken,
3468  tune_index_e tuneId)
3469 {
3471  .separationLine = false,
3472  .progressAndBack.activePage = activePage,
3473  .progressAndBack.nbPages = nbPages,
3474  .progressAndBack.token = backToken,
3475  .progressAndBack.tuneId = tuneId,
3476  .progressAndBack.withBack = withBack,
3477  .progressAndBack.actionIcon = NULL,
3478  .progressAndBack.actionToken = NBGL_INVALID_TOKEN};
3479  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressIndicator():\n");
3480 
3481  return nbgl_layoutAddHeader(layout, &headerDesc);
3482 }
3483 
3493 int nbgl_layoutAddSpinner(nbgl_layout_t *layout, const char *text, const char *subText, bool fixed)
3494 {
3495  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3496  nbgl_container_t *container;
3497  nbgl_text_area_t *textArea;
3498  nbgl_spinner_t *spinner;
3499 
3500  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSpinner():\n");
3501  if (layout == NULL) {
3502  return -1;
3503  }
3504 
3505  container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
3506  // spinner + text + subText
3507  container->nbChildren = 3;
3508  container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
3509 
3510  container->obj.area.width = AVAILABLE_WIDTH;
3511  container->layout = VERTICAL;
3512  container->obj.alignment = CENTER;
3513 
3514  // create spinner
3515  spinner = (nbgl_spinner_t *) nbgl_objPoolGet(SPINNER, layoutInt->layer);
3516  spinner->position = fixed ? 0xFF : 0;
3517  spinner->obj.alignment = TOP_MIDDLE;
3518  // set this new spinner as child of the container
3519  container->children[0] = (nbgl_obj_t *) spinner;
3520 
3521  // update container height
3522  container->obj.area.height += SPINNER_HEIGHT;
3523 
3524  // create text area
3525  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3526  textArea->textColor = BLACK;
3527  textArea->text = PIC(text);
3528  textArea->textAlignment = CENTER;
3529  textArea->fontId = (subText != NULL) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
3530  textArea->wrapping = true;
3531 #ifdef TARGET_STAX
3532  textArea->obj.alignmentMarginY = 20;
3533 #else // TARGET_STAX
3534  textArea->obj.alignmentMarginY = 24;
3535 #endif // TARGET_STAX
3536  textArea->obj.alignTo = (nbgl_obj_t *) spinner;
3537  textArea->obj.alignment = BOTTOM_MIDDLE;
3538  textArea->obj.area.width = AVAILABLE_WIDTH;
3539  textArea->obj.area.height = nbgl_getTextHeightInWidth(
3540  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3541  textArea->style = NO_STYLE;
3542 
3543  // update container height
3544  container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
3545 
3546  // set this text as child of the container
3547  container->children[1] = (nbgl_obj_t *) textArea;
3548 
3549  if (subText != NULL) {
3550  nbgl_text_area_t *subTextArea;
3551  // create sub-text area
3552  subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3553  subTextArea->textColor = BLACK;
3554  subTextArea->text = PIC(subText);
3555  subTextArea->textAlignment = CENTER;
3556  subTextArea->fontId = SMALL_REGULAR_FONT;
3557  subTextArea->wrapping = true;
3558 #ifdef TARGET_STAX
3559  subTextArea->obj.alignmentMarginY = 20;
3560 #else // TARGET_STAX
3561  subTextArea->obj.alignmentMarginY = 16;
3562 #endif // TARGET_STAX
3563  subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
3564  subTextArea->obj.alignment = BOTTOM_MIDDLE;
3565  subTextArea->obj.area.width = AVAILABLE_WIDTH;
3566  subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
3567  subTextArea->text,
3568  subTextArea->obj.area.width,
3569  subTextArea->wrapping);
3570  subTextArea->style = NO_STYLE;
3571 
3572  // update container height
3573  container->obj.area.height
3574  += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
3575 
3576  // set thissub-text as child of the container
3577  container->children[2] = (nbgl_obj_t *) subTextArea;
3578  }
3579  layoutAddObject(layoutInt, (nbgl_obj_t *) container);
3580 
3581  if (!fixed) {
3582  // update ticker to update the spinner periodically
3584 
3585  tickerCfg.tickerIntervale = SPINNER_REFRESH_PERIOD; // ms
3586  tickerCfg.tickerValue = SPINNER_REFRESH_PERIOD; // ms
3587  tickerCfg.tickerCallback = &spinnerTickerCallback;
3588  nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
3589  }
3590 
3591  return 0;
3592 }
3593 
3601 {
3602  nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3603 
3604  if (layout == NULL) {
3605  return -1;
3606  }
3608  "nbgl_layoutDraw(): container.nbChildren =%d, layout->nbChildren = %d\n",
3609  layout->container->nbChildren,
3610  layout->nbChildren);
3611  if (layout->tapText) {
3612  // set this new container as child of main container
3613  layoutAddObject(layout, (nbgl_obj_t *) layout->tapText);
3614  }
3615  if (layout->withLeftBorder == true) {
3616  // draw now the line
3617  nbgl_line_t *line = createLeftVerticalLine(layout->layer);
3618  layout->children[LEFT_BORDER_INDEX] = (nbgl_obj_t *) line;
3619  }
3621 
3622  return 0;
3623 }
3624 
3632 {
3633  nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3634  LOG_DEBUG(PAGE_LOGGER, "nbgl_layoutRelease(): \n");
3635  if (layout == NULL) {
3636  return -1;
3637  }
3638  // if modal
3639  if (layout->modal) {
3640  nbgl_screenPop(layout->layer);
3641  }
3642  layout->nbChildren = 0;
3643  return 0;
3644 }
3645 
3646 #endif // HAVE_SE_TOUCH
Random Number Generation.
@ ANIM_ILLUSTRATION
animation
Definition: nbgl_content.h:77
@ ICON_ILLUSTRATION
simple icon
Definition: nbgl_content.h:76
@ LARGE_CASE_BOLD_INFO
Definition: nbgl_content.h:37
@ NORMAL_INFO
Definition: nbgl_content.h:41
@ LARGE_CASE_GRAY_INFO
Definition: nbgl_content.h:39
debug traces management
#define LOG_WARN(__logger,...)
Definition: nbgl_debug.h:87
#define LOG_DEBUG(__logger,...)
Definition: nbgl_debug.h:86
#define LOG_FATAL(__logger,...)
Definition: nbgl_debug.h:88
@ LAYOUT_LOGGER
Definition: nbgl_debug.h:33
@ PAGE_LOGGER
Definition: nbgl_debug.h:34
#define qrcode
Definition: nbgl_draw.c:46
Middle Level API of the new BOLOS Graphical Library.
#define QR_V10_NB_PIX_SIZE
Definition: nbgl_draw.h:24
#define QR_V4_NB_PIX_SIZE
Definition: nbgl_draw.h:23
uint8_t nbgl_getFontHeight(nbgl_font_id_e fontId)
return the height in pixels of the font with the given font ID
Definition: nbgl_fonts.c:390
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:352
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.
Definition: nbgl_fonts.c:1040
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:723
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:402
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.
Definition: nbgl_layout.c:2048
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.
Definition: nbgl_layout.c:1761
#define HOLD_TO_APPROVE_STEP_PERCENT
Definition: nbgl_layout.c:134
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,...
Definition: nbgl_layout.c:3163
int nbgl_layoutDraw(nbgl_layout_t *layoutParam)
Applies given layout. The screen will be redrawn.
Definition: nbgl_layout.c:3600
#define NB_MAX_CONTAINER_CHILDREN
Definition: nbgl_layout.c:38
int nbgl_layoutAddTextContent(nbgl_layout_t *layout, const char *title, const char *description, const char *info)
Creates in the main container three text areas:
Definition: nbgl_layout.c:1522
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)
Definition: nbgl_layout.c:1392
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 ...
Definition: nbgl_layout.c:3463
int nbgl_layoutAddNavigationBar(nbgl_layout_t *layout, const nbgl_layoutNavigationBar_t *info)
Creates a navigation bar on bottom of main container.
Definition: nbgl_layout.c:1261
#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT
Definition: nbgl_layout.c:58
int nbgl_layoutAddSeparationLine(nbgl_layout_t *layout)
adds a separation line on bottom of the last added item
Definition: nbgl_layout.c:2271
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...
Definition: nbgl_layout.c:1877
#define ACTION_AND_FOOTER_FOOTER_HEIGHT
Definition: nbgl_layout.c:59
int nbgl_layoutAddRadioChoice(nbgl_layout_t *layout, const nbgl_layoutRadioChoice_t *choices)
Creates a list of radio buttons (on the right)
Definition: nbgl_layout.c:1604
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...
Definition: nbgl_layout.c:1701
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...
Definition: nbgl_layout.c:2448
int nbgl_layoutAddProgressBar(nbgl_layout_t *layout, const nbgl_layoutProgressBar_t *barLayout)
Creates an area in main panel to display a progress bar, with a title text and a description under th...
Definition: nbgl_layout.c:2200
int nbgl_layoutAddSubHeaderText(nbgl_layout_t *layout, const char *text)
Creates an area with given text in small regular font, under the header.
Definition: nbgl_layout.c:1426
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 ...
Definition: nbgl_layout.c:1409
int nbgl_layoutAddTouchableBar(nbgl_layout_t *layout, const nbgl_layoutBar_t *barLayout)
Creates a touchable bar in main panel.
Definition: nbgl_layout.c:1313
#define SPINNER_REFRESH_PERIOD
Definition: nbgl_layout.c:63
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.
Definition: nbgl_layout.c:1216
const char * get_ux_loc_string(uint32_t index)
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.
Definition: nbgl_layout.c:1175
#define RADIO_CHOICE_HEIGHT
Definition: nbgl_layout.c:52
int nbgl_layoutAddSpinner(nbgl_layout_t *layout, const char *text, const char *subText, bool fixed)
Creates a centered (vertically & horizontally) spinner with a text under it.
Definition: nbgl_layout.c:3493
int nbgl_layoutAddSwitch(nbgl_layout_t *layout, const nbgl_layoutSwitch_t *switchLayout)
Creates a switch with the given text and its state.
Definition: nbgl_layout.c:1352
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...
Definition: nbgl_layout.c:2027
listItemType_t
Definition: nbgl_layout.c:85
@ NB_ITEM_TYPES
Definition: nbgl_layout.c:88
@ TOUCHABLE_BAR_ITEM
Definition: nbgl_layout.c:86
@ SWITCH_ITEM
Definition: nbgl_layout.c:87
int nbgl_layoutAddButton(nbgl_layout_t *layout, const nbgl_layoutButton_t *buttonInfo)
Creates a rounded button in the main container.
Definition: nbgl_layout.c:2291
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,...
Definition: nbgl_layout.c:2761
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.
Definition: nbgl_layout.c:1783
#define NB_MAX_LAYOUTS
Definition: nbgl_layout.c:35
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,...
Definition: nbgl_layout.c:2004
int nbgl_layoutRelease(nbgl_layout_t *layoutParam)
Release the layout obtained with nbgl_layoutGet()
Definition: nbgl_layout.c:3631
#define BAR_INTERVALE
Definition: nbgl_layout.c:54
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.
Definition: nbgl_layout.c:2393
@ LEFT_HALF_INDEX
Definition: nbgl_layout.c:71
@ PAGE_INDICATOR_INDEX
Definition: nbgl_layout.c:70
@ FIRST_BUTTON_INDEX
Definition: nbgl_layout.c:73
@ RIGHT_HALF_INDEX
Definition: nbgl_layout.c:72
void layoutAddObject(nbgl_layoutInternal_t *layout, nbgl_obj_t *obj)
adds the given obj to the main container
Definition: nbgl_layout.c:584
layoutObj_t * layoutAddCallbackObj(nbgl_layoutInternal_t *layout, nbgl_obj_t *obj, uint8_t token, tune_index_e tuneId)
Definition: nbgl_layout.c:557
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,...
Definition: nbgl_layout.c:2474
#define BACK_KEY_WIDTH
Definition: nbgl_layout.c:55
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.
Definition: nbgl_layout.c:1287
#define HOLD_TO_APPROVE_STEP_DURATION_MS
Definition: nbgl_layout.c:135
#define FOOTER_BUTTON_HEIGHT
Definition: nbgl_layout.c:56
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...
Definition: nbgl_layout.c:2421
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)
Definition: nbgl_layout.c:1468
#define UP_FOOTER_BUTTON_HEIGHT
Definition: nbgl_layout.c:57
nbgl_layout_t * nbgl_layoutGet(const nbgl_layoutDescription_t *description)
returns a layout of the given type. The layout is reset
Definition: nbgl_layout.c:1086
void(* nbgl_layoutTouchCallback_t)(int token, uint8_t index)
prototype of function to be called when an object is touched
Definition: nbgl_layout.h:104
#define SIMPLE_FOOTER_HEIGHT
Definition: nbgl_layout.h:60
#define LONG_PRESS_BUTTON_HEIGHT
Definition: nbgl_layout.h:63
#define PRE_TEXT_MARGIN
Definition: nbgl_layout.h:75
#define TOUCHABLE_MAIN_BAR_HEIGHT
Definition: nbgl_layout.h:57
#define TEXT_SUBTEXT_MARGIN
Definition: nbgl_layout.h:76
#define TOUCHABLE_BAR_HEIGHT
Definition: nbgl_layout.h:58
@ WHITE_BACKGROUND
rounded bordered button, with text/icon in black, on white background
Definition: nbgl_layout.h:336
@ NO_BORDER
simple clickable text, in black
Definition: nbgl_layout.h:337
@ BLACK_BACKGROUND
rounded bordered button, with text/icon in white, on black background
Definition: nbgl_layout.h:334
#define AVAILABLE_WIDTH
Definition: nbgl_layout.h:66
@ UP_FOOTER_TEXT
grayed-out text, for example "Tap to continue"
Definition: nbgl_layout.h:532
@ NB_UP_FOOTER_TYPES
Definition: nbgl_layout.h:533
@ UP_FOOTER_BUTTON
simple button
Definition: nbgl_layout.h:529
@ UP_FOOTER_LONG_PRESS
long-press button
Definition: nbgl_layout.h:528
@ UP_FOOTER_TIP_BOX
Tip-box.
Definition: nbgl_layout.h:531
@ UP_FOOTER_HORIZONTAL_BUTTONS
2 buttons, on the same line
Definition: nbgl_layout.h:530
void * nbgl_layout_t
type shared externally
Definition: nbgl_layout.h:96
@ HEADER_TITLE
simple centered text
Definition: nbgl_layout.h:419
@ HEADER_BACK_AND_PROGRESS
optional back key and progress indicator (only on Stax)
Definition: nbgl_layout.h:418
@ HEADER_EMPTY
empty space, to have a better vertical centering of centered info
Definition: nbgl_layout.h:415
@ NB_HEADER_TYPES
Definition: nbgl_layout.h:422
@ HEADER_EXTENDED_BACK
back key, centered text and touchable key on the right
Definition: nbgl_layout.h:420
@ HEADER_BACK_AND_TEXT
back key and optional text
Definition: nbgl_layout.h:416
@ HEADER_RIGHT_TEXT
touchable text on the right, with a vertical separation line
Definition: nbgl_layout.h:421
@ HEADER_BACK_ICON_AND_TEXT
back key and optional icon and text
Definition: nbgl_layout.h:417
#define SMALL_FOOTER_HEIGHT
Definition: nbgl_layout.h:59
#define NBGL_INVALID_TOKEN
Definition: nbgl_layout.h:33
@ SOFT_ACTION_AND_FOOTER_STYLE
A white button on top of a footer, with a separation line.
Definition: nbgl_layout.h:293
@ BOTH_ROUNDED_STYLE
A black button on top of a white button.
Definition: nbgl_layout.h:294
@ ROUNDED_AND_FOOTER_STYLE
A black background button on top of a footer.
Definition: nbgl_layout.h:291
#define NBGL_NO_PROGRESS_INDICATOR
To be used when a control token shall not be used.
Definition: nbgl_layout.h:30
#define POST_SUBTEXT_MARGIN
Definition: nbgl_layout.h:77
#define NBGL_NO_TUNE
Definition: nbgl_layout.h:29
@ FOOTER_SIMPLE_TEXT
simple touchable text in bold
Definition: nbgl_layout.h:477
@ FOOTER_NAV
navigation bar
Definition: nbgl_layout.h:481
@ FOOTER_SIMPLE_BUTTON
simple black or white button (see nbgl_layoutButtonStyle_t)
Definition: nbgl_layout.h:482
@ FOOTER_TEXT_AND_NAV
Definition: nbgl_layout.h:479
@ FOOTER_DOUBLE_TEXT
2 touchable texts in bold, separated by a vertical line (only on Stax)
Definition: nbgl_layout.h:478
@ NB_FOOTER_TYPES
Definition: nbgl_layout.h:484
@ FOOTER_EMPTY
empty space, to have a better vertical centering of centered info
Definition: nbgl_layout.h:476
@ FOOTER_CHOICE_BUTTONS
double buttons (see nbgl_layoutChoiceButtonsStyle_t)
Definition: nbgl_layout.h:483
#define TOUCHABLE_HEADER_BAR_HEIGHT
Definition: nbgl_layout.h:56
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)
@ UP_FOOTER_INDEX
@ FOOTER_INDEX
@ HEADER_INDEX
@ TOP_RIGHT_BUTTON_INDEX
@ MAIN_CONTAINER_INDEX
@ LEFT_BORDER_INDEX
@ NB_MAX_SCREEN_CHILDREN
#define SMALL_BUTTON_HEIGHT
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
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 PUSH_ICON
Definition: nbgl_obj.h:138
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:221
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:1522
struct PACKED__ nbgl_progress_bar_s nbgl_progress_bar_t
struct to represent a progress bar (PROGRESS_BAR type)
#define SWIPE_MASK
Definition: nbgl_obj.h:167
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.
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:1571
struct PACKED__ nbgl_image_s nbgl_image_t
struct to represent an image (IMAGE type)
#define RADIO_WIDTH
Definition: nbgl_obj.h:106
#define BUTTON_RADIUS
Definition: nbgl_obj.h:87
#define BORDER_MARGIN
Definition: nbgl_obj.h:71
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 VALIDATE_ICON
Definition: nbgl_obj.h:135
#define MINI_PUSH_ICON
Definition: nbgl_obj.h:147
void nbgl_refreshSpecialWithPostRefresh(nbgl_refresh_mode_t mode, nbgl_post_refresh_t post_refresh)
Definition: nbgl_obj.c:1587
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.
#define BUTTON_DIAMETER
Definition: nbgl_obj.h:88
#define BOTTOM_BORDER_MARGIN
Definition: nbgl_obj.h:72
#define SPINNER_HEIGHT
Definition: nbgl_obj.h:98
struct PACKED__ nbgl_container_s nbgl_container_t
struct to represent a container (CONTAINER type)
@ CHOICE_1_ID
Definition: nbgl_obj.h:553
@ TOP_RIGHT_BUTTON_ID
Definition: nbgl_obj.h:549
@ LONG_PRESS_BUTTON_ID
Definition: nbgl_obj.h:562
@ CONTROLS_ID
Definition: nbgl_obj.h:564
@ RIGHT_BUTTON_ID
Definition: nbgl_obj.h:547
@ WHOLE_SCREEN_ID
Definition: nbgl_obj.h:548
@ BOTTOM_BUTTON_ID
Definition: nbgl_obj.h:545
@ SINGLE_BUTTON_ID
Definition: nbgl_obj.h:551
@ BACK_BUTTON_ID
Definition: nbgl_obj.h:550
@ VALUE_BUTTON_1_ID
Definition: nbgl_obj.h:559
@ TIP_BOX_ID
Definition: nbgl_obj.h:563
@ EXTRA_BUTTON_ID
Definition: nbgl_obj.h:552
@ CHOICE_2_ID
Definition: nbgl_obj.h:554
struct PACKED__ nbgl_switch_s nbgl_switch_t
struct to represent a switch (size is fixed) (SWITCH type)
#define LEFT_ARROW_ICON
Definition: nbgl_obj.h:139
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...
Definition: nbgl_screen.c:225
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....
Definition: nbgl_screen.c:330
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.
Definition: nbgl_screen.c:289
int nbgl_screenPop(uint8_t screenIndex)
Release the screen at the given index in screen array (index returned by nbgl_screenPush())....
Definition: nbgl_screen.c:403
struct PACKED__ nbgl_screenTickerConfiguration_s nbgl_screenTickerConfiguration_t
struct to configure a screen layer
void nbgl_screenRedraw(void)
This function redraws the whole screen on top of stack and its children.
Definition: nbgl_screen.c:66
uint32_t nbgl_touchGetTouchDuration(nbgl_obj_t *obj)
Definition: nbgl_touch.c:402
color_t
Definition: nbgl_types.h:101
@ WHITE
Definition: nbgl_types.h:105
@ DARK_GRAY
Definition: nbgl_types.h:103
@ LIGHT_GRAY
Definition: nbgl_types.h:104
@ BLACK
Definition: nbgl_types.h:102
nbgl_state_t
to represent a boolean state.
Definition: nbgl_types.h:160
@ ON_STATE
Definition: nbgl_types.h:162
@ OFF_STATE
Definition: nbgl_types.h:161
@ POST_REFRESH_FORCE_POWER_OFF
Force screen power off after refresh.
Definition: nbgl_types.h:316
@ POST_REFRESH_FORCE_POWER_ON
Force screen power on after refresh.
Definition: nbgl_types.h:317
@ POST_REFRESH_FORCE_POWER_ON_WITH_PIPELINE
Force screen power on and enable pipeline.
Definition: nbgl_types.h:318
#define SCREEN_WIDTH
Definition: nbgl_types.h:32
@ RADIUS_32_PIXELS
32 pixels
Definition: nbgl_types.h:328
nbgl_touchType_t
The different types of Touchscreen events.
Definition: nbgl_types.h:220
@ SWIPED_LEFT
Definition: nbgl_types.h:236
@ SWIPED_UP
Definition: nbgl_types.h:233
@ SWIPED_DOWN
Definition: nbgl_types.h:234
@ SWIPED_RIGHT
Definition: nbgl_types.h:235
@ TOUCH_RELEASED
Definition: nbgl_types.h:230
@ TOUCHED
Definition: nbgl_types.h:221
@ TOUCHING
corresponding to an object that is currently touched
Definition: nbgl_types.h:225
@ OUT_OF_TOUCH
Definition: nbgl_types.h:226
@ QRCODE_V10
QRCode V10, can encode text len up to 1500 chars, display size = 228*228.
Definition: nbgl_types.h:192
@ QRCODE_V4_SMALL
QRCode V4, can encode text len up to 1500 chars, display size = 132*132.
Definition: nbgl_types.h:193
@ QRCODE_V4
QRCode V4, can encode text len up to 62 chars, display size = 264*264.
Definition: nbgl_types.h:191
@ VERTICAL
from top to bottom
Definition: nbgl_types.h:170
@ HORIZONTAL
from left to right
Definition: nbgl_types.h:171
@ LOOP_PARSING
0, 1, 2, 0, 1, 2, ...
Definition: nbgl_types.h:374
#define SCREEN_HEIGHT
Definition: nbgl_types.h:45
struct PACKED__ nbgl_icon_details_s nbgl_icon_details_t
Represents all information about an icon.
@ TOP_MIDDLE
Definition: nbgl_types.h:143
@ CENTER
Definition: nbgl_types.h:146
@ BOTTOM_RIGHT
Definition: nbgl_types.h:150
@ TOP_LEFT
Definition: nbgl_types.h:142
@ NO_ALIGNMENT
used when parent container layout is used
Definition: nbgl_types.h:141
@ BOTTOM_LEFT
Definition: nbgl_types.h:148
@ MID_RIGHT
Definition: nbgl_types.h:147
@ RIGHT_TOP
on outside right
Definition: nbgl_types.h:153
@ TOP_RIGHT
Definition: nbgl_types.h:144
@ MID_LEFT
Definition: nbgl_types.h:145
@ BOTTOM_MIDDLE
Definition: nbgl_types.h:149
@ IMAGE
Bitmap (y and height must be multiple of 4 on Stax)
Definition: nbgl_types.h:118
@ SWITCH
Switch to turn on/off something.
Definition: nbgl_types.h:122
@ RADIO_BUTTON
Indicator to inform whether something is on or off.
Definition: nbgl_types.h:125
@ SPINNER
Spinner.
Definition: nbgl_types.h:129
@ BUTTON
Rounded rectangle button with icon and/or text.
Definition: nbgl_types.h:121
@ PROGRESS_BAR
horizontal bar to indicate progression of something (between 0% and 100%)
Definition: nbgl_types.h:124
@ QR_CODE
QR Code.
Definition: nbgl_types.h:126
@ PAGE_INDICATOR
horizontal bar to indicate position within pages
Definition: nbgl_types.h:123
@ LINE
Vertical or Horizontal line.
Definition: nbgl_types.h:119
@ CONTAINER
Empty container.
Definition: nbgl_types.h:117
@ TEXT_AREA
Area to contain text line(s)
Definition: nbgl_types.h:120
#define MAX(x, y)
Definition: nbgl_types.h:82
@ NBGL_BPP_1
1 bit per pixel
Definition: nbgl_types.h:245
@ NO_STYLE
no border
Definition: nbgl_types.h:179
@ BLACK_AND_WHITE_REFRESH
to be used for pure B&W area, when contrast is important
Definition: nbgl_types.h:291
@ BLACK_AND_WHITE_FAST_REFRESH
to be used for pure B&W area, when contrast is not priority
Definition: nbgl_types.h:292
@ FULL_COLOR_PARTIAL_REFRESH
to be used for small partial refresh (radio buttons, switches)
Definition: nbgl_types.h:289
nbgl_obj_t * obj
tune_index_e tuneId
nbgl_state_t state
Definition: nbgl_layout.c:101
const char * text
Definition: nbgl_layout.c:98
const nbgl_icon_details_t * iconRight
Definition: nbgl_layout.c:96
const char * subText
Definition: nbgl_layout.c:99
listItemType_t type
Definition: nbgl_layout.c:93
uint8_t token
Definition: nbgl_layout.c:100
const nbgl_icon_details_t * iconLeft
Definition: nbgl_layout.c:94
uint16_t delayMs
delay between 2 drawings
Definition: nbgl_types.h:386
uint8_t nbIcons
number of icons in icons array
Definition: nbgl_types.h:384
const nbgl_icon_details_t ** icons
array of nbIcons pointers on icons
Definition: nbgl_types.h:383
nbgl_parsingType_t parsing
Definition: nbgl_types.h:385
This structure contains info to build a centered (vertically and horizontally) area,...
Definition: nbgl_content.h:90
uint16_t iconHug
vertical margin to apply on top and bottom of the icon
Definition: nbgl_content.h:101
const nbgl_icon_details_t * icon
the icon (can be null)
Definition: nbgl_content.h:92
const char * title
title in black large (can be null)
Definition: nbgl_content.h:97
const char * description
description in black small regular case (can be null)
Definition: nbgl_content.h:99
const char * subText
sub-text in dark gray regular small case
Definition: nbgl_content.h:100
uint16_t animOffsetY
vertical offset of animation in icon if integrated (but usually 0)
Definition: nbgl_content.h:96
bool padding
if true, apply a padding of 40px at the bottom
Definition: nbgl_content.h:102
const nbgl_animation_t * animation
the animation (can be null), used if illustrType is ANIM_ILLUSTRATION
Definition: nbgl_content.h:94
const char * smallTitle
sub-title in black small bold case (can be null)
Definition: nbgl_content.h:98
uint16_t animOffsetX
horizontal offset of animation in icon if integrated (but usually 0)
Definition: nbgl_content.h:95
nbgl_contentIllustrationType_t illustrType
Definition: nbgl_content.h:91
This structure contains info to build a centered (vertically and horizontally) area,...
Definition: nbgl_content.h:57
const char * text2
second text (can be null)
Definition: nbgl_content.h:59
const char * text1
first text (can be null)
Definition: nbgl_content.h:58
bool onTop
if set to true, align only horizontally
Definition: nbgl_content.h:64
nbgl_contentCenteredInfoStyle_t style
style to apply to this info
Definition: nbgl_content.h:65
int16_t offsetY
vertical shift to apply to this info (if >0, shift to bottom)
Definition: nbgl_content.h:67
const char * text3
third text (can be null)
Definition: nbgl_content.h:61
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon
Definition: nbgl_content.h:63
This structure contains a list of names to build a list of radio buttons (on the right part of screen...
Definition: nbgl_content.h:300
uint8_t token
the token that will be used as argument of the callback
Definition: nbgl_content.h:310
bool localized
if set to true, use nameIds and not names
Definition: nbgl_content.h:307
uint8_t initChoice
index of the current choice
Definition: nbgl_content.h:309
const char *const * names
array of strings giving the choices (nbChoices)
Definition: nbgl_content.h:302
uint8_t nbChoices
number of choices
Definition: nbgl_content.h:308
This structure contains info to build a switch (on the right) with a description (on the left),...
Definition: nbgl_content.h:260
const char * text
main text for the switch
Definition: nbgl_content.h:261
uint8_t token
the token that will be used as argument of the callback
Definition: nbgl_content.h:265
nbgl_state_t initState
initial state of the switch
Definition: nbgl_content.h:264
const char * subText
description under main text (NULL terminated, single line, may be null)
Definition: nbgl_content.h:263
This structure contains a list of [tag,value] pairs.
Definition: nbgl_content.h:199
const nbgl_contentTagValue_t * pairs
array of [tag,value] pairs (nbPairs items). If NULL, callback is used instead
Definition: nbgl_content.h:201
bool wrapping
if set to true, value text will be wrapped on ' ' to avoid cutting words
Definition: nbgl_content.h:212
uint8_t startIndex
index of the first pair to get with callback
Definition: nbgl_content.h:205
nbgl_contentTagValueCallback_t callback
function to call to retrieve a given pair
Definition: nbgl_content.h:202
This structure contains a [tag,value] pair.
Definition: nbgl_content.h:162
const nbgl_icon_details_t * valueIcon
Definition: nbgl_content.h:167
const char * value
string giving the value name
Definition: nbgl_content.h:164
const char * item
string giving the tag name
Definition: nbgl_content.h:163
const char * text
text of the tip-box
Definition: nbgl_content.h:334
const nbgl_icon_details_t * icon
icon of the tip-box
Definition: nbgl_content.h:335
uint8_t token
token used when tip-box is tapped
Definition: nbgl_content.h:336
structure defining an ASCII font
Definition: nbgl_fonts.h:80
uint8_t line_height
height of a line for all characters in pixels
Definition: nbgl_fonts.h:85
This structure contains info to build a clickable "bar" with a text and an icon.
Definition: nbgl_layout.h:197
bool inactive
if set to true, the bar is grayed-out and cannot be touched
Definition: nbgl_layout.h:206
const char * text
text (can be NULL)
Definition: nbgl_layout.h:200
uint8_t token
the token that will be used as argument of the callback
Definition: nbgl_layout.h:205
bool large
set to true only for the main level of OS settings
Definition: nbgl_layout.h:204
const char * subText
sub text (can be NULL)
Definition: nbgl_layout.h:203
const nbgl_icon_details_t * iconLeft
a buffer containing the 1BPP icon for icon on left (can be NULL)
Definition: nbgl_layout.h:199
const nbgl_icon_details_t * iconRight
Definition: nbgl_layout.h:201
This structure contains info to build a single button.
Definition: nbgl_layout.h:344
const char * text
button text
Definition: nbgl_layout.h:345
uint8_t token
the token that will be used as argument of the callback
Definition: nbgl_layout.h:347
const nbgl_icon_details_t * icon
a buffer containing the 1BPP icon for button
Definition: nbgl_layout.h:346
nbgl_layoutButtonStyle_t style
Definition: nbgl_layout.h:348
bool fittingContent
if set to true, fit the width of button to text, otherwise full width
Definition: nbgl_layout.h:349
This structure contains info to build a pair of buttons, one on top of the other.
Definition: nbgl_layout.h:302
nbgl_layoutChoiceButtonsStyle_t style
the style of the pair
Definition: nbgl_layout.h:307
const nbgl_icon_details_t * topIcon
icon of top button
Definition: nbgl_layout.h:305
uint8_t token
the token that will be used as argument of the callback
Definition: nbgl_layout.h:306
const char * topText
up-button text (index 0)
Definition: nbgl_layout.h:303
const char * bottomText
bottom-button text (index 1)
Definition: nbgl_layout.h:304
Structure containing all information when creating a layout. This structure must be passed as argumen...
Definition: nbgl_layout.h:171
nbgl_screenTickerConfiguration_t ticker
Definition: nbgl_layout.h:190
const char * tapActionText
Light gray text used when main container is "tapable".
Definition: nbgl_layout.h:177
nbgl_layoutTouchCallback_t onActionCallback
the callback to be called on any action on the layout
Definition: nbgl_layout.h:185
This structure contains info to build an extended footer.
Definition: nbgl_layout.h:491
nbgl_layoutChoiceButtons_t choiceButtons
if type is FOOTER_CHOICE_BUTTONS
Definition: nbgl_layout.h:519
struct nbgl_layoutFooter_t::@15::@17 emptySpace
if type is FOOTER_EMPTY
bool separationLine
if true, a separation line is added at the top of this control
Definition: nbgl_layout.h:493
struct nbgl_layoutFooter_t::@15::@18 simpleText
if type is FOOTER_SIMPLE_TEXT
nbgl_layoutButton_t button
if type is FOOTER_SIMPLE_BUTTON
Definition: nbgl_layout.h:518
nbgl_layoutFooterType_t type
type of footer
Definition: nbgl_layout.h:492
nbgl_layoutNavigationBar_t navigation
if type is FOOTER_NAV
Definition: nbgl_layout.h:512
struct nbgl_layoutFooter_t::@15::@20 textAndNav
if type is FOOTER_TEXT_AND_NAV
struct nbgl_layoutFooter_t::@15::@19 doubleText
if type is FOOTER_DOUBLE_TEXT
This structure contains info to build a header.
Definition: nbgl_layout.h:429
nbgl_layoutHeaderType_t type
type of header
Definition: nbgl_layout.h:430
struct nbgl_layoutHeader_t::@7::@12 title
if type is HEADER_TITLE
struct nbgl_layoutHeader_t::@7::@10 backAndText
if type is HEADER_BACK_ICON_AND_TEXT or HEADER_BACK_AND_TEXT
bool separationLine
if true, a separation line is added at the bottom of this control
Definition: nbgl_layout.h:431
struct nbgl_layoutHeader_t::@7::@13 extendedBack
if type is HEADER_EXTENDED_BACK
struct nbgl_layoutHeader_t::@7::@11 progressAndBack
if type is HEADER_BACK_AND_PROGRESS
struct nbgl_layoutHeader_t::@7::@9 emptySpace
if type is HEADER_EMPTY
struct nbgl_layoutHeader_t::@7::@14 rightText
if type is HEADER_RIGHT_TEXT
This structure contains info to build a pair of buttons, the small one, with icon,...
Definition: nbgl_layout.h:319
const nbgl_icon_details_t * leftIcon
a buffer containing the 1BPP icon for left button
Definition: nbgl_layout.h:320
uint8_t leftToken
the token used when left button is pressed
Definition: nbgl_layout.h:322
uint8_t rightToken
the token used when right button is pressed
Definition: nbgl_layout.h:323
const char * rightText
right-button text
Definition: nbgl_layout.h:321
Structure containing all information about the current layout.
nbgl_container_t * footerContainer
container used to store footer (buttons, nav....)
bool incrementAnim
if true, means that animation index is currently incrementing
uint8_t activePage
index of active page for navigation bar
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
bool modal
if true, means the screen is a modal
uint8_t nbChildren
number of children in above array
nbgl_layoutHeaderType_t headerType
type of header
nbgl_container_t * upFooterContainer
uint8_t nbPages
number of pages for navigation bar
nbgl_layoutUpFooterType_t upFooterType
type of up-footer
layoutObj_t callbackObjPool[LAYOUT_OBJ_POOL_LEN]
nbgl_text_area_t * tapText
nbgl_obj_t ** children
children for main screen
const nbgl_animation_t * animation
current animation (if not NULL)
This structure contains info to build a left content area.
Definition: nbgl_layout.h:262
uint8_t nbRows
number of rows in the area
Definition: nbgl_layout.h:263
const char * title
title of area in bold
Definition: nbgl_layout.h:264
const nbgl_icon_details_t ** rowIcons
array of nbRows icon
Definition: nbgl_layout.h:266
const char ** rowTexts
array of nbRows texts (displayed in regular)
Definition: nbgl_layout.h:265
This structure contains info to build a navigation bar at the bottom of the screen.
Definition: nbgl_layout.h:118
uint8_t activePage
index of active page (from 0 to nbPages-1).
Definition: nbgl_layout.h:121
bool withBackKey
if set to true, the "back" key is drawn
Definition: nbgl_layout.h:123
bool withExitKey
if set to true, an exit button is drawn (X on the left)
Definition: nbgl_layout.h:122
uint8_t token
the token that will be used as argument of the callback
Definition: nbgl_layout.h:119
uint8_t nbPages
number of pages. (if 0, no navigation)
Definition: nbgl_layout.h:120
This structure contains info to build a progress bar with info. The progress bar itself is 120px widt...
Definition: nbgl_layout.h:566
uint8_t percentage
percentage of completion, from 0 to 100.
Definition: nbgl_layout.h:567
const char * text
text in black, on top of progress bar
Definition: nbgl_layout.h:568
const char * subText
text in gray, under progress bar
Definition: nbgl_layout.h:569
This structure contains info to build a centered (vertically and horizontally) area,...
Definition: nbgl_layout.h:277
const char * text2
second text (can be null)
Definition: nbgl_layout.h:280
const char * text1
first text (can be null)
Definition: nbgl_layout.h:279
const char * url
URL for QR code.
Definition: nbgl_layout.h:278
bool largeText1
if set to true, use 32px font for text1
Definition: nbgl_layout.h:283
int16_t offsetY
vertical shift to apply to this info (if > 0, shift to bottom)
Definition: nbgl_layout.h:281
bool centered
if set to true, center vertically
Definition: nbgl_layout.h:282
This structure contains info to build an up-footer (area on top of footer).
Definition: nbgl_layout.h:540
nbgl_layoutButton_t button
if type is UP_FOOTER_BUTTON
Definition: nbgl_layout.h:548
nbgl_contentTipBox_t tipBox
if type is UP_FOOTER_TIP_BOX
Definition: nbgl_layout.h:551
const char * text
text in the long-press button
Definition: nbgl_layout.h:544
nbgl_layoutUpFooterType_t type
type of up-footer
Definition: nbgl_layout.h:541
nbgl_layoutHorizontalButtons_t horizontalButtons
if type is UP_FOOTER_HORIZONTAL_BUTTONS
Definition: nbgl_layout.h:550
struct nbgl_layoutUpFooter_t::@21::@23 longPress
if type is UP_FOOTER_LONG_PRESS
signed short int16_t
Definition: usbd_conf.h:50
unsigned short uint16_t
Definition: usbd_conf.h:54
unsigned char uint8_t
Definition: usbd_conf.h:53