Embedded SDK
Embedded SDK
nbgl_layout_keypad.c
Go to the documentation of this file.
1 
7 #ifdef HAVE_SE_TOUCH
8 #ifdef NBGL_KEYPAD
9 /*********************
10  * INCLUDES
11  *********************/
12 #include <string.h>
13 #include <stdlib.h>
14 #include "nbgl_debug.h"
15 #include "nbgl_front.h"
16 #include "nbgl_layout_internal.h"
17 #include "nbgl_obj.h"
18 #include "nbgl_draw.h"
19 #include "nbgl_screen.h"
20 #include "nbgl_touch.h"
21 #include "glyphs.h"
22 #include "os_pic.h"
23 #include "os_helpers.h"
24 
25 /*********************
26  * DEFINES
27  *********************/
28 #ifdef TARGET_STAX
29 #define DIGIT_ICON C_round_24px
30 #else // TARGET_STAX
31 #define DIGIT_ICON C_pin_24
32 #endif // TARGET_STAX
33 
34 enum {
39 };
40 
41 /**********************
42  * MACROS
43  **********************/
44 
45 /**********************
46  * TYPEDEFS
47  **********************/
48 
49 /**********************
50  * VARIABLES
51  **********************/
52 
53 /**********************
54  * STATIC PROTOTYPES
55  **********************/
56 
57 /**********************
58  * GLOBAL API FUNCTIONS
59  **********************/
60 
71 int nbgl_layoutAddKeypad(nbgl_layout_t *layout, keyboardCallback_t callback, bool shuffled)
72 {
73  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
74  nbgl_keypad_t *keypad;
75 
76  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddKeypad():\n");
77  if (layout == NULL) {
78  return -1;
79  }
80  // footer must be empty
81  if (layoutInt->footerContainer != NULL) {
82  return -1;
83  }
84 
85  // create keypad
86  keypad = (nbgl_keypad_t *) nbgl_objPoolGet(KEYPAD, layoutInt->layer);
87  keypad->obj.alignmentMarginY = 0;
88  keypad->obj.alignment = BOTTOM_MIDDLE;
89  keypad->obj.alignTo = NULL;
90  keypad->obj.area.width = SCREEN_WIDTH;
91  keypad->obj.area.height = 4 * KEYPAD_KEY_HEIGHT;
92  keypad->borderColor = LIGHT_GRAY;
93  keypad->callback = PIC(callback);
94  keypad->enableDigits = true;
95  keypad->enableBackspace = false;
96  keypad->enableValidate = false;
97  keypad->shuffled = shuffled;
98 
99  // the keypad occupies the footer
100  layoutInt->footerContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
101  layoutInt->footerContainer->obj.area.width = SCREEN_WIDTH;
102  layoutInt->footerContainer->layout = VERTICAL;
103  layoutInt->footerContainer->children
104  = (nbgl_obj_t **) nbgl_containerPoolGet(1, layoutInt->layer);
105  layoutInt->footerContainer->obj.alignment = BOTTOM_MIDDLE;
106  layoutInt->footerContainer->obj.area.height = keypad->obj.area.height;
107  layoutInt->footerContainer->children[layoutInt->footerContainer->nbChildren]
108  = (nbgl_obj_t *) keypad;
109  layoutInt->footerContainer->nbChildren++;
110 
111  // add to layout children
112  layoutInt->children[FOOTER_INDEX] = (nbgl_obj_t *) layoutInt->footerContainer;
113 
114  // subtract footer height from main container height
115  layoutInt->container->obj.area.height -= layoutInt->footerContainer->obj.area.height;
116 
117  layoutInt->footerType = KEYPAD_FOOTER_TYPE;
118 
119  return layoutInt->footerContainer->obj.area.height;
120 }
121 
133  uint8_t index,
134  bool enableValidate,
135  bool enableBackspace,
136  bool enableDigits)
137 {
138  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
139  nbgl_keypad_t *keypad;
140 
142  "nbgl_layoutUpdateKeypad(): enableValidate = %d, enableBackspace = %d\n",
143  enableValidate,
144  enableBackspace);
145  if (layout == NULL) {
146  return -1;
147  }
148  UNUSED(index);
149 
150  // get existing keypad (in the footer container)
151  keypad = (nbgl_keypad_t *) layoutInt->footerContainer->children[0];
152  if ((keypad == NULL) || (keypad->obj.type != KEYPAD)) {
153  return -1;
154  }
155  // partial redraw only if only validate and backspace have changed
156  keypad->partial = (keypad->enableDigits == enableDigits);
157  keypad->enableValidate = enableValidate;
158  keypad->enableBackspace = enableBackspace;
159  keypad->enableDigits = enableDigits;
160 
161  nbgl_objDraw((nbgl_obj_t *) keypad);
162 
163  return 0;
164 }
165 
180 {
181  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
182  nbgl_container_t *container;
183  uint8_t space;
184 
185  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddHiddenDigits():\n");
186  if (layout == NULL) {
187  return -1;
188  }
189  if (nbDigits > KEYPAD_MAX_DIGITS) {
190  return -1;
191  }
192  if (nbDigits > 8) {
193  space = 4;
194  }
195  else {
196  space = 12;
197  }
198 
199  // create a container, invisible or bordered
200  container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
201  container->nbChildren = nbDigits;
202 #ifdef TARGET_STAX
203  container->nbChildren++; // +1 for the line
204 #endif // TARGET_STAX
205  container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
206  // <space> pixels between each icon (knowing that the effective round are 18px large and the
207  // icon 24px)
208  container->obj.area.width = nbDigits * DIGIT_ICON.width + (nbDigits - 1) * space;
209 #ifdef TARGET_STAX
210  container->obj.area.height = 50;
211 #else // TARGET_STAX
212  container->obj.area.height = 64;
213 #endif // TARGET_STAX
214 
215  // item N-2 is the title
216  container->obj.alignTo = layoutInt->container->children[layoutInt->container->nbChildren - 2];
217  container->obj.alignment = BOTTOM_MIDDLE;
218 
219  // set this new container as child of the main container
220  layoutAddObject(layoutInt, (nbgl_obj_t *) container);
221 
222  // create children of the container, as images (empty circles)
223  nbgl_objPoolGetArray(IMAGE, nbDigits, layoutInt->layer, (nbgl_obj_t **) container->children);
224  for (int i = 0; i < nbDigits; i++) {
225  nbgl_image_t *image = (nbgl_image_t *) container->children[i];
226  image->buffer = &DIGIT_ICON;
227  image->foregroundColor = WHITE;
228  if (i > 0) {
229  image->obj.alignment = MID_RIGHT;
230  image->obj.alignTo = (nbgl_obj_t *) container->children[i - 1];
231  image->obj.alignmentMarginX = space;
232  }
233  else {
234  image->obj.alignment = MID_LEFT;
235  }
236  }
237 #ifdef TARGET_STAX
238  nbgl_line_t *line;
239  // create gray line
240  line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
241  line->lineColor = LIGHT_GRAY;
242  line->obj.alignmentMarginY = 0;
243  line->obj.alignTo = NULL;
244  line->obj.alignment = BOTTOM_MIDDLE;
245  line->obj.area.width = container->obj.area.width;
246  line->obj.area.height = 4;
247  line->direction = HORIZONTAL;
248  line->thickness = 2;
249  line->offset = 2;
250  container->children[nbDigits] = (nbgl_obj_t *) line;
251 #endif // TARGET_STAX
252 
253  // return index of keypad to be modified later on
254  return (layoutInt->container->nbChildren - 1);
255 }
256 
267 {
268  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
269  nbgl_container_t *container;
270  nbgl_image_t *image;
271 
272  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutUpdateHiddenDigits(): nbActive = %d\n", nbActive);
273  if (layout == NULL) {
274  return -1;
275  }
276 
277  // get container
278  container = (nbgl_container_t *) layoutInt->container->children[index];
279  // sanity check
280  if ((container == NULL) || (container->obj.type != CONTAINER)) {
281  return -1;
282  }
283  if (nbActive > container->nbChildren) {
284  return -1;
285  }
286  if (nbActive == 0) {
287  // deactivate the first digit
288  image = (nbgl_image_t *) container->children[0];
289  if ((image == NULL) || (image->obj.type != IMAGE)) {
290  return -1;
291  }
292  image->foregroundColor = WHITE;
293  }
294  else {
295  image = (nbgl_image_t *) container->children[nbActive - 1];
296  if ((image == NULL) || (image->obj.type != IMAGE)) {
297  return -1;
298  }
299  // if the last "active" is already active, it means that we are decreasing the number of
300  // active otherwise we are increasing it
301  if (image->foregroundColor == BLACK) {
302  // all digits are already active
303  if (nbActive == container->nbChildren) {
304  return 0;
305  }
306  // deactivate the next digit
307  image = (nbgl_image_t *) container->children[nbActive];
308  image->foregroundColor = WHITE;
309  }
310  else {
311  image->buffer = &DIGIT_ICON;
312  image->foregroundColor = BLACK;
313  }
314  }
315 
316  nbgl_objDraw((nbgl_obj_t *) image);
317 
318  return 0;
319 }
320 
338  const char *title,
339  bool hidden,
340  uint8_t nbDigits,
341  const char *text)
342 {
343  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
344  nbgl_container_t *container;
345  nbgl_text_area_t *textArea;
346 
347  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutAddKeypadContent():\n");
348  if (layout == NULL) {
349  return -1;
350  }
351  // create a container, to store both title and "digits" (and line on Stax)
352  container = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
353  container->nbChildren = NB_CHILDREN;
354  container->children = nbgl_containerPoolGet(container->nbChildren, layoutInt->layer);
355  container->obj.area.width = AVAILABLE_WIDTH;
356  container->obj.alignment = TOP_MIDDLE;
357  container->obj.alignmentMarginY = 8;
358 
359  // create text area for title
360  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
361  textArea->textColor = BLACK;
362  textArea->text = title;
363  textArea->textAlignment = CENTER;
364  textArea->fontId = SMALL_REGULAR_FONT;
365  textArea->wrapping = true;
366  textArea->obj.alignment = TOP_MIDDLE;
367  textArea->obj.area.width = AVAILABLE_WIDTH;
368  textArea->obj.area.height = nbgl_getTextHeightInWidth(
369  textArea->fontId, textArea->text, textArea->obj.area.width, textArea->wrapping);
370  container->children[TITLE_INDEX] = (nbgl_obj_t *) textArea;
371  container->obj.area.height += textArea->obj.area.height;
372 
373  if (hidden) {
374  nbgl_container_t *digitsContainer;
375  uint8_t space;
376 
377  if (nbDigits > KEYPAD_MAX_DIGITS) {
378  return -1;
379  }
380  // space between "digits"
381  if (nbDigits > 8) {
382  space = 4;
383  }
384  else {
385 #ifdef TARGET_STAX
386  space = 10;
387 #else // TARGET_STAX
388  space = 12;
389 #endif // TARGET_STAX
390  }
391 
392  // create digits container, to store "discs"
393  digitsContainer = (nbgl_container_t *) nbgl_objPoolGet(CONTAINER, layoutInt->layer);
394  digitsContainer->nbChildren = nbDigits;
395  digitsContainer->children
396  = nbgl_containerPoolGet(digitsContainer->nbChildren, layoutInt->layer);
397  // <space> pixels between each icon (knowing that the effective round are 18px large and the
398  // icon 24px)
399  digitsContainer->obj.area.width = nbDigits * DIGIT_ICON.width + (nbDigits - 1) * space;
400 #ifdef TARGET_STAX
401  digitsContainer->obj.area.height = 44;
402 #else // TARGET_STAX
403  digitsContainer->obj.area.height = 64;
404 #endif // TARGET_STAX
405  // align at the bottom of title
406  digitsContainer->obj.alignTo = container->children[0];
407  digitsContainer->obj.alignment = BOTTOM_MIDDLE;
408 #ifdef TARGET_STAX
409  digitsContainer->obj.alignmentMarginY = 28;
410 #endif // TARGET_STAX
411  container->children[INPUT_INDEX] = (nbgl_obj_t *) digitsContainer;
412  container->obj.area.height += digitsContainer->obj.area.height;
413 
414  // create children of the container, as images (empty circles)
416  IMAGE, nbDigits, layoutInt->layer, (nbgl_obj_t **) digitsContainer->children);
417  for (int i = 0; i < nbDigits; i++) {
418  nbgl_image_t *image = (nbgl_image_t *) digitsContainer->children[i];
419  image->buffer = &DIGIT_ICON;
420  image->foregroundColor = WHITE;
421  if (i > 0) {
422  image->obj.alignment = MID_RIGHT;
423  image->obj.alignTo = (nbgl_obj_t *) digitsContainer->children[i - 1];
424  image->obj.alignmentMarginX = space;
425  }
426  else {
427  image->obj.alignment = MID_LEFT;
428  }
429  }
430  }
431  else {
432  // create text area
433  textArea = (nbgl_text_area_t *) nbgl_objPoolGet(TEXT_AREA, layoutInt->layer);
434  textArea->textColor = BLACK;
435  textArea->text = text;
436  textArea->textAlignment = MID_LEFT;
437  textArea->fontId = LARGE_MEDIUM_1BPP_FONT;
438  textArea->obj.area.width = container->obj.area.width;
439  textArea->obj.area.height = nbgl_getFontLineHeight(textArea->fontId);
440  textArea->autoHideLongLine = true;
441  // align at the bottom of title
442  textArea->obj.alignTo = container->children[TITLE_INDEX];
443  textArea->obj.alignment = BOTTOM_MIDDLE;
444 #ifdef TARGET_STAX
445  textArea->obj.alignmentMarginY = 24;
446 #endif // TARGET_STAX
447  container->children[INPUT_INDEX] = (nbgl_obj_t *) textArea;
448  container->obj.area.height += textArea->obj.area.height;
449  }
450 
451  // set this new container as child of the main container
452  layoutAddObject(layoutInt, (nbgl_obj_t *) container);
453 #ifdef TARGET_STAX
454  nbgl_line_t *line;
455  // create gray line
456  line = (nbgl_line_t *) nbgl_objPoolGet(LINE, layoutInt->layer);
457  line->lineColor = LIGHT_GRAY;
458  line->obj.alignTo = container->children[INPUT_INDEX];
459  line->obj.alignment = BOTTOM_MIDDLE;
460  line->obj.area.width = 288;
461  line->obj.area.height = 4;
462  line->direction = HORIZONTAL;
463  line->thickness = 2;
464  line->offset = 2;
465  container->children[LINE_INDEX] = (nbgl_obj_t *) line;
466 #endif // TARGET_STAX
467 
468  // return height of the area
469  return container->obj.area.height;
470 }
471 
485  bool hidden,
486  uint8_t nbActiveDigits,
487  const char *text)
488 {
489  nbgl_layoutInternal_t *layoutInt = (nbgl_layoutInternal_t *) layout;
490  nbgl_container_t *container;
491  nbgl_image_t *image;
492 
493  LOG_DEBUG(LAYOUT_LOGGER, "nbgl_layoutUpdateHiddenDigits(): nbActive = %d\n", nbActiveDigits);
494  if (layout == NULL) {
495  return -1;
496  }
497 
498  UNUSED(index);
499 
500  if (hidden) {
501  // get digits container (second child of the main container)
502  container = (nbgl_container_t *) ((nbgl_container_t *) layoutInt->container->children[0])
503  ->children[1];
504  // sanity check
505  if ((container == NULL) || (container->obj.type != CONTAINER)) {
506  return -1;
507  }
508  if (nbActiveDigits > container->nbChildren) {
509  return -1;
510  }
511  if (nbActiveDigits == 0) {
512  // deactivate the first digit
513  image = (nbgl_image_t *) container->children[0];
514  if ((image == NULL) || (image->obj.type != IMAGE)) {
515  return -1;
516  }
517  image->foregroundColor = WHITE;
518  }
519  else {
520  image = (nbgl_image_t *) container->children[nbActiveDigits - 1];
521  if ((image == NULL) || (image->obj.type != IMAGE)) {
522  return -1;
523  }
524  // if the last "active" is already active, it means that we are decreasing the number of
525  // active otherwise we are increasing it
526  if (image->foregroundColor == BLACK) {
527  // all digits are already active
528  if (nbActiveDigits == container->nbChildren) {
529  return 0;
530  }
531  // deactivate the next digit by turning it to white
532  image = (nbgl_image_t *) container->children[nbActiveDigits];
533  image->foregroundColor = WHITE;
534  }
535  else {
536  // activate it the last digit by turning it to black
537  image->foregroundColor = BLACK;
538  }
539  }
540 
541  nbgl_objDraw((nbgl_obj_t *) image);
542  }
543  else {
544  // update main text area (second child of the main container)
545  nbgl_text_area_t *textArea
546  = (nbgl_text_area_t *) ((nbgl_container_t *) layoutInt->container->children[0])
547  ->children[1];
548  if ((textArea == NULL) || (textArea->obj.type != TEXT_AREA)) {
549  return -1;
550  }
551  textArea->text = text;
552  textArea->textColor = BLACK;
553  textArea->textAlignment = MID_LEFT;
554  nbgl_objDraw((nbgl_obj_t *) textArea);
555 
556  // if the text doesn't fit, indicate it by returning 1 instead of 0, for different refresh
557  if (nbgl_getSingleLineTextWidth(textArea->fontId, text) > textArea->obj.area.width) {
558  return 1;
559  }
560  }
561 
562  return 0;
563 }
564 #endif // NBGL_KEYPAD
565 #endif // HAVE_SE_TOUCH
debug traces management
#define LOG_DEBUG(__logger,...)
Definition: nbgl_debug.h:86
@ LAYOUT_LOGGER
Definition: nbgl_debug.h:33
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
Definition: nbgl_fonts.c:334
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.
Definition: nbgl_fonts.c:1048
uint8_t nbgl_getFontLineHeight(nbgl_font_id_e fontId)
return the height in pixels of the line of font with the given font ID
Definition: nbgl_fonts.c:410
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
Definition: nbgl_layout.c:519
#define AVAILABLE_WIDTH
Definition: nbgl_layout.h:66
void * nbgl_layout_t
type shared externally
Definition: nbgl_layout.h:96
Internal functions/constants of NBGL layout layer.
@ FOOTER_INDEX
#define KEYPAD_FOOTER_TYPE
int nbgl_layoutAddHiddenDigits(nbgl_layout_t *layout, uint8_t nbDigits)
Adds a placeholder for hidden digits on top of a keypad, to represent the entered digits,...
int nbgl_layoutUpdateHiddenDigits(nbgl_layout_t *layout, uint8_t index, uint8_t nbActive)
Updates an existing set of hidden digits, with the given configuration.
int nbgl_layoutUpdateKeypad(nbgl_layout_t *layout, uint8_t index, bool enableValidate, bool enableBackspace, bool enableDigits)
Updates an existing keypad on bottom of the screen, with the given configuration.
#define DIGIT_ICON
@ LINE_INDEX
@ INPUT_INDEX
@ NB_CHILDREN
@ TITLE_INDEX
int nbgl_layoutAddKeypadContent(nbgl_layout_t *layout, const char *title, bool hidden, uint8_t nbDigits, const char *text)
Adds an area with a title and a placeholder for hidden digits on top of a keypad, to represent the en...
int nbgl_layoutUpdateKeypadContent(nbgl_layout_t *layout, bool hidden, uint8_t nbActiveDigits, const char *text)
Updates an existing set of hidden digits, with the given configuration.
int nbgl_layoutAddKeypad(nbgl_layout_t *layout, keyboardCallback_t callback, bool shuffled)
Adds a keypad on bottom of the screen, with the associated callback.
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_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)
Definition: nbgl_obj.c:1521
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.
struct PACKED__ nbgl_keypad_s nbgl_keypad_t
struct to represent a keypad (KEYPAD type)
#define KEYPAD_MAX_DIGITS
Definition: nbgl_obj.h:59
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)....
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.
void(* keyboardCallback_t)(char touchedKey)
prototype of function to be called when a valid key is pressed on keyboard Backspace is equal to 0x8 ...
Definition: nbgl_obj.h:466
struct PACKED__ nbgl_container_s nbgl_container_t
struct to represent a container (CONTAINER type)
#define KEYPAD_KEY_HEIGHT
Definition: nbgl_obj.h:55
struct PACKED__ nbgl_obj_s nbgl_obj_t
Common structure for all graphical objects.
API to manage screens.
@ WHITE
Definition: nbgl_types.h:105
@ LIGHT_GRAY
Definition: nbgl_types.h:104
@ BLACK
Definition: nbgl_types.h:102
#define SCREEN_WIDTH
Definition: nbgl_types.h:32
@ VERTICAL
from top to bottom
Definition: nbgl_types.h:170
@ HORIZONTAL
from left to right
Definition: nbgl_types.h:171
@ TOP_MIDDLE
Definition: nbgl_types.h:143
@ CENTER
Definition: nbgl_types.h:146
@ MID_RIGHT
Definition: nbgl_types.h:147
@ MID_LEFT
Definition: nbgl_types.h:145
@ BOTTOM_MIDDLE
Definition: nbgl_types.h:149
@ KEYPAD
Keypad.
Definition: nbgl_types.h:128
@ IMAGE
Bitmap (y and height must be multiple of 4 on Stax)
Definition: nbgl_types.h:118
@ LINE
Vertical or Horizontal line.
Definition: nbgl_types.h:119
@ CONTAINER
Empty container.
Definition: nbgl_types.h:117
@ TEXT_AREA
Area to contain text line(s)
Definition: nbgl_types.h:120
Structure containing all information about the current layout.
nbgl_container_t * footerContainer
container used to store footer (buttons, nav....)
nbgl_container_t * container
nbgl_layoutFooterType_t footerType
type of footer
nbgl_obj_t ** children
children for main screen
unsigned char uint8_t
Definition: usbd_conf.h:53