Path: blob/master/editor/inspector/property_selector.cpp
9905 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/themes/editor_scale.h"35#include "scene/gui/line_edit.h"36#include "scene/gui/tree.h"3738void PropertySelector::_text_changed(const String &p_newtext) {39_update_search();40}4142void PropertySelector::_sbox_input(const Ref<InputEvent> &p_event) {43// Redirect navigational key events to the tree.44Ref<InputEventKey> key = p_event;45if (key.is_valid()) {46if (key->is_action("ui_up", true) || key->is_action("ui_down", true) || key->is_action("ui_page_up") || key->is_action("ui_page_down")) {47search_options->gui_input(key);48search_box->accept_event();4950TreeItem *root = search_options->get_root();51if (!root->get_first_child()) {52return;53}5455TreeItem *current = search_options->get_selected();5657TreeItem *item = search_options->get_next_selected(root);58while (item) {59item->deselect(0);60item = search_options->get_next_selected(item);61}6263current->select(0);64}65}66}6768void PropertySelector::_update_search() {69if (properties) {70set_title(TTR("Select Property"));71} else if (virtuals_only) {72set_title(TTR("Select Virtual Method"));73} else {74set_title(TTR("Select Method"));75}7677search_options->clear();78help_bit->set_custom_text(String(), String(), String());7980TreeItem *root = search_options->create_item();8182// Allow using spaces in place of underscores in the search string (makes the search more fault-tolerant).83const String search_text = search_box->get_text().replace_char(' ', '_');8485if (properties) {86List<PropertyInfo> props;8788if (instance) {89instance->get_property_list(&props, true);90} else if (type != Variant::NIL) {91Variant v;92Callable::CallError ce;93Variant::construct(type, v, nullptr, 0, ce);9495v.get_property_list(&props);96} else {97Object *obj = ObjectDB::get_instance(script);98if (Object::cast_to<Script>(obj)) {99props.push_back(PropertyInfo(Variant::NIL, "Script Variables", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY));100Object::cast_to<Script>(obj)->get_script_property_list(&props);101}102103StringName base = base_type;104while (base) {105props.push_back(PropertyInfo(Variant::NIL, base, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_CATEGORY));106ClassDB::get_property_list(base, &props, true);107base = ClassDB::get_parent_class(base);108}109}110111TreeItem *category = nullptr;112113bool found = false;114for (const PropertyInfo &E : props) {115if (E.usage == PROPERTY_USAGE_CATEGORY) {116if (category && category->get_first_child() == nullptr) {117memdelete(category); //old category was unused118}119category = search_options->create_item(root);120category->set_text(0, E.name);121category->set_selectable(0, false);122123Ref<Texture2D> icon;124if (E.name == "Script Variables") {125icon = search_options->get_editor_theme_icon(SNAME("Script"));126} else {127icon = EditorNode::get_singleton()->get_class_icon(E.name);128}129category->set_icon(0, icon);130continue;131}132133if (!(E.usage & PROPERTY_USAGE_EDITOR) && !(E.usage & PROPERTY_USAGE_SCRIPT_VARIABLE)) {134continue;135}136137if (!search_box->get_text().is_empty() && !E.name.containsn(search_text)) {138continue;139}140141if (!type_filter.is_empty() && !type_filter.has(E.type)) {142continue;143}144145TreeItem *item = search_options->create_item(category ? category : root);146item->set_text(0, E.name);147item->set_metadata(0, E.name);148item->set_icon(0, search_options->get_editor_theme_icon(Variant::get_type_name(E.type)));149150if (!found && !search_box->get_text().is_empty() && E.name.containsn(search_text)) {151item->select(0);152found = true;153} else if (!found && search_box->get_text().is_empty() && E.name == selected) {154item->select(0);155found = true;156}157158item->set_selectable(0, true);159160_create_subproperties(item, E.type);161item->set_collapsed(true);162}163164if (category && category->get_first_child() == nullptr) {165memdelete(category); //old category was unused166}167168if (found) {169// As we call this while adding items, defer until list is completely populated.170callable_mp(search_options, &Tree::scroll_to_item).call_deferred(search_options->get_selected(), true);171}172173} else {174List<MethodInfo> methods;175176if (type != Variant::NIL) {177Variant v;178Callable::CallError ce;179Variant::construct(type, v, nullptr, 0, ce);180v.get_method_list(&methods);181} else {182Ref<Script> script_ref = ObjectDB::get_ref<Script>(script);183if (script_ref.is_valid()) {184if (script_ref->is_built_in()) {185script_ref->reload(true);186}187188List<MethodInfo> script_methods;189script_ref->get_script_method_list(&script_methods);190191methods.push_back(MethodInfo("*Script Methods")); // TODO: Split by inheritance.192193for (const MethodInfo &mi : script_methods) {194if (mi.name.begins_with("@")) {195// GH-92782. GDScript inline setters/getters are historically present in `get_method_list()`196// and can be called using `Object.call()`. However, these functions are meant to be internal197// and their names are not valid identifiers, so let's hide them from the user.198continue;199}200methods.push_back(mi);201}202}203204StringName base = base_type;205while (base) {206methods.push_back(MethodInfo("*" + String(base)));207ClassDB::get_method_list(base, &methods, true, true);208base = ClassDB::get_parent_class(base);209}210}211212TreeItem *category = nullptr;213214bool found = false;215bool script_methods = false;216217for (MethodInfo &mi : methods) {218if (mi.name.begins_with("*")) {219if (category && category->get_first_child() == nullptr) {220memdelete(category); //old category was unused221}222category = search_options->create_item(root);223category->set_text(0, mi.name.replace_first("*", ""));224category->set_selectable(0, false);225226Ref<Texture2D> icon;227script_methods = false;228String rep = mi.name.remove_char('*');229if (mi.name == "*Script Methods") {230icon = search_options->get_editor_theme_icon(SNAME("Script"));231script_methods = true;232} else {233icon = EditorNode::get_singleton()->get_class_icon(rep);234}235category->set_icon(0, icon);236237continue;238}239240String name = mi.name.get_slicec(':', 0);241if (!script_methods && name.begins_with("_") && !(mi.flags & METHOD_FLAG_VIRTUAL)) {242continue;243}244245if (virtuals_only && !(mi.flags & METHOD_FLAG_VIRTUAL)) {246continue;247}248249if (!virtuals_only && (mi.flags & METHOD_FLAG_VIRTUAL)) {250continue;251}252253if (!search_box->get_text().is_empty() && !name.containsn(search_text)) {254continue;255}256257TreeItem *item = search_options->create_item(category ? category : root);258259String desc;260if (mi.name.contains_char(':')) {261desc = mi.name.get_slicec(':', 1) + " ";262mi.name = mi.name.get_slicec(':', 0);263} else if (mi.return_val.type != Variant::NIL) {264desc = Variant::get_type_name(mi.return_val.type);265} else {266desc = "void";267}268269desc += vformat(" %s(", mi.name);270271for (int64_t i = 0; i < mi.arguments.size(); ++i) {272PropertyInfo &arg = mi.arguments.write[i];273if (i > 0) {274desc += ", ";275}276277desc += arg.name;278279if (arg.type == Variant::NIL) {280desc += ": Variant";281} else if (arg.name.contains_char(':')) {282desc += vformat(": %s", arg.name.get_slicec(':', 1));283arg.name = arg.name.get_slicec(':', 0);284} else {285desc += vformat(": %s", Variant::get_type_name(arg.type));286}287}288289if (mi.flags & METHOD_FLAG_VARARG) {290desc += mi.arguments.is_empty() ? "..." : ", ...";291}292293desc += ")";294295if (mi.flags & METHOD_FLAG_VARARG) {296desc += " vararg";297}298299if (mi.flags & METHOD_FLAG_CONST) {300desc += " const";301}302303if (mi.flags & METHOD_FLAG_VIRTUAL) {304desc += " virtual";305}306307item->set_text(0, desc);308item->set_metadata(0, name);309item->set_selectable(0, true);310311if (!found && !search_box->get_text().is_empty() && name.containsn(search_text)) {312item->select(0);313found = true;314} else if (!found && search_box->get_text().is_empty() && name == selected) {315item->select(0);316found = true;317}318}319320if (category && category->get_first_child() == nullptr) {321memdelete(category); //old category was unused322}323324if (found) {325// As we call this while adding items, defer until list is completely populated.326callable_mp(search_options, &Tree::scroll_to_item).call_deferred(search_options->get_selected(), true);327}328}329330get_ok_button()->set_disabled(search_options->get_selected() == nullptr);331}332333void PropertySelector::_confirmed() {334TreeItem *ti = search_options->get_selected();335if (!ti) {336return;337}338emit_signal(SNAME("selected"), ti->get_metadata(0));339hide();340}341342void PropertySelector::_item_selected() {343help_bit->set_custom_text(String(), String(), String());344345TreeItem *item = search_options->get_selected();346get_ok_button()->set_disabled(item == nullptr);347348if (!item) {349return;350}351String name = item->get_metadata(0);352353String class_type;354if (type != Variant::NIL) {355class_type = Variant::get_type_name(type);356} else if (!base_type.is_empty()) {357class_type = base_type;358} else if (instance) {359class_type = instance->get_class();360}361362String text;363while (!class_type.is_empty()) {364if (properties) {365if (ClassDB::has_property(class_type, name, true)) {366help_bit->parse_symbol("property|" + class_type + "|" + name);367break;368}369} else {370if (ClassDB::has_method(class_type, name, true)) {371help_bit->parse_symbol("method|" + class_type + "|" + name);372break;373}374}375376// It may be from a parent class, keep looking.377class_type = ClassDB::get_parent_class(class_type);378}379}380381void PropertySelector::_hide_requested() {382_cancel_pressed(); // From AcceptDialog.383}384385void PropertySelector::_create_subproperties(TreeItem *p_parent_item, Variant::Type p_type) {386switch (p_type) {387case Variant::VECTOR2: {388_create_subproperty(p_parent_item, "x", Variant::FLOAT);389_create_subproperty(p_parent_item, "y", Variant::FLOAT);390} break;391392case Variant::VECTOR2I: {393_create_subproperty(p_parent_item, "x", Variant::INT);394_create_subproperty(p_parent_item, "y", Variant::INT);395} break;396397case Variant::RECT2: {398_create_subproperty(p_parent_item, "position", Variant::VECTOR2);399_create_subproperty(p_parent_item, "size", Variant::VECTOR2);400_create_subproperty(p_parent_item, "end", Variant::VECTOR2);401} break;402403case Variant::RECT2I: {404_create_subproperty(p_parent_item, "position", Variant::VECTOR2I);405_create_subproperty(p_parent_item, "size", Variant::VECTOR2I);406_create_subproperty(p_parent_item, "end", Variant::VECTOR2I);407} break;408409case Variant::VECTOR3: {410_create_subproperty(p_parent_item, "x", Variant::FLOAT);411_create_subproperty(p_parent_item, "y", Variant::FLOAT);412_create_subproperty(p_parent_item, "z", Variant::FLOAT);413} break;414415case Variant::VECTOR3I: {416_create_subproperty(p_parent_item, "x", Variant::INT);417_create_subproperty(p_parent_item, "y", Variant::INT);418_create_subproperty(p_parent_item, "z", Variant::INT);419} break;420421case Variant::TRANSFORM2D: {422_create_subproperty(p_parent_item, "origin", Variant::VECTOR2);423_create_subproperty(p_parent_item, "x", Variant::VECTOR2);424_create_subproperty(p_parent_item, "y", Variant::VECTOR2);425} break;426427case Variant::VECTOR4: {428_create_subproperty(p_parent_item, "x", Variant::FLOAT);429_create_subproperty(p_parent_item, "y", Variant::FLOAT);430_create_subproperty(p_parent_item, "z", Variant::FLOAT);431_create_subproperty(p_parent_item, "w", Variant::FLOAT);432} break;433434case Variant::VECTOR4I: {435_create_subproperty(p_parent_item, "x", Variant::INT);436_create_subproperty(p_parent_item, "y", Variant::INT);437_create_subproperty(p_parent_item, "z", Variant::INT);438_create_subproperty(p_parent_item, "w", Variant::INT);439} break;440441case Variant::PLANE: {442_create_subproperty(p_parent_item, "x", Variant::FLOAT);443_create_subproperty(p_parent_item, "y", Variant::FLOAT);444_create_subproperty(p_parent_item, "z", Variant::FLOAT);445_create_subproperty(p_parent_item, "normal", Variant::VECTOR3);446_create_subproperty(p_parent_item, "d", Variant::FLOAT);447} break;448449case Variant::QUATERNION: {450_create_subproperty(p_parent_item, "x", Variant::FLOAT);451_create_subproperty(p_parent_item, "y", Variant::FLOAT);452_create_subproperty(p_parent_item, "z", Variant::FLOAT);453_create_subproperty(p_parent_item, "w", Variant::FLOAT);454} break;455456case Variant::AABB: {457_create_subproperty(p_parent_item, "position", Variant::VECTOR3);458_create_subproperty(p_parent_item, "size", Variant::VECTOR3);459_create_subproperty(p_parent_item, "end", Variant::VECTOR3);460} break;461462case Variant::BASIS: {463_create_subproperty(p_parent_item, "x", Variant::VECTOR3);464_create_subproperty(p_parent_item, "y", Variant::VECTOR3);465_create_subproperty(p_parent_item, "z", Variant::VECTOR3);466} break;467468case Variant::TRANSFORM3D: {469_create_subproperty(p_parent_item, "basis", Variant::BASIS);470_create_subproperty(p_parent_item, "origin", Variant::VECTOR3);471} break;472473case Variant::PROJECTION: {474_create_subproperty(p_parent_item, "x", Variant::VECTOR4);475_create_subproperty(p_parent_item, "y", Variant::VECTOR4);476_create_subproperty(p_parent_item, "z", Variant::VECTOR4);477_create_subproperty(p_parent_item, "w", Variant::VECTOR4);478} break;479480case Variant::COLOR: {481_create_subproperty(p_parent_item, "r", Variant::FLOAT);482_create_subproperty(p_parent_item, "g", Variant::FLOAT);483_create_subproperty(p_parent_item, "b", Variant::FLOAT);484_create_subproperty(p_parent_item, "a", Variant::FLOAT);485_create_subproperty(p_parent_item, "r8", Variant::INT);486_create_subproperty(p_parent_item, "g8", Variant::INT);487_create_subproperty(p_parent_item, "b8", Variant::INT);488_create_subproperty(p_parent_item, "a8", Variant::INT);489_create_subproperty(p_parent_item, "h", Variant::FLOAT);490_create_subproperty(p_parent_item, "s", Variant::FLOAT);491_create_subproperty(p_parent_item, "v", Variant::FLOAT);492} break;493494default: {495}496}497}498499void PropertySelector::_create_subproperty(TreeItem *p_parent_item, const String &p_name, Variant::Type p_type) {500if (!type_filter.is_empty() && !type_filter.has(p_type)) {501return;502}503504TreeItem *item = search_options->create_item(p_parent_item);505item->set_text(0, p_name);506item->set_metadata(0, String(p_parent_item->get_metadata(0)) + ":" + p_name);507item->set_icon(0, search_options->get_editor_theme_icon(Variant::get_type_name(p_type)));508509_create_subproperties(item, p_type);510}511512void PropertySelector::_notification(int p_what) {513switch (p_what) {514case NOTIFICATION_ENTER_TREE: {515connect(SceneStringName(confirmed), callable_mp(this, &PropertySelector::_confirmed));516} break;517518case NOTIFICATION_EXIT_TREE: {519disconnect(SceneStringName(confirmed), callable_mp(this, &PropertySelector::_confirmed));520} break;521}522}523524void PropertySelector::select_method_from_base_type(const String &p_base, const String &p_current, bool p_virtuals_only) {525base_type = p_base;526selected = p_current;527type = Variant::NIL;528script = ObjectID();529properties = false;530instance = nullptr;531virtuals_only = p_virtuals_only;532533popup_centered_ratio(0.6);534search_box->set_text("");535search_box->grab_focus();536_update_search();537}538539void PropertySelector::select_method_from_script(const Ref<Script> &p_script, const String &p_current) {540ERR_FAIL_COND(p_script.is_null());541base_type = p_script->get_instance_base_type();542selected = p_current;543type = Variant::NIL;544script = p_script->get_instance_id();545properties = false;546instance = nullptr;547virtuals_only = false;548549popup_centered_ratio(0.6);550search_box->set_text("");551search_box->grab_focus();552_update_search();553}554555void PropertySelector::select_method_from_basic_type(Variant::Type p_type, const String &p_current) {556ERR_FAIL_COND(p_type == Variant::NIL);557base_type = "";558selected = p_current;559type = p_type;560script = ObjectID();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_method_from_instance(Object *p_instance, const String &p_current) {572base_type = p_instance->get_class();573selected = p_current;574type = Variant::NIL;575script = ObjectID();576{577Ref<Script> scr = p_instance->get_script();578if (scr.is_valid()) {579script = scr->get_instance_id();580}581}582properties = false;583instance = nullptr;584virtuals_only = false;585586popup_centered_ratio(0.6);587search_box->set_text("");588search_box->grab_focus();589_update_search();590}591592void PropertySelector::select_property_from_base_type(const String &p_base, const String &p_current) {593base_type = p_base;594selected = p_current;595type = Variant::NIL;596script = ObjectID();597properties = true;598instance = nullptr;599virtuals_only = false;600601popup_centered_ratio(0.6);602search_box->set_text("");603search_box->grab_focus();604_update_search();605}606607void PropertySelector::select_property_from_script(const Ref<Script> &p_script, const String &p_current) {608ERR_FAIL_COND(p_script.is_null());609610base_type = p_script->get_instance_base_type();611selected = p_current;612type = Variant::NIL;613script = p_script->get_instance_id();614properties = true;615instance = nullptr;616virtuals_only = false;617618popup_centered_ratio(0.6);619search_box->set_text("");620search_box->grab_focus();621_update_search();622}623624void PropertySelector::select_property_from_basic_type(Variant::Type p_type, const String &p_current) {625ERR_FAIL_COND(p_type == Variant::NIL);626base_type = "";627selected = p_current;628type = p_type;629script = ObjectID();630properties = true;631instance = nullptr;632virtuals_only = false;633634popup_centered_ratio(0.6);635search_box->set_text("");636search_box->grab_focus();637_update_search();638}639640void PropertySelector::select_property_from_instance(Object *p_instance, const String &p_current) {641base_type = "";642selected = p_current;643type = Variant::NIL;644script = ObjectID();645properties = true;646instance = p_instance;647virtuals_only = false;648649popup_centered_ratio(0.6);650search_box->set_text("");651search_box->grab_focus();652_update_search();653}654655void PropertySelector::set_type_filter(const Vector<Variant::Type> &p_type_filter) {656type_filter = p_type_filter;657}658659void PropertySelector::_bind_methods() {660ADD_SIGNAL(MethodInfo("selected", PropertyInfo(Variant::STRING, "name")));661}662663PropertySelector::PropertySelector() {664VBoxContainer *vbc = memnew(VBoxContainer);665add_child(vbc);666//set_child_rect(vbc);667search_box = memnew(LineEdit);668vbc->add_margin_child(TTR("Search:"), search_box);669search_box->connect(SceneStringName(text_changed), callable_mp(this, &PropertySelector::_text_changed));670search_box->connect(SceneStringName(gui_input), callable_mp(this, &PropertySelector::_sbox_input));671search_options = memnew(Tree);672search_options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);673vbc->add_margin_child(TTR("Matches:"), search_options, true);674set_ok_button_text(TTR("Open"));675get_ok_button()->set_disabled(true);676register_text_enter(search_box);677set_hide_on_ok(false);678search_options->connect("item_activated", callable_mp(this, &PropertySelector::_confirmed));679search_options->connect("cell_selected", callable_mp(this, &PropertySelector::_item_selected));680search_options->set_hide_root(true);681682help_bit = memnew(EditorHelpBit);683help_bit->set_content_height_limits(80 * EDSCALE, 80 * EDSCALE);684help_bit->connect("request_hide", callable_mp(this, &PropertySelector::_hide_requested));685vbc->add_margin_child(TTR("Description:"), help_bit);686}687688689