[Concept,14/16] expo: support cursor positioning for multiline textedits

Message ID 20260122041155.174721-15-sjg@u-boot.org
State New
Headers
Series expo: Add multiline editing support for textedit |

Commit Message

Simon Glass Jan. 22, 2026, 4:11 a.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

For multiline text input, using backspaces to position the cursor does
not work correctly across wrapped line boundaries: the vidconsole
cursor-position tracking goes wrong when backspacing at the start of a
wrapped line.

Fix this by using direct cursor-positioning via a call to
vidconsole_set_cursor_pos() in scene_txtin_render_deps(), using the
line-measurement info attached to the text object to calculate the
correct position for the cursor.

Single-line textline objects continue to use the backspace approach.

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 boot/scene_txtin.c | 56 +++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 53 insertions(+), 3 deletions(-)
  

Patch

diff --git a/boot/scene_txtin.c b/boot/scene_txtin.c
index 338c7da63d3..72552222fe1 100644
--- a/boot/scene_txtin.c
+++ b/boot/scene_txtin.c
@@ -68,6 +68,46 @@  int scene_txtin_arrange(struct scene *scn, struct expo_arrange_info *arr,
 	return x;
 }
 
+/**
+ * set_cursor_pos() - Set cursor position for multiline text
+ *
+ * Finds the visual line containing the cursor and sets the cursor position
+ * to the correct pixel location within that line.
+ *
+ * @cons: Vidconsole device
+ * @ctx: Vidconsole context
+ * @txt: Text object containing line measurement info
+ * @buf: Text buffer
+ * @pos: Cursor position in buffer
+ */
+static void set_cursor_pos(struct udevice *cons, void *ctx,
+			   struct scene_obj_txt *txt, const char *buf, uint pos)
+{
+	const struct vidconsole_mline *mline, *res;
+	struct vidconsole_bbox bbox;
+	struct alist lines;
+	uint i;
+
+	alist_init_struct(&lines, struct vidconsole_mline);
+	for (i = 0; i < txt->gen.lines.count; i++) {
+		mline = alist_get(&txt->gen.lines, i, struct vidconsole_mline);
+		if (pos < mline->start || pos > mline->start + mline->len)
+			continue;
+		if (vidconsole_measure(cons, txt->gen.font_name,
+				       txt->gen.font_size, buf + mline->start,
+				       pos - mline->start, -1, &bbox, &lines))
+			break;
+		/* measured text is within a single line, so only one result */
+		res = alist_get(&lines, 0, struct vidconsole_mline);
+		if (!res)
+			break;
+		vidconsole_set_cursor_pos(cons, ctx, txt->obj.bbox.x0 + res->xpos,
+					  txt->obj.bbox.y0 + mline->bbox.y0);
+		break;
+	}
+	alist_uninit(&lines);
+}
+
 int scene_txtin_render_deps(struct scene *scn, struct scene_obj *obj,
 			    struct scene_txtin *tin)
 {
@@ -81,9 +121,19 @@  int scene_txtin_render_deps(struct scene *scn, struct scene_obj *obj,
 	if (open) {
 		scene_render_obj(scn, tin->edit_id, ctx);
 
-		/* move cursor back to the correct position */
-		for (i = cls->num; i < cls->eol_num; i++)
-			vidconsole_put_char(cons, ctx, '\b');
+		if (cls->multiline) {
+			/* for multiline, set cursor position directly */
+			struct scene_obj_txt *txt;
+
+			txt = scene_obj_find(scn, tin->edit_id, SCENEOBJT_NONE);
+			if (txt)
+				set_cursor_pos(cons, ctx, txt, cls->buf,
+					       cls->num);
+		} else {
+			/* for single-line, use backspaces */
+			for (i = cls->num; i < cls->eol_num; i++)
+				vidconsole_put_char(cons, ctx, '\b');
+		}
 
 		vidconsole_show_cursor(cons, ctx);
 	}