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