|
@@ -30,10 +30,11 @@
|
|
|
|
|
|
#include "rich_text_label.h"
|
|
|
|
|
|
+#include "core/math/math_defs.h"
|
|
|
#include "core/os/keyboard.h"
|
|
|
#include "core/os/os.h"
|
|
|
+#include "modules/regex/regex.h"
|
|
|
#include "scene/scene_string_names.h"
|
|
|
-
|
|
|
#ifdef TOOLS_ENABLED
|
|
|
#include "editor/editor_scale.h"
|
|
|
#endif
|
|
@@ -139,6 +140,7 @@ Rect2 RichTextLabel::_get_text_rect() {
|
|
|
Ref<StyleBox> style = get_stylebox("normal");
|
|
|
return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
|
|
|
}
|
|
|
+
|
|
|
int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
|
|
|
|
|
|
RID ci;
|
|
@@ -292,7 +294,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
|
|
|
Color selection_bg;
|
|
|
|
|
|
if (p_mode == PROCESS_DRAW) {
|
|
|
-
|
|
|
selection_fg = get_color("font_color_selected");
|
|
|
selection_bg = get_color("selection_color");
|
|
|
}
|
|
@@ -343,18 +344,24 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
|
|
|
Color font_color_shadow;
|
|
|
bool underline = false;
|
|
|
bool strikethrough = false;
|
|
|
+ ItemFade *fade = NULL;
|
|
|
+ int it_char_start = p_char_count;
|
|
|
+
|
|
|
+ Vector<ItemFX *> fx_stack = Vector<ItemFX *>();
|
|
|
+ bool custom_fx_ok = true;
|
|
|
|
|
|
if (p_mode == PROCESS_DRAW) {
|
|
|
color = _find_color(text, p_base_color);
|
|
|
font_color_shadow = _find_color(text, p_font_color_shadow);
|
|
|
if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) {
|
|
|
-
|
|
|
underline = true;
|
|
|
} else if (_find_strikethrough(text)) {
|
|
|
-
|
|
|
strikethrough = true;
|
|
|
}
|
|
|
|
|
|
+ fade = _fetch_by_type<ItemFade>(text, ITEM_FADE);
|
|
|
+ _fetch_item_stack<ItemFX>(text, fx_stack);
|
|
|
+
|
|
|
} else if (p_mode == PROCESS_CACHE) {
|
|
|
l.char_count += text->text.length();
|
|
|
}
|
|
@@ -431,8 +438,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
|
|
|
|
|
|
ofs += cw;
|
|
|
} else if (p_mode == PROCESS_DRAW) {
|
|
|
-
|
|
|
bool selected = false;
|
|
|
+ Color fx_color = Color(color);
|
|
|
+ Point2 fx_offset;
|
|
|
+ CharType fx_char = c[i];
|
|
|
+
|
|
|
if (selection.active) {
|
|
|
|
|
|
int cofs = (&c[i]) - cf;
|
|
@@ -442,8 +452,78 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
|
|
|
}
|
|
|
|
|
|
int cw = 0;
|
|
|
+ int c_item_offset = p_char_count - it_char_start;
|
|
|
+
|
|
|
+ float faded_visibility = 1.0f;
|
|
|
+ if (fade) {
|
|
|
+ if (c_item_offset >= fade->starting_index) {
|
|
|
+ faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length;
|
|
|
+ faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
|
|
|
+ }
|
|
|
+ fx_color.a = faded_visibility;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool visible = visible_characters < 0 || ((p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)) &&
|
|
|
+ faded_visibility > 0.0f);
|
|
|
+
|
|
|
+ for (int j = 0; j < fx_stack.size(); j++) {
|
|
|
+ ItemCustomFX *item_custom = Object::cast_to<ItemCustomFX>(fx_stack[j]);
|
|
|
+ ItemShake *item_shake = Object::cast_to<ItemShake>(fx_stack[j]);
|
|
|
+ ItemWave *item_wave = Object::cast_to<ItemWave>(fx_stack[j]);
|
|
|
+ ItemTornado *item_tornado = Object::cast_to<ItemTornado>(fx_stack[j]);
|
|
|
+ ItemRainbow *item_rainbow = Object::cast_to<ItemRainbow>(fx_stack[j]);
|
|
|
+
|
|
|
+ if (item_custom && custom_fx_ok) {
|
|
|
+ Ref<CharFXTransform> charfx = Ref<CharFXTransform>(memnew(CharFXTransform));
|
|
|
+ Ref<RichTextEffect> custom_effect = _get_custom_effect_by_code(item_custom->identifier);
|
|
|
+ if (!custom_effect.is_null()) {
|
|
|
+ charfx->elapsed_time = item_custom->elapsed_time;
|
|
|
+ charfx->environment = item_custom->environment;
|
|
|
+ charfx->relative_index = c_item_offset;
|
|
|
+ charfx->absolute_index = p_char_count;
|
|
|
+ charfx->visibility = visible;
|
|
|
+ charfx->offset = fx_offset;
|
|
|
+ charfx->color = fx_color;
|
|
|
+ charfx->character = fx_char;
|
|
|
+
|
|
|
+ bool effect_status = custom_effect->_process_effect_impl(charfx);
|
|
|
+ custom_fx_ok = effect_status;
|
|
|
+
|
|
|
+ fx_offset += charfx->offset;
|
|
|
+ fx_color = charfx->color;
|
|
|
+ visible &= charfx->visibility;
|
|
|
+ fx_char = charfx->character;
|
|
|
+ }
|
|
|
+ } else if (item_shake) {
|
|
|
+ uint64_t char_current_rand = item_shake->offset_random(c_item_offset);
|
|
|
+ uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset);
|
|
|
+ uint64_t max_rand = 2147483647;
|
|
|
+ double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI);
|
|
|
+ double previous_offset = Math::range_lerp(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;
|
|
|
+ fx_offset += 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;
|
|
|
+ } else if (item_wave) {
|
|
|
+ double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f);
|
|
|
+ fx_offset += Point2(0, 1) * value;
|
|
|
+ } else if (item_tornado) {
|
|
|
+ double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius);
|
|
|
+ double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius);
|
|
|
+ fx_offset += Point2(torn_x, torn_y);
|
|
|
+ } else if (item_rainbow) {
|
|
|
+ fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)),
|
|
|
+ item_rainbow->saturation,
|
|
|
+ item_rainbow->value,
|
|
|
+ fx_color.a);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent));
|
|
|
if (visible)
|
|
|
line_is_blank = false;
|
|
|
|
|
@@ -451,27 +531,28 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
|
|
|
visible = false;
|
|
|
|
|
|
if (visible) {
|
|
|
+
|
|
|
if (selected) {
|
|
|
- cw = font->get_char_size(c[i], c[i + 1]).x;
|
|
|
+ cw = font->get_char_size(fx_char, c[i + 1]).x;
|
|
|
draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
|
|
|
}
|
|
|
|
|
|
if (p_font_color_shadow.a > 0) {
|
|
|
float x_ofs_shadow = align_ofs + pofs;
|
|
|
float y_ofs_shadow = y + lh - line_descent;
|
|
|
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, c[i], c[i + 1], p_font_color_shadow);
|
|
|
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, fx_char, c[i + 1], p_font_color_shadow);
|
|
|
|
|
|
if (p_shadow_as_outline) {
|
|
|
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
|
|
|
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
|
|
|
- font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
|
|
|
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow);
|
|
|
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow);
|
|
|
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (selected) {
|
|
|
- drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
|
|
|
+ drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color);
|
|
|
} else {
|
|
|
- cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color);
|
|
|
+ cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -800,6 +881,31 @@ void RichTextLabel::_update_scroll() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, float p_delta_time) {
|
|
|
+ Item *it = p_frame;
|
|
|
+ while (it) {
|
|
|
+ ItemFX *ifx = Object::cast_to<ItemFX>(it);
|
|
|
+
|
|
|
+ if (!ifx) {
|
|
|
+ it = _get_next_item(it, true);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ ifx->elapsed_time += p_delta_time;
|
|
|
+
|
|
|
+ ItemShake *shake = Object::cast_to<ItemShake>(it);
|
|
|
+ if (shake) {
|
|
|
+ bool cycle = (shake->elapsed_time > (1.0f / shake->rate));
|
|
|
+ if (cycle) {
|
|
|
+ shake->elapsed_time -= (1.0f / shake->rate);
|
|
|
+ shake->reroll_random();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ it = _get_next_item(it, true);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void RichTextLabel::_notification(int p_what) {
|
|
|
|
|
|
switch (p_what) {
|
|
@@ -873,6 +979,15 @@ void RichTextLabel::_notification(int p_what) {
|
|
|
|
|
|
from_line++;
|
|
|
}
|
|
|
+ } break;
|
|
|
+ case NOTIFICATION_INTERNAL_PROCESS: {
|
|
|
+ float dt = get_process_delta_time();
|
|
|
+
|
|
|
+ for (int i = 0; i < custom_effects.size(); i++) {
|
|
|
+ }
|
|
|
+
|
|
|
+ _update_fx(main, dt);
|
|
|
+ update();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -1026,15 +1141,11 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) {
|
|
|
}
|
|
|
|
|
|
if (b->get_button_index() == BUTTON_WHEEL_UP) {
|
|
|
-
|
|
|
if (scroll_active)
|
|
|
-
|
|
|
vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8);
|
|
|
}
|
|
|
if (b->get_button_index() == BUTTON_WHEEL_DOWN) {
|
|
|
-
|
|
|
if (scroll_active)
|
|
|
-
|
|
|
vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8);
|
|
|
}
|
|
|
}
|
|
@@ -1285,8 +1396,19 @@ bool RichTextLabel::_find_strikethrough(Item *p_item) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) {
|
|
|
+bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) {
|
|
|
+ Item *item = p_item;
|
|
|
|
|
|
+ while (item) {
|
|
|
+ if (item->type == p_type) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ item = item->parent;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) {
|
|
|
Item *item = p_item;
|
|
|
|
|
|
while (item) {
|
|
@@ -1618,6 +1740,49 @@ void RichTextLabel::push_table(int p_columns) {
|
|
|
_add_item(item, true, true);
|
|
|
}
|
|
|
|
|
|
+void RichTextLabel::push_fade(int p_start_index, int p_length) {
|
|
|
+ ItemFade *item = memnew(ItemFade);
|
|
|
+ item->starting_index = p_start_index;
|
|
|
+ item->length = p_length;
|
|
|
+ _add_item(item, true);
|
|
|
+}
|
|
|
+
|
|
|
+void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) {
|
|
|
+ ItemShake *item = memnew(ItemShake);
|
|
|
+ item->strength = p_strength;
|
|
|
+ item->rate = p_rate;
|
|
|
+ _add_item(item, true);
|
|
|
+}
|
|
|
+
|
|
|
+void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) {
|
|
|
+ ItemWave *item = memnew(ItemWave);
|
|
|
+ item->frequency = p_frequency;
|
|
|
+ item->amplitude = p_amplitude;
|
|
|
+ _add_item(item, true);
|
|
|
+}
|
|
|
+
|
|
|
+void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) {
|
|
|
+ ItemTornado *item = memnew(ItemTornado);
|
|
|
+ item->frequency = p_frequency;
|
|
|
+ item->radius = p_radius;
|
|
|
+ _add_item(item, true);
|
|
|
+}
|
|
|
+
|
|
|
+void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_frequency) {
|
|
|
+ ItemRainbow *item = memnew(ItemRainbow);
|
|
|
+ item->frequency = p_frequency;
|
|
|
+ item->saturation = p_saturation;
|
|
|
+ item->value = p_value;
|
|
|
+ _add_item(item, true);
|
|
|
+}
|
|
|
+
|
|
|
+void RichTextLabel::push_customfx(String p_identifier, Dictionary p_environment) {
|
|
|
+ ItemCustomFX *item = memnew(ItemCustomFX);
|
|
|
+ item->identifier = p_identifier;
|
|
|
+ item->environment = p_environment;
|
|
|
+ _add_item(item, true);
|
|
|
+}
|
|
|
+
|
|
|
void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) {
|
|
|
|
|
|
ERR_FAIL_COND(current->type != ITEM_TABLE);
|
|
@@ -1762,6 +1927,8 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
|
|
|
bool in_bold = false;
|
|
|
bool in_italics = false;
|
|
|
|
|
|
+ set_process_internal(false);
|
|
|
+
|
|
|
while (pos < p_bbcode.length()) {
|
|
|
|
|
|
int brk_pos = p_bbcode.find("[", pos);
|
|
@@ -1785,7 +1952,6 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
|
|
|
}
|
|
|
|
|
|
String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
|
|
|
-
|
|
|
if (tag.begins_with("/") && tag_stack.size()) {
|
|
|
|
|
|
bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
|
|
@@ -1798,9 +1964,8 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
|
|
|
indent_level--;
|
|
|
|
|
|
if (!tag_ok) {
|
|
|
-
|
|
|
- add_text("[");
|
|
|
- pos++;
|
|
|
+ add_text("[" + tag);
|
|
|
+ pos = brk_end;
|
|
|
continue;
|
|
|
}
|
|
|
|
|
@@ -1992,10 +2157,145 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
|
|
|
pos = brk_end + 1;
|
|
|
tag_stack.push_front("font");
|
|
|
|
|
|
- } else {
|
|
|
+ } else if (tag.begins_with("fade")) {
|
|
|
+ Vector<String> tags = tag.split(" ", false);
|
|
|
+ int startIndex = 0;
|
|
|
+ int length = 10;
|
|
|
+
|
|
|
+ if (tags.size() > 1) {
|
|
|
+ tags.remove(0);
|
|
|
+ for (int i = 0; i < tags.size(); i++) {
|
|
|
+ String expr = tags[i];
|
|
|
+ if (expr.begins_with("start=")) {
|
|
|
+ String start_str = expr.substr(6, expr.length());
|
|
|
+ startIndex = start_str.to_int();
|
|
|
+ } else if (expr.begins_with("length=")) {
|
|
|
+ String end_str = expr.substr(7, expr.length());
|
|
|
+ length = end_str.to_int();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ push_fade(startIndex, length);
|
|
|
+ pos = brk_end + 1;
|
|
|
+ tag_stack.push_front("fade");
|
|
|
+ } else if (tag.begins_with("shake")) {
|
|
|
+ Vector<String> tags = tag.split(" ", false);
|
|
|
+ int strength = 5;
|
|
|
+ float rate = 20.0f;
|
|
|
+
|
|
|
+ if (tags.size() > 1) {
|
|
|
+ tags.remove(0);
|
|
|
+ for (int i = 0; i < tags.size(); i++) {
|
|
|
+ String expr = tags[i];
|
|
|
+ if (expr.begins_with("level=")) {
|
|
|
+ String str_str = expr.substr(6, expr.length());
|
|
|
+ strength = str_str.to_int();
|
|
|
+ } else if (expr.begins_with("rate=")) {
|
|
|
+ String rate_str = expr.substr(5, expr.length());
|
|
|
+ rate = rate_str.to_float();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ push_shake(strength, rate);
|
|
|
+ pos = brk_end + 1;
|
|
|
+ tag_stack.push_front("shake");
|
|
|
+ set_process_internal(true);
|
|
|
+ } else if (tag.begins_with("wave")) {
|
|
|
+ Vector<String> tags = tag.split(" ", false);
|
|
|
+ float amplitude = 20.0f;
|
|
|
+ float period = 5.0f;
|
|
|
+
|
|
|
+ if (tags.size() > 1) {
|
|
|
+ tags.remove(0);
|
|
|
+ for (int i = 0; i < tags.size(); i++) {
|
|
|
+ String expr = tags[i];
|
|
|
+ if (expr.begins_with("amp=")) {
|
|
|
+ String amp_str = expr.substr(4, expr.length());
|
|
|
+ amplitude = amp_str.to_float();
|
|
|
+ } else if (expr.begins_with("freq=")) {
|
|
|
+ String period_str = expr.substr(5, expr.length());
|
|
|
+ period = period_str.to_float();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- add_text("["); //ignore
|
|
|
- pos = brk_pos + 1;
|
|
|
+ push_wave(period, amplitude);
|
|
|
+ pos = brk_end + 1;
|
|
|
+ tag_stack.push_front("wave");
|
|
|
+ set_process_internal(true);
|
|
|
+ } else if (tag.begins_with("tornado")) {
|
|
|
+ Vector<String> tags = tag.split(" ", false);
|
|
|
+ float radius = 10.0f;
|
|
|
+ float frequency = 1.0f;
|
|
|
+
|
|
|
+ if (tags.size() > 1) {
|
|
|
+ tags.remove(0);
|
|
|
+ for (int i = 0; i < tags.size(); i++) {
|
|
|
+ String expr = tags[i];
|
|
|
+ if (expr.begins_with("radius=")) {
|
|
|
+ String amp_str = expr.substr(7, expr.length());
|
|
|
+ radius = amp_str.to_float();
|
|
|
+ } else if (expr.begins_with("freq=")) {
|
|
|
+ String period_str = expr.substr(5, expr.length());
|
|
|
+ frequency = period_str.to_float();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ push_tornado(frequency, radius);
|
|
|
+ pos = brk_end + 1;
|
|
|
+ tag_stack.push_front("tornado");
|
|
|
+ set_process_internal(true);
|
|
|
+ } else if (tag.begins_with("rainbow")) {
|
|
|
+ Vector<String> tags = tag.split(" ", false);
|
|
|
+ float saturation = 0.8f;
|
|
|
+ float value = 0.8f;
|
|
|
+ float frequency = 1.0f;
|
|
|
+
|
|
|
+ if (tags.size() > 1) {
|
|
|
+ tags.remove(0);
|
|
|
+ for (int i = 0; i < tags.size(); i++) {
|
|
|
+ String expr = tags[i];
|
|
|
+ if (expr.begins_with("sat=")) {
|
|
|
+ String sat_str = expr.substr(4, expr.length());
|
|
|
+ saturation = sat_str.to_float();
|
|
|
+ } else if (expr.begins_with("val=")) {
|
|
|
+ String val_str = expr.substr(4, expr.length());
|
|
|
+ value = val_str.to_float();
|
|
|
+ } else if (expr.begins_with("freq=")) {
|
|
|
+ String freq_str = expr.substr(5, expr.length());
|
|
|
+ frequency = freq_str.to_float();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ push_rainbow(saturation, value, frequency);
|
|
|
+ pos = brk_end + 1;
|
|
|
+ tag_stack.push_front("rainbow");
|
|
|
+ set_process_internal(true);
|
|
|
+ } else {
|
|
|
+ Vector<String> expr = tag.split(" ", false);
|
|
|
+ if (expr.size() < 1) {
|
|
|
+ add_text("[");
|
|
|
+ pos = brk_pos + 1;
|
|
|
+ } else {
|
|
|
+ String identifier = expr[0];
|
|
|
+ expr.remove(0);
|
|
|
+ Dictionary properties = parse_expressions_for_values(expr);
|
|
|
+ Ref<RichTextEffect> effect = _get_custom_effect_by_code(identifier);
|
|
|
+
|
|
|
+ if (!effect.is_null()) {
|
|
|
+ push_customfx(identifier, properties);
|
|
|
+ pos = brk_end + 1;
|
|
|
+ tag_stack.push_front(identifier);
|
|
|
+ set_process_internal(true);
|
|
|
+ } else {
|
|
|
+ add_text("["); //ignore
|
|
|
+ pos = brk_pos + 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2204,6 +2504,34 @@ float RichTextLabel::get_percent_visible() const {
|
|
|
return percent_visible;
|
|
|
}
|
|
|
|
|
|
+void RichTextLabel::set_effects(const Vector<Variant> &effects) {
|
|
|
+ custom_effects.clear();
|
|
|
+ for (int i = 0; i < effects.size(); i++) {
|
|
|
+ Ref<RichTextEffect> effect = Ref<RichTextEffect>(effects[i]);
|
|
|
+ custom_effects.push_back(effect);
|
|
|
+ }
|
|
|
+
|
|
|
+ parse_bbcode(bbcode);
|
|
|
+}
|
|
|
+
|
|
|
+Vector<Variant> RichTextLabel::get_effects() {
|
|
|
+ Vector<Variant> r;
|
|
|
+ for (int i = 0; i < custom_effects.size(); i++) {
|
|
|
+ r.push_back(custom_effects[i].get_ref_ptr());
|
|
|
+ }
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+void RichTextLabel::install_effect(const Variant effect) {
|
|
|
+ Ref<RichTextEffect> rteffect;
|
|
|
+ rteffect = effect;
|
|
|
+
|
|
|
+ if (rteffect.is_valid()) {
|
|
|
+ custom_effects.push_back(effect);
|
|
|
+ parse_bbcode(bbcode);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
int RichTextLabel::get_content_height() {
|
|
|
int total_height = 0;
|
|
|
if (main->lines.size())
|
|
@@ -2280,6 +2608,12 @@ void RichTextLabel::_bind_methods() {
|
|
|
|
|
|
ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height);
|
|
|
|
|
|
+ ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values);
|
|
|
+
|
|
|
+ ClassDB::bind_method(D_METHOD("set_effects", "effects"), &RichTextLabel::set_effects);
|
|
|
+ ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects);
|
|
|
+ ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect);
|
|
|
+
|
|
|
ADD_GROUP("BBCode", "bbcode_");
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode");
|
|
@@ -2297,6 +2631,8 @@ void RichTextLabel::_bind_methods() {
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled");
|
|
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
|
|
|
|
|
|
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", (PropertyHint)(PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE), "17/17:RichTextEffect", PROPERTY_USAGE_DEFAULT, "RichTextEffect"), "set_effects", "get_effects");
|
|
|
+
|
|
|
ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
|
|
|
ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
|
|
|
ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
|
|
@@ -2322,11 +2658,16 @@ void RichTextLabel::_bind_methods() {
|
|
|
BIND_ENUM_CONSTANT(ITEM_INDENT);
|
|
|
BIND_ENUM_CONSTANT(ITEM_LIST);
|
|
|
BIND_ENUM_CONSTANT(ITEM_TABLE);
|
|
|
+ BIND_ENUM_CONSTANT(ITEM_FADE);
|
|
|
+ BIND_ENUM_CONSTANT(ITEM_SHAKE);
|
|
|
+ BIND_ENUM_CONSTANT(ITEM_WAVE);
|
|
|
+ BIND_ENUM_CONSTANT(ITEM_TORNADO);
|
|
|
+ BIND_ENUM_CONSTANT(ITEM_RAINBOW);
|
|
|
+ BIND_ENUM_CONSTANT(ITEM_CUSTOMFX);
|
|
|
BIND_ENUM_CONSTANT(ITEM_META);
|
|
|
}
|
|
|
|
|
|
void RichTextLabel::set_visible_characters(int p_visible) {
|
|
|
-
|
|
|
visible_characters = p_visible;
|
|
|
update();
|
|
|
}
|
|
@@ -2358,6 +2699,77 @@ Size2 RichTextLabel::get_minimum_size() const {
|
|
|
return Size2();
|
|
|
}
|
|
|
|
|
|
+Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) {
|
|
|
+ Ref<RichTextEffect> r;
|
|
|
+ for (int i = 0; i < custom_effects.size(); i++) {
|
|
|
+ if (!custom_effects[i].is_valid())
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (custom_effects[i]->get_bbcode() == p_bbcode_identifier) {
|
|
|
+ r = custom_effects[i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return r;
|
|
|
+}
|
|
|
+
|
|
|
+Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) {
|
|
|
+ Dictionary d = Dictionary();
|
|
|
+ for (int i = 0; i < p_expressions.size(); i++) {
|
|
|
+ String expression = p_expressions[i];
|
|
|
+
|
|
|
+ Array a = Array();
|
|
|
+ Vector<String> parts = expression.split("=", true);
|
|
|
+ String key = parts[0];
|
|
|
+ if (parts.size() != 2) {
|
|
|
+ return d;
|
|
|
+ }
|
|
|
+
|
|
|
+ Vector<String> values = parts[1].split(",", false);
|
|
|
+
|
|
|
+ RegEx color = RegEx();
|
|
|
+ color.compile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$");
|
|
|
+ RegEx nodepath = RegEx();
|
|
|
+ nodepath.compile("^\\$");
|
|
|
+ RegEx boolean = RegEx();
|
|
|
+ boolean.compile("^(true|false)$");
|
|
|
+ RegEx decimal = RegEx();
|
|
|
+ decimal.compile("^-?^.?\\d+(\\.\\d+?)?$");
|
|
|
+ RegEx numerical = RegEx();
|
|
|
+ numerical.compile("^\\d+$");
|
|
|
+
|
|
|
+ for (int j = 0; j < values.size(); j++) {
|
|
|
+ if (!color.search(values[j]).is_null()) {
|
|
|
+ a.append(Color::html(values[j]));
|
|
|
+ } else if (!nodepath.search(values[j]).is_null()) {
|
|
|
+ if (values[j].begins_with("$")) {
|
|
|
+ String v = values[j].substr(1, values[j].length());
|
|
|
+ a.append(NodePath(v));
|
|
|
+ }
|
|
|
+ } else if (!boolean.search(values[j]).is_null()) {
|
|
|
+ if (values[j] == "true") {
|
|
|
+ a.append(true);
|
|
|
+ } else if (values[j] == "false") {
|
|
|
+ a.append(false);
|
|
|
+ }
|
|
|
+ } else if (!decimal.search(values[j]).is_null()) {
|
|
|
+ a.append(values[j].to_double());
|
|
|
+ } else if (!numerical.search(values[j]).is_null()) {
|
|
|
+ a.append(values[j].to_int());
|
|
|
+ } else {
|
|
|
+ a.append(values[j]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (values.size() > 1) {
|
|
|
+ d[key] = a;
|
|
|
+ } else if (values.size() == 1) {
|
|
|
+ d[key] = a[0];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return d;
|
|
|
+}
|
|
|
+
|
|
|
RichTextLabel::RichTextLabel() {
|
|
|
|
|
|
main = memnew(ItemFrame);
|