Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance NodePath property editing #75274

Merged
merged 1 commit into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 102 additions & 34 deletions editor/editor_properties.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "editor/plugins/script_editor_plugin.h"
#include "editor/project_settings_editor.h"
#include "editor/property_selector.h"
#include "editor/scene_tree_dock.h"
#include "scene/2d/gpu_particles_2d.h"
#include "scene/3d/fog_volume.h"
#include "scene/3d/gpu_particles_3d.h"
Expand Down Expand Up @@ -2769,7 +2770,7 @@ EditorPropertyColor::EditorPropertyColor() {

void EditorPropertyNodePath::_set_read_only(bool p_read_only) {
assign->set_disabled(p_read_only);
clear->set_disabled(p_read_only);
menu->set_disabled(p_read_only);
};

Variant EditorPropertyNodePath::_get_cache_value(const StringName &p_prop, bool &r_valid) const {
Expand Down Expand Up @@ -2817,9 +2818,79 @@ void EditorPropertyNodePath::_node_assign() {
scene_tree->popup_scenetree_dialog();
}

void EditorPropertyNodePath::_node_clear() {
emit_changed(get_edited_property(), Variant());
update_property();
void EditorPropertyNodePath::_update_menu() {
const NodePath &np = _get_node_path();

menu->get_popup()->set_item_disabled(ACTION_CLEAR, np.is_empty());
menu->get_popup()->set_item_disabled(ACTION_COPY, np.is_empty());

Node *edited_node = Object::cast_to<Node>(get_edited_object());
menu->get_popup()->set_item_disabled(ACTION_SELECT, !edited_node || !edited_node->has_node(np));
}

void EditorPropertyNodePath::_menu_option(int p_idx) {
switch (p_idx) {
case ACTION_CLEAR: {
emit_changed(get_edited_property(), NodePath());
update_property();
} break;

case ACTION_COPY: {
DisplayServer::get_singleton()->clipboard_set(_get_node_path());
} break;

case ACTION_EDIT: {
assign->hide();
menu->hide();

const NodePath &np = _get_node_path();
edit->set_text(np);
edit->show();
callable_mp((Control *)edit, &Control::grab_focus).call_deferred();
} break;

case ACTION_SELECT: {
const Node *edited_node = get_base_node();
ERR_FAIL_NULL(edited_node);

const NodePath &np = _get_node_path();
Node *target_node = edited_node->get_node_or_null(np);
ERR_FAIL_NULL(target_node);

SceneTreeDock::get_singleton()->set_selected(target_node);
} break;
}
}

void EditorPropertyNodePath::_accept_text() {
_text_submitted(edit->get_text());
}

void EditorPropertyNodePath::_text_submitted(const String &p_text) {
NodePath np = p_text;
emit_changed(get_edited_property(), np);
edit->hide();
assign->show();
menu->show();
}

const NodePath EditorPropertyNodePath::_get_node_path() const {
const Node *base_node = const_cast<EditorPropertyNodePath *>(this)->get_base_node();

Variant val = get_edited_property_value();
Node *n = Object::cast_to<Node>(val);
if (n) {
if (!n->is_inside_tree()) {
return NodePath();
}
if (base_node) {
return base_node->get_path_to(n);
} else {
return get_tree()->get_edited_scene_root()->get_path_to(n);
}
} else {
return val;
}
}

bool EditorPropertyNodePath::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
Expand Down Expand Up @@ -2865,26 +2936,11 @@ bool EditorPropertyNodePath::is_drop_valid(const Dictionary &p_drag_data) const
}

void EditorPropertyNodePath::update_property() {
Node *base_node = get_base_node();

NodePath p;
Variant val = get_edited_object()->get(get_edited_property());
Node *n = Object::cast_to<Node>(val);
if (n) {
if (!n->is_inside_tree()) {
return;
}
if (base_node) {
p = base_node->get_path_to(n);
} else {
p = get_tree()->get_edited_scene_root()->get_path_to(n);
}
} else {
p = get_edited_property_value();
}

const Node *base_node = get_base_node();
const NodePath &p = _get_node_path();
assign->set_tooltip_text(p);
if (p == NodePath()) {

if (p.is_empty()) {
assign->set_icon(Ref<Texture2D>());
assign->set_text(TTR("Assign..."));
assign->set_flat(false);
Expand All @@ -2898,7 +2954,7 @@ void EditorPropertyNodePath::update_property() {
return;
}

Node *target_node = base_node->get_node(p);
const Node *target_node = base_node->get_node(p);
ERR_FAIL_NULL(target_node);

if (String(target_node->get_name()).contains("@")) {
Expand All @@ -2922,14 +2978,15 @@ void EditorPropertyNodePath::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
Ref<Texture2D> t = get_editor_theme_icon(SNAME("Clear"));
clear->set_icon(t);
menu->set_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
menu->get_popup()->set_item_icon(ACTION_CLEAR, get_editor_theme_icon(SNAME("Clear")));
menu->get_popup()->set_item_icon(ACTION_COPY, get_editor_theme_icon(SNAME("ActionCopy")));
menu->get_popup()->set_item_icon(ACTION_EDIT, get_editor_theme_icon(SNAME("Edit")));
menu->get_popup()->set_item_icon(ACTION_SELECT, get_editor_theme_icon(SNAME("ExternalLink")));
} break;
}
}

void EditorPropertyNodePath::_bind_methods() {
}
Node *EditorPropertyNodePath::get_base_node() {
if (!base_hint.is_empty() && get_tree()->get_root()->has_node(base_hint)) {
return get_tree()->get_root()->get_node(base_hint);
Expand Down Expand Up @@ -2973,12 +3030,23 @@ EditorPropertyNodePath::EditorPropertyNodePath() {
SET_DRAG_FORWARDING_CD(assign, EditorPropertyNodePath);
hbc->add_child(assign);

clear = memnew(Button);
clear->set_flat(true);
clear->connect("pressed", callable_mp(this, &EditorPropertyNodePath::_node_clear));
hbc->add_child(clear);

scene_tree = nullptr; //do not allocate unnecessarily
menu = memnew(MenuButton);
menu->set_flat(true);
menu->connect(SNAME("about_to_popup"), callable_mp(this, &EditorPropertyNodePath::_update_menu));
hbc->add_child(menu);

menu->get_popup()->add_item(TTR("Clear"), ACTION_CLEAR);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious: Why not take a reference/pointer to the popup and then popup->add_item(x)? I'm seeing such pattern (repeating getters or whatever on multiple lines) quite often in Godot's code base.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk, I just haven't thought about that. It's not like it's done consistently.

menu->get_popup()->add_item(TTR("Copy as Text"), ACTION_COPY);
menu->get_popup()->add_item(TTR("Edit"), ACTION_EDIT);
menu->get_popup()->add_item(TTR("Show Node in Tree"), ACTION_SELECT);
menu->get_popup()->connect(SNAME("id_pressed"), callable_mp(this, &EditorPropertyNodePath::_menu_option));

edit = memnew(LineEdit);
edit->set_h_size_flags(SIZE_EXPAND_FILL);
edit->hide();
edit->connect(SNAME("focus_exited"), callable_mp(this, &EditorPropertyNodePath::_accept_text));
edit->connect(SNAME("text_submitted"), callable_mp(this, &EditorPropertyNodePath::_text_submitted));
hbc->add_child(edit);
}

///////////////////// RID /////////////////////////
Expand Down
20 changes: 17 additions & 3 deletions editor/editor_properties.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class EditorFileDialog;
class EditorLocaleDialog;
class EditorResourcePicker;
class EditorSpinSlider;
class MenuButton;
class PropertySelector;
class SceneTreeDialog;
class TextEdit;
Expand Down Expand Up @@ -649,8 +650,18 @@ class EditorPropertyColor : public EditorProperty {

class EditorPropertyNodePath : public EditorProperty {
GDCLASS(EditorPropertyNodePath, EditorProperty);

enum {
ACTION_CLEAR,
ACTION_COPY,
ACTION_EDIT,
ACTION_SELECT,
};

Button *assign = nullptr;
Button *clear = nullptr;
MenuButton *menu = nullptr;
LineEdit *edit = nullptr;

SceneTreeDialog *scene_tree = nullptr;
NodePath base_hint;
bool use_path_from_scene_root = false;
Expand All @@ -659,8 +670,12 @@ class EditorPropertyNodePath : public EditorProperty {
Vector<StringName> valid_types;
void _node_selected(const NodePath &p_path);
void _node_assign();
void _node_clear();
Node *get_base_node();
void _update_menu();
void _menu_option(int p_idx);
void _accept_text();
void _text_submitted(const String &p_text);
const NodePath _get_node_path() const;

bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
Expand All @@ -670,7 +685,6 @@ class EditorPropertyNodePath : public EditorProperty {

protected:
virtual void _set_read_only(bool p_read_only) override;
static void _bind_methods();
void _notification(int p_what);

public:
Expand Down