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;
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;
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;
obj->bbox.y1, vid_priv->colour_bg);
}
- if (!gen->lines.count) {
+ 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;
+
+ if (!mline) {
vidconsole_set_cursor_pos(cons, x, y);
vidconsole_put_string(cons, str);
}
+
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)
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) {
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:
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
----------------
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
*
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
*
* @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
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;
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
*
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);
ut_asserteq(ITEM2, scene_menu_get_cur_item(scn, OBJ_MENU));
ut_assertok(scene_arrange(scn));
ut_assertok(expo_render(exp));
- ut_asserteq(14883, video_compress_fb(uts, dev, false));
+ ut_asserteq(16304, 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(16368, video_compress_fb(uts, dev, false));
+ ut_assertok(scene_obj_set_halign(scn, OBJ_TEXT3, SCENEOA_RIGHT));
+ ut_assertok(expo_render(exp));
+ ut_asserteq(16321, 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(18763, video_compress_fb(uts, dev, false));
+ ut_assertok(scene_obj_set_valign(scn, OBJ_TEXT3, SCENEOA_BOTTOM));
+ ut_assertok(expo_render(exp));
+ ut_asserteq(18714, 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);