[RTL] Move shadow and foreground/background boxes drawing into a separate draw steps.
This commit is contained in:
parent
a7b860250f
commit
fe8737da49
2 changed files with 400 additions and 547 deletions
|
@ -927,9 +927,12 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
|
||||||
}
|
}
|
||||||
|
|
||||||
RID rid = l.text_buf->get_line_rid(line);
|
RID rid = l.text_buf->get_line_rid(line);
|
||||||
//draw_rect(Rect2(p_ofs + off, TS->shaped_text_get_size(rid)), Color(1,0,0), false, 2); //DEBUG_RECTS
|
double l_ascent = TS->shaped_text_get_ascent(rid);
|
||||||
|
Size2 l_size = TS->shaped_text_get_size(rid);
|
||||||
|
double upos = TS->shaped_text_get_underline_position(rid);
|
||||||
|
double uth = TS->shaped_text_get_underline_thickness(rid);
|
||||||
|
|
||||||
off.y += TS->shaped_text_get_ascent(rid);
|
off.y += l_ascent;
|
||||||
// Draw inlined objects.
|
// Draw inlined objects.
|
||||||
Array objects = TS->shaped_text_get_objects(rid);
|
Array objects = TS->shaped_text_get_objects(rid);
|
||||||
for (int i = 0; i < objects.size(); i++) {
|
for (int i = 0; i < objects.size(); i++) {
|
||||||
|
@ -946,7 +949,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
|
Rect2 rect = TS->shaped_text_get_object_rect(rid, objects[i]);
|
||||||
//draw_rect(rect, Color(1,0,0), false, 2); //DEBUG_RECTS
|
|
||||||
switch (it->type) {
|
switch (it->type) {
|
||||||
case ITEM_IMAGE: {
|
case ITEM_IMAGE: {
|
||||||
ItemImage *img = static_cast<ItemImage *>(it);
|
ItemImage *img = static_cast<ItemImage *>(it);
|
||||||
|
@ -1011,514 +1013,416 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
|
||||||
|
|
||||||
const Glyph *glyphs = TS->shaped_text_get_glyphs(rid);
|
const Glyph *glyphs = TS->shaped_text_get_glyphs(rid);
|
||||||
int gl_size = TS->shaped_text_get_glyph_count(rid);
|
int gl_size = TS->shaped_text_get_glyph_count(rid);
|
||||||
|
|
||||||
Vector2 gloff = off;
|
|
||||||
// Draw outlines and shadow.
|
|
||||||
int processed_glyphs_ol = r_processed_glyphs;
|
|
||||||
for (int i = 0; i < gl_size; i++) {
|
|
||||||
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
|
|
||||||
int size = _find_outline_size(it, p_outline_size);
|
|
||||||
Color font_color = _find_color(it, p_base_color);
|
|
||||||
Color font_outline_color = _find_outline_color(it, p_outline_color);
|
|
||||||
Color font_shadow_color = p_font_shadow_color;
|
|
||||||
if ((size <= 0 || font_outline_color.a == 0) && (font_shadow_color.a == 0)) {
|
|
||||||
gloff.x += glyphs[i].advance;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get FX.
|
|
||||||
ItemFade *fade = nullptr;
|
|
||||||
Item *fade_item = it;
|
|
||||||
while (fade_item) {
|
|
||||||
if (fade_item->type == ITEM_FADE) {
|
|
||||||
fade = static_cast<ItemFade *>(fade_item);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fade_item = fade_item->parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<ItemFX *> fx_stack;
|
|
||||||
_fetch_item_fx_stack(it, fx_stack);
|
|
||||||
bool custom_fx_ok = true;
|
|
||||||
|
|
||||||
Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
|
|
||||||
RID frid = glyphs[i].font_rid;
|
|
||||||
uint32_t gl = glyphs[i].index;
|
|
||||||
uint16_t gl_fl = glyphs[i].flags;
|
|
||||||
uint8_t gl_cn = glyphs[i].count;
|
|
||||||
bool cprev_cluster = false;
|
|
||||||
bool cprev_conn = false;
|
|
||||||
if (gl_cn == 0) { // Parts of the same cluster, always connected.
|
|
||||||
cprev_cluster = true;
|
|
||||||
}
|
|
||||||
if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
|
|
||||||
if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
|
|
||||||
cprev_conn = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
|
|
||||||
cprev_conn = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Apply fx.
|
|
||||||
if (fade) {
|
|
||||||
float faded_visibility = 1.0f;
|
|
||||||
if (glyphs[i].start >= fade->starting_index) {
|
|
||||||
faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
|
|
||||||
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
|
|
||||||
}
|
|
||||||
font_outline_color.a = faded_visibility;
|
|
||||||
font_shadow_color.a = faded_visibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool txt_visible = (font_outline_color.a != 0) || (font_shadow_color.a != 0);
|
|
||||||
Transform2D char_xform;
|
|
||||||
char_xform.set_origin(gloff + p_ofs);
|
|
||||||
|
|
||||||
for (int j = 0; j < fx_stack.size(); j++) {
|
|
||||||
ItemFX *item_fx = fx_stack[j];
|
|
||||||
bool cn = cprev_cluster || (cprev_conn && item_fx->connected);
|
|
||||||
|
|
||||||
if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
|
|
||||||
ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
|
|
||||||
|
|
||||||
Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
|
|
||||||
Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
|
|
||||||
|
|
||||||
if (!custom_effect.is_null()) {
|
|
||||||
charfx->elapsed_time = item_custom->elapsed_time;
|
|
||||||
charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
|
|
||||||
charfx->relative_index = l.char_offset + glyphs[i].start - item_fx->char_ofs;
|
|
||||||
charfx->visibility = txt_visible;
|
|
||||||
charfx->outline = true;
|
|
||||||
charfx->font = frid;
|
|
||||||
charfx->glyph_index = gl;
|
|
||||||
charfx->glyph_flags = gl_fl;
|
|
||||||
charfx->glyph_count = gl_cn;
|
|
||||||
charfx->offset = fx_offset;
|
|
||||||
charfx->color = font_color;
|
|
||||||
charfx->transform = char_xform;
|
|
||||||
|
|
||||||
bool effect_status = custom_effect->_process_effect_impl(charfx);
|
|
||||||
custom_fx_ok = effect_status;
|
|
||||||
|
|
||||||
char_xform = charfx->transform;
|
|
||||||
fx_offset += charfx->offset;
|
|
||||||
font_color = charfx->color;
|
|
||||||
frid = charfx->font;
|
|
||||||
gl = charfx->glyph_index;
|
|
||||||
txt_visible &= charfx->visibility;
|
|
||||||
}
|
|
||||||
} else if (item_fx->type == ITEM_SHAKE) {
|
|
||||||
ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
|
|
||||||
|
|
||||||
if (!cn) {
|
|
||||||
uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
|
|
||||||
uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
|
|
||||||
uint64_t max_rand = 2147483647;
|
|
||||||
double current_offset = Math::remap(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
|
|
||||||
double previous_offset = Math::remap(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
|
|
||||||
double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
|
|
||||||
n_time = (n_time > 1.0) ? 1.0 : n_time;
|
|
||||||
item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
|
|
||||||
}
|
|
||||||
fx_offset += item_shake->prev_off;
|
|
||||||
} else if (item_fx->type == ITEM_WAVE) {
|
|
||||||
ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
|
|
||||||
|
|
||||||
if (!cn) {
|
|
||||||
double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_wave->amplitude / 10.0f);
|
|
||||||
item_wave->prev_off = Point2(0, 1) * value;
|
|
||||||
}
|
|
||||||
fx_offset += item_wave->prev_off;
|
|
||||||
} else if (item_fx->type == ITEM_TORNADO) {
|
|
||||||
ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
|
|
||||||
|
|
||||||
if (!cn) {
|
|
||||||
double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
|
|
||||||
double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
|
|
||||||
item_tornado->prev_off = Point2(torn_x, torn_y);
|
|
||||||
}
|
|
||||||
fx_offset += item_tornado->prev_off;
|
|
||||||
} else if (item_fx->type == ITEM_RAINBOW) {
|
|
||||||
ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
|
|
||||||
|
|
||||||
font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + gloff.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
|
|
||||||
} else if (item_fx->type == ITEM_PULSE) {
|
|
||||||
ItemPulse *item_pulse = static_cast<ItemPulse *>(item_fx);
|
|
||||||
|
|
||||||
const float sined_time = (Math::ease(Math::pingpong(item_pulse->elapsed_time, 1.0 / item_pulse->frequency) * item_pulse->frequency, item_pulse->ease));
|
|
||||||
font_color = font_color.lerp(font_color * item_pulse->color, sined_time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_inside_tree() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
|
|
||||||
fx_offset = fx_offset.round();
|
|
||||||
}
|
|
||||||
Vector2 char_off = char_xform.get_origin();
|
|
||||||
|
|
||||||
// Draw glyph outlines.
|
|
||||||
const Color modulated_outline_color = font_outline_color * Color(1, 1, 1, font_color.a);
|
|
||||||
const Color modulated_shadow_color = font_shadow_color * Color(1, 1, 1, font_color.a);
|
|
||||||
for (int j = 0; j < glyphs[i].repeat; j++) {
|
|
||||||
if (txt_visible) {
|
|
||||||
bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
|
|
||||||
if (!skip && frid != RID()) {
|
|
||||||
if (modulated_shadow_color.a > 0) {
|
|
||||||
Transform2D char_reverse_xform;
|
|
||||||
char_reverse_xform.set_origin(-char_off - p_shadow_ofs);
|
|
||||||
Transform2D char_final_xform = char_xform * char_reverse_xform;
|
|
||||||
char_final_xform.columns[2] += p_shadow_ofs;
|
|
||||||
draw_set_transform_matrix(char_final_xform);
|
|
||||||
|
|
||||||
TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off + p_shadow_ofs, gl, modulated_shadow_color);
|
|
||||||
if (p_shadow_outline_size > 0) {
|
|
||||||
TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, fx_offset + char_off + p_shadow_ofs, gl, modulated_shadow_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (modulated_outline_color.a != 0.0 && size > 0) {
|
|
||||||
Transform2D char_reverse_xform;
|
|
||||||
char_reverse_xform.set_origin(-char_off);
|
|
||||||
Transform2D char_final_xform = char_xform * char_reverse_xform;
|
|
||||||
draw_set_transform_matrix(char_final_xform);
|
|
||||||
|
|
||||||
TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, fx_offset + char_off, gl, modulated_outline_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
processed_glyphs_ol++;
|
|
||||||
}
|
|
||||||
gloff.x += glyphs[i].advance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
draw_set_transform_matrix(Transform2D());
|
|
||||||
|
|
||||||
Vector2 fbg_line_off = off + p_ofs;
|
|
||||||
// Draw background color box
|
|
||||||
Vector2i chr_range = TS->shaped_text_get_range(rid);
|
Vector2i chr_range = TS->shaped_text_get_range(rid);
|
||||||
_draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 0);
|
|
||||||
|
|
||||||
// Draw main text.
|
|
||||||
Color selection_bg = theme_cache.selection_color;
|
|
||||||
|
|
||||||
int sel_start = -1;
|
int sel_start = -1;
|
||||||
int sel_end = -1;
|
int sel_end = -1;
|
||||||
|
|
||||||
if (selection.active && (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) <= (l.char_offset + TS->shaped_text_get_range(rid).y) && (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) >= (l.char_offset + TS->shaped_text_get_range(rid).x)) {
|
if (selection.active && (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) <= (l.char_offset + chr_range.y) && (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) >= (l.char_offset + chr_range.x)) {
|
||||||
sel_start = MAX(TS->shaped_text_get_range(rid).x, (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) - l.char_offset);
|
sel_start = MAX(chr_range.x, (selection.from_frame->lines[selection.from_line].char_offset + selection.from_char) - l.char_offset);
|
||||||
sel_end = MIN(TS->shaped_text_get_range(rid).y, (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) - l.char_offset);
|
sel_end = MIN(chr_range.y, (selection.to_frame->lines[selection.to_line].char_offset + selection.to_char) - l.char_offset);
|
||||||
|
|
||||||
Vector<Vector2> sel = TS->shaped_text_get_selection(rid, sel_start, sel_end);
|
|
||||||
for (int i = 0; i < sel.size(); i++) {
|
|
||||||
Rect2 rect = Rect2(sel[i].x + p_ofs.x + off.x, p_ofs.y + off.y - TS->shaped_text_get_ascent(rid), sel[i].y - sel[i].x, TS->shaped_text_get_size(rid).y);
|
|
||||||
RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_bg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 ul_start;
|
int processed_glyphs_step = 0;
|
||||||
bool ul_started = false;
|
for (int step = DRAW_STEP_BACKGROUND; step < DRAW_STEP_MAX; step++) {
|
||||||
Color ul_color_prev;
|
Vector2 off_step = off;
|
||||||
Color ul_color;
|
processed_glyphs_step = r_processed_glyphs;
|
||||||
|
|
||||||
Vector2 dot_ul_start;
|
Vector2 ul_start;
|
||||||
bool dot_ul_started = false;
|
bool ul_started = false;
|
||||||
Color dot_ul_color_prev;
|
Color ul_color_prev;
|
||||||
Color dot_ul_color;
|
Color ul_color;
|
||||||
|
|
||||||
Vector2 st_start;
|
Vector2 dot_ul_start;
|
||||||
bool st_started = false;
|
bool dot_ul_started = false;
|
||||||
Color st_color_prev;
|
Color dot_ul_color_prev;
|
||||||
Color st_color;
|
Color dot_ul_color;
|
||||||
|
|
||||||
for (int i = 0; i < gl_size; i++) {
|
Vector2 st_start;
|
||||||
bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end);
|
bool st_started = false;
|
||||||
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
|
Color st_color_prev;
|
||||||
bool has_ul = _find_underline(it);
|
Color st_color;
|
||||||
if (!has_ul && underline_meta) {
|
|
||||||
ItemMeta *meta = nullptr;
|
|
||||||
if (_find_meta(it, nullptr, &meta) && meta) {
|
|
||||||
switch (meta->underline) {
|
|
||||||
case META_UNDERLINE_ALWAYS: {
|
|
||||||
has_ul = true;
|
|
||||||
} break;
|
|
||||||
case META_UNDERLINE_NEVER: {
|
|
||||||
has_ul = false;
|
|
||||||
} break;
|
|
||||||
case META_UNDERLINE_ON_HOVER: {
|
|
||||||
has_ul = (meta == meta_hovering);
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Color font_color = _find_color(it, p_base_color);
|
|
||||||
if (has_ul) {
|
|
||||||
if (ul_started && font_color != ul_color_prev) {
|
|
||||||
float y_off = TS->shaped_text_get_underline_position(rid);
|
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
|
||||||
draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width);
|
|
||||||
ul_start = p_ofs + Vector2(off.x, off.y);
|
|
||||||
ul_color_prev = font_color;
|
|
||||||
ul_color = font_color;
|
|
||||||
ul_color.a *= 0.5;
|
|
||||||
} else if (!ul_started) {
|
|
||||||
ul_started = true;
|
|
||||||
ul_start = p_ofs + Vector2(off.x, off.y);
|
|
||||||
ul_color_prev = font_color;
|
|
||||||
ul_color = font_color;
|
|
||||||
ul_color.a *= 0.5;
|
|
||||||
}
|
|
||||||
} else if (ul_started) {
|
|
||||||
ul_started = false;
|
|
||||||
float y_off = TS->shaped_text_get_underline_position(rid);
|
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
|
||||||
draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width);
|
|
||||||
}
|
|
||||||
if (_find_hint(it, nullptr) && underline_hint) {
|
|
||||||
if (dot_ul_started && font_color != dot_ul_color_prev) {
|
|
||||||
float y_off = TS->shaped_text_get_underline_position(rid);
|
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
|
||||||
draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
|
|
||||||
dot_ul_start = p_ofs + Vector2(off.x, off.y);
|
|
||||||
dot_ul_color_prev = font_color;
|
|
||||||
dot_ul_color = font_color;
|
|
||||||
dot_ul_color.a *= 0.5;
|
|
||||||
} else if (!dot_ul_started) {
|
|
||||||
dot_ul_started = true;
|
|
||||||
dot_ul_start = p_ofs + Vector2(off.x, off.y);
|
|
||||||
dot_ul_color_prev = font_color;
|
|
||||||
dot_ul_color = font_color;
|
|
||||||
dot_ul_color.a *= 0.5;
|
|
||||||
}
|
|
||||||
} else if (dot_ul_started) {
|
|
||||||
dot_ul_started = false;
|
|
||||||
float y_off = TS->shaped_text_get_underline_position(rid);
|
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
|
||||||
draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
|
|
||||||
}
|
|
||||||
if (_find_strikethrough(it)) {
|
|
||||||
if (st_started && font_color != st_color_prev) {
|
|
||||||
float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
|
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
|
||||||
draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width);
|
|
||||||
st_start = p_ofs + Vector2(off.x, off.y);
|
|
||||||
st_color_prev = font_color;
|
|
||||||
st_color = font_color;
|
|
||||||
st_color.a *= 0.5;
|
|
||||||
} else if (!st_started) {
|
|
||||||
st_started = true;
|
|
||||||
st_start = p_ofs + Vector2(off.x, off.y);
|
|
||||||
st_color_prev = font_color;
|
|
||||||
st_color = font_color;
|
|
||||||
st_color.a *= 0.5;
|
|
||||||
}
|
|
||||||
} else if (st_started) {
|
|
||||||
st_started = false;
|
|
||||||
float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
|
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
|
||||||
draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get FX.
|
float box_start = 0.0;
|
||||||
ItemFade *fade = nullptr;
|
Color last_color = Color(0, 0, 0, 0);
|
||||||
Item *fade_item = it;
|
|
||||||
while (fade_item) {
|
|
||||||
if (fade_item->type == ITEM_FADE) {
|
|
||||||
fade = static_cast<ItemFade *>(fade_item);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
fade_item = fade_item->parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector<ItemFX *> fx_stack;
|
for (int i = 0; i < gl_size; i++) {
|
||||||
_fetch_item_fx_stack(it, fx_stack);
|
bool selected = selection.active && (sel_start != -1) && (glyphs[i].start >= sel_start) && (glyphs[i].end <= sel_end);
|
||||||
bool custom_fx_ok = true;
|
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
|
||||||
|
|
||||||
Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
|
Color font_color = (step == DRAW_STEP_SHADOW || step == DRAW_STEP_OUTLINE || step == DRAW_STEP_TEXT) ? _find_color(it, p_base_color) : Color();
|
||||||
RID frid = glyphs[i].font_rid;
|
int outline_size = (step == DRAW_STEP_OUTLINE) ? _find_outline_size(it, p_outline_size) : 0;
|
||||||
uint32_t gl = glyphs[i].index;
|
Color font_outline_color = (step == DRAW_STEP_OUTLINE) ? _find_outline_color(it, p_outline_color) : Color();
|
||||||
uint16_t gl_fl = glyphs[i].flags;
|
Color font_shadow_color = p_font_shadow_color;
|
||||||
uint8_t gl_cn = glyphs[i].count;
|
bool txt_visible = false;
|
||||||
bool cprev_cluster = false;
|
if (step == DRAW_STEP_OUTLINE) {
|
||||||
bool cprev_conn = false;
|
txt_visible = (font_outline_color.a != 0 && outline_size > 0);
|
||||||
if (gl_cn == 0) { // Parts of the same grapheme cluster, always connected.
|
} else if (step == DRAW_STEP_SHADOW) {
|
||||||
cprev_cluster = true;
|
txt_visible = (font_shadow_color.a != 0);
|
||||||
}
|
} else if (step == DRAW_STEP_TEXT) {
|
||||||
if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
|
txt_visible = (font_color.a != 0);
|
||||||
if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
|
bool has_ul = _find_underline(it);
|
||||||
cprev_conn = true;
|
if (!has_ul && underline_meta) {
|
||||||
}
|
ItemMeta *meta = nullptr;
|
||||||
} else {
|
if (_find_meta(it, nullptr, &meta) && meta) {
|
||||||
if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
|
switch (meta->underline) {
|
||||||
cprev_conn = true;
|
case META_UNDERLINE_ALWAYS: {
|
||||||
}
|
has_ul = true;
|
||||||
}
|
} break;
|
||||||
|
case META_UNDERLINE_NEVER: {
|
||||||
//Apply fx.
|
has_ul = false;
|
||||||
if (fade) {
|
} break;
|
||||||
float faded_visibility = 1.0f;
|
case META_UNDERLINE_ON_HOVER: {
|
||||||
if (glyphs[i].start >= fade->starting_index) {
|
has_ul = (meta == meta_hovering);
|
||||||
faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
|
} break;
|
||||||
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
|
}
|
||||||
}
|
|
||||||
font_color.a = faded_visibility;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool txt_visible = (font_color.a != 0);
|
|
||||||
|
|
||||||
Transform2D char_xform;
|
|
||||||
char_xform.set_origin(p_ofs + off);
|
|
||||||
|
|
||||||
for (int j = 0; j < fx_stack.size(); j++) {
|
|
||||||
ItemFX *item_fx = fx_stack[j];
|
|
||||||
bool cn = cprev_cluster || (cprev_conn && item_fx->connected);
|
|
||||||
|
|
||||||
if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
|
|
||||||
ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
|
|
||||||
|
|
||||||
Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
|
|
||||||
Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
|
|
||||||
|
|
||||||
if (!custom_effect.is_null()) {
|
|
||||||
charfx->elapsed_time = item_custom->elapsed_time;
|
|
||||||
charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
|
|
||||||
charfx->relative_index = l.char_offset + glyphs[i].start - item_fx->char_ofs;
|
|
||||||
charfx->visibility = txt_visible;
|
|
||||||
charfx->outline = false;
|
|
||||||
charfx->font = frid;
|
|
||||||
charfx->glyph_index = gl;
|
|
||||||
charfx->glyph_flags = gl_fl;
|
|
||||||
charfx->glyph_count = gl_cn;
|
|
||||||
charfx->offset = fx_offset;
|
|
||||||
charfx->color = font_color;
|
|
||||||
charfx->transform = char_xform;
|
|
||||||
|
|
||||||
bool effect_status = custom_effect->_process_effect_impl(charfx);
|
|
||||||
custom_fx_ok = effect_status;
|
|
||||||
|
|
||||||
char_xform = charfx->transform;
|
|
||||||
fx_offset += charfx->offset;
|
|
||||||
font_color = charfx->color;
|
|
||||||
frid = charfx->font;
|
|
||||||
gl = charfx->glyph_index;
|
|
||||||
txt_visible &= charfx->visibility;
|
|
||||||
}
|
|
||||||
} else if (item_fx->type == ITEM_SHAKE) {
|
|
||||||
ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
|
|
||||||
|
|
||||||
if (!cn) {
|
|
||||||
uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
|
|
||||||
uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
|
|
||||||
uint64_t max_rand = 2147483647;
|
|
||||||
double current_offset = Math::remap(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
|
|
||||||
double previous_offset = Math::remap(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
|
|
||||||
double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
|
|
||||||
n_time = (n_time > 1.0) ? 1.0 : n_time;
|
|
||||||
item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
|
|
||||||
}
|
|
||||||
fx_offset += item_shake->prev_off;
|
|
||||||
} else if (item_fx->type == ITEM_WAVE) {
|
|
||||||
ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
|
|
||||||
|
|
||||||
if (!cn) {
|
|
||||||
double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f);
|
|
||||||
item_wave->prev_off = Point2(0, 1) * value;
|
|
||||||
}
|
|
||||||
fx_offset += item_wave->prev_off;
|
|
||||||
} else if (item_fx->type == ITEM_TORNADO) {
|
|
||||||
ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
|
|
||||||
|
|
||||||
if (!cn) {
|
|
||||||
double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
|
|
||||||
double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
|
|
||||||
item_tornado->prev_off = Point2(torn_x, torn_y);
|
|
||||||
}
|
|
||||||
fx_offset += item_tornado->prev_off;
|
|
||||||
} else if (item_fx->type == ITEM_RAINBOW) {
|
|
||||||
ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
|
|
||||||
|
|
||||||
font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + off.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
|
|
||||||
} else if (item_fx->type == ITEM_PULSE) {
|
|
||||||
ItemPulse *item_pulse = static_cast<ItemPulse *>(item_fx);
|
|
||||||
|
|
||||||
const float sined_time = (Math::ease(Math::pingpong(item_pulse->elapsed_time, 1.0 / item_pulse->frequency) * item_pulse->frequency, item_pulse->ease));
|
|
||||||
font_color = font_color.lerp(font_color * item_pulse->color, sined_time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_inside_tree() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
|
|
||||||
fx_offset = fx_offset.round();
|
|
||||||
}
|
|
||||||
Vector2 char_off = char_xform.get_origin();
|
|
||||||
|
|
||||||
Transform2D char_reverse_xform;
|
|
||||||
char_reverse_xform.set_origin(-char_off);
|
|
||||||
char_xform = char_xform * char_reverse_xform;
|
|
||||||
draw_set_transform_matrix(char_xform);
|
|
||||||
|
|
||||||
if (selected && use_selected_font_color) {
|
|
||||||
font_color = theme_cache.font_selected_color;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw glyphs.
|
|
||||||
for (int j = 0; j < glyphs[i].repeat; j++) {
|
|
||||||
bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (r_processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (r_processed_glyphs < total_glyphs - visible_glyphs));
|
|
||||||
if (txt_visible) {
|
|
||||||
if (!skip) {
|
|
||||||
if (frid != RID()) {
|
|
||||||
TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color);
|
|
||||||
} else if (((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) {
|
|
||||||
TS->draw_hex_code_box(ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
r_processed_glyphs++;
|
if (has_ul) {
|
||||||
}
|
if (ul_started && font_color != ul_color_prev) {
|
||||||
if (skip) {
|
float y_off = upos;
|
||||||
// End underline/overline/strikethrough is previous glyph is skipped.
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
if (ul_started) {
|
draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width);
|
||||||
|
ul_start = p_ofs + Vector2(off_step.x, off_step.y);
|
||||||
|
ul_color_prev = font_color;
|
||||||
|
ul_color = font_color;
|
||||||
|
ul_color.a *= 0.5;
|
||||||
|
} else if (!ul_started) {
|
||||||
|
ul_started = true;
|
||||||
|
ul_start = p_ofs + Vector2(off_step.x, off_step.y);
|
||||||
|
ul_color_prev = font_color;
|
||||||
|
ul_color = font_color;
|
||||||
|
ul_color.a *= 0.5;
|
||||||
|
}
|
||||||
|
} else if (ul_started) {
|
||||||
ul_started = false;
|
ul_started = false;
|
||||||
float y_off = TS->shaped_text_get_underline_position(rid);
|
float y_off = upos;
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width);
|
draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width);
|
||||||
}
|
}
|
||||||
if (dot_ul_started) {
|
if (_find_hint(it, nullptr) && underline_hint) {
|
||||||
|
if (dot_ul_started && font_color != dot_ul_color_prev) {
|
||||||
|
float y_off = upos;
|
||||||
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
|
draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
|
||||||
|
dot_ul_start = p_ofs + Vector2(off_step.x, off_step.y);
|
||||||
|
dot_ul_color_prev = font_color;
|
||||||
|
dot_ul_color = font_color;
|
||||||
|
dot_ul_color.a *= 0.5;
|
||||||
|
} else if (!dot_ul_started) {
|
||||||
|
dot_ul_started = true;
|
||||||
|
dot_ul_start = p_ofs + Vector2(off_step.x, off_step.y);
|
||||||
|
dot_ul_color_prev = font_color;
|
||||||
|
dot_ul_color = font_color;
|
||||||
|
dot_ul_color.a *= 0.5;
|
||||||
|
}
|
||||||
|
} else if (dot_ul_started) {
|
||||||
dot_ul_started = false;
|
dot_ul_started = false;
|
||||||
float y_off = TS->shaped_text_get_underline_position(rid);
|
float y_off = upos;
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
|
draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
|
||||||
}
|
}
|
||||||
if (st_started) {
|
if (_find_strikethrough(it)) {
|
||||||
|
if (st_started && font_color != st_color_prev) {
|
||||||
|
float y_off = -l_ascent + l_size.y / 2;
|
||||||
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
|
draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width);
|
||||||
|
st_start = p_ofs + Vector2(off_step.x, off_step.y);
|
||||||
|
st_color_prev = font_color;
|
||||||
|
st_color = font_color;
|
||||||
|
st_color.a *= 0.5;
|
||||||
|
} else if (!st_started) {
|
||||||
|
st_started = true;
|
||||||
|
st_start = p_ofs + Vector2(off_step.x, off_step.y);
|
||||||
|
st_color_prev = font_color;
|
||||||
|
st_color = font_color;
|
||||||
|
st_color.a *= 0.5;
|
||||||
|
}
|
||||||
|
} else if (st_started) {
|
||||||
st_started = false;
|
st_started = false;
|
||||||
float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
|
float y_off = -l_ascent + l_size.y / 2;
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width);
|
draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (step == DRAW_STEP_SHADOW || step == DRAW_STEP_OUTLINE || step == DRAW_STEP_TEXT) {
|
||||||
|
ItemFade *fade = nullptr;
|
||||||
|
Item *fade_item = it;
|
||||||
|
while (fade_item) {
|
||||||
|
if (fade_item->type == ITEM_FADE) {
|
||||||
|
fade = static_cast<ItemFade *>(fade_item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fade_item = fade_item->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<ItemFX *> fx_stack;
|
||||||
|
_fetch_item_fx_stack(it, fx_stack);
|
||||||
|
bool custom_fx_ok = true;
|
||||||
|
|
||||||
|
Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
|
||||||
|
RID frid = glyphs[i].font_rid;
|
||||||
|
uint32_t gl = glyphs[i].index;
|
||||||
|
uint16_t gl_fl = glyphs[i].flags;
|
||||||
|
uint8_t gl_cn = glyphs[i].count;
|
||||||
|
bool cprev_cluster = false;
|
||||||
|
bool cprev_conn = false;
|
||||||
|
if (gl_cn == 0) { // Parts of the same grapheme cluster, always connected.
|
||||||
|
cprev_cluster = true;
|
||||||
|
}
|
||||||
|
if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
|
||||||
|
if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
|
||||||
|
cprev_conn = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
|
||||||
|
cprev_conn = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Apply fx.
|
||||||
|
if (fade) {
|
||||||
|
float faded_visibility = 1.0f;
|
||||||
|
if (glyphs[i].start >= fade->starting_index) {
|
||||||
|
faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
|
||||||
|
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
|
||||||
|
}
|
||||||
|
font_color.a = faded_visibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform2D char_xform;
|
||||||
|
char_xform.set_origin(p_ofs + off_step);
|
||||||
|
|
||||||
|
for (int j = 0; j < fx_stack.size(); j++) {
|
||||||
|
ItemFX *item_fx = fx_stack[j];
|
||||||
|
bool cn = cprev_cluster || (cprev_conn && item_fx->connected);
|
||||||
|
|
||||||
|
if (item_fx->type == ITEM_CUSTOMFX && custom_fx_ok) {
|
||||||
|
ItemCustomFX *item_custom = static_cast<ItemCustomFX *>(item_fx);
|
||||||
|
|
||||||
|
Ref<CharFXTransform> charfx = item_custom->char_fx_transform;
|
||||||
|
Ref<RichTextEffect> custom_effect = item_custom->custom_effect;
|
||||||
|
|
||||||
|
if (!custom_effect.is_null()) {
|
||||||
|
charfx->elapsed_time = item_custom->elapsed_time;
|
||||||
|
charfx->range = Vector2i(l.char_offset + glyphs[i].start, l.char_offset + glyphs[i].end);
|
||||||
|
charfx->relative_index = l.char_offset + glyphs[i].start - item_fx->char_ofs;
|
||||||
|
charfx->visibility = txt_visible;
|
||||||
|
charfx->outline = (step == DRAW_STEP_SHADOW) || (step == DRAW_STEP_OUTLINE);
|
||||||
|
charfx->font = frid;
|
||||||
|
charfx->glyph_index = gl;
|
||||||
|
charfx->glyph_flags = gl_fl;
|
||||||
|
charfx->glyph_count = gl_cn;
|
||||||
|
charfx->offset = fx_offset;
|
||||||
|
charfx->color = font_color;
|
||||||
|
charfx->transform = char_xform;
|
||||||
|
|
||||||
|
bool effect_status = custom_effect->_process_effect_impl(charfx);
|
||||||
|
custom_fx_ok = effect_status;
|
||||||
|
|
||||||
|
char_xform = charfx->transform;
|
||||||
|
fx_offset += charfx->offset;
|
||||||
|
font_color = charfx->color;
|
||||||
|
frid = charfx->font;
|
||||||
|
gl = charfx->glyph_index;
|
||||||
|
txt_visible &= charfx->visibility;
|
||||||
|
}
|
||||||
|
} else if (item_fx->type == ITEM_SHAKE) {
|
||||||
|
ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
|
||||||
|
|
||||||
|
if (!cn) {
|
||||||
|
uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
|
||||||
|
uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
|
||||||
|
uint64_t max_rand = 2147483647;
|
||||||
|
double current_offset = Math::remap(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
|
||||||
|
double previous_offset = Math::remap(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
|
||||||
|
double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate));
|
||||||
|
n_time = (n_time > 1.0) ? 1.0 : n_time;
|
||||||
|
item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
|
||||||
|
}
|
||||||
|
fx_offset += item_shake->prev_off;
|
||||||
|
} else if (item_fx->type == ITEM_WAVE) {
|
||||||
|
ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
|
||||||
|
|
||||||
|
if (!cn) {
|
||||||
|
double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off_step.x) / 50)) * (item_wave->amplitude / 10.0f);
|
||||||
|
item_wave->prev_off = Point2(0, 1) * value;
|
||||||
|
}
|
||||||
|
fx_offset += item_wave->prev_off;
|
||||||
|
} else if (item_fx->type == ITEM_TORNADO) {
|
||||||
|
ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
|
||||||
|
|
||||||
|
if (!cn) {
|
||||||
|
double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off_step.x) / 50)) * (item_tornado->radius);
|
||||||
|
double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off_step.x) / 50)) * (item_tornado->radius);
|
||||||
|
item_tornado->prev_off = Point2(torn_x, torn_y);
|
||||||
|
}
|
||||||
|
fx_offset += item_tornado->prev_off;
|
||||||
|
} else if (item_fx->type == ITEM_RAINBOW) {
|
||||||
|
ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
|
||||||
|
|
||||||
|
font_color = font_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + off_step.x) / 50)), item_rainbow->saturation, item_rainbow->value, font_color.a);
|
||||||
|
} else if (item_fx->type == ITEM_PULSE) {
|
||||||
|
ItemPulse *item_pulse = static_cast<ItemPulse *>(item_fx);
|
||||||
|
|
||||||
|
const float sined_time = (Math::ease(Math::pingpong(item_pulse->elapsed_time, 1.0 / item_pulse->frequency) * item_pulse->frequency, item_pulse->ease));
|
||||||
|
font_color = font_color.lerp(font_color * item_pulse->color, sined_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_inside_tree() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) {
|
||||||
|
fx_offset = fx_offset.round();
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2 char_off = char_xform.get_origin();
|
||||||
|
Transform2D char_reverse_xform;
|
||||||
|
if (step == DRAW_STEP_TEXT) {
|
||||||
|
if (selected && use_selected_font_color) {
|
||||||
|
font_color = theme_cache.font_selected_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
char_reverse_xform.set_origin(-char_off);
|
||||||
|
Transform2D char_final_xform = char_xform * char_reverse_xform;
|
||||||
|
draw_set_transform_matrix(char_final_xform);
|
||||||
|
} else if (step == DRAW_STEP_SHADOW) {
|
||||||
|
font_color = font_shadow_color * Color(1, 1, 1, font_color.a);
|
||||||
|
|
||||||
|
char_reverse_xform.set_origin(-char_off - p_shadow_ofs);
|
||||||
|
Transform2D char_final_xform = char_xform * char_reverse_xform;
|
||||||
|
char_final_xform.columns[2] += p_shadow_ofs;
|
||||||
|
draw_set_transform_matrix(char_final_xform);
|
||||||
|
} else if (step == DRAW_STEP_OUTLINE) {
|
||||||
|
font_color = font_outline_color * Color(1, 1, 1, font_color.a);
|
||||||
|
|
||||||
|
char_reverse_xform.set_origin(-char_off);
|
||||||
|
Transform2D char_final_xform = char_xform * char_reverse_xform;
|
||||||
|
draw_set_transform_matrix(char_final_xform);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw glyphs.
|
||||||
|
for (int j = 0; j < glyphs[i].repeat; j++) {
|
||||||
|
bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (processed_glyphs_step >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_step < total_glyphs - visible_glyphs));
|
||||||
|
if (!skip) {
|
||||||
|
if (txt_visible) {
|
||||||
|
if (step == DRAW_STEP_TEXT) {
|
||||||
|
if (frid != RID()) {
|
||||||
|
TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color);
|
||||||
|
} else if (((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) && ((glyphs[i].flags & TextServer::GRAPHEME_IS_EMBEDDED_OBJECT) != TextServer::GRAPHEME_IS_EMBEDDED_OBJECT)) {
|
||||||
|
TS->draw_hex_code_box(ci, glyphs[i].font_size, fx_offset + char_off, gl, font_color);
|
||||||
|
}
|
||||||
|
} else if (step == DRAW_STEP_SHADOW && frid != RID()) {
|
||||||
|
TS->font_draw_glyph(frid, ci, glyphs[i].font_size, fx_offset + char_off + p_shadow_ofs, gl, font_color);
|
||||||
|
if (p_shadow_outline_size > 0) {
|
||||||
|
TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, fx_offset + char_off + p_shadow_ofs, gl, font_color);
|
||||||
|
}
|
||||||
|
} else if (step == DRAW_STEP_OUTLINE && frid != RID() && outline_size > 0) {
|
||||||
|
TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, outline_size, fx_offset + char_off, gl, font_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processed_glyphs_step++;
|
||||||
|
}
|
||||||
|
if (step == DRAW_STEP_TEXT && skip) {
|
||||||
|
// Finish underline/overline/strikethrough is previous glyph is skipped.
|
||||||
|
if (ul_started) {
|
||||||
|
ul_started = false;
|
||||||
|
float y_off = upos;
|
||||||
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
|
draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width);
|
||||||
|
}
|
||||||
|
if (dot_ul_started) {
|
||||||
|
dot_ul_started = false;
|
||||||
|
float y_off = upos;
|
||||||
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
|
draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
|
||||||
|
}
|
||||||
|
if (st_started) {
|
||||||
|
st_started = false;
|
||||||
|
float y_off = -l_ascent + l_size.y / 2;
|
||||||
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
|
draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
off_step.x += glyphs[i].advance;
|
||||||
|
}
|
||||||
|
draw_set_transform_matrix(Transform2D());
|
||||||
|
}
|
||||||
|
// Draw boxes.
|
||||||
|
if (step == DRAW_STEP_BACKGROUND || step == DRAW_STEP_FOREGROUND) {
|
||||||
|
for (int j = 0; j < glyphs[i].repeat; j++) {
|
||||||
|
bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (processed_glyphs_step >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_step < total_glyphs - visible_glyphs));
|
||||||
|
if (!skip) {
|
||||||
|
Color color;
|
||||||
|
if (step == DRAW_STEP_BACKGROUND) {
|
||||||
|
color = _find_bgcolor(it);
|
||||||
|
} else if (step == DRAW_STEP_FOREGROUND) {
|
||||||
|
color = _find_fgcolor(it);
|
||||||
|
}
|
||||||
|
if (color != last_color) {
|
||||||
|
if (last_color.a > 0.0) {
|
||||||
|
Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding);
|
||||||
|
Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding);
|
||||||
|
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color);
|
||||||
|
}
|
||||||
|
if (color.a > 0.0) {
|
||||||
|
box_start = off_step.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_color = color;
|
||||||
|
processed_glyphs_step++;
|
||||||
|
} else {
|
||||||
|
// Finish box is previous glyph is skipped.
|
||||||
|
if (last_color.a > 0.0) {
|
||||||
|
Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding);
|
||||||
|
Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding);
|
||||||
|
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color);
|
||||||
|
}
|
||||||
|
last_color = Color(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
off_step.x += glyphs[i].advance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
off.x += glyphs[i].advance;
|
|
||||||
}
|
}
|
||||||
|
// Finish lines and boxes.
|
||||||
|
if (step == DRAW_STEP_BACKGROUND) {
|
||||||
|
if (sel_start != -1) {
|
||||||
|
Color selection_bg = theme_cache.selection_color;
|
||||||
|
Vector<Vector2> sel = TS->shaped_text_get_selection(rid, sel_start, sel_end);
|
||||||
|
for (int i = 0; i < sel.size(); i++) {
|
||||||
|
Rect2 rect = Rect2(sel[i].x + p_ofs.x + off.x, p_ofs.y + off.y - l_ascent, sel[i].y - sel[i].x, l_size.y); // Note: use "off" not "off_step", selection is relative to the line start.
|
||||||
|
RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, selection_bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (step == DRAW_STEP_BACKGROUND || step == DRAW_STEP_FOREGROUND) {
|
||||||
|
if (last_color.a > 0.0) {
|
||||||
|
Vector2 rect_off = p_ofs + Vector2(box_start - theme_cache.text_highlight_h_padding, off_step.y - l_ascent - theme_cache.text_highlight_v_padding);
|
||||||
|
Vector2 rect_size = Vector2(off_step.x - box_start + 2 * theme_cache.text_highlight_h_padding, l_size.y + 2 * theme_cache.text_highlight_v_padding);
|
||||||
|
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(rect_off, rect_size), last_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (step == DRAW_STEP_TEXT) {
|
||||||
|
if (ul_started) {
|
||||||
|
ul_started = false;
|
||||||
|
float y_off = upos;
|
||||||
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
|
draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), ul_color, underline_width);
|
||||||
|
}
|
||||||
|
if (dot_ul_started) {
|
||||||
|
dot_ul_started = false;
|
||||||
|
float y_off = upos;
|
||||||
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
|
draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
|
||||||
|
}
|
||||||
|
if (st_started) {
|
||||||
|
st_started = false;
|
||||||
|
float y_off = -l_ascent + l_size.y / 2;
|
||||||
|
float underline_width = MAX(1.0, uth * theme_cache.base_scale);
|
||||||
|
draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off_step.x, off_step.y + y_off), st_color, underline_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
draw_set_transform_matrix(Transform2D());
|
r_processed_glyphs = processed_glyphs_step;
|
||||||
}
|
|
||||||
if (ul_started) {
|
|
||||||
ul_started = false;
|
|
||||||
float y_off = TS->shaped_text_get_underline_position(rid);
|
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
|
||||||
draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width);
|
|
||||||
}
|
|
||||||
if (dot_ul_started) {
|
|
||||||
dot_ul_started = false;
|
|
||||||
float y_off = TS->shaped_text_get_underline_position(rid);
|
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
|
||||||
draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
|
|
||||||
}
|
|
||||||
if (st_started) {
|
|
||||||
st_started = false;
|
|
||||||
float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
|
|
||||||
float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale);
|
|
||||||
draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width);
|
|
||||||
}
|
|
||||||
// Draw foreground color box
|
|
||||||
_draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 1);
|
|
||||||
|
|
||||||
off.y += TS->shaped_text_get_descent(rid);
|
off.y += TS->shaped_text_get_descent(rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6374,65 +6278,6 @@ void RichTextLabel::menu_option(int p_option) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag) {
|
|
||||||
Vector2i fbg_index = Vector2i(end, start);
|
|
||||||
Color last_color = Color(0, 0, 0, 0);
|
|
||||||
bool draw_box = false;
|
|
||||||
// Draw a box based on color tags associated with glyphs
|
|
||||||
for (int i = start; i < end; i++) {
|
|
||||||
Item *it = _get_item_at_pos(it_from, it_to, i);
|
|
||||||
Color color;
|
|
||||||
|
|
||||||
if (fbg_flag == 0) {
|
|
||||||
color = _find_bgcolor(it);
|
|
||||||
} else {
|
|
||||||
color = _find_fgcolor(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool change_to_color = ((color.a > 0) && ((last_color.a - 0.0) < 0.01));
|
|
||||||
bool change_from_color = (((color.a - 0.0) < 0.01) && (last_color.a > 0.0));
|
|
||||||
bool change_color = (((color.a > 0) == (last_color.a > 0)) && (color != last_color));
|
|
||||||
|
|
||||||
if (change_to_color) {
|
|
||||||
fbg_index.x = MIN(i, fbg_index.x);
|
|
||||||
fbg_index.y = MAX(i, fbg_index.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (change_from_color || change_color) {
|
|
||||||
fbg_index.x = MIN(i, fbg_index.x);
|
|
||||||
fbg_index.y = MAX(i, fbg_index.y);
|
|
||||||
draw_box = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (draw_box) {
|
|
||||||
Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, fbg_index.y);
|
|
||||||
for (int j = 0; j < sel.size(); j++) {
|
|
||||||
Vector2 rect_off = line_off + Vector2(sel[j].x - theme_cache.text_highlight_h_padding, -TS->shaped_text_get_ascent(p_rid) - theme_cache.text_highlight_v_padding);
|
|
||||||
Vector2 rect_size = Vector2(sel[j].y - sel[j].x + 2 * theme_cache.text_highlight_h_padding, TS->shaped_text_get_size(p_rid).y + 2 * theme_cache.text_highlight_v_padding);
|
|
||||||
RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color);
|
|
||||||
}
|
|
||||||
fbg_index = Vector2i(end, start);
|
|
||||||
draw_box = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (change_color) {
|
|
||||||
fbg_index.x = MIN(i, fbg_index.x);
|
|
||||||
fbg_index.y = MAX(i, fbg_index.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
last_color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (last_color.a > 0) {
|
|
||||||
Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, end);
|
|
||||||
for (int i = 0; i < sel.size(); i++) {
|
|
||||||
Vector2 rect_off = line_off + Vector2(sel[i].x - theme_cache.text_highlight_h_padding, -TS->shaped_text_get_ascent(p_rid) - theme_cache.text_highlight_v_padding);
|
|
||||||
Vector2 rect_size = Vector2(sel[i].y - sel[i].x + 2 * theme_cache.text_highlight_h_padding, TS->shaped_text_get_size(p_rid).y + 2 * theme_cache.text_highlight_v_padding);
|
|
||||||
RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) {
|
Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) {
|
||||||
for (int i = 0; i < custom_effects.size(); i++) {
|
for (int i = 0; i < custom_effects.size(); i++) {
|
||||||
Ref<RichTextEffect> effect = custom_effects[i];
|
Ref<RichTextEffect> effect = custom_effects[i];
|
||||||
|
|
|
@ -43,6 +43,15 @@ class RichTextEffect;
|
||||||
class RichTextLabel : public Control {
|
class RichTextLabel : public Control {
|
||||||
GDCLASS(RichTextLabel, Control);
|
GDCLASS(RichTextLabel, Control);
|
||||||
|
|
||||||
|
enum RTLDrawStep {
|
||||||
|
DRAW_STEP_BACKGROUND,
|
||||||
|
DRAW_STEP_SHADOW,
|
||||||
|
DRAW_STEP_OUTLINE,
|
||||||
|
DRAW_STEP_TEXT,
|
||||||
|
DRAW_STEP_FOREGROUND,
|
||||||
|
DRAW_STEP_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum ListType {
|
enum ListType {
|
||||||
LIST_NUMBERS,
|
LIST_NUMBERS,
|
||||||
|
@ -596,7 +605,6 @@ private:
|
||||||
|
|
||||||
Size2 _get_image_size(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Rect2 &p_region = Rect2());
|
Size2 _get_image_size(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Rect2 &p_region = Rect2());
|
||||||
|
|
||||||
void _draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item *it_from, Item *it_to, int start, int end, int fbg_flag);
|
|
||||||
#ifndef DISABLE_DEPRECATED
|
#ifndef DISABLE_DEPRECATED
|
||||||
// Kept for compatibility from 3.x to 4.0.
|
// Kept for compatibility from 3.x to 4.0.
|
||||||
bool _set(const StringName &p_name, const Variant &p_value);
|
bool _set(const StringName &p_name, const Variant &p_value);
|
||||||
|
|
Loading…
Reference in a new issue