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