diff options
Diffstat (limited to 'boot/scene_menu.c')
-rw-r--r-- | boot/scene_menu.c | 290 |
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; +} |