[Concept,10/18] expo: Allow searching click positions from top to bottom

Message ID 20251010034255.1099728-11-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>

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.

This is not true when a menu is popped up, since we only care about the
menu in that case.

Add a way to reverse the direction of the object search. For now there
are no new test cases, since OBJ_OVERLAP is a text object and cannot
currently be clicked on.

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

 boot/scene.c          | 45 +++++++++++++++++++++++++++++--------------
 boot/scene_internal.h |  5 ++++-
 test/boot/expo.c      | 10 +++++-----
 3 files changed, 40 insertions(+), 20 deletions(-)
  

Patch

diff --git a/boot/scene.c b/boot/scene.c
index a9e0d1f1266..05a683dd1da 100644
--- a/boot/scene.c
+++ b/boot/scene.c
@@ -1193,20 +1193,37 @@  bool scene_obj_within(const struct scene *scn, struct scene_obj *obj, int x,
 	return within;
 }
 
-struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y)
+struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y,
+					bool reverse)
 {
 	struct scene_obj *obj;
 
-	log_debug("within: x %d y %d\n", x, y);
-	list_for_each_entry(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) &&
-		    scene_obj_within(scn, obj, x, y)) {
-			log_debug("- returning obj %d '%s'\n", obj->id,
-				  obj->name);
-			return obj;
+	log_debug("within: x %d y %d reverse %d\n", x, y, reverse);
+	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) &&
+			    scene_obj_within(scn, obj, x, y)) {
+				log_debug("- returning obj %d '%s'\n", obj->id,
+					  obj->name);
+				return obj;
+			}
+		}
+	} else {
+		list_for_each_entry(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) &&
+			    scene_obj_within(scn, obj, x, y)) {
+				log_debug("- returning obj %d '%s'\n", obj->id,
+					  obj->name);
+				return obj;
+			}
 		}
 	}
 	log_debug("- no object\n");
@@ -1238,7 +1255,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);
+	obj = scene_find_obj_within(scn, x, y, false);
 	if (obj) {
 		event->type = EXPOACT_POINT_OPEN;
 		event->select.id = obj->id;
@@ -1264,7 +1281,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);
+	chk = scene_find_obj_within(scn, x, y, false);
 	log_debug("chk %d '%s' (obj %d '%s')\n", chk ? chk->id : -1,
 		  chk ? chk->name : "(none)", obj->id, obj->name);
 	if (!chk) {
@@ -1323,7 +1340,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);
+	obj = scene_find_obj_within(scn, x, y, 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 8e61067d4b4..e2f4cc066d0 100644
--- a/boot/scene_internal.h
+++ b/boot/scene_internal.h
@@ -562,8 +562,11 @@  const char *scene_obj_type_name(enum scene_obj_t type);
  * @scn: Scene to check
  * @x: X coordinates of the click
  * @y: Y coordinate of the click
+ * @reverse: true to search from top to bottom (reverse order), false for
+ *	bottom to top
  * 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);
+struct scene_obj *scene_find_obj_within(const struct scene *scn, int x, int y,
+					bool reverse);
 
 #endif /* __SCENE_INTERNAL_H */
diff --git a/test/boot/expo.c b/test/boot/expo.c
index db4a54e7cd7..e32550a6685 100644
--- a/test/boot/expo.c
+++ b/test/boot/expo.c
@@ -1289,19 +1289,19 @@  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);
+	obj = scene_find_obj_within(scn, 60, 440, 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));
-	ut_assertnull(scene_find_obj_within(scn, 410, 110));
+	ut_assertnull(scene_find_obj_within(scn, 60, 30, false));
+	ut_assertnull(scene_find_obj_within(scn, 410, 110, false));
 
 	/* empty space */
-	ut_assertnull(scene_find_obj_within(scn, 10, 10));
+	ut_assertnull(scene_find_obj_within(scn, 10, 10, false));
 
 	/* way outside bounds */
-	ut_assertnull(scene_find_obj_within(scn, 9999, 9999));
+	ut_assertnull(scene_find_obj_within(scn, 9999, 9999, false));
 
 	abuf_uninit(&buf);
 	abuf_uninit(&logo_copy);