Merge pull request #39113 from nekomatata/bbcode-image-color

Add color option for img bbcode tag in RichTextLabel to tint images
This commit is contained in:
Rémi Verschelde 2020-06-03 13:16:13 +02:00 committed by GitHub
commit 901832e21c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 164 additions and 154 deletions

View File

@ -20,8 +20,10 @@
</argument> </argument>
<argument index="2" name="height" type="int" default="0"> <argument index="2" name="height" type="int" default="0">
</argument> </argument>
<argument index="3" name="color" type="Color" default="Color( 1, 1, 1, 1 )">
</argument>
<description> <description>
Adds an image's opening and closing tags to the tag stack, optionally providing a [code]width[/code] and [code]height[/code] to resize the image. Adds an image's opening and closing tags to the tag stack, optionally providing a [code]width[/code] and [code]height[/code] to resize the image and a [code]color[/code] to tint the image.
If [code]width[/code] or [code]height[/code] is set to 0, the image size will be adjusted in order to keep the original aspect ratio. If [code]width[/code] or [code]height[/code] is set to 0, the image size will be adjusted in order to keep the original aspect ratio.
</description> </description>
</method> </method>

View File

@ -646,7 +646,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
} }
if (p_mode == PROCESS_DRAW && visible) { if (p_mode == PROCESS_DRAW && visible) {
img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size)); img->image->draw_rect(ci, Rect2(p_ofs + Point2(align_ofs + wofs, y + lh - font->get_descent() - img->size.height), img->size), false, img->color);
} }
p_char_count++; p_char_count++;
@ -1443,6 +1443,46 @@ void RichTextLabel::_fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack
} }
} }
Color RichTextLabel::_get_color_from_string(const String &p_color_str, const Color &p_default_color) {
if (p_color_str.begins_with("#")) {
return Color::html(p_color_str);
} else if (p_color_str == "aqua") {
return Color(0, 1, 1);
} else if (p_color_str == "black") {
return Color(0, 0, 0);
} else if (p_color_str == "blue") {
return Color(0, 0, 1);
} else if (p_color_str == "fuchsia") {
return Color(1, 0, 1);
} else if (p_color_str == "gray" || p_color_str == "grey") {
return Color(0.5, 0.5, 0.5);
} else if (p_color_str == "green") {
return Color(0, 0.5, 0);
} else if (p_color_str == "lime") {
return Color(0, 1, 0);
} else if (p_color_str == "maroon") {
return Color(0.5, 0, 0);
} else if (p_color_str == "navy") {
return Color(0, 0, 0.5);
} else if (p_color_str == "olive") {
return Color(0.5, 0.5, 0);
} else if (p_color_str == "purple") {
return Color(0.5, 0, 0.5);
} else if (p_color_str == "red") {
return Color(1, 0, 0);
} else if (p_color_str == "silver") {
return Color(0.75, 0.75, 0.75);
} else if (p_color_str == "teal") {
return Color(0, 0.5, 0.5);
} else if (p_color_str == "white") {
return Color(1, 1, 1);
} else if (p_color_str == "yellow") {
return Color(1, 1, 0);
} else {
return p_default_color;
}
}
bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) {
Item *item = p_item; Item *item = p_item;
@ -1636,7 +1676,7 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub
} }
} }
void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height) { void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color) {
if (current->type == ITEM_TABLE) { if (current->type == ITEM_TABLE) {
return; return;
} }
@ -1645,6 +1685,7 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width,
ItemImage *item = memnew(ItemImage); ItemImage *item = memnew(ItemImage);
item->image = p_image; item->image = p_image;
item->color = p_color;
if (p_width > 0) { if (p_width > 0) {
// custom width // custom width
@ -2034,7 +2075,32 @@ 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);
Vector<String> split_tag_block = tag.split(" ", false); Vector<String> split_tag_block = tag.split(" ", false);
String bbcode = !split_tag_block.empty() ? split_tag_block[0] : "";
// Find optional parameters.
String bbcode_name;
typedef Map<String, String> OptionMap;
OptionMap bbcode_options;
if (!split_tag_block.empty()) {
bbcode_name = split_tag_block[0];
for (int i = 1; i < split_tag_block.size(); i++) {
const String &expr = split_tag_block[i];
int value_pos = expr.find("=");
if (value_pos > -1) {
bbcode_options[expr.substr(0, value_pos)] = expr.substr(value_pos + 1);
}
}
} else {
bbcode_name = tag;
}
// Find main parameter.
String bbcode_value;
int main_value_pos = bbcode_name.find("=");
if (main_value_pos > -1) {
bbcode_value = bbcode_name.substr(main_value_pos + 1);
bbcode_name = bbcode_name.substr(0, main_value_pos);
}
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());
@ -2160,7 +2226,7 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
push_meta(url); push_meta(url);
pos = brk_end + 1; pos = brk_end + 1;
tag_stack.push_front("url"); tag_stack.push_front("url");
} else if (tag == "img") { } else if (bbcode_name == "img") {
int end = p_bbcode.find("[", brk_end); int end = p_bbcode.find("[", brk_end);
if (end == -1) { if (end == -1) {
end = p_bbcode.length(); end = p_bbcode.length();
@ -2170,80 +2236,42 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) {
Ref<Texture2D> texture = ResourceLoader::load(image, "Texture2D"); Ref<Texture2D> texture = ResourceLoader::load(image, "Texture2D");
if (texture.is_valid()) { if (texture.is_valid()) {
add_image(texture); Color color = Color(1.0, 1.0, 1.0);
OptionMap::Element *color_option = bbcode_options.find("color");
if (color_option) {
color = _get_color_from_string(color_option->value(), color);
}
int width = 0;
int height = 0;
if (!bbcode_value.empty()) {
int sep = bbcode_value.find("x");
if (sep == -1) {
width = bbcode_value.to_int();
} else {
width = bbcode_value.substr(0, sep).to_int();
height = bbcode_value.substr(sep + 1).to_int();
}
} else {
OptionMap::Element *width_option = bbcode_options.find("width");
if (width_option) {
width = width_option->value().to_int();
}
OptionMap::Element *height_option = bbcode_options.find("height");
if (height_option) {
height = height_option->value().to_int();
}
}
add_image(texture, width, height, color);
} }
pos = end; pos = end;
tag_stack.push_front(tag); tag_stack.push_front(bbcode_name);
} else if (tag.begins_with("img=")) {
int width = 0;
int height = 0;
String params = tag.substr(4, tag.length());
int sep = params.find("x");
if (sep == -1) {
width = params.to_int();
} else {
width = params.substr(0, sep).to_int();
height = params.substr(sep + 1, params.length()).to_int();
}
int end = p_bbcode.find("[", brk_end);
if (end == -1) {
end = p_bbcode.length();
}
String image = p_bbcode.substr(brk_end + 1, end - brk_end - 1);
Ref<Texture2D> texture = ResourceLoader::load(image, "Texture");
if (texture.is_valid()) {
add_image(texture, width, height);
}
pos = end;
tag_stack.push_front("img");
} else if (tag.begins_with("color=")) { } else if (tag.begins_with("color=")) {
String col = tag.substr(6, tag.length()); String color_str = tag.substr(6, tag.length());
Color color; Color color = _get_color_from_string(color_str, base_color);
if (col.begins_with("#")) {
color = Color::html(col);
} else if (col == "aqua") {
color = Color(0, 1, 1);
} else if (col == "black") {
color = Color(0, 0, 0);
} else if (col == "blue") {
color = Color(0, 0, 1);
} else if (col == "fuchsia") {
color = Color(1, 0, 1);
} else if (col == "gray" || col == "grey") {
color = Color(0.5, 0.5, 0.5);
} else if (col == "green") {
color = Color(0, 0.5, 0);
} else if (col == "lime") {
color = Color(0, 1, 0);
} else if (col == "maroon") {
color = Color(0.5, 0, 0);
} else if (col == "navy") {
color = Color(0, 0, 0.5);
} else if (col == "olive") {
color = Color(0.5, 0.5, 0);
} else if (col == "purple") {
color = Color(0.5, 0, 0.5);
} else if (col == "red") {
color = Color(1, 0, 0);
} else if (col == "silver") {
color = Color(0.75, 0.75, 0.75);
} else if (col == "teal") {
color = Color(0, 0.5, 0.5);
} else if (col == "white") {
color = Color(1, 1, 1);
} else if (col == "yellow") {
color = Color(1, 1, 0);
} else {
color = base_color;
}
push_color(color); push_color(color);
pos = brk_end + 1; pos = brk_end + 1;
tag_stack.push_front("color"); tag_stack.push_front("color");
@ -2261,113 +2289,90 @@ 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 if (bbcode == "fade") { } else if (bbcode_name == "fade") {
int startIndex = 0; int start_index = 0;
int length = 10; OptionMap::Element *start_option = bbcode_options.find("start");
if (start_option) {
if (split_tag_block.size() > 1) { start_index = start_option->value().to_int();
split_tag_block.remove(0);
for (int i = 0; i < split_tag_block.size(); i++) {
String expr = split_tag_block[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); int length = 10;
OptionMap::Element *length_option = bbcode_options.find("length");
if (length_option) {
length = length_option->value().to_int();
}
push_fade(start_index, length);
pos = brk_end + 1; pos = brk_end + 1;
tag_stack.push_front("fade"); tag_stack.push_front("fade");
} else if (bbcode == "shake") { } else if (bbcode_name == "shake") {
int strength = 5; int strength = 5;
float rate = 20.0f; OptionMap::Element *strength_option = bbcode_options.find("level");
if (strength_option) {
strength = strength_option->value().to_int();
}
if (split_tag_block.size() > 1) { float rate = 20.0f;
split_tag_block.remove(0); OptionMap::Element *rate_option = bbcode_options.find("rate");
for (int i = 0; i < split_tag_block.size(); i++) { if (rate_option) {
String expr = split_tag_block[i]; rate = rate_option->value().to_float();
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); push_shake(strength, rate);
pos = brk_end + 1; pos = brk_end + 1;
tag_stack.push_front("shake"); tag_stack.push_front("shake");
set_process_internal(true); set_process_internal(true);
} else if (bbcode == "wave") { } else if (bbcode_name == "wave") {
float amplitude = 20.0f; float amplitude = 20.0f;
float period = 5.0f; OptionMap::Element *amplitude_option = bbcode_options.find("amp");
if (amplitude_option) {
amplitude = amplitude_option->value().to_float();
}
if (split_tag_block.size() > 1) { float period = 5.0f;
split_tag_block.remove(0); OptionMap::Element *period_option = bbcode_options.find("freq");
for (int i = 0; i < split_tag_block.size(); i++) { if (period_option) {
String expr = split_tag_block[i]; period = period_option->value().to_float();
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); push_wave(period, amplitude);
pos = brk_end + 1; pos = brk_end + 1;
tag_stack.push_front("wave"); tag_stack.push_front("wave");
set_process_internal(true); set_process_internal(true);
} else if (bbcode == "tornado") { } else if (bbcode_name == "tornado") {
float radius = 10.0f; float radius = 10.0f;
float frequency = 1.0f; OptionMap::Element *radius_option = bbcode_options.find("radius");
if (radius_option) {
radius = radius_option->value().to_float();
}
if (split_tag_block.size() > 1) { float frequency = 1.0f;
split_tag_block.remove(0); OptionMap::Element *frequency_option = bbcode_options.find("freq");
for (int i = 0; i < split_tag_block.size(); i++) { if (frequency_option) {
String expr = split_tag_block[i]; frequency = frequency_option->value().to_float();
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); push_tornado(frequency, radius);
pos = brk_end + 1; pos = brk_end + 1;
tag_stack.push_front("tornado"); tag_stack.push_front("tornado");
set_process_internal(true); set_process_internal(true);
} else if (bbcode == "rainbow") { } else if (bbcode_name == "rainbow") {
float saturation = 0.8f; float saturation = 0.8f;
float value = 0.8f; OptionMap::Element *saturation_option = bbcode_options.find("sat");
float frequency = 1.0f; if (saturation_option) {
saturation = saturation_option->value().to_float();
}
if (split_tag_block.size() > 1) { float value = 0.8f;
split_tag_block.remove(0); OptionMap::Element *value_option = bbcode_options.find("val");
for (int i = 0; i < split_tag_block.size(); i++) { if (value_option) {
String expr = split_tag_block[i]; value = value_option->value().to_float();
if (expr.begins_with("sat=")) { }
String sat_str = expr.substr(4, expr.length());
saturation = sat_str.to_float(); float frequency = 1.0f;
} else if (expr.begins_with("val=")) { OptionMap::Element *frequency_option = bbcode_options.find("freq");
String val_str = expr.substr(4, expr.length()); if (frequency_option) {
value = val_str.to_float(); frequency = frequency_option->value().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); push_rainbow(saturation, value, frequency);
@ -2634,7 +2639,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text); ClassDB::bind_method(D_METHOD("get_text"), &RichTextLabel::get_text);
ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text); ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text);
ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text); ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text);
ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)));
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line); ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);
ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font); ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);

View File

@ -149,6 +149,7 @@ private:
struct ItemImage : public Item { struct ItemImage : public Item {
Ref<Texture2D> image; Ref<Texture2D> image;
Size2 size; Size2 size;
Color color;
ItemImage() { type = ITEM_IMAGE; } ItemImage() { type = ITEM_IMAGE; }
}; };
@ -381,6 +382,8 @@ private:
bool _find_by_type(Item *p_item, ItemType p_type); bool _find_by_type(Item *p_item, ItemType p_type);
void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack); void _fetch_item_fx_stack(Item *p_item, Vector<ItemFX *> &r_stack);
static Color _get_color_from_string(const String &p_color_str, const Color &p_default_color);
void _update_scroll(); void _update_scroll();
void _update_fx(ItemFrame *p_frame, float p_delta_time); void _update_fx(ItemFrame *p_frame, float p_delta_time);
void _scroll_changed(double); void _scroll_changed(double);
@ -406,7 +409,7 @@ protected:
public: public:
String get_text(); String get_text();
void add_text(const String &p_text); void add_text(const String &p_text);
void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0); void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0));
void add_newline(); void add_newline();
bool remove_line(const int p_line); bool remove_line(const int p_line);
void push_font(const Ref<Font> &p_font); void push_font(const Ref<Font> &p_font);