summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/code_edit.cpp23
-rw-r--r--scene/gui/code_edit.h2
-rw-r--r--scene/gui/container.cpp5
-rw-r--r--scene/gui/label.cpp102
-rw-r--r--scene/gui/menu_button.cpp8
-rw-r--r--scene/gui/rich_text_effect.cpp8
-rw-r--r--scene/gui/rich_text_effect.h14
-rw-r--r--scene/gui/rich_text_label.cpp161
-rw-r--r--scene/gui/rich_text_label.h3
-rw-r--r--scene/gui/spin_box.cpp31
-rw-r--r--scene/gui/spin_box.h6
-rw-r--r--scene/gui/tab_container.cpp2
12 files changed, 245 insertions, 120 deletions
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index e7769f9372..4dfd6902e6 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -467,7 +467,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
/* MISC */
- if (k->is_action("ui_cancel", true)) {
+ if (!code_hint.is_empty() && k->is_action("ui_cancel", true)) {
set_code_hint("");
accept_event();
return;
@@ -1725,14 +1725,17 @@ bool CodeEdit::is_code_completion_enabled() const {
void CodeEdit::set_code_completion_prefixes(const TypedArray<String> &p_prefixes) {
code_completion_prefixes.clear();
for (int i = 0; i < p_prefixes.size(); i++) {
- code_completion_prefixes.insert(p_prefixes[i]);
+ const String prefix = p_prefixes[i];
+
+ ERR_CONTINUE_MSG(prefix.is_empty(), "Code completion prefix cannot be empty.");
+ code_completion_prefixes.insert(prefix[0]);
}
}
TypedArray<String> CodeEdit::get_code_completion_prefixes() const {
TypedArray<String> prefixes;
- for (Set<String>::Element *E = code_completion_prefixes.front(); E; E = E->next()) {
- prefixes.push_back(E->get());
+ for (const Set<char32_t>::Element *E = code_completion_prefixes.front(); E; E = E->next()) {
+ prefixes.push_back(String::chr(E->get()));
}
return prefixes;
}
@@ -1795,9 +1798,9 @@ void CodeEdit::request_code_completion(bool p_force) {
String line = get_line(get_caret_line());
int ofs = CLAMP(get_caret_column(), 0, line.length());
- if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || _is_char(line[ofs - 1]) || code_completion_prefixes.has(String::chr(line[ofs - 1])))) {
+ if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || _is_char(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) {
emit_signal(SNAME("request_code_completion"));
- } else if (ofs > 1 && line[ofs - 1] == ' ' && code_completion_prefixes.has(String::chr(line[ofs - 2]))) {
+ } else if (ofs > 1 && line[ofs - 1] == ' ' && code_completion_prefixes.has(line[ofs - 2])) {
emit_signal(SNAME("request_code_completion"));
}
}
@@ -1969,7 +1972,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
end_complex_operation();
cancel_code_completion();
- if (code_completion_prefixes.has(String::chr(last_completion_char))) {
+ if (code_completion_prefixes.has(last_completion_char)) {
request_code_completion();
}
}
@@ -2764,7 +2767,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
bool prev_is_word = false;
/* Cancel if we are at the close of a string. */
- if (in_string == -1 && first_quote_col == cofs - 1) {
+ if (caret_column > 0 && in_string == -1 && first_quote_col == cofs - 1) {
cancel_code_completion();
return;
/* In a string, therefore we are trying to complete the string text. */
@@ -2790,9 +2793,9 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
/* If all else fails, check for a prefix. */
/* Single space between caret and prefix is okay. */
bool prev_is_prefix = false;
- if (cofs > 0 && code_completion_prefixes.has(String::chr(line[cofs - 1]))) {
+ if (cofs > 0 && code_completion_prefixes.has(line[cofs - 1])) {
prev_is_prefix = true;
- } else if (cofs > 1 && line[cofs - 1] == ' ' && code_completion_prefixes.has(String::chr(line[cofs - 2]))) {
+ } else if (cofs > 1 && line[cofs - 1] == ' ' && code_completion_prefixes.has(line[cofs - 2])) {
prev_is_prefix = true;
}
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index 740548d559..d8eccb7d32 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -214,7 +214,7 @@ private:
int code_completion_longest_line = 0;
Rect2i code_completion_rect;
- Set<String> code_completion_prefixes;
+ Set<char32_t> code_completion_prefixes;
List<ScriptCodeCompletionOption> code_completion_option_submitted;
List<ScriptCodeCompletionOption> code_completion_option_sources;
String code_completion_base;
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index c97434f69b..11941529cd 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -96,17 +96,18 @@ void Container::fit_child_in_rect(Control *p_child, const Rect2 &p_rect) {
ERR_FAIL_COND(!p_child);
ERR_FAIL_COND(p_child->get_parent() != this);
+ bool rtl = is_layout_rtl();
Size2 minsize = p_child->get_combined_minimum_size();
Rect2 r = p_rect;
if (!(p_child->get_h_size_flags() & SIZE_FILL)) {
r.size.x = minsize.width;
if (p_child->get_h_size_flags() & SIZE_SHRINK_END) {
- r.position.x += p_rect.size.width - minsize.width;
+ r.position.x += rtl ? 0 : (p_rect.size.width - minsize.width);
} else if (p_child->get_h_size_flags() & SIZE_SHRINK_CENTER) {
r.position.x += Math::floor((p_rect.size.x - minsize.width) / 2);
} else {
- r.position.x += 0;
+ r.position.x += rtl ? (p_rect.size.width - minsize.width) : 0;
}
}
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 5600816b2d..18cde25e55 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -92,8 +92,12 @@ void Label::_shape() {
const Ref<Font> &font = get_theme_font(SNAME("font"));
int font_size = get_theme_font_size(SNAME("font_size"));
ERR_FAIL_COND(font.is_null());
- TS->shaped_text_add_string(text_rid, (uppercase) ? xl_text.to_upper() : xl_text, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
- TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, xl_text));
+ String text = (uppercase) ? xl_text.to_upper() : xl_text;
+ if (visible_chars >= 0) {
+ text = text.substr(0, visible_chars);
+ }
+ TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text));
dirty = false;
lines_dirty = true;
}
@@ -258,11 +262,18 @@ void Label::_notification(int p_what) {
return; // Nothing new.
}
xl_text = new_text;
+ if (percent_visible < 1) {
+ visible_chars = get_total_character_count() * percent_visible;
+ }
dirty = true;
update();
}
+ if (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) {
+ update();
+ }
+
if (p_what == NOTIFICATION_DRAW) {
if (clip) {
RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
@@ -286,6 +297,7 @@ void Label::_notification(int p_what) {
int outline_size = get_theme_constant(SNAME("outline_size"));
int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
bool rtl = TS->shaped_text_get_direction(text_rid);
+ bool rtl_layout = is_layout_rtl();
style->draw(ci, Rect2(Point2(0, 0), get_size()));
@@ -342,24 +354,6 @@ void Label::_notification(int p_what) {
}
}
- int visible_glyphs = -1;
- int glyhps_drawn = 0;
- if (percent_visible < 1) {
- int total_glyphs = 0;
- for (int i = lines_skipped; i < last_line; i++) {
- const Vector<TextServer::Glyph> visual = TS->shaped_text_get_glyphs(lines_rid[i]);
- const TextServer::Glyph *glyphs = visual.ptr();
- int gl_size = visual.size();
- for (int j = 0; j < gl_size; j++) {
- if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- total_glyphs++;
- }
- }
- }
-
- visible_glyphs = MIN(total_glyphs, visible_chars);
- }
-
Vector2 ofs;
ofs.y = style->get_offset().y + vbegin;
for (int i = lines_skipped; i < last_line; i++) {
@@ -375,13 +369,21 @@ void Label::_notification(int p_what) {
}
break;
case ALIGN_LEFT: {
- ofs.x = style->get_offset().x;
+ if (rtl_layout) {
+ ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
+ } else {
+ ofs.x = style->get_offset().x;
+ }
} break;
case ALIGN_CENTER: {
ofs.x = int(size.width - line_size.width) / 2;
} break;
case ALIGN_RIGHT: {
- ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
+ if (rtl_layout) {
+ ofs.x = style->get_offset().x;
+ } else {
+ ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
+ }
} break;
}
@@ -407,14 +409,6 @@ void Label::_notification(int p_what) {
// Draw main text.
for (int j = 0; j < gl_size; j++) {
for (int k = 0; k < glyphs[j].repeat; k++) {
- if (visible_glyphs != -1) {
- if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- if (glyhps_drawn >= visible_glyphs) {
- return;
- }
- }
- }
-
// Trim when necessary.
if (trim_data.trim_pos >= 0) {
if (rtl) {
@@ -431,7 +425,6 @@ void Label::_notification(int p_what) {
// Draw glyph outlines and shadow.
draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
offset.x += glyphs[j].advance;
- glyhps_drawn++;
}
}
// Draw LTR ellipsis string when necessary.
@@ -462,14 +455,6 @@ void Label::_notification(int p_what) {
// Draw main text.
for (int j = 0; j < gl_size; j++) {
for (int k = 0; k < glyphs[j].repeat; k++) {
- if (visible_glyphs != -1) {
- if ((glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- if (glyhps_drawn >= visible_glyphs) {
- return;
- }
- }
- }
-
// Trim when necessary.
if (trim_data.trim_pos >= 0) {
if (rtl) {
@@ -486,7 +471,6 @@ void Label::_notification(int p_what) {
// Draw glyph outlines and shadow.
draw_glyph(glyphs[j], ci, font_color, ofs);
ofs.x += glyphs[j].advance;
- glyhps_drawn++;
}
}
// Draw LTR ellipsis string when necessary.
@@ -709,16 +693,16 @@ String Label::get_text() const {
}
void Label::set_visible_characters(int p_amount) {
- visible_chars = p_amount;
- if (get_total_character_count() > 0) {
- percent_visible = (float)p_amount / (float)get_total_character_count();
- } else {
- percent_visible = 1.0;
- }
- if (p_amount == -1) {
- lines_dirty = true;
+ if (visible_chars != p_amount) {
+ visible_chars = p_amount;
+ if (get_total_character_count() > 0) {
+ percent_visible = (float)p_amount / (float)get_total_character_count();
+ } else {
+ percent_visible = 1.0;
+ }
+ dirty = true;
+ update();
}
- update();
}
int Label::get_visible_characters() const {
@@ -726,15 +710,17 @@ int Label::get_visible_characters() const {
}
void Label::set_percent_visible(float p_percent) {
- if (p_percent < 0 || p_percent >= 1) {
- visible_chars = -1;
- percent_visible = 1;
- lines_dirty = true;
- } else {
- visible_chars = get_total_character_count() * p_percent;
- percent_visible = p_percent;
+ if (percent_visible != p_percent) {
+ if (p_percent < 0 || p_percent >= 1) {
+ visible_chars = -1;
+ percent_visible = 1;
+ } else {
+ visible_chars = get_total_character_count() * p_percent;
+ percent_visible = p_percent;
+ }
+ dirty = true;
+ update();
}
- update();
}
float Label::get_percent_visible() const {
@@ -889,7 +875,7 @@ void Label::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1", PROPERTY_USAGE_EDITOR), "set_visible_characters", "get_visible_characters");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible");
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 0cc53a7832..ceb2092e3a 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -89,13 +89,15 @@ void MenuButton::pressed() {
emit_signal(SNAME("about_to_popup"));
Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
+ popup->set_size(Size2(size.width, 0));
Point2 gp = get_screen_position();
gp.y += size.y;
-
+ if (is_layout_rtl()) {
+ gp.x += size.width - popup->get_size().width;
+ }
popup->set_position(gp);
-
- popup->set_size(Size2(size.width, 0));
popup->set_parent_rect(Rect2(Point2(gp - popup->get_position()), size));
+
popup->take_mouse_focus();
popup->popup();
}
diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp
index 236d106af8..076fa132c0 100644
--- a/scene/gui/rich_text_effect.cpp
+++ b/scene/gui/rich_text_effect.cpp
@@ -90,6 +90,12 @@ void CharFXTransform::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_glyph_index"), &CharFXTransform::get_glyph_index);
ClassDB::bind_method(D_METHOD("set_glyph_index", "glyph_index"), &CharFXTransform::set_glyph_index);
+ ClassDB::bind_method(D_METHOD("get_glyph_count"), &CharFXTransform::get_glyph_count);
+ ClassDB::bind_method(D_METHOD("set_glyph_count", "glyph_count"), &CharFXTransform::set_glyph_count);
+
+ ClassDB::bind_method(D_METHOD("get_glyph_flags"), &CharFXTransform::get_glyph_flags);
+ ClassDB::bind_method(D_METHOD("set_glyph_flags", "glyph_flags"), &CharFXTransform::set_glyph_flags);
+
ClassDB::bind_method(D_METHOD("get_font"), &CharFXTransform::get_font);
ClassDB::bind_method(D_METHOD("set_font", "font"), &CharFXTransform::set_font);
@@ -101,5 +107,7 @@ void CharFXTransform::_bind_methods() {
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, "glyph_index"), "set_glyph_index", "get_glyph_index");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_count"), "set_glyph_count", "get_glyph_count");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "glyph_flags"), "set_glyph_flags", "get_glyph_flags");
ADD_PROPERTY(PropertyInfo(Variant::RID, "font"), "set_font", "get_font");
}
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index f5506542bb..5681f9b193 100644
--- a/scene/gui/rich_text_effect.h
+++ b/scene/gui/rich_text_effect.h
@@ -50,6 +50,8 @@ public:
double elapsed_time = 0.0f;
Dictionary environment;
uint32_t glyph_index = 0;
+ uint16_t glyph_flags = 0;
+ uint8_t glyph_count = 0;
RID font;
CharFXTransform();
@@ -57,19 +59,31 @@ public:
Vector2i get_range() { return range; }
void set_range(const Vector2i &p_range) { range = p_range; }
+
double get_elapsed_time() { return elapsed_time; }
void set_elapsed_time(double p_elapsed_time) { elapsed_time = p_elapsed_time; }
+
bool is_visible() { return visibility; }
void set_visibility(bool p_visibility) { visibility = p_visibility; }
+
bool is_outline() { return outline; }
void set_outline(bool p_outline) { outline = p_outline; }
+
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; }
uint32_t get_glyph_index() const { return glyph_index; };
void set_glyph_index(uint32_t p_glyph_index) { glyph_index = p_glyph_index; };
+
+ uint16_t get_glyph_flags() const { return glyph_index; };
+ void set_glyph_flags(uint16_t p_glyph_flags) { glyph_flags = p_glyph_flags; };
+
+ uint8_t get_glyph_count() const { return glyph_count; };
+ void set_glyph_count(uint8_t p_glyph_count) { glyph_count = p_glyph_count; };
+
RID get_font() const { return font; };
void set_font(RID p_font) { font = p_font; };
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index d4788775c5..eb88570663 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -399,8 +399,9 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
// Shape current paragraph.
String text;
Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ int remaining_characters = visible_characters - l.char_offset;
for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
- if (visible_characters >= 0 && l.char_offset + l.char_count > visible_characters) {
+ if (visible_characters >= 0 && remaining_characters <= 0) {
break;
}
switch (it->type) {
@@ -427,7 +428,8 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
l.text_buf->add_string("\n", font, font_size, Dictionary(), "");
text += "\n";
- l.char_count += 1;
+ l.char_count++;
+ remaining_characters--;
} break;
case ITEM_TEXT: {
ItemText *t = (ItemText *)it;
@@ -442,9 +444,10 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
Dictionary font_ftr = _find_font_features(it);
String lang = _find_language(it);
String tx = t->text;
- if (visible_characters >= 0 && l.char_offset + l.char_count + tx.length() > visible_characters) {
- tx = tx.substr(0, l.char_offset + l.char_count + tx.length() - visible_characters);
+ if (visible_characters >= 0 && remaining_characters >= 0) {
+ tx = tx.substr(0, remaining_characters);
}
+ remaining_characters -= tx.length();
l.text_buf->add_string(tx, font, font_size, font_ftr, lang);
text += tx;
@@ -454,7 +457,8 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
ItemImage *img = (ItemImage *)it;
l.text_buf->add_object((uint64_t)it, img->image->get_size(), img->inline_align, 1);
text += String::chr(0xfffc);
- l.char_count += 1;
+ l.char_count++;
+ remaining_characters--;
} break;
case ITEM_TABLE: {
ItemTable *table = static_cast<ItemTable *>(it);
@@ -483,6 +487,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
int cell_ch = (char_offset - (l.char_offset + l.char_count));
l.char_count += cell_ch;
t_char_count += cell_ch;
+ remaining_characters -= cell_ch;
table->columns.write[column].min_width = MAX(table->columns[column].min_width, ceil(frame->lines[i].text_buf->get_size().x));
table->columns.write[column].max_width = MAX(table->columns[column].max_width, ceil(frame->lines[i].text_buf->get_non_wraped_size().x));
@@ -847,6 +852,21 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
RID frid = glyphs[i].font_rid;
uint32_t gl = glyphs[i].index;
+ uint16_t gl_fl = glyphs[i].flags;
+ uint8_t gl_cn = glyphs[i].count;
+ bool cprev = false;
+ if (gl_cn == 0) { // Parts of the same cluster, always connected.
+ cprev = true;
+ }
+ if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
+ if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
+ cprev = true;
+ }
+ } else {
+ if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
+ cprev = true;
+ }
+ }
//Apply fx.
float faded_visibility = 1.0f;
@@ -875,6 +895,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
charfx->outline = true;
charfx->font = frid;
charfx->glyph_index = gl;
+ charfx->glyph_flags = gl_fl;
+ charfx->glyph_count = gl_cn;
charfx->offset = fx_offset;
charfx->color = font_color;
@@ -890,25 +912,34 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
} else if (item_fx->type == ITEM_SHAKE) {
ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
- uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
- uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
- uint64_t max_rand = 2147483647;
- double current_offset = Math::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;
+ if (!cprev) {
+ uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
+ uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
+ uint64_t max_rand = 2147483647;
+ double current_offset = Math::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;
+ item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
+ }
+ fx_offset += item_shake->prev_off;
} else if (item_fx->type == ITEM_WAVE) {
ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
- double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f);
- fx_offset += Point2(0, 1) * value;
+ if (!cprev) {
+ double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_wave->amplitude / 10.0f);
+ item_wave->prev_off = Point2(0, 1) * value;
+ }
+ fx_offset += item_wave->prev_off;
} else if (item_fx->type == ITEM_TORNADO) {
ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
- double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
- double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
- fx_offset += Point2(torn_x, torn_y);
+ if (!cprev) {
+ double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
+ double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + gloff.x) / 50)) * (item_tornado->radius);
+ item_tornado->prev_off = Point2(torn_x, torn_y);
+ }
+ fx_offset += item_tornado->prev_off;
} else if (item_fx->type == ITEM_RAINBOW) {
ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
@@ -999,6 +1030,21 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Point2 fx_offset = Vector2(glyphs[i].x_off, glyphs[i].y_off);
RID frid = glyphs[i].font_rid;
uint32_t gl = glyphs[i].index;
+ uint16_t gl_fl = glyphs[i].flags;
+ uint8_t gl_cn = glyphs[i].count;
+ bool cprev = false;
+ if (gl_cn == 0) { // Parts of the same grapheme cluster, always connected.
+ cprev = true;
+ }
+ if (gl_fl & TextServer::GRAPHEME_IS_RTL) { // Check if previous grapheme cluster is connected.
+ if (i > 0 && (glyphs[i - 1].flags & TextServer::GRAPHEME_IS_CONNECTED)) {
+ cprev = true;
+ }
+ } else {
+ if (glyphs[i].flags & TextServer::GRAPHEME_IS_CONNECTED) {
+ cprev = true;
+ }
+ }
//Apply fx.
float faded_visibility = 1.0f;
@@ -1027,6 +1073,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
charfx->outline = false;
charfx->font = frid;
charfx->glyph_index = gl;
+ charfx->glyph_flags = gl_fl;
+ charfx->glyph_count = gl_cn;
charfx->offset = fx_offset;
charfx->color = font_color;
@@ -1042,25 +1090,34 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
} else if (item_fx->type == ITEM_SHAKE) {
ItemShake *item_shake = static_cast<ItemShake *>(item_fx);
- uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
- uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
- uint64_t max_rand = 2147483647;
- double current_offset = Math::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;
+ if (!cprev) {
+ uint64_t char_current_rand = item_shake->offset_random(glyphs[i].start);
+ uint64_t char_previous_rand = item_shake->offset_previous_random(glyphs[i].start);
+ uint64_t max_rand = 2147483647;
+ double current_offset = Math::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;
+ item_shake->prev_off = Point2(Math::lerp(Math::sin(previous_offset), Math::sin(current_offset), n_time), Math::lerp(Math::cos(previous_offset), Math::cos(current_offset), n_time)) * (float)item_shake->strength / 10.0f;
+ }
+ fx_offset += item_shake->prev_off;
} else if (item_fx->type == ITEM_WAVE) {
ItemWave *item_wave = static_cast<ItemWave *>(item_fx);
- double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f);
- fx_offset += Point2(0, 1) * value;
+ if (!cprev) {
+ double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_wave->amplitude / 10.0f);
+ item_wave->prev_off = Point2(0, 1) * value;
+ }
+ fx_offset += item_wave->prev_off;
} else if (item_fx->type == ITEM_TORNADO) {
ItemTornado *item_tornado = static_cast<ItemTornado *>(item_fx);
- double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
- double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
- fx_offset += Point2(torn_x, torn_y);
+ if (!cprev) {
+ double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
+ double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + off.x) / 50)) * (item_tornado->radius);
+ item_tornado->prev_off = Point2(torn_x, torn_y);
+ }
+ fx_offset += item_tornado->prev_off;
} else if (item_fx->type == ITEM_RAINBOW) {
ItemRainbow *item_rainbow = static_cast<ItemRainbow *>(item_fx);
@@ -3855,7 +3912,12 @@ String RichTextLabel::get_parsed_text() const {
String text = "";
Item *it = main;
while (it) {
- if (it->type == ITEM_TEXT) {
+ if (it->type == ITEM_DROPCAP) {
+ const ItemDropcap *dc = (ItemDropcap *)it;
+ if (dc != nullptr) {
+ text += dc->text;
+ }
+ } else if (it->type == ITEM_TEXT) {
ItemText *t = static_cast<ItemText *>(it);
text += t->text;
} else if (it->type == ITEM_NEWLINE) {
@@ -3926,7 +3988,6 @@ void RichTextLabel::set_percent_visible(float p_percent) {
if (p_percent < 0 || p_percent >= 1) {
visible_characters = -1;
percent_visible = 1;
-
} else {
visible_characters = get_total_character_count() * p_percent;
percent_visible = p_percent;
@@ -4160,16 +4221,20 @@ void RichTextLabel::_bind_methods() {
}
void RichTextLabel::set_visible_characters(int p_visible) {
- visible_characters = p_visible;
- if (p_visible == -1) {
- percent_visible = 1;
- } else {
- int total_char_count = get_total_character_count();
- if (total_char_count > 0) {
- percent_visible = (float)p_visible / (float)total_char_count;
+ if (visible_characters != p_visible) {
+ visible_characters = p_visible;
+ if (p_visible == -1) {
+ percent_visible = 1;
+ } else {
+ int total_char_count = get_total_character_count();
+ if (total_char_count > 0) {
+ percent_visible = (float)p_visible / (float)total_char_count;
+ }
}
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ update();
}
- update();
}
int RichTextLabel::get_visible_characters() const {
@@ -4177,9 +4242,19 @@ int RichTextLabel::get_visible_characters() const {
}
int RichTextLabel::get_total_character_count() const {
+ // Note: Do not use line buffer "char_count", it includes only visible characters.
int tc = 0;
- for (int i = 0; i < current_frame->lines.size(); i++) {
- tc += current_frame->lines[i].char_count;
+ Item *it = main;
+ while (it) {
+ if (it->type == ITEM_TEXT) {
+ ItemText *t = static_cast<ItemText *>(it);
+ tc += t->text.length();
+ } else if (it->type == ITEM_NEWLINE) {
+ tc++;
+ } else if (it->type == ITEM_IMAGE) {
+ tc++;
+ }
+ it = _get_next_item(it, true);
}
return tc;
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 806f684b67..94f02a3989 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -267,6 +267,7 @@ private:
float rate = 0.0f;
uint64_t _current_rng = 0;
uint64_t _previous_rng = 0;
+ Vector2 prev_off;
ItemShake() { type = ITEM_SHAKE; }
@@ -289,6 +290,7 @@ private:
struct ItemWave : public ItemFX {
float frequency = 1.0f;
float amplitude = 1.0f;
+ Vector2 prev_off;
ItemWave() { type = ITEM_WAVE; }
};
@@ -296,6 +298,7 @@ private:
struct ItemTornado : public ItemFX {
float radius = 1.0f;
float frequency = 1.0f;
+ Vector2 prev_off;
ItemTornado() { type = ITEM_TORNADO; }
};
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 1074d0d8a0..0aec017649 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -68,6 +68,15 @@ void SpinBox::_text_submitted(const String &p_string) {
_value_changed(0);
}
+void SpinBox::_text_changed(const String &p_string) {
+ int cursor_pos = line_edit->get_caret_column();
+
+ _text_submitted(p_string);
+
+ // Line edit 'set_text' method resets the cursor position so we need to undo that.
+ line_edit->set_caret_column(cursor_pos);
+}
+
LineEdit *SpinBox::get_line_edit() {
return line_edit;
}
@@ -244,6 +253,24 @@ String SpinBox::get_prefix() const {
return prefix;
}
+void SpinBox::set_update_on_text_changed(bool p_update) {
+ if (update_on_text_changed == p_update) {
+ return;
+ }
+
+ update_on_text_changed = p_update;
+
+ if (p_update) {
+ line_edit->connect("text_changed", callable_mp(this, &SpinBox::_text_changed), Vector<Variant>(), CONNECT_DEFERRED);
+ } else {
+ line_edit->disconnect("text_changed", callable_mp(this, &SpinBox::_text_changed));
+ }
+}
+
+bool SpinBox::get_update_on_text_changed() const {
+ return update_on_text_changed;
+}
+
void SpinBox::set_editable(bool p_editable) {
line_edit->set_editable(p_editable);
}
@@ -267,11 +294,14 @@ void SpinBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_prefix"), &SpinBox::get_prefix);
ClassDB::bind_method(D_METHOD("set_editable", "editable"), &SpinBox::set_editable);
ClassDB::bind_method(D_METHOD("is_editable"), &SpinBox::is_editable);
+ ClassDB::bind_method(D_METHOD("set_update_on_text_changed"), &SpinBox::set_update_on_text_changed);
+ ClassDB::bind_method(D_METHOD("get_update_on_text_changed"), &SpinBox::get_update_on_text_changed);
ClassDB::bind_method(D_METHOD("apply"), &SpinBox::apply);
ClassDB::bind_method(D_METHOD("get_line_edit"), &SpinBox::get_line_edit);
ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "update_on_text_changed"), "set_update_on_text_changed", "get_update_on_text_changed");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "prefix"), "set_prefix", "get_prefix");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "suffix"), "set_suffix", "get_suffix");
}
@@ -284,7 +314,6 @@ SpinBox::SpinBox() {
line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
line_edit->set_align(LineEdit::ALIGN_LEFT);
- //connect("value_changed",this,"_value_changed");
line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), Vector<Variant>(), CONNECT_DEFERRED);
line_edit->connect("focus_exited", callable_mp(this, &SpinBox::_line_edit_focus_exit), Vector<Variant>(), CONNECT_DEFERRED);
line_edit->connect("gui_input", callable_mp(this, &SpinBox::_line_edit_input));
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index 9ec3885f1f..9828b894da 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -40,6 +40,7 @@ class SpinBox : public Range {
LineEdit *line_edit;
int last_w = 0;
+ bool update_on_text_changed = false;
Timer *range_click_timer;
void _range_click_timeout();
@@ -47,6 +48,8 @@ class SpinBox : public Range {
void _text_submitted(const String &p_string);
virtual void _value_changed(double) override;
+ void _text_changed(const String &p_string);
+
String prefix;
String suffix;
@@ -88,6 +91,9 @@ public:
void set_prefix(const String &p_prefix);
String get_prefix() const;
+ void set_update_on_text_changed(bool p_update);
+ bool get_update_on_text_changed() const;
+
void apply();
SpinBox();
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 137ce7e96f..a423dc0173 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -538,7 +538,6 @@ void TabContainer::_notification(int p_what) {
void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) {
Control *control = get_tab_control(p_index);
RID canvas = get_canvas_item();
- Ref<Font> font = get_theme_font(SNAME("font"));
Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
int outline_size = get_theme_constant(SNAME("outline_size"));
int icon_text_distance = get_theme_constant(SNAME("icon_separation"));
@@ -1134,7 +1133,6 @@ Size2 TabContainer::get_minimum_size() const {
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
- Ref<Font> font = get_theme_font(SNAME("font"));
if (tabs_visible) {
ms.y += MAX(MAX(tab_unselected->get_minimum_size().y, tab_selected->get_minimum_size().y), tab_disabled->get_minimum_size().y);