Add support for left, right and centred alignment for text, in the horizontal dimension.
Also support top, bottom and centred in the vertical dimension, for the text object as a whole. Alignment is not yet implemented for images. It has no meaning for menus. A textline object uses a text object internally, so alignment is supported there. Provide some documentation to explain how objects are positioned. Signed-off-by: Simon Glass <s...@chromium.org> --- boot/scene.c | 104 +++++++++++++++++++++++++++++++++++++++++-- doc/develop/expo.rst | 31 +++++++++++++ include/expo.h | 63 ++++++++++++++++++++++++++ test/boot/expo.c | 23 ++++++++-- 4 files changed, 214 insertions(+), 7 deletions(-) diff --git a/boot/scene.c b/boot/scene.c index 640d972ce8f..4f9d4a44e2b 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -305,6 +305,30 @@ int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1, return 0; } +int scene_obj_set_halign(struct scene *scn, uint id, enum scene_obj_align aln) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("osh", -ENOENT); + obj->horiz = aln; + + return 0; +} + +int scene_obj_set_valign(struct scene *scn, uint id, enum scene_obj_align aln) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("osv", -ENOENT); + obj->vert = aln; + + return 0; +} + int scene_obj_set_hide(struct scene *scn, uint id, bool hide) { int ret; @@ -330,6 +354,44 @@ int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set) return 0; } +static void handle_alignment(enum scene_obj_align horiz, + enum scene_obj_align vert, + struct scene_obj_bbox *bbox, + struct scene_obj_dims *dims, + int xsize, int ysize, + struct scene_obj_offset *offset) +{ + int width, height; + + width = bbox->x1 - bbox->x0; + height = bbox->y1 - bbox->y0; + + switch (horiz) { + case SCENEOA_CENTRE: + offset->xofs = (width - dims->x) / 2; + break; + case SCENEOA_RIGHT: + offset->xofs = width - dims->x; + break; + case SCENEOA_LEFT: + offset->xofs = 0; + break; + } + + switch (vert) { + case SCENEOA_CENTRE: + offset->yofs = (height - dims->y) / 2; + break; + case SCENEOA_BOTTOM: + offset->yofs = height - dims->y; + break; + case SCENEOA_TOP: + default: + offset->yofs = 0; + break; + } +} + int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) { struct scene_obj *obj; @@ -444,10 +506,12 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, struct scene_txt_generic *gen, int x, int y, int menu_inset) { - const struct vidconsole_mline *mline; + const struct vidconsole_mline *mline, *last; struct video_priv *vid_priv; struct vidconsole_colour old; enum colour_idx fore, back; + struct scene_obj_dims dims; + struct scene_obj_bbox bbox; const char *str; int ret; @@ -481,9 +545,28 @@ static int scene_txt_render(struct expo *exp, struct udevice *dev, obj->bbox.y1, vid_priv->colour_bg); } + mline = alist_get(&gen->lines, 0, typeof(*mline)); + last = alist_get(&gen->lines, gen->lines.count - 1, typeof(*mline)); + if (mline) + dims.y = last->bbox.y1 - mline->bbox.y0; + bbox.y0 = obj->bbox.y0; + bbox.y1 = obj->bbox.y1; + alist_for_each(mline, &gen->lines) { - vidconsole_set_cursor_pos(cons, x + mline->bbox.x0, - y + mline->bbox.y0); + struct scene_obj_offset offset; + + bbox.x0 = obj->bbox.x0; + bbox.x1 = obj->bbox.x1; + dims.x = mline->bbox.x1 - mline->bbox.x0; + handle_alignment(obj->horiz, obj->vert, &bbox, &dims, + obj->bbox.x1 - obj->bbox.x0, + obj->bbox.y1 - obj->bbox.y0, &offset); + + x = obj->bbox.x0 + offset.xofs; + y = obj->bbox.y0 + offset.yofs + mline->bbox.y0; + if (y > bbox.y1) + break; /* clip this line and any following */ + vidconsole_set_cursor_pos(cons, x, y); vidconsole_put_stringn(cons, str + mline->start, mline->len); } if (obj->flags & SCENEOF_POINT) @@ -510,7 +593,7 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) int x, y, ret; y = obj->bbox.y0; - x = obj->bbox.x0; + x = obj->bbox.x0 + obj->ofs.xofs; vid_priv = dev_get_uclass_priv(dev); switch (obj->type) { @@ -621,14 +704,27 @@ int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr) int scene_arrange(struct scene *scn) { struct expo_arrange_info arr; + int xsize = 0, ysize = 0; struct scene_obj *obj; + struct udevice *dev; int ret; + dev = scn->expo->display; + if (dev) { + struct video_priv *priv = dev_get_uclass_priv(dev); + + xsize = priv->xsize; + ysize = priv->ysize; + } + ret = scene_calc_arrange(scn, &arr); if (ret < 0) return log_msg_ret("arr", ret); list_for_each_entry(obj, &scn->obj_head, sibling) { + handle_alignment(obj->horiz, obj->vert, &obj->bbox, &obj->dims, + xsize, ysize, &obj->ofs); + switch (obj->type) { case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index 8f63ccbe3ef..d6fc487e030 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -107,6 +107,37 @@ refer to objects which have been created. So a menu item is just a collection of IDs of text and image objects. When adding a menu item you must create these objects first, then create the menu item, passing in the relevant IDs. +Position and alignment +~~~~~~~~~~~~~~~~~~~~~~ + +Objects are typically positioned automatically, when scene_arrange() is called. +However it is possible to position objects manually. The scene_obj_set_pos() +sets the coordinates of the top left of the object. + +All objects have a bounding box. Typically this is calculated by looking at the +object contents, in `scene_calc_arrange()`. The calculated dimensions of each +object are stored in the object's `dims` field. + +It is possible to adjust the size of an object with `scene_obj_set_size()` or +even set the bounding box, with `scene_obj_set_bbox()`. The `SCENEOF_SIZE_VALID` +flag tracks whether the width/height should be maintained when the position +changes. + +If the bounding box is larger than the object needs, the object can be aligned +to different edges within the box. Objects can be left- or right-aligned, +or centred. For text objects this applies to each line of text. Normally objects +are drawn starting at the top of their bounding box, but they can be aligned +vertically to the bottom, or centred vertically within the box. + +Where the width of a text object's bounding box is smaller than the space needed +to show the next, the text is word-wrapped onto multiple lines, assuming there +is enough vertical space. Newline characters in the next cause a new line to be +started. The measurement information is created by the Truetype console driver +and stored in an alist in `struct scene_txt_generic`. + +When the object is drawn the `ofs` field indicates the x and y offset to use, +from the top left of the bounding box. These values are affected by alignment. + Creating an expo ---------------- diff --git a/include/expo.h b/include/expo.h index 8833dcceb7e..001f7db2553 100644 --- a/include/expo.h +++ b/include/expo.h @@ -210,6 +210,19 @@ struct scene_obj_bbox { int y1; }; +/** + * struct scene_obj_offset - Offsets for drawing the object + * + * Stores the offset from x0, x1 at which objects are drawn + * + * @xofs: x offset + * @yofs: y offset + */ +struct scene_obj_offset { + int xofs; + int yofs; +}; + /** * struct scene_obj_dims - Dimensions of the object being drawn * @@ -225,6 +238,30 @@ struct scene_obj_dims { int y; }; +/** + * enum scene_obj_halign - Horizontal alignment of objects + * + * Objects are normally drawn on the left size of their bounding box. This + * properly allows aligning on the right or having the object centred. + * + * @SCENEOA_LEFT: Left of object is aligned with its x coordinate + * @SCENEOA_RIGHT: Right of object is aligned with x + w + * @SCENEOA_CENTRE: Centre of object is aligned with centre of bounding box + * @SCENEOA_TOP: Left of object is aligned with its x coordinate + * @SCENEOA_BOTTOM: Right of object is aligned with x + w + * + * Note: It would be nice to make this a char type but Sphinx riddles: + * ./include/expo.h:258: error: Cannot parse enum! + * enum scene_obj_align : char { + */ +enum scene_obj_align { + SCENEOA_LEFT, + SCENEOA_RIGHT, + SCENEOA_CENTRE, + SCENEOA_TOP = SCENEOA_LEFT, + SCENEOA_BOTTOM = SCENEOA_RIGHT, +}; + /** * enum scene_obj_flags_t - flags for objects * @@ -255,7 +292,10 @@ enum { * @id: ID number of the object * @type: Type of this object * @bbox: Bounding box for this object + * @ofs: Offset from x0, y0 where the object is drawn * @dims: Dimensions of the text/image (may be smaller than bbox) + * @horiz: Horizonal alignment + * @vert: Vertical alignment * @flags: Flags for this object * @bit_length: Number of bits used for this object in CMOS RAM * @start_bit: Start bit to use for this object in CMOS RAM @@ -267,7 +307,10 @@ struct scene_obj { uint id; enum scene_obj_t type; struct scene_obj_bbox bbox; + struct scene_obj_offset ofs; struct scene_obj_dims dims; + enum scene_obj_align horiz; + enum scene_obj_align vert; u8 flags; u8 bit_length; u16 start_bit; @@ -755,6 +798,26 @@ int scene_obj_set_width(struct scene *scn, uint id, int w); int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1, int y1); +/** + * scene_obj_set_halign() - Set the horizontal alignment of an object + * + * @scn: Scene to update + * @id: ID of object to update + * @aln: Horizontal alignment to use + * Returns: 0 if OK, -ENOENT if @id is invalid + */ +int scene_obj_set_halign(struct scene *scn, uint id, enum scene_obj_align aln); + +/** + * scene_obj_set_valign() - Set the vertical alignment of an object + * + * @scn: Scene to update + * @id: ID of object to update + * @aln: Vertical alignment to use + * Returns: 0 if OK, -ENOENT if @id is invalid + */ +int scene_obj_set_valign(struct scene *scn, uint id, enum scene_obj_align aln); + /** * scene_obj_set_hide() - Set whether an object is hidden * diff --git a/test/boot/expo.c b/test/boot/expo.c index c76051e1c7d..7bea189da04 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -492,13 +492,14 @@ static int expo_render_image(struct unit_test_state *uts) 60)); ut_assertok(scene_obj_set_pos(scn, OBJ_TEXT2, 200, 600)); + /* this string is clipped as it extends beyond its bottom bound */ id = scene_txt_str(scn, "text", OBJ_TEXT3, STR_TEXT3, - "this is yet\nanother string, with word-wrap", + "this is yet\nanother string, with word-wrap and it goes on for quite a while", NULL); ut_assert(id > 0); ut_assertok(scene_txt_set_font(scn, OBJ_TEXT3, "nimbus_sans_l_regular", 60)); - ut_assertok(scene_obj_set_bbox(scn, OBJ_TEXT3, 500, 200, 1000, 700)); + ut_assertok(scene_obj_set_bbox(scn, OBJ_TEXT3, 500, 200, 1000, 350)); id = scene_menu(scn, "main", OBJ_MENU, &menu); ut_assert(id > 0); @@ -666,9 +667,25 @@ static int expo_render_image(struct unit_test_state *uts) ut_assertok(scene_arrange(scn)); ut_assertok(expo_render(exp)); - ut_asserteq(15016, video_compress_fb(uts, dev, false)); + ut_asserteq(16450, video_compress_fb(uts, dev, false)); ut_assertok(video_check_copy_fb(uts, dev)); + /* do some alignment checks */ + ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_CENTRE)); + ut_assertok(expo_render(exp)); + ut_asserteq(16438, video_compress_fb(uts, dev, false)); + ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_RIGHT)); + ut_assertok(expo_render(exp)); + ut_asserteq(16449, video_compress_fb(uts, dev, false)); + + ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_LEFT)); + ut_assertok(scene_obj_set_valign(scn, OBJ_TEXT3, SCENEOA_CENTRE)); + ut_assertok(expo_render(exp)); + ut_asserteq(18909, video_compress_fb(uts, dev, false)); + ut_assertok(scene_obj_set_valign(scn, OBJ_TEXT3, SCENEOA_BOTTOM)); + ut_assertok(expo_render(exp)); + ut_asserteq(18839, video_compress_fb(uts, dev, false)); + /* make sure only the preview for the second item is shown */ obj = scene_obj_find(scn, ITEM1_PREVIEW, SCENEOBJT_NONE); ut_asserteq(true, obj->flags & SCENEOF_HIDE); -- 2.43.0