Path: blob/master/editor/inspector/property_selector.cpp
20829 views
/**************************************************************************/1/* property_selector.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "property_selector.h"3132#include "editor/doc/editor_help.h"33#include "editor/editor_node.h"34#include "editor/gui/filter_line_edit.h"35#include "editor/themes/editor_scale.h"36#include "scene/gui/margin_container.h"37#include "scene/gui/tree.h"3839void PropertySelector::_text_changed(const String &p_newtext) {40_update_search();41}4243void PropertySelector::_update_search() {44if (properties) {45set_title(TTRC("Select Property"));46} else if (virtuals_only) {47set_title(TTRC("Select Virtual Method"));48} else {49set_title(TTRC("Select Method"));50}5152search_options->clear();53help_bit->set_custom_text(String(), String(), String());5455TreeItem *root = search_options->create_item();5657// Allow using spaces in place of underscores in the search string (makes the search more fault-tolerant).58const String search_text = search_box->get_text().replace_char(' ', '_');5960if (properties) {61List<PropertyInfo> props;6263if (instance) {64instance->get_property_list(&props, true);65} else if (type != Variant::NIL) {66Variant v;67Callable::CallError ce;68Variant::construct(type, v, nullptr, 0, ce);6970v.get_property_list(&props);71} else {72Object *obj = ObjectDB::get_instance(script);73if (Object::cast_to<Script>(obj)) {74props.push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY));75Object::cast_to<Script>(obj)->get_script_property_list(&props);76}7778StringName base = base_type;79while (base) {80props.push_back(PropertyInfo(Variant::NIL, base, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY));81ClassDB::get_property_list(base, &props, true);82base = ClassDB::get_parent_class(base);83}84}8586TreeItem *category = nullptr;8788bool found = false;89for (const PropertyInfo &E : props) {90if (E.usage == PROPERTY_USAGE_CATEGORY) {91if (category && category->get_first_child() == nullptr) {92memdelete(category); //old category was unused93}94category = search_options->create_item(root);95category->set_text(0, E.name);96category->set_selectable(0, false);9798Ref<Texture2D> icon;99if (E.name == "Script Variables") {100icon = search_options->get_editor_theme_icon(SNAME("Script"));101} else {102icon = EditorNode::get_singleton()->get_class_icon(E.name);103}104category->set_icon(0, icon);105continue;106}107108if (!(E.usage & PROPERTY_USAGE_EDITOR) && !(E.usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) {109continue;110}111112if (!search_box->get_text().is_empty() && !E.name.containsn(search_text)) {113continue;114}115116if (!type_filter.is_empty() && !type_filter.has(E.type)) {117continue;118}119120TreeItem *item = search_options->create_item(category ? category : root);121item->set_text(0, E.name);122item->set_metadata(0, E.name);123item->set_icon(0, search_options->get_editor_theme_icon(Variant::get_type_name(E.type)));124125if (!found && !search_box->get_text().is_empty() && E.name.containsn(search_text)) {126item->select(0);127found = true;128} else if (!found && search_box->get_text().is_empty() && E.name == selected) {129item->select(0);130found = true;131}132133item->set_selectable(0, true);134135_create_subproperties(item, E.type);136item->set_collapsed(true);137}138139if (category && category->get_first_child() == nullptr) {140memdelete(category); //old category was unused141}142143if (found) {144// As we call this while adding items, defer until list is completely populated.145callable_mp(search_options, &Tree::scroll_to_item).call_deferred(search_options->get_selected(), true);146}147148} else {149List<MethodInfo> methods;150151if (type != Variant::NIL) {152Variant v;153Callable::CallError ce;154Variant::construct(type, v, nullptr, 0, ce);155v.get_method_list(&methods);156} else {157Ref<Script> script_ref = ObjectDB::get_ref<Script>(script);158if (script_ref.is_valid()) {159if (script_ref->is_built_in()) {160script_ref->reload(true);161}162163List<MethodInfo> script_methods;164script_ref->get_script_method_list(&script_methods);165166methods.push_back(MethodInfo("*Script Methods")); // TODO: Split by inheritance.167168for (const MethodInfo &mi : script_methods) {169if (mi.name.begins_with("@")) {170// GH-92782. GDScript inline setters/getters are historically present in `get_method_list()`171// and can be called using `Object.call()`. However, these functions are meant to be internal172// and their names are not valid identifiers, so let's hide them from the user.173continue;174}175methods.push_back(mi);176}177}178179StringName base = base_type;180while (base) {181methods.push_back(MethodInfo("*" + String(base)));182ClassDB::get_method_list(base, &methods, true, true);183base = ClassDB::get_parent_class(base);184}185}186187TreeItem *category = nullptr;188189bool found = false;190bool script_methods = false;191192for (MethodInfo &mi : methods) {193if (mi.name.begins_with("*")) {194if (category && category->get_first_child() == nullptr) {195memdelete(category); //old category was unused196}197category = search_options->create_item(root);198category->set_text(0, mi.name.replace_first("*", ""));199category->set_selectable(0, false);200201Ref<Texture2D> icon;202script_methods = false;203String rep = mi.name.remove_char('*');204if (mi.name == "*Script Methods") {205icon = search_options->get_editor_theme_icon(SNAME("Script"));206script_methods = true;207} else {208icon = EditorNode::get_singleton()->get_class_icon(rep);209}210category->set_icon(0, icon);211212continue;213}214215String name = mi.name.get_slicec(':', 0);216if (!script_methods && name.begins_with("_") && !(mi.flags & METHOD_FLAG_VIRTUAL)) {217continue;218}219220if (virtuals_only && !(mi.flags & METHOD_FLAG_VIRTUAL)) {221continue;222}223224if (!virtuals_only && (mi.flags & METHOD_FLAG_VIRTUAL)) {225continue;226}227228if (!search_box->get_text().is_empty() && !name.containsn(search_text)) {229continue;230}231232TreeItem *item = search_options->create_item(category ? category : root);233234String desc;235if (mi.name.contains_char(':')) {236desc = mi.name.get_slicec(':', 1) + " ";237mi.name = mi.name.get_slicec(':', 0);238} else if (mi.return_val.type != Variant::NIL) {239desc = Variant::get_type_name(mi.return_val.type);240} else {241desc = "void";242}243244desc += vformat(" %s(", mi.name);245246for (int64_t i = 0; i < mi.arguments.size(); ++i) {247PropertyInfo &arg = mi.arguments.write[i];248if (i > 0) {249desc += ", ";250}251252desc += arg.name;253254if (arg.type == Variant::NIL) {255desc += ": Variant";256} else if (arg.name.contains_char(':')) {257desc += vformat(": %s", arg.name.get_slicec(':', 1));258arg.name = arg.name.get_slicec(':', 0);259} else {260desc += vformat(": %s", Variant::get_type_name(arg.type));261}262}263264if (mi.flags & METHOD_FLAG_VARARG) {265desc += mi.arguments.is_empty() ? "..." : ", ...";266}267268desc += ")";269270if (mi.flags & METHOD_FLAG_VARARG) {271desc += " vararg";272}273274if (mi.flags & METHOD_FLAG_CONST) {275desc += " const";276}277278if (mi.flags & METHOD_FLAG_VIRTUAL) {279desc += " virtual";280}281282item->set_text(0, desc);283item->set_metadata(0, name);284item->set_selectable(0, true);285286if (!found && !search_box->get_text().is_empty() && name.containsn(search_text)) {287item->select(0);288found = true;289} else if (!found && search_box->get_text().is_empty() && name == selected) {290item->select(0);291found = true;292}293}294295if (category && category->get_first_child() == nullptr) {296memdelete(category); //old category was unused297}298299if (found) {300// As we call this while adding items, defer until list is completely populated.301callable_mp(search_options, &Tree::scroll_to_item).call_deferred(search_options->get_selected(), true);302}303}304305get_ok_button()->set_disabled(search_options->get_selected() == nullptr);306}307308void PropertySelector::_confirmed() {309TreeItem *ti = search_options->get_selected();310if (!ti) {311return;312}313emit_signal(SNAME("selected"), ti->get_metadata(0));314hide();315}316317void PropertySelector::_item_selected() {318help_bit->set_custom_text(String(), String(), String());319320TreeItem *item = search_options->get_selected();321get_ok_button()->set_disabled(item == nullptr);322323if (!item) {324return;325}326String name = item->get_metadata(0);327328String class_type;329if (type != Variant::NIL) {330class_type = Variant::get_type_name(type);331} else if (!base_type.is_empty()) {332class_type = base_type;333} else if (instance) {334class_type = instance->get_class();335}336337String text;338while (!class_type.is_empty()) {339if (properties) {340if (ClassDB::has_property(class_type, name, true)) {341help_bit->parse_symbol("property|" + class_type + "|" + name);342break;343}344} else {345if (ClassDB::has_method(class_type, name, true)) {346help_bit->parse_symbol("method|" + class_type + "|" + name);347break;348}349}350351// It may be from a parent class, keep looking.352class_type = ClassDB::get_parent_class(class_type);353}354}355356void PropertySelector::_hide_requested() {357_cancel_pressed(); // From AcceptDialog.358}359360void PropertySelector::_create_subproperties(TreeItem *p_parent_item, Variant::Type p_type) {361switch (p_type) {362case Variant::VECTOR2: {363_create_subproperty(p_parent_item, "x", Variant::FLOAT);364_create_subproperty(p_parent_item, "y", Variant::FLOAT);365} break;366367case Variant::VECTOR2I: {368_create_subproperty(p_parent_item, "x", Variant::INT);369_create_subproperty(p_parent_item, "y", Variant::INT);370} break;371372case Variant::RECT2: {373_create_subproperty(p_parent_item, "position", Variant::VECTOR2);374_create_subproperty(p_parent_item, "size", Variant::VECTOR2);375_create_subproperty(p_parent_item, "end", Variant::VECTOR2);376} break;377378case Variant::RECT2I: {379_create_subproperty(p_parent_item, "position", Variant::VECTOR2I);380_create_subproperty(p_parent_item, "size", Variant::VECTOR2I);381_create_subproperty(p_parent_item, "end", Variant::VECTOR2I);382} break;383384case Variant::VECTOR3: {385_create_subproperty(p_parent_item, "x", Variant::FLOAT);386_create_subproperty(p_parent_item, "y", Variant::FLOAT);387_create_subproperty(p_parent_item, "z", Variant::FLOAT);388} break;389390case Variant::VECTOR3I: {391_create_subproperty(p_parent_item, "x", Variant::INT);392_create_subproperty(p_parent_item, "y", Variant::INT);393_create_subproperty(p_parent_item, "z", Variant::INT);394} break;395396case Variant::TRANSFORM2D: {397_create_subproperty(p_parent_item, "origin", Variant::VECTOR2);398_create_subproperty(p_parent_item, "x", Variant::VECTOR2);399_create_subproperty(p_parent_item, "y", Variant::VECTOR2);400} break;401402case Variant::VECTOR4: {403_create_subproperty(p_parent_item, "x", Variant::FLOAT);404_create_subproperty(p_parent_item, "y", Variant::FLOAT);405_create_subproperty(p_parent_item, "z", Variant::FLOAT);406_create_subproperty(p_parent_item, "w", Variant::FLOAT);407} break;408409case Variant::VECTOR4I: {410_create_subproperty(p_parent_item, "x", Variant::INT);411_create_subproperty(p_parent_item, "y", Variant::INT);412_create_subproperty(p_parent_item, "z", Variant::INT);413_create_subproperty(p_parent_item, "w", Variant::INT);414} break;415416case Variant::PLANE: {417_create_subproperty(p_parent_item, "x", Variant::FLOAT);418_create_subproperty(p_parent_item, "y", Variant::FLOAT);419_create_subproperty(p_parent_item, "z", Variant::FLOAT);420_create_subproperty(p_parent_item, "normal", Variant::VECTOR3);421_create_subproperty(p_parent_item, "d", Variant::FLOAT);422} break;423424case Variant::QUATERNION: {425_create_subproperty(p_parent_item, "x", Variant::FLOAT);426_create_subproperty(p_parent_item, "y", Variant::FLOAT);427_create_subproperty(p_parent_item, "z", Variant::FLOAT);428_create_subproperty(p_parent_item, "w", Variant::FLOAT);429} break;430431case Variant::AABB: {432_create_subproperty(p_parent_item, "position", Variant::VECTOR3);433_create_subproperty(p_parent_item, "size", Variant::VECTOR3);434_create_subproperty(p_parent_item, "end", Variant::VECTOR3);435} break;436437case Variant::BASIS: {438_create_subproperty(p_parent_item, "x", Variant::VECTOR3);439_create_subproperty(p_parent_item, "y", Variant::VECTOR3);440_create_subproperty(p_parent_item, "z", Variant::VECTOR3);441} break;442443case Variant::TRANSFORM3D: {444_create_subproperty(p_parent_item, "basis", Variant::BASIS);445_create_subproperty(p_parent_item, "origin", Variant::VECTOR3);446} break;447448case Variant::PROJECTION: {449_create_subproperty(p_parent_item, "x", Variant::VECTOR4);450_create_subproperty(p_parent_item, "y", Variant::VECTOR4);451_create_subproperty(p_parent_item, "z", Variant::VECTOR4);452_create_subproperty(p_parent_item, "w", Variant::VECTOR4);453} break;454455case Variant::COLOR: {456_create_subproperty(p_parent_item, "r", Variant::FLOAT);457_create_subproperty(p_parent_item, "g", Variant::FLOAT);458_create_subproperty(p_parent_item, "b", Variant::FLOAT);459_create_subproperty(p_parent_item, "a", Variant::FLOAT);460_create_subproperty(p_parent_item, "r8", Variant::INT);461_create_subproperty(p_parent_item, "g8", Variant::INT);462_create_subproperty(p_parent_item, "b8", Variant::INT);463_create_subproperty(p_parent_item, "a8", Variant::INT);464_create_subproperty(p_parent_item, "h", Variant::FLOAT);465_create_subproperty(p_parent_item, "s", Variant::FLOAT);466_create_subproperty(p_parent_item, "v", Variant::FLOAT);467} break;468469default: {470}471}472}473474void PropertySelector::_create_subproperty(TreeItem *p_parent_item, const String &p_name, Variant::Type p_type) {475if (!type_filter.is_empty() && !type_filter.has(p_type)) {476return;477}478479TreeItem *item = search_options->create_item(p_parent_item);480item->set_text(0, p_name);481item->set_metadata(0, String(p_parent_item->get_metadata(0)) + ":" + p_name);482item->set_icon(0, search_options->get_editor_theme_icon(Variant::get_type_name(p_type)));483484_create_subproperties(item, p_type);485}486487void PropertySelector::_notification(int p_what) {488switch (p_what) {489case NOTIFICATION_ENTER_TREE: {490connect(SceneStringName(confirmed), callable_mp(this, &PropertySelector::_confirmed));491} break;492493case NOTIFICATION_EXIT_TREE: {494disconnect(SceneStringName(confirmed), callable_mp(this, &PropertySelector::_confirmed));495} break;496497case NOTIFICATION_THEME_CHANGED: {498search_box->set_right_icon(get_editor_theme_icon(SNAME("Search")));499} break;500}501}502503void PropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only) {504base_type = p_base;505selected = p_current;506type = Variant::NIL;507script = ObjectID();508properties = false;509instance = nullptr;510virtuals_only = p_virtuals_only;511512popup_centered_ratio(0.6);513search_box->set_text("");514search_box->grab_focus();515_update_search();516}517518void PropertySelector::select_method_from_script(const Ref<Script> &p_script, const String &p_current) {519ERR_FAIL_COND(p_script.is_null());520base_type = p_script->get_instance_base_type();521selected = p_current;522type = Variant::NIL;523script = p_script->get_instance_id();524properties = false;525instance = nullptr;526virtuals_only = false;527528popup_centered_ratio(0.6);529search_box->set_text("");530search_box->grab_focus();531_update_search();532}533534void PropertySelector::select_method_from_basic_type(Variant::Type p_type, const String &p_current) {535ERR_FAIL_COND(p_type == Variant::NIL);536base_type = "";537selected = p_current;538type = p_type;539script = ObjectID();540properties = false;541instance = nullptr;542virtuals_only = false;543544popup_centered_ratio(0.6);545search_box->set_text("");546search_box->grab_focus();547_update_search();548}549550void PropertySelector::select_method_from_instance(Object *p_instance, const String &p_current) {551base_type = p_instance->get_class();552selected = p_current;553type = Variant::NIL;554script = ObjectID();555{556Ref<Script> scr = p_instance->get_script();557if (scr.is_valid()) {558script = scr->get_instance_id();559}560}561properties = false;562instance = nullptr;563virtuals_only = false;564565popup_centered_ratio(0.6);566search_box->set_text("");567search_box->grab_focus();568_update_search();569}570571void PropertySelector::select_property_from_base_type(const String &p_base, const String &p_current) {572base_type = p_base;573selected = p_current;574type = Variant::NIL;575script = ObjectID();576properties = true;577instance = nullptr;578virtuals_only = false;579580popup_centered_ratio(0.6);581search_box->set_text("");582search_box->grab_focus();583_update_search();584}585586void PropertySelector::select_property_from_script(const Ref<Script> &p_script, const String &p_current) {587ERR_FAIL_COND(p_script.is_null());588589base_type = p_script->get_instance_base_type();590selected = p_current;591type = Variant::NIL;592script = p_script->get_instance_id();593properties = true;594instance = nullptr;595virtuals_only = false;596597popup_centered_ratio(0.6);598search_box->set_text("");599search_box->grab_focus();600_update_search();601}602603void PropertySelector::select_property_from_basic_type(Variant::Type p_type, const String &p_current) {604ERR_FAIL_COND(p_type == Variant::NIL);605base_type = "";606selected = p_current;607type = p_type;608script = ObjectID();609properties = true;610instance = nullptr;611virtuals_only = false;612613popup_centered_ratio(0.6);614search_box->set_text("");615search_box->grab_focus();616_update_search();617}618619void PropertySelector::select_property_from_instance(Object *p_instance, const String &p_current) {620base_type = "";621selected = p_current;622type = Variant::NIL;623script = ObjectID();624properties = true;625instance = p_instance;626virtuals_only = false;627628popup_centered_ratio(0.6);629search_box->set_text("");630search_box->grab_focus();631_update_search();632}633634void PropertySelector::set_type_filter(const Vector<Variant::Type> &p_type_filter) {635type_filter = p_type_filter;636}637638void PropertySelector::_bind_methods() {639ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name")));640}641642PropertySelector::PropertySelector() {643VBoxContainer *vbc = memnew(VBoxContainer);644add_child(vbc);645646search_box = memnew(FilterLineEdit);647search_box->set_accessibility_name(TTRC("Search:"));648vbc->add_margin_child(TTRC("Search:"), search_box);649search_box->connect(SceneStringName(text_changed), callable_mp(this, &PropertySelector::_text_changed));650651search_options = memnew(Tree);652search_box->set_forward_control(search_options);653search_options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);654search_options->set_scroll_hint_mode(Tree::SCROLL_HINT_MODE_BOTH);655MarginContainer *mc = vbc->add_margin_child(TTRC("Matches:"), search_options, true);656mc->set_theme_type_variation("NoBorderHorizontalWindow");657658search_options->connect("item_activated", callable_mp(this, &PropertySelector::_confirmed));659search_options->connect("cell_selected", callable_mp(this, &PropertySelector::_item_selected));660search_options->set_hide_root(true);661662help_bit = memnew(EditorHelpBit);663help_bit->set_content_height_limits(80 * EDSCALE, 80 * EDSCALE);664help_bit->connect("request_hide", callable_mp(this, &PropertySelector::_hide_requested));665vbc->add_margin_child(TTRC("Description:"), help_bit);666667set_ok_button_text(TTRC("Open"));668get_ok_button()->set_disabled(true);669register_text_enter(search_box);670set_hide_on_ok(false);671}672673674