[Concept,11/18] expo: Allow searching for any object type by position

Message ID 20251010034255.1099728-12-sjg@u-boot.org
State New
Headers
Series expo: Extend the boot menu |

Commit Message

Simon Glass Oct. 10, 2025, 3:42 a.m. UTC
  From: Simon Glass <sjg@chromium.org>

At present only highlightable objects can be clicked on, i.e. menus and
textlines. Update scene_find_obj_within() so that it can find any type
of object, if requested. Update all the callers to false, so things work
the same.

Since the scene is drawn by iterating through the list of objects, when
the user clicks somewhere we should look at the top-most object under
the mouse first. So reverse the direction of the object search.

Update the tests to cover this new feature.

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

 boot/scene.c          | 15 ++++++-------
 boot/scene_internal.h |  3 ++-
 test/boot/expo.c      | 49 +++++++++++++++++++++++++++++++++++++------
 3 files changed, 53 insertions(+), 14 deletions(-)
  

Patch

diff --git a/boot/scene.c b/boot/scene.c
index 05a683dd1da..4cf1b6d5852 100644
--- a/boot/scene.c
+++ b/boot/scene.c
@@ -1194,18 +1194,19 @@  bool scene_obj_within(const struct scene *scn, struct scene_obj *obj, int x,
 }
 
 struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y,
-					bool reverse)
+					bool reverse, bool allow_any)
 {
 	struct scene_obj *obj;
 
-	log_debug("within: x %d y %d reverse %d\n", x, y, reverse);
+	log_debug("within: x %d y %d reverse %d allow_any %d\n", x, y, reverse,
+		  allow_any);
 	if (reverse) {
 		list_for_each_entry_reverse(obj, &scn->obj_head, sibling) {
 			log_debug(" - obj %d '%s' can_highlight %d within %d\n",
 				  obj->id, obj->name,
 				  scene_obj_can_highlight(obj),
 				  scene_obj_within(scn, obj, x, y));
-			if (scene_obj_can_highlight(obj) &&
+			if ((allow_any || scene_obj_can_highlight(obj)) &&
 			    scene_obj_within(scn, obj, x, y)) {
 				log_debug("- returning obj %d '%s'\n", obj->id,
 					  obj->name);
@@ -1218,7 +1219,7 @@  struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y,
 				  obj->id, obj->name,
 				  scene_obj_can_highlight(obj),
 				  scene_obj_within(scn, obj, x, y));
-			if (scene_obj_can_highlight(obj) &&
+			if ((allow_any || scene_obj_can_highlight(obj)) &&
 			    scene_obj_within(scn, obj, x, y)) {
 				log_debug("- returning obj %d '%s'\n", obj->id,
 					  obj->name);
@@ -1255,7 +1256,7 @@  static void send_click_obj(struct scene *scn, struct scene_obj *obj, int x,
 	}
 
 	log_debug("no object; finding...\n");
-	obj = scene_find_obj_within(scn, x, y, false);
+	obj = scene_find_obj_within(scn, x, y, false, false);
 	if (obj) {
 		event->type = EXPOACT_POINT_OPEN;
 		event->select.id = obj->id;
@@ -1281,7 +1282,7 @@  static int scene_click_popup(struct scene *scn, int x, int y,
 	}
 
 	/* check that the click is within our object */
-	chk = scene_find_obj_within(scn, x, y, false);
+	chk = scene_find_obj_within(scn, x, y, false, false);
 	log_debug("chk %d '%s' (obj %d '%s')\n", chk ? chk->id : -1,
 		  chk ? chk->name : "(none)", obj->id, obj->name);
 	if (!chk) {
@@ -1340,7 +1341,7 @@  int scene_send_click(struct scene *scn, int x, int y, struct expo_action *event)
 		return 0;
 	}
 
-	obj = scene_find_obj_within(scn, x, y, false);
+	obj = scene_find_obj_within(scn, x, y, false, false);
 	log_debug("non-popup obj %d '%s'\n", obj ? obj->id : -1,
 		  obj ? obj->name : "(none)");
 	if (!obj)
diff --git a/boot/scene_internal.h b/boot/scene_internal.h
index e2f4cc066d0..2bfbb5dcf50 100644
--- a/boot/scene_internal.h
+++ b/boot/scene_internal.h
@@ -564,9 +564,10 @@  const char *scene_obj_type_name(enum scene_obj_t type);
  * @y: Y coordinate of the click
  * @reverse: true to search from top to bottom (reverse order), false for
  *	bottom to top
+ * @allow_any: true to allow searching non-highlight objects
  * Return: object that is being clicked on, NULL if none
  */
 struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y,
-					bool reverse);
+					bool reverse, bool allow_any);
 
 #endif /* __SCENE_INTERNAL_H */
diff --git a/test/boot/expo.c b/test/boot/expo.c
index e32550a6685..66fd5a2873f 100644
--- a/test/boot/expo.c
+++ b/test/boot/expo.c
@@ -1289,19 +1289,56 @@  static int expo_find_obj_within(struct unit_test_state *uts)
 	 * Check finding a menu by 'clicking' on a menu item label - menu items
 	 * are at (50,436) for ITEM1 and (50,454) for ITEM2
 	 */
-	obj = scene_find_obj_within(scn, 60, 440, false);
+	obj = scene_find_obj_within(scn, 60, 440, false, false);
 	ut_assertnonnull(obj);
 	ut_asserteq(OBJ_MENU, obj->id);
 
-	/* logo and text are not highlightable, so they should not be found */
-	ut_assertnull(scene_find_obj_within(scn, 60, 30, false));
-	ut_assertnull(scene_find_obj_within(scn, 410, 110, false));
+	/*
+	 * Check with allow_any=false for non-highlightable objects - logo and
+	 * text are not highlightable, so they should not be found
+	 */
+	ut_assertnull(scene_find_obj_within(scn, 60, 30, false, false));
+	ut_assertnull(scene_find_obj_within(scn, 410, 110, false, false));
+
+	/* Test with allow_any=true for non-highlightable objects */
+	obj = scene_find_obj_within(scn, 60, 30, false, true);
+	ut_assertnonnull(obj);
+	ut_asserteq(OBJ_LOGO, obj->id);
+
+	/*
+	 * Check reversing search order with allow_any=true at the overlapping
+	 * position. OBJ_TEXT was created first at (400, 100), and the
+	 * "overlap" text object was created second at (405, 105). They
+	 * overlap at position (410, 110).
+	 *
+	 * With reverse=false, we search from start of list (bottom to top) and
+	 * find OBJ_TEXT first.
+	 */
+	obj = scene_find_obj_within(scn, 410, 110, false, true);
+	ut_assertnonnull(obj);
+	ut_asserteq(OBJ_TEXT, obj->id);
+
+	/*
+	 * With reverse=true, we search from end of list (top to bottom) and
+	 * find the OBJ_OVERLAP_TEST object first.
+	 */
+	obj = scene_find_obj_within(scn, 410, 110, true, true);
+	ut_assertnonnull(obj);
+	ut_asserteq(OBJ_OVERLAP, obj->id);
+
+	/*
+	 * Test reverse=true with a non-overlapping object - should get same
+	 * result as reverse=false
+	 */
+	obj = scene_find_obj_within(scn, 60, 30, true, true);
+	ut_assertnonnull(obj);
+	ut_asserteq(OBJ_LOGO, obj->id);
 
 	/* empty space */
-	ut_assertnull(scene_find_obj_within(scn, 10, 10, false));
+	ut_assertnull(scene_find_obj_within(scn, 10, 10, false, false));
 
 	/* way outside bounds */
-	ut_assertnull(scene_find_obj_within(scn, 9999, 9999, false));
+	ut_assertnull(scene_find_obj_within(scn, 9999, 9999, false, false));
 
 	abuf_uninit(&buf);
 	abuf_uninit(&logo_copy);