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 <sjg@chromium.org>
diff --git a/boot/scene.c b/boot/scene.c
index bc213bc..ea94b90 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 @@
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 6aab276..dfe5692 100644
--- a/boot/scene_menu.c
+++ b/boot/scene_menu.c
@@ -323,6 +323,7 @@
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 @@
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);