// SPDX-License-Identifier: GPL-2.0+ /* * Implementation of a menu in a scene * * Copyright 2023 Google LLC * Written by Simon Glass */ #define LOG_CATEGORY LOGC_EXPO #include #include #include #include #include #include #include "scene_internal.h" int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars, struct scene_obj_textline **tlinep) { struct scene_obj_textline *tline; char *buf; int ret; if (max_chars >= EXPO_MAX_CHARS) return log_msg_ret("chr", -E2BIG); ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXTLINE, sizeof(struct scene_obj_textline), (struct scene_obj **)&tline); if (ret < 0) return log_msg_ret("obj", -ENOMEM); if (!abuf_init_size(&tline->buf, max_chars + 1)) return log_msg_ret("buf", -ENOMEM); buf = abuf_data(&tline->buf); *buf = '\0'; tline->pos = max_chars; tline->max_chars = max_chars; if (tlinep) *tlinep = tline; return tline->obj.id; } void scene_textline_calc_bbox(struct scene_obj_textline *tline, struct vidconsole_bbox *bbox, struct vidconsole_bbox *edit_bbox) { const struct expo_theme *theme = &tline->obj.scene->expo->theme; int inset = theme->menu_inset; bbox->valid = false; scene_bbox_union(tline->obj.scene, tline->label_id, inset, bbox); scene_bbox_union(tline->obj.scene, tline->edit_id, inset, bbox); edit_bbox->valid = false; scene_bbox_union(tline->obj.scene, tline->edit_id, inset, edit_bbox); } int scene_textline_calc_dims(struct scene_obj_textline *tline, struct udevice *cons) { struct scene *scn = tline->obj.scene; struct vidconsole_bbox bbox; struct scene_obj_txt *txt; int ret; txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE); if (!txt) return log_msg_ret("dim", -ENOENT); ret = vidconsole_nominal(cons, txt->gen.font_name, txt->gen.font_size, tline->max_chars, &bbox); if (ret) return log_msg_ret("nom", ret); if (bbox.valid) { struct scene_obj *obj = &txt->obj; obj->dims.x = bbox.x1 - bbox.x0; obj->dims.y = bbox.y1 - bbox.y0; } return 0; } int scene_textline_arrange(struct scene *scn, struct expo_arrange_info *arr, struct scene_obj_textline *tline) { const bool open = tline->obj.flags & SCENEOF_OPEN; const struct expo_theme *theme = &scn->expo->theme; bool point; int x, y; int ret; x = tline->obj.req_bbox.x0; y = tline->obj.req_bbox.y0; if (tline->label_id) { struct scene_obj *edit; ret = scene_obj_set_pos(scn, tline->label_id, x, y); if (ret < 0) return log_msg_ret("tit", ret); x += arr->label_width + theme->textline_label_margin_x; ret = scene_obj_set_pos(scn, tline->edit_id, x, y); if (ret < 0) return log_msg_ret("til", ret); edit = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE); if (!edit) return log_msg_ret("tie", -ENOENT); x += edit->dims.x; y += edit->dims.y; } point = scn->highlight_id == tline->obj.id; point &= !open; scene_obj_flag_clrset(scn, tline->edit_id, SCENEOF_POINT, point ? SCENEOF_POINT : 0); tline->obj.dims.x = x - tline->obj.req_bbox.x0; tline->obj.dims.y = y - tline->obj.req_bbox.y0; scene_obj_set_size(scn, tline->obj.id, tline->obj.dims.x, tline->obj.dims.y); return 0; } int scene_textline_send_key(struct scene *scn, struct scene_obj_textline *tline, int key, struct expo_action *event) { const bool open = tline->obj.flags & SCENEOF_OPEN; log_debug("key=%d\n", key); switch (key) { case BKEY_QUIT: if (open) { event->type = EXPOACT_CLOSE; event->select.id = tline->obj.id; /* Copy the backup text from the scene buffer */ memcpy(abuf_data(&tline->buf), abuf_data(&scn->buf), abuf_size(&scn->buf)); /* cursor is not needed now */ vidconsole_readline_end(); } else { event->type = EXPOACT_QUIT; log_debug("menu quit\n"); } break; case BKEY_SELECT: if (!open) break; event->type = EXPOACT_CLOSE; event->select.id = tline->obj.id; key = '\n'; fallthrough; default: { struct udevice *cons = scn->expo->cons; int ret; ret = vidconsole_entry_restore(cons, &scn->entry_save); if (ret) return log_msg_ret("sav", ret); ret = cread_line_process_ch(&scn->cls, key); ret = vidconsole_entry_save(cons, &scn->entry_save); if (ret) return log_msg_ret("sav", ret); break; } } return 0; } bool scene_textline_within(const struct scene *scn, struct scene_obj_textline *tline, int x, int y) { return scene_within(scn, tline->edit_id, x, y); } int scene_textline_render_deps(struct scene *scn, struct scene_obj_textline *tline) { const bool open = tline->obj.flags & SCENEOF_OPEN; struct udevice *cons = scn->expo->cons; uint i; /* if open, render the edit text on top of the background */ if (open) { int ret; ret = vidconsole_entry_restore(cons, &scn->entry_save); if (ret) return log_msg_ret("sav", ret); scene_render_obj(scn, tline->edit_id); /* move cursor back to the correct position */ for (i = scn->cls.num; i < scn->cls.eol_num; i++) vidconsole_put_char(cons, '\b'); ret = vidconsole_entry_save(cons, &scn->entry_save); if (ret) return log_msg_ret("sav", ret); vidconsole_show_cursor(cons); } return 0; } int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline) { struct udevice *cons = scn->expo->cons; struct scene_obj_txt *txt; int ret; /* Copy the text into the scene buffer in case the edit is cancelled */ memcpy(abuf_data(&scn->buf), abuf_data(&tline->buf), abuf_size(&scn->buf)); /* get the position of the editable */ txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE); if (!txt) return log_msg_ret("cur", -ENOENT); vidconsole_set_cursor_pos(cons, txt->obj.bbox.x0, txt->obj.bbox.y0); vidconsole_entry_start(cons); cli_cread_init(&scn->cls, abuf_data(&tline->buf), tline->max_chars); scn->cls.insert = true; ret = vidconsole_entry_save(cons, &scn->entry_save); if (ret) return log_msg_ret("sav", ret); /* make sure the cursor is visible */ vidconsole_readline_start(true); return 0; }