2018-08-29 22:38:13 +02:00
|
|
|
/**************************************************************************/
|
|
|
|
/* editor_properties_array_dict.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. */
|
|
|
|
/**************************************************************************/
|
|
|
|
|
2018-05-19 21:09:38 +02:00
|
|
|
#include "editor_properties_array_dict.h"
|
2019-01-13 15:43:25 +01:00
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
#include "core/input/input.h"
|
2018-11-22 02:10:27 +01:00
|
|
|
#include "core/io/marshalls.h"
|
2022-02-12 02:46:22 +01:00
|
|
|
#include "editor/editor_properties.h"
|
2023-05-16 17:30:51 +02:00
|
|
|
#include "editor/editor_properties_vector.h"
|
2022-11-19 12:45:49 +01:00
|
|
|
#include "editor/editor_settings.h"
|
2023-08-13 02:33:39 +02:00
|
|
|
#include "editor/editor_string_names.h"
|
2023-04-07 18:59:49 +02:00
|
|
|
#include "editor/gui/editor_spin_slider.h"
|
2022-02-14 14:00:03 +01:00
|
|
|
#include "editor/inspector_dock.h"
|
2024-01-15 13:14:55 +01:00
|
|
|
#include "editor/themes/editor_scale.h"
|
2023-04-07 18:59:49 +02:00
|
|
|
#include "scene/gui/button.h"
|
2023-02-13 23:54:44 +01:00
|
|
|
#include "scene/resources/packed_scene.h"
|
2019-01-13 15:43:25 +01:00
|
|
|
|
2018-05-19 21:09:38 +02:00
|
|
|
bool EditorPropertyArrayObject::_set(const StringName &p_name, const Variant &p_value) {
|
2021-03-29 18:45:42 +02:00
|
|
|
String name = p_name;
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2023-05-19 14:03:01 +02:00
|
|
|
if (!name.begins_with("indices")) {
|
2023-02-13 23:54:44 +01:00
|
|
|
return false;
|
2018-05-19 21:09:38 +02:00
|
|
|
}
|
|
|
|
|
2023-02-13 23:54:44 +01:00
|
|
|
int index;
|
|
|
|
if (name.begins_with("metadata/")) {
|
|
|
|
index = name.get_slice("/", 2).to_int();
|
|
|
|
} else {
|
|
|
|
index = name.get_slice("/", 1).to_int();
|
|
|
|
}
|
|
|
|
|
|
|
|
array.set(index, p_value);
|
|
|
|
return true;
|
2018-05-19 21:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool EditorPropertyArrayObject::_get(const StringName &p_name, Variant &r_ret) const {
|
2021-03-29 18:45:42 +02:00
|
|
|
String name = p_name;
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2023-05-19 14:03:01 +02:00
|
|
|
if (!name.begins_with("indices")) {
|
2023-02-13 23:54:44 +01:00
|
|
|
return false;
|
|
|
|
}
|
2018-11-22 02:10:27 +01:00
|
|
|
|
2023-02-13 23:54:44 +01:00
|
|
|
int index;
|
|
|
|
if (name.begins_with("metadata/")) {
|
|
|
|
index = name.get_slice("/", 2).to_int();
|
|
|
|
} else {
|
|
|
|
index = name.get_slice("/", 1).to_int();
|
2018-05-19 21:09:38 +02:00
|
|
|
}
|
|
|
|
|
2023-02-13 23:54:44 +01:00
|
|
|
bool valid;
|
|
|
|
r_ret = array.get(index, &valid);
|
|
|
|
|
|
|
|
if (r_ret.get_type() == Variant::OBJECT && Object::cast_to<EncodedObjectAsID>(r_ret)) {
|
|
|
|
r_ret = Object::cast_to<EncodedObjectAsID>(r_ret)->get_object_id();
|
|
|
|
}
|
|
|
|
|
|
|
|
return valid;
|
2018-05-19 21:09:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyArrayObject::set_array(const Variant &p_array) {
|
|
|
|
array = p_array;
|
|
|
|
}
|
|
|
|
|
|
|
|
Variant EditorPropertyArrayObject::get_array() {
|
|
|
|
return array;
|
|
|
|
}
|
|
|
|
|
|
|
|
EditorPropertyArrayObject::EditorPropertyArrayObject() {
|
|
|
|
}
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
///////////////////
|
|
|
|
|
|
|
|
bool EditorPropertyDictionaryObject::_set(const StringName &p_name, const Variant &p_value) {
|
2021-03-29 18:45:42 +02:00
|
|
|
String name = p_name;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
if (name == "new_item_key") {
|
2018-05-19 22:57:44 +02:00
|
|
|
new_item_key = p_value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
if (name == "new_item_value") {
|
2018-05-19 22:57:44 +02:00
|
|
|
new_item_value = p_value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
if (name.begins_with("indices")) {
|
|
|
|
int index = name.get_slicec('/', 1).to_int();
|
|
|
|
Variant key = dict.get_key_at_index(index);
|
2018-05-19 22:57:44 +02:00
|
|
|
dict[key] = p_value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EditorPropertyDictionaryObject::_get(const StringName &p_name, Variant &r_ret) const {
|
2021-03-29 18:45:42 +02:00
|
|
|
String name = p_name;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
if (name == "new_item_key") {
|
2018-05-19 22:57:44 +02:00
|
|
|
r_ret = new_item_key;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
if (name == "new_item_value") {
|
2018-05-19 22:57:44 +02:00
|
|
|
r_ret = new_item_value;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
if (name.begins_with("indices")) {
|
|
|
|
int index = name.get_slicec('/', 1).to_int();
|
|
|
|
Variant key = dict.get_key_at_index(index);
|
2018-05-19 22:57:44 +02:00
|
|
|
r_ret = dict[key];
|
2018-11-22 02:10:27 +01:00
|
|
|
if (r_ret.get_type() == Variant::OBJECT && Object::cast_to<EncodedObjectAsID>(r_ret)) {
|
|
|
|
r_ret = Object::cast_to<EncodedObjectAsID>(r_ret)->get_object_id();
|
|
|
|
}
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyDictionaryObject::set_dict(const Dictionary &p_dict) {
|
|
|
|
dict = p_dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
Dictionary EditorPropertyDictionaryObject::get_dict() {
|
|
|
|
return dict;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyDictionaryObject::set_new_item_key(const Variant &p_new_item) {
|
|
|
|
new_item_key = p_new_item;
|
|
|
|
}
|
|
|
|
|
|
|
|
Variant EditorPropertyDictionaryObject::get_new_item_key() {
|
|
|
|
return new_item_key;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyDictionaryObject::set_new_item_value(const Variant &p_new_item) {
|
|
|
|
new_item_value = p_new_item;
|
|
|
|
}
|
|
|
|
|
|
|
|
Variant EditorPropertyDictionaryObject::get_new_item_value() {
|
|
|
|
return new_item_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() {
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////// ARRAY ///////////////////////////
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2022-11-27 08:56:53 +01:00
|
|
|
void EditorPropertyArray::initialize_array(Variant &p_array) {
|
|
|
|
if (array_type == Variant::ARRAY && subtype != Variant::NIL) {
|
|
|
|
Array array;
|
|
|
|
StringName subtype_class;
|
|
|
|
Ref<Script> subtype_script;
|
|
|
|
if (subtype == Variant::OBJECT && !subtype_hint_string.is_empty()) {
|
|
|
|
if (ClassDB::class_exists(subtype_hint_string)) {
|
|
|
|
subtype_class = subtype_hint_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
array.set_typed(subtype, subtype_class, subtype_script);
|
|
|
|
p_array = array;
|
|
|
|
} else {
|
|
|
|
VariantInternal::initialize(&p_array, array_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-05 15:33:01 +01:00
|
|
|
void EditorPropertyArray::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
|
2023-05-19 14:03:01 +02:00
|
|
|
if (!p_property.begins_with("indices")) {
|
2023-02-13 23:54:44 +01:00
|
|
|
return;
|
|
|
|
}
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2023-10-31 09:36:29 +01:00
|
|
|
if (p_value.get_type() == Variant::OBJECT && p_value.is_null()) {
|
|
|
|
p_value = Variant(); // `EditorResourcePicker` resets to `Ref<Resource>()`. See GH-82716.
|
|
|
|
}
|
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
int index = p_property.get_slice("/", 1).to_int();
|
2023-02-13 23:54:44 +01:00
|
|
|
|
2023-05-19 14:03:01 +02:00
|
|
|
Variant array = object->get_array().duplicate();
|
2023-02-13 23:54:44 +01:00
|
|
|
array.set(index, p_value);
|
2023-11-23 10:16:53 +01:00
|
|
|
emit_changed(get_edited_property(), array, p_name, p_changing);
|
2018-05-19 21:09:38 +02:00
|
|
|
}
|
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
void EditorPropertyArray::_change_type(Object *p_button, int p_slot_index) {
|
2018-05-19 21:09:38 +02:00
|
|
|
Button *button = Object::cast_to<Button>(p_button);
|
2024-02-23 20:41:32 +01:00
|
|
|
changing_type_index = p_slot_index;
|
2020-03-12 13:37:40 +01:00
|
|
|
Rect2 rect = button->get_screen_rect();
|
2022-03-06 00:57:42 +01:00
|
|
|
change_type->reset_size();
|
2021-09-22 10:24:45 +02:00
|
|
|
change_type->set_position(rect.get_end() - Vector2(change_type->get_contents_minimum_size().x, 0));
|
2018-05-19 21:09:38 +02:00
|
|
|
change_type->popup();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyArray::_change_type_menu(int p_index) {
|
2019-06-10 16:37:41 +02:00
|
|
|
if (p_index == Variant::VARIANT_MAX) {
|
2021-03-29 18:45:42 +02:00
|
|
|
_remove_pressed(changing_type_index);
|
2019-06-10 16:37:41 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-19 21:09:38 +02:00
|
|
|
Variant value;
|
2022-11-27 08:56:53 +01:00
|
|
|
VariantInternal::initialize(&value, Variant::Type(p_index));
|
|
|
|
|
|
|
|
Variant array = object->get_array().duplicate();
|
2024-02-23 20:41:32 +01:00
|
|
|
array.set(slots[changing_type_index].index, value);
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2019-01-18 17:01:24 +01:00
|
|
|
emit_changed(get_edited_property(), array, "", true);
|
2018-05-19 21:09:38 +02:00
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
2020-03-03 10:46:03 +01:00
|
|
|
void EditorPropertyArray::_object_id_selected(const StringName &p_property, ObjectID p_id) {
|
2021-07-17 23:22:52 +02:00
|
|
|
emit_signal(SNAME("object_id_selected"), p_property, p_id);
|
2018-11-22 02:10:27 +01:00
|
|
|
}
|
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
void EditorPropertyArray::create_new_property_slot() {
|
|
|
|
int idx = slots.size();
|
|
|
|
HBoxContainer *hbox = memnew(HBoxContainer);
|
|
|
|
|
|
|
|
Button *reorder_button = memnew(Button);
|
|
|
|
reorder_button->set_icon(get_editor_theme_icon(SNAME("TripleBar")));
|
|
|
|
reorder_button->set_default_cursor_shape(Control::CURSOR_MOVE);
|
|
|
|
reorder_button->set_disabled(is_read_only());
|
|
|
|
reorder_button->connect(SNAME("gui_input"), callable_mp(this, &EditorPropertyArray::_reorder_button_gui_input));
|
|
|
|
reorder_button->connect(SNAME("button_up"), callable_mp(this, &EditorPropertyArray::_reorder_button_up));
|
|
|
|
reorder_button->connect(SNAME("button_down"), callable_mp(this, &EditorPropertyArray::_reorder_button_down).bind(idx));
|
|
|
|
|
|
|
|
hbox->add_child(reorder_button);
|
|
|
|
EditorProperty *prop = memnew(EditorPropertyNil);
|
|
|
|
hbox->add_child(prop);
|
|
|
|
|
|
|
|
bool is_untyped_array = object->get_array().get_type() == Variant::ARRAY && subtype == Variant::NIL;
|
|
|
|
|
|
|
|
if (is_untyped_array) {
|
|
|
|
Button *edit_btn = memnew(Button);
|
|
|
|
edit_btn->set_icon(get_editor_theme_icon(SNAME("Edit")));
|
|
|
|
edit_btn->set_disabled(is_read_only());
|
|
|
|
edit_btn->connect("pressed", callable_mp(this, &EditorPropertyArray::_change_type).bind(edit_btn, idx));
|
|
|
|
hbox->add_child(edit_btn);
|
|
|
|
} else {
|
|
|
|
Button *remove_btn = memnew(Button);
|
|
|
|
remove_btn->set_icon(get_editor_theme_icon(SNAME("Remove")));
|
|
|
|
remove_btn->set_disabled(is_read_only());
|
|
|
|
remove_btn->connect("pressed", callable_mp(this, &EditorPropertyArray::_remove_pressed).bind(idx));
|
|
|
|
hbox->add_child(remove_btn);
|
|
|
|
}
|
|
|
|
property_vbox->add_child(hbox);
|
|
|
|
|
|
|
|
Slot slot;
|
|
|
|
slot.prop = prop;
|
|
|
|
slot.object = object;
|
|
|
|
slot.container = hbox;
|
|
|
|
slot.reorder_button = reorder_button;
|
|
|
|
slot.set_index(idx + page_index * page_length);
|
|
|
|
slots.push_back(slot);
|
|
|
|
}
|
|
|
|
|
2018-05-19 21:09:38 +02:00
|
|
|
void EditorPropertyArray::update_property() {
|
2023-05-26 09:13:24 +02:00
|
|
|
Variant array = get_edited_property_value();
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2022-04-21 04:57:21 +02:00
|
|
|
String array_type_name = Variant::get_type_name(array_type);
|
|
|
|
if (array_type == Variant::ARRAY && subtype != Variant::NIL) {
|
2022-07-13 19:07:01 +02:00
|
|
|
String type_name;
|
2023-04-28 09:27:10 +02:00
|
|
|
if (subtype == Variant::OBJECT && (subtype_hint == PROPERTY_HINT_RESOURCE_TYPE || subtype_hint == PROPERTY_HINT_NODE_TYPE)) {
|
2022-07-13 19:07:01 +02:00
|
|
|
type_name = subtype_hint_string;
|
|
|
|
} else {
|
|
|
|
type_name = Variant::get_type_name(subtype);
|
|
|
|
}
|
|
|
|
|
|
|
|
array_type_name = vformat("%s[%s]", array_type_name, type_name);
|
2018-05-19 21:09:38 +02:00
|
|
|
}
|
|
|
|
|
2023-08-25 02:11:12 +02:00
|
|
|
if (!array.is_array()) {
|
2022-04-21 04:57:21 +02:00
|
|
|
edit->set_text(vformat(TTR("(Nil) %s"), array_type_name));
|
2018-06-18 21:24:31 +02:00
|
|
|
edit->set_pressed(false);
|
2022-04-01 03:10:46 +02:00
|
|
|
if (container) {
|
2020-04-02 01:20:12 +02:00
|
|
|
set_bottom_editor(nullptr);
|
2022-04-01 03:10:46 +02:00
|
|
|
memdelete(container);
|
2022-03-15 08:51:58 +01:00
|
|
|
button_add_item = nullptr;
|
2022-04-01 03:10:46 +02:00
|
|
|
container = nullptr;
|
2018-06-18 21:24:31 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-11-27 08:56:53 +01:00
|
|
|
object->set_array(array);
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
int size = array.call("size");
|
2022-02-06 01:11:15 +01:00
|
|
|
int max_page = MAX(0, size - 1) / page_length;
|
2024-02-23 20:41:32 +01:00
|
|
|
if (page_index > max_page) {
|
|
|
|
_page_changed(max_page);
|
|
|
|
}
|
2021-03-29 18:45:42 +02:00
|
|
|
|
2022-04-21 04:57:21 +02:00
|
|
|
edit->set_text(vformat(TTR("%s (size %s)"), array_type_name, itos(size)));
|
2018-05-19 21:09:38 +02:00
|
|
|
|
|
|
|
bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
|
|
|
|
if (edit->is_pressed() != unfolded) {
|
|
|
|
edit->set_pressed(unfolded);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unfolded) {
|
|
|
|
updating = true;
|
|
|
|
|
2022-04-01 03:10:46 +02:00
|
|
|
if (!container) {
|
|
|
|
container = memnew(MarginContainer);
|
|
|
|
container->set_theme_type_variation("MarginContainer4px");
|
2023-08-25 02:11:12 +02:00
|
|
|
container->set_mouse_filter(MOUSE_FILTER_STOP);
|
2022-04-01 03:10:46 +02:00
|
|
|
add_child(container);
|
|
|
|
set_bottom_editor(container);
|
|
|
|
|
|
|
|
VBoxContainer *vbox = memnew(VBoxContainer);
|
|
|
|
container->add_child(vbox);
|
2021-03-29 18:45:42 +02:00
|
|
|
|
|
|
|
HBoxContainer *hbox = memnew(HBoxContainer);
|
|
|
|
vbox->add_child(hbox);
|
|
|
|
|
2022-09-29 11:53:28 +02:00
|
|
|
Label *size_label = memnew(Label(TTR("Size:")));
|
|
|
|
size_label->set_h_size_flags(SIZE_EXPAND_FILL);
|
|
|
|
hbox->add_child(size_label);
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
size_slider = memnew(EditorSpinSlider);
|
|
|
|
size_slider->set_step(1);
|
2023-05-19 22:23:21 +02:00
|
|
|
size_slider->set_max(INT32_MAX);
|
2021-03-29 18:45:42 +02:00
|
|
|
size_slider->set_h_size_flags(SIZE_EXPAND_FILL);
|
2022-09-06 17:59:36 +02:00
|
|
|
size_slider->set_read_only(is_read_only());
|
2021-03-29 18:45:42 +02:00
|
|
|
size_slider->connect("value_changed", callable_mp(this, &EditorPropertyArray::_length_changed));
|
|
|
|
hbox->add_child(size_slider);
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2022-02-06 01:11:15 +01:00
|
|
|
property_vbox = memnew(VBoxContainer);
|
|
|
|
property_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
|
|
|
|
vbox->add_child(property_vbox);
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2022-05-25 01:38:13 +02:00
|
|
|
button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Element"));
|
2023-08-13 02:33:39 +02:00
|
|
|
button_add_item->set_icon(get_editor_theme_icon(SNAME("Add")));
|
2022-02-06 01:11:15 +01:00
|
|
|
button_add_item->connect(SNAME("pressed"), callable_mp(this, &EditorPropertyArray::_add_element));
|
2022-09-06 17:59:36 +02:00
|
|
|
button_add_item->set_disabled(is_read_only());
|
2022-02-06 01:11:15 +01:00
|
|
|
vbox->add_child(button_add_item);
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2022-02-06 01:11:15 +01:00
|
|
|
paginator = memnew(EditorPaginator);
|
|
|
|
paginator->connect("page_changed", callable_mp(this, &EditorPropertyArray::_page_changed));
|
|
|
|
vbox->add_child(paginator);
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
for (int i = 0; i < page_length; i++) {
|
|
|
|
create_new_property_slot();
|
2021-03-29 18:45:42 +02:00
|
|
|
}
|
|
|
|
}
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
size_slider->set_value(size);
|
2022-02-06 01:11:15 +01:00
|
|
|
property_vbox->set_visible(size > 0);
|
|
|
|
button_add_item->set_visible(page_index == max_page);
|
|
|
|
paginator->update(page_index, max_page);
|
|
|
|
paginator->set_visible(max_page > 0);
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
for (Slot &slot : slots) {
|
|
|
|
bool slot_visible = &slot != &reorder_slot && slot.index < size;
|
|
|
|
slot.container->set_visible(slot_visible);
|
|
|
|
// If not visible no need to update it
|
|
|
|
if (!slot_visible) {
|
2021-03-29 18:45:42 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
int idx = slot.index;
|
|
|
|
Variant::Type value_type = subtype;
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
if (value_type == Variant::NIL) {
|
|
|
|
value_type = array.get(idx).get_type();
|
2018-12-03 04:41:18 +01:00
|
|
|
}
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
// Check if the editor property needs to be updated.
|
|
|
|
bool value_as_id = Object::cast_to<EncodedObjectAsID>(array.get(idx));
|
|
|
|
if (value_type != slot.type || (value_type == Variant::OBJECT && (value_as_id != slot.as_id))) {
|
|
|
|
slot.as_id = value_as_id;
|
|
|
|
slot.type = value_type;
|
|
|
|
EditorProperty *new_prop = nullptr;
|
|
|
|
if (value_type == Variant::OBJECT && value_as_id) {
|
|
|
|
EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
|
|
|
|
editor->setup("Object");
|
|
|
|
new_prop = editor;
|
|
|
|
} else {
|
|
|
|
new_prop = EditorInspector::instantiate_property_editor(nullptr, value_type, "", subtype_hint, subtype_hint_string, PROPERTY_USAGE_NONE);
|
|
|
|
}
|
|
|
|
new_prop->set_selectable(false);
|
|
|
|
new_prop->set_use_folding(is_using_folding());
|
|
|
|
new_prop->connect(SNAME("property_changed"), callable_mp(this, &EditorPropertyArray::_property_changed));
|
|
|
|
new_prop->connect(SNAME("object_id_selected"), callable_mp(this, &EditorPropertyArray::_object_id_selected));
|
|
|
|
new_prop->set_h_size_flags(SIZE_EXPAND_FILL);
|
|
|
|
new_prop->set_read_only(is_read_only());
|
2024-01-13 09:20:38 +01:00
|
|
|
slot.prop->add_sibling(new_prop, false);
|
|
|
|
slot.prop->queue_free();
|
2023-11-23 10:16:53 +01:00
|
|
|
slot.prop = new_prop;
|
|
|
|
slot.set_index(idx);
|
2018-05-19 21:09:38 +02:00
|
|
|
}
|
2023-11-23 10:16:53 +01:00
|
|
|
slot.prop->update_property();
|
2021-03-29 18:45:42 +02:00
|
|
|
}
|
|
|
|
|
2018-05-19 21:09:38 +02:00
|
|
|
updating = false;
|
|
|
|
|
|
|
|
} else {
|
2022-04-01 03:10:46 +02:00
|
|
|
if (container) {
|
2020-04-02 01:20:12 +02:00
|
|
|
set_bottom_editor(nullptr);
|
2022-04-01 03:10:46 +02:00
|
|
|
memdelete(container);
|
2022-03-15 08:51:58 +01:00
|
|
|
button_add_item = nullptr;
|
2022-04-01 03:10:46 +02:00
|
|
|
container = nullptr;
|
2023-11-23 10:16:53 +01:00
|
|
|
slots.clear();
|
2018-05-19 21:09:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
void EditorPropertyArray::_remove_pressed(int p_slot_index) {
|
2022-11-27 08:56:53 +01:00
|
|
|
Variant array = object->get_array().duplicate();
|
2023-11-23 10:16:53 +01:00
|
|
|
array.call("remove_at", slots[p_slot_index].index);
|
2019-06-10 16:37:41 +02:00
|
|
|
|
|
|
|
emit_changed(get_edited_property(), array, "", false);
|
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
2020-04-16 16:25:27 +02:00
|
|
|
void EditorPropertyArray::_button_draw() {
|
|
|
|
if (dropping) {
|
2023-08-13 02:33:39 +02:00
|
|
|
Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));
|
2020-04-16 16:25:27 +02:00
|
|
|
edit->draw_rect(Rect2(Point2(), edit->get_size()), color, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EditorPropertyArray::_is_drop_valid(const Dictionary &p_drag_data) const {
|
2022-09-06 17:59:36 +02:00
|
|
|
if (is_read_only()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-04-16 16:25:27 +02:00
|
|
|
String allowed_type = Variant::get_type_name(subtype);
|
|
|
|
|
2021-07-24 15:12:34 +02:00
|
|
|
// When the subtype is of type Object, an additional subtype may be specified in the hint string
|
|
|
|
// (e.g. Resource, Texture2D, ShaderMaterial, etc). We want the allowed type to be that, not just "Object".
|
2021-12-09 10:42:46 +01:00
|
|
|
if (subtype == Variant::OBJECT && !subtype_hint_string.is_empty()) {
|
2021-07-24 15:12:34 +02:00
|
|
|
allowed_type = subtype_hint_string;
|
|
|
|
}
|
|
|
|
|
2020-04-16 16:25:27 +02:00
|
|
|
Dictionary drag_data = p_drag_data;
|
|
|
|
|
|
|
|
if (drag_data.has("type") && String(drag_data["type"]) == "files") {
|
|
|
|
Vector<String> files = drag_data["files"];
|
|
|
|
|
|
|
|
for (int i = 0; i < files.size(); i++) {
|
2023-11-18 23:40:56 +01:00
|
|
|
const String &file = files[i];
|
2020-04-16 16:25:27 +02:00
|
|
|
String ftype = EditorFileSystem::get_singleton()->get_file_type(file);
|
|
|
|
|
|
|
|
for (int j = 0; j < allowed_type.get_slice_count(","); j++) {
|
|
|
|
String at = allowed_type.get_slice(",", j).strip_edges();
|
2021-03-29 18:45:42 +02:00
|
|
|
// Fail if one of the files is not of allowed type.
|
2020-04-16 16:25:27 +02:00
|
|
|
if (!ClassDB::is_parent_class(ftype, at)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
// If no files fail, drop is valid.
|
2020-04-16 16:25:27 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EditorPropertyArray::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
|
|
|
|
return _is_drop_valid(p_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyArray::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
|
|
|
|
ERR_FAIL_COND(!_is_drop_valid(p_data));
|
|
|
|
|
|
|
|
Dictionary drag_data = p_data;
|
|
|
|
|
|
|
|
if (drag_data.has("type") && String(drag_data["type"]) == "files") {
|
|
|
|
Vector<String> files = drag_data["files"];
|
|
|
|
|
|
|
|
Variant array = object->get_array();
|
|
|
|
|
2022-11-01 15:29:38 +01:00
|
|
|
// Handle the case where array is not initialized yet.
|
2020-04-16 16:25:27 +02:00
|
|
|
if (!array.is_array()) {
|
2022-11-27 08:56:53 +01:00
|
|
|
initialize_array(array);
|
|
|
|
} else {
|
|
|
|
array = array.duplicate();
|
2020-04-16 16:25:27 +02:00
|
|
|
}
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
// Loop the file array and add to existing array.
|
2020-04-16 16:25:27 +02:00
|
|
|
for (int i = 0; i < files.size(); i++) {
|
2023-11-18 23:40:56 +01:00
|
|
|
const String &file = files[i];
|
2020-04-16 16:25:27 +02:00
|
|
|
|
2022-05-03 01:43:50 +02:00
|
|
|
Ref<Resource> res = ResourceLoader::load(file);
|
2020-04-16 16:25:27 +02:00
|
|
|
if (res.is_valid()) {
|
|
|
|
array.call("push_back", res);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
emit_changed(get_edited_property(), array, "", false);
|
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-10 16:37:41 +02:00
|
|
|
void EditorPropertyArray::_notification(int p_what) {
|
2022-02-16 00:52:32 +01:00
|
|
|
switch (p_what) {
|
2022-08-29 11:04:31 +02:00
|
|
|
case NOTIFICATION_THEME_CHANGED:
|
|
|
|
case NOTIFICATION_ENTER_TREE: {
|
2022-02-16 00:52:32 +01:00
|
|
|
change_type->clear();
|
|
|
|
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
2022-05-05 17:04:50 +02:00
|
|
|
if (i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) {
|
|
|
|
// These types can't be constructed or serialized properly, so skip them.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-02-16 00:52:32 +01:00
|
|
|
String type = Variant::get_type_name(Variant::Type(i));
|
2023-08-13 02:33:39 +02:00
|
|
|
change_type->add_icon_item(get_editor_theme_icon(type), type, i);
|
2022-02-16 00:52:32 +01:00
|
|
|
}
|
|
|
|
change_type->add_separator();
|
2023-08-13 02:33:39 +02:00
|
|
|
change_type->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Remove Item"), Variant::VARIANT_MAX);
|
2022-02-06 01:11:15 +01:00
|
|
|
|
2022-10-24 15:59:18 +02:00
|
|
|
if (button_add_item) {
|
2023-08-13 02:33:39 +02:00
|
|
|
button_add_item->set_icon(get_editor_theme_icon(SNAME("Add")));
|
2022-02-16 00:52:32 +01:00
|
|
|
}
|
|
|
|
} break;
|
2022-01-25 01:16:05 +01:00
|
|
|
|
2022-02-16 00:52:32 +01:00
|
|
|
case NOTIFICATION_DRAG_BEGIN: {
|
|
|
|
if (is_visible_in_tree()) {
|
|
|
|
if (_is_drop_valid(get_viewport()->gui_get_drag_data())) {
|
|
|
|
dropping = true;
|
2022-08-13 23:21:24 +02:00
|
|
|
edit->queue_redraw();
|
2022-02-16 00:52:32 +01:00
|
|
|
}
|
2020-04-16 16:25:27 +02:00
|
|
|
}
|
2022-02-16 00:52:32 +01:00
|
|
|
} break;
|
2020-04-16 16:25:27 +02:00
|
|
|
|
2022-02-16 00:52:32 +01:00
|
|
|
case NOTIFICATION_DRAG_END: {
|
|
|
|
if (dropping) {
|
|
|
|
dropping = false;
|
2022-08-13 23:21:24 +02:00
|
|
|
edit->queue_redraw();
|
2022-02-16 00:52:32 +01:00
|
|
|
}
|
|
|
|
} break;
|
2020-04-16 16:25:27 +02:00
|
|
|
}
|
2018-05-19 21:09:38 +02:00
|
|
|
}
|
2020-04-16 16:25:27 +02:00
|
|
|
|
2018-05-19 21:09:38 +02:00
|
|
|
void EditorPropertyArray::_edit_pressed() {
|
2023-05-26 09:13:24 +02:00
|
|
|
Variant array = get_edited_property_value();
|
2022-11-27 08:56:53 +01:00
|
|
|
if (!array.is_array() && edit->is_pressed()) {
|
|
|
|
initialize_array(array);
|
2018-06-18 21:24:31 +02:00
|
|
|
get_edited_object()->set(get_edited_property(), array);
|
|
|
|
}
|
|
|
|
|
2018-05-19 21:09:38 +02:00
|
|
|
get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed());
|
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
2022-02-06 01:11:15 +01:00
|
|
|
void EditorPropertyArray::_page_changed(int p_page) {
|
2020-05-14 16:41:43 +02:00
|
|
|
if (updating) {
|
2018-05-19 21:09:38 +02:00
|
|
|
return;
|
2020-05-14 16:41:43 +02:00
|
|
|
}
|
2021-03-29 18:45:42 +02:00
|
|
|
page_index = p_page;
|
2023-11-23 10:16:53 +01:00
|
|
|
int i = p_page * page_length;
|
|
|
|
|
|
|
|
if (reorder_slot.index < 0) {
|
|
|
|
for (Slot &slot : slots) {
|
|
|
|
slot.set_index(i);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int reorder_from_page = reorder_slot.index / page_length;
|
|
|
|
if (reorder_from_page < p_page) {
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
for (Slot &slot : slots) {
|
|
|
|
if (slot.index != reorder_slot.index) {
|
|
|
|
slot.set_index(i);
|
|
|
|
i++;
|
|
|
|
} else if (i == reorder_slot.index) {
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-05-19 21:09:38 +02:00
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyArray::_length_changed(double p_page) {
|
2020-05-14 16:41:43 +02:00
|
|
|
if (updating) {
|
2018-05-19 21:09:38 +02:00
|
|
|
return;
|
2020-05-14 16:41:43 +02:00
|
|
|
}
|
2018-05-19 21:09:38 +02:00
|
|
|
|
2022-11-27 08:56:53 +01:00
|
|
|
Variant array = object->get_array().duplicate();
|
2018-05-19 21:09:38 +02:00
|
|
|
array.call("resize", int(p_page));
|
|
|
|
|
2019-02-26 13:58:39 +01:00
|
|
|
emit_changed(get_edited_property(), array, "", false);
|
2018-05-19 21:09:38 +02:00
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
2022-02-06 01:11:15 +01:00
|
|
|
void EditorPropertyArray::_add_element() {
|
|
|
|
_length_changed(double(object->get_array().call("size")) + 1.0);
|
|
|
|
}
|
|
|
|
|
2018-12-03 04:41:18 +01:00
|
|
|
void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint_string) {
|
2018-06-18 21:24:31 +02:00
|
|
|
array_type = p_array_type;
|
2018-12-03 04:41:18 +01:00
|
|
|
|
2021-06-25 14:55:26 +02:00
|
|
|
// The format of p_hint_string is:
|
2021-03-29 18:45:42 +02:00
|
|
|
// subType/subTypeHint:nextSubtype ... etc.
|
2023-02-04 13:53:22 +01:00
|
|
|
if (!p_hint_string.is_empty()) {
|
2019-10-31 13:40:58 +01:00
|
|
|
int hint_subtype_separator = p_hint_string.find(":");
|
|
|
|
if (hint_subtype_separator >= 0) {
|
|
|
|
String subtype_string = p_hint_string.substr(0, hint_subtype_separator);
|
2018-12-03 04:41:18 +01:00
|
|
|
int slash_pos = subtype_string.find("/");
|
|
|
|
if (slash_pos >= 0) {
|
|
|
|
subtype_hint = PropertyHint(subtype_string.substr(slash_pos + 1, subtype_string.size() - slash_pos - 1).to_int());
|
|
|
|
subtype_string = subtype_string.substr(0, slash_pos);
|
|
|
|
}
|
|
|
|
|
2019-10-31 13:40:58 +01:00
|
|
|
subtype_hint_string = p_hint_string.substr(hint_subtype_separator + 1, p_hint_string.size() - hint_subtype_separator - 1);
|
2018-12-03 04:41:18 +01:00
|
|
|
subtype = Variant::Type(subtype_string.to_int());
|
|
|
|
}
|
|
|
|
}
|
2018-06-18 21:24:31 +02:00
|
|
|
}
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_event) {
|
2023-11-23 10:16:53 +01:00
|
|
|
if (reorder_slot.index < 0 || is_read_only()) {
|
2021-03-29 18:45:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ref<InputEventMouseMotion> mm = p_event;
|
|
|
|
if (mm.is_valid()) {
|
|
|
|
Variant array = object->get_array();
|
|
|
|
int size = array.call("size");
|
|
|
|
|
2022-05-22 16:17:59 +02:00
|
|
|
// Cumulate the mouse delta, many small changes (dragging slowly) should result in reordering at some point.
|
|
|
|
reorder_mouse_y_delta += mm->get_relative().y;
|
|
|
|
|
|
|
|
// Reordering is done by moving the dragged element by +1/-1 index at a time based on the cumulated mouse delta so if
|
|
|
|
// already at the array bounds make sure to ignore the remaining out of bounds drag (by resetting the cumulated delta).
|
|
|
|
if ((reorder_to_index == 0 && reorder_mouse_y_delta < 0.0f) || (reorder_to_index == size - 1 && reorder_mouse_y_delta > 0.0f)) {
|
|
|
|
reorder_mouse_y_delta = 0.0f;
|
2021-03-29 18:45:42 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
float required_y_distance = 20.0f * EDSCALE;
|
|
|
|
if (ABS(reorder_mouse_y_delta) > required_y_distance) {
|
|
|
|
int direction = reorder_mouse_y_delta > 0.0f ? 1 : -1;
|
|
|
|
reorder_mouse_y_delta -= required_y_distance * direction;
|
|
|
|
|
|
|
|
reorder_to_index += direction;
|
2023-11-23 10:16:53 +01:00
|
|
|
|
|
|
|
property_vbox->move_child(reorder_slot.container, reorder_to_index % page_length);
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
if ((direction < 0 && reorder_to_index % page_length == page_length - 1) || (direction > 0 && reorder_to_index % page_length == 0)) {
|
|
|
|
// Automatically move to the next/previous page.
|
2022-02-06 01:11:15 +01:00
|
|
|
_page_changed(page_index + direction);
|
2021-03-29 18:45:42 +02:00
|
|
|
}
|
|
|
|
// Ensure the moving element is visible.
|
2023-11-23 10:16:53 +01:00
|
|
|
InspectorDock::get_inspector_singleton()->ensure_control_visible(reorder_slot.container);
|
2021-03-29 18:45:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
void EditorPropertyArray::_reorder_button_down(int p_slot_index) {
|
2022-09-06 17:59:36 +02:00
|
|
|
if (is_read_only()) {
|
|
|
|
return;
|
|
|
|
}
|
2023-11-23 10:16:53 +01:00
|
|
|
reorder_slot = slots[p_slot_index];
|
|
|
|
reorder_to_index = reorder_slot.index;
|
2021-03-29 18:45:42 +02:00
|
|
|
// Ideally it'd to be able to show the mouse but I had issues with
|
|
|
|
// Control's `mouse_exit()`/`mouse_entered()` signals not getting called.
|
|
|
|
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyArray::_reorder_button_up() {
|
2022-09-06 17:59:36 +02:00
|
|
|
if (is_read_only()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
if (reorder_slot.index != reorder_to_index) {
|
2021-03-29 18:45:42 +02:00
|
|
|
// Move the element.
|
2022-11-27 08:56:53 +01:00
|
|
|
Variant array = object->get_array().duplicate();
|
2021-03-29 18:45:42 +02:00
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
property_vbox->move_child(reorder_slot.container, reorder_slot.index % page_length);
|
|
|
|
Variant value_to_move = array.get(reorder_slot.index);
|
|
|
|
array.call("remove_at", reorder_slot.index);
|
2021-03-29 18:45:42 +02:00
|
|
|
array.call("insert", reorder_to_index, value_to_move);
|
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
reorder_slot.index = reorder_slot.index % page_length + page_index * page_length;
|
2021-03-29 18:45:42 +02:00
|
|
|
emit_changed(get_edited_property(), array, "", false);
|
|
|
|
}
|
|
|
|
|
|
|
|
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
|
2021-08-10 15:05:36 +02:00
|
|
|
|
2023-11-23 10:16:53 +01:00
|
|
|
ERR_FAIL_NULL(reorder_slot.reorder_button);
|
|
|
|
reorder_slot.reorder_button->warp_mouse(reorder_slot.reorder_button->get_size() / 2.0f);
|
|
|
|
reorder_to_index = -1;
|
|
|
|
reorder_mouse_y_delta = 0.0f;
|
|
|
|
reorder_slot = Slot();
|
|
|
|
_page_changed(page_index);
|
2021-03-29 18:45:42 +02:00
|
|
|
}
|
|
|
|
|
2018-05-19 21:09:38 +02:00
|
|
|
void EditorPropertyArray::_bind_methods() {
|
|
|
|
}
|
|
|
|
|
|
|
|
EditorPropertyArray::EditorPropertyArray() {
|
2021-06-18 00:03:09 +02:00
|
|
|
object.instantiate();
|
2021-03-29 18:45:42 +02:00
|
|
|
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
|
2022-02-06 01:11:15 +01:00
|
|
|
|
2018-05-19 21:09:38 +02:00
|
|
|
edit = memnew(Button);
|
|
|
|
edit->set_h_size_flags(SIZE_EXPAND_FILL);
|
|
|
|
edit->set_clip_text(true);
|
2020-02-21 18:28:45 +01:00
|
|
|
edit->connect("pressed", callable_mp(this, &EditorPropertyArray::_edit_pressed));
|
2018-05-19 21:09:38 +02:00
|
|
|
edit->set_toggle_mode(true);
|
2023-01-14 03:37:19 +01:00
|
|
|
SET_DRAG_FORWARDING_CD(edit, EditorPropertyArray);
|
2020-04-16 16:25:27 +02:00
|
|
|
edit->connect("draw", callable_mp(this, &EditorPropertyArray::_button_draw));
|
2018-05-19 21:09:38 +02:00
|
|
|
add_child(edit);
|
|
|
|
add_focusable(edit);
|
2022-02-06 01:11:15 +01:00
|
|
|
|
2018-05-19 21:09:38 +02:00
|
|
|
change_type = memnew(PopupMenu);
|
|
|
|
add_child(change_type);
|
2020-02-21 18:28:45 +01:00
|
|
|
change_type->connect("id_pressed", callable_mp(this, &EditorPropertyArray::_change_type_menu));
|
2021-03-29 18:45:42 +02:00
|
|
|
changing_type_index = -1;
|
2018-12-03 04:41:18 +01:00
|
|
|
|
|
|
|
subtype = Variant::NIL;
|
|
|
|
subtype_hint = PROPERTY_HINT_NONE;
|
|
|
|
subtype_hint_string = "";
|
2018-05-19 21:09:38 +02:00
|
|
|
}
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
///////////////////// DICTIONARY ///////////////////////////
|
|
|
|
|
2020-03-05 15:33:01 +01:00
|
|
|
void EditorPropertyDictionary::_property_changed(const String &p_property, Variant p_value, const String &p_name, bool p_changing) {
|
2023-10-31 09:36:29 +01:00
|
|
|
if (p_value.get_type() == Variant::OBJECT && p_value.is_null()) {
|
|
|
|
p_value = Variant(); // `EditorResourcePicker` resets to `Ref<Resource>()`. See GH-82716.
|
|
|
|
}
|
|
|
|
|
2020-03-03 10:46:03 +01:00
|
|
|
if (p_property == "new_item_key") {
|
2018-05-19 22:57:44 +02:00
|
|
|
object->set_new_item_key(p_value);
|
2020-03-03 10:46:03 +01:00
|
|
|
} else if (p_property == "new_item_value") {
|
2018-05-19 22:57:44 +02:00
|
|
|
object->set_new_item_value(p_value);
|
2020-03-03 10:46:03 +01:00
|
|
|
} else if (p_property.begins_with("indices")) {
|
2021-03-29 18:45:42 +02:00
|
|
|
int index = p_property.get_slice("/", 1).to_int();
|
2022-11-27 08:56:53 +01:00
|
|
|
|
|
|
|
Dictionary dict = object->get_dict().duplicate();
|
2021-03-29 18:45:42 +02:00
|
|
|
Variant key = dict.get_key_at_index(index);
|
2018-05-19 22:57:44 +02:00
|
|
|
dict[key] = p_value;
|
|
|
|
|
|
|
|
object->set_dict(dict);
|
2022-11-27 08:56:53 +01:00
|
|
|
emit_changed(get_edited_property(), dict, "", true);
|
2018-05-19 22:57:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyDictionary::_change_type(Object *p_button, int p_index) {
|
|
|
|
Button *button = Object::cast_to<Button>(p_button);
|
|
|
|
|
2020-03-12 13:37:40 +01:00
|
|
|
Rect2 rect = button->get_screen_rect();
|
2023-06-05 05:38:09 +02:00
|
|
|
change_type->set_item_disabled(change_type->get_item_index(Variant::VARIANT_MAX), p_index < 0);
|
2022-03-06 00:57:42 +01:00
|
|
|
change_type->reset_size();
|
2021-09-22 10:24:45 +02:00
|
|
|
change_type->set_position(rect.get_end() - Vector2(change_type->get_contents_minimum_size().x, 0));
|
2018-05-19 22:57:44 +02:00
|
|
|
change_type->popup();
|
2021-03-29 18:45:42 +02:00
|
|
|
changing_type_index = p_index;
|
2018-05-19 22:57:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyDictionary::_add_key_value() {
|
2018-07-24 02:42:31 +02:00
|
|
|
// Do not allow nil as valid key. I experienced errors with this
|
|
|
|
if (object->get_new_item_key().get_type() == Variant::NIL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-11-27 08:56:53 +01:00
|
|
|
Dictionary dict = object->get_dict().duplicate();
|
2024-02-21 17:44:05 +01:00
|
|
|
Variant new_key = object->get_new_item_key();
|
|
|
|
Variant new_value = object->get_new_item_value();
|
|
|
|
dict[new_key] = new_value;
|
|
|
|
|
|
|
|
Variant::Type type = new_key.get_type();
|
|
|
|
new_key.zero();
|
|
|
|
VariantInternal::initialize(&new_key, type);
|
|
|
|
object->set_new_item_key(new_key);
|
|
|
|
|
|
|
|
type = new_value.get_type();
|
|
|
|
new_value.zero();
|
|
|
|
VariantInternal::initialize(&new_value, type);
|
|
|
|
object->set_new_item_value(new_value);
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2019-01-18 17:01:24 +01:00
|
|
|
emit_changed(get_edited_property(), dict, "", false);
|
2018-05-19 22:57:44 +02:00
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyDictionary::_change_type_menu(int p_index) {
|
2021-03-29 18:45:42 +02:00
|
|
|
if (changing_type_index < 0) {
|
2018-05-19 22:57:44 +02:00
|
|
|
Variant value;
|
2022-11-27 08:56:53 +01:00
|
|
|
VariantInternal::initialize(&value, Variant::Type(p_index));
|
2021-03-29 18:45:42 +02:00
|
|
|
if (changing_type_index == -1) {
|
2018-05-19 22:57:44 +02:00
|
|
|
object->set_new_item_key(value);
|
|
|
|
} else {
|
|
|
|
object->set_new_item_value(value);
|
|
|
|
}
|
|
|
|
update_property();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-11-27 08:56:53 +01:00
|
|
|
Dictionary dict = object->get_dict().duplicate();
|
2018-05-19 22:57:44 +02:00
|
|
|
if (p_index < Variant::VARIANT_MAX) {
|
|
|
|
Variant value;
|
2022-11-27 08:56:53 +01:00
|
|
|
VariantInternal::initialize(&value, Variant::Type(p_index));
|
2021-03-29 18:45:42 +02:00
|
|
|
Variant key = dict.get_key_at_index(changing_type_index);
|
2018-05-19 22:57:44 +02:00
|
|
|
dict[key] = value;
|
|
|
|
} else {
|
2021-03-29 18:45:42 +02:00
|
|
|
Variant key = dict.get_key_at_index(changing_type_index);
|
2018-05-19 22:57:44 +02:00
|
|
|
dict.erase(key);
|
|
|
|
}
|
|
|
|
|
2019-01-18 17:01:24 +01:00
|
|
|
emit_changed(get_edited_property(), dict, "", false);
|
2018-05-19 22:57:44 +02:00
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
2022-12-24 23:46:57 +01:00
|
|
|
void EditorPropertyDictionary::setup(PropertyHint p_hint) {
|
|
|
|
property_hint = p_hint;
|
|
|
|
}
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
void EditorPropertyDictionary::update_property() {
|
2023-05-26 09:13:24 +02:00
|
|
|
Variant updated_val = get_edited_property_value();
|
2018-07-24 02:42:31 +02:00
|
|
|
|
|
|
|
if (updated_val.get_type() == Variant::NIL) {
|
2022-03-04 14:04:59 +01:00
|
|
|
edit->set_text(TTR("Dictionary (Nil)")); // This provides symmetry with the array property.
|
2018-07-24 02:42:31 +02:00
|
|
|
edit->set_pressed(false);
|
2022-04-01 03:10:46 +02:00
|
|
|
if (container) {
|
2020-04-02 01:20:12 +02:00
|
|
|
set_bottom_editor(nullptr);
|
2022-04-01 03:10:46 +02:00
|
|
|
memdelete(container);
|
2022-03-15 08:51:58 +01:00
|
|
|
button_add_item = nullptr;
|
2022-04-01 03:10:46 +02:00
|
|
|
container = nullptr;
|
2018-07-24 02:42:31 +02:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Dictionary dict = updated_val;
|
2022-11-27 08:56:53 +01:00
|
|
|
object->set_dict(updated_val);
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2022-03-04 14:04:59 +01:00
|
|
|
edit->set_text(vformat(TTR("Dictionary (size %d)"), dict.size()));
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
|
|
|
|
if (edit->is_pressed() != unfolded) {
|
|
|
|
edit->set_pressed(unfolded);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unfolded) {
|
|
|
|
updating = true;
|
|
|
|
|
2022-04-01 03:10:46 +02:00
|
|
|
if (!container) {
|
|
|
|
container = memnew(MarginContainer);
|
|
|
|
container->set_theme_type_variation("MarginContainer4px");
|
2024-02-26 06:53:40 +01:00
|
|
|
container->set_mouse_filter(MOUSE_FILTER_STOP);
|
2022-04-01 03:10:46 +02:00
|
|
|
add_child(container);
|
|
|
|
set_bottom_editor(container);
|
|
|
|
|
|
|
|
VBoxContainer *vbox = memnew(VBoxContainer);
|
|
|
|
container->add_child(vbox);
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2022-02-06 01:11:15 +01:00
|
|
|
property_vbox = memnew(VBoxContainer);
|
|
|
|
property_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
|
|
|
|
vbox->add_child(property_vbox);
|
|
|
|
|
|
|
|
paginator = memnew(EditorPaginator);
|
|
|
|
paginator->connect("page_changed", callable_mp(this, &EditorPropertyDictionary::_page_changed));
|
|
|
|
vbox->add_child(paginator);
|
2018-05-19 22:57:44 +02:00
|
|
|
} else {
|
2019-05-19 12:34:40 +02:00
|
|
|
// Queue children for deletion, deleting immediately might cause errors.
|
2022-02-06 01:11:15 +01:00
|
|
|
for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
|
2022-10-24 23:07:02 +02:00
|
|
|
property_vbox->get_child(i)->queue_free();
|
2018-05-19 22:57:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
int size = dict.size();
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2022-02-06 01:11:15 +01:00
|
|
|
int max_page = MAX(0, size - 1) / page_length;
|
|
|
|
page_index = MIN(page_index, max_page);
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2022-02-06 01:11:15 +01:00
|
|
|
paginator->update(page_index, max_page);
|
|
|
|
paginator->set_visible(max_page > 0);
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
int offset = page_index * page_length;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
int amount = MIN(size - offset, page_length);
|
2022-02-06 01:11:15 +01:00
|
|
|
int total_amount = page_index == max_page ? amount + 2 : amount; // For the "Add Key/Value Pair" box on last page.
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2020-04-02 01:20:12 +02:00
|
|
|
VBoxContainer *add_vbox = nullptr;
|
2021-10-29 14:24:28 +02:00
|
|
|
double default_float_step = EDITOR_GET("interface/inspector/default_float_step");
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2022-02-06 01:11:15 +01:00
|
|
|
for (int i = 0; i < total_amount; i++) {
|
2018-05-19 22:57:44 +02:00
|
|
|
String prop_name;
|
|
|
|
Variant key;
|
|
|
|
Variant value;
|
|
|
|
|
|
|
|
if (i < amount) {
|
|
|
|
prop_name = "indices/" + itos(i + offset);
|
|
|
|
key = dict.get_key_at_index(i + offset);
|
|
|
|
value = dict.get_value_at_index(i + offset);
|
|
|
|
} else if (i == amount) {
|
|
|
|
prop_name = "new_item_key";
|
|
|
|
value = object->get_new_item_key();
|
|
|
|
} else if (i == amount + 1) {
|
|
|
|
prop_name = "new_item_value";
|
|
|
|
value = object->get_new_item_value();
|
|
|
|
}
|
|
|
|
|
2020-04-02 01:20:12 +02:00
|
|
|
EditorProperty *prop = nullptr;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
switch (value.get_type()) {
|
|
|
|
case Variant::NIL: {
|
|
|
|
prop = memnew(EditorPropertyNil);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
// Atomic types.
|
2018-05-19 22:57:44 +02:00
|
|
|
case Variant::BOOL: {
|
|
|
|
prop = memnew(EditorPropertyCheck);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case Variant::INT: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyInteger *editor = memnew(EditorPropertyInteger);
|
2022-12-08 13:38:01 +01:00
|
|
|
editor->setup(-100000, 100000, 1, false, true, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
} break;
|
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 19:20:53 +01:00
|
|
|
case Variant::FLOAT: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyFloat *editor = memnew(EditorPropertyFloat);
|
2021-10-29 14:24:28 +02:00
|
|
|
editor->setup(-100000, 100000, default_float_step, true, false, true, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
|
|
|
case Variant::STRING: {
|
2022-12-24 23:46:57 +01:00
|
|
|
if (i != amount && property_hint == PROPERTY_HINT_MULTILINE_TEXT) {
|
|
|
|
// If this is NOT the new key field and there's a multiline hint,
|
|
|
|
// show the field as multiline
|
|
|
|
prop = memnew(EditorPropertyMultilineText);
|
|
|
|
} else {
|
|
|
|
prop = memnew(EditorPropertyText);
|
|
|
|
}
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
} break;
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
// Math types.
|
2018-05-19 22:57:44 +02:00
|
|
|
case Variant::VECTOR2: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyVector2 *editor = memnew(EditorPropertyVector2);
|
2021-10-29 14:24:28 +02:00
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2020-04-17 04:52:00 +02:00
|
|
|
} break;
|
|
|
|
case Variant::VECTOR2I: {
|
|
|
|
EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i);
|
2022-12-08 13:38:01 +01:00
|
|
|
editor->setup(-100000, 100000);
|
2020-04-17 04:52:00 +02:00
|
|
|
prop = editor;
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
|
|
|
case Variant::RECT2: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyRect2 *editor = memnew(EditorPropertyRect2);
|
2021-10-29 14:24:28 +02:00
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2020-04-17 04:52:00 +02:00
|
|
|
} break;
|
|
|
|
case Variant::RECT2I: {
|
|
|
|
EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i);
|
2022-12-08 13:38:01 +01:00
|
|
|
editor->setup(-100000, 100000);
|
2020-04-17 04:52:00 +02:00
|
|
|
prop = editor;
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
|
|
|
case Variant::VECTOR3: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyVector3 *editor = memnew(EditorPropertyVector3);
|
2021-10-29 14:24:28 +02:00
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2020-04-17 04:52:00 +02:00
|
|
|
} break;
|
|
|
|
case Variant::VECTOR3I: {
|
|
|
|
EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i);
|
2022-12-08 13:38:01 +01:00
|
|
|
editor->setup(-100000, 100000);
|
2020-04-17 04:52:00 +02:00
|
|
|
prop = editor;
|
|
|
|
|
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
|
|
|
} break;
|
|
|
|
case Variant::VECTOR4: {
|
|
|
|
EditorPropertyVector4 *editor = memnew(EditorPropertyVector4);
|
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
|
|
|
prop = editor;
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case Variant::VECTOR4I: {
|
|
|
|
EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);
|
2022-12-08 13:38:01 +01:00
|
|
|
editor->setup(-100000, 100000);
|
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
|
|
|
prop = editor;
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
|
|
|
case Variant::TRANSFORM2D: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D);
|
2021-10-29 14:24:28 +02:00
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
} break;
|
|
|
|
case Variant::PLANE: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyPlane *editor = memnew(EditorPropertyPlane);
|
2021-10-29 14:24:28 +02:00
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
} break;
|
2021-01-20 08:02:02 +01:00
|
|
|
case Variant::QUATERNION: {
|
|
|
|
EditorPropertyQuaternion *editor = memnew(EditorPropertyQuaternion);
|
2021-10-29 14:24:28 +02:00
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
} break;
|
|
|
|
case Variant::AABB: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyAABB *editor = memnew(EditorPropertyAABB);
|
2021-10-29 14:24:28 +02:00
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
} break;
|
|
|
|
case Variant::BASIS: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyBasis *editor = memnew(EditorPropertyBasis);
|
2021-10-29 14:24:28 +02:00
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
} break;
|
2021-04-28 09:36:08 +02:00
|
|
|
case Variant::TRANSFORM3D: {
|
2021-06-04 03:58:26 +02:00
|
|
|
EditorPropertyTransform3D *editor = memnew(EditorPropertyTransform3D);
|
2021-10-29 14:24:28 +02:00
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
Implement Vector4, Vector4i, Projection
Implement built-in classes Vector4, Vector4i and Projection.
* Two versions of Vector4 (float and integer).
* A Projection class, which is a 4x4 matrix specialized in projection types.
These types have been requested for a long time, but given they were very corner case they were not added before.
Because in Godot 4, reimplementing parts of the rendering engine is now possible, access to these types (heavily used by the rendering code) becomes a necessity.
**Q**: Why Projection and not Matrix4?
**A**: Godot does not use Matrix2, Matrix3, Matrix4x3, etc. naming convention because, within the engine, these types always have a *purpose*. As such, Godot names them: Transform2D, Transform3D or Basis. In this case, this 4x4 matrix is _always_ used as a _Projection_, hence the naming.
2022-07-20 01:11:13 +02:00
|
|
|
} break;
|
|
|
|
case Variant::PROJECTION: {
|
|
|
|
EditorPropertyProjection *editor = memnew(EditorPropertyProjection);
|
|
|
|
editor->setup(-100000, 100000, default_float_step, true);
|
|
|
|
prop = editor;
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
// Miscellaneous types.
|
2018-05-19 22:57:44 +02:00
|
|
|
case Variant::COLOR: {
|
|
|
|
prop = memnew(EditorPropertyColor);
|
|
|
|
|
2020-02-20 22:58:05 +01:00
|
|
|
} break;
|
|
|
|
case Variant::STRING_NAME: {
|
|
|
|
EditorPropertyText *ept = memnew(EditorPropertyText);
|
|
|
|
ept->set_string_name(true);
|
|
|
|
prop = ept;
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
|
|
|
case Variant::NODE_PATH: {
|
|
|
|
prop = memnew(EditorPropertyNodePath);
|
|
|
|
|
|
|
|
} break;
|
2020-11-09 14:53:05 +01:00
|
|
|
case Variant::RID: {
|
2019-06-26 15:08:25 +02:00
|
|
|
prop = memnew(EditorPropertyRID);
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2023-01-20 16:32:16 +01:00
|
|
|
} break;
|
|
|
|
case Variant::SIGNAL: {
|
|
|
|
prop = memnew(EditorPropertySignal);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case Variant::CALLABLE: {
|
|
|
|
prop = memnew(EditorPropertyCallable);
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
|
|
|
case Variant::OBJECT: {
|
2018-11-22 02:10:27 +01:00
|
|
|
if (Object::cast_to<EncodedObjectAsID>(value)) {
|
|
|
|
EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);
|
|
|
|
editor->setup("Object");
|
|
|
|
prop = editor;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
EditorPropertyResource *editor = memnew(EditorPropertyResource);
|
2021-05-19 14:19:07 +02:00
|
|
|
editor->setup(object.ptr(), prop_name, "Resource");
|
2022-06-15 06:23:59 +02:00
|
|
|
editor->set_use_folding(is_using_folding());
|
2018-11-22 02:10:27 +01:00
|
|
|
prop = editor;
|
|
|
|
}
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
} break;
|
|
|
|
case Variant::DICTIONARY: {
|
|
|
|
prop = memnew(EditorPropertyDictionary);
|
|
|
|
|
|
|
|
} break;
|
|
|
|
case Variant::ARRAY: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyArray *editor = memnew(EditorPropertyArray);
|
|
|
|
editor->setup(Variant::ARRAY);
|
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
// Arrays.
|
2020-02-17 22:06:54 +01:00
|
|
|
case Variant::PACKED_BYTE_ARRAY: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyArray *editor = memnew(EditorPropertyArray);
|
2020-02-17 22:06:54 +01:00
|
|
|
editor->setup(Variant::PACKED_BYTE_ARRAY);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 19:20:53 +01:00
|
|
|
case Variant::PACKED_INT32_ARRAY: {
|
|
|
|
EditorPropertyArray *editor = memnew(EditorPropertyArray);
|
|
|
|
editor->setup(Variant::PACKED_INT32_ARRAY);
|
|
|
|
prop = editor;
|
|
|
|
} break;
|
|
|
|
case Variant::PACKED_FLOAT32_ARRAY: {
|
|
|
|
EditorPropertyArray *editor = memnew(EditorPropertyArray);
|
|
|
|
editor->setup(Variant::PACKED_FLOAT32_ARRAY);
|
|
|
|
prop = editor;
|
|
|
|
} break;
|
|
|
|
case Variant::PACKED_INT64_ARRAY: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyArray *editor = memnew(EditorPropertyArray);
|
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 19:20:53 +01:00
|
|
|
editor->setup(Variant::PACKED_INT64_ARRAY);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 19:20:53 +01:00
|
|
|
case Variant::PACKED_FLOAT64_ARRAY: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyArray *editor = memnew(EditorPropertyArray);
|
Variant: Added 64-bit packed arrays, renamed Variant::REAL to FLOAT.
- Renames PackedIntArray to PackedInt32Array.
- Renames PackedFloatArray to PackedFloat32Array.
- Adds PackedInt64Array and PackedFloat64Array.
- Renames Variant::REAL to Variant::FLOAT for consistency.
Packed arrays are for storing large amount of data and creating stuff like
meshes, buffers. textures, etc. Forcing them to be 64 is a huge waste of
memory. That said, many users requested the ability to have 64 bits packed
arrays for their games, so this is just an optional added type.
For Variant, the float datatype is always 64 bits, and exposed as `float`.
We still have `real_t` which is the datatype that can change from 32 to 64
bits depending on a compile flag (not entirely working right now, but that's
the idea). It affects math related datatypes and code only.
Neither Variant nor PackedArray make use of real_t, which is only intended
for math precision, so the term is removed from there to keep only float.
2020-02-24 19:20:53 +01:00
|
|
|
editor->setup(Variant::PACKED_FLOAT64_ARRAY);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
2020-02-17 22:06:54 +01:00
|
|
|
case Variant::PACKED_STRING_ARRAY: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyArray *editor = memnew(EditorPropertyArray);
|
2020-02-17 22:06:54 +01:00
|
|
|
editor->setup(Variant::PACKED_STRING_ARRAY);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
2020-02-17 22:06:54 +01:00
|
|
|
case Variant::PACKED_VECTOR2_ARRAY: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyArray *editor = memnew(EditorPropertyArray);
|
2020-02-17 22:06:54 +01:00
|
|
|
editor->setup(Variant::PACKED_VECTOR2_ARRAY);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
2020-02-17 22:06:54 +01:00
|
|
|
case Variant::PACKED_VECTOR3_ARRAY: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyArray *editor = memnew(EditorPropertyArray);
|
2020-02-17 22:06:54 +01:00
|
|
|
editor->setup(Variant::PACKED_VECTOR3_ARRAY);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
2020-02-17 22:06:54 +01:00
|
|
|
case Variant::PACKED_COLOR_ARRAY: {
|
2018-09-19 23:35:22 +02:00
|
|
|
EditorPropertyArray *editor = memnew(EditorPropertyArray);
|
2020-02-17 22:06:54 +01:00
|
|
|
editor->setup(Variant::PACKED_COLOR_ARRAY);
|
2018-09-19 23:35:22 +02:00
|
|
|
prop = editor;
|
2018-05-19 22:57:44 +02:00
|
|
|
} break;
|
2019-04-09 17:08:36 +02:00
|
|
|
default: {
|
|
|
|
}
|
2018-05-19 22:57:44 +02:00
|
|
|
}
|
|
|
|
|
2023-09-09 17:24:40 +02:00
|
|
|
ERR_FAIL_NULL(prop);
|
2022-09-06 17:59:36 +02:00
|
|
|
|
|
|
|
prop->set_read_only(is_read_only());
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
if (i == amount) {
|
|
|
|
PanelContainer *pc = memnew(PanelContainer);
|
2022-02-06 01:11:15 +01:00
|
|
|
property_vbox->add_child(pc);
|
2023-08-13 02:33:39 +02:00
|
|
|
pc->add_theme_style_override(SNAME("panel"), get_theme_stylebox(SNAME("DictionaryAddItem"), EditorStringName(EditorStyles)));
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
add_vbox = memnew(VBoxContainer);
|
|
|
|
pc->add_child(add_vbox);
|
|
|
|
}
|
|
|
|
prop->set_object_and_property(object.ptr(), prop_name);
|
2018-10-01 17:33:08 +02:00
|
|
|
int change_index = 0;
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
if (i < amount) {
|
|
|
|
String cs = key.get_construct_string();
|
|
|
|
prop->set_label(key.get_construct_string());
|
2022-08-25 12:42:17 +02:00
|
|
|
prop->set_tooltip_text(cs);
|
2018-05-19 22:57:44 +02:00
|
|
|
change_index = i + offset;
|
|
|
|
} else if (i == amount) {
|
|
|
|
prop->set_label(TTR("New Key:"));
|
|
|
|
change_index = -1;
|
|
|
|
} else if (i == amount + 1) {
|
|
|
|
prop->set_label(TTR("New Value:"));
|
|
|
|
change_index = -2;
|
|
|
|
}
|
|
|
|
|
|
|
|
prop->set_selectable(false);
|
2020-03-05 15:33:01 +01:00
|
|
|
prop->connect("property_changed", callable_mp(this, &EditorPropertyDictionary::_property_changed));
|
2020-02-21 18:28:45 +01:00
|
|
|
prop->connect("object_id_selected", callable_mp(this, &EditorPropertyDictionary::_object_id_selected));
|
2018-05-19 22:57:44 +02:00
|
|
|
|
2021-03-29 18:45:42 +02:00
|
|
|
HBoxContainer *hbox = memnew(HBoxContainer);
|
2018-05-19 22:57:44 +02:00
|
|
|
if (add_vbox) {
|
2021-03-29 18:45:42 +02:00
|
|
|
add_vbox->add_child(hbox);
|
2018-05-19 22:57:44 +02:00
|
|
|
} else {
|
2022-02-06 01:11:15 +01:00
|
|
|
property_vbox->add_child(hbox);
|
2018-05-19 22:57:44 +02:00
|
|
|
}
|
2021-03-29 18:45:42 +02:00
|
|
|
hbox->add_child(prop);
|
2018-05-19 22:57:44 +02:00
|
|
|
prop->set_h_size_flags(SIZE_EXPAND_FILL);
|
2022-09-29 11:53:28 +02:00
|
|
|
Button *edit_btn = memnew(Button);
|
2023-08-13 02:33:39 +02:00
|
|
|
edit_btn->set_icon(get_editor_theme_icon(SNAME("Edit")));
|
2022-09-29 11:53:28 +02:00
|
|
|
edit_btn->set_disabled(is_read_only());
|
|
|
|
hbox->add_child(edit_btn);
|
|
|
|
edit_btn->connect("pressed", callable_mp(this, &EditorPropertyDictionary::_change_type).bind(edit_btn, change_index));
|
2018-05-19 22:57:44 +02:00
|
|
|
|
|
|
|
prop->update_property();
|
|
|
|
|
|
|
|
if (i == amount + 1) {
|
2022-05-25 01:38:13 +02:00
|
|
|
button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Key/Value Pair"));
|
2023-08-13 02:33:39 +02:00
|
|
|
button_add_item->set_icon(get_editor_theme_icon(SNAME("Add")));
|
2022-09-06 17:59:36 +02:00
|
|
|
button_add_item->set_disabled(is_read_only());
|
2022-01-25 01:16:05 +01:00
|
|
|
button_add_item->connect("pressed", callable_mp(this, &EditorPropertyDictionary::_add_key_value));
|
|
|
|
add_vbox->add_child(button_add_item);
|
2018-05-19 22:57:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
updating = false;
|
|
|
|
|
|
|
|
} else {
|
2022-04-01 03:10:46 +02:00
|
|
|
if (container) {
|
2020-04-02 01:20:12 +02:00
|
|
|
set_bottom_editor(nullptr);
|
2022-04-01 03:10:46 +02:00
|
|
|
memdelete(container);
|
2022-03-04 14:04:59 +01:00
|
|
|
button_add_item = nullptr;
|
2022-04-01 03:10:46 +02:00
|
|
|
container = nullptr;
|
2018-05-19 22:57:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-03 10:46:03 +01:00
|
|
|
void EditorPropertyDictionary::_object_id_selected(const StringName &p_property, ObjectID p_id) {
|
2021-07-17 23:22:52 +02:00
|
|
|
emit_signal(SNAME("object_id_selected"), p_property, p_id);
|
2018-11-22 02:10:27 +01:00
|
|
|
}
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
void EditorPropertyDictionary::_notification(int p_what) {
|
2022-02-16 00:52:32 +01:00
|
|
|
switch (p_what) {
|
2022-08-29 11:04:31 +02:00
|
|
|
case NOTIFICATION_THEME_CHANGED:
|
|
|
|
case NOTIFICATION_ENTER_TREE: {
|
2022-02-16 00:52:32 +01:00
|
|
|
change_type->clear();
|
|
|
|
for (int i = 0; i < Variant::VARIANT_MAX; i++) {
|
2022-05-05 17:04:50 +02:00
|
|
|
if (i == Variant::CALLABLE || i == Variant::SIGNAL || i == Variant::RID) {
|
|
|
|
// These types can't be constructed or serialized properly, so skip them.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-02-16 00:52:32 +01:00
|
|
|
String type = Variant::get_type_name(Variant::Type(i));
|
2023-08-13 02:33:39 +02:00
|
|
|
change_type->add_icon_item(get_editor_theme_icon(type), type, i);
|
2022-02-16 00:52:32 +01:00
|
|
|
}
|
|
|
|
change_type->add_separator();
|
2023-08-13 02:33:39 +02:00
|
|
|
change_type->add_icon_item(get_editor_theme_icon(SNAME("Remove")), TTR("Remove Item"), Variant::VARIANT_MAX);
|
2022-01-25 01:16:05 +01:00
|
|
|
|
2022-10-24 15:59:18 +02:00
|
|
|
if (button_add_item) {
|
2023-08-13 02:33:39 +02:00
|
|
|
button_add_item->set_icon(get_editor_theme_icon(SNAME("Add")));
|
2022-02-16 00:52:32 +01:00
|
|
|
}
|
|
|
|
} break;
|
2022-01-25 01:16:05 +01:00
|
|
|
}
|
2018-05-19 22:57:44 +02:00
|
|
|
}
|
2018-07-24 02:42:31 +02:00
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
void EditorPropertyDictionary::_edit_pressed() {
|
2023-05-26 09:13:24 +02:00
|
|
|
Variant prop_val = get_edited_property_value();
|
2022-11-27 08:56:53 +01:00
|
|
|
if (prop_val.get_type() == Variant::NIL && edit->is_pressed()) {
|
|
|
|
VariantInternal::initialize(&prop_val, Variant::DICTIONARY);
|
2018-07-24 02:42:31 +02:00
|
|
|
get_edited_object()->set(get_edited_property(), prop_val);
|
|
|
|
}
|
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed());
|
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
2022-02-06 01:11:15 +01:00
|
|
|
void EditorPropertyDictionary::_page_changed(int p_page) {
|
2020-05-14 16:41:43 +02:00
|
|
|
if (updating) {
|
2018-05-19 22:57:44 +02:00
|
|
|
return;
|
2020-05-14 16:41:43 +02:00
|
|
|
}
|
2021-03-29 18:45:42 +02:00
|
|
|
page_index = p_page;
|
2018-05-19 22:57:44 +02:00
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyDictionary::_bind_methods() {
|
|
|
|
}
|
|
|
|
|
|
|
|
EditorPropertyDictionary::EditorPropertyDictionary() {
|
2021-06-18 00:03:09 +02:00
|
|
|
object.instantiate();
|
2021-03-29 18:45:42 +02:00
|
|
|
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
|
2022-02-06 01:11:15 +01:00
|
|
|
|
2018-05-19 22:57:44 +02:00
|
|
|
edit = memnew(Button);
|
|
|
|
edit->set_h_size_flags(SIZE_EXPAND_FILL);
|
|
|
|
edit->set_clip_text(true);
|
2020-02-21 18:28:45 +01:00
|
|
|
edit->connect("pressed", callable_mp(this, &EditorPropertyDictionary::_edit_pressed));
|
2018-05-19 22:57:44 +02:00
|
|
|
edit->set_toggle_mode(true);
|
|
|
|
add_child(edit);
|
|
|
|
add_focusable(edit);
|
2022-02-06 01:11:15 +01:00
|
|
|
|
2022-04-01 03:10:46 +02:00
|
|
|
container = nullptr;
|
2022-01-25 01:16:05 +01:00
|
|
|
button_add_item = nullptr;
|
2022-02-06 01:11:15 +01:00
|
|
|
paginator = nullptr;
|
2018-05-19 22:57:44 +02:00
|
|
|
change_type = memnew(PopupMenu);
|
|
|
|
add_child(change_type);
|
2020-02-21 18:28:45 +01:00
|
|
|
change_type->connect("id_pressed", callable_mp(this, &EditorPropertyDictionary::_change_type_menu));
|
2021-03-29 18:45:42 +02:00
|
|
|
changing_type_index = -1;
|
2018-05-19 22:57:44 +02:00
|
|
|
}
|
2022-03-04 14:04:59 +01:00
|
|
|
|
|
|
|
///////////////////// LOCALIZABLE STRING ///////////////////////////
|
|
|
|
|
2024-02-15 17:25:58 +01:00
|
|
|
void EditorPropertyLocalizableString::_property_changed(const String &p_property, const Variant &p_value, const String &p_name, bool p_changing) {
|
2022-03-04 14:04:59 +01:00
|
|
|
if (p_property.begins_with("indices")) {
|
|
|
|
int index = p_property.get_slice("/", 1).to_int();
|
2022-11-27 08:56:53 +01:00
|
|
|
|
|
|
|
Dictionary dict = object->get_dict().duplicate();
|
2022-03-04 14:04:59 +01:00
|
|
|
Variant key = dict.get_key_at_index(index);
|
|
|
|
dict[key] = p_value;
|
|
|
|
|
|
|
|
object->set_dict(dict);
|
2022-11-27 08:56:53 +01:00
|
|
|
emit_changed(get_edited_property(), dict, "", true);
|
2022-03-04 14:04:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyLocalizableString::_add_locale_popup() {
|
|
|
|
locale_select->popup_locale_dialog();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyLocalizableString::_add_locale(const String &p_locale) {
|
2022-11-27 08:56:53 +01:00
|
|
|
Dictionary dict = object->get_dict().duplicate();
|
2022-03-04 14:04:59 +01:00
|
|
|
object->set_new_item_key(p_locale);
|
|
|
|
object->set_new_item_value(String());
|
|
|
|
dict[object->get_new_item_key()] = object->get_new_item_value();
|
|
|
|
|
|
|
|
emit_changed(get_edited_property(), dict, "", false);
|
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyLocalizableString::_remove_item(Object *p_button, int p_index) {
|
2022-11-27 08:56:53 +01:00
|
|
|
Dictionary dict = object->get_dict().duplicate();
|
2022-03-04 14:04:59 +01:00
|
|
|
|
|
|
|
Variant key = dict.get_key_at_index(p_index);
|
|
|
|
dict.erase(key);
|
|
|
|
|
|
|
|
emit_changed(get_edited_property(), dict, "", false);
|
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyLocalizableString::update_property() {
|
2023-05-26 09:13:24 +02:00
|
|
|
Variant updated_val = get_edited_property_value();
|
2022-03-04 14:04:59 +01:00
|
|
|
|
|
|
|
if (updated_val.get_type() == Variant::NIL) {
|
|
|
|
edit->set_text(TTR("Localizable String (Nil)")); // This provides symmetry with the array property.
|
|
|
|
edit->set_pressed(false);
|
2022-04-01 03:10:46 +02:00
|
|
|
if (container) {
|
2022-03-04 14:04:59 +01:00
|
|
|
set_bottom_editor(nullptr);
|
2022-04-01 03:10:46 +02:00
|
|
|
memdelete(container);
|
2022-03-15 08:51:58 +01:00
|
|
|
button_add_item = nullptr;
|
2022-04-01 03:10:46 +02:00
|
|
|
container = nullptr;
|
2022-03-04 14:04:59 +01:00
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Dictionary dict = updated_val;
|
2022-11-27 08:56:53 +01:00
|
|
|
object->set_dict(dict);
|
2022-03-04 14:04:59 +01:00
|
|
|
|
|
|
|
edit->set_text(vformat(TTR("Localizable String (size %d)"), dict.size()));
|
|
|
|
|
|
|
|
bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());
|
|
|
|
if (edit->is_pressed() != unfolded) {
|
|
|
|
edit->set_pressed(unfolded);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unfolded) {
|
|
|
|
updating = true;
|
|
|
|
|
2022-04-01 03:10:46 +02:00
|
|
|
if (!container) {
|
|
|
|
container = memnew(MarginContainer);
|
|
|
|
container->set_theme_type_variation("MarginContainer4px");
|
|
|
|
add_child(container);
|
|
|
|
set_bottom_editor(container);
|
|
|
|
|
|
|
|
VBoxContainer *vbox = memnew(VBoxContainer);
|
|
|
|
container->add_child(vbox);
|
2022-03-04 14:04:59 +01:00
|
|
|
|
|
|
|
property_vbox = memnew(VBoxContainer);
|
|
|
|
property_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
|
|
|
|
vbox->add_child(property_vbox);
|
|
|
|
|
|
|
|
paginator = memnew(EditorPaginator);
|
|
|
|
paginator->connect("page_changed", callable_mp(this, &EditorPropertyLocalizableString::_page_changed));
|
|
|
|
vbox->add_child(paginator);
|
|
|
|
} else {
|
|
|
|
// Queue children for deletion, deleting immediately might cause errors.
|
|
|
|
for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
|
2022-10-24 23:07:02 +02:00
|
|
|
property_vbox->get_child(i)->queue_free();
|
2022-03-04 14:04:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int size = dict.size();
|
|
|
|
|
|
|
|
int max_page = MAX(0, size - 1) / page_length;
|
|
|
|
page_index = MIN(page_index, max_page);
|
|
|
|
|
|
|
|
paginator->update(page_index, max_page);
|
|
|
|
paginator->set_visible(max_page > 0);
|
|
|
|
|
|
|
|
int offset = page_index * page_length;
|
|
|
|
|
|
|
|
int amount = MIN(size - offset, page_length);
|
|
|
|
|
|
|
|
for (int i = 0; i < amount; i++) {
|
|
|
|
String prop_name;
|
|
|
|
Variant key;
|
|
|
|
Variant value;
|
|
|
|
|
|
|
|
prop_name = "indices/" + itos(i + offset);
|
|
|
|
key = dict.get_key_at_index(i + offset);
|
|
|
|
value = dict.get_value_at_index(i + offset);
|
|
|
|
|
|
|
|
EditorProperty *prop = memnew(EditorPropertyText);
|
|
|
|
|
|
|
|
prop->set_object_and_property(object.ptr(), prop_name);
|
|
|
|
int remove_index = 0;
|
|
|
|
|
|
|
|
String cs = key.get_construct_string();
|
|
|
|
prop->set_label(cs);
|
2022-08-25 12:42:17 +02:00
|
|
|
prop->set_tooltip_text(cs);
|
2022-03-04 14:04:59 +01:00
|
|
|
remove_index = i + offset;
|
|
|
|
|
|
|
|
prop->set_selectable(false);
|
|
|
|
prop->connect("property_changed", callable_mp(this, &EditorPropertyLocalizableString::_property_changed));
|
|
|
|
prop->connect("object_id_selected", callable_mp(this, &EditorPropertyLocalizableString::_object_id_selected));
|
|
|
|
|
|
|
|
HBoxContainer *hbox = memnew(HBoxContainer);
|
|
|
|
property_vbox->add_child(hbox);
|
|
|
|
hbox->add_child(prop);
|
|
|
|
prop->set_h_size_flags(SIZE_EXPAND_FILL);
|
2022-09-29 11:53:28 +02:00
|
|
|
Button *edit_btn = memnew(Button);
|
2023-08-13 02:33:39 +02:00
|
|
|
edit_btn->set_icon(get_editor_theme_icon(SNAME("Remove")));
|
2022-09-29 11:53:28 +02:00
|
|
|
hbox->add_child(edit_btn);
|
|
|
|
edit_btn->connect("pressed", callable_mp(this, &EditorPropertyLocalizableString::_remove_item).bind(edit_btn, remove_index));
|
2022-03-04 14:04:59 +01:00
|
|
|
|
|
|
|
prop->update_property();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (page_index == max_page) {
|
2022-05-25 01:38:13 +02:00
|
|
|
button_add_item = EditorInspector::create_inspector_action_button(TTR("Add Translation"));
|
2023-08-13 02:33:39 +02:00
|
|
|
button_add_item->set_icon(get_editor_theme_icon(SNAME("Add")));
|
2022-03-04 14:04:59 +01:00
|
|
|
button_add_item->connect("pressed", callable_mp(this, &EditorPropertyLocalizableString::_add_locale_popup));
|
|
|
|
property_vbox->add_child(button_add_item);
|
|
|
|
}
|
|
|
|
|
|
|
|
updating = false;
|
|
|
|
|
|
|
|
} else {
|
2022-04-01 03:10:46 +02:00
|
|
|
if (container) {
|
2022-03-04 14:04:59 +01:00
|
|
|
set_bottom_editor(nullptr);
|
2022-04-01 03:10:46 +02:00
|
|
|
memdelete(container);
|
2022-03-04 14:04:59 +01:00
|
|
|
button_add_item = nullptr;
|
2022-04-01 03:10:46 +02:00
|
|
|
container = nullptr;
|
2022-03-04 14:04:59 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyLocalizableString::_object_id_selected(const StringName &p_property, ObjectID p_id) {
|
|
|
|
emit_signal(SNAME("object_id_selected"), p_property, p_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyLocalizableString::_notification(int p_what) {
|
|
|
|
switch (p_what) {
|
2022-08-29 11:04:31 +02:00
|
|
|
case NOTIFICATION_THEME_CHANGED:
|
|
|
|
case NOTIFICATION_ENTER_TREE: {
|
2022-10-24 15:59:18 +02:00
|
|
|
if (button_add_item) {
|
2023-08-13 02:33:39 +02:00
|
|
|
button_add_item->set_icon(get_editor_theme_icon(SNAME("Add")));
|
2022-03-04 14:04:59 +01:00
|
|
|
}
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyLocalizableString::_edit_pressed() {
|
2023-05-26 09:13:24 +02:00
|
|
|
Variant prop_val = get_edited_property_value();
|
2022-11-27 08:56:53 +01:00
|
|
|
if (prop_val.get_type() == Variant::NIL && edit->is_pressed()) {
|
|
|
|
VariantInternal::initialize(&prop_val, Variant::DICTIONARY);
|
2022-03-04 14:04:59 +01:00
|
|
|
get_edited_object()->set(get_edited_property(), prop_val);
|
|
|
|
}
|
|
|
|
|
|
|
|
get_edited_object()->editor_set_section_unfold(get_edited_property(), edit->is_pressed());
|
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyLocalizableString::_page_changed(int p_page) {
|
|
|
|
if (updating) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
page_index = p_page;
|
|
|
|
update_property();
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorPropertyLocalizableString::_bind_methods() {
|
|
|
|
}
|
|
|
|
|
|
|
|
EditorPropertyLocalizableString::EditorPropertyLocalizableString() {
|
|
|
|
object.instantiate();
|
|
|
|
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
|
|
|
|
|
|
|
|
edit = memnew(Button);
|
|
|
|
edit->set_h_size_flags(SIZE_EXPAND_FILL);
|
|
|
|
edit->set_clip_text(true);
|
|
|
|
edit->connect("pressed", callable_mp(this, &EditorPropertyLocalizableString::_edit_pressed));
|
|
|
|
edit->set_toggle_mode(true);
|
|
|
|
add_child(edit);
|
|
|
|
add_focusable(edit);
|
|
|
|
|
2022-04-01 03:10:46 +02:00
|
|
|
container = nullptr;
|
2022-03-04 14:04:59 +01:00
|
|
|
button_add_item = nullptr;
|
|
|
|
paginator = nullptr;
|
|
|
|
updating = false;
|
|
|
|
|
|
|
|
locale_select = memnew(EditorLocaleDialog);
|
|
|
|
locale_select->connect("locale_selected", callable_mp(this, &EditorPropertyLocalizableString::_add_locale));
|
|
|
|
add_child(locale_select);
|
|
|
|
}
|