1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Implementation of a menu in a scene
4 *
5 * Copyright 2023 Google LLC
6 * Written by Simon Glass <sjg@chromium.org>
7 */
8
9#define LOG_CATEGORY	LOGC_EXPO
10
11#include <common.h>
12#include <expo.h>
13#include <menu.h>
14#include <video_console.h>
15#include "scene_internal.h"
16
17int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars,
18		   struct scene_obj_textline **tlinep)
19{
20	struct scene_obj_textline *tline;
21	char *buf;
22	int ret;
23
24	if (max_chars >= EXPO_MAX_CHARS)
25		return log_msg_ret("chr", -E2BIG);
26
27	ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXTLINE,
28			    sizeof(struct scene_obj_textline),
29			    (struct scene_obj **)&tline);
30	if (ret < 0)
31		return log_msg_ret("obj", -ENOMEM);
32	abuf_init(&tline->buf);
33	if (!abuf_realloc(&tline->buf, max_chars + 1))
34		return log_msg_ret("buf", -ENOMEM);
35	buf = abuf_data(&tline->buf);
36	*buf = '\0';
37	tline->pos = max_chars;
38	tline->max_chars = max_chars;
39
40	if (tlinep)
41		*tlinep = tline;
42
43	return tline->obj.id;
44}
45
46void scene_textline_calc_bbox(struct scene_obj_textline *tline,
47			      struct vidconsole_bbox *bbox,
48			      struct vidconsole_bbox *edit_bbox)
49{
50	const struct expo_theme *theme = &tline->obj.scene->expo->theme;
51
52	bbox->valid = false;
53	scene_bbox_union(tline->obj.scene, tline->label_id, 0, bbox);
54	scene_bbox_union(tline->obj.scene, tline->edit_id, 0, bbox);
55
56	edit_bbox->valid = false;
57	scene_bbox_union(tline->obj.scene, tline->edit_id, theme->menu_inset,
58			 edit_bbox);
59}
60
61int scene_textline_calc_dims(struct scene_obj_textline *tline)
62{
63	struct scene *scn = tline->obj.scene;
64	struct vidconsole_bbox bbox;
65	struct scene_obj_txt *txt;
66	int ret;
67
68	txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE);
69	if (!txt)
70		return log_msg_ret("dim", -ENOENT);
71
72	ret = vidconsole_nominal(scn->expo->cons, txt->font_name,
73				 txt->font_size, tline->max_chars, &bbox);
74	if (ret)
75		return log_msg_ret("nom", ret);
76
77	if (bbox.valid) {
78		tline->obj.dim.w = bbox.x1 - bbox.x0;
79		tline->obj.dim.h = bbox.y1 - bbox.y0;
80
81		scene_obj_set_size(scn, tline->edit_id, tline->obj.dim.w,
82				   tline->obj.dim.h);
83	}
84
85	return 0;
86}
87
88int scene_textline_arrange(struct scene *scn, struct scene_obj_textline *tline)
89{
90	const bool open = tline->obj.flags & SCENEOF_OPEN;
91	bool point;
92	int x, y;
93	int ret;
94
95	x = tline->obj.dim.x;
96	y = tline->obj.dim.y;
97	if (tline->label_id) {
98		ret = scene_obj_set_pos(scn, tline->label_id, tline->obj.dim.x,
99					y);
100		if (ret < 0)
101			return log_msg_ret("tit", ret);
102
103		ret = scene_obj_set_pos(scn, tline->edit_id,
104					tline->obj.dim.x + 200, y);
105		if (ret < 0)
106			return log_msg_ret("tit", ret);
107
108		ret = scene_obj_get_hw(scn, tline->label_id, NULL);
109		if (ret < 0)
110			return log_msg_ret("hei", ret);
111
112		y += ret * 2;
113	}
114
115	point = scn->highlight_id == tline->obj.id;
116	point &= !open;
117	scene_obj_flag_clrset(scn, tline->edit_id, SCENEOF_POINT,
118			      point ? SCENEOF_POINT : 0);
119
120	return 0;
121}
122
123int scene_textline_send_key(struct scene *scn, struct scene_obj_textline *tline,
124			    int key, struct expo_action *event)
125{
126	const bool open = tline->obj.flags & SCENEOF_OPEN;
127
128	log_debug("key=%d\n", key);
129	switch (key) {
130	case BKEY_QUIT:
131		if (open) {
132			event->type = EXPOACT_CLOSE;
133			event->select.id = tline->obj.id;
134
135			/* Copy the backup text from the scene buffer */
136			memcpy(abuf_data(&tline->buf), abuf_data(&scn->buf),
137			       abuf_size(&scn->buf));
138		} else {
139			event->type = EXPOACT_QUIT;
140			log_debug("menu quit\n");
141		}
142		break;
143	case BKEY_SELECT:
144		if (!open)
145			break;
146		event->type = EXPOACT_CLOSE;
147		event->select.id = tline->obj.id;
148		key = '\n';
149		fallthrough;
150	default: {
151		struct udevice *cons = scn->expo->cons;
152		int ret;
153
154		ret = vidconsole_entry_restore(cons, &scn->entry_save);
155		if (ret)
156			return log_msg_ret("sav", ret);
157		ret = cread_line_process_ch(&scn->cls, key);
158		ret = vidconsole_entry_save(cons, &scn->entry_save);
159		if (ret)
160			return log_msg_ret("sav", ret);
161		break;
162	}
163	}
164
165	return 0;
166}
167
168int scene_textline_render_deps(struct scene *scn,
169			       struct scene_obj_textline *tline)
170{
171	const bool open = tline->obj.flags & SCENEOF_OPEN;
172	struct udevice *cons = scn->expo->cons;
173	struct scene_obj_txt *txt;
174	int ret;
175
176	scene_render_deps(scn, tline->label_id);
177	scene_render_deps(scn, tline->edit_id);
178
179	/* show the vidconsole cursor if open */
180	if (open) {
181		/* get the position within the field */
182		txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE);
183		if (!txt)
184			return log_msg_ret("cur", -ENOENT);
185
186		if (txt->font_name || txt->font_size) {
187			ret = vidconsole_select_font(cons,
188						     txt->font_name,
189						     txt->font_size);
190		} else {
191			ret = vidconsole_select_font(cons, NULL, 0);
192		}
193
194		ret = vidconsole_entry_restore(cons, &scn->entry_save);
195		if (ret)
196			return log_msg_ret("sav", ret);
197
198		vidconsole_set_cursor_visible(cons, true, txt->obj.dim.x,
199					      txt->obj.dim.y, scn->cls.num);
200	}
201
202	return 0;
203}
204
205int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline)
206{
207	struct udevice *cons = scn->expo->cons;
208	struct scene_obj_txt *txt;
209	int ret;
210
211	/* Copy the text into the scene buffer in case the edit is cancelled */
212	memcpy(abuf_data(&scn->buf), abuf_data(&tline->buf),
213	       abuf_size(&scn->buf));
214
215	/* get the position of the editable */
216	txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE);
217	if (!txt)
218		return log_msg_ret("cur", -ENOENT);
219
220	vidconsole_set_cursor_pos(cons, txt->obj.dim.x, txt->obj.dim.y);
221	vidconsole_entry_start(cons);
222	cli_cread_init(&scn->cls, abuf_data(&tline->buf), tline->max_chars);
223	scn->cls.insert = true;
224	ret = vidconsole_entry_save(cons, &scn->entry_save);
225	if (ret)
226		return log_msg_ret("sav", ret);
227
228	return 0;
229}
230