本文由 简悦SimpRead 转码,原文地址 medium.com
*这些文章是我的个人发展日志,如果我对某些东西有错误的理解,请感觉......。
读者:这些文章是我的个人发展日志,如果我在某些方面有错误的理解,请随时告诉我。
**了解我们正在开发的区块链技术:Zoobc in zoobc.com/_
第一部分:建立开发环境
第三部分:与账本进行通信
第四部分:生成公钥和签署数据
如果你尝试探索LedgerHQ github中的应用程序,你可能会发现实现菜单或屏幕的不同方式。一些应用程序为每个屏幕手动声明组件,而其他一些应用程序则使用UX_FLOW。我已经尝试使用UX_FLOW很长时间了,但不幸的是我仍然不能使用它。因此,因为我们仍然处于Ledger应用程序的初始开发阶段,我决定不被吸收过多的尝试。一旦主要功能实现后,我将能够重新审视这个问题。
但是,我觉得自己不方便为我们的分类账应用程序中的每个屏幕的所有操作手动声明所有的组件。这不仅会使处理屏幕转换的逻辑变得复杂,而且也会使源代码变得杂乱无章,使其他开发者以后很难继续使用我的代码。因此,我决定自己动手制作自己的菜单屏幕。
为了做到这一点,它需要几个方面。
- 常见的屏幕组件
- 将处理屏幕转换和互动的逻辑
为了更好地理解这一点,让我们试着创建一个显示3种状态的屏幕的应用程序。
- "应用准备就绪"
- "版本"
- 和 "退出应用程序"
创建常见的屏幕组件
Ledger的内存非常有限,所以我们最好尽量减少内存的使用,这样我们在进行操作时就不会碰到天花板。为了这样做我们的UI菜单,我们需要为所有3个屏幕做一个共同的模板。下面是我们要创建的屏幕的结构。
但是在我们开始声明我们的普通屏幕组件之前,首先我们需要先了解单个组件的结构。注意,在声明单个组件时,我们通常会用#ifdef指令预测该组件是为Ledger Blue还是Ledger Nano S编译的。下面是一个单一组件的结构。
// {
// {type, userid, x, y, width, height, stroke, radius, fill,
// fgcolor, bgcolor, font_id, icon_id},
// text,
// touch_area_brim,
// overfgcolor,
// overbgcolor,
// tap,
// out,
// over,
// },
现在我们知道了声明一个UI组件的结构。接下来我们需要决定需要哪些组件来创建一个普通的屏幕模板。
- 背景(没有背景,有时文本是重叠的)
- 顶部和底部的文本(它们需要被声明为独立的组件,以方便我们单独处理它们)
- 右和左箭头
有了这些,我们就有了常见的屏幕组件。
static const bagl_element_t bagl_ui_menu_template_nanos[] = {
{
// background
{BAGL_RECTANGLE, 0x00, 0, 0, 128, 32, 0, 0, BAGL_FILL, 0x000000,
0xFFFFFF, 0, 0},
NULL,
#ifdef TARGET_BLUE
0,
0,
0,
NULL,
NULL,
NULL,
#endif /* TARGET_BLUE */
},
{
// text line 1
{BAGL_LABELINE, 0x01, 0, 12, 128, 32, 0, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 0},
textTop,
#ifdef TARGET_BLUE
0,
0,
0,
NULL,
NULL,
NULL,
#endif /* TARGET_BLUE */
},
{
// text line 2
{BAGL_LABELINE, 0x83, 15, 26, 97, 12, 0x80 | 10, 0, 0, 0xFFFFFF, 0x000000,
BAGL_FONT_OPEN_SANS_REGULAR_11px | BAGL_FONT_ALIGNMENT_CENTER, 26},
textBottom,
#ifdef TARGET_BLUE
0,
0,
0,
NULL,
NULL,
NULL,
#endif /* TARGET_BLUE */
},
{
// left arrow
{BAGL_ICON, 0x00, 3, 12, 7, 7, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_LEFT},
NULL,
#ifdef TARGET_BLUE
0,
0,
0,
NULL,
NULL,
NULL,
#endif /* TARGET_BLUE */
},
{
// right arrow
{BAGL_ICON, 0x00, 117, 12, 8, 6, 0, 0, 0, 0xFFFFFF, 0x000000, 0,
BAGL_GLYPH_ICON_RIGHT},
NULL,
#ifdef TARGET_BLUE
0,
0,
0,
NULL,
NULL,
NULL,
#endif /* TARGET_BLUE */
},
};
创建逻辑来处理屏幕转换和互动
因为我们正在创建一个具有多种功能的应用程序,我们需要在屏幕上显示关于正在进行的操作和用户进行的交互的信息。例如,我想在用户点击右键或左键时显示 "退出应用程序"(一个只有2个屏幕的菜单:"应用程序就绪 "和 "退出应用程序")。
#define MENU_ENTRY_STEP_STATUS 0
#define MENU_ENTRY_STEP_VERSION 1
#define MENU_ENTRY_STEP_QUIT 2
// UX Flow state
#define MENU_STATE_SIZE 2
static int menuState[MENU_STATE_SIZE];
static int menuStateNext[MENU_STATE_SIZE];
static int currentMenuMaxStep = 0;
static void ui_screen_menu_entry(void)
{
menuStateNext[0] = MENU_ENTRY;
menuStateNext[1] = MENU_ENTRY_STEP_STATUS;
currentMenuMaxStep = 3;
ui_render();
}
static void ui_render(void)
{
switch (menuStateNext[0])
{
case MENU_ENTRY:
switch (menuStateNext[1])
{
case MENU_ENTRY_STEP_STATUS:
render_menu_text_top("Application");
render_menu_text_bottom("is ready");
break;
case MENU_ENTRY_STEP_VERSION:
render_menu_text_top("Version");
render_menu_text_bottom(APPVERSION);
break;
case MENU_ENTRY_STEP_QUIT:
render_menu_text_top("Quit");
render_menu_text_bottom("Applicatiom");
break;
default:
break;
}
break;
default:
break;
}
UX_DISPLAY(bagl_ui_menu_template_nanos, NULL);
}static void render_menu_text_top(char text[TEXT_TOP_SIZE])
{
for (int i = 0; i < TEXT_TOP_SIZE; i++)
{
textTop[i] = text[i];
}
}
static void render_menu_text_bottom(char text[TEXT_BOTTOM_SIZE])
{
for (int i = 0; i < TEXT_BOTTOM_SIZE; i++)
{
textBottom[i] = text[i];
}
}
static unsigned int
bagl_ui_menu_template_nanos_button(unsigned int button_mask,
unsigned int button_mask_counter)
{
uint32_t length = 0;
for (int i = 0; i < MENU_STATE_SIZE; i++)
{
menuState[i] = menuStateNext[i];
}
switch (menuState[0])
{
case MENU_ENTRY:
switch (menuState[1])
{
case MENU_ENTRY_STEP_QUIT:
switch (button_mask)
{
case BUTTON_EVT_RELEASED | BUTTON_LEFT | BUTTON_RIGHT: // EXIT
io_seproxyhal_touch_exit(NULL);
return 0;
}
break;
default:
break;
}
break;
default:
break;
}
// right and left default navigation
switch (button_mask)
{
case BUTTON_EVT_RELEASED | BUTTON_RIGHT:
if (menuStateNext[1] >= currentMenuMaxStep - 1)
{
menuStateNext[1] = 0;
}
else
{
menuStateNext[1]++;
}
ui_render();
break;
case BUTTON_EVT_RELEASED | BUTTON_LEFT:
if (menuStateNext[1] <= 0)
{
menuStateNext[1] = currentMenuMaxStep - 1;
}
else
{
menuStateNext[1]--;
}
ui_render();
break;
}
return 0;
}
我们在开始时声明的变量是用来保持我们当前所处的屏幕状态的。你可能注意到我有2个相同的变量static int menuState[MENU_STATE_SIZE]和static int menuStateNext[MENU_STATE_SIZE]。在正常的应用程序中,我们实际上只需要其中一个来跟踪屏幕的状态。但是当我在Ledger Nano S中实现它时,由于某些原因,设备会挂起。我怀疑这是设备安全系统的一部分,不允许在某些类型的函数中连续读和写一个变量。
方法ui_screen_menu_entry是用来初始化一个入口屏幕,它将指向 "应用程序准备 "屏幕。ui_render负责渲染与当前屏幕状态有关的特定文本。bagl_ui_menu_template_nanos_button将处理每个scree中的按钮交互。在上面的场景中,我们只想在 "退出应用程序 "的屏幕上,左右按钮都被按下时退出应用程序。
这就是我为Zoobc Ledger应用程序制作入口菜单的经验。让我知道你的想法,如果你需要更多关于任何部分的详细解释。
此外,如果你想了解更多关于我的公司将要推出的区块链技术,你可以访问zoobc.com/。
请继续关注下一部分的内容。🎉 🎉 🎉🎉🎉