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#ifdef TARGET_APEX
425 // for Apex, it's necessary to redraw the dotted line, that has been partially wiped
426 nbgl_line_t *line = (nbgl_line_t *) container->children[2];
427 nbgl_objDraw((nbgl_obj_t *) line);
428#endif // TARGET_APEX
430 }
431}
432
433// callback for radio button touch
434static void radioTouchCallback(nbgl_obj_t *obj,
435 nbgl_touchType_t eventType,
436 nbgl_layoutInternal_t *layout)
437{
438 uint8_t i = NB_MAX_LAYOUTS, radioIndex = 0, foundRadio = 0xFF, foundRadioIndex;
439
440 if (eventType != TOUCHED) {
441 return;
442 }
443
444 i = 0;
445 // parse all objs to find all containers of radio buttons
446 while (i < layout->nbUsedCallbackObjs) {
447 if ((obj == (nbgl_obj_t *) layout->callbackObjPool[i].obj)
448 && (layout->callbackObjPool[i].obj->type == CONTAINER)) {
449 nbgl_radio_t *radio
450 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
451 ->children[1];
452 nbgl_text_area_t *textArea
454 ->children[0];
455 foundRadio = i;
456 foundRadioIndex = radioIndex;
457 // set text as active (black and bold)
458 textArea->textColor = BLACK;
459 textArea->fontId = SMALL_BOLD_FONT;
460 // ensure that radio button is ON
461 radio->state = ON_STATE;
462 // redraw container
463 nbgl_objDraw((nbgl_obj_t *) obj);
464 }
465 else if ((layout->callbackObjPool[i].obj->type == CONTAINER)
466 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->nbChildren == 2)
467 && (((nbgl_container_t *) layout->callbackObjPool[i].obj)->children[1]->type
468 == RADIO_BUTTON)) {
469 nbgl_radio_t *radio
470 = (nbgl_radio_t *) ((nbgl_container_t *) layout->callbackObjPool[i].obj)
471 ->children[1];
472 nbgl_text_area_t *textArea
474 ->children[0];
475 radioIndex++;
476 // set to OFF the one that was in ON
477 if (radio->state == ON_STATE) {
478 radio->state = OFF_STATE;
479 // set text it as inactive (gray and normal)
480 textArea->textColor = LIGHT_TEXT_COLOR;
481 textArea->fontId = SMALL_REGULAR_FONT;
482 // redraw container
484 }
485 }
486 i++;
487 }
488 // call callback after redraw to avoid asynchronicity
489 if (foundRadio != 0xFF) {
490 if (layout->callback != NULL) {
491#ifdef HAVE_PIEZO_SOUND
492 if (layout->callbackObjPool[foundRadio].tuneId < NBGL_NO_TUNE) {
493 os_io_seph_cmd_piezo_play_tune(layout->callbackObjPool[foundRadio].tuneId);
494 }
496#endif // HAVE_PIEZO_SOUND
497 layout->callback(layout->callbackObjPool[foundRadio].token, foundRadioIndex);
498 }
499 }
500}
501
502// callback for spinner ticker
503static void spinnerTickerCallback(void)
504{
505 nbgl_spinner_t *spinner;
506 uint8_t i = 0;
507
508 if (!topLayout || !topLayout->isUsed) {
509 return;
510 }
511
512 // get index of obj
513 while (i < topLayout->container->nbChildren) {
514 if (topLayout->container->children[i]->type == CONTAINER) {
515 nbgl_container_t *container = (nbgl_container_t *) topLayout->container->children[i];
516 if (container->nbChildren && (container->children[0]->type == SPINNER)) {
517 spinner = (nbgl_spinner_t *) container->children[0];
518 spinner->position++;
519 // there are only NB_SPINNER_POSITIONS positions
520 if (spinner->position == NB_SPINNER_POSITIONS) {
521 spinner->position = 0;
522 }
523 nbgl_objDraw((nbgl_obj_t *) spinner);
525 return;
526 }
527 }
528 i++;
529 }
530}
531
532// callback for animation ticker
533static void animTickerCallback(void)
534{
535 nbgl_image_t *image;
536 uint8_t i = 0;
537 nbgl_layoutInternal_t *layout = topLayout;
538
539 if (!layout || !layout->isUsed || (layout->animation == NULL)) {
540 return;
541 }
542
543 // get index of image obj
544 while (i < layout->container->nbChildren) {
545 if (layout->container->children[i]->type == CONTAINER) {
546 nbgl_container_t *container = (nbgl_container_t *) layout->container->children[i];
547 if (container->children[1]->type == IMAGE) {
548 image = (nbgl_image_t *) container->children[1];
549 if (layout->animation->parsing == LOOP_PARSING) {
550 if (layout->iconIdxInAnim == (layout->animation->nbIcons - 1)) {
551 layout->iconIdxInAnim = 0;
552 }
553 else {
554 layout->iconIdxInAnim++;
555 }
556 }
557 else {
558 // Flip incrementAnim when reaching upper or lower limit
559 if ((layout->incrementAnim)
560 && (layout->iconIdxInAnim >= layout->animation->nbIcons - 1)) {
561 layout->incrementAnim = false;
562 }
563 else if (layout->iconIdxInAnim == 0) {
564 layout->incrementAnim = true;
565 }
566
567 // Increase / Decrease index according to incrementAnim
568 if (layout->incrementAnim) {
569 layout->iconIdxInAnim++;
570 }
571 else {
572 layout->iconIdxInAnim--;
573 }
574 }
575 image->buffer = layout->animation->icons[layout->iconIdxInAnim];
576 nbgl_objDraw((nbgl_obj_t *) image);
579 return;
580 }
581 }
582 i++;
583 }
584}
585
586static nbgl_line_t *createHorizontalLine(uint8_t layer)
587{
588 nbgl_line_t *line;
589
590 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
591 line->lineColor = LIGHT_GRAY;
592 line->obj.area.width = SCREEN_WIDTH;
593 line->obj.area.height = 1;
594 line->direction = HORIZONTAL;
595 line->thickness = 1;
596 return line;
597}
598
599#ifdef TARGET_STAX
600static nbgl_line_t *createLeftVerticalLine(uint8_t layer)
601{
602 nbgl_line_t *line;
603
604 line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layer);
605 line->lineColor = LIGHT_GRAY;
606 line->obj.area.width = 1;
607 line->obj.area.height = SCREEN_HEIGHT;
608 line->direction = VERTICAL;
609 line->thickness = 1;
610 line->obj.alignment = MID_LEFT;
611 return line;
612}
613#endif // TARGET_STAX
614
615// function adding a layout object in the callbackObjPool array for the given layout, and
616// configuring it
618 nbgl_obj_t *obj,
619 uint8_t token,
620 tune_index_e tuneId)
621{
622 layoutObj_t *layoutObj = NULL;
623
624 if (layout->nbUsedCallbackObjs < (LAYOUT_OBJ_POOL_LEN - 1)) {
625 layoutObj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
626 layout->nbUsedCallbackObjs++;
627 layoutObj->obj = obj;
628 layoutObj->token = token;
629 layoutObj->tuneId = tuneId;
630 }
631 else {
632 LOG_FATAL(LAYOUT_LOGGER, "layoutAddCallbackObj: no more callback obj\n");
633 }
634
635 return layoutObj;
636}
637
645{
646 if (layout->container->nbChildren == NB_MAX_CONTAINER_CHILDREN) {
647 LOG_FATAL(LAYOUT_LOGGER, "layoutAddObject(): No more object\n");
648 }
649 layout->container->children[layout->container->nbChildren] = obj;
650 layout->container->nbChildren++;
651}
652
662static int addSwipeInternal(nbgl_layoutInternal_t *layoutInt,
663 uint16_t swipesMask,
664 nbgl_swipe_usage_t usage,
665 uint8_t token,
666 tune_index_e tuneId)
667{
668 layoutObj_t *obj;
669
670 if ((swipesMask & SWIPE_MASK) == 0) {
671 return -1;
672 }
673
674 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) layoutInt->container, token, tuneId);
675 if (obj == NULL) {
676 return -1;
677 }
678 layoutInt->container->obj.touchMask = swipesMask;
679 layoutInt->swipeUsage = usage;
680
681 return 0;
682}
683
691static nbgl_container_t *addListItem(nbgl_layoutInternal_t *layoutInt, const listItem_t *itemDesc)
692{
693 layoutObj_t *obj;
694 nbgl_text_area_t *textArea = NULL;
695 nbgl_container_t *container;
696 color_t color = ((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == OFF_STATE))
698 : BLACK;
699 nbgl_font_id_e fontId
700 = (((itemDesc->type == TOUCHABLE_BAR_ITEM) || (itemDesc->type == SWITCH_ITEM))
701 && (itemDesc->state == OFF_STATE))
703 : SMALL_BOLD_FONT;
704
705 LOG_DEBUG(LAYOUT_LOGGER, "addListItem():\n");
706
707 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
709 layoutInt, (nbgl_obj_t *) container, itemDesc->token, itemDesc->tuneId);
710 if (obj == NULL) {
711 return NULL;
712 }
713 obj->index = itemDesc->index;
714
715 // get container children (up to 4: text + left+right icons + sub text)
716 container->children = nbgl_containerPoolGet(4, layoutInt->layer);
717 container->nbChildren = 0;
718
719 container->obj.area.width = AVAILABLE_WIDTH;
720 container->obj.area.height
721 = LIST_ITEM_MIN_TEXT_HEIGHT
722 + 2 * (itemDesc->large ? LIST_ITEM_PRE_HEADING_LARGE : LIST_ITEM_PRE_HEADING);
723 container->layout = HORIZONTAL;
724 container->obj.alignmentMarginX = BORDER_MARGIN;
725 container->obj.alignment = NO_ALIGNMENT;
726 container->obj.alignTo = NULL;
727 // the bar can only be touched if not inactive AND if one of the icon is present
728 // otherwise it is seen as a title
729 if (((itemDesc->type == TOUCHABLE_BAR_ITEM) && (itemDesc->state == ON_STATE))
730 || (itemDesc->type == SWITCH_ITEM)) {
731 container->obj.touchMask = (1 << TOUCHED);
732 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
733 nbTouchableControls++;
734 }
735
736 // allocate main text if not NULL
737 if (itemDesc->text != NULL) {
738 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
739 textArea->textColor = color;
740 textArea->text = PIC(itemDesc->text);
741 textArea->onDrawCallback = NULL;
742 textArea->fontId = fontId;
743 textArea->wrapping = true;
744 textArea->obj.area.width = container->obj.area.width;
745 if (itemDesc->iconLeft != NULL) {
746 // reduce text width accordingly
747 textArea->obj.area.width
748 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE;
749 }
750 if (itemDesc->iconRight != NULL) {
751 // reduce text width accordingly
752 textArea->obj.area.width
753 -= ((nbgl_icon_details_t *) PIC(itemDesc->iconRight))->width + BAR_INTERVALE;
754 }
755 else if (itemDesc->type == SWITCH_ITEM) {
756 textArea->obj.area.width -= SWITCH_ICON.width + BAR_INTERVALE;
757 }
758 textArea->obj.area.height = MAX(
759 LIST_ITEM_MIN_TEXT_HEIGHT,
761 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping));
762 textArea->style = NO_STYLE;
763 textArea->obj.alignment = TOP_LEFT;
764 textArea->obj.alignmentMarginY
765 = itemDesc->large ? LIST_ITEM_PRE_HEADING_LARGE : LIST_ITEM_PRE_HEADING;
766 if (textArea->obj.area.height > LIST_ITEM_MIN_TEXT_HEIGHT) {
767 textArea->obj.alignmentMarginY
768 -= (textArea->obj.area.height - LIST_ITEM_MIN_TEXT_HEIGHT) / 2;
769 }
770 textArea->textAlignment = MID_LEFT;
771 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
772 container->nbChildren++;
773 }
774
775 // allocate left icon if present
776 if (itemDesc->iconLeft != NULL) {
777 nbgl_image_t *imageLeft = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
778 imageLeft->foregroundColor = color;
779 imageLeft->buffer = PIC(itemDesc->iconLeft);
780 // align at the left of text
781 imageLeft->obj.alignment = MID_LEFT;
782 imageLeft->obj.alignTo = (nbgl_obj_t *) textArea;
783 imageLeft->obj.alignmentMarginX = BAR_INTERVALE;
784 container->children[container->nbChildren] = (nbgl_obj_t *) imageLeft;
785 container->nbChildren++;
786
787 if (textArea != NULL) {
788 textArea->obj.alignmentMarginX = imageLeft->buffer->width + BAR_INTERVALE;
789 }
790 }
791 // allocate right icon if present
792 if (itemDesc->iconRight != NULL) {
793 nbgl_image_t *imageRight = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
794 imageRight->foregroundColor = color;
795 imageRight->buffer = PIC(itemDesc->iconRight);
796 // align at the right of text
797 imageRight->obj.alignment = MID_RIGHT;
798 imageRight->obj.alignmentMarginX = BAR_INTERVALE;
799 imageRight->obj.alignTo = (nbgl_obj_t *) textArea;
800
801 container->children[container->nbChildren] = (nbgl_obj_t *) imageRight;
802 container->nbChildren++;
803 }
804 else if (itemDesc->type == SWITCH_ITEM) {
805 nbgl_switch_t *switchObj = (nbgl_switch_t *) nbgl_objPoolGet(SWITCH, layoutInt->layer);
806 switchObj->onColor = BLACK;
807 switchObj->offColor = LIGHT_GRAY;
808 switchObj->state = itemDesc->state;
809 switchObj->obj.alignment = MID_RIGHT;
810 switchObj->obj.alignmentMarginX = BAR_INTERVALE;
811 switchObj->obj.alignTo = (nbgl_obj_t *) textArea;
812
813 container->children[container->nbChildren] = (nbgl_obj_t *) switchObj;
814 container->nbChildren++;
815 }
816
817 if (itemDesc->subText != NULL) {
818 nbgl_text_area_t *subTextArea
820
821 subTextArea->textColor = color;
822 subTextArea->text = PIC(itemDesc->subText);
823 subTextArea->textAlignment = MID_LEFT;
824 subTextArea->fontId = SMALL_REGULAR_FONT;
825 subTextArea->style = NO_STYLE;
826 subTextArea->wrapping = true;
827 if (itemDesc->text != NULL) {
828 subTextArea->obj.alignment = BOTTOM_LEFT;
829 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
830 subTextArea->obj.alignmentMarginY = LIST_ITEM_HEADING_SUB_TEXT;
831 }
832 else {
833 subTextArea->obj.alignment = TOP_LEFT;
834 subTextArea->obj.alignmentMarginY = SUB_HEADER_MARGIN;
835 container->obj.area.height = SUB_HEADER_MARGIN;
836 }
837 if (itemDesc->iconLeft != NULL) {
838 subTextArea->obj.alignmentMarginX
839 = -(((nbgl_icon_details_t *) PIC(itemDesc->iconLeft))->width + BAR_INTERVALE);
840 }
841 subTextArea->obj.area.width = container->obj.area.width;
842 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
843 subTextArea->text,
844 subTextArea->obj.area.width,
845 subTextArea->wrapping);
846 container->children[container->nbChildren] = (nbgl_obj_t *) subTextArea;
847 container->nbChildren++;
848 container->obj.area.height
849 += subTextArea->obj.area.height + subTextArea->obj.alignmentMarginY;
850 }
851 // set this new container as child of main container
852 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
853
854 return container;
855}
856
865static nbgl_container_t *addContentCenter(nbgl_layoutInternal_t *layoutInt,
866 const nbgl_contentCenter_t *info)
867{
868 nbgl_container_t *container;
869 nbgl_text_area_t *textArea = NULL;
870 nbgl_image_t *image = NULL;
871 uint16_t fullHeight = 0;
872
873 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
874
875 // get container children
876 container->nbChildren = 0;
877 // the max number is: icon + anim + title + small title + description + sub-text
878 container->children = nbgl_containerPoolGet(6, layoutInt->layer);
879
880 // add icon or animation if present
881 if (info->icon != NULL) {
882 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
883 image->foregroundColor = (layoutInt->invertedColors) ? WHITE : BLACK;
884 image->buffer = PIC(info->icon);
885 image->obj.alignment = TOP_MIDDLE;
886 image->obj.alignmentMarginY = info->iconHug;
887
888 fullHeight += image->buffer->height + info->iconHug;
889 container->children[container->nbChildren] = (nbgl_obj_t *) image;
890 container->nbChildren++;
891
892 if (info->illustrType == ANIM_ILLUSTRATION) {
893 nbgl_image_t *anim;
894 anim = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
895 anim->foregroundColor = (layoutInt->invertedColors) ? WHITE : BLACK;
896 anim->buffer = PIC(info->animation->icons[0]);
897 anim->obj.alignment = TOP_MIDDLE;
898 anim->obj.alignmentMarginY = info->iconHug + info->animOffsetY;
899 anim->obj.alignmentMarginX = info->animOffsetX;
900
901 container->children[container->nbChildren] = (nbgl_obj_t *) anim;
902 container->nbChildren++;
903
904 layoutInt->animation = info->animation;
905 layoutInt->incrementAnim = true;
906 layoutInt->iconIdxInAnim = 0;
907
908 // update ticker to update the animation periodically
910
911 tickerCfg.tickerIntervale = info->animation->delayMs; // ms
912 tickerCfg.tickerValue = info->animation->delayMs; // ms
913 tickerCfg.tickerCallback = &animTickerCallback;
914 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
915 }
916 }
917 // add title if present
918 if (info->title != NULL) {
919 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
920 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
921 textArea->text = PIC(info->title);
922 textArea->textAlignment = CENTER;
923 textArea->fontId = LARGE_MEDIUM_FONT;
924 textArea->wrapping = true;
925 textArea->obj.area.width = AVAILABLE_WIDTH;
926 textArea->obj.area.height = nbgl_getTextHeightInWidth(
927 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
928
929 // if not the first child, put on bottom of the previous, with a margin
930 if (container->nbChildren > 0) {
931 textArea->obj.alignment = BOTTOM_MIDDLE;
932 textArea->obj.alignTo = (nbgl_obj_t *) image;
933 textArea->obj.alignmentMarginY = ICON_TITLE_MARGIN + info->iconHug;
934 }
935 else {
936 textArea->obj.alignment = TOP_MIDDLE;
937 }
938
939 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
940
941 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
942 container->nbChildren++;
943 }
944 // add small title if present
945 if (info->smallTitle != NULL) {
946 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
947 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
948 textArea->text = PIC(info->smallTitle);
949 textArea->textAlignment = CENTER;
950 textArea->fontId = SMALL_BOLD_FONT;
951 textArea->wrapping = true;
952 textArea->obj.area.width = AVAILABLE_WIDTH;
953 textArea->obj.area.height = nbgl_getTextHeightInWidth(
954 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
955
956 // if not the first child, put on bottom of the previous, with a margin
957 if (container->nbChildren > 0) {
958 textArea->obj.alignment = BOTTOM_MIDDLE;
959 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
960 textArea->obj.alignmentMarginY = VERTICAL_BORDER_MARGIN;
961 if (container->children[container->nbChildren - 1]->type == IMAGE) {
962 textArea->obj.alignmentMarginY = VERTICAL_BORDER_MARGIN + info->iconHug;
963 }
964 else {
965 textArea->obj.alignmentMarginY = TITLE_DESC_MARGIN;
966 }
967 }
968 else {
969 textArea->obj.alignment = TOP_MIDDLE;
970 }
971
972 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
973
974 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
975 container->nbChildren++;
976 }
977 // add description if present
978 if (info->description != NULL) {
979 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
980 textArea->textColor = (layoutInt->invertedColors) ? WHITE : BLACK;
981 textArea->text = PIC(info->description);
982 textArea->textAlignment = CENTER;
983 textArea->fontId = SMALL_REGULAR_FONT;
984 textArea->wrapping = true;
985 textArea->obj.area.width = AVAILABLE_WIDTH;
986 textArea->obj.area.height = nbgl_getTextHeightInWidth(
987 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
988
989 // if not the first child, put on bottom of the previous, with a margin
990 if (container->nbChildren > 0) {
991 textArea->obj.alignment = BOTTOM_MIDDLE;
992 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
993 if (container->children[container->nbChildren - 1]->type == TEXT_AREA) {
994 // if previous element is text, only space of 16 px
995 textArea->obj.alignmentMarginY = TITLE_DESC_MARGIN;
996 }
997 else {
998 textArea->obj.alignmentMarginY = ICON_TITLE_MARGIN + info->iconHug;
999 }
1000 }
1001 else {
1002 textArea->obj.alignment = TOP_MIDDLE;
1003 }
1004
1005 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1006
1007 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1008 container->nbChildren++;
1009 }
1010 // add sub-text if present
1011 if (info->subText != NULL) {
1012 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1013 textArea->textColor = LIGHT_TEXT_COLOR;
1014 textArea->text = PIC(info->subText);
1015 textArea->textAlignment = CENTER;
1016 textArea->fontId = SMALL_REGULAR_FONT;
1017 textArea->wrapping = true;
1018 textArea->obj.area.width = AVAILABLE_WIDTH;
1019 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1020 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1021 // sub-text is included in a hug of 8px
1022 textArea->obj.area.height += 2 * 8;
1023 // if not the first child, put on bottom of the previous, with a margin
1024 if (container->nbChildren > 0) {
1025 textArea->obj.alignment = BOTTOM_MIDDLE;
1026 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1027 textArea->obj.alignmentMarginY = 16;
1028 if (container->children[container->nbChildren - 1]->type == IMAGE) {
1029 textArea->obj.alignmentMarginY += info->iconHug;
1030 }
1031 }
1032 else {
1033 textArea->obj.alignment = TOP_MIDDLE;
1034 }
1035
1036 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1037
1038 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1039 container->nbChildren++;
1040 }
1041 container->layout = VERTICAL;
1042 container->obj.alignment = CENTER;
1043 container->obj.area.width = AVAILABLE_WIDTH;
1044 container->obj.area.height = fullHeight;
1045 if (info->padding) {
1046 container->obj.area.height += 40;
1047 }
1048
1049 // set this new container as child of main container
1050 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1051
1052 return container;
1053}
1054
1055/**********************
1056 * GLOBAL FUNCTIONS
1057 **********************/
1058
1066{
1067 nbgl_layoutInternal_t *layout = NULL;
1068
1069 if (description->modal) {
1070 int i;
1071 // find an empty layout in the array of layouts (0 is reserved for background)
1072 for (i = 1; i < NB_MAX_LAYOUTS; i++) {
1073 if (!gLayout[i].isUsed) {
1074 layout = &gLayout[i];
1075 }
1076 }
1077 }
1078 else {
1079 // always use layout 0 for background
1080 layout = &gLayout[0];
1081 if (topLayout == NULL) {
1082 topLayout = layout;
1083 }
1084 }
1085 if (layout == NULL) {
1086 LOG_WARN(LAYOUT_LOGGER, "nbgl_layoutGet(): impossible to get a layout!\n");
1087 return NULL;
1088 }
1089
1090 // reset globals
1091 nbgl_layoutInternal_t *backgroundTop = gLayout[0].top;
1092 memset(layout, 0, sizeof(nbgl_layoutInternal_t));
1093 // link layout to other ones
1094 if (description->modal) {
1095 if (topLayout != NULL) {
1096 // if topLayout already existing, push this new one on top of it
1097 topLayout->top = layout;
1098 layout->bottom = topLayout;
1099 }
1100 else {
1101 // otherwise put it on top of background layout
1102 layout->bottom = &gLayout[0];
1103 gLayout[0].top = layout;
1104 }
1105 topLayout = layout;
1106 }
1107 else {
1108 // restore potentially valid background top layer
1109 gLayout[0].top = backgroundTop;
1110 }
1111
1112 nbTouchableControls = 0;
1113
1114 layout->callback = (nbgl_layoutTouchCallback_t) PIC(description->onActionCallback);
1115 layout->modal = description->modal;
1116 layout->withLeftBorder = description->withLeftBorder;
1117 if (description->modal) {
1118 layout->layer = nbgl_screenPush(&layout->children,
1120 &description->ticker,
1121 (nbgl_touchCallback_t) touchCallback);
1122 }
1123 else {
1124 nbgl_screenSet(&layout->children,
1126 &description->ticker,
1127 (nbgl_touchCallback_t) touchCallback);
1128 layout->layer = 0;
1129 }
1131 layout->container->obj.area.width = SCREEN_WIDTH;
1132 layout->container->obj.area.height = SCREEN_HEIGHT;
1133 layout->container->layout = VERTICAL;
1134 layout->container->children = nbgl_containerPoolGet(NB_MAX_CONTAINER_CHILDREN, layout->layer);
1135 // by default, if no header, main container is aligned on top-left
1136 layout->container->obj.alignment = TOP_LEFT;
1137 // main container is always the second object, leaving space for header
1138 layout->children[MAIN_CONTAINER_INDEX] = (nbgl_obj_t *) layout->container;
1139 layout->isUsed = true;
1140
1141 // if a tap text is defined, make the container tapable and display this text in gray
1142 if (description->tapActionText != NULL) {
1143 layoutObj_t *obj;
1144 const char *tapActionText = PIC(description->tapActionText);
1145
1146 obj = &layout->callbackObjPool[layout->nbUsedCallbackObjs];
1147 layout->nbUsedCallbackObjs++;
1148 obj->obj = (nbgl_obj_t *) layout->container;
1149 obj->token = description->tapActionToken;
1150 obj->tuneId = description->tapTuneId;
1151 layout->container->obj.touchMask = (1 << TOUCHED);
1152 layout->container->obj.touchId = WHOLE_SCREEN_ID;
1153
1154 if (strlen(tapActionText) > 0) {
1155 nbgl_layoutUpFooter_t footerDesc;
1156 footerDesc.type = UP_FOOTER_TEXT;
1157 footerDesc.text.text = tapActionText;
1158 footerDesc.text.token = description->tapActionToken;
1159 footerDesc.text.tuneId = description->tapTuneId;
1160 nbgl_layoutAddUpFooter((nbgl_layout_t *) layout, &footerDesc);
1161 }
1162 }
1163 return (nbgl_layout_t *) layout;
1164}
1165
1178 uint16_t swipesMask,
1179 const char *text,
1180 uint8_t token,
1181 tune_index_e tuneId)
1182{
1183 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1184
1185 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwipe():\n");
1186 if (layout == NULL) {
1187 return -1;
1188 }
1189
1190 if (text) {
1191 // create 'tap to continue' text area
1193 layoutInt->tapText->text = PIC(text);
1194 layoutInt->tapText->textColor = LIGHT_TEXT_COLOR;
1195 layoutInt->tapText->fontId = SMALL_REGULAR_FONT;
1196 layoutInt->tapText->obj.area.width = AVAILABLE_WIDTH;
1197 layoutInt->tapText->obj.area.height = nbgl_getFontLineHeight(layoutInt->tapText->fontId);
1198 layoutInt->tapText->textAlignment = CENTER;
1199 layoutInt->tapText->obj.alignmentMarginY = TAP_TO_CONTINUE_MARGIN;
1200 layoutInt->tapText->obj.alignment = BOTTOM_MIDDLE;
1201 }
1202 return addSwipeInternal(layoutInt, swipesMask, SWIPE_USAGE_CUSTOM, token, tuneId);
1203}
1204
1213{
1214 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1215
1216 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutInvertBackground():\n");
1217 if (layout == NULL) {
1218 return -1;
1219 }
1220 layoutInt->invertedColors = true;
1222
1223 // invert potential "Tap to continue"
1224 if ((layoutInt->upFooterContainer) && (layoutInt->upFooterContainer->nbChildren == 1)
1225 && (layoutInt->upFooterContainer->children[0]->type == TEXT_AREA)) {
1226 nbgl_text_area_t *textArea = (nbgl_text_area_t *) layoutInt->upFooterContainer->children[0];
1227 if (textArea->textColor == LIGHT_TEXT_COLOR) {
1228 textArea->textColor = INACTIVE_COLOR;
1229 }
1230 }
1231
1232 return 0;
1233}
1234
1245 const nbgl_icon_details_t *icon,
1246 uint8_t token,
1247 tune_index_e tuneId)
1248{
1249 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1250 layoutObj_t *obj;
1251 nbgl_button_t *button;
1252
1253 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTopRightButton():\n");
1254 if (layout == NULL) {
1255 return -1;
1256 }
1257 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
1258 obj = layoutAddCallbackObj(layoutInt, (nbgl_obj_t *) button, token, tuneId);
1259 if (obj == NULL) {
1260 return -1;
1261 }
1262
1263 button->obj.area.width = BUTTON_WIDTH;
1264 button->obj.area.height = BUTTON_DIAMETER;
1265 button->radius = BUTTON_RADIUS;
1266 button->obj.alignmentMarginX = BORDER_MARGIN;
1267 button->obj.alignmentMarginY = BORDER_MARGIN;
1268 button->foregroundColor = BLACK;
1269 button->innerColor = WHITE;
1270 button->borderColor = LIGHT_GRAY;
1271 button->obj.touchMask = (1 << TOUCHED);
1272 button->obj.touchId = TOP_RIGHT_BUTTON_ID;
1273 button->icon = PIC(icon);
1274 button->obj.alignment = TOP_RIGHT;
1275
1276 // add to screen
1277 layoutInt->children[TOP_RIGHT_BUTTON_INDEX] = (nbgl_obj_t *) button;
1278
1279 return 0;
1280}
1281
1290{
1291 nbgl_layoutFooter_t footerDesc;
1292 footerDesc.type = FOOTER_NAV;
1293 footerDesc.separationLine = info->withSeparationLine;
1294 footerDesc.navigation.activePage = info->activePage;
1295 footerDesc.navigation.nbPages = info->nbPages;
1296 footerDesc.navigation.withExitKey = info->withExitKey;
1297 footerDesc.navigation.withBackKey = info->withBackKey;
1298 footerDesc.navigation.withPageIndicator = false;
1299 footerDesc.navigation.token = info->token;
1300 footerDesc.navigation.tuneId = info->tuneId;
1301 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1302}
1303
1316 const nbgl_icon_details_t *icon,
1317 uint8_t token,
1318 bool separationLine,
1319 tune_index_e tuneId)
1320{
1321 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddBottomButton():\n");
1322 nbgl_layoutFooter_t footerDesc;
1323 footerDesc.type = FOOTER_SIMPLE_BUTTON;
1324 footerDesc.separationLine = separationLine;
1325 footerDesc.button.fittingContent = false;
1326 footerDesc.button.icon = PIC(icon);
1327 footerDesc.button.text = NULL;
1328 footerDesc.button.token = token;
1329 footerDesc.button.tuneId = tuneId;
1330 footerDesc.button.style = WHITE_BACKGROUND;
1331 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
1332}
1333
1342{
1343 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1344 nbgl_container_t *container;
1345 listItem_t itemDesc = {0};
1346
1347 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar():\n");
1348 if (layout == NULL) {
1349 return -1;
1350 }
1351 // main text is mandatory
1352 if (barLayout->text == NULL) {
1353 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddTouchableBar(): main text is mandatory\n");
1354 }
1355
1356 itemDesc.iconLeft = barLayout->iconLeft;
1357 itemDesc.iconRight = barLayout->iconRight;
1358 itemDesc.text = barLayout->text;
1359 itemDesc.subText = barLayout->subText;
1360 itemDesc.token = barLayout->token;
1361 itemDesc.tuneId = barLayout->tuneId;
1362 itemDesc.state = (barLayout->inactive) ? OFF_STATE : ON_STATE;
1363 itemDesc.large = barLayout->large;
1364 itemDesc.type = TOUCHABLE_BAR_ITEM;
1365 container = addListItem(layoutInt, &itemDesc);
1366
1367 if (container == NULL) {
1368 return -1;
1369 }
1370 return container->obj.area.height;
1371}
1372
1381{
1382 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1383 nbgl_container_t *container;
1384 listItem_t itemDesc = {0};
1385
1386 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSwitch():\n");
1387 if (layout == NULL) {
1388 return -1;
1389 }
1390 // main text is mandatory
1391 if (switchLayout->text == NULL) {
1392 LOG_FATAL(LAYOUT_LOGGER, "nbgl_layoutAddSwitch(): main text is mandatory\n");
1393 }
1394
1395 itemDesc.text = switchLayout->text;
1396 itemDesc.subText = switchLayout->subText;
1397 itemDesc.token = switchLayout->token;
1398 itemDesc.tuneId = switchLayout->tuneId;
1399 itemDesc.state = switchLayout->initState;
1400 itemDesc.large = false;
1401 itemDesc.type = SWITCH_ITEM;
1402 container = addListItem(layoutInt, &itemDesc);
1403
1404 if (container == NULL) {
1405 return -1;
1406 }
1407 return container->obj.area.height;
1408}
1409
1418int nbgl_layoutAddText(nbgl_layout_t *layout, const char *text, const char *subText)
1419{
1420 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1421 nbgl_container_t *container;
1422 listItem_t itemDesc = {0};
1423
1424 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddText():\n");
1425 if (layout == NULL) {
1426 return -1;
1427 }
1428
1429 itemDesc.text = text;
1430 itemDesc.subText = subText;
1431 itemDesc.token = NBGL_INVALID_TOKEN;
1432 itemDesc.tuneId = NBGL_NO_TUNE;
1433 itemDesc.type = TEXT_ITEM;
1434 container = addListItem(layoutInt, &itemDesc);
1435
1436 if (container == NULL) {
1437 return -1;
1438 }
1439 return container->obj.area.height;
1440}
1441
1454 const char *text,
1455 const char *subText,
1456 uint8_t token,
1457 uint8_t index)
1458{
1459 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1460 nbgl_container_t *container;
1461 listItem_t itemDesc = {0};
1462 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextWithAlias():\n");
1463 if (layout == NULL) {
1464 return -1;
1465 }
1466
1467 itemDesc.text = text;
1468 itemDesc.subText = subText;
1469 itemDesc.iconRight = &MINI_PUSH_ICON;
1470 itemDesc.token = token;
1471 itemDesc.tuneId = NBGL_NO_TUNE;
1472 itemDesc.type = TOUCHABLE_BAR_ITEM;
1473 itemDesc.index = index;
1474 itemDesc.state = ON_STATE;
1475 container = addListItem(layoutInt, &itemDesc);
1476
1477 if (container == NULL) {
1478 return -1;
1479 }
1480 return container->obj.area.height;
1481}
1482
1491int nbgl_layoutAddLargeCaseText(nbgl_layout_t *layout, const char *text, bool grayedOut)
1492{
1493 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1494 nbgl_text_area_t *textArea;
1495
1496 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLargeCaseText():\n");
1497 if (layout == NULL) {
1498 return -1;
1499 }
1500 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1501
1502 textArea->textColor = grayedOut ? INACTIVE_TEXT_COLOR : BLACK;
1503 textArea->text = PIC(text);
1504 textArea->textAlignment = MID_LEFT;
1505 textArea->fontId = LARGE_MEDIUM_FONT;
1506 textArea->obj.area.width = AVAILABLE_WIDTH;
1507 textArea->wrapping = true;
1508 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1509 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1510 textArea->style = NO_STYLE;
1511 textArea->obj.alignment = NO_ALIGNMENT;
1512 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1513 if (layoutInt->container->nbChildren == 0) {
1514 // if first object of container, increase the margin from top
1515 textArea->obj.alignmentMarginY += PRE_FIRST_TEXT_MARGIN;
1516 }
1517 else {
1518 textArea->obj.alignmentMarginY = INTER_PARAGRAPHS_MARGIN; // between paragraphs
1519 }
1520
1521 // set this new obj as child of main container
1522 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1523
1524 return 0;
1525}
1526
1540 const char *title,
1541 const char *description,
1542 const char *info)
1543{
1544 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1545 nbgl_text_area_t *textArea;
1546
1547 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTextContent():\n");
1548 if (layout == NULL) {
1549 return -1;
1550 }
1551
1552 // create title
1553 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1554 textArea->textColor = BLACK;
1555 textArea->text = PIC(title);
1556 textArea->textAlignment = MID_LEFT;
1557 textArea->fontId = LARGE_MEDIUM_FONT;
1558 textArea->style = NO_STYLE;
1559 textArea->wrapping = true;
1560 textArea->obj.alignment = NO_ALIGNMENT;
1561 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1562 textArea->obj.alignmentMarginY = PRE_TITLE_MARGIN;
1563 textArea->obj.area.width = AVAILABLE_WIDTH;
1564 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1565 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1566 // set this new obj as child of main container
1567 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1568
1569 // create description
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 // create info on the bottom
1587 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1588 textArea->textColor = LIGHT_TEXT_COLOR;
1589 textArea->text = PIC(info);
1590 textArea->fontId = SMALL_REGULAR_FONT;
1591 textArea->style = NO_STYLE;
1592 textArea->wrapping = true;
1593 textArea->obj.area.width = AVAILABLE_WIDTH;
1594 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1595 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1596 textArea->textAlignment = MID_LEFT;
1597 textArea->obj.alignment = BOTTOM_LEFT;
1598 textArea->obj.alignmentMarginX = BORDER_MARGIN;
1599 textArea->obj.alignmentMarginY = 40;
1600 // set this new obj as child of main container
1601 layoutAddObject(layoutInt, (nbgl_obj_t *) textArea);
1602
1603 return layoutInt->container->obj.area.height;
1604}
1605
1614{
1615 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1616 layoutObj_t *obj;
1617 uint8_t i;
1618
1619 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddRadioChoice():\n");
1620 if (layout == NULL) {
1621 return -1;
1622 }
1623 for (i = 0; i < choices->nbChoices; i++) {
1624 nbgl_container_t *container;
1625 nbgl_text_area_t *textArea;
1626 nbgl_radio_t *button;
1627 nbgl_line_t *line;
1628
1629 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1630 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1631 button = (nbgl_radio_t *) nbgl_objPoolGet(RADIO_BUTTON, layoutInt->layer);
1632
1634 layoutInt, (nbgl_obj_t *) container, choices->token, choices->tuneId);
1635 if (obj == NULL) {
1636 return -1;
1637 }
1638
1639 // get container children (max 2)
1640 container->nbChildren = 2;
1641 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1642 container->obj.area.width = AVAILABLE_WIDTH;
1643 container->obj.area.height = RADIO_CHOICE_HEIGHT;
1644 container->obj.alignment = NO_ALIGNMENT;
1645 container->obj.alignmentMarginX = BORDER_MARGIN;
1646 container->obj.alignTo = (nbgl_obj_t *) NULL;
1647
1648 // init button for this choice
1649 button->activeColor = BLACK;
1650 button->borderColor = LIGHT_GRAY;
1651 button->obj.alignTo = (nbgl_obj_t *) container;
1652 button->obj.alignment = MID_RIGHT;
1653 button->state = OFF_STATE;
1654 container->children[1] = (nbgl_obj_t *) button;
1655
1656 // init text area for this choice
1657 if (choices->localized == true) {
1658#ifdef HAVE_LANGUAGE_PACK
1659 textArea->text = get_ux_loc_string(choices->nameIds[i]);
1660#endif // HAVE_LANGUAGE_PACK
1661 }
1662 else {
1663 textArea->text = PIC(choices->names[i]);
1664 }
1665 textArea->textAlignment = MID_LEFT;
1666 textArea->obj.area.width = container->obj.area.width - RADIO_WIDTH;
1667 textArea->style = NO_STYLE;
1668 textArea->obj.alignment = MID_LEFT;
1669 textArea->obj.alignTo = (nbgl_obj_t *) container;
1670 container->children[0] = (nbgl_obj_t *) textArea;
1671
1672 // whole container should be touchable
1673 container->obj.touchMask = (1 << TOUCHED);
1674 container->obj.touchId = CONTROLS_ID + nbTouchableControls;
1675 nbTouchableControls++;
1676
1677 // highlight init choice
1678 if (i == choices->initChoice) {
1679 button->state = ON_STATE;
1680 textArea->textColor = BLACK;
1681 textArea->fontId = SMALL_BOLD_FONT;
1682 }
1683 else {
1684 button->state = OFF_STATE;
1685 textArea->textColor = LIGHT_TEXT_COLOR;
1686 textArea->fontId = SMALL_REGULAR_FONT;
1687 }
1688 textArea->obj.area.height = nbgl_getFontHeight(textArea->fontId);
1689
1690 line = createHorizontalLine(layoutInt->layer);
1691 line->obj.alignmentMarginY = -1;
1692
1693 // set these new objs as child of main container
1694 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1695 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
1696 }
1697
1698 return 0;
1699}
1700
1710{
1711 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1712 nbgl_container_t *container;
1713 nbgl_contentCenter_t centeredInfo = {0};
1714
1715 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddCenteredInfo():\n");
1716 if (layout == NULL) {
1717 return -1;
1718 }
1719
1720 centeredInfo.icon = info->icon;
1721 centeredInfo.illustrType = ICON_ILLUSTRATION;
1722
1723 if (info->text1 != NULL) {
1724 if (info->style != NORMAL_INFO) {
1725 centeredInfo.title = info->text1;
1726 }
1727 else {
1728 centeredInfo.smallTitle = info->text1;
1729 }
1730 }
1731 if (info->text2 != NULL) {
1732 if (info->style != LARGE_CASE_BOLD_INFO) {
1733 centeredInfo.description = info->text2;
1734 }
1735 else {
1736 centeredInfo.smallTitle = info->text2;
1737 }
1738 }
1739 if (info->text3 != NULL) {
1740 if (info->style == LARGE_CASE_GRAY_INFO) {
1741 centeredInfo.subText = info->text3;
1742 }
1743 else {
1744 centeredInfo.description = info->text3;
1745 }
1746 }
1747 container = addContentCenter(layoutInt, &centeredInfo);
1748
1749 if (info->onTop) {
1750 container->obj.alignmentMarginX = BORDER_MARGIN;
1751 container->obj.alignmentMarginY = BORDER_MARGIN + info->offsetY;
1752 container->obj.alignment = NO_ALIGNMENT;
1753 }
1754 else {
1755 container->obj.alignmentMarginY = info->offsetY;
1756 }
1757
1758 return container->obj.area.height;
1759}
1760
1770{
1771 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1772 nbgl_container_t *container;
1773
1774 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddContentCenter():\n");
1775 if (layout == NULL) {
1776 return -1;
1777 }
1778
1779 container = addContentCenter(layoutInt, info);
1780
1781 return container->obj.area.height;
1782}
1783
1792{
1793 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1794 nbgl_container_t *container;
1795 nbgl_text_area_t *textArea;
1796 uint8_t row;
1797
1798 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLeftContent():\n");
1799 if (layout == NULL) {
1800 return -1;
1801 }
1802 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1803
1804 // get container children
1805 container->nbChildren = info->nbRows + 1;
1806 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
1807 container->layout = VERTICAL;
1808 container->obj.area.width = AVAILABLE_WIDTH;
1809 container->obj.alignmentMarginX = BORDER_MARGIN;
1810
1811 // create title
1812 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1813 textArea->textColor = BLACK;
1814 textArea->text = PIC(info->title);
1815 textArea->textAlignment = MID_LEFT;
1816 textArea->fontId = LARGE_MEDIUM_FONT;
1817 textArea->wrapping = true;
1818#ifdef TARGET_STAX
1819 textArea->obj.alignmentMarginY = 24;
1820#endif
1821 textArea->obj.area.width = AVAILABLE_WIDTH;
1822 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1823 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1824
1825 container->obj.area.height += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1826
1827 container->children[0] = (nbgl_obj_t *) textArea;
1828
1829 for (row = 0; row < info->nbRows; row++) {
1830 nbgl_container_t *rowContainer;
1831 nbgl_image_t *image;
1832
1833 // create a grid with 1 icon on the left column and 1 text on the right one
1834 rowContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, 0);
1835 rowContainer->nbChildren = 2; // 1 icon + 1 text
1836 rowContainer->children = nbgl_containerPoolGet(rowContainer->nbChildren, 0);
1837 rowContainer->obj.area.width = AVAILABLE_WIDTH;
1838
1839 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, 0);
1840 image->foregroundColor = BLACK;
1841 image->buffer = info->rowIcons[row];
1842 rowContainer->children[0] = (nbgl_obj_t *) image;
1843
1844 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, 0);
1845 textArea->textColor = BLACK;
1846 textArea->text = info->rowTexts[row];
1847 textArea->textAlignment = MID_LEFT;
1848 textArea->fontId = SMALL_REGULAR_FONT;
1849 textArea->wrapping = true;
1850 textArea->obj.area.width
1851 = AVAILABLE_WIDTH - image->buffer->width - LEFT_CONTENT_ICON_TEXT_X;
1852 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1853 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1854 textArea->obj.alignment = MID_RIGHT;
1855 rowContainer->children[1] = (nbgl_obj_t *) textArea;
1856 rowContainer->obj.area.height
1857 = MAX(image->buffer->height, textArea->obj.area.height + LEFT_CONTENT_TEXT_PADDING);
1858
1859 if (row == 0) {
1860 rowContainer->obj.alignmentMarginY = PRE_FIRST_ROW_MARGIN;
1861 }
1862 else {
1863 rowContainer->obj.alignmentMarginY = INTER_ROWS_MARGIN;
1864 }
1865 container->children[1 + row] = (nbgl_obj_t *) rowContainer;
1866 container->obj.area.height
1867 += rowContainer->obj.area.height + rowContainer->obj.alignmentMarginY;
1868 }
1869 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1870
1871 return container->obj.area.height;
1872}
1873
1874#ifdef NBGL_QRCODE
1884{
1885 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
1886 nbgl_container_t *container;
1887 nbgl_text_area_t *textArea = NULL;
1889 uint16_t fullHeight = 0;
1890
1891 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddQRCode():\n");
1892 if (layout == NULL) {
1893 return -1;
1894 }
1895
1896 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
1897
1898 // get container children (max 2 (QRCode + text1 + text2))
1899 container->children = nbgl_containerPoolGet(3, layoutInt->layer);
1900 container->nbChildren = 0;
1901
1903 // version is forced to V10 if url is longer than 62 characters
1904 if (strlen(PIC(info->url)) > 62) {
1905 qrcode->version = QRCODE_V10;
1906 }
1907 else {
1908 qrcode->version = QRCODE_V4;
1909 }
1910 qrcode->foregroundColor = BLACK;
1911 // in QR V4, we use:
1912 // - 8*8 screen pixels for one QR pixel on Stax/Flex
1913 // - 5*5 screen pixels for one QR pixel on Apex
1914 // in QR V10, we use 4*4 screen pixels for one QR pixel
1915#ifndef TARGET_APEX
1916 qrcode->obj.area.width
1917 = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 8) : (QR_V10_NB_PIX_SIZE * 4);
1918#else // TARGET_APEX
1919 qrcode->obj.area.width
1920 = (qrcode->version == QRCODE_V4) ? (QR_V4_NB_PIX_SIZE * 5) : (QR_V10_NB_PIX_SIZE * 4);
1921#endif // TARGET_APEX
1922 qrcode->obj.area.height = qrcode->obj.area.width;
1923 qrcode->text = PIC(info->url);
1924 qrcode->obj.area.bpp = NBGL_BPP_1;
1925 qrcode->obj.alignment = TOP_MIDDLE;
1926
1927 fullHeight += qrcode->obj.area.height;
1928 container->children[container->nbChildren] = (nbgl_obj_t *) qrcode;
1929 container->nbChildren++;
1930
1931 if (info->text1 != NULL) {
1932 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1933 textArea->textColor = BLACK;
1934 textArea->text = PIC(info->text1);
1935 textArea->textAlignment = CENTER;
1936 textArea->fontId = (info->largeText1 == true) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
1937 textArea->wrapping = true;
1938 textArea->obj.area.width = AVAILABLE_WIDTH;
1939 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1940 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1941 textArea->obj.alignment = BOTTOM_MIDDLE;
1942 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1943 textArea->obj.alignmentMarginY = QR_PRE_TEXT_MARGIN;
1944
1945 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY;
1946
1947 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1948 container->nbChildren++;
1949 }
1950 if (info->text2 != NULL) {
1951 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
1952 textArea->textColor = LIGHT_TEXT_COLOR;
1953 textArea->text = PIC(info->text2);
1954 textArea->textAlignment = CENTER;
1955 textArea->fontId = SMALL_REGULAR_FONT;
1956 textArea->wrapping = true;
1957 textArea->obj.area.width = AVAILABLE_WIDTH;
1958 textArea->obj.area.height = nbgl_getTextHeightInWidth(
1959 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
1960 textArea->obj.alignment = BOTTOM_MIDDLE;
1961 textArea->obj.alignTo = (nbgl_obj_t *) container->children[container->nbChildren - 1];
1962 if (info->text1 != NULL) {
1963 textArea->obj.alignmentMarginY = QR_INTER_TEXTS_MARGIN;
1964 }
1965 else {
1966 textArea->obj.alignmentMarginY = QR_PRE_TEXT_MARGIN + 8;
1967 }
1968
1969 fullHeight += textArea->obj.area.height + textArea->obj.alignmentMarginY + 8;
1970
1971 container->children[container->nbChildren] = (nbgl_obj_t *) textArea;
1972 container->nbChildren++;
1973 }
1974 // ensure that fullHeight is fitting in main container height (with 16 px margin)
1975 if ((fullHeight >= (layoutInt->container->obj.area.height - 16))
1976 && (qrcode->version == QRCODE_V4)) {
1977 qrcode->version = QRCODE_V4_SMALL;
1978 // in QR V4 small, we use 4*4 screen pixels for one QR pixel
1979 qrcode->obj.area.width = QR_V4_NB_PIX_SIZE * 4;
1980 qrcode->obj.area.height = qrcode->obj.area.width;
1981 fullHeight -= QR_V4_NB_PIX_SIZE * 4;
1982 }
1983 container->obj.area.height = fullHeight;
1984 container->layout = VERTICAL;
1985 if (info->centered) {
1986 container->obj.alignment = CENTER;
1987 }
1988 else {
1989 container->obj.alignment = BOTTOM_MIDDLE;
1990 container->obj.alignTo
1991 = layoutInt->container->children[layoutInt->container->nbChildren - 1];
1992 }
1993 container->obj.alignmentMarginY = info->offsetY;
1994
1995 container->obj.area.width = AVAILABLE_WIDTH;
1996
1997 // set this new container as child of main container
1998 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
1999
2000 return fullHeight;
2001}
2002#endif // NBGL_QRCODE
2003
2013{
2014 nbgl_layoutFooter_t footerDesc;
2015 footerDesc.type = FOOTER_CHOICE_BUTTONS;
2016 footerDesc.separationLine = false;
2017 footerDesc.choiceButtons.bottomText = info->bottomText;
2018 footerDesc.choiceButtons.token = info->token;
2019 footerDesc.choiceButtons.topText = info->topText;
2020 footerDesc.choiceButtons.topIcon = info->topIcon;
2021 footerDesc.choiceButtons.style = info->style;
2022 footerDesc.choiceButtons.tuneId = info->tuneId;
2023 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2024}
2025
2037{
2039 .horizontalButtons.leftIcon = info->leftIcon,
2040 .horizontalButtons.leftToken = info->leftToken,
2041 .horizontalButtons.rightText = info->rightText,
2042 .horizontalButtons.rightToken = info->rightToken,
2043 .horizontalButtons.tuneId = info->tuneId};
2044
2045 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHorizontalButtons():\n");
2046 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2047}
2048
2057{
2058 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2059 nbgl_text_area_t *itemTextArea;
2060 nbgl_text_area_t *valueTextArea;
2061 nbgl_container_t *container;
2062 uint8_t i;
2063
2064 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddTagValueList():\n");
2065 if (layout == NULL) {
2066 return -1;
2067 }
2068
2069 for (i = 0; i < list->nbPairs; i++) {
2070 const nbgl_layoutTagValue_t *pair;
2071 uint16_t fullHeight = 0;
2072 const nbgl_icon_details_t *valueIcon = NULL;
2073
2074 if (list->pairs != NULL) {
2075 pair = &list->pairs[i];
2076 }
2077 else {
2078 pair = list->callback(list->startIndex + i);
2079 }
2080
2081 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2082
2083 // get container children (max 3 if a valueIcon, otherwise 2)
2084 container->children
2085 = nbgl_containerPoolGet((pair->valueIcon != NULL) ? 3 : 2, layoutInt->layer);
2086
2087 itemTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2088 valueTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2089
2090 // init text area for this choice
2091 itemTextArea->textColor = LIGHT_TEXT_COLOR;
2092 itemTextArea->text = PIC(pair->item);
2093 itemTextArea->textAlignment = MID_LEFT;
2094 itemTextArea->fontId = SMALL_REGULAR_FONT;
2095 itemTextArea->wrapping = true;
2096 itemTextArea->obj.area.width = AVAILABLE_WIDTH;
2097 itemTextArea->obj.area.height = nbgl_getTextHeightInWidth(
2098 itemTextArea->fontId, itemTextArea->text, AVAILABLE_WIDTH, itemTextArea->wrapping);
2099 itemTextArea->style = NO_STYLE;
2100 itemTextArea->obj.alignment = NO_ALIGNMENT;
2101 itemTextArea->obj.alignmentMarginX = 0;
2102 itemTextArea->obj.alignmentMarginY = 0;
2103 itemTextArea->obj.alignTo = NULL;
2104 container->children[container->nbChildren] = (nbgl_obj_t *) itemTextArea;
2105 container->nbChildren++;
2106
2107 fullHeight += itemTextArea->obj.area.height;
2108
2109 // init button for this choice
2110 valueTextArea->textColor = BLACK;
2111 valueTextArea->text = PIC(pair->value);
2112 valueTextArea->textAlignment = MID_LEFT;
2113 if (list->smallCaseForValue) {
2114 valueTextArea->fontId = SMALL_BOLD_FONT;
2115 }
2116 else {
2117 valueTextArea->fontId = LARGE_MEDIUM_FONT;
2118 }
2119 if ((pair->aliasValue == 0) && (pair->valueIcon == NULL)) {
2120 valueTextArea->obj.area.width = AVAILABLE_WIDTH;
2121 }
2122 else {
2123 if (pair->aliasValue) {
2124 // if the value is an alias, we automatically display a (>) icon
2125 valueIcon = &MINI_PUSH_ICON;
2126 }
2127 else {
2128 // otherwise use the provided icon
2129 valueIcon = PIC(pair->valueIcon);
2130 }
2131 // decrease the available width for value text
2132 valueTextArea->obj.area.width = AVAILABLE_WIDTH - valueIcon->width - 12;
2133 }
2134
2135 // handle the nbMaxLinesForValue parameter, used to automatically keep only
2136 // nbMaxLinesForValue lines
2137 uint16_t nbLines = nbgl_getTextNbLinesInWidth(valueTextArea->fontId,
2138 valueTextArea->text,
2139 valueTextArea->obj.area.width,
2140 list->wrapping);
2141 // use this nbMaxLinesForValue parameter only if >0
2142 if ((list->nbMaxLinesForValue > 0) && (nbLines > list->nbMaxLinesForValue)) {
2143 nbLines = list->nbMaxLinesForValue;
2144 valueTextArea->nbMaxLines = list->nbMaxLinesForValue;
2145 }
2146 const nbgl_font_t *font = nbgl_getFont(valueTextArea->fontId);
2147 valueTextArea->obj.area.height = nbLines * font->line_height;
2148 valueTextArea->style = NO_STYLE;
2149 valueTextArea->obj.alignment = BOTTOM_LEFT;
2150 valueTextArea->obj.alignmentMarginY = 4;
2151 valueTextArea->obj.alignTo = (nbgl_obj_t *) itemTextArea;
2152 valueTextArea->wrapping = list->wrapping;
2153 container->children[container->nbChildren] = (nbgl_obj_t *) valueTextArea;
2154 container->nbChildren++;
2155
2156 fullHeight += valueTextArea->obj.area.height + valueTextArea->obj.alignmentMarginY;
2157 if (valueIcon != NULL) {
2158 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2159 // set the container as touchable, not only the image
2161 layoutInt, (nbgl_obj_t *) container, list->token, TUNE_TAP_CASUAL);
2162 obj->index = i;
2163 image->foregroundColor = BLACK;
2164 image->buffer = valueIcon;
2165 image->obj.alignment = RIGHT_TOP;
2166 image->obj.alignmentMarginX = 12;
2167 image->obj.alignTo = (nbgl_obj_t *) valueTextArea;
2168 // set the container as touchable, not only the image
2169 container->obj.touchMask = (1 << TOUCHED);
2170 container->obj.touchId = VALUE_BUTTON_1_ID + i;
2171
2172 container->children[container->nbChildren] = (nbgl_obj_t *) image;
2173 container->nbChildren++;
2174 }
2175
2176 container->obj.area.width = AVAILABLE_WIDTH;
2177 container->obj.area.height = fullHeight;
2178 container->layout = VERTICAL;
2179 container->obj.alignmentMarginX = BORDER_MARGIN;
2180 if (i > 0) {
2181 container->obj.alignmentMarginY = INTER_TAG_VALUE_MARGIN;
2182 }
2183 else {
2184 container->obj.alignmentMarginY = PRE_TAG_VALUE_MARGIN;
2185 }
2186 container->obj.alignment = NO_ALIGNMENT;
2187
2188 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2189 }
2190
2191 return 0;
2192}
2193
2207 const char *text,
2208 const char *subText,
2209 uint8_t percentage)
2210{
2211 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2212 nbgl_container_t *container;
2213 nbgl_text_area_t *textArea;
2214 nbgl_progress_bar_t *progress;
2215
2216 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressBar():\n");
2217 if (layout == NULL) {
2218 return -1;
2219 }
2220
2221 // First Create Container :
2222 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2223 // progressbar + text + subText
2224 container->nbChildren = (subText != NULL) ? 3 : 2;
2225 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
2226
2227 container->obj.area.width = AVAILABLE_WIDTH;
2228 container->layout = VERTICAL;
2229 container->obj.alignment = CENTER;
2230
2231 // Create progressbar
2232 progress = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
2233 progress->foregroundColor = BLACK;
2234 progress->withBorder = true;
2235 progress->state = percentage;
2236 progress->obj.area.width = PROGRESSBAR_WIDTH;
2237 progress->obj.area.height = PROGRESSBAR_HEIGHT;
2238 progress->obj.alignment = TOP_MIDDLE;
2239
2240 // set this new progressbar as child of the container
2241 container->children[0] = (nbgl_obj_t *) progress;
2242
2243 // update container height
2244 container->obj.area.height = progress->obj.alignmentMarginY + progress->obj.area.height;
2245
2246 // create text area
2247 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2248 textArea->textColor = BLACK;
2249 textArea->text = PIC(text);
2250 textArea->textAlignment = CENTER;
2251 textArea->fontId = LARGE_MEDIUM_FONT;
2252 textArea->wrapping = true;
2253 textArea->obj.alignmentMarginY = BAR_TEXT_MARGIN;
2254 textArea->obj.alignTo = (nbgl_obj_t *) progress;
2255 textArea->obj.alignment = BOTTOM_MIDDLE;
2256 textArea->obj.area.width = AVAILABLE_WIDTH;
2257 textArea->obj.area.height = nbgl_getTextHeightInWidth(
2258 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
2259 textArea->style = NO_STYLE;
2260
2261 // update container height
2262 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
2263
2264 // set this text as child of the container
2265 container->children[1] = (nbgl_obj_t *) textArea;
2266
2267 if (subText != NULL) {
2268 nbgl_text_area_t *subTextArea;
2269 // create sub-text area
2270 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2271 subTextArea->textColor = BLACK;
2272 subTextArea->text = PIC(subText);
2273 subTextArea->textAlignment = CENTER;
2274 subTextArea->fontId = SMALL_REGULAR_FONT;
2275 subTextArea->wrapping = true;
2276 subTextArea->obj.alignmentMarginY = BAR_INTER_TEXTS_MARGIN;
2277 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
2278 subTextArea->obj.alignment = BOTTOM_MIDDLE;
2279 subTextArea->obj.area.width = AVAILABLE_WIDTH;
2280 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
2281 subTextArea->text,
2282 subTextArea->obj.area.width,
2283 subTextArea->wrapping);
2284 subTextArea->style = NO_STYLE;
2285
2286 // update container height
2287 container->obj.area.height
2288 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
2289
2290 // set thissub-text as child of the container
2291 container->children[2] = (nbgl_obj_t *) subTextArea;
2292 }
2293 // The height of containers must be a multiple of eight
2294 container->obj.area.height = (container->obj.area.height + 7) & 0xFFF8;
2295
2296 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
2297
2298 return 2;
2299}
2300
2308{
2309 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2310 nbgl_line_t *line;
2311
2312 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSeparationLine():\n");
2313 line = createHorizontalLine(layoutInt->layer);
2314 line->obj.alignmentMarginY = -1;
2315 layoutAddObject(layoutInt, (nbgl_obj_t *) line);
2316 return 0;
2317}
2318
2327{
2328 layoutObj_t *obj;
2329 nbgl_button_t *button;
2330 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2331
2332 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddButton():\n");
2333 if (layout == NULL) {
2334 return -1;
2335 }
2336
2337 // Add in footer if matching
2338 if ((buttonInfo->onBottom) && (!buttonInfo->fittingContent)) {
2339 if (layoutInt->footerContainer == NULL) {
2340 nbgl_layoutFooter_t footerDesc;
2341 footerDesc.type = FOOTER_SIMPLE_BUTTON;
2342 footerDesc.separationLine = false;
2343 footerDesc.button.text = buttonInfo->text;
2344 footerDesc.button.token = buttonInfo->token;
2345 footerDesc.button.tuneId = buttonInfo->tuneId;
2346 footerDesc.button.icon = buttonInfo->icon;
2347 footerDesc.button.style = buttonInfo->style;
2348 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2349 }
2350 else {
2351 nbgl_layoutUpFooter_t upFooterDesc;
2352 upFooterDesc.type = UP_FOOTER_BUTTON;
2353 upFooterDesc.button.text = buttonInfo->text;
2354 upFooterDesc.button.token = buttonInfo->token;
2355 upFooterDesc.button.tuneId = buttonInfo->tuneId;
2356 upFooterDesc.button.icon = buttonInfo->icon;
2357 upFooterDesc.button.style = buttonInfo->style;
2358 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2359 }
2360 }
2361
2362 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2364 layoutInt, (nbgl_obj_t *) button, buttonInfo->token, buttonInfo->tuneId);
2365 if (obj == NULL) {
2366 return -1;
2367 }
2368
2369 button->obj.alignmentMarginX = BORDER_MARGIN;
2370 button->obj.alignmentMarginY = 12;
2371 button->obj.alignment = NO_ALIGNMENT;
2372 if (buttonInfo->style == BLACK_BACKGROUND) {
2373 button->innerColor = BLACK;
2374 button->foregroundColor = WHITE;
2375 }
2376 else {
2377 button->innerColor = WHITE;
2378 button->foregroundColor = BLACK;
2379 }
2380 if (buttonInfo->style == NO_BORDER) {
2381 button->borderColor = WHITE;
2382 }
2383 else {
2384 if (buttonInfo->style == BLACK_BACKGROUND) {
2385 button->borderColor = BLACK;
2386 }
2387 else {
2388 button->borderColor = LIGHT_GRAY;
2389 }
2390 }
2391 button->text = PIC(buttonInfo->text);
2392 button->fontId = SMALL_BOLD_FONT;
2393 button->icon = PIC(buttonInfo->icon);
2394 if (buttonInfo->fittingContent == true) {
2395 button->obj.area.width = nbgl_getTextWidth(button->fontId, button->text)
2396 + SMALL_BUTTON_HEIGHT
2397 + ((button->icon) ? (button->icon->width + 12) : 0);
2398 button->obj.area.height = SMALL_BUTTON_HEIGHT;
2399 button->radius = SMALL_BUTTON_RADIUS_INDEX;
2400 if (buttonInfo->onBottom != true) {
2401 button->obj.alignmentMarginX += (AVAILABLE_WIDTH - button->obj.area.width) / 2;
2402 }
2403 }
2404 else {
2405 button->obj.area.width = AVAILABLE_WIDTH;
2406 button->obj.area.height = BUTTON_DIAMETER;
2407 button->radius = BUTTON_RADIUS;
2408 }
2409 button->obj.alignTo = NULL;
2410 button->obj.touchMask = (1 << TOUCHED);
2411 button->obj.touchId = (buttonInfo->fittingContent) ? EXTRA_BUTTON_ID : SINGLE_BUTTON_ID;
2412 // set this new button as child of the container
2413 layoutAddObject(layoutInt, (nbgl_obj_t *) button);
2414
2415 return 0;
2416}
2417
2428 const char *text,
2429 uint8_t token,
2430 tune_index_e tuneId)
2431{
2433 .longPress.text = text,
2434 .longPress.token = token,
2435 .longPress.tuneId = tuneId};
2436
2437 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddLongPressButton():\n");
2438 if (layout == NULL) {
2439 return -1;
2440 }
2441
2442 return nbgl_layoutAddUpFooter(layout, &upFooterDesc);
2443}
2444
2456 const char *text,
2457 uint8_t token,
2458 tune_index_e tuneId)
2459{
2460 nbgl_layoutFooter_t footerDesc;
2461 footerDesc.type = FOOTER_SIMPLE_TEXT;
2462 footerDesc.separationLine = true;
2463 footerDesc.simpleText.text = text;
2464 footerDesc.simpleText.mutedOut = false;
2465 footerDesc.simpleText.token = token;
2466 footerDesc.simpleText.tuneId = tuneId;
2467 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2468}
2469
2483 const char *leftText,
2484 uint8_t leftToken,
2485 const char *rightText,
2486 uint8_t rightToken,
2487 tune_index_e tuneId)
2488{
2489 nbgl_layoutFooter_t footerDesc;
2490 footerDesc.type = FOOTER_DOUBLE_TEXT;
2491 footerDesc.separationLine = true;
2492 footerDesc.doubleText.leftText = leftText;
2493 footerDesc.doubleText.leftToken = leftToken;
2494 footerDesc.doubleText.rightText = rightText;
2495 footerDesc.doubleText.rightToken = rightToken;
2496 footerDesc.doubleText.tuneId = tuneId;
2497 return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
2498}
2499
2509{
2510 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2511 layoutObj_t *obj;
2512 nbgl_text_area_t *textArea = NULL;
2513 nbgl_line_t *line = NULL;
2514 nbgl_image_t *image = NULL;
2515 nbgl_button_t *button = NULL;
2516
2517 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHeader(): type = %d\n", headerDesc->type);
2518 if (layout == NULL) {
2519 return -1;
2520 }
2521 if ((headerDesc == NULL) || (headerDesc->type >= NB_HEADER_TYPES)) {
2522 return -2;
2523 }
2524
2525 layoutInt->headerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2526 layoutInt->headerContainer->obj.area.width = SCREEN_WIDTH;
2527 layoutInt->headerContainer->layout = VERTICAL;
2528 layoutInt->headerContainer->children
2529 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2530 layoutInt->headerContainer->obj.alignment = TOP_MIDDLE;
2531
2532 switch (headerDesc->type) {
2533 case HEADER_EMPTY: {
2534 layoutInt->headerContainer->obj.area.height = headerDesc->emptySpace.height;
2535 break;
2536 }
2539 case HEADER_EXTENDED_BACK: {
2540 const char *text = (headerDesc->type == HEADER_EXTENDED_BACK)
2541 ? PIC(headerDesc->extendedBack.text)
2542 : PIC(headerDesc->backAndText.text);
2543 uint8_t backToken = (headerDesc->type == HEADER_EXTENDED_BACK)
2544 ? headerDesc->extendedBack.backToken
2545 : headerDesc->backAndText.token;
2546 nbgl_button_t *actionButton = NULL;
2547 // add back button
2548 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2549 // only make it active if valid token
2550 if (backToken != NBGL_INVALID_TOKEN) {
2551 obj = layoutAddCallbackObj(layoutInt,
2552 (nbgl_obj_t *) button,
2553 backToken,
2554 (headerDesc->type == HEADER_EXTENDED_BACK)
2555 ? headerDesc->extendedBack.tuneId
2556 : headerDesc->backAndText.tuneId);
2557 if (obj == NULL) {
2558 return -1;
2559 }
2560 }
2561
2562 button->obj.alignment = MID_LEFT;
2563 button->innerColor = WHITE;
2564 button->foregroundColor = (backToken != NBGL_INVALID_TOKEN) ? BLACK : WHITE;
2565 button->borderColor = WHITE;
2566 button->obj.area.width = BACK_KEY_WIDTH;
2567 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2568 button->text = NULL;
2569 button->icon = PIC(&LEFT_ARROW_ICON);
2570 button->obj.touchMask = (backToken != NBGL_INVALID_TOKEN) ? (1 << TOUCHED) : 0;
2571 button->obj.touchId = BACK_BUTTON_ID;
2572 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2573 = (nbgl_obj_t *) button;
2574 layoutInt->headerContainer->nbChildren++;
2575
2576 // add optional text if needed
2577 if (text != NULL) {
2578 // add optional icon if type is HEADER_BACK_ICON_AND_TEXT
2579 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2580 image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
2581 image->buffer = PIC(headerDesc->backAndText.icon);
2582 image->foregroundColor = BLACK;
2583 image->obj.alignment = CENTER;
2584 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2585 = (nbgl_obj_t *) image;
2586 layoutInt->headerContainer->nbChildren++;
2587 }
2588 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2589 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2590 && (headerDesc->extendedBack.textToken != NBGL_INVALID_TOKEN)) {
2591 obj = layoutAddCallbackObj(layoutInt,
2592 (nbgl_obj_t *) textArea,
2593 headerDesc->extendedBack.textToken,
2594 headerDesc->extendedBack.tuneId);
2595 if (obj == NULL) {
2596 return -1;
2597 }
2598 textArea->obj.touchMask = (1 << TOUCHED);
2599 }
2600 textArea->obj.alignment = CENTER;
2601 textArea->textColor = BLACK;
2602 textArea->obj.area.width
2603 = layoutInt->headerContainer->obj.area.width - 2 * BACK_KEY_WIDTH;
2604 // if icon, reduce to 1 line and fit text width
2605 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2606 textArea->obj.area.width -= 16 + image->buffer->width;
2607 }
2608 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2609 textArea->text = text;
2610 textArea->fontId = SMALL_BOLD_FONT;
2611 textArea->textAlignment = CENTER;
2612 textArea->wrapping = true;
2613 uint8_t nbMaxLines = (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) ? 1 : 2;
2614 // ensure that text fits on 2 lines maximum
2615 if (nbgl_getTextNbLinesInWidth(textArea->fontId,
2616 textArea->text,
2617 textArea->obj.area.width,
2618 textArea->wrapping)
2619 > nbMaxLines) {
2621 "nbgl_layoutAddHeader: text [%s] is too long for header\n",
2622 text);
2623 }
2624 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2625 textArea->obj.area.width = nbgl_getTextWidth(textArea->fontId, textArea->text);
2626 }
2627 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2628 = (nbgl_obj_t *) textArea;
2629 layoutInt->headerContainer->nbChildren++;
2630 // if icon, realign icon & text
2631 if (headerDesc->type == HEADER_BACK_ICON_AND_TEXT) {
2632 textArea->obj.alignmentMarginX = 8 + image->buffer->width / 2;
2633 image->obj.alignmentMarginX = -8 - textArea->obj.area.width / 2;
2634 }
2635 }
2636 // add action key if the type is HEADER_EXTENDED_BACK
2637 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2638 && (headerDesc->extendedBack.actionIcon)) {
2639 actionButton = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2640 // if token is valid
2641 if (headerDesc->extendedBack.actionToken != NBGL_INVALID_TOKEN) {
2642 obj = layoutAddCallbackObj(layoutInt,
2643 (nbgl_obj_t *) actionButton,
2644 headerDesc->extendedBack.actionToken,
2645 headerDesc->extendedBack.tuneId);
2646 if (obj == NULL) {
2647 return -1;
2648 }
2649 actionButton->obj.touchMask = (1 << TOUCHED);
2650 }
2651
2652 actionButton->obj.alignment = MID_RIGHT;
2653 actionButton->innerColor = WHITE;
2654 button->foregroundColor
2656 : LIGHT_GRAY;
2657 actionButton->borderColor = WHITE;
2658 actionButton->obj.area.width = BACK_KEY_WIDTH;
2659 actionButton->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2660 actionButton->text = NULL;
2661 actionButton->icon = PIC(headerDesc->extendedBack.actionIcon);
2662 actionButton->obj.touchId = EXTRA_BUTTON_ID;
2663 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2664 = (nbgl_obj_t *) actionButton;
2665 layoutInt->headerContainer->nbChildren++;
2666 }
2667
2668 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2669 // add potential text under the line if the type is HEADER_EXTENDED_BACK
2670 if ((headerDesc->type == HEADER_EXTENDED_BACK)
2671 && (headerDesc->extendedBack.subText != NULL)) {
2672 nbgl_text_area_t *subTextArea;
2673
2674 line = createHorizontalLine(layoutInt->layer);
2675 line->obj.alignment = TOP_MIDDLE;
2676 line->obj.alignmentMarginY = TOUCHABLE_HEADER_BAR_HEIGHT;
2677 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2678 = (nbgl_obj_t *) line;
2679 layoutInt->headerContainer->nbChildren++;
2680
2681 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2682 subTextArea->textColor = BLACK;
2683 subTextArea->text = PIC(headerDesc->extendedBack.subText);
2684 subTextArea->textAlignment = MID_LEFT;
2685 subTextArea->fontId = SMALL_REGULAR_FONT;
2686 subTextArea->wrapping = true;
2687 subTextArea->obj.alignment = BOTTOM_MIDDLE;
2688 subTextArea->obj.alignmentMarginY = SUB_HEADER_MARGIN;
2689 subTextArea->obj.area.width = AVAILABLE_WIDTH;
2690 subTextArea->obj.area.height
2691 = nbgl_getTextHeightInWidth(subTextArea->fontId,
2692 subTextArea->text,
2693 subTextArea->obj.area.width,
2694 subTextArea->wrapping);
2695 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2696 = (nbgl_obj_t *) subTextArea;
2697 layoutInt->headerContainer->nbChildren++;
2698 layoutInt->headerContainer->obj.area.height
2699 += subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN;
2701 if (button != NULL) {
2702 button->obj.alignmentMarginY
2703 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2704 }
2705 if (textArea != NULL) {
2706 textArea->obj.alignmentMarginY
2707 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2708 }
2709 if (actionButton != NULL) {
2710 actionButton->obj.alignmentMarginY
2711 -= (subTextArea->obj.area.height + 2 * SUB_HEADER_MARGIN) / 2;
2712 }
2713 }
2714 break;
2715 }
2717#ifdef TARGET_STAX
2718 // add optional back button
2719 if (headerDesc->progressAndBack.withBack) {
2720 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
2721 obj = layoutAddCallbackObj(layoutInt,
2722 (nbgl_obj_t *) button,
2723 headerDesc->progressAndBack.token,
2724 headerDesc->progressAndBack.tuneId);
2725 if (obj == NULL) {
2726 return -1;
2727 }
2728
2729 button->obj.alignment = MID_LEFT;
2730 button->innerColor = WHITE;
2731 button->foregroundColor = BLACK;
2732 button->borderColor = WHITE;
2733 button->obj.area.width = BACK_KEY_WIDTH;
2734 button->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2735 button->text = NULL;
2736 button->icon = PIC(&LEFT_ARROW_ICON);
2737 button->obj.touchMask = (1 << TOUCHED);
2738 button->obj.touchId = BACK_BUTTON_ID;
2739 // add to container
2740 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2741 = (nbgl_obj_t *) button;
2742 layoutInt->headerContainer->nbChildren++;
2743 }
2744
2745 // add progress indicator
2746 if (headerDesc->progressAndBack.nbPages > 1
2748 nbgl_page_indicator_t *progress;
2749
2750 progress
2752 progress->activePage = headerDesc->progressAndBack.activePage;
2753 progress->nbPages = headerDesc->progressAndBack.nbPages;
2754 progress->obj.area.width = 224;
2755 progress->obj.alignment = CENTER;
2756 // add to container
2757 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2758 = (nbgl_obj_t *) progress;
2759 layoutInt->headerContainer->nbChildren++;
2760 }
2761 layoutInt->activePage = headerDesc->progressAndBack.activePage;
2762 layoutInt->nbPages = headerDesc->progressAndBack.nbPages;
2763 layoutInt->headerContainer->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2764#endif // TARGET_STAX
2765 break;
2766 }
2767 case HEADER_TITLE: {
2768 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2769 textArea->textColor = BLACK;
2770 textArea->obj.area.width = AVAILABLE_WIDTH;
2771 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2772 textArea->text = PIC(headerDesc->title.text);
2773 textArea->fontId = SMALL_BOLD_FONT;
2774 textArea->textAlignment = CENTER;
2775 textArea->wrapping = true;
2776 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2777 = (nbgl_obj_t *) textArea;
2778 layoutInt->headerContainer->nbChildren++;
2779 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2780 break;
2781 }
2782 case HEADER_RIGHT_TEXT: {
2783 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2784 obj = layoutAddCallbackObj(layoutInt,
2785 (nbgl_obj_t *) textArea,
2786 headerDesc->rightText.token,
2787 headerDesc->rightText.tuneId);
2788 if (obj == NULL) {
2789 return -1;
2790 }
2791 textArea->obj.alignment = MID_RIGHT;
2792 textArea->obj.alignmentMarginX = BORDER_MARGIN;
2793 textArea->textColor = BLACK;
2794 textArea->obj.area.width = AVAILABLE_WIDTH;
2795 textArea->obj.area.height = TOUCHABLE_HEADER_BAR_HEIGHT;
2796 textArea->text = PIC(headerDesc->rightText.text);
2797 textArea->fontId = SMALL_BOLD_FONT;
2798 textArea->textAlignment = MID_RIGHT;
2799 textArea->obj.touchMask = (1 << TOUCHED);
2800 textArea->obj.touchId = TOP_RIGHT_BUTTON_ID;
2801 // add to bottom container
2802 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2803 = (nbgl_obj_t *) textArea;
2804 layoutInt->headerContainer->nbChildren++;
2805 layoutInt->headerContainer->obj.area.height = textArea->obj.area.height;
2806 break;
2807 }
2808 default:
2809 return -2;
2810 }
2811 // draw separation line at bottom of container
2812 if (headerDesc->separationLine) {
2813 line = createHorizontalLine(layoutInt->layer);
2814 line->obj.alignment = BOTTOM_MIDDLE;
2815 layoutInt->headerContainer->children[layoutInt->headerContainer->nbChildren]
2816 = (nbgl_obj_t *) line;
2817 layoutInt->headerContainer->nbChildren++;
2818 }
2819 // header must be the first child
2820 layoutInt->children[HEADER_INDEX] = (nbgl_obj_t *) layoutInt->headerContainer;
2821
2822 // subtract header height from main container height
2823 layoutInt->container->obj.area.height -= layoutInt->headerContainer->obj.area.height;
2824 layoutInt->container->obj.alignTo = (nbgl_obj_t *) layoutInt->headerContainer;
2825 layoutInt->container->obj.alignment = BOTTOM_LEFT;
2826
2827 layoutInt->headerType = headerDesc->type;
2828
2829 return layoutInt->headerContainer->obj.area.height;
2830}
2831
2841{
2842 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
2843 layoutObj_t *obj;
2844 nbgl_text_area_t *textArea;
2845 nbgl_line_t *line, *separationLine = NULL;
2846 nbgl_button_t *button;
2847
2848 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddExtendedFooter():\n");
2849 if (layout == NULL) {
2850 return -1;
2851 }
2852 if ((footerDesc == NULL) || (footerDesc->type >= NB_FOOTER_TYPES)) {
2853 return -2;
2854 }
2855
2856 layoutInt->footerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
2857 layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
2858 layoutInt->footerContainer->layout = VERTICAL;
2859 layoutInt->footerContainer->children
2860 = (nbgl_obj_t **) nbgl_containerPoolGet(5, layoutInt->layer);
2861 layoutInt->footerContainer->obj.alignment = BOTTOM_MIDDLE;
2862
2863 switch (footerDesc->type) {
2864 case FOOTER_EMPTY: {
2865 layoutInt->footerContainer->obj.area.height = footerDesc->emptySpace.height;
2866 break;
2867 }
2868 case FOOTER_SIMPLE_TEXT: {
2869 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2870 obj = layoutAddCallbackObj(layoutInt,
2871 (nbgl_obj_t *) textArea,
2872 footerDesc->simpleText.token,
2873 footerDesc->simpleText.tuneId);
2874 if (obj == NULL) {
2875 return -1;
2876 }
2877
2878 textArea->obj.alignment = BOTTOM_MIDDLE;
2879 textArea->textColor = (footerDesc->simpleText.mutedOut) ? LIGHT_TEXT_COLOR : BLACK;
2880 textArea->obj.area.width = AVAILABLE_WIDTH;
2881 textArea->obj.area.height
2882 = (footerDesc->simpleText.mutedOut) ? SMALL_FOOTER_HEIGHT : SIMPLE_FOOTER_HEIGHT;
2883 textArea->text = PIC(footerDesc->simpleText.text);
2884 textArea->fontId
2885 = (footerDesc->simpleText.mutedOut) ? SMALL_REGULAR_FONT : SMALL_BOLD_FONT;
2886 textArea->textAlignment = CENTER;
2887 textArea->obj.touchMask = (1 << TOUCHED);
2888 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2889 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2890 = (nbgl_obj_t *) textArea;
2891 layoutInt->footerContainer->nbChildren++;
2892 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2893 break;
2894 }
2895 case FOOTER_DOUBLE_TEXT: {
2896 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2897 obj = layoutAddCallbackObj(layoutInt,
2898 (nbgl_obj_t *) textArea,
2899 footerDesc->doubleText.leftToken,
2900 footerDesc->doubleText.tuneId);
2901 if (obj == NULL) {
2902 return -1;
2903 }
2904 textArea->obj.alignment = BOTTOM_LEFT;
2905 textArea->textColor = BLACK;
2906 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2907 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2908 textArea->text = PIC(footerDesc->doubleText.leftText);
2909 textArea->fontId = SMALL_BOLD_FONT;
2910 textArea->textAlignment = CENTER;
2911 textArea->obj.touchMask = (1 << TOUCHED);
2912 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2913 // add to bottom container
2914 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2915 = (nbgl_obj_t *) textArea;
2916 layoutInt->footerContainer->nbChildren++;
2917
2918 // create right touchable text
2919 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2920 obj = layoutAddCallbackObj(layoutInt,
2921 (nbgl_obj_t *) textArea,
2922 footerDesc->doubleText.rightToken,
2923 footerDesc->doubleText.tuneId);
2924 if (obj == NULL) {
2925 return -1;
2926 }
2927
2928 textArea->obj.alignment = BOTTOM_RIGHT;
2929 textArea->textColor = BLACK;
2930 textArea->obj.area.width = AVAILABLE_WIDTH / 2;
2931 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2932 textArea->text = PIC(footerDesc->doubleText.rightText);
2933 textArea->fontId = SMALL_BOLD_FONT;
2934 textArea->textAlignment = CENTER;
2935 textArea->obj.touchMask = (1 << TOUCHED);
2936 textArea->obj.touchId = RIGHT_BUTTON_ID;
2937 // add to bottom container
2938 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2939 = (nbgl_obj_t *) textArea;
2940 layoutInt->footerContainer->nbChildren++;
2941 layoutInt->footerContainer->obj.area.height = textArea->obj.area.height;
2942
2943 // create vertical line separating texts
2944 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
2945 separationLine->lineColor = LIGHT_GRAY;
2946 separationLine->obj.area.width = 1;
2947 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
2948 separationLine->direction = VERTICAL;
2949 separationLine->thickness = 1;
2950 separationLine->obj.alignment = MID_LEFT;
2951 separationLine->obj.alignTo = (nbgl_obj_t *) textArea;
2952 separationLine->obj.alignmentMarginX = -1;
2953 break;
2954 }
2955 case FOOTER_TEXT_AND_NAV: {
2956 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2957 // add touchable text on the left
2958 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
2959 obj = layoutAddCallbackObj(layoutInt,
2960 (nbgl_obj_t *) textArea,
2961 footerDesc->textAndNav.token,
2962 footerDesc->textAndNav.tuneId);
2963 if (obj == NULL) {
2964 return -1;
2965 }
2966 textArea->obj.alignment = BOTTOM_LEFT;
2967 textArea->textColor = BLACK;
2968 textArea->obj.area.width = FOOTER_TEXT_AND_NAV_WIDTH;
2969 textArea->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2970 textArea->text = PIC(footerDesc->textAndNav.text);
2971 textArea->fontId = SMALL_BOLD_FONT;
2972 textArea->textAlignment = CENTER;
2973 textArea->obj.touchMask = (1 << TOUCHED);
2974 textArea->obj.touchId = BOTTOM_BUTTON_ID;
2975 // add to bottom container
2976 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
2977 = (nbgl_obj_t *) textArea;
2978 layoutInt->footerContainer->nbChildren++;
2979
2980 // add navigation on the right
2981 nbgl_container_t *navContainer
2983 navContainer->obj.area.width = AVAILABLE_WIDTH;
2984 navContainer->layout = VERTICAL;
2985 navContainer->nbChildren = 4;
2986 navContainer->children
2987 = (nbgl_obj_t **) nbgl_containerPoolGet(navContainer->nbChildren, layoutInt->layer);
2988 navContainer->obj.alignment = BOTTOM_RIGHT;
2989 navContainer->obj.area.width = SCREEN_WIDTH - textArea->obj.area.width;
2990 navContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
2991 layoutNavigationPopulate(navContainer, &footerDesc->navigation, layoutInt->layer);
2992 obj = layoutAddCallbackObj(layoutInt,
2993 (nbgl_obj_t *) navContainer,
2994 footerDesc->textAndNav.navigation.token,
2995 footerDesc->textAndNav.navigation.tuneId);
2996 if (obj == NULL) {
2997 return -1;
2998 }
2999
3000 // create vertical line separating text from nav
3001 separationLine = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
3002 separationLine->lineColor = LIGHT_GRAY;
3003 separationLine->obj.area.width = 1;
3004 separationLine->obj.area.height = layoutInt->footerContainer->obj.area.height;
3005 separationLine->direction = VERTICAL;
3006 separationLine->thickness = 1;
3007 separationLine->obj.alignment = MID_LEFT;
3008 separationLine->obj.alignTo = (nbgl_obj_t *) navContainer;
3009 separationLine->obj.alignmentMarginX = -1;
3010
3011 layoutInt->activePage = footerDesc->textAndNav.navigation.activePage;
3012 layoutInt->nbPages = footerDesc->textAndNav.navigation.nbPages;
3013 // add to bottom container
3014 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3015 = (nbgl_obj_t *) navContainer;
3016 layoutInt->footerContainer->nbChildren++;
3017 break;
3018 }
3019 case FOOTER_NAV: {
3020 layoutInt->footerContainer->obj.area.height = SIMPLE_FOOTER_HEIGHT;
3022 layoutInt->footerContainer, &footerDesc->navigation, layoutInt->layer);
3023 layoutInt->footerContainer->nbChildren = 4;
3024 obj = layoutAddCallbackObj(layoutInt,
3025 (nbgl_obj_t *) layoutInt->footerContainer,
3026 footerDesc->navigation.token,
3027 footerDesc->navigation.tuneId);
3028 if (obj == NULL) {
3029 return -1;
3030 }
3031
3032 layoutInt->activePage = footerDesc->navigation.activePage;
3033 layoutInt->nbPages = footerDesc->navigation.nbPages;
3034 break;
3035 }
3036 case FOOTER_SIMPLE_BUTTON: {
3037 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3038 obj = layoutAddCallbackObj(layoutInt,
3039 (nbgl_obj_t *) button,
3040 footerDesc->button.token,
3041 footerDesc->button.tuneId);
3042 if (obj == NULL) {
3043 return -1;
3044 }
3045
3046 button->obj.alignment = BOTTOM_MIDDLE;
3047 button->obj.alignmentMarginY = SINGLE_BUTTON_MARGIN;
3048 if (footerDesc->button.style == BLACK_BACKGROUND) {
3049 button->innerColor = BLACK;
3050 button->foregroundColor = WHITE;
3051 }
3052 else {
3053 button->innerColor = WHITE;
3054 button->foregroundColor = BLACK;
3055 }
3056
3057 if (footerDesc->button.style == NO_BORDER) {
3058 button->borderColor = WHITE;
3059 }
3060 else {
3061 if (footerDesc->button.style == BLACK_BACKGROUND) {
3062 button->borderColor = BLACK;
3063 }
3064 else {
3065 button->borderColor = LIGHT_GRAY;
3066 }
3067 }
3068 button->text = PIC(footerDesc->button.text);
3069 button->fontId = SMALL_BOLD_FONT;
3070 button->icon = PIC(footerDesc->button.icon);
3071 button->radius = BUTTON_RADIUS;
3072 button->obj.area.height = BUTTON_DIAMETER;
3073 layoutInt->footerContainer->obj.area.height = FOOTER_BUTTON_HEIGHT;
3074 if (footerDesc->button.text == NULL) {
3075 button->obj.area.width = BUTTON_DIAMETER;
3076 }
3077 else {
3078 button->obj.area.width = AVAILABLE_WIDTH;
3079 }
3080 button->obj.touchMask = (1 << TOUCHED);
3081 button->obj.touchId = button->text ? SINGLE_BUTTON_ID : BOTTOM_BUTTON_ID;
3082 // add to bottom container
3083 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3084 = (nbgl_obj_t *) button;
3085 layoutInt->footerContainer->nbChildren++;
3086 break;
3087 }
3088 case FOOTER_CHOICE_BUTTONS: {
3089 // texts cannot be NULL
3090 if ((footerDesc->choiceButtons.bottomText == NULL)
3091 || (footerDesc->choiceButtons.topText == NULL)) {
3092 return -1;
3093 }
3094
3095 // create bottom button (footer) at first
3096 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3097 obj = layoutAddCallbackObj(layoutInt,
3098 (nbgl_obj_t *) button,
3099 footerDesc->choiceButtons.token,
3100 footerDesc->choiceButtons.tuneId);
3101 if (obj == NULL) {
3102 return -1;
3103 }
3104 // associate with with index 1
3105 obj->index = 1;
3106 // put at the bottom of the container
3107 button->obj.alignment = BOTTOM_MIDDLE;
3108 button->innerColor = WHITE;
3109 if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3110 button->obj.alignmentMarginY
3111 = SINGLE_BUTTON_MARGIN; // pixels from bottom of container
3112 button->borderColor = LIGHT_GRAY;
3113 button->obj.area.height = BUTTON_DIAMETER;
3114 }
3115 else {
3116 button->obj.alignmentMarginY
3117 = BUTTON_FROM_BOTTOM_MARGIN; // pixels from screen bottom
3118 button->borderColor = WHITE;
3119 button->obj.area.height = FOOTER_IN_PAIR_HEIGHT;
3120 }
3121 button->foregroundColor = BLACK;
3122 button->obj.area.width = AVAILABLE_WIDTH;
3123 button->radius = BUTTON_RADIUS;
3124 button->text = PIC(footerDesc->choiceButtons.bottomText);
3125 button->fontId = SMALL_BOLD_FONT;
3126 button->obj.touchMask = (1 << TOUCHED);
3127 button->obj.touchId = CHOICE_2_ID;
3128 // add to bottom container
3129 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3130 = (nbgl_obj_t *) button;
3131 layoutInt->footerContainer->nbChildren++;
3132
3133 // add line if needed
3134 if ((footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3135 && (footerDesc->choiceButtons.style != BOTH_ROUNDED_STYLE)) {
3136 line = createHorizontalLine(layoutInt->layer);
3137 line->obj.alignment = TOP_MIDDLE;
3138 line->obj.alignTo = (nbgl_obj_t *) button;
3139 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3140 = (nbgl_obj_t *) line;
3141 layoutInt->footerContainer->nbChildren++;
3142 }
3143
3144 // then top button, on top of it
3145 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3146 obj = layoutAddCallbackObj(layoutInt,
3147 (nbgl_obj_t *) button,
3148 footerDesc->choiceButtons.token,
3149 footerDesc->choiceButtons.tuneId);
3150 if (obj == NULL) {
3151 return -1;
3152 }
3153 // associate with with index 0
3154 obj->index = 0;
3155 button->obj.alignment = TOP_MIDDLE;
3156 if (footerDesc->choiceButtons.style == ROUNDED_AND_FOOTER_STYLE) {
3157 button->obj.alignmentMarginY = TOP_BUTTON_MARGIN; // pixels from top of container
3158 }
3159 else if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3160 button->obj.alignmentMarginY
3161 = SINGLE_BUTTON_MARGIN; // pixels from top of container
3162 }
3163 else {
3164 button->obj.alignmentMarginY
3165 = TOP_BUTTON_MARGIN_WITH_ACTION; // pixels from top of container
3166 }
3168 button->innerColor = WHITE;
3169 button->borderColor = LIGHT_GRAY;
3170 button->foregroundColor = BLACK;
3171 }
3172 else {
3173 button->innerColor = BLACK;
3174 button->borderColor = BLACK;
3175 button->foregroundColor = WHITE;
3176 }
3177 button->obj.area.width = AVAILABLE_WIDTH;
3178 button->obj.area.height = BUTTON_DIAMETER;
3179 button->radius = BUTTON_RADIUS;
3180 button->text = PIC(footerDesc->choiceButtons.topText);
3181 button->icon = (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
3182 ? PIC(footerDesc->choiceButtons.topIcon)
3183 : NULL;
3184 button->fontId = SMALL_BOLD_FONT;
3185 button->obj.touchMask = (1 << TOUCHED);
3186 button->obj.touchId = CHOICE_1_ID;
3187 // add to bottom container
3188 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3189 = (nbgl_obj_t *) button;
3190 layoutInt->footerContainer->nbChildren++;
3191
3192 if (footerDesc->choiceButtons.style == ROUNDED_AND_FOOTER_STYLE) {
3193 layoutInt->footerContainer->obj.area.height = ROUNDED_AND_FOOTER_FOOTER_HEIGHT;
3194 }
3195 else if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
3196 layoutInt->footerContainer->obj.area.height = BOTH_ROUNDED_FOOTER_HEIGHT;
3197 }
3198 else {
3199 layoutInt->footerContainer->obj.area.height = ACTION_AND_FOOTER_FOOTER_HEIGHT;
3200 }
3201
3202 break;
3203 }
3204 default:
3205 return -2;
3206 }
3207
3208 // add swipable feature for navigation
3209 if ((footerDesc->type == FOOTER_NAV) || (footerDesc->type == FOOTER_TEXT_AND_NAV)) {
3210 addSwipeInternal(layoutInt,
3211 ((1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT)),
3213 (footerDesc->type == FOOTER_NAV) ? footerDesc->navigation.token
3214 : footerDesc->textAndNav.navigation.token,
3215 (footerDesc->type == FOOTER_NAV)
3216 ? footerDesc->navigation.tuneId
3217 : footerDesc->textAndNav.navigation.tuneId);
3218 }
3219
3220 if (footerDesc->separationLine) {
3221 line = createHorizontalLine(layoutInt->layer);
3222 line->obj.alignment = TOP_MIDDLE;
3223 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3224 = (nbgl_obj_t *) line;
3225 layoutInt->footerContainer->nbChildren++;
3226 }
3227 if (separationLine != NULL) {
3228 layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
3229 = (nbgl_obj_t *) separationLine;
3230 layoutInt->footerContainer->nbChildren++;
3231 }
3232
3233 layoutInt->children[FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->footerContainer;
3234
3235 // subtract footer height from main container height
3236 layoutInt->container->obj.area.height -= layoutInt->footerContainer->obj.area.height;
3237
3238 layoutInt->footerType = footerDesc->type;
3239
3240 return layoutInt->footerContainer->obj.area.height;
3241}
3242
3252{
3253 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3254 layoutObj_t *obj;
3255 nbgl_text_area_t *textArea;
3256 nbgl_line_t *line;
3257 nbgl_button_t *button;
3258
3259 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddUpFooter():\n");
3260 if (layout == NULL) {
3261 return -1;
3262 }
3263 if ((upFooterDesc == NULL) || (upFooterDesc->type >= NB_UP_FOOTER_TYPES)) {
3264 return -2;
3265 }
3266
3267 layoutInt->upFooterContainer
3269 layoutInt->upFooterContainer->obj.area.width = SCREEN_WIDTH;
3270 layoutInt->upFooterContainer->layout = VERTICAL;
3271 // maximum 4 children for long press button
3272 layoutInt->upFooterContainer->children
3273 = (nbgl_obj_t **) nbgl_containerPoolGet(4, layoutInt->layer);
3274 layoutInt->upFooterContainer->obj.alignTo = (nbgl_obj_t *) layoutInt->container;
3275 layoutInt->upFooterContainer->obj.alignment = BOTTOM_MIDDLE;
3276
3277 switch (upFooterDesc->type) {
3278 case UP_FOOTER_LONG_PRESS: {
3279 nbgl_progress_bar_t *progressBar;
3280
3281 obj = layoutAddCallbackObj(layoutInt,
3282 (nbgl_obj_t *) layoutInt->upFooterContainer,
3283 upFooterDesc->longPress.token,
3284 upFooterDesc->longPress.tuneId);
3285 if (obj == NULL) {
3286 return -1;
3287 }
3288 layoutInt->upFooterContainer->nbChildren = 4;
3289 layoutInt->upFooterContainer->obj.area.height = LONG_PRESS_BUTTON_HEIGHT;
3290 layoutInt->upFooterContainer->obj.touchId = LONG_PRESS_BUTTON_ID;
3291 layoutInt->upFooterContainer->obj.touchMask
3292 = ((1 << TOUCHING) | (1 << TOUCH_RELEASED) | (1 << OUT_OF_TOUCH)
3293 | (1 << SWIPED_LEFT) | (1 << SWIPED_RIGHT));
3294
3295 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3296 button->obj.alignmentMarginX = BORDER_MARGIN;
3297 button->obj.alignment = MID_RIGHT;
3298 button->innerColor = BLACK;
3299 button->foregroundColor = WHITE;
3300 button->borderColor = BLACK;
3301 button->obj.area.width = BUTTON_DIAMETER;
3302 button->obj.area.height = BUTTON_DIAMETER;
3303 button->radius = BUTTON_RADIUS;
3304 button->icon = PIC(&VALIDATE_ICON);
3305 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3306
3307 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3308 textArea->textColor = BLACK;
3309 textArea->text = PIC(upFooterDesc->longPress.text);
3310 textArea->textAlignment = MID_LEFT;
3311 textArea->fontId = LARGE_MEDIUM_FONT;
3312 textArea->wrapping = true;
3313 textArea->obj.area.width = SCREEN_WIDTH - 3 * BORDER_MARGIN - button->obj.area.width;
3314 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3315 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3316 textArea->style = NO_STYLE;
3317 textArea->obj.alignment = MID_LEFT;
3318 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3319 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) textArea;
3320
3321 line = createHorizontalLine(layoutInt->layer);
3322 line->obj.alignment = TOP_MIDDLE;
3323 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) line;
3324
3325 progressBar = (nbgl_progress_bar_t *) nbgl_objPoolGet(PROGRESS_BAR, layoutInt->layer);
3326 progressBar->obj.area.width = SCREEN_WIDTH;
3327 progressBar->obj.area.height = LONG_PRESS_PROGRESS_HEIGHT;
3328 progressBar->obj.alignment = TOP_MIDDLE;
3329 progressBar->obj.alignmentMarginY = LONG_PRESS_PROGRESS_ALIGN;
3330 progressBar->resetIfOverriden = true;
3331 progressBar->partialRedraw = true;
3332 layoutInt->upFooterContainer->children[3] = (nbgl_obj_t *) progressBar;
3333 break;
3334 }
3335 case UP_FOOTER_BUTTON: {
3336 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3337 obj = layoutAddCallbackObj(layoutInt,
3338 (nbgl_obj_t *) button,
3339 upFooterDesc->button.token,
3340 upFooterDesc->button.tuneId);
3341 if (obj == NULL) {
3342 return -1;
3343 }
3344
3345 layoutInt->upFooterContainer->nbChildren = 1;
3346 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3347 button->obj.alignment = CENTER;
3348
3349 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3350 button->innerColor = BLACK;
3351 button->foregroundColor = WHITE;
3352 }
3353 else {
3354 button->innerColor = WHITE;
3355 button->foregroundColor = BLACK;
3356 }
3357 if (upFooterDesc->button.style == NO_BORDER) {
3358 button->borderColor = WHITE;
3359 }
3360 else {
3361 if (upFooterDesc->button.style == BLACK_BACKGROUND) {
3362 button->borderColor = BLACK;
3363 }
3364 else {
3365 button->borderColor = LIGHT_GRAY;
3366 }
3367 }
3368 button->text = PIC(upFooterDesc->button.text);
3369 button->fontId = SMALL_BOLD_FONT;
3370 button->icon = PIC(upFooterDesc->button.icon);
3371 button->obj.area.width = AVAILABLE_WIDTH;
3372 button->obj.area.height = BUTTON_DIAMETER;
3373 button->radius = BUTTON_RADIUS;
3374
3375 button->obj.alignTo = NULL;
3376 button->obj.touchMask = (1 << TOUCHED);
3377 button->obj.touchId = SINGLE_BUTTON_ID;
3378 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3379 break;
3380 }
3382 // icon & text cannot be NULL
3383 if ((upFooterDesc->horizontalButtons.leftIcon == NULL)
3384 || (upFooterDesc->horizontalButtons.rightText == NULL)) {
3385 return -1;
3386 }
3387
3388 layoutInt->upFooterContainer->nbChildren = 2;
3389 layoutInt->upFooterContainer->obj.area.height = UP_FOOTER_BUTTON_HEIGHT;
3390
3391 // create left button (in white) at first
3392 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3393 obj = layoutAddCallbackObj(layoutInt,
3394 (nbgl_obj_t *) button,
3395 upFooterDesc->horizontalButtons.leftToken,
3396 upFooterDesc->horizontalButtons.tuneId);
3397 if (obj == NULL) {
3398 return -1;
3399 }
3400 // associate with with index 1
3401 obj->index = 1;
3402 button->obj.alignment = MID_LEFT;
3403 button->obj.alignmentMarginX = BORDER_MARGIN;
3404 button->borderColor = LIGHT_GRAY;
3405 button->innerColor = WHITE;
3406 button->foregroundColor = BLACK;
3407 button->obj.area.width = BUTTON_WIDTH;
3408 button->obj.area.height = BUTTON_DIAMETER;
3409 button->radius = BUTTON_RADIUS;
3410 button->icon = PIC(upFooterDesc->horizontalButtons.leftIcon);
3411 button->fontId = SMALL_BOLD_FONT;
3412 button->obj.touchMask = (1 << TOUCHED);
3413 button->obj.touchId = CHOICE_2_ID;
3414 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) button;
3415
3416 // then black button, on right
3417 button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
3418 obj = layoutAddCallbackObj(layoutInt,
3419 (nbgl_obj_t *) button,
3420 upFooterDesc->horizontalButtons.rightToken,
3421 upFooterDesc->horizontalButtons.tuneId);
3422 if (obj == NULL) {
3423 return -1;
3424 }
3425 // associate with with index 0
3426 obj->index = 0;
3427 button->obj.alignment = MID_RIGHT;
3428 button->obj.alignmentMarginX = BORDER_MARGIN;
3429 button->innerColor = BLACK;
3430 button->borderColor = BLACK;
3431 button->foregroundColor = WHITE;
3432 button->obj.area.width = AVAILABLE_WIDTH - BUTTON_WIDTH - LEFT_CONTENT_ICON_TEXT_X;
3433 button->obj.area.height = BUTTON_DIAMETER;
3434 button->radius = BUTTON_RADIUS;
3435 button->text = PIC(upFooterDesc->horizontalButtons.rightText);
3436 button->fontId = SMALL_BOLD_FONT;
3437 button->obj.touchMask = (1 << TOUCHED);
3438 button->obj.touchId = CHOICE_1_ID;
3439 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) button;
3440 break;
3441 }
3442 case UP_FOOTER_TIP_BOX: {
3443 // text cannot be NULL
3444 if (upFooterDesc->tipBox.text == NULL) {
3445 return -1;
3446 }
3447 obj = layoutAddCallbackObj(layoutInt,
3448 (nbgl_obj_t *) layoutInt->upFooterContainer,
3449 upFooterDesc->tipBox.token,
3450 upFooterDesc->tipBox.tuneId);
3451 if (obj == NULL) {
3452 return -1;
3453 }
3454 layoutInt->upFooterContainer->nbChildren = 3;
3455 layoutInt->upFooterContainer->obj.touchId = TIP_BOX_ID;
3456 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3457
3458 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3459 textArea->textColor = BLACK;
3460 textArea->text = PIC(upFooterDesc->tipBox.text);
3461 textArea->textAlignment = MID_LEFT;
3462 textArea->fontId = SMALL_REGULAR_FONT;
3463 textArea->wrapping = true;
3464 textArea->obj.area.width = AVAILABLE_WIDTH;
3465 if (upFooterDesc->tipBox.icon != NULL) {
3466 textArea->obj.area.width
3467 -= ((nbgl_icon_details_t *) PIC(upFooterDesc->tipBox.icon))->width
3468 + TIP_BOX_TEXT_ICON_MARGIN;
3469 }
3470 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3471 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3472 textArea->obj.alignment = MID_LEFT;
3473 textArea->obj.alignmentMarginX = BORDER_MARGIN;
3474 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3475 layoutInt->upFooterContainer->obj.area.height = textArea->obj.area.height;
3476
3477 line = createHorizontalLine(layoutInt->layer);
3478 line->obj.alignment = TOP_MIDDLE;
3479 layoutInt->upFooterContainer->children[1] = (nbgl_obj_t *) line;
3480
3481 if (upFooterDesc->tipBox.icon != NULL) {
3482 nbgl_image_t *image = (nbgl_image_t *) nbgl_objPoolGet(IMAGE, layoutInt->layer);
3483 image->obj.alignmentMarginX = BORDER_MARGIN;
3484 image->obj.alignment = MID_RIGHT;
3485 image->foregroundColor = BLACK;
3486 image->buffer = PIC(upFooterDesc->tipBox.icon);
3487 layoutInt->upFooterContainer->children[2] = (nbgl_obj_t *) image;
3488 if (layoutInt->upFooterContainer->obj.area.height < image->buffer->height) {
3489 layoutInt->upFooterContainer->obj.area.height = image->buffer->height;
3490 }
3491 }
3492 layoutInt->upFooterContainer->obj.area.height += 2 * TIP_BOX_MARGIN_Y;
3493
3494 break;
3495 }
3496 case UP_FOOTER_TEXT: {
3497 obj = layoutAddCallbackObj(layoutInt,
3498 (nbgl_obj_t *) layoutInt->upFooterContainer,
3499 upFooterDesc->text.token,
3500 upFooterDesc->text.tuneId);
3501 if (obj == NULL) {
3502 return -1;
3503 }
3504 layoutInt->upFooterContainer->nbChildren = 1;
3505 layoutInt->upFooterContainer->obj.area.height = SMALL_FOOTER_HEIGHT;
3506 layoutInt->upFooterContainer->obj.touchId = WHOLE_SCREEN_ID;
3507 layoutInt->upFooterContainer->obj.touchMask = (1 << TOUCHED);
3508
3509 // only create text_area if text is not empty
3510 if (strlen(PIC(upFooterDesc->text.text))) {
3511 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3512 textArea->textColor = LIGHT_TEXT_COLOR;
3513 textArea->text = PIC(upFooterDesc->text.text);
3514 textArea->textAlignment = CENTER;
3515 textArea->fontId = SMALL_REGULAR_FONT;
3516 textArea->wrapping = true;
3517 textArea->obj.area.width = AVAILABLE_WIDTH;
3518 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3519 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3520 textArea->obj.alignment = CENTER;
3521 layoutInt->upFooterContainer->children[0] = (nbgl_obj_t *) textArea;
3522 }
3523 break;
3524 }
3525 default:
3526 return -2;
3527 }
3528
3529 // subtract up footer height from main container height
3530 layoutInt->container->obj.area.height -= layoutInt->upFooterContainer->obj.area.height;
3531
3532 layoutInt->children[UP_FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->upFooterContainer;
3533
3534 layoutInt->upFooterType = upFooterDesc->type;
3535
3536 return layoutInt->upFooterContainer->obj.area.height;
3537}
3538
3553 uint8_t activePage,
3554 uint8_t nbPages,
3555 bool withBack,
3556 uint8_t backToken,
3557 tune_index_e tuneId)
3558{
3560 .separationLine = false,
3561 .progressAndBack.activePage = activePage,
3562 .progressAndBack.nbPages = nbPages,
3563 .progressAndBack.token = backToken,
3564 .progressAndBack.tuneId = tuneId,
3565 .progressAndBack.withBack = withBack,
3566 .progressAndBack.actionIcon = NULL,
3567 .progressAndBack.actionToken = NBGL_INVALID_TOKEN};
3568 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddProgressIndicator():\n");
3569
3570 return nbgl_layoutAddHeader(layout, &headerDesc);
3571}
3572
3584 const char *text,
3585 const char *subText,
3586 uint8_t initPosition)
3587{
3588 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3589 nbgl_container_t *container;
3590 nbgl_text_area_t *textArea;
3591 nbgl_spinner_t *spinner;
3592
3593 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddSpinner():\n");
3594 if (layout == NULL) {
3595 return -1;
3596 }
3597
3598 container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
3599 // spinner + text + subText
3600 container->nbChildren = 3;
3601 container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
3602
3603 container->obj.area.width = AVAILABLE_WIDTH;
3604 container->layout = VERTICAL;
3605 container->obj.alignment = CENTER;
3606
3607 // create spinner
3608 spinner = (nbgl_spinner_t *) nbgl_objPoolGet(SPINNER, layoutInt->layer);
3609 spinner->position = initPosition;
3610 spinner->obj.alignment = TOP_MIDDLE;
3611 // set this new spinner as child of the container
3612 container->children[0] = (nbgl_obj_t *) spinner;
3613
3614 // update container height
3615 container->obj.area.height += SPINNER_HEIGHT;
3616
3617 // create text area
3618 textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3619 textArea->textColor = BLACK;
3620 textArea->text = PIC(text);
3621 textArea->textAlignment = CENTER;
3622 textArea->fontId = (subText != NULL) ? LARGE_MEDIUM_FONT : SMALL_REGULAR_FONT;
3623 textArea->wrapping = true;
3624 textArea->obj.alignmentMarginY = SPINNER_TEXT_MARGIN;
3625 textArea->obj.alignTo = (nbgl_obj_t *) spinner;
3626 textArea->obj.alignment = BOTTOM_MIDDLE;
3627 textArea->obj.area.width = AVAILABLE_WIDTH;
3628 textArea->obj.area.height = nbgl_getTextHeightInWidth(
3629 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
3630 textArea->style = NO_STYLE;
3631
3632 // update container height
3633 container->obj.area.height += textArea->obj.alignmentMarginY + textArea->obj.area.height;
3634
3635 // set this text as child of the container
3636 container->children[1] = (nbgl_obj_t *) textArea;
3637
3638 if (subText != NULL) {
3639 nbgl_text_area_t *subTextArea;
3640 // create sub-text area
3641 subTextArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
3642 subTextArea->textColor = BLACK;
3643 subTextArea->text = PIC(subText);
3644 subTextArea->textAlignment = CENTER;
3645 subTextArea->fontId = SMALL_REGULAR_FONT;
3646 subTextArea->wrapping = true;
3647 subTextArea->obj.alignmentMarginY = SPINNER_INTER_TEXTS_MARGIN;
3648 subTextArea->obj.alignTo = (nbgl_obj_t *) textArea;
3649 subTextArea->obj.alignment = BOTTOM_MIDDLE;
3650 subTextArea->obj.area.width = AVAILABLE_WIDTH;
3651 subTextArea->obj.area.height = nbgl_getTextHeightInWidth(subTextArea->fontId,
3652 subTextArea->text,
3653 subTextArea->obj.area.width,
3654 subTextArea->wrapping);
3655 subTextArea->style = NO_STYLE;
3656
3657 // update container height
3658 container->obj.area.height
3659 += subTextArea->obj.alignmentMarginY + subTextArea->obj.area.height;
3660
3661 // set thissub-text as child of the container
3662 container->children[2] = (nbgl_obj_t *) subTextArea;
3663 }
3664 layoutAddObject(layoutInt, (nbgl_obj_t *) container);
3665
3666 if (initPosition != SPINNER_FIXED) {
3667 // update ticker to update the spinner periodically
3669
3670 tickerCfg.tickerIntervale = SPINNER_REFRESH_PERIOD; // ms
3671 tickerCfg.tickerValue = SPINNER_REFRESH_PERIOD; // ms
3672 tickerCfg.tickerCallback = &spinnerTickerCallback;
3673 nbgl_screenUpdateTicker(layoutInt->layer, &tickerCfg);
3674 }
3675
3676 return 0;
3677}
3678
3691 const char *text,
3692 const char *subText,
3693 uint8_t position)
3694{
3695 nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
3696 nbgl_container_t *container;
3697 nbgl_text_area_t *textArea;
3698 nbgl_spinner_t *spinner;
3699 int ret = 0;
3700
3701 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutUpdateSpinner():\n");
3702 if ((layout == NULL) || (layoutInt->container->nbChildren == 0)) {
3703 return -1;
3704 }
3705
3706 container = (nbgl_container_t *) layoutInt->container->children[0];
3707 if ((container->obj.type != CONTAINER) || (container->nbChildren < 2)) {
3708 return -1;
3709 }
3710
3711 spinner = (nbgl_spinner_t *) container->children[0];
3712 if (spinner->obj.type != SPINNER) {
3713 return -1;
3714 }
3715 // if position is different, redraw
3716 if (spinner->position != position) {
3717 spinner->position = position;
3718 nbgl_objDraw((nbgl_obj_t *) spinner);
3719 ret = 1;
3720 }
3721
3722 // update text area if necessary
3723 textArea = (nbgl_text_area_t *) container->children[1];
3724 if (textArea->obj.type != TEXT_AREA) {
3725 return -1;
3726 }
3727 const char *newText = PIC(text);
3728 size_t newTextLen = strlen(newText);
3729 // if text is different, redraw (don't use strcmp because it crashes with Rust SDK)
3730 if ((newTextLen != strlen(textArea->text)) || memcmp(textArea->text, newText, newTextLen)) {
3731 textArea->text = newText;
3732 nbgl_objDraw((nbgl_obj_t *) textArea);
3733 ret = 2;
3734 }
3735
3736 if (subText != NULL) {
3737 nbgl_text_area_t *subTextArea;
3738
3739 if (container->nbChildren != 3) {
3740 return -1;
3741 }
3742 subTextArea = (nbgl_text_area_t *) container->children[2];
3743 if (subTextArea->obj.type != TEXT_AREA) {
3744 return -1;
3745 }
3746 const char *newSubText = PIC(subText);
3747 size_t newSubTextLen = strlen(newSubText);
3748 // if text is different, redraw
3749 if ((newSubTextLen != strlen(subTextArea->text))
3750 || memcmp(subTextArea->text, newSubText, newSubTextLen)) {
3751 subTextArea->text = newSubText;
3752 nbgl_objDraw((nbgl_obj_t *) subTextArea);
3753 ret = 2;
3754 }
3755 }
3756
3757 return ret;
3758}
3759
3767{
3768 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3769
3770 if (layout == NULL) {
3771 return -1;
3772 }
3773 LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutDraw(): layout->isUsed = %d\n", layout->isUsed);
3774 if (layout->tapText) {
3775 // set this new container as child of main container
3776 layoutAddObject(layout, (nbgl_obj_t *) layout->tapText);
3777 }
3778#ifdef TARGET_STAX
3779 if (layout->withLeftBorder == true) {
3780 // draw now the line
3781 nbgl_line_t *line = createLeftVerticalLine(layout->layer);
3782 layout->children[LEFT_BORDER_INDEX] = (nbgl_obj_t *) line;
3783 }
3784#endif // TARGET_STAX
3786
3787 return 0;
3788}
3789
3797{
3798 nbgl_layoutInternal_t *layout = (nbgl_layoutInternal_t *) layoutParam;
3799 LOG_DEBUG(PAGE_LOGGER, "nbgl_layoutRelease(): \n");
3800 if ((layout == NULL) || (!layout->isUsed)) {
3801 return -1;
3802 }
3803 // if modal
3804 if (layout->modal) {
3805 nbgl_screenPop(layout->layer);
3806 // if this layout was on top, use its bottom layout as top
3807 if (layout == topLayout) {
3808 topLayout = layout->bottom;
3809 topLayout->top = NULL;
3810 }
3811 else {
3812 // otherwise connect top to bottom
3813 layout->bottom->top = layout->top;
3814 layout->top->bottom = layout->bottom;
3815 }
3816 }
3817 layout->isUsed = false;
3818 return 0;
3819}
3820
3821#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:392
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:354
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:725
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:404
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.
#define LAYOUT_OBJ_POOL_LEN
Max number of complex objects with callback retrievable from pool.
bool layoutNavigationCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType, uint8_t nbPages, uint8_t *activePage)
function to be called when any of the controls in navigation bar is touched
@ UP_FOOTER_INDEX
@ FOOTER_INDEX
@ HEADER_INDEX
@ TOP_RIGHT_BUTTON_INDEX
@ MAIN_CONTAINER_INDEX
@ LEFT_BORDER_INDEX
@ NB_MAX_SCREEN_CHILDREN
API to draw all basic graphic objects.
struct PACKED__ nbgl_line_s nbgl_line_t
struct to represent a vertical or horizontal line
struct PACKED__ nbgl_navigation_bar_s nbgl_page_indicator_t
struct to represent a navigation bar (PAGE_INDICATOR type) There can be up to 5 page indicators,...
struct PACKED__ nbgl_radio_s nbgl_radio_t
struct to represent a radio button (RADIO_BUTTON type)
#define NB_SPINNER_POSITIONS
Definition nbgl_obj.h:264
#define LIGHT_TEXT_COLOR
Definition nbgl_obj.h:282
void(* nbgl_touchCallback_t)(void *obj, nbgl_touchType_t eventType)
prototype of function to be called when a touch event is received by an object
Definition nbgl_obj.h:346
struct PACKED__ nbgl_text_area_s nbgl_text_area_t
struct to represent a text area (TEXT_AREA type)
void nbgl_objDraw(nbgl_obj_t *obj)
This function draws or redraws the given object and its children (recursive version)
Definition nbgl_obj.c:1654
struct PACKED__ nbgl_progress_bar_s nbgl_progress_bar_t
struct to represent a progress bar (PROGRESS_BAR type)
nbgl_obj_t ** nbgl_containerPoolGet(uint8_t nbObjs, uint8_t layer)
Gets a new container from the pool, with the given number of obj pointers.
#define SWIPE_MASK
Definition nbgl_obj.h:292
nbgl_obj_t * nbgl_objPoolGet(nbgl_obj_type_t type, uint8_t layer)
Gets a new graphic object from the pool, with the given type. The type field of the object is set.
void nbgl_refreshSpecial(nbgl_refresh_mode_t mode)
This functions refreshes the actual screen on display with what has changed since the last refresh,...
Definition nbgl_obj.c:1707
struct PACKED__ nbgl_image_s nbgl_image_t
struct to represent an image (IMAGE type)
#define INACTIVE_SMALL_FONT
Definition nbgl_obj.h:281
struct PACKED__ nbgl_button_s nbgl_button_t
struct to represent a button (BUTTON type) that can contain a text and/or an icon
#define INACTIVE_COLOR
Definition nbgl_obj.h:279
#define INACTIVE_TEXT_COLOR
Definition nbgl_obj.h:280
void nbgl_refreshSpecialWithPostRefresh(nbgl_refresh_mode_t mode, nbgl_post_refresh_t post_refresh)
Definition nbgl_obj.c:1723
@ CHOICE_1_ID
Definition nbgl_obj.h:683
@ TOP_RIGHT_BUTTON_ID
Definition nbgl_obj.h:679
@ LONG_PRESS_BUTTON_ID
Definition nbgl_obj.h:692
@ CONTROLS_ID
Definition nbgl_obj.h:694
@ RIGHT_BUTTON_ID
Definition nbgl_obj.h:677
@ WHOLE_SCREEN_ID
Definition nbgl_obj.h:678
@ BOTTOM_BUTTON_ID
Definition nbgl_obj.h:675
@ SINGLE_BUTTON_ID
Definition nbgl_obj.h:681
@ BACK_BUTTON_ID
Definition nbgl_obj.h:680
@ VALUE_BUTTON_1_ID
Definition nbgl_obj.h:689
@ TIP_BOX_ID
Definition nbgl_obj.h:693
@ EXTRA_BUTTON_ID
Definition nbgl_obj.h:682
@ CHOICE_2_ID
Definition nbgl_obj.h:684
#define BUTTON_DIAMETER
Definition nbgl_obj.h:99
struct PACKED__ nbgl_container_s nbgl_container_t
struct to represent a container (CONTAINER type)
#define BUTTON_WIDTH
Definition nbgl_obj.h:104
struct PACKED__ nbgl_switch_s nbgl_switch_t
struct to represent a switch (size is fixed) (SWITCH type)
struct PACKED__ nbgl_obj_s nbgl_obj_t
Common structure for all graphical objects.
struct PACKED__ nbgl_spinner_s nbgl_spinner_t
struct to represent a "spinner", represented by the Ledger corners, in gray, with one of the corners ...
struct PACKED__ nbgl_qrcode_s nbgl_qrcode_t
struct to represent a QR code (QR_CODE type), whose size is fixed
API to manage screens.
int nbgl_screenSet(nbgl_obj_t ***elements, uint8_t nbElements, const nbgl_screenTickerConfiguration_t *ticker, nbgl_touchCallback_t touchCallback)
Configures the lowest layer screen. To be used by applications A nbgl_screenRedraw() can be called af...
int nbgl_screenPush(nbgl_obj_t ***elements, uint8_t nbElements, const nbgl_screenTickerConfiguration_t *ticker, nbgl_touchCallback_t touchCallback)
Pushes a screen on top of the stack, with the given number of elements, if possible....
void nbgl_wait_pipeline(void)
int nbgl_screenUpdateTicker(uint8_t screenIndex, const nbgl_screenTickerConfiguration_t *ticker)
Updates the ticker configuration of the screen at the given screenIndex, always set at WHITE in.
int nbgl_screenPop(uint8_t screenIndex)
Release the screen at the given index in screen array (index returned by nbgl_screenPush())....
struct PACKED__ nbgl_screenTickerConfiguration_s nbgl_screenTickerConfiguration_t
struct to configure a screen layer
int nbgl_screenUpdateBackgroundColor(uint8_t screenIndex, color_t color)
Updates the background color of the screen at the given screenIndex, always set at WHITE in.
void nbgl_screenRedraw(void)
This function redraws the whole screen on top of stack and its children.
Definition nbgl_screen.c:66
uint32_t nbgl_touchGetTouchDuration(nbgl_obj_t *obj)
Definition nbgl_touch.c:403
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
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