summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot/expo.c24
-rw-r--r--boot/scene.c52
-rw-r--r--boot/scene_internal.h21
-rw-r--r--boot/scene_menu.c79
-rw-r--r--doc/develop/expo.rst2
-rw-r--r--include/expo.h21
-rw-r--r--test/boot/expo.c42
7 files changed, 239 insertions, 2 deletions
diff --git a/boot/expo.c b/boot/expo.c
index be11cfd4e9..67cae3c7e2 100644
--- a/boot/expo.c
+++ b/boot/expo.c
@@ -114,6 +114,30 @@ int expo_set_display(struct expo *exp, struct udevice *dev)
return 0;
}
+int expo_calc_dims(struct expo *exp)
+{
+ struct scene *scn;
+ int ret;
+
+ if (!exp->cons)
+ return log_msg_ret("dim", -ENOTSUPP);
+
+ list_for_each_entry(scn, &exp->scene_head, sibling) {
+ /*
+ * Do the menus last so that all the menus' text objects
+ * are dimensioned
+ */
+ ret = scene_calc_dims(scn, false);
+ if (ret)
+ return log_msg_ret("scn", ret);
+ ret = scene_calc_dims(scn, true);
+ if (ret)
+ return log_msg_ret("scn", ret);
+ }
+
+ return 0;
+}
+
void expo_set_text_mode(struct expo *exp, bool text_mode)
{
exp->text_mode = text_mode;
diff --git a/boot/scene.c b/boot/scene.c
index 981a18b3ba..6d5e3c1f03 100644
--- a/boot/scene.c
+++ b/boot/scene.c
@@ -207,6 +207,19 @@ int scene_obj_set_pos(struct scene *scn, uint id, int x, int y)
return 0;
}
+int scene_obj_set_size(struct scene *scn, uint id, int w, int h)
+{
+ struct scene_obj *obj;
+
+ obj = scene_obj_find(scn, id, SCENEOBJT_NONE);
+ if (!obj)
+ return log_msg_ret("find", -ENOENT);
+ obj->dim.w = w;
+ obj->dim.h = h;
+
+ return 0;
+}
+
int scene_obj_set_hide(struct scene *scn, uint id, bool hide)
{
int ret;
@@ -414,3 +427,42 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event)
return 0;
}
+
+int scene_calc_dims(struct scene *scn, bool do_menus)
+{
+ struct scene_obj *obj;
+ int ret;
+
+ list_for_each_entry(obj, &scn->obj_head, sibling) {
+ switch (obj->type) {
+ case SCENEOBJT_NONE:
+ case SCENEOBJT_TEXT:
+ case SCENEOBJT_IMAGE: {
+ int width;
+
+ if (!do_menus) {
+ ret = scene_obj_get_hw(scn, obj->id, &width);
+ if (ret < 0)
+ return log_msg_ret("get", ret);
+ obj->dim.w = width;
+ obj->dim.h = ret;
+ }
+ break;
+ }
+ case SCENEOBJT_MENU: {
+ struct scene_obj_menu *menu;
+
+ if (do_menus) {
+ menu = (struct scene_obj_menu *)obj;
+
+ ret = scene_menu_calc_dims(menu);
+ if (ret)
+ return log_msg_ret("men", ret);
+ }
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/boot/scene_internal.h b/boot/scene_internal.h
index 24a2ba6a6a..00085a2f55 100644
--- a/boot/scene_internal.h
+++ b/boot/scene_internal.h
@@ -66,6 +66,17 @@ int scene_obj_add(struct scene *scn, const char *name, uint id,
int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set);
/**
+ * scene_calc_dims() - Calculate the dimensions of the scene objects
+ *
+ * Updates the width and height of all objects based on their contents
+ *
+ * @scn: Scene to update
+ * @do_menus: true to calculate only menus, false to calculate everything else
+ * Returns 0 if OK, -ENOTSUPP if there is no graphical console
+ */
+int scene_calc_dims(struct scene *scn, bool do_menus);
+
+/**
* scene_menu_arrange() - Set the position of things in the menu
*
* This updates any items associated with a menu to make sure they are
@@ -133,4 +144,14 @@ int scene_render(struct scene *scn);
*/
int scene_send_key(struct scene *scn, int key, struct expo_action *event);
+/**
+ * scene_menu_calc_dims() - Calculate the dimensions of a menu
+ *
+ * Updates the width and height of the menu based on its contents
+ *
+ * @menu: Menu to update
+ * Returns 0 if OK, -ENOTSUPP if there is no graphical console
+ */
+int scene_menu_calc_dims(struct scene_obj_menu *menu);
+
#endif /* __SCENE_INTERNAL_H */
diff --git a/boot/scene_menu.c b/boot/scene_menu.c
index eed7565f6a..fa79cecdbd 100644
--- a/boot/scene_menu.c
+++ b/boot/scene_menu.c
@@ -43,6 +43,85 @@ static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id)
menu->cur_item_id = item_id;
}
+static int scene_bbox_union(struct scene *scn, uint id,
+ 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);
+ bbox->y0 = min(bbox->y0, obj->dim.y);
+ bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w);
+ bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h);
+ } else {
+ bbox->x0 = obj->dim.x;
+ bbox->y0 = obj->dim.y;
+ bbox->x1 = obj->dim.x + obj->dim.w;
+ 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 scene_menitem *item;
+
+ bbox->valid = false;
+ scene_bbox_union(menu->obj.scene, menu->title_id, bbox);
+
+ label_bbox->valid = false;
+
+ list_for_each_entry(item, &menu->item_head, sibling) {
+ scene_bbox_union(menu->obj.scene, item->label_id, bbox);
+ scene_bbox_union(menu->obj.scene, item->key_id, bbox);
+ scene_bbox_union(menu->obj.scene, item->desc_id, bbox);
+ scene_bbox_union(menu->obj.scene, item->preview_id, bbox);
+
+ /* Get the bounding box of all labels */
+ scene_bbox_union(menu->obj.scene, item->label_id, label_bbox);
+ }
+}
+
+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)
{
struct scene_menitem *item;
diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst
index 9565974a28..54861b93ac 100644
--- a/doc/develop/expo.rst
+++ b/doc/develop/expo.rst
@@ -182,8 +182,6 @@ Some ideas for future work:
- Add a Kconfig option to drop the names to save code / data space
- Add a Kconfig option to disable vidconsole support to save code / data space
- Support both graphical and text menus at the same time on different devices
-- Implement proper measurement of object bounding boxes, to permit more exact
- layout. This would tidy up the layout when Truetype is not used
- Support unicode
- Support curses for proper serial-terminal menus
diff --git a/include/expo.h b/include/expo.h
index b6777cebcb..6c45c403cf 100644
--- a/include/expo.h
+++ b/include/expo.h
@@ -328,6 +328,16 @@ const char *expo_get_str(struct expo *exp, uint id);
int expo_set_display(struct expo *exp, struct udevice *dev);
/**
+ * expo_calc_dims() - Calculate the dimensions of the objects
+ *
+ * Updates the width and height of all objects based on their contents
+ *
+ * @exp: Expo to update
+ * Returns 0 if OK, -ENOTSUPP if there is no graphical console
+ */
+int expo_calc_dims(struct expo *exp);
+
+/**
* expo_set_scene_id() - Set the current scene ID
*
* @exp: Expo to update
@@ -469,6 +479,17 @@ int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
int scene_obj_set_pos(struct scene *scn, uint id, int x, int y);
/**
+ * scene_obj_set_size() - Set the size of an object
+ *
+ * @scn: Scene to update
+ * @id: ID of object to update
+ * @w: width in pixels
+ * @h: height in pixels
+ * Returns: 0 if OK, -ENOENT if @id is invalid
+ */
+int scene_obj_set_size(struct scene *scn, uint id, int w, int h);
+
+/**
* scene_obj_set_hide() - Set whether an object is hidden
*
* The update happens when the expo is next rendered.
diff --git a/test/boot/expo.c b/test/boot/expo.c
index 5088776f7b..493d050baf 100644
--- a/test/boot/expo.c
+++ b/test/boot/expo.c
@@ -473,6 +473,48 @@ static int expo_render_image(struct unit_test_state *uts)
/* render without a scene */
ut_asserteq(-ECHILD, expo_render(exp));
+ ut_assertok(expo_calc_dims(exp));
+ ut_assertok(scene_arrange(scn));
+
+ /* check dimensions of text */
+ obj = scene_obj_find(scn, OBJ_TEXT, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(400, obj->dim.x);
+ ut_asserteq(100, obj->dim.y);
+ ut_asserteq(126, obj->dim.w);
+ ut_asserteq(40, obj->dim.h);
+
+ /* check dimensions of image */
+ obj = scene_obj_find(scn, OBJ_LOGO, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(20, obj->dim.y);
+ ut_asserteq(160, obj->dim.w);
+ ut_asserteq(160, obj->dim.h);
+
+ /* check dimensions of menu labels - both should be the same width */
+ obj = scene_obj_find(scn, ITEM1_LABEL, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(436, obj->dim.y);
+ ut_asserteq(29, obj->dim.w);
+ ut_asserteq(18, obj->dim.h);
+
+ obj = scene_obj_find(scn, ITEM2_LABEL, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(454, obj->dim.y);
+ ut_asserteq(29, obj->dim.w);
+ ut_asserteq(18, obj->dim.h);
+
+ /* check dimensions of menu */
+ obj = scene_obj_find(scn, OBJ_MENU, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(400, obj->dim.y);
+ ut_asserteq(160, obj->dim.w);
+ ut_asserteq(160, obj->dim.h);
+
/* render it */
expo_set_scene_id(exp, SCENE1);
ut_assertok(expo_render(exp));