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#include "nbgl_types.h"
9
10#ifdef HAVE_SE_TOUCH
11/*********************
12 * INCLUDES
13 *********************/
14#include <string.h>
15#include <stdlib.h>
16
17#ifdef HAVE_BOLOS
18#include "localization.h"
19#endif
20#include "nbgl_debug.h"
21#include "nbgl_front.h"
23#include "nbgl_obj.h"
24#include "nbgl_draw.h"
25#include "nbgl_screen.h"
26#include "nbgl_touch.h"
27#include "glyphs.h"
28#include "os_io_seph_ux.h"
29#include "os_pic.h"
30#include "os_helpers.h"
31#include "lcx_rng.h"
32
33/*********************
34 * DEFINES
35 *********************/
36// half internal margin, between items
37#define INTERNAL_SPACE 16
38// inner margin, between buttons
39#define INNER_MARGIN 12
40
41#define NB_MAX_LAYOUTS 3
42
43// used by container
44#define NB_MAX_CONTAINER_CHILDREN 20
45
46#define TAG_VALUE_ICON_WIDTH 32
47
48#if defined(TARGET_STAX)
49#define RADIO_CHOICE_HEIGHT 96
50#define BAR_INTERVALE 12
51#define FOOTER_BUTTON_HEIGHT 128
52#define FOOTER_IN_PAIR_HEIGHT 80
53#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 192
54#define FOOTER_TEXT_AND_NAV_WIDTH 160
55#define TAP_TO_CONTINUE_MARGIN 24
56#define SUB_HEADER_MARGIN 24
57#define PRE_FIRST_TEXT_MARGIN 24
58#define INTER_PARAGRAPHS_MARGIN 40
59#define PRE_TITLE_MARGIN 24
60#define PRE_DESCRIPTION_MARGIN 16
61#define INTER_DESCRIPTIONS_MARGIN 16
62#define PRE_FIRST_ROW_MARGIN 32
63#define INTER_ROWS_MARGIN 16
64#define QR_PRE_TEXT_MARGIN 24
65#define QR_INTER_TEXTS_MARGIN 40
66#define SPINNER_TEXT_MARGIN 20
67#define SPINNER_INTER_TEXTS_MARGIN 20
68#define BAR_TEXT_MARGIN 24
69#define BAR_INTER_TEXTS_MARGIN 16
70#define LEFT_CONTENT_TEXT_PADDING 0
71#define BUTTON_FROM_BOTTOM_MARGIN 4
72#define TOP_BUTTON_MARGIN VERTICAL_BORDER_MARGIN
73#define TOP_BUTTON_MARGIN_WITH_ACTION VERTICAL_BORDER_MARGIN
74#define SINGLE_BUTTON_MARGIN 24
75#define LONG_PRESS_PROGRESS_HEIGHT 8
76#define LONG_PRESS_PROGRESS_ALIGN 1
77#define LEFT_CONTENT_ICON_TEXT_X 16
78#define TIP_BOX_MARGIN_Y 24
79#define TIP_BOX_TEXT_ICON_MARGIN 24
80#elif defined(TARGET_FLEX)
81#define RADIO_CHOICE_HEIGHT 92
82#define BAR_INTERVALE 16
83#define FOOTER_BUTTON_HEIGHT 136
84#define FOOTER_IN_PAIR_HEIGHT 88
85#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 208
86#define FOOTER_TEXT_AND_NAV_WIDTH 192
87#define TAP_TO_CONTINUE_MARGIN 30
88#define SUB_HEADER_MARGIN 28
89#define PRE_FIRST_TEXT_MARGIN 0
90#define INTER_PARAGRAPHS_MARGIN 24
91#define PRE_TITLE_MARGIN 16
92#define PRE_DESCRIPTION_MARGIN 24
93#define INTER_DESCRIPTIONS_MARGIN 24
94#define PRE_FIRST_ROW_MARGIN 32
95#define INTER_ROWS_MARGIN 24
96#define QR_PRE_TEXT_MARGIN 24
97#define QR_INTER_TEXTS_MARGIN 28
98#define SPINNER_TEXT_MARGIN 24
99#define SPINNER_INTER_TEXTS_MARGIN 16
100#define BAR_TEXT_MARGIN 24
101#define BAR_INTER_TEXTS_MARGIN 16
102#define LEFT_CONTENT_TEXT_PADDING 4
103#define BUTTON_FROM_BOTTOM_MARGIN 4
104#define TOP_BUTTON_MARGIN VERTICAL_BORDER_MARGIN
105#define TOP_BUTTON_MARGIN_WITH_ACTION VERTICAL_BORDER_MARGIN
106#define SINGLE_BUTTON_MARGIN 24
107#define LONG_PRESS_PROGRESS_HEIGHT 8
108#define LONG_PRESS_PROGRESS_ALIGN 1
109#define LEFT_CONTENT_ICON_TEXT_X 16
110#define TIP_BOX_MARGIN_Y 24
111#define TIP_BOX_TEXT_ICON_MARGIN 32
112#elif defined(TARGET_APEX)
113#define RADIO_CHOICE_HEIGHT 68
114#define BAR_INTERVALE 8
115#define FOOTER_BUTTON_HEIGHT 88
116#define FOOTER_IN_PAIR_HEIGHT 60
117#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 128
118#define FOOTER_TEXT_AND_NAV_WIDTH 120
119#define TAP_TO_CONTINUE_MARGIN 30
120#define SUB_HEADER_MARGIN 16
121#define PRE_FIRST_TEXT_MARGIN 0
122#define INTER_PARAGRAPHS_MARGIN 16
123#define PRE_TITLE_MARGIN 16
124#define PRE_DESCRIPTION_MARGIN 12
125#define INTER_DESCRIPTIONS_MARGIN 12
126#define PRE_FIRST_ROW_MARGIN 24
127#define INTER_ROWS_MARGIN 12
128#define QR_PRE_TEXT_MARGIN 16
129#define QR_INTER_TEXTS_MARGIN 20
130#define SPINNER_TEXT_MARGIN 16
131#define SPINNER_INTER_TEXTS_MARGIN 16
132#define BAR_TEXT_MARGIN 16
133#define BAR_INTER_TEXTS_MARGIN 12
134#define LEFT_CONTENT_TEXT_PADDING 4
135#define BUTTON_FROM_BOTTOM_MARGIN 0
136#define TOP_BUTTON_MARGIN 12
137#define TOP_BUTTON_MARGIN_WITH_ACTION 0
138#define SINGLE_BUTTON_MARGIN 16
139#define LONG_PRESS_PROGRESS_HEIGHT 4
140#define LONG_PRESS_PROGRESS_ALIGN 0
141#define LEFT_CONTENT_ICON_TEXT_X 8
142#define TIP_BOX_MARGIN_Y 12
143#define TIP_BOX_TEXT_ICON_MARGIN 20
144#else // TARGETS
145#error Undefined target
146#endif // TARGETS
147
148// refresh period of the spinner, in ms
149#define SPINNER_REFRESH_PERIOD 400
150
151/**********************
152 * MACROS
153 **********************/
154
155/**********************
156 * TYPEDEFS
157 **********************/
158
165
166// used to build either a touchable bar or a switch
167typedef struct {
169 const nbgl_icon_details_t *iconLeft; // a buffer containing the 1BPP icon for icon on
170 // left (can be NULL)
171 const nbgl_icon_details_t *iconRight; // a buffer containing the 1BPP icon for icon 2 (can be
172 // NULL). Dimensions must be the same as iconLeft
173 const char *text; // text (can be NULL)
174 const char *subText; // sub text (can be NULL)
175 uint8_t token; // the token that will be used as argument of the callback
176 nbgl_state_t state; // state of the item
177 bool large; // set to true only for the main level of OS settings
178 uint8_t index; // index of obj (for callback)
179 tune_index_e tuneId; // if not @ref NBGL_NO_TUNE, a tune will be played
180} listItem_t;
181
182/**********************
183 * VARIABLES
184 **********************/
185
190static nbgl_layoutInternal_t gLayout[NB_MAX_LAYOUTS] = {0};
191
192// current top of stack
193static nbgl_layoutInternal_t *topLayout;
194
195// numbers of touchable controls for the whole page
196static uint8_t nbTouchableControls = 0;
197
198/**********************
199 * STATIC PROTOTYPES
200 **********************/
201// extern const char *get_ux_loc_string(UX_LOC_STRINGS_INDEX index);
202
203// Hold-to-approve parameters
204#if defined(TARGET_FLEX)
205// Percent of a single "hold-to-approve" bar
206#define HOLD_TO_APPROVE_STEP_PERCENT (7)
207// Duration between two bars
208#define HOLD_TO_APPROVE_STEP_DURATION_MS (100)
209// Number of bars displayed when the user start the "hold-to-approve" mechanism
210#define HOLD_TO_APPROVE_FIRST_STEP (0)
211#elif defined(TARGET_STAX)
212#define HOLD_TO_APPROVE_STEP_PERCENT (25)
213#define HOLD_TO_APPROVE_STEP_DURATION_MS (400)
214#define HOLD_TO_APPROVE_FIRST_STEP (1)
215#elif defined(TARGET_APEX)
216#define HOLD_TO_APPROVE_STEP_PERCENT (20)
217#define HOLD_TO_APPROVE_STEP_DURATION_MS (300)
218#define HOLD_TO_APPROVE_FIRST_STEP (1)
219#endif // TARGETS
220
221static inline uint8_t get_hold_to_approve_percent(uint32_t touch_duration)
222{
223 uint8_t current_step_nb
224 = (touch_duration / HOLD_TO_APPROVE_STEP_DURATION_MS) + HOLD_TO_APPROVE_FIRST_STEP;
225 return (current_step_nb * HOLD_TO_APPROVE_STEP_PERCENT);
226}
227
228// function used to retrieve the concerned layout and layout obj matching the given touched obj
229static bool getLayoutAndLayoutObj(nbgl_obj_t *obj,
230 nbgl_layoutInternal_t **layout,
231 layoutObj_t **layoutObj)
232{
233 // only try to find the object on top layout on the stack (the other cannot receive touch
234 // events)
235 *layout = NULL;
236 if ((topLayout) && (topLayout->isUsed)) {
237 uint8_t j;
238
239 // search index of obj in this layout
240 for (j = 0; j < topLayout->nbUsedCallbackObjs; j++) {
241 if (obj == topLayout->callbackObjPool[j].obj) {
243 "getLayoutAndLayoutObj(): obj found in layout %p "
244 "nbUsedCallbackObjs = %d\n",
245 topLayout,
246 topLayout->nbUsedCallbackObjs);
247 *layout = topLayout;
248 *layoutObj = &(topLayout->callbackObjPool[j]);
249 return true;
250 }
251 }
252 }
253 // not found
254 return false;
255}
256
257static void radioTouchCallback(nbgl_obj_t *obj,
258 nbgl_touchType_t eventType,
259 nbgl_layoutInternal_t *layout);
260static void longTouchCallback(nbgl_obj_t *obj,
261 nbgl_touchType_t eventType,
262 nbgl_layoutInternal_t *layout,
263 layoutObj_t *layoutObj);
264
265// callback for most touched object
266static void touchCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType)
267{
268 nbgl_layoutInternal_t *layout;
269 layoutObj_t *layoutObj;
270 bool needRefresh = false;
271
272 if (obj == NULL) {
273 return;
274 }
275 LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): eventType = %d, obj = %p\n", eventType, obj);
276 if (getLayoutAndLayoutObj(obj, &layout, &layoutObj) == false) {
277 // try with parent, if existing
278 if (getLayoutAndLayoutObj(obj->parent, &layout, &layoutObj) == false) {
279 LOG_WARN(
281 "touchCallback(): eventType = %d, obj = %p, no active layout or obj not found\n",
282 eventType,
283 obj);
284 return;
285 }
286 }
287
288 // case of swipe
289 if (((eventType == SWIPED_UP) || (eventType == SWIPED_DOWN) || (eventType == SWIPED_LEFT)
290 || (eventType == SWIPED_RIGHT))
291 && (obj->type == CONTAINER)) {
292#ifdef NBGL_KEYBOARD
293 if (layout->swipeUsage == SWIPE_USAGE_SUGGESTIONS) {
294 keyboardSwipeCallback(layout, obj, eventType);
295 return;
296 }
297#endif // NBGL_KEYBOARD
298 if (layout->swipeUsage == SWIPE_USAGE_CUSTOM) {
299 layoutObj->index = eventType;
300 }
301 else if ((layout->swipeUsage == SWIPE_USAGE_NAVIGATION)
302 && ((nbgl_obj_t *) layout->container == obj)) {
303 nbgl_container_t *navContainer;
304 if (layout->footerType == FOOTER_NAV) {
305 navContainer = (nbgl_container_t *) layout->footerContainer;
306 }
307 else if (layout->footerType == FOOTER_TEXT_AND_NAV) {
308 navContainer = (nbgl_container_t *) layout->footerContainer->children[1];
309 }
310 else {
311 return;
312 }
313
315 (nbgl_obj_t *) navContainer, eventType, layout->nbPages, &layout->activePage)
316 == false) {
317 // navigation was impossible
318 return;
319 }
320 layoutObj->index = layout->activePage;
321 }
322 }
323
324 // case of navigation bar
325 if (((obj->parent == (nbgl_obj_t *) layout->footerContainer)
326 && (layout->footerType == FOOTER_NAV))
327 || ((obj->parent->type == CONTAINER)
328 && (obj->parent->parent == (nbgl_obj_t *) layout->footerContainer)
329 && (layout->footerType == FOOTER_TEXT_AND_NAV))) {
330 if (layoutNavigationCallback(obj, eventType, layout->nbPages, &layout->activePage)
331 == false) {
332 // navigation was impossible
333 return;
334 }
335 layoutObj->index = layout->activePage;
336 }
337
338 // case of switch
339 if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren >= 2)
340 && (((nbgl_container_t *) obj)->children[1] != NULL)
341 && (((nbgl_container_t *) obj)->children[1]->type == SWITCH)) {
342 nbgl_switch_t *lSwitch = (nbgl_switch_t *) ((nbgl_container_t *) obj)->children[1];
343 lSwitch->state = (lSwitch->state == ON_STATE) ? OFF_STATE : ON_STATE;
344 nbgl_objDraw((nbgl_obj_t *) lSwitch);
345 // refresh will be done after tune playback
346 needRefresh = true;
347 // index is used for state
348 layoutObj->index = lSwitch->state;
349 }
350 // case of radio
351 else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 2)
352 && (((nbgl_container_t *) obj)->children[1] != NULL)
353 && (((nbgl_container_t *) obj)->children[1]->type == RADIO_BUTTON)) {
354 radioTouchCallback(obj, eventType, layout);
355 return;
356 }
357 // case of long press
358 else if ((obj->type == CONTAINER) && (((nbgl_container_t *) obj)->nbChildren == 4)
359 && (((nbgl_container_t *) obj)->children[3] != NULL)
360 && (((nbgl_container_t *) obj)->children[3]->type == PROGRESS_BAR)) {
361 longTouchCallback(obj, eventType, layout, layoutObj);
362 return;
363 }
364 LOG_DEBUG(LAYOUT_LOGGER, "touchCallback(): layout->callback = %p\n", layout->callback);
365 if ((layout->callback != NULL) && (layoutObj->token != NBGL_INVALID_TOKEN)) {
366#ifdef HAVE_PIEZO_SOUND
367 if (layoutObj->tuneId < NBGL_NO_TUNE) {
368 os_io_seph_cmd_piezo_play_tune(layoutObj->tuneId);
369 }
370#endif // HAVE_PIEZO_SOUND
371 if (needRefresh) {
373 }
374 layout->callback(layoutObj->token, layoutObj->index);
375 }
376}
377
378// callback for long press button
379static void longTouchCallback(nbgl_obj_t *obj,
380 nbgl_touchType_t eventType,
381 nbgl_layoutInternal_t *layout,
382 layoutObj_t *layoutObj)
383{
384 nbgl_container_t *container = (nbgl_container_t *) obj;
385 // 4th child of container is the progress bar
386 nbgl_progress_bar_t *progressBar = (nbgl_progress_bar_t *) container->children[3];
387
388 LOG_DEBUG(LAYOUT_LOGGER, "longTouchCallback(): eventType = %d, obj = %p\n", eventType, obj);
389
390 // case of pressing a long press button
391 if (eventType == TOUCHING) {
392 uint32_t touchDuration = nbgl_touchGetTouchDuration(obj);
393
394 // Compute the new progress bar state in %
395 uint8_t new_state = get_hold_to_approve_percent(touchDuration);
396
397 // Ensure the callback is triggered once,
398 // when the progress bar state reaches 100%
399 bool trigger_callback = (new_state >= 100) && (progressBar->state < 100);
400
401 // Cap progress bar state at 100%
402 if (new_state >= 100) {
403 new_state = 100;
404 }
405
406 // Update progress bar state
407 if (new_state != progressBar->state) {
408 progressBar->partialRedraw = true;
409 progressBar->state = new_state;
410
411 nbgl_objDraw((nbgl_obj_t *) progressBar);
412 // Ensure progress bar is fully drawn
413 // before calling the callback.
416 }
417
418 if (trigger_callback) {
419 // End of progress bar reached: trigger callback
420 if (layout->callback != NULL) {
421 layout->callback(layoutObj->token, layoutObj->index);
422 }
423 }
424 }
425 // case of releasing a long press button (or getting out of it)
426 else if ((eventType == TOUCH_RELEASED) || (eventType == OUT_OF_TOUCH)
427 || (eventType == SWIPED_LEFT) || (eventType == SWIPED_RIGHT)) {
429 progressBar->partialRedraw = true;
430 progressBar->state = 0;
431 nbgl_objDraw((nbgl_obj_t *) progressBar);
432 // It's necessary to redraw the grey/dotted line, that has been partially wiped
433 nbgl_line_t *line = (nbgl_line_t *) container->children[2];
434 nbgl_objDraw((nbgl_obj_t *) line);
436 }
437}
438
439// callback for radio button touch
440static void radioTouchCallback(nbgl_obj_t *obj,
441 nbgl_touchType_t eventType,
442 nbgl_layoutInternal_t *layout)
443{
444 uint8_t i = NB_MAX_LAYOUTS, radioIndex = 0, foundRadio = 0xFF, foundRadioIndex;
445
446 if (eventType != TOUCHED) {
447 return;
448 }
449
450 i = 0;
451 // parse all objs to find all containers of radio buttons
452 while (i < layout->nbUsedCallbackObjs) {
453 if ((obj == (nbgl_obj_t *) layout->callbackObjPool[i].obj)
454 && (layout->callbackObjPool[i].obj->type == CONTAINER)) {
455 nbgl_radio_t *radio
456 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
457 ->children[1];
458 nbgl_text_area_t *textArea
460 ->children[0];
461 foundRadio = i;
462 foundRadioIndex = radioIndex;
463 // set text as active (black and bold)
464 textArea->textColor = BLACK;
465 textArea->fontId = SMALL_BOLD_FONT;
466 // ensure that radio button is ON
467 radio->state = ON_STATE;
468 // redraw container
469 nbgl_objDraw((nbgl_obj_t *) obj);
470 }
471 else if ((layout->callbackObjPool[i].obj->type == CONTAINER)
472 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->nbChildren == 2)
473 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->children[1]->type
474 == RADIO_BUTTON)) {
475 nbgl_radio_t *radio
476 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
477 ->children[1];
478 nbgl_text_area_t *textArea
480 ->children[0];
481 radioIndex++;
482 // set to OFF the one that was in ON
483 if (radio->state == ON_STATE) {
484 radio->state = OFF_STATE;
485 // set text it as inactive (gray and normal)
486 textArea->textColor = LIGHT_TEXT_COLOR;
487 textArea->fontId = SMALL_REGULAR_FONT;
488 // redraw container
490 }
491 }
492 i++;
493 }
494 // call callback after redraw to avoid asynchronicity
495 if (foundRadio != 0xFF) {
496 if (layout->callback != NULL) {
497#ifdef HAVE_PIEZO_SOUND
498 if (layout->callbackObjPool[foundRadio].tuneId < NBGL_NO_TUNE) {
499 os_io_seph_cmd_piezo_play_tune(layout->callbackObjPool[foundRadio].tuneId);
500 }
501#endif // HAVE_PIEZO_SOUND
503 layout->callback(layout->callbackObjPool[foundRadio].token, foundRadioIndex);
504 }
505 }
506}
507
508// callback for spinner ticker
509static void spinnerTickerCallback(void)
510{
511 nbgl_spinner_t *spinner;
512 uint8_t i = 0;
513
514 if (!topLayout || !topLayout->isUsed) {
515 return;
516 }
517
518 // get index of obj
519 while (i < topLayout->container->nbChildren) {
520 if (topLayout->container->children[i]->type == CONTAINER) {
521 nbgl_container_t *container = (nbgl_container_t *) topLayout->container->children[i];
522 if (container->nbChildren && (container->children[0]->type == SPINNER)) {
523 spinner = (nbgl_spinner_t *) container->children[0];
524 spinner->position++;
525 // there are only NB_SPINNER_POSITIONS positions
526 if (spinner->position == NB_SPINNER_POSITIONS) {
527 spinner->position = 0;
528 }
529 nbgl_objDraw((nbgl_obj_t *) spinner);
531 return;
532 }
533 }
534 i++;
535 }
536}
537
538// callback for animation ticker
539static void animTickerCallback(void)
540{
541 nbgl_image_t *image;
542 uint8_t i = 0;
543 nbgl_layoutInternal_t *layout = topLayout;
544
545 if (!layout || !layout->isUsed || (layout->animation == NULL)) {
546 return;
547 }
548
549 // get index of image obj
550 while (i < layout->container->nbChildren) {
551 if (layout->container->children[i]->type == CONTAINER) {
552 nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
553 if (container->children[1]->type == IMAGE) {
554 image = (nbgl_image_t *) container->children[1];
555 if (layout->animation->parsing == LOOP_PARSING) {
556 if (layout->iconIdxInAnim == (layout->animation->nbIcons - 1)) {
557 layout->iconIdxInAnim = 0;
558 }
559 else {
560 layout->iconIdxInAnim++;
561 }
562 }
563 else {
564 // Flip incrementAnim when reaching upper or lower limit
565 if ((layout->incrementAnim)
566 && (layout->iconIdxInAnim >= layout->animation->nbIcons - 1)) {
567 layout->incrementAnim = false;
568 }
569 else if (layout->iconIdxInAnim == 0) {
570 layout->incrementAnim = true;
571 }
572
573 // Increase / Decrease index according to incrementAnim
574 if (layout->incrementAnim) {
575 layout->iconIdxInAnim++;
576 }
577 else {
578 layout->iconIdxInAnim--;
579 }
580 }
581 image->buffer = layout->animation->icons[layout->iconIdxInAnim];
582 nbgl_objDraw((nbgl_obj_t *) image);
585 return;
586 }
587 }
588 i++;
589 }
590}
591
592static nbgl_line_t *createHorizontalLine(uint8_t layer)
593{
594 nbgl_line_t *line;
595
596 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
597 line->lineColor = LIGHT_GRAY;
598 line->obj.area.width = SCREEN_WIDTH;
599 line->obj.area.height = 1;
600 line->direction = HORIZONTAL;
601 line->thickness = 1;
602 return line;
603}
604
605#ifdef TARGET_STAX
606static nbgl_line_t *createLeftVerticalLine(uint8_t layer)
607{
608 nbgl_line_t *line;
609
610 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
611 line->lineColor = LIGHT_GRAY;
612 line->obj.area.width = 1;
613 line->obj.area.height = SCREEN_HEIGHT;
614 line->direction = VERTICAL;
615 line->thickness = 1;
616 line->obj.alignment = MID_LEFT;
617 return line;
618}
619#endif // TARGET_STAX
620
621// function adding a layout object in the callbackObjPool array for the given layout, and
622// configuring it
624 nbgl_obj_t *obj,
625 uint8_t token,
626 tune_index_e tuneId)
627{
628 layoutObj_t *layoutObj = NULL;
629
630 if (layout->nbUsedCallbackObjs < (LAYOUT_OBJ_POOL_LEN - 1)) {
631 layoutObj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
632 layout->nbUsedCallbackObjs++;
633 layoutObj->obj = obj;
634 layoutObj->token = token;
635 layoutObj->tuneId = tuneId;
636 }
637 else {
638 LOG_FATAL(LAYOUT_LOGGER, "layoutAddCallbackObj: no more callback obj\n");
639 }
640
641 return layoutObj;
642}
643
653{
654 if (layout == NULL) {
655 return;
656 }
657
658 if (obj == NULL) {
659 return;
660 }
661
662 layoutObj_t *layoutObj = NULL;
663 for (size_t i = 0; i < layout->nbUsedCallbackObjs; i++) {
664 layoutObj = &layout->callbackObjPool[i];
665 if (layoutObj->obj == obj) {
666 layoutObj->token = token;
667 return;
668 }
669 }
670}
671
679{
680 if (layout->container->nbChildren == NB_MAX_CONTAINER_CHILDREN) {
681 LOG_FATAL(LAYOUT_LOGGER, "layoutAddObject(): No more object\n");
682 }
683 layout->container->children[layout->container->nbChildren] = obj;
684 layout->container->nbChildren++;
685}
686
696static int addSwipeInternal(nbgl_layoutInternal_t *layoutInt,
697 uint16_t swipesMask,
698 nbgl_swipe_usage_t usage,
699 uint8_t token,
700 tune_index_e tuneId)
701{
702 layoutObj_t *obj;
703
704 if ((swipesMask & SWIPE_MASK) == 0) {
705 return -1;
706 }
707
708 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) layoutInt->container, token, tuneId);
709 if (obj == NULL) {
710 return -1;
711 }
712 layoutInt->container->obj.touchMask = swipesMask;
713 layoutInt->swipeUsage = usage;
714
715 return 0;
716}
717
725static nbgl_container_t *addListItem(nbgl_layoutInternal_t *layoutInt, const listItem_t *itemDesc)
726{
727 layoutObj_t *obj;
728 nbgl_text_area_t *textArea = NULL;
729 nbgl_container_t *container;
730
731 color_t common_color = WHITE; // Color of all elements, except the right icon
732 color_t right_icon_color = WHITE; // Color of right icon, only
733 if ((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == OFF_STATE)) {
734 common_color = INACTIVE_TEXT_COLOR;
735 right_icon_color = INACTIVE_ARROW_COLOR;
736 }
737 else {
738 common_color = BLACK;
739 right_icon_color = BLACK;
740 }
741
742 nbgl_font_id_e fontId
743 = ((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == OFF_STATE))
745 : SMALL_BOLD_FONT;
746
747 LOG_DEBUG(LAYOUT_LOGGER, "addListItem():\n");
748
749 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
751 layoutInt, (nbgl_obj_t *) container, itemDesc->token, itemDesc->tuneId);
752 if (obj == NULL) {
753 return NULL;
754 }
755 obj->index = itemDesc->index;
756
757 // get container children (up to 4: text + left+right icons + sub text)
758 container->children = nbgl_containerPoolGet(4, layoutInt->layer);
759 container->nbChildren = 0;
760
761 container->obj.area.width = AVAILABLE_WIDTH;
762 container->obj.area.height
763 = LIST_ITEM_MIN_TEXT_HEIGHT
764 + 2 * (itemDesc->large ? LIST_ITEM_PRE_HEADING_LARGE : LIST_ITEM_PRE_HEADING);
765 container->layout = HORIZONTAL;
766 container->obj.alignmentMarginX = BORDER_MARGIN;
767 container->obj.alignment = NO_ALIGNMENT;
768 container->obj.alignTo = NULL;
769 // the bar can only be touched if not inactive AND if one of the icon is present
770 // otherwise it is seen as a title
771 if (((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == ON_STATE))
772 || (itemDesc->type == SWITCH_ITEM)) {
773 container->obj.touchMask = (1 << TOUCHED);
774 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
775 nbTouchableControls++;
776 }
777
778 // allocate main text if not NULL
779 if (itemDesc->text != NULL) {
780 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
781 textArea->textColor = common_color;
782 textArea->text = PIC(itemDesc->text);
783 textArea->onDrawCallback = NULL;
784 textArea->fontId = fontId;
785 textArea->wrapping = true;
786 textArea->obj.area.width = container->obj.area.width;
787 if (itemDesc->iconLeft != NULL) {
788 // reduce text width accordingly
789 textArea->obj.area.width
790 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE;
791 }
792 if (itemDesc->iconRight != NULL) {
793 // reduce text width accordingly
794 textArea->obj.area.width
795 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconRight))->width + BAR_INTERVALE;
796 }
797 else if (itemDesc->type == SWITCH_ITEM) {
798 textArea->obj.area.width -= SWITCH_ICON.width + BAR_INTERVALE;
799 }
800 textArea->obj.area.height = MAX(
801 LIST_ITEM_MIN_TEXT_HEIGHT,
803 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping));
804 textArea->style = NO_STYLE;
805 textArea->obj.alignment = TOP_LEFT;
806 textArea->obj.alignmentMarginY
807 = itemDesc->large ? LIST_ITEM_PRE_HEADING_LARGE : LIST_ITEM_PRE_HEADING;
808 if (textArea->obj.area.height > LIST_ITEM_MIN_TEXT_HEIGHT) {
809 textArea->obj.alignmentMarginY
810 -= (textArea->obj.area.height - LIST_ITEM_MIN_TEXT_HEIGHT) / 2;
811 }
812 textArea->textAlignment = MID_LEFT;
813 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
814 container->nbChildren++;
815 }
816
817 // allocate left icon if present
818 if (itemDesc->iconLeft != NULL) {
819 nbgl_image_t *imageLeft = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
820 imageLeft->foregroundColor = common_color;
821 imageLeft->buffer = PIC(itemDesc->iconLeft);
822 // align at the left of text
823 imageLeft->obj.alignment = MID_LEFT;
824 imageLeft->obj.alignTo = (nbgl_obj_t *) textArea;
825 imageLeft->obj.alignmentMarginX = BAR_INTERVALE;
826 container->children[container->nbChildren] = (nbgl_obj_t *) imageLeft;
827 container->nbChildren++;
828
829 if (textArea != NULL) {
830 textArea->obj.alignmentMarginX = imageLeft->buffer->width + BAR_INTERVALE;
831 }
832 }
833 // allocate right icon if present
834 if (itemDesc->iconRight != NULL) {
835 nbgl_image_t *imageRight = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
836 imageRight->foregroundColor = right_icon_color;
837 imageRight->buffer = PIC(itemDesc->iconRight);
838 // align at the right of text
839 imageRight->obj.alignment = MID_RIGHT;
840 imageRight->obj.alignmentMarginX = BAR_INTERVALE;
841 imageRight->obj.alignTo = (nbgl_obj_t *) textArea;
842
843 container->children[container->nbChildren] = (nbgl_obj_t *) imageRight;
844 container->nbChildren++;
845 }
846 else if (itemDesc->type == SWITCH_ITEM) {
847 nbgl_switch_t *switchObj = (nbgl_switch_t *) nbgl_objPoolGet(SWITCH, layoutInt->layer);
848 switchObj->onColor = BLACK;
849 switchObj->offColor = LIGHT_GRAY;
850 switchObj->state = itemDesc->state;
851 switchObj->obj.alignment = MID_RIGHT;
852 switchObj->obj.alignmentMarginX = BAR_INTERVALE;
853 switchObj->obj.alignTo = (nbgl_obj_t *) textArea;
854
855 container->children[container->nbChildren] = (nbgl_obj_t *) switchObj;
856 container->nbChildren++;
857 }
858
859 if (itemDesc->subText != NULL) {
860 nbgl_text_area_t *subTextArea
862
863 subTextArea->textColor = common_color;
864 subTextArea->text = PIC(itemDesc->subText);
865 subTextArea->textAlignment = MID_LEFT;
866 subTextArea->fontId = SMALL_REGULAR_FONT;
867 subTextArea->style = NO_STYLE;
868 subTextArea->wrapping = true;
869 if (itemDesc->text != NULL) {
870 subTextArea->obj.alignment = BOTTOM_LEFT;
871 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
872 subTextArea->obj.alignmentMarginY = LIST_ITEM_HEADING_SUB_TEXT;
873 }
874 else {
875 subTextArea->obj.alignment = TOP_LEFT;
876 subTextArea->obj.alignmentMarginY = SUB_HEADER_MARGIN;
877 container->obj.area.height = SUB_HEADER_MARGIN;
878 }
879 if (itemDesc->iconLeft != NULL) {
880 subTextArea->obj.alignmentMarginX
881 = -(((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE);
882 }
883 subTextArea->obj.area.width = container->obj.area.width;
884 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
885 subTextArea->text,
886 subTextArea->obj.area.width,
887 subTextArea->wrapping);
888 container->children[container->nbChildren] = (nbgl_obj_t *) subTextArea;
889 container->nbChildren++;
890 container->obj.area.height
891 += subTextArea->obj.area.height + subTextArea->obj.alignmentMarginY;
892 }
893 // set this new container as child of main container
894 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
895
896 return container;
897}
898
907static nbgl_container_t *addContentCenter(nbgl_layoutInternal_t *layoutInt,
908 const nbgl_contentCenter_t *info)
909{
910 nbgl_container_t *container;
911 nbgl_text_area_t *textArea = NULL;
912 nbgl_image_t *image = NULL;
913 uint16_t fullHeight = 0;
914
915 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
916
917 // get container children
918 container->nbChildren = 0;
919 // the max number is: icon + anim + title + small title + description + sub-text
920 container->children = nbgl_containerPoolGet(6, layoutInt->layer);
921
922 // add icon or animation if present
923 if (info->icon != NULL) {
924 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
925 image->foregroundColor = (layoutInt->invertedColors) ? WHITE : BLACK;
926 image->buffer = PIC(info->icon);
927 image->obj.alignment = TOP_MIDDLE;
928 image->obj.alignmentMarginY = info->iconHug;
929
930 fullHeight += image->buffer->height + info->iconHug;
931 container->children[container->nbChildren] = (nbgl_obj_t *) image;
932 container->nbChildren++;
933
934 if (info->illustrType == ANIM_ILLUSTRATION) {
935 nbgl_image_t *anim;
936 anim = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
937 anim->foregroundColor = (layoutInt->invertedColors) ? WHITE : BLACK;
938 anim->buffer = PIC(info->animation->icons[0]);
939 anim->obj.alignment = TOP_MIDDLE;
940 anim->obj.alignmentMarginY = info->iconHug + info->animOffsetY;
941 anim->obj.alignmentMarginX = info->animOffsetX;
942
943 container->children[container->nbChildren] = (nbgl_obj_t *) anim;
944 container->nbChildren++;
945
946 layoutInt->animation = info->animation;
947 layoutInt->incrementAnim = true;
948 layoutInt->iconIdxInAnim = 0;
949
950 // update ticker to update the animation periodically
951 nbgl_screenTickerConfiguration_t tickerCfg = {0};
952
953 tickerCfg.tickerIntervale = info->animation->delayMs; // ms
954 tickerCfg.tickerValue = info->animation->delayMs; // ms
955 tickerCfg.tickerCallback = &animTickerCallback;
956 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
957 }
958 }
959 // add title if present
960 if (info->title != NULL) {
961 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
962 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
963 textArea->text = PIC(info->title);
964 textArea->textAlignment = CENTER;
965 textArea->fontId = LARGE_MEDIUM_FONT;
966 textArea->wrapping = true;
967 textArea->obj.area.width = AVAILABLE_WIDTH;
968 textArea->obj.area.height = nbgl_getTextHeightInWidth(
969 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
970
971 // if not the first child, put on bottom of the previous, with a margin
972 if (container->nbChildren > 0) {
973 textArea->obj.alignment = BOTTOM_MIDDLE;
974 textArea->obj.alignTo = (nbgl_obj_t *) image;
975 textArea->obj.alignmentMarginY = ICON_TITLE_MARGIN + info->iconHug;
976 }
977 else {
978 textArea->obj.alignment = TOP_MIDDLE;
979 }
980
981 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
982
983 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
984 container->nbChildren++;
985 }
986 // add small title if present
987 if (info->smallTitle != NULL) {
988 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
989 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
990 textArea->text = PIC(info->smallTitle);
991 textArea->textAlignment = CENTER;
992 textArea->fontId = SMALL_BOLD_FONT;
993 textArea->wrapping = true;
994 textArea->obj.area.width = AVAILABLE_WIDTH;
995 textArea->obj.area.height = nbgl_getTextHeightInWidth(
996 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
997
998 // if not the first child, put on bottom of the previous, with a margin
999 if (container->nbChildren > 0) {
1000 textArea->obj.alignment = BOTTOM_MIDDLE;
1001 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1002 textArea->obj.alignmentMarginY = VERTICAL_BORDER_MARGIN;
1003 if (container->children[container->nbChildren - 1]->type == IMAGE) {
1004 textArea->obj.alignmentMarginY = VERTICAL_BORDER_MARGIN + info->iconHug;
1005 }
1006 else {
1007 textArea->obj.alignmentMarginY = TITLE_DESC_MARGIN;
1008 }
1009 }
1010 else {
1011 textArea->obj.alignment = TOP_MIDDLE;
1012 }
1013
1014 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1015
1016 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1017 container->nbChildren++;
1018 }
1019 // add description if present
1020 if (info->description != NULL) {
1021 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1022 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
1023 textArea->text = PIC(info->description);
1024 textArea->textAlignment = CENTER;
1025 textArea->fontId = SMALL_REGULAR_FONT;
1026 textArea->wrapping = true;
1027 textArea->obj.area.width = AVAILABLE_WIDTH;
1028 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1029 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1030
1031 // if not the first child, put on bottom of the previous, with a margin
1032 if (container->nbChildren > 0) {
1033 textArea->obj.alignment = BOTTOM_MIDDLE;
1034 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1035 if (container->children[container->nbChildren - 1]->type == TEXT_AREA) {
1036 // if previous element is text, only space of 16 px
1037 textArea->obj.alignmentMarginY = TITLE_DESC_MARGIN;
1038 }
1039 else {
1040 textArea->obj.alignmentMarginY = ICON_TITLE_MARGIN + info->iconHug;
1041 }
1042 }
1043 else {
1044 textArea->obj.alignment = TOP_MIDDLE;
1045 }
1046
1047 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1048
1049 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1050 container->nbChildren++;
1051 }
1052 // add sub-text if present
1053 if (info->subText != NULL) {
1054 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1055 textArea->textColor = LIGHT_TEXT_COLOR;
1056 textArea->text = PIC(info->subText);
1057 textArea->textAlignment = CENTER;
1058 textArea->fontId = SMALL_REGULAR_FONT;
1059 textArea->wrapping = true;
1060 textArea->obj.area.width = AVAILABLE_WIDTH;
1061 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1062 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1063 // sub-text is included in a hug of 8px
1064 textArea->obj.area.height += 2 * 8;
1065 // if not the first child, put on bottom of the previous, with a margin
1066 if (container->nbChildren > 0) {
1067 textArea->obj.alignment = BOTTOM_MIDDLE;
1068 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1069 textArea->obj.alignmentMarginY = 16;
1070 if (container->children[container->nbChildren - 1]->type == IMAGE) {
1071 textArea->obj.alignmentMarginY += info->iconHug;
1072 }
1073 }
1074 else {
1075 textArea->obj.alignment = TOP_MIDDLE;
1076 }
1077
1078 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1079
1080 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1081 container->nbChildren++;
1082 }
1083 container->layout = VERTICAL;
1084 container->obj.alignment = CENTER;
1085 container->obj.area.width = AVAILABLE_WIDTH;
1086 container->obj.area.height = fullHeight;
1087 if (info->padding) {
1088 container->obj.area.height += 40;
1089 }
1090
1091 // set this new container as child of main container
1092 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1093
1094 return container;
1095}
1096
1097/**********************
1098 * GLOBAL FUNCTIONS
1099 **********************/
1100
1108{
1109 nbgl_layoutInternal_t *layout = NULL;
1110
1111 if (description->modal) {
1112 int i;
1113 // find an empty layout in the array of layouts (0 is reserved for background)
1114 for (i = 1; i < NB_MAX_LAYOUTS; i++) {
1115 if (!gLayout[i].isUsed) {
1116 layout = &gLayout[i];
1117 }
1118 }
1119 }
1120 else {
1121 // always use layout 0 for background
1122 layout = &gLayout[0];
1123 if (topLayout == NULL) {
1124 topLayout = layout;
1125 }
1126 }
1127 if (layout == NULL) {
1128 LOG_WARN(LAYOUT_LOGGER, "nbgl_layoutGet(): impossible to get a layout!\n");
1129 return NULL;
1130 }
1131
1132 // reset globals
1133 nbgl_layoutInternal_t *backgroundTop = gLayout[0].top;
1134 memset(layout, 0, sizeof(nbgl_layoutInternal_t));
1135 // link layout to other ones
1136 if (description->modal) {
1137 if (topLayout != NULL) {
1138 // if topLayout already existing, push this new one on top of it
1139 topLayout->top = layout;
1140 layout->bottom = topLayout;
1141 }
1142 else {
1143 // otherwise put it on top of background layout
1144 layout->bottom = &gLayout[0];
1145 gLayout[0].top = layout;
1146 }
1147 topLayout = layout;
1148 }
1149 else {
1150 // restore potentially valid background top layer
1151 gLayout[0].top = backgroundTop;
1152 }
1153
1154 nbTouchableControls = 0;
1155
1156 layout->callback = (nbgl_layoutTouchCallback_t) PIC(description->onActionCallback);
1157 layout->modal = description->modal;
1158 layout->withLeftBorder = description->withLeftBorder;
1159 if (description->modal) {
1160 layout->layer = nbgl_screenPush(&layout->children,
1162 &description->ticker,
1163 (nbgl_touchCallback_t) touchCallback);
1164 }
1165 else {
1166 nbgl_screenSet(&layout->children,
1168 &description->ticker,
1169 (nbgl_touchCallback_t) touchCallback);
1170 layout->layer = 0;
1171 }
1173 layout->container->obj.area.width = SCREEN_WIDTH;
1174 layout->container->obj.area.height = SCREEN_HEIGHT;
1175 layout->container->layout = VERTICAL;
1176 layout->container->children = nbgl_containerPoolGet(NB_MAX_CONTAINER_CHILDREN, layout->layer);
1177 // by default, if no header, main container is aligned on top-left
1178 layout->container->obj.alignment = TOP_LEFT;
1179 // main container is always the second object, leaving space for header
1180 layout->children[MAIN_CONTAINER_INDEX] = (nbgl_obj_t *) layout->container;
1181 layout->isUsed = true;
1182
1183 // if a tap text is defined, make the container tapable and display this text in gray
1184 if (description->tapActionText != NULL) {
1185 layoutObj_t *obj;
1186 const char *tapActionText = PIC(description->tapActionText);
1187
1188 obj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
1189 layout->nbUsedCallbackObjs++;
1190 obj->obj = (nbgl_obj_t *) layout->container;
1191 obj->token = description->tapActionToken;
1192 obj->tuneId = description->tapTuneId;
1193 layout->container->obj.touchMask = (1 << TOUCHED);
1194 layout->container->obj.touchId = WHOLE_SCREEN_ID;
1195
1196 if (strlen(tapActionText) > 0) {
1197 nbgl_layoutUpFooter_t footerDesc = {0};
1198 footerDesc.type = UP_FOOTER_TEXT;
1199 footerDesc.text.text = tapActionText;
1200 footerDesc.text.token = description->tapActionToken;
1201 footerDesc.text.tuneId = description->tapTuneId;
1202 nbgl_layoutAddUpFooter((nbgl_layout_t *) layout, &footerDesc);
1203 }
1204 }
1205 return (nbgl_layout_t *) layout;
1206}
1207
1220 uint16_t swipesMask,
1221 const char *text,
1222 uint8_t token,
1223 tune_index_e tuneId)
1224{
1225 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1226
1227 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwipe():\n");
1228 if (layout == NULL) {
1229 return -1;
1230 }
1231
1232 if (text) {
1233 // create 'tap to continue' text area
1235 layoutInt->tapText->text = PIC(text);
1236 layoutInt->tapText->textColor = LIGHT_TEXT_COLOR;
1237 layoutInt->tapText->fontId = SMALL_REGULAR_FONT;
1238 layoutInt->tapText->obj.area.width = AVAILABLE_WIDTH;
1239 layoutInt->tapText->obj.area.height = nbgl_getFontLineHeight(layoutInt->tapText->fontId);
1240 layoutInt->tapText->textAlignment = CENTER;
1241 layoutInt->tapText->obj.alignmentMarginY = TAP_TO_CONTINUE_MARGIN;
1242 layoutInt->tapText->obj.alignment = BOTTOM_MIDDLE;
1243 }
1244 return addSwipeInternal(layoutInt, swipesMask, SWIPE_USAGE_CUSTOM, token, tuneId);
1245}
1246
1255{
1256 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1257
1258 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutInvertBackground():\n");
1259 if (layout == NULL) {
1260 return -1;
1261 }
1262 layoutInt->invertedColors = true;
1264
1265 // invert potential "Tap to continue"
1266 if ((layoutInt->upFooterContainer) && (layoutInt->upFooterContainer->nbChildren == 1)
1267 && (layoutInt->upFooterContainer->children[0]->type == TEXT_AREA)) {
1268 nbgl_text_area_t *textArea = (nbgl_text_area_t *) layoutInt->upFooterContainer->children[0];
1269 if (textArea->textColor == LIGHT_TEXT_COLOR) {
1270 textArea->textColor = INACTIVE_COLOR;
1271 }
1272 }
1273
1274 return 0;
1275}
1276
1287 const nbgl_icon_details_t *icon,
1288 uint8_t token,
1289 tune_index_e tuneId)
1290{
1291 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1292 layoutObj_t *obj;
1293 nbgl_button_t *button;
1294
1295 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTopRightButton():\n");
1296 if (layout == NULL) {
1297 return -1;
1298 }
1299 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
1300 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) button, token, tuneId);
1301 if (obj == NULL) {
1302 return -1;
1303 }
1304
1305 button->obj.area.width = BUTTON_WIDTH;
1306 button->obj.area.height = BUTTON_DIAMETER;
1307 button->radius = BUTTON_RADIUS;
1308 button->obj.alignmentMarginX = BORDER_MARGIN;
1309 button->obj.alignmentMarginY = BORDER_MARGIN;
1310 button->foregroundColor = BLACK;
1311 button->innerColor = WHITE;
1312 button->borderColor = LIGHT_GRAY;
1313 button->obj.touchMask = (1 << TOUCHED);
1314 button->obj.touchId = TOP_RIGHT_BUTTON_ID;
1315 button->icon = PIC(icon);
1316 button->obj.alignment = TOP_RIGHT;
1317
1318 // add to screen
1319 layoutInt->children[TOP_RIGHT_BUTTON_INDEX] = (nbgl_obj_t *) button;
1320
1321 return 0;
1322}
1323
1332{
1333 nbgl_layoutFooter_t footerDesc = {0};
1334 footerDesc.type = FOOTER_NAV;
1335 footerDesc.separationLine = info->withSeparationLine;
1336 footerDesc.navigation.activePage = info->activePage;
1337 footerDesc.navigation.nbPages = info->nbPages;
1338 footerDesc.navigation.withExitKey = info->withExitKey;
1339 footerDesc.navigation.withBackKey = info->withBackKey;
1340 footerDesc.navigation.token = info->token;
1341 footerDesc.navigation.tuneId = info->tuneId;
1342 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1343}
1344
1357 const nbgl_icon_details_t *icon,
1358 uint8_t token,
1359 bool separationLine,
1360 tune_index_e tuneId)
1361{
1362 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddBottomButton():\n");
1363 nbgl_layoutFooter_t footerDesc = {0};
1364 footerDesc.type = FOOTER_SIMPLE_BUTTON;
1365 footerDesc.separationLine = separationLine;
1366 footerDesc.button.icon = PIC(icon);
1367 footerDesc.button.token = token;
1368 footerDesc.button.tuneId = tuneId;
1369 footerDesc.button.style = WHITE_BACKGROUND;
1370 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1371}
1372
1381{
1382 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1383 nbgl_container_t *container;
1384 listItem_t itemDesc = {0};
1385
1386 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar():\n");
1387 if (layout == NULL) {
1388 return -1;
1389 }
1390 // main text is mandatory
1391 if (barLayout->text == NULL) {
1392 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar(): main text is mandatory\n");
1393 }
1394
1395 itemDesc.iconLeft = barLayout->iconLeft;
1396 itemDesc.iconRight = barLayout->iconRight;
1397 itemDesc.text = barLayout->text;
1398 itemDesc.subText = barLayout->subText;
1399 itemDesc.token = barLayout->token;
1400 itemDesc.tuneId = barLayout->tuneId;
1401 itemDesc.state = (barLayout->inactive) ? OFF_STATE : ON_STATE;
1402 itemDesc.large = barLayout->large;
1403 itemDesc.type = TOUCHABLE_BAR_ITEM;
1404 container = addListItem(layoutInt, &itemDesc);
1405
1406 if (container == NULL) {
1407 return -1;
1408 }
1409 return container->obj.area.height;
1410}
1411
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_layoutAddSwitch():\n");
1426 if (layout == NULL) {
1427 return -1;
1428 }
1429 // main text is mandatory
1430 if (switchLayout->text == NULL) {
1431 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddSwitch(): main text is mandatory\n");
1432 }
1433
1434 itemDesc.text = switchLayout->text;
1435 itemDesc.subText = switchLayout->subText;
1436 itemDesc.token = switchLayout->token;
1437 itemDesc.tuneId = switchLayout->tuneId;
1438 itemDesc.state = switchLayout->initState;
1439 itemDesc.type = SWITCH_ITEM;
1440 container = addListItem(layoutInt, &itemDesc);
1441
1442 if (container == NULL) {
1443 return -1;
1444 }
1445 return container->obj.area.height;
1446}
1447
1456int nbgl_layoutAddText(nbgl_layout_t *layout, const char *text, const char *subText)
1457{
1458 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1459 nbgl_container_t *container;
1460 listItem_t itemDesc = {0};
1461
1462 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddText():\n");
1463 if (layout == NULL) {
1464 return -1;
1465 }
1466
1467 itemDesc.text = text;
1468 itemDesc.subText = subText;
1469 itemDesc.token = NBGL_INVALID_TOKEN;
1470 itemDesc.tuneId = NBGL_NO_TUNE;
1471 itemDesc.type = TEXT_ITEM;
1472 container = addListItem(layoutInt, &itemDesc);
1473
1474 if (container == NULL) {
1475 return -1;
1476 }
1477 return container->obj.area.height;
1478}
1479
1492 const char *text,
1493 const char *subText,
1494 uint8_t token,
1495 uint8_t index)
1496{
1497 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1498 nbgl_container_t *container;
1499 listItem_t itemDesc = {0};
1500 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextWithAlias():\n");
1501 if (layout == NULL) {
1502 return -1;
1503 }
1504
1505 itemDesc.text = text;
1506 itemDesc.subText = subText;
1507 itemDesc.iconRight = &MINI_PUSH_ICON;
1508 itemDesc.token = token;
1509 itemDesc.tuneId = NBGL_NO_TUNE;
1510 itemDesc.type = TOUCHABLE_BAR_ITEM;
1511 itemDesc.index = index;
1512 itemDesc.state = ON_STATE;
1513 container = addListItem(layoutInt, &itemDesc);
1514
1515 if (container == NULL) {
1516 return -1;
1517 }
1518 return container->obj.area.height;
1519}
1520
1529int nbgl_layoutAddLargeCaseText(nbgl_layout_t *layout, const char *text, bool grayedOut)
1530{
1531 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1532 nbgl_text_area_t *textArea;
1533
1534 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLargeCaseText():\n");
1535 if (layout == NULL) {
1536 return -1;
1537 }
1538 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1539
1540 textArea->textColor = grayedOut ? INACTIVE_TEXT_COLOR : BLACK;
1541 textArea->text = PIC(text);
1542 textArea->textAlignment = MID_LEFT;
1543 textArea->fontId = LARGE_MEDIUM_FONT;
1544 textArea->obj.area.width = AVAILABLE_WIDTH;
1545 textArea->wrapping = true;
1546 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1547 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1548 textArea->style = NO_STYLE;
1549 textArea->obj.alignment = NO_ALIGNMENT;
1550 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1551 if (layoutInt->container->nbChildren == 0) {
1552 // if first object of container, increase the margin from top
1553 textArea->obj.alignmentMarginY += PRE_FIRST_TEXT_MARGIN;
1554 }
1555 else {
1556 textArea->obj.alignmentMarginY = INTER_PARAGRAPHS_MARGIN; // between paragraphs
1557 }
1558
1559 // set this new obj as child of main container
1560 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1561
1562 return 0;
1563}
1564
1576{
1577 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1578 nbgl_text_area_t *textArea;
1579 uint8_t i;
1580
1581 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextContent():\n");
1582 if (layout == NULL) {
1583 return -1;
1584 }
1585
1586 // create title
1587 if (content->title != NULL) {
1588 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1589 textArea->textColor = BLACK;
1590 textArea->text = PIC(content->title);
1591 textArea->textAlignment = MID_LEFT;
1592 textArea->fontId = LARGE_MEDIUM_FONT;
1593 textArea->style = NO_STYLE;
1594 textArea->wrapping = true;
1595 textArea->obj.alignment = NO_ALIGNMENT;
1596 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1597 textArea->obj.alignmentMarginY = PRE_TITLE_MARGIN;
1598 textArea->obj.area.width = AVAILABLE_WIDTH;
1599 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1600 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1601 // set this new obj as child of main container
1602 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1603 }
1604
1605 // create descriptions
1606 for (i = 0; i < content->nbDescriptions; i++) {
1607 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1608 textArea->textColor = BLACK;
1609 textArea->text = PIC(content->descriptions[i]);
1610 textArea->fontId = SMALL_REGULAR_FONT;
1611 textArea->style = NO_STYLE;
1612 textArea->wrapping = true;
1613 textArea->obj.area.width = AVAILABLE_WIDTH;
1614 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1615 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1616 textArea->textAlignment = MID_LEFT;
1617 textArea->obj.alignment = NO_ALIGNMENT;
1618 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1619 textArea->obj.alignmentMarginY
1620 = (i == 0) ? PRE_DESCRIPTION_MARGIN : INTER_DESCRIPTIONS_MARGIN;
1621 // set this new obj as child of main container
1622 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1623 }
1624
1625 // create info on the bottom
1626 if (content->info != NULL) {
1627 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1628 textArea->textColor = LIGHT_TEXT_COLOR;
1629 textArea->text = PIC(content->info);
1630 textArea->fontId = SMALL_REGULAR_FONT;
1631 textArea->style = NO_STYLE;
1632 textArea->wrapping = true;
1633 textArea->obj.area.width = AVAILABLE_WIDTH;
1634 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1635 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1636 textArea->textAlignment = MID_LEFT;
1637 textArea->obj.alignment = BOTTOM_LEFT;
1638 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1639 textArea->obj.alignmentMarginY = 40;
1640 // set this new obj as child of main container
1641 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1642 }
1643
1644 return layoutInt->container->obj.area.height;
1645}
1646
1655{
1656 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1657 layoutObj_t *obj;
1658 uint8_t i;
1659
1660 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddRadioChoice():\n");
1661 if (layout == NULL) {
1662 return -1;
1663 }
1664 for (i = 0; i < choices->nbChoices; i++) {
1665 nbgl_container_t *container;
1666 nbgl_text_area_t *textArea;
1667 nbgl_radio_t *button;
1668 nbgl_line_t *line;
1669
1670 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1671 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1672 button = (nbgl_radio_t *) nbgl_objPoolGet(RADIO_BUTTON, layoutInt->layer);
1673
1675 layoutInt, (nbgl_obj_t *) container, choices->token, choices->tuneId);
1676 if (obj == NULL) {
1677 return -1;
1678 }
1679
1680 // get container children (max 2)
1681 container->nbChildren = 2;
1682 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1683 container->obj.area.width = AVAILABLE_WIDTH;
1684 container->obj.area.height = RADIO_CHOICE_HEIGHT;
1685 container->obj.alignment = NO_ALIGNMENT;
1686 container->obj.alignmentMarginX = BORDER_MARGIN;
1687 container->obj.alignTo = (nbgl_obj_t *) NULL;
1688
1689 // init button for this choice
1690 button->activeColor = BLACK;
1691 button->borderColor = LIGHT_GRAY;
1692 button->obj.alignTo = (nbgl_obj_t *) container;
1693 button->obj.alignment = MID_RIGHT;
1694 button->state = OFF_STATE;
1695 container->children[1] = (nbgl_obj_t *) button;
1696
1697 // init text area for this choice
1698 if (choices->localized == true) {
1699#ifdef HAVE_LANGUAGE_PACK
1700 textArea->text = get_ux_loc_string(choices->nameIds[i]);
1701#endif // HAVE_LANGUAGE_PACK
1702 }
1703 else {
1704 textArea->text = PIC(choices->names[i]);
1705 }
1706 textArea->textAlignment = MID_LEFT;
1707 textArea->obj.area.width = container->obj.area.width - RADIO_WIDTH;
1708 textArea->style = NO_STYLE;
1709 textArea->obj.alignment = MID_LEFT;
1710 textArea->obj.alignTo = (nbgl_obj_t *) container;
1711 container->children[0] = (nbgl_obj_t *) textArea;
1712
1713 // whole container should be touchable
1714 container->obj.touchMask = (1 << TOUCHED);
1715 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
1716 nbTouchableControls++;
1717
1718 // highlight init choice
1719 if (i == choices->initChoice) {
1720 button->state = ON_STATE;
1721 textArea->textColor = BLACK;
1722 textArea->fontId = SMALL_BOLD_FONT;
1723 }
1724 else {
1725 button->state = OFF_STATE;
1726 textArea->textColor = LIGHT_TEXT_COLOR;
1727 textArea->fontId = SMALL_REGULAR_FONT;
1728 }
1729 textArea->obj.area.height = nbgl_getFontHeight(textArea->fontId);
1730
1731 line = createHorizontalLine(layoutInt->layer);
1732 line->obj.alignmentMarginY = -1;
1733
1734 // set these new objs as child of main container
1735 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1736 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
1737 }
1738
1739 return 0;
1740}
1741
1751{
1752 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1753 nbgl_container_t *container;
1754 nbgl_contentCenter_t centeredInfo = {0};
1755
1756 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddCenteredInfo():\n");
1757 if (layout == NULL) {
1758 return -1;
1759 }
1760
1761 centeredInfo.icon = info->icon;
1762 centeredInfo.illustrType = ICON_ILLUSTRATION;
1763
1764 if (info->text1 != NULL) {
1765 if (info->style != NORMAL_INFO) {
1766 centeredInfo.title = info->text1;
1767 }
1768 else {
1769 centeredInfo.smallTitle = info->text1;
1770 }
1771 }
1772 if (info->text2 != NULL) {
1773 if (info->style != LARGE_CASE_BOLD_INFO) {
1774 centeredInfo.description = info->text2;
1775 }
1776 else {
1777 centeredInfo.smallTitle = info->text2;
1778 }
1779 }
1780 if (info->text3 != NULL) {
1781 if (info->style == LARGE_CASE_GRAY_INFO) {
1782 centeredInfo.subText = info->text3;
1783 }
1784 else {
1785 centeredInfo.description = info->text3;
1786 }
1787 }
1788 container = addContentCenter(layoutInt, &centeredInfo);
1789
1790 if (info->onTop) {
1791 container->obj.alignmentMarginX = BORDER_MARGIN;
1792 container->obj.alignmentMarginY = BORDER_MARGIN + info->offsetY;
1793 container->obj.alignment = NO_ALIGNMENT;
1794 }
1795 else {
1796 container->obj.alignmentMarginY = info->offsetY;
1797 }
1798
1799 return container->obj.area.height;
1800}
1801
1811{
1812 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1813 nbgl_container_t *container;
1814
1815 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddContentCenter():\n");
1816 if (layout == NULL) {
1817 return -1;
1818 }
1819
1820 container = addContentCenter(layoutInt, info);
1821
1822 return container->obj.area.height;
1823}
1824
1833{
1834 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1835 nbgl_container_t *container;
1836 nbgl_text_area_t *textArea;
1837 uint8_t row;
1838
1839 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLeftContent():\n");
1840 if (layout == NULL) {
1841 return -1;
1842 }
1843 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1844
1845 // get container children
1846 container->nbChildren = info->nbRows + 1;
1847 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1848 container->layout = VERTICAL;
1849 container->obj.area.width = AVAILABLE_WIDTH;
1850 container->obj.alignmentMarginX = BORDER_MARGIN;
1851
1852 // create title
1853 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1854 textArea->textColor = BLACK;
1855 textArea->text = PIC(info->title);
1856 textArea->textAlignment = MID_LEFT;
1857 textArea->fontId = LARGE_MEDIUM_FONT;
1858 textArea->wrapping = true;
1859#ifdef TARGET_STAX
1860 textArea->obj.alignmentMarginY = 24;
1861#endif
1862 textArea->obj.area.width = AVAILABLE_WIDTH;
1863 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1864 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1865
1866 container->obj.area.height += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1867
1868 container->children[0] = (nbgl_obj_t *) textArea;
1869
1870 for (row = 0; row < info->nbRows; row++) {
1871 nbgl_container_t *rowContainer;
1872 nbgl_image_t *image;
1873
1874 // create a grid with 1 icon on the left column and 1 text on the right one
1875 rowContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, 0);
1876 rowContainer->nbChildren = 2; // 1 icon + 1 text
1877 rowContainer->children = nbgl_containerPoolGet(rowContainer->nbChildren, 0);
1878 rowContainer->obj.area.width = AVAILABLE_WIDTH;
1879
1880 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, 0);
1881 image->foregroundColor = BLACK;
1882 image->buffer = PIC(info->rowIcons[row]);
1883 rowContainer->children[0] = (nbgl_obj_t *) image;
1884
1885 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, 0);
1886 textArea->textColor = BLACK;
1887 textArea->text = PIC(info->rowTexts[row]);
1888 textArea->textAlignment = MID_LEFT;
1889 textArea->fontId = SMALL_REGULAR_FONT;
1890 textArea->wrapping = true;
1891 textArea->obj.area.width
1892 = AVAILABLE_WIDTH - image->buffer->width - LEFT_CONTENT_ICON_TEXT_X;
1893 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1894 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1895 textArea->obj.alignment = MID_RIGHT;
1896 rowContainer->children[1] = (nbgl_obj_t *) textArea;
1897 rowContainer->obj.area.height
1898 = MAX(image->buffer->height, textArea->obj.area.height + LEFT_CONTENT_TEXT_PADDING);
1899
1900 if (row == 0) {
1901 rowContainer->obj.alignmentMarginY = PRE_FIRST_ROW_MARGIN;
1902 }
1903 else {
1904 rowContainer->obj.alignmentMarginY = INTER_ROWS_MARGIN;
1905 }
1906 container->children[1 + row] = (nbgl_obj_t *) rowContainer;
1907 container->obj.area.height
1908 += rowContainer->obj.area.height + rowContainer->obj.alignmentMarginY;
1909 }
1910 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1911
1912 return container->obj.area.height;
1913}
1914
1915#ifdef NBGL_QRCODE
1925{
1926 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1927 nbgl_container_t *container;
1928 nbgl_text_area_t *textArea = NULL;
1929 nbgl_qrcode_t *qrcode;
1930 uint16_t fullHeight = 0;
1931
1932 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddQRCode():\n");
1933 if (layout == NULL) {
1934 return -1;
1935 }
1936
1937 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1938
1939 // get container children (max 2 (QRCode + text1 + text2))
1940 container->children = nbgl_containerPoolGet(3, layoutInt->layer);
1941 container->nbChildren = 0;
1942
1943 qrcode = (nbgl_qrcode_t *) nbgl_objPoolGet(QR_CODE, layoutInt->layer);
1944 // version is forced to V10 if url is longer than 62 characters
1945 if (strlen(PIC(info->url)) > 62) {
1946 qrcode->version = QRCODE_V10;
1947 }
1948 else {
1949 qrcode->version = QRCODE_V4;
1950 }
1951 qrcode->foregroundColor = BLACK;
1952 // in QR V4, we use:
1953 // - 8*8 screen pixels for one QR pixel on Stax/Flex
1954 // - 5*5 screen pixels for one QR pixel on Apex
1955 // in QR V10, we use 4*4 screen pixels for one QR pixel
1956#ifndef TARGET_APEX
1957 qrcode->obj.area.width
1958 = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 8) : (QR_V10_NB_PIX_SIZE * 4);
1959#else // TARGET_APEX
1960 qrcode->obj.area.width
1961 = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 5) : (QR_V10_NB_PIX_SIZE * 4);
1962#endif // TARGET_APEX
1963 qrcode->obj.area.height = qrcode->obj.area.width;
1964 qrcode->text = PIC(info->url);
1965 qrcode->obj.area.bpp = NBGL_BPP_1;
1966 qrcode->obj.alignment = TOP_MIDDLE;
1967
1968 fullHeight += qrcode->obj.area.height;
1969 container->children[container->nbChildren] = (nbgl_obj_t *) qrcode;
1970 container->nbChildren++;
1971
1972 if (info->text1 != NULL) {
1973 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1974 textArea->textColor = BLACK;
1975 textArea->text = PIC(info->text1);
1976 textArea->textAlignment = CENTER;
1977 textArea->fontId = (info->largeText1 == true) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
1978 textArea->wrapping = true;
1979 textArea->obj.area.width = AVAILABLE_WIDTH;
1980 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1981 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1982 textArea->obj.alignment = BOTTOM_MIDDLE;
1983 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1984 textArea->obj.alignmentMarginY = QR_PRE_TEXT_MARGIN;
1985
1986 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1987
1988 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1989 container->nbChildren++;
1990 }
1991 if (info->text2 != NULL) {
1992 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1993 textArea->textColor = LIGHT_TEXT_COLOR;
1994 textArea->text = PIC(info->text2);
1995 textArea->textAlignment = CENTER;
1996 textArea->fontId = SMALL_REGULAR_FONT;
1997 textArea->wrapping = true;
1998 textArea->obj.area.width = AVAILABLE_WIDTH;
1999 textArea->obj.area.height = nbgl_getTextHeightInWidth(
2000 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
2001 textArea->obj.alignment = BOTTOM_MIDDLE;
2002 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
2003 if (info->text1 != NULL) {
2004 textArea->obj.alignmentMarginY = QR_INTER_TEXTS_MARGIN;
2005 }
2006 else {
2007 textArea->obj.alignmentMarginY = QR_PRE_TEXT_MARGIN + 8;
2008 }
2009
2010 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY + 8;
2011
2012 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
2013 container->nbChildren++;
2014 }
2015 // ensure that fullHeight is fitting in main container height (with 16 px margin)
2016 if ((fullHeight >= (layoutInt->container->obj.area.height - 16))
2017 && (qrcode->version == QRCODE_V4)) {
2018 qrcode->version = QRCODE_V4_SMALL;
2019 // in QR V4 small, we use 4*4 screen pixels for one QR pixel
2020 qrcode->obj.area.width = QR_V4_NB_PIX_SIZE * 4;
2021 qrcode->obj.area.height = qrcode->obj.area.width;
2022 fullHeight -= QR_V4_NB_PIX_SIZE * 4;
2023 }
2024 container->obj.area.height = fullHeight;
2025 container->layout = VERTICAL;
2026 if (info->centered) {
2027 container->obj.alignment = CENTER;
2028 }
2029 else {
2030 container->obj.alignment = BOTTOM_MIDDLE;
2031 container->obj.alignTo
2032 = layoutInt->container->children[layoutInt->container->nbChildren - 1];
2033 }
2034 container->obj.alignmentMarginY = info->offsetY;
2035
2036 container->obj.area.width = AVAILABLE_WIDTH;
2037
2038 // set this new container as child of main container
2039 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2040
2041 return fullHeight;
2042}
2043#endif // NBGL_QRCODE
2044
2054{
2055 nbgl_layoutFooter_t footerDesc = {0};
2056 footerDesc.type = FOOTER_CHOICE_BUTTONS;
2057 footerDesc.choiceButtons.bottomText = info->bottomText;
2058 footerDesc.choiceButtons.token = info->token;
2059 footerDesc.choiceButtons.topText = info->topText;
2060 footerDesc.choiceButtons.topIcon = info->topIcon;
2061 footerDesc.choiceButtons.style = info->style;
2062 footerDesc.choiceButtons.tuneId = info->tuneId;
2063 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2064}
2065
2077{
2079 .horizontalButtons.leftIcon = info->leftIcon,
2080 .horizontalButtons.leftToken = info->leftToken,
2081 .horizontalButtons.rightText = info->rightText,
2082 .horizontalButtons.rightToken = info->rightToken,
2083 .horizontalButtons.tuneId = info->tuneId};
2084
2085 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHorizontalButtons():\n");
2086 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2087}
2088
2097{
2098 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2099 nbgl_container_t *container;
2100 uint8_t i;
2101
2102 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTagValueList():\n");
2103 if (layout == NULL) {
2104 return -1;
2105 }
2106
2107 for (i = 0; i < list->nbPairs; i++) {
2108 const nbgl_layoutTagValue_t *pair;
2109 uint16_t fullHeight = 0;
2110 const nbgl_icon_details_t *valueIcon = NULL;
2111 nbgl_text_area_t *itemTextArea;
2112 nbgl_text_area_t *valueTextArea;
2113 uint8_t nbChildren = 2;
2114
2115 // Retrieve pair
2116 if (list->pairs != NULL) {
2117 pair = &list->pairs[i];
2118 }
2119 else {
2120 pair = list->callback(list->startIndex + i);
2121 }
2122
2123 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2124
2125 // tune container number of children (max 4 if a valueIcon and aliasSubName)
2126 if (pair->valueIcon != NULL) {
2127 nbChildren++;
2128 // if it's a alias with a subName, add a child
2129 if ((pair->aliasValue) && (pair->extension->aliasSubName)) {
2130 nbChildren++;
2131 }
2132 }
2133 // get container children
2134 container->children = nbgl_containerPoolGet(nbChildren, layoutInt->layer);
2135
2136 itemTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2137 valueTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2138
2139 // init text area for this choice
2140 itemTextArea->textColor = LIGHT_TEXT_COLOR;
2141 itemTextArea->text = PIC(pair->item);
2142 itemTextArea->textAlignment = MID_LEFT;
2143 itemTextArea->fontId = SMALL_REGULAR_FONT;
2144 itemTextArea->wrapping = true;
2145 itemTextArea->obj.area.width = AVAILABLE_WIDTH;
2146 itemTextArea->obj.area.height = nbgl_getTextHeightInWidth(
2147 itemTextArea->fontId, itemTextArea->text, AVAILABLE_WIDTH, itemTextArea->wrapping);
2148 container->children[container->nbChildren] = (nbgl_obj_t *) itemTextArea;
2149 container->nbChildren++;
2150
2151 fullHeight += itemTextArea->obj.area.height;
2152
2153 // init button for this choice
2154 valueTextArea->textColor = BLACK;
2155 valueTextArea->text = PIC(pair->value);
2156 valueTextArea->textAlignment = MID_LEFT;
2157 if (list->smallCaseForValue) {
2158 valueTextArea->fontId = SMALL_BOLD_FONT;
2159 }
2160 else {
2161 valueTextArea->fontId = LARGE_MEDIUM_FONT;
2162 }
2163 if ((pair->aliasValue == 0) && (pair->valueIcon == NULL)) {
2164 valueTextArea->obj.area.width = AVAILABLE_WIDTH;
2165 }
2166 else {
2167 if (pair->aliasValue) {
2168 // if the value is an alias, we automatically display a (>) icon
2169 valueIcon = &MINI_PUSH_ICON;
2170 }
2171 else {
2172 // otherwise use the provided icon
2173 valueIcon = PIC(pair->valueIcon);
2174 }
2175 // decrease the available width for value text
2176 valueTextArea->obj.area.width
2177 = AVAILABLE_WIDTH - valueIcon->width - VALUE_ICON_INTERVALE;
2178 }
2179
2180 // handle the nbMaxLinesForValue parameter, used to automatically keep only
2181 // nbMaxLinesForValue lines
2182 uint16_t nbLines = nbgl_getTextNbLinesInWidth(valueTextArea->fontId,
2183 valueTextArea->text,
2184 valueTextArea->obj.area.width,
2185 list->wrapping);
2186 // use this nbMaxLinesForValue parameter only if >0
2187 if ((list->nbMaxLinesForValue > 0) && (nbLines > list->nbMaxLinesForValue)) {
2188 nbLines = list->nbMaxLinesForValue;
2189 valueTextArea->nbMaxLines = list->nbMaxLinesForValue;
2190 valueTextArea->hideEndOfLastLine = list->hideEndOfLastLine;
2191 }
2192 // alias values (named addresses): always cap at 2 lines with ellipsis
2193 if (pair->aliasValue && (nbLines > 2)) {
2194 nbLines = 2;
2195 valueTextArea->nbMaxLines = 2;
2196 valueTextArea->hideEndOfLastLine = true;
2197 }
2198 const nbgl_font_t *font = nbgl_getFont(valueTextArea->fontId);
2199 valueTextArea->obj.area.height = nbLines * font->line_height;
2200 valueTextArea->obj.alignment = BOTTOM_LEFT;
2201 valueTextArea->obj.alignmentMarginY = TAG_VALUE_INTERVALE;
2202 valueTextArea->obj.alignTo = (nbgl_obj_t *) itemTextArea;
2203 valueTextArea->wrapping = list->wrapping;
2204 container->children[container->nbChildren] = (nbgl_obj_t *) valueTextArea;
2205 container->nbChildren++;
2206
2207 fullHeight += valueTextArea->obj.area.height + valueTextArea->obj.alignmentMarginY;
2208 if (valueIcon != NULL) {
2209 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2210 // set the container as touchable, not only the image
2212 layoutInt, (nbgl_obj_t *) container, list->token, TUNE_TAP_CASUAL);
2213 obj->index = i;
2214 image->foregroundColor = BLACK;
2215 image->buffer = valueIcon;
2216 image->obj.alignment = RIGHT_TOP;
2217 image->obj.alignmentMarginX = VALUE_ICON_INTERVALE;
2218 image->obj.alignTo = (nbgl_obj_t *) valueTextArea;
2219 // set the container as touchable, not only the image
2220 container->obj.touchMask = (1 << TOUCHED);
2221 container->obj.touchId = VALUE_BUTTON_1_ID + i;
2222
2223 container->children[container->nbChildren] = (nbgl_obj_t *) image;
2224 container->nbChildren++;
2225
2226 // if an aliasSubName is provided, display it under value
2227 if ((pair->aliasValue) && (pair->extension->aliasSubName)) {
2228 nbgl_text_area_t *textArea
2230 textArea->textColor = BLACK;
2231 textArea->text = PIC(pair->extension->aliasSubName);
2232 textArea->textAlignment = MID_LEFT;
2233 textArea->nbMaxLines = 1;
2234 textArea->fontId = SMALL_REGULAR_FONT;
2235 textArea->obj.area.height = nbgl_getFontLineHeight(textArea->fontId);
2236 textArea->obj.area.width = valueTextArea->obj.area.width;
2237 textArea->obj.alignment = BOTTOM_LEFT;
2238 textArea->obj.alignmentMarginY = TAG_VALUE_INTERVALE;
2239 textArea->obj.alignTo = (nbgl_obj_t *) valueTextArea;
2240 textArea->wrapping = list->wrapping;
2241 textArea->hideEndOfLastLine
2242 = (nbgl_getSingleLineTextWidth(textArea->fontId, textArea->text)
2243 > textArea->obj.area.width);
2244 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
2245 container->nbChildren++;
2246 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
2247 }
2248 }
2249
2250 container->obj.area.width = AVAILABLE_WIDTH;
2251 container->obj.area.height = fullHeight;
2252 container->layout = VERTICAL;
2253 container->obj.alignmentMarginX = BORDER_MARGIN;
2254 if (i > 0) {
2255 container->obj.alignmentMarginY = INTER_TAG_VALUE_MARGIN;
2256 }
2257 else {
2258 // if there is a header with a separation line, add a margin
2259 if (layoutInt->headerContainer && (layoutInt->headerContainer->nbChildren > 0)
2260 && (layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren - 1]
2261 ->type
2262 == LINE)) {
2263 container->obj.alignmentMarginY = INTER_TAG_VALUE_MARGIN;
2264 }
2265 else {
2266 container->obj.alignmentMarginY = PRE_TAG_VALUE_MARGIN;
2267 }
2268 }
2269 container->obj.alignment = NO_ALIGNMENT;
2270
2271 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2272 }
2273
2274 return 0;
2275}
2276
2290 const char *text,
2291 const char *subText,
2292 uint8_t percentage)
2293{
2294 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2295 nbgl_container_t *container;
2296 nbgl_text_area_t *textArea;
2297 nbgl_progress_bar_t *progress;
2298
2299 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressBar():\n");
2300 if (layout == NULL) {
2301 return -1;
2302 }
2303
2304 // First Create Container :
2305 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2306 // progressbar + text + subText
2307 container->nbChildren = (subText != NULL) ? 3 : 2;
2308 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
2309
2310 container->obj.area.width = AVAILABLE_WIDTH;
2311 container->layout = VERTICAL;
2312 container->obj.alignment = CENTER;
2313
2314 // Create progressbar
2315 progress = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
2316 progress->foregroundColor = BLACK;
2317 progress->withBorder = true;
2318 progress->state = percentage;
2319 progress->obj.area.width = PROGRESSBAR_WIDTH;
2320 progress->obj.area.height = PROGRESSBAR_HEIGHT;
2321 progress->obj.alignment = TOP_MIDDLE;
2322
2323 // set this new progressbar as child of the container
2324 container->children[0] = (nbgl_obj_t *) progress;
2325
2326 // update container height
2327 container->obj.area.height = progress->obj.alignmentMarginY + progress->obj.area.height;
2328
2329 // create text area
2330 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2331 textArea->textColor = BLACK;
2332 textArea->text = PIC(text);
2333 textArea->textAlignment = CENTER;
2334 textArea->fontId = LARGE_MEDIUM_FONT;
2335 textArea->wrapping = true;
2336 textArea->obj.alignmentMarginY = BAR_TEXT_MARGIN;
2337 textArea->obj.alignTo = (nbgl_obj_t *) progress;
2338 textArea->obj.alignment = BOTTOM_MIDDLE;
2339 textArea->obj.area.width = AVAILABLE_WIDTH;
2340 textArea->obj.area.height = nbgl_getTextHeightInWidth(
2341 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
2342 textArea->style = NO_STYLE;
2343
2344 // update container height
2345 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
2346
2347 // set this text as child of the container
2348 container->children[1] = (nbgl_obj_t *) textArea;
2349
2350 if (subText != NULL) {
2351 nbgl_text_area_t *subTextArea;
2352 // create sub-text area
2353 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2354 subTextArea->textColor = BLACK;
2355 subTextArea->text = PIC(subText);
2356 subTextArea->textAlignment = CENTER;
2357 subTextArea->fontId = SMALL_REGULAR_FONT;
2358 subTextArea->wrapping = true;
2359 subTextArea->obj.alignmentMarginY = BAR_INTER_TEXTS_MARGIN;
2360 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
2361 subTextArea->obj.alignment = BOTTOM_MIDDLE;
2362 subTextArea->obj.area.width = AVAILABLE_WIDTH;
2363 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
2364 subTextArea->text,
2365 subTextArea->obj.area.width,
2366 subTextArea->wrapping);
2367 subTextArea->style = NO_STYLE;
2368
2369 // update container height
2370 container->obj.area.height
2371 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
2372
2373 // set thissub-text as child of the container
2374 container->children[2] = (nbgl_obj_t *) subTextArea;
2375 }
2376 // The height of containers must be a multiple of eight
2377 container->obj.area.height = (container->obj.area.height + 7) & 0xFFF8;
2378
2379 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2380
2381 return 2;
2382}
2383
2391{
2392 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2393 nbgl_line_t *line;
2394
2395 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSeparationLine():\n");
2396 line = createHorizontalLine(layoutInt->layer);
2397 line->obj.alignmentMarginY = -1;
2398 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
2399 return 0;
2400}
2401
2410{
2411 layoutObj_t *obj;
2412 nbgl_button_t *button;
2413 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2414
2415 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddButton():\n");
2416 if (layout == NULL) {
2417 return -1;
2418 }
2419
2420 // Add in footer if matching
2421 if ((buttonInfo->onBottom) && (!buttonInfo->fittingContent)) {
2422 if (layoutInt->footerContainer == NULL) {
2423 nbgl_layoutFooter_t footerDesc = {0};
2424 footerDesc.type = FOOTER_SIMPLE_BUTTON;
2425 footerDesc.button.text = buttonInfo->text;
2426 footerDesc.button.token = buttonInfo->token;
2427 footerDesc.button.tuneId = buttonInfo->tuneId;
2428 footerDesc.button.icon = buttonInfo->icon;
2429 footerDesc.button.style = buttonInfo->style;
2430 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2431 }
2432 else {
2433 nbgl_layoutUpFooter_t upFooterDesc = {0};
2434 upFooterDesc.type = UP_FOOTER_BUTTON;
2435 upFooterDesc.button.text = buttonInfo->text;
2436 upFooterDesc.button.token = buttonInfo->token;
2437 upFooterDesc.button.tuneId = buttonInfo->tuneId;
2438 upFooterDesc.button.icon = buttonInfo->icon;
2439 upFooterDesc.button.style = buttonInfo->style;
2440 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2441 }
2442 }
2443
2444 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2446 layoutInt, (nbgl_obj_t *) button, buttonInfo->token, buttonInfo->tuneId);
2447 if (obj == NULL) {
2448 return -1;
2449 }
2450
2451 button->obj.alignmentMarginX = BORDER_MARGIN;
2452 button->obj.alignmentMarginY = 12;
2453 button->obj.alignment = NO_ALIGNMENT;
2454 if (buttonInfo->style == BLACK_BACKGROUND) {
2455 button->innerColor = BLACK;
2456 button->foregroundColor = WHITE;
2457 }
2458 else {
2459 button->innerColor = WHITE;
2460 button->foregroundColor = BLACK;
2461 }
2462 if (buttonInfo->style == NO_BORDER) {
2463 button->borderColor = WHITE;
2464 }
2465 else {
2466 if (buttonInfo->style == BLACK_BACKGROUND) {
2467 button->borderColor = BLACK;
2468 }
2469 else {
2470 button->borderColor = LIGHT_GRAY;
2471 }
2472 }
2473 button->text = PIC(buttonInfo->text);
2474 button->fontId = SMALL_BOLD_FONT;
2475 button->icon = PIC(buttonInfo->icon);
2476 if (buttonInfo->fittingContent == true) {
2477 button->obj.area.width = nbgl_getTextWidth(button->fontId, button->text)
2478 + SMALL_BUTTON_HEIGHT
2479 + ((button->icon) ? (button->icon->width + 12) : 0);
2480 button->obj.area.height = SMALL_BUTTON_HEIGHT;
2481 button->radius = SMALL_BUTTON_RADIUS_INDEX;
2482 if (buttonInfo->onBottom != true) {
2483 button->obj.alignmentMarginX += (AVAILABLE_WIDTH - button->obj.area.width) / 2;
2484 }
2485 }
2486 else {
2487 button->obj.area.width = AVAILABLE_WIDTH;
2488 button->obj.area.height = BUTTON_DIAMETER;
2489 button->radius = BUTTON_RADIUS;
2490 }
2491 button->obj.alignTo = NULL;
2492 button->obj.touchMask = (1 << TOUCHED);
2493 button->obj.touchId = (buttonInfo->fittingContent) ? EXTRA_BUTTON_ID : SINGLE_BUTTON_ID;
2494 // set this new button as child of the container
2495 layoutAddObject(layoutInt, (nbgl_obj_t *) button);
2496
2497 return 0;
2498}
2499
2510 const char *text,
2511 uint8_t token,
2512 tune_index_e tuneId)
2513{
2515 .longPress.text = text,
2516 .longPress.token = token,
2517 .longPress.tuneId = tuneId};
2518
2519 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLongPressButton():\n");
2520 if (layout == NULL) {
2521 return -1;
2522 }
2523
2524 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2525}
2526
2538 const char *text,
2539 uint8_t token,
2540 tune_index_e tuneId)
2541{
2542 nbgl_layoutFooter_t footerDesc = {0};
2543 footerDesc.type = FOOTER_SIMPLE_TEXT;
2544 footerDesc.separationLine = true;
2545 footerDesc.simpleText.text = text;
2546 footerDesc.simpleText.token = token;
2547 footerDesc.simpleText.tuneId = tuneId;
2548 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2549}
2550
2564 const char *leftText,
2565 uint8_t leftToken,
2566 const char *rightText,
2567 uint8_t rightToken,
2568 tune_index_e tuneId)
2569{
2570 nbgl_layoutFooter_t footerDesc = {0};
2571 footerDesc.type = FOOTER_DOUBLE_TEXT;
2572 footerDesc.separationLine = true;
2573 footerDesc.doubleText.leftText = leftText;
2574 footerDesc.doubleText.leftToken = leftToken;
2575 footerDesc.doubleText.rightText = rightText;
2576 footerDesc.doubleText.rightToken = rightToken;
2577 footerDesc.doubleText.tuneId = tuneId;
2578 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2579}
2580
2590{
2591 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2592 layoutObj_t *obj;
2593 nbgl_text_area_t *textArea = NULL;
2594 nbgl_line_t *line = NULL;
2595 nbgl_image_t *image = NULL;
2596 nbgl_button_t *button = NULL;
2597
2598 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHeader(): type = %d\n", headerDesc->type);
2599 if (layout == NULL) {
2600 return -1;
2601 }
2602 if ((headerDesc == NULL) || (headerDesc->type >= NB_HEADER_TYPES)) {
2603 return -2;
2604 }
2605
2606 layoutInt->headerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2607 layoutInt->headerContainer->obj.area.width = SCREEN_WIDTH;
2608 layoutInt->headerContainer->layout = VERTICAL;
2609 layoutInt->headerContainer->children
2610 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2611 layoutInt->headerContainer->obj.alignment = TOP_MIDDLE;
2612
2613 switch (headerDesc->type) {
2614 case HEADER_EMPTY: {
2615 layoutInt->headerContainer->obj.area.height = headerDesc->emptySpace.height;
2616 break;
2617 }
2620 case HEADER_EXTENDED_BACK: {
2621 const char *text = (headerDesc->type == HEADER_EXTENDED_BACK)
2622 ? PIC(headerDesc->extendedBack.text)
2623 : PIC(headerDesc->backAndText.text);
2624 uint8_t backToken = (headerDesc->type == HEADER_EXTENDED_BACK)
2625 ? headerDesc->extendedBack.backToken
2626 : headerDesc->backAndText.token;
2627 nbgl_button_t *actionButton = NULL;
2628 // add back button
2629 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2630 // only make it active if valid token
2631 if (backToken != NBGL_INVALID_TOKEN) {
2632 obj = layoutAddCallbackObj(layoutInt,
2633 (nbgl_obj_t *) button,
2634 backToken,
2635 (headerDesc->type == HEADER_EXTENDED_BACK)
2636 ? headerDesc->extendedBack.tuneId
2637 : headerDesc->backAndText.tuneId);
2638 if (obj == NULL) {
2639 return -1;
2640 }
2641 }
2642
2643 button->obj.alignment = MID_LEFT;
2644 button->innerColor = WHITE;
2645 button->foregroundColor = (backToken != NBGL_INVALID_TOKEN) ? BLACK : WHITE;
2646 button->borderColor = WHITE;
2647 button->obj.area.width = BACK_KEY_WIDTH;
2648 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2649 button->text = NULL;
2650 button->icon = PIC(&LEFT_ARROW_ICON);
2651 button->obj.touchMask = (backToken != NBGL_INVALID_TOKEN) ? (1 << TOUCHED) : 0;
2652 button->obj.touchId = BACK_BUTTON_ID;
2653 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2654 = (nbgl_obj_t *) button;
2655 layoutInt->headerContainer->nbChildren++;
2656
2657 // add optional text if needed
2658 if (text != NULL) {
2659 // add optional icon if type is HEADER_BACK_ICON_AND_TEXT
2660 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2661 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2662 image->buffer = PIC(headerDesc->backAndText.icon);
2663 image->foregroundColor = BLACK;
2664 image->obj.alignment = CENTER;
2665 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2666 = (nbgl_obj_t *) image;
2667 layoutInt->headerContainer->nbChildren++;
2668 }
2669 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2670 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2671 && (headerDesc->extendedBack.textToken != NBGL_INVALID_TOKEN)) {
2672 obj = layoutAddCallbackObj(layoutInt,
2673 (nbgl_obj_t *) textArea,
2674 headerDesc->extendedBack.textToken,
2675 headerDesc->extendedBack.tuneId);
2676 if (obj == NULL) {
2677 return -1;
2678 }
2679 textArea->obj.touchMask = (1 << TOUCHED);
2680 }
2681 textArea->obj.alignment = CENTER;
2682 textArea->textColor = BLACK;
2683 textArea->obj.area.width
2684 = layoutInt->headerContainer->obj.area.width - 2 * BACK_KEY_WIDTH;
2685 // if icon, reduce to 1 line and fit text width
2686 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2687 textArea->obj.area.width -= 16 + image->buffer->width;
2688 }
2689 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2690 textArea->text = text;
2691 textArea->fontId = SMALL_BOLD_FONT;
2692 textArea->textAlignment = CENTER;
2693 textArea->wrapping = true;
2694 uint8_t nbMaxLines = (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) ? 1 : 2;
2695 // ensure that text fits on 2 lines maximum
2696 if (nbgl_getTextNbLinesInWidth(textArea->fontId,
2697 textArea->text,
2698 textArea->obj.area.width,
2699 textArea->wrapping)
2700 > nbMaxLines) {
2701 textArea->obj.area.height
2702 = nbMaxLines * nbgl_getFontLineHeight(textArea->fontId);
2703#ifndef BUILD_SCREENSHOTS
2705 "nbgl_layoutAddHeader: text [%s] is too long for header\n",
2706 text);
2707#endif // BUILD_SCREENSHOTS
2708 }
2709 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2710 textArea->obj.area.width = nbgl_getTextWidth(textArea->fontId, textArea->text);
2711 }
2712 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2713 = (nbgl_obj_t *) textArea;
2714 layoutInt->headerContainer->nbChildren++;
2715 // if icon, realign icon & text
2716 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2717 textArea->obj.alignmentMarginX = 8 + image->buffer->width / 2;
2718 image->obj.alignmentMarginX = -8 - textArea->obj.area.width / 2;
2719 }
2720 }
2721 // add action key if the type is HEADER_EXTENDED_BACK
2722 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2723 && (headerDesc->extendedBack.actionIcon)) {
2724 actionButton = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2725 // if token is valid
2726 if (headerDesc->extendedBack.actionToken != NBGL_INVALID_TOKEN) {
2727 obj = layoutAddCallbackObj(layoutInt,
2728 (nbgl_obj_t *) actionButton,
2729 headerDesc->extendedBack.actionToken,
2730 headerDesc->extendedBack.tuneId);
2731 if (obj == NULL) {
2732 return -1;
2733 }
2734 actionButton->obj.touchMask = (1 << TOUCHED);
2735 }
2736
2737 actionButton->obj.alignment = MID_RIGHT;
2738 actionButton->innerColor = WHITE;
2739 button->foregroundColor
2741 : LIGHT_GRAY;
2742 actionButton->borderColor = WHITE;
2743 actionButton->obj.area.width = BACK_KEY_WIDTH;
2744 actionButton->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2745 actionButton->text = NULL;
2746 actionButton->icon = PIC(headerDesc->extendedBack.actionIcon);
2747 actionButton->obj.touchId = EXTRA_BUTTON_ID;
2748 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2749 = (nbgl_obj_t *) actionButton;
2750 layoutInt->headerContainer->nbChildren++;
2751 }
2752
2753 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2754 // add potential text under the line if the type is HEADER_EXTENDED_BACK
2755 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2756 && (headerDesc->extendedBack.subText != NULL)) {
2757 nbgl_text_area_t *subTextArea;
2758
2759 line = createHorizontalLine(layoutInt->layer);
2760 line->obj.alignment = TOP_MIDDLE;
2761 line->obj.alignmentMarginY = TOUCHABLE_HEADER_BAR_HEIGHT;
2762 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2763 = (nbgl_obj_t *) line;
2764 layoutInt->headerContainer->nbChildren++;
2765
2766 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2767 subTextArea->textColor = BLACK;
2768 subTextArea->text = PIC(headerDesc->extendedBack.subText);
2769 subTextArea->textAlignment = MID_LEFT;
2770 subTextArea->fontId = SMALL_REGULAR_FONT;
2771 subTextArea->wrapping = true;
2772 subTextArea->obj.alignment = BOTTOM_MIDDLE;
2773 subTextArea->obj.alignmentMarginY = SUB_HEADER_MARGIN;
2774 subTextArea->obj.area.width = AVAILABLE_WIDTH;
2775 subTextArea->obj.area.height
2776 = nbgl_getTextHeightInWidth(subTextArea->fontId,
2777 subTextArea->text,
2778 subTextArea->obj.area.width,
2779 subTextArea->wrapping);
2780 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2781 = (nbgl_obj_t *) subTextArea;
2782 layoutInt->headerContainer->nbChildren++;
2783 layoutInt->headerContainer->obj.area.height
2784 += subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN;
2786 if (button != NULL) {
2787 button->obj.alignmentMarginY
2788 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2789 }
2790 if (textArea != NULL) {
2791 textArea->obj.alignmentMarginY
2792 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2793 }
2794 if (actionButton != NULL) {
2795 actionButton->obj.alignmentMarginY
2796 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2797 }
2798 }
2799 break;
2800 }
2802#ifdef TARGET_STAX
2803 // add optional back button
2804 if (headerDesc->progressAndBack.withBack) {
2805 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2806 obj = layoutAddCallbackObj(layoutInt,
2807 (nbgl_obj_t *) button,
2808 headerDesc->progressAndBack.token,
2809 headerDesc->progressAndBack.tuneId);
2810 if (obj == NULL) {
2811 return -1;
2812 }
2813
2814 button->obj.alignment = MID_LEFT;
2815 button->innerColor = WHITE;
2816 button->foregroundColor = BLACK;
2817 button->borderColor = WHITE;
2818 button->obj.area.width = BACK_KEY_WIDTH;
2819 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2820 button->text = NULL;
2821 button->icon = PIC(&LEFT_ARROW_ICON);
2822 button->obj.touchMask = (1 << TOUCHED);
2823 button->obj.touchId = BACK_BUTTON_ID;
2824 // add to container
2825 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2826 = (nbgl_obj_t *) button;
2827 layoutInt->headerContainer->nbChildren++;
2828 }
2829
2830 // add progress indicator
2831 if (headerDesc->progressAndBack.nbPages > 1
2833 nbgl_page_indicator_t *progress;
2834
2835 progress
2837 progress->activePage = headerDesc->progressAndBack.activePage;
2838 progress->nbPages = headerDesc->progressAndBack.nbPages;
2839 progress->obj.area.width = 224;
2840 progress->obj.alignment = CENTER;
2841 // add to container
2842 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2843 = (nbgl_obj_t *) progress;
2844 layoutInt->headerContainer->nbChildren++;
2845 }
2846 layoutInt->activePage = headerDesc->progressAndBack.activePage;
2847 layoutInt->nbPages = headerDesc->progressAndBack.nbPages;
2848 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2849#endif // TARGET_STAX
2850 break;
2851 }
2852 case HEADER_TITLE: {
2853 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2854 textArea->textColor = BLACK;
2855 textArea->obj.area.width = AVAILABLE_WIDTH;
2856 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2857 textArea->text = PIC(headerDesc->title.text);
2858 textArea->fontId = SMALL_BOLD_FONT;
2859 textArea->textAlignment = CENTER;
2860 textArea->wrapping = true;
2861 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2862 = (nbgl_obj_t *) textArea;
2863 layoutInt->headerContainer->nbChildren++;
2864 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2865 break;
2866 }
2867 case HEADER_RIGHT_TEXT: {
2868 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2869 obj = layoutAddCallbackObj(layoutInt,
2870 (nbgl_obj_t *) textArea,
2871 headerDesc->rightText.token,
2872 headerDesc->rightText.tuneId);
2873 if (obj == NULL) {
2874 return -1;
2875 }
2876 textArea->obj.alignment = MID_RIGHT;
2877 textArea->obj.alignmentMarginX = BORDER_MARGIN;
2878 textArea->textColor = BLACK;
2879 textArea->obj.area.width = AVAILABLE_WIDTH;
2880 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2881 textArea->text = PIC(headerDesc->rightText.text);
2882 textArea->fontId = SMALL_BOLD_FONT;
2883 textArea->textAlignment = MID_RIGHT;
2884 textArea->obj.touchMask = (1 << TOUCHED);
2885 textArea->obj.touchId = TOP_RIGHT_BUTTON_ID;
2886 // add to bottom container
2887 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2888 = (nbgl_obj_t *) textArea;
2889 layoutInt->headerContainer->nbChildren++;
2890 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2891 break;
2892 }
2893 default:
2894 return -2;
2895 }
2896 // draw separation line at bottom of container
2897 if (headerDesc->separationLine) {
2898 line = createHorizontalLine(layoutInt->layer);
2899 line->obj.alignment = BOTTOM_MIDDLE;
2900 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2901 = (nbgl_obj_t *) line;
2902 layoutInt->headerContainer->nbChildren++;
2903 }
2904 // header must be the first child
2905 layoutInt->children[HEADER_INDEX] = (nbgl_obj_t *) layoutInt->headerContainer;
2906
2907 // subtract header height from main container height
2908 layoutInt->container->obj.area.height -= layoutInt->headerContainer->obj.area.height;
2909 layoutInt->container->obj.alignTo = (nbgl_obj_t *) layoutInt->headerContainer;
2910 layoutInt->container->obj.alignment = BOTTOM_LEFT;
2911
2912 layoutInt->headerType = headerDesc->type;
2913
2914 return layoutInt->headerContainer->obj.area.height;
2915}
2916
2926{
2927 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2928 layoutObj_t *obj;
2929 nbgl_text_area_t *textArea;
2930 nbgl_line_t *line, *separationLine = NULL;
2931 nbgl_button_t *button;
2932
2933 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddExtendedFooter():\n");
2934 if (layout == NULL) {
2935 return -1;
2936 }
2937 if ((footerDesc == NULL) || (footerDesc->type >= NB_FOOTER_TYPES)) {
2938 return -2;
2939 }
2940
2941 layoutInt->footerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2942 layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
2943 layoutInt->footerContainer->layout = VERTICAL;
2944 layoutInt->footerContainer->children
2945 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2946 layoutInt->footerContainer->obj.alignment = BOTTOM_MIDDLE;
2947
2948 switch (footerDesc->type) {
2949 case FOOTER_EMPTY: {
2950 layoutInt->footerContainer->obj.area.height = footerDesc->emptySpace.height;
2951 break;
2952 }
2953 case FOOTER_SIMPLE_TEXT: {
2954 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2955 obj = layoutAddCallbackObj(layoutInt,
2956 (nbgl_obj_t *) textArea,
2957 footerDesc->simpleText.token,
2958 footerDesc->simpleText.tuneId);
2959 if (obj == NULL) {
2960 return -1;
2961 }
2962
2963 textArea->obj.alignment = BOTTOM_MIDDLE;
2964 textArea->textColor = (footerDesc->simpleText.mutedOut) ? LIGHT_TEXT_COLOR : BLACK;
2965 textArea->obj.area.width = AVAILABLE_WIDTH;
2966 textArea->obj.area.height
2967 = (footerDesc->simpleText.mutedOut) ? SMALL_FOOTER_HEIGHT : SIMPLE_FOOTER_HEIGHT;
2968 textArea->text = PIC(footerDesc->simpleText.text);
2969 textArea->fontId
2970 = (footerDesc->simpleText.mutedOut) ? SMALL_REGULAR_FONT : SMALL_BOLD_FONT;
2971 textArea->textAlignment = CENTER;
2972 textArea->obj.touchMask = (1 << TOUCHED);
2973 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2974 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2975 = (nbgl_obj_t *) textArea;
2976 layoutInt->footerContainer->nbChildren++;
2977 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2978 break;
2979 }
2980 case FOOTER_DOUBLE_TEXT: {
2981 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2982 obj = layoutAddCallbackObj(layoutInt,
2983 (nbgl_obj_t *) textArea,
2984 footerDesc->doubleText.leftToken,
2985 footerDesc->doubleText.tuneId);
2986 if (obj == NULL) {
2987 return -1;
2988 }
2989 textArea->obj.alignment = BOTTOM_LEFT;
2990 textArea->textColor = BLACK;
2991 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2992 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2993 textArea->text = PIC(footerDesc->doubleText.leftText);
2994 textArea->fontId = SMALL_BOLD_FONT;
2995 textArea->textAlignment = CENTER;
2996 textArea->obj.touchMask = (1 << TOUCHED);
2997 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2998 // add to bottom container
2999 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3000 = (nbgl_obj_t *) textArea;
3001 layoutInt->footerContainer->nbChildren++;
3002
3003 // create right touchable text
3004 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3005 obj = layoutAddCallbackObj(layoutInt,
3006 (nbgl_obj_t *) textArea,
3007 footerDesc->doubleText.rightToken,
3008 footerDesc->doubleText.tuneId);
3009 if (obj == NULL) {
3010 return -1;
3011 }
3012
3013 textArea->obj.alignment = BOTTOM_RIGHT;
3014 textArea->textColor = BLACK;
3015 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
3016 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3017 textArea->text = PIC(footerDesc->doubleText.rightText);
3018 textArea->fontId = SMALL_BOLD_FONT;
3019 textArea->textAlignment = CENTER;
3020 textArea->obj.touchMask = (1 << TOUCHED);
3021 textArea->obj.touchId = RIGHT_BUTTON_ID;
3022 // add to bottom container
3023 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3024 = (nbgl_obj_t *) textArea;
3025 layoutInt->footerContainer->nbChildren++;
3026 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
3027
3028 // create vertical line separating texts
3029 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
3030 separationLine->lineColor = LIGHT_GRAY;
3031 separationLine->obj.area.width = 1;
3032 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
3033 separationLine->direction = VERTICAL;
3034 separationLine->thickness = 1;
3035 separationLine->obj.alignment = MID_LEFT;
3036 separationLine->obj.alignTo = (nbgl_obj_t *) textArea;
3037 separationLine->obj.alignmentMarginX = -1;
3038 break;
3039 }
3040 case FOOTER_TEXT_AND_NAV: {
3041 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3042 // add touchable text on the left
3043 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3044 obj = layoutAddCallbackObj(layoutInt,
3045 (nbgl_obj_t *) textArea,
3046 footerDesc->textAndNav.token,
3047 footerDesc->textAndNav.tuneId);
3048 if (obj == NULL) {
3049 return -1;
3050 }
3051 textArea->obj.alignment = BOTTOM_LEFT;
3052 textArea->textColor = BLACK;
3053 textArea->obj.area.width = FOOTER_TEXT_AND_NAV_WIDTH;
3054 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3055 textArea->text = PIC(footerDesc->textAndNav.text);
3056 textArea->fontId = SMALL_BOLD_FONT;
3057 textArea->textAlignment = CENTER;
3058 textArea->obj.touchMask = (1 << TOUCHED);
3059 textArea->obj.touchId = BOTTOM_BUTTON_ID;
3060 // add to bottom container
3061 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3062 = (nbgl_obj_t *) textArea;
3063 layoutInt->footerContainer->nbChildren++;
3064
3065 // add navigation on the right
3066 nbgl_container_t *navContainer
3068 navContainer->obj.area.width = AVAILABLE_WIDTH;
3069 navContainer->layout = VERTICAL;
3070 navContainer->nbChildren = 4;
3071 navContainer->children
3072 = (nbgl_obj_t **) nbgl_containerPoolGet(navContainer->nbChildren, layoutInt->layer);
3073 navContainer->obj.alignment = BOTTOM_RIGHT;
3074 navContainer->obj.area.width = SCREEN_WIDTH - textArea->obj.area.width;
3075 navContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3076 layoutNavigationPopulate(navContainer, &footerDesc->navigation, layoutInt->layer);
3077 obj = layoutAddCallbackObj(layoutInt,
3078 (nbgl_obj_t *) navContainer,
3079 footerDesc->textAndNav.navigation.token,
3080 footerDesc->textAndNav.navigation.tuneId);
3081 if (obj == NULL) {
3082 return -1;
3083 }
3084
3085 // create vertical line separating text from nav
3086 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
3087 separationLine->lineColor = LIGHT_GRAY;
3088 separationLine->obj.area.width = 1;
3089 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
3090 separationLine->direction = VERTICAL;
3091 separationLine->thickness = 1;
3092 separationLine->obj.alignment = MID_LEFT;
3093 separationLine->obj.alignTo = (nbgl_obj_t *) navContainer;
3094 separationLine->obj.alignmentMarginX = -1;
3095
3096 layoutInt->activePage = footerDesc->textAndNav.navigation.activePage;
3097 layoutInt->nbPages = footerDesc->textAndNav.navigation.nbPages;
3098 // add to bottom container
3099 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3100 = (nbgl_obj_t *) navContainer;
3101 layoutInt->footerContainer->nbChildren++;
3102 break;
3103 }
3104 case FOOTER_NAV: {
3105 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3107 layoutInt->footerContainer, &footerDesc->navigation, layoutInt->layer);
3108 layoutInt->footerContainer->nbChildren = 4;
3109 obj = layoutAddCallbackObj(layoutInt,
3110 (nbgl_obj_t *) layoutInt->footerContainer,
3111 footerDesc->navigation.token,
3112 footerDesc->navigation.tuneId);
3113 if (obj == NULL) {
3114 return -1;
3115 }
3116
3117 layoutInt->activePage = footerDesc->navigation.activePage;
3118 layoutInt->nbPages = footerDesc->navigation.nbPages;
3119 break;
3120 }
3121 case FOOTER_SIMPLE_BUTTON: {
3122 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3123 obj = layoutAddCallbackObj(layoutInt,
3124 (nbgl_obj_t *) button,
3125 footerDesc->button.token,
3126 footerDesc->button.tuneId);
3127 if (obj == NULL) {
3128 return -1;
3129 }
3130
3131 button->obj.alignment = BOTTOM_MIDDLE;
3132 button->obj.alignmentMarginY = SINGLE_BUTTON_MARGIN;
3133 if (footerDesc->button.style == BLACK_BACKGROUND) {
3134 button->innerColor = BLACK;
3135 button->foregroundColor = WHITE;
3136 }
3137 else {
3138 button->innerColor = WHITE;
3139 button->foregroundColor = BLACK;
3140 }
3141
3142 if (footerDesc->button.style == NO_BORDER) {
3143 button->borderColor = WHITE;
3144 }
3145 else {
3146 if (footerDesc->button.style == BLACK_BACKGROUND) {
3147 button->borderColor = BLACK;
3148 }
3149 else {
3150 button->borderColor = LIGHT_GRAY;
3151 }
3152 }
3153 button->text = PIC(footerDesc->button.text);
3154 button->fontId = SMALL_BOLD_FONT;
3155 button->icon = PIC(footerDesc->button.icon);
3156 button->radius = BUTTON_RADIUS;
3157 button->obj.area.height = BUTTON_DIAMETER;
3158 layoutInt->footerContainer->obj.area.height = FOOTER_BUTTON_HEIGHT;
3159 if (footerDesc->button.text == NULL) {
3160 button->obj.area.width = BUTTON_DIAMETER;
3161 }
3162 else {
3163 button->obj.area.width = AVAILABLE_WIDTH;
3164 }
3165 button->obj.touchMask = (1 << TOUCHED);
3166 button->obj.touchId = button->text ? SINGLE_BUTTON_ID : BOTTOM_BUTTON_ID;
3167 // add to bottom container
3168 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3169 = (nbgl_obj_t *) button;
3170 layoutInt->footerContainer->nbChildren++;
3171 break;
3172 }
3173 case FOOTER_CHOICE_BUTTONS: {
3174 // texts cannot be NULL
3175 if ((footerDesc->choiceButtons.bottomText == NULL)
3176 || (footerDesc->choiceButtons.topText == NULL)) {
3177 return -1;
3178 }
3179
3180 // create bottom button (footer) at first
3181 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3182 obj = layoutAddCallbackObj(layoutInt,
3183 (nbgl_obj_t *) button,
3184 footerDesc->choiceButtons.token,
3185 footerDesc->choiceButtons.tuneId);
3186 if (obj == NULL) {
3187 return -1;
3188 }
3189 // associate with with index 1
3190 obj->index = 1;
3191 // put at the bottom of the container
3192 button->obj.alignment = BOTTOM_MIDDLE;
3193 button->innerColor = WHITE;
3194 if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3195 button->obj.alignmentMarginY
3196 = SINGLE_BUTTON_MARGIN; // pixels from bottom of container
3197 button->borderColor = LIGHT_GRAY;
3198 button->obj.area.height = BUTTON_DIAMETER;
3199 }
3200 else {
3201 button->obj.alignmentMarginY
3202 = BUTTON_FROM_BOTTOM_MARGIN; // pixels from screen bottom
3203 button->borderColor = WHITE;
3204 button->obj.area.height = FOOTER_IN_PAIR_HEIGHT;
3205 }
3206 button->foregroundColor = BLACK;
3207 button->obj.area.width = AVAILABLE_WIDTH;
3208 button->radius = BUTTON_RADIUS;
3209 button->text = PIC(footerDesc->choiceButtons.bottomText);
3210 button->fontId = SMALL_BOLD_FONT;
3211 button->obj.touchMask = (1 << TOUCHED);
3212 button->obj.touchId = CHOICE_2_ID;
3213 // add to bottom container
3214 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3215 = (nbgl_obj_t *) button;
3216 layoutInt->footerContainer->nbChildren++;
3217
3218 // add line if needed
3219 if ((footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3220 && (footerDesc->choiceButtons.style != BOTH_ROUNDED_STYLE)) {
3221 line = createHorizontalLine(layoutInt->layer);
3222 line->obj.alignment = TOP_MIDDLE;
3223 line->obj.alignTo = (nbgl_obj_t *) button;
3224 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3225 = (nbgl_obj_t *) line;
3226 layoutInt->footerContainer->nbChildren++;
3227 }
3228
3229 // then top button, on top of it
3230 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3231 obj = layoutAddCallbackObj(layoutInt,
3232 (nbgl_obj_t *) button,
3233 footerDesc->choiceButtons.token,
3234 footerDesc->choiceButtons.tuneId);
3235 if (obj == NULL) {
3236 return -1;
3237 }
3238 // associate with with index 0
3239 obj->index = 0;
3240 button->obj.alignment = TOP_MIDDLE;
3241 if (footerDesc->choiceButtons.style == ROUNDED_AND_FOOTER_STYLE) {
3242 button->obj.alignmentMarginY = TOP_BUTTON_MARGIN; // pixels from top of container
3243 }
3244 else if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3245 button->obj.alignmentMarginY
3246 = SINGLE_BUTTON_MARGIN; // pixels from top of container
3247 }
3248 else {
3249 button->obj.alignmentMarginY
3250 = TOP_BUTTON_MARGIN_WITH_ACTION; // pixels from top of container
3251 }
3253 button->innerColor = WHITE;
3254 button->borderColor = LIGHT_GRAY;
3255 button->foregroundColor = BLACK;
3256 }
3257 else {
3258 button->innerColor = BLACK;
3259 button->borderColor = BLACK;
3260 button->foregroundColor = WHITE;
3261 }
3262 button->obj.area.width = AVAILABLE_WIDTH;
3263 button->obj.area.height = BUTTON_DIAMETER;
3264 button->radius = BUTTON_RADIUS;
3265 button->text = PIC(footerDesc->choiceButtons.topText);
3266 button->icon = (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3267 ? PIC(footerDesc->choiceButtons.topIcon)
3268 : NULL;
3269 button->fontId = SMALL_BOLD_FONT;
3270 button->obj.touchMask = (1 << TOUCHED);
3271 button->obj.touchId = CHOICE_1_ID;
3272 // add to bottom container
3273 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3274 = (nbgl_obj_t *) button;
3275 layoutInt->footerContainer->nbChildren++;
3276
3277 if (footerDesc->choiceButtons.style == ROUNDED_AND_FOOTER_STYLE) {
3278 layoutInt->footerContainer->obj.area.height = ROUNDED_AND_FOOTER_FOOTER_HEIGHT;
3279 }
3280 else if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3281 layoutInt->footerContainer->obj.area.height = BOTH_ROUNDED_FOOTER_HEIGHT;
3282 }
3283 else {
3284 layoutInt->footerContainer->obj.area.height = ACTION_AND_FOOTER_FOOTER_HEIGHT;
3285 }
3286
3287 break;
3288 }
3289 default:
3290 return -2;
3291 }
3292
3293 // add swipable feature for navigation
3294 if ((footerDesc->type == FOOTER_NAV) || (footerDesc->type == FOOTER_TEXT_AND_NAV)) {
3295 addSwipeInternal(layoutInt,
3296 ((1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT)),
3298 (footerDesc->type == FOOTER_NAV) ? footerDesc->navigation.token
3299 : footerDesc->textAndNav.navigation.token,
3300 (footerDesc->type == FOOTER_NAV)
3301 ? footerDesc->navigation.tuneId
3302 : footerDesc->textAndNav.navigation.tuneId);
3303 }
3304
3305 if (footerDesc->separationLine) {
3306 line = createHorizontalLine(layoutInt->layer);
3307 line->obj.alignment = TOP_MIDDLE;
3308 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3309 = (nbgl_obj_t *) line;
3310 layoutInt->footerContainer->nbChildren++;
3311 }
3312 if (separationLine != NULL) {
3313 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3314 = (nbgl_obj_t *) separationLine;
3315 layoutInt->footerContainer->nbChildren++;
3316 }
3317
3318 layoutInt->children[FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->footerContainer;
3319
3320 // subtract footer height from main container height
3321 layoutInt->container->obj.area.height -= layoutInt->footerContainer->obj.area.height;
3322
3323 layoutInt->footerType = footerDesc->type;
3324
3325 return layoutInt->footerContainer->obj.area.height;
3326}
3327
3337{
3338 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3339 layoutObj_t *obj;
3340 nbgl_text_area_t *textArea;
3341 nbgl_line_t *line;
3342 nbgl_button_t *button;
3343
3344 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddUpFooter():\n");
3345 if (layout == NULL) {
3346 return -1;
3347 }
3348 if ((upFooterDesc == NULL) || (upFooterDesc->type >= NB_UP_FOOTER_TYPES)) {
3349 return -2;
3350 }
3351
3352 layoutInt->upFooterContainer
3354 layoutInt->upFooterContainer->obj.area.width = SCREEN_WIDTH;
3355 layoutInt->upFooterContainer->layout = VERTICAL;
3356 // maximum 4 children for long press button
3357 layoutInt->upFooterContainer->children
3358 = (nbgl_obj_t **) nbgl_containerPoolGet(4, layoutInt->layer);
3359 layoutInt->upFooterContainer->obj.alignTo = (nbgl_obj_t *) layoutInt->container;
3360 layoutInt->upFooterContainer->obj.alignment = BOTTOM_MIDDLE;
3361
3362 switch (upFooterDesc->type) {
3363 case UP_FOOTER_LONG_PRESS: {
3364 nbgl_progress_bar_t *progressBar;
3365
3366 obj = layoutAddCallbackObj(layoutInt,
3367 (nbgl_obj_t *) layoutInt->upFooterContainer,
3368 upFooterDesc->longPress.token,
3369 upFooterDesc->longPress.tuneId);
3370 if (obj == NULL) {
3371 return -1;
3372 }
3373 layoutInt->upFooterContainer->nbChildren = 4;
3374 layoutInt->upFooterContainer->obj.area.height = LONG_PRESS_BUTTON_HEIGHT;
3375 layoutInt->upFooterContainer->obj.touchId = LONG_PRESS_BUTTON_ID;
3376 layoutInt->upFooterContainer->obj.touchMask
3377 = ((1 << TOUCHING) | (1 << TOUCH_RELEASED) | (1 << OUT_OF_TOUCH)
3378 | (1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT));
3379
3380 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3381 button->obj.alignmentMarginX = BORDER_MARGIN;
3382 button->obj.alignment = MID_RIGHT;
3383 button->innerColor = BLACK;
3384 button->foregroundColor = WHITE;
3385 button->borderColor = BLACK;
3386 button->obj.area.width = BUTTON_DIAMETER;
3387 button->obj.area.height = BUTTON_DIAMETER;
3388 button->radius = BUTTON_RADIUS;
3389 button->icon = PIC(&VALIDATE_ICON);
3390 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3391
3392 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3393 textArea->textColor = BLACK;
3394 textArea->text = PIC(upFooterDesc->longPress.text);
3395 textArea->textAlignment = MID_LEFT;
3396 textArea->fontId = LARGE_MEDIUM_FONT;
3397 textArea->wrapping = true;
3398 textArea->obj.area.width = SCREEN_WIDTH - 3 * BORDER_MARGIN - button->obj.area.width;
3399 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3400 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3401 textArea->style = NO_STYLE;
3402 textArea->obj.alignment = MID_LEFT;
3403 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3404 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) textArea;
3405
3406 line = createHorizontalLine(layoutInt->layer);
3407 line->obj.alignment = TOP_MIDDLE;
3408 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) line;
3409
3410 progressBar = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
3411 progressBar->obj.area.width = SCREEN_WIDTH;
3412 progressBar->obj.area.height = LONG_PRESS_PROGRESS_HEIGHT;
3413 progressBar->obj.alignment = TOP_MIDDLE;
3414 progressBar->obj.alignmentMarginY = LONG_PRESS_PROGRESS_ALIGN;
3415 progressBar->resetIfOverriden = true;
3416 progressBar->partialRedraw = true;
3417 layoutInt->upFooterContainer->children[3] = (nbgl_obj_t *) progressBar;
3418 break;
3419 }
3420 case UP_FOOTER_BUTTON: {
3421 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3422 obj = layoutAddCallbackObj(layoutInt,
3423 (nbgl_obj_t *) button,
3424 upFooterDesc->button.token,
3425 upFooterDesc->button.tuneId);
3426 if (obj == NULL) {
3427 return -1;
3428 }
3429
3430 layoutInt->upFooterContainer->nbChildren = 1;
3431 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3432 button->obj.alignment = CENTER;
3433
3434 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3435 button->innerColor = BLACK;
3436 button->foregroundColor = WHITE;
3437 }
3438 else {
3439 button->innerColor = WHITE;
3440 button->foregroundColor = BLACK;
3441 }
3442 if (upFooterDesc->button.style == NO_BORDER) {
3443 button->borderColor = WHITE;
3444 }
3445 else {
3446 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3447 button->borderColor = BLACK;
3448 }
3449 else {
3450 button->borderColor = LIGHT_GRAY;
3451 }
3452 }
3453 button->text = PIC(upFooterDesc->button.text);
3454 button->fontId = SMALL_BOLD_FONT;
3455 button->icon = PIC(upFooterDesc->button.icon);
3456 button->obj.area.width = AVAILABLE_WIDTH;
3457 button->obj.area.height = BUTTON_DIAMETER;
3458 button->radius = BUTTON_RADIUS;
3459
3460 button->obj.alignTo = NULL;
3461 button->obj.touchMask = (1 << TOUCHED);
3462 button->obj.touchId = SINGLE_BUTTON_ID;
3463 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3464 break;
3465 }
3467 // icon & text cannot be NULL
3468 if ((upFooterDesc->horizontalButtons.leftIcon == NULL)
3469 || (upFooterDesc->horizontalButtons.rightText == NULL)) {
3470 return -1;
3471 }
3472
3473 layoutInt->upFooterContainer->nbChildren = 2;
3474 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3475
3476 // create left button (in white) at first
3477 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3478 obj = layoutAddCallbackObj(layoutInt,
3479 (nbgl_obj_t *) button,
3480 upFooterDesc->horizontalButtons.leftToken,
3481 upFooterDesc->horizontalButtons.tuneId);
3482 if (obj == NULL) {
3483 return -1;
3484 }
3485 // associate with with index 1
3486 obj->index = 1;
3487 button->obj.alignment = MID_LEFT;
3488 button->obj.alignmentMarginX = BORDER_MARGIN;
3489 button->borderColor = LIGHT_GRAY;
3490 button->innerColor = WHITE;
3491 button->foregroundColor = BLACK;
3492 button->obj.area.width = BUTTON_WIDTH;
3493 button->obj.area.height = BUTTON_DIAMETER;
3494 button->radius = BUTTON_RADIUS;
3495 button->icon = PIC(upFooterDesc->horizontalButtons.leftIcon);
3496 button->fontId = SMALL_BOLD_FONT;
3497 button->obj.touchMask = (1 << TOUCHED);
3498 button->obj.touchId = CHOICE_2_ID;
3499 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3500
3501 // then black button, on right
3502 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3503 obj = layoutAddCallbackObj(layoutInt,
3504 (nbgl_obj_t *) button,
3505 upFooterDesc->horizontalButtons.rightToken,
3506 upFooterDesc->horizontalButtons.tuneId);
3507 if (obj == NULL) {
3508 return -1;
3509 }
3510 // associate with with index 0
3511 obj->index = 0;
3512 button->obj.alignment = MID_RIGHT;
3513 button->obj.alignmentMarginX = BORDER_MARGIN;
3514 button->innerColor = BLACK;
3515 button->borderColor = BLACK;
3516 button->foregroundColor = WHITE;
3517 button->obj.area.width = AVAILABLE_WIDTH - BUTTON_WIDTH - LEFT_CONTENT_ICON_TEXT_X;
3518 button->obj.area.height = BUTTON_DIAMETER;
3519 button->radius = BUTTON_RADIUS;
3520 button->text = PIC(upFooterDesc->horizontalButtons.rightText);
3521 button->fontId = SMALL_BOLD_FONT;
3522 button->obj.touchMask = (1 << TOUCHED);
3523 button->obj.touchId = CHOICE_1_ID;
3524 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) button;
3525 break;
3526 }
3527 case UP_FOOTER_TIP_BOX: {
3528 // text cannot be NULL
3529 if (upFooterDesc->tipBox.text == NULL) {
3530 return -1;
3531 }
3532 obj = layoutAddCallbackObj(layoutInt,
3533 (nbgl_obj_t *) layoutInt->upFooterContainer,
3534 upFooterDesc->tipBox.token,
3535 upFooterDesc->tipBox.tuneId);
3536 if (obj == NULL) {
3537 return -1;
3538 }
3539 layoutInt->upFooterContainer->nbChildren = 3;
3540 layoutInt->upFooterContainer->obj.touchId = TIP_BOX_ID;
3541 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3542
3543 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3544 textArea->textColor = BLACK;
3545 textArea->text = PIC(upFooterDesc->tipBox.text);
3546 textArea->textAlignment = MID_LEFT;
3547 textArea->fontId = SMALL_REGULAR_FONT;
3548 textArea->wrapping = true;
3549 textArea->obj.area.width = AVAILABLE_WIDTH;
3550 if (upFooterDesc->tipBox.icon != NULL) {
3551 textArea->obj.area.width
3552 -= ((nbgl_icon_details_t *) PIC(upFooterDesc->tipBox.icon))->width
3553 + TIP_BOX_TEXT_ICON_MARGIN;
3554 }
3555 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3556 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3557 textArea->obj.alignment = MID_LEFT;
3558 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3559 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3560 layoutInt->upFooterContainer->obj.area.height = textArea->obj.area.height;
3561
3562 line = createHorizontalLine(layoutInt->layer);
3563 line->obj.alignment = TOP_MIDDLE;
3564 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) line;
3565
3566 if (upFooterDesc->tipBox.icon != NULL) {
3567 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
3568 image->obj.alignmentMarginX = BORDER_MARGIN;
3569 image->obj.alignment = MID_RIGHT;
3570 image->foregroundColor = BLACK;
3571 image->buffer = PIC(upFooterDesc->tipBox.icon);
3572 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) image;
3573 if (layoutInt->upFooterContainer->obj.area.height < image->buffer->height) {
3574 layoutInt->upFooterContainer->obj.area.height = image->buffer->height;
3575 }
3576 }
3577 layoutInt->upFooterContainer->obj.area.height += 2 * TIP_BOX_MARGIN_Y;
3578
3579 break;
3580 }
3581 case UP_FOOTER_TEXT: {
3582 obj = layoutAddCallbackObj(layoutInt,
3583 (nbgl_obj_t *) layoutInt->upFooterContainer,
3584 upFooterDesc->text.token,
3585 upFooterDesc->text.tuneId);
3586 if (obj == NULL) {
3587 return -1;
3588 }
3589 layoutInt->upFooterContainer->nbChildren = 1;
3590 layoutInt->upFooterContainer->obj.area.height = SMALL_FOOTER_HEIGHT;
3591 layoutInt->upFooterContainer->obj.touchId = WHOLE_SCREEN_ID;
3592 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3593
3594 // only create text_area if text is not empty
3595 if (strlen(PIC(upFooterDesc->text.text))) {
3596 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3597 textArea->textColor = LIGHT_TEXT_COLOR;
3598 textArea->text = PIC(upFooterDesc->text.text);
3599 textArea->textAlignment = CENTER;
3600 textArea->fontId = SMALL_REGULAR_FONT;
3601 textArea->wrapping = true;
3602 textArea->obj.area.width = AVAILABLE_WIDTH;
3603 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3604 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3605 textArea->obj.alignment = CENTER;
3606 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3607 }
3608 break;
3609 }
3610 default:
3611 return -2;
3612 }
3613
3614 // subtract up footer height from main container height
3615 layoutInt->container->obj.area.height -= layoutInt->upFooterContainer->obj.area.height;
3616
3617 layoutInt->children[UP_FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->upFooterContainer;
3618
3619 layoutInt->upFooterType = upFooterDesc->type;
3620
3621 return layoutInt->upFooterContainer->obj.area.height;
3622}
3623
3638 uint8_t activePage,
3639 uint8_t nbPages,
3640 bool withBack,
3641 uint8_t backToken,
3642 tune_index_e tuneId)
3643{
3645 .separationLine = false,
3646 .progressAndBack.activePage = activePage,
3647 .progressAndBack.nbPages = nbPages,
3648 .progressAndBack.token = backToken,
3649 .progressAndBack.tuneId = tuneId,
3650 .progressAndBack.withBack = withBack,
3651 .progressAndBack.actionIcon = NULL,
3652 .progressAndBack.actionToken = NBGL_INVALID_TOKEN};
3653 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressIndicator():\n");
3654
3655 return nbgl_layoutAddHeader(layout, &headerDesc);
3656}
3657
3669 const char *text,
3670 const char *subText,
3671 uint8_t initPosition)
3672{
3673 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3674 nbgl_container_t *container;
3675 nbgl_text_area_t *textArea;
3676 nbgl_spinner_t *spinner;
3677
3678 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSpinner():\n");
3679 if (layout == NULL) {
3680 return -1;
3681 }
3682
3683 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
3684 // spinner + text + subText
3685 container->nbChildren = 3;
3686 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
3687
3688 container->obj.area.width = AVAILABLE_WIDTH;
3689 container->layout = VERTICAL;
3690 container->obj.alignment = CENTER;
3691
3692 // create spinner
3693 spinner = (nbgl_spinner_t *) nbgl_objPoolGet(SPINNER, layoutInt->layer);
3694 spinner->position = initPosition;
3695 spinner->obj.alignment = TOP_MIDDLE;
3696 // set this new spinner as child of the container
3697 container->children[0] = (nbgl_obj_t *) spinner;
3698
3699 // update container height
3700 container->obj.area.height += SPINNER_HEIGHT;
3701
3702 // create text area
3703 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3704 textArea->textColor = BLACK;
3705 textArea->text = PIC(text);
3706 textArea->textAlignment = CENTER;
3707 textArea->fontId = (subText != NULL) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
3708 textArea->wrapping = true;
3709 textArea->obj.alignmentMarginY = SPINNER_TEXT_MARGIN;
3710 textArea->obj.alignTo = (nbgl_obj_t *) spinner;
3711 textArea->obj.alignment = BOTTOM_MIDDLE;
3712 textArea->obj.area.width = AVAILABLE_WIDTH;
3713 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3714 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3715 textArea->style = NO_STYLE;
3716
3717 // update container height
3718 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
3719
3720 // set this text as child of the container
3721 container->children[1] = (nbgl_obj_t *) textArea;
3722
3723 if (subText != NULL) {
3724 nbgl_text_area_t *subTextArea;
3725 // create sub-text area
3726 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3727 subTextArea->textColor = BLACK;
3728 subTextArea->text = PIC(subText);
3729 subTextArea->textAlignment = CENTER;
3730 subTextArea->fontId = SMALL_REGULAR_FONT;
3731 subTextArea->wrapping = true;
3732 subTextArea->obj.alignmentMarginY = SPINNER_INTER_TEXTS_MARGIN;
3733 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
3734 subTextArea->obj.alignment = BOTTOM_MIDDLE;
3735 subTextArea->obj.area.width = AVAILABLE_WIDTH;
3736 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
3737 subTextArea->text,
3738 subTextArea->obj.area.width,
3739 subTextArea->wrapping);
3740 subTextArea->style = NO_STYLE;
3741
3742 // update container height
3743 container->obj.area.height
3744 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
3745
3746 // set thissub-text as child of the container
3747 container->children[2] = (nbgl_obj_t *) subTextArea;
3748 }
3749 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
3750
3751 if (initPosition != SPINNER_FIXED) {
3752 // update ticker to update the spinner periodically
3753 nbgl_screenTickerConfiguration_t tickerCfg = {0};
3754
3755 tickerCfg.tickerIntervale = SPINNER_REFRESH_PERIOD; // ms
3756 tickerCfg.tickerValue = SPINNER_REFRESH_PERIOD; // ms
3757 tickerCfg.tickerCallback = &spinnerTickerCallback;
3758 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
3759 }
3760
3761 return 0;
3762}
3763
3776 const char *text,
3777 const char *subText,
3778 uint8_t position)
3779{
3780 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3781 nbgl_container_t *container;
3782 nbgl_text_area_t *textArea;
3783 nbgl_spinner_t *spinner;
3784 int ret = 0;
3785
3786 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutUpdateSpinner():\n");
3787 if ((layout == NULL) || (layoutInt->container->nbChildren == 0)) {
3788 return -1;
3789 }
3790
3791 container = (nbgl_container_t *) layoutInt->container->children[0];
3792 if ((container->obj.type != CONTAINER) || (container->nbChildren < 2)) {
3793 return -1;
3794 }
3795
3796 spinner = (nbgl_spinner_t *) container->children[0];
3797 if (spinner->obj.type != SPINNER) {
3798 return -1;
3799 }
3800 // if position is different, redraw
3801 if (spinner->position != position) {
3802 spinner->position = position;
3803 nbgl_objDraw((nbgl_obj_t *) spinner);
3804 ret = 1;
3805 }
3806
3807 // update text area if necessary
3808 textArea = (nbgl_text_area_t *) container->children[1];
3809 if (textArea->obj.type != TEXT_AREA) {
3810 return -1;
3811 }
3812 const char *newText = PIC(text);
3813 size_t newTextLen = strlen(newText);
3814 // if text is different, redraw (don't use strcmp because it crashes with Rust SDK)
3815 if ((newTextLen != strlen(textArea->text)) || memcmp(textArea->text, newText, newTextLen)) {
3816 textArea->text = newText;
3817 nbgl_objDraw((nbgl_obj_t *) textArea);
3818 ret = 2;
3819 }
3820
3821 if (subText != NULL) {
3822 nbgl_text_area_t *subTextArea;
3823
3824 if (container->nbChildren != 3) {
3825 return -1;
3826 }
3827 subTextArea = (nbgl_text_area_t *) container->children[2];
3828 if (subTextArea->obj.type != TEXT_AREA) {
3829 return -1;
3830 }
3831 const char *newSubText = PIC(subText);
3832 size_t newSubTextLen = strlen(newSubText);
3833 // if text is different, redraw
3834 if ((newSubTextLen != strlen(subTextArea->text))
3835 || memcmp(subTextArea->text, newSubText, newSubTextLen)) {
3836 subTextArea->text = newSubText;
3837 nbgl_objDraw((nbgl_obj_t *) subTextArea);
3838 ret = 2;
3839 }
3840 }
3841
3842 return ret;
3843}
3844
3852{
3853 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3854
3855 if (layout == NULL) {
3856 return -1;
3857 }
3858 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutDraw(): layout->isUsed = %d\n", layout->isUsed);
3859 if (layout->tapText) {
3860 // set this new container as child of main container
3861 layoutAddObject(layout, (nbgl_obj_t *) layout->tapText);
3862 }
3863#ifdef TARGET_STAX
3864 if (layout->withLeftBorder == true) {
3865 // draw now the line
3866 nbgl_line_t *line = createLeftVerticalLine(layout->layer);
3867 layout->children[LEFT_BORDER_INDEX] = (nbgl_obj_t *) line;
3868 }
3869#endif // TARGET_STAX
3871
3872 return 0;
3873}
3874
3882{
3883 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3884 LOG_DEBUG(PAGE_LOGGER, "nbgl_layoutRelease(): \n");
3885 if ((layout == NULL) || (!layout->isUsed)) {
3886 return -1;
3887 }
3888 // if modal
3889 if (layout->modal) {
3890 nbgl_screenPop(layout->layer);
3891 // if this layout was on top, use its bottom layout as top
3892 if (layout == topLayout) {
3893 topLayout = layout->bottom;
3894 topLayout->top = NULL;
3895 }
3896 else {
3897 // otherwise connect top to bottom
3898 layout->bottom->top = layout->top;
3899 layout->top->bottom = layout->bottom;
3900 }
3901 }
3902 layout->isUsed = false;
3903 return 0;
3904}
3905
3906#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
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
uint16_t nbgl_getSingleLineTextWidth(nbgl_font_id_e fontId, const char *text)
nbgl_font_id_e
Definition nbgl_fonts.h:141
uint8_t nbgl_getFontHeight(nbgl_font_id_e fontId)
uint16_t nbgl_getTextWidth(nbgl_font_id_e fontId, const char *text)
uint16_t nbgl_getTextHeightInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
uint16_t nbgl_getTextNbLinesInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
const nbgl_font_t * nbgl_getFont(nbgl_font_id_e fontId)
uint8_t nbgl_getFontLineHeight(nbgl_font_id_e fontId)
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:44
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_layoutAddTextContent(nbgl_layout_t *layout, nbgl_layoutTextContent_t *content)
Creates in the main container three text areas:
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:41
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
void layoutUpdateCallbackObjToken(nbgl_layoutInternal_t *layout, nbgl_obj_t *obj, uint8_t token)
Update the token associated with a specific object in a layout.
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
#define VALUE_ICON_INTERVALE
@ 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
#define TAG_VALUE_INTERVALE
@ 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
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.
bool keyboardSwipeCallback(nbgl_layoutInternal_t *layoutInt, nbgl_obj_t *obj, nbgl_touchType_t eventType)
@ UP_FOOTER_INDEX
@ FOOTER_INDEX
@ HEADER_INDEX
@ TOP_RIGHT_BUTTON_INDEX
@ MAIN_CONTAINER_INDEX
@ LEFT_BORDER_INDEX
@ NB_MAX_SCREEN_CHILDREN
#define LAYOUT_OBJ_POOL_LEN
Max number of complex objects with callback retrievable from pool.
bool layoutNavigationCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType, uint8_t nbPages, uint8_t *activePage)
function to be called when any of the controls in navigation bar is touched
API to draw all basic graphic objects.
struct PACKED__ nbgl_line_s nbgl_line_t
struct to represent a vertical or horizontal line
struct PACKED__ nbgl_navigation_bar_s nbgl_page_indicator_t
struct to represent a navigation bar (PAGE_INDICATOR type) There can be up to 5 page indicators,...
struct PACKED__ nbgl_radio_s nbgl_radio_t
struct to represent a radio button (RADIO_BUTTON type)
#define NB_SPINNER_POSITIONS
Definition nbgl_obj.h:271
#define LIGHT_TEXT_COLOR
Definition nbgl_obj.h:292
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:354
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)
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)
#define SWIPE_MASK
Definition nbgl_obj.h:302
nbgl_obj_t * nbgl_objPoolGet(nbgl_obj_type_t type, uint8_t layer)
#define INACTIVE_ARROW_COLOR
Definition nbgl_obj.h:290
void nbgl_refreshSpecial(nbgl_refresh_mode_t mode)
struct PACKED__ nbgl_image_s nbgl_image_t
struct to represent an image (IMAGE type)
#define INACTIVE_SMALL_FONT
Definition nbgl_obj.h:291
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:288
#define INACTIVE_TEXT_COLOR
Definition nbgl_obj.h:289
void nbgl_refreshSpecialWithPostRefresh(nbgl_refresh_mode_t mode, nbgl_post_refresh_t post_refresh)
@ CHOICE_1_ID
Definition nbgl_obj.h:690
@ TOP_RIGHT_BUTTON_ID
Definition nbgl_obj.h:686
@ LONG_PRESS_BUTTON_ID
Definition nbgl_obj.h:699
@ CONTROLS_ID
Definition nbgl_obj.h:701
@ RIGHT_BUTTON_ID
Definition nbgl_obj.h:684
@ WHOLE_SCREEN_ID
Definition nbgl_obj.h:685
@ BOTTOM_BUTTON_ID
Definition nbgl_obj.h:682
@ SINGLE_BUTTON_ID
Definition nbgl_obj.h:688
@ BACK_BUTTON_ID
Definition nbgl_obj.h:687
@ VALUE_BUTTON_1_ID
Definition nbgl_obj.h:696
@ TIP_BOX_ID
Definition nbgl_obj.h:700
@ EXTRA_BUTTON_ID
Definition nbgl_obj.h:689
@ CHOICE_2_ID
Definition nbgl_obj.h:691
#define BUTTON_DIAMETER
Definition nbgl_obj.h:104
struct PACKED__ nbgl_container_s nbgl_container_t
struct to represent a container (CONTAINER type)
#define BUTTON_WIDTH
Definition nbgl_obj.h:109
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)
int nbgl_screenPush(nbgl_obj_t ***elements, uint8_t nbElements, const nbgl_screenTickerConfiguration_t *ticker, nbgl_touchCallback_t touchCallback)
void nbgl_wait_pipeline(void)
int nbgl_screenUpdateTicker(uint8_t screenIndex, const nbgl_screenTickerConfiguration_t *ticker)
int nbgl_screenPop(uint8_t screenIndex)
struct PACKED__ nbgl_screenTickerConfiguration_s nbgl_screenTickerConfiguration_t
struct to configure a screen layer
int nbgl_screenUpdateBackgroundColor(uint8_t screenIndex, color_t color)
void nbgl_screenRedraw(void)
uint32_t nbgl_touchGetTouchDuration(nbgl_obj_t *obj)
common types for Graphical Library
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:352
@ POST_REFRESH_FORCE_POWER_ON
Force screen power on after refresh.
Definition nbgl_types.h:353
@ POST_REFRESH_FORCE_POWER_ON_WITH_PIPELINE
Force screen power on and enable pipeline.
Definition nbgl_types.h:354
nbgl_touchType_t
The different types of Touchscreen events.
Definition nbgl_types.h:259
@ SWIPED_LEFT
Definition nbgl_types.h:274
@ SWIPED_UP
Definition nbgl_types.h:271
@ SWIPED_DOWN
Definition nbgl_types.h:272
@ SWIPED_RIGHT
Definition nbgl_types.h:273
@ 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:408
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:283
@ 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:329
@ BLACK_AND_WHITE_FAST_REFRESH
to be used for pure B&W area, when contrast is not priority
Definition nbgl_types.h:330
@ FULL_COLOR_PARTIAL_REFRESH
to be used for small partial refresh (radio buttons, switches)
Definition nbgl_types.h:327
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:420
uint8_t nbIcons
number of icons in icons array
Definition nbgl_types.h:418
const nbgl_icon_details_t ** icons
array of nbIcons pointers on icons
Definition nbgl_types.h:417
nbgl_parsingType_t parsing
Definition nbgl_types.h:419
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_contentValueExt_t * extension
if not NULL, gives additional info on value field
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
const char * aliasSubName
string displayed under alias and in details view
structure defining an ASCII font
Definition nbgl_fonts.h:86
uint8_t line_height
height of a line for all characters in pixels
Definition nbgl_fonts.h:91
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 for Text content, to be set with nbgl_layoutAddTextContent().
const char * descriptions[NB_MAX_DESCRIPTIONS]
uint8_t nbDescriptions
number of used descriptions in above array
const char * info
description at bottom (in small gray)
const char * title
main text (in large bold font)
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