From d3db0216dc163594309a9930b31e3161261cd873 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 1 Jun 2023 10:22:55 -0600 Subject: [PATCH] expo: Support drawing of popup menus At present only a single menu is supported. All items are shown and a pointer object points to the current item. Add support for multiple menus, one of which is highlighted, indicated by the highlight_id property in the scene. The highlighted menu item has a SCENEOF_POINT flag, indicating that it is currently pointed to. The popup menu is normally closed, in which case it shows only the current menu item. When it is opened, it shows all items, allowing the user to select one. Rather than requiring the menu item to have a description, require it to have a label. Use the label (only) for the popup menu. With this, most of the drawing and layout logic is complete. Signed-off-by: Simon Glass --- boot/scene_menu.c | 58 ++++++++++++++++++++++++++++++----------------- include/expo.h | 9 ++++++++ 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 154be7f012..f2832b4ddf 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -56,6 +56,7 @@ static struct scene_menitem *scene_menuitem_find(struct scene_obj_menu *menu, static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) { struct scene *scn = menu->obj.scene; + const bool stack = scn->expo->popup; const struct scene_menitem *item; int ret; @@ -75,6 +76,12 @@ static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) return log_msg_ret("ptr", ret); } + if (stack) { + point &= scn->highlight_id == menu->obj.id; + scene_obj_flag_clrset(scn, item->label_id, SCENEOF_POINT, + point ? SCENEOF_POINT : 0); + } + return 0; } @@ -172,11 +179,15 @@ int scene_menu_calc_dims(struct scene_obj_menu *menu) int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) { + const bool open = menu->obj.flags & SCENEOF_OPEN; + struct expo *exp = scn->expo; + const bool stack = exp->popup; struct scene_menitem *item; uint sel_id; - int y, cur_y; + int x, y; int ret; + x = menu->obj.dim.x; y = menu->obj.dim.y; if (menu->title_id) { ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y); @@ -187,7 +198,10 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) if (ret < 0) return log_msg_ret("hei", ret); - y += ret * 2; + if (stack) + x += 200; + else + y += ret * 2; } /* @@ -198,9 +212,10 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) */ sel_id = menu->cur_item_id; list_for_each_entry(item, &menu->item_head, sibling) { + bool selected; int height; - ret = scene_obj_get_hw(scn, item->desc_id, NULL); + ret = scene_obj_get_hw(scn, item->label_id, NULL); if (ret < 0) return log_msg_ret("get", ret); height = ret; @@ -212,29 +227,29 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) if (!sel_id) sel_id = item->id; + selected = sel_id == item->id; + /* * Put the label on the left, then leave a space for the * pointer, then the key and the description */ - if (item->label_id) { - ret = scene_obj_set_pos(scn, item->label_id, menu->obj.dim.x, - y); - if (ret < 0) - return log_msg_ret("nam", ret); - } - - ret = scene_obj_set_pos(scn, item->key_id, menu->obj.dim.x + 230, - y); + ret = scene_obj_set_pos(scn, item->label_id, x, y); if (ret < 0) - return log_msg_ret("key", ret); + return log_msg_ret("nam", ret); + scene_obj_set_hide(scn, item->label_id, + stack && !open && !selected); - ret = scene_obj_set_pos(scn, item->desc_id, menu->obj.dim.x + 280, - y); - if (ret < 0) - return log_msg_ret("des", ret); + if (item->key_id) { + ret = scene_obj_set_pos(scn, item->key_id, x + 230, y); + if (ret < 0) + return log_msg_ret("key", ret); + } - if (menu->cur_item_id == item->id) - cur_y = y; + if (item->desc_id) { + ret = scene_obj_set_pos(scn, item->desc_id, x + 280, y); + if (ret < 0) + return log_msg_ret("des", ret); + } if (item->preview_id) { bool hide; @@ -253,7 +268,8 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu) return log_msg_ret("hid", ret); } - y += height; + if (!stack || open) + y += height; } if (sel_id) @@ -380,7 +396,7 @@ int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id, return log_msg_ret("find", -ENOENT); /* Check that the text ID is valid */ - if (!scene_obj_find(scn, desc_id, SCENEOBJT_TEXT)) + if (!scene_obj_find(scn, label_id, SCENEOBJT_TEXT)) return log_msg_ret("txt", -EINVAL); item = calloc(1, sizeof(struct scene_obj_menu)); diff --git a/include/expo.h b/include/expo.h index ea8f38913d..0c55d60f71 100644 --- a/include/expo.h +++ b/include/expo.h @@ -70,6 +70,7 @@ struct expo_theme { * @action: Action selected by user. At present only one is supported, with the * type set to EXPOACT_NONE if there is no action * @text_mode: true to use text mode for the menu (no vidconsole) + * @popup: true to use popup menus, instead of showing all items * @priv: Private data for the controller * @theme: Information about fonts styles, etc. * @scene_head: List of scenes @@ -83,6 +84,7 @@ struct expo { uint next_id; struct expo_action action; bool text_mode; + bool popup; void *priv; struct expo_theme theme; struct list_head scene_head; @@ -111,6 +113,7 @@ struct expo_string { * @name: Name of the scene (allocated) * @id: ID number of the scene * @title_id: String ID of title of the scene (allocated) + * @highlight_id: ID of highlighted object, if any * @sibling: Node to link this scene to its siblings * @obj_head: List of objects in the scene */ @@ -119,6 +122,7 @@ struct scene { char *name; uint id; uint title_id; + uint highlight_id; struct list_head sibling; struct list_head obj_head; }; @@ -157,9 +161,14 @@ struct scene_dim { * enum scene_obj_flags_t - flags for objects * * @SCENEOF_HIDE: object should be hidden + * @SCENEOF_POINT: object should be highlighted + * @SCENEOF_OPEN: object should be opened (e.g. menu is opened so that an option + * can be selected) */ enum scene_obj_flags_t { SCENEOF_HIDE = 1 << 0, + SCENEOF_POINT = 1 << 1, + SCENEOF_OPEN = 1 << 2, }; /** -- 2.39.5