Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve action button in Home Screen #770

Merged
merged 1 commit into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
83 changes: 31 additions & 52 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
- @ref nbgl_useCaseAddressConfirmationExt() to draw an address confirmation page, with a possibility to see it as QR Code and some extra tag/value pairs
- 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,54 +465,16 @@ void staticReview(void) {
}
@endcode

@subsection use_case_addr_confirm Address Confirmation Use Case

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

When an address needs to be confirmed, it can be displayed in a Address Confirmation Use Case, at first as simple page with
the raw address (as text) and a black button/Footer pair to choose to confirm or reject the address.

An extra button under the raw address enables to open a modal page to see the address as a QR code.

The @ref nbgl_useCaseAddressConfirmation() function enables to create such a page, 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)
@subsection use_case_addr_review Address Review Use Case

Here is the code to display something similar to example picture:
\image{inline} html UseCase-AddressReview.png "caption" height=500

@code
// 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);
}
}

// called when tapping on review start page to actually display address
static void displayAddr(void) {
nbgl_useCaseAddressConfirmation("bc1pkdcufjh6dxjaaa05hudvxqg5fhspfmwmp8g92gq8cv4gwwnmgrfqfd4jlg", &displayAddressCallback);
}

void app_ethereumVerifyAddress(void) {
nbgl_useCaseReviewStart(myAppIcon,"Verify MyCoin\naddress",NULL,"Cancel",
displayAddr, appMain);
}
@endcode

@subsection use_case_addr_confirm_ext Extended Address Confirmation Use Case

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

When an address needs to be confirmed, it can be displayed in a Address Confirmation Use Case, at first as simple 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.
When an address needs to be confirmed, it can be displayed in a Address Review Use Case.
After a title page, a second page is displayed 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_useCaseAddressConfirmationExt() function enables to create such a set of pages, with the following parameters:
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)
Expand Down Expand Up @@ -522,14 +502,13 @@ static void displayAddressCallback(bool confirm) {
}
}

// called when tapping on review start page to actually display address
static void displayAddr(void) {
nbgl_useCaseAddressConfirmationExt("bc1pkdcufjh6dxjaaa05hudvxqg5fhspfmwmp8g92gq8cv4gwwnmgrfqfd4jlg", &displayAddressCallback, &pairList);
}

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

Expand Down
Binary file not shown.
Binary file not shown.
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
87 changes: 40 additions & 47 deletions lib_nbgl/src/nbgl_layout.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,23 @@
#define TAG_VALUE_ICON_WIDTH 32

#ifdef TARGET_STAX
#define RADIO_CHOICE_HEIGHT 96
#define FOOTER_HEIGHT 80
#define BAR_INTERVALE 12
#define BACK_KEY_WIDTH 88
#define FOOTER_BUTTON_HEIGHT 128
#define UP_FOOTER_BUTTON_HEIGHT 120
#define RADIO_CHOICE_HEIGHT 96
#define FOOTER_HEIGHT 80
#define BAR_INTERVALE 12
#define BACK_KEY_WIDTH 88
#define FOOTER_BUTTON_HEIGHT 128
#define UP_FOOTER_BUTTON_HEIGHT 120
#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 192
#define ACTION_AND_FOOTER_FOOTER_HEIGHT 216
#else // TARGET_STAX
#define RADIO_CHOICE_HEIGHT 92
#define FOOTER_HEIGHT 80
#define BAR_INTERVALE 16
#define BACK_KEY_WIDTH 104
#define FOOTER_BUTTON_HEIGHT 136
#define UP_FOOTER_BUTTON_HEIGHT 136
#define RADIO_CHOICE_HEIGHT 92
#define FOOTER_HEIGHT 80
#define BAR_INTERVALE 16
#define BACK_KEY_WIDTH 104
#define FOOTER_BUTTON_HEIGHT 136
#define UP_FOOTER_BUTTON_HEIGHT 136
#define ROUNDED_AND_FOOTER_FOOTER_HEIGHT 208
#define ACTION_AND_FOOTER_FOOTER_HEIGHT 232
#endif // TARGET_STAX

// refresh period of the spinner, in ms
Expand Down Expand Up @@ -1765,6 +1769,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 +2754,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 +2766,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 +2794,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 +2806,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 = BOTTOM_BORDER_MARGIN; // 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 +2821,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 @@ -2830,21 +2832,12 @@ int nbgl_layoutAddExtendedFooter(nbgl_layout_t *layout, const nbgl_layoutFooter_
= (nbgl_obj_t *) button;
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) {
nroggeman-ledger marked this conversation as resolved.
Show resolved Hide resolved
layoutInt->footerContainer->obj.area.height = ACTION_AND_FOOTER_FOOTER_HEIGHT;
}
else {
layoutInt->footerContainer->obj.area.height = 192;
layoutInt->footerContainer->obj.area.height = ROUNDED_AND_FOOTER_FOOTER_HEIGHT;
}
#else // TARGET_STAX
if (footerDesc->choiceButtons.style == BOTH_ROUNDED_STYLE) {
layoutInt->footerContainer->obj.area.height = 232;
}
else {
layoutInt->footerContainer->obj.area.height = 208;
}
#endif // TARGET_STAX

break;
}
Expand Down
Loading
Loading