浏览代码

Merge pull request #77135 from KoBeWi/Vector∞

Refactor vector editor properties
Yuri Sizov 2 年之前
父节点
当前提交
8d6c472f6a

+ 5 - 772
editor/editor_properties.cpp

@@ -35,6 +35,7 @@
 #include "editor/create_dialog.h"
 #include "editor/editor_node.h"
 #include "editor/editor_properties_array_dict.h"
+#include "editor/editor_properties_vector.h"
 #include "editor/editor_resource_picker.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
@@ -1700,137 +1701,6 @@ EditorPropertyEasing::EditorPropertyEasing() {
 	add_child(spin);
 }
 
-///////////////////// VECTOR2 /////////////////////////
-
-void EditorPropertyVector2::_set_read_only(bool p_read_only) {
-	for (int i = 0; i < 2; i++) {
-		spin[i]->set_read_only(p_read_only);
-	}
-}
-
-void EditorPropertyVector2::_value_changed(double val, const String &p_name) {
-	if (setting) {
-		return;
-	}
-
-	if (linked->is_pressed()) {
-		setting = true;
-		if (p_name == "x") {
-			spin[1]->set_value(spin[0]->get_value() * ratio_yx);
-		}
-
-		if (p_name == "y") {
-			spin[0]->set_value(spin[1]->get_value() * ratio_xy);
-		}
-		setting = false;
-	}
-
-	Vector2 v2;
-	v2.x = spin[0]->get_value();
-	v2.y = spin[1]->get_value();
-	emit_changed(get_edited_property(), v2, linked->is_pressed() ? "" : p_name);
-}
-
-void EditorPropertyVector2::update_property() {
-	Vector2 val = get_edited_property_value();
-	setting = true;
-	spin[0]->set_value(val.x);
-	spin[1]->set_value(val.y);
-	setting = false;
-	_update_ratio();
-}
-
-void EditorPropertyVector2::_update_ratio() {
-	linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5));
-
-	if (spin[0]->get_value() != 0 && spin[1]->get_value() != 0) {
-		ratio_xy = spin[0]->get_value() / spin[1]->get_value();
-		ratio_yx = spin[1]->get_value() / spin[0]->get_value();
-	} else {
-		ratio_xy = 1.0;
-		ratio_yx = 1.0;
-	}
-}
-
-void EditorPropertyVector2::_notification(int p_what) {
-	switch (p_what) {
-		case NOTIFICATION_ENTER_TREE:
-		case NOTIFICATION_THEME_CHANGED: {
-			linked->set_texture_normal(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons")));
-			linked->set_texture_pressed(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
-
-			const Color *colors = _get_property_colors();
-			for (int i = 0; i < 2; i++) {
-				spin[i]->add_theme_color_override("label_color", colors[i]);
-			}
-		} break;
-	}
-}
-
-void EditorPropertyVector2::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_link, const String &p_suffix) {
-	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_hide_slider);
-		spin[i]->set_allow_greater(true);
-		spin[i]->set_allow_lesser(true);
-		spin[i]->set_suffix(p_suffix);
-	}
-
-	if (!p_link) {
-		linked->hide();
-	} else {
-		linked->set_pressed(true);
-	}
-}
-
-EditorPropertyVector2::EditorPropertyVector2(bool p_force_wide) {
-	bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector2_editing"));
-
-	HBoxContainer *hb = memnew(HBoxContainer);
-	hb->set_h_size_flags(SIZE_EXPAND_FILL);
-
-	BoxContainer *bc;
-
-	if (p_force_wide) {
-		bc = memnew(HBoxContainer);
-		hb->add_child(bc);
-	} else if (horizontal) {
-		bc = memnew(HBoxContainer);
-		hb->add_child(bc);
-		set_bottom_editor(hb);
-	} else {
-		bc = memnew(VBoxContainer);
-		hb->add_child(bc);
-	}
-	bc->set_h_size_flags(SIZE_EXPAND_FILL);
-
-	static const char *desc[2] = { "x", "y" };
-	for (int i = 0; i < 2; i++) {
-		spin[i] = memnew(EditorSpinSlider);
-		spin[i]->set_flat(true);
-		spin[i]->set_label(desc[i]);
-		bc->add_child(spin[i]);
-		add_focusable(spin[i]);
-		spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVector2::_value_changed).bind(desc[i]));
-		if (horizontal) {
-			spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
-		}
-	}
-
-	linked = memnew(TextureButton);
-	linked->set_toggle_mode(true);
-	linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
-	linked->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyVector2::_update_ratio));
-	hb->add_child(linked);
-
-	add_child(hb);
-	if (!horizontal) {
-		set_label_reference(spin[0]); //show text and buttons around this
-	}
-}
-
 ///////////////////// RECT2 /////////////////////////
 
 void EditorPropertyRect2::_set_read_only(bool p_read_only) {
@@ -1934,315 +1804,6 @@ EditorPropertyRect2::EditorPropertyRect2(bool p_force_wide) {
 	}
 }
 
-///////////////////// VECTOR3 /////////////////////////
-
-void EditorPropertyVector3::_set_read_only(bool p_read_only) {
-	for (int i = 0; i < 3; i++) {
-		spin[i]->set_read_only(p_read_only);
-	}
-}
-
-void EditorPropertyVector3::_value_changed(double val, const String &p_name) {
-	if (setting) {
-		return;
-	}
-
-	if (linked->is_pressed()) {
-		setting = true;
-		if (p_name == "x") {
-			spin[1]->set_value(spin[0]->get_value() * ratio_yx);
-			spin[2]->set_value(spin[0]->get_value() * ratio_zx);
-		}
-
-		if (p_name == "y") {
-			spin[0]->set_value(spin[1]->get_value() * ratio_xy);
-			spin[2]->set_value(spin[1]->get_value() * ratio_zy);
-		}
-
-		if (p_name == "z") {
-			spin[0]->set_value(spin[2]->get_value() * ratio_xz);
-			spin[1]->set_value(spin[2]->get_value() * ratio_yz);
-		}
-		setting = false;
-	}
-
-	Vector3 v3;
-	v3.x = spin[0]->get_value();
-	v3.y = spin[1]->get_value();
-	v3.z = spin[2]->get_value();
-	if (angle_in_radians) {
-		v3.x = Math::deg_to_rad(v3.x);
-		v3.y = Math::deg_to_rad(v3.y);
-		v3.z = Math::deg_to_rad(v3.z);
-	}
-	emit_changed(get_edited_property(), v3, linked->is_pressed() ? "" : p_name);
-}
-
-void EditorPropertyVector3::update_property() {
-	update_using_vector(get_edited_property_value());
-	_update_ratio();
-}
-
-void EditorPropertyVector3::_update_ratio() {
-	linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5));
-
-	if (spin[0]->get_value() != 0 && spin[1]->get_value() != 0) {
-		ratio_yx = spin[1]->get_value() / spin[0]->get_value();
-		ratio_zx = spin[2]->get_value() / spin[0]->get_value();
-		ratio_xy = spin[0]->get_value() / spin[1]->get_value();
-		ratio_zy = spin[2]->get_value() / spin[1]->get_value();
-		ratio_xz = spin[0]->get_value() / spin[2]->get_value();
-		ratio_yz = spin[1]->get_value() / spin[2]->get_value();
-	} else {
-		ratio_yx = 1.0;
-		ratio_zx = 1.0;
-		ratio_xy = 1.0;
-		ratio_zy = 1.0;
-		ratio_xz = 1.0;
-		ratio_yz = 1.0;
-	}
-}
-
-void EditorPropertyVector3::update_using_vector(Vector3 p_vector) {
-	if (angle_in_radians) {
-		p_vector.x = Math::rad_to_deg(p_vector.x);
-		p_vector.y = Math::rad_to_deg(p_vector.y);
-		p_vector.z = Math::rad_to_deg(p_vector.z);
-	}
-	setting = true;
-	spin[0]->set_value(p_vector.x);
-	spin[1]->set_value(p_vector.y);
-	spin[2]->set_value(p_vector.z);
-	setting = false;
-}
-
-Vector3 EditorPropertyVector3::get_vector() {
-	Vector3 v3;
-	v3.x = spin[0]->get_value();
-	v3.y = spin[1]->get_value();
-	v3.z = spin[2]->get_value();
-	if (angle_in_radians) {
-		v3.x = Math::deg_to_rad(v3.x);
-		v3.y = Math::deg_to_rad(v3.y);
-		v3.z = Math::deg_to_rad(v3.z);
-	}
-
-	return v3;
-}
-
-void EditorPropertyVector3::_notification(int p_what) {
-	switch (p_what) {
-		case NOTIFICATION_ENTER_TREE:
-		case NOTIFICATION_THEME_CHANGED: {
-			linked->set_texture_normal(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons")));
-			linked->set_texture_pressed(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
-
-			const Color *colors = _get_property_colors();
-			for (int i = 0; i < 3; i++) {
-				spin[i]->add_theme_color_override("label_color", colors[i]);
-			}
-		} break;
-	}
-}
-
-void EditorPropertyVector3::_bind_methods() {
-}
-
-void EditorPropertyVector3::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_link, const String &p_suffix, bool p_angle_in_radians) {
-	angle_in_radians = p_angle_in_radians;
-	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_hide_slider);
-		spin[i]->set_allow_greater(true);
-		spin[i]->set_allow_lesser(true);
-		spin[i]->set_suffix(p_suffix);
-	}
-
-	if (!p_link) {
-		linked->hide();
-	} else {
-		linked->set_pressed(true);
-	}
-}
-
-EditorPropertyVector3::EditorPropertyVector3(bool p_force_wide) {
-	bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));
-
-	HBoxContainer *hb = memnew(HBoxContainer);
-	hb->set_h_size_flags(SIZE_EXPAND_FILL);
-
-	BoxContainer *bc;
-
-	if (p_force_wide) {
-		bc = memnew(HBoxContainer);
-		hb->add_child(bc);
-	} else if (horizontal) {
-		bc = memnew(HBoxContainer);
-		hb->add_child(bc);
-		set_bottom_editor(hb);
-	} else {
-		bc = memnew(VBoxContainer);
-		hb->add_child(bc);
-	}
-	bc->set_h_size_flags(SIZE_EXPAND_FILL);
-
-	static const char *desc[3] = { "x", "y", "z" };
-	for (int i = 0; i < 3; i++) {
-		spin[i] = memnew(EditorSpinSlider);
-		spin[i]->set_flat(true);
-		spin[i]->set_label(desc[i]);
-		bc->add_child(spin[i]);
-		add_focusable(spin[i]);
-		spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVector3::_value_changed).bind(desc[i]));
-		if (horizontal) {
-			spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
-		}
-	}
-
-	linked = memnew(TextureButton);
-	linked->set_toggle_mode(true);
-	linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
-	linked->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyVector3::_update_ratio));
-	hb->add_child(linked);
-
-	add_child(hb);
-	if (!horizontal) {
-		set_label_reference(spin[0]); //show text and buttons around this
-	}
-}
-
-///////////////////// VECTOR2i /////////////////////////
-
-void EditorPropertyVector2i::_set_read_only(bool p_read_only) {
-	for (int i = 0; i < 2; i++) {
-		spin[i]->set_read_only(p_read_only);
-	}
-}
-
-void EditorPropertyVector2i::_value_changed(double val, const String &p_name) {
-	if (setting) {
-		return;
-	}
-
-	if (linked->is_pressed()) {
-		setting = true;
-		if (p_name == "x") {
-			spin[1]->set_value(spin[0]->get_value() * ratio_yx);
-		}
-
-		if (p_name == "y") {
-			spin[0]->set_value(spin[1]->get_value() * ratio_xy);
-		}
-		setting = false;
-	}
-
-	Vector2i v2;
-	v2.x = spin[0]->get_value();
-	v2.y = spin[1]->get_value();
-	emit_changed(get_edited_property(), v2, linked->is_pressed() ? "" : p_name);
-}
-
-void EditorPropertyVector2i::update_property() {
-	Vector2i val = get_edited_property_value();
-	setting = true;
-	spin[0]->set_value(val.x);
-	spin[1]->set_value(val.y);
-	setting = false;
-	_update_ratio();
-}
-
-void EditorPropertyVector2i::_update_ratio() {
-	linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5));
-
-	if (spin[0]->get_value() != 0 && spin[1]->get_value() != 0) {
-		ratio_xy = spin[0]->get_value() / spin[1]->get_value();
-		ratio_yx = spin[1]->get_value() / spin[0]->get_value();
-	} else {
-		ratio_xy = 1.0;
-		ratio_yx = 1.0;
-	}
-}
-
-void EditorPropertyVector2i::_notification(int p_what) {
-	switch (p_what) {
-		case NOTIFICATION_ENTER_TREE:
-		case NOTIFICATION_THEME_CHANGED: {
-			linked->set_texture_normal(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons")));
-			linked->set_texture_pressed(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
-
-			const Color *colors = _get_property_colors();
-			for (int i = 0; i < 2; i++) {
-				spin[i]->add_theme_color_override("label_color", colors[i]);
-			}
-		} break;
-	}
-}
-
-void EditorPropertyVector2i::setup(int p_min, int p_max, bool p_link, const String &p_suffix) {
-	for (int i = 0; i < 2; i++) {
-		spin[i]->set_min(p_min);
-		spin[i]->set_max(p_max);
-		spin[i]->set_step(1);
-		spin[i]->set_allow_greater(true);
-		spin[i]->set_allow_lesser(true);
-		spin[i]->set_suffix(p_suffix);
-	}
-
-	if (!p_link) {
-		linked->hide();
-	} else {
-		linked->set_pressed(true);
-	}
-}
-
-EditorPropertyVector2i::EditorPropertyVector2i(bool p_force_wide) {
-	bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector2_editing"));
-
-	HBoxContainer *hb = memnew(HBoxContainer);
-	hb->set_h_size_flags(SIZE_EXPAND_FILL);
-
-	BoxContainer *bc;
-
-	if (p_force_wide) {
-		bc = memnew(HBoxContainer);
-		hb->add_child(bc);
-	} else if (horizontal) {
-		bc = memnew(HBoxContainer);
-		hb->add_child(bc);
-		set_bottom_editor(hb);
-	} else {
-		bc = memnew(VBoxContainer);
-		hb->add_child(bc);
-	}
-	bc->set_h_size_flags(SIZE_EXPAND_FILL);
-
-	static const char *desc[2] = { "x", "y" };
-	for (int i = 0; i < 2; i++) {
-		spin[i] = memnew(EditorSpinSlider);
-		spin[i]->set_flat(true);
-		spin[i]->set_label(desc[i]);
-		bc->add_child(spin[i]);
-		add_focusable(spin[i]);
-		spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVector2i::_value_changed).bind(desc[i]));
-		if (horizontal) {
-			spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
-		}
-	}
-
-	linked = memnew(TextureButton);
-	linked->set_toggle_mode(true);
-	linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
-	linked->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyVector2i::_update_ratio));
-	hb->add_child(linked);
-
-	add_child(hb);
-	if (!horizontal) {
-		set_label_reference(spin[0]); //show text and buttons around this
-	}
-}
-
 ///////////////////// RECT2i /////////////////////////
 
 void EditorPropertyRect2i::_set_read_only(bool p_read_only) {
@@ -2345,156 +1906,6 @@ EditorPropertyRect2i::EditorPropertyRect2i(bool p_force_wide) {
 	}
 }
 
-///////////////////// VECTOR3I /////////////////////////
-
-void EditorPropertyVector3i::_set_read_only(bool p_read_only) {
-	for (int i = 0; i < 3; i++) {
-		spin[i]->set_read_only(p_read_only);
-	}
-}
-
-void EditorPropertyVector3i::_value_changed(double val, const String &p_name) {
-	if (setting) {
-		return;
-	}
-
-	if (linked->is_pressed()) {
-		setting = true;
-		if (p_name == "x") {
-			spin[1]->set_value(spin[0]->get_value() * ratio_yx);
-			spin[2]->set_value(spin[0]->get_value() * ratio_zx);
-		}
-
-		if (p_name == "y") {
-			spin[0]->set_value(spin[1]->get_value() * ratio_xy);
-			spin[2]->set_value(spin[1]->get_value() * ratio_zy);
-		}
-
-		if (p_name == "z") {
-			spin[0]->set_value(spin[2]->get_value() * ratio_xz);
-			spin[1]->set_value(spin[2]->get_value() * ratio_yz);
-		}
-		setting = false;
-	}
-
-	Vector3i v3;
-	v3.x = spin[0]->get_value();
-	v3.y = spin[1]->get_value();
-	v3.z = spin[2]->get_value();
-	emit_changed(get_edited_property(), v3, linked->is_pressed() ? "" : p_name);
-}
-
-void EditorPropertyVector3i::update_property() {
-	Vector3i val = get_edited_property_value();
-	setting = true;
-	spin[0]->set_value(val.x);
-	spin[1]->set_value(val.y);
-	spin[2]->set_value(val.z);
-	setting = false;
-	_update_ratio();
-}
-
-void EditorPropertyVector3i::_update_ratio() {
-	linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5));
-
-	if (spin[0]->get_value() != 0 && spin[1]->get_value() != 0) {
-		ratio_yx = spin[1]->get_value() / spin[0]->get_value();
-		ratio_zx = spin[2]->get_value() / spin[0]->get_value();
-		ratio_xy = spin[0]->get_value() / spin[1]->get_value();
-		ratio_zy = spin[2]->get_value() / spin[1]->get_value();
-		ratio_xz = spin[0]->get_value() / spin[2]->get_value();
-		ratio_yz = spin[1]->get_value() / spin[2]->get_value();
-	} else {
-		ratio_yx = 1.0;
-		ratio_zx = 1.0;
-		ratio_xy = 1.0;
-		ratio_zy = 1.0;
-		ratio_xz = 1.0;
-		ratio_yz = 1.0;
-	}
-}
-
-void EditorPropertyVector3i::_notification(int p_what) {
-	switch (p_what) {
-		case NOTIFICATION_ENTER_TREE:
-		case NOTIFICATION_THEME_CHANGED: {
-			linked->set_texture_normal(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons")));
-			linked->set_texture_pressed(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
-
-			const Color *colors = _get_property_colors();
-			for (int i = 0; i < 3; i++) {
-				spin[i]->add_theme_color_override("label_color", colors[i]);
-			}
-		} break;
-	}
-}
-
-void EditorPropertyVector3i::_bind_methods() {
-}
-
-void EditorPropertyVector3i::setup(int p_min, int p_max, bool p_link, const String &p_suffix) {
-	for (int i = 0; i < 3; i++) {
-		spin[i]->set_min(p_min);
-		spin[i]->set_max(p_max);
-		spin[i]->set_step(1);
-		spin[i]->set_hide_slider(false);
-		spin[i]->set_allow_greater(true);
-		spin[i]->set_allow_lesser(true);
-		spin[i]->set_suffix(p_suffix);
-	}
-
-	if (!p_link) {
-		linked->hide();
-	} else {
-		linked->set_pressed(true);
-	}
-}
-
-EditorPropertyVector3i::EditorPropertyVector3i(bool p_force_wide) {
-	bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));
-
-	HBoxContainer *hb = memnew(HBoxContainer);
-	hb->set_h_size_flags(SIZE_EXPAND_FILL);
-
-	BoxContainer *bc;
-	if (p_force_wide) {
-		bc = memnew(HBoxContainer);
-		hb->add_child(bc);
-	} else if (horizontal) {
-		bc = memnew(HBoxContainer);
-		hb->add_child(bc);
-		set_bottom_editor(hb);
-	} else {
-		bc = memnew(VBoxContainer);
-		hb->add_child(bc);
-	}
-
-	static const char *desc[3] = { "x", "y", "z" };
-	for (int i = 0; i < 3; i++) {
-		spin[i] = memnew(EditorSpinSlider);
-		spin[i]->set_flat(true);
-		spin[i]->set_label(desc[i]);
-		bc->add_child(spin[i]);
-		add_focusable(spin[i]);
-		spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVector3i::_value_changed).bind(desc[i]));
-		if (horizontal) {
-			spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
-		}
-	}
-	bc->set_h_size_flags(SIZE_EXPAND_FILL);
-
-	linked = memnew(TextureButton);
-	linked->set_toggle_mode(true);
-	linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
-	linked->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyVector3i::_update_ratio));
-	hb->add_child(linked);
-
-	add_child(hb);
-	if (!horizontal) {
-		set_label_reference(spin[0]); //show text and buttons around this
-	}
-}
-
 ///////////////////// PLANE /////////////////////////
 
 void EditorPropertyPlane::_set_read_only(bool p_read_only) {
@@ -2804,184 +2215,6 @@ EditorPropertyQuaternion::EditorPropertyQuaternion() {
 		set_label_reference(spin[0]); //show text and buttons around this
 	}
 }
-///////////////////// VECTOR4 /////////////////////////
-
-void EditorPropertyVector4::_set_read_only(bool p_read_only) {
-	for (int i = 0; i < 4; i++) {
-		spin[i]->set_read_only(p_read_only);
-	}
-}
-
-void EditorPropertyVector4::_value_changed(double val, const String &p_name) {
-	if (setting) {
-		return;
-	}
-
-	Vector4 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_changed(get_edited_property(), p, p_name);
-}
-
-void EditorPropertyVector4::update_property() {
-	Vector4 val = get_edited_property_value();
-	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 EditorPropertyVector4::_notification(int p_what) {
-	switch (p_what) {
-		case NOTIFICATION_ENTER_TREE:
-		case NOTIFICATION_THEME_CHANGED: {
-			const Color *colors = _get_property_colors();
-			for (int i = 0; i < 4; i++) {
-				spin[i]->add_theme_color_override("label_color", colors[i]);
-			}
-		} break;
-	}
-}
-
-void EditorPropertyVector4::_bind_methods() {
-}
-
-void EditorPropertyVector4::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
-	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_hide_slider);
-		spin[i]->set_allow_greater(true);
-		spin[i]->set_allow_lesser(true);
-		spin[i]->set_suffix(p_suffix);
-	}
-}
-
-EditorPropertyVector4::EditorPropertyVector4() {
-	bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing");
-
-	BoxContainer *bc;
-
-	if (horizontal) {
-		bc = memnew(HBoxContainer);
-		add_child(bc);
-		set_bottom_editor(bc);
-	} else {
-		bc = memnew(VBoxContainer);
-		add_child(bc);
-	}
-
-	static const char *desc[4] = { "x", "y", "z", "w" };
-	for (int i = 0; i < 4; i++) {
-		spin[i] = memnew(EditorSpinSlider);
-		spin[i]->set_flat(true);
-		spin[i]->set_label(desc[i]);
-		bc->add_child(spin[i]);
-		add_focusable(spin[i]);
-		spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVector4::_value_changed).bind(desc[i]));
-		if (horizontal) {
-			spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
-		}
-	}
-
-	if (!horizontal) {
-		set_label_reference(spin[0]); //show text and buttons around this
-	}
-}
-
-///////////////////// VECTOR4I /////////////////////////
-
-void EditorPropertyVector4i::_set_read_only(bool p_read_only) {
-	for (int i = 0; i < 4; i++) {
-		spin[i]->set_read_only(p_read_only);
-	}
-}
-
-void EditorPropertyVector4i::_value_changed(double val, const String &p_name) {
-	if (setting) {
-		return;
-	}
-
-	Vector4i 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_changed(get_edited_property(), p, p_name);
-}
-
-void EditorPropertyVector4i::update_property() {
-	Vector4i val = get_edited_property_value();
-	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 EditorPropertyVector4i::_notification(int p_what) {
-	switch (p_what) {
-		case NOTIFICATION_ENTER_TREE:
-		case NOTIFICATION_THEME_CHANGED: {
-			const Color *colors = _get_property_colors();
-			for (int i = 0; i < 4; i++) {
-				spin[i]->add_theme_color_override("label_color", colors[i]);
-			}
-		} break;
-	}
-}
-
-void EditorPropertyVector4i::_bind_methods() {
-}
-
-void EditorPropertyVector4i::setup(double p_min, double p_max, const String &p_suffix) {
-	for (int i = 0; i < 4; i++) {
-		spin[i]->set_min(p_min);
-		spin[i]->set_max(p_max);
-		spin[i]->set_step(1);
-		spin[i]->set_allow_greater(true);
-		spin[i]->set_allow_lesser(true);
-		spin[i]->set_suffix(p_suffix);
-	}
-}
-
-EditorPropertyVector4i::EditorPropertyVector4i() {
-	bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing");
-
-	BoxContainer *bc;
-
-	if (horizontal) {
-		bc = memnew(HBoxContainer);
-		add_child(bc);
-		set_bottom_editor(bc);
-	} else {
-		bc = memnew(VBoxContainer);
-		add_child(bc);
-	}
-
-	static const char *desc[4] = { "x", "y", "z", "w" };
-	for (int i = 0; i < 4; i++) {
-		spin[i] = memnew(EditorSpinSlider);
-		spin[i]->set_flat(true);
-		spin[i]->set_label(desc[i]);
-		bc->add_child(spin[i]);
-		add_focusable(spin[i]);
-		spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVector4i::_value_changed).bind(desc[i]));
-		if (horizontal) {
-			spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
-		}
-	}
-
-	if (!horizontal) {
-		set_label_reference(spin[0]); //show text and buttons around this
-	}
-}
 
 ///////////////////// AABB /////////////////////////
 
@@ -4430,7 +3663,7 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
 		case Variant::VECTOR2I: {
 			EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide));
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
-			editor->setup(hint.min, hint.max, p_hint == PROPERTY_HINT_LINK, hint.suffix);
+			editor->setup(hint.min, hint.max, 1, true, p_hint == PROPERTY_HINT_LINK, hint.suffix);
 			return editor;
 
 		} break;
@@ -4457,21 +3690,21 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
 		case Variant::VECTOR3I: {
 			EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide));
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
-			editor->setup(hint.min, hint.max, p_hint == PROPERTY_HINT_LINK, hint.suffix);
+			editor->setup(hint.min, hint.max, 1, true, p_hint == PROPERTY_HINT_LINK, hint.suffix);
 			return editor;
 
 		} break;
 		case Variant::VECTOR4: {
 			EditorPropertyVector4 *editor = memnew(EditorPropertyVector4);
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);
-			editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);
+			editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix);
 			return editor;
 
 		} break;
 		case Variant::VECTOR4I: {
 			EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
 			EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);
-			editor->setup(hint.min, hint.max, hint.suffix);
+			editor->setup(hint.min, hint.max, 1, true, p_hint == PROPERTY_HINT_LINK, hint.suffix);
 			return editor;
 
 		} break;

+ 0 - 127
editor/editor_properties.h

@@ -455,26 +455,6 @@ public:
 	EditorPropertyEasing();
 };
 
-class EditorPropertyVector2 : public EditorProperty {
-	GDCLASS(EditorPropertyVector2, EditorProperty);
-	EditorSpinSlider *spin[2];
-	bool setting = false;
-	double ratio_xy = 1.0;
-	double ratio_yx = 1.0;
-	TextureButton *linked = nullptr;
-	void _update_ratio();
-	void _value_changed(double p_val, const String &p_name);
-
-protected:
-	virtual void _set_read_only(bool p_read_only) override;
-	void _notification(int p_what);
-
-public:
-	virtual void update_property() override;
-	void setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_link = false, const String &p_suffix = String());
-	EditorPropertyVector2(bool p_force_wide = false);
-};
-
 class EditorPropertyRect2 : public EditorProperty {
 	GDCLASS(EditorPropertyRect2, EditorProperty);
 	EditorSpinSlider *spin[4];
@@ -492,54 +472,6 @@ public:
 	EditorPropertyRect2(bool p_force_wide = false);
 };
 
-class EditorPropertyVector3 : public EditorProperty {
-	GDCLASS(EditorPropertyVector3, EditorProperty);
-	EditorSpinSlider *spin[3];
-	bool setting = false;
-	bool angle_in_radians = false;
-	double ratio_yx = 1.0;
-	double ratio_zx = 1.0;
-	double ratio_xy = 1.0;
-	double ratio_zy = 1.0;
-	double ratio_xz = 1.0;
-	double ratio_yz = 1.0;
-	TextureButton *linked = nullptr;
-	void _update_ratio();
-	void _value_changed(double p_val, const String &p_name);
-
-protected:
-	virtual void _set_read_only(bool p_read_only) override;
-	void _notification(int p_what);
-	static void _bind_methods();
-
-public:
-	virtual void update_property() override;
-	virtual void update_using_vector(Vector3 p_vector);
-	virtual Vector3 get_vector();
-	void setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_link = false, const String &p_suffix = String(), bool p_angle_in_radians = false);
-	EditorPropertyVector3(bool p_force_wide = false);
-};
-
-class EditorPropertyVector2i : public EditorProperty {
-	GDCLASS(EditorPropertyVector2i, EditorProperty);
-	EditorSpinSlider *spin[2];
-	bool setting = false;
-	double ratio_xy = 1.0;
-	double ratio_yx = 1.0;
-	TextureButton *linked = nullptr;
-	void _update_ratio();
-	void _value_changed(double p_val, const String &p_name);
-
-protected:
-	virtual void _set_read_only(bool p_read_only) override;
-	void _notification(int p_what);
-
-public:
-	virtual void update_property() override;
-	void setup(int p_min, int p_max, bool p_link = false, const String &p_suffix = String());
-	EditorPropertyVector2i(bool p_force_wide = false);
-};
-
 class EditorPropertyRect2i : public EditorProperty {
 	GDCLASS(EditorPropertyRect2i, EditorProperty);
 	EditorSpinSlider *spin[4];
@@ -557,31 +489,6 @@ public:
 	EditorPropertyRect2i(bool p_force_wide = false);
 };
 
-class EditorPropertyVector3i : public EditorProperty {
-	GDCLASS(EditorPropertyVector3i, EditorProperty);
-	EditorSpinSlider *spin[3];
-	bool setting = false;
-	double ratio_yx = 1.0;
-	double ratio_zx = 1.0;
-	double ratio_xy = 1.0;
-	double ratio_zy = 1.0;
-	double ratio_xz = 1.0;
-	double ratio_yz = 1.0;
-	TextureButton *linked = nullptr;
-	void _update_ratio();
-	void _value_changed(double p_val, const String &p_name);
-
-protected:
-	virtual void _set_read_only(bool p_read_only) override;
-	void _notification(int p_what);
-	static void _bind_methods();
-
-public:
-	virtual void update_property() override;
-	void setup(int p_min, int p_max, bool p_link = false, const String &p_suffix = String());
-	EditorPropertyVector3i(bool p_force_wide = false);
-};
-
 class EditorPropertyPlane : public EditorProperty {
 	GDCLASS(EditorPropertyPlane, EditorProperty);
 	EditorSpinSlider *spin[4];
@@ -633,40 +540,6 @@ public:
 	EditorPropertyQuaternion();
 };
 
-class EditorPropertyVector4 : public EditorProperty {
-	GDCLASS(EditorPropertyVector4, EditorProperty);
-	EditorSpinSlider *spin[4];
-	bool setting = false;
-	void _value_changed(double p_val, const String &p_name);
-
-protected:
-	virtual void _set_read_only(bool p_read_only) override;
-	void _notification(int p_what);
-	static void _bind_methods();
-
-public:
-	virtual void update_property() override;
-	void setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix = String());
-	EditorPropertyVector4();
-};
-
-class EditorPropertyVector4i : public EditorProperty {
-	GDCLASS(EditorPropertyVector4i, EditorProperty);
-	EditorSpinSlider *spin[4];
-	bool setting = false;
-	void _value_changed(double p_val, const String &p_name);
-
-protected:
-	virtual void _set_read_only(bool p_read_only) override;
-	void _notification(int p_what);
-	static void _bind_methods();
-
-public:
-	virtual void update_property() override;
-	void setup(double p_min, double p_max, const String &p_suffix = String());
-	EditorPropertyVector4i();
-};
-
 class EditorPropertyAABB : public EditorProperty {
 	GDCLASS(EditorPropertyAABB, EditorProperty);
 	EditorSpinSlider *spin[6];

+ 1 - 0
editor/editor_properties_array_dict.cpp

@@ -33,6 +33,7 @@
 #include "core/input/input.h"
 #include "core/io/marshalls.h"
 #include "editor/editor_properties.h"
+#include "editor/editor_properties_vector.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/gui/editor_spin_slider.h"

+ 258 - 0
editor/editor_properties_vector.cpp

@@ -0,0 +1,258 @@
+/**************************************************************************/
+/*  editor_properties_vector.cpp                                          */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#include "editor_properties_vector.h"
+
+#include "editor/editor_settings.h"
+#include "editor/gui/editor_spin_slider.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/texture_button.h"
+
+const String EditorPropertyVectorN::COMPONENT_LABELS[4] = { "x", "y", "z", "w" };
+
+int EditorPropertyVectorN::_get_ratio_component(int p_idx, int p_component) const {
+	int i = p_idx / (component_count - 1);
+	if (p_component == 1) {
+		return i;
+	} else {
+		return (i + p_idx % (component_count - 1) + 1) % component_count;
+	}
+}
+
+void EditorPropertyVectorN::_set_read_only(bool p_read_only) {
+	for (EditorSpinSlider *spin : spin_sliders) {
+		spin->set_read_only(p_read_only);
+	}
+}
+
+void EditorPropertyVectorN::_value_changed(double val, const String &p_name) {
+	if (linked->is_pressed()) {
+		// TODO: The logic here can be simplified. _get_ratio_component() only exists,
+		// because the exact code was difficult to figure out.
+		for (int i = 0; i < component_count; i++) {
+			if (p_name == COMPONENT_LABELS[i]) {
+				for (int j = 0; j < ratio.size(); j++) {
+					if (_get_ratio_component(j, 1) == i) {
+						for (int k = 0; k < component_count - 1; k++) {
+							spin_sliders[_get_ratio_component(j + k, 0)]->set_value_no_signal(spin_sliders[_get_ratio_component(j + k, 1)]->get_value() * ratio[j + k]);
+						}
+						break;
+					}
+				}
+				break;
+			}
+		}
+	}
+
+	Variant v;
+	Callable::CallError cerror;
+	Variant::construct(vector_type, v, nullptr, 0, cerror);
+
+	for (int i = 0; i < component_count; i++) {
+		if (angle_in_radians) {
+			v.set(i, Math::deg_to_rad(spin_sliders[i]->get_value()));
+		} else {
+			v.set(i, spin_sliders[i]->get_value());
+		}
+	}
+	emit_changed(get_edited_property(), v, linked->is_pressed() ? "" : p_name);
+}
+
+void EditorPropertyVectorN::update_property() {
+	Variant val = get_edited_property_value();
+	for (int i = 0; i < component_count; i++) {
+		if (angle_in_radians) {
+			spin_sliders[i]->set_value_no_signal(Math::rad_to_deg((real_t)val.get(i)));
+		} else {
+			spin_sliders[i]->set_value_no_signal(val.get(i));
+		}
+	}
+	_update_ratio();
+}
+
+void EditorPropertyVectorN::_update_ratio() {
+	linked->set_modulate(Color(1, 1, 1, linked->is_pressed() ? 1.0 : 0.5));
+
+	bool non_zero = true;
+	for (int i = 0; i < component_count; i++) {
+		if (spin_sliders[i]->get_value() == 0) {
+			non_zero = false;
+			break;
+		}
+	}
+
+	double *ratio_write = ratio.ptrw();
+	for (int i = 0; i < ratio.size(); i++) {
+		if (non_zero) {
+			ratio_write[i] = spin_sliders[_get_ratio_component(i, 0)]->get_value() / spin_sliders[_get_ratio_component(i, 1)]->get_value();
+		} else {
+			ratio_write[i] = 1.0;
+		}
+	}
+}
+
+void EditorPropertyVectorN::_store_link(bool p_linked) {
+	if (!get_edited_object()) {
+		return;
+	}
+	const String key = vformat("%s:%s", get_edited_object()->get_class(), get_edited_property());
+	EditorSettings::get_singleton()->set_project_metadata("linked_properties", key, p_linked);
+}
+
+void EditorPropertyVectorN::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_READY: {
+			if (linked->is_visible()) {
+				const String key = vformat("%s:%s", get_edited_object()->get_class(), get_edited_property());
+				linked->set_pressed(EditorSettings::get_singleton()->get_project_metadata("linked_properties", key, true));
+			}
+		} break;
+
+		case NOTIFICATION_THEME_CHANGED: {
+			linked->set_texture_normal(get_theme_icon(SNAME("Unlinked"), SNAME("EditorIcons")));
+			linked->set_texture_pressed(get_theme_icon(SNAME("Instance"), SNAME("EditorIcons")));
+
+			const Color *colors = _get_property_colors();
+			for (int i = 0; i < component_count; i++) {
+				spin_sliders[i]->add_theme_color_override("label_color", colors[i]);
+			}
+		} break;
+	}
+}
+
+void EditorPropertyVectorN::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_link, const String &p_suffix, bool p_angle_in_radians) {
+	angle_in_radians = p_angle_in_radians;
+
+	for (EditorSpinSlider *spin : spin_sliders) {
+		spin->set_min(p_min);
+		spin->set_max(p_max);
+		spin->set_step(p_step);
+		spin->set_hide_slider(p_hide_slider);
+		spin->set_allow_greater(true);
+		spin->set_allow_lesser(true);
+		spin->set_suffix(p_suffix);
+	}
+
+	if (!p_link) {
+		linked->hide();
+	}
+}
+
+EditorPropertyVectorN::EditorPropertyVectorN(Variant::Type p_type, bool p_force_wide, bool p_horizontal) {
+	vector_type = p_type;
+	switch (vector_type) {
+		case Variant::VECTOR2:
+		case Variant::VECTOR2I:
+			component_count = 2;
+			break;
+
+		case Variant::VECTOR3:
+		case Variant::VECTOR3I:
+			component_count = 3;
+			break;
+
+		case Variant::VECTOR4:
+		case Variant::VECTOR4I:
+			component_count = 4;
+			break;
+
+		default: // Needed to silence a warning.
+			ERR_PRINT("Not a Vector type.");
+			break;
+	}
+	bool horizontal = p_force_wide || p_horizontal;
+
+	HBoxContainer *hb = memnew(HBoxContainer);
+	hb->set_h_size_flags(SIZE_EXPAND_FILL);
+
+	BoxContainer *bc;
+
+	if (p_force_wide) {
+		bc = memnew(HBoxContainer);
+		hb->add_child(bc);
+	} else if (horizontal) {
+		bc = memnew(HBoxContainer);
+		hb->add_child(bc);
+		set_bottom_editor(hb);
+	} else {
+		bc = memnew(VBoxContainer);
+		hb->add_child(bc);
+	}
+	bc->set_h_size_flags(SIZE_EXPAND_FILL);
+
+	spin_sliders.resize(component_count);
+	EditorSpinSlider **spin = spin_sliders.ptrw();
+
+	for (int i = 0; i < component_count; i++) {
+		spin[i] = memnew(EditorSpinSlider);
+		spin[i]->set_flat(true);
+		spin[i]->set_label(String(COMPONENT_LABELS[i]));
+		bc->add_child(spin[i]);
+		add_focusable(spin[i]);
+		spin[i]->connect("value_changed", callable_mp(this, &EditorPropertyVectorN::_value_changed).bind(String(COMPONENT_LABELS[i])));
+		if (horizontal) {
+			spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);
+		}
+	}
+
+	ratio.resize(component_count * (component_count - 1));
+	ratio.fill(1.0);
+
+	linked = memnew(TextureButton);
+	linked->set_toggle_mode(true);
+	linked->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);
+	linked->set_tooltip_text(TTR("Lock/Unlock Component Ratio"));
+	linked->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyVectorN::_update_ratio));
+	linked->connect(SNAME("toggled"), callable_mp(this, &EditorPropertyVectorN::_store_link));
+	hb->add_child(linked);
+
+	add_child(hb);
+	if (!horizontal) {
+		set_label_reference(spin_sliders[0]); // Show text and buttons around this.
+	}
+}
+
+EditorPropertyVector2::EditorPropertyVector2(bool p_force_wide) :
+		EditorPropertyVectorN(Variant::VECTOR2, p_force_wide, EDITOR_GET("interface/inspector/horizontal_vector2_editing")) {}
+
+EditorPropertyVector2i::EditorPropertyVector2i(bool p_force_wide) :
+		EditorPropertyVectorN(Variant::VECTOR2I, p_force_wide, EDITOR_GET("interface/inspector/horizontal_vector2_editing")) {}
+
+EditorPropertyVector3::EditorPropertyVector3(bool p_force_wide) :
+		EditorPropertyVectorN(Variant::VECTOR3, p_force_wide, EDITOR_GET("interface/inspector/horizontal_vector_types_editing")) {}
+
+EditorPropertyVector3i::EditorPropertyVector3i(bool p_force_wide) :
+		EditorPropertyVectorN(Variant::VECTOR3I, p_force_wide, EDITOR_GET("interface/inspector/horizontal_vector_types_editing")) {}
+
+EditorPropertyVector4::EditorPropertyVector4(bool p_force_wide) :
+		EditorPropertyVectorN(Variant::VECTOR4, p_force_wide, EDITOR_GET("interface/inspector/horizontal_vector_types_editing")) {}
+
+EditorPropertyVector4i::EditorPropertyVector4i(bool p_force_wide) :
+		EditorPropertyVectorN(Variant::VECTOR4I, p_force_wide, EDITOR_GET("interface/inspector/horizontal_vector_types_editing")) {}

+ 110 - 0
editor/editor_properties_vector.h

@@ -0,0 +1,110 @@
+/**************************************************************************/
+/*  editor_properties_vector.h                                            */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#ifndef EDITOR_PROPERTIES_VECTOR_H
+#define EDITOR_PROPERTIES_VECTOR_H
+
+#include "editor/editor_inspector.h"
+
+class EditorSpinSlider;
+class TextureButton;
+
+class EditorPropertyVectorN : public EditorProperty {
+	GDCLASS(EditorPropertyVectorN, EditorProperty);
+
+	static const String COMPONENT_LABELS[4];
+
+	int component_count = 0;
+	Variant::Type vector_type;
+
+	Vector<EditorSpinSlider *> spin_sliders;
+	TextureButton *linked = nullptr;
+	Vector<double> ratio;
+
+	bool angle_in_radians = false;
+
+	void _update_ratio();
+	int _get_ratio_component(int p_idx, int p_component) const;
+	void _store_link(bool p_linked);
+	void _value_changed(double p_val, const String &p_name);
+
+protected:
+	virtual void _set_read_only(bool p_read_only) override;
+	void _notification(int p_what);
+
+public:
+	virtual void update_property() override;
+	void setup(double p_min, double p_max, double p_step = 1.0, bool p_hide_slider = true, bool p_link = false, const String &p_suffix = String(), bool p_angle_in_radians = false);
+	EditorPropertyVectorN(Variant::Type p_type, bool p_force_wide, bool p_horizontal);
+};
+
+class EditorPropertyVector2 : public EditorPropertyVectorN {
+	GDCLASS(EditorPropertyVector2, EditorPropertyVectorN);
+
+public:
+	EditorPropertyVector2(bool p_force_wide = false);
+};
+
+class EditorPropertyVector2i : public EditorPropertyVectorN {
+	GDCLASS(EditorPropertyVector2i, EditorPropertyVectorN);
+
+public:
+	EditorPropertyVector2i(bool p_force_wide = false);
+};
+
+class EditorPropertyVector3 : public EditorPropertyVectorN {
+	GDCLASS(EditorPropertyVector3, EditorPropertyVectorN);
+
+public:
+	EditorPropertyVector3(bool p_force_wide = false);
+};
+
+class EditorPropertyVector3i : public EditorPropertyVectorN {
+	GDCLASS(EditorPropertyVector3i, EditorPropertyVectorN);
+
+public:
+	EditorPropertyVector3i(bool p_force_wide = false);
+};
+
+class EditorPropertyVector4 : public EditorPropertyVectorN {
+	GDCLASS(EditorPropertyVector4, EditorPropertyVectorN);
+
+public:
+	EditorPropertyVector4(bool p_force_wide = false);
+};
+
+class EditorPropertyVector4i : public EditorPropertyVectorN {
+	GDCLASS(EditorPropertyVector4i, EditorPropertyVectorN);
+
+public:
+	EditorPropertyVector4i(bool p_force_wide = false);
+};
+
+#endif // EDITOR_PROPERTIES_VECTOR_H

+ 1 - 0
editor/plugins/skeleton_3d_editor_plugin.cpp

@@ -33,6 +33,7 @@
 #include "core/io/resource_saver.h"
 #include "editor/editor_node.h"
 #include "editor/editor_properties.h"
+#include "editor/editor_properties_vector.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"

+ 1 - 0
editor/plugins/skeleton_3d_editor_plugin.h

@@ -41,6 +41,7 @@
 #include "scene/resources/immediate_mesh.h"
 
 class EditorInspectorPluginSkeleton;
+class EditorPropertyVector3;
 class Joint;
 class PhysicalBone3D;
 class Skeleton3DEditorPlugin;

+ 1 - 0
editor/plugins/tiles/atlas_merging_dialog.cpp

@@ -30,6 +30,7 @@
 
 #include "atlas_merging_dialog.h"
 
+#include "editor/editor_properties_vector.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_undo_redo_manager.h"
 #include "editor/gui/editor_file_dialog.h"

+ 1 - 0
editor/plugins/tiles/atlas_merging_dialog.h

@@ -38,6 +38,7 @@
 #include "scene/resources/tile_set.h"
 
 class EditorFileDialog;
+class EditorPropertyVector2i;
 
 class AtlasMergingDialog : public ConfirmationDialog {
 	GDCLASS(AtlasMergingDialog, ConfirmationDialog);

+ 1 - 0
editor/plugins/tiles/tile_proxies_manager_dialog.cpp

@@ -30,6 +30,7 @@
 
 #include "tile_proxies_manager_dialog.h"
 
+#include "editor/editor_properties_vector.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"

+ 1 - 0
editor/plugins/tiles/tile_proxies_manager_dialog.h

@@ -36,6 +36,7 @@
 #include "scene/gui/dialogs.h"
 #include "scene/gui/item_list.h"
 
+class EditorPropertyVector2i;
 class EditorUndoRedoManager;
 
 class TileProxiesManagerDialog : public ConfirmationDialog {

+ 1 - 0
editor/plugins/visual_shader_editor_plugin.cpp

@@ -37,6 +37,7 @@
 #include "core/os/keyboard.h"
 #include "editor/editor_node.h"
 #include "editor/editor_properties.h"
+#include "editor/editor_properties_vector.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"