summaryrefslogtreecommitdiff
path: root/boot/scene_menu.c
diff options
context:
space:
mode:
Diffstat (limited to 'boot/scene_menu.c')
-rw-r--r--boot/scene_menu.c290
1 files changed, 240 insertions, 50 deletions
diff --git a/boot/scene_menu.c b/boot/scene_menu.c
index 18998e862ab..8a355f838cc 100644
--- a/boot/scene_menu.c
+++ b/boot/scene_menu.c
@@ -6,7 +6,7 @@
* Written by Simon Glass <sjg@chromium.org>
*/
-#define LOG_CATEGORY LOGC_BOOT
+#define LOG_CATEGORY LOGC_EXPO
#include <common.h>
#include <dm.h>
@@ -33,6 +33,58 @@ void scene_menu_destroy(struct scene_obj_menu *menu)
scene_menuitem_destroy(item);
}
+static struct scene_menitem *scene_menuitem_find(struct scene_obj_menu *menu,
+ int id)
+{
+ struct scene_menitem *item;
+
+ list_for_each_entry(item, &menu->item_head, sibling) {
+ if (item->id == id)
+ return item;
+ }
+
+ return NULL;
+}
+
+/**
+ * update_pointers() - Update the pointer object and handle highlights
+ *
+ * @menu: Menu to update
+ * @id: ID of menu item to select/deselect
+ * @point: true if @id is being selected, false if it is being deselected
+ */
+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;
+
+ item = scene_menuitem_find(menu, id);
+ if (!item)
+ return log_msg_ret("itm", -ENOENT);
+
+ /* adjust the pointer object to point to the selected item */
+ if (menu->pointer_id && item && point) {
+ struct scene_obj *label;
+
+ label = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE);
+
+ ret = scene_obj_set_pos(scn, menu->pointer_id,
+ menu->obj.dim.x + 200, label->dim.y);
+ if (ret < 0)
+ 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;
+}
+
/**
* menu_point_to_item() - Point to a particular menu item
*
@@ -40,18 +92,115 @@ void scene_menu_destroy(struct scene_obj_menu *menu)
*/
static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id)
{
+ if (menu->cur_item_id)
+ update_pointers(menu, menu->cur_item_id, false);
menu->cur_item_id = item_id;
+ update_pointers(menu, item_id, true);
+}
+
+static int scene_bbox_union(struct scene *scn, uint id, int inset,
+ struct vidconsole_bbox *bbox)
+{
+ struct scene_obj *obj;
+
+ if (!id)
+ return 0;
+ obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
+ if (!obj)
+ return log_msg_ret("obj", -ENOENT);
+ if (bbox->valid) {
+ bbox->x0 = min(bbox->x0, obj->dim.x - inset);
+ bbox->y0 = min(bbox->y0, obj->dim.y);
+ bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset);
+ bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h);
+ } else {
+ bbox->x0 = obj->dim.x - inset;
+ bbox->y0 = obj->dim.y;
+ bbox->x1 = obj->dim.x + obj->dim.w + inset;
+ bbox->y1 = obj->dim.y + obj->dim.h;
+ bbox->valid = true;
+ }
+
+ return 0;
+}
+
+/**
+ * scene_menu_calc_bbox() - Calculate bounding boxes for the menu
+ *
+ * @menu: Menu to process
+ * @bbox: Returns bounding box of menu including prompts
+ * @label_bbox: Returns bounding box of labels
+ */
+static void scene_menu_calc_bbox(struct scene_obj_menu *menu,
+ struct vidconsole_bbox *bbox,
+ struct vidconsole_bbox *label_bbox)
+{
+ const struct expo_theme *theme = &menu->obj.scene->expo->theme;
+ const struct scene_menitem *item;
+
+ bbox->valid = false;
+ scene_bbox_union(menu->obj.scene, menu->title_id, 0, bbox);
+
+ label_bbox->valid = false;
+
+ list_for_each_entry(item, &menu->item_head, sibling) {
+ scene_bbox_union(menu->obj.scene, item->label_id,
+ theme->menu_inset, bbox);
+ scene_bbox_union(menu->obj.scene, item->key_id, 0, bbox);
+ scene_bbox_union(menu->obj.scene, item->desc_id, 0, bbox);
+ scene_bbox_union(menu->obj.scene, item->preview_id, 0, bbox);
+
+ /* Get the bounding box of all labels */
+ scene_bbox_union(menu->obj.scene, item->label_id,
+ theme->menu_inset, label_bbox);
+ }
+
+ /*
+ * subtract the final menuitem's gap to keep the insert the same top
+ * and bottom
+ */
+ label_bbox->y1 -= theme->menuitem_gap_y;
+}
+
+int scene_menu_calc_dims(struct scene_obj_menu *menu)
+{
+ struct vidconsole_bbox bbox, label_bbox;
+ const struct scene_menitem *item;
+
+ scene_menu_calc_bbox(menu, &bbox, &label_bbox);
+
+ /* Make all labels the same size */
+ if (label_bbox.valid) {
+ list_for_each_entry(item, &menu->item_head, sibling) {
+ scene_obj_set_size(menu->obj.scene, item->label_id,
+ label_bbox.x1 - label_bbox.x0,
+ label_bbox.y1 - label_bbox.y0);
+ }
+ }
+
+ if (bbox.valid) {
+ menu->obj.dim.w = bbox.x1 - bbox.x0;
+ menu->obj.dim.h = bbox.y1 - bbox.y0;
+ }
+
+ return 0;
}
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;
+ const struct expo_theme *theme = &exp->theme;
struct scene_menitem *item;
- int y, cur_y;
+ uint sel_id;
+ int x, y;
int ret;
- y = menu->obj.y;
+ 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.x, y);
+ ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y);
if (ret < 0)
return log_msg_ret("tit", ret);
@@ -59,7 +208,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;
}
/*
@@ -68,11 +220,12 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
* small. This can be updated once text measuring is supported in
* vidconsole
*/
- cur_y = -1;
+ 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;
@@ -81,32 +234,33 @@ int scene_menu_arrange(struct scene *scn, struct scene_obj_menu *menu)
y += height;
/* select an item if not done already */
- if (!menu->cur_item_id)
- menu_point_to_item(menu, item->id);
+ 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.x,
- y);
- if (ret < 0)
- return log_msg_ret("nam", ret);
- }
-
- ret = scene_obj_set_pos(scn, item->key_id, menu->obj.x + 230,
- y);
+ ret = scene_obj_set_pos(scn, item->label_id,
+ x + theme->menu_inset, 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.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;
@@ -125,19 +279,12 @@ 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 + theme->menuitem_gap_y;
}
- if (menu->pointer_id && cur_y != -1) {
- /*
- * put the pointer to the right of and level with the item it
- * points to
- */
- ret = scene_obj_set_pos(scn, menu->pointer_id,
- menu->obj.x + 200, cur_y);
- if (ret < 0)
- return log_msg_ret("ptr", ret);
- }
+ if (sel_id)
+ menu_point_to_item(menu, sel_id);
return 0;
}
@@ -158,10 +305,6 @@ int scene_menu(struct scene *scn, const char *name, uint id,
*menup = menu;
INIT_LIST_HEAD(&menu->item_head);
- ret = scene_menu_arrange(scn, menu);
- if (ret)
- return log_msg_ret("pos", ret);
-
return menu->obj.id;
}
@@ -191,6 +334,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;
@@ -215,7 +359,7 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
struct scene_menitem, sibling)) {
item = list_entry(item->sibling.prev,
struct scene_menitem, sibling);
- event->type = EXPOACT_POINT;
+ event->type = EXPOACT_POINT_ITEM;
event->select.id = item->id;
log_debug("up to item %d\n", event->select.id);
}
@@ -224,7 +368,7 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key,
if (!list_is_last(&item->sibling, &menu->item_head)) {
item = list_entry(item->sibling.next,
struct scene_menitem, sibling);
- event->type = EXPOACT_POINT;
+ event->type = EXPOACT_POINT_ITEM;
event->select.id = item->id;
log_debug("down to item %d\n", event->select.id);
}
@@ -235,8 +379,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);
@@ -258,14 +407,13 @@ int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id,
{
struct scene_obj_menu *menu;
struct scene_menitem *item;
- int ret;
menu = scene_obj_find(scn, menu_id, SCENEOBJT_MENU);
if (!menu)
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));
@@ -285,10 +433,6 @@ int scene_menuitem(struct scene *scn, uint menu_id, const char *name, uint id,
item->flags = flags;
list_add_tail(&item->sibling, &menu->item_head);
- ret = scene_menu_arrange(scn, menu);
- if (ret)
- return log_msg_ret("pos", ret);
-
if (itemp)
*itemp = item;
@@ -388,3 +532,49 @@ int scene_menu_display(struct scene_obj_menu *menu)
return -ENOTSUPP;
}
+
+void scene_menu_render(struct scene_obj_menu *menu)
+{
+ struct expo *exp = menu->obj.scene->expo;
+ const struct expo_theme *theme = &exp->theme;
+ struct vidconsole_bbox bbox, label_bbox;
+ struct udevice *dev = exp->display;
+ struct video_priv *vid_priv;
+ struct udevice *cons = exp->cons;
+ struct vidconsole_colour old;
+ enum colour_idx fore, back;
+
+ if (CONFIG_IS_ENABLED(SYS_WHITE_ON_BLACK)) {
+ fore = VID_BLACK;
+ back = VID_WHITE;
+ } else {
+ fore = VID_LIGHT_GRAY;
+ back = VID_BLACK;
+ }
+
+ scene_menu_calc_bbox(menu, &bbox, &label_bbox);
+ vidconsole_push_colour(cons, fore, back, &old);
+ vid_priv = dev_get_uclass_priv(dev);
+ video_fill_part(dev, label_bbox.x0 - theme->menu_inset,
+ label_bbox.y0 - theme->menu_inset,
+ label_bbox.x1, label_bbox.y1 + theme->menu_inset,
+ vid_priv->colour_fg);
+ vidconsole_pop_colour(cons, &old);
+}
+
+int scene_menu_render_deps(struct scene *scn, struct scene_obj_menu *menu)
+{
+ struct scene_menitem *item;
+
+ scene_render_deps(scn, menu->title_id);
+ scene_render_deps(scn, menu->cur_item_id);
+ scene_render_deps(scn, menu->pointer_id);
+
+ list_for_each_entry(item, &menu->item_head, sibling) {
+ scene_render_deps(scn, item->key_id);
+ scene_render_deps(scn, item->label_id);
+ scene_render_deps(scn, item->desc_id);
+ }
+
+ return 0;
+}