From 4e64beeba7de7720b42714cbc542c9f7dfbba841 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:59 -0600 Subject: [PATCH] expo: Implement the keypress logic for popup menus In 'popup' mode, the expo allows moving around the objects in a scene. When 'enter' is pressed on a menu, it opens and the user can move around the items in the menu. Implement this using keypress handles and actions. Signed-off-by: Simon Glass --- boot/scene.c | 80 ++++++++++++++++++++++++++++++++++++++++++++ boot/scene_menu.c | 10 ++++-- doc/develop/expo.rst | 3 +- include/expo.h | 7 ++++ 4 files changed, 96 insertions(+), 4 deletions(-) diff --git a/boot/scene.c b/boot/scene.c index bc213bc08b..ea94b90584 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -469,11 +470,90 @@ int scene_render(struct scene *scn) return 0; } +/** + * send_key_obj() - Handle a keypress for moving between objects + * + * @scn: Scene to receive the key + * @key: Key to send (KEYCODE_UP) + * @event: Returns resulting event from this keypress + * Returns: 0 if OK, -ve on error + */ +static void send_key_obj(struct scene *scn, struct scene_obj *obj, int key, + struct expo_action *event) +{ + switch (key) { + case BKEY_UP: + while (obj != list_first_entry(&scn->obj_head, struct scene_obj, + sibling)) { + obj = list_entry(obj->sibling.prev, + struct scene_obj, sibling); + if (obj->type == SCENEOBJT_MENU) { + event->type = EXPOACT_POINT_OBJ; + event->select.id = obj->id; + log_debug("up to obj %d\n", event->select.id); + break; + } + } + break; + case BKEY_DOWN: + while (!list_is_last(&obj->sibling, &scn->obj_head)) { + obj = list_entry(obj->sibling.next, struct scene_obj, + sibling); + if (obj->type == SCENEOBJT_MENU) { + event->type = EXPOACT_POINT_OBJ; + event->select.id = obj->id; + log_debug("down to obj %d\n", event->select.id); + break; + } + } + break; + case BKEY_SELECT: + if (obj->type == SCENEOBJT_MENU) { + event->type = EXPOACT_OPEN; + event->select.id = obj->id; + log_debug("open obj %d\n", event->select.id); + } + break; + case BKEY_QUIT: + event->type = EXPOACT_QUIT; + log_debug("obj quit\n"); + break; + } +} + int scene_send_key(struct scene *scn, int key, struct expo_action *event) { + struct scene_obj_menu *menu; struct scene_obj *obj; int ret; + event->type = EXPOACT_NONE; + + /* + * In 'popup' mode, arrow keys move betwen objects, unless a menu is + * opened + */ + if (scn->expo->popup) { + obj = NULL; + if (scn->highlight_id) { + obj = scene_obj_find(scn, scn->highlight_id, + SCENEOBJT_NONE); + } + if (!obj) + return 0; + + if (!(obj->flags & SCENEOF_OPEN)) { + send_key_obj(scn, obj, key, event); + return 0; + } + + menu = (struct scene_obj_menu *)obj, + ret = scene_menu_send_key(scn, menu, key, event); + if (ret) + return log_msg_ret("key", ret); + return 0; + } + list_for_each_entry(obj, &scn->obj_head, sibling) { if (obj->type == SCENEOBJT_MENU) { struct scene_obj_menu *menu; diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 6aab27611d..dfe5692d6c 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -323,6 +323,7 @@ static struct scene_menitem *scene_menu_find_key(struct scene *scn, int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, struct expo_action *event) { + const bool open = menu->obj.flags & SCENEOF_OPEN; struct scene_menitem *item, *cur, *key_item; cur = NULL; @@ -367,8 +368,13 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, log_debug("select item %d\n", event->select.id); break; case BKEY_QUIT: - event->type = EXPOACT_QUIT; - log_debug("quit\n"); + if (scn->expo->popup && open) { + event->type = EXPOACT_CLOSE; + event->select.id = menu->obj.id; + } else { + event->type = EXPOACT_QUIT; + log_debug("menu quit\n"); + } break; case '0'...'9': key_item = scene_menu_find_key(scn, menu, key); diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index 2f4882899b..80e435c5e6 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -178,8 +178,7 @@ Some ideas for future work: - Image formats other than BMP - Use of ANSI sequences to control a serial terminal - Colour selection -- Better support for handling lots of settings, e.g. with multiple menus and - radio/option widgets +- Better support for handling lots of settings, e.g. with radio/option widgets - Mouse support - Integrate Nuklear, NxWidgets or some other library for a richer UI - Optimise rendering by only updating the display with changes since last render diff --git a/include/expo.h b/include/expo.h index 0699cdb4c1..f7febe1c9a 100644 --- a/include/expo.h +++ b/include/expo.h @@ -16,14 +16,21 @@ struct udevice; * enum expoact_type - types of actions reported by the expo * * @EXPOACT_NONE: no action + * @EXPOACT_POINT_OBJ: object was highlighted (@id indicates which) * @EXPOACT_POINT_ITEM: menu item was highlighted (@id indicates which) * @EXPOACT_SELECT: menu item was selected (@id indicates which) + * @EXPOACT_OPEN: menu was opened, so an item can be selected (@id indicates + * which menu object) + * @EXPOACT_CLOSE: menu was closed (@id indicates which menu object) * @EXPOACT_QUIT: request to exit the menu */ enum expoact_type { EXPOACT_NONE, + EXPOACT_POINT_OBJ, EXPOACT_POINT_ITEM, EXPOACT_SELECT, + EXPOACT_OPEN, + EXPOACT_CLOSE, EXPOACT_QUIT, }; -- 2.39.5