diff options
Diffstat (limited to 'scene')
-rw-r--r-- | scene/2d/physics_body_2d.cpp | 8 | ||||
-rw-r--r-- | scene/3d/physics_body.cpp | 12 | ||||
-rw-r--r-- | scene/3d/physics_body.h | 1 | ||||
-rw-r--r-- | scene/animation/tween.cpp | 4 | ||||
-rw-r--r-- | scene/gui/button.cpp | 287 | ||||
-rw-r--r-- | scene/gui/button.h | 6 | ||||
-rw-r--r-- | scene/gui/color_picker.cpp | 2 | ||||
-rw-r--r-- | scene/gui/control.cpp | 4 | ||||
-rw-r--r-- | scene/gui/line_edit.cpp | 28 | ||||
-rw-r--r-- | scene/gui/rich_text_effect.cpp | 122 | ||||
-rw-r--r-- | scene/gui/rich_text_effect.h | 87 | ||||
-rw-r--r-- | scene/gui/rich_text_label.cpp | 464 | ||||
-rw-r--r-- | scene/gui/rich_text_label.h | 150 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 27 | ||||
-rw-r--r-- | scene/gui/texture_button.cpp | 16 | ||||
-rw-r--r-- | scene/gui/tree.cpp | 19 | ||||
-rw-r--r-- | scene/main/node.cpp | 24 | ||||
-rw-r--r-- | scene/main/node.h | 5 | ||||
-rwxr-xr-x | scene/main/timer.cpp | 3 | ||||
-rw-r--r-- | scene/main/viewport.cpp | 14 | ||||
-rw-r--r-- | scene/register_scene_types.cpp | 1 | ||||
-rw-r--r-- | scene/resources/mesh_data_tool.cpp | 3 |
22 files changed, 1082 insertions, 205 deletions
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 7cc937a64a..9b6020e0fd 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1446,6 +1446,14 @@ void KinematicBody2D::_direct_state_changed(Object *p_state) { void KinematicBody2D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { last_valid_transform = get_global_transform(); + + // Reset move_and_slide() data. + on_floor = false; + on_floor_body = RID(); + on_ceiling = false; + on_wall = false; + colliders.clear(); + floor_velocity = Vector2(); } if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index ebac968cb4..0756be5fc8 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1387,6 +1387,18 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) { return slide_colliders[p_bounce]; } +void KinematicBody::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + // Reset move_and_slide() data. + on_floor = false; + on_floor_body = RID(); + on_ceiling = false; + on_wall = false; + colliders.clear(); + floor_velocity = Vector3(); + } +} + void KinematicBody::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false)); diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 0e2e614717..0967cb9cd5 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -315,6 +315,7 @@ private: Ref<KinematicCollision> _get_slide_collision(int p_bounce); protected: + void _notification(int p_what); static void _bind_methods(); public: diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 2609924f33..0f7d4466c8 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -783,10 +783,12 @@ float Tween::get_speed_scale() const { } bool Tween::start() { + + ERR_FAIL_COND_V_MSG(!is_inside_tree(), false, "Tween was not added to the SceneTree!"); + // Are there any pending updates? if (pending_update != 0) { // Start the tweens after deferring - call_deferred("start"); return true; } diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 4ce3f18505..6b3e89af6c 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -39,162 +39,191 @@ Size2 Button::get_minimum_size() const { if (clip_text) minsize.width = 0; - Ref<Texture> _icon; - if (icon.is_null() && has_icon("icon")) - _icon = Control::get_icon("icon"); - else - _icon = icon; - - if (!_icon.is_null()) { - - minsize.height = MAX(minsize.height, _icon->get_height()); - minsize.width += _icon->get_width(); - if (xl_text != "") - minsize.width += get_constant("hseparation"); + if (!expand_icon) { + Ref<Texture> _icon; + if (icon.is_null() && has_icon("icon")) + _icon = Control::get_icon("icon"); + else + _icon = icon; + + if (!_icon.is_null()) { + + minsize.height = MAX(minsize.height, _icon->get_height()); + minsize.width += _icon->get_width(); + if (xl_text != "") + minsize.width += get_constant("hseparation"); + } } return get_stylebox("normal")->get_minimum_size() + minsize; } void Button::_set_internal_margin(Margin p_margin, float p_value) { + _internal_margin[p_margin] = p_value; } void Button::_notification(int p_what) { - if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { + switch (p_what) { + case NOTIFICATION_TRANSLATION_CHANGED: { - xl_text = tr(text); - minimum_size_changed(); - update(); - } + xl_text = tr(text); + minimum_size_changed(); + update(); + } break; + case NOTIFICATION_DRAW: { - if (p_what == NOTIFICATION_DRAW) { + RID ci = get_canvas_item(); + Size2 size = get_size(); + Color color; + Color color_icon(1, 1, 1, 1); - RID ci = get_canvas_item(); - Size2 size = get_size(); - Color color; - Color color_icon(1, 1, 1, 1); + Ref<StyleBox> style = get_stylebox("normal"); - Ref<StyleBox> style = get_stylebox("normal"); + switch (get_draw_mode()) { + case DRAW_NORMAL: { - switch (get_draw_mode()) { - - case DRAW_NORMAL: { + style = get_stylebox("normal"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + color = get_color("font_color"); + if (has_color("icon_color_normal")) + color_icon = get_color("icon_color_normal"); + } break; + case DRAW_HOVER_PRESSED: { + + if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) { + style = get_stylebox("hover_pressed"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + if (has_color("font_color_hover_pressed")) + color = get_color("font_color_hover_pressed"); + else + color = get_color("font_color"); + if (has_color("icon_color_hover_pressed")) + color_icon = get_color("icon_color_hover_pressed"); + + break; + } + FALLTHROUGH; + } + case DRAW_PRESSED: { - style = get_stylebox("normal"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - color = get_color("font_color"); - if (has_color("icon_color_normal")) - color_icon = get_color("icon_color_normal"); - } break; - case DRAW_HOVER_PRESSED: { - if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) { - style = get_stylebox("hover_pressed"); + style = get_stylebox("pressed"); if (!flat) style->draw(ci, Rect2(Point2(0, 0), size)); - if (has_color("font_color_hover_pressed")) - color = get_color("font_color_hover_pressed"); + if (has_color("font_color_pressed")) + color = get_color("font_color_pressed"); else color = get_color("font_color"); - if (has_color("icon_color_hover_pressed")) - color_icon = get_color("icon_color_hover_pressed"); + if (has_color("icon_color_pressed")) + color_icon = get_color("icon_color_pressed"); - break; - } - FALLTHROUGH; + } break; + case DRAW_HOVER: { + + style = get_stylebox("hover"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + color = get_color("font_color_hover"); + if (has_color("icon_color_hover")) + color_icon = get_color("icon_color_hover"); + + } break; + case DRAW_DISABLED: { + + style = get_stylebox("disabled"); + if (!flat) + style->draw(ci, Rect2(Point2(0, 0), size)); + color = get_color("font_color_disabled"); + if (has_color("icon_color_disabled")) + color_icon = get_color("icon_color_disabled"); + + } break; } - case DRAW_PRESSED: { - - style = get_stylebox("pressed"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - if (has_color("font_color_pressed")) - color = get_color("font_color_pressed"); - else - color = get_color("font_color"); - if (has_color("icon_color_pressed")) - color_icon = get_color("icon_color_pressed"); - - } break; - case DRAW_HOVER: { - - style = get_stylebox("hover"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - color = get_color("font_color_hover"); - if (has_color("icon_color_hover")) - color_icon = get_color("icon_color_hover"); - - } break; - case DRAW_DISABLED: { - - style = get_stylebox("disabled"); - if (!flat) - style->draw(ci, Rect2(Point2(0, 0), size)); - color = get_color("font_color_disabled"); - if (has_color("icon_color_disabled")) - color_icon = get_color("icon_color_disabled"); - - } break; - } - if (has_focus()) { + if (has_focus()) { - Ref<StyleBox> style2 = get_stylebox("focus"); - style2->draw(ci, Rect2(Point2(), size)); - } + Ref<StyleBox> style2 = get_stylebox("focus"); + style2->draw(ci, Rect2(Point2(), size)); + } - Ref<Font> font = get_font("font"); - Ref<Texture> _icon; - if (icon.is_null() && has_icon("icon")) - _icon = Control::get_icon("icon"); - else - _icon = icon; + Ref<Font> font = get_font("font"); + Ref<Texture> _icon; + if (icon.is_null() && has_icon("icon")) + _icon = Control::get_icon("icon"); + else + _icon = icon; - Point2 icon_ofs = (!_icon.is_null()) ? Point2(_icon->get_width() + get_constant("hseparation"), 0) : Point2(); - int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width; - Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0; + Rect2 icon_region = Rect2(); + if (!_icon.is_null()) { - switch (align) { - case ALIGN_LEFT: { + int valign = size.height - style->get_minimum_size().y; + if (is_disabled()) { + color_icon.a = 0.4; + } + + float icon_ofs_region = 0; if (_internal_margin[MARGIN_LEFT] > 0) { - text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation"); - } else { - text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x; + icon_ofs_region = _internal_margin[MARGIN_LEFT] + get_constant("hseparation"); } - text_ofs.y += style->get_offset().y; - } break; - case ALIGN_CENTER: { - if (text_ofs.x < 0) - text_ofs.x = 0; - text_ofs += icon_ofs; - text_ofs += style->get_offset(); - } break; - case ALIGN_RIGHT: { - if (_internal_margin[MARGIN_RIGHT] > 0) { - text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation"); + + if (expand_icon) { + Size2 _size = get_size() - style->get_offset() * 2; + _size.width -= get_constant("hseparation") + icon_ofs_region; + if (!clip_text) + _size.width -= get_font("font")->get_string_size(xl_text).width; + float icon_width = icon->get_width() * _size.height / icon->get_height(); + float icon_height = _size.height; + + if (icon_width > _size.width) { + icon_width = _size.width; + icon_height = icon->get_height() * icon_width / icon->get_width(); + } + + icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, (_size.height - icon_height) / 2), Size2(icon_width, icon_height)); } else { - text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x; + icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), icon->get_size()); } - text_ofs.y += style->get_offset().y; - } break; - } + } - text_ofs.y += font->get_ascent(); - font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1); - if (!_icon.is_null()) { + Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_constant("hseparation"), 0) : Point2(); + int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width; + Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0; + + switch (align) { + case ALIGN_LEFT: { + if (_internal_margin[MARGIN_LEFT] > 0) { + text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation"); + } else { + text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x; + } + text_ofs.y += style->get_offset().y; + } break; + case ALIGN_CENTER: { + if (text_ofs.x < 0) + text_ofs.x = 0; + text_ofs += icon_ofs; + text_ofs += style->get_offset(); + } break; + case ALIGN_RIGHT: { + if (_internal_margin[MARGIN_RIGHT] > 0) { + text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation"); + } else { + text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x; + } + text_ofs.y += style->get_offset().y; + } break; + } + + text_ofs.y += font->get_ascent(); + font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1); - int valign = size.height - style->get_minimum_size().y; - if (is_disabled()) - color_icon.a = 0.4; - if (_internal_margin[MARGIN_LEFT] > 0) { - _icon->draw(ci, style->get_offset() + Point2(_internal_margin[MARGIN_LEFT] + get_constant("hseparation"), Math::floor((valign - _icon->get_height()) / 2.0)), color_icon); - } else { - _icon->draw(ci, style->get_offset() + Point2(0, Math::floor((valign - _icon->get_height()) / 2.0)), color_icon); + if (!_icon.is_null() && icon_region.size.width > 0) { + draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), icon->get_size()), color_icon); } - } + } break; } } @@ -228,6 +257,18 @@ Ref<Texture> Button::get_icon() const { return icon; } +void Button::set_expand_icon(bool p_expand_icon) { + + expand_icon = p_expand_icon; + update(); + minimum_size_changed(); +} + +bool Button::is_expand_icon() const { + + return expand_icon; +} + void Button::set_flat(bool p_flat) { flat = p_flat; @@ -269,6 +310,8 @@ void Button::_bind_methods() { ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text); ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon); ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon); + ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon); + ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon); ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat); ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text); ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text); @@ -285,12 +328,14 @@ void Button::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon"); } Button::Button(const String &p_text) { flat = false; clip_text = false; + expand_icon = false; set_mouse_filter(MOUSE_FILTER_STOP); set_text(p_text); align = ALIGN_CENTER; diff --git a/scene/gui/button.h b/scene/gui/button.h index 370809060e..1fff2cfda7 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -49,6 +49,7 @@ private: String text; String xl_text; Ref<Texture> icon; + bool expand_icon; bool clip_text; TextAlign align; float _internal_margin[4]; @@ -59,8 +60,6 @@ protected: static void _bind_methods(); public: - // - virtual Size2 get_minimum_size() const; void set_text(const String &p_text); @@ -69,6 +68,9 @@ public: void set_icon(const Ref<Texture> &p_icon); Ref<Texture> get_icon() const; + void set_expand_icon(bool p_expand_icon); + bool is_expand_icon() const; + void set_flat(bool p_flat); bool is_flat() const; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 1d529f4e72..6dd9e401f6 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -964,6 +964,7 @@ void ColorPickerButton::_update_picker() { popup->connect("popup_hide", this, "set_pressed", varray(false)); picker->set_pick_color(color); picker->set_edit_alpha(edit_alpha); + emit_signal("picker_created"); } } @@ -980,6 +981,7 @@ void ColorPickerButton::_bind_methods() { ADD_SIGNAL(MethodInfo("color_changed", PropertyInfo(Variant::COLOR, "color"))); ADD_SIGNAL(MethodInfo("popup_closed")); + ADD_SIGNAL(MethodInfo("picker_created")); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); } diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 034e9266f6..174c2fce7d 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2149,9 +2149,7 @@ bool Control::has_focus() const { void Control::grab_focus() { - if (!is_inside_tree()) { - ERR_FAIL_COND(!is_inside_tree()); - } + ERR_FAIL_COND(!is_inside_tree()); if (data.focus_mode == FOCUS_NONE) { WARN_PRINT("This control can't grab focus. Use set_focus_mode() to allow a control to get focus."); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 4a763844f8..6893c38733 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -872,7 +872,9 @@ void LineEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_ENTER: { - if (!caret_blink_enabled) { + if (caret_blink_enabled) { + caret_blink_timer->start(); + } else { draw_caret = true; } @@ -886,6 +888,10 @@ void LineEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_EXIT: { + if (caret_blink_enabled) { + caret_blink_timer->stop(); + } + OS::get_singleton()->set_ime_position(Point2()); OS::get_singleton()->set_ime_active(false); ime_text = ""; @@ -1053,11 +1059,15 @@ bool LineEdit::cursor_get_blink_enabled() const { void LineEdit::cursor_set_blink_enabled(const bool p_enabled) { caret_blink_enabled = p_enabled; - if (p_enabled) { - caret_blink_timer->start(); - } else { - caret_blink_timer->stop(); + + if (has_focus()) { + if (p_enabled) { + caret_blink_timer->start(); + } else { + caret_blink_timer->stop(); + } } + draw_caret = true; } @@ -1072,10 +1082,12 @@ void LineEdit::cursor_set_blink_speed(const float p_speed) { void LineEdit::_reset_caret_blink_timer() { if (caret_blink_enabled) { - caret_blink_timer->stop(); - caret_blink_timer->start(); draw_caret = true; - update(); + if (has_focus()) { + caret_blink_timer->stop(); + caret_blink_timer->start(); + update(); + } } } diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp new file mode 100644 index 0000000000..8d9b8ad1e0 --- /dev/null +++ b/scene/gui/rich_text_effect.cpp @@ -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; +} diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h new file mode 100644 index 0000000000..f9c3e15399 --- /dev/null +++ b/scene/gui/rich_text_effect.h @@ -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 diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 1aed858c94..77d5c8e91f 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -30,10 +30,11 @@ #include "rich_text_label.h" +#include "core/math/math_defs.h" #include "core/os/keyboard.h" #include "core/os/os.h" +#include "modules/regex/regex.h" #include "scene/scene_string_names.h" - #ifdef TOOLS_ENABLED #include "editor/editor_scale.h" #endif @@ -139,6 +140,7 @@ Rect2 RichTextLabel::_get_text_rect() { Ref<StyleBox> style = get_stylebox("normal"); return Rect2(style->get_offset(), get_size() - style->get_minimum_size()); } + int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) { RID ci; @@ -292,7 +294,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & Color selection_bg; if (p_mode == PROCESS_DRAW) { - selection_fg = get_color("font_color_selected"); selection_bg = get_color("selection_color"); } @@ -343,18 +344,24 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & Color font_color_shadow; bool underline = false; bool strikethrough = false; + ItemFade *fade = NULL; + int it_char_start = p_char_count; + + Vector<ItemFX *> fx_stack = Vector<ItemFX *>(); + bool custom_fx_ok = true; if (p_mode == PROCESS_DRAW) { color = _find_color(text, p_base_color); font_color_shadow = _find_color(text, p_font_color_shadow); if (_find_underline(text) || (_find_meta(text, &meta) && underline_meta)) { - underline = true; } else if (_find_strikethrough(text)) { - strikethrough = true; } + fade = _fetch_by_type<ItemFade>(text, ITEM_FADE); + _fetch_item_stack<ItemFX>(text, fx_stack); + } else if (p_mode == PROCESS_CACHE) { l.char_count += text->text.length(); } @@ -431,8 +438,11 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & ofs += cw; } else if (p_mode == PROCESS_DRAW) { - bool selected = false; + Color fx_color = Color(color); + Point2 fx_offset; + CharType fx_char = c[i]; + if (selection.active) { int cofs = (&c[i]) - cf; @@ -442,8 +452,78 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & } int cw = 0; + int c_item_offset = p_char_count - it_char_start; + + float faded_visibility = 1.0f; + if (fade) { + if (c_item_offset >= fade->starting_index) { + faded_visibility -= (float)(c_item_offset - fade->starting_index) / (float)fade->length; + faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility; + } + fx_color.a = faded_visibility; + } + + bool visible = visible_characters < 0 || ((p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)) && + faded_visibility > 0.0f); + + for (int j = 0; j < fx_stack.size(); j++) { + ItemCustomFX *item_custom = Object::cast_to<ItemCustomFX>(fx_stack[j]); + ItemShake *item_shake = Object::cast_to<ItemShake>(fx_stack[j]); + ItemWave *item_wave = Object::cast_to<ItemWave>(fx_stack[j]); + ItemTornado *item_tornado = Object::cast_to<ItemTornado>(fx_stack[j]); + ItemRainbow *item_rainbow = Object::cast_to<ItemRainbow>(fx_stack[j]); + + if (item_custom && custom_fx_ok) { + Ref<CharFXTransform> charfx = Ref<CharFXTransform>(memnew(CharFXTransform)); + Ref<RichTextEffect> custom_effect = _get_custom_effect_by_code(item_custom->identifier); + if (!custom_effect.is_null()) { + charfx->elapsed_time = item_custom->elapsed_time; + charfx->environment = item_custom->environment; + charfx->relative_index = c_item_offset; + charfx->absolute_index = p_char_count; + charfx->visibility = visible; + charfx->offset = fx_offset; + charfx->color = fx_color; + charfx->character = fx_char; + + bool effect_status = custom_effect->_process_effect_impl(charfx); + custom_fx_ok = effect_status; + + fx_offset += charfx->offset; + fx_color = charfx->color; + visible &= charfx->visibility; + fx_char = charfx->character; + } + } else if (item_shake) { + uint64_t char_current_rand = item_shake->offset_random(c_item_offset); + uint64_t char_previous_rand = item_shake->offset_previous_random(c_item_offset); + uint64_t max_rand = 2147483647; + double current_offset = Math::range_lerp(char_current_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double previous_offset = Math::range_lerp(char_previous_rand % max_rand, 0, max_rand, 0.0f, 2.f * (float)Math_PI); + double n_time = (double)(item_shake->elapsed_time / (0.5f / item_shake->rate)); + n_time = (n_time > 1.0) ? 1.0 : n_time; + fx_offset += Point2(Math::lerp(Math::sin(previous_offset), + Math::sin(current_offset), + n_time), + Math::lerp(Math::cos(previous_offset), + Math::cos(current_offset), + n_time)) * + (float)item_shake->strength / 10.0f; + } else if (item_wave) { + double value = Math::sin(item_wave->frequency * item_wave->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_wave->amplitude / 10.0f); + fx_offset += Point2(0, 1) * value; + } else if (item_tornado) { + double torn_x = Math::sin(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); + double torn_y = Math::cos(item_tornado->frequency * item_tornado->elapsed_time + ((p_ofs.x + pofs) / 50)) * (item_tornado->radius); + fx_offset += Point2(torn_x, torn_y); + } else if (item_rainbow) { + fx_color = fx_color.from_hsv(item_rainbow->frequency * (item_rainbow->elapsed_time + ((p_ofs.x + pofs) / 50)), + item_rainbow->saturation, + item_rainbow->value, + fx_color.a); + } + } - bool visible = visible_characters < 0 || (p_char_count < visible_characters && YRANGE_VISIBLE(y + lh - line_descent - line_ascent, line_ascent + line_descent)); if (visible) line_is_blank = false; @@ -451,27 +531,28 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int & visible = false; if (visible) { + if (selected) { - cw = font->get_char_size(c[i], c[i + 1]).x; + cw = font->get_char_size(fx_char, c[i + 1]).x; draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg); } if (p_font_color_shadow.a > 0) { float x_ofs_shadow = align_ofs + pofs; float y_ofs_shadow = y + lh - line_descent; - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, c[i], c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, fx_char, c[i + 1], p_font_color_shadow); if (p_shadow_as_outline) { - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow); - font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow); + font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), fx_char, c[i + 1], p_font_color_shadow); } } if (selected) { - drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color); + drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), fx_char, c[i + 1], override_selected_font_color ? selection_fg : fx_color); } else { - cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color); + cw = drawer.draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent) + fx_offset, fx_char, c[i + 1], fx_color); } } @@ -800,6 +881,31 @@ void RichTextLabel::_update_scroll() { } } +void RichTextLabel::_update_fx(RichTextLabel::ItemFrame *p_frame, float p_delta_time) { + Item *it = p_frame; + while (it) { + ItemFX *ifx = Object::cast_to<ItemFX>(it); + + if (!ifx) { + it = _get_next_item(it, true); + continue; + } + + ifx->elapsed_time += p_delta_time; + + ItemShake *shake = Object::cast_to<ItemShake>(it); + if (shake) { + bool cycle = (shake->elapsed_time > (1.0f / shake->rate)); + if (cycle) { + shake->elapsed_time -= (1.0f / shake->rate); + shake->reroll_random(); + } + } + + it = _get_next_item(it, true); + } +} + void RichTextLabel::_notification(int p_what) { switch (p_what) { @@ -873,6 +979,15 @@ void RichTextLabel::_notification(int p_what) { from_line++; } + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + float dt = get_process_delta_time(); + + for (int i = 0; i < custom_effects.size(); i++) { + } + + _update_fx(main, dt); + update(); } } } @@ -1026,15 +1141,11 @@ void RichTextLabel::_gui_input(Ref<InputEvent> p_event) { } if (b->get_button_index() == BUTTON_WHEEL_UP) { - if (scroll_active) - vscroll->set_value(vscroll->get_value() - vscroll->get_page() * b->get_factor() * 0.5 / 8); } if (b->get_button_index() == BUTTON_WHEEL_DOWN) { - if (scroll_active) - vscroll->set_value(vscroll->get_value() + vscroll->get_page() * b->get_factor() * 0.5 / 8); } } @@ -1285,8 +1396,19 @@ bool RichTextLabel::_find_strikethrough(Item *p_item) { return false; } -bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { +bool RichTextLabel::_find_by_type(Item *p_item, ItemType p_type) { + Item *item = p_item; + while (item) { + if (item->type == p_type) { + return true; + } + item = item->parent; + } + return false; +} + +bool RichTextLabel::_find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item) { Item *item = p_item; while (item) { @@ -1618,6 +1740,49 @@ void RichTextLabel::push_table(int p_columns) { _add_item(item, true, true); } +void RichTextLabel::push_fade(int p_start_index, int p_length) { + ItemFade *item = memnew(ItemFade); + item->starting_index = p_start_index; + item->length = p_length; + _add_item(item, true); +} + +void RichTextLabel::push_shake(int p_strength = 10, float p_rate = 24.0f) { + ItemShake *item = memnew(ItemShake); + item->strength = p_strength; + item->rate = p_rate; + _add_item(item, true); +} + +void RichTextLabel::push_wave(float p_frequency = 1.0f, float p_amplitude = 10.0f) { + ItemWave *item = memnew(ItemWave); + item->frequency = p_frequency; + item->amplitude = p_amplitude; + _add_item(item, true); +} + +void RichTextLabel::push_tornado(float p_frequency = 1.0f, float p_radius = 10.0f) { + ItemTornado *item = memnew(ItemTornado); + item->frequency = p_frequency; + item->radius = p_radius; + _add_item(item, true); +} + +void RichTextLabel::push_rainbow(float p_saturation, float p_value, float p_frequency) { + ItemRainbow *item = memnew(ItemRainbow); + item->frequency = p_frequency; + item->saturation = p_saturation; + item->value = p_value; + _add_item(item, true); +} + +void RichTextLabel::push_customfx(String p_identifier, Dictionary p_environment) { + ItemCustomFX *item = memnew(ItemCustomFX); + item->identifier = p_identifier; + item->environment = p_environment; + _add_item(item, true); +} + void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) { ERR_FAIL_COND(current->type != ITEM_TABLE); @@ -1762,6 +1927,8 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { bool in_bold = false; bool in_italics = false; + set_process_internal(false); + while (pos < p_bbcode.length()) { int brk_pos = p_bbcode.find("[", pos); @@ -1785,7 +1952,6 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { } String tag = p_bbcode.substr(brk_pos + 1, brk_end - brk_pos - 1); - if (tag.begins_with("/") && tag_stack.size()) { bool tag_ok = tag_stack.size() && tag_stack.front()->get() == tag.substr(1, tag.length()); @@ -1798,9 +1964,8 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { indent_level--; if (!tag_ok) { - - add_text("["); - pos++; + add_text("[" + tag); + pos = brk_end; continue; } @@ -1992,10 +2157,145 @@ Error RichTextLabel::append_bbcode(const String &p_bbcode) { pos = brk_end + 1; tag_stack.push_front("font"); - } else { + } else if (tag.begins_with("fade")) { + Vector<String> tags = tag.split(" ", false); + int startIndex = 0; + int length = 10; + + if (tags.size() > 1) { + tags.remove(0); + for (int i = 0; i < tags.size(); i++) { + String expr = tags[i]; + if (expr.begins_with("start=")) { + String start_str = expr.substr(6, expr.length()); + startIndex = start_str.to_int(); + } else if (expr.begins_with("length=")) { + String end_str = expr.substr(7, expr.length()); + length = end_str.to_int(); + } + } + } + + push_fade(startIndex, length); + pos = brk_end + 1; + tag_stack.push_front("fade"); + } else if (tag.begins_with("shake")) { + Vector<String> tags = tag.split(" ", false); + int strength = 5; + float rate = 20.0f; + + if (tags.size() > 1) { + tags.remove(0); + for (int i = 0; i < tags.size(); i++) { + String expr = tags[i]; + if (expr.begins_with("level=")) { + String str_str = expr.substr(6, expr.length()); + strength = str_str.to_int(); + } else if (expr.begins_with("rate=")) { + String rate_str = expr.substr(5, expr.length()); + rate = rate_str.to_float(); + } + } + } + + push_shake(strength, rate); + pos = brk_end + 1; + tag_stack.push_front("shake"); + set_process_internal(true); + } else if (tag.begins_with("wave")) { + Vector<String> tags = tag.split(" ", false); + float amplitude = 20.0f; + float period = 5.0f; + + if (tags.size() > 1) { + tags.remove(0); + for (int i = 0; i < tags.size(); i++) { + String expr = tags[i]; + if (expr.begins_with("amp=")) { + String amp_str = expr.substr(4, expr.length()); + amplitude = amp_str.to_float(); + } else if (expr.begins_with("freq=")) { + String period_str = expr.substr(5, expr.length()); + period = period_str.to_float(); + } + } + } - add_text("["); //ignore - pos = brk_pos + 1; + push_wave(period, amplitude); + pos = brk_end + 1; + tag_stack.push_front("wave"); + set_process_internal(true); + } else if (tag.begins_with("tornado")) { + Vector<String> tags = tag.split(" ", false); + float radius = 10.0f; + float frequency = 1.0f; + + if (tags.size() > 1) { + tags.remove(0); + for (int i = 0; i < tags.size(); i++) { + String expr = tags[i]; + if (expr.begins_with("radius=")) { + String amp_str = expr.substr(7, expr.length()); + radius = amp_str.to_float(); + } else if (expr.begins_with("freq=")) { + String period_str = expr.substr(5, expr.length()); + frequency = period_str.to_float(); + } + } + } + + push_tornado(frequency, radius); + pos = brk_end + 1; + tag_stack.push_front("tornado"); + set_process_internal(true); + } else if (tag.begins_with("rainbow")) { + Vector<String> tags = tag.split(" ", false); + float saturation = 0.8f; + float value = 0.8f; + float frequency = 1.0f; + + if (tags.size() > 1) { + tags.remove(0); + for (int i = 0; i < tags.size(); i++) { + String expr = tags[i]; + if (expr.begins_with("sat=")) { + String sat_str = expr.substr(4, expr.length()); + saturation = sat_str.to_float(); + } else if (expr.begins_with("val=")) { + String val_str = expr.substr(4, expr.length()); + value = val_str.to_float(); + } else if (expr.begins_with("freq=")) { + String freq_str = expr.substr(5, expr.length()); + frequency = freq_str.to_float(); + } + } + } + + push_rainbow(saturation, value, frequency); + pos = brk_end + 1; + tag_stack.push_front("rainbow"); + set_process_internal(true); + } else { + Vector<String> expr = tag.split(" ", false); + if (expr.size() < 1) { + add_text("["); + pos = brk_pos + 1; + } else { + String identifier = expr[0]; + expr.remove(0); + Dictionary properties = parse_expressions_for_values(expr); + Ref<RichTextEffect> effect = _get_custom_effect_by_code(identifier); + + if (!effect.is_null()) { + push_customfx(identifier, properties); + pos = brk_end + 1; + tag_stack.push_front(identifier); + set_process_internal(true); + } else { + add_text("["); //ignore + pos = brk_pos + 1; + } + } } } @@ -2204,6 +2504,34 @@ float RichTextLabel::get_percent_visible() const { return percent_visible; } +void RichTextLabel::set_effects(const Vector<Variant> &effects) { + custom_effects.clear(); + for (int i = 0; i < effects.size(); i++) { + Ref<RichTextEffect> effect = Ref<RichTextEffect>(effects[i]); + custom_effects.push_back(effect); + } + + parse_bbcode(bbcode); +} + +Vector<Variant> RichTextLabel::get_effects() { + Vector<Variant> r; + for (int i = 0; i < custom_effects.size(); i++) { + r.push_back(custom_effects[i].get_ref_ptr()); + } + return r; +} + +void RichTextLabel::install_effect(const Variant effect) { + Ref<RichTextEffect> rteffect; + rteffect = effect; + + if (rteffect.is_valid()) { + custom_effects.push_back(effect); + parse_bbcode(bbcode); + } +} + int RichTextLabel::get_content_height() { int total_height = 0; if (main->lines.size()) @@ -2280,6 +2608,12 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height); + ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values); + + ClassDB::bind_method(D_METHOD("set_effects", "effects"), &RichTextLabel::set_effects); + ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects); + ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect); + ADD_GROUP("BBCode", "bbcode_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "bbcode_text", PROPERTY_HINT_MULTILINE_TEXT), "set_bbcode", "get_bbcode"); @@ -2297,6 +2631,8 @@ void RichTextLabel::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", (PropertyHint)(PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE), "17/17:RichTextEffect", PROPERTY_USAGE_DEFAULT, "RichTextEffect"), "set_effects", "get_effects"); + ADD_SIGNAL(MethodInfo("meta_clicked", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT))); @@ -2322,11 +2658,16 @@ void RichTextLabel::_bind_methods() { BIND_ENUM_CONSTANT(ITEM_INDENT); BIND_ENUM_CONSTANT(ITEM_LIST); BIND_ENUM_CONSTANT(ITEM_TABLE); + BIND_ENUM_CONSTANT(ITEM_FADE); + BIND_ENUM_CONSTANT(ITEM_SHAKE); + BIND_ENUM_CONSTANT(ITEM_WAVE); + BIND_ENUM_CONSTANT(ITEM_TORNADO); + BIND_ENUM_CONSTANT(ITEM_RAINBOW); + BIND_ENUM_CONSTANT(ITEM_CUSTOMFX); BIND_ENUM_CONSTANT(ITEM_META); } void RichTextLabel::set_visible_characters(int p_visible) { - visible_characters = p_visible; update(); } @@ -2358,6 +2699,77 @@ Size2 RichTextLabel::get_minimum_size() const { return Size2(); } +Ref<RichTextEffect> RichTextLabel::_get_custom_effect_by_code(String p_bbcode_identifier) { + Ref<RichTextEffect> r; + for (int i = 0; i < custom_effects.size(); i++) { + if (!custom_effects[i].is_valid()) + continue; + + if (custom_effects[i]->get_bbcode() == p_bbcode_identifier) { + r = custom_effects[i]; + } + } + + return r; +} + +Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressions) { + Dictionary d = Dictionary(); + for (int i = 0; i < p_expressions.size(); i++) { + String expression = p_expressions[i]; + + Array a = Array(); + Vector<String> parts = expression.split("=", true); + String key = parts[0]; + if (parts.size() != 2) { + return d; + } + + Vector<String> values = parts[1].split(",", false); + + RegEx color = RegEx(); + color.compile("^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$"); + RegEx nodepath = RegEx(); + nodepath.compile("^\\$"); + RegEx boolean = RegEx(); + boolean.compile("^(true|false)$"); + RegEx decimal = RegEx(); + decimal.compile("^-?^.?\\d+(\\.\\d+?)?$"); + RegEx numerical = RegEx(); + numerical.compile("^\\d+$"); + + for (int j = 0; j < values.size(); j++) { + if (!color.search(values[j]).is_null()) { + a.append(Color::html(values[j])); + } else if (!nodepath.search(values[j]).is_null()) { + if (values[j].begins_with("$")) { + String v = values[j].substr(1, values[j].length()); + a.append(NodePath(v)); + } + } else if (!boolean.search(values[j]).is_null()) { + if (values[j] == "true") { + a.append(true); + } else if (values[j] == "false") { + a.append(false); + } + } else if (!decimal.search(values[j]).is_null()) { + a.append(values[j].to_double()); + } else if (!numerical.search(values[j]).is_null()) { + a.append(values[j].to_int()); + } else { + a.append(values[j]); + } + } + + if (values.size() > 1) { + d[key] = a; + } else if (values.size() == 1) { + d[key] = a[0]; + } + } + return d; +} + RichTextLabel::RichTextLabel() { main = memnew(ItemFrame); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 21d099c37a..6755f9ef2a 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -31,6 +31,7 @@ #ifndef RICH_TEXT_LABEL_H #define RICH_TEXT_LABEL_H +#include "rich_text_effect.h" #include "scene/gui/scroll_bar.h" class RichTextLabel : public Control { @@ -67,7 +68,13 @@ public: ITEM_INDENT, ITEM_LIST, ITEM_TABLE, - ITEM_META + ITEM_FADE, + ITEM_SHAKE, + ITEM_WAVE, + ITEM_TORNADO, + ITEM_RAINBOW, + ITEM_META, + ITEM_CUSTOMFX }; protected: @@ -96,7 +103,7 @@ private: } }; - struct Item { + struct Item : public Object { int index; Item *parent; @@ -214,6 +221,101 @@ private: 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; Item *current; ItemFrame *current_frame; @@ -239,6 +341,8 @@ private: ItemMeta *meta_hovering; Variant current_meta; + Vector<Ref<RichTextEffect> > custom_effects; + void _invalidate_current_line(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); struct ProcessState { - int line_width; }; @@ -287,8 +390,36 @@ private: bool _find_strikethrough(Item *p_item); bool _find_meta(Item *p_item, Variant *r_meta, ItemMeta **r_item = NULL); 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_fx(ItemFrame *p_frame, float p_delta_time); void _scroll_changed(double); void _gui_input(Ref<InputEvent> p_event); @@ -296,6 +427,8 @@ private: Item *_get_prev_item(Item *p_item, bool p_free = false); 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; String bbcode; @@ -322,6 +455,12 @@ public: void push_list(ListType p_list); void push_meta(const Variant &p_meta); 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); int get_current_table_column() const; void push_cell(); @@ -380,6 +519,11 @@ public: void set_percent_visible(float p_percent); 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); virtual Size2 get_minimum_size() const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 0464cc1ac8..7bcef5f9ab 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1738,7 +1738,9 @@ void TextEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_ENTER: { - if (!caret_blink_enabled) { + if (caret_blink_enabled) { + caret_blink_timer->start(); + } else { draw_caret = true; } @@ -1751,6 +1753,10 @@ void TextEdit::_notification(int p_what) { } break; case NOTIFICATION_FOCUS_EXIT: { + if (caret_blink_enabled) { + caret_blink_timer->stop(); + } + OS::get_singleton()->set_ime_position(Point2()); OS::get_singleton()->set_ime_active(false); ime_text = ""; @@ -4396,11 +4402,14 @@ bool TextEdit::cursor_get_blink_enabled() const { void TextEdit::cursor_set_blink_enabled(const bool p_enabled) { caret_blink_enabled = p_enabled; - if (p_enabled) { - caret_blink_timer->start(); - } else { - caret_blink_timer->stop(); + if (has_focus()) { + if (p_enabled) { + caret_blink_timer->start(); + } else { + caret_blink_timer->stop(); + } } + draw_caret = true; } @@ -4817,10 +4826,12 @@ int TextEdit::get_max_chars() const { void TextEdit::_reset_caret_blink_timer() { if (caret_blink_enabled) { - caret_blink_timer->stop(); - caret_blink_timer->start(); draw_caret = true; - update(); + if (has_focus()) { + caret_blink_timer->stop(); + caret_blink_timer->start(); + update(); + } } } diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index b5f949aeb7..e9b0bd8f38 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -205,24 +205,26 @@ void TextureButton::_notification(int p_what) { case STRETCH_KEEP_ASPECT_COVERED: { size = get_size(); Size2 tex_size = texdraw->get_size(); - Size2 scaleSize(size.width / tex_size.width, size.height / tex_size.height); - float scale = scaleSize.width > scaleSize.height ? scaleSize.width : scaleSize.height; - Size2 scaledTexSize = tex_size * scale; - Point2 ofs2 = ((scaledTexSize - size) / scale).abs() / 2.0f; + Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height); + float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height; + Size2 scaled_tex_size = tex_size * scale; + Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f; _texture_region = Rect2(ofs2, size / scale); } break; } } + _position_rect = Rect2(ofs, size); - if (_tile) + if (_tile) { draw_texture_rect(texdraw, _position_rect, _tile); - else + } else { draw_texture_rect_region(texdraw, _position_rect, _texture_region); + } } else { _position_rect = Rect2(); } - if (has_focus() && focused.is_valid()) { + if (has_focus() && focused.is_valid()) { draw_texture_rect(focused, _position_rect, false); }; } break; diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index b7451faad3..2a18436a5e 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -1184,23 +1184,22 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 } } - if (p_item->cells[i].selected && select_mode != SELECT_ROW) { - + if (select_mode != SELECT_ROW && (p_item->cells[i].selected || selected_item == p_item)) { Rect2i r(cell_rect.position, cell_rect.size); + if (p_item->cells[i].text.size() > 0) { float icon_width = p_item->cells[i].get_icon_size().width; r.position.x += icon_width; r.size.x -= icon_width; } p_item->set_meta("__focus_rect", Rect2(r.position, r.size)); - if (has_focus()) { - cache.selected_focus->draw(ci, r); - } else { - cache.selected->draw(ci, r); - } - if (text_editor->is_visible_in_tree()) { - Vector2 ofs2(0, (text_editor->get_size().height - r.size.height) / 2); - text_editor->set_position(get_global_position() + r.position - ofs2); + + if (p_item->cells[i].selected) { + if (has_focus()) { + cache.selected_focus->draw(ci, r); + } else { + cache.selected->draw(ci, r); + } } } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 0b3a193d18..bd01ca2886 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1875,6 +1875,19 @@ String Node::get_filename() const { return data.filename; } +void Node::set_editor_description(const String &p_editor_description) { + + set_meta("_editor_description_", p_editor_description); +} +String Node::get_editor_description() const { + + if (has_meta("_editor_description_")) { + return get_meta("_editor_description_"); + } else { + return ""; + } +} + void Node::set_editable_instance(Node *p_node, bool p_editable) { ERR_FAIL_NULL(p_node); @@ -2788,6 +2801,10 @@ void Node::_bind_methods() { ClassDB::bind_method(D_METHOD("rpc_config", "method", "mode"), &Node::rpc_config); ClassDB::bind_method(D_METHOD("rset_config", "property", "mode"), &Node::rset_config); + ClassDB::bind_method(D_METHOD("_set_editor_description", "editor_description"), &Node::set_editor_description); + ClassDB::bind_method(D_METHOD("_get_editor_description"), &Node::get_editor_description); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "editor_description", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_editor_description", "_get_editor_description"); + ClassDB::bind_method(D_METHOD("_set_import_path", "import_path"), &Node::set_import_path); ClassDB::bind_method(D_METHOD("_get_import_path"), &Node::get_import_path); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "_import_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_import_path", "_get_import_path"); @@ -2860,10 +2877,6 @@ void Node::_bind_methods() { ADD_SIGNAL(MethodInfo("tree_exiting")); ADD_SIGNAL(MethodInfo("tree_exited")); - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/process" ),"set_process","is_processing") ; - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/physics_process" ), "set_physics_process","is_physics_processing") ; - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/input" ), "set_process_input","is_processing_input" ) ; - //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), "set_process_unhandled_input","is_processing_unhandled_input" ) ; ADD_GROUP("Pause", "pause_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "pause_mode", PROPERTY_HINT_ENUM, "Inherit,Stop,Process"), "set_pause_mode", "get_pause_mode"); @@ -2887,9 +2900,6 @@ void Node::_bind_methods() { BIND_VMETHOD(MethodInfo("_unhandled_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); BIND_VMETHOD(MethodInfo("_unhandled_key_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEventKey"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "_get_configuration_warning")); - - //ClassDB::bind_method(D_METHOD("get_child",&Node::get_child,PH("index"))); - //ClassDB::bind_method(D_METHOD("get_node",&Node::get_node,PH("path"))); } String Node::_get_name_num_separator() { diff --git a/scene/main/node.h b/scene/main/node.h index 67b40f6dfc..51a1436014 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -318,6 +318,9 @@ public: void set_filename(const String &p_filename); String get_filename() const; + void set_editor_description(const String &p_editor_description); + String get_editor_description() const; + void set_editable_instance(Node *p_node, bool p_editable); bool is_editable_instance(const Node *p_node) const; void set_editable_instances(const HashMap<NodePath, int> &p_editable_instances); @@ -363,8 +366,6 @@ public: Node *duplicate_from_editor(Map<const Node *, Node *> &r_duplimap) const; #endif - //Node *clone_tree() const; - // used by editors, to save what has changed only void set_scene_instance_state(const Ref<SceneState> &p_state); Ref<SceneState> get_scene_instance_state() const; diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp index 03d46fd28d..14cc705edb 100755 --- a/scene/main/timer.cpp +++ b/scene/main/timer.cpp @@ -107,6 +107,9 @@ bool Timer::has_autostart() const { } void Timer::start(float p_time) { + + ERR_FAIL_COND_MSG(!is_inside_tree(), "Timer was not added to the SceneTree!"); + if (p_time > 0) { set_wait_time(p_time); } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 93eea2ad0b..b5c82ce4e3 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2289,32 +2289,34 @@ void Viewport::_gui_input_event(Ref<InputEvent> p_event) { if (from && p_event->is_pressed()) { Control *next = NULL; - if (p_event->is_action_pressed("ui_focus_next")) { + Input *input = Input::get_singleton(); + + if (p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) { next = from->find_next_valid_focus(); } - if (p_event->is_action_pressed("ui_focus_prev")) { + if (p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) { next = from->find_prev_valid_focus(); } - if (!mods && p_event->is_action_pressed("ui_up")) { + if (!mods && p_event->is_action_pressed("ui_up") && input->is_action_just_pressed("ui_up")) { next = from->_get_focus_neighbour(MARGIN_TOP); } - if (!mods && p_event->is_action_pressed("ui_left")) { + if (!mods && p_event->is_action_pressed("ui_left") && input->is_action_just_pressed("ui_left")) { next = from->_get_focus_neighbour(MARGIN_LEFT); } - if (!mods && p_event->is_action_pressed("ui_right")) { + if (!mods && p_event->is_action_pressed("ui_right") && input->is_action_just_pressed("ui_right")) { next = from->_get_focus_neighbour(MARGIN_RIGHT); } - if (!mods && p_event->is_action_pressed("ui_down")) { + if (!mods && p_event->is_action_pressed("ui_down") && input->is_action_just_pressed("ui_down")) { next = from->_get_focus_neighbour(MARGIN_BOTTOM); } diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 06d84302a3..8f675e8f64 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -343,6 +343,7 @@ void register_scene_types() { ClassDB::register_class<ColorPicker>(); ClassDB::register_class<ColorPickerButton>(); ClassDB::register_class<RichTextLabel>(); + ClassDB::register_class<RichTextEffect>(); ClassDB::register_class<PopupDialog>(); ClassDB::register_class<WindowDialog>(); ClassDB::register_class<AcceptDialog>(); diff --git a/scene/resources/mesh_data_tool.cpp b/scene/resources/mesh_data_tool.cpp index 7cd765fb5d..0c39c3cbb1 100644 --- a/scene/resources/mesh_data_tool.cpp +++ b/scene/resources/mesh_data_tool.cpp @@ -165,11 +165,12 @@ Error MeshDataTool::create_from_surface(const Ref<ArrayMesh> &p_mesh, int p_surf e.vertex[0] = edge.x; e.vertex[1] = edge.y; edges.push_back(e); + v[j]->edges.push_back(face.edges[j]); + v[(j + 1) % 3]->edges.push_back(face.edges[j]); } edges.write[face.edges[j]].faces.push_back(fidx); v[j]->faces.push_back(fidx); - v[j]->edges.push_back(face.edges[j]); } faces.push_back(face); |