summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2018-05-15 17:12:35 -0300
committerJuan Linietsky <reduzio@gmail.com>2018-05-15 17:14:31 -0300
commit005b69cf6e276209464cc8c36ebc7376679925b6 (patch)
tree2271a91a727cb9afcc2f58b4b91023f4a0a7f7f8
parent3b8bd50b41e0197ab3bce653548715872a93ea80 (diff)
-New inspector.
-Changed UI resizing code, gained huge amount of speed. -Reorganized timer sync to clean up behavior (sorry forgot commit this before) -
-rw-r--r--core/object.h2
-rw-r--r--editor/animation_editor.cpp1
-rw-r--r--editor/editor_inspector.cpp1797
-rw-r--r--editor/editor_inspector.h283
-rw-r--r--editor/editor_node.cpp74
-rw-r--r--editor/editor_node.h6
-rw-r--r--editor/editor_properties.cpp2411
-rw-r--r--editor/editor_properties.h490
-rw-r--r--editor/editor_spin_slider.cpp315
-rw-r--r--editor/editor_spin_slider.h57
-rw-r--r--editor/icons/icon_GUI_slider_grabber.svg85
-rw-r--r--editor/icons/icon_GUI_slider_grabber_hl.svg84
-rw-r--r--editor/multi_node_edit.cpp4
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp4
-rw-r--r--editor/plugins/cube_grid_theme_editor_plugin.cpp2
-rw-r--r--editor/plugins/item_list_editor_plugin.cpp2
-rw-r--r--editor/project_settings_editor.cpp4
-rw-r--r--editor/property_editor.cpp4
-rw-r--r--editor/property_editor.h3
-rw-r--r--editor/scene_tree_dock.cpp2
-rw-r--r--editor/script_editor_debugger.cpp4
-rw-r--r--main/main.cpp234
-rw-r--r--main/timer_sync.cpp193
-rw-r--r--main/timer_sync.h71
-rw-r--r--modules/mono/csharp_script.cpp2
-rw-r--r--modules/visual_script/visual_script_editor.cpp2
-rw-r--r--scene/2d/audio_stream_player_2d.cpp2
-rw-r--r--scene/3d/light.cpp12
-rw-r--r--scene/gui/color_picker.cpp49
-rw-r--r--scene/gui/color_picker.h6
-rw-r--r--scene/gui/container.cpp6
-rw-r--r--scene/gui/control.cpp58
-rw-r--r--scene/gui/control.h8
-rw-r--r--scene/gui/range.cpp8
-rw-r--r--scene/gui/scroll_container.cpp16
-rw-r--r--scene/main/node.cpp3
-rw-r--r--scene/main/node.h1
-rw-r--r--scene/main/viewport.cpp2
-rw-r--r--scene/resources/default_theme/default_theme.cpp5
39 files changed, 5975 insertions, 337 deletions
diff --git a/core/object.h b/core/object.h
index c405e22557..7963a43fd6 100644
--- a/core/object.h
+++ b/core/object.h
@@ -55,7 +55,7 @@ enum PropertyHint {
PROPERTY_HINT_RANGE, ///< hint_text = "min,max,step,slider; //slider is optional"
PROPERTY_HINT_EXP_RANGE, ///< hint_text = "min,max,step", exponential edit
PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
- PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease)
+ PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout")
PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer)
PROPERTY_HINT_SPRITE_FRAME,
PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
diff --git a/editor/animation_editor.cpp b/editor/animation_editor.cpp
index f7c8cac93f..a03bf76d1b 100644
--- a/editor/animation_editor.cpp
+++ b/editor/animation_editor.cpp
@@ -2969,6 +2969,7 @@ void AnimationKeyEditor::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
+ update_keying();
EditorNode::get_singleton()->update_keying();
emit_signal("keying_changed");
} break;
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
new file mode 100644
index 0000000000..21ad71fe06
--- /dev/null
+++ b/editor/editor_inspector.cpp
@@ -0,0 +1,1797 @@
+#include "editor_inspector.h"
+#include "array_property_edit.h"
+#include "dictionary_property_edit.h"
+#include "editor_node.h"
+#include "editor_scale.h"
+#include "multi_node_edit.h"
+#include "scene/resources/packed_scene.h"
+
+// TODO:
+// arrays
+
+Size2 EditorProperty::get_minimum_size() const {
+
+ Size2 ms;
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c)
+ continue;
+ if (c->is_set_as_toplevel())
+ continue;
+ if (!c->is_visible())
+ continue;
+ Size2 minsize = c->get_combined_minimum_size();
+ ms.width = MAX(ms.width, minsize.width);
+ ms.height = MAX(ms.height, minsize.height);
+ }
+
+ if (keying) {
+ Ref<Texture> key = get_icon("Key", "EditorIcons");
+ ms.width += key->get_width() + get_constant("hseparator", "Tree");
+ }
+
+ if (checkable) {
+ Ref<Texture> check = get_icon("checked", "CheckBox");
+ ms.width += check->get_width() + get_constant("hseparator", "Tree");
+ }
+
+ if (label_layout == LABEL_LAYOUT_TOP) {
+ Ref<Font> font = get_font("font", "Tree");
+ ms.height += font->get_height();
+ ms.height += get_constant("vseparation", "Tree");
+ }
+
+ return ms;
+}
+
+void EditorProperty::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_SORT_CHILDREN) {
+
+ Size2 size = get_size();
+ Rect2 rect;
+
+ if (label_layout == LABEL_LAYOUT_LEFT) {
+ int child_room = size.width / 2;
+
+ //compute room needed
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c)
+ continue;
+ if (c->is_set_as_toplevel())
+ continue;
+
+ Size2 minsize = c->get_combined_minimum_size();
+ child_room = MAX(child_room, minsize.width);
+ }
+
+ text_size = MAX(0, size.width - child_room + 4 * EDSCALE);
+
+ rect = Rect2(text_size, 0, size.width - text_size, size.height);
+ } else {
+ Ref<Font> font = get_font("font", "Tree");
+
+ text_size = size.width;
+ rect.position.x = 0;
+ rect.position.y = font->get_height() + get_constant("vseparation", "Tree");
+ rect.size = get_size();
+ rect.size.height -= rect.position.y;
+ }
+
+ if (keying) {
+ Ref<Texture> key;
+
+ if (use_keying_next()) {
+ key = get_icon("KeyNext", "EditorIcons");
+ } else {
+ key = get_icon("Key", "EditorIcons");
+ }
+
+ rect.size.x -= key->get_width() + get_constant("hseparator", "Tree");
+ }
+
+ //set children
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c)
+ continue;
+ if (c->is_set_as_toplevel())
+ continue;
+
+ fit_child_in_rect(c, rect);
+ }
+
+ update(); //need to redraw text
+ }
+
+ if (p_what == NOTIFICATION_DRAW) {
+ Ref<Font> font = get_font("font", "Tree");
+
+ Size2 size = get_size();
+ if (label_layout == LABEL_LAYOUT_TOP) {
+ size.height = font->get_height();
+ } else if (label_reference) {
+ size.height = label_reference->get_size().height;
+ }
+
+ if (selected) {
+ Ref<StyleBox> sb = get_stylebox("selected", "Tree");
+ draw_style_box(sb, Rect2(Vector2(), size));
+ }
+
+ Color color;
+ if (draw_red) {
+ color = get_color("error_color", "Editor");
+ } else {
+ color = get_color("font_color", "Tree");
+ }
+ if (label.find(".") != -1) {
+ color.a = 0.5; //this should be un-hacked honestly, as it's used for editor overrides
+ }
+
+ int ofs = 0;
+ if (checkable) {
+ Ref<Texture> checkbox;
+ if (checked)
+ checkbox = get_icon("checked", "CheckBox");
+ else
+ checkbox = get_icon("unchecked", "CheckBox");
+
+ Color color(1, 1, 1);
+ if (check_hover) {
+ color.r *= 1.2;
+ color.g *= 1.2;
+ color.b *= 1.2;
+ }
+ check_rect = Rect2(ofs, ((size.height - checkbox->get_height()) / 2), checkbox->get_width(), checkbox->get_height());
+ draw_texture(checkbox, check_rect.position, color);
+ ofs += get_constant("hseparator", "Tree");
+ ofs += checkbox->get_width();
+ } else {
+ check_rect = Rect2();
+ }
+
+ int text_limit = text_size;
+
+ if (can_revert) {
+ Ref<Texture> reload_icon = get_icon("ReloadSmall", "EditorIcons");
+ text_limit -= reload_icon->get_width() + get_constant("hseparator", "Tree") * 2;
+ revert_rect = Rect2(text_limit + get_constant("hseparator", "Tree"), (size.height - reload_icon->get_height()) / 2, reload_icon->get_width(), reload_icon->get_height());
+
+ Color color(1, 1, 1);
+ if (revert_hover) {
+ color.r *= 1.2;
+ color.g *= 1.2;
+ color.b *= 1.2;
+ }
+
+ draw_texture(reload_icon, revert_rect.position, color);
+ } else {
+ revert_rect = Rect2();
+ }
+
+ int v_ofs = (size.height - font->get_height()) / 2;
+ draw_string(font, Point2(ofs, v_ofs + font->get_ascent()), label, color, text_limit);
+
+ if (keying) {
+ Ref<Texture> key;
+
+ if (use_keying_next()) {
+ key = get_icon("KeyNext", "EditorIcons");
+ } else {
+ key = get_icon("Key", "EditorIcons");
+ }
+
+ ofs = size.width - key->get_width() - get_constant("hseparator", "Tree");
+
+ Color color(1, 1, 1);
+ if (keying_hover) {
+ color.r *= 1.2;
+ color.g *= 1.2;
+ color.b *= 1.2;
+ }
+ keying_rect = Rect2(ofs, ((size.height - key->get_height()) / 2), key->get_width(), key->get_height());
+ draw_texture(key, keying_rect.position, color);
+ } else {
+ keying_rect = Rect2();
+ }
+
+ //int vs = get_constant("vseparation", "Tree");
+ Color guide_color = get_color("guide_color", "Tree");
+ int vs_height = get_size().height; // vs / 2;
+ draw_line(Point2(0, vs_height), Point2(get_size().width, vs_height), guide_color);
+ }
+}
+
+void EditorProperty::set_label(const String &p_label) {
+ label = p_label;
+ update();
+}
+
+String EditorProperty::get_label() const {
+ return label;
+}
+
+Object *EditorProperty::get_edited_object() {
+ return object;
+}
+
+StringName EditorProperty::get_edited_property() {
+ return property;
+}
+
+void EditorProperty::update_property() {
+ if (get_script_instance())
+ get_script_instance()->call("update_property");
+}
+
+void EditorProperty::set_read_only(bool p_read_only) {
+ read_only = p_read_only;
+}
+
+bool EditorProperty::is_read_only() const {
+ return read_only;
+}
+
+bool EditorProperty::_might_be_in_instance() {
+
+ if (!object)
+ return false;
+
+ Node *node = Object::cast_to<Node>(object);
+
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+
+ bool might_be = false;
+
+ while (node) {
+
+ if (node->get_scene_instance_state().is_valid()) {
+ might_be = true;
+ break;
+ }
+ if (node == edited_scene) {
+ if (node->get_scene_inherited_state().is_valid()) {
+ might_be = true;
+ break;
+ }
+ might_be = false;
+ break;
+ }
+ node = node->get_owner();
+ }
+
+ return might_be; // or might not be
+}
+
+bool EditorProperty::_get_instanced_node_original_property(const StringName &p_prop, Variant &value) {
+
+ Node *node = Object::cast_to<Node>(object);
+
+ if (!node)
+ return false;
+
+ Node *orig = node;
+
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+
+ bool found = false;
+
+ while (node) {
+
+ Ref<SceneState> ss;
+
+ if (node == edited_scene) {
+ ss = node->get_scene_inherited_state();
+
+ } else {
+ ss = node->get_scene_instance_state();
+ }
+
+ if (ss.is_valid()) {
+
+ NodePath np = node->get_path_to(orig);
+ int node_idx = ss->find_node_by_path(np);
+ if (node_idx >= 0) {
+ bool lfound = false;
+ Variant lvar;
+ lvar = ss->get_property_value(node_idx, p_prop, lfound);
+ if (lfound) {
+
+ found = true;
+ value = lvar;
+ }
+ }
+ }
+ if (node == edited_scene) {
+ //just in case
+ break;
+ }
+ node = node->get_owner();
+ }
+
+ return found;
+}
+
+bool EditorProperty::_is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage) {
+
+ // this is a pretty difficult function, because a property may not be saved but may have
+ // the flag to not save if one or if zero
+
+ {
+ Node *node = Object::cast_to<Node>(object);
+ if (!node)
+ return false;
+
+ Node *edited_scene = EditorNode::get_singleton()->get_edited_scene();
+ bool found_state = false;
+
+ while (node) {
+
+ Ref<SceneState> ss;
+
+ if (node == edited_scene) {
+ ss = node->get_scene_inherited_state();
+
+ } else {
+ ss = node->get_scene_instance_state();
+ }
+
+ if (ss.is_valid()) {
+ found_state = true;
+ }
+ if (node == edited_scene) {
+ //just in case
+ break;
+ }
+ node = node->get_owner();
+ }
+
+ if (!found_state)
+ return false; //pointless to check if we are not comparing against anything.
+ }
+
+ if (p_orig.get_type() == Variant::NIL) {
+ // not found (was not saved)
+ // check if it was not saved due to being zero or one
+ if (p_current.is_zero() && property_usage & PROPERTY_USAGE_STORE_IF_NONZERO)
+ return false;
+ if (p_current.is_one() && property_usage & PROPERTY_USAGE_STORE_IF_NONONE)
+ return false;
+ }
+
+ if (p_current.get_type() == Variant::REAL && p_orig.get_type() == Variant::REAL) {
+ float a = p_current;
+ float b = p_orig;
+
+ return Math::abs(a - b) > CMP_EPSILON; //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
+ }
+
+ return bool(Variant::evaluate(Variant::OP_NOT_EQUAL, p_current, p_orig));
+}
+
+bool EditorProperty::_is_instanced_node_with_original_property_different() {
+
+ bool mbi = _might_be_in_instance();
+ if (mbi) {
+ Variant vorig;
+ int usage = property_usage & (PROPERTY_USAGE_STORE_IF_NONONE | PROPERTY_USAGE_STORE_IF_NONZERO);
+ if (_get_instanced_node_original_property(property, vorig) || usage) {
+ Variant v = object->get(property);
+
+ if (_is_property_different(v, vorig, usage)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void EditorProperty::update_reload_status() {
+
+ if (property == StringName())
+ return; //no property, so nothing to do
+
+ bool has_reload = false;
+
+ if (_is_instanced_node_with_original_property_different()) {
+ has_reload = true;
+ }
+
+ if (object->call("property_can_revert", property).operator bool()) {
+
+ has_reload = true;
+ }
+
+ if (!has_reload && !object->get_script().is_null()) {
+ Ref<Script> scr = object->get_script();
+ Variant orig_value;
+ if (scr->get_property_default_value(property, orig_value)) {
+ if (orig_value != object->get(property)) {
+ has_reload = true;
+ }
+ }
+ }
+
+ if (has_reload != can_revert) {
+ can_revert = has_reload;
+ update();
+ }
+}
+
+bool EditorProperty::use_keying_next() const {
+ return false;
+}
+void EditorProperty::set_checkable(bool p_checkable) {
+
+ checkable = p_checkable;
+ update();
+ queue_sort();
+}
+
+bool EditorProperty::is_checkable() const {
+
+ return checkable;
+}
+
+void EditorProperty::set_checked(bool p_checked) {
+
+ checked = p_checked;
+ update();
+}
+
+bool EditorProperty::is_checked() const {
+
+ return checked;
+}
+
+void EditorProperty::set_draw_red(bool p_draw_red) {
+
+ draw_red = p_draw_red;
+ update();
+}
+
+void EditorProperty::set_keying(bool p_keying) {
+ keying = p_keying;
+ update();
+ queue_sort();
+}
+
+bool EditorProperty::is_keying() const {
+ return keying;
+}
+
+bool EditorProperty::is_draw_red() const {
+
+ return draw_red;
+}
+
+void EditorProperty::_focusable_focused(int p_index) {
+
+ bool already_selected = selected;
+ selected = true;
+ selected_focusable = p_index;
+ update();
+ if (!already_selected && selected) {
+ emit_signal("selected", property, selected_focusable);
+ }
+}
+
+void EditorProperty::add_focusable(Control *p_control) {
+
+ p_control->connect("focus_entered", this, "_focusable_focused", varray(focusables.size()));
+ focusables.push_back(p_control);
+}
+
+void EditorProperty::select(int p_focusable) {
+
+ bool already_selected = selected;
+
+ if (p_focusable >= 0) {
+ ERR_FAIL_INDEX(p_focusable, focusables.size());
+ focusables[p_focusable]->grab_focus();
+ } else {
+ selected = true;
+ update();
+ }
+
+ if (!already_selected && selected) {
+ emit_signal("selected", property, selected_focusable);
+ }
+}
+
+void EditorProperty::deselect() {
+ selected = false;
+ selected_focusable = -1;
+ update();
+}
+
+bool EditorProperty::is_selected() const {
+ return selected;
+}
+
+void EditorProperty::_gui_input(const Ref<InputEvent> &p_event) {
+
+ if (property == StringName())
+ return;
+
+ Ref<InputEventMouse> me = p_event;
+
+ if (me.is_valid()) {
+
+ bool button_left = me->get_button_mask() & BUTTON_MASK_LEFT;
+
+ bool new_keying_hover = keying_rect.has_point(me->get_position()) && !button_left;
+ if (new_keying_hover != keying_hover) {
+ keying_hover = new_keying_hover;
+ update();
+ }
+
+ bool new_revert_hover = revert_rect.has_point(me->get_position()) && !button_left;
+ if (new_revert_hover != revert_hover) {
+ revert_hover = new_revert_hover;
+ update();
+ }
+
+ bool new_check_hover = check_rect.has_point(me->get_position()) && !button_left;
+ if (new_check_hover != check_hover) {
+ check_hover = new_check_hover;
+ update();
+ }
+ }
+
+ Ref<InputEventMouseButton> mb = p_event;
+
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+
+ if (!selected) {
+ selected = true;
+ emit_signal("selected", property, -1);
+ update();
+ }
+
+ if (keying_rect.has_point(mb->get_position())) {
+ emit_signal("property_keyed", property);
+ }
+
+ if (revert_rect.has_point(mb->get_position())) {
+
+ Variant vorig;
+
+ if (_might_be_in_instance() && _get_instanced_node_original_property(property, vorig)) {
+
+ emit_signal("property_changed", property, vorig.duplicate(true));
+ update_property();
+ return;
+ }
+
+ if (object->call("property_can_revert", property).operator bool()) {
+ Variant rev = object->call("property_get_revert", property);
+ emit_signal("property_changed", property, rev);
+ update_property();
+ }
+
+ if (!object->get_script().is_null()) {
+ Ref<Script> scr = object->get_script();
+ Variant orig_value;
+ if (scr->get_property_default_value(property, orig_value)) {
+ emit_signal("property_changed", property, orig_value);
+ update_property();
+ }
+ }
+ }
+ if (check_rect.has_point(mb->get_position())) {
+ checked = !checked;
+ update();
+ emit_signal("property_checked", property, checked);
+ }
+ }
+}
+
+void EditorProperty::set_label_reference(Control *p_control) {
+
+ label_reference = p_control;
+}
+
+Variant EditorProperty::get_drag_data(const Point2 &p_point) {
+
+ if (property == StringName())
+ return Variant();
+
+ Dictionary dp;
+ dp["type"] = "obj_property";
+ dp["object"] = object;
+ dp["property"] = property;
+ dp["value"] = object->get(property);
+
+ Label *label = memnew(Label);
+ label->set_text(property);
+ set_drag_preview(label);
+ return dp;
+}
+
+void EditorProperty::set_label_layout(LabelLayout p_layout) {
+ label_layout = p_layout;
+ queue_sort();
+ update();
+}
+
+void EditorProperty::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_label", "text"), &EditorProperty::set_label);
+ ClassDB::bind_method(D_METHOD("get_label"), &EditorProperty::get_label);
+
+ ClassDB::bind_method(D_METHOD("set_read_only", "read_only"), &EditorProperty::set_read_only);
+ ClassDB::bind_method(D_METHOD("is_read_only"), &EditorProperty::is_read_only);
+
+ ClassDB::bind_method(D_METHOD("set_checkable", "checkable"), &EditorProperty::set_checkable);
+ ClassDB::bind_method(D_METHOD("is_checkable"), &EditorProperty::is_checkable);
+
+ ClassDB::bind_method(D_METHOD("set_checked", "checked"), &EditorProperty::set_checked);
+ ClassDB::bind_method(D_METHOD("is_checked"), &EditorProperty::is_checked);
+
+ ClassDB::bind_method(D_METHOD("set_draw_red", "draw_red"), &EditorProperty::set_draw_red);
+ ClassDB::bind_method(D_METHOD("is_draw_red"), &EditorProperty::is_draw_red);
+
+ ClassDB::bind_method(D_METHOD("set_keying", "keying"), &EditorProperty::set_keying);
+ ClassDB::bind_method(D_METHOD("is_keying"), &EditorProperty::is_keying);
+
+ ClassDB::bind_method(D_METHOD("get_edited_property"), &EditorProperty::get_edited_property);
+ ClassDB::bind_method(D_METHOD("get_edited_object"), &EditorProperty::get_edited_object);
+
+ ClassDB::bind_method(D_METHOD("_gui_input"), &EditorProperty::_gui_input);
+ ClassDB::bind_method(D_METHOD("_focusable_focused"), &EditorProperty::_focusable_focused);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "label"), "set_label", "get_label");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_only"), "set_read_only", "is_read_only");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checkable"), "set_checkable", "is_checkable");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "checked"), "set_checked", "is_checked");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_red"), "set_draw_red", "is_draw_red");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keying"), "set_keying", "is_keying");
+ ADD_SIGNAL(MethodInfo("property_changed", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
+ ADD_SIGNAL(MethodInfo("multiple_properties_changed", PropertyInfo(Variant::POOL_STRING_ARRAY, "properties"), PropertyInfo(Variant::ARRAY, "value")));
+ ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property")));
+ ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::STRING, "bool")));
+ ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
+ ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::STRING, "property"), PropertyInfo(Variant::INT, "id")));
+ ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "focusable_idx")));
+
+ MethodInfo vm;
+ vm.name = "update_property";
+ BIND_VMETHOD(vm);
+}
+
+EditorProperty::EditorProperty() {
+
+ text_size = 0;
+ read_only = false;
+ checkable = false;
+ checked = false;
+ draw_red = false;
+ keying = false;
+ keying_hover = false;
+ revert_hover = false;
+ check_hover = false;
+ can_revert = false;
+ property_usage = 0;
+ selected = false;
+ selected_focusable = -1;
+ label_reference = NULL;
+ label_layout = LABEL_LAYOUT_LEFT;
+}
+////////////////////////////////////////////////
+////////////////////////////////////////////////
+
+void EditorInspectorPlugin::add_custom_control(Control *control) {
+
+ AddedEditor ae;
+ ae.property_editor = control;
+ added_editors.push_back(ae);
+}
+
+void EditorInspectorPlugin::add_property_editor(const String &p_for_property, Control *p_prop) {
+
+ ERR_FAIL_COND(Object::cast_to<EditorProperty>(p_prop) == NULL);
+
+ AddedEditor ae;
+ ae.properties.push_back(p_for_property);
+ ae.property_editor = p_prop;
+ added_editors.push_back(ae);
+}
+
+void EditorInspectorPlugin::add_property_editor_for_multiple_properties(const String &p_label, const Vector<String> &p_properties, Control *p_prop) {
+
+ AddedEditor ae;
+ ae.properties = p_properties;
+ ae.property_editor = p_prop;
+ ae.label = p_label;
+ added_editors.push_back(ae);
+}
+
+bool EditorInspectorPlugin::can_handle(Object *p_object) {
+
+ if (get_script_instance()) {
+ return get_script_instance()->call("can_handle", p_object);
+ }
+ return false;
+}
+void EditorInspectorPlugin::parse_begin(Object *p_object) {
+
+ if (get_script_instance()) {
+ get_script_instance()->call("parse_begin", p_object);
+ }
+}
+bool EditorInspectorPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
+
+ if (get_script_instance()) {
+ Variant arg[6] = {
+ p_object, p_type, p_path, p_hint, p_hint_text, p_usage
+ };
+ const Variant *argptr[6] = {
+ &arg[0], &arg[1], &arg[2], &arg[3], &arg[4], &arg[5]
+ };
+
+ Variant::CallError err;
+ return get_script_instance()->call("parse_property", (const Variant **)&argptr, 6, err);
+ }
+ return false;
+}
+void EditorInspectorPlugin::parse_end() {
+
+ if (get_script_instance()) {
+ get_script_instance()->call("parse_end");
+ }
+}
+
+void EditorInspectorPlugin::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("add_custom_control", "control"), &EditorInspectorPlugin::add_custom_control);
+ ClassDB::bind_method(D_METHOD("add_property_editor", "property", "editor"), &EditorInspectorPlugin::add_property_editor);
+ ClassDB::bind_method(D_METHOD("add_property_editor_for_multiple_properties", "label", "properties", "editor"), &EditorInspectorPlugin::add_property_editor_for_multiple_properties);
+
+ MethodInfo vm;
+ vm.name = "can_handle";
+ vm.arguments.push_back(PropertyInfo(Variant::OBJECT, "object"));
+ BIND_VMETHOD(vm);
+ vm.name = "parse_begin";
+ BIND_VMETHOD(vm);
+ vm.name = "parse_property";
+ vm.return_val.type = Variant::BOOL;
+ vm.arguments.push_back(PropertyInfo(Variant::INT, "type"));
+ vm.arguments.push_back(PropertyInfo(Variant::STRING, "path"));
+ vm.arguments.push_back(PropertyInfo(Variant::INT, "hint"));
+ vm.arguments.push_back(PropertyInfo(Variant::STRING, "hint_text"));
+ vm.arguments.push_back(PropertyInfo(Variant::INT, "usage"));
+ BIND_VMETHOD(vm);
+ vm.arguments.clear();
+ vm.return_val.type = Variant::NIL;
+ vm.name = "parse_end";
+ BIND_VMETHOD(vm);
+}
+
+////////////////////////////////////////////////
+////////////////////////////////////////////////
+
+void EditorInspectorCategory::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_DRAW) {
+
+ draw_rect(Rect2(Vector2(), get_size()), bg_color);
+ Ref<Font> font = get_font("font", "Tree");
+
+ int hs = get_constant("hseparation", "Tree");
+
+ int w = font->get_string_size(label).width;
+ if (icon.is_valid()) {
+ w += hs + icon->get_width();
+ }
+
+ int ofs = (get_size().width - w) / 2;
+
+ if (icon.is_valid()) {
+ draw_texture(icon, Point2(ofs, (get_size().height - icon->get_height()) / 2).floor());
+ ofs += hs + icon->get_width();
+ }
+
+ Color color = get_color("font_color", "Tree");
+ draw_string(font, Point2(ofs, font->get_ascent() + (get_size().height - font->get_height()) / 2).floor(), label, color, get_size().width);
+ }
+}
+
+Size2 EditorInspectorCategory::get_minimum_size() const {
+
+ Ref<Font> font = get_font("font", "Tree");
+
+ Size2 ms;
+ ms.width = 1;
+ ms.height = font->get_height();
+ if (icon.is_valid()) {
+ ms.height = MAX(icon->get_height(), ms.height);
+ }
+ ms.height += get_constant("vseparation", "Tree");
+
+ return ms;
+}
+
+EditorInspectorCategory::EditorInspectorCategory() {
+}
+
+////////////////////////////////////////////////
+////////////////////////////////////////////////
+
+void EditorInspectorSection::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_SORT_CHILDREN) {
+
+ Ref<Font> font = get_font("font", "Tree");
+ Ref<Texture> arrow;
+
+#ifdef TOOLS_ENABLED
+ if (foldable) {
+ if (object->editor_is_section_unfolded(section)) {
+ arrow = get_icon("arrow", "Tree");
+ } else {
+ arrow = get_icon("arrow_collapsed", "Tree");
+ }
+ }
+#endif
+
+ Size2 size = get_size();
+ Point2 offset;
+ offset.y = font->get_height();
+ if (arrow.is_valid()) {
+ offset.y = MAX(offset.y, arrow->get_height());
+ }
+
+ offset.y += get_constant("vseparation", "Tree");
+ offset.x += get_constant("item_margin", "Tree");
+
+ Rect2 rect(offset, size - offset);
+
+ //set children
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c)
+ continue;
+ if (c->is_set_as_toplevel())
+ continue;
+ if (!c->is_visible_in_tree())
+ continue;
+
+ fit_child_in_rect(c, rect);
+ }
+
+ update(); //need to redraw text
+ }
+
+ if (p_what == NOTIFICATION_DRAW) {
+
+ Ref<Texture> arrow;
+
+#ifdef TOOLS_ENABLED
+ if (foldable) {
+ if (object->editor_is_section_unfolded(section)) {
+ arrow = get_icon("arrow", "Tree");
+ } else {
+ arrow = get_icon("arrow_collapsed", "Tree");
+ }
+ }
+#endif
+
+ Ref<Font> font = get_font("font", "Tree");
+
+ int h = font->get_height();
+ if (arrow.is_valid()) {
+ h = MAX(h, arrow->get_height());
+ }
+ h += get_constant("vseparation", "Tree");
+
+ draw_rect(Rect2(Vector2(), Vector2(get_size().width, h)), bg_color);
+
+ int hs = get_constant("hseparation", "Tree");
+
+ int ofs = 0;
+ if (arrow.is_valid()) {
+ draw_texture(arrow, Point2(ofs, (h - arrow->get_height()) / 2).floor());
+ ofs += hs + arrow->get_width();
+ }
+
+ Color color = get_color("font_color", "Tree");
+ draw_string(font, Point2(ofs, font->get_ascent() + (h - font->get_height()) / 2).floor(), label, color, get_size().width);
+ }
+}
+
+Size2 EditorInspectorSection::get_minimum_size() const {
+
+ Size2 ms;
+ for (int i = 0; i < get_child_count(); i++) {
+
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c)
+ continue;
+ if (c->is_set_as_toplevel())
+ continue;
+ if (!c->is_visible())
+ continue;
+ Size2 minsize = c->get_combined_minimum_size();
+ ms.width = MAX(ms.width, minsize.width);
+ ms.height = MAX(ms.height, minsize.height);
+ }
+
+ Ref<Font> font = get_font("font", "Tree");
+ ms.height += font->get_ascent() + get_constant("vseparation", "Tree");
+ ms.width += get_constant("item_margin", "Tree");
+
+ return ms;
+}
+
+void EditorInspectorSection::setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable) {
+
+ section = p_section;
+ label = p_label;
+ object = p_object;
+ bg_color = p_bg_color;
+ foldable = p_foldable;
+
+#ifdef TOOLS_ENABLED
+ if (foldable) {
+ if (object->editor_is_section_unfolded(section)) {
+ vbox->show();
+ } else {
+ vbox->hide();
+ }
+ }
+ // void editor_set_section_unfold(const String &p_section, bool p_unfolded);
+
+#endif
+}
+
+void EditorInspectorSection::_gui_input(const Ref<InputEvent> &p_event) {
+
+ if (!foldable)
+ return;
+
+#ifdef TOOLS_ENABLED
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) {
+ bool unfold = !object->editor_is_section_unfolded(section);
+ object->editor_set_section_unfold(section, unfold);
+ if (unfold) {
+ vbox->show();
+ } else {
+ vbox->hide();
+ }
+ }
+#endif
+}
+
+VBoxContainer *EditorInspectorSection::get_vbox() {
+ return vbox;
+}
+
+void EditorInspectorSection::unfold() {
+
+ if (!foldable)
+ return;
+#ifdef TOOLS_ENABLED
+
+ object->editor_set_section_unfold(section, true);
+ vbox->show();
+ update();
+#endif
+}
+
+void EditorInspectorSection::fold() {
+ if (!foldable)
+ return;
+
+#ifdef TOOLS_ENABLED
+
+ object->editor_set_section_unfold(section, false);
+ vbox->hide();
+ update();
+#endif
+}
+
+void EditorInspectorSection::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("setup", "section", "label", "object", "bg_color", "foldable"), &EditorInspectorSection::setup);
+ ClassDB::bind_method(D_METHOD("get_vbox"), &EditorInspectorSection::get_vbox);
+ ClassDB::bind_method(D_METHOD("unfold"), &EditorInspectorSection::unfold);
+ ClassDB::bind_method(D_METHOD("fold"), &EditorInspectorSection::fold);
+ ClassDB::bind_method(D_METHOD("_gui_input"), &EditorInspectorSection::_gui_input);
+}
+
+EditorInspectorSection::EditorInspectorSection() {
+ object = NULL;
+ foldable = false;
+ vbox = memnew(VBoxContainer);
+ add_child(vbox);
+}
+
+////////////////////////////////////////////////
+////////////////////////////////////////////////
+
+Ref<EditorInspectorPlugin> EditorInspector::inspector_plugins[MAX_PLUGINS];
+int EditorInspector::inspector_plugin_count = 0;
+
+void EditorInspector::add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
+
+ ERR_FAIL_COND(inspector_plugin_count == MAX_PLUGINS);
+
+ for (int i = 0; i < inspector_plugin_count; i++) {
+ if (inspector_plugins[i] == p_plugin)
+ return; //already exists
+ }
+ inspector_plugins[inspector_plugin_count++] = p_plugin;
+}
+
+void EditorInspector::remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin) {
+
+ ERR_FAIL_COND(inspector_plugin_count == MAX_PLUGINS);
+
+ int idx = -1;
+ for (int i = 0; i < inspector_plugin_count; i++) {
+ if (inspector_plugins[i] == p_plugin) {
+ idx = i;
+ break;
+ }
+ }
+
+ for (int i = idx; i < inspector_plugin_count - 1; i++) {
+ inspector_plugins[i] = inspector_plugins[i + 1];
+ }
+ inspector_plugin_count--;
+}
+
+void EditorInspector::cleanup_plugins() {
+ for (int i = 0; i < inspector_plugin_count; i++) {
+ inspector_plugins[i].unref();
+ }
+ inspector_plugin_count = 0;
+}
+
+void EditorInspector::set_undo_redo(UndoRedo *p_undo_redo) {
+ undo_redo = p_undo_redo;
+}
+
+String EditorInspector::get_selected_path() const {
+
+ return property_selected;
+}
+
+void EditorInspector::update_tree() {
+
+ //to update properly if all is refreshed
+ StringName current_selected = property_selected;
+ int current_focusable = property_focusable;
+
+ _clear();
+
+ if (!object)
+ return;
+
+ List<Ref<EditorInspectorPlugin> > valid_plugins;
+
+ for (int i = inspector_plugin_count - 1; i >= 0; i--) { //start by last, so lastly added can override newly added
+ if (!inspector_plugins[i]->can_handle(object))
+ continue;
+ valid_plugins.push_back(inspector_plugins[i]);
+ }
+
+ bool draw_red = false;
+
+ {
+ Node *nod = Object::cast_to<Node>(object);
+ Node *es = EditorNode::get_singleton()->get_edited_scene();
+ if (nod && es != nod && nod->get_owner() != es) {
+ draw_red = true;
+ }
+ }
+
+ // TreeItem *current_category = NULL;
+
+ String filter = search_box ? search_box->get_text() : "";
+ String group;
+ String group_base;
+
+ List<PropertyInfo> plist;
+ object->get_property_list(&plist, true);
+
+ HashMap<String, VBoxContainer *> item_path;
+ item_path[""] = main_vbox;
+
+ Color sscolor = get_color("prop_subsection", "Editor");
+
+ for (List<PropertyInfo>::Element *I = plist.front(); I; I = I->next()) {
+
+ PropertyInfo &p = I->get();
+
+ //make sure the property can be edited
+
+ if (p.usage & PROPERTY_USAGE_GROUP) {
+
+ group = p.name;
+ group_base = p.hint_string;
+
+ continue;
+
+ } else if (p.usage & PROPERTY_USAGE_CATEGORY) {
+
+ group = "";
+ group_base = "";
+
+ if (!show_categories)
+ continue;
+
+ List<PropertyInfo>::Element *N = I->next();
+ bool valid = true;
+ //if no properties in category, skip
+ while (N) {
+ if (N->get().usage & PROPERTY_USAGE_EDITOR)
+ break;
+ if (N->get().usage & PROPERTY_USAGE_CATEGORY) {
+ valid = false;
+ break;
+ }
+ N = N->next();
+ }
+ if (!valid)
+ continue; //empty, ignore
+
+ EditorInspectorCategory *category = memnew(EditorInspectorCategory);
+ main_vbox->add_child(category);
+
+ String type = p.name;
+ if (has_icon(type, "EditorIcons"))
+ category->icon = get_icon(type, "EditorIcons");
+ else
+ category->icon = get_icon("Object", "EditorIcons");
+ category->label = type;
+
+ category->bg_color = get_color("prop_category", "Editor");
+ if (use_doc_hints) {
+ StringName type = p.name;
+ if (!class_descr_cache.has(type)) {
+
+ String descr;
+ DocData *dd = EditorHelp::get_doc_data();
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(type);
+ if (E) {
+ descr = E->get().brief_description;
+ }
+ class_descr_cache[type] = descr.word_wrap(80);
+ }
+
+ category->set_tooltip(TTR("Class:") + " " + p.name + (class_descr_cache[type] == "" ? "" : "\n\n" + class_descr_cache[type]));
+ }
+ continue;
+
+ } else if (!(p.usage & PROPERTY_USAGE_EDITOR))
+ continue;
+
+ if (hide_script && p.name == "script")
+ continue;
+
+ String basename = p.name;
+ if (group != "") {
+ if (group_base != "") {
+ if (basename.begins_with(group_base)) {
+ basename = basename.replace_first(group_base, "");
+ } else if (group_base.begins_with(basename)) {
+ //keep it, this is used pretty often
+ } else {
+ group = ""; //no longer using group base, clear
+ }
+ }
+ }
+
+ if (group != "") {
+ basename = group + "/" + basename;
+ }
+
+ String name = (basename.find("/") != -1) ? basename.right(basename.find_last("/") + 1) : basename;
+
+ if (capitalize_paths) {
+ int dot = name.find(".");
+ if (dot != -1) {
+ String ov = name.right(dot);
+ name = name.substr(0, dot);
+ name = name.camelcase_to_underscore().capitalize();
+ name += ov;
+
+ } else {
+ name = name.camelcase_to_underscore().capitalize();
+ }
+ }
+
+ String path = basename.left(basename.find_last("/"));
+
+ if (use_filter && filter != "") {
+
+ String cat = path;
+
+ if (capitalize_paths)
+ cat = cat.capitalize();
+
+ if (!filter.is_subsequence_ofi(cat) && !filter.is_subsequence_ofi(name))
+ continue;
+ }
+
+ VBoxContainer *current_vbox = main_vbox;
+
+ {
+
+ String acc_path = "";
+ int level = 1;
+ for (int i = 0; i < path.get_slice_count("/"); i++) {
+ String path_name = path.get_slice("/", i);
+ if (i > 0)
+ acc_path += "/";
+ acc_path += path_name;
+ if (!item_path.has(acc_path)) {
+ EditorInspectorSection *section = memnew(EditorInspectorSection);
+ current_vbox->add_child(section);
+ sections.push_back(section);
+
+ if (capitalize_paths)
+ path_name = path_name.capitalize();
+ Color c = sscolor;
+ c.a /= level;
+ section->setup(path_name, acc_path, object, c, use_folding);
+
+ item_path[acc_path] = section->get_vbox();
+ }
+ current_vbox = item_path[acc_path];
+ level = (MIN(level + 1, 4));
+ }
+ }
+
+ bool checkable = false;
+ bool checked = false;
+ if (p.usage & PROPERTY_USAGE_CHECKABLE) {
+ checkable = true;
+ checked = p.usage & PROPERTY_USAGE_CHECKED;
+ }
+
+ String doc_hint;
+
+ if (use_doc_hints) {
+
+ StringName classname = object->get_class_name();
+ StringName propname = p.name;
+ String descr;
+ bool found = false;
+
+ Map<StringName, Map<StringName, String> >::Element *E = descr_cache.find(classname);
+ if (E) {
+ Map<StringName, String>::Element *F = E->get().find(propname);
+ if (F) {
+ found = true;
+ descr = F->get();
+ }
+ }
+
+ if (!found) {
+ DocData *dd = EditorHelp::get_doc_data();
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(classname);
+ while (E && descr == String()) {
+ for (int i = 0; i < E->get().properties.size(); i++) {
+ if (E->get().properties[i].name == propname.operator String()) {
+ descr = E->get().properties[i].description.strip_edges().word_wrap(80);
+ break;
+ }
+ }
+ if (!E->get().inherits.empty()) {
+ E = dd->class_list.find(E->get().inherits);
+ } else {
+ break;
+ }
+ }
+ descr_cache[classname][propname] = descr;
+ }
+
+ doc_hint = descr;
+ }
+
+#if 0
+ if (p.name == selected_property) {
+
+ item->select(1);
+ }
+#endif
+ for (List<Ref<EditorInspectorPlugin> >::Element *E = valid_plugins.front(); E; E = E->next()) {
+ Ref<EditorInspectorPlugin> ped = E->get();
+ ped->parse_property(object, p.type, p.name, p.hint, p.hint_string, p.usage);
+ for (List<EditorInspectorPlugin::AddedEditor>::Element *F = ped->added_editors.front(); F; F = F->next()) {
+
+ EditorProperty *ep = Object::cast_to<EditorProperty>(F->get().property_editor);
+ current_vbox->add_child(F->get().property_editor);
+
+ if (ep) {
+
+ ep->object = object;
+ ep->connect("property_changed", this, "_property_changed");
+ ep->connect("property_keyed", this, "_property_keyed");
+ ep->connect("property_checked", this, "_property_checked");
+ ep->connect("selected", this, "_property_selected");
+ ep->connect("multiple_properties_changed", this, "_multiple_properties_changed");
+ ep->connect("resource_selected", this, "_resource_selected", varray(), CONNECT_DEFERRED);
+ ep->connect("object_id_selected", this, "_object_id_selected", varray(), CONNECT_DEFERRED);
+ ep->set_tooltip(doc_hint);
+ ep->set_draw_red(draw_red);
+ ep->set_checkable(checkable);
+ ep->set_checked(checked);
+ ep->set_keying(keying);
+
+ if (F->get().properties.size()) {
+
+ if (F->get().properties.size() == 1) {
+ //since it's one, associate:
+ ep->property = F->get().properties[0];
+ ep->property_usage = p.usage;
+ //and set label?
+ }
+
+ if (F->get().label != String()) {
+ ep->set_label(F->get().label);
+ } else {
+ //use existin one
+ ep->set_label(name);
+ }
+ for (int i = 0; i < F->get().properties.size(); i++) {
+ String prop = F->get().properties[i];
+
+ if (!editor_property_map.has(prop)) {
+ editor_property_map[prop] = List<EditorProperty *>();
+ }
+ editor_property_map[prop].push_back(ep);
+ }
+ }
+
+ ep->set_read_only(read_only);
+ ep->update_property();
+ ep->update_reload_status();
+
+ if (current_selected && ep->property == current_selected) {
+ ep->select(current_focusable);
+ }
+ }
+ }
+ ped->added_editors.clear();
+ }
+ }
+
+ //see if this property exists and should be kept
+}
+void EditorInspector::update_property(const String &p_prop) {
+ if (!editor_property_map.has(p_prop))
+ return;
+
+ for (List<EditorProperty *>::Element *E = editor_property_map[p_prop].front(); E; E = E->next()) {
+ E->get()->update_property();
+ E->get()->update_reload_status();
+ }
+}
+
+void EditorInspector::_clear() {
+
+ editor_property_map.clear();
+ sections.clear();
+ pending.clear();
+ property_selected = StringName();
+ property_focusable = -1;
+ while (main_vbox->get_child_count()) {
+ memdelete(main_vbox->get_child(0));
+ }
+}
+
+void EditorInspector::refresh() {
+
+ if (refresh_countdown > 0)
+ return;
+ refresh_countdown = EditorSettings::get_singleton()->get("docks/property_editor/auto_refresh_interval");
+}
+
+void EditorInspector::edit(Object *p_object) {
+ if (object != p_object) {
+ _clear();
+ }
+
+ if (object) {
+
+ object->remove_change_receptor(this);
+ }
+
+ object = p_object;
+ if (object) {
+ object->add_change_receptor(this);
+ update_tree();
+ }
+}
+
+void EditorInspector::set_keying(bool p_active) {
+ if (keying == p_active)
+ return;
+ keying = p_active;
+ update_tree();
+}
+void EditorInspector::set_read_only(bool p_read_only) {
+ read_only = p_read_only;
+ update_tree();
+}
+
+bool EditorInspector::is_capitalize_paths_enabled() const {
+
+ return capitalize_paths;
+}
+void EditorInspector::set_enable_capitalize_paths(bool p_capitalize) {
+ capitalize_paths = p_capitalize;
+ update_tree();
+}
+
+void EditorInspector::set_autoclear(bool p_enable) {
+ autoclear = p_enable;
+}
+
+void EditorInspector::set_show_categories(bool p_show) {
+ show_categories = p_show;
+ update_tree();
+}
+
+void EditorInspector::set_use_doc_hints(bool p_enable) {
+ use_doc_hints = p_enable;
+ update_tree();
+}
+void EditorInspector::set_hide_script(bool p_hide) {
+ hide_script = p_hide;
+ update_tree();
+}
+void EditorInspector::set_use_filter(bool p_use) {
+ use_filter = p_use;
+ update_tree();
+}
+void EditorInspector::register_text_enter(Node *p_line_edit) {
+ search_box = Object::cast_to<LineEdit>(p_line_edit);
+ if (search_box)
+ search_box->connect("text_changed", this, "_filter_changed");
+}
+
+void EditorInspector::_filter_changed(const String &p_text) {
+
+ update_tree();
+}
+
+void EditorInspector::set_subsection_selectable(bool p_selectable) {
+}
+
+void EditorInspector::set_property_selectable(bool p_selectable) {
+}
+
+void EditorInspector::set_use_folding(bool p_enable) {
+ use_folding = p_enable;
+ update_tree();
+}
+
+void EditorInspector::collapse_all_folding() {
+
+ for (List<EditorInspectorSection *>::Element *E = sections.front(); E; E = E->next()) {
+ E->get()->fold();
+ }
+}
+
+void EditorInspector::expand_all_folding() {
+ for (List<EditorInspectorSection *>::Element *E = sections.front(); E; E = E->next()) {
+ E->get()->unfold();
+ }
+}
+
+void EditorInspector::set_scroll_offset(int p_offset) {
+ set_v_scroll(p_offset);
+}
+
+int EditorInspector::get_scroll_offset() const {
+ return get_v_scroll();
+}
+
+void EditorInspector::_edit_request_change(Object *p_object, const String &p_property) {
+
+ if (object != p_object) //may be undoing/redoing for a non edited object, so ignore
+ return;
+
+ if (changing)
+ return;
+
+ if (p_property == String())
+ update_tree_pending = true;
+ else {
+ pending.insert(p_property);
+ }
+}
+
+void EditorInspector::_edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field) {
+
+ if (autoclear && editor_property_map.has(p_name)) {
+ for (List<EditorProperty *>::Element *E = editor_property_map[p_name].front(); E; E = E->next()) {
+ if (E->get()->is_checkable()) {
+ E->get()->set_checked(true);
+ }
+ }
+ }
+
+ if (!undo_redo || Object::cast_to<ArrayPropertyEdit>(object) || Object::cast_to<DictionaryPropertyEdit>(object)) { //kind of hacky
+
+ object->set(p_name, p_value);
+ if (p_refresh_all)
+ _edit_request_change(object, "");
+ else
+ _edit_request_change(object, p_name);
+
+ emit_signal(_prop_edited, p_name);
+
+ } else if (Object::cast_to<MultiNodeEdit>(object)) {
+
+ Object::cast_to<MultiNodeEdit>(object)->set_property_field(p_name, p_value, p_changed_field);
+ _edit_request_change(object, p_name);
+ emit_signal(_prop_edited, p_name);
+ } else {
+
+ undo_redo->create_action(TTR("Set") + " " + p_name, UndoRedo::MERGE_ENDS);
+ undo_redo->add_do_property(object, p_name, p_value);
+ undo_redo->add_undo_property(object, p_name, object->get(p_name));
+
+ if (p_refresh_all) {
+ undo_redo->add_do_method(this, "_edit_request_change", object, "");
+ undo_redo->add_undo_method(this, "_edit_request_change", object, "");
+ } else {
+
+ undo_redo->add_do_method(this, "_edit_request_change", object, p_name);
+ undo_redo->add_undo_method(this, "_edit_request_change", object, p_name);
+ }
+
+ Resource *r = Object::cast_to<Resource>(object);
+ if (r) {
+ if (!r->is_edited() && String(p_name) != "resource/edited") {
+ undo_redo->add_do_method(r, "set_edited", true);
+ undo_redo->add_undo_method(r, "set_edited", false);
+ }
+
+ if (String(p_name) == "resource_local_to_scene") {
+ bool prev = object->get(p_name);
+ bool next = p_value;
+ if (next) {
+ undo_redo->add_do_method(r, "setup_local_to_scene");
+ }
+ if (prev) {
+ undo_redo->add_undo_method(r, "setup_local_to_scene");
+ }
+ }
+ }
+ undo_redo->add_do_method(this, "emit_signal", _prop_edited, p_name);
+ undo_redo->add_undo_method(this, "emit_signal", _prop_edited, p_name);
+ changing++;
+ undo_redo->commit_action();
+ changing--;
+ }
+
+ if (editor_property_map.has(p_name)) {
+ for (List<EditorProperty *>::Element *E = editor_property_map[p_name].front(); E; E = E->next()) {
+ E->get()->update_reload_status();
+ }
+ }
+}
+
+void EditorInspector::_property_changed(const String &p_path, const Variant &p_value) {
+
+ _edit_set(p_path, p_value, false, "");
+}
+
+void EditorInspector::_multiple_properties_changed(Vector<String> p_paths, Array p_values) {
+
+ ERR_FAIL_COND(p_paths.size() == 0 || p_values.size() == 0);
+ ERR_FAIL_COND(p_paths.size() != p_values.size());
+ String names;
+ for (int i = 0; i < p_paths.size(); i++) {
+ if (i > 0)
+ names += ",";
+ names += p_paths[i];
+ }
+ undo_redo->create_action(TTR("Set Multiple:") + " " + names, UndoRedo::MERGE_ENDS);
+ for (int i = 0; i < p_paths.size(); i++) {
+ _edit_set(p_paths[i], p_values[i], false, "");
+ }
+ changing++;
+ undo_redo->commit_action();
+ changing--;
+}
+
+void EditorInspector::_property_keyed(const String &p_path) {
+
+ if (!object)
+ return;
+
+ emit_signal("property_keyed", p_path, object->get(p_path), false); //second param is deprecated
+}
+
+void EditorInspector::_property_checked(const String &p_path, bool p_checked) {
+
+ if (!object)
+ return;
+
+ //property checked
+ if (autoclear) {
+
+ if (!p_checked) {
+ object->set(p_path, Variant());
+ } else {
+
+ Variant to_create;
+ List<PropertyInfo> pinfo;
+ object->get_property_list(&pinfo);
+ for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+ if (E->get().name == p_path) {
+ Variant::CallError ce;
+ to_create = Variant::construct(E->get().type, NULL, 0, ce);
+ break;
+ }
+ }
+ object->set(p_path, to_create);
+ }
+
+ if (editor_property_map.has(p_path)) {
+ for (List<EditorProperty *>::Element *E = editor_property_map[p_path].front(); E; E = E->next()) {
+ E->get()->update_property();
+ E->get()->update_reload_status();
+ }
+ }
+
+ } else {
+ emit_signal("property_toggled", p_path, p_checked);
+ }
+}
+
+void EditorInspector::_property_selected(const String &p_path, int p_focusable) {
+
+ property_selected = p_path;
+ property_focusable = p_focusable;
+ //deselect the others
+ for (Map<StringName, List<EditorProperty *> >::Element *F = editor_property_map.front(); F; F = F->next()) {
+ if (F->key() == property_selected)
+ continue;
+ for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) {
+ if (E->get()->is_selected())
+ E->get()->deselect();
+ }
+ }
+}
+
+void EditorInspector::_object_id_selected(const String &p_path, ObjectID p_id) {
+
+ emit_signal("object_id_selected", p_id);
+}
+
+void EditorInspector::_resource_selected(const String &p_path, RES p_resource) {
+ emit_signal("resource_selected", p_resource, p_path);
+}
+
+void EditorInspector::_node_removed(Node *p_node) {
+
+ if (p_node == object) {
+ edit(NULL);
+ }
+}
+
+void EditorInspector::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE) {
+
+ get_tree()->connect("node_removed", this, "_node_removed");
+ add_style_override("bg", get_stylebox("bg", "Tree"));
+ }
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+
+ get_tree()->disconnect("node_removed", this, "_node_removed");
+ edit(NULL);
+ }
+
+ if (p_what == NOTIFICATION_PROCESS) {
+
+ if (refresh_countdown > 0) {
+ refresh_countdown -= get_process_delta_time();
+ if (refresh_countdown <= 0) {
+ for (Map<StringName, List<EditorProperty *> >::Element *F = editor_property_map.front(); F; F = F->next()) {
+ for (List<EditorProperty *>::Element *E = F->get().front(); E; E = E->next()) {
+ E->get()->update_property();
+ E->get()->update_reload_status();
+ }
+ }
+ }
+ }
+
+ changing++;
+
+ if (update_tree_pending) {
+
+ update_tree();
+ update_tree_pending = false;
+ pending.clear();
+
+ } else {
+
+ while (pending.size()) {
+ StringName prop = pending.front()->get();
+ if (editor_property_map.has(prop)) {
+ for (List<EditorProperty *>::Element *E = editor_property_map[prop].front(); E; E = E->next()) {
+ E->get()->update_property();
+ E->get()->update_reload_status();
+ }
+ }
+ pending.erase(pending.front());
+ }
+ }
+
+ changing--;
+ }
+
+ if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
+ update_tree();
+ }
+}
+
+void EditorInspector::_changed_callback(Object *p_changed, const char *p_prop) {
+ //this is called when property change is notified via _change_notify()
+ _edit_request_change(p_changed, p_prop);
+}
+
+void EditorInspector::_bind_methods() {
+
+ ClassDB::bind_method("_multiple_properties_changed", &EditorInspector::_multiple_properties_changed);
+ ClassDB::bind_method("_property_changed", &EditorInspector::_property_changed);
+ ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change);
+ ClassDB::bind_method("_node_removed", &EditorInspector::_node_removed);
+ ClassDB::bind_method("_filter_changed", &EditorInspector::_filter_changed);
+ ClassDB::bind_method("_property_keyed", &EditorInspector::_property_keyed);
+ ClassDB::bind_method("_property_checked", &EditorInspector::_property_checked);
+ ClassDB::bind_method("_property_selected", &EditorInspector::_property_selected);
+ ClassDB::bind_method("_resource_selected", &EditorInspector::_resource_selected);
+ ClassDB::bind_method("_object_id_selected", &EditorInspector::_object_id_selected);
+
+ ADD_SIGNAL(MethodInfo("property_keyed", PropertyInfo(Variant::STRING, "property")));
+ ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::OBJECT, "res"), PropertyInfo(Variant::STRING, "prop")));
+ ADD_SIGNAL(MethodInfo("object_id_selected", PropertyInfo(Variant::INT, "id")));
+}
+
+EditorInspector::EditorInspector() {
+ object = NULL;
+ undo_redo = NULL;
+ main_vbox = memnew(VBoxContainer);
+ main_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_child(main_vbox);
+ main_vbox->set_name("pipirulo");
+ set_h_scroll(false);
+ set_v_scroll(true);
+
+ show_categories = false;
+ hide_script = true;
+ use_doc_hints = false;
+ capitalize_paths = false;
+ use_filter = false;
+ autoclear = false;
+ changing = 0;
+ use_folding = false;
+ update_all_pending = false;
+ update_tree_pending = false;
+ refresh_countdown = 0;
+ read_only = false;
+ search_box = NULL;
+ keying = false;
+ _prop_edited = "property_edited";
+ set_process(true);
+ property_focusable = -1;
+}
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
new file mode 100644
index 0000000000..64be316b0e
--- /dev/null
+++ b/editor/editor_inspector.h
@@ -0,0 +1,283 @@
+#ifndef EDITOR_INSPECTOR_H
+#define EDITOR_INSPECTOR_H
+
+#include "editor_data.h"
+#include "scene/gui/scroll_container.h"
+
+class EditorProperty : public Container {
+
+ GDCLASS(EditorProperty, Container)
+public:
+ enum LabelLayout {
+ LABEL_LAYOUT_LEFT,
+ LABEL_LAYOUT_TOP,
+ };
+
+private:
+ String label;
+ int text_size;
+ friend class EditorInspector;
+ Object *object;
+ StringName property;
+
+ LabelLayout label_layout;
+
+ int property_usage;
+
+ bool read_only;
+ bool checkable;
+ bool checked;
+ bool draw_red;
+ bool keying;
+
+ Rect2 keying_rect;
+ bool keying_hover;
+ Rect2 revert_rect;
+ bool revert_hover;
+ Rect2 check_rect;
+ bool check_hover;
+
+ bool can_revert;
+
+ bool _might_be_in_instance();
+ bool _is_property_different(const Variant &p_current, const Variant &p_orig, int p_usage);
+ bool _is_instanced_node_with_original_property_different();
+ bool _get_instanced_node_original_property(const StringName &p_prop, Variant &value);
+ void _focusable_focused(int p_index);
+
+ bool selected;
+ int selected_focusable;
+
+ Vector<Control *> focusables;
+ Control *label_reference;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ void _gui_input(const Ref<InputEvent> &p_event);
+
+public:
+ virtual Size2 get_minimum_size() const;
+
+ void set_label(const String &p_label);
+ String get_label() const;
+
+ void set_read_only(bool p_read_only);
+ bool is_read_only() const;
+
+ Object *get_edited_object();
+ StringName get_edited_property();
+
+ virtual void update_property();
+ void update_reload_status();
+
+ virtual bool use_keying_next() const;
+
+ void set_checkable(bool p_checkable);
+ bool is_checkable() const;
+
+ void set_checked(bool p_checked);
+ bool is_checked() const;
+
+ void set_draw_red(bool p_draw_red);
+ bool is_draw_red() const;
+
+ void set_keying(bool p_keying);
+ bool is_keying() const;
+
+ void add_focusable(Control *p_control);
+ void select(int p_focusable = -1);
+ void deselect();
+ bool is_selected() const;
+
+ void set_label_reference(Control *p_control);
+
+ virtual Variant get_drag_data(const Point2 &p_point);
+
+ void set_label_layout(LabelLayout p_layout);
+ EditorProperty();
+};
+
+class EditorInspectorPlugin : public Reference {
+ GDCLASS(EditorInspectorPlugin, Reference)
+
+ friend class EditorInspector;
+ struct AddedEditor {
+ Control *property_editor;
+ Vector<String> properties;
+ String label;
+ };
+
+ List<AddedEditor> added_editors;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void add_custom_control(Control *control);
+ void add_property_editor(const String &p_for_property, Control *p_prop);
+ void add_property_editor_for_multiple_properties(const String &p_label, const Vector<String> &p_properties, Control *p_prop);
+
+ virtual bool can_handle(Object *p_object);
+ virtual void parse_begin(Object *p_object);
+ virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage);
+ virtual void parse_end();
+};
+
+class EditorInspectorCategory : public Control {
+ GDCLASS(EditorInspectorCategory, Control);
+
+ friend class EditorInspector;
+ Ref<Texture> icon;
+ String label;
+ Color bg_color;
+
+protected:
+ void _notification(int p_what);
+
+public:
+ virtual Size2 get_minimum_size() const;
+
+ EditorInspectorCategory();
+};
+
+class EditorInspectorSection : public Container {
+ GDCLASS(EditorInspectorSection, Container);
+
+ String label;
+ String section;
+ Object *object;
+ VBoxContainer *vbox;
+ Color bg_color;
+ bool foldable;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+ void _gui_input(const Ref<InputEvent> &p_event);
+
+public:
+ virtual Size2 get_minimum_size() const;
+
+ void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable);
+ VBoxContainer *get_vbox();
+ void unfold();
+ void fold();
+
+ Object *get_edited_object();
+
+ EditorInspectorSection();
+};
+
+class EditorInspector : public ScrollContainer {
+ GDCLASS(EditorInspector, ScrollContainer);
+
+ UndoRedo *undo_redo;
+ enum {
+ MAX_PLUGINS = 1024
+ };
+ static Ref<EditorInspectorPlugin> inspector_plugins[MAX_PLUGINS];
+ static int inspector_plugin_count;
+
+ VBoxContainer *main_vbox;
+
+ //map use to cache the instanced editors
+ Map<StringName, List<EditorProperty *> > editor_property_map;
+ List<EditorInspectorSection *> sections;
+ Set<StringName> pending;
+
+ void _clear();
+ Object *object;
+
+ //
+
+ LineEdit *search_box;
+ bool show_categories;
+ bool hide_script;
+ bool use_doc_hints;
+ bool capitalize_paths;
+ bool use_filter;
+ bool autoclear;
+ bool use_folding;
+ int changing;
+ bool update_all_pending;
+ bool read_only;
+ bool keying;
+
+ int refresh_countdown;
+ bool update_tree_pending;
+ StringName _prop_edited;
+ StringName property_selected;
+ int property_focusable;
+
+ Map<StringName, Map<StringName, String> > descr_cache;
+ Map<StringName, String> class_descr_cache;
+
+ void _edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field);
+
+ void _property_changed(const String &p_path, const Variant &p_value);
+ void _multiple_properties_changed(Vector<String> p_paths, Array p_values);
+ void _property_keyed(const String &p_path);
+ void _property_checked(const String &p_path, bool p_checked);
+
+ void _resource_selected(const String &p_path, RES p_resource);
+ void _property_selected(const String &p_path, int p_focusable);
+ void _object_id_selected(const String &p_path, ObjectID p_id);
+
+ void _node_removed(Node *p_node);
+
+ void _changed_callback(Object *p_changed, const char *p_prop);
+ void _edit_request_change(Object *p_changed, const String &p_prop);
+
+ void _filter_changed(const String &p_text);
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ static void add_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
+ static void remove_inspector_plugin(const Ref<EditorInspectorPlugin> &p_plugin);
+ static void cleanup_plugins();
+
+ void set_undo_redo(UndoRedo *p_undo_redo);
+
+ String get_selected_path() const;
+
+ void update_tree();
+ void update_property(const String &p_prop);
+
+ void refresh();
+
+ void edit(Object *p_object);
+
+ void set_keying(bool p_active);
+ void set_read_only(bool p_read_only);
+
+ bool is_capitalize_paths_enabled() const;
+ void set_enable_capitalize_paths(bool p_capitalize);
+ void set_autoclear(bool p_enable);
+
+ void set_show_categories(bool p_show);
+ void set_use_doc_hints(bool p_enable);
+ void set_hide_script(bool p_hide);
+
+ void set_use_filter(bool p_use);
+ void register_text_enter(Node *p_line_edit);
+
+ void set_subsection_selectable(bool p_selectable);
+ void set_property_selectable(bool p_selectable);
+
+ void set_use_folding(bool p_enable);
+
+ void collapse_all_folding();
+ void expand_all_folding();
+
+ void set_scroll_offset(int p_offset);
+ int get_scroll_offset() const;
+
+ EditorInspector();
+};
+
+#endif // INSPECTOR_H
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 3f0a09cfd9..49f70e3215 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -56,6 +56,7 @@
#include "editor/editor_file_system.h"
#include "editor/editor_help.h"
#include "editor/editor_initialize_ssl.h"
+#include "editor/editor_properties.h"
#include "editor/editor_settings.h"
#include "editor/editor_themes.h"
#include "editor/import/editor_import_collada.h"
@@ -548,8 +549,8 @@ void EditorNode::_vp_resized() {
void EditorNode::_node_renamed() {
- if (property_editor)
- property_editor->update_tree();
+ if (inspector)
+ inspector->update_tree();
}
void EditorNode::_editor_select_next() {
@@ -1351,7 +1352,7 @@ void EditorNode::_dialog_action(String p_file) {
void EditorNode::push_item(Object *p_object, const String &p_property) {
if (!p_object) {
- property_editor->edit(NULL);
+ inspector->edit(NULL);
node_dock->set_node(NULL);
scene_tree_dock->set_selected(NULL);
return;
@@ -1444,12 +1445,12 @@ void EditorNode::_property_editor_back() {
void EditorNode::_menu_collapseall() {
- property_editor->collapse_all_folding();
+ inspector->collapse_all_folding();
}
void EditorNode::_menu_expandall() {
- property_editor->expand_all_folding();
+ inspector->expand_all_folding();
}
void EditorNode::_save_default_environment() {
@@ -1515,7 +1516,7 @@ void EditorNode::_edit_current() {
if (!current_obj) {
scene_tree_dock->set_selected(NULL);
- property_editor->edit(NULL);
+ inspector->edit(NULL);
node_dock->set_node(NULL);
object_menu->set_disabled(true);
@@ -1536,7 +1537,7 @@ void EditorNode::_edit_current() {
Resource *current_res = Object::cast_to<Resource>(current_obj);
ERR_FAIL_COND(!current_res);
scene_tree_dock->set_selected(NULL);
- property_editor->edit(current_res);
+ inspector->edit(current_res);
node_dock->set_node(NULL);
object_menu->set_disabled(false);
EditorNode::get_singleton()->get_import_dock()->set_edit_path(current_res->get_path());
@@ -1561,7 +1562,7 @@ void EditorNode::_edit_current() {
Node *current_node = Object::cast_to<Node>(current_obj);
ERR_FAIL_COND(!current_node);
- property_editor->edit(current_node);
+ inspector->edit(current_node);
if (current_node->is_inside_tree()) {
node_dock->set_node(current_node);
scene_tree_dock->set_selected(current_node);
@@ -1585,7 +1586,7 @@ void EditorNode::_edit_current() {
capitalize = false;
}
- property_editor->edit(current_obj);
+ inspector->edit(current_obj);
node_dock->set_node(NULL);
}
@@ -1594,8 +1595,8 @@ void EditorNode::_edit_current() {
property_editable_warning_dialog->set_text(editable_warning);
}
- if (property_editor->is_capitalize_paths_enabled() != capitalize) {
- property_editor->set_enable_capitalize_paths(capitalize);
+ if (inspector->is_capitalize_paths_enabled() != capitalize) {
+ inspector->set_enable_capitalize_paths(capitalize);
}
/* Take care of PLUGIN EDITOR */
@@ -2939,7 +2940,7 @@ Dictionary EditorNode::_get_main_scene_state() {
Dictionary state;
state["main_tab"] = _get_current_main_editor();
state["scene_tree_offset"] = scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->get_value();
- state["property_edit_offset"] = get_property_editor()->get_scene_tree()->get_vscroll_bar()->get_value();
+ state["property_edit_offset"] = get_inspector()->get_scroll_offset();
state["saved_version"] = saved_version;
state["node_filter"] = scene_tree_dock->get_filter();
return state;
@@ -2985,7 +2986,7 @@ void EditorNode::_set_main_scene_state(Dictionary p_state, Node *p_for_scene) {
if (p_state.has("scene_tree_offset"))
scene_tree_dock->get_tree_editor()->get_scene_tree()->get_vscroll_bar()->set_value(p_state["scene_tree_offset"]);
if (p_state.has("property_edit_offset"))
- get_property_editor()->get_scene_tree()->get_vscroll_bar()->set_value(p_state["property_edit_offset"]);
+ get_inspector()->set_scroll_offset(p_state["property_edit_offset"]);
if (p_state.has("node_filter"))
scene_tree_dock->set_filter(p_state["node_filter"]);
@@ -3278,9 +3279,7 @@ void EditorNode::update_keying() {
}
}
- property_editor->set_keying(valid);
-
- AnimationPlayerEditor::singleton->get_key_editor()->update_keying();
+ inspector->set_keying(valid);
}
void EditorNode::_close_messages() {
@@ -3425,6 +3424,9 @@ void EditorNode::register_editor_types() {
ClassDB::register_class<EditorExportPlugin>();
ClassDB::register_class<EditorResourceConversionPlugin>();
ClassDB::register_class<EditorSceneImporter>();
+ ClassDB::register_class<EditorInspector>();
+ ClassDB::register_class<EditorInspectorPlugin>();
+ ClassDB::register_class<EditorProperty>();
// FIXME: Is this stuff obsolete, or should it be ported to new APIs?
ClassDB::register_class<EditorScenePostImport>();
@@ -4236,7 +4238,7 @@ void EditorNode::_scene_tab_changed(int p_tab) {
void EditorNode::_toggle_search_bar(bool p_pressed) {
- property_editor->set_use_filter(p_pressed);
+ inspector->set_use_filter(p_pressed);
if (p_pressed) {
@@ -4255,7 +4257,7 @@ void EditorNode::_clear_search_box() {
return;
search_box->clear();
- property_editor->update_tree();
+ inspector->update_tree();
}
ToolButton *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) {
@@ -5007,6 +5009,12 @@ EditorNode::EditorNode() {
ResourceFormatImporter::get_singleton()->add_importer(import_bitmap);
}
+ {
+ Ref<EditorInspectorDefaultPlugin> eidp;
+ eidp.instance();
+ EditorInspector::add_inspector_plugin(eidp);
+ }
+
_pvrtc_register_compressors();
editor_selection = memnew(EditorSelection);
@@ -5665,21 +5673,21 @@ EditorNode::EditorNode() {
property_editable_warning->hide();
property_editable_warning->connect("pressed", this, "_property_editable_warning_pressed");
- property_editor = memnew(PropertyEditor);
- property_editor->set_autoclear(true);
- property_editor->set_show_categories(true);
- property_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
- property_editor->set_use_doc_hints(true);
- property_editor->set_hide_script(false);
- property_editor->set_enable_capitalize_paths(bool(EDITOR_DEF("interface/editor/capitalize_properties", true)));
- property_editor->set_use_folding(!bool(EDITOR_DEF("interface/editor/disable_inspector_folding", false)));
+ inspector = memnew(EditorInspector);
+ inspector->set_autoclear(true);
+ inspector->set_show_categories(true);
+ inspector->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ inspector->set_use_doc_hints(true);
+ inspector->set_hide_script(false);
+ inspector->set_enable_capitalize_paths(bool(EDITOR_DEF("interface/editor/capitalize_properties", true)));
+ inspector->set_use_folding(!bool(EDITOR_DEF("interface/editor/disable_inspector_folding", false)));
- property_editor->hide_top_label();
- property_editor->register_text_enter(search_box);
+ // inspector->hide_top_label();
+ inspector->register_text_enter(search_box);
Button *property_editable_warning;
- prop_editor_base->add_child(property_editor);
- property_editor->set_undo_redo(&editor_data.get_undo_redo());
+ prop_editor_base->add_child(inspector);
+ inspector->set_undo_redo(&editor_data.get_undo_redo());
import_dock = memnew(ImportDock);
dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(import_dock);
@@ -5815,8 +5823,8 @@ EditorNode::EditorNode() {
file->connect("file_selected", this, "_dialog_action");
file_templates->connect("file_selected", this, "_dialog_action");
- property_editor->connect("resource_selected", this, "_resource_selected");
- property_editor->connect("property_keyed", this, "_property_keyed");
+ inspector->connect("resource_selected", this, "_resource_selected");
+ inspector->connect("property_keyed", this, "_property_keyed");
//plugin stuff
@@ -6039,6 +6047,8 @@ EditorNode::EditorNode() {
EditorNode::~EditorNode() {
+ EditorInspector::cleanup_plugins();
+
remove_print_handler(&print_handler);
memdelete(EditorHelp::get_doc_data());
memdelete(editor_selection);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index f774fa0a2e..86b85663ab 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -37,6 +37,7 @@
#include "editor/editor_about.h"
#include "editor/editor_data.h"
#include "editor/editor_export.h"
+#include "editor/editor_inspector.h"
#include "editor/editor_log.h"
#include "editor/editor_name_dialog.h"
#include "editor/editor_path.h"
@@ -80,7 +81,6 @@
#include "scene/gui/tool_button.h"
#include "scene/gui/tree.h"
#include "scene/gui/viewport_container.h"
-
/**
@author Juan Linietsky <reduzio@gmail.com>
*/
@@ -267,7 +267,7 @@ private:
Button *property_back;
Button *property_forward;
SceneTreeDock *scene_tree_dock;
- PropertyEditor *property_editor;
+ EditorInspector *inspector;
Button *property_editable_warning;
AcceptDialog *property_editable_warning_dialog;
void _property_editable_warning_pressed();
@@ -640,7 +640,7 @@ public:
EditorPluginList *get_editor_plugins_over() { return editor_plugins_over; }
EditorPluginList *get_editor_plugins_force_over() { return editor_plugins_force_over; }
EditorPluginList *get_editor_plugins_force_input_forwarding() { return editor_plugins_force_input_forwarding; }
- PropertyEditor *get_property_editor() { return property_editor; }
+ EditorInspector *get_inspector() { return inspector; }
VBoxContainer *get_property_editor_vb() { return prop_editor_vb; }
ProjectSettingsEditor *get_project_settings() { return project_settings; }
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
new file mode 100644
index 0000000000..51be64bd3f
--- /dev/null
+++ b/editor/editor_properties.cpp
@@ -0,0 +1,2411 @@
+#include "editor_properties.h"
+#include "editor/editor_resource_preview.h"
+#include "editor_node.h"
+#include "scene/main/viewport.h"
+///////////////////// TEXT /////////////////////////
+void EditorPropertyText::_text_changed(const String &p_string) {
+ if (updating)
+ return;
+
+ emit_signal("property_changed", get_edited_property(), p_string);
+}
+
+void EditorPropertyText::update_property() {
+ String s = get_edited_object()->get(get_edited_property());
+ updating = true;
+ text->set_text(s);
+ text->set_editable(!is_read_only());
+ updating = false;
+}
+
+void EditorPropertyText::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_text_changed", "txt"), &EditorPropertyText::_text_changed);
+}
+
+EditorPropertyText::EditorPropertyText() {
+ text = memnew(LineEdit);
+ add_child(text);
+ add_focusable(text);
+ text->connect("text_changed", this, "_text_changed");
+ updating = false;
+}
+
+///////////////////// MULTILINE TEXT /////////////////////////
+
+void EditorPropertyMultilineText::_big_text_changed() {
+ text->set_text(big_text->get_text());
+ emit_signal("property_changed", get_edited_property(), big_text->get_text());
+}
+
+void EditorPropertyMultilineText::_text_changed() {
+
+ emit_signal("property_changed", get_edited_property(), text->get_text());
+}
+
+void EditorPropertyMultilineText::_open_big_text() {
+
+ if (!big_text_dialog) {
+ big_text = memnew(TextEdit);
+ big_text->connect("text_changed", this, "_big_text_changed");
+ big_text_dialog = memnew(AcceptDialog);
+ big_text_dialog->add_child(big_text);
+ big_text_dialog->set_title("Edit Text:");
+ add_child(big_text_dialog);
+ }
+
+ big_text->set_text(text->get_text());
+ big_text_dialog->popup_centered_ratio();
+}
+
+void EditorPropertyMultilineText::update_property() {
+ String t = get_edited_object()->get(get_edited_property());
+ text->set_text(t);
+ if (big_text && big_text->is_visible_in_tree()) {
+ big_text->set_text(t);
+ }
+}
+
+void EditorPropertyMultilineText::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_ENTER_TREE: {
+ Ref<Texture> df = get_icon("DistractionFree", "EditorIcons");
+ open_big_text->set_icon(df);
+ Ref<Font> font = get_font("font", "Label");
+ text->set_custom_minimum_size(Vector2(0, font->get_height() * 6));
+
+ } break;
+ }
+}
+
+void EditorPropertyMultilineText::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_text_changed"), &EditorPropertyMultilineText::_text_changed);
+ ClassDB::bind_method(D_METHOD("_big_text_changed"), &EditorPropertyMultilineText::_big_text_changed);
+ ClassDB::bind_method(D_METHOD("_open_big_text"), &EditorPropertyMultilineText::_open_big_text);
+}
+
+EditorPropertyMultilineText::EditorPropertyMultilineText() {
+ HBoxContainer *hb = memnew(HBoxContainer);
+ set_label_layout(LABEL_LAYOUT_TOP);
+ add_child(hb);
+ text = memnew(TextEdit);
+ text->connect("text_changed", this, "_text_changed");
+ add_focusable(text);
+ hb->add_child(text);
+ text->set_h_size_flags(SIZE_EXPAND_FILL);
+ open_big_text = memnew(ToolButton);
+ open_big_text->connect("pressed", this, "_open_big_text");
+ hb->add_child(open_big_text);
+ big_text_dialog = NULL;
+ big_text = NULL;
+}
+
+///////////////////// TEXT ENUM /////////////////////////
+
+void EditorPropertyTextEnum::_option_selected(int p_which) {
+
+ emit_signal("property_changed", get_edited_property(), options->get_item_text(p_which));
+}
+
+void EditorPropertyTextEnum::update_property() {
+
+ String which = get_edited_object()->get(get_edited_property());
+ for (int i = 0; i < options->get_item_count(); i++) {
+ String t = options->get_item_text(i);
+ if (t == which) {
+ options->select(i);
+ return;
+ }
+ }
+}
+
+void EditorPropertyTextEnum::setup(const Vector<String> &p_options) {
+ for (int i = 0; i < p_options.size(); i++) {
+ options->add_item(p_options[i], i);
+ }
+}
+
+void EditorPropertyTextEnum::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyTextEnum::_option_selected);
+}
+
+EditorPropertyTextEnum::EditorPropertyTextEnum() {
+ options = memnew(OptionButton);
+ options->set_clip_text(true);
+ add_child(options);
+ add_focusable(options);
+ options->connect("item_selected", this, "_option_selected");
+}
+///////////////////// PATH /////////////////////////
+
+void EditorPropertyPath::_path_selected(const String &p_path) {
+
+ emit_signal("property_changed", get_edited_property(), p_path);
+ update_property();
+}
+void EditorPropertyPath::_path_pressed() {
+
+ if (!dialog) {
+ dialog = memnew(EditorFileDialog);
+ dialog->connect("file_selected", this, "_path_selected");
+ dialog->connect("dir_selected", this, "_path_selected");
+ add_child(dialog);
+ }
+
+ String full_path = get_edited_object()->get(get_edited_property());
+
+ dialog->clear_filters();
+
+ if (global) {
+ dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
+ } else {
+ dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);
+ }
+
+ if (folder) {
+ dialog->set_mode(EditorFileDialog::MODE_OPEN_DIR);
+ dialog->set_current_dir(full_path);
+ } else {
+ dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ for (int i = 0; i < extensions.size(); i++) {
+ String e = extensions[i].strip_edges();
+ if (e != String()) {
+ dialog->add_filter(extensions[i].strip_edges());
+ }
+ }
+ dialog->set_current_path(full_path);
+ }
+
+ dialog->popup_centered_ratio();
+}
+
+void EditorPropertyPath::update_property() {
+
+ String full_path = get_edited_object()->get(get_edited_property());
+ path->set_text(full_path);
+ path->set_tooltip(full_path);
+}
+
+void EditorPropertyPath::setup(const Vector<String> &p_extensions, bool p_folder, bool p_global) {
+
+ extensions = p_extensions;
+ folder = p_folder;
+ global = p_global;
+}
+
+void EditorPropertyPath::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_path_pressed"), &EditorPropertyPath::_path_pressed);
+ ClassDB::bind_method(D_METHOD("_path_selected"), &EditorPropertyPath::_path_selected);
+}
+
+EditorPropertyPath::EditorPropertyPath() {
+ path = memnew(Button);
+ path->set_clip_text(true);
+ add_child(path);
+ add_focusable(path);
+ dialog = NULL;
+ path->connect("pressed", this, "_path_pressed");
+ folder = false;
+ global = false;
+}
+
+///////////////////// MEMBER /////////////////////////
+
+void EditorPropertyMember::_property_selected(const String &p_selected) {
+
+ emit_signal("property_changed", get_edited_property(), p_selected);
+ update_property();
+}
+
+void EditorPropertyMember::_property_select() {
+
+ if (!selector) {
+ selector = memnew(PropertySelector);
+ selector->connect("selected", this, "_property_selected");
+ add_child(selector);
+ }
+
+ String current = get_edited_object()->get(get_edited_property());
+
+ if (hint == MEMBER_METHOD_OF_VARIANT_TYPE) {
+
+ Variant::Type type = Variant::NIL;
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ if (hint_text == Variant::get_type_name(Variant::Type(i))) {
+ type = Variant::Type(i);
+ }
+ }
+ if (type)
+ selector->select_method_from_basic_type(type, current);
+
+ } else if (hint == MEMBER_METHOD_OF_BASE_TYPE) {
+
+ selector->select_method_from_base_type(hint_text, current);
+
+ } else if (hint == MEMBER_METHOD_OF_INSTANCE) {
+
+ Object *instance = ObjectDB::get_instance(hint_text.to_int64());
+ if (instance)
+ selector->select_method_from_instance(instance, current);
+
+ } else if (hint == MEMBER_METHOD_OF_SCRIPT) {
+
+ Object *obj = ObjectDB::get_instance(hint_text.to_int64());
+ if (Object::cast_to<Script>(obj)) {
+ selector->select_method_from_script(Object::cast_to<Script>(obj), current);
+ }
+
+ } else if (hint == MEMBER_PROPERTY_OF_VARIANT_TYPE) {
+
+ Variant::Type type = Variant::NIL;
+ String tname = hint_text;
+ if (tname.find(".") != -1)
+ tname = tname.get_slice(".", 0);
+ for (int i = 0; i < Variant::VARIANT_MAX; i++) {
+ if (tname == Variant::get_type_name(Variant::Type(i))) {
+ type = Variant::Type(Variant::Type(i));
+ }
+ }
+
+ if (type != Variant::NIL)
+ selector->select_property_from_basic_type(type, current);
+
+ } else if (hint == MEMBER_PROPERTY_OF_BASE_TYPE) {
+
+ selector->select_property_from_base_type(hint_text, current);
+
+ } else if (hint == MEMBER_PROPERTY_OF_INSTANCE) {
+
+ Object *instance = ObjectDB::get_instance(hint_text.to_int64());
+ if (instance)
+ selector->select_property_from_instance(instance, current);
+
+ } else if (hint == MEMBER_PROPERTY_OF_SCRIPT) {
+
+ Object *obj = ObjectDB::get_instance(hint_text.to_int64());
+ if (Object::cast_to<Script>(obj)) {
+ selector->select_property_from_script(Object::cast_to<Script>(obj), current);
+ }
+ }
+}
+
+void EditorPropertyMember::setup(Type p_hint, const String &p_hint_text) {
+ hint = p_hint;
+ hint_text = p_hint_text;
+}
+
+void EditorPropertyMember::update_property() {
+
+ String full_path = get_edited_object()->get(get_edited_property());
+ property->set_text(full_path);
+}
+
+void EditorPropertyMember::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_property_selected"), &EditorPropertyMember::_property_selected);
+ ClassDB::bind_method(D_METHOD("_property_select"), &EditorPropertyMember::_property_select);
+}
+
+EditorPropertyMember::EditorPropertyMember() {
+ selector = NULL;
+ property = memnew(Button);
+ property->set_clip_text(true);
+ add_child(property);
+ add_focusable(property);
+ property->connect("pressed", this, "_property_select");
+}
+
+///////////////////// CHECK /////////////////////////
+void EditorPropertyCheck::_checkbox_pressed() {
+
+ emit_signal("property_changed", get_edited_property(), checkbox->is_pressed());
+}
+
+void EditorPropertyCheck::update_property() {
+ bool c = get_edited_object()->get(get_edited_property());
+ checkbox->set_pressed(c);
+ checkbox->set_disabled(is_read_only());
+}
+
+void EditorPropertyCheck::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_checkbox_pressed"), &EditorPropertyCheck::_checkbox_pressed);
+}
+
+EditorPropertyCheck::EditorPropertyCheck() {
+ checkbox = memnew(CheckBox);
+ checkbox->set_text(TTR("On"));
+ add_child(checkbox);
+ add_focusable(checkbox);
+ checkbox->connect("pressed", this, "_checkbox_pressed");
+}
+
+///////////////////// ENUM /////////////////////////
+
+void EditorPropertyEnum::_option_selected(int p_which) {
+
+ emit_signal("property_changed", get_edited_property(), p_which);
+}
+
+void EditorPropertyEnum::update_property() {
+
+ int which = get_edited_object()->get(get_edited_property());
+ options->select(which);
+}
+
+void EditorPropertyEnum::setup(const Vector<String> &p_options) {
+ for (int i = 0; i < p_options.size(); i++) {
+ options->add_item(p_options[i], i);
+ }
+}
+
+void EditorPropertyEnum::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_option_selected"), &EditorPropertyEnum::_option_selected);
+}
+
+EditorPropertyEnum::EditorPropertyEnum() {
+ options = memnew(OptionButton);
+ options->set_clip_text(true);
+ add_child(options);
+ add_focusable(options);
+ options->connect("item_selected", this, "_option_selected");
+}
+
+///////////////////// FLAGS /////////////////////////
+
+void EditorPropertyFlags::_flag_toggled() {
+
+ uint32_t value = 0;
+ for (int i = 0; i < flags.size(); i++) {
+ if (flags[i]->is_pressed()) {
+ uint32_t val = 1;
+ val <<= flag_indices[i];
+ value |= val;
+ }
+ }
+
+ emit_signal("property_changed", get_edited_property(), value);
+}
+
+void EditorPropertyFlags::update_property() {
+
+ uint32_t value = get_edited_object()->get(get_edited_property());
+
+ for (int i = 0; i < flags.size(); i++) {
+ uint32_t val = 1;
+ val <<= flag_indices[i];
+ if (value & val) {
+
+ flags[i]->set_pressed(true);
+ } else {
+ flags[i]->set_pressed(false);
+ }
+ }
+}
+
+void EditorPropertyFlags::setup(const Vector<String> &p_options) {
+ ERR_FAIL_COND(flags.size());
+
+ bool first = true;
+ for (int i = 0; i < p_options.size(); i++) {
+ String option = p_options[i].strip_edges();
+ if (option != "") {
+ CheckBox *cb = memnew(CheckBox);
+ cb->set_text(option);
+ cb->set_clip_text(true);
+ cb->connect("pressed", this, "_flag_toggled");
+ add_focusable(cb);
+ vbox->add_child(cb);
+ flags.push_back(cb);
+ flag_indices.push_back(i);
+ if (first) {
+ set_label_reference(cb);
+ first = false;
+ }
+ }
+ }
+}
+
+void EditorPropertyFlags::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_flag_toggled"), &EditorPropertyFlags::_flag_toggled);
+}
+
+EditorPropertyFlags::EditorPropertyFlags() {
+
+ vbox = memnew(VBoxContainer);
+ add_child(vbox);
+}
+
+///////////////////// LAYERS /////////////////////////
+
+class EditorPropertyLayersGrid : public Control {
+ GDCLASS(EditorPropertyLayersGrid, Control)
+public:
+ uint32_t value;
+ Vector<Rect2> flag_rects;
+ Vector<String> names;
+
+ virtual Size2 get_minimum_size() const {
+ Ref<Font> font = get_font("font", "Label");
+ return Vector2(0, font->get_height() * 2);
+ }
+
+ virtual String get_tooltip(const Point2 &p_pos) const {
+ for (int i = 0; i < flag_rects.size(); i++) {
+ if (flag_rects[i].has_point(p_pos) && i < names.size()) {
+ return names[i];
+ }
+ }
+ return String();
+ }
+ void _gui_input(const Ref<InputEvent> &p_ev) {
+ Ref<InputEventMouseButton> mb = p_ev;
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT && mb->is_pressed()) {
+ for (int i = 0; i < flag_rects.size(); i++) {
+ if (flag_rects[i].has_point(mb->get_position())) {
+ //toggle
+ if (value & (1 << i)) {
+ value &= ~(1 << i);
+ } else {
+ value |= (1 << i);
+ }
+ emit_signal("flag_changed", value);
+ update();
+ }
+ }
+ }
+ }
+
+ void _notification(int p_what) {
+ if (p_what == NOTIFICATION_DRAW) {
+
+ Rect2 rect;
+ rect.size = get_size();
+ flag_rects.clear();
+
+ int bsize = (rect.size.height * 80 / 100) / 2;
+
+ int h = bsize * 2 + 1;
+ int vofs = (rect.size.height - h) / 2;
+
+ for (int i = 0; i < 2; i++) {
+
+ Point2 ofs(4, vofs);
+ if (i == 1)
+ ofs.y += bsize + 1;
+
+ ofs += rect.position;
+ for (int j = 0; j < 10; j++) {
+
+ Point2 o = ofs + Point2(j * (bsize + 1), 0);
+ if (j >= 5)
+ o.x += 1;
+
+ uint32_t idx = i * 10 + j;
+ bool on = value & (1 << idx);
+ Rect2 rect = Rect2(o, Size2(bsize, bsize));
+ draw_rect(rect, Color(0, 0, 0, on ? 0.8 : 0.3));
+ flag_rects.push_back(rect);
+ }
+ }
+ }
+ }
+
+ void set_flag(uint32_t p_flag) {
+ value = p_flag;
+ update();
+ }
+
+ static void _bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_gui_input"), &EditorPropertyLayersGrid::_gui_input);
+ ADD_SIGNAL(MethodInfo("flag_changed", PropertyInfo(Variant::INT, "flag")));
+ }
+
+ EditorPropertyLayersGrid() {
+ value = 0;
+ }
+};
+void EditorPropertyLayers::_grid_changed(uint32_t p_grid) {
+
+ emit_signal("property_changed", get_edited_property(), p_grid);
+}
+
+void EditorPropertyLayers::update_property() {
+
+ uint32_t value = get_edited_object()->get(get_edited_property());
+
+ grid->set_flag(value);
+}
+
+void EditorPropertyLayers::setup(LayerType p_layer_type) {
+
+ String basename;
+ switch (p_layer_type) {
+ case LAYER_RENDER_2D:
+ basename = "layer_names/2d_render";
+ break;
+ case LAYER_PHYSICS_2D:
+ basename = "layer_names/2d_physics";
+ break;
+ case LAYER_RENDER_3D:
+ basename = "layer_names/3d_render";
+ break;
+ case LAYER_PHYSICS_3D:
+ basename = "layer_names/3d_physics";
+ break;
+ }
+
+ Vector<String> names;
+ for (int i = 0; i < 20; i++) {
+ String name;
+
+ if (ProjectSettings::get_singleton()->has_setting(basename + "/layer_" + itos(i + 1))) {
+ name = ProjectSettings::get_singleton()->get(basename + "/layer_" + itos(i + 1));
+ }
+
+ if (name == "") {
+ name = "Layer " + itos(i + 1);
+ }
+
+ names.push_back(name);
+ }
+
+ grid->names = names;
+}
+
+void EditorPropertyLayers::_button_pressed() {
+
+ layers->clear();
+ for (int i = 0; i < 20; i++) {
+ if (i == 5 || i == 10 || i == 15) {
+ layers->add_separator();
+ }
+ layers->add_check_item(grid->names[i], i);
+ int idx = layers->get_item_index(i);
+ layers->set_item_checked(idx, grid->value & (1 << i));
+ }
+
+ Rect2 gp = button->get_global_rect();
+ Vector2 popup_pos = gp.position - Vector2(layers->get_combined_minimum_size().x, 0);
+ layers->set_global_position(popup_pos);
+ layers->popup();
+}
+
+void EditorPropertyLayers::_menu_pressed(int p_menu) {
+ if (grid->value & (1 << p_menu)) {
+ grid->value &= ~(1 << p_menu);
+ } else {
+ grid->value |= (1 << p_menu);
+ }
+ grid->update();
+ _grid_changed(grid->value);
+}
+
+void EditorPropertyLayers::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_grid_changed"), &EditorPropertyLayers::_grid_changed);
+ ClassDB::bind_method(D_METHOD("_button_pressed"), &EditorPropertyLayers::_button_pressed);
+ ClassDB::bind_method(D_METHOD("_menu_pressed"), &EditorPropertyLayers::_menu_pressed);
+}
+
+EditorPropertyLayers::EditorPropertyLayers() {
+
+ HBoxContainer *hb = memnew(HBoxContainer);
+ add_child(hb);
+ grid = memnew(EditorPropertyLayersGrid);
+ grid->connect("flag_changed", this, "_grid_changed");
+ grid->set_h_size_flags(SIZE_EXPAND_FILL);
+ hb->add_child(grid);
+ button = memnew(Button);
+ button->set_text("..");
+ button->connect("pressed", this, "_button_pressed");
+ hb->add_child(button);
+ set_label_layout(LABEL_LAYOUT_TOP);
+ layers = memnew(PopupMenu);
+ add_child(layers);
+ layers->connect("id_pressed", this, "_menu_pressed");
+}
+///////////////////// INT /////////////////////////
+
+void EditorPropertyInteger::_value_changed(double val) {
+ if (setting)
+ return;
+ emit_signal("property_changed", get_edited_property(), int(val));
+}
+
+void EditorPropertyInteger::update_property() {
+ int val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin->set_value(val);
+ setting = false;
+}
+
+void EditorPropertyInteger::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyInteger::_value_changed);
+}
+
+void EditorPropertyInteger::setup(int p_min, int p_max) {
+ spin->set_min(p_min);
+ spin->set_max(p_max);
+ spin->set_step(1);
+}
+
+EditorPropertyInteger::EditorPropertyInteger() {
+ spin = memnew(EditorSpinSlider);
+ add_child(spin);
+ add_focusable(spin);
+ spin->connect("value_changed", this, "_value_changed");
+ setting = false;
+}
+
+///////////////////// OBJECT ID /////////////////////////
+
+void EditorPropertyObjectID::_edit_pressed() {
+
+ emit_signal("object_id_selected", get_edited_property(), get_edited_object()->get(get_edited_property()));
+}
+
+void EditorPropertyObjectID::update_property() {
+ String type = base_type;
+ if (type == "")
+ type = "Object";
+
+ String icon_type = type;
+ if (has_icon(icon_type, "EditorIcons")) {
+ type = icon_type;
+ } else {
+ type = "Object";
+ }
+
+ ObjectID id = get_edited_object()->get(get_edited_property());
+ if (id != 0) {
+ edit->set_text(type + " ID: " + itos(id));
+ edit->set_disabled(false);
+ edit->set_icon(get_icon(icon_type, "EditorIcons"));
+ } else {
+ edit->set_text(TTR("[Empty]"));
+ edit->set_disabled(true);
+ edit->set_icon(Ref<Texture>());
+ }
+}
+
+void EditorPropertyObjectID::setup(const String &p_base_type) {
+ base_type = p_base_type;
+}
+
+void EditorPropertyObjectID::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("_edit_pressed"), &EditorPropertyObjectID::_edit_pressed);
+}
+
+EditorPropertyObjectID::EditorPropertyObjectID() {
+ edit = memnew(Button);
+ add_child(edit);
+ add_focusable(edit);
+ edit->connect("pressed", this, "_edit_pressed");
+}
+
+///////////////////// FLOAT /////////////////////////
+
+void EditorPropertyFloat::_value_changed(double val) {
+ if (setting)
+ return;
+
+ emit_signal("property_changed", get_edited_property(), val);
+}
+
+void EditorPropertyFloat::update_property() {
+ double val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin->set_value(val);
+ setting = false;
+}
+
+void EditorPropertyFloat::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyFloat::_value_changed);
+}
+
+void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_exp_range) {
+
+ spin->set_min(p_min);
+ spin->set_max(p_max);
+ spin->set_step(p_step);
+ spin->set_hide_slider(p_no_slider);
+ spin->set_exp_ratio(p_exp_range);
+}
+
+EditorPropertyFloat::EditorPropertyFloat() {
+ spin = memnew(EditorSpinSlider);
+ add_child(spin);
+ add_focusable(spin);
+ spin->connect("value_changed", this, "_value_changed");
+ setting = false;
+}
+
+///////////////////// EASING /////////////////////////
+
+void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) {
+
+ Ref<InputEventMouseMotion> mm = p_ev;
+
+ if (mm.is_valid() && mm->get_button_mask() & BUTTON_MASK_LEFT) {
+
+ float rel = mm->get_relative().x;
+ if (rel == 0)
+ return;
+
+ if (flip)
+ rel = -rel;
+
+ float val = get_edited_object()->get(get_edited_property());
+ if (val == 0)
+ return;
+ bool sg = val < 0;
+ val = Math::absf(val);
+
+ val = Math::log(val) / Math::log((float)2.0);
+ //logspace
+ val += rel * 0.05;
+
+ val = Math::pow(2.0f, val);
+ if (sg)
+ val = -val;
+
+ emit_signal("property_changed", get_edited_property(), val);
+ easing_draw->update();
+ }
+}
+
+void EditorPropertyEasing::_draw_easing() {
+
+ RID ci = easing_draw->get_canvas_item();
+
+ Size2 s = easing_draw->get_size();
+ Rect2 r(Point2(), s);
+ r = r.grow(3);
+ get_stylebox("normal", "LineEdit")->draw(ci, r);
+
+ int points = 48;
+
+ float prev = 1.0;
+ float exp = get_edited_object()->get(get_edited_property());
+
+ Ref<Font> f = get_font("font", "Label");
+ Color color = get_color("font_color", "Label");
+
+ for (int i = 1; i <= points; i++) {
+
+ float ifl = i / float(points);
+ float iflp = (i - 1) / float(points);
+
+ float h = 1.0 - Math::ease(ifl, exp);
+
+ if (flip) {
+ ifl = 1.0 - ifl;
+ iflp = 1.0 - iflp;
+ }
+
+ VisualServer::get_singleton()->canvas_item_add_line(ci, Point2(iflp * s.width, prev * s.height), Point2(ifl * s.width, h * s.height), color);
+ prev = h;
+ }
+
+ f->draw(ci, Point2(10, 10 + f->get_ascent()), String::num(exp, 2), color);
+}
+
+void EditorPropertyEasing::update_property() {
+ easing_draw->update();
+}
+
+void EditorPropertyEasing::_set_preset(float p_val) {
+ emit_signal("property_changed", get_edited_property(), p_val);
+ easing_draw->update();
+}
+
+void EditorPropertyEasing::setup(bool p_full, bool p_flip) {
+
+ flip = p_flip;
+ if (p_full) {
+ HBoxContainer *hb2 = memnew(HBoxContainer);
+ vb->add_child(hb2);
+ button_out_in = memnew(ToolButton);
+ button_out_in->set_tooltip(TTR("Out-In"));
+ button_out_in->set_h_size_flags(SIZE_EXPAND_FILL);
+ button_out_in->connect("pressed", this, "_set_preset", varray(-0.5));
+ hb2->add_child(button_out_in);
+
+ button_in_out = memnew(ToolButton);
+ button_in_out->set_tooltip(TTR("In"));
+ button_in_out->set_h_size_flags(SIZE_EXPAND_FILL);
+ button_in_out->connect("pressed", this, "_set_preset", varray(-2));
+ hb2->add_child(button_in_out);
+ }
+}
+
+void EditorPropertyEasing::_notification(int p_what) {
+
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_ENTER_TREE: {
+ easing_draw->set_custom_minimum_size(Size2(0, get_font("font", "Label")->get_height() * 2));
+ button_linear->set_icon(get_icon("CurveLinear", "EditorIcons"));
+ button_out->set_icon(get_icon("CurveOut", "EditorIcons"));
+ button_in->set_icon(get_icon("CurveIn", "EditorIcons"));
+ button_constant->set_icon(get_icon("CurveConstant", "EditorIcons"));
+ if (button_out_in)
+ button_out_in->set_icon(get_icon("CurveOutIn", "EditorIcons"));
+ if (button_in_out)
+ button_in_out->set_icon(get_icon("CurveInOut", "EditorIcons"));
+ } break;
+ }
+}
+
+void EditorPropertyEasing::_bind_methods() {
+
+ ClassDB::bind_method("_draw_easing", &EditorPropertyEasing::_draw_easing);
+ ClassDB::bind_method("_drag_easing", &EditorPropertyEasing::_drag_easing);
+ ClassDB::bind_method("_set_preset", &EditorPropertyEasing::_set_preset);
+}
+
+EditorPropertyEasing::EditorPropertyEasing() {
+
+ vb = memnew(VBoxContainer);
+ add_child(vb);
+ HBoxContainer *hb = memnew(HBoxContainer);
+ set_label_reference(hb);
+
+ vb->add_child(hb);
+
+ button_linear = memnew(ToolButton);
+ button_linear->set_tooltip(TTR("Linear"));
+ button_linear->set_h_size_flags(SIZE_EXPAND_FILL);
+ button_linear->connect("pressed", this, "_set_preset", varray(1));
+ hb->add_child(button_linear);
+
+ button_constant = memnew(ToolButton);
+ button_constant->set_tooltip(TTR("Linear"));
+ button_constant->set_h_size_flags(SIZE_EXPAND_FILL);
+ button_constant->connect("pressed", this, "_set_preset", varray(0));
+ hb->add_child(button_constant);
+
+ button_out = memnew(ToolButton);
+ button_out->set_tooltip(TTR("Out"));
+ button_out->set_h_size_flags(SIZE_EXPAND_FILL);
+ button_out->connect("pressed", this, "_set_preset", varray(0.5));
+ hb->add_child(button_out);
+
+ button_in = memnew(ToolButton);
+ button_in->set_tooltip(TTR("In"));
+ button_in->set_h_size_flags(SIZE_EXPAND_FILL);
+ button_in->connect("pressed", this, "_set_preset", varray(2));
+ hb->add_child(button_in);
+
+ button_in_out = NULL;
+ button_out_in = NULL;
+
+ easing_draw = memnew(Control);
+ easing_draw->connect("draw", this, "_draw_easing");
+ easing_draw->connect("gui_input", this, "_drag_easing");
+ easing_draw->set_default_cursor_shape(Control::CURSOR_MOVE);
+ vb->add_child(easing_draw);
+
+ flip = false;
+}
+
+///////////////////// VECTOR2 /////////////////////////
+
+void EditorPropertyVector2::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Vector2 v2;
+ v2.x = spin[0]->get_value();
+ v2.y = spin[1]->get_value();
+ emit_signal("property_changed", get_edited_property(), v2);
+}
+
+void EditorPropertyVector2::update_property() {
+ Vector2 val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.x);
+ spin[1]->set_value(val.y);
+ setting = false;
+}
+
+void EditorPropertyVector2::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector2::_value_changed);
+}
+
+void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 2; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyVector2::EditorPropertyVector2() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+ static const char *desc[2] = { "x", "y" };
+ for (int i = 0; i < 2; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ vb->add_child(spin[i]);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ setting = false;
+}
+
+///////////////////// RECT2 /////////////////////////
+
+void EditorPropertyRect2::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Rect2 r2;
+ r2.position.x = spin[0]->get_value();
+ r2.position.x = spin[1]->get_value();
+ r2.size.y = spin[2]->get_value();
+ r2.size.y = spin[3]->get_value();
+ emit_signal("property_changed", get_edited_property(), r2);
+}
+
+void EditorPropertyRect2::update_property() {
+ Rect2 val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.position.x);
+ spin[1]->set_value(val.position.y);
+ spin[2]->set_value(val.size.x);
+ spin[3]->set_value(val.size.y);
+ setting = false;
+}
+
+void EditorPropertyRect2::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyRect2::_value_changed);
+}
+
+void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 4; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyRect2::EditorPropertyRect2() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+ static const char *desc[4] = { "x", "y", "w", "h" };
+ for (int i = 0; i < 4; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ vb->add_child(spin[i]);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ setting = false;
+}
+///////////////////// VECTOR3 /////////////////////////
+
+void EditorPropertyVector3::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Vector3 v3;
+ v3.x = spin[0]->get_value();
+ v3.y = spin[1]->get_value();
+ v3.z = spin[2]->get_value();
+ emit_signal("property_changed", get_edited_property(), v3);
+}
+
+void EditorPropertyVector3::update_property() {
+ Vector3 val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.x);
+ spin[1]->set_value(val.y);
+ spin[2]->set_value(val.z);
+ setting = false;
+}
+
+void EditorPropertyVector3::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyVector3::_value_changed);
+}
+
+void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 3; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyVector3::EditorPropertyVector3() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+ static const char *desc[3] = { "x", "y", "z" };
+ for (int i = 0; i < 3; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ vb->add_child(spin[i]);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ setting = false;
+}
+///////////////////// PLANE /////////////////////////
+
+void EditorPropertyPlane::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Plane p;
+ p.normal.x = spin[0]->get_value();
+ p.normal.y = spin[1]->get_value();
+ p.normal.z = spin[2]->get_value();
+ p.d = spin[3]->get_value();
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyPlane::update_property() {
+ Plane val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.normal.x);
+ spin[1]->set_value(val.normal.y);
+ spin[2]->set_value(val.normal.z);
+ spin[3]->set_value(val.d);
+ setting = false;
+}
+
+void EditorPropertyPlane::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyPlane::_value_changed);
+}
+
+void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 4; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyPlane::EditorPropertyPlane() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+ static const char *desc[4] = { "x", "y", "z", "d" };
+ for (int i = 0; i < 4; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ vb->add_child(spin[i]);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ setting = false;
+}
+
+///////////////////// QUAT /////////////////////////
+
+void EditorPropertyQuat::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Quat p;
+ p.x = spin[0]->get_value();
+ p.y = spin[1]->get_value();
+ p.z = spin[2]->get_value();
+ p.w = spin[3]->get_value();
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyQuat::update_property() {
+ Quat val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.x);
+ spin[1]->set_value(val.y);
+ spin[2]->set_value(val.z);
+ spin[3]->set_value(val.w);
+ setting = false;
+}
+
+void EditorPropertyQuat::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyQuat::_value_changed);
+}
+
+void EditorPropertyQuat::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 4; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyQuat::EditorPropertyQuat() {
+ VBoxContainer *vb = memnew(VBoxContainer);
+ add_child(vb);
+ static const char *desc[4] = { "x", "y", "z", "w" };
+ for (int i = 0; i < 4; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ vb->add_child(spin[i]);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ setting = false;
+}
+
+///////////////////// AABB /////////////////////////
+
+void EditorPropertyAABB::_value_changed(double val) {
+ if (setting)
+ return;
+
+ AABB p;
+ p.position.x = spin[0]->get_value();
+ p.position.y = spin[1]->get_value();
+ p.position.z = spin[2]->get_value();
+ p.size.x = spin[3]->get_value();
+ p.size.y = spin[4]->get_value();
+ p.size.z = spin[5]->get_value();
+
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyAABB::update_property() {
+ AABB val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.position.x);
+ spin[1]->set_value(val.position.y);
+ spin[2]->set_value(val.position.z);
+ spin[3]->set_value(val.size.x);
+ spin[4]->set_value(val.size.y);
+ spin[5]->set_value(val.size.z);
+
+ setting = false;
+}
+
+void EditorPropertyAABB::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyAABB::_value_changed);
+}
+
+void EditorPropertyAABB::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 6; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyAABB::EditorPropertyAABB() {
+ GridContainer *g = memnew(GridContainer);
+ g->set_columns(3);
+ add_child(g);
+
+ static const char *desc[6] = { "x", "y", "z", "w", "h", "d" };
+ for (int i = 0; i < 6; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ g->add_child(spin[i]);
+ spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ set_label_layout(LABEL_LAYOUT_TOP);
+ setting = false;
+}
+
+///////////////////// TRANSFORM2D /////////////////////////
+
+void EditorPropertyTransform2D::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Transform2D p;
+ p[0][0] = spin[0]->get_value();
+ p[0][1] = spin[1]->get_value();
+ p[1][0] = spin[2]->get_value();
+ p[1][1] = spin[3]->get_value();
+ p[2][0] = spin[4]->get_value();
+ p[2][1] = spin[5]->get_value();
+
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyTransform2D::update_property() {
+ Transform2D val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val[0][0]);
+ spin[1]->set_value(val[0][1]);
+ spin[2]->set_value(val[1][0]);
+ spin[3]->set_value(val[1][1]);
+ spin[4]->set_value(val[2][0]);
+ spin[5]->set_value(val[2][1]);
+
+ setting = false;
+}
+
+void EditorPropertyTransform2D::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyTransform2D::_value_changed);
+}
+
+void EditorPropertyTransform2D::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 6; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyTransform2D::EditorPropertyTransform2D() {
+ GridContainer *g = memnew(GridContainer);
+ g->set_columns(2);
+ add_child(g);
+
+ static const char *desc[6] = { "xx", "xy", "yx", "yy", "ox", "oy" };
+ for (int i = 0; i < 6; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ g->add_child(spin[i]);
+ spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ set_label_layout(LABEL_LAYOUT_TOP);
+ setting = false;
+}
+
+///////////////////// BASIS /////////////////////////
+
+void EditorPropertyBasis::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Basis p;
+ p[0][0] = spin[0]->get_value();
+ p[1][0] = spin[1]->get_value();
+ p[2][0] = spin[2]->get_value();
+ p[0][1] = spin[3]->get_value();
+ p[1][1] = spin[4]->get_value();
+ p[2][1] = spin[5]->get_value();
+ p[0][2] = spin[6]->get_value();
+ p[1][2] = spin[7]->get_value();
+ p[2][2] = spin[8]->get_value();
+
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyBasis::update_property() {
+ Basis val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val[0][0]);
+ spin[1]->set_value(val[1][0]);
+ spin[2]->set_value(val[2][0]);
+ spin[3]->set_value(val[0][1]);
+ spin[4]->set_value(val[1][1]);
+ spin[5]->set_value(val[2][1]);
+ spin[6]->set_value(val[0][2]);
+ spin[7]->set_value(val[1][2]);
+ spin[8]->set_value(val[2][2]);
+
+ setting = false;
+}
+
+void EditorPropertyBasis::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyBasis::_value_changed);
+}
+
+void EditorPropertyBasis::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 9; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyBasis::EditorPropertyBasis() {
+ GridContainer *g = memnew(GridContainer);
+ g->set_columns(3);
+ add_child(g);
+
+ static const char *desc[9] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz" };
+ for (int i = 0; i < 9; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ g->add_child(spin[i]);
+ spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ set_label_layout(LABEL_LAYOUT_TOP);
+ setting = false;
+}
+
+///////////////////// TRANSFORM /////////////////////////
+
+void EditorPropertyTransform::_value_changed(double val) {
+ if (setting)
+ return;
+
+ Transform p;
+ p.basis[0][0] = spin[0]->get_value();
+ p.basis[1][0] = spin[1]->get_value();
+ p.basis[2][0] = spin[2]->get_value();
+ p.basis[0][1] = spin[3]->get_value();
+ p.basis[1][1] = spin[4]->get_value();
+ p.basis[2][1] = spin[5]->get_value();
+ p.basis[0][2] = spin[6]->get_value();
+ p.basis[1][2] = spin[7]->get_value();
+ p.basis[2][2] = spin[8]->get_value();
+ p.origin[0] = spin[9]->get_value();
+ p.origin[1] = spin[10]->get_value();
+ p.origin[2] = spin[11]->get_value();
+
+ emit_signal("property_changed", get_edited_property(), p);
+}
+
+void EditorPropertyTransform::update_property() {
+ Transform val = get_edited_object()->get(get_edited_property());
+ setting = true;
+ spin[0]->set_value(val.basis[0][0]);
+ spin[1]->set_value(val.basis[1][0]);
+ spin[2]->set_value(val.basis[2][0]);
+ spin[3]->set_value(val.basis[0][1]);
+ spin[4]->set_value(val.basis[1][1]);
+ spin[5]->set_value(val.basis[2][1]);
+ spin[6]->set_value(val.basis[0][2]);
+ spin[7]->set_value(val.basis[1][2]);
+ spin[8]->set_value(val.basis[2][2]);
+ spin[9]->set_value(val.origin[0]);
+ spin[10]->set_value(val.origin[1]);
+ spin[11]->set_value(val.origin[2]);
+
+ setting = false;
+}
+
+void EditorPropertyTransform::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_value_changed"), &EditorPropertyTransform::_value_changed);
+}
+
+void EditorPropertyTransform::setup(double p_min, double p_max, double p_step, bool p_no_slider) {
+ for (int i = 0; i < 12; i++) {
+ spin[i]->set_min(p_min);
+ spin[i]->set_max(p_max);
+ spin[i]->set_step(p_step);
+ spin[i]->set_hide_slider(p_no_slider);
+ }
+}
+
+EditorPropertyTransform::EditorPropertyTransform() {
+ GridContainer *g = memnew(GridContainer);
+ g->set_columns(3);
+ add_child(g);
+
+ static const char *desc[12] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz", "ox", "oy", "oz" };
+ for (int i = 0; i < 12; i++) {
+ spin[i] = memnew(EditorSpinSlider);
+ spin[i]->set_label(desc[i]);
+ g->add_child(spin[i]);
+ spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_focusable(spin[i]);
+ spin[i]->connect("value_changed", this, "_value_changed");
+ }
+ set_label_reference(spin[0]); //show text and buttons around this
+ set_label_layout(LABEL_LAYOUT_TOP);
+ setting = false;
+}
+
+////////////// COLOR PICKER //////////////////////
+
+void EditorPropertyColor::_color_changed(const Color &p_color) {
+
+ emit_signal("property_changed", get_edited_property(), p_color);
+}
+
+void EditorPropertyColor::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_color_changed"), &EditorPropertyColor::_color_changed);
+}
+
+void EditorPropertyColor::update_property() {
+
+ picker->set_pick_color(get_edited_object()->get(get_edited_property()));
+}
+
+void EditorPropertyColor::setup(bool p_show_alpha) {
+ picker->set_edit_alpha(p_show_alpha);
+}
+
+EditorPropertyColor::EditorPropertyColor() {
+
+ picker = memnew(ColorPickerButton);
+ add_child(picker);
+ picker->set_flat(true);
+ picker->connect("color_changed", this, "_color_changed");
+}
+
+////////////// NODE PATH //////////////////////
+
+void EditorPropertyNodePath::_node_selected(const NodePath &p_path) {
+
+ emit_signal("property_changed", get_edited_property(), p_path);
+ update_property();
+}
+
+void EditorPropertyNodePath::_node_assign() {
+ if (!scene_tree) {
+ scene_tree = memnew(SceneTreeDialog);
+ add_child(scene_tree);
+ scene_tree->connect("selected", this, "_node_selected");
+ }
+ scene_tree->popup_centered_ratio();
+}
+
+void EditorPropertyNodePath::_node_clear() {
+
+ emit_signal("property_changed", get_edited_property(), NodePath());
+ update_property();
+}
+
+void EditorPropertyNodePath::update_property() {
+
+ NodePath p = get_edited_object()->get(get_edited_property());
+
+ assign->set_tooltip(p);
+ if (p == NodePath()) {
+ assign->set_icon(Ref<Texture>());
+ assign->set_text(TTR("Assign.."));
+ assign->set_flat(false);
+ return;
+ }
+ assign->set_flat(true);
+
+ Node *base_node = NULL;
+ if (base_hint != NodePath()) {
+ if (get_tree()->get_root()->has_node(base_hint)) {
+ base_node = get_tree()->get_root()->get_node(base_hint);
+ }
+ } else {
+ base_node = Object::cast_to<Node>(get_edited_object());
+ }
+
+ if (!base_node || !base_node->has_node(p)) {
+ assign->set_icon(Ref<Texture>());
+ assign->set_text(p);
+ return;
+ }
+
+ Node *target_node = base_node->get_node(p);
+ ERR_FAIL_COND(!target_node);
+
+ assign->set_text(target_node->get_name());
+
+ Ref<Texture> icon;
+ if (has_icon(target_node->get_class(), "EditorIcons"))
+ icon = get_icon(target_node->get_class(), "EditorIcons");
+ else
+ icon = get_icon("Node", "EditorIcons");
+
+ assign->set_icon(icon);
+}
+
+void EditorPropertyNodePath::setup(const NodePath &p_base_hint) {
+
+ base_hint = p_base_hint;
+}
+
+void EditorPropertyNodePath::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Ref<Texture> t = get_icon("Clear", "EditorIcons");
+ clear->set_icon(t);
+ }
+}
+
+void EditorPropertyNodePath::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_node_selected"), &EditorPropertyNodePath::_node_selected);
+ ClassDB::bind_method(D_METHOD("_node_assign"), &EditorPropertyNodePath::_node_assign);
+ ClassDB::bind_method(D_METHOD("_node_clear"), &EditorPropertyNodePath::_node_clear);
+}
+
+EditorPropertyNodePath::EditorPropertyNodePath() {
+
+ HBoxContainer *hbc = memnew(HBoxContainer);
+ add_child(hbc);
+ assign = memnew(Button);
+ assign->set_flat(true);
+ assign->set_h_size_flags(SIZE_EXPAND_FILL);
+ assign->set_clip_text(true);
+ assign->connect("pressed", this, "_node_assign");
+ hbc->add_child(assign);
+
+ clear = memnew(Button);
+ clear->set_flat(true);
+ clear->connect("pressed", this, "_node_clear");
+ hbc->add_child(clear);
+
+ scene_tree = NULL; //do not allocate unnecesarily
+}
+
+////////////// RESOURCE //////////////////////
+
+void EditorPropertyResource::_file_selected(const String &p_path) {
+
+ RES res = ResourceLoader::load(p_path);
+ emit_signal("property_changed", get_edited_property(), res);
+ update_property();
+}
+
+void EditorPropertyResource::_menu_option(int p_which) {
+
+ // scene_tree->popup_centered_ratio();
+ switch (p_which) {
+ case OBJ_MENU_LOAD: {
+
+ if (!file) {
+ file = memnew(EditorFileDialog);
+ file->connect("file_selected", this, "_file_selected");
+ add_child(file);
+ }
+ file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
+ String type = base_type;
+
+ List<String> extensions;
+ for (int i = 0; i < type.get_slice_count(","); i++) {
+
+ ResourceLoader::get_recognized_extensions_for_type(type.get_slice(",", i), &extensions);
+ }
+
+ Set<String> valid_extensions;
+ for (List<String>::Element *E = extensions.front(); E; E = E->next()) {
+ valid_extensions.insert(E->get());
+ }
+
+ file->clear_filters();
+ for (Set<String>::Element *E = valid_extensions.front(); E; E = E->next()) {
+
+ file->add_filter("*." + E->get() + " ; " + E->get().to_upper());
+ }
+
+ file->popup_centered_ratio();
+ } break;
+
+ case OBJ_MENU_EDIT: {
+
+ RES res = get_edited_object()->get(get_edited_property());
+
+ if (!res.is_null()) {
+
+ emit_signal("resource_selected", get_edited_property(), res);
+ }
+ } break;
+ case OBJ_MENU_CLEAR: {
+
+ emit_signal("property_changed", get_edited_property(), RES());
+ update_property();
+
+ } break;
+
+ case OBJ_MENU_MAKE_UNIQUE: {
+
+ RES res_orig = get_edited_object()->get(get_edited_property());
+ if (res_orig.is_null())
+ return;
+
+ List<PropertyInfo> property_list;
+ res_orig->get_property_list(&property_list);
+ List<Pair<String, Variant> > propvalues;
+
+ for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) {
+
+ Pair<String, Variant> p;
+ PropertyInfo &pi = E->get();
+ if (pi.usage & PROPERTY_USAGE_STORAGE) {
+
+ p.first = pi.name;
+ p.second = res_orig->get(pi.name);
+ }
+
+ propvalues.push_back(p);
+ }
+
+ String orig_type = res_orig->get_class();
+
+ Object *inst = ClassDB::instance(orig_type);
+
+ Ref<Resource> res = Ref<Resource>(Object::cast_to<Resource>(inst));
+
+ ERR_FAIL_COND(res.is_null());
+
+ for (List<Pair<String, Variant> >::Element *E = propvalues.front(); E; E = E->next()) {
+
+ Pair<String, Variant> &p = E->get();
+ res->set(p.first, p.second);
+ }
+
+ emit_signal("property_changed", get_edited_property(), res);
+ update_property();
+
+ } break;
+
+ case OBJ_MENU_COPY: {
+ RES res = get_edited_object()->get(get_edited_property());
+
+ EditorSettings::get_singleton()->set_resource_clipboard(res);
+
+ } break;
+ case OBJ_MENU_PASTE: {
+
+ RES res = EditorSettings::get_singleton()->get_resource_clipboard();
+ emit_signal("property_changed", get_edited_property(), res);
+ update_property();
+
+ } break;
+ case OBJ_MENU_NEW_SCRIPT: {
+
+ if (Object::cast_to<Node>(get_edited_object())) {
+ EditorNode::get_singleton()->get_scene_tree_dock()->open_script_dialog(Object::cast_to<Node>(get_edited_object()));
+ }
+
+ } break;
+ case OBJ_MENU_SHOW_IN_FILE_SYSTEM: {
+ RES res = get_edited_object()->get(get_edited_property());
+
+ FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
+ file_system_dock->navigate_to_path(res->get_path());
+ // Ensure that the FileSystem dock is visible.
+ TabContainer *tab_container = (TabContainer *)file_system_dock->get_parent_control();
+ tab_container->set_current_tab(file_system_dock->get_position_in_parent());
+ } break;
+ default: {
+
+ RES res = get_edited_object()->get(get_edited_property());
+
+ if (p_which >= CONVERT_BASE_ID) {
+
+ int to_type = p_which - CONVERT_BASE_ID;
+
+ Vector<Ref<EditorResourceConversionPlugin> > conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(res);
+
+ ERR_FAIL_INDEX(to_type, conversions.size());
+
+ Ref<Resource> new_res = conversions[to_type]->convert(res);
+
+ emit_signal("property_changed", get_edited_property(), new_res);
+ update_property();
+ break;
+ }
+ ERR_FAIL_COND(inheritors_array.empty());
+
+ String intype = inheritors_array[p_which - TYPE_BASE_ID];
+
+ if (intype == "ViewportTexture") {
+
+ if (!scene_tree) {
+ scene_tree = memnew(SceneTreeDialog);
+ add_child(scene_tree);
+ scene_tree->connect("selected", this, "_viewport_selected");
+ scene_tree->set_title(TTR("Pick a Viewport"));
+ }
+ scene_tree->popup_centered_ratio();
+
+ return;
+ }
+
+ Object *obj = ClassDB::instance(intype);
+
+ if (!obj) {
+ obj = EditorNode::get_editor_data().instance_custom_type(intype, "Resource");
+ }
+
+ ERR_BREAK(!obj);
+ Resource *resp = Object::cast_to<Resource>(obj);
+ ERR_BREAK(!resp);
+ if (get_edited_object() && base_type != String() && base_type == "Script") {
+ //make visual script the right type
+ res->call("set_instance_base_type", get_edited_object()->get_class());
+ }
+
+ res = Ref<Resource>(resp);
+ emit_signal("property_changed", get_edited_property(), res);
+ update_property();
+
+ } break;
+ }
+}
+
+void EditorPropertyResource::_resource_preview(const String &p_path, const Ref<Texture> &p_preview, ObjectID p_obj) {
+
+ RES p = get_edited_object()->get(get_edited_property());
+ if (p.is_valid() && p->get_instance_id() == p_obj) {
+ if (p_preview.is_valid()) {
+ assign->set_icon(p_preview);
+ }
+ }
+}
+
+void EditorPropertyResource::_update_menu() {
+ //////////////////// UPDATE MENU //////////////////////////
+ RES res = get_edited_object()->get(get_edited_property());
+
+ menu->clear();
+
+ if (get_edited_property() == "script" && base_type == "Script" && Object::cast_to<Node>(get_edited_object())) {
+ menu->add_icon_item(get_icon("Script", "EditorIcons"), TTR("New Script"), OBJ_MENU_NEW_SCRIPT);
+ menu->add_separator();
+ } else if (base_type != "") {
+ int idx = 0;
+
+ Vector<EditorData::CustomType> custom_resources;
+
+ if (EditorNode::get_editor_data().get_custom_types().has("Resource")) {
+ custom_resources = EditorNode::get_editor_data().get_custom_types()["Resource"];
+ }
+
+ for (int i = 0; i < base_type.get_slice_count(","); i++) {
+
+ String base = base_type.get_slice(",", i);
+
+ Set<String> valid_inheritors;
+ valid_inheritors.insert(base);
+ List<StringName> inheritors;
+ ClassDB::get_inheriters_from_class(base.strip_edges(), &inheritors);
+
+ for (int i = 0; i < custom_resources.size(); i++) {
+ inheritors.push_back(custom_resources[i].name);
+ }
+
+ List<StringName>::Element *E = inheritors.front();
+ while (E) {
+ valid_inheritors.insert(E->get());
+ E = E->next();
+ }
+
+ for (Set<String>::Element *E = valid_inheritors.front(); E; E = E->next()) {
+ String t = E->get();
+
+ bool is_custom_resource = false;
+ Ref<Texture> icon;
+ if (!custom_resources.empty()) {
+ for (int i = 0; i < custom_resources.size(); i++) {
+ if (custom_resources[i].name == t) {
+ is_custom_resource = true;
+ if (custom_resources[i].icon.is_valid())
+ icon = custom_resources[i].icon;
+ break;
+ }
+ }
+ }
+
+ if (!is_custom_resource && !ClassDB::can_instance(t))
+ continue;
+
+ inheritors_array.push_back(t);
+
+ int id = TYPE_BASE_ID + idx;
+
+ if (!icon.is_valid() && has_icon(t, "EditorIcons")) {
+ icon = get_icon(t, "EditorIcons");
+ }
+
+ if (icon.is_valid()) {
+
+ menu->add_icon_item(icon, vformat(TTR("New %s"), t), id);
+ } else {
+
+ menu->add_item(vformat(TTR("New %s"), t), id);
+ }
+
+ idx++;
+ }
+ }
+
+ if (menu->get_item_count())
+ menu->add_separator();
+ }
+
+ menu->add_icon_item(get_icon("Load", "EditorIcons"), TTR("Load"), OBJ_MENU_LOAD);
+
+ if (!res.is_null()) {
+
+ menu->add_icon_item(get_icon("Edit", "EditorIcons"), TTR("Edit"), OBJ_MENU_EDIT);
+ menu->add_icon_item(get_icon("Clear", "EditorIcons"), TTR("Clear"), OBJ_MENU_CLEAR);
+ menu->add_icon_item(get_icon("Duplicate", "EditorIcons"), TTR("Make Unique"), OBJ_MENU_MAKE_UNIQUE);
+ RES r = res;
+ if (r.is_valid() && r->get_path().is_resource_file()) {
+ menu->add_separator();
+ menu->add_item(TTR("Show in File System"), OBJ_MENU_SHOW_IN_FILE_SYSTEM);
+ }
+ } else {
+ }
+
+ RES cb = EditorSettings::get_singleton()->get_resource_clipboard();
+ bool paste_valid = false;
+ if (cb.is_valid()) {
+ if (base_type == "")
+ paste_valid = true;
+ else
+ for (int i = 0; i < base_type.get_slice_count(","); i++)
+ if (ClassDB::is_parent_class(cb->get_class(), base_type.get_slice(",", i))) {
+ paste_valid = true;
+ break;
+ }
+ }
+
+ if (!res.is_null() || paste_valid) {
+ menu->add_separator();
+
+ if (!res.is_null()) {
+
+ menu->add_item(TTR("Copy"), OBJ_MENU_COPY);
+ }
+
+ if (paste_valid) {
+
+ menu->add_item(TTR("Paste"), OBJ_MENU_PASTE);
+ }
+ }
+
+ if (!res.is_null()) {
+
+ Vector<Ref<EditorResourceConversionPlugin> > conversions = EditorNode::get_singleton()->find_resource_conversion_plugin(res);
+ if (conversions.size()) {
+ menu->add_separator();
+ }
+ for (int i = 0; i < conversions.size(); i++) {
+ String what = conversions[i]->converts_to();
+ Ref<Texture> icon;
+ if (has_icon(what, "EditorIcons")) {
+
+ icon = get_icon(what, "EditorIcons");
+ } else {
+
+ icon = get_icon(what, "Resource");
+ }
+
+ menu->add_icon_item(icon, vformat(TTR("Convert To %s"), what), CONVERT_BASE_ID + i);
+ }
+ }
+
+ Rect2 gt = get_global_rect();
+ int ms = menu->get_combined_minimum_size().width;
+ Vector2 popup_pos = gt.position + gt.size - Vector2(ms, 0);
+ menu->set_position(popup_pos);
+ menu->popup();
+}
+
+void EditorPropertyResource::update_property() {
+
+ RES res = get_edited_object()->get(get_edited_property());
+
+ if (res == RES()) {
+ assign->set_icon(Ref<Texture>());
+ assign->set_text(TTR("[empty]"));
+ assign->set_disabled(true);
+ } else {
+ assign->set_disabled(false);
+
+ Ref<Texture> icon;
+ if (has_icon(res->get_class(), "EditorIcons"))
+ icon = get_icon(res->get_class(), "EditorIcons");
+ else
+ icon = get_icon("Node", "EditorIcons");
+
+ assign->set_icon(icon);
+
+ if (res->get_name() != String()) {
+ assign->set_text(res->get_name());
+ } else if (res->get_path().is_resource_file()) {
+ assign->set_text(res->get_name());
+ assign->set_tooltip(res->get_path());
+ } else {
+ assign->set_text(res->get_class());
+ }
+
+ if (res->get_path().is_resource_file()) {
+ assign->set_tooltip(res->get_path());
+ }
+
+ //preview will override the above, so called at the end
+ EditorResourcePreview::get_singleton()->queue_edited_resource_preview(res, this, "_resource_preview", res->get_instance_id());
+ }
+}
+
+void EditorPropertyResource::_resource_selected() {
+ RES res = get_edited_object()->get(get_edited_property());
+
+ if (!res.is_null()) {
+
+ emit_signal("resource_selected", get_edited_property(), res);
+ }
+}
+
+void EditorPropertyResource::setup(const String &p_base_type) {
+ base_type = p_base_type;
+}
+
+void EditorPropertyResource::_notification(int p_what) {
+
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ Ref<Texture> t = get_icon("select_arrow", "Tree");
+ edit->set_icon(t);
+ }
+}
+
+void EditorPropertyResource::_viewport_selected(const NodePath &p_path) {
+
+ Node *to_node = get_node(p_path);
+ if (!Object::cast_to<Viewport>(to_node)) {
+ EditorNode::get_singleton()->show_warning(TTR("Selected node is not a Viewport!"));
+ return;
+ }
+
+ Ref<ViewportTexture> vt;
+ vt.instance();
+ vt->set_viewport_path_in_scene(get_tree()->get_edited_scene_root()->get_path_to(to_node));
+ vt->setup_local_to_scene();
+
+ emit_signal("property_changed", get_edited_property(), vt);
+ update_property();
+}
+void EditorPropertyResource::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("_file_selected"), &EditorPropertyResource::_file_selected);
+ ClassDB::bind_method(D_METHOD("_menu_option"), &EditorPropertyResource::_menu_option);
+ ClassDB::bind_method(D_METHOD("_update_menu"), &EditorPropertyResource::_update_menu);
+ ClassDB::bind_method(D_METHOD("_resource_preview"), &EditorPropertyResource::_resource_preview);
+ ClassDB::bind_method(D_METHOD("_resource_selected"), &EditorPropertyResource::_resource_selected);
+ ClassDB::bind_method(D_METHOD("_viewport_selected"), &EditorPropertyResource::_viewport_selected);
+}
+
+EditorPropertyResource::EditorPropertyResource() {
+
+ HBoxContainer *hbc = memnew(HBoxContainer);
+ add_child(hbc);
+ assign = memnew(Button);
+ assign->set_flat(true);
+ assign->set_h_size_flags(SIZE_EXPAND_FILL);
+ assign->set_clip_text(true);
+ assign->connect("pressed", this, "_resource_selected");
+ hbc->add_child(assign);
+
+ menu = memnew(PopupMenu);
+ add_child(menu);
+ edit = memnew(Button);
+ edit->set_flat(true);
+ menu->connect("id_pressed", this, "_menu_option");
+ edit->connect("pressed", this, "_update_menu");
+ hbc->add_child(edit);
+
+ file = NULL;
+ scene_tree = NULL;
+}
+
+////////////// DEFAULT PLUGIN //////////////////////
+
+bool EditorInspectorDefaultPlugin::can_handle(Object *p_object) {
+ return true; //can handle everything
+}
+
+void EditorInspectorDefaultPlugin::parse_begin(Object *p_object) {
+ //do none
+}
+
+bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage) {
+
+ switch (p_type) {
+
+ // atomic types
+ case Variant::BOOL: {
+ EditorPropertyCheck *editor = memnew(EditorPropertyCheck);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::INT: {
+
+ if (p_hint == PROPERTY_HINT_ENUM) {
+ EditorPropertyEnum *editor = memnew(EditorPropertyEnum);
+ Vector<String> options = p_hint_text.split(",");
+ editor->setup(options);
+ add_property_editor(p_path, editor);
+
+ } else if (p_hint == PROPERTY_HINT_FLAGS) {
+ EditorPropertyFlags *editor = memnew(EditorPropertyFlags);
+ Vector<String> options = p_hint_text.split(",");
+ editor->setup(options);
+ add_property_editor(p_path, editor);
+
+ } else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_2D_RENDER || p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS || p_hint == PROPERTY_HINT_LAYERS_3D_RENDER) {
+
+ EditorPropertyLayers::LayerType lt;
+ switch (p_hint) {
+ case PROPERTY_HINT_LAYERS_2D_RENDER:
+ lt = EditorPropertyLayers::LAYER_RENDER_2D;
+ break;
+ case PROPERTY_HINT_LAYERS_2D_PHYSICS:
+ lt = EditorPropertyLayers::LAYER_PHYSICS_2D;
+ break;
+ case PROPERTY_HINT_LAYERS_3D_RENDER:
+ lt = EditorPropertyLayers::LAYER_RENDER_3D;
+ break;
+ case PROPERTY_HINT_LAYERS_3D_PHYSICS:
+ lt = EditorPropertyLayers::LAYER_PHYSICS_3D;
+ break;
+ default: {} //compiler could be smarter here and realize this cant happen
+ }
+ EditorPropertyLayers *editor = memnew(EditorPropertyLayers);
+ editor->setup(lt);
+ add_property_editor(p_path, editor);
+ } else if (p_hint == PROPERTY_HINT_OBJECT_ID) {
+
+ EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
+ editor->setup(p_hint_text);
+ add_property_editor(p_path, editor);
+
+ } else {
+ EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
+ int min = 0, max = 65535;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_int();
+ max = p_hint_text.get_slice(",", 1).to_int();
+ }
+
+ editor->setup(min, max);
+
+ add_property_editor(p_path, editor);
+ }
+ } break;
+ case Variant::REAL: {
+
+ if (p_hint == PROPERTY_HINT_EXP_EASING) {
+ EditorPropertyEasing *editor = memnew(EditorPropertyEasing);
+ bool full = true;
+ bool flip = false;
+ Vector<String> hints = p_hint_text.split(",");
+ for (int i = 0; i < hints.size(); i++) {
+ String h = hints[i].strip_edges();
+ if (h == "attenuation") {
+ flip = true;
+ }
+ if (h == "inout") {
+ full = true;
+ }
+ }
+
+ editor->setup(full, flip);
+ add_property_editor(p_path, editor);
+
+ } else {
+ EditorPropertyFloat *editor = memnew(EditorPropertyFloat);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+ bool exp_range = false;
+
+ if ((p_hint == PROPERTY_HINT_RANGE || p_hint == PROPERTY_HINT_EXP_RANGE) && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ exp_range = p_hint == PROPERTY_HINT_EXP_RANGE;
+ }
+
+ editor->setup(min, max, step, hide_slider, exp_range);
+
+ add_property_editor(p_path, editor);
+ }
+ } break;
+ case Variant::STRING: {
+
+ if (p_hint == PROPERTY_HINT_ENUM) {
+ EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);
+ Vector<String> options = p_hint_text.split(",");
+ editor->setup(options);
+ add_property_editor(p_path, editor);
+ } else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) {
+ EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText);
+ add_property_editor(p_path, editor);
+ } else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE) {
+
+ Vector<String> extensions = p_hint_text.split(",");
+ bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE;
+ bool folder = p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_GLOBAL_DIR;
+ EditorPropertyPath *editor = memnew(EditorPropertyPath);
+ editor->setup(extensions, folder, global);
+ add_property_editor(p_path, editor);
+ } else if (p_hint == PROPERTY_HINT_METHOD_OF_VARIANT_TYPE ||
+ p_hint == PROPERTY_HINT_METHOD_OF_BASE_TYPE ||
+ p_hint == PROPERTY_HINT_METHOD_OF_INSTANCE ||
+ p_hint == PROPERTY_HINT_METHOD_OF_SCRIPT ||
+ p_hint == PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE ||
+ p_hint == PROPERTY_HINT_PROPERTY_OF_BASE_TYPE ||
+ p_hint == PROPERTY_HINT_PROPERTY_OF_INSTANCE ||
+ p_hint == PROPERTY_HINT_PROPERTY_OF_SCRIPT) {
+
+ EditorPropertyMember *editor = memnew(EditorPropertyMember);
+
+ EditorPropertyMember::Type type = EditorPropertyMember::MEMBER_METHOD_OF_BASE_TYPE;
+ switch (p_hint) {
+ case PROPERTY_HINT_METHOD_OF_BASE_TYPE: type = EditorPropertyMember::MEMBER_METHOD_OF_BASE_TYPE; break;
+ case PROPERTY_HINT_METHOD_OF_INSTANCE: type = EditorPropertyMember::MEMBER_METHOD_OF_INSTANCE; break;
+ case PROPERTY_HINT_METHOD_OF_SCRIPT: type = EditorPropertyMember::MEMBER_METHOD_OF_SCRIPT; break;
+ case PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_VARIANT_TYPE; break;
+ case PROPERTY_HINT_PROPERTY_OF_BASE_TYPE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_BASE_TYPE; break;
+ case PROPERTY_HINT_PROPERTY_OF_INSTANCE: type = EditorPropertyMember::MEMBER_PROPERTY_OF_INSTANCE; break;
+ case PROPERTY_HINT_PROPERTY_OF_SCRIPT: type = EditorPropertyMember::MEMBER_PROPERTY_OF_SCRIPT; break;
+ default: {}
+ }
+ editor->setup(type, p_hint_text);
+ add_property_editor(p_path, editor);
+
+ } else {
+
+ EditorPropertyText *editor = memnew(EditorPropertyText);
+ add_property_editor(p_path, editor);
+ }
+ } break;
+
+ // math types
+
+ case Variant::VECTOR2: {
+ EditorPropertyVector2 *editor = memnew(EditorPropertyVector2);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+
+ } break; // 5
+ case Variant::RECT2: {
+ EditorPropertyRect2 *editor = memnew(EditorPropertyRect2);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::VECTOR3: {
+ EditorPropertyVector3 *editor = memnew(EditorPropertyVector3);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+
+ } break;
+ case Variant::TRANSFORM2D: {
+ EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+
+ } break;
+ case Variant::PLANE: {
+ EditorPropertyPlane *editor = memnew(EditorPropertyPlane);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::QUAT: {
+ EditorPropertyQuat *editor = memnew(EditorPropertyQuat);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+ } break; // 10
+ case Variant::AABB: {
+ EditorPropertyAABB *editor = memnew(EditorPropertyAABB);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::BASIS: {
+ EditorPropertyBasis *editor = memnew(EditorPropertyBasis);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::TRANSFORM: {
+ EditorPropertyTransform *editor = memnew(EditorPropertyTransform);
+ double min = -65535, max = 65535, step = 0.001;
+ bool hide_slider = true;
+
+ if (p_hint == PROPERTY_HINT_RANGE && p_hint_text.get_slice_count(",") >= 2) {
+ min = p_hint_text.get_slice(",", 0).to_double();
+ max = p_hint_text.get_slice(",", 1).to_double();
+ if (p_hint_text.get_slice_count(",") >= 3) {
+ step = p_hint_text.get_slice(",", 2).to_double();
+ }
+ hide_slider = false;
+ }
+
+ editor->setup(min, max, step, hide_slider);
+ add_property_editor(p_path, editor);
+
+ } break;
+
+ // misc types
+ case Variant::COLOR: {
+ EditorPropertyColor *editor = memnew(EditorPropertyColor);
+ editor->setup(p_hint != PROPERTY_HINT_COLOR_NO_ALPHA);
+ add_property_editor(p_path, editor);
+ } break;
+ case Variant::NODE_PATH: {
+
+ EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath);
+ if (p_hint == PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE && p_hint_text != String()) {
+ editor->setup(p_hint_text);
+ }
+ add_property_editor(p_path, editor);
+
+ } break; // 15
+ case Variant::_RID: {
+ } break;
+ case Variant::OBJECT: {
+ EditorPropertyResource *editor = memnew(EditorPropertyResource);
+ editor->setup(p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource");
+ add_property_editor(p_path, editor);
+
+ } break;
+ case Variant::DICTIONARY: {
+ } break;
+ case Variant::ARRAY: {
+ } break;
+
+ // arrays
+ case Variant::POOL_BYTE_ARRAY: {
+ } break; // 20
+ case Variant::POOL_INT_ARRAY: {
+ } break;
+ case Variant::POOL_REAL_ARRAY: {
+ } break;
+ case Variant::POOL_STRING_ARRAY: {
+ } break;
+ case Variant::POOL_VECTOR2_ARRAY: {
+ } break;
+ case Variant::POOL_VECTOR3_ARRAY: {
+ } break; // 25
+ case Variant::POOL_COLOR_ARRAY: {
+ } break;
+ default: {}
+ }
+
+ return false; //can be overriden, although it will most likely be last anyway
+}
+
+void EditorInspectorDefaultPlugin::parse_end() {
+ //do none
+}
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
new file mode 100644
index 0000000000..0a831bfcfe
--- /dev/null
+++ b/editor/editor_properties.h
@@ -0,0 +1,490 @@
+#ifndef EDITOR_PROPERTIES_H
+#define EDITOR_PROPERTIES_H
+
+#include "editor/create_dialog.h"
+#include "editor/editor_file_system.h"
+#include "editor/editor_inspector.h"
+#include "editor/editor_spin_slider.h"
+#include "editor/property_selector.h"
+#include "editor/scene_tree_editor.h"
+#include "scene/gui/color_picker.h"
+
+class EditorPropertyText : public EditorProperty {
+ GDCLASS(EditorPropertyText, EditorProperty)
+ LineEdit *text;
+
+ bool updating;
+ void _text_changed(const String &p_string);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ EditorPropertyText();
+};
+
+class EditorPropertyMultilineText : public EditorProperty {
+ GDCLASS(EditorPropertyMultilineText, EditorProperty)
+ TextEdit *text;
+
+ AcceptDialog *big_text_dialog;
+ TextEdit *big_text;
+ Button *open_big_text;
+
+ void _big_text_changed();
+ void _text_changed();
+ void _open_big_text();
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ EditorPropertyMultilineText();
+};
+
+class EditorPropertyTextEnum : public EditorProperty {
+ GDCLASS(EditorPropertyTextEnum, EditorProperty)
+ OptionButton *options;
+
+ void _option_selected(int p_which);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(const Vector<String> &p_options);
+ virtual void update_property();
+ EditorPropertyTextEnum();
+};
+
+class EditorPropertyPath : public EditorProperty {
+ GDCLASS(EditorPropertyPath, EditorProperty)
+ Vector<String> extensions;
+ bool folder;
+ bool global;
+ EditorFileDialog *dialog;
+ Button *path;
+
+ void _path_selected(const String &p_path);
+ void _path_pressed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(const Vector<String> &p_extensions, bool p_folder, bool p_global);
+ virtual void update_property();
+ EditorPropertyPath();
+};
+
+class EditorPropertyMember : public EditorProperty {
+ GDCLASS(EditorPropertyMember, EditorProperty)
+public:
+ enum Type {
+ MEMBER_METHOD_OF_VARIANT_TYPE, ///< a method of a type
+ MEMBER_METHOD_OF_BASE_TYPE, ///< a method of a base type
+ MEMBER_METHOD_OF_INSTANCE, ///< a method of an instance
+ MEMBER_METHOD_OF_SCRIPT, ///< a method of a script & base
+ MEMBER_PROPERTY_OF_VARIANT_TYPE, ///< a property of a type
+ MEMBER_PROPERTY_OF_BASE_TYPE, ///< a property of a base type
+ MEMBER_PROPERTY_OF_INSTANCE, ///< a property of an instance
+ MEMBER_PROPERTY_OF_SCRIPT, ///< a property of a script & base
+
+ };
+
+private:
+ Type hint;
+ PropertySelector *selector;
+ Button *property;
+ String hint_text;
+
+ void _property_selected(const String &p_selected);
+ void _property_select();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(Type p_hint, const String &p_hint_text);
+ virtual void update_property();
+ EditorPropertyMember();
+};
+
+class EditorPropertyCheck : public EditorProperty {
+ GDCLASS(EditorPropertyCheck, EditorProperty)
+ CheckBox *checkbox;
+
+ void _checkbox_pressed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ EditorPropertyCheck();
+};
+
+class EditorPropertyEnum : public EditorProperty {
+ GDCLASS(EditorPropertyEnum, EditorProperty)
+ OptionButton *options;
+
+ void _option_selected(int p_which);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(const Vector<String> &p_options);
+ virtual void update_property();
+ EditorPropertyEnum();
+};
+
+class EditorPropertyFlags : public EditorProperty {
+ GDCLASS(EditorPropertyFlags, EditorProperty)
+ VBoxContainer *vbox;
+ Vector<CheckBox *> flags;
+ Vector<int> flag_indices;
+
+ void _flag_toggled();
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(const Vector<String> &p_options);
+ virtual void update_property();
+ EditorPropertyFlags();
+};
+
+class EditorPropertyLayersGrid;
+
+class EditorPropertyLayers : public EditorProperty {
+ GDCLASS(EditorPropertyLayers, EditorProperty)
+public:
+ enum LayerType {
+ LAYER_PHYSICS_2D,
+ LAYER_RENDER_2D,
+ LAYER_PHYSICS_3D,
+ LAYER_RENDER_3D,
+ };
+
+private:
+ EditorPropertyLayersGrid *grid;
+ void _grid_changed(uint32_t p_grid);
+ LayerType layer_type;
+ PopupMenu *layers;
+ Button *button;
+
+ void _button_pressed();
+ void _menu_pressed(int p_menu);
+
+protected:
+ static void _bind_methods();
+
+public:
+ void setup(LayerType p_layer_type);
+ virtual void update_property();
+ EditorPropertyLayers();
+};
+
+class EditorPropertyInteger : public EditorProperty {
+ GDCLASS(EditorPropertyInteger, EditorProperty)
+ EditorSpinSlider *spin;
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(int p_min, int p_max);
+ EditorPropertyInteger();
+};
+
+class EditorPropertyObjectID : public EditorProperty {
+ GDCLASS(EditorPropertyObjectID, EditorProperty)
+ Button *edit;
+ String base_type;
+ void _edit_pressed();
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(const String &p_base_type);
+ EditorPropertyObjectID();
+};
+
+class EditorPropertyFloat : public EditorProperty {
+ GDCLASS(EditorPropertyFloat, EditorProperty)
+ EditorSpinSlider *spin;
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider, bool p_exp_range);
+ EditorPropertyFloat();
+};
+
+class EditorPropertyEasing : public EditorProperty {
+ GDCLASS(EditorPropertyEasing, EditorProperty)
+ Control *easing_draw;
+ ToolButton *button_out, *button_in, *button_linear, *button_constant;
+ ToolButton *button_in_out, *button_out_in;
+ VBoxContainer *vb;
+
+ bool flip;
+
+ void _drag_easing(const Ref<InputEvent> &p_ev);
+ void _draw_easing();
+ void _notification(int p_what);
+ void _set_preset(float p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(bool p_full, bool p_flip);
+ EditorPropertyEasing();
+};
+
+class EditorPropertyVector2 : public EditorProperty {
+ GDCLASS(EditorPropertyVector2, EditorProperty)
+ EditorSpinSlider *spin[2];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyVector2();
+};
+
+class EditorPropertyRect2 : public EditorProperty {
+ GDCLASS(EditorPropertyRect2, EditorProperty)
+ EditorSpinSlider *spin[4];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyRect2();
+};
+
+class EditorPropertyVector3 : public EditorProperty {
+ GDCLASS(EditorPropertyVector3, EditorProperty)
+ EditorSpinSlider *spin[3];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyVector3();
+};
+
+class EditorPropertyPlane : public EditorProperty {
+ GDCLASS(EditorPropertyPlane, EditorProperty)
+ EditorSpinSlider *spin[4];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyPlane();
+};
+
+class EditorPropertyQuat : public EditorProperty {
+ GDCLASS(EditorPropertyQuat, EditorProperty)
+ EditorSpinSlider *spin[4];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyQuat();
+};
+
+class EditorPropertyAABB : public EditorProperty {
+ GDCLASS(EditorPropertyAABB, EditorProperty)
+ EditorSpinSlider *spin[6];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyAABB();
+};
+
+class EditorPropertyTransform2D : public EditorProperty {
+ GDCLASS(EditorPropertyTransform2D, EditorProperty)
+ EditorSpinSlider *spin[6];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyTransform2D();
+};
+
+class EditorPropertyBasis : public EditorProperty {
+ GDCLASS(EditorPropertyBasis, EditorProperty)
+ EditorSpinSlider *spin[9];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyBasis();
+};
+
+class EditorPropertyTransform : public EditorProperty {
+ GDCLASS(EditorPropertyTransform, EditorProperty)
+ EditorSpinSlider *spin[12];
+ bool setting;
+ void _value_changed(double p_val);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(double p_min, double p_max, double p_step, bool p_no_slider);
+ EditorPropertyTransform();
+};
+
+class EditorPropertyColor : public EditorProperty {
+ GDCLASS(EditorPropertyColor, EditorProperty)
+ ColorPickerButton *picker;
+ void _color_changed(const Color &p_color);
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual void update_property();
+ void setup(bool p_show_alpha);
+ EditorPropertyColor();
+};
+
+class EditorPropertyNodePath : public EditorProperty {
+ GDCLASS(EditorPropertyNodePath, EditorProperty)
+ Button *assign;
+ Button *clear;
+ SceneTreeDialog *scene_tree;
+ NodePath base_hint;
+
+ void _node_selected(const NodePath &p_path);
+ void _node_assign();
+ void _node_clear();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ virtual void update_property();
+ void setup(const NodePath &p_base_hint);
+ EditorPropertyNodePath();
+};
+
+class EditorPropertyResource : public EditorProperty {
+ GDCLASS(EditorPropertyResource, EditorProperty)
+
+ enum MenuOption {
+
+ OBJ_MENU_LOAD = 0,
+ OBJ_MENU_EDIT = 1,
+ OBJ_MENU_CLEAR = 2,
+ OBJ_MENU_MAKE_UNIQUE = 3,
+ OBJ_MENU_COPY = 4,
+ OBJ_MENU_PASTE = 5,
+ OBJ_MENU_NEW_SCRIPT = 6,
+ OBJ_MENU_SHOW_IN_FILE_SYSTEM = 7,
+ TYPE_BASE_ID = 100,
+ CONVERT_BASE_ID = 1000
+
+ };
+
+ Button *assign;
+ Button *edit;
+ PopupMenu *menu;
+ EditorFileDialog *file;
+ Vector<String> inheritors_array;
+
+ String base_type;
+
+ SceneTreeDialog *scene_tree;
+
+ void _file_selected(const String &p_path);
+ void _menu_option(int p_which);
+ void _resource_preview(const String &p_path, const Ref<Texture> &p_preview, ObjectID p_obj);
+ void _resource_selected();
+ void _viewport_selected(const NodePath &p_path);
+
+ void _update_menu();
+
+protected:
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+ virtual void update_property();
+ void setup(const String &p_base_type);
+ EditorPropertyResource();
+};
+
+///////////////////////////////////////////////////
+/// \brief The EditorInspectorDefaultPlugin class
+///
+class EditorInspectorDefaultPlugin : public EditorInspectorPlugin {
+ GDCLASS(EditorInspectorDefaultPlugin, EditorInspectorPlugin)
+
+public:
+ virtual bool can_handle(Object *p_object);
+ virtual void parse_begin(Object *p_object);
+ virtual bool parse_property(Object *p_object, Variant::Type p_type, const String &p_path, PropertyHint p_hint, const String &p_hint_text, int p_usage);
+ virtual void parse_end();
+};
+
+#endif // EDITOR_PROPERTIES_H
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
new file mode 100644
index 0000000000..8143e74a40
--- /dev/null
+++ b/editor/editor_spin_slider.cpp
@@ -0,0 +1,315 @@
+#include "editor_spin_slider.h"
+#include "editor_scale.h"
+#include "os/input.h"
+String EditorSpinSlider::get_text_value() const {
+ int zeros = Math::step_decimals(get_step());
+ return String::num(get_value(), zeros);
+}
+void EditorSpinSlider::_gui_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
+
+ if (mb->is_pressed()) {
+
+ if (updown_offset != -1 && mb->get_position().x > updown_offset) {
+ //there is an updown, so use it.
+ if (mb->get_position().y < get_size().height / 2) {
+ set_value(get_value() + get_step());
+ } else {
+ set_value(get_value() - get_step());
+ }
+ return;
+ } else {
+
+ grabbing_spinner_attempt = true;
+ grabbing_spinner = false;
+ grabbing_spinner_mouse_pos = Input::get_singleton()->get_mouse_position();
+ }
+ } else {
+
+ if (grabbing_spinner_attempt) {
+
+ if (grabbing_spinner) {
+
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ Input::get_singleton()->warp_mouse_position(grabbing_spinner_mouse_pos);
+ update();
+ } else {
+ Rect2 gr = get_global_rect();
+ value_input->set_text(get_text_value());
+ value_input->set_position(gr.position);
+ value_input->set_size(gr.size);
+ value_input->call_deferred("show_modal");
+ value_input->call_deferred("grab_focus");
+ value_input->call_deferred("select_all");
+ }
+
+ grabbing_spinner = false;
+ grabbing_spinner_attempt = false;
+ }
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+
+ if (grabbing_spinner_attempt) {
+
+ if (!grabbing_spinner) {
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
+ grabbing_spinner = true;
+ }
+
+ double v = get_value();
+
+ double diff_x = mm->get_relative().x;
+ diff_x = Math::pow(ABS(diff_x), 1.8f) * SGN(diff_x);
+ diff_x *= 0.1;
+
+ v += diff_x * get_step();
+
+ set_value(v);
+
+ } else if (updown_offset != -1) {
+ bool new_hover = (mm->get_position().x > updown_offset);
+ if (new_hover != hover_updown) {
+ hover_updown = new_hover;
+ update();
+ }
+ }
+ }
+
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid() && k->is_pressed() && k->is_action("ui_accept")) {
+ Rect2 gr = get_global_rect();
+ value_input->set_text(get_text_value());
+ value_input->set_position(gr.position);
+ value_input->set_size(gr.size);
+ value_input->call_deferred("show_modal");
+ value_input->call_deferred("grab_focus");
+ value_input->call_deferred("select_all");
+ }
+}
+
+void EditorSpinSlider::_value_input_closed() {
+ set_value(value_input->get_text().to_double());
+}
+
+void EditorSpinSlider::_value_input_entered(const String &p_text) {
+ set_value(p_text.to_double());
+ value_input->hide();
+}
+
+void EditorSpinSlider::_grabber_gui_input(const Ref<InputEvent> &p_event) {
+
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid() && mb->get_button_index() == BUTTON_LEFT) {
+
+ if (mb->is_pressed()) {
+
+ grabbing_grabber = true;
+ grabbing_ratio = get_as_ratio();
+ grabbing_from = grabber->get_transform().xform(mb->get_position()).x;
+ } else {
+ grabbing_grabber = false;
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid() && grabbing_grabber) {
+
+ float grabbing_ofs = (grabber->get_transform().xform(mm->get_position()).x - grabbing_from) / float(grabber_range);
+ set_as_ratio(grabbing_ratio + grabbing_ofs);
+ update();
+ }
+}
+
+void EditorSpinSlider::_notification(int p_what) {
+
+ if (p_what == MainLoop::NOTIFICATION_WM_FOCUS_OUT || p_what == MainLoop::NOTIFICATION_WM_FOCUS_OUT) {
+ if (grabbing_spinner) {
+ Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
+ grabbing_spinner = false;
+ grabbing_spinner_attempt = false;
+ }
+ }
+
+ if (p_what == NOTIFICATION_DRAW) {
+
+ updown_offset = -1;
+
+ Ref<StyleBox> sb = get_stylebox("normal", "LineEdit");
+ draw_style_box(sb, Rect2(Vector2(), get_size()));
+ Ref<Font> font = get_font("font", "LineEdit");
+
+ int avail_width = get_size().width - sb->get_minimum_size().width - sb->get_minimum_size().width;
+ avail_width -= font->get_string_size(label).width;
+ Ref<Texture> updown = get_icon("updown", "SpinBox");
+
+ if (get_step() == 1) {
+ avail_width -= updown->get_width();
+ }
+
+ if (has_focus()) {
+ Ref<StyleBox> focus = get_stylebox("focus", "LineEdit");
+ draw_style_box(focus, Rect2(Vector2(), get_size()));
+ }
+
+ String numstr = get_text_value();
+
+ int vofs = (get_size().height - font->get_height()) / 2 + font->get_ascent();
+
+ Color fc = get_color("font_color", "LineEdit");
+
+ int label_ofs = sb->get_offset().x + avail_width;
+ draw_string(font, Vector2(label_ofs, vofs), label, fc * Color(1, 1, 1, 0.5));
+ draw_string(font, Vector2(sb->get_offset().x, vofs), numstr, fc, avail_width);
+
+ if (get_step() == 1) {
+ Ref<Texture> updown = get_icon("updown", "SpinBox");
+ int updown_vofs = (get_size().height - updown->get_height()) / 2;
+ updown_offset = get_size().width - sb->get_margin(MARGIN_RIGHT) - updown->get_width();
+ Color c(1, 1, 1);
+ if (hover_updown) {
+ c *= Color(1.2, 1.2, 1.2);
+ }
+ draw_texture(updown, Vector2(updown_offset, updown_vofs), c);
+ if (grabber->is_visible()) {
+ grabber->hide();
+ }
+ } else if (!hide_slider) {
+ int grabber_w = 4 * EDSCALE;
+ int width = get_size().width - sb->get_minimum_size().width - grabber_w;
+ int ofs = sb->get_offset().x;
+ int svofs = (get_size().height + vofs) / 2 - 1;
+ Color c = fc;
+ c.a = 0.2;
+
+ draw_rect(Rect2(ofs, svofs + 1, width, 2 * EDSCALE), c);
+ int gofs = get_as_ratio() * width;
+ c.a = 0.9;
+ Rect2 grabber_rect = Rect2(ofs + gofs, svofs + 1, grabber_w, 2 * EDSCALE);
+ draw_rect(grabber_rect, c);
+
+ bool display_grabber = (mouse_over_spin || mouse_over_grabber) && !grabbing_spinner;
+ if (grabber->is_visible() != display_grabber) {
+ if (display_grabber) {
+ grabber->show();
+ } else {
+ grabber->hide();
+ }
+ }
+
+ if (display_grabber) {
+ Ref<Texture> grabber_tex;
+ if (mouse_over_grabber) {
+ grabber_tex = get_icon("grabber_highlight", "HSlider");
+ } else {
+ grabber_tex = get_icon("grabber", "HSlider");
+ }
+
+ if (grabber->get_texture() != grabber_tex) {
+ grabber->set_texture(grabber_tex);
+ }
+
+ grabber->set_size(Size2(0, 0));
+ grabber->set_position(get_global_position() + grabber_rect.position + grabber_rect.size * 0.5 - grabber->get_size() * 0.5);
+ grabber_range = width;
+ }
+ }
+ }
+
+ if (p_what == NOTIFICATION_MOUSE_ENTER) {
+
+ mouse_over_spin = true;
+ update();
+ }
+ if (p_what == NOTIFICATION_MOUSE_EXIT) {
+
+ mouse_over_spin = false;
+ update();
+ }
+}
+
+Size2 EditorSpinSlider::get_minimum_size() const {
+
+ Ref<StyleBox> sb = get_stylebox("normal", "LineEdit");
+ Ref<Font> font = get_font("font", "LineEdit");
+
+ Size2 ms = sb->get_minimum_size();
+ ms.height += font->get_height();
+
+ return ms;
+}
+
+void EditorSpinSlider::set_hide_slider(bool p_hide) {
+ hide_slider = p_hide;
+ update();
+}
+
+bool EditorSpinSlider::is_hiding_slider() const {
+ return hide_slider;
+}
+
+void EditorSpinSlider::set_label(const String &p_label) {
+ label = p_label;
+ update();
+}
+
+String EditorSpinSlider::get_label() const {
+ return label;
+}
+
+void EditorSpinSlider::_grabber_mouse_entered() {
+ mouse_over_grabber = true;
+ update();
+}
+
+void EditorSpinSlider::_grabber_mouse_exited() {
+ mouse_over_grabber = false;
+ update();
+}
+
+void EditorSpinSlider::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_label", "label"), &EditorSpinSlider::set_label);
+ ClassDB::bind_method(D_METHOD("get_label"), &EditorSpinSlider::get_label);
+
+ ClassDB::bind_method(D_METHOD("_gui_input"), &EditorSpinSlider::_gui_input);
+ ClassDB::bind_method(D_METHOD("_grabber_mouse_entered"), &EditorSpinSlider::_grabber_mouse_entered);
+ ClassDB::bind_method(D_METHOD("_grabber_mouse_exited"), &EditorSpinSlider::_grabber_mouse_exited);
+ ClassDB::bind_method(D_METHOD("_grabber_gui_input"), &EditorSpinSlider::_grabber_gui_input);
+ ClassDB::bind_method(D_METHOD("_value_input_closed"), &EditorSpinSlider::_value_input_closed);
+ ClassDB::bind_method(D_METHOD("_value_input_entered"), &EditorSpinSlider::_value_input_entered);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "label"), "set_label", "get_label");
+}
+
+EditorSpinSlider::EditorSpinSlider() {
+
+ grabbing_spinner_attempt = false;
+ grabbing_spinner = false;
+
+ set_focus_mode(FOCUS_ALL);
+ updown_offset = -1;
+ hover_updown = false;
+ grabber = memnew(TextureRect);
+ add_child(grabber);
+ grabber->hide();
+ grabber->set_as_toplevel(true);
+ grabber->set_mouse_filter(MOUSE_FILTER_STOP);
+ grabber->connect("mouse_entered", this, "_grabber_mouse_entered");
+ grabber->connect("mouse_exited", this, "_grabber_mouse_exited");
+ grabber->connect("gui_input", this, "_grabber_gui_input");
+ mouse_over_spin = false;
+ mouse_over_grabber = false;
+ grabbing_grabber = false;
+ grabber_range = 1;
+ value_input = memnew(LineEdit);
+ add_child(value_input);
+ value_input->set_as_toplevel(true);
+ value_input->hide();
+ value_input->connect("modal_closed", this, "_value_input_closed");
+ value_input->connect("text_entered", this, "_value_input_entered");
+ hide_slider = false;
+}
diff --git a/editor/editor_spin_slider.h b/editor/editor_spin_slider.h
new file mode 100644
index 0000000000..ac8d9e15d6
--- /dev/null
+++ b/editor/editor_spin_slider.h
@@ -0,0 +1,57 @@
+#ifndef EDITOR_SPIN_SLIDER_H
+#define EDITOR_SPIN_SLIDER_H
+
+#include "scene/gui/line_edit.h"
+#include "scene/gui/range.h"
+#include "scene/gui/texture_rect.h"
+
+class EditorSpinSlider : public Range {
+ GDCLASS(EditorSpinSlider, Range)
+
+ String label;
+ int updown_offset;
+ bool hover_updown;
+ bool mouse_hover;
+
+ TextureRect *grabber;
+ int grabber_range;
+
+ bool mouse_over_spin;
+ bool mouse_over_grabber;
+
+ bool grabbing_grabber;
+ int grabbing_from;
+ float grabbing_ratio;
+
+ bool grabbing_spinner_attempt;
+ bool grabbing_spinner;
+ Vector2 grabbing_spinner_mouse_pos;
+
+ LineEdit *value_input;
+
+ void _grabber_gui_input(const Ref<InputEvent> &p_event);
+ void _value_input_closed();
+ void _value_input_entered(const String &);
+
+ bool hide_slider;
+
+protected:
+ void _notification(int p_what);
+ void _gui_input(const Ref<InputEvent> &p_event);
+ static void _bind_methods();
+ void _grabber_mouse_entered();
+ void _grabber_mouse_exited();
+
+public:
+ String get_text_value() const;
+ void set_label(const String &p_label);
+ String get_label() const;
+
+ void set_hide_slider(bool p_hide);
+ bool is_hiding_slider() const;
+
+ virtual Size2 get_minimum_size() const;
+ EditorSpinSlider();
+};
+
+#endif // EDITOR_SPIN_SLIDER_H
diff --git a/editor/icons/icon_GUI_slider_grabber.svg b/editor/icons/icon_GUI_slider_grabber.svg
index b1dcf980a5..b8e6f0a654 100644
--- a/editor/icons/icon_GUI_slider_grabber.svg
+++ b/editor/icons/icon_GUI_slider_grabber.svg
@@ -1,5 +1,82 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<circle cx="8" cy="1044.4" r="3" fill="#fff" fill-opacity=".78431" stroke-linejoin="round" stroke-opacity=".39216" stroke-width="3"/>
-</g>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 15.999999"
+ id="svg8"
+ sodipodi:docname="icon_GUI_slider_grabber.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1211"
+ inkscape:window-height="644"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="-5.7627119"
+ inkscape:cy="8"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="g6" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g6">
+ <path
+ transform="translate(0 1036.4)"
+ d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7 -7 7 7 0 0 0 -7 -7zm0 2a5 5 0 0 1 0.5 0.025391 5 5 0 0 1 0.49414 0.074219 5 5 0 0 1 0.48438 0.12305 5 5 0 0 1 0.46875 0.17188 5 5 0 0 1 0.44922 0.2168 5 5 0 0 1 0.42578 0.26172 5 5 0 0 1 0.39844 0.30273 5 5 0 0 1 0.36524 0.33984 5 5 0 0 1 0.33008 0.37695 5 5 0 0 1 0.29102 0.40625 5 5 0 0 1 0.24805 0.43359 5 5 0 0 1 0.20508 0.45508 5 5 0 0 1 0.1582 0.47461 5 5 0 0 1 0.10938 0.48828 5 5 0 0 1 0.060547 0.49609 5 5 0 0 1 0.011719 0.35352 5 5 0 0 1 -0.025391 0.5 5 5 0 0 1 -0.074218 0.49414 5 5 0 0 1 -0.12305 0.48438 5 5 0 0 1 -0.17188 0.46875 5 5 0 0 1 -0.2168 0.44922 5 5 0 0 1 -0.26172 0.42578 5 5 0 0 1 -0.30273 0.39844 5 5 0 0 1 -0.33984 0.36524 5 5 0 0 1 -0.37695 0.33008 5 5 0 0 1 -0.40625 0.29102 5 5 0 0 1 -0.43359 0.24805 5 5 0 0 1 -0.45508 0.20508 5 5 0 0 1 -0.47461 0.1582 5 5 0 0 1 -0.48828 0.10938 5 5 0 0 1 -0.49609 0.060547 5 5 0 0 1 -0.35352 0.011719 5 5 0 0 1 -0.5 -0.025391 5 5 0 0 1 -0.49414 -0.074218 5 5 0 0 1 -0.48438 -0.12305 5 5 0 0 1 -0.46875 -0.17188 5 5 0 0 1 -0.44922 -0.2168 5 5 0 0 1 -0.42578 -0.26172 5 5 0 0 1 -0.39844 -0.30273 5 5 0 0 1 -0.36523 -0.33984 5 5 0 0 1 -0.33008 -0.37695 5 5 0 0 1 -0.29102 -0.40625 5 5 0 0 1 -0.24805 -0.43359 5 5 0 0 1 -0.20508 -0.45508 5 5 0 0 1 -0.1582 -0.47461 5 5 0 0 1 -0.10938 -0.48828 5 5 0 0 1 -0.060547 -0.49609 5 5 0 0 1 -0.011719 -0.35352 5 5 0 0 1 0.025391 -0.5 5 5 0 0 1 0.074219 -0.49414 5 5 0 0 1 0.12305 -0.48438 5 5 0 0 1 0.17188 -0.46875 5 5 0 0 1 0.2168 -0.44922 5 5 0 0 1 0.26172 -0.42578 5 5 0 0 1 0.30273 -0.39844 5 5 0 0 1 0.33984 -0.36523 5 5 0 0 1 0.37695 -0.33008 5 5 0 0 1 0.40625 -0.29102 5 5 0 0 1 0.43359 -0.24805 5 5 0 0 1 0.45508 -0.20508 5 5 0 0 1 0.47461 -0.1582 5 5 0 0 1 0.48828 -0.10938 5 5 0 0 1 0.49609 -0.060547 5 5 0 0 1 0.35352 -0.011719z"
+ fill="#e0e0e0"
+ id="path2"
+ style="fill:#e0e0e0;fill-opacity:0.28925619" />
+ <circle
+ cx="8"
+ cy="1044.4"
+ r="3"
+ fill="#fff"
+ fill-opacity=".58824"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ stroke-opacity=".32549"
+ stroke-width="3"
+ id="circle4" />
+ </g>
+ <g
+ transform="translate(-0.06779632,-1036.4)"
+ id="g18">
+ <circle
+ style="fill:#ffffff;fill-opacity:0.78430996;stroke-width:3;stroke-linejoin:round;stroke-opacity:0.39216003"
+ cx="8"
+ cy="1044.4"
+ r="3"
+ id="circle16" />
+ </g>
</svg>
diff --git a/editor/icons/icon_GUI_slider_grabber_hl.svg b/editor/icons/icon_GUI_slider_grabber_hl.svg
index 73252751ce..a04ac44cf6 100644
--- a/editor/icons/icon_GUI_slider_grabber_hl.svg
+++ b/editor/icons/icon_GUI_slider_grabber_hl.svg
@@ -1,6 +1,80 @@
-<svg width="16" height="16" version="1.1" viewBox="0 0 16 15.999999" xmlns="http://www.w3.org/2000/svg">
-<g transform="translate(0 -1036.4)">
-<path transform="translate(0 1036.4)" d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7 -7 7 7 0 0 0 -7 -7zm0 2a5 5 0 0 1 0.5 0.025391 5 5 0 0 1 0.49414 0.074219 5 5 0 0 1 0.48438 0.12305 5 5 0 0 1 0.46875 0.17188 5 5 0 0 1 0.44922 0.2168 5 5 0 0 1 0.42578 0.26172 5 5 0 0 1 0.39844 0.30273 5 5 0 0 1 0.36524 0.33984 5 5 0 0 1 0.33008 0.37695 5 5 0 0 1 0.29102 0.40625 5 5 0 0 1 0.24805 0.43359 5 5 0 0 1 0.20508 0.45508 5 5 0 0 1 0.1582 0.47461 5 5 0 0 1 0.10938 0.48828 5 5 0 0 1 0.060547 0.49609 5 5 0 0 1 0.011719 0.35352 5 5 0 0 1 -0.025391 0.5 5 5 0 0 1 -0.074218 0.49414 5 5 0 0 1 -0.12305 0.48438 5 5 0 0 1 -0.17188 0.46875 5 5 0 0 1 -0.2168 0.44922 5 5 0 0 1 -0.26172 0.42578 5 5 0 0 1 -0.30273 0.39844 5 5 0 0 1 -0.33984 0.36524 5 5 0 0 1 -0.37695 0.33008 5 5 0 0 1 -0.40625 0.29102 5 5 0 0 1 -0.43359 0.24805 5 5 0 0 1 -0.45508 0.20508 5 5 0 0 1 -0.47461 0.1582 5 5 0 0 1 -0.48828 0.10938 5 5 0 0 1 -0.49609 0.060547 5 5 0 0 1 -0.35352 0.011719 5 5 0 0 1 -0.5 -0.025391 5 5 0 0 1 -0.49414 -0.074218 5 5 0 0 1 -0.48438 -0.12305 5 5 0 0 1 -0.46875 -0.17188 5 5 0 0 1 -0.44922 -0.2168 5 5 0 0 1 -0.42578 -0.26172 5 5 0 0 1 -0.39844 -0.30273 5 5 0 0 1 -0.36523 -0.33984 5 5 0 0 1 -0.33008 -0.37695 5 5 0 0 1 -0.29102 -0.40625 5 5 0 0 1 -0.24805 -0.43359 5 5 0 0 1 -0.20508 -0.45508 5 5 0 0 1 -0.1582 -0.47461 5 5 0 0 1 -0.10938 -0.48828 5 5 0 0 1 -0.060547 -0.49609 5 5 0 0 1 -0.011719 -0.35352 5 5 0 0 1 0.025391 -0.5 5 5 0 0 1 0.074219 -0.49414 5 5 0 0 1 0.12305 -0.48438 5 5 0 0 1 0.17188 -0.46875 5 5 0 0 1 0.2168 -0.44922 5 5 0 0 1 0.26172 -0.42578 5 5 0 0 1 0.30273 -0.39844 5 5 0 0 1 0.33984 -0.36523 5 5 0 0 1 0.37695 -0.33008 5 5 0 0 1 0.40625 -0.29102 5 5 0 0 1 0.43359 -0.24805 5 5 0 0 1 0.45508 -0.20508 5 5 0 0 1 0.47461 -0.1582 5 5 0 0 1 0.48828 -0.10938 5 5 0 0 1 0.49609 -0.060547 5 5 0 0 1 0.35352 -0.011719z" fill="#e0e0e0"/>
-<circle cx="8" cy="1044.4" r="3" fill="#fff" fill-opacity=".58824" stroke-linecap="round" stroke-linejoin="round" stroke-opacity=".32549" stroke-width="3"/>
-</g>
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 15.999999"
+ id="svg8"
+ sodipodi:docname="icon_GUI_slider_grabber_hl.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata14">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs12" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="944"
+ inkscape:window-height="480"
+ id="namedview10"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g6">
+ <path
+ transform="translate(0 1036.4)"
+ d="m8 1a7 7 0 0 0 -7 7 7 7 0 0 0 7 7 7 7 0 0 0 7 -7 7 7 0 0 0 -7 -7zm0 2a5 5 0 0 1 0.5 0.025391 5 5 0 0 1 0.49414 0.074219 5 5 0 0 1 0.48438 0.12305 5 5 0 0 1 0.46875 0.17188 5 5 0 0 1 0.44922 0.2168 5 5 0 0 1 0.42578 0.26172 5 5 0 0 1 0.39844 0.30273 5 5 0 0 1 0.36524 0.33984 5 5 0 0 1 0.33008 0.37695 5 5 0 0 1 0.29102 0.40625 5 5 0 0 1 0.24805 0.43359 5 5 0 0 1 0.20508 0.45508 5 5 0 0 1 0.1582 0.47461 5 5 0 0 1 0.10938 0.48828 5 5 0 0 1 0.060547 0.49609 5 5 0 0 1 0.011719 0.35352 5 5 0 0 1 -0.025391 0.5 5 5 0 0 1 -0.074218 0.49414 5 5 0 0 1 -0.12305 0.48438 5 5 0 0 1 -0.17188 0.46875 5 5 0 0 1 -0.2168 0.44922 5 5 0 0 1 -0.26172 0.42578 5 5 0 0 1 -0.30273 0.39844 5 5 0 0 1 -0.33984 0.36524 5 5 0 0 1 -0.37695 0.33008 5 5 0 0 1 -0.40625 0.29102 5 5 0 0 1 -0.43359 0.24805 5 5 0 0 1 -0.45508 0.20508 5 5 0 0 1 -0.47461 0.1582 5 5 0 0 1 -0.48828 0.10938 5 5 0 0 1 -0.49609 0.060547 5 5 0 0 1 -0.35352 0.011719 5 5 0 0 1 -0.5 -0.025391 5 5 0 0 1 -0.49414 -0.074218 5 5 0 0 1 -0.48438 -0.12305 5 5 0 0 1 -0.46875 -0.17188 5 5 0 0 1 -0.44922 -0.2168 5 5 0 0 1 -0.42578 -0.26172 5 5 0 0 1 -0.39844 -0.30273 5 5 0 0 1 -0.36523 -0.33984 5 5 0 0 1 -0.33008 -0.37695 5 5 0 0 1 -0.29102 -0.40625 5 5 0 0 1 -0.24805 -0.43359 5 5 0 0 1 -0.20508 -0.45508 5 5 0 0 1 -0.1582 -0.47461 5 5 0 0 1 -0.10938 -0.48828 5 5 0 0 1 -0.060547 -0.49609 5 5 0 0 1 -0.011719 -0.35352 5 5 0 0 1 0.025391 -0.5 5 5 0 0 1 0.074219 -0.49414 5 5 0 0 1 0.12305 -0.48438 5 5 0 0 1 0.17188 -0.46875 5 5 0 0 1 0.2168 -0.44922 5 5 0 0 1 0.26172 -0.42578 5 5 0 0 1 0.30273 -0.39844 5 5 0 0 1 0.33984 -0.36523 5 5 0 0 1 0.37695 -0.33008 5 5 0 0 1 0.40625 -0.29102 5 5 0 0 1 0.43359 -0.24805 5 5 0 0 1 0.45508 -0.20508 5 5 0 0 1 0.47461 -0.1582 5 5 0 0 1 0.48828 -0.10938 5 5 0 0 1 0.49609 -0.060547 5 5 0 0 1 0.35352 -0.011719z"
+ fill="#e0e0e0"
+ id="path2" />
+ <circle
+ cx="8"
+ cy="1044.4"
+ r="3"
+ fill="#fff"
+ fill-opacity=".58824"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ stroke-opacity=".32549"
+ stroke-width="3"
+ id="circle4" />
+ </g>
+ <g
+ transform="translate(-0.06779632,-1036.4)"
+ id="g18">
+ <circle
+ style="fill:#ffffff;fill-opacity:0.78430996;stroke-width:3;stroke-linejoin:round;stroke-opacity:0.39216003"
+ cx="8"
+ cy="1044.4"
+ r="3"
+ id="circle16" />
+ </g>
</svg>
diff --git a/editor/multi_node_edit.cpp b/editor/multi_node_edit.cpp
index 73e78ddf2a..173be01586 100644
--- a/editor/multi_node_edit.cpp
+++ b/editor/multi_node_edit.cpp
@@ -80,8 +80,8 @@ bool MultiNodeEdit::_set_impl(const StringName &p_name, const Variant &p_value,
ur->add_undo_property(n, name, n->get(name));
}
- ur->add_do_method(EditorNode::get_singleton()->get_property_editor(), "refresh");
- ur->add_undo_method(EditorNode::get_singleton()->get_property_editor(), "refresh");
+ ur->add_do_method(EditorNode::get_singleton()->get_inspector(), "refresh");
+ ur->add_undo_method(EditorNode::get_singleton()->get_inspector(), "refresh");
ur->commit_action();
return true;
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index b387972558..23c5e36a92 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -85,7 +85,7 @@ void AnimationPlayerEditor::_notification(int p_what) {
}
frame->set_value(player->get_current_animation_position());
key_editor->set_anim_pos(player->get_current_animation_position());
- EditorNode::get_singleton()->get_property_editor()->refresh();
+ EditorNode::get_singleton()->get_inspector()->refresh();
} else if (last_active) {
//need the last frame after it stopped
@@ -1073,7 +1073,7 @@ void AnimationPlayerEditor::_animation_key_editor_seek(float p_pos, bool p_drag)
updating = false;
_seek_value_changed(p_pos, !p_drag);
- EditorNode::get_singleton()->get_property_editor()->refresh();
+ EditorNode::get_singleton()->get_inspector()->refresh();
//seekit
}
diff --git a/editor/plugins/cube_grid_theme_editor_plugin.cpp b/editor/plugins/cube_grid_theme_editor_plugin.cpp
index 81f45b9f55..68d5ea5247 100644
--- a/editor/plugins/cube_grid_theme_editor_plugin.cpp
+++ b/editor/plugins/cube_grid_theme_editor_plugin.cpp
@@ -198,7 +198,7 @@ void MeshLibraryEditor::_menu_cbk(int p_option) {
} break;
case MENU_OPTION_REMOVE_ITEM: {
- String p = editor->get_property_editor()->get_selected_path();
+ String p = editor->get_inspector()->get_selected_path();
if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/") >= 3) {
to_erase = p.get_slice("/", 3).to_int();
diff --git a/editor/plugins/item_list_editor_plugin.cpp b/editor/plugins/item_list_editor_plugin.cpp
index 8b44f672b0..f75fb0d109 100644
--- a/editor/plugins/item_list_editor_plugin.cpp
+++ b/editor/plugins/item_list_editor_plugin.cpp
@@ -388,7 +388,7 @@ ItemListEditor::ItemListEditor() {
vbc->add_child(property_editor);
property_editor->set_v_size_flags(SIZE_EXPAND_FILL);
- tree = property_editor->get_scene_tree();
+ tree = property_editor->get_property_tree();
}
ItemListEditor::~ItemListEditor() {
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index a4265b4e32..82fd727620 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -785,7 +785,7 @@ void ProjectSettingsEditor::popup_project_settings() {
void ProjectSettingsEditor::_item_selected() {
- TreeItem *ti = globals_editor->get_property_editor()->get_scene_tree()->get_selected();
+ TreeItem *ti = globals_editor->get_property_editor()->get_property_tree()->get_selected();
if (!ti)
return;
if (!ti->get_parent())
@@ -1727,7 +1727,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
//globals_editor->hide_top_label();
globals_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
globals_editor->register_search_box(search_box);
- globals_editor->get_property_editor()->get_scene_tree()->connect("cell_selected", this, "_item_selected");
+ globals_editor->get_property_editor()->get_property_tree()->connect("cell_selected", this, "_item_selected");
globals_editor->get_property_editor()->connect("property_toggled", this, "_item_checked", varray(), CONNECT_DEFERRED);
globals_editor->get_property_editor()->connect("property_edited", this, "_settings_prop_edited");
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index e0063925b1..e912ebe03a 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -4212,7 +4212,7 @@ void PropertyEditor::_bind_methods() {
ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property")));
}
-Tree *PropertyEditor::get_scene_tree() {
+Tree *PropertyEditor::get_property_tree() {
return tree;
}
@@ -4695,7 +4695,7 @@ SectionedPropertyEditor::SectionedPropertyEditor() {
editor->set_v_size_flags(SIZE_EXPAND_FILL);
right_vb->add_child(editor, true);
- editor->get_scene_tree()->set_column_titles_visible(false);
+ editor->get_property_tree()->set_column_titles_visible(false);
editor->hide_top_label();
diff --git a/editor/property_editor.h b/editor/property_editor.h
index 017a190adb..56743822d2 100644
--- a/editor/property_editor.h
+++ b/editor/property_editor.h
@@ -275,7 +275,7 @@ public:
String get_selected_path() const;
- Tree *get_scene_tree();
+ Tree *get_property_tree();
Label *get_top_label();
void hide_top_label();
void update_tree();
@@ -309,6 +309,7 @@ public:
void collapse_all_folding();
void expand_all_folding();
+
PropertyEditor();
~PropertyEditor();
};
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 8b99a3d503..f5cee4cd65 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -732,7 +732,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (node) {
node->set_scene_inherited_state(Ref<SceneState>());
scene_tree->update_tree();
- EditorNode::get_singleton()->get_property_editor()->update_tree();
+ EditorNode::get_singleton()->get_inspector()->update_tree();
}
}
} break;
diff --git a/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp
index a83de1627d..9ce4305683 100644
--- a/editor/script_editor_debugger.cpp
+++ b/editor/script_editor_debugger.cpp
@@ -1851,7 +1851,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream));
ppeer->set_input_buffer_max_size(1024 * 1024 * 8); //8mb should be enough
editor = p_editor;
- editor->get_property_editor()->connect("object_id_selected", this, "_scene_tree_property_select_object");
+ editor->get_inspector()->connect("object_id_selected", this, "_scene_tree_property_select_object");
tabs = memnew(TabContainer);
tabs->set_tab_align(TabContainer::ALIGN_LEFT);
@@ -1936,7 +1936,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
inspector = memnew(PropertyEditor);
inspector->set_h_size_flags(SIZE_EXPAND_FILL);
inspector->hide_top_label();
- inspector->get_scene_tree()->set_column_title(0, TTR("Variable"));
+ inspector->get_property_tree()->set_column_title(0, TTR("Variable"));
inspector->set_enable_capitalize_paths(false);
inspector->set_read_only(true);
inspector->connect("object_id_selected", this, "_scene_tree_property_select_object");
diff --git a/main/main.cpp b/main/main.cpp
index 92b4e31679..c287bc81cb 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -82,6 +82,8 @@
#include "version.h"
#include "version_hash.gen.h"
+#include "main/timer_sync.h"
+
static ProjectSettings *globals = NULL;
static Engine *engine = NULL;
static InputMap *input_map = NULL;
@@ -1221,227 +1223,8 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
}
// everything the main loop needs to know about frame timings
-struct _FrameTime {
- float animation_step; // time to advance animations for (argument to process())
- int physics_steps; // number of times to iterate the physics engine
-
- void clamp_animation(float min_animation_step, float max_animation_step) {
- if (animation_step < min_animation_step) {
- animation_step = min_animation_step;
- } else if (animation_step > max_animation_step) {
- animation_step = max_animation_step;
- }
- }
-};
-
-class _TimerSync {
- // wall clock time measured on the main thread
- uint64_t last_cpu_ticks_usec;
- uint64_t current_cpu_ticks_usec;
-
- // logical game time since last physics timestep
- float time_accum;
-
- // current difference between wall clock time and reported sum of animation_steps
- float time_deficit;
-
- // number of frames back for keeping accumulated physics steps roughly constant.
- // value of 12 chosen because that is what is required to make 144 Hz monitors
- // behave well with 60 Hz physics updates. The only worse commonly available refresh
- // would be 85, requiring CONTROL_STEPS = 17.
- static const int CONTROL_STEPS = 12;
-
- // sum of physics steps done over the last (i+1) frames
- int accumulated_physics_steps[CONTROL_STEPS];
-
- // typical value for accumulated_physics_steps[i] is either this or this plus one
- int typical_physics_steps[CONTROL_STEPS];
-
-protected:
- // returns the fraction of p_frame_slice required for the timer to overshoot
- // before advance_core considers changing the physics_steps return from
- // the typical values as defined by typical_physics_steps
- float get_physics_jitter_fix() {
- return Engine::get_singleton()->get_physics_jitter_fix();
- }
-
- // gets our best bet for the average number of physics steps per render frame
- // return value: number of frames back this data is consistent
- int get_average_physics_steps(float &p_min, float &p_max) {
- p_min = typical_physics_steps[0];
- p_max = p_min + 1;
-
- for (int i = 1; i < CONTROL_STEPS; ++i) {
- const float typical_lower = typical_physics_steps[i];
- const float current_min = typical_lower / (i + 1);
- if (current_min > p_max)
- return i; // bail out of further restrictions would void the interval
- else if (current_min > p_min)
- p_min = current_min;
- const float current_max = (typical_lower + 1) / (i + 1);
- if (current_max < p_min)
- return i;
- else if (current_max < p_max)
- p_max = current_max;
- }
-
- return CONTROL_STEPS;
- }
-
- // advance physics clock by p_animation_step, return appropriate number of steps to simulate
- _FrameTime advance_core(float p_frame_slice, int p_iterations_per_second, float p_animation_step) {
- _FrameTime ret;
-
- ret.animation_step = p_animation_step;
-
- // simple determination of number of physics iteration
- time_accum += ret.animation_step;
- ret.physics_steps = floor(time_accum * p_iterations_per_second);
-
- int min_typical_steps = typical_physics_steps[0];
- int max_typical_steps = min_typical_steps + 1;
-
- // given the past recorded steps and typcial steps to match, calculate bounds for this
- // step to be typical
- bool update_typical = false;
-
- for (int i = 0; i < CONTROL_STEPS - 1; ++i) {
- int steps_left_to_match_typical = typical_physics_steps[i + 1] - accumulated_physics_steps[i];
- if (steps_left_to_match_typical > max_typical_steps ||
- steps_left_to_match_typical + 1 < min_typical_steps) {
- update_typical = true;
- break;
- }
-
- if (steps_left_to_match_typical > min_typical_steps)
- min_typical_steps = steps_left_to_match_typical;
- if (steps_left_to_match_typical + 1 < max_typical_steps)
- max_typical_steps = steps_left_to_match_typical + 1;
- }
-
- // try to keep it consistent with previous iterations
- if (ret.physics_steps < min_typical_steps) {
- const int max_possible_steps = floor((time_accum)*p_iterations_per_second + get_physics_jitter_fix());
- if (max_possible_steps < min_typical_steps) {
- ret.physics_steps = max_possible_steps;
- update_typical = true;
- } else {
- ret.physics_steps = min_typical_steps;
- }
- } else if (ret.physics_steps > max_typical_steps) {
- const int min_possible_steps = floor((time_accum)*p_iterations_per_second - get_physics_jitter_fix());
- if (min_possible_steps > max_typical_steps) {
- ret.physics_steps = min_possible_steps;
- update_typical = true;
- } else {
- ret.physics_steps = max_typical_steps;
- }
- }
-
- time_accum -= ret.physics_steps * p_frame_slice;
-
- // keep track of accumulated step counts
- for (int i = CONTROL_STEPS - 2; i >= 0; --i) {
- accumulated_physics_steps[i + 1] = accumulated_physics_steps[i] + ret.physics_steps;
- }
- accumulated_physics_steps[0] = ret.physics_steps;
-
- if (update_typical) {
- for (int i = CONTROL_STEPS - 1; i >= 0; --i) {
- if (typical_physics_steps[i] > accumulated_physics_steps[i]) {
- typical_physics_steps[i] = accumulated_physics_steps[i];
- } else if (typical_physics_steps[i] < accumulated_physics_steps[i] - 1) {
- typical_physics_steps[i] = accumulated_physics_steps[i] - 1;
- }
- }
- }
-
- return ret;
- }
-
- // calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero
- _FrameTime advance_checked(float p_frame_slice, int p_iterations_per_second, float p_animation_step) {
- if (fixed_fps != -1)
- p_animation_step = 1.0 / fixed_fps;
-
- // compensate for last deficit
- p_animation_step += time_deficit;
-
- _FrameTime ret = advance_core(p_frame_slice, p_iterations_per_second, p_animation_step);
-
- // we will do some clamping on ret.animation_step and need to sync those changes to time_accum,
- // that's easiest if we just remember their fixed difference now
- const double animation_minus_accum = ret.animation_step - time_accum;
-
- // first, least important clamping: keep ret.animation_step consistent with typical_physics_steps.
- // this smoothes out the animation steps and culls small but quick variations.
- {
- float min_average_physics_steps, max_average_physics_steps;
- int consistent_steps = get_average_physics_steps(min_average_physics_steps, max_average_physics_steps);
- if (consistent_steps > 3) {
- ret.clamp_animation(min_average_physics_steps * p_frame_slice, max_average_physics_steps * p_frame_slice);
- }
- }
-
- // second clamping: keep abs(time_deficit) < jitter_fix * frame_slise
- float max_clock_deviation = get_physics_jitter_fix() * p_frame_slice;
- ret.clamp_animation(p_animation_step - max_clock_deviation, p_animation_step + max_clock_deviation);
-
- // last clamping: make sure time_accum is between 0 and p_frame_slice for consistency between physics and animation
- ret.clamp_animation(animation_minus_accum, animation_minus_accum + p_frame_slice);
-
- // restore time_accum
- time_accum = ret.animation_step - animation_minus_accum;
-
- // track deficit
- time_deficit = p_animation_step - ret.animation_step;
-
- return ret;
- }
-
- // determine wall clock step since last iteration
- float get_cpu_animation_step() {
- uint64_t cpu_ticks_elapsed = current_cpu_ticks_usec - last_cpu_ticks_usec;
- last_cpu_ticks_usec = current_cpu_ticks_usec;
-
- return cpu_ticks_elapsed / 1000000.0;
- }
-
-public:
- explicit _TimerSync() :
- last_cpu_ticks_usec(0),
- current_cpu_ticks_usec(0),
- time_accum(0),
- time_deficit(0) {
- for (int i = CONTROL_STEPS - 1; i >= 0; --i) {
- typical_physics_steps[i] = i;
- accumulated_physics_steps[i] = i;
- }
- }
-
- // start the clock
- void init(uint64_t p_cpu_ticks_usec) {
- current_cpu_ticks_usec = last_cpu_ticks_usec = p_cpu_ticks_usec;
- }
-
- // set measured wall clock time
- void set_cpu_ticks_usec(uint64_t p_cpu_ticks_usec) {
- current_cpu_ticks_usec = p_cpu_ticks_usec;
- }
-
- // advance one frame, return timesteps to take
- _FrameTime advance(float p_frame_slice, int p_iterations_per_second) {
- float cpu_animation_step = get_cpu_animation_step();
-
- return advance_checked(p_frame_slice, p_iterations_per_second, cpu_animation_step);
- }
-
- void before_start_render() {
- VisualServer::get_singleton()->sync();
- }
-};
-static _TimerSync _timer_sync;
+static MainTimerSync main_timer_sync;
bool Main::start() {
@@ -1457,7 +1240,7 @@ bool Main::start() {
String _export_preset;
bool export_debug = false;
- _timer_sync.init(OS::get_singleton()->get_ticks_usec());
+ main_timer_sync.init(OS::get_singleton()->get_ticks_usec());
List<String> args = OS::get_singleton()->get_cmdline_args();
for (int i = 0; i < args.size(); i++) {
@@ -1958,15 +1741,16 @@ bool Main::iteration() {
uint64_t ticks = OS::get_singleton()->get_ticks_usec();
Engine::get_singleton()->_frame_ticks = ticks;
- _timer_sync.set_cpu_ticks_usec(ticks);
+ main_timer_sync.set_cpu_ticks_usec(ticks);
+ main_timer_sync.set_fixed_fps(fixed_fps);
uint64_t ticks_elapsed = ticks - last_ticks;
int physics_fps = Engine::get_singleton()->get_iterations_per_second();
float frame_slice = 1.0 / physics_fps;
- _FrameTime advance = _timer_sync.advance(frame_slice, physics_fps);
- double step = advance.animation_step;
+ MainFrameTime advance = main_timer_sync.advance(frame_slice, physics_fps);
+ double step = advance.idle_step;
Engine::get_singleton()->_frame_step = step;
@@ -2030,7 +1814,7 @@ bool Main::iteration() {
OS::get_singleton()->get_main_loop()->idle(step * time_scale);
message_queue->flush();
- _timer_sync.before_start_render(); //sync if still drawing from previous frames.
+ VisualServer::get_singleton()->sync(); //sync if still drawing from previous frames.
if (OS::get_singleton()->can_draw() && !disable_render_loop) {
diff --git a/main/timer_sync.cpp b/main/timer_sync.cpp
new file mode 100644
index 0000000000..c33cbafee8
--- /dev/null
+++ b/main/timer_sync.cpp
@@ -0,0 +1,193 @@
+#include "timer_sync.h"
+
+void MainFrameTime::clamp_idle(float min_idle_step, float max_idle_step) {
+ if (idle_step < min_idle_step) {
+ idle_step = min_idle_step;
+ } else if (idle_step > max_idle_step) {
+ idle_step = max_idle_step;
+ }
+}
+
+/////////////////////////////////
+
+// returns the fraction of p_frame_slice required for the timer to overshoot
+// before advance_core considers changing the physics_steps return from
+// the typical values as defined by typical_physics_steps
+float MainTimerSync::get_physics_jitter_fix() {
+ return Engine::get_singleton()->get_physics_jitter_fix();
+}
+
+// gets our best bet for the average number of physics steps per render frame
+// return value: number of frames back this data is consistent
+int MainTimerSync::get_average_physics_steps(float &p_min, float &p_max) {
+ p_min = typical_physics_steps[0];
+ p_max = p_min + 1;
+
+ for (int i = 1; i < CONTROL_STEPS; ++i) {
+ const float typical_lower = typical_physics_steps[i];
+ const float current_min = typical_lower / (i + 1);
+ if (current_min > p_max)
+ return i; // bail out of further restrictions would void the interval
+ else if (current_min > p_min)
+ p_min = current_min;
+ const float current_max = (typical_lower + 1) / (i + 1);
+ if (current_max < p_min)
+ return i;
+ else if (current_max < p_max)
+ p_max = current_max;
+ }
+
+ return CONTROL_STEPS;
+}
+
+// advance physics clock by p_idle_step, return appropriate number of steps to simulate
+MainFrameTime MainTimerSync::advance_core(float p_frame_slice, int p_iterations_per_second, float p_idle_step) {
+ MainFrameTime ret;
+
+ ret.idle_step = p_idle_step;
+
+ // simple determination of number of physics iteration
+ time_accum += ret.idle_step;
+ ret.physics_steps = floor(time_accum * p_iterations_per_second);
+
+ int min_typical_steps = typical_physics_steps[0];
+ int max_typical_steps = min_typical_steps + 1;
+
+ // given the past recorded steps and typcial steps to match, calculate bounds for this
+ // step to be typical
+ bool update_typical = false;
+
+ for (int i = 0; i < CONTROL_STEPS - 1; ++i) {
+ int steps_left_to_match_typical = typical_physics_steps[i + 1] - accumulated_physics_steps[i];
+ if (steps_left_to_match_typical > max_typical_steps ||
+ steps_left_to_match_typical + 1 < min_typical_steps) {
+ update_typical = true;
+ break;
+ }
+
+ if (steps_left_to_match_typical > min_typical_steps)
+ min_typical_steps = steps_left_to_match_typical;
+ if (steps_left_to_match_typical + 1 < max_typical_steps)
+ max_typical_steps = steps_left_to_match_typical + 1;
+ }
+
+ // try to keep it consistent with previous iterations
+ if (ret.physics_steps < min_typical_steps) {
+ const int max_possible_steps = floor((time_accum)*p_iterations_per_second + get_physics_jitter_fix());
+ if (max_possible_steps < min_typical_steps) {
+ ret.physics_steps = max_possible_steps;
+ update_typical = true;
+ } else {
+ ret.physics_steps = min_typical_steps;
+ }
+ } else if (ret.physics_steps > max_typical_steps) {
+ const int min_possible_steps = floor((time_accum)*p_iterations_per_second - get_physics_jitter_fix());
+ if (min_possible_steps > max_typical_steps) {
+ ret.physics_steps = min_possible_steps;
+ update_typical = true;
+ } else {
+ ret.physics_steps = max_typical_steps;
+ }
+ }
+
+ time_accum -= ret.physics_steps * p_frame_slice;
+
+ // keep track of accumulated step counts
+ for (int i = CONTROL_STEPS - 2; i >= 0; --i) {
+ accumulated_physics_steps[i + 1] = accumulated_physics_steps[i] + ret.physics_steps;
+ }
+ accumulated_physics_steps[0] = ret.physics_steps;
+
+ if (update_typical) {
+ for (int i = CONTROL_STEPS - 1; i >= 0; --i) {
+ if (typical_physics_steps[i] > accumulated_physics_steps[i]) {
+ typical_physics_steps[i] = accumulated_physics_steps[i];
+ } else if (typical_physics_steps[i] < accumulated_physics_steps[i] - 1) {
+ typical_physics_steps[i] = accumulated_physics_steps[i] - 1;
+ }
+ }
+ }
+
+ return ret;
+}
+
+// calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero
+MainFrameTime MainTimerSync::advance_checked(float p_frame_slice, int p_iterations_per_second, float p_idle_step) {
+ if (fixed_fps != -1)
+ p_idle_step = 1.0 / fixed_fps;
+
+ // compensate for last deficit
+ p_idle_step += time_deficit;
+
+ MainFrameTime ret = advance_core(p_frame_slice, p_iterations_per_second, p_idle_step);
+
+ // we will do some clamping on ret.idle_step and need to sync those changes to time_accum,
+ // that's easiest if we just remember their fixed difference now
+ const double idle_minus_accum = ret.idle_step - time_accum;
+
+ // first, least important clamping: keep ret.idle_step consistent with typical_physics_steps.
+ // this smoothes out the idle steps and culls small but quick variations.
+ {
+ float min_average_physics_steps, max_average_physics_steps;
+ int consistent_steps = get_average_physics_steps(min_average_physics_steps, max_average_physics_steps);
+ if (consistent_steps > 3) {
+ ret.clamp_idle(min_average_physics_steps * p_frame_slice, max_average_physics_steps * p_frame_slice);
+ }
+ }
+
+ // second clamping: keep abs(time_deficit) < jitter_fix * frame_slise
+ float max_clock_deviation = get_physics_jitter_fix() * p_frame_slice;
+ ret.clamp_idle(p_idle_step - max_clock_deviation, p_idle_step + max_clock_deviation);
+
+ // last clamping: make sure time_accum is between 0 and p_frame_slice for consistency between physics and idle
+ ret.clamp_idle(idle_minus_accum, idle_minus_accum + p_frame_slice);
+
+ // restore time_accum
+ time_accum = ret.idle_step - idle_minus_accum;
+
+ // track deficit
+ time_deficit = p_idle_step - ret.idle_step;
+
+ return ret;
+}
+
+// determine wall clock step since last iteration
+float MainTimerSync::get_cpu_idle_step() {
+ uint64_t cpu_ticks_elapsed = current_cpu_ticks_usec - last_cpu_ticks_usec;
+ last_cpu_ticks_usec = current_cpu_ticks_usec;
+
+ return cpu_ticks_elapsed / 1000000.0;
+}
+
+MainTimerSync::MainTimerSync() :
+ last_cpu_ticks_usec(0),
+ current_cpu_ticks_usec(0),
+ time_accum(0),
+ time_deficit(0),
+ fixed_fps(0) {
+ for (int i = CONTROL_STEPS - 1; i >= 0; --i) {
+ typical_physics_steps[i] = i;
+ accumulated_physics_steps[i] = i;
+ }
+}
+
+// start the clock
+void MainTimerSync::init(uint64_t p_cpu_ticks_usec) {
+ current_cpu_ticks_usec = last_cpu_ticks_usec = p_cpu_ticks_usec;
+}
+
+// set measured wall clock time
+void MainTimerSync::set_cpu_ticks_usec(uint64_t p_cpu_ticks_usec) {
+ current_cpu_ticks_usec = p_cpu_ticks_usec;
+}
+
+void MainTimerSync::set_fixed_fps(int p_fixed_fps) {
+ fixed_fps = p_fixed_fps;
+}
+
+// advance one frame, return timesteps to take
+MainFrameTime MainTimerSync::advance(float p_frame_slice, int p_iterations_per_second) {
+ float cpu_idle_step = get_cpu_idle_step();
+
+ return advance_checked(p_frame_slice, p_iterations_per_second, cpu_idle_step);
+}
diff --git a/main/timer_sync.h b/main/timer_sync.h
new file mode 100644
index 0000000000..6ef4254270
--- /dev/null
+++ b/main/timer_sync.h
@@ -0,0 +1,71 @@
+#ifndef TIMER_SYNC_H
+#define TIMER_SYNC_H
+
+#include "core/engine.h"
+
+struct MainFrameTime {
+ float idle_step; // time to advance idles for (argument to process())
+ int physics_steps; // number of times to iterate the physics engine
+
+ void clamp_idle(float min_idle_step, float max_idle_step);
+};
+
+class MainTimerSync {
+ // wall clock time measured on the main thread
+ uint64_t last_cpu_ticks_usec;
+ uint64_t current_cpu_ticks_usec;
+
+ // logical game time since last physics timestep
+ float time_accum;
+
+ // current difference between wall clock time and reported sum of idle_steps
+ float time_deficit;
+
+ // number of frames back for keeping accumulated physics steps roughly constant.
+ // value of 12 chosen because that is what is required to make 144 Hz monitors
+ // behave well with 60 Hz physics updates. The only worse commonly available refresh
+ // would be 85, requiring CONTROL_STEPS = 17.
+ static const int CONTROL_STEPS = 12;
+
+ // sum of physics steps done over the last (i+1) frames
+ int accumulated_physics_steps[CONTROL_STEPS];
+
+ // typical value for accumulated_physics_steps[i] is either this or this plus one
+ int typical_physics_steps[CONTROL_STEPS];
+
+ int fixed_fps;
+
+protected:
+ // returns the fraction of p_frame_slice required for the timer to overshoot
+ // before advance_core considers changing the physics_steps return from
+ // the typical values as defined by typical_physics_steps
+ float get_physics_jitter_fix();
+
+ // gets our best bet for the average number of physics steps per render frame
+ // return value: number of frames back this data is consistent
+ int get_average_physics_steps(float &p_min, float &p_max);
+
+ // advance physics clock by p_idle_step, return appropriate number of steps to simulate
+ MainFrameTime advance_core(float p_frame_slice, int p_iterations_per_second, float p_idle_step);
+
+ // calls advance_core, keeps track of deficit it adds to animaption_step, make sure the deficit sum stays close to zero
+ MainFrameTime advance_checked(float p_frame_slice, int p_iterations_per_second, float p_idle_step);
+
+ // determine wall clock step since last iteration
+ float get_cpu_idle_step();
+
+public:
+ MainTimerSync();
+
+ // start the clock
+ void init(uint64_t p_cpu_ticks_usec);
+ // set measured wall clock time
+ void set_cpu_ticks_usec(uint64_t p_cpu_ticks_usec);
+ //set fixed fps
+ void set_fixed_fps(int p_fixed_fps);
+
+ // advance one frame, return timesteps to take
+ MainFrameTime advance(float p_frame_slice, int p_iterations_per_second);
+};
+
+#endif // TIMER_SYNC_H
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index bbe245951e..161e62c81f 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -782,7 +782,7 @@ void CSharpLanguage::reload_assemblies_if_needed(bool p_soft_reload) {
}
if (Engine::get_singleton()->is_editor_hint()) {
- EditorNode::get_singleton()->get_property_editor()->update_tree();
+ EditorNode::get_singleton()->get_inspector()->update_tree();
NodeDock::singleton->update_lists();
}
}
diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp
index eb10c5e99f..dfaa873b13 100644
--- a/modules/visual_script/visual_script_editor.cpp
+++ b/modules/visual_script/visual_script_editor.cpp
@@ -268,7 +268,7 @@ protected:
if (String(p_name) == "export") {
script->set_variable_export(var, p_value);
- EditorNode::get_singleton()->get_property_editor()->update_tree();
+ EditorNode::get_singleton()->get_inspector()->update_tree();
return true;
}
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index f998f23d3b..3878559b60 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -462,7 +462,7 @@ void AudioStreamPlayer2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_distance", PROPERTY_HINT_RANGE, "1,65536,1"), "set_max_distance", "get_max_distance");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation", PROPERTY_HINT_EXP_EASING), "set_attenuation", "get_attenuation");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_attenuation", "get_attenuation");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
index 240bd631a1..4d854ff578 100644
--- a/scene/3d/light.cpp
+++ b/scene/3d/light.cpp
@@ -375,7 +375,7 @@ void DirectionalLight::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_normal_bias", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_bias_split_scale", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_BIAS_SPLIT_SCALE);
ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_depth_range", PROPERTY_HINT_ENUM, "Stable,Optimized"), "set_shadow_depth_range", "get_shadow_depth_range");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_max_distance", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "directional_shadow_max_distance", PROPERTY_HINT_EXP_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE);
BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL);
BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS);
@@ -428,8 +428,8 @@ void OmniLight::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_shadow_detail"), &OmniLight::get_shadow_detail);
ADD_GROUP("Omni", "omni_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_range", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_RANGE);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_ATTENUATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1"), "set_param", "get_param", PARAM_RANGE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "omni_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION);
ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_mode", PROPERTY_HINT_ENUM, "Dual Paraboloid,Cube"), "set_shadow_mode", "get_shadow_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_detail", PROPERTY_HINT_ENUM, "Vertical,Horizontal"), "set_shadow_detail", "get_shadow_detail");
@@ -450,8 +450,8 @@ OmniLight::OmniLight() :
void SpotLight::_bind_methods() {
ADD_GROUP("Spot", "spot_");
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_range", PROPERTY_HINT_RANGE, "0,65536,0.1"), "set_param", "get_param", PARAM_RANGE);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_ATTENUATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1"), "set_param", "get_param", PARAM_RANGE);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION);
ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_param", "get_param", PARAM_SPOT_ANGLE);
- ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING), "set_param", "get_param", PARAM_SPOT_ATTENUATION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_SPOT_ATTENUATION);
}
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 6f34f3e49f..1e0db6e8cd 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -656,8 +656,9 @@ ColorPicker::ColorPicker() :
void ColorPickerButton::_color_changed(const Color &p_color) {
+ color = p_color;
update();
- emit_signal("color_changed", p_color);
+ emit_signal("color_changed", color);
}
void ColorPickerButton::_modal_closed() {
@@ -667,6 +668,7 @@ void ColorPickerButton::_modal_closed() {
void ColorPickerButton::pressed() {
+ _update_picker();
popup->set_position(get_global_position() - picker->get_combined_minimum_size());
popup->popup();
picker->set_focus_on_line_edit();
@@ -679,7 +681,7 @@ void ColorPickerButton::_notification(int p_what) {
Ref<StyleBox> normal = get_stylebox("normal");
Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size());
draw_texture_rect(Control::get_icon("bg", "ColorPickerButton"), r, true);
- draw_rect(r, picker->get_pick_color());
+ draw_rect(r, color);
}
if (p_what == MainLoop::NOTIFICATION_WM_QUIT_REQUEST) {
@@ -689,27 +691,34 @@ void ColorPickerButton::_notification(int p_what) {
void ColorPickerButton::set_pick_color(const Color &p_color) {
- picker->set_pick_color(p_color);
+ color = p_color;
+ if (picker) {
+ picker->set_pick_color(p_color);
+ }
+
update();
- emit_signal("color_changed", p_color);
}
Color ColorPickerButton::get_pick_color() const {
- return picker->get_pick_color();
+ return color;
}
void ColorPickerButton::set_edit_alpha(bool p_show) {
- picker->set_edit_alpha(p_show);
+ edit_alpha = p_show;
+ if (picker) {
+ picker->set_edit_alpha(p_show);
+ }
}
bool ColorPickerButton::is_editing_alpha() const {
- return picker->is_editing_alpha();
+ return edit_alpha;
}
-ColorPicker *ColorPickerButton::get_picker() const {
+ColorPicker *ColorPickerButton::get_picker() {
+ _update_picker();
return picker;
}
@@ -718,6 +727,19 @@ PopupPanel *ColorPickerButton::get_popup() const {
return popup;
}
+void ColorPickerButton::_update_picker() {
+ if (!picker) {
+ popup = memnew(PopupPanel);
+ picker = memnew(ColorPicker);
+ popup->add_child(picker);
+ add_child(popup);
+ picker->connect("color_changed", this, "_color_changed");
+ popup->connect("modal_closed", this, "_modal_closed");
+ picker->set_pick_color(color);
+ picker->set_edit_alpha(edit_alpha);
+ }
+}
+
void ColorPickerButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPickerButton::set_pick_color);
@@ -737,12 +759,7 @@ void ColorPickerButton::_bind_methods() {
ColorPickerButton::ColorPickerButton() {
- popup = memnew(PopupPanel);
- picker = memnew(ColorPicker);
- popup->add_child(picker);
-
- picker->connect("color_changed", this, "_color_changed");
- popup->connect("modal_closed", this, "_modal_closed");
-
- add_child(popup);
+ picker = NULL;
+ popup = NULL;
+ edit_alpha = true;
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index 7d1a554ada..6b63e5fe60 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -118,12 +118,16 @@ class ColorPickerButton : public Button {
PopupPanel *popup;
ColorPicker *picker;
+ Color color;
+ bool edit_alpha;
void _color_changed(const Color &p_color);
void _modal_closed();
virtual void pressed();
+ void _update_picker();
+
protected:
void _notification(int);
static void _bind_methods();
@@ -135,7 +139,7 @@ public:
void set_edit_alpha(bool p_show);
bool is_editing_alpha() const;
- ColorPicker *get_picker() const;
+ ColorPicker *get_picker();
PopupPanel *get_popup() const;
ColorPickerButton();
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index 7cb0ad5707..177582c87c 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -34,9 +34,9 @@
void Container::_child_minsize_changed() {
- Size2 ms = get_combined_minimum_size();
- if (ms.width > get_size().width || ms.height > get_size().height)
- minimum_size_changed();
+ //Size2 ms = get_combined_minimum_size();
+ //if (ms.width > get_size().width || ms.height > get_size().height) {
+ minimum_size_changed();
queue_sort();
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index b7c1d35fd7..3097ecaf16 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -155,12 +155,21 @@ Size2 Control::get_custom_minimum_size() const {
return data.custom_minimum_size;
}
-Size2 Control::get_combined_minimum_size() const {
+void Control::_update_minimum_size_cache() {
Size2 minsize = get_minimum_size();
minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
minsize.y = MAX(minsize.y, data.custom_minimum_size.y);
- return minsize;
+ data.minimum_size_cache = minsize;
+ data.minimum_size_valid = true;
+}
+
+Size2 Control::get_combined_minimum_size() const {
+
+ if (!data.minimum_size_valid) {
+ const_cast<Control *>(this)->_update_minimum_size_cache();
+ }
+ return data.minimum_size_cache;
}
Size2 Control::_edit_get_minimum_size() const {
@@ -259,14 +268,17 @@ void Control::_update_minimum_size() {
if (!is_inside_tree())
return;
- data.pending_min_size_update = false;
Size2 minsize = get_combined_minimum_size();
if (minsize.x > data.size_cache.x ||
minsize.y > data.size_cache.y) {
_size_changed();
}
- emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
+ data.updating_last_minimum_size = false;
+
+ if (minsize != data.last_minimum_size) {
+ emit_signal(SceneStringNames::get_singleton()->minimum_size_changed);
+ }
}
bool Control::_get(const StringName &p_name, Variant &r_ret) const {
@@ -437,8 +449,12 @@ void Control::_notification(int p_notification) {
case NOTIFICATION_ENTER_TREE: {
- _size_changed();
-
+ } break;
+ case NOTIFICATION_POST_ENTER_TREE: {
+ if (is_visible_in_tree()) {
+ data.minimum_size_valid = false;
+ _size_changed();
+ }
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -620,13 +636,12 @@ void Control::_notification(int p_notification) {
if (is_inside_tree()) {
_modal_stack_remove();
- minimum_size_changed();
}
//remove key focus
//remove modalness
} else {
-
+ data.minimum_size_valid = false;
_size_changed();
}
@@ -2464,17 +2479,25 @@ void Control::minimum_size_changed() {
if (!is_inside_tree() || data.block_minimum_size_adjust)
return;
- if (data.pending_min_size_update)
+ Control *invalidate = this;
+
+ //invalidate cache upwards
+ while (invalidate && invalidate->data.minimum_size_valid) {
+ invalidate->data.minimum_size_valid = false;
+ if (invalidate->is_set_as_toplevel())
+ break; // do not go further up
+ invalidate = invalidate->data.parent;
+ }
+
+ if (!is_visible_in_tree())
+ return;
+
+ if (data.updating_last_minimum_size)
return;
- data.pending_min_size_update = true;
- MessageQueue::get_singleton()->push_call(this, "_update_minimum_size");
+ data.updating_last_minimum_size = true;
- if (!is_toplevel_control()) {
- Control *pc = get_parent_control();
- if (pc)
- pc->minimum_size_changed();
- }
+ MessageQueue::get_singleton()->push_call(this, "_update_minimum_size");
}
int Control::get_v_size_flags() const {
@@ -2985,7 +3008,6 @@ Control::Control() {
data.h_size_flags = SIZE_FILL;
data.v_size_flags = SIZE_FILL;
data.expand = 1;
- data.pending_min_size_update = false;
data.rotation = 0;
data.parent_canvas_item = NULL;
data.scale = Vector2(1, 1);
@@ -2995,6 +3017,8 @@ Control::Control() {
data.disable_visibility_clip = false;
data.h_grow = GROW_DIRECTION_END;
data.v_grow = GROW_DIRECTION_END;
+ data.minimum_size_valid = false;
+ data.updating_last_minimum_size = false;
data.clip_contents = false;
for (int i = 0; i < 4; i++) {
diff --git a/scene/gui/control.h b/scene/gui/control.h
index b5453e60f5..9124256624 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -148,6 +148,11 @@ private:
Point2 pos_cache;
Size2 size_cache;
+ Size2 minimum_size_cache;
+ bool minimum_size_valid;
+
+ Size2 last_minimum_size;
+ bool updating_last_minimum_size;
float margin[4];
float anchor[4];
@@ -164,7 +169,6 @@ private:
int h_size_flags;
int v_size_flags;
float expand;
- bool pending_min_size_update;
Point2 custom_minimum_size;
bool pass_on_modal_close_click;
@@ -244,6 +248,8 @@ private:
void _modal_stack_remove();
void _modal_set_prev_focus_owner(ObjectID p_prev);
+ void _update_minimum_size_cache();
+
protected:
virtual void add_child_notify(Node *p_child);
virtual void remove_child_notify(Node *p_child);
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index cd6c6bb65c..caafda2c65 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -136,9 +136,9 @@ void Range::set_as_ratio(double p_value) {
double v;
- if (shared->exp_ratio && get_min() > 0) {
+ if (shared->exp_ratio && get_min() >= 0) {
- double exp_min = Math::log(get_min()) / Math::log((double)2);
+ double exp_min = get_min() == 0 ? 0.0 : Math::log(get_min()) / Math::log((double)2);
double exp_max = Math::log(get_max()) / Math::log((double)2);
v = Math::pow(2, exp_min + (exp_max - exp_min) * p_value);
} else {
@@ -155,9 +155,9 @@ void Range::set_as_ratio(double p_value) {
}
double Range::get_as_ratio() const {
- if (shared->exp_ratio && get_min() > 0) {
+ if (shared->exp_ratio && get_min() >= 0) {
- double exp_min = Math::log(get_min()) / Math::log((double)2);
+ double exp_min = get_min() == 0 ? 0.0 : Math::log(get_min()) / Math::log((double)2);
double exp_max = Math::log(get_max()) / Math::log((double)2);
double v = Math::log(get_value()) / Math::log((double)2);
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index a1dcf3b002..2dd5c64378 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -37,6 +37,7 @@ bool ScrollContainer::clips_input() const {
Size2 ScrollContainer::get_minimum_size() const {
+ Ref<StyleBox> sb = get_stylebox("bg");
Size2 min_size;
for (int i = 0; i < get_child_count(); i++) {
@@ -64,8 +65,9 @@ Size2 ScrollContainer::get_minimum_size() const {
if (v_scroll->is_visible_in_tree()) {
min_size.x += v_scroll->get_minimum_size().x;
}
+ min_size += sb->get_minimum_size();
return min_size;
-};
+}
void ScrollContainer::_cancel_drag() {
set_physics_process_internal(false);
@@ -233,6 +235,12 @@ void ScrollContainer::_notification(int p_what) {
child_max_size = Size2(0, 0);
Size2 size = get_size();
+ Point2 ofs;
+
+ Ref<StyleBox> sb = get_stylebox("bg");
+ size -= sb->get_minimum_size();
+ ofs += sb->get_offset();
+
if (h_scroll->is_visible_in_tree())
size.y -= h_scroll->get_minimum_size().y;
@@ -268,6 +276,7 @@ void ScrollContainer::_notification(int p_what) {
else
r.size.height = minsize.height;
}
+ r.position += ofs;
fit_child_in_rect(c, r);
}
update();
@@ -275,6 +284,9 @@ void ScrollContainer::_notification(int p_what) {
if (p_what == NOTIFICATION_DRAW) {
+ Ref<StyleBox> sb = get_stylebox("bg");
+ draw_style_box(sb, Rect2(Vector2(), get_size()));
+
update_scrollbars();
}
@@ -353,6 +365,8 @@ void ScrollContainer::_notification(int p_what) {
void ScrollContainer::update_scrollbars() {
Size2 size = get_size();
+ Ref<StyleBox> sb = get_stylebox("bg");
+ size -= sb->get_minimum_size();
Size2 hmin = h_scroll->get_combined_minimum_size();
Size2 vmin = v_scroll->get_combined_minimum_size();
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index e6ea4e4b4a..3643aedb85 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -176,6 +176,9 @@ void Node::_propagate_ready() {
data.children[i]->_propagate_ready();
}
data.blocked--;
+
+ notification(NOTIFICATION_POST_ENTER_TREE);
+
if (data.ready_first) {
data.ready_first = false;
notification(NOTIFICATION_READY);
diff --git a/scene/main/node.h b/scene/main/node.h
index 4ff1247e14..540f34cba7 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -237,6 +237,7 @@ public:
NOTIFICATION_TRANSLATION_CHANGED = 24,
NOTIFICATION_INTERNAL_PROCESS = 25,
NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26,
+ NOTIFICATION_POST_ENTER_TREE = 27,
};
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 295f131db3..5a3447bec0 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1345,7 +1345,7 @@ void Viewport::_gui_show_tooltip() {
gui.tooltip_label->set_anchor_and_margin(MARGIN_RIGHT, Control::ANCHOR_END, -ttp->get_margin(MARGIN_RIGHT));
gui.tooltip_label->set_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM));
gui.tooltip_label->set_text(tooltip.strip_edges());
- Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_label->get_combined_minimum_size() + ttp->get_minimum_size());
+ Rect2 r(gui.tooltip_pos + Point2(10, 10), gui.tooltip_label->get_minimum_size() + ttp->get_minimum_size());
Rect2 vr = gui.tooltip_label->get_viewport_rect();
if (r.size.x + r.position.x > vr.size.x)
r.position.x = vr.size.x - r.size.x;
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 5ac9344f31..3ea856541e 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -544,6 +544,11 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_icon("updown", "SpinBox", make_icon(spinbox_updown_png));
+ //scroll container
+ Ref<StyleBoxEmpty> empty;
+ empty.instance();
+ theme->set_stylebox("bg", "ScrollContainer", empty);
+
// WindowDialog
theme->set_stylebox("panel", "WindowDialog", sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6));