Skip to content

Commit 4d10500

Browse files
Support tag/value list alias in tag/value list (for multi-sig)
1 parent 2de8f9a commit 4d10500

File tree

7 files changed

+313
-17
lines changed

7 files changed

+313
-17
lines changed

lib_nbgl/include/nbgl_content.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ typedef enum {
143143
ENS_ALIAS, ///< alias comes from ENS
144144
ADDRESS_BOOK_ALIAS, ///< alias comes from Address Book
145145
QR_CODE_ALIAS, ///< alias is an address to be displayed as a QR Code
146-
INFO_LIST_ALIAS ///< alias is list of infos
146+
INFO_LIST_ALIAS, ///< alias is list of infos
147+
TAG_VALUE_LIST_ALIAS
147148
} nbgl_contentValueAliasType_t;
148149

149150
/**
@@ -158,8 +159,12 @@ typedef struct {
158159
///< the QR Code
159160
const char
160161
*backText; ///< used as title of the popping page, if not NULL, otherwise "item" is used
161-
const struct nbgl_contentInfoList_s *infolist; ///< if aliasType is INFO_LIST_ALIAS
162-
nbgl_contentValueAliasType_t aliasType; ///< type of alias
162+
union {
163+
const struct nbgl_contentInfoList_s *infolist; ///< if aliasType is INFO_LIST_ALIAS
164+
const struct nbgl_contentTagValueList_s
165+
*tagValuelist; ///< if aliasType is TAG_VALUE_LIST_ALIAS
166+
};
167+
nbgl_contentValueAliasType_t aliasType; ///< type of alias
163168
} nbgl_contentValueExt_t;
164169

165170
/**
@@ -204,7 +209,7 @@ typedef void (*nbgl_contentActionCallback_t)(int token, uint8_t index, int page)
204209
/**
205210
* @brief This structure contains a list of [tag,value] pairs
206211
*/
207-
typedef struct {
212+
typedef struct nbgl_contentTagValueList_s {
208213
const nbgl_contentTagValue_t
209214
*pairs; ///< array of [tag,value] pairs (nbPairs items). If NULL, callback is used instead
210215
nbgl_contentTagValueCallback_t callback; ///< function to call to retrieve a given pair

lib_nbgl/src/nbgl_use_case.c

Lines changed: 192 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
(pagesData[pageIdx / PAGES_PER_UINT8] = pageData \
3838
<< ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS))
3939

40-
#define MAX_PAGE_NB 256
40+
#define MAX_PAGE_NB 256
41+
#define MAX_MODAL_PAGE_NB 32
4142

4243
/* Alias to clarify usage of genericContext hasStartingContent and hasFinishingContent feature */
4344
#define STARTING_CONTENT localContentsList[0]
@@ -80,6 +81,7 @@ enum {
8081
NEXT_TOKEN,
8182
QUIT_TOKEN,
8283
NAV_TOKEN,
84+
MODAL_NAV_TOKEN,
8385
SKIP_TOKEN,
8486
CONTINUE_TOKEN,
8587
ADDRESS_QRCODE_BUTTON_TOKEN,
@@ -112,6 +114,7 @@ typedef enum {
112114
typedef struct DetailsContext_s {
113115
uint8_t nbPages;
114116
uint8_t currentPage;
117+
uint8_t currentPairIdx;
115118
bool wrapping;
116119
const char *tag;
117120
const char *value;
@@ -184,7 +187,8 @@ typedef struct {
184187
currentCallback; // to be used to retrieve the pairs with value alias
185188
nbgl_layout_t modalLayout;
186189
nbgl_layout_t backgroundLayout;
187-
const nbgl_contentInfoList_t *currentInfos;
190+
const nbgl_contentInfoList_t *currentInfos;
191+
const nbgl_contentTagValueList_t *currentTagValues;
188192
} GenericContext_t;
189193

190194
typedef struct {
@@ -270,6 +274,7 @@ static GenericContext_t genericContext;
270274
static nbgl_content_t
271275
localContentsList[3]; // 3 needed for nbgl_useCaseReview (starting page / tags / final page)
272276
static uint8_t genericContextPagesInfo[MAX_PAGE_NB / PAGES_PER_UINT8];
277+
static uint8_t modalContextPagesInfo[MAX_MODAL_PAGE_NB / PAGES_PER_UINT8];
273278

274279
// contexts for bundle navigation
275280
static nbgl_BundleNavContext_t bundleNavContext;
@@ -334,12 +339,14 @@ static char reducedAddress[QRCODE_REDUCED_ADDR_LEN];
334339
**********************/
335340
static void displayReviewPage(uint8_t page, bool forceFullRefresh);
336341
static void displayDetailsPage(uint8_t page, bool forceFullRefresh);
342+
static void displayTagValueListModalPage(uint8_t pageIdx, bool forceFullRefresh);
337343
static void displayFullValuePage(const char *backText,
338344
const char *aliasText,
339345
const nbgl_contentValueExt_t *extension);
340346
static void displayInfosListModal(const char *modalTitle,
341347
const nbgl_contentInfoList_t *infos,
342348
bool fromReview);
349+
static void displayTagValueListModal(const nbgl_contentTagValueList_t *tagValues);
343350
static void displaySettingsPage(uint8_t page, bool forceFullRefresh);
344351
static void displayGenericContextPage(uint8_t pageIdx, bool forceFullRefresh);
345352
static void pageCallback(int token, uint8_t index);
@@ -421,6 +428,27 @@ static void genericContextGetPageInfo(uint8_t pageIdx, uint8_t *nbElements, bool
421428
}
422429
}
423430

431+
// Helper to set modalContext page info
432+
static void modalContextSetPageInfo(uint8_t pageIdx, uint8_t nbElements)
433+
{
434+
uint8_t pageData = SET_PAGE_NB_ELEMENTS(nbElements);
435+
436+
modalContextPagesInfo[pageIdx / PAGES_PER_UINT8]
437+
&= ~(0x0F << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS));
438+
modalContextPagesInfo[pageIdx / PAGES_PER_UINT8]
439+
|= pageData << ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
440+
}
441+
442+
// Helper to get modalContext page info
443+
static void modalContextGetPageInfo(uint8_t pageIdx, uint8_t *nbElements)
444+
{
445+
uint8_t pageData = modalContextPagesInfo[pageIdx / PAGES_PER_UINT8]
446+
>> ((pageIdx % PAGES_PER_UINT8) * PAGE_DATA_BITS);
447+
if (nbElements != NULL) {
448+
*nbElements = GET_PAGE_NB_ELEMENTS(pageData);
449+
}
450+
}
451+
424452
// Simple helper to get the number of elements inside a nbgl_content_t
425453
static uint8_t getContentNbElement(const nbgl_content_t *content)
426454
{
@@ -578,6 +606,16 @@ static void pageModalCallback(int token, uint8_t index)
578606
displayDetailsPage(index, false);
579607
}
580608
}
609+
if (token == MODAL_NAV_TOKEN) {
610+
if (index == EXIT_PAGE) {
611+
// redraw the background layer
612+
nbgl_screenRedraw();
613+
nbgl_refresh();
614+
}
615+
else {
616+
displayTagValueListModalPage(index, false);
617+
}
618+
}
581619
else if (token == QUIT_TOKEN) {
582620
// redraw the background layer
583621
nbgl_screenRedraw();
@@ -1071,28 +1109,36 @@ static bool genericContextPreparePageContent(const nbgl_content_t *p_content,
10711109

10721110
break;
10731111
}
1074-
case TAG_VALUE_CONFIRM:
1112+
case TAG_VALUE_CONFIRM: {
1113+
nbgl_contentTagValueList_t *p_tagValueList;
10751114
// only display a TAG_VALUE_CONFIRM if we are at the last page
10761115
if ((nextElementIdx + nbElementsInPage)
10771116
== p_content->content.tagValueConfirm.tagValueList.nbPairs) {
10781117
memcpy(&pageContent->tagValueConfirm,
10791118
&p_content->content.tagValueConfirm,
10801119
sizeof(pageContent->tagValueConfirm));
1081-
nbgl_contentTagValueList_t *p_tagValueList
1082-
= &pageContent->tagValueConfirm.tagValueList;
1083-
p_tagValueList->nbPairs = nbElementsInPage;
1084-
p_tagValueList->pairs
1085-
= PIC(&p_content->content.tagValueConfirm.tagValueList.pairs[nextElementIdx]);
1120+
p_tagValueList = &pageContent->tagValueConfirm.tagValueList;
10861121
}
10871122
else {
10881123
// else display it as a TAG_VALUE_LIST
1089-
pageContent->type = TAG_VALUE_LIST;
1090-
nbgl_contentTagValueList_t *p_tagValueList = &pageContent->tagValueList;
1091-
p_tagValueList->nbPairs = nbElementsInPage;
1092-
p_tagValueList->pairs
1093-
= PIC(&p_content->content.tagValueConfirm.tagValueList.pairs[nextElementIdx]);
1124+
pageContent->type = TAG_VALUE_LIST;
1125+
p_tagValueList = &pageContent->tagValueList;
1126+
}
1127+
p_tagValueList->nbPairs = nbElementsInPage;
1128+
p_tagValueList->pairs
1129+
= PIC(&p_content->content.tagValueConfirm.tagValueList.pairs[nextElementIdx]);
1130+
// parse pairs to check if any contains an alias for value
1131+
for (uint8_t i = 0; i < nbElementsInPage; i++) {
1132+
if (p_tagValueList->pairs[i].aliasValue) {
1133+
p_tagValueList->token = VALUE_ALIAS_TOKEN;
1134+
break;
1135+
}
10941136
}
1137+
// memorize pairs (or callback) for usage when alias is used
1138+
genericContext.currentPairs = p_tagValueList->pairs;
1139+
genericContext.currentCallback = p_tagValueList->callback;
10951140
break;
1141+
}
10961142
case SWITCHES_LIST:
10971143
pageContent->switchesList.nbSwitches = nbElementsInPage;
10981144
pageContent->switchesList.switches
@@ -1331,6 +1377,10 @@ static void displayFullValuePage(const char *backText,
13311377
genericContext.currentInfos = extension->infolist;
13321378
displayInfosListModal(modalTitle, extension->infolist, true);
13331379
}
1380+
else if (extension->aliasType == TAG_VALUE_LIST_ALIAS) {
1381+
genericContext.currentTagValues = extension->tagValuelist;
1382+
displayTagValueListModal(extension->tagValuelist);
1383+
}
13341384
else {
13351385
nbgl_layoutDescription_t layoutDescription = {.modal = true,
13361386
.withLeftBorder = true,
@@ -1414,6 +1464,60 @@ static void displayInfosListModal(const char *modalTitle,
14141464
nbgl_refreshSpecial(FULL_COLOR_CLEAN_REFRESH);
14151465
}
14161466

1467+
// function used to display the modal containing alias tag-value pairs
1468+
static void displayTagValueListModalPage(uint8_t pageIdx, bool forceFullRefresh)
1469+
{
1470+
nbgl_pageNavigationInfo_t info = {.activePage = pageIdx,
1471+
.nbPages = detailsContext.nbPages,
1472+
.navType = NAV_WITH_BUTTONS,
1473+
.quitToken = QUIT_TOKEN,
1474+
.navWithButtons.navToken = MODAL_NAV_TOKEN,
1475+
.navWithButtons.quitButton = true,
1476+
.navWithButtons.backButton = true,
1477+
.navWithButtons.quitText = NULL,
1478+
.progressIndicator = false,
1479+
.tuneId = TUNE_TAP_CASUAL};
1480+
nbgl_pageContent_t content = {.type = TAG_VALUE_LIST,
1481+
.topRightIcon = NULL,
1482+
.tagValueList.smallCaseForValue = true,
1483+
.tagValueList.wrapping = detailsContext.wrapping};
1484+
uint8_t nbElementsInPage;
1485+
1486+
// if first page or forward
1487+
if (detailsContext.currentPage <= pageIdx) {
1488+
modalContextGetPageInfo(pageIdx, &nbElementsInPage);
1489+
}
1490+
else {
1491+
// backward direction
1492+
modalContextGetPageInfo(pageIdx + 1, &nbElementsInPage);
1493+
detailsContext.currentPairIdx -= nbElementsInPage;
1494+
modalContextGetPageInfo(pageIdx, &nbElementsInPage);
1495+
detailsContext.currentPairIdx -= nbElementsInPage;
1496+
}
1497+
detailsContext.currentPage = pageIdx;
1498+
1499+
content.tagValueList.pairs
1500+
= &genericContext.currentTagValues->pairs[detailsContext.currentPairIdx];
1501+
content.tagValueList.nbPairs = nbElementsInPage;
1502+
detailsContext.currentPairIdx += nbElementsInPage;
1503+
if (info.nbPages == 1) {
1504+
// if only one page, no navigation bar, and use a footer instead
1505+
info.navWithButtons.quitText = "Close";
1506+
}
1507+
1508+
if (modalPageContext != NULL) {
1509+
nbgl_pageRelease(modalPageContext);
1510+
}
1511+
modalPageContext = nbgl_pageDrawGenericContentExt(&pageModalCallback, &info, &content, true);
1512+
1513+
if (forceFullRefresh) {
1514+
nbgl_refreshSpecial(FULL_COLOR_CLEAN_REFRESH);
1515+
}
1516+
else {
1517+
nbgl_refreshSpecial(FULL_COLOR_PARTIAL_REFRESH);
1518+
}
1519+
}
1520+
14171521
#ifdef NBGL_QRCODE
14181522
static void displayAddressQRCode(void)
14191523
{
@@ -1861,6 +1965,58 @@ static uint8_t getNbTagValuesInPage(uint8_t nbPairs,
18611965
return nbPairsInPage;
18621966
}
18631967

1968+
/**
1969+
* @brief computes the number of tag/values pairs displayable in a details page, with the given list
1970+
* of tag/value pairs
1971+
*
1972+
* @param nbPairs number of tag/value pairs to use in \b tagValueList
1973+
* @param tagValueList list of tag/value pairs
1974+
* @param startIndex first index to consider in \b tagValueList
1975+
* @return the number of tag/value pairs fitting in a page
1976+
*/
1977+
static uint8_t getNbTagValuesInDetailsPage(uint8_t nbPairs,
1978+
const nbgl_contentTagValueList_t *tagValueList,
1979+
uint8_t startIndex)
1980+
{
1981+
uint8_t nbPairsInPage = 0;
1982+
uint16_t currentHeight = PRE_TAG_VALUE_MARGIN; // upper margin
1983+
uint16_t maxUsableHeight = TAG_VALUE_AREA_HEIGHT;
1984+
1985+
while (nbPairsInPage < nbPairs) {
1986+
const nbgl_layoutTagValue_t *pair;
1987+
1988+
// margin between pairs
1989+
// 12 or 24 px between each tag/value pair
1990+
if (nbPairsInPage > 0) {
1991+
currentHeight += INTER_TAG_VALUE_MARGIN;
1992+
}
1993+
// fetch tag/value pair strings.
1994+
if (tagValueList->pairs != NULL) {
1995+
pair = PIC(&tagValueList->pairs[startIndex + nbPairsInPage]);
1996+
}
1997+
else {
1998+
pair = PIC(tagValueList->callback(startIndex + nbPairsInPage));
1999+
}
2000+
2001+
// tag height
2002+
currentHeight += nbgl_getTextHeightInWidth(
2003+
SMALL_REGULAR_FONT, pair->item, AVAILABLE_WIDTH, tagValueList->wrapping);
2004+
// space between tag and value
2005+
currentHeight += 4;
2006+
2007+
// value height
2008+
currentHeight += nbgl_getTextHeightInWidth(
2009+
SMALL_REGULAR_FONT, pair->value, AVAILABLE_WIDTH, tagValueList->wrapping);
2010+
2011+
// we have reached the maximum height, it means than there are to many pairs
2012+
if (currentHeight >= maxUsableHeight) {
2013+
break;
2014+
}
2015+
nbPairsInPage++;
2016+
}
2017+
return nbPairsInPage;
2018+
}
2019+
18642020
static uint8_t getNbPagesForContent(const nbgl_content_t *content,
18652021
uint8_t pageIdxStart,
18662022
bool isLast,
@@ -2729,6 +2885,29 @@ static void displayDetails(const char *tag, const char *value, bool wrapping)
27292885
displayDetailsPage(0, true);
27302886
}
27312887

2888+
// function used to display the modal containing alias tag-value pairs
2889+
static void displayTagValueListModal(const nbgl_contentTagValueList_t *tagValues)
2890+
{
2891+
uint8_t nbElements = 0;
2892+
uint8_t nbElementsInPage;
2893+
uint8_t elemIdx = 0;
2894+
2895+
// initialize context
2896+
memset(&detailsContext, 0, sizeof(detailsContext));
2897+
nbElements = tagValues->nbPairs;
2898+
2899+
while (nbElements > 0) {
2900+
nbElementsInPage = getNbTagValuesInDetailsPage(nbElements, tagValues, elemIdx);
2901+
2902+
elemIdx += nbElementsInPage;
2903+
modalContextSetPageInfo(detailsContext.nbPages, nbElementsInPage);
2904+
nbElements -= nbElementsInPage;
2905+
detailsContext.nbPages++;
2906+
}
2907+
2908+
displayTagValueListModalPage(0, true);
2909+
}
2910+
27322911
/**********************
27332912
* GLOBAL FUNCTIONS
27342913
**********************/

0 commit comments

Comments
 (0)