Update RichTextLabel to support real time effects and custom BBCodes.
Added a new ItemFX type to RichTextLabel which supports dynamic text effects. RichTextEffect Resource Type was added which can be extended for more real time text effects.
This commit is contained in:
parent
d2900347bf
commit
feedd6c615
|
@ -0,0 +1,122 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* rich_text_effect.cpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#include "rich_text_effect.h"
|
||||||
|
|
||||||
|
#include "core/script_language.h"
|
||||||
|
|
||||||
|
void RichTextEffect::_bind_methods() {
|
||||||
|
BIND_VMETHOD(MethodInfo(Variant::INT, "_process_custom_fx", PropertyInfo(Variant::OBJECT, "char_fx", PROPERTY_HINT_RESOURCE_TYPE, "CustomFXChar")));
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant RichTextEffect::get_bbcode() const {
|
||||||
|
Variant r;
|
||||||
|
if (get_script_instance()) {
|
||||||
|
if (!get_script_instance()->get("bbcode", r)) {
|
||||||
|
String path = get_script_instance()->get_script()->get_path();
|
||||||
|
r = path.get_file().get_basename();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RichTextEffect::_process_effect_impl(Ref<CharFXTransform> p_cfx) {
|
||||||
|
bool return_value = false;
|
||||||
|
if (get_script_instance()) {
|
||||||
|
Variant v = get_script_instance()->call("_process_custom_fx", p_cfx);
|
||||||
|
if (v.get_type() != Variant::BOOL) {
|
||||||
|
return_value = false;
|
||||||
|
} else {
|
||||||
|
return_value = (bool)v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return return_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
RichTextEffect::RichTextEffect() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharFXTransform::_bind_methods() {
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_relative_index"), &CharFXTransform::get_relative_index);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_relative_index", "index"), &CharFXTransform::set_relative_index);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_absolute_index"), &CharFXTransform::get_absolute_index);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_absolute_index", "index"), &CharFXTransform::set_absolute_index);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_elapsed_time"), &CharFXTransform::get_elapsed_time);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_elapsed_time", "time"), &CharFXTransform::set_elapsed_time);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("is_visible"), &CharFXTransform::is_visible);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_visibility", "visibility"), &CharFXTransform::set_visibility);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_offset"), &CharFXTransform::get_offset);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_offset", "offset"), &CharFXTransform::set_offset);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_color"), &CharFXTransform::get_color);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_color", "color"), &CharFXTransform::set_color);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_environment"), &CharFXTransform::get_environment);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_environment", "environment"), &CharFXTransform::set_environment);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_character"), &CharFXTransform::get_character);
|
||||||
|
ClassDB::bind_method(D_METHOD("set_character", "character"), &CharFXTransform::set_character);
|
||||||
|
|
||||||
|
ClassDB::bind_method(D_METHOD("get_value_or", "key", "default_value"), &CharFXTransform::get_value_or);
|
||||||
|
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "relative_index"), "set_relative_index", "get_relative_index");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "absolute_index"), "set_absolute_index", "get_absolute_index");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::REAL, "elapsed_time"), "set_elapsed_time", "get_elapsed_time");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visibility", "is_visible");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "env"), "set_environment", "get_environment");
|
||||||
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "character"), "set_character", "get_character");
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant CharFXTransform::get_value_or(String p_key, Variant p_default_value) {
|
||||||
|
if (!this->environment.has(p_key))
|
||||||
|
return p_default_value;
|
||||||
|
|
||||||
|
Variant r = environment[p_key];
|
||||||
|
if (r.get_type() != p_default_value.get_type())
|
||||||
|
return p_default_value;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
CharFXTransform::CharFXTransform() {
|
||||||
|
relative_index = 0;
|
||||||
|
absolute_index = 0;
|
||||||
|
visibility = true;
|
||||||
|
offset = Point2();
|
||||||
|
color = Color();
|
||||||
|
character = 0;
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* rich_text_effect.h */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
|
||||||
|
/* */
|
||||||
|
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
/* a copy of this software and associated documentation files (the */
|
||||||
|
/* "Software"), to deal in the Software without restriction, including */
|
||||||
|
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
/* the following conditions: */
|
||||||
|
/* */
|
||||||
|
/* The above copyright notice and this permission notice shall be */
|
||||||
|
/* included in all copies or substantial portions of the Software. */
|
||||||
|
/* */
|
||||||
|
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
#ifndef RICH_TEXT_EFFECT_H
|
||||||
|
#define RICH_TEXT_EFFECT_H
|
||||||
|
|
||||||
|
#include "core/resource.h"
|
||||||
|
|
||||||
|
class RichTextEffect : public Resource {
|
||||||
|
GDCLASS(RichTextEffect, Resource);
|
||||||
|
OBJ_SAVE_TYPE(RichTextEffect);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Variant get_bbcode() const;
|
||||||
|
bool _process_effect_impl(Ref<class CharFXTransform> p_cfx);
|
||||||
|
|
||||||
|
RichTextEffect();
|
||||||
|
};
|
||||||
|
|
||||||
|
class CharFXTransform : public Reference {
|
||||||
|
GDCLASS(CharFXTransform, Reference);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint64_t relative_index;
|
||||||
|
uint64_t absolute_index;
|
||||||
|
bool visibility;
|
||||||
|
Point2 offset;
|
||||||
|
Color color;
|
||||||
|
CharType character;
|
||||||
|
float elapsed_time;
|
||||||
|
Dictionary environment;
|
||||||
|
|
||||||
|
CharFXTransform();
|
||||||
|
uint64_t get_relative_index() { return relative_index; }
|
||||||
|
void set_relative_index(uint64_t p_index) { relative_index = p_index; }
|
||||||
|
uint64_t get_absolute_index() { return absolute_index; }
|
||||||
|
void set_absolute_index(uint64_t p_index) { absolute_index = p_index; }
|
||||||
|
float get_elapsed_time() { return elapsed_time; }
|
||||||
|
void set_elapsed_time(float p_elapsed_time) { elapsed_time = p_elapsed_time; }
|
||||||
|
bool is_visible() { return visibility; }
|
||||||
|
void set_visibility(bool p_vis) { visibility = p_vis; }
|
||||||
|
Point2 get_offset() { return offset; }
|
||||||
|
void set_offset(Point2 p_offset) { offset = p_offset; }
|
||||||
|
Color get_color() { return color; }
|
||||||
|
void set_color(Color p_color) { color = p_color; }
|
||||||
|
int get_character() { return (int)character; }
|
||||||
|
void set_character(int p_char) { character = (CharType)p_char; }
|
||||||
|
Dictionary get_environment() { return environment; }
|
||||||
|
void set_environment(Dictionary p_environment) { environment = p_environment; }
|
||||||
|
|
||||||
|
Variant get_value_or(String p_key, Variant p_default_value);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // RICH_TEXT_EFFECT_H
|
|
@ -30,10 +30,11 @@
|
||||||
|
|
||||||
#include "rich_text_label.h"
|
#include "rich_text_label.h"
|
||||||
|
|
||||||
|
#include "core/math/math_defs.h"
|
||||||
#include "core/os/keyboard.h"
|
#include "core/os/keyboard.h"
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
|
#include "modules/regex/regex.h"
|
||||||
#include "scene/scene_string_names.h"
|
#include "scene/scene_string_names.h"
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
#include "editor/editor_scale.h"
|
#include "editor/editor_scale.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -139,6 +140,7 @@ Rect2 RichTextLabel::_get_text_rect() {
|
||||||
Ref<StyleBox> style = get_stylebox("normal");
|
Ref<StyleBox> style = get_stylebox("normal");
|
||||||
return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
|
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) {
|
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;
|
RID ci;
|
||||||
|
@ -292,7 +294,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
|
||||||
Color selection_bg;
|
Color selection_bg;
|
||||||
|
|
||||||
if (p_mode == PROCESS_DRAW) {
|
if (p_mode == PROCESS_DRAW) {
|
||||||
|
|
||||||
selection_fg = get_color("font_color_selected");
|
selection_fg = get_color("font_color_selected");
|
||||||
selection_bg = get_color("selection_color");
|
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;
|
Color font_color_shadow;
|
||||||
bool underline = false;
|
bool underline = false;
|
||||||
bool strikethrough = 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) {
|
if (p_mode == PROCESS_DRAW) {
|
||||||
color = _find_color(text, p_base_color);
|
color = _find_color(text, p_base_color);
|
||||||
font_color_shadow = _find_color(text, p_font_color_shadow);
|
font_color_shadow = _find_color(text, p_font_color_shadow);
|
||||||
if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) {
|
if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) {
|
||||||
|
|
||||||
underline = true;
|
underline = true;
|
||||||
} else if (_find_strikethrough(text)) {
|
} else if (_find_strikethrough(text)) {
|
||||||
|
|
||||||
strikethrough = true;
|
strikethrough = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fade = _fetch_by_type<ItemFade>(text, ITEM_FADE);
|
||||||
|
_fetch_item_stack<ItemFX>(text, fx_stack);
|
||||||
|
|
||||||
} else if (p_mode == PROCESS_CACHE) {
|
} else if (p_mode == PROCESS_CACHE) {
|
||||||
l.char_count += text->text.length();
|
l.char_count += text->text.length();
|
||||||
}
|
}
|
||||||
|
@ -431,8 +438,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
|
||||||
|
|
||||||
ofs += cw;
|
ofs += cw;
|
||||||
} else if (p_mode == PROCESS_DRAW) {
|
} else if (p_mode == PROCESS_DRAW) {
|
||||||
|
|
||||||
bool selected = false;
|
bool selected = false;
|
||||||
|
Color fx_color = Color(color);
|
||||||
|
Point2 fx_offset;
|
||||||
|
CharType fx_char = c[i];
|
||||||
|
|
||||||
if (selection.active) {
|
if (selection.active) {
|
||||||
|
|
||||||
int cofs = (&c[i]) - cf;
|
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 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)
|
if (visible)
|
||||||
line_is_blank = false;
|
line_is_blank = false;
|
||||||
|
|
||||||
|
@ -451,27 +531,28 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
|
||||||
visible = false;
|
visible = false;
|
||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
|
||||||
if (selected) {
|
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);
|
draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_font_color_shadow.a > 0) {
|
if (p_font_color_shadow.a > 0) {
|
||||||
float x_ofs_shadow = align_ofs + pofs;
|
float x_ofs_shadow = align_ofs + pofs;
|
||||||
float y_ofs_shadow = y + lh - line_descent;
|
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) {
|
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), 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), 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), 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selected) {
|
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 {
|
} 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) {
|
void RichTextLabel::_notification(int p_what) {
|
||||||
|
|
||||||
switch (p_what) {
|
switch (p_what) {
|
||||||
|
@ -873,6 +979,15 @@ void RichTextLabel::_notification(int p_what) {
|
||||||
|
|
||||||
from_line++;
|
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 (b->get_button_index() == BUTTON_WHEEL_UP) {
|
||||||
|
|
||||||
if (scroll_active)
|
if (scroll_active)
|
||||||
|
|
||||||
vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8);
|
vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8);
|
||||||
}
|
}
|
||||||
if (b->get_button_index() == BUTTON_WHEEL_DOWN) {
|
if (b->get_button_index() == BUTTON_WHEEL_DOWN) {
|
||||||
|
|
||||||
if (scroll_active)
|
if (scroll_active)
|
||||||
|
|
||||||
vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8);
|
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;
|
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;
|
Item *item = p_item;
|
||||||
|
|
||||||
while (item) {
|
while (item) {
|
||||||
|
@ -1618,6 +1740,49 @@ void RichTextLabel::push_table(int p_columns) {
|
||||||
_add_item(item, true, true);
|
_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) {
|
void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) {
|
||||||
|
|
||||||
ERR_FAIL_COND(current->type != ITEM_TABLE);
|
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_bold = false;
|
||||||
bool in_italics = false;
|
bool in_italics = false;
|
||||||
|
|
||||||
|
set_process_internal(false);
|
||||||
|
|
||||||
while (pos < p_bbcode.length()) {
|
while (pos < p_bbcode.length()) {
|
||||||
|
|
||||||
int brk_pos = p_bbcode.find("[", pos);
|
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);
|
String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1);
|
||||||
|
|
||||||
if (tag.begins_with("/") && tag_stack.size()) {
|
if (tag.begins_with("/") && tag_stack.size()) {
|
||||||
|
|
||||||
bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length());
|
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--;
|
indent_level--;
|
||||||
|
|
||||||
if (!tag_ok) {
|
if (!tag_ok) {
|
||||||
|
add_text("[" + tag);
|
||||||
add_text("[");
|
pos = brk_end;
|
||||||
pos++;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1992,12 +2157,147 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
|
||||||
pos = brk_end + 1;
|
pos = brk_end + 1;
|
||||||
tag_stack.push_front("font");
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
add_text("["); //ignore
|
||||||
pos = brk_pos + 1;
|
pos = brk_pos + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -2204,6 +2504,34 @@ float RichTextLabel::get_percent_visible() const {
|
||||||
return percent_visible;
|
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 RichTextLabel::get_content_height() {
|
||||||
int total_height = 0;
|
int total_height = 0;
|
||||||
if (main->lines.size())
|
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("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_GROUP("BBCode", "bbcode_");
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_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");
|
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, "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::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_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_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)));
|
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_INDENT);
|
||||||
BIND_ENUM_CONSTANT(ITEM_LIST);
|
BIND_ENUM_CONSTANT(ITEM_LIST);
|
||||||
BIND_ENUM_CONSTANT(ITEM_TABLE);
|
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);
|
BIND_ENUM_CONSTANT(ITEM_META);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RichTextLabel::set_visible_characters(int p_visible) {
|
void RichTextLabel::set_visible_characters(int p_visible) {
|
||||||
|
|
||||||
visible_characters = p_visible;
|
visible_characters = p_visible;
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
@ -2358,6 +2699,77 @@ Size2 RichTextLabel::get_minimum_size() const {
|
||||||
return Size2();
|
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() {
|
RichTextLabel::RichTextLabel() {
|
||||||
|
|
||||||
main = memnew(ItemFrame);
|
main = memnew(ItemFrame);
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#ifndef RICH_TEXT_LABEL_H
|
#ifndef RICH_TEXT_LABEL_H
|
||||||
#define RICH_TEXT_LABEL_H
|
#define RICH_TEXT_LABEL_H
|
||||||
|
|
||||||
|
#include "rich_text_effect.h"
|
||||||
#include "scene/gui/scroll_bar.h"
|
#include "scene/gui/scroll_bar.h"
|
||||||
|
|
||||||
class RichTextLabel : public Control {
|
class RichTextLabel : public Control {
|
||||||
|
@ -67,7 +68,13 @@ public:
|
||||||
ITEM_INDENT,
|
ITEM_INDENT,
|
||||||
ITEM_LIST,
|
ITEM_LIST,
|
||||||
ITEM_TABLE,
|
ITEM_TABLE,
|
||||||
ITEM_META
|
ITEM_FADE,
|
||||||
|
ITEM_SHAKE,
|
||||||
|
ITEM_WAVE,
|
||||||
|
ITEM_TORNADO,
|
||||||
|
ITEM_RAINBOW,
|
||||||
|
ITEM_META,
|
||||||
|
ITEM_CUSTOMFX
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -96,7 +103,7 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Item {
|
struct Item : public Object {
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
Item *parent;
|
Item *parent;
|
||||||
|
@ -214,6 +221,101 @@ private:
|
||||||
ItemTable() { type = ITEM_TABLE; }
|
ItemTable() { type = ITEM_TABLE; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ItemFade : public Item {
|
||||||
|
int starting_index;
|
||||||
|
int length;
|
||||||
|
|
||||||
|
ItemFade() { type = ITEM_FADE; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ItemFX : public Item {
|
||||||
|
float elapsed_time;
|
||||||
|
|
||||||
|
ItemFX() {
|
||||||
|
elapsed_time = 0.0f;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ItemShake : public ItemFX {
|
||||||
|
int strength;
|
||||||
|
float rate;
|
||||||
|
uint64_t _current_rng;
|
||||||
|
uint64_t _previous_rng;
|
||||||
|
|
||||||
|
ItemShake() {
|
||||||
|
strength = 0;
|
||||||
|
rate = 0.0f;
|
||||||
|
_current_rng = 0;
|
||||||
|
type = ITEM_SHAKE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reroll_random() {
|
||||||
|
_previous_rng = _current_rng;
|
||||||
|
_current_rng = Math::rand();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t offset_random(int index) {
|
||||||
|
return (_current_rng >> (index % 64)) |
|
||||||
|
(_current_rng << (64 - (index % 64)));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t offset_previous_random(int index) {
|
||||||
|
return (_previous_rng >> (index % 64)) |
|
||||||
|
(_previous_rng << (64 - (index % 64)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ItemWave : public ItemFX {
|
||||||
|
float frequency;
|
||||||
|
float amplitude;
|
||||||
|
|
||||||
|
ItemWave() {
|
||||||
|
frequency = 1.0f;
|
||||||
|
amplitude = 1.0f;
|
||||||
|
type = ITEM_WAVE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ItemTornado : public ItemFX {
|
||||||
|
float radius;
|
||||||
|
float frequency;
|
||||||
|
|
||||||
|
ItemTornado() {
|
||||||
|
radius = 1.0f;
|
||||||
|
frequency = 1.0f;
|
||||||
|
type = ITEM_TORNADO;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ItemRainbow : public ItemFX {
|
||||||
|
float saturation;
|
||||||
|
float value;
|
||||||
|
float frequency;
|
||||||
|
|
||||||
|
ItemRainbow() {
|
||||||
|
saturation = 0.8f;
|
||||||
|
value = 0.8f;
|
||||||
|
frequency = 1.0f;
|
||||||
|
type = ITEM_RAINBOW;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ItemCustomFX : public ItemFX {
|
||||||
|
String identifier;
|
||||||
|
Dictionary environment;
|
||||||
|
|
||||||
|
ItemCustomFX() {
|
||||||
|
identifier = "";
|
||||||
|
environment = Dictionary();
|
||||||
|
type = ITEM_CUSTOMFX;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~ItemCustomFX() {
|
||||||
|
_clear_children();
|
||||||
|
environment.clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
ItemFrame *main;
|
ItemFrame *main;
|
||||||
Item *current;
|
Item *current;
|
||||||
ItemFrame *current_frame;
|
ItemFrame *current_frame;
|
||||||
|
@ -239,6 +341,8 @@ private:
|
||||||
ItemMeta *meta_hovering;
|
ItemMeta *meta_hovering;
|
||||||
Variant current_meta;
|
Variant current_meta;
|
||||||
|
|
||||||
|
Vector<Ref<RichTextEffect> > custom_effects;
|
||||||
|
|
||||||
void _invalidate_current_line(ItemFrame *p_frame);
|
void _invalidate_current_line(ItemFrame *p_frame);
|
||||||
void _validate_line_caches(ItemFrame *p_frame);
|
void _validate_line_caches(ItemFrame *p_frame);
|
||||||
|
|
||||||
|
@ -246,7 +350,6 @@ private:
|
||||||
void _remove_item(Item *p_item, const int p_line, const int p_subitem_line);
|
void _remove_item(Item *p_item, const int p_line, const int p_subitem_line);
|
||||||
|
|
||||||
struct ProcessState {
|
struct ProcessState {
|
||||||
|
|
||||||
int line_width;
|
int line_width;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -287,8 +390,36 @@ private:
|
||||||
bool _find_strikethrough(Item *p_item);
|
bool _find_strikethrough(Item *p_item);
|
||||||
bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = NULL);
|
bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = NULL);
|
||||||
bool _find_layout_subitem(Item *from, Item *to);
|
bool _find_layout_subitem(Item *from, Item *to);
|
||||||
|
bool _find_by_type(Item *p_item, ItemType p_type);
|
||||||
|
template <typename T>
|
||||||
|
T *_fetch_by_type(Item *p_item, ItemType p_type) {
|
||||||
|
Item *item = p_item;
|
||||||
|
T *result = NULL;
|
||||||
|
while (item) {
|
||||||
|
if (item->type == p_type) {
|
||||||
|
result = Object::cast_to<T>(item);
|
||||||
|
if (result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
item = item->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
template <typename T>
|
||||||
|
void _fetch_item_stack(Item *p_item, Vector<T *> &r_stack) {
|
||||||
|
Item *item = p_item;
|
||||||
|
while (item) {
|
||||||
|
T *found = Object::cast_to<T>(item);
|
||||||
|
if (found) {
|
||||||
|
r_stack.push_back(found);
|
||||||
|
}
|
||||||
|
item = item->parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _update_scroll();
|
void _update_scroll();
|
||||||
|
void _update_fx(ItemFrame *p_frame, float p_delta_time);
|
||||||
void _scroll_changed(double);
|
void _scroll_changed(double);
|
||||||
|
|
||||||
void _gui_input(Ref<InputEvent> p_event);
|
void _gui_input(Ref<InputEvent> p_event);
|
||||||
|
@ -296,6 +427,8 @@ private:
|
||||||
Item *_get_prev_item(Item *p_item, bool p_free = false);
|
Item *_get_prev_item(Item *p_item, bool p_free = false);
|
||||||
|
|
||||||
Rect2 _get_text_rect();
|
Rect2 _get_text_rect();
|
||||||
|
Ref<RichTextEffect> _get_custom_effect_by_code(String p_bbcode_identifier);
|
||||||
|
virtual Dictionary parse_expressions_for_values(Vector<String> p_expressions);
|
||||||
|
|
||||||
bool use_bbcode;
|
bool use_bbcode;
|
||||||
String bbcode;
|
String bbcode;
|
||||||
|
@ -322,6 +455,12 @@ public:
|
||||||
void push_list(ListType p_list);
|
void push_list(ListType p_list);
|
||||||
void push_meta(const Variant &p_meta);
|
void push_meta(const Variant &p_meta);
|
||||||
void push_table(int p_columns);
|
void push_table(int p_columns);
|
||||||
|
void push_fade(int p_start_index, int p_length);
|
||||||
|
void push_shake(int p_level, float p_rate);
|
||||||
|
void push_wave(float p_frequency, float p_amplitude);
|
||||||
|
void push_tornado(float p_frequency, float p_radius);
|
||||||
|
void push_rainbow(float p_saturation, float p_value, float p_frequency);
|
||||||
|
void push_customfx(String p_identifier, Dictionary p_environment);
|
||||||
void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1);
|
void set_table_column_expand(int p_column, bool p_expand, int p_ratio = 1);
|
||||||
int get_current_table_column() const;
|
int get_current_table_column() const;
|
||||||
void push_cell();
|
void push_cell();
|
||||||
|
@ -380,6 +519,11 @@ public:
|
||||||
void set_percent_visible(float p_percent);
|
void set_percent_visible(float p_percent);
|
||||||
float get_percent_visible() const;
|
float get_percent_visible() const;
|
||||||
|
|
||||||
|
void set_effects(const Vector<Variant> &effects);
|
||||||
|
Vector<Variant> get_effects();
|
||||||
|
|
||||||
|
void install_effect(const Variant effect);
|
||||||
|
|
||||||
void set_fixed_size_to_width(int p_width);
|
void set_fixed_size_to_width(int p_width);
|
||||||
virtual Size2 get_minimum_size() const;
|
virtual Size2 get_minimum_size() const;
|
||||||
|
|
||||||
|
|
|
@ -343,6 +343,7 @@ void register_scene_types() {
|
||||||
ClassDB::register_class<ColorPicker>();
|
ClassDB::register_class<ColorPicker>();
|
||||||
ClassDB::register_class<ColorPickerButton>();
|
ClassDB::register_class<ColorPickerButton>();
|
||||||
ClassDB::register_class<RichTextLabel>();
|
ClassDB::register_class<RichTextLabel>();
|
||||||
|
ClassDB::register_class<RichTextEffect>();
|
||||||
ClassDB::register_class<PopupDialog>();
|
ClassDB::register_class<PopupDialog>();
|
||||||
ClassDB::register_class<WindowDialog>();
|
ClassDB::register_class<WindowDialog>();
|
||||||
ClassDB::register_class<AcceptDialog>();
|
ClassDB::register_class<AcceptDialog>();
|
||||||
|
|
Loading…
Reference in New Issue