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