[Concept,04/23] expo: Support boxes fully

Message ID 20250915122905.1217249-5-sjg@u-boot.org
State New
Headers
Series expo: Support interactions with a mouse or touchpad |

Commit Message

Simon Glass Sept. 15, 2025, 12:28 p.m. UTC
  From: Simon Glass <sjg@chromium.org>

At present boxes are not supported in the expo_build format. Also, it is
now possible to draw filled boxes, a recently added feature to the video
API.

Expand the box feature slightly to resolve these two items.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <sjg@chromium.org>
---

 arch/sandbox/dts/cedit.dtsi | 13 +++++++++++++
 boot/bootflow_menu.c        |  2 +-
 boot/expo_build.c           | 30 ++++++++++++++++++++++++++++++
 boot/scene.c                | 18 ++++++++++++++++--
 doc/develop/expo.rst        | 16 ++++++++++++++++
 include/expo.h              | 15 ++++++++++++++-
 include/test/cedit-test.h   |  5 ++++-
 test/boot/expo.c            | 37 +++++++++++++++++++++++++++++++++++--
 8 files changed, 129 insertions(+), 7 deletions(-)
  

Patch

diff --git a/arch/sandbox/dts/cedit.dtsi b/arch/sandbox/dts/cedit.dtsi
index facd7a49bef..b4408eeefd8 100644
--- a/arch/sandbox/dts/cedit.dtsi
+++ b/arch/sandbox/dts/cedit.dtsi
@@ -62,6 +62,19 @@ 
 				title = "Machine name";
 				edit-id = <ID_MACHINE_NAME_EDIT>;
 			};
+
+			test-box {
+				type = "box";
+				id = <ID_TEST_BOX>;
+				width = <5>;
+			};
+
+			test-box-filled {
+				type = "box";
+				id = <ID_TEST_BOX_FILLED>;
+				width = <3>;
+				fill;
+			};
 		};
 	};
 
diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c
index 5e0e365d12b..dc075a8f4b2 100644
--- a/boot/bootflow_menu.c
+++ b/boot/bootflow_menu.c
@@ -55,7 +55,7 @@  int bootflow_menu_new(struct expo **expp)
 	if (ret < 0)
 		return log_msg_ret("scn", ret);
 
-	ret = scene_box(scn, "box", OBJ_BOX, 2, NULL);
+	ret = scene_box(scn, "box", OBJ_BOX, 2, false, NULL);
 	if (ret < 0)
 		return log_msg_ret("bmb", ret);
 	ret |= scene_obj_set_bbox(scn, OBJ_BOX, 30, 90, 1366 - 30, 720);
diff --git a/boot/expo_build.c b/boot/expo_build.c
index 9a99a21e5e0..f8ae5bcbbf7 100644
--- a/boot/expo_build.c
+++ b/boot/expo_build.c
@@ -282,6 +282,34 @@  static int menu_build(struct build_info *info, ofnode node, struct scene *scn,
 	return 0;
 }
 
+static int box_build(struct build_info *info, ofnode node,
+		     struct scene *scn, uint id, struct scene_obj **objp)
+{
+	struct scene_obj_box *box;
+	const char *name;
+	u32 width;
+	bool fill;
+	int ret;
+
+	name = ofnode_get_name(node);
+
+	info->err_prop = "width";
+	ret = ofnode_read_u32(node, "width", &width);
+	if (ret)
+		return log_msg_ret("wid", -ENOENT);
+
+	/* fill property is optional, defaults to false */
+	fill = ofnode_read_bool(node, "fill");
+
+	ret = scene_box(scn, name, id, width, fill, &box);
+	if (ret < 0)
+		return log_msg_ret("box", ret);
+
+	*objp = &box->obj;
+
+	return 0;
+}
+
 static int textline_build(struct build_info *info, ofnode node,
 			  struct scene *scn, uint id, struct scene_obj **objp)
 {
@@ -354,6 +382,8 @@  static int obj_build(struct build_info *info, ofnode node, struct scene *scn)
 		ret = menu_build(info, node, scn, id, &obj);
 	else if (!strcmp("textline", type))
 		ret = textline_build(info, node, scn, id, &obj);
+	else if (!strcmp("box", type))
+		ret = box_build(info, node, scn, id, &obj);
 	else
 		ret = -EOPNOTSUPP;
 	if (ret)
diff --git a/boot/scene.c b/boot/scene.c
index 493f2bbab97..0a62dc6212b 100644
--- a/boot/scene.c
+++ b/boot/scene.c
@@ -212,7 +212,7 @@  int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id,
 }
 
 int scene_box(struct scene *scn, const char *name, uint id, uint width,
-	      struct scene_obj_box **boxp)
+	      bool fill, struct scene_obj_box **boxp)
 {
 	struct scene_obj_box *box;
 	int ret;
@@ -224,6 +224,7 @@  int scene_box(struct scene *scn, const char *name, uint id, uint width,
 		return log_msg_ret("obj", ret);
 
 	box->width = width;
+	box->fill = fill;
 
 	if (boxp)
 		*boxp = box;
@@ -231,6 +232,19 @@  int scene_box(struct scene *scn, const char *name, uint id, uint width,
 	return box->obj.id;
 }
 
+int scene_box_set_fill(struct scene *scn, uint id, bool fill)
+{
+	struct scene_obj_box *box;
+
+	box = scene_obj_find(scn, id, SCENEOBJT_BOX);
+	if (!box)
+		return log_msg_ret("find", -ENOENT);
+
+	box->fill = fill;
+
+	return 0;
+}
+
 int scene_txt_set_font(struct scene *scn, uint id, const char *font_name,
 		       uint font_size)
 {
@@ -681,7 +695,7 @@  static int scene_obj_render(struct scene_obj *obj, bool text_mode)
 		struct scene_obj_box *box = (struct scene_obj_box *)obj;
 
 		video_draw_box(dev, obj->bbox.x0, obj->bbox.y0, obj->bbox.x1,
-			       obj->bbox.y1, box->width, vid_priv->colour_fg, false);
+			       obj->bbox.y1, box->width, vid_priv->colour_fg, box->fill);
 		break;
 	}
 	case SCENEOBJT_TEXTEDIT: {
diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst
index 5bdaffb14bb..5c2e3157c8f 100644
--- a/doc/develop/expo.rst
+++ b/doc/develop/expo.rst
@@ -366,6 +366,9 @@  type
     "textline"
         A line of text which can be edited
 
+    "box"
+        A rectangle with a given line width (not filled)
+
 id
     type: u32, required
 
@@ -443,6 +446,19 @@  max-chars:
     Specifies the maximum number of characters permitted to be in the textline.
     The user will be prevented from adding more.
 
+Box nodes have the following additional properties:
+
+width
+    type: u32, required
+
+    Specifies the line width of the box in pixels.
+
+fill
+    type: bool, optional
+
+    Specifies whether to fill the box (true) or draw outline only (false).
+    Defaults to false if not specified.
+
 
 Expo layout
 ~~~~~~~~~~~
diff --git a/include/expo.h b/include/expo.h
index 522f6a93f98..c025e5494a1 100644
--- a/include/expo.h
+++ b/include/expo.h
@@ -495,10 +495,12 @@  struct scene_obj_textline {
  *
  * @obj: Basic object information
  * @width: Line-width in pixels
+ * @fill: true to fill the box, false to draw outline only
  */
 struct scene_obj_box {
 	struct scene_obj obj;
 	uint width;
+	bool fill;
 };
 
 /**
@@ -809,11 +811,22 @@  int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars,
  * @name: Name to use (this is allocated by this call)
  * @id: ID to use for the new object (0 to allocate one)
  * @width: Line-width in pixels
+ * @fill: true to fill the box, false to draw outline only
  * @boxp: If non-NULL, returns the new object
  * Returns: ID number for the object (typically @id), or -ve on error
  */
 int scene_box(struct scene *scn, const char *name, uint id, uint width,
-	      struct scene_obj_box **boxp);
+	      bool fill, struct scene_obj_box **boxp);
+
+/**
+ * scene_box_set_fill() - Set the fill property of a box object
+ *
+ * @scn: Scene containing the box
+ * @id: ID of the box object to update
+ * @fill: true to fill the box, false to draw outline only
+ * Returns: 0 if OK, -ENOENT if the object is not found or is not a box
+ */
+int scene_box_set_fill(struct scene *scn, uint id, bool fill);
 
 /**
  *  scene_texted() - create a text editor
diff --git a/include/test/cedit-test.h b/include/test/cedit-test.h
index 0d38a953415..ec4f3cc0322 100644
--- a/include/test/cedit-test.h
+++ b/include/test/cedit-test.h
@@ -27,6 +27,9 @@ 
 #define ID_MACHINE_NAME		17
 #define ID_MACHINE_NAME_EDIT	18
 
-#define ID_DYNAMIC_START	19
+#define ID_TEST_BOX		19
+#define ID_TEST_BOX_FILLED	20
+
+#define ID_DYNAMIC_START	21
 
 #endif
diff --git a/test/boot/expo.c b/test/boot/expo.c
index 9c3eb984f97..634bab8f203 100644
--- a/test/boot/expo.c
+++ b/test/boot/expo.c
@@ -570,11 +570,11 @@  static int expo_render_image(struct unit_test_state *uts)
 
 	ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400));
 
-	id = scene_box(scn, "box", OBJ_BOX, 3, NULL);
+	id = scene_box(scn, "box", OBJ_BOX, 3, false, NULL);
 	ut_assert(id > 0);
 	ut_assertok(scene_obj_set_bbox(scn, OBJ_BOX, 40, 390, 1000, 510));
 
-	id = scene_box(scn, "box2", OBJ_BOX2, 1, NULL);
+	id = scene_box(scn, "box2", OBJ_BOX2, 1, false, NULL);
 	ut_assert(id > 0);
 	ut_assertok(scene_obj_set_bbox(scn, OBJ_BOX, 500, 200, 1000, 350));
 
@@ -880,6 +880,39 @@  static int expo_test_build(struct unit_test_state *uts)
 	count = list_count_nodes(&menu->item_head);
 	ut_asserteq(3, count);
 
+	/* check the box */
+	struct scene_obj_box *box = scene_obj_find(scn, ID_TEST_BOX, SCENEOBJT_NONE);
+	ut_assertnonnull(box);
+	obj = &box->obj;
+	ut_asserteq_ptr(scn, obj->scene);
+	ut_asserteq_str("test-box", obj->name);
+	ut_asserteq(ID_TEST_BOX, obj->id);
+	ut_asserteq(SCENEOBJT_BOX, obj->type);
+	ut_asserteq(0, obj->flags);
+	ut_asserteq(5, box->width);
+	ut_asserteq(false, box->fill);
+
+	/* check the filled box */
+	struct scene_obj_box *filled_box = scene_obj_find(scn, ID_TEST_BOX_FILLED, SCENEOBJT_NONE);
+	ut_assertnonnull(filled_box);
+	obj = &filled_box->obj;
+	ut_asserteq_ptr(scn, obj->scene);
+	ut_asserteq_str("test-box-filled", obj->name);
+	ut_asserteq(ID_TEST_BOX_FILLED, obj->id);
+	ut_asserteq(SCENEOBJT_BOX, obj->type);
+	ut_asserteq(0, obj->flags);
+	ut_asserteq(3, filled_box->width);
+	ut_asserteq(true, filled_box->fill);
+
+	/* test scene_box_set_fill() function */
+	ut_assertok(scene_box_set_fill(scn, ID_TEST_BOX, true));
+	ut_asserteq(true, box->fill);
+	ut_assertok(scene_box_set_fill(scn, ID_TEST_BOX_FILLED, false));
+	ut_asserteq(false, filled_box->fill);
+
+	/* test error case */
+	ut_asserteq(-ENOENT, scene_box_set_fill(scn, 9999, true));
+
 	/* try editing some text */
 	ut_assertok(expo_edit_str(exp, txt->gen.str_id, &orig, &copy));
 	ut_asserteq_str("2 GHz", orig.data);