23#include "os_io_seph_cmd.h"
24#include "os_io_seph_ux.h"
26#include "os_helpers.h"
31#if defined(TARGET_FLEX) || defined(TARGET_APEX)
32#define USE_PARTIAL_BUTTONS 1
38#ifdef USE_PARTIAL_BUTTONS
44#ifndef USE_PARTIAL_BUTTONS
59#if defined(TARGET_STAX)
60#define TEXT_ENTRY_NORMAL_HEIGHT 64
61#define TEXT_ENTRY_COMPACT_HEIGHT 64
62#define BOTTOM_NORMAL_MARGIN 24
63#define BOTTOM_CONFIRM_MARGIN 24
64#define BOTTOM_COMPACT_MARGIN 24
65#define TOP_NORMAL_MARGIN 20
66#define TOP_CONFIRM_MARGIN 20
67#define TOP_COMPACT_MARGIN 20
68#define TITLE_ENTRY_MARGIN_Y 4
69#define TEXT_ENTRY_FONT LARGE_MEDIUM_1BPP_FONT
71#define NUMBER_TEXT_SPACE 8
72#define NUMBER_WIDTH 56
73#define DELETE_ICON C_Close_32px
74#elif defined(TARGET_FLEX)
75#define TEXT_ENTRY_NORMAL_HEIGHT 72
76#define TEXT_ENTRY_COMPACT_HEIGHT 56
77#define BOTTOM_NORMAL_MARGIN 24
78#define BOTTOM_CONFIRM_MARGIN 24
79#define BOTTOM_COMPACT_MARGIN 12
80#define TOP_NORMAL_MARGIN 20
81#define TOP_CONFIRM_MARGIN 20
82#define TOP_COMPACT_MARGIN 12
83#define TITLE_ENTRY_MARGIN_Y 4
84#define TEXT_ENTRY_FONT LARGE_MEDIUM_1BPP_FONT
86#define NUMBER_TEXT_SPACE 8
87#define NUMBER_WIDTH 56
88#define DELETE_ICON C_Close_40px
89#elif defined(TARGET_APEX)
90#define TEXT_ENTRY_NORMAL_HEIGHT 44
91#define TEXT_ENTRY_COMPACT_HEIGHT 44
92#define BOTTOM_NORMAL_MARGIN 20
93#define BOTTOM_CONFIRM_MARGIN 16
94#define BOTTOM_COMPACT_MARGIN 8
95#define TOP_NORMAL_MARGIN 20
96#define TOP_CONFIRM_MARGIN 12
97#define TOP_COMPACT_MARGIN 8
98#define TITLE_ENTRY_MARGIN_Y 4
99#define TEXT_ENTRY_FONT LARGE_MEDIUM_1BPP_FONT
101#define NUMBER_TEXT_SPACE 4
102#define NUMBER_WIDTH 40
103#define DELETE_ICON C_Close_Tiny_24px
106#ifdef USE_PARTIAL_BUTTONS
107#if defined(TARGET_FLEX)
108#define LEFT_HALF_ICON C_left_half_64px
109#define SUGGESTION_CONTAINER_HEIGHT 92
110#elif defined(TARGET_APEX)
111#define LEFT_HALF_ICON C_half_disc_left_40px_1bpp
112#define SUGGESTION_CONTAINER_HEIGHT 56
117#define SUGGESTION_BUTTONS_SIDE_MARGIN BORDER_MARGIN
118#if defined(TARGET_APEX)
119#define LINE_THICKNESS 1
120#define LINE_COLOR BLACK
122#define LINE_THICKNESS 2
123#define LINE_COLOR LIGHT_GRAY
138static nbgl_button_t *choiceButtons[NB_MAX_SUGGESTION_BUTTONS];
139static char numText[5];
140static uint8_t nbActiveButtons;
141#ifdef USE_PARTIAL_BUTTONS
153 uint8_t currentLeftButtonIndex)
155 bool needRefresh =
false;
160 && (currentLeftButtonIndex
161 < (uint32_t) (nbActiveButtons - NB_MAX_VISIBLE_SUGGESTION_BUTTONS))) {
164 currentLeftButtonIndex += NB_MAX_VISIBLE_SUGGESTION_BUTTONS;
166 = (
nbgl_obj_t *) choiceButtons[currentLeftButtonIndex];
168 for (i = 1; i < NB_MAX_VISIBLE_SUGGESTION_BUTTONS; i++) {
169 if (currentLeftButtonIndex < (uint32_t) (nbActiveButtons - i)) {
171 = (
nbgl_obj_t *) choiceButtons[currentLeftButtonIndex + i];
177 page = currentLeftButtonIndex / NB_MAX_VISIBLE_SUGGESTION_BUTTONS;
181 && (currentLeftButtonIndex > (NB_MAX_VISIBLE_SUGGESTION_BUTTONS - 1))) {
184 currentLeftButtonIndex -= NB_MAX_VISIBLE_SUGGESTION_BUTTONS;
185 for (i = 0; i < NB_MAX_VISIBLE_SUGGESTION_BUTTONS; i++) {
187 = (
nbgl_obj_t *) choiceButtons[currentLeftButtonIndex + i];
189 page = currentLeftButtonIndex / NB_MAX_VISIBLE_SUGGESTION_BUTTONS;
207#ifndef USE_PARTIAL_BUTTONS
229 indicator->activePage = page;
231#ifdef USE_PARTIAL_BUTTONS
233 if (currentLeftButtonIndex > 0) {
234 container->children[LEFT_HALF_INDEX] = (
nbgl_obj_t *) partialButtonImages[0];
237 container->children[LEFT_HALF_INDEX] = NULL;
240 if (currentLeftButtonIndex < (nbActiveButtons - NB_MAX_VISIBLE_SUGGESTION_BUTTONS)) {
241 container->children[RIGHT_HALF_INDEX] = (
nbgl_obj_t *) partialButtonImages[1];
244 container->children[RIGHT_HALF_INDEX] = NULL;
259 if ((container->nbChildren < 2) || (container->children[1]->type !=
CONTAINER)) {
266 && (nbActiveButtons > NB_MAX_VISIBLE_SUGGESTION_BUTTONS)) {
268 while (i < (uint32_t) nbActiveButtons) {
276 if (i < (uint32_t) nbActiveButtons) {
277 if (updateSuggestionButtons(suggestionsContainer, eventType, i)) {
278 os_io_seph_cmd_piezo_play_tune(TUNE_TAP_CASUAL);
300 uint16_t textEntryHeight = (compactMode ? TEXT_ENTRY_COMPACT_HEIGHT : TEXT_ENTRY_NORMAL_HEIGHT);
301 bool withCross = ((text != NULL) && (strlen(text) > 0));
305 mainContainer->nbChildren = 2;
308 mainContainer->obj.alignment =
CENTER;
313 textArea->textColor =
BLACK;
314 textArea->text = title;
315 textArea->textAlignment =
CENTER;
316 textArea->fontId = SMALL_REGULAR_FONT;
317 textArea->wrapping =
true;
321 textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
322 mainContainer->children[0] = (
nbgl_obj_t *) textArea;
323 mainContainer->obj.area.height = textArea->obj.area.height + TITLE_ENTRY_MARGIN_Y;
328 container->nbChildren = 4;
336 textArea->textColor =
BLACK;
337 snprintf(numText,
sizeof(numText),
"%d.", number);
338 textArea->text = numText;
339 textArea->textAlignment =
CENTER;
340 textArea->fontId = TEXT_ENTRY_FONT;
341 textArea->obj.area.width = NUMBER_WIDTH;
350 textArea->textColor =
BLACK;
351 textArea->text = text;
353 textArea->fontId = TEXT_ENTRY_FONT;
354 textArea->obj.area.width =
AVAILABLE_WIDTH - DELETE_ICON.width - NUMBER_TEXT_SPACE;
356 textArea->obj.alignmentMarginX = NUMBER_TEXT_SPACE;
357 textArea->obj.alignTo = container->children[0];
359 textArea->obj.area.width -= textArea->obj.alignmentMarginX + NUMBER_WIDTH;
365 textArea->autoHideLongLine =
true;
368 container->obj.area.height = textEntryHeight;
372 image->buffer = &DELETE_ICON;
374 image->foregroundColor = withCross ?
BLACK :
WHITE;
380 image->obj.alignTo = container->children[
TEXT_INDEX];
381 image->obj.alignmentMarginX = NUMBER_TEXT_SPACE;
382 image->obj.touchMask = (1 <<
TOUCHED);
399 mainContainer->children[1] = (
nbgl_obj_t *) container;
400 mainContainer->obj.area.height += container->obj.area.height;
402 return mainContainer;
406 uint8_t nbUsedButtons,
407 const char **buttonTexts,
408 int firstButtonToken,
414 nbActiveButtons = nbUsedButtons;
416 suggestionsContainer->layout =
VERTICAL;
417 suggestionsContainer->obj.area.width = SCREEN_WIDTH;
418#ifndef USE_PARTIAL_BUTTONS
420 suggestionsContainer->obj.area.height = 2 * SMALL_BUTTON_HEIGHT +
INTERNAL_MARGIN + 28;
423 suggestionsContainer->obj.area.height = SUGGESTION_CONTAINER_HEIGHT;
426 suggestionsContainer->children
430 suggestionsContainer->obj.alignmentMarginY = BOTTOM_NORMAL_MARGIN;
436 for (
int i = 0; i < NB_MAX_SUGGESTION_BUTTONS; i++) {
438 layoutInt, (
nbgl_obj_t *) choiceButtons[i], firstButtonToken + i, tuneId);
443 choiceButtons[i]->innerColor =
BLACK;
444 choiceButtons[i]->borderColor =
BLACK;
445 choiceButtons[i]->foregroundColor =
WHITE;
446 choiceButtons[i]->obj.area.width
448 choiceButtons[i]->obj.area.height = SMALL_BUTTON_HEIGHT;
449 choiceButtons[i]->radius = SMALL_BUTTON_RADIUS_INDEX;
450 choiceButtons[i]->fontId = SMALL_BOLD_1BPP_FONT;
451 choiceButtons[i]->text = buttonTexts[i];
452 choiceButtons[i]->obj.touchMask = (1 <<
TOUCHED);
455 if (i <
MIN(NB_MAX_VISIBLE_SUGGESTION_BUTTONS, nbActiveButtons)) {
464 indicator->activePage = 0;
465 indicator->nbPages = (nbActiveButtons + NB_MAX_VISIBLE_SUGGESTION_BUTTONS - 1)
466 / NB_MAX_VISIBLE_SUGGESTION_BUTTONS;
467 indicator->obj.area.width
468 = (indicator->nbPages < 3) ? STEPPER_2_PAGES_WIDTH : STEPPER_N_PAGES_WIDTH;
472#ifdef USE_PARTIAL_BUTTONS
476 partialButtonImages[0]->buffer = &LEFT_HALF_ICON;
477 partialButtonImages[0]->obj.alignment =
TOP_LEFT;
478 partialButtonImages[0]->foregroundColor =
BLACK;
480 partialButtonImages[1]->buffer = &LEFT_HALF_ICON;
481 partialButtonImages[1]->obj.alignment =
TOP_RIGHT;
482 partialButtonImages[1]->foregroundColor =
BLACK;
484 updateSuggestionButtons(suggestionsContainer, 0, 0);
487 return suggestionsContainer;
507 button->obj.alignmentMarginY = compactMode ? BOTTOM_COMPACT_MARGIN : BOTTOM_CONFIRM_MARGIN;
509 button->foregroundColor =
WHITE;
511 button->innerColor =
BLACK;
512 button->borderColor =
BLACK;
513 button->obj.touchMask = (1 <<
TOUCHED);
520 button->text = PIC(text);
521 button->fontId = SMALL_BOLD_1BPP_FONT;
524 button->radius = BUTTON_RADIUS;
546 if (layout == NULL) {
556 keyboard->obj.area.width = SCREEN_WIDTH;
557 keyboard->obj.area.height = 3 * KEYBOARD_KEY_HEIGHT;
559 keyboard->obj.area.height += KEYBOARD_KEY_HEIGHT;
562 keyboard->obj.alignmentMarginY = 56;
566 keyboard->callback = PIC(kbdInfo->
callback);
568 keyboard->mode = kbdInfo->
mode;
569 keyboard->keyMask = kbdInfo->
keyMask;
570 keyboard->casing = kbdInfo->
casing;
580 = keyboard->obj.area.height + keyboard->obj.alignmentMarginY;
619 if (layout == NULL) {
626 if ((keyboard == NULL) || (keyboard->obj.type !=
KEYBOARD)) {
629 keyboard->keyMask = keyMask;
631 keyboard->casing = casing;
652 if (layout == NULL) {
659 if ((keyboard == NULL) || (keyboard->obj.type !=
KEYBOARD)) {
662 if (keyboard->needsRefresh) {
663 keyboard->needsRefresh =
false;
697 uint8_t enteredTextIndex = (layoutInt->
container->nbChildren == 2) ? 0 : 1;
698 bool compactMode = ((layoutInt->
container->children[enteredTextIndex + 1] != NULL)
699 && (layoutInt->
container->children[enteredTextIndex + 1]->type ==
BUTTON)
700 && (layoutInt->
container->nbChildren == 3));
703 if (layout == NULL) {
709 container = addTextEntry(layoutInt, NULL, text, numbered, number, token, compactMode);
714 if (layoutInt->
container->children[enteredTextIndex + 1] != NULL) {
715 if (layoutInt->
container->children[enteredTextIndex + 1]->type ==
BUTTON) {
718 container->obj.alignmentMarginY
719 -= (button->obj.area.height + button->obj.alignmentMarginY
720 + (compactMode ? TOP_COMPACT_MARGIN : TOP_NORMAL_MARGIN))
726 container->obj.alignmentMarginY
727 -= (suggestionContainer->obj.area.height + suggestionContainer->obj.alignmentMarginY
728 + (compactMode ? TOP_COMPACT_MARGIN : TOP_NORMAL_MARGIN))
734 if (layoutInt->
container->nbChildren == 3) {
735 container->obj.alignmentMarginY += layoutInt->
container->children[0]->area.height / 2;
765 uint8_t enteredTextIndex = (layoutInt->
container->nbChildren == 2) ? 0 : 1;
768 if (layout == NULL) {
775 if ((container == NULL) || (container->obj.type !=
CONTAINER)) {
779 if ((textArea == NULL) || (textArea->obj.type !=
TEXT_AREA)) {
782 textArea->text = text;
791 snprintf(numText,
sizeof(numText),
"%d.", number);
792 textArea->text = numText;
821 uint8_t enteredTextIndex = (layoutInt->
container->nbChildren == 2) ? 0 : 1;
822 bool compactMode = (layoutInt->
container->nbChildren == 3);
825 if (layout == NULL) {
829 button = addConfirmationButton(layoutInt, active, text, token, tuneId, compactMode);
832 if (layoutInt->
container->children[enteredTextIndex] != NULL) {
834 ->obj.alignmentMarginY
835 -= (button->obj.area.height + button->obj.alignmentMarginY
836 + (compactMode ? TOP_COMPACT_MARGIN : TOP_NORMAL_MARGIN))
861 uint8_t enteredTextIndex = (layoutInt->
container->nbChildren == 2) ? 0 : 1;
866 if (layout == NULL) {
872 if ((button == NULL) || (button->obj.type !=
BUTTON)) {
878 button->innerColor =
BLACK;
879 button->borderColor =
BLACK;
880 button->obj.touchMask = (1 <<
TOUCHED);
906 if (layout == NULL) {
910 textEntryContainer = addTextEntry(layoutInt,
923 = addSuggestionButtons(layoutInt,
938 textEntryContainer->obj.alignmentMarginY
939 -= (suggestionsContainer->obj.area.height + suggestionsContainer->obj.alignmentMarginY
949 (content->
title != NULL));
952 textEntryContainer->obj.alignmentMarginY
953 -= (button->obj.area.height + button->obj.alignmentMarginY
954 + ((content->
title != NULL) ? TOP_COMPACT_MARGIN : TOP_CONFIRM_MARGIN))
957 return layoutInt->
container->obj.area.height;
978 if (layout == NULL) {
989 snprintf(numText,
sizeof(numText),
"%d.", content->
number);
995 textArea->text = content->
text;
1000 if ((textArea->text != NULL) && (strlen(textArea->text) > 0)) {
1001 if (image->foregroundColor ==
WHITE) {
1002 image->foregroundColor =
BLACK;
1007 if (image->foregroundColor ==
BLACK) {
1008 image->foregroundColor =
WHITE;
1020 for (
int i = 0; i < NB_MAX_SUGGESTION_BUTTONS; i++) {
1023 if (i <
MIN(NB_MAX_VISIBLE_SUGGESTION_BUTTONS, nbActiveButtons)) {
1031 suggestionsContainer->forceClean =
true;
1036 indicator->nbPages = (nbActiveButtons + NB_MAX_VISIBLE_SUGGESTION_BUTTONS - 1)
1037 / NB_MAX_VISIBLE_SUGGESTION_BUTTONS;
1038 indicator->activePage = 0;
1039 updateSuggestionButtons(suggestionsContainer, 0, 0);
1046 if ((button == NULL) || (button->obj.type !=
BUTTON)) {
1052 button->innerColor =
BLACK;
1053 button->borderColor =
BLACK;
1054 button->obj.touchMask = (1 <<
TOUCHED);
#define LOG_DEBUG(__logger,...)
Middle Level API of the new BOLOS Graphical Library.
uint16_t nbgl_getSingleLineTextWidth(nbgl_font_id_e fontId, const char *text)
return the max width in pixels of the given text until the first or \0 is encountered
uint8_t nbgl_getFontHeight(nbgl_font_id_e fontId)
return the height in pixels of the font with the given font ID
uint16_t nbgl_getTextHeightInWidth(nbgl_font_id_e fontId, const char *text, uint16_t maxWidth, bool wrapping)
return the height of the given multiline text, with the given font.
Font screen low-Level driver API, to draw elementary forms.
void layoutAddObject(nbgl_layoutInternal_t *layout, nbgl_obj_t *obj)
adds the given obj to the main container
layoutObj_t * layoutAddCallbackObj(nbgl_layoutInternal_t *layout, nbgl_obj_t *obj, uint8_t token, tune_index_e tuneId)
void * nbgl_layout_t
type shared externally
@ KEYBOARD_WITH_BUTTON
text entry area + confirmation button
@ KEYBOARD_WITH_SUGGESTIONS
text entry area + suggestion buttons
Internal functions/constants of NBGL layout layer.
@ SWIPE_USAGE_SUGGESTIONS
#define KEYBOARD_FOOTER_TYPE
int nbgl_layoutUpdateKeyboard(nbgl_layout_t *layout, uint8_t index, uint32_t keyMask, bool updateCasing, keyboardCase_t casing)
Updates an existing keyboard on bottom of the screen, with the given configuration.
int nbgl_layoutAddKeyboard(nbgl_layout_t *layout, const nbgl_layoutKbd_t *kbdInfo)
Creates a keyboard on bottom of the screen, with the given configuration.
bool keyboardSwipeCallback(nbgl_obj_t *obj, nbgl_touchType_t eventType)
int nbgl_layoutAddKeyboardContent(nbgl_layout_t *layout, nbgl_layoutKeyboardContent_t *content)
Adds an area containing a potential title, a text entry and either confirmation or suggestion buttons...
int nbgl_layoutAddEnteredText(nbgl_layout_t *layout, bool numbered, uint8_t number, const char *text, bool grayedOut, int offsetY, int token)
Adds a "text entry" area under the previously entered object. This area can be preceded (beginning of...
int nbgl_layoutUpdateConfirmationButton(nbgl_layout_t *layout, uint8_t index, bool active, const char *text)
Updates an existing black full width confirmation button on top of the previously added keyboard.
int nbgl_layoutUpdateEnteredText(nbgl_layout_t *layout, uint8_t index, bool numbered, uint8_t number, const char *text, bool grayedOut)
Updates an existing "text entry" area, created with nbgl_layoutAddEnteredText()
#define SUGGESTION_BUTTONS_SIDE_MARGIN
int nbgl_layoutUpdateKeyboardContent(nbgl_layout_t *layout, nbgl_layoutKeyboardContent_t *content)
Updates an area containing a potential title, a text entry and either confirmation or suggestion butt...
int nbgl_layoutAddConfirmationButton(nbgl_layout_t *layout, bool active, const char *text, int token, tune_index_e tuneId)
Adds a black full width confirmation button on top of the previously added keyboard.
bool nbgl_layoutKeyboardNeedsRefresh(nbgl_layout_t *layout, uint8_t index)
function called to know whether the keyboard has been redrawn and needs a refresh
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_text_area_s nbgl_text_area_t
struct to represent a text area (TEXT_AREA type)
void nbgl_objDraw(nbgl_obj_t *obj)
This function draws or redraws the given object and its children (recursive version)
nbgl_obj_t ** nbgl_containerPoolGet(uint8_t nbObjs, uint8_t layer)
Gets a new container from the pool, with the given number of obj pointers.
keyboardCase_t
Letters casing in which to open/set the keyboard.
nbgl_obj_t * nbgl_objPoolGet(nbgl_obj_type_t type, uint8_t layer)
Gets a new graphic object from the pool, with the given type. The type field of the object is set.
struct PACKED__ nbgl_keyboard_s nbgl_keyboard_t
struct to represent a keyboard (KEYBOARD type)
void nbgl_refreshSpecial(nbgl_refresh_mode_t mode)
This functions refreshes the actual screen on display with what has changed since the last refresh,...
struct PACKED__ nbgl_image_s nbgl_image_t
struct to represent an image (IMAGE type)
int nbgl_objPoolGetArray(nbgl_obj_type_t type, uint8_t nbObjs, uint8_t layer, nbgl_obj_t **objArray)
Gets nbObjects new graphic object from the pool, with the given type, for the given layer (screen)....
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_TEXT_COLOR
struct PACKED__ nbgl_container_s nbgl_container_t
struct to represent a container (CONTAINER type)
@ CURRENT_INDICATOR
only current page dash is black
struct PACKED__ nbgl_obj_s nbgl_obj_t
Common structure for all graphical objects.
nbgl_touchType_t
The different types of Touchscreen events.
@ VERTICAL
from top to bottom
@ HORIZONTAL
from left to right
@ IMAGE
Bitmap (y and height must be multiple of 4 on Stax)
@ BUTTON
Rounded rectangle button with icon and/or text.
@ PAGE_INDICATOR
horizontal bar to indicate position within pages
@ LINE
Vertical or Horizontal line.
@ CONTAINER
Empty container.
@ TEXT_AREA
Area to contain text line(s)
#define NO_TRANSFORMATION
@ FULL_COLOR_PARTIAL_REFRESH
to be used for small partial refresh (radio buttons, switches)
Structure containing all information about the current layout.
nbgl_container_t * footerContainer
container used to store footer (buttons, nav....)
uint8_t layer
layer in screen stack
nbgl_swipe_usage_t swipeUsage
nbgl_container_t * container
nbgl_layoutFooterType_t footerType
type of footer
nbgl_obj_t ** children
children for main screen
This structure contains info to build a keyboard with nbgl_layoutAddKeyboard()
bool lettersOnly
if true, only display letter keys and Backspace
keyboardCallback_t callback
function called when an active key is pressed
keyboardMode_t mode
keyboard mode to start with
keyboardCase_t casing
keyboard casing mode (lower, upper once or upper locked)
This structure contains info to build a keyboard content (controls that are linked to keyboard)
uint8_t number
if numbered is true, number used to build 'number.' text
const char * text
already entered text
tune_index_e tuneId
if not NBGL_NO_TUNE, a tune will be played
nbgl_layoutKeyboardContentType_t type
type of content
nbgl_layoutSuggestionButtons_t suggestionButtons
nbgl_layoutConfirmationButton_t confirmationButton
used if type is KEYBOARD_WITH_SUGGESTIONS
const char * title
centered title explaining the screen
bool numbered
if set to true, the text is preceded on the left by 'number.'