/**
* @internal
- * Free the layout paragraph and all of it's lines.
+ * Free the visual lines in the paragraph (logical items are kept)
*/
static void
-_paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
+_paragraph_clear(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
{
while (par->lines)
{
par->lines = (Evas_Object_Textblock_Line *)eina_inlist_remove(EINA_INLIST_GET(par->lines), EINA_INLIST_GET(par->lines));
_line_free(obj, ln);
}
+}
+
+/**
+ * @internal
+ * Free the layout paragraph and all of it's lines and logical items.
+ */
+static void
+_paragraph_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *par)
+{
+ _paragraph_clear(obj, par);
{
Eina_List *i, *i_prev;
_paragraphs_clear(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
{
Evas_Object_Textblock *o;
+ Evas_Object_Textblock_Paragraph *par;
+ o = (Evas_Object_Textblock *)(obj->object_data);
+
+ EINA_INLIST_FOREACH(EINA_INLIST_GET(pars), par)
+ {
+ _paragraph_clear(obj, par);
+ }
+}
+
+/**
+ * @internal
+ * Free the paragraphs from the inlist pars, the difference between this and
+ * _paragraphs_clear is that the latter keeps the logical items and the par
+ * items, while the former frees them as well.
+ *
+ * @param obj the evas object - Not NULL.
+ * @param pars the paragraphs to clean - Not NULL.
+ */
+static void
+_paragraphs_free(const Evas_Object *obj, Evas_Object_Textblock_Paragraph *pars)
+{
+ Evas_Object_Textblock *o;
o = (Evas_Object_Textblock *)(obj->object_data);
while (pars)
{
_layout_text_add_logical_item(c, ti->parent.format, new_ti, _ITEM(ti));
/* FIXME: Will break with kerning and a bunch of other stuff, should
- * maybe adjust the last adv of the prev and the offset of the cur*/
+ * maybe adjust the last adv of the prev and the offset of the cur
+ * There's also another similar fixme below (same case, not marked) */
ti->parent.w -= new_ti->parent.w;
ti->parent.adv -= new_ti->parent.adv;
/**
* @internal
+ * Merge item2 into item1 and free item2.
+ *
+ * @param c the context to work on - Not NULL.
+ * @param item1 the item to copy to
+ * @param item2 the item to copy from
+ */
+static void
+_layout_item_merge_and_free(Ctxt *c,
+ Evas_Object_Textblock_Text_Item *item1,
+ Evas_Object_Textblock_Text_Item *item2)
+{
+ Eina_Unicode *tmp;
+ size_t len1, len2;
+ item1->parent.w += item2->parent.w;
+ item1->parent.adv += item2->parent.adv;
+ evas_common_text_props_merge(&item1->parent.text_props,
+ &item2->parent.text_props);
+ len1 = eina_unicode_strlen(item1->text);
+ len2 = eina_unicode_strlen(item2->text);
+ tmp = realloc(item1->text, (len1 + len2 + 1) * sizeof(Eina_Unicode));
+ eina_unicode_strcpy(tmp + len1, item2->text);
+ item1->text = tmp;
+ item1->text[len1 + len2] = 0;
+
+ item1->parent.merge = EINA_FALSE;
+ item1->parent.visually_deleted = EINA_FALSE;
+
+ _item_free(c->obj, NULL, _ITEM(item2));
+}
+
+/**
+ * @internal
* Return the start of the last word up until start.
*
* @param str the string to work on.
}
- /* If there are no nodes and lines, do the initial creation. */
- if (!c->o->text_nodes)
+ if (o->content_changed)
{
- _layout_paragraph_new(c, NULL);
- _layout_line_new(c, fmt);
- _layout_text_append(c, fmt, NULL, 0, 0, NULL);
- _layout_line_finalize(c, fmt);
- }
-
+ _paragraphs_free(obj, o->paragraphs);
/* Go through all the text nodes to create the logical layout */
- EINA_INLIST_FOREACH(c->o->text_nodes, n)
- {
- Evas_Object_Textblock_Node_Format *fnode;
- size_t start;
- int off;
-
- _layout_paragraph_new(c, n); /* Each node is a paragraph */
-
- /* For each text node to thorugh all of it's format nodes
- * append text from the start to the offset of the next format
- * using the last format got. if needed it also creates format items
- * this is the core algorithm of the layout mechanism.
- * Skip the unicode replacement chars when there are because
- * we don't want to print them. */
- fnode = n->format_node;
- start = off = 0;
- while (fnode && (fnode->text_node == n))
+ EINA_INLIST_FOREACH(c->o->text_nodes, n)
{
- off += fnode->offset;
- /* No need to skip on the first run, or a non-visible one */
- _layout_text_append(c, fmt, n, start, off, o->repch);
- _layout_do_format(obj, c, &fmt, fnode, &style_pad_l, &style_pad_r, &style_pad_t, &style_pad_b);
- if ((c->have_underline2) || (c->have_underline))
- {
- if (style_pad_b < c->underline_extend)
- style_pad_b = c->underline_extend;
- c->have_underline = 0;
- c->have_underline2 = 0;
- c->underline_extend = 0;
- }
- start += off;
- if (fnode->visible)
+ Evas_Object_Textblock_Node_Format *fnode;
+ size_t start;
+ int off;
+
+ n->dirty = 0; /* Mark as if we cleaned the paragraph, although
+ we should really use it to fine tune the
+ changes here, and not just blindly mark */
+ _layout_paragraph_new(c, n); /* Each node is a paragraph */
+
+ /* For each text node to thorugh all of it's format nodes
+ * append text from the start to the offset of the next format
+ * using the last format got. if needed it also creates format items
+ * this is the core algorithm of the layout mechanism.
+ * Skip the unicode replacement chars when there are because
+ * we don't want to print them. */
+ fnode = n->format_node;
+ start = off = 0;
+ while (fnode && (fnode->text_node == n))
{
- off = -1;
- start++;
+ off += fnode->offset;
+ /* No need to skip on the first run, or a non-visible one */
+ _layout_text_append(c, fmt, n, start, off, o->repch);
+ _layout_do_format(obj, c, &fmt, fnode, &style_pad_l,
+ &style_pad_r, &style_pad_t, &style_pad_b);
+ if ((c->have_underline2) || (c->have_underline))
+ {
+ if (style_pad_b < c->underline_extend)
+ style_pad_b = c->underline_extend;
+ c->have_underline = 0;
+ c->have_underline2 = 0;
+ c->underline_extend = 0;
+ }
+ start += off;
+ if (fnode->visible)
+ {
+ off = -1;
+ start++;
+ }
+ else
+ {
+ off = 0;
+ }
+ fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
}
- else
+ _layout_text_append(c, fmt, n, start, -1, o->repch);
+ }
+ }
+ else
+ {
+ _paragraphs_clear(obj, o->paragraphs);
+ c->paragraphs = o->paragraphs;
+ /* Merge the ones that need merging. */
+ /* Go through all the paragraphs, lines, items and merge if should be
+ * merged we merge backwards!!! */
+ Evas_Object_Textblock_Paragraph *par;
+ EINA_INLIST_FOREACH(EINA_INLIST_GET(c->paragraphs), par)
+ {
+ Eina_List *itr, *itr_next;
+ Evas_Object_Textblock_Item *it, *prev_it = NULL;
+ EINA_LIST_FOREACH_SAFE(par->logical_items, itr, itr_next, it)
{
- off = 0;
+ if (it->merge && prev_it &&
+ (prev_it->type == EVAS_TEXTBLOCK_ITEM_TEXT) &&
+ (it->type == EVAS_TEXTBLOCK_ITEM_TEXT))
+ {
+ _layout_item_merge_and_free(c, _ITEM_TEXT(prev_it),
+ _ITEM_TEXT(it));
+ par->logical_items =
+ eina_list_remove_list(par->logical_items, itr);
+ }
+ else
+ {
+ prev_it = it;
+ }
}
- fnode = _NODE_FORMAT(EINA_INLIST_GET(fnode)->next);
}
- _layout_text_append(c, fmt, n, start, -1, o->repch);
}
+ /* If there are no paragraphs, create the minimum needed */
+ if (!c->paragraphs)
+ {
+ _layout_paragraph_new(c, NULL);
+ _layout_line_new(c, fmt);
+ _layout_text_append(c, fmt, NULL, 0, 0, NULL);
+ _layout_line_finalize(c, fmt);
+ }
+
+
/* End of logical layout creation */
/* Start of visual layout creation */
if (w_ret) *w_ret = c->wmax;
if (h_ret) *h_ret = c->hmax;
+ o->paragraphs = c->paragraphs;
+
if ((o->style_pad.l != style_pad_l) || (o->style_pad.r != style_pad_r) ||
(o->style_pad.t != style_pad_t) || (o->style_pad.b != style_pad_b))
{
- c->par->lines = NULL;
o->style_pad.l = style_pad_l;
o->style_pad.r = style_pad_r;
o->style_pad.t = style_pad_t;
o->style_pad.b = style_pad_b;
- _layout(obj, calc_only, w, h, w_ret, h_ret);
_paragraphs_clear(obj, c->paragraphs);
+ _layout(obj, calc_only, w, h, w_ret, h_ret);
return;
}
- if (!calc_only)
- {
- o->paragraphs = c->paragraphs;
- return;
- }
- if (c->paragraphs) _paragraphs_clear(obj, c->paragraphs);
+
+ return;
}
/*
_relayout(const Evas_Object *obj)
{
Evas_Object_Textblock *o;
- Evas_Object_Textblock_Paragraph *paragraphs;
o = (Evas_Object_Textblock *)(obj->object_data);
- paragraphs = o->paragraphs;
- o->paragraphs = NULL;
_layout(obj,
0,
obj->cur.geometry.w, obj->cur.geometry.h,
&o->formatted.w, &o->formatted.h);
o->formatted.valid = 1;
- if (paragraphs) _paragraphs_clear(obj, paragraphs);
o->last_w = obj->cur.geometry.w;
o->changed = 0;
o->content_changed = 0;
}
if (o->paragraphs)
{
- _paragraphs_clear(obj, o->paragraphs);
+ _paragraphs_free(obj, o->paragraphs);
o->paragraphs = NULL;
}
_evas_textblock_text_node_changed(o, obj, NULL);
if ((o->changed) || (o->content_changed) ||
(o->last_w != obj->cur.geometry.w))
{
- Evas_Object_Textblock_Paragraph *paragraphs;
-
- paragraphs = o->paragraphs;
- o->paragraphs = NULL;
o->formatted.valid = 0;
_layout(obj,
0,
obj->cur.geometry.w, obj->cur.geometry.h,
&o->formatted.w, &o->formatted.h);
o->formatted.valid = 1;
- if (paragraphs)
- {
- _paragraphs_clear(obj, paragraphs);
- }
o->last_w = obj->cur.geometry.w;
o->redraw = 0;
evas_object_render_pre_prev_cur_add(&obj->layer->evas->clip_changes, obj);
printf("next = %p, prev = %p, last = %p\n", EINA_INLIST_GET(n)->next, EINA_INLIST_GET(n)->prev, EINA_INLIST_GET(n)->last);
printf("format_node = %p\n", n->format_node);
printf("'%ls'\n", eina_ustrbuf_string_get(n->unicode));
- if (n->bidi_props)
- {
- size_t i;
- for (i = 0 ; i < eina_ustrbuf_length_get(n->unicode) ; i++)
- {
- printf("%d", n->bidi_props->embedding_levels[i]);
- }
- printf("\n");
- }
}
void
}
/* Won't work in the middle of ligatures
- * assumes ext doesn't have an already init ot_data
+ * aissumes ext doesn't have an already init ot_data
* we assume there's at least one char in each part */
EAPI void
evas_common_font_ot_split_text_props(Evas_Text_Props *base,
{
ext->ot_data->items[i].source_cluster -= min;
}
+ ext->ot_data->offset = base->ot_data->offset + min;
}
tmp = realloc(base->ot_data->items,
cutoff * sizeof(Evas_Font_OT_Data_Item));
}
+/* Won't work in the middle of ligatures
+ * assumes both are init correctly and that both are from the
+ * same origin item, i.e both have the same script + direction.
+ * assume item1 is logically first */
+EAPI void
+evas_common_font_ot_merge_text_props(Evas_Text_Props *item1,
+ const Evas_Text_Props *item2)
+{
+ Evas_Font_OT_Data_Item *tmp, *itr; /* Itr will be used for adding back
+ the offsets */
+ size_t len;
+ if (!item1->ot_data || !item2->ot_data)
+ return;
+ len = item1->ot_data->len + item2->ot_data->len;
+ tmp = calloc(len, sizeof(Evas_Font_OT_Data_Item));
+ if (item1->bidi.dir == EVAS_BIDI_DIRECTION_RTL)
+ {
+ memcpy(tmp, item2->ot_data->items,
+ item2->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
+ memcpy(tmp + item2->ot_data->len, item1->ot_data->items,
+ item1->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
+ itr = tmp;
+ }
+ else
+ {
+ memcpy(tmp, item1->ot_data->items,
+ item1->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
+ memcpy(tmp + item1->ot_data->len, item2->ot_data->items,
+ item2->ot_data->len * sizeof(Evas_Font_OT_Data_Item));
+ itr = tmp + item1->ot_data->len;
+ }
+ free(item1->ot_data->items);
+ item1->ot_data->items = tmp;
+ item1->ot_data->len = len;
+ /* Add back the offset of item2 to the newly created */
+ if (item2->ot_data->offset > 0)
+ {
+ int i;
+ for (i = 0 ; i < (int) item2->ot_data->len ; i++, itr++)
+ {
+ /* This must be > 0, just because this is how it works */
+ itr->source_cluster += item2->ot_data->offset -
+ item1->ot_data->offset;
+ }
+ }
+}
+
EAPI Eina_Bool
evas_common_font_ot_populate_text_props(void *_fn, const Eina_Unicode *text,
Evas_Text_Props *props, int len)