Skip to content

Commit

Permalink
Improve action button in Home Screen
Browse files Browse the repository at this point in the history
  • Loading branch information
nroggeman-ledger committed Sep 18, 2024
1 parent 73b6dbb commit 1e3558d
Show file tree
Hide file tree
Showing 11 changed files with 243 additions and 118 deletions.
4 changes: 2 additions & 2 deletions lib_nbgl/doc/mainpage.dox
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#ifdef HAVE_SE_TOUCH
/** @page nbgl_mainpage New BOLOS Graphic API for Stax
/** @page nbgl_mainpage New BOLOS Graphic API for Stax/Flex

@section nbgl_mainpage_intro Introduction

This documentation describes the different interfaces of <b>NBGL</b>, the library that
is targeted to be integrated in Stax product.
is targeted to be integrated in Stax or Flex product.

<b>NBGL Engine</b> is responsible for constructing screens and handling Touchscreen input.

Expand Down
69 changes: 67 additions & 2 deletions lib_nbgl/doc/nbgl_use_case.dox
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ A few APIs are available to draw typical Use-Cases, such as:
- @ref nbgl_useCaseReviewLight() to draw the pages of a transaction review with a simple button confirmation, when all info are available from the beginning (see @subpage use_case_review_light)
- @ref nbgl_useCaseReviewStreamingStart() to draw the pages of a regular coin transaction review, when all info are not available from the beginning (see @subpage use_case_review_streaming)
- for address verification:
- @ref nbgl_useCaseAddressConfirmation() to draw an address confirmation page, with a possibility to see it as QR Code (see @subpage use_case_addr_confirm)
- @ref nbgl_useCaseAddressConfirmationExt() to draw an address confirmation page, with a possibility to see it as QR Code and some extra tag/value pairs (see @subpage use_case_addr_confirm_ext)
- @ref nbgl_useCaseAddressReview() to draw an address confirmation page, with a possibility to see it as QR Code and some extra tag/value pairs (see @subpage use_case_addr_review)
- for keypad:
- @ref nbgl_useCaseKeypadPIN() to draw a default keypad implementation with hidden digits (see @subpage use_case_keypad)
- @ref nbgl_useCaseKeypadDigits() to draw a default keypad implementation, showing digits (see @subpage use_case_keypad)
Expand All @@ -69,6 +68,9 @@ Some APIs have also been kept for backward compatibility, and for some rare case
- @ref nbgl_useCaseReviewStart() to draw the cover page of a review (initial page, without data)
- @ref nbgl_useCaseStaticReview() to draw the data pages of a regular review, when all info are available from the beginning (all pages but the cover one)
- @ref nbgl_useCaseRegularReview() to draw the data pages of a regular review (all pages but the cover one)
- for address verification:
- @ref nbgl_useCaseAddressConfirmation() to draw an address confirmation page, with a possibility to see it as QR Code (see @subpage use_case_addr_confirm)
- @ref nbgl_useCaseAddressConfirmationExt() to draw an address confirmation page, with a possibility to see it as QR Code and some extra tag/value pairs (see @subpage use_case_addr_confirm_ext)
- for rare reviews:
- @ref nbgl_useCaseForwardOnlyReview() to draw the pages of a forward-only review (without back key)
- @ref nbgl_useCaseViewDetails() to draw the pages displaying the full value of a given long data of a review
Expand Down Expand Up @@ -155,6 +157,22 @@ void appMain(void) {
}
@endcode

@subsubsection use_case_home_settings_with_action Home & Settings screen with action button Use Case

\image{inline} html UseCase-HomeAction.png "caption" height=300

For some rare applications, one may need an action button in the Home screen, to perform either:

- The main action of the Application
- Or an side-action, as to display an address

The \b action argument of @ref nbgl_useCaseHomeAndSettings() can be used for that. This structure (@ref nbgl_homeAction_t)
enables to specify:

- A text & an icon for the button
- A function to be called when the button is touched
- The type of button (either @ref STRONG_HOME_ACTION for main action, in black, or @ref SOFT_HOME_ACTION for side action, in white)

@subsection use_case_confirm Confirmation Use Case

\image{inline} html UseCase-Confirm.png "caption" height=300
Expand Down Expand Up @@ -447,6 +465,53 @@ void staticReview(void) {
}
@endcode

@subsection use_case_addr_review Address Review Use Case

\image{inline} html UseCase-AddressReview.png "caption" height=500

When an address needs to be confirmed, it can be displayed in a Address Review Use Case.
After a title page, a sexond page with the raw address (as text). An extra button under the raw address enables to open a modal page to see the address as a QR code.
Moreover, if extra information need to be displayed, for example a derivation path, it is provided in a second page, also containing
a black button/Footer pair to choose to confirm or reject the address.

The @ref nbgl_useCaseAddressReview() function enables to create such a set of pages, with the following parameters:

- the address to confirm (NULL terminated string)
- a callback called when button or footer is touched (if true, confirm, if false reject)
- the list of extra tag/value pairs

Here is the code to display something similar to example picture:

@code
// 2 pairs of tag/value to display in second page
static nbgl_layoutTagValue_t pairs[2];

static const nbgl_contentTagValueList_t pairList = {
.nbMaxLinesForValue = 0,
.nbPairs = 2,
.pairs = (nbgl_layoutTagValue_t*)pairs
};

// called when either confirm button or reject token is called
static void displayAddressCallback(bool confirm) {
if (confirm) {
nbgl_useCaseStatus("ADDRESS\nVERIFIED",true,app_fullEthereum);
}
else {
nbgl_useCaseStatus("Address rejected",false,app_fullEthereum);
}
}

void app_ethereumVerifyAddress(void) {
nbgl_useCaseAddressReview("bc1pkdcufjh6dxjaaa05hudvxqg5fhspfmwmp8g92gq8cv4gwwnmgrfqfd4jlg",
&pairList
myAppIcon,
"Verify MyCoin\naddress",
NULL,"Cancel",
appMain);
}
@endcode

@subsection use_case_addr_confirm Address Confirmation Use Case

\image{inline} html UseCase-AddressConfirmation.png "caption" height=300
Expand Down
Binary file added lib_nbgl/doc/resources/UseCase-AddressReview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed lib_nbgl/doc/resources/UseCase-Home.png
Binary file not shown.
Binary file added lib_nbgl/doc/resources/UseCase-HomeAction.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 11 additions & 8 deletions lib_nbgl/include/nbgl_layout.h
Original file line number Diff line number Diff line change
Expand Up @@ -277,21 +277,24 @@ typedef struct {
*
*/
typedef enum {
ROUNDED_AND_FOOTER_STYLE
= 0, ///< A rounded black background full width button on top of a footer
BOTH_ROUNDED_STYLE ///< A rounded black background full width button on top of a rounded white
///< background full width button
ROUNDED_AND_FOOTER_STYLE = 0, ///< A black background button on top of a footer
STRONG_ACTION_AND_FOOTER_STYLE, ///< A black button on top of a footer, with a separation line
SOFT_ACTION_AND_FOOTER_STYLE ///< A white button on top of a footer, with a separation line
} nbgl_layoutChoiceButtonsStyle_t;

// for backward compatibility
#define BOTH_ROUNDED_STYLE SOFT_ACTION_AND_FOOTER_STYLE

/**
* @brief This structure contains info to build a pair of buttons, one on top of the other.
*
* @note the pair of button is automatically put on bottom of screen, in the footer
*/
typedef struct {
const char *topText; ///< up-button text (index 0)
const char *bottomText; ///< bottom-button text (index 1)
uint8_t token; ///< the token that will be used as argument of the callback
const char *topText; ///< up-button text (index 0)
const char *bottomText; ///< bottom-button text (index 1)
const nbgl_icon_details_t *topIcon; ///< icon of top button
uint8_t token; ///< the token that will be used as argument of the callback
nbgl_layoutChoiceButtonsStyle_t style; ///< the style of the pair
#ifdef HAVE_PIEZO_SOUND
tune_index_e tuneId; ///< if not @ref NBGL_NO_TUNE, a tune will be played
Expand Down Expand Up @@ -501,7 +504,7 @@ typedef struct {
} textAndNav; ///< if type is @ref FOOTER_TEXT_AND_NAV
nbgl_layoutNavigationBar_t navigation; ///< if type is @ref FOOTER_NAV
nbgl_layoutButton_t button; ///< if type is @ref FOOTER_SIMPLE_BUTTON
nbgl_layoutChoiceButtons_t choiceButtons; ///< if type is @ref FOOTER_SIMPLE_BUTTON
nbgl_layoutChoiceButtons_t choiceButtons; ///< if type is @ref FOOTER_CHOICE_BUTTONS
};
} nbgl_layoutFooter_t;

Expand Down
4 changes: 3 additions & 1 deletion lib_nbgl/include/nbgl_page.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ typedef struct nbgl_pageInfoDescription_s {
uint8_t tapActionToken; ///< the token that will be used as argument of the onActionCallback,
///< when tapped or swiped
const char
*actionButtonText; ///< if not NULL a black "action" button is set under the centered info
*actionButtonText; ///< if not NULL an "action" button is set under the centered info
const nbgl_icon_details_t *actionButtonIcon; ///< potential icon of "action" button
nbgl_layoutButtonStyle_t actionButtonStyle; ///< style of "action" button
tune_index_e
tuneId; ///< if not @ref NBGL_NO_TUNE, a tune will be played when button/footer is pressed
} nbgl_pageInfoDescription_t;
Expand Down
16 changes: 15 additions & 1 deletion lib_nbgl/include/nbgl_use_case.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,24 @@ typedef struct {
uint8_t nbContents; ///< number of contents
} nbgl_genericContents_t;

/**
* @brief The different types of action button in Home Screen
*
*/
typedef enum {
STRONG_HOME_ACTION = 0, ///< Black button, implicating the main action of the App
SOFT_HOME_ACTION ///< White button, more for extended features
} nbgl_homeActionStyle_t;

/**
* @brief Structure describing the action button in Home Screen
*
*/
typedef struct {
const char *text; ///< text to use in action button in Home page
const nbgl_icon_details_t *icon; ///< icon to use in action button in Home page
nbgl_callback_t callback; ///< function to call when action button is touched in Home page
nbgl_callback_t callback; ///< function to call when action button is touched in Home page
nbgl_homeActionStyle_t style; ///< style of action button
} nbgl_homeAction_t;

/**
Expand Down
50 changes: 24 additions & 26 deletions lib_nbgl/src/nbgl_layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -1765,6 +1765,7 @@ int nbgl_layoutAddChoiceButtons(nbgl_layout_t *layout, const nbgl_layoutChoiceBu
footerDesc.choiceButtons.bottomText = info->bottomText;
footerDesc.choiceButtons.token = info->token;
footerDesc.choiceButtons.topText = info->topText;
footerDesc.choiceButtons.topIcon = info->topIcon;
footerDesc.choiceButtons.style = info->style;
footerDesc.choiceButtons.tuneId = info->tuneId;
return nbgl_layoutAddExtendedFooter(layout, &footerDesc);
Expand Down Expand Up @@ -2749,7 +2750,7 @@ int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_
return -1;
}

// create bottomButton (in white) at first
// create bottom button (footer) at first
button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
obj = layoutAddCallbackObj(layoutInt,
(nbgl_obj_t *) button,
Expand All @@ -2761,31 +2762,25 @@ int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_
// associate with with index 1
obj->index = 1;
// put at the bottom of the container
button->obj.alignment = BOTTOM_MIDDLE;
if (footerDesc->choiceButtons.style == ROUNDED_AND_FOOTER_STYLE) {
button->obj.alignmentMarginY = 4; // 4 pixels from screen bottom
button->borderColor = WHITE;
}
else if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
button->obj.alignmentMarginY = 4; // 4 pixels from screen bottom
button->borderColor = WHITE; // not a real round button on Flex
}
button->innerColor = WHITE;
button->foregroundColor = BLACK;
button->obj.area.width = AVAILABLE_WIDTH;
button->obj.area.height = BUTTON_DIAMETER;
button->radius = BUTTON_RADIUS;
button->text = PIC(footerDesc->choiceButtons.bottomText);
button->fontId = SMALL_BOLD_FONT;
button->obj.touchMask = (1 << TOUCHED);
button->obj.touchId = CHOICE_2_ID;
button->obj.alignment = BOTTOM_MIDDLE;
button->obj.alignmentMarginY = 4; // 4 pixels from screen bottom
button->borderColor = WHITE;
button->innerColor = WHITE;
button->foregroundColor = BLACK;
button->obj.area.width = AVAILABLE_WIDTH;
button->obj.area.height = BUTTON_DIAMETER;
button->radius = BUTTON_RADIUS;
button->text = PIC(footerDesc->choiceButtons.bottomText);
button->fontId = SMALL_BOLD_FONT;
button->obj.touchMask = (1 << TOUCHED);
button->obj.touchId = CHOICE_2_ID;
// add to bottom container
layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
= (nbgl_obj_t *) button;
layoutInt->footerContainer->nbChildren++;

// add line if needed
if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
if (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE) {
line = createHorizontalLine(layoutInt->layer);
line->obj.alignment = TOP_MIDDLE;
line->obj.alignmentMarginY = 4;
Expand All @@ -2795,7 +2790,7 @@ int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_
layoutInt->footerContainer->nbChildren++;
}

// then black button, on top of it
// then top button, on top of it
button = (nbgl_button_t *) nbgl_objPoolGet(BUTTON, layoutInt->layer);
obj = layoutAddCallbackObj(layoutInt,
(nbgl_obj_t *) button,
Expand All @@ -2807,8 +2802,8 @@ int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_
// associate with with index 0
obj->index = 0;
button->obj.alignment = TOP_MIDDLE;
button->obj.alignmentMarginY = 24; // 12 pixels from bottom button
if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
button->obj.alignmentMarginY = 24; // 24 pixels from top of container
if (footerDesc->choiceButtons.style == SOFT_ACTION_AND_FOOTER_STYLE) {
button->innerColor = WHITE;
button->borderColor = LIGHT_GRAY;
button->foregroundColor = BLACK;
Expand All @@ -2822,6 +2817,9 @@ int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_
button->obj.area.height = BUTTON_DIAMETER;
button->radius = BUTTON_RADIUS;
button->text = PIC(footerDesc->choiceButtons.topText);
button->icon = (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE)
? PIC(footerDesc->choiceButtons.topIcon)
: NULL;
button->fontId = SMALL_BOLD_FONT;
button->obj.touchMask = (1 << TOUCHED);
button->obj.touchId = CHOICE_1_ID;
Expand All @@ -2831,14 +2829,14 @@ int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_
layoutInt->footerContainer->nbChildren++;

#ifdef TARGET_STAX
if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
layoutInt->footerContainer->obj.area.height = 232;
if (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE) {
layoutInt->footerContainer->obj.area.height = 216;
}
else {
layoutInt->footerContainer->obj.area.height = 192;
}
#else // TARGET_STAX
if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
if (footerDesc->choiceButtons.style != ROUNDED_AND_FOOTER_STYLE) {
layoutInt->footerContainer->obj.area.height = 232;
}
else {
Expand Down
10 changes: 7 additions & 3 deletions lib_nbgl/src/nbgl_page.c
Original file line number Diff line number Diff line change
Expand Up @@ -362,9 +362,9 @@ nbgl_page_t *nbgl_pageDrawInfo(nbgl_layoutTouchCallback_t onActionC
// if action button but not QUIT_APP_TEXT bottom button, use a small black button
if ((info->actionButtonText != NULL) && (info->bottomButtonStyle != QUIT_APP_TEXT)) {
nbgl_layoutButton_t buttonInfo = {.fittingContent = true,
.icon = NULL,
.icon = info->actionButtonIcon,
.onBottom = false,
.style = BLACK_BACKGROUND,
.style = info->actionButtonStyle,
.text = info->actionButtonText,
.token = info->bottomButtonsToken,
.tuneId = info->tuneId};
Expand Down Expand Up @@ -397,7 +397,11 @@ nbgl_page_t *nbgl_pageDrawInfo(nbgl_layoutTouchCallback_t onActionC
.bottomText = "Quit app",
.token = info->bottomButtonsToken,
.style = BOTH_ROUNDED_STYLE,
.tuneId = info->tuneId};
.tuneId = info->tuneId,
.topIcon = info->actionButtonIcon};
buttonsInfo.style = (info->actionButtonStyle == BLACK_BACKGROUND)
? STRONG_ACTION_AND_FOOTER_STYLE
: SOFT_ACTION_AND_FOOTER_STYLE;
nbgl_layoutAddChoiceButtons(layout, &buttonsInfo);
}
else {
Expand Down
Loading

0 comments on commit 1e3558d

Please sign in to comment.