expo: Implement the keypress logic for popup menus
authorSimon Glass <sjg@chromium.org>
Thu, 1 Jun 2023 16:22:59 +0000 (10:22 -0600)
committerTom Rini <trini@konsulko.com>
Fri, 14 Jul 2023 16:54:51 +0000 (12:54 -0400)
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 <sjg@chromium.org>
boot/scene.c
boot/scene_menu.c
doc/develop/expo.rst
include/expo.h

index bc213bc..ea94b90 100644 (file)
@@ -13,6 +13,7 @@
 #include <expo.h>
 #include <malloc.h>
 #include <mapmem.h>
+#include <menu.h>
 #include <video.h>
 #include <video_console.h>
 #include <linux/input.h>
@@ -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;
index 6aab276..dfe5692 100644 (file)
@@ -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);
index 2f48828..80e435c 100644 (file)
@@ -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
index 0699cdb..f7febe1 100644 (file)
@@ -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,
 };