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