Insets are handled inconsistently at present, since menus use them to offset the label text, whereas textlines don't. This is done because menus need the margin to be visible when opened. However this causes an alignment issue when menus and textlines appear in the same cedit. Remove the offsets from menus and compensate by adjusting the bounding boxes used for highlighting and the opened menu. Line up menu items and textlines vertically and add a style option for textlines to control how much padding is added. Add a test to check the positions of objects in a cedit, since this is more direct than the rendering tests. Add style information so that the impact can be seen. Signed-off-by: Simon Glass <sjg@chromium.org>
239 lines
5.9 KiB
C
239 lines
5.9 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Implementation of a menu in a scene
|
|
*
|
|
* Copyright 2023 Google LLC
|
|
* Written by Simon Glass <sjg@chromium.org>
|
|
*/
|
|
|
|
#define LOG_CATEGORY LOGC_EXPO
|
|
|
|
#include <expo.h>
|
|
#include <menu.h>
|
|
#include <log.h>
|
|
#include <video_console.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/string.h>
|
|
#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 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(scn->expo->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));
|
|
} 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;
|
|
}
|
|
|
|
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;
|
|
struct scene_obj_txt *txt;
|
|
int ret;
|
|
|
|
scene_render_deps(scn, tline->label_id);
|
|
scene_render_deps(scn, tline->edit_id);
|
|
|
|
/* show the vidconsole cursor if open */
|
|
if (open) {
|
|
/* get the position within the field */
|
|
txt = scene_obj_find(scn, tline->edit_id, SCENEOBJT_NONE);
|
|
if (!txt)
|
|
return log_msg_ret("cur", -ENOENT);
|
|
|
|
if (txt->gen.font_name || txt->gen.font_size) {
|
|
ret = vidconsole_select_font(cons,
|
|
txt->gen.font_name,
|
|
txt->gen.font_size);
|
|
} else {
|
|
ret = vidconsole_select_font(cons, NULL, 0);
|
|
}
|
|
|
|
ret = vidconsole_entry_restore(cons, &scn->entry_save);
|
|
if (ret)
|
|
return log_msg_ret("sav", ret);
|
|
|
|
vidconsole_set_cursor_visible(cons, true, txt->obj.bbox.x0,
|
|
txt->obj.bbox.y0, scn->cls.num);
|
|
}
|
|
|
|
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);
|
|
|
|
return 0;
|
|
}
|