[PATCH 35/40] expo: Implement the keypress logic for popup menus

Simon Glass sjg at chromium.org
Thu Jun 1 18:22:59 CEST 2023


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 at chromium.org>
---

 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 bc213bc08b87..ea94b90584ee 100644
--- a/boot/scene.c
+++ b/boot/scene.c
@@ -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;
diff --git a/boot/scene_menu.c b/boot/scene_menu.c
index 6aab27611d71..dfe5692d6ce7 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 2f4882899b6d..80e435c5e65e 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 0699cdb4c19c..f7febe1c9ae7 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.41.0.rc0.172.g3f132b7071-goog



More information about the U-Boot mailing list