Merge pull request #80706 from ajreckof/rework-array-update-property

Rework `update_property` for array
This commit is contained in:
Rémi Verschelde 2024-01-02 15:08:41 +01:00
commit 0d88840e81
No known key found for this signature in database
GPG key ID: C3336907360768E1
2 changed files with 145 additions and 117 deletions

View file

@ -202,22 +202,16 @@ void EditorPropertyArray::_property_changed(const String &p_property, Variant p_
p_value = Variant(); // `EditorResourcePicker` resets to `Ref<Resource>()`. See GH-82716. p_value = Variant(); // `EditorResourcePicker` resets to `Ref<Resource>()`. See GH-82716.
} }
int index; int index = p_property.get_slice("/", 1).to_int();
if (p_property.begins_with("metadata/")) {
index = p_property.get_slice("/", 2).to_int();
} else {
index = p_property.get_slice("/", 1).to_int();
}
Variant array = object->get_array().duplicate(); Variant array = object->get_array().duplicate();
array.set(index, p_value); array.set(index, p_value);
object->set_array(array); emit_changed(get_edited_property(), array, p_name, p_changing);
emit_changed(get_edited_property(), array, "", true);
} }
void EditorPropertyArray::_change_type(Object *p_button, int p_index) { void EditorPropertyArray::_change_type(Object *p_button, int p_slot_index) {
Button *button = Object::cast_to<Button>(p_button); Button *button = Object::cast_to<Button>(p_button);
changing_type_index = p_index; changing_type_index = slots[p_slot_index].index;
Rect2 rect = button->get_screen_rect(); Rect2 rect = button->get_screen_rect();
change_type->reset_size(); change_type->reset_size();
change_type->set_position(rect.get_end() - Vector2(change_type->get_contents_minimum_size().x, 0)); change_type->set_position(rect.get_end() - Vector2(change_type->get_contents_minimum_size().x, 0));
@ -244,6 +238,48 @@ void EditorPropertyArray::_object_id_selected(const StringName &p_property, Obje
emit_signal(SNAME("object_id_selected"), p_property, p_id); emit_signal(SNAME("object_id_selected"), p_property, p_id);
} }
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);
}
void EditorPropertyArray::update_property() { void EditorPropertyArray::update_property() {
Variant array = get_edited_property_value(); Variant array = get_edited_property_value();
@ -276,7 +312,6 @@ void EditorPropertyArray::update_property() {
int size = array.call("size"); int size = array.call("size");
int max_page = MAX(0, size - 1) / page_length; int max_page = MAX(0, size - 1) / page_length;
page_index = MIN(page_index, max_page); page_index = MIN(page_index, max_page);
int offset = page_index * page_length;
edit->set_text(vformat(TTR("%s (size %s)"), array_type_name, itos(size))); edit->set_text(vformat(TTR("%s (size %s)"), array_type_name, itos(size)));
@ -326,16 +361,9 @@ void EditorPropertyArray::update_property() {
paginator = memnew(EditorPaginator); paginator = memnew(EditorPaginator);
paginator->connect("page_changed", callable_mp(this, &EditorPropertyArray::_page_changed)); paginator->connect("page_changed", callable_mp(this, &EditorPropertyArray::_page_changed));
vbox->add_child(paginator); vbox->add_child(paginator);
} else {
// Bye bye children of the box.
for (int i = property_vbox->get_child_count() - 1; i >= 0; i--) {
Node *child = property_vbox->get_child(i);
if (child == reorder_selected_element_hbox) {
continue; // Don't remove the property that the user is moving.
}
child->queue_free(); // Button still needed after pressed is called. for (int i = 0; i < page_length; i++) {
property_vbox->remove_child(child); create_new_property_slot();
} }
} }
@ -345,81 +373,46 @@ void EditorPropertyArray::update_property() {
paginator->update(page_index, max_page); paginator->update(page_index, max_page);
paginator->set_visible(max_page > 0); paginator->set_visible(max_page > 0);
int amount = MIN(size - offset, page_length); for (Slot &slot : slots) {
for (int i = 0; i < amount; i++) { bool slot_visible = &slot != &reorder_slot && slot.index < size;
bool reorder_is_from_current_page = reorder_from_index / page_length == page_index; slot.container->set_visible(slot_visible);
if (reorder_is_from_current_page && i == reorder_from_index % page_length) { // If not visible no need to update it
// Don't duplicate the property that the user is moving. if (!slot_visible) {
continue;
}
if (!reorder_is_from_current_page && i == reorder_to_index % page_length) {
// Don't create the property the moving property will take the place of,
// e.g. (if page_length == 20) don't create element 20 if dragging an item from
// the first page to the second page because element 20 would become element 19.
continue; continue;
} }
HBoxContainer *hbox = memnew(HBoxContainer); int idx = slot.index;
property_vbox->add_child(hbox); Variant::Type value_type = subtype;
Button *reorder_button = memnew(Button); if (value_type == Variant::NIL) {
reorder_button->set_icon(get_editor_theme_icon(SNAME("TripleBar"))); value_type = array.get(idx).get_type();
reorder_button->set_default_cursor_shape(Control::CURSOR_MOVE);
reorder_button->set_disabled(is_read_only());
reorder_button->connect("gui_input", callable_mp(this, &EditorPropertyArray::_reorder_button_gui_input));
reorder_button->connect("button_down", callable_mp(this, &EditorPropertyArray::_reorder_button_down).bind(i + offset));
reorder_button->connect("button_up", callable_mp(this, &EditorPropertyArray::_reorder_button_up));
hbox->add_child(reorder_button);
String prop_name = "indices/" + itos(i + offset);
EditorProperty *prop = nullptr;
Variant value = array.get(i + offset);
Variant::Type value_type = value.get_type();
if (value_type == Variant::NIL && subtype != Variant::NIL) {
value_type = subtype;
} }
if (value_type == Variant::OBJECT && Object::cast_to<EncodedObjectAsID>(value)) { // Check if the editor property needs to be updated.
EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID); bool value_as_id = Object::cast_to<EncodedObjectAsID>(array.get(idx));
editor->setup("Object"); if (value_type != slot.type || (value_type == Variant::OBJECT && (value_as_id != slot.as_id))) {
prop = editor; slot.as_id = value_as_id;
} else { slot.type = value_type;
prop = EditorInspector::instantiate_property_editor(nullptr, value_type, "", subtype_hint, subtype_hint_string, PROPERTY_USAGE_NONE); 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());
slot.prop->call_deferred("add_sibling", new_prop);
slot.prop->call_deferred("queue_free");
slot.prop = new_prop;
slot.set_index(idx);
} }
slot.prop->update_property();
prop->set_object_and_property(object.ptr(), prop_name);
prop->set_label(itos(i + offset));
prop->set_selectable(false);
prop->set_use_folding(is_using_folding());
prop->connect("property_changed", callable_mp(this, &EditorPropertyArray::_property_changed));
prop->connect("object_id_selected", callable_mp(this, &EditorPropertyArray::_object_id_selected));
prop->set_h_size_flags(SIZE_EXPAND_FILL);
prop->set_read_only(is_read_only());
hbox->add_child(prop);
bool is_untyped_array = 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")));
hbox->add_child(edit_btn);
edit_btn->set_disabled(is_read_only());
edit_btn->connect("pressed", callable_mp(this, &EditorPropertyArray::_change_type).bind(edit_btn, i + offset));
} 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(i + offset));
hbox->add_child(remove_btn);
}
prop->update_property();
}
if (reorder_to_index % page_length > 0) {
property_vbox->move_child(property_vbox->get_child(0), reorder_to_index % page_length);
} }
updating = false; updating = false;
@ -430,13 +423,14 @@ void EditorPropertyArray::update_property() {
memdelete(container); memdelete(container);
button_add_item = nullptr; button_add_item = nullptr;
container = nullptr; container = nullptr;
slots.clear();
} }
} }
} }
void EditorPropertyArray::_remove_pressed(int p_index) { void EditorPropertyArray::_remove_pressed(int p_slot_index) {
Variant array = object->get_array().duplicate(); Variant array = object->get_array().duplicate();
array.call("remove_at", p_index); array.call("remove_at", slots[p_slot_index].index);
emit_changed(get_edited_property(), array, "", false); emit_changed(get_edited_property(), array, "", false);
update_property(); update_property();
@ -579,6 +573,27 @@ void EditorPropertyArray::_page_changed(int p_page) {
return; return;
} }
page_index = p_page; page_index = p_page;
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++;
}
}
}
update_property(); update_property();
} }
@ -620,7 +635,7 @@ void EditorPropertyArray::setup(Variant::Type p_array_type, const String &p_hint
} }
void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_event) { void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_event) {
if (reorder_from_index < 0 || is_read_only()) { if (reorder_slot.index < 0 || is_read_only()) {
return; return;
} }
@ -645,26 +660,25 @@ void EditorPropertyArray::_reorder_button_gui_input(const Ref<InputEvent> &p_eve
reorder_mouse_y_delta -= required_y_distance * direction; reorder_mouse_y_delta -= required_y_distance * direction;
reorder_to_index += direction; reorder_to_index += direction;
property_vbox->move_child(reorder_slot.container, reorder_to_index % page_length);
if ((direction < 0 && reorder_to_index % page_length == page_length - 1) || (direction > 0 && reorder_to_index % page_length == 0)) { 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. // Automatically move to the next/previous page.
_page_changed(page_index + direction); _page_changed(page_index + direction);
} }
property_vbox->move_child(reorder_selected_element_hbox, reorder_to_index % page_length);
// Ensure the moving element is visible. // Ensure the moving element is visible.
InspectorDock::get_inspector_singleton()->ensure_control_visible(reorder_selected_element_hbox); InspectorDock::get_inspector_singleton()->ensure_control_visible(reorder_slot.container);
} }
} }
} }
void EditorPropertyArray::_reorder_button_down(int p_index) { void EditorPropertyArray::_reorder_button_down(int p_slot_index) {
if (is_read_only()) { if (is_read_only()) {
return; return;
} }
reorder_slot = slots[p_slot_index];
reorder_from_index = p_index; reorder_to_index = reorder_slot.index;
reorder_to_index = p_index;
reorder_selected_element_hbox = Object::cast_to<HBoxContainer>(property_vbox->get_child(p_index % page_length));
reorder_selected_button = Object::cast_to<Button>(reorder_selected_element_hbox->get_child(0));
// Ideally it'd to be able to show the mouse but I had issues with // 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. // Control's `mouse_exit()`/`mouse_entered()` signals not getting called.
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED);
@ -675,29 +689,27 @@ void EditorPropertyArray::_reorder_button_up() {
return; return;
} }
if (reorder_from_index != reorder_to_index) { if (reorder_slot.index != reorder_to_index) {
// Move the element. // Move the element.
Variant array = object->get_array().duplicate(); Variant array = object->get_array().duplicate();
Variant value_to_move = array.get(reorder_from_index); property_vbox->move_child(reorder_slot.container, reorder_slot.index % page_length);
array.call("remove_at", reorder_from_index); Variant value_to_move = array.get(reorder_slot.index);
array.call("remove_at", reorder_slot.index);
array.call("insert", reorder_to_index, value_to_move); array.call("insert", reorder_to_index, value_to_move);
reorder_slot.index = reorder_slot.index % page_length + page_index * page_length;
emit_changed(get_edited_property(), array, "", false); emit_changed(get_edited_property(), array, "", false);
update_property();
} }
reorder_from_index = -1;
reorder_to_index = -1;
reorder_mouse_y_delta = 0.0f;
Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE);
ERR_FAIL_NULL(reorder_selected_button); ERR_FAIL_NULL(reorder_slot.reorder_button);
reorder_selected_button->warp_mouse(reorder_selected_button->get_size() / 2.0f); reorder_slot.reorder_button->warp_mouse(reorder_slot.reorder_button->get_size() / 2.0f);
reorder_to_index = -1;
reorder_selected_element_hbox = nullptr; reorder_mouse_y_delta = 0.0f;
reorder_selected_button = nullptr; reorder_slot = Slot();
_page_changed(page_index);
} }
void EditorPropertyArray::_bind_methods() { void EditorPropertyArray::_bind_methods() {

View file

@ -81,6 +81,23 @@ public:
class EditorPropertyArray : public EditorProperty { class EditorPropertyArray : public EditorProperty {
GDCLASS(EditorPropertyArray, EditorProperty); GDCLASS(EditorPropertyArray, EditorProperty);
struct Slot {
Ref<EditorPropertyArrayObject> object;
HBoxContainer *container = nullptr;
int index = -1;
Variant::Type type = Variant::VARIANT_MAX;
bool as_id = false;
EditorProperty *prop = nullptr;
Button *reorder_button = nullptr;
void set_index(int p_idx) {
String prop_name = "indices/" + itos(p_idx);
prop->set_object_and_property(object.ptr(), prop_name);
prop->set_label(itos(p_idx));
index = p_idx;
}
};
PopupMenu *change_type = nullptr; PopupMenu *change_type = nullptr;
int page_length = 20; int page_length = 20;
@ -96,13 +113,11 @@ class EditorPropertyArray : public EditorProperty {
Variant::Type subtype; Variant::Type subtype;
PropertyHint subtype_hint; PropertyHint subtype_hint;
String subtype_hint_string; String subtype_hint_string;
LocalVector<Slot> slots;
int reorder_from_index = -1; Slot reorder_slot;
int reorder_to_index = -1; int reorder_to_index = -1;
float reorder_mouse_y_delta = 0.0f; float reorder_mouse_y_delta = 0.0f;
HBoxContainer *reorder_selected_element_hbox = nullptr;
Button *reorder_selected_button = nullptr;
void initialize_array(Variant &p_array); void initialize_array(Variant &p_array);
void _page_changed(int p_page); void _page_changed(int p_page);
@ -110,6 +125,7 @@ class EditorPropertyArray : public EditorProperty {
void _reorder_button_gui_input(const Ref<InputEvent> &p_event); void _reorder_button_gui_input(const Ref<InputEvent> &p_event);
void _reorder_button_down(int p_index); void _reorder_button_down(int p_index);
void _reorder_button_up(); void _reorder_button_up();
void create_new_property_slot();
protected: protected:
Ref<EditorPropertyArrayObject> object; Ref<EditorPropertyArrayObject> object;
@ -124,7 +140,7 @@ protected:
virtual void _length_changed(double p_page); virtual void _length_changed(double p_page);
virtual void _edit_pressed(); virtual void _edit_pressed();
virtual void _property_changed(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false); virtual void _property_changed(const String &p_property, Variant p_value, const String &p_name = "", bool p_changing = false);
virtual void _change_type(Object *p_button, int p_index); virtual void _change_type(Object *p_button, int p_slot_index);
virtual void _change_type_menu(int p_index); virtual void _change_type_menu(int p_index);
virtual void _object_id_selected(const StringName &p_property, ObjectID p_id); virtual void _object_id_selected(const StringName &p_property, ObjectID p_id);