|
@@ -29,6 +29,7 @@
|
|
|
/**************************************************************************/
|
|
|
|
|
|
#include "rich_text_label.h"
|
|
|
+#include "rich_text_label.compat.inc"
|
|
|
|
|
|
#include "core/input/input_map.h"
|
|
|
#include "core/math/math_defs.h"
|
|
@@ -301,6 +302,14 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font
|
|
|
Item *it_to = (p_line + 1 < (int)p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
|
|
|
for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
|
|
|
switch (it->type) {
|
|
|
+ case ITEM_IMAGE: {
|
|
|
+ ItemImage *img = static_cast<ItemImage *>(it);
|
|
|
+ Size2 img_size = img->size;
|
|
|
+ if (img->size_in_percent) {
|
|
|
+ img_size = _get_image_size(img->image, p_width * img->rq_size.width / 100.f, p_width * img->rq_size.height / 100.f, img->region);
|
|
|
+ l.text_buf->resize_object((uint64_t)it, img_size, img->inline_align, 1);
|
|
|
+ }
|
|
|
+ } break;
|
|
|
case ITEM_TABLE: {
|
|
|
ItemTable *table = static_cast<ItemTable *>(it);
|
|
|
int col_count = table->columns.size();
|
|
@@ -575,7 +584,11 @@ float RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
|
|
|
} break;
|
|
|
case ITEM_IMAGE: {
|
|
|
ItemImage *img = static_cast<ItemImage *>(it);
|
|
|
- l.text_buf->add_object((uint64_t)it, img->size, img->inline_align, 1);
|
|
|
+ Size2 img_size = img->size;
|
|
|
+ if (img->size_in_percent) {
|
|
|
+ img_size = _get_image_size(img->image, p_width * img->rq_size.width / 100.f, p_width * img->rq_size.height / 100.f, img->region);
|
|
|
+ }
|
|
|
+ l.text_buf->add_object((uint64_t)it, img_size, img->inline_align, 1);
|
|
|
txt += String::chr(0xfffc);
|
|
|
l.char_count++;
|
|
|
remaining_characters--;
|
|
@@ -933,7 +946,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
|
|
|
switch (it->type) {
|
|
|
case ITEM_IMAGE: {
|
|
|
ItemImage *img = static_cast<ItemImage *>(it);
|
|
|
- img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color);
|
|
|
+ if (img->pad) {
|
|
|
+ Size2 pad_size = Size2(MIN(rect.size.x, img->image->get_width()), MIN(rect.size.y, img->image->get_height()));
|
|
|
+ Vector2 pad_off = (rect.size - pad_size) / 2;
|
|
|
+ img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off + pad_off, pad_size), false, img->color);
|
|
|
+ } else {
|
|
|
+ img->image->draw_rect(ci, Rect2(p_ofs + rect.position + off, rect.size), false, img->color);
|
|
|
+ }
|
|
|
} break;
|
|
|
case ITEM_TABLE: {
|
|
|
ItemTable *table = static_cast<ItemTable *>(it);
|
|
@@ -2215,11 +2234,15 @@ String RichTextLabel::get_tooltip(const Point2 &p_pos) const {
|
|
|
const_cast<RichTextLabel *>(this)->_find_click(main, p_pos, nullptr, nullptr, &c_item, nullptr, &outside, true);
|
|
|
|
|
|
String description;
|
|
|
- if (c_item && !outside && const_cast<RichTextLabel *>(this)->_find_hint(c_item, &description)) {
|
|
|
- return description;
|
|
|
- } else {
|
|
|
- return Control::get_tooltip(p_pos);
|
|
|
+ if (c_item && !outside) {
|
|
|
+ if (const_cast<RichTextLabel *>(this)->_find_hint(c_item, &description)) {
|
|
|
+ return description;
|
|
|
+ } else if (c_item->type == ITEM_IMAGE && !static_cast<ItemImage *>(c_item)->tooltip.is_empty()) {
|
|
|
+ return static_cast<ItemImage *>(c_item)->tooltip;
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ return Control::get_tooltip(p_pos);
|
|
|
}
|
|
|
|
|
|
void RichTextLabel::_find_frame(Item *p_item, ItemFrame **r_frame, int *r_line) {
|
|
@@ -3121,69 +3144,157 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub
|
|
|
memdelete(p_item);
|
|
|
}
|
|
|
|
|
|
-void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region) {
|
|
|
- _stop_thread();
|
|
|
- MutexLock data_lock(data_mutex);
|
|
|
-
|
|
|
- if (current->type == ITEM_TABLE) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- ERR_FAIL_COND(p_image.is_null());
|
|
|
- ERR_FAIL_COND(p_image->get_width() == 0);
|
|
|
- ERR_FAIL_COND(p_image->get_height() == 0);
|
|
|
- ItemImage *item = memnew(ItemImage);
|
|
|
-
|
|
|
- if (p_region.has_area()) {
|
|
|
- Ref<AtlasTexture> atlas_tex = memnew(AtlasTexture);
|
|
|
- atlas_tex->set_atlas(p_image);
|
|
|
- atlas_tex->set_region(p_region);
|
|
|
- item->image = atlas_tex;
|
|
|
- } else {
|
|
|
- item->image = p_image;
|
|
|
- }
|
|
|
-
|
|
|
- item->color = p_color;
|
|
|
- item->inline_align = p_alignment;
|
|
|
-
|
|
|
+Size2 RichTextLabel::_get_image_size(const Ref<Texture2D> &p_image, int p_width, int p_height, const Rect2 &p_region) {
|
|
|
+ Size2 ret;
|
|
|
if (p_width > 0) {
|
|
|
// custom width
|
|
|
- item->size.width = p_width;
|
|
|
+ ret.width = p_width;
|
|
|
if (p_height > 0) {
|
|
|
// custom height
|
|
|
- item->size.height = p_height;
|
|
|
+ ret.height = p_height;
|
|
|
} else {
|
|
|
// calculate height to keep aspect ratio
|
|
|
if (p_region.has_area()) {
|
|
|
- item->size.height = p_region.get_size().height * p_width / p_region.get_size().width;
|
|
|
+ ret.height = p_region.get_size().height * p_width / p_region.get_size().width;
|
|
|
} else {
|
|
|
- item->size.height = p_image->get_height() * p_width / p_image->get_width();
|
|
|
+ ret.height = p_image->get_height() * p_width / p_image->get_width();
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
if (p_height > 0) {
|
|
|
// custom height
|
|
|
- item->size.height = p_height;
|
|
|
+ ret.height = p_height;
|
|
|
// calculate width to keep aspect ratio
|
|
|
if (p_region.has_area()) {
|
|
|
- item->size.width = p_region.get_size().width * p_height / p_region.get_size().height;
|
|
|
+ ret.width = p_region.get_size().width * p_height / p_region.get_size().height;
|
|
|
} else {
|
|
|
- item->size.width = p_image->get_width() * p_height / p_image->get_height();
|
|
|
+ ret.width = p_image->get_width() * p_height / p_image->get_height();
|
|
|
}
|
|
|
} else {
|
|
|
if (p_region.has_area()) {
|
|
|
// if the image has a region, keep the region size
|
|
|
- item->size = p_region.get_size();
|
|
|
+ ret = p_region.get_size();
|
|
|
} else {
|
|
|
// keep original width and height
|
|
|
- item->size = p_image->get_size();
|
|
|
+ ret = p_image->get_size();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+void RichTextLabel::add_image(const Ref<Texture2D> &p_image, int p_width, int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region, const Variant &p_key, bool p_pad, const String &p_tooltip, bool p_size_in_percent) {
|
|
|
+ _stop_thread();
|
|
|
+ MutexLock data_lock(data_mutex);
|
|
|
+
|
|
|
+ if (current->type == ITEM_TABLE) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ERR_FAIL_COND(p_image.is_null());
|
|
|
+ ERR_FAIL_COND(p_image->get_width() == 0);
|
|
|
+ ERR_FAIL_COND(p_image->get_height() == 0);
|
|
|
+ ItemImage *item = memnew(ItemImage);
|
|
|
+
|
|
|
+ if (p_region.has_area()) {
|
|
|
+ Ref<AtlasTexture> atlas_tex = memnew(AtlasTexture);
|
|
|
+ atlas_tex->set_atlas(p_image);
|
|
|
+ atlas_tex->set_region(p_region);
|
|
|
+ item->image = atlas_tex;
|
|
|
+ } else {
|
|
|
+ item->image = p_image;
|
|
|
+ }
|
|
|
+ item->color = p_color;
|
|
|
+ item->inline_align = p_alignment;
|
|
|
+ item->rq_size = Size2(p_width, p_height);
|
|
|
+ item->region = p_region;
|
|
|
+ item->size = _get_image_size(p_image, p_width, p_height, p_region);
|
|
|
+ item->size_in_percent = p_size_in_percent;
|
|
|
+ item->pad = p_pad;
|
|
|
+ item->key = p_key;
|
|
|
+ item->tooltip = p_tooltip;
|
|
|
|
|
|
_add_item(item, false);
|
|
|
}
|
|
|
|
|
|
+void RichTextLabel::update_image(const Variant &p_key, BitField<ImageUpdateMask> p_mask, const Ref<Texture2D> &p_image, int p_width, int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region, bool p_pad, const String &p_tooltip, bool p_size_in_percent) {
|
|
|
+ _stop_thread();
|
|
|
+ MutexLock data_lock(data_mutex);
|
|
|
+
|
|
|
+ if (p_mask & UPDATE_TEXTURE) {
|
|
|
+ ERR_FAIL_COND(p_image.is_null());
|
|
|
+ ERR_FAIL_COND(p_image->get_width() == 0);
|
|
|
+ ERR_FAIL_COND(p_image->get_height() == 0);
|
|
|
+ }
|
|
|
+
|
|
|
+ bool reshape = false;
|
|
|
+
|
|
|
+ Item *it = main;
|
|
|
+ while (it) {
|
|
|
+ if (it->type == ITEM_IMAGE) {
|
|
|
+ ItemImage *it_img = static_cast<ItemImage *>(it);
|
|
|
+ if (it_img->key == p_key) {
|
|
|
+ ItemImage *item = it_img;
|
|
|
+ if (p_mask & UPDATE_REGION) {
|
|
|
+ item->region = p_region;
|
|
|
+ }
|
|
|
+ if (p_mask & UPDATE_TEXTURE) {
|
|
|
+ if (item->region.has_area()) {
|
|
|
+ Ref<AtlasTexture> atlas_tex = memnew(AtlasTexture);
|
|
|
+ atlas_tex->set_atlas(p_image);
|
|
|
+ atlas_tex->set_region(item->region);
|
|
|
+ item->image = atlas_tex;
|
|
|
+ } else {
|
|
|
+ item->image = p_image;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (p_mask & UPDATE_COLOR) {
|
|
|
+ item->color = p_color;
|
|
|
+ }
|
|
|
+ if (p_mask & UPDATE_TOOLTIP) {
|
|
|
+ item->tooltip = p_tooltip;
|
|
|
+ }
|
|
|
+ if (p_mask & UPDATE_PAD) {
|
|
|
+ item->pad = p_pad;
|
|
|
+ }
|
|
|
+ if (p_mask & UPDATE_ALIGNMENT) {
|
|
|
+ if (item->inline_align != p_alignment) {
|
|
|
+ reshape = true;
|
|
|
+ item->inline_align = p_alignment;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (p_mask & UPDATE_WIDTH_IN_PERCENT) {
|
|
|
+ if (item->size_in_percent != p_size_in_percent) {
|
|
|
+ reshape = true;
|
|
|
+ item->size_in_percent = p_size_in_percent;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (p_mask & UPDATE_SIZE) {
|
|
|
+ if (p_width > 0) {
|
|
|
+ item->rq_size.width = p_width;
|
|
|
+ }
|
|
|
+ if (p_height > 0) {
|
|
|
+ item->rq_size.height = p_height;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((p_mask & UPDATE_SIZE) || (p_mask & UPDATE_REGION) || (p_mask & UPDATE_TEXTURE)) {
|
|
|
+ Size2 new_size = _get_image_size(item->image, item->rq_size.width, item->rq_size.height, item->region);
|
|
|
+ if (item->size != new_size) {
|
|
|
+ reshape = true;
|
|
|
+ item->size = new_size;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ it = _get_next_item(it, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (reshape) {
|
|
|
+ main->first_invalid_line.store(0);
|
|
|
+ }
|
|
|
+ queue_redraw();
|
|
|
+}
|
|
|
+
|
|
|
void RichTextLabel::add_newline() {
|
|
|
_stop_thread();
|
|
|
MutexLock data_lock(data_mutex);
|
|
@@ -4479,6 +4590,9 @@ void RichTextLabel::append_text(const String &p_bbcode) {
|
|
|
|
|
|
int width = 0;
|
|
|
int height = 0;
|
|
|
+ bool pad = false;
|
|
|
+ String tooltip;
|
|
|
+ bool size_in_percent = false;
|
|
|
if (!bbcode_value.is_empty()) {
|
|
|
int sep = bbcode_value.find("x");
|
|
|
if (sep == -1) {
|
|
@@ -4491,15 +4605,31 @@ void RichTextLabel::append_text(const String &p_bbcode) {
|
|
|
OptionMap::Iterator width_option = bbcode_options.find("width");
|
|
|
if (width_option) {
|
|
|
width = width_option->value.to_int();
|
|
|
+ if (width_option->value.ends_with("%")) {
|
|
|
+ size_in_percent = true;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
OptionMap::Iterator height_option = bbcode_options.find("height");
|
|
|
if (height_option) {
|
|
|
height = height_option->value.to_int();
|
|
|
+ if (height_option->value.ends_with("%")) {
|
|
|
+ size_in_percent = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ OptionMap::Iterator tooltip_option = bbcode_options.find("tooltip");
|
|
|
+ if (tooltip_option) {
|
|
|
+ tooltip = tooltip_option->value;
|
|
|
+ }
|
|
|
+
|
|
|
+ OptionMap::Iterator pad_option = bbcode_options.find("pad");
|
|
|
+ if (pad_option) {
|
|
|
+ pad = (pad_option->value == "true");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- add_image(texture, width, height, color, (InlineAlignment)alignment, region);
|
|
|
+ add_image(texture, width, height, color, (InlineAlignment)alignment, region, Variant(), pad, tooltip, size_in_percent);
|
|
|
}
|
|
|
|
|
|
pos = end;
|
|
@@ -5624,7 +5754,8 @@ void RichTextLabel::_bind_methods() {
|
|
|
ClassDB::bind_method(D_METHOD("get_parsed_text"), &RichTextLabel::get_parsed_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("add_image", "image", "width", "height", "color", "inline_align", "region"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2(0, 0, 0, 0)));
|
|
|
+ ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region", "key", "pad", "tooltip", "size_in_percent"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2()), DEFVAL(Variant()), DEFVAL(false), DEFVAL(String()), DEFVAL(false));
|
|
|
+ ClassDB::bind_method(D_METHOD("update_image", "key", "mask", "image", "width", "height", "color", "inline_align", "region", "pad", "tooltip", "size_in_percent"), &RichTextLabel::update_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2()), DEFVAL(false), DEFVAL(String()), DEFVAL(false));
|
|
|
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
|
|
|
ClassDB::bind_method(D_METHOD("remove_paragraph", "paragraph"), &RichTextLabel::remove_paragraph);
|
|
|
ClassDB::bind_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::push_font, DEFVAL(0));
|
|
@@ -5832,6 +5963,15 @@ void RichTextLabel::_bind_methods() {
|
|
|
BIND_ENUM_CONSTANT(MENU_SELECT_ALL);
|
|
|
BIND_ENUM_CONSTANT(MENU_MAX);
|
|
|
|
|
|
+ BIND_BITFIELD_FLAG(UPDATE_TEXTURE);
|
|
|
+ BIND_BITFIELD_FLAG(UPDATE_SIZE);
|
|
|
+ BIND_BITFIELD_FLAG(UPDATE_COLOR);
|
|
|
+ BIND_BITFIELD_FLAG(UPDATE_ALIGNMENT);
|
|
|
+ BIND_BITFIELD_FLAG(UPDATE_REGION);
|
|
|
+ BIND_BITFIELD_FLAG(UPDATE_PAD);
|
|
|
+ BIND_BITFIELD_FLAG(UPDATE_TOOLTIP);
|
|
|
+ BIND_BITFIELD_FLAG(UPDATE_WIDTH_IN_PERCENT);
|
|
|
+
|
|
|
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, RichTextLabel, normal_style, "normal");
|
|
|
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, RichTextLabel, focus_style, "focus");
|
|
|
BIND_THEME_ITEM_EXT(Theme::DATA_TYPE_STYLEBOX, RichTextLabel, progress_bg_style, "background", "ProgressBar");
|