[TextServer] Improvements for line breaking, "Fill" alignment, overrun, and interaction between these modes.
Fix "Fill" alignment processing wrong side of the text if overrun trim was applied. Improve "Fill" alignment to avoid adding excessive subsequent spaces or elongations. Add font detection to the overrun, to correctly add ellipsis (was using last glyph font, which doesn't necessary have dot character). Improve line breaking to avoid adding excessive subsequent soft break points for languages without word separator. Port missing overrun/justification code to the Fallback text server. Fix inferred text direction detection by controls. Add tests for "Fill" alignment and line breaking glyph flags.
This commit is contained in:
parent
2f4d76f068
commit
baec983d8a
@ -1051,6 +1051,13 @@
|
|||||||
Returns composite character's bounds as offsets from the start of the line.
|
Returns composite character's bounds as offsets from the start of the line.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="shaped_text_get_inferred_direction" qualifiers="const">
|
||||||
|
<return type="int" enum="TextServer.Direction" />
|
||||||
|
<argument index="0" name="shaped" type="RID" />
|
||||||
|
<description>
|
||||||
|
Returns direction of the text, inferred by the BiDi algorithm.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="shaped_text_get_line_breaks" qualifiers="const">
|
<method name="shaped_text_get_line_breaks" qualifiers="const">
|
||||||
<return type="PackedInt32Array" />
|
<return type="PackedInt32Array" />
|
||||||
<argument index="0" name="shaped" type="RID" />
|
<argument index="0" name="shaped" type="RID" />
|
||||||
|
@ -1061,6 +1061,13 @@
|
|||||||
Returns composite character's bounds as offsets from the start of the line.
|
Returns composite character's bounds as offsets from the start of the line.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="_shaped_text_get_inferred_direction" qualifiers="virtual const">
|
||||||
|
<return type="int" />
|
||||||
|
<argument index="0" name="shaped" type="RID" />
|
||||||
|
<description>
|
||||||
|
Returns direction of the text, inferred by the BiDi algorithm.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="_shaped_text_get_line_breaks" qualifiers="virtual const">
|
<method name="_shaped_text_get_line_breaks" qualifiers="virtual const">
|
||||||
<return type="PackedInt32Array" />
|
<return type="PackedInt32Array" />
|
||||||
<argument index="0" name="shaped" type="RID" />
|
<argument index="0" name="shaped" type="RID" />
|
||||||
|
@ -3029,6 +3029,14 @@ TextServer::Direction TextServerAdvanced::shaped_text_get_direction(RID p_shaped
|
|||||||
return sd->direction;
|
return sd->direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextServer::Direction TextServerAdvanced::shaped_text_get_inferred_direction(RID p_shaped) const {
|
||||||
|
const ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||||
|
ERR_FAIL_COND_V(!sd, TextServer::DIRECTION_LTR);
|
||||||
|
|
||||||
|
MutexLock lock(sd->mutex);
|
||||||
|
return sd->para_direction;
|
||||||
|
}
|
||||||
|
|
||||||
void TextServerAdvanced::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
|
void TextServerAdvanced::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
|
||||||
_THREAD_SAFE_METHOD_
|
_THREAD_SAFE_METHOD_
|
||||||
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
ShapedTextDataAdvanced *sd = shaped_owner.get_or_null(p_shaped);
|
||||||
@ -3582,7 +3590,11 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
|
|||||||
float justification_width;
|
float justification_width;
|
||||||
if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
|
if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
|
||||||
if (sd->overrun_trim_data.trim_pos >= 0) {
|
if (sd->overrun_trim_data.trim_pos >= 0) {
|
||||||
|
if (sd->para_direction == DIRECTION_RTL) {
|
||||||
start_pos = sd->overrun_trim_data.trim_pos;
|
start_pos = sd->overrun_trim_data.trim_pos;
|
||||||
|
} else {
|
||||||
|
end_pos = sd->overrun_trim_data.trim_pos;
|
||||||
|
}
|
||||||
justification_width = sd->width_trimmed;
|
justification_width = sd->width_trimmed;
|
||||||
} else {
|
} else {
|
||||||
return sd->width;
|
return sd->width;
|
||||||
@ -3592,6 +3604,7 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
|
if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
|
||||||
|
// Trim spaces.
|
||||||
while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
|
while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
|
||||||
justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
|
justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
|
||||||
sd->glyphs.write[start_pos].advance = 0;
|
sd->glyphs.write[start_pos].advance = 0;
|
||||||
@ -3602,6 +3615,14 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
|
|||||||
sd->glyphs.write[end_pos].advance = 0;
|
sd->glyphs.write[end_pos].advance = 0;
|
||||||
end_pos -= sd->glyphs[end_pos].count;
|
end_pos -= sd->glyphs[end_pos].count;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Skip breaks, but do not reset size.
|
||||||
|
while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
|
||||||
|
start_pos += sd->glyphs[start_pos].count;
|
||||||
|
}
|
||||||
|
while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
|
||||||
|
end_pos -= sd->glyphs[end_pos].count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int space_count = 0;
|
int space_count = 0;
|
||||||
@ -3610,8 +3631,11 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
|
|||||||
const Glyph &gl = sd->glyphs[i];
|
const Glyph &gl = sd->glyphs[i];
|
||||||
if (gl.count > 0) {
|
if (gl.count > 0) {
|
||||||
if ((gl.flags & GRAPHEME_IS_ELONGATION) == GRAPHEME_IS_ELONGATION) {
|
if ((gl.flags & GRAPHEME_IS_ELONGATION) == GRAPHEME_IS_ELONGATION) {
|
||||||
|
if ((i > 0) && ((sd->glyphs[i - 1].flags & GRAPHEME_IS_ELONGATION) != GRAPHEME_IS_ELONGATION)) {
|
||||||
|
// Expand once per elongation sequence.
|
||||||
elongation_count++;
|
elongation_count++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
|
if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
|
||||||
space_count++;
|
space_count++;
|
||||||
}
|
}
|
||||||
@ -3624,16 +3648,21 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
|
|||||||
Glyph &gl = sd->glyphs.write[i];
|
Glyph &gl = sd->glyphs.write[i];
|
||||||
if (gl.count > 0) {
|
if (gl.count > 0) {
|
||||||
if (((gl.flags & GRAPHEME_IS_ELONGATION) == GRAPHEME_IS_ELONGATION) && (gl.advance > 0)) {
|
if (((gl.flags & GRAPHEME_IS_ELONGATION) == GRAPHEME_IS_ELONGATION) && (gl.advance > 0)) {
|
||||||
|
if ((i > 0) && ((sd->glyphs[i - 1].flags & GRAPHEME_IS_ELONGATION) != GRAPHEME_IS_ELONGATION)) {
|
||||||
|
// Expand once per elongation sequence.
|
||||||
int count = delta_width_per_kashida / gl.advance;
|
int count = delta_width_per_kashida / gl.advance;
|
||||||
int prev_count = gl.repeat;
|
int prev_count = gl.repeat;
|
||||||
if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
|
if ((gl.flags & GRAPHEME_IS_VIRTUAL) == GRAPHEME_IS_VIRTUAL) {
|
||||||
gl.repeat = MAX(count, 0);
|
gl.repeat = MAX(count, 0);
|
||||||
|
} else {
|
||||||
|
gl.repeat = MAX(count + 1, 1);
|
||||||
}
|
}
|
||||||
justification_width += (gl.repeat - prev_count) * gl.advance;
|
justification_width += (gl.repeat - prev_count) * gl.advance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
float adv_remain = 0;
|
float adv_remain = 0;
|
||||||
if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
|
if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
|
||||||
float delta_width_per_space = (p_width - justification_width) / space_count;
|
float delta_width_per_space = (p_width - justification_width) / space_count;
|
||||||
@ -3671,7 +3700,7 @@ float TextServerAdvanced::shaped_text_fit_to_width(RID p_shaped, float p_width,
|
|||||||
sd->width = justification_width;
|
sd->width = justification_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sd->width;
|
return justification_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
|
float TextServerAdvanced::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
|
||||||
@ -3759,23 +3788,56 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<ShapedTextDataAdvanced::Span> &spans = sd->spans;
|
||||||
|
if (sd->parent != RID()) {
|
||||||
|
ShapedTextDataAdvanced *parent_sd = shaped_owner.get_or_null(sd->parent);
|
||||||
|
ERR_FAIL_COND(!parent_sd->valid);
|
||||||
|
spans = parent_sd->spans;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spans.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int sd_size = sd->glyphs.size();
|
int sd_size = sd->glyphs.size();
|
||||||
RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
|
|
||||||
int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;
|
int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;
|
||||||
int32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, last_gl_font_size, '.');
|
|
||||||
Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, dot_gl_idx);
|
// Find usable fonts, if fonts from the last glyph do not have required chars.
|
||||||
int32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, last_gl_font_size, ' ');
|
RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
|
||||||
Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, whitespace_gl_idx);
|
if (!font_has_char(dot_gl_font_rid, '.')) {
|
||||||
|
const Vector<RID> &fonts = spans[spans.size() - 1].fonts;
|
||||||
|
for (const RID &font : fonts) {
|
||||||
|
if (font_has_char(font, '.')) {
|
||||||
|
dot_gl_font_rid = font;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
|
||||||
|
if (!font_has_char(whitespace_gl_font_rid, '.')) {
|
||||||
|
const Vector<RID> &fonts = spans[spans.size() - 1].fonts;
|
||||||
|
for (const RID &font : fonts) {
|
||||||
|
if (font_has_char(font, ' ')) {
|
||||||
|
whitespace_gl_font_rid = font;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, '.') : -10;
|
||||||
|
Vector2 dot_adv = dot_gl_font_rid.is_valid() ? font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
|
||||||
|
int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ') : -10;
|
||||||
|
Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();
|
||||||
|
|
||||||
int ellipsis_width = 0;
|
int ellipsis_width = 0;
|
||||||
if (add_ellipsis) {
|
if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {
|
||||||
ellipsis_width = 3 * dot_adv.x + font_get_spacing(last_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
|
ellipsis_width = 3 * dot_adv.x + font_get_spacing(whitespace_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ell_min_characters = 6;
|
int ell_min_characters = 6;
|
||||||
float width = sd->width;
|
float width = sd->width;
|
||||||
|
|
||||||
bool is_rtl = sd->direction == DIRECTION_RTL || (sd->direction == DIRECTION_AUTO && sd->para_direction == DIRECTION_RTL);
|
bool is_rtl = sd->para_direction == DIRECTION_RTL;
|
||||||
|
|
||||||
int trim_pos = (is_rtl) ? sd_size : 0;
|
int trim_pos = (is_rtl) ? sd_size : 0;
|
||||||
int ellipsis_pos = (enforce_ellipsis) ? 0 : -1;
|
int ellipsis_pos = (enforce_ellipsis) ? 0 : -1;
|
||||||
@ -3833,24 +3895,26 @@ void TextServerAdvanced::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
|
|||||||
gl.count = 1;
|
gl.count = 1;
|
||||||
gl.advance = whitespace_adv.x;
|
gl.advance = whitespace_adv.x;
|
||||||
gl.index = whitespace_gl_idx;
|
gl.index = whitespace_gl_idx;
|
||||||
gl.font_rid = last_gl_font_rid;
|
gl.font_rid = whitespace_gl_font_rid;
|
||||||
gl.font_size = last_gl_font_size;
|
gl.font_size = last_gl_font_size;
|
||||||
gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0);
|
gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0);
|
||||||
|
|
||||||
sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
|
sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
|
||||||
}
|
}
|
||||||
// Add ellipsis dots.
|
// Add ellipsis dots.
|
||||||
|
if (dot_gl_idx != 0) {
|
||||||
Glyph gl;
|
Glyph gl;
|
||||||
gl.count = 1;
|
gl.count = 1;
|
||||||
gl.repeat = 3;
|
gl.repeat = 3;
|
||||||
gl.advance = dot_adv.x;
|
gl.advance = dot_adv.x;
|
||||||
gl.index = dot_gl_idx;
|
gl.index = dot_gl_idx;
|
||||||
gl.font_rid = last_gl_font_rid;
|
gl.font_rid = dot_gl_font_rid;
|
||||||
gl.font_size = last_gl_font_size;
|
gl.font_size = last_gl_font_size;
|
||||||
gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0);
|
gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL | (is_rtl ? GRAPHEME_IS_RTL : 0);
|
||||||
|
|
||||||
sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
|
sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sd->text_trimmed = true;
|
sd->text_trimmed = true;
|
||||||
sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0);
|
sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0);
|
||||||
@ -3920,16 +3984,15 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
|
|||||||
for (int j = r_start; j < r_end; j++) {
|
for (int j = r_start; j < r_end; j++) {
|
||||||
char32_t c = sd->text[j - sd->start];
|
char32_t c = sd->text[j - sd->start];
|
||||||
if (is_whitespace(c)) {
|
if (is_whitespace(c)) {
|
||||||
breaks[j] = false;
|
breaks[j + 1] = false;
|
||||||
}
|
}
|
||||||
if (is_linebreak(c)) {
|
if (is_linebreak(c)) {
|
||||||
breaks[j] = true;
|
breaks[j + 1] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (ubrk_next(bi) != UBRK_DONE) {
|
while (ubrk_next(bi) != UBRK_DONE) {
|
||||||
int pos = _convert_pos(sd, ubrk_current(bi)) + r_start - 1;
|
int pos = _convert_pos(sd, ubrk_current(bi)) + r_start;
|
||||||
if (pos != r_end) {
|
|
||||||
if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
|
if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_HARD) && (ubrk_getRuleStatus(bi) < UBRK_LINE_HARD_LIMIT)) {
|
||||||
breaks[pos] = true;
|
breaks[pos] = true;
|
||||||
} else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
|
} else if ((ubrk_getRuleStatus(bi) >= UBRK_LINE_SOFT) && (ubrk_getRuleStatus(bi) < UBRK_LINE_SOFT_LIMIT)) {
|
||||||
@ -3937,7 +4000,6 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
ubrk_close(bi);
|
ubrk_close(bi);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
@ -3978,36 +4040,48 @@ bool TextServerAdvanced::shaped_text_update_breaks(RID p_shaped) {
|
|||||||
if (is_underscore(c)) {
|
if (is_underscore(c)) {
|
||||||
sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
|
sd_glyphs[i].flags |= GRAPHEME_IS_UNDERSCORE;
|
||||||
}
|
}
|
||||||
if (breaks.has(sd->glyphs[i].start)) {
|
if (breaks.has(sd_glyphs[i].end)) {
|
||||||
if (breaks[sd->glyphs[i].start]) {
|
if (breaks[sd_glyphs[i].end] && (is_linebreak(c))) {
|
||||||
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
|
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
|
||||||
} else {
|
} else if (is_whitespace(c)) {
|
||||||
if (is_whitespace(c)) {
|
|
||||||
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
|
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
|
||||||
} else {
|
} else {
|
||||||
|
int count = sd_glyphs[i].count;
|
||||||
|
// Do not add extra space at the end of the line.
|
||||||
|
if (sd_glyphs[i].end == sd->end) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Do not add extra space after existing space.
|
||||||
|
if (sd_glyphs[i].flags & GRAPHEME_IS_RTL) {
|
||||||
|
if ((i + count < sd_size - 1) && ((sd_glyphs[i + count].flags & (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) == (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((i > 0) && ((sd_glyphs[i - 1].flags & (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) == (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
Glyph gl;
|
Glyph gl;
|
||||||
gl.start = sd_glyphs[i].start;
|
gl.start = sd_glyphs[i].start;
|
||||||
gl.end = sd_glyphs[i].end;
|
gl.end = sd_glyphs[i].end;
|
||||||
gl.count = 1;
|
gl.count = 1;
|
||||||
gl.font_rid = sd_glyphs[i].font_rid;
|
gl.font_rid = sd_glyphs[i].font_rid;
|
||||||
gl.font_size = sd_glyphs[i].font_size;
|
gl.font_size = sd_glyphs[i].font_size;
|
||||||
gl.flags = GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL;
|
gl.flags = GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL | GRAPHEME_IS_SPACE;
|
||||||
if (sd->glyphs[i].flags & GRAPHEME_IS_RTL) {
|
if (sd_glyphs[i].flags & GRAPHEME_IS_RTL) {
|
||||||
gl.flags |= GRAPHEME_IS_RTL;
|
gl.flags |= GRAPHEME_IS_RTL;
|
||||||
sd->glyphs.insert(i, gl); // Insert before.
|
sd->glyphs.insert(i, gl); // Insert before.
|
||||||
} else {
|
} else {
|
||||||
sd->glyphs.insert(i + sd_glyphs[i].count, gl); // Insert after.
|
sd->glyphs.insert(i + count, gl); // Insert after.
|
||||||
}
|
}
|
||||||
|
i += count;
|
||||||
|
|
||||||
// Update write pointer and size.
|
// Update write pointer and size.
|
||||||
sd_size = sd->glyphs.size();
|
sd_size = sd->glyphs.size();
|
||||||
sd_glyphs = sd->glyphs.ptrw();
|
sd_glyphs = sd->glyphs.ptrw();
|
||||||
|
|
||||||
i += sd_glyphs[i].count;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
i += (sd_glyphs[i].count - 1);
|
i += (sd_glyphs[i].count - 1);
|
||||||
}
|
}
|
||||||
@ -4151,53 +4225,80 @@ bool TextServerAdvanced::shaped_text_update_justification_ops(RID p_shaped) {
|
|||||||
|
|
||||||
sd->sort_valid = false;
|
sd->sort_valid = false;
|
||||||
sd->glyphs_logical.clear();
|
sd->glyphs_logical.clear();
|
||||||
int sd_size = sd->glyphs.size();
|
|
||||||
|
|
||||||
|
Glyph *sd_glyphs = sd->glyphs.ptrw();
|
||||||
|
int sd_size = sd->glyphs.size();
|
||||||
if (jstops.size() > 0) {
|
if (jstops.size() > 0) {
|
||||||
for (int i = 0; i < sd_size; i++) {
|
for (int i = 0; i < sd_size; i++) {
|
||||||
if (sd->glyphs[i].count > 0) {
|
if (sd_glyphs[i].count > 0) {
|
||||||
if (jstops.has(sd->glyphs[i].start)) {
|
char32_t c = sd->text[sd_glyphs[i].start - sd->start];
|
||||||
char32_t c = sd->text[sd->glyphs[i].start - sd->start];
|
if (c == 0x0640) {
|
||||||
|
sd_glyphs[i].flags |= GRAPHEME_IS_ELONGATION;
|
||||||
|
}
|
||||||
|
if (jstops.has(sd_glyphs[i].start)) {
|
||||||
if (c == 0xfffc) {
|
if (c == 0xfffc) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (jstops[sd->glyphs[i].start]) {
|
if (jstops[sd_glyphs[i].start]) {
|
||||||
if (c == 0x0640) {
|
if (c != 0x0640) {
|
||||||
sd->glyphs.write[i].flags |= GRAPHEME_IS_ELONGATION;
|
if (sd_glyphs[i].font_rid != RID()) {
|
||||||
} else {
|
|
||||||
if (sd->glyphs[i].font_rid != RID()) {
|
|
||||||
Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size);
|
Glyph gl = _shape_single_glyph(sd, 0x0640, HB_SCRIPT_ARABIC, HB_DIRECTION_RTL, sd->glyphs[i].font_rid, sd->glyphs[i].font_size);
|
||||||
if ((gl.flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) {
|
if ((sd_glyphs[i].flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) {
|
||||||
gl.start = sd->glyphs[i].start;
|
gl.start = sd_glyphs[i].start;
|
||||||
gl.end = sd->glyphs[i].end;
|
gl.end = sd_glyphs[i].end;
|
||||||
gl.repeat = 0;
|
gl.repeat = 0;
|
||||||
gl.count = 1;
|
gl.count = 1;
|
||||||
if (sd->orientation == ORIENTATION_HORIZONTAL) {
|
if (sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||||
gl.y_off = sd->glyphs[i].y_off;
|
gl.y_off = sd_glyphs[i].y_off;
|
||||||
} else {
|
} else {
|
||||||
gl.x_off = sd->glyphs[i].x_off;
|
gl.x_off = sd_glyphs[i].x_off;
|
||||||
}
|
}
|
||||||
gl.flags |= GRAPHEME_IS_ELONGATION | GRAPHEME_IS_VIRTUAL;
|
gl.flags |= GRAPHEME_IS_ELONGATION | GRAPHEME_IS_VIRTUAL;
|
||||||
sd->glyphs.insert(i, gl);
|
sd->glyphs.insert(i, gl);
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
|
// Update write pointer and size.
|
||||||
|
sd_size = sd->glyphs.size();
|
||||||
|
sd_glyphs = sd->glyphs.ptrw();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!is_whitespace(c)) {
|
} else if ((sd_glyphs[i].flags & GRAPHEME_IS_SPACE) != GRAPHEME_IS_SPACE) {
|
||||||
|
int count = sd_glyphs[i].count;
|
||||||
|
// Do not add extra spaces at the end of the line.
|
||||||
|
if (sd_glyphs[i].end == sd->end) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Do not add extra space after existing space.
|
||||||
|
if (sd_glyphs[i].flags & GRAPHEME_IS_RTL) {
|
||||||
|
if ((i + count < sd_size - 1) && ((sd_glyphs[i + count].flags & (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) == (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ((i > 0) && ((sd_glyphs[i - 1].flags & (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT)) == (GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Inject virtual space for alignment.
|
||||||
Glyph gl;
|
Glyph gl;
|
||||||
gl.start = sd->glyphs[i].start;
|
gl.start = sd_glyphs[i].start;
|
||||||
gl.end = sd->glyphs[i].end;
|
gl.end = sd_glyphs[i].end;
|
||||||
gl.count = 1;
|
gl.count = 1;
|
||||||
gl.font_rid = sd->glyphs[i].font_rid;
|
gl.font_rid = sd_glyphs[i].font_rid;
|
||||||
gl.font_size = sd->glyphs[i].font_size;
|
gl.font_size = sd_glyphs[i].font_size;
|
||||||
gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_VIRTUAL;
|
gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_VIRTUAL;
|
||||||
if (sd->glyphs[i].flags & GRAPHEME_IS_RTL) {
|
if (sd_glyphs[i].flags & GRAPHEME_IS_RTL) {
|
||||||
gl.flags |= GRAPHEME_IS_RTL;
|
gl.flags |= GRAPHEME_IS_RTL;
|
||||||
sd->glyphs.insert(i, gl); // Insert before.
|
sd->glyphs.insert(i, gl); // Insert before.
|
||||||
} else {
|
} else {
|
||||||
sd->glyphs.insert(i + sd->glyphs[i].count, gl); // Insert after.
|
sd->glyphs.insert(i + count, gl); // Insert after.
|
||||||
}
|
}
|
||||||
i += sd->glyphs[i].count;
|
i += count;
|
||||||
|
|
||||||
|
// Update write pointer and size.
|
||||||
|
sd_size = sd->glyphs.size();
|
||||||
|
sd_glyphs = sd->glyphs.ptrw();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -466,6 +466,7 @@ public:
|
|||||||
|
|
||||||
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
|
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
|
||||||
virtual Direction shaped_text_get_direction(RID p_shaped) const override;
|
virtual Direction shaped_text_get_direction(RID p_shaped) const override;
|
||||||
|
virtual Direction shaped_text_get_inferred_direction(RID p_shaped) const override;
|
||||||
|
|
||||||
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
|
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
|
||||||
|
|
||||||
|
@ -2128,6 +2128,10 @@ TextServer::Direction TextServerFallback::shaped_text_get_direction(RID p_shaped
|
|||||||
return TextServer::DIRECTION_LTR;
|
return TextServer::DIRECTION_LTR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextServer::Direction TextServerFallback::shaped_text_get_inferred_direction(RID p_shaped) const {
|
||||||
|
return TextServer::DIRECTION_LTR;
|
||||||
|
}
|
||||||
|
|
||||||
void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
|
void TextServerFallback::shaped_text_set_custom_punctuation(RID p_shaped, const String &p_punct) {
|
||||||
_THREAD_SAFE_METHOD_
|
_THREAD_SAFE_METHOD_
|
||||||
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
ShapedTextData *sd = shaped_owner.get_or_null(p_shaped);
|
||||||
@ -2631,17 +2635,38 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float justification_width;
|
||||||
|
if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) == JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
|
||||||
|
if (sd->overrun_trim_data.trim_pos >= 0) {
|
||||||
|
end_pos = sd->overrun_trim_data.trim_pos;
|
||||||
|
justification_width = sd->width_trimmed;
|
||||||
|
} else {
|
||||||
|
return sd->width;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
justification_width = sd->width;
|
||||||
|
}
|
||||||
|
|
||||||
if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
|
if ((p_jst_flags & JUSTIFICATION_TRIM_EDGE_SPACES) == JUSTIFICATION_TRIM_EDGE_SPACES) {
|
||||||
|
// Trim spaces.
|
||||||
while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
|
while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
|
||||||
sd->width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
|
justification_width -= sd->glyphs[start_pos].advance * sd->glyphs[start_pos].repeat;
|
||||||
sd->glyphs.write[start_pos].advance = 0;
|
sd->glyphs.write[start_pos].advance = 0;
|
||||||
start_pos += sd->glyphs[start_pos].count;
|
start_pos += sd->glyphs[start_pos].count;
|
||||||
}
|
}
|
||||||
while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
|
while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD || (sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_SOFT) == GRAPHEME_IS_BREAK_SOFT)) {
|
||||||
sd->width -= sd->glyphs[end_pos].advance * sd->glyphs[end_pos].repeat;
|
justification_width -= sd->glyphs[end_pos].advance * sd->glyphs[end_pos].repeat;
|
||||||
sd->glyphs.write[end_pos].advance = 0;
|
sd->glyphs.write[end_pos].advance = 0;
|
||||||
end_pos -= sd->glyphs[end_pos].count;
|
end_pos -= sd->glyphs[end_pos].count;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Skip breaks, but do not reset size.
|
||||||
|
while ((start_pos < end_pos) && ((sd->glyphs[start_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD)) {
|
||||||
|
start_pos += sd->glyphs[start_pos].count;
|
||||||
|
}
|
||||||
|
while ((start_pos < end_pos) && ((sd->glyphs[end_pos].flags & GRAPHEME_IS_BREAK_HARD) == GRAPHEME_IS_BREAK_HARD)) {
|
||||||
|
end_pos -= sd->glyphs[end_pos].count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int space_count = 0;
|
int space_count = 0;
|
||||||
@ -2655,20 +2680,28 @@ float TextServerFallback::shaped_text_fit_to_width(RID p_shaped, float p_width,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
|
if ((space_count > 0) && ((p_jst_flags & JUSTIFICATION_WORD_BOUND) == JUSTIFICATION_WORD_BOUND)) {
|
||||||
float delta_width_per_space = (p_width - sd->width) / space_count;
|
float delta_width_per_space = (p_width - justification_width) / space_count;
|
||||||
for (int i = start_pos; i <= end_pos; i++) {
|
for (int i = start_pos; i <= end_pos; i++) {
|
||||||
Glyph &gl = sd->glyphs.write[i];
|
Glyph &gl = sd->glyphs.write[i];
|
||||||
if (gl.count > 0) {
|
if (gl.count > 0) {
|
||||||
if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
|
if ((gl.flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) {
|
||||||
float old_adv = gl.advance;
|
float old_adv = gl.advance;
|
||||||
gl.advance = MAX(gl.advance + delta_width_per_space, Math::round(0.1 * gl.font_size));
|
gl.advance = MAX(gl.advance + delta_width_per_space, Math::round(0.1 * gl.font_size));
|
||||||
sd->width += (gl.advance - old_adv);
|
justification_width += (gl.advance - old_adv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sd->width;
|
if (Math::floor(p_width) < Math::floor(justification_width)) {
|
||||||
|
sd->fit_width_minimum_reached = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((p_jst_flags & JUSTIFICATION_CONSTRAIN_ELLIPSIS) != JUSTIFICATION_CONSTRAIN_ELLIPSIS) {
|
||||||
|
sd->width = justification_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
return justification_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
|
float TextServerFallback::shaped_text_tab_align(RID p_shaped, const PackedFloat32Array &p_tab_stops) {
|
||||||
@ -2769,6 +2802,7 @@ bool TextServerFallback::shaped_text_update_breaks(RID p_shaped) {
|
|||||||
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
|
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_SOFT;
|
||||||
}
|
}
|
||||||
if (is_linebreak(c)) {
|
if (is_linebreak(c)) {
|
||||||
|
sd_glyphs[i].flags |= GRAPHEME_IS_SPACE;
|
||||||
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
|
sd_glyphs[i].flags |= GRAPHEME_IS_BREAK_HARD;
|
||||||
}
|
}
|
||||||
if (c == 0x0009 || c == 0x000b) {
|
if (c == 0x0009 || c == 0x000b) {
|
||||||
@ -2827,17 +2861,50 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector<ShapedTextData::Span> &spans = sd->spans;
|
||||||
|
if (sd->parent != RID()) {
|
||||||
|
ShapedTextData *parent_sd = shaped_owner.get_or_null(sd->parent);
|
||||||
|
ERR_FAIL_COND(!parent_sd->valid);
|
||||||
|
spans = parent_sd->spans;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spans.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int sd_size = sd->glyphs.size();
|
int sd_size = sd->glyphs.size();
|
||||||
RID last_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
|
|
||||||
int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;
|
int last_gl_font_size = sd_glyphs[sd_size - 1].font_size;
|
||||||
int32_t dot_gl_idx = font_get_glyph_index(last_gl_font_rid, '.', 0);
|
|
||||||
Vector2 dot_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, dot_gl_idx);
|
// Find usable fonts, if fonts from the last glyph do not have required chars.
|
||||||
int32_t whitespace_gl_idx = font_get_glyph_index(last_gl_font_rid, ' ', 0);
|
RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
|
||||||
Vector2 whitespace_adv = font_get_glyph_advance(last_gl_font_rid, last_gl_font_size, whitespace_gl_idx);
|
if (!font_has_char(dot_gl_font_rid, '.')) {
|
||||||
|
const Vector<RID> &fonts = spans[spans.size() - 1].fonts;
|
||||||
|
for (const RID &font : fonts) {
|
||||||
|
if (font_has_char(font, '.')) {
|
||||||
|
dot_gl_font_rid = font;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RID whitespace_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
|
||||||
|
if (!font_has_char(whitespace_gl_font_rid, '.')) {
|
||||||
|
const Vector<RID> &fonts = spans[spans.size() - 1].fonts;
|
||||||
|
for (const RID &font : fonts) {
|
||||||
|
if (font_has_char(font, ' ')) {
|
||||||
|
whitespace_gl_font_rid = font;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, '.') : -10;
|
||||||
|
Vector2 dot_adv = dot_gl_font_rid.is_valid() ? font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
|
||||||
|
int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ') : -10;
|
||||||
|
Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();
|
||||||
|
|
||||||
int ellipsis_width = 0;
|
int ellipsis_width = 0;
|
||||||
if (add_ellipsis) {
|
if (add_ellipsis && whitespace_gl_font_rid.is_valid()) {
|
||||||
ellipsis_width = 3 * dot_adv.x + font_get_spacing(last_gl_font_rid, last_gl_font_size, TextServer::SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
|
ellipsis_width = 3 * dot_adv.x + font_get_spacing(whitespace_gl_font_rid, last_gl_font_size, SPACING_GLYPH) + (cut_per_word ? whitespace_adv.x : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ell_min_characters = 6;
|
int ell_min_characters = 6;
|
||||||
@ -2891,24 +2958,26 @@ void TextServerFallback::shaped_text_overrun_trim_to_width(RID p_shaped_line, fl
|
|||||||
gl.count = 1;
|
gl.count = 1;
|
||||||
gl.advance = whitespace_adv.x;
|
gl.advance = whitespace_adv.x;
|
||||||
gl.index = whitespace_gl_idx;
|
gl.index = whitespace_gl_idx;
|
||||||
gl.font_rid = last_gl_font_rid;
|
gl.font_rid = whitespace_gl_font_rid;
|
||||||
gl.font_size = last_gl_font_size;
|
gl.font_size = last_gl_font_size;
|
||||||
gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL;
|
gl.flags = GRAPHEME_IS_SPACE | GRAPHEME_IS_BREAK_SOFT | GRAPHEME_IS_VIRTUAL;
|
||||||
|
|
||||||
sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
|
sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
|
||||||
}
|
}
|
||||||
// Add ellipsis dots.
|
// Add ellipsis dots.
|
||||||
|
if (dot_gl_idx != 0) {
|
||||||
Glyph gl;
|
Glyph gl;
|
||||||
gl.count = 1;
|
gl.count = 1;
|
||||||
gl.repeat = 3;
|
gl.repeat = 3;
|
||||||
gl.advance = dot_adv.x;
|
gl.advance = dot_adv.x;
|
||||||
gl.index = dot_gl_idx;
|
gl.index = dot_gl_idx;
|
||||||
gl.font_rid = last_gl_font_rid;
|
gl.font_rid = dot_gl_font_rid;
|
||||||
gl.font_size = last_gl_font_size;
|
gl.font_size = last_gl_font_size;
|
||||||
gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL;
|
gl.flags = GRAPHEME_IS_PUNCTUATION | GRAPHEME_IS_VIRTUAL;
|
||||||
|
|
||||||
sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
|
sd->overrun_trim_data.ellipsis_glyph_buf.append(gl);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sd->text_trimmed = true;
|
sd->text_trimmed = true;
|
||||||
sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0);
|
sd->width_trimmed = width + ((ellipsis_pos != -1) ? ellipsis_width : 0);
|
||||||
@ -3023,13 +3092,13 @@ bool TextServerFallback::shaped_text_shape(RID p_shaped) {
|
|||||||
if (gl.font_rid.is_valid()) {
|
if (gl.font_rid.is_valid()) {
|
||||||
if (sd->text[j - sd->start] != 0 && !is_linebreak(sd->text[j - sd->start])) {
|
if (sd->text[j - sd->start] != 0 && !is_linebreak(sd->text[j - sd->start])) {
|
||||||
if (sd->orientation == ORIENTATION_HORIZONTAL) {
|
if (sd->orientation == ORIENTATION_HORIZONTAL) {
|
||||||
gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x;
|
gl.advance = Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x);
|
||||||
gl.x_off = 0;
|
gl.x_off = 0;
|
||||||
gl.y_off = 0;
|
gl.y_off = 0;
|
||||||
sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size));
|
sd->ascent = MAX(sd->ascent, font_get_ascent(gl.font_rid, gl.font_size));
|
||||||
sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, gl.font_size));
|
sd->descent = MAX(sd->descent, font_get_descent(gl.font_rid, gl.font_size));
|
||||||
} else {
|
} else {
|
||||||
gl.advance = font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y;
|
gl.advance = Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).y);
|
||||||
gl.x_off = -Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5);
|
gl.x_off = -Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5);
|
||||||
gl.y_off = font_get_ascent(gl.font_rid, gl.font_size);
|
gl.y_off = font_get_ascent(gl.font_rid, gl.font_size);
|
||||||
sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
|
sd->ascent = MAX(sd->ascent, Math::round(font_get_glyph_advance(gl.font_rid, gl.font_size, gl.index).x * 0.5));
|
||||||
|
@ -375,6 +375,7 @@ public:
|
|||||||
|
|
||||||
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
|
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
|
||||||
virtual Direction shaped_text_get_direction(RID p_shaped) const override;
|
virtual Direction shaped_text_get_direction(RID p_shaped) const override;
|
||||||
|
virtual Direction shaped_text_get_inferred_direction(RID p_shaped) const override;
|
||||||
|
|
||||||
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
|
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
|
||||||
|
|
||||||
|
@ -183,11 +183,9 @@ void Label::_shape() {
|
|||||||
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
|
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (lines_hidden) {
|
} else if (lines_hidden) {
|
||||||
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
|
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Autowrap disabled.
|
// Autowrap disabled.
|
||||||
for (int i = 0; i < lines_rid.size(); i++) {
|
for (int i = 0; i < lines_rid.size(); i++) {
|
||||||
@ -294,7 +292,7 @@ void Label::_notification(int p_what) {
|
|||||||
Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
|
Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
|
||||||
int outline_size = get_theme_constant(SNAME("outline_size"));
|
int outline_size = get_theme_constant(SNAME("outline_size"));
|
||||||
int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
|
int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
|
||||||
bool rtl = TS->shaped_text_get_direction(text_rid);
|
bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL);
|
||||||
bool rtl_layout = is_layout_rtl();
|
bool rtl_layout = is_layout_rtl();
|
||||||
|
|
||||||
style->draw(ci, Rect2(Point2(0, 0), get_size()));
|
style->draw(ci, Rect2(Point2(0, 0), get_size()));
|
||||||
@ -422,19 +420,19 @@ void Label::_notification(int p_what) {
|
|||||||
|
|
||||||
// Draw main text.
|
// Draw main text.
|
||||||
for (int j = 0; j < gl_size; j++) {
|
for (int j = 0; j < gl_size; j++) {
|
||||||
for (int k = 0; k < glyphs[j].repeat; k++) {
|
|
||||||
// Trim when necessary.
|
// Trim when necessary.
|
||||||
if (trim_pos >= 0) {
|
if (trim_pos >= 0) {
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
if (j < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
|
if (j < trim_pos) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (j >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
|
if (j >= trim_pos) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (int k = 0; k < glyphs[j].repeat; k++) {
|
||||||
bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
|
bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
|
||||||
|
|
||||||
// Draw glyph outlines and shadow.
|
// Draw glyph outlines and shadow.
|
||||||
@ -480,19 +478,19 @@ void Label::_notification(int p_what) {
|
|||||||
|
|
||||||
// Draw main text.
|
// Draw main text.
|
||||||
for (int j = 0; j < gl_size; j++) {
|
for (int j = 0; j < gl_size; j++) {
|
||||||
for (int k = 0; k < glyphs[j].repeat; k++) {
|
|
||||||
// Trim when necessary.
|
// Trim when necessary.
|
||||||
if (trim_pos >= 0) {
|
if (trim_pos >= 0) {
|
||||||
if (rtl) {
|
if (rtl) {
|
||||||
if (j < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
|
if (j < trim_pos) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (j >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
|
if (j >= trim_pos) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (int k = 0; k < glyphs[j].repeat; k++) {
|
||||||
bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
|
bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
|
||||||
|
|
||||||
// Draw glyph outlines and shadow.
|
// Draw glyph outlines and shadow.
|
||||||
|
@ -561,7 +561,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
|
|||||||
if (h_offset > 0) {
|
if (h_offset > 0) {
|
||||||
// Draw dropcap.
|
// Draw dropcap.
|
||||||
Vector2 dc_off = ofs;
|
Vector2 dc_off = ofs;
|
||||||
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
|
if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
|
||||||
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
|
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
|
||||||
dc_off.x += width - h_offset;
|
dc_off.x += width - h_offset;
|
||||||
} else {
|
} else {
|
||||||
@ -579,7 +579,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
|
|||||||
ofs.x = p_pos.x;
|
ofs.x = p_pos.x;
|
||||||
ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
|
ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
|
||||||
if (i <= dropcap_lines) {
|
if (i <= dropcap_lines) {
|
||||||
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
|
if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
|
||||||
ofs.x -= h_offset;
|
ofs.x -= h_offset;
|
||||||
}
|
}
|
||||||
l_width -= h_offset;
|
l_width -= h_offset;
|
||||||
@ -588,7 +588,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
|
|||||||
ofs.y = p_pos.y;
|
ofs.y = p_pos.y;
|
||||||
ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
|
ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
|
||||||
if (i <= dropcap_lines) {
|
if (i <= dropcap_lines) {
|
||||||
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
|
if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
|
||||||
ofs.x -= h_offset;
|
ofs.x -= h_offset;
|
||||||
}
|
}
|
||||||
l_width -= h_offset;
|
l_width -= h_offset;
|
||||||
@ -598,7 +598,7 @@ void TextParagraph::draw(RID p_canvas, const Vector2 &p_pos, const Color &p_colo
|
|||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
switch (alignment) {
|
switch (alignment) {
|
||||||
case HORIZONTAL_ALIGNMENT_FILL:
|
case HORIZONTAL_ALIGNMENT_FILL:
|
||||||
if (TS->shaped_text_get_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
|
if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
|
||||||
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
|
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
|
||||||
ofs.x += l_width - line_width;
|
ofs.x += l_width - line_width;
|
||||||
} else {
|
} else {
|
||||||
@ -655,7 +655,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
|
|||||||
if (h_offset > 0) {
|
if (h_offset > 0) {
|
||||||
// Draw dropcap.
|
// Draw dropcap.
|
||||||
Vector2 dc_off = ofs;
|
Vector2 dc_off = ofs;
|
||||||
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
|
if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
|
||||||
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
|
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
|
||||||
dc_off.x += width - h_offset;
|
dc_off.x += width - h_offset;
|
||||||
} else {
|
} else {
|
||||||
@ -671,7 +671,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
|
|||||||
ofs.x = p_pos.x;
|
ofs.x = p_pos.x;
|
||||||
ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
|
ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
|
||||||
if (i <= dropcap_lines) {
|
if (i <= dropcap_lines) {
|
||||||
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
|
if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
|
||||||
ofs.x -= h_offset;
|
ofs.x -= h_offset;
|
||||||
}
|
}
|
||||||
l_width -= h_offset;
|
l_width -= h_offset;
|
||||||
@ -680,7 +680,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
|
|||||||
ofs.y = p_pos.y;
|
ofs.y = p_pos.y;
|
||||||
ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
|
ofs.x += TS->shaped_text_get_ascent(lines_rid[i]) + spacing_top;
|
||||||
if (i <= dropcap_lines) {
|
if (i <= dropcap_lines) {
|
||||||
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
|
if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_LTR) {
|
||||||
ofs.x -= h_offset;
|
ofs.x -= h_offset;
|
||||||
}
|
}
|
||||||
l_width -= h_offset;
|
l_width -= h_offset;
|
||||||
@ -690,7 +690,7 @@ void TextParagraph::draw_outline(RID p_canvas, const Vector2 &p_pos, int p_outli
|
|||||||
if (width > 0) {
|
if (width > 0) {
|
||||||
switch (alignment) {
|
switch (alignment) {
|
||||||
case HORIZONTAL_ALIGNMENT_FILL:
|
case HORIZONTAL_ALIGNMENT_FILL:
|
||||||
if (TS->shaped_text_get_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
|
if (TS->shaped_text_get_inferred_direction(lines_rid[i]) == TextServer::DIRECTION_RTL) {
|
||||||
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
|
if (TS->shaped_text_get_orientation(lines_rid[i]) == TextServer::ORIENTATION_HORIZONTAL) {
|
||||||
ofs.x += l_width - length;
|
ofs.x += l_width - length;
|
||||||
} else {
|
} else {
|
||||||
@ -772,7 +772,7 @@ void TextParagraph::draw_dropcap(RID p_canvas, const Vector2 &p_pos, const Color
|
|||||||
|
|
||||||
if (h_offset > 0) {
|
if (h_offset > 0) {
|
||||||
// Draw dropcap.
|
// Draw dropcap.
|
||||||
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
|
if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
|
||||||
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
|
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
|
||||||
ofs.x += width - h_offset;
|
ofs.x += width - h_offset;
|
||||||
} else {
|
} else {
|
||||||
@ -794,7 +794,7 @@ void TextParagraph::draw_dropcap_outline(RID p_canvas, const Vector2 &p_pos, int
|
|||||||
|
|
||||||
if (h_offset > 0) {
|
if (h_offset > 0) {
|
||||||
// Draw dropcap.
|
// Draw dropcap.
|
||||||
if (TS->shaped_text_get_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
|
if (TS->shaped_text_get_inferred_direction(dropcap_rid) == TextServer::DIRECTION_RTL) {
|
||||||
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
|
if (TS->shaped_text_get_orientation(dropcap_rid) == TextServer::ORIENTATION_HORIZONTAL) {
|
||||||
ofs.x += width - h_offset;
|
ofs.x += width - h_offset;
|
||||||
} else {
|
} else {
|
||||||
|
@ -194,6 +194,7 @@ void TextServerExtension::_bind_methods() {
|
|||||||
|
|
||||||
GDVIRTUAL_BIND(_shaped_text_set_direction, "shaped", "direction");
|
GDVIRTUAL_BIND(_shaped_text_set_direction, "shaped", "direction");
|
||||||
GDVIRTUAL_BIND(_shaped_text_get_direction, "shaped");
|
GDVIRTUAL_BIND(_shaped_text_get_direction, "shaped");
|
||||||
|
GDVIRTUAL_BIND(_shaped_text_get_inferred_direction, "shaped");
|
||||||
|
|
||||||
GDVIRTUAL_BIND(_shaped_text_set_bidi_override, "shaped", "override");
|
GDVIRTUAL_BIND(_shaped_text_set_bidi_override, "shaped", "override");
|
||||||
|
|
||||||
@ -954,6 +955,14 @@ TextServer::Direction TextServerExtension::shaped_text_get_direction(RID p_shape
|
|||||||
return TextServer::Direction::DIRECTION_AUTO;
|
return TextServer::Direction::DIRECTION_AUTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TextServer::Direction TextServerExtension::shaped_text_get_inferred_direction(RID p_shaped) const {
|
||||||
|
int ret;
|
||||||
|
if (GDVIRTUAL_CALL(_shaped_text_get_inferred_direction, p_shaped, ret)) {
|
||||||
|
return (TextServer::Direction)ret;
|
||||||
|
}
|
||||||
|
return TextServer::Direction::DIRECTION_LTR;
|
||||||
|
}
|
||||||
|
|
||||||
void TextServerExtension::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
|
void TextServerExtension::shaped_text_set_orientation(RID p_shaped, TextServer::Orientation p_orientation) {
|
||||||
GDVIRTUAL_CALL(_shaped_text_set_orientation, p_shaped, p_orientation);
|
GDVIRTUAL_CALL(_shaped_text_set_orientation, p_shaped, p_orientation);
|
||||||
}
|
}
|
||||||
|
@ -315,8 +315,10 @@ public:
|
|||||||
|
|
||||||
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
|
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) override;
|
||||||
virtual Direction shaped_text_get_direction(RID p_shaped) const override;
|
virtual Direction shaped_text_get_direction(RID p_shaped) const override;
|
||||||
|
virtual Direction shaped_text_get_inferred_direction(RID p_shaped) const override;
|
||||||
GDVIRTUAL2(_shaped_text_set_direction, RID, Direction);
|
GDVIRTUAL2(_shaped_text_set_direction, RID, Direction);
|
||||||
GDVIRTUAL1RC(/*Direction*/ int, _shaped_text_get_direction, RID);
|
GDVIRTUAL1RC(/*Direction*/ int, _shaped_text_get_direction, RID);
|
||||||
|
GDVIRTUAL1RC(/*Direction*/ int, _shaped_text_get_inferred_direction, RID);
|
||||||
|
|
||||||
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
|
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) override;
|
||||||
GDVIRTUAL2(_shaped_text_set_bidi_override, RID, const Array &);
|
GDVIRTUAL2(_shaped_text_set_bidi_override, RID, const Array &);
|
||||||
|
@ -347,6 +347,7 @@ void TextServer::_bind_methods() {
|
|||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("shaped_text_set_direction", "shaped", "direction"), &TextServer::shaped_text_set_direction, DEFVAL(DIRECTION_AUTO));
|
ClassDB::bind_method(D_METHOD("shaped_text_set_direction", "shaped", "direction"), &TextServer::shaped_text_set_direction, DEFVAL(DIRECTION_AUTO));
|
||||||
ClassDB::bind_method(D_METHOD("shaped_text_get_direction", "shaped"), &TextServer::shaped_text_get_direction);
|
ClassDB::bind_method(D_METHOD("shaped_text_get_direction", "shaped"), &TextServer::shaped_text_get_direction);
|
||||||
|
ClassDB::bind_method(D_METHOD("shaped_text_get_inferred_direction", "shaped"), &TextServer::shaped_text_get_inferred_direction);
|
||||||
|
|
||||||
ClassDB::bind_method(D_METHOD("shaped_text_set_bidi_override", "shaped", "override"), &TextServer::shaped_text_set_bidi_override);
|
ClassDB::bind_method(D_METHOD("shaped_text_set_bidi_override", "shaped", "override"), &TextServer::shaped_text_set_bidi_override);
|
||||||
|
|
||||||
@ -1224,6 +1225,17 @@ void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_p
|
|||||||
}
|
}
|
||||||
// Draw at the baseline.
|
// Draw at the baseline.
|
||||||
for (int i = 0; i < v_size; i++) {
|
for (int i = 0; i < v_size; i++) {
|
||||||
|
if (trim_pos >= 0) {
|
||||||
|
if (rtl) {
|
||||||
|
if (i < trim_pos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (i >= trim_pos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int j = 0; j < glyphs[i].repeat; j++) {
|
for (int j = 0; j < glyphs[i].repeat; j++) {
|
||||||
if (p_clip_r > 0) {
|
if (p_clip_r > 0) {
|
||||||
// Clip right / bottom.
|
// Clip right / bottom.
|
||||||
@ -1251,17 +1263,6 @@ void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (trim_pos >= 0) {
|
|
||||||
if (rtl) {
|
|
||||||
if (i < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (i >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (glyphs[i].font_rid != RID()) {
|
if (glyphs[i].font_rid != RID()) {
|
||||||
font_draw_glyph(glyphs[i].font_rid, p_canvas, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color);
|
font_draw_glyph(glyphs[i].font_rid, p_canvas, glyphs[i].font_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color);
|
||||||
@ -1293,7 +1294,7 @@ void TextServer::shaped_text_draw(RID p_shaped, RID p_canvas, const Vector2 &p_p
|
|||||||
void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l, float p_clip_r, int p_outline_size, const Color &p_color) const {
|
void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vector2 &p_pos, float p_clip_l, float p_clip_r, int p_outline_size, const Color &p_color) const {
|
||||||
TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
|
TextServer::Orientation orientation = shaped_text_get_orientation(p_shaped);
|
||||||
|
|
||||||
bool rtl = (shaped_text_get_direction(p_shaped) == DIRECTION_RTL);
|
bool rtl = (shaped_text_get_inferred_direction(p_shaped) == DIRECTION_RTL);
|
||||||
|
|
||||||
int ellipsis_pos = shaped_text_get_ellipsis_pos(p_shaped);
|
int ellipsis_pos = shaped_text_get_ellipsis_pos(p_shaped);
|
||||||
int trim_pos = shaped_text_get_trim_pos(p_shaped);
|
int trim_pos = shaped_text_get_trim_pos(p_shaped);
|
||||||
@ -1319,6 +1320,17 @@ void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vect
|
|||||||
}
|
}
|
||||||
// Draw at the baseline.
|
// Draw at the baseline.
|
||||||
for (int i = 0; i < v_size; i++) {
|
for (int i = 0; i < v_size; i++) {
|
||||||
|
if (trim_pos >= 0) {
|
||||||
|
if (rtl) {
|
||||||
|
if (i < trim_pos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (i >= trim_pos) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (int j = 0; j < glyphs[i].repeat; j++) {
|
for (int j = 0; j < glyphs[i].repeat; j++) {
|
||||||
if (p_clip_r > 0) {
|
if (p_clip_r > 0) {
|
||||||
// Clip right / bottom.
|
// Clip right / bottom.
|
||||||
@ -1346,17 +1358,6 @@ void TextServer::shaped_text_draw_outline(RID p_shaped, RID p_canvas, const Vect
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (trim_pos >= 0) {
|
|
||||||
if (rtl) {
|
|
||||||
if (i < trim_pos) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (i >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (glyphs[i].font_rid != RID()) {
|
if (glyphs[i].font_rid != RID()) {
|
||||||
font_draw_glyph_outline(glyphs[i].font_rid, p_canvas, glyphs[i].font_size, p_outline_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color);
|
font_draw_glyph_outline(glyphs[i].font_rid, p_canvas, glyphs[i].font_size, p_outline_size, ofs + Vector2(glyphs[i].x_off, glyphs[i].y_off), glyphs[i].index, p_color);
|
||||||
}
|
}
|
||||||
|
@ -370,6 +370,7 @@ public:
|
|||||||
|
|
||||||
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) = 0;
|
virtual void shaped_text_set_direction(RID p_shaped, Direction p_direction = DIRECTION_AUTO) = 0;
|
||||||
virtual Direction shaped_text_get_direction(RID p_shaped) const = 0;
|
virtual Direction shaped_text_get_direction(RID p_shaped) const = 0;
|
||||||
|
virtual Direction shaped_text_get_inferred_direction(RID p_shaped) const = 0;
|
||||||
|
|
||||||
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) = 0;
|
virtual void shaped_text_set_bidi_override(RID p_shaped, const Array &p_override) = 0;
|
||||||
|
|
||||||
|
@ -154,6 +154,212 @@ TEST_SUITE("[[TextServer]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SUBCASE("[TextServer] Text layout: Line break and align points") {
|
||||||
|
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
|
||||||
|
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
|
||||||
|
TEST_FAIL_COND(ts.is_null(), "Invalid TS interface.");
|
||||||
|
|
||||||
|
RID font1 = ts->create_font();
|
||||||
|
ts->font_set_data_ptr(font1, _font_NotoSans_Regular, _font_NotoSans_Regular_size);
|
||||||
|
RID font2 = ts->create_font();
|
||||||
|
ts->font_set_data_ptr(font2, _font_NotoSansThaiUI_Regular, _font_NotoSansThaiUI_Regular_size);
|
||||||
|
RID font3 = ts->create_font();
|
||||||
|
ts->font_set_data_ptr(font3, _font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size);
|
||||||
|
|
||||||
|
Vector<RID> font;
|
||||||
|
font.push_back(font1);
|
||||||
|
font.push_back(font2);
|
||||||
|
font.push_back(font3);
|
||||||
|
|
||||||
|
{
|
||||||
|
String test = U"Test test long text long text\n";
|
||||||
|
RID ctx = ts->create_shaped_text();
|
||||||
|
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
|
||||||
|
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
|
||||||
|
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
|
||||||
|
ts->shaped_text_update_breaks(ctx);
|
||||||
|
ts->shaped_text_update_justification_ops(ctx);
|
||||||
|
|
||||||
|
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
|
||||||
|
int gl_size = ts->shaped_text_get_glyph_count(ctx);
|
||||||
|
|
||||||
|
TEST_FAIL_COND(gl_size != 30, "Invalid glyph count.");
|
||||||
|
for (int j = 0; j < gl_size; j++) {
|
||||||
|
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
|
||||||
|
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
|
||||||
|
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
|
||||||
|
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
|
||||||
|
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
|
||||||
|
if (j == 4 || j == 9 || j == 14 || j == 19 || j == 24) {
|
||||||
|
TEST_FAIL_COND((!soft || !space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
} else if (j == 29) {
|
||||||
|
TEST_FAIL_COND((soft || !space || !hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
} else {
|
||||||
|
TEST_FAIL_COND((soft || space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ts->free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
String test = U"الحمـد";
|
||||||
|
RID ctx = ts->create_shaped_text();
|
||||||
|
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
|
||||||
|
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
|
||||||
|
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
|
||||||
|
ts->shaped_text_update_breaks(ctx);
|
||||||
|
|
||||||
|
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
|
||||||
|
int gl_size = ts->shaped_text_get_glyph_count(ctx);
|
||||||
|
TEST_FAIL_COND(gl_size != 6, "Invalid glyph count.");
|
||||||
|
for (int j = 0; j < gl_size; j++) {
|
||||||
|
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
|
||||||
|
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
|
||||||
|
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
|
||||||
|
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
|
||||||
|
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
|
||||||
|
TEST_FAIL_COND((soft || space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts->has_feature(TextServer::FEATURE_KASHIDA_JUSTIFICATION)) {
|
||||||
|
ts->shaped_text_update_justification_ops(ctx);
|
||||||
|
|
||||||
|
glyphs = ts->shaped_text_get_glyphs(ctx);
|
||||||
|
gl_size = ts->shaped_text_get_glyph_count(ctx);
|
||||||
|
|
||||||
|
TEST_FAIL_COND(gl_size != 6, "Invalid glyph count.");
|
||||||
|
for (int j = 0; j < gl_size; j++) {
|
||||||
|
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
|
||||||
|
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
|
||||||
|
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
|
||||||
|
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
|
||||||
|
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
|
||||||
|
if (j == 1) {
|
||||||
|
TEST_FAIL_COND((soft || space || hard || virt || !elo), "Invalid glyph flags.");
|
||||||
|
} else {
|
||||||
|
TEST_FAIL_COND((soft || space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ts->free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
String test = U"الحمـد الرياضي العربي";
|
||||||
|
RID ctx = ts->create_shaped_text();
|
||||||
|
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
|
||||||
|
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
|
||||||
|
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
|
||||||
|
ts->shaped_text_update_breaks(ctx);
|
||||||
|
|
||||||
|
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
|
||||||
|
int gl_size = ts->shaped_text_get_glyph_count(ctx);
|
||||||
|
|
||||||
|
TEST_FAIL_COND(gl_size != 21, "Invalid glyph count.");
|
||||||
|
for (int j = 0; j < gl_size; j++) {
|
||||||
|
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
|
||||||
|
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
|
||||||
|
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
|
||||||
|
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
|
||||||
|
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
|
||||||
|
if (j == 6 || j == 14) {
|
||||||
|
TEST_FAIL_COND((!soft || !space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
} else {
|
||||||
|
TEST_FAIL_COND((soft || space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts->has_feature(TextServer::FEATURE_KASHIDA_JUSTIFICATION)) {
|
||||||
|
ts->shaped_text_update_justification_ops(ctx);
|
||||||
|
|
||||||
|
glyphs = ts->shaped_text_get_glyphs(ctx);
|
||||||
|
gl_size = ts->shaped_text_get_glyph_count(ctx);
|
||||||
|
|
||||||
|
TEST_FAIL_COND(gl_size != 23, "Invalid glyph count.");
|
||||||
|
for (int j = 0; j < gl_size; j++) {
|
||||||
|
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
|
||||||
|
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
|
||||||
|
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
|
||||||
|
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
|
||||||
|
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
|
||||||
|
if (j == 7 || j == 16) {
|
||||||
|
TEST_FAIL_COND((!soft || !space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
} else if (j == 3 || j == 9) {
|
||||||
|
TEST_FAIL_COND((soft || space || hard || !virt || !elo), "Invalid glyph flags.");
|
||||||
|
} else if (j == 18) {
|
||||||
|
TEST_FAIL_COND((soft || space || hard || virt || !elo), "Invalid glyph flags.");
|
||||||
|
} else {
|
||||||
|
TEST_FAIL_COND((soft || space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ts->free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
String test = U"เป็น ภาษา ราชการ และ ภาษา";
|
||||||
|
RID ctx = ts->create_shaped_text();
|
||||||
|
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
|
||||||
|
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
|
||||||
|
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
|
||||||
|
ts->shaped_text_update_breaks(ctx);
|
||||||
|
ts->shaped_text_update_justification_ops(ctx);
|
||||||
|
|
||||||
|
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
|
||||||
|
int gl_size = ts->shaped_text_get_glyph_count(ctx);
|
||||||
|
|
||||||
|
TEST_FAIL_COND(gl_size != 25, "Invalid glyph count.");
|
||||||
|
for (int j = 0; j < gl_size; j++) {
|
||||||
|
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
|
||||||
|
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
|
||||||
|
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
|
||||||
|
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
|
||||||
|
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
|
||||||
|
if (j == 4 || j == 9 || j == 16 || j == 20) {
|
||||||
|
TEST_FAIL_COND((!soft || !space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
} else {
|
||||||
|
TEST_FAIL_COND((soft || space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ts->free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts->has_feature(TextServer::FEATURE_BREAK_ITERATORS)) {
|
||||||
|
String test = U"เป็นภาษาราชการและภาษา";
|
||||||
|
RID ctx = ts->create_shaped_text();
|
||||||
|
TEST_FAIL_COND(ctx == RID(), "Creating text buffer failed.");
|
||||||
|
bool ok = ts->shaped_text_add_string(ctx, test, font, 16);
|
||||||
|
TEST_FAIL_COND(!ok, "Adding text to the buffer failed.");
|
||||||
|
ts->shaped_text_update_breaks(ctx);
|
||||||
|
ts->shaped_text_update_justification_ops(ctx);
|
||||||
|
|
||||||
|
const Glyph *glyphs = ts->shaped_text_get_glyphs(ctx);
|
||||||
|
int gl_size = ts->shaped_text_get_glyph_count(ctx);
|
||||||
|
|
||||||
|
TEST_FAIL_COND(gl_size != 25, "Invalid glyph count.");
|
||||||
|
for (int j = 0; j < gl_size; j++) {
|
||||||
|
bool hard = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_HARD) == TextServer::GRAPHEME_IS_BREAK_HARD;
|
||||||
|
bool soft = (glyphs[j].flags & TextServer::GRAPHEME_IS_BREAK_SOFT) == TextServer::GRAPHEME_IS_BREAK_SOFT;
|
||||||
|
bool space = (glyphs[j].flags & TextServer::GRAPHEME_IS_SPACE) == TextServer::GRAPHEME_IS_SPACE;
|
||||||
|
bool virt = (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) == TextServer::GRAPHEME_IS_VIRTUAL;
|
||||||
|
bool elo = (glyphs[j].flags & TextServer::GRAPHEME_IS_ELONGATION) == TextServer::GRAPHEME_IS_ELONGATION;
|
||||||
|
if (j == 4 || j == 9 || j == 16 || j == 20) {
|
||||||
|
TEST_FAIL_COND((!soft || !space || hard || !virt || elo), "Invalid glyph flags.");
|
||||||
|
} else {
|
||||||
|
TEST_FAIL_COND((soft || space || hard || virt || elo), "Invalid glyph flags.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ts->free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < font.size(); j++) {
|
||||||
|
ts->free(font[j]);
|
||||||
|
}
|
||||||
|
font.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SUBCASE("[TextServer] Text layout: Line breaking") {
|
SUBCASE("[TextServer] Text layout: Line breaking") {
|
||||||
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
|
for (int i = 0; i < TextServerManager::get_singleton()->get_interface_count(); i++) {
|
||||||
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
|
Ref<TextServer> ts = TextServerManager::get_singleton()->get_interface(i);
|
||||||
|
Loading…
Reference in New Issue
Block a user