Browse Source

-New inspector.
-Changed UI resizing code, gained huge amount of speed.
-Reorganized timer sync to clean up behavior (sorry forgot commit this before)

-

Juan Linietsky 7 years ago
parent
commit
005b69cf6e
39 changed files with 5975 additions and 337 deletions
  1. 1 1
      core/object.h
  2. 1 0
      editor/animation_editor.cpp
  3. 1797 0
      editor/editor_inspector.cpp
  4. 283 0
      editor/editor_inspector.h
  5. 42 32
      editor/editor_node.cpp
  6. 3 3
      editor/editor_node.h
  7. 2411 0
      editor/editor_properties.cpp
  8. 490 0
      editor/editor_properties.h
  9. 315 0
      editor/editor_spin_slider.cpp
  10. 57 0
      editor/editor_spin_slider.h
  11. 81 4
      editor/icons/icon_GUI_slider_grabber.svg
  12. 79 5
      editor/icons/icon_GUI_slider_grabber_hl.svg
  13. 2 2
      editor/multi_node_edit.cpp
  14. 2 2
      editor/plugins/animation_player_editor_plugin.cpp
  15. 1 1
      editor/plugins/cube_grid_theme_editor_plugin.cpp
  16. 1 1
      editor/plugins/item_list_editor_plugin.cpp
  17. 2 2
      editor/project_settings_editor.cpp
  18. 2 2
      editor/property_editor.cpp
  19. 2 1
      editor/property_editor.h
  20. 1 1
      editor/scene_tree_dock.cpp
  21. 2 2
      editor/script_editor_debugger.cpp
  22. 9 225
      main/main.cpp
  23. 193 0
      main/timer_sync.cpp
  24. 71 0
      main/timer_sync.h
  25. 1 1
      modules/mono/csharp_script.cpp
  26. 1 1
      modules/visual_script/visual_script_editor.cpp
  27. 1 1
      scene/2d/audio_stream_player_2d.cpp
  28. 6 6
      scene/3d/light.cpp
  29. 33 16
      scene/gui/color_picker.cpp
  30. 5 1
      scene/gui/color_picker.h
  31. 3 3
      scene/gui/container.cpp
  32. 41 17
      scene/gui/control.cpp
  33. 7 1
      scene/gui/control.h
  34. 4 4
      scene/gui/range.cpp
  35. 15 1
      scene/gui/scroll_container.cpp
  36. 3 0
      scene/main/node.cpp
  37. 1 0
      scene/main/node.h
  38. 1 1
      scene/main/viewport.cpp
  39. 5 0
      scene/resources/default_theme/default_theme.cpp

+ 1 - 1
core/object.h

@@ -55,7 +55,7 @@ enum PropertyHint {
 	PROPERTY_HINT_RANGE, ///< hint_text = "min,max,step,slider; //slider is optional"
 	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_EXP_RANGE, ///< hint_text = "min,max,step", exponential edit
 	PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc"
 	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_LENGTH, ///< hint_text= "length" (as integer)
 	PROPERTY_HINT_SPRITE_FRAME,
 	PROPERTY_HINT_SPRITE_FRAME,
 	PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)
 	PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer)

+ 1 - 0
editor/animation_editor.cpp

@@ -2969,6 +2969,7 @@ void AnimationKeyEditor::_notification(int p_what) {
 	switch (p_what) {
 	switch (p_what) {
 		case NOTIFICATION_VISIBILITY_CHANGED: {
 		case NOTIFICATION_VISIBILITY_CHANGED: {
 
 
+			update_keying();
 			EditorNode::get_singleton()->update_keying();
 			EditorNode::get_singleton()->update_keying();
 			emit_signal("keying_changed");
 			emit_signal("keying_changed");
 		} break;
 		} break;

+ 1797 - 0
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;
+}

+ 283 - 0
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

+ 42 - 32
editor/editor_node.cpp

@@ -56,6 +56,7 @@
 #include "editor/editor_file_system.h"
 #include "editor/editor_file_system.h"
 #include "editor/editor_help.h"
 #include "editor/editor_help.h"
 #include "editor/editor_initialize_ssl.h"
 #include "editor/editor_initialize_ssl.h"
+#include "editor/editor_properties.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_themes.h"
 #include "editor/editor_themes.h"
 #include "editor/import/editor_import_collada.h"
 #include "editor/import/editor_import_collada.h"
@@ -548,8 +549,8 @@ void EditorNode::_vp_resized() {
 
 
 void EditorNode::_node_renamed() {
 void EditorNode::_node_renamed() {
 
 
-	if (property_editor)
-		property_editor->update_tree();
+	if (inspector)
+		inspector->update_tree();
 }
 }
 
 
 void EditorNode::_editor_select_next() {
 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) {
 void EditorNode::push_item(Object *p_object, const String &p_property) {
 
 
 	if (!p_object) {
 	if (!p_object) {
-		property_editor->edit(NULL);
+		inspector->edit(NULL);
 		node_dock->set_node(NULL);
 		node_dock->set_node(NULL);
 		scene_tree_dock->set_selected(NULL);
 		scene_tree_dock->set_selected(NULL);
 		return;
 		return;
@@ -1444,12 +1445,12 @@ void EditorNode::_property_editor_back() {
 
 
 void EditorNode::_menu_collapseall() {
 void EditorNode::_menu_collapseall() {
 
 
-	property_editor->collapse_all_folding();
+	inspector->collapse_all_folding();
 }
 }
 
 
 void EditorNode::_menu_expandall() {
 void EditorNode::_menu_expandall() {
 
 
-	property_editor->expand_all_folding();
+	inspector->expand_all_folding();
 }
 }
 
 
 void EditorNode::_save_default_environment() {
 void EditorNode::_save_default_environment() {
@@ -1515,7 +1516,7 @@ void EditorNode::_edit_current() {
 	if (!current_obj) {
 	if (!current_obj) {
 
 
 		scene_tree_dock->set_selected(NULL);
 		scene_tree_dock->set_selected(NULL);
-		property_editor->edit(NULL);
+		inspector->edit(NULL);
 		node_dock->set_node(NULL);
 		node_dock->set_node(NULL);
 		object_menu->set_disabled(true);
 		object_menu->set_disabled(true);
 
 
@@ -1536,7 +1537,7 @@ void EditorNode::_edit_current() {
 		Resource *current_res = Object::cast_to<Resource>(current_obj);
 		Resource *current_res = Object::cast_to<Resource>(current_obj);
 		ERR_FAIL_COND(!current_res);
 		ERR_FAIL_COND(!current_res);
 		scene_tree_dock->set_selected(NULL);
 		scene_tree_dock->set_selected(NULL);
-		property_editor->edit(current_res);
+		inspector->edit(current_res);
 		node_dock->set_node(NULL);
 		node_dock->set_node(NULL);
 		object_menu->set_disabled(false);
 		object_menu->set_disabled(false);
 		EditorNode::get_singleton()->get_import_dock()->set_edit_path(current_res->get_path());
 		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);
 		Node *current_node = Object::cast_to<Node>(current_obj);
 		ERR_FAIL_COND(!current_node);
 		ERR_FAIL_COND(!current_node);
 
 
-		property_editor->edit(current_node);
+		inspector->edit(current_node);
 		if (current_node->is_inside_tree()) {
 		if (current_node->is_inside_tree()) {
 			node_dock->set_node(current_node);
 			node_dock->set_node(current_node);
 			scene_tree_dock->set_selected(current_node);
 			scene_tree_dock->set_selected(current_node);
@@ -1585,7 +1586,7 @@ void EditorNode::_edit_current() {
 			capitalize = false;
 			capitalize = false;
 		}
 		}
 
 
-		property_editor->edit(current_obj);
+		inspector->edit(current_obj);
 		node_dock->set_node(NULL);
 		node_dock->set_node(NULL);
 	}
 	}
 
 
@@ -1594,8 +1595,8 @@ void EditorNode::_edit_current() {
 		property_editable_warning_dialog->set_text(editable_warning);
 		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 */
 	/* Take care of PLUGIN EDITOR */
@@ -2939,7 +2940,7 @@ Dictionary EditorNode::_get_main_scene_state() {
 	Dictionary state;
 	Dictionary state;
 	state["main_tab"] = _get_current_main_editor();
 	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["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["saved_version"] = saved_version;
 	state["node_filter"] = scene_tree_dock->get_filter();
 	state["node_filter"] = scene_tree_dock->get_filter();
 	return state;
 	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"))
 	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"]);
 		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"))
 	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"))
 	if (p_state.has("node_filter"))
 		scene_tree_dock->set_filter(p_state["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() {
 void EditorNode::_close_messages() {
@@ -3425,6 +3424,9 @@ void EditorNode::register_editor_types() {
 	ClassDB::register_class<EditorExportPlugin>();
 	ClassDB::register_class<EditorExportPlugin>();
 	ClassDB::register_class<EditorResourceConversionPlugin>();
 	ClassDB::register_class<EditorResourceConversionPlugin>();
 	ClassDB::register_class<EditorSceneImporter>();
 	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?
 	// FIXME: Is this stuff obsolete, or should it be ported to new APIs?
 	ClassDB::register_class<EditorScenePostImport>();
 	ClassDB::register_class<EditorScenePostImport>();
@@ -4236,7 +4238,7 @@ void EditorNode::_scene_tab_changed(int p_tab) {
 
 
 void EditorNode::_toggle_search_bar(bool p_pressed) {
 void EditorNode::_toggle_search_bar(bool p_pressed) {
 
 
-	property_editor->set_use_filter(p_pressed);
+	inspector->set_use_filter(p_pressed);
 
 
 	if (p_pressed) {
 	if (p_pressed) {
 
 
@@ -4255,7 +4257,7 @@ void EditorNode::_clear_search_box() {
 		return;
 		return;
 
 
 	search_box->clear();
 	search_box->clear();
-	property_editor->update_tree();
+	inspector->update_tree();
 }
 }
 
 
 ToolButton *EditorNode::add_bottom_panel_item(String p_text, Control *p_item) {
 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);
 		ResourceFormatImporter::get_singleton()->add_importer(import_bitmap);
 	}
 	}
 
 
+	{
+		Ref<EditorInspectorDefaultPlugin> eidp;
+		eidp.instance();
+		EditorInspector::add_inspector_plugin(eidp);
+	}
+
 	_pvrtc_register_compressors();
 	_pvrtc_register_compressors();
 
 
 	editor_selection = memnew(EditorSelection);
 	editor_selection = memnew(EditorSelection);
@@ -5665,21 +5673,21 @@ EditorNode::EditorNode() {
 	property_editable_warning->hide();
 	property_editable_warning->hide();
 	property_editable_warning->connect("pressed", this, "_property_editable_warning_pressed");
 	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;
 	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);
 	import_dock = memnew(ImportDock);
 	dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(import_dock);
 	dock_slot[DOCK_SLOT_RIGHT_UL]->add_child(import_dock);
@@ -5815,8 +5823,8 @@ EditorNode::EditorNode() {
 
 
 	file->connect("file_selected", this, "_dialog_action");
 	file->connect("file_selected", this, "_dialog_action");
 	file_templates->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
 	//plugin stuff
 
 
@@ -6039,6 +6047,8 @@ EditorNode::EditorNode() {
 
 
 EditorNode::~EditorNode() {
 EditorNode::~EditorNode() {
 
 
+	EditorInspector::cleanup_plugins();
+
 	remove_print_handler(&print_handler);
 	remove_print_handler(&print_handler);
 	memdelete(EditorHelp::get_doc_data());
 	memdelete(EditorHelp::get_doc_data());
 	memdelete(editor_selection);
 	memdelete(editor_selection);

+ 3 - 3
editor/editor_node.h

@@ -37,6 +37,7 @@
 #include "editor/editor_about.h"
 #include "editor/editor_about.h"
 #include "editor/editor_data.h"
 #include "editor/editor_data.h"
 #include "editor/editor_export.h"
 #include "editor/editor_export.h"
+#include "editor/editor_inspector.h"
 #include "editor/editor_log.h"
 #include "editor/editor_log.h"
 #include "editor/editor_name_dialog.h"
 #include "editor/editor_name_dialog.h"
 #include "editor/editor_path.h"
 #include "editor/editor_path.h"
@@ -80,7 +81,6 @@
 #include "scene/gui/tool_button.h"
 #include "scene/gui/tool_button.h"
 #include "scene/gui/tree.h"
 #include "scene/gui/tree.h"
 #include "scene/gui/viewport_container.h"
 #include "scene/gui/viewport_container.h"
-
 /**
 /**
 	@author Juan Linietsky <[email protected]>
 	@author Juan Linietsky <[email protected]>
 */
 */
@@ -267,7 +267,7 @@ private:
 	Button *property_back;
 	Button *property_back;
 	Button *property_forward;
 	Button *property_forward;
 	SceneTreeDock *scene_tree_dock;
 	SceneTreeDock *scene_tree_dock;
-	PropertyEditor *property_editor;
+	EditorInspector *inspector;
 	Button *property_editable_warning;
 	Button *property_editable_warning;
 	AcceptDialog *property_editable_warning_dialog;
 	AcceptDialog *property_editable_warning_dialog;
 	void _property_editable_warning_pressed();
 	void _property_editable_warning_pressed();
@@ -640,7 +640,7 @@ public:
 	EditorPluginList *get_editor_plugins_over() { return editor_plugins_over; }
 	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_over() { return editor_plugins_force_over; }
 	EditorPluginList *get_editor_plugins_force_input_forwarding() { return editor_plugins_force_input_forwarding; }
 	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; }
 	VBoxContainer *get_property_editor_vb() { return prop_editor_vb; }
 
 
 	ProjectSettingsEditor *get_project_settings() { return project_settings; }
 	ProjectSettingsEditor *get_project_settings() { return project_settings; }

+ 2411 - 0
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
+}

+ 490 - 0
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

+ 315 - 0
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;
+}

+ 57 - 0
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

+ 81 - 4
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>
 </svg>

+ 79 - 5
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>
 </svg>

+ 2 - 2
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_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();
 	ur->commit_action();
 	return true;
 	return true;

+ 2 - 2
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());
 				frame->set_value(player->get_current_animation_position());
 				key_editor->set_anim_pos(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) {
 			} else if (last_active) {
 				//need the last frame after it stopped
 				//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;
 	updating = false;
 	_seek_value_changed(p_pos, !p_drag);
 	_seek_value_changed(p_pos, !p_drag);
 
 
-	EditorNode::get_singleton()->get_property_editor()->refresh();
+	EditorNode::get_singleton()->get_inspector()->refresh();
 
 
 	//seekit
 	//seekit
 }
 }

+ 1 - 1
editor/plugins/cube_grid_theme_editor_plugin.cpp

@@ -198,7 +198,7 @@ void MeshLibraryEditor::_menu_cbk(int p_option) {
 		} break;
 		} break;
 		case MENU_OPTION_REMOVE_ITEM: {
 		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) {
 			if (p.begins_with("/MeshLibrary/item") && p.get_slice_count("/") >= 3) {
 
 
 				to_erase = p.get_slice("/", 3).to_int();
 				to_erase = p.get_slice("/", 3).to_int();

+ 1 - 1
editor/plugins/item_list_editor_plugin.cpp

@@ -388,7 +388,7 @@ ItemListEditor::ItemListEditor() {
 	vbc->add_child(property_editor);
 	vbc->add_child(property_editor);
 	property_editor->set_v_size_flags(SIZE_EXPAND_FILL);
 	property_editor->set_v_size_flags(SIZE_EXPAND_FILL);
 
 
-	tree = property_editor->get_scene_tree();
+	tree = property_editor->get_property_tree();
 }
 }
 
 
 ItemListEditor::~ItemListEditor() {
 ItemListEditor::~ItemListEditor() {

+ 2 - 2
editor/project_settings_editor.cpp

@@ -785,7 +785,7 @@ void ProjectSettingsEditor::popup_project_settings() {
 
 
 void ProjectSettingsEditor::_item_selected() {
 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)
 	if (!ti)
 		return;
 		return;
 	if (!ti->get_parent())
 	if (!ti->get_parent())
@@ -1727,7 +1727,7 @@ ProjectSettingsEditor::ProjectSettingsEditor(EditorData *p_data) {
 	//globals_editor->hide_top_label();
 	//globals_editor->hide_top_label();
 	globals_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	globals_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
 	globals_editor->register_search_box(search_box);
 	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_toggled", this, "_item_checked", varray(), CONNECT_DEFERRED);
 	globals_editor->get_property_editor()->connect("property_edited", this, "_settings_prop_edited");
 	globals_editor->get_property_editor()->connect("property_edited", this, "_settings_prop_edited");
 
 

+ 2 - 2
editor/property_editor.cpp

@@ -4212,7 +4212,7 @@ void PropertyEditor::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property")));
 	ADD_SIGNAL(MethodInfo("property_edited", PropertyInfo(Variant::STRING, "property")));
 }
 }
 
 
-Tree *PropertyEditor::get_scene_tree() {
+Tree *PropertyEditor::get_property_tree() {
 
 
 	return tree;
 	return tree;
 }
 }
@@ -4695,7 +4695,7 @@ SectionedPropertyEditor::SectionedPropertyEditor() {
 	editor->set_v_size_flags(SIZE_EXPAND_FILL);
 	editor->set_v_size_flags(SIZE_EXPAND_FILL);
 	right_vb->add_child(editor, true);
 	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();
 	editor->hide_top_label();
 
 

+ 2 - 1
editor/property_editor.h

@@ -275,7 +275,7 @@ public:
 
 
 	String get_selected_path() const;
 	String get_selected_path() const;
 
 
-	Tree *get_scene_tree();
+	Tree *get_property_tree();
 	Label *get_top_label();
 	Label *get_top_label();
 	void hide_top_label();
 	void hide_top_label();
 	void update_tree();
 	void update_tree();
@@ -309,6 +309,7 @@ public:
 
 
 	void collapse_all_folding();
 	void collapse_all_folding();
 	void expand_all_folding();
 	void expand_all_folding();
+
 	PropertyEditor();
 	PropertyEditor();
 	~PropertyEditor();
 	~PropertyEditor();
 };
 };

+ 1 - 1
editor/scene_tree_dock.cpp

@@ -732,7 +732,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
 				if (node) {
 				if (node) {
 					node->set_scene_inherited_state(Ref<SceneState>());
 					node->set_scene_inherited_state(Ref<SceneState>());
 					scene_tree->update_tree();
 					scene_tree->update_tree();
-					EditorNode::get_singleton()->get_property_editor()->update_tree();
+					EditorNode::get_singleton()->get_inspector()->update_tree();
 				}
 				}
 			}
 			}
 		} break;
 		} break;

+ 2 - 2
editor/script_editor_debugger.cpp

@@ -1851,7 +1851,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
 	ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream));
 	ppeer = Ref<PacketPeerStream>(memnew(PacketPeerStream));
 	ppeer->set_input_buffer_max_size(1024 * 1024 * 8); //8mb should be enough
 	ppeer->set_input_buffer_max_size(1024 * 1024 * 8); //8mb should be enough
 	editor = p_editor;
 	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 = memnew(TabContainer);
 	tabs->set_tab_align(TabContainer::ALIGN_LEFT);
 	tabs->set_tab_align(TabContainer::ALIGN_LEFT);
@@ -1936,7 +1936,7 @@ ScriptEditorDebugger::ScriptEditorDebugger(EditorNode *p_editor) {
 		inspector = memnew(PropertyEditor);
 		inspector = memnew(PropertyEditor);
 		inspector->set_h_size_flags(SIZE_EXPAND_FILL);
 		inspector->set_h_size_flags(SIZE_EXPAND_FILL);
 		inspector->hide_top_label();
 		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_enable_capitalize_paths(false);
 		inspector->set_read_only(true);
 		inspector->set_read_only(true);
 		inspector->connect("object_id_selected", this, "_scene_tree_property_select_object");
 		inspector->connect("object_id_selected", this, "_scene_tree_property_select_object");

+ 9 - 225
main/main.cpp

@@ -82,6 +82,8 @@
 #include "version.h"
 #include "version.h"
 #include "version_hash.gen.h"
 #include "version_hash.gen.h"
 
 
+#include "main/timer_sync.h"
+
 static ProjectSettings *globals = NULL;
 static ProjectSettings *globals = NULL;
 static Engine *engine = NULL;
 static Engine *engine = NULL;
 static InputMap *input_map = 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
 // 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() {
 bool Main::start() {
 
 
@@ -1457,7 +1240,7 @@ bool Main::start() {
 	String _export_preset;
 	String _export_preset;
 	bool export_debug = false;
 	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();
 	List<String> args = OS::get_singleton()->get_cmdline_args();
 	for (int i = 0; i < args.size(); i++) {
 	for (int i = 0; i < args.size(); i++) {
@@ -1958,15 +1741,16 @@ bool Main::iteration() {
 
 
 	uint64_t ticks = OS::get_singleton()->get_ticks_usec();
 	uint64_t ticks = OS::get_singleton()->get_ticks_usec();
 	Engine::get_singleton()->_frame_ticks = ticks;
 	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;
 	uint64_t ticks_elapsed = ticks - last_ticks;
 
 
 	int physics_fps = Engine::get_singleton()->get_iterations_per_second();
 	int physics_fps = Engine::get_singleton()->get_iterations_per_second();
 	float frame_slice = 1.0 / physics_fps;
 	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;
 	Engine::get_singleton()->_frame_step = step;
 
 
@@ -2030,7 +1814,7 @@ bool Main::iteration() {
 	OS::get_singleton()->get_main_loop()->idle(step * time_scale);
 	OS::get_singleton()->get_main_loop()->idle(step * time_scale);
 	message_queue->flush();
 	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) {
 	if (OS::get_singleton()->can_draw() && !disable_render_loop) {
 
 

+ 193 - 0
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);
+}

+ 71 - 0
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

+ 1 - 1
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()) {
 	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();
 		NodeDock::singleton->update_lists();
 	}
 	}
 }
 }

+ 1 - 1
modules/visual_script/visual_script_editor.cpp

@@ -268,7 +268,7 @@ protected:
 
 
 		if (String(p_name) == "export") {
 		if (String(p_name) == "export") {
 			script->set_variable_export(var, p_value);
 			script->set_variable_export(var, p_value);
-			EditorNode::get_singleton()->get_property_editor()->update_tree();
+			EditorNode::get_singleton()->get_inspector()->update_tree();
 			return true;
 			return true;
 		}
 		}
 
 

+ 1 - 1
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, "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::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, "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::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");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask");
 
 

+ 6 - 6
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_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_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_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_ORTHOGONAL);
 	BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS);
 	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);
 	ClassDB::bind_method(D_METHOD("get_shadow_detail"), &OmniLight::get_shadow_detail);
 
 
 	ADD_GROUP("Omni", "omni_");
 	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_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");
 	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() {
 void SpotLight::_bind_methods() {
 
 
 	ADD_GROUP("Spot", "spot_");
 	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", 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);
 }
 }

+ 33 - 16
scene/gui/color_picker.cpp

@@ -656,8 +656,9 @@ ColorPicker::ColorPicker() :
 
 
 void ColorPickerButton::_color_changed(const Color &p_color) {
 void ColorPickerButton::_color_changed(const Color &p_color) {
 
 
+	color = p_color;
 	update();
 	update();
-	emit_signal("color_changed", p_color);
+	emit_signal("color_changed", color);
 }
 }
 
 
 void ColorPickerButton::_modal_closed() {
 void ColorPickerButton::_modal_closed() {
@@ -667,6 +668,7 @@ void ColorPickerButton::_modal_closed() {
 
 
 void ColorPickerButton::pressed() {
 void ColorPickerButton::pressed() {
 
 
+	_update_picker();
 	popup->set_position(get_global_position() - picker->get_combined_minimum_size());
 	popup->set_position(get_global_position() - picker->get_combined_minimum_size());
 	popup->popup();
 	popup->popup();
 	picker->set_focus_on_line_edit();
 	picker->set_focus_on_line_edit();
@@ -679,7 +681,7 @@ void ColorPickerButton::_notification(int p_what) {
 		Ref<StyleBox> normal = get_stylebox("normal");
 		Ref<StyleBox> normal = get_stylebox("normal");
 		Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size());
 		Rect2 r = Rect2(normal->get_offset(), get_size() - normal->get_minimum_size());
 		draw_texture_rect(Control::get_icon("bg", "ColorPickerButton"), r, true);
 		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) {
 	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) {
 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();
 	update();
-	emit_signal("color_changed", p_color);
 }
 }
 Color ColorPickerButton::get_pick_color() const {
 Color ColorPickerButton::get_pick_color() const {
 
 
-	return picker->get_pick_color();
+	return color;
 }
 }
 
 
 void ColorPickerButton::set_edit_alpha(bool p_show) {
 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 {
 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;
 	return picker;
 }
 }
 
 
@@ -718,6 +727,19 @@ PopupPanel *ColorPickerButton::get_popup() const {
 	return popup;
 	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() {
 void ColorPickerButton::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPickerButton::set_pick_color);
 	ClassDB::bind_method(D_METHOD("set_pick_color", "color"), &ColorPickerButton::set_pick_color);
@@ -737,12 +759,7 @@ void ColorPickerButton::_bind_methods() {
 
 
 ColorPickerButton::ColorPickerButton() {
 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;
 }
 }

+ 5 - 1
scene/gui/color_picker.h

@@ -118,12 +118,16 @@ class ColorPickerButton : public Button {
 
 
 	PopupPanel *popup;
 	PopupPanel *popup;
 	ColorPicker *picker;
 	ColorPicker *picker;
+	Color color;
+	bool edit_alpha;
 
 
 	void _color_changed(const Color &p_color);
 	void _color_changed(const Color &p_color);
 	void _modal_closed();
 	void _modal_closed();
 
 
 	virtual void pressed();
 	virtual void pressed();
 
 
+	void _update_picker();
+
 protected:
 protected:
 	void _notification(int);
 	void _notification(int);
 	static void _bind_methods();
 	static void _bind_methods();
@@ -135,7 +139,7 @@ public:
 	void set_edit_alpha(bool p_show);
 	void set_edit_alpha(bool p_show);
 	bool is_editing_alpha() const;
 	bool is_editing_alpha() const;
 
 
-	ColorPicker *get_picker() const;
+	ColorPicker *get_picker();
 	PopupPanel *get_popup() const;
 	PopupPanel *get_popup() const;
 
 
 	ColorPickerButton();
 	ColorPickerButton();

+ 3 - 3
scene/gui/container.cpp

@@ -34,9 +34,9 @@
 
 
 void Container::_child_minsize_changed() {
 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();
 	queue_sort();
 }
 }
 
 

+ 41 - 17
scene/gui/control.cpp

@@ -155,12 +155,21 @@ Size2 Control::get_custom_minimum_size() const {
 	return data.custom_minimum_size;
 	return data.custom_minimum_size;
 }
 }
 
 
-Size2 Control::get_combined_minimum_size() const {
+void Control::_update_minimum_size_cache() {
 
 
 	Size2 minsize = get_minimum_size();
 	Size2 minsize = get_minimum_size();
 	minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
 	minsize.x = MAX(minsize.x, data.custom_minimum_size.x);
 	minsize.y = MAX(minsize.y, data.custom_minimum_size.y);
 	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 {
 Size2 Control::_edit_get_minimum_size() const {
@@ -259,14 +268,17 @@ void Control::_update_minimum_size() {
 	if (!is_inside_tree())
 	if (!is_inside_tree())
 		return;
 		return;
 
 
-	data.pending_min_size_update = false;
 	Size2 minsize = get_combined_minimum_size();
 	Size2 minsize = get_combined_minimum_size();
 	if (minsize.x > data.size_cache.x ||
 	if (minsize.x > data.size_cache.x ||
 			minsize.y > data.size_cache.y) {
 			minsize.y > data.size_cache.y) {
 		_size_changed();
 		_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 {
 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: {
 		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;
 		} break;
 		case NOTIFICATION_EXIT_TREE: {
 		case NOTIFICATION_EXIT_TREE: {
 
 
@@ -620,13 +636,12 @@ void Control::_notification(int p_notification) {
 
 
 				if (is_inside_tree()) {
 				if (is_inside_tree()) {
 					_modal_stack_remove();
 					_modal_stack_remove();
-					minimum_size_changed();
 				}
 				}
 
 
 				//remove key focus
 				//remove key focus
 				//remove modalness
 				//remove modalness
 			} else {
 			} else {
-
+				data.minimum_size_valid = false;
 				_size_changed();
 				_size_changed();
 			}
 			}
 
 
@@ -2464,17 +2479,25 @@ void Control::minimum_size_changed() {
 	if (!is_inside_tree() || data.block_minimum_size_adjust)
 	if (!is_inside_tree() || data.block_minimum_size_adjust)
 		return;
 		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;
 		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 {
 int Control::get_v_size_flags() const {
@@ -2985,7 +3008,6 @@ Control::Control() {
 	data.h_size_flags = SIZE_FILL;
 	data.h_size_flags = SIZE_FILL;
 	data.v_size_flags = SIZE_FILL;
 	data.v_size_flags = SIZE_FILL;
 	data.expand = 1;
 	data.expand = 1;
-	data.pending_min_size_update = false;
 	data.rotation = 0;
 	data.rotation = 0;
 	data.parent_canvas_item = NULL;
 	data.parent_canvas_item = NULL;
 	data.scale = Vector2(1, 1);
 	data.scale = Vector2(1, 1);
@@ -2995,6 +3017,8 @@ Control::Control() {
 	data.disable_visibility_clip = false;
 	data.disable_visibility_clip = false;
 	data.h_grow = GROW_DIRECTION_END;
 	data.h_grow = GROW_DIRECTION_END;
 	data.v_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;
 	data.clip_contents = false;
 	for (int i = 0; i < 4; i++) {
 	for (int i = 0; i < 4; i++) {

+ 7 - 1
scene/gui/control.h

@@ -148,6 +148,11 @@ private:
 
 
 		Point2 pos_cache;
 		Point2 pos_cache;
 		Size2 size_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 margin[4];
 		float anchor[4];
 		float anchor[4];
@@ -164,7 +169,6 @@ private:
 		int h_size_flags;
 		int h_size_flags;
 		int v_size_flags;
 		int v_size_flags;
 		float expand;
 		float expand;
-		bool pending_min_size_update;
 		Point2 custom_minimum_size;
 		Point2 custom_minimum_size;
 
 
 		bool pass_on_modal_close_click;
 		bool pass_on_modal_close_click;
@@ -244,6 +248,8 @@ private:
 	void _modal_stack_remove();
 	void _modal_stack_remove();
 	void _modal_set_prev_focus_owner(ObjectID p_prev);
 	void _modal_set_prev_focus_owner(ObjectID p_prev);
 
 
+	void _update_minimum_size_cache();
+
 protected:
 protected:
 	virtual void add_child_notify(Node *p_child);
 	virtual void add_child_notify(Node *p_child);
 	virtual void remove_child_notify(Node *p_child);
 	virtual void remove_child_notify(Node *p_child);

+ 4 - 4
scene/gui/range.cpp

@@ -136,9 +136,9 @@ void Range::set_as_ratio(double p_value) {
 
 
 	double v;
 	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);
 		double exp_max = Math::log(get_max()) / Math::log((double)2);
 		v = Math::pow(2, exp_min + (exp_max - exp_min) * p_value);
 		v = Math::pow(2, exp_min + (exp_max - exp_min) * p_value);
 	} else {
 	} else {
@@ -155,9 +155,9 @@ void Range::set_as_ratio(double p_value) {
 }
 }
 double Range::get_as_ratio() const {
 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 exp_max = Math::log(get_max()) / Math::log((double)2);
 		double v = Math::log(get_value()) / Math::log((double)2);
 		double v = Math::log(get_value()) / Math::log((double)2);
 
 

+ 15 - 1
scene/gui/scroll_container.cpp

@@ -37,6 +37,7 @@ bool ScrollContainer::clips_input() const {
 
 
 Size2 ScrollContainer::get_minimum_size() const {
 Size2 ScrollContainer::get_minimum_size() const {
 
 
+	Ref<StyleBox> sb = get_stylebox("bg");
 	Size2 min_size;
 	Size2 min_size;
 
 
 	for (int i = 0; i < get_child_count(); i++) {
 	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()) {
 	if (v_scroll->is_visible_in_tree()) {
 		min_size.x += v_scroll->get_minimum_size().x;
 		min_size.x += v_scroll->get_minimum_size().x;
 	}
 	}
+	min_size += sb->get_minimum_size();
 	return min_size;
 	return min_size;
-};
+}
 
 
 void ScrollContainer::_cancel_drag() {
 void ScrollContainer::_cancel_drag() {
 	set_physics_process_internal(false);
 	set_physics_process_internal(false);
@@ -233,6 +235,12 @@ void ScrollContainer::_notification(int p_what) {
 
 
 		child_max_size = Size2(0, 0);
 		child_max_size = Size2(0, 0);
 		Size2 size = get_size();
 		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())
 		if (h_scroll->is_visible_in_tree())
 			size.y -= h_scroll->get_minimum_size().y;
 			size.y -= h_scroll->get_minimum_size().y;
 
 
@@ -268,6 +276,7 @@ void ScrollContainer::_notification(int p_what) {
 				else
 				else
 					r.size.height = minsize.height;
 					r.size.height = minsize.height;
 			}
 			}
+			r.position += ofs;
 			fit_child_in_rect(c, r);
 			fit_child_in_rect(c, r);
 		}
 		}
 		update();
 		update();
@@ -275,6 +284,9 @@ void ScrollContainer::_notification(int p_what) {
 
 
 	if (p_what == NOTIFICATION_DRAW) {
 	if (p_what == NOTIFICATION_DRAW) {
 
 
+		Ref<StyleBox> sb = get_stylebox("bg");
+		draw_style_box(sb, Rect2(Vector2(), get_size()));
+
 		update_scrollbars();
 		update_scrollbars();
 	}
 	}
 
 
@@ -353,6 +365,8 @@ void ScrollContainer::_notification(int p_what) {
 void ScrollContainer::update_scrollbars() {
 void ScrollContainer::update_scrollbars() {
 
 
 	Size2 size = get_size();
 	Size2 size = get_size();
+	Ref<StyleBox> sb = get_stylebox("bg");
+	size -= sb->get_minimum_size();
 
 
 	Size2 hmin = h_scroll->get_combined_minimum_size();
 	Size2 hmin = h_scroll->get_combined_minimum_size();
 	Size2 vmin = v_scroll->get_combined_minimum_size();
 	Size2 vmin = v_scroll->get_combined_minimum_size();

+ 3 - 0
scene/main/node.cpp

@@ -176,6 +176,9 @@ void Node::_propagate_ready() {
 		data.children[i]->_propagate_ready();
 		data.children[i]->_propagate_ready();
 	}
 	}
 	data.blocked--;
 	data.blocked--;
+
+	notification(NOTIFICATION_POST_ENTER_TREE);
+
 	if (data.ready_first) {
 	if (data.ready_first) {
 		data.ready_first = false;
 		data.ready_first = false;
 		notification(NOTIFICATION_READY);
 		notification(NOTIFICATION_READY);

+ 1 - 0
scene/main/node.h

@@ -237,6 +237,7 @@ public:
 		NOTIFICATION_TRANSLATION_CHANGED = 24,
 		NOTIFICATION_TRANSLATION_CHANGED = 24,
 		NOTIFICATION_INTERNAL_PROCESS = 25,
 		NOTIFICATION_INTERNAL_PROCESS = 25,
 		NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26,
 		NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26,
+		NOTIFICATION_POST_ENTER_TREE = 27,
 
 
 	};
 	};
 
 

+ 1 - 1
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_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_anchor_and_margin(MARGIN_BOTTOM, Control::ANCHOR_END, -ttp->get_margin(MARGIN_BOTTOM));
 	gui.tooltip_label->set_text(tooltip.strip_edges());
 	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();
 	Rect2 vr = gui.tooltip_label->get_viewport_rect();
 	if (r.size.x + r.position.x > vr.size.x)
 	if (r.size.x + r.position.x > vr.size.x)
 		r.position.x = vr.size.x - r.size.x;
 		r.position.x = vr.size.x - r.size.x;

+ 5 - 0
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));
 	theme->set_icon("updown", "SpinBox", make_icon(spinbox_updown_png));
 
 
+	//scroll container
+	Ref<StyleBoxEmpty> empty;
+	empty.instance();
+	theme->set_stylebox("bg", "ScrollContainer", empty);
+
 	// WindowDialog
 	// WindowDialog
 
 
 	theme->set_stylebox("panel", "WindowDialog", sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6));
 	theme->set_stylebox("panel", "WindowDialog", sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6));