Path: blob/master/editor/inspector/editor_properties.cpp
20897 views
/**************************************************************************/1/* editor_properties.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 "editor_properties.h"3132#include "core/config/project_settings.h"33#include "core/input/input_map.h"34#include "core/string/translation_server.h"35#include "editor/docks/inspector_dock.h"36#include "editor/docks/scene_tree_dock.h"37#include "editor/editor_node.h"38#include "editor/editor_string_names.h"39#include "editor/gui/create_dialog.h"40#include "editor/gui/editor_file_dialog.h"41#include "editor/gui/editor_spin_slider.h"42#include "editor/gui/editor_variant_type_selectors.h"43#include "editor/inspector/editor_properties_array_dict.h"44#include "editor/inspector/editor_properties_vector.h"45#include "editor/inspector/editor_resource_picker.h"46#include "editor/inspector/property_selector.h"47#include "editor/scene/scene_tree_editor.h"48#include "editor/script/syntax_highlighters.h"49#include "editor/settings/editor_settings.h"50#include "editor/settings/project_settings_editor.h"51#include "editor/themes/editor_scale.h"52#include "scene/2d/gpu_particles_2d.h"53#include "scene/3d/fog_volume.h"54#include "scene/3d/gpu_particles_3d.h"55#include "scene/gui/color_picker.h"56#include "scene/gui/grid_container.h"57#include "scene/gui/text_edit.h"58#include "scene/main/window.h"59#include "scene/resources/font.h"60#include "scene/resources/mesh.h"61#include "scene/resources/sky.h"62#include "scene/resources/visual_shader_nodes.h"6364///////////////////// NIL /////////////////////////6566void EditorPropertyNil::update_property() {67}6869EditorPropertyNil::EditorPropertyNil() {70Label *prop_label = memnew(Label);71prop_label->set_text("<null>");72add_child(prop_label);73}7475//////////////////// VARIANT ///////////////////////7677void EditorPropertyVariant::_change_type(int p_to_type) {78new_type = Variant::Type(p_to_type);7980Variant zero;81Callable::CallError ce;82Variant::construct(new_type, zero, nullptr, 0, ce);83emit_changed(get_edited_property(), zero);84}8586void EditorPropertyVariant::_popup_edit_menu() {87if (change_type == nullptr) {88change_type = memnew(EditorVariantTypePopupMenu(false));89change_type->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyVariant::_change_type));90content->add_child(change_type);91}9293Rect2 rect = edit_button->get_screen_rect();94change_type->set_position(rect.get_end() - Vector2(change_type->get_contents_minimum_size().x, 0));95change_type->popup();96}9798void EditorPropertyVariant::_set_read_only(bool p_read_only) {99edit_button->set_disabled(p_read_only);100if (sub_property) {101sub_property->set_read_only(p_read_only);102}103}104105void EditorPropertyVariant::_notification(int p_what) {106if (p_what == NOTIFICATION_THEME_CHANGED) {107edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));108}109}110111void EditorPropertyVariant::update_property() {112const Variant &value = get_edited_property_value();113if (new_type == Variant::VARIANT_MAX) {114new_type = value.get_type();115}116117if (new_type != current_type) {118current_type = new_type;119120if (sub_property) {121memdelete(sub_property);122sub_property = nullptr;123}124125if (current_type == Variant::OBJECT) {126sub_property = EditorInspector::instantiate_property_editor(nullptr, current_type, "", PROPERTY_HINT_RESOURCE_TYPE, Resource::get_class_static(), PROPERTY_USAGE_NONE);127} else {128sub_property = EditorInspector::instantiate_property_editor(nullptr, current_type, "", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE);129}130ERR_FAIL_NULL(sub_property);131132sub_property->set_object_and_property(get_edited_object(), get_edited_property());133sub_property->set_name_split_ratio(0);134sub_property->set_selectable(false);135sub_property->set_use_folding(is_using_folding());136sub_property->set_read_only(is_read_only());137sub_property->set_h_size_flags(SIZE_EXPAND_FILL);138sub_property->connect(SNAME("property_changed"), callable_mp((EditorProperty *)this, &EditorProperty::emit_changed));139content->add_child(sub_property);140content->move_child(sub_property, 0);141sub_property->update_property();142} else if (sub_property) {143sub_property->update_property();144}145new_type = Variant::VARIANT_MAX;146}147148EditorPropertyVariant::EditorPropertyVariant() {149content = memnew(HBoxContainer);150add_child(content);151152edit_button = memnew(Button);153edit_button->set_flat(true);154edit_button->set_theme_type_variation(SNAME("EditorInspectorButton"));155edit_button->set_accessibility_name(TTRC("Edit"));156edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyVariant::_popup_edit_menu));157content->add_child(edit_button);158}159160///////////////////// TEXT /////////////////////////161162void EditorPropertyText::_notification(int p_what) {163switch (p_what) {164case NOTIFICATION_THEME_CHANGED: {165_update_theme();166} break;167}168}169170void EditorPropertyText::_set_read_only(bool p_read_only) {171text->set_editable(!p_read_only);172}173174void EditorPropertyText::_update_theme() {175Ref<Font> font;176int font_size;177178if (monospaced) {179font = get_theme_font(SNAME("source"), EditorStringName(EditorFonts));180font_size = get_theme_font_size(SNAME("source_size"), EditorStringName(EditorFonts));181} else {182font = get_theme_font(SceneStringName(font), SNAME("LineEdit"));183font_size = get_theme_font_size(SceneStringName(font_size), SNAME("LineEdit"));184}185186text->add_theme_font_override(SceneStringName(font), font);187text->add_theme_font_size_override(SceneStringName(font_size), font_size);188}189190void EditorPropertyText::_text_submitted(const String &p_string) {191if (updating) {192return;193}194195if (text->has_focus()) {196_text_changed(p_string);197}198}199200void EditorPropertyText::_text_changed(const String &p_string) {201if (updating) {202return;203}204205// Set tooltip so that the full text is displayed in a tooltip if hovered.206// This is useful when using a narrow inspector, as the text can be trimmed otherwise.207if (text->is_secret()) {208text->set_tooltip_text(get_tooltip_string(text->get_placeholder()));209} else {210text->set_tooltip_text(get_tooltip_string(text->get_text()));211}212213if (string_name) {214emit_changed(get_edited_property(), StringName(p_string));215} else {216emit_changed(get_edited_property(), p_string);217}218}219220void EditorPropertyText::update_property() {221String s = get_edited_property_value();222updating = true;223if (text->get_text() != s) {224int caret = text->get_caret_column();225text->set_text(s);226if (text->is_secret()) {227text->set_tooltip_text(get_tooltip_string(text->get_placeholder()));228} else {229text->set_tooltip_text(get_tooltip_string(s));230}231text->set_caret_column(caret);232}233text->set_editable(!is_read_only());234updating = false;235}236237void EditorPropertyText::set_string_name(bool p_enabled) {238string_name = p_enabled;239if (p_enabled) {240Label *prefix = memnew(Label("&"));241prefix->set_tooltip_text("StringName");242prefix->set_mouse_filter(MOUSE_FILTER_STOP);243text->get_parent()->add_child(prefix);244text->get_parent()->move_child(prefix, 0);245}246}247248void EditorPropertyText::set_secret(bool p_enabled) {249text->set_secret(p_enabled);250}251252void EditorPropertyText::set_placeholder(const String &p_string) {253text->set_placeholder(p_string);254}255256void EditorPropertyText::set_monospaced(bool p_monospaced) {257if (p_monospaced == monospaced) {258return;259}260monospaced = p_monospaced;261_update_theme();262}263264EditorPropertyText::EditorPropertyText() {265HBoxContainer *hb = memnew(HBoxContainer);266add_child(hb);267268text = memnew(LineEdit);269text->set_h_size_flags(SIZE_EXPAND_FILL);270text->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Prevents translating placeholder.271hb->add_child(text);272add_focusable(text);273text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyText::_text_changed));274text->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyText::_text_submitted));275}276277///////////////////// MULTILINE TEXT /////////////////////////278279void EditorPropertyMultilineText::_set_read_only(bool p_read_only) {280text->set_editable(!p_read_only);281open_big_text->set_disabled(p_read_only);282}283284void EditorPropertyMultilineText::_big_text_changed() {285text->set_text(big_text->get_text());286// Set tooltip so that the full text is displayed in a tooltip if hovered.287// This is useful when using a narrow inspector, as the text can be trimmed otherwise.288text->set_tooltip_text(get_tooltip_string(big_text->get_text()));289emit_changed(get_edited_property(), big_text->get_text(), "", true);290}291292void EditorPropertyMultilineText::_text_changed() {293text->set_tooltip_text(get_tooltip_string(text->get_text()));294emit_changed(get_edited_property(), text->get_text(), "", true);295}296297void EditorPropertyMultilineText::_open_big_text() {298if (!big_text_dialog) {299big_text = memnew(TextEdit);300if (expression) {301big_text->set_syntax_highlighter(text->get_syntax_highlighter());302}303big_text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyMultilineText::_big_text_changed));304big_text->set_line_wrapping_mode(wrap_lines305? TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY306: TextEdit::LineWrappingMode::LINE_WRAPPING_NONE);307big_text_dialog = memnew(AcceptDialog);308big_text_dialog->add_child(big_text);309big_text_dialog->set_title(TTR("Edit Text:"));310add_child(big_text_dialog);311}312313big_text_dialog->popup_centered_clamped(Size2(1000, 900) * EDSCALE, 0.8);314big_text->set_text(text->get_text());315big_text->grab_focus();316317_update_theme();318}319320void EditorPropertyMultilineText::update_property() {321String t = get_edited_property_value();322if (text->get_text() != t) {323text->set_text(t);324text->set_tooltip_text(get_tooltip_string(t));325if (big_text && big_text->is_visible_in_tree()) {326big_text->set_text(t);327}328}329}330331void EditorPropertyMultilineText::_update_theme() {332Ref<Texture2D> df = get_editor_theme_icon(SNAME("DistractionFree"));333open_big_text->set_button_icon(df);334335Ref<Font> font;336int font_size;337if (expression) {338font = get_theme_font(SNAME("expression"), EditorStringName(EditorFonts));339font_size = get_theme_font_size(SNAME("expression_size"), EditorStringName(EditorFonts));340} else {341// Non expression.342if (monospaced) {343font = get_theme_font(SNAME("source"), EditorStringName(EditorFonts));344font_size = get_theme_font_size(SNAME("source_size"), EditorStringName(EditorFonts));345} else {346font = get_theme_font(SceneStringName(font), SNAME("TextEdit"));347font_size = get_theme_font_size(SceneStringName(font_size), SNAME("TextEdit"));348}349}350text->add_theme_font_override(SceneStringName(font), font);351text->add_theme_font_size_override(SceneStringName(font_size), font_size);352text->set_line_wrapping_mode(wrap_lines353? TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY354: TextEdit::LineWrappingMode::LINE_WRAPPING_NONE);355if (big_text) {356big_text->add_theme_font_override(SceneStringName(font), font);357big_text->add_theme_font_size_override(SceneStringName(font_size), font_size);358big_text->set_line_wrapping_mode(wrap_lines359? TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY360: TextEdit::LineWrappingMode::LINE_WRAPPING_NONE);361}362363text->set_custom_minimum_size(Vector2(0, font->get_height(font_size) * 6));364}365366void EditorPropertyMultilineText::_notification(int p_what) {367switch (p_what) {368case NOTIFICATION_THEME_CHANGED: {369_update_theme();370} break;371}372}373374void EditorPropertyMultilineText::EditorPropertyMultilineText::set_monospaced(bool p_monospaced) {375if (p_monospaced == monospaced) {376return;377}378monospaced = p_monospaced;379_update_theme();380}381382bool EditorPropertyMultilineText::EditorPropertyMultilineText::get_monospaced() {383return monospaced;384}385386void EditorPropertyMultilineText::EditorPropertyMultilineText::set_wrap_lines(bool p_wrap_lines) {387if (p_wrap_lines == wrap_lines) {388return;389}390wrap_lines = p_wrap_lines;391_update_theme();392}393394bool EditorPropertyMultilineText::EditorPropertyMultilineText::get_wrap_lines() {395return wrap_lines;396}397398EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) :399expression(p_expression) {400HBoxContainer *hb = memnew(HBoxContainer);401hb->add_theme_constant_override("separation", 0);402add_child(hb);403set_bottom_editor(hb);404text = memnew(TextEdit);405text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyMultilineText::_text_changed));406text->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);407add_focusable(text);408hb->add_child(text);409text->set_h_size_flags(SIZE_EXPAND_FILL);410open_big_text = memnew(Button);411open_big_text->set_accessibility_name(TTRC("Open Text Edit Dialog"));412open_big_text->set_flat(true);413open_big_text->set_theme_type_variation(SNAME("EditorInspectorButton"));414open_big_text->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyMultilineText::_open_big_text));415hb->add_child(open_big_text);416big_text_dialog = nullptr;417big_text = nullptr;418419if (expression) {420Ref<EditorStandardSyntaxHighlighter> highlighter;421highlighter.instantiate();422text->set_syntax_highlighter(highlighter);423}424}425426///////////////////// TEXT ENUM /////////////////////////427428void EditorPropertyTextEnum::_set_read_only(bool p_read_only) {429option_button->set_disabled(p_read_only);430edit_button->set_disabled(p_read_only);431}432433void EditorPropertyTextEnum::_emit_changed_value(const String &p_string) {434if (string_name) {435emit_changed(get_edited_property(), StringName(p_string));436} else {437emit_changed(get_edited_property(), p_string);438}439}440441void EditorPropertyTextEnum::_option_selected(int p_which) {442_emit_changed_value(option_button->get_item_metadata(p_which));443}444445void EditorPropertyTextEnum::_edit_custom_value() {446default_layout->hide();447edit_custom_layout->show();448custom_value_edit->grab_focus(true);449}450451void EditorPropertyTextEnum::_custom_value_submitted(const String &p_value) {452edit_custom_layout->hide();453default_layout->show();454455_emit_changed_value(p_value.strip_edges());456}457458void EditorPropertyTextEnum::_custom_value_accepted() {459String new_value = custom_value_edit->get_text().strip_edges();460_custom_value_submitted(new_value);461}462463void EditorPropertyTextEnum::_custom_value_canceled() {464custom_value_edit->set_text(get_edited_property_value());465466edit_custom_layout->hide();467default_layout->show();468}469470void EditorPropertyTextEnum::update_property() {471String current_value = get_edited_property_value();472int default_option = options.find(current_value);473474// The list can change in the loose mode.475if (loose_mode) {476custom_value_edit->set_text(current_value);477option_button->clear();478479// Manually entered value.480if (default_option < 0 && !current_value.is_empty()) {481option_button->add_item(current_value, options.size() + 1001);482option_button->set_item_metadata(-1, current_value);483option_button->select(0);484485option_button->add_separator();486}487488// Add an explicit empty value for clearing the property.489option_button->add_item("", options.size() + 1000);490option_button->set_item_metadata(-1, String());491492for (int i = 0; i < options.size(); i++) {493option_button->add_item(option_names[i], i);494option_button->set_item_metadata(-1, options[i]);495if (options[i] == current_value) {496option_button->select(option_button->get_item_count() - 1);497}498}499} else {500option_button->select(default_option);501if (default_option < 0) {502option_button->set_text(current_value);503}504}505}506507void EditorPropertyTextEnum::setup(const Vector<String> &p_options, const Vector<String> &p_option_names, bool p_string_name, bool p_loose_mode) {508ERR_FAIL_COND(!p_option_names.is_empty() && p_option_names.size() != p_options.size());509510string_name = p_string_name;511loose_mode = p_loose_mode;512513options = p_options;514if (p_option_names.is_empty()) {515option_names = p_options;516} else {517option_names = p_option_names;518}519520if (loose_mode) {521// Add an explicit empty value for clearing the property in the loose mode.522option_button->add_item("", options.size() + 1000);523option_button->set_item_metadata(-1, String());524}525526for (int i = 0; i < options.size(); i++) {527option_button->add_item(option_names[i], i);528option_button->set_item_metadata(-1, options[i]);529}530531if (loose_mode) {532edit_button->show();533}534}535536void EditorPropertyTextEnum::_notification(int p_what) {537switch (p_what) {538case NOTIFICATION_THEME_CHANGED: {539edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));540accept_button->set_button_icon(get_editor_theme_icon(SNAME("ImportCheck")));541cancel_button->set_button_icon(get_editor_theme_icon(SNAME("ImportFail")));542} break;543}544}545546EditorPropertyTextEnum::EditorPropertyTextEnum() {547HBoxContainer *hb = memnew(HBoxContainer);548add_child(hb);549550default_layout = memnew(HBoxContainer);551default_layout->set_h_size_flags(SIZE_EXPAND_FILL);552hb->add_child(default_layout);553554edit_custom_layout = memnew(HBoxContainer);555edit_custom_layout->set_h_size_flags(SIZE_EXPAND_FILL);556edit_custom_layout->hide();557hb->add_child(edit_custom_layout);558559option_button = memnew(OptionButton);560option_button->set_accessibility_name(TTRC("Enum Options"));561option_button->set_h_size_flags(SIZE_EXPAND_FILL);562option_button->set_clip_text(true);563option_button->set_flat(true);564option_button->set_theme_type_variation(SNAME("EditorInspectorButton"));565option_button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);566default_layout->add_child(option_button);567option_button->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyTextEnum::_option_selected));568569edit_button = memnew(Button);570edit_button->set_accessibility_name(TTRC("Edit"));571edit_button->set_flat(true);572edit_button->set_theme_type_variation(SNAME("EditorInspectorButton"));573edit_button->hide();574default_layout->add_child(edit_button);575edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_edit_custom_value));576577custom_value_edit = memnew(LineEdit);578custom_value_edit->set_accessibility_name(TTRC("Custom Value"));579custom_value_edit->set_h_size_flags(SIZE_EXPAND_FILL);580edit_custom_layout->add_child(custom_value_edit);581custom_value_edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyTextEnum::_custom_value_submitted));582583accept_button = memnew(Button);584accept_button->set_accessibility_name(TTRC("Accept Custom Value Edit"));585accept_button->set_flat(true);586accept_button->set_theme_type_variation(SNAME("EditorInspectorButton"));587edit_custom_layout->add_child(accept_button);588accept_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_custom_value_accepted));589590cancel_button = memnew(Button);591cancel_button->set_accessibility_name(TTRC("Cancel Custom Value Edit"));592cancel_button->set_flat(true);593cancel_button->set_theme_type_variation(SNAME("EditorInspectorButton"));594edit_custom_layout->add_child(cancel_button);595cancel_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_custom_value_canceled));596597add_focusable(option_button);598add_focusable(edit_button);599add_focusable(custom_value_edit);600add_focusable(accept_button);601add_focusable(cancel_button);602}603604//////////////////// LOCALE ////////////////////////605606void EditorPropertyLocale::_locale_selected(const String &p_locale) {607emit_changed(get_edited_property(), p_locale);608update_property();609}610611void EditorPropertyLocale::_locale_pressed() {612if (!dialog) {613dialog = memnew(EditorLocaleDialog);614dialog->connect("locale_selected", callable_mp(this, &EditorPropertyLocale::_locale_selected));615add_child(dialog);616}617618String locale_code = get_edited_property_value();619dialog->set_locale(locale_code);620dialog->popup_locale_dialog();621}622623void EditorPropertyLocale::update_property() {624String locale_code = get_edited_property_value();625locale->set_text(locale_code);626locale->set_tooltip_text(locale_code);627}628629void EditorPropertyLocale::setup(const String &p_hint_text) {630}631632void EditorPropertyLocale::_notification(int p_what) {633switch (p_what) {634case NOTIFICATION_THEME_CHANGED: {635locale_edit->set_button_icon(get_editor_theme_icon(SNAME("Translation")));636} break;637}638}639640void EditorPropertyLocale::_locale_focus_exited() {641_locale_selected(locale->get_text());642}643644EditorPropertyLocale::EditorPropertyLocale() {645HBoxContainer *locale_hb = memnew(HBoxContainer);646add_child(locale_hb);647locale = memnew(LineEdit);648locale->set_accessibility_name(TTRC("Locale"));649locale_hb->add_child(locale);650locale->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyLocale::_locale_selected));651locale->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyLocale::_locale_focus_exited));652locale->set_h_size_flags(SIZE_EXPAND_FILL);653654locale_edit = memnew(Button);655locale_edit->set_accessibility_name(TTRC("Edit"));656locale_edit->set_clip_text(true);657locale_edit->set_theme_type_variation(SNAME("EditorInspectorButton"));658locale_hb->add_child(locale_edit);659add_focusable(locale);660dialog = nullptr;661locale_edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLocale::_locale_pressed));662}663664///////////////////// PATH /////////////////////////665666void EditorPropertyPath::_set_read_only(bool p_read_only) {667path->set_editable(!p_read_only);668path_edit->set_disabled(p_read_only);669}670671void EditorPropertyPath::_path_selected(const String &p_path) {672String full_path = p_path;673674if (enable_uid) {675const ResourceUID::ID id = ResourceLoader::get_resource_uid(full_path);676if (id != ResourceUID::INVALID_ID) {677full_path = ResourceUID::get_singleton()->id_to_text(id);678}679}680681emit_changed(get_edited_property(), full_path);682update_property();683}684685String EditorPropertyPath::_get_path_text(bool p_allow_uid) {686String full_path = get_edited_property_value();687if (!p_allow_uid && full_path.begins_with("uid://")) {688full_path = ResourceUID::uid_to_path(full_path);689}690691return full_path;692}693694void EditorPropertyPath::_path_pressed() {695if (!dialog) {696dialog = memnew(EditorFileDialog);697dialog->connect("file_selected", callable_mp(this, &EditorPropertyPath::_path_selected));698dialog->connect("dir_selected", callable_mp(this, &EditorPropertyPath::_path_selected));699add_child(dialog);700}701702String full_path = _get_path_text();703704dialog->clear_filters();705706if (global) {707dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);708} else {709dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);710}711712if (folder) {713dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);714dialog->set_current_dir(full_path);715} else {716dialog->set_file_mode(save_mode ? EditorFileDialog::FILE_MODE_SAVE_FILE : EditorFileDialog::FILE_MODE_OPEN_FILE);717for (int i = 0; i < extensions.size(); i++) {718String e = extensions[i].strip_edges();719if (!e.is_empty()) {720dialog->add_filter(extensions[i].strip_edges());721}722}723dialog->set_current_path(full_path);724}725726dialog->popup_file_dialog();727}728729void EditorPropertyPath::update_property() {730String full_path = _get_path_text(display_uid);731path->set_text(full_path);732path->set_tooltip_text(full_path);733734toggle_uid->set_visible(get_edited_property_value().operator String().begins_with("uid://"));735}736737void EditorPropertyPath::setup(const Vector<String> &p_extensions, bool p_folder, bool p_global, bool p_enable_uid) {738extensions = p_extensions;739folder = p_folder;740global = p_global;741enable_uid = p_enable_uid;742}743744void EditorPropertyPath::set_save_mode() {745save_mode = true;746}747748void EditorPropertyPath::_notification(int p_what) {749switch (p_what) {750case NOTIFICATION_THEME_CHANGED: {751if (folder) {752path_edit->set_button_icon(get_editor_theme_icon(SNAME("FolderBrowse")));753} else {754path_edit->set_button_icon(get_editor_theme_icon(SNAME("FileBrowse")));755}756_update_uid_icon();757} break;758}759}760761void EditorPropertyPath::_path_focus_exited() {762_path_selected(path->get_text());763}764765void EditorPropertyPath::_toggle_uid_display() {766display_uid = !display_uid;767_update_uid_icon();768update_property();769}770771void EditorPropertyPath::_update_uid_icon() {772toggle_uid->set_button_icon(get_editor_theme_icon(display_uid ? SNAME("UID") : SNAME("NodePath")));773}774775void EditorPropertyPath::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {776const Dictionary drag_data = p_data;777if (!drag_data.has("type")) {778return;779}780if (String(drag_data["type"]) != "files") {781return;782}783const Vector<String> filesPaths = drag_data["files"];784if (filesPaths.is_empty()) {785return;786}787788_path_selected(filesPaths[0]);789}790791bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {792const Dictionary drag_data = p_data;793if (!drag_data.has("type")) {794return false;795}796if (String(drag_data["type"]) != "files") {797return false;798}799const Vector<String> filesPaths = drag_data["files"];800if (filesPaths.is_empty()) {801return false;802}803804for (const String &extension : extensions) {805if (filesPaths[0].ends_with(extension.substr(1))) {806return true;807}808}809810return false;811}812813EditorPropertyPath::EditorPropertyPath() {814HBoxContainer *path_hb = memnew(HBoxContainer);815add_child(path_hb);816path = memnew(LineEdit);817path->set_accessibility_name(TTRC("Path"));818SET_DRAG_FORWARDING_CDU(path, EditorPropertyPath);819path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);820path_hb->add_child(path);821path->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyPath::_path_selected));822path->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyPath::_path_focus_exited));823path->set_h_size_flags(SIZE_EXPAND_FILL);824825toggle_uid = memnew(Button);826toggle_uid->set_accessibility_name(TTRC("Toggle Display UID"));827toggle_uid->set_tooltip_text(TTRC("Toggles displaying between path and UID.\nThe UID is the actual value of this property."));828toggle_uid->set_pressed(false);829toggle_uid->set_theme_type_variation(SNAME("EditorInspectorButton"));830path_hb->add_child(toggle_uid);831add_focusable(toggle_uid);832toggle_uid->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyPath::_toggle_uid_display));833834path_edit = memnew(Button);835path_edit->set_theme_type_variation(SNAME("EditorInspectorButton"));836path_edit->set_accessibility_name(TTRC("Edit"));837path_hb->add_child(path_edit);838add_focusable(path);839path_edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyPath::_path_pressed));840}841842///////////////////// CLASS NAME /////////////////////////843844void EditorPropertyClassName::_set_read_only(bool p_read_only) {845property->set_disabled(p_read_only);846}847848void EditorPropertyClassName::setup(const String &p_base_type, const String &p_selected_type) {849base_type = p_base_type;850dialog->set_base_type(base_type);851selected_type = p_selected_type;852property->set_text(selected_type);853}854855void EditorPropertyClassName::update_property() {856String s = get_edited_property_value();857property->set_text(s);858selected_type = s;859}860861void EditorPropertyClassName::_property_selected() {862dialog->popup_create(true, true, get_edited_property_value(), get_edited_property());863}864865void EditorPropertyClassName::_dialog_created() {866selected_type = dialog->get_selected_type();867emit_changed(get_edited_property(), selected_type);868update_property();869}870871EditorPropertyClassName::EditorPropertyClassName() {872property = memnew(Button);873property->set_clip_text(true);874property->set_theme_type_variation(SNAME("EditorInspectorButton"));875add_child(property);876add_focusable(property);877property->set_text(selected_type);878property->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyClassName::_property_selected));879dialog = memnew(CreateDialog);880dialog->set_base_type(base_type);881dialog->connect("create", callable_mp(this, &EditorPropertyClassName::_dialog_created));882add_child(dialog);883}884885///////////////////// CHECK /////////////////////////886887void EditorPropertyCheck::_set_read_only(bool p_read_only) {888checkbox->set_disabled(p_read_only);889}890891void EditorPropertyCheck::_checkbox_pressed() {892emit_changed(get_edited_property(), checkbox->is_pressed());893}894895void EditorPropertyCheck::update_property() {896bool c = get_edited_property_value();897checkbox->set_pressed(c);898checkbox->set_disabled(is_read_only());899}900901EditorPropertyCheck::EditorPropertyCheck() {902checkbox = memnew(CheckBox);903checkbox->set_text(TTR("On"));904add_child(checkbox);905add_focusable(checkbox);906checkbox->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyCheck::_checkbox_pressed));907}908909///////////////////// ENUM /////////////////////////910911void EditorPropertyEnum::_set_read_only(bool p_read_only) {912options->set_disabled(p_read_only);913}914915void EditorPropertyEnum::_option_selected(int p_which) {916int64_t val = options->get_item_metadata(p_which);917emit_changed(get_edited_property(), val);918}919920void EditorPropertyEnum::update_property() {921Variant current = get_edited_property_value();922if (current.get_type() == Variant::NIL) {923options->select(-1);924options->set_text("<null>");925return;926}927928int64_t which = current;929for (int i = 0; i < options->get_item_count(); i++) {930if (which == (int64_t)options->get_item_metadata(i)) {931options->select(i);932return;933}934}935options->select(-1);936options->set_text(itos(which));937}938939void EditorPropertyEnum::setup(const Vector<String> &p_options) {940options->clear();941HashMap<int64_t, Vector<String>> items;942int64_t current_val = 0;943for (const String &option : p_options) {944if (option.get_slice_count(":") != 1) {945current_val = option.get_slicec(':', 1).to_int();946}947items[current_val].push_back(option.get_slicec(':', 0));948current_val += 1;949}950951for (const KeyValue<int64_t, Vector<String>> &K : items) {952options->add_item(String(", ").join(K.value));953options->set_item_metadata(-1, K.key);954}955}956957void EditorPropertyEnum::set_option_button_clip(bool p_enable) {958options->set_clip_text(p_enable);959}960961OptionButton *EditorPropertyEnum::get_option_button() {962return options;963}964965EditorPropertyEnum::EditorPropertyEnum() {966options = memnew(OptionButton);967options->set_clip_text(true);968options->set_flat(true);969options->set_theme_type_variation(SNAME("EditorInspectorButton"));970options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);971add_child(options);972add_focusable(options);973options->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyEnum::_option_selected));974}975976///////////////////// FLAGS /////////////////////////977978void EditorPropertyFlags::_set_read_only(bool p_read_only) {979for (CheckBox *check : flags) {980check->set_disabled(p_read_only);981}982}983984void EditorPropertyFlags::_flag_toggled(int p_index) {985uint32_t value = get_edited_property_value();986if (flags[p_index]->is_pressed()) {987value |= flag_values[p_index];988} else {989value &= ~flag_values[p_index];990}991992emit_changed(get_edited_property(), value);993}994995void EditorPropertyFlags::update_property() {996uint32_t value = get_edited_property_value();997998for (int i = 0; i < flags.size(); i++) {999flags[i]->set_pressed((value & flag_values[i]) == flag_values[i]);1000}1001}10021003void EditorPropertyFlags::setup(const Vector<String> &p_options) {1004ERR_FAIL_COND(flags.size());10051006bool first = true;1007uint32_t current_val;1008for (int i = 0; i < p_options.size(); i++) {1009// An empty option is not considered a "flag".1010String option = p_options[i].strip_edges();1011if (option.is_empty()) {1012continue;1013}1014const int flag_index = flags.size(); // Index of the next element (added by the code below).10151016// Value for a flag can be explicitly overridden.1017Vector<String> text_split = option.split(":");1018if (text_split.size() != 1) {1019current_val = text_split[1].to_int();1020// Skip entries like "None:0" (it's not an actual flag).1021if (current_val == 0) {1022continue;1023}1024} else {1025current_val = 1u << i;1026}1027flag_values.push_back(current_val);10281029// Create a CheckBox for the current flag.1030CheckBox *cb = memnew(CheckBox);1031cb->set_text(text_split[0]);1032cb->set_clip_text(true);1033cb->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyFlags::_flag_toggled).bind(flag_index));1034add_focusable(cb);1035vbox->add_child(cb);1036flags.push_back(cb);10371038// Can't use `i == 0` because we want to find the first none-empty option.1039if (first) {1040set_label_reference(cb);1041first = false;1042}1043}1044}10451046EditorPropertyFlags::EditorPropertyFlags() {1047vbox = memnew(VBoxContainer);1048vbox->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);1049add_child(vbox);1050}10511052///////////////////// LAYERS /////////////////////////10531054void EditorPropertyLayersGrid::_rename_pressed(int p_menu) {1055// Show rename popup for active layer.1056ERR_FAIL_INDEX(renamed_layer_index, names.size());1057String name = names[renamed_layer_index];1058rename_dialog->set_title(vformat(TTR("Renaming Layer %d:"), renamed_layer_index + 1));1059rename_dialog_text->set_text(name);1060// Indicate that leaving it blank reverts back to "Layer [Number]".1061rename_dialog_text->set_placeholder(vformat(TTR("Layer %d"), renamed_layer_index + 1));1062rename_dialog_text->select(0, name.length());1063rename_dialog->popup_centered(Size2(300, 80) * EDSCALE);1064rename_dialog_text->grab_focus();1065}10661067void EditorPropertyLayersGrid::_rename_operation_confirm() {1068String new_name = rename_dialog_text->get_text().strip_edges();1069if (new_name.contains_char('/') || new_name.contains_char('\\') || new_name.contains_char(':')) {1070EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));1071return;1072}10731074names.set(renamed_layer_index, new_name);1075tooltips.set(renamed_layer_index, new_name + "\n" + vformat(TTR("Bit %d, Value %d"), renamed_layer_index, 1u << renamed_layer_index));1076emit_signal(SNAME("rename_confirmed"), renamed_layer_index, new_name);1077}10781079EditorPropertyLayersGrid::EditorPropertyLayersGrid() {1080rename_dialog = memnew(ConfirmationDialog);1081VBoxContainer *rename_dialog_vb = memnew(VBoxContainer);1082rename_dialog->add_child(rename_dialog_vb);1083rename_dialog_text = memnew(LineEdit);1084rename_dialog_vb->add_margin_child(TTR("Name:"), rename_dialog_text);1085rename_dialog->set_ok_button_text(TTR("Rename"));1086add_child(rename_dialog);1087rename_dialog->register_text_enter(rename_dialog_text);1088rename_dialog->connect(SceneStringName(confirmed), callable_mp(this, &EditorPropertyLayersGrid::_rename_operation_confirm));1089layer_rename = memnew(PopupMenu);1090layer_rename->add_item(TTR("Rename Layer"), 0);1091add_child(layer_rename);1092layer_rename->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyLayersGrid::_rename_pressed));1093}10941095Size2 EditorPropertyLayersGrid::get_grid_size() const {1096Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));1097int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));1098return Vector2(0, font->get_height(font_size) * 3);1099}11001101void EditorPropertyLayersGrid::set_read_only(bool p_read_only) {1102read_only = p_read_only;1103}11041105Size2 EditorPropertyLayersGrid::get_minimum_size() const {1106Size2 min_size = get_grid_size();11071108// Add extra rows when expanded.1109if (expanded) {1110const int bsize = (min_size.height * 80 / 100) / 2;1111for (int i = 0; i < expansion_rows; ++i) {1112min_size.y += 2 * (bsize + 1) + 3;1113}1114}11151116return min_size;1117}11181119String EditorPropertyLayersGrid::get_tooltip(const Point2 &p_pos) const {1120for (int i = 0; i < flag_rects.size(); i++) {1121if (i < tooltips.size() && flag_rects[i].has_point(p_pos)) {1122return tooltips[i];1123}1124}1125return String();1126}11271128void EditorPropertyLayersGrid::_update_hovered(const Vector2 &p_position) {1129bool expand_was_hovered = expand_hovered;1130expand_hovered = expand_rect.has_point(p_position);1131if (expand_hovered != expand_was_hovered) {1132queue_redraw();1133}11341135if (!expand_hovered) {1136for (int i = 0; i < flag_rects.size(); i++) {1137if (flag_rects[i].has_point(p_position)) {1138// Used to highlight the hovered flag in the layers grid.1139hovered_index = i;1140queue_redraw();1141return;1142}1143}1144}11451146// Remove highlight when no square is hovered.1147if (hovered_index != HOVERED_INDEX_NONE) {1148hovered_index = HOVERED_INDEX_NONE;1149queue_redraw();1150}1151}11521153void EditorPropertyLayersGrid::_on_hover_exit() {1154if (expand_hovered) {1155expand_hovered = false;1156queue_redraw();1157}1158if (hovered_index != HOVERED_INDEX_NONE) {1159hovered_index = HOVERED_INDEX_NONE;1160queue_redraw();1161}1162if (dragging) {1163dragging = false;1164}1165}11661167void EditorPropertyLayersGrid::_update_flag(bool p_replace) {1168if (hovered_index != HOVERED_INDEX_NONE) {1169// Toggle the flag.1170// We base our choice on the hovered flag, so that it always matches the hovered flag.1171if (p_replace) {1172// Replace all flags with the hovered flag ("solo mode"),1173// instead of toggling the hovered flags while preserving other flags' state.1174if (value == 1u << hovered_index) {1175// If the flag is already enabled, enable all other items and disable the current flag.1176// This allows for quicker toggling.1177value = ~value;1178} else {1179value = 1u << hovered_index;1180}1181} else {1182value ^= 1u << hovered_index;1183}11841185emit_signal(SNAME("flag_changed"), value);1186queue_redraw();1187} else if (expand_hovered) {1188expanded = !expanded;1189update_minimum_size();1190queue_redraw();1191}1192}11931194void EditorPropertyLayersGrid::gui_input(const Ref<InputEvent> &p_ev) {1195if (read_only) {1196return;1197}1198const Ref<InputEventMouseMotion> mm = p_ev;1199if (mm.is_valid()) {1200_update_hovered(mm->get_position());1201if (dragging && hovered_index != HOVERED_INDEX_NONE && dragging_value_to_set != bool(value & (1u << hovered_index))) {1202value ^= 1u << hovered_index;1203emit_signal(SNAME("flag_changed"), value);1204queue_redraw();1205}1206return;1207}12081209const Ref<InputEventMouseButton> mb = p_ev;1210if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {1211_update_hovered(mb->get_position());1212bool replace_mode = mb->is_command_or_control_pressed();1213_update_flag(replace_mode);1214if (!replace_mode && hovered_index != HOVERED_INDEX_NONE) {1215dragging = true;1216dragging_value_to_set = bool(value & (1u << hovered_index));1217}1218}1219if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) {1220dragging = false;1221}1222if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {1223if (hovered_index != HOVERED_INDEX_NONE) {1224renamed_layer_index = hovered_index;1225layer_rename->set_position(get_screen_position() + mb->get_position());1226layer_rename->reset_size();1227layer_rename->popup();1228}1229}1230}12311232void EditorPropertyLayersGrid::_notification(int p_what) {1233switch (p_what) {1234case NOTIFICATION_DRAW: {1235Size2 grid_size = get_grid_size();1236grid_size.x = get_size().x;12371238flag_rects.clear();12391240int prev_expansion_rows = expansion_rows;1241expansion_rows = 0;12421243const int bsize = (grid_size.height * 80 / 100) / 2;1244const int h = bsize * 2 + 1;12451246Color color = get_theme_color(read_only ? SNAME("highlight_disabled_color") : SNAME("highlight_color"), EditorStringName(Editor));12471248Color text_color = get_theme_color(read_only ? SNAME("font_disabled_color") : SceneStringName(font_color), EditorStringName(Editor));1249text_color.a *= 0.5;12501251Color text_color_on = get_theme_color(read_only ? SNAME("font_disabled_color") : SNAME("font_hover_color"), EditorStringName(Editor));1252text_color_on.a *= 0.7;12531254const int vofs = (grid_size.height - h) / 2;12551256uint32_t layer_index = 0;12571258Point2 arrow_pos;12591260Point2 block_ofs(4, vofs);12611262while (true) {1263Point2 ofs = block_ofs;12641265for (int i = 0; i < 2; i++) {1266for (int j = 0; j < layer_group_size; j++) {1267const bool on = value & (1u << layer_index);1268Rect2 rect2 = Rect2(ofs, Size2(bsize, bsize));12691270color.a = on ? 0.6 : 0.2;1271if (layer_index == hovered_index) {1272// Add visual feedback when hovering a flag.1273color.a += 0.15;1274}12751276draw_rect(rect2, color);1277flag_rects.push_back(rect2);12781279Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));1280int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));1281Vector2 offset;1282offset.y = rect2.size.y * 0.75;12831284draw_string(font, rect2.position + offset, itos(layer_index + 1), HORIZONTAL_ALIGNMENT_CENTER, rect2.size.x, font_size, on ? text_color_on : text_color);12851286ofs.x += bsize + 1;12871288++layer_index;1289}12901291ofs.x = block_ofs.x;1292ofs.y += bsize + 1;1293}12941295if (layer_index >= layer_count) {1296if (!flag_rects.is_empty() && (expansion_rows == 0)) {1297const Rect2 &last_rect = flag_rects[flag_rects.size() - 1];1298arrow_pos = last_rect.get_end();1299}1300break;1301}13021303int block_size_x = layer_group_size * (bsize + 1);1304block_ofs.x += block_size_x + 3;13051306if (block_ofs.x + block_size_x + 12 > grid_size.width) {1307// Keep last valid cell position for the expansion icon.1308if (!flag_rects.is_empty() && (expansion_rows == 0)) {1309const Rect2 &last_rect = flag_rects[flag_rects.size() - 1];1310arrow_pos = last_rect.get_end();1311}1312++expansion_rows;13131314if (expanded) {1315// Expand grid to next line.1316block_ofs.x = 4;1317block_ofs.y += 2 * (bsize + 1) + 3;1318} else {1319// Skip remaining blocks.1320break;1321}1322}1323}13241325if ((expansion_rows != prev_expansion_rows) && expanded) {1326update_minimum_size();1327}13281329if ((expansion_rows == 0) && (layer_index == layer_count)) {1330// Whole grid was drawn, no need for expansion icon.1331break;1332}13331334Ref<Texture2D> arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree"));1335ERR_FAIL_COND(arrow.is_null());13361337Color arrow_color = get_theme_color(SNAME("highlight_color"), EditorStringName(Editor));1338arrow_color.a = expand_hovered ? 1.0 : 0.6;13391340arrow_pos.x += 2.0;1341arrow_pos.y -= arrow->get_height();13421343Rect2 arrow_draw_rect(arrow_pos, arrow->get_size());1344expand_rect = arrow_draw_rect;1345if (expanded) {1346arrow_draw_rect.size.y *= -1.0; // Flip arrow vertically when expanded.1347}13481349RID ci = get_canvas_item();1350arrow->draw_rect(ci, arrow_draw_rect, false, arrow_color);13511352} break;13531354case NOTIFICATION_MOUSE_EXIT: {1355_on_hover_exit();1356} break;1357}1358}13591360void EditorPropertyLayersGrid::set_flag(uint32_t p_flag) {1361value = p_flag;1362queue_redraw();1363}13641365void EditorPropertyLayersGrid::_bind_methods() {1366ADD_SIGNAL(MethodInfo("flag_changed", PropertyInfo(Variant::INT, "flag")));1367ADD_SIGNAL(MethodInfo("rename_confirmed", PropertyInfo(Variant::INT, "layer_id"), PropertyInfo(Variant::STRING, "new_name")));1368}13691370void EditorPropertyLayers::_notification(int p_what) {1371switch (p_what) {1372case NOTIFICATION_THEME_CHANGED: {1373button->set_texture_normal(get_editor_theme_icon(SNAME("GuiTabMenuHl")));1374button->set_texture_pressed(get_editor_theme_icon(SNAME("GuiTabMenuHl")));1375button->set_texture_disabled(get_editor_theme_icon(SNAME("GuiTabMenu")));1376} break;1377}1378}13791380void EditorPropertyLayers::_set_read_only(bool p_read_only) {1381button->set_disabled(p_read_only);1382grid->set_read_only(p_read_only);1383}13841385void EditorPropertyLayers::_grid_changed(uint32_t p_grid) {1386emit_changed(get_edited_property(), p_grid);1387}13881389void EditorPropertyLayers::update_property() {1390uint32_t value = get_edited_property_value();13911392grid->set_flag(value);1393}13941395void EditorPropertyLayers::setup(LayerType p_layer_type) {1396layer_type = p_layer_type;1397int layer_group_size = 0;1398int layer_count = 0;1399switch (p_layer_type) {1400case LAYER_RENDER_2D: {1401basename = "layer_names/2d_render";1402layer_group_size = 5;1403layer_count = 20;1404} break;14051406case LAYER_PHYSICS_2D: {1407basename = "layer_names/2d_physics";1408layer_group_size = 4;1409layer_count = 32;1410} break;14111412case LAYER_NAVIGATION_2D: {1413basename = "layer_names/2d_navigation";1414layer_group_size = 4;1415layer_count = 32;1416} break;14171418case LAYER_RENDER_3D: {1419basename = "layer_names/3d_render";1420layer_group_size = 5;1421layer_count = 20;1422} break;14231424case LAYER_PHYSICS_3D: {1425basename = "layer_names/3d_physics";1426layer_group_size = 4;1427layer_count = 32;1428} break;14291430case LAYER_NAVIGATION_3D: {1431basename = "layer_names/3d_navigation";1432layer_group_size = 4;1433layer_count = 32;1434} break;14351436case LAYER_AVOIDANCE: {1437basename = "layer_names/avoidance";1438layer_group_size = 4;1439layer_count = 32;1440} break;1441}14421443Vector<String> names;1444Vector<String> tooltips;1445for (int i = 0; i < layer_count; i++) {1446String name;14471448if (ProjectSettings::get_singleton()->has_setting(basename + vformat("/layer_%d", i + 1))) {1449name = GLOBAL_GET(basename + vformat("/layer_%d", i + 1));1450}14511452if (name.is_empty()) {1453name = vformat(TTR("Layer %d"), i + 1);1454}14551456names.push_back(name);1457tooltips.push_back(name + "\n" + vformat(TTR("Bit %d, value %d"), i, 1u << i));1458}14591460grid->names = names;1461grid->tooltips = tooltips;1462grid->layer_group_size = layer_group_size;1463grid->layer_count = layer_count;1464}14651466void EditorPropertyLayers::set_layer_name(int p_index, const String &p_name) {1467const String property_name = basename + vformat("/layer_%d", p_index + 1);1468if (ProjectSettings::get_singleton()->has_setting(property_name)) {1469ProjectSettings::get_singleton()->set(property_name, p_name);1470ProjectSettings::get_singleton()->save();1471}1472}14731474String EditorPropertyLayers::get_layer_name(int p_index) const {1475const String property_name = basename + vformat("/layer_%d", p_index + 1);1476if (ProjectSettings::get_singleton()->has_setting(property_name)) {1477return GLOBAL_GET(property_name);1478}1479return String();1480}14811482void EditorPropertyLayers::_button_pressed() {1483int layer_count = grid->layer_count;1484layers->clear();1485for (int i = 0; i < layer_count; i++) {1486const String name = get_layer_name(i);1487if (name.is_empty()) {1488continue;1489}1490layers->add_check_item(name, i);1491int idx = layers->get_item_index(i);1492layers->set_item_checked(idx, grid->value & (1u << i));1493}14941495if (layers->get_item_count() == 0) {1496layers->add_item(TTR("No Named Layers"));1497layers->set_item_disabled(0, true);1498}1499layers->add_separator();1500layers->add_icon_item(get_editor_theme_icon("Edit"), TTR("Edit Layer Names"), grid->layer_count);15011502Rect2 gp = button->get_screen_rect();1503layers->reset_size();1504Vector2 popup_pos = gp.position - Vector2(layers->get_contents_minimum_size().x, 0);1505layers->set_position(popup_pos);1506layers->popup();1507}15081509void EditorPropertyLayers::_menu_pressed(int p_menu) {1510if (uint32_t(p_menu) == grid->layer_count) {1511ProjectSettingsEditor::get_singleton()->popup_project_settings(true);1512ProjectSettingsEditor::get_singleton()->set_general_page(basename);1513} else {1514grid->value ^= 1u << p_menu;1515grid->queue_redraw();1516layers->set_item_checked(layers->get_item_index(p_menu), grid->value & (1u << p_menu));1517_grid_changed(grid->value);1518}1519}15201521void EditorPropertyLayers::_refresh_names() {1522setup(layer_type);1523}15241525EditorPropertyLayers::EditorPropertyLayers() {1526HBoxContainer *hb = memnew(HBoxContainer);1527hb->set_clip_contents(true);1528add_child(hb);1529grid = memnew(EditorPropertyLayersGrid);1530grid->connect("flag_changed", callable_mp(this, &EditorPropertyLayers::_grid_changed));1531grid->connect("rename_confirmed", callable_mp(this, &EditorPropertyLayers::set_layer_name));1532grid->set_h_size_flags(SIZE_EXPAND_FILL);1533hb->add_child(grid);15341535button = memnew(TextureButton);1536button->set_accessibility_name(TTRC("Layers"));1537button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);1538button->set_toggle_mode(true);1539button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLayers::_button_pressed));1540hb->add_child(button);15411542set_bottom_editor(hb);15431544layers = memnew(PopupMenu);1545add_child(layers);1546layers->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);1547layers->set_hide_on_checkable_item_selection(false);1548layers->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyLayers::_menu_pressed));1549layers->connect("popup_hide", callable_mp((BaseButton *)button, &BaseButton::set_pressed).bind(false));1550ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorPropertyLayers::_refresh_names));1551}15521553///////////////////// INT /////////////////////////15541555void EditorPropertyInteger::_set_read_only(bool p_read_only) {1556spin->set_read_only(p_read_only);1557}15581559void EditorPropertyInteger::_value_changed(int64_t val) {1560emit_changed(get_edited_property(), val);1561}15621563void EditorPropertyInteger::update_property() {1564int64_t val = get_edited_property_display_value();1565spin->set_value_no_signal(val);1566#ifdef DEBUG_ENABLED1567// If spin (currently EditorSplinSlider : Range) is changed so that it can use int64_t, then the below warning wouldn't be a problem.1568if (val != (int64_t)(double)(val)) {1569WARN_PRINT("Cannot reliably represent '" + itos(val) + "' in the inspector, value is too large.");1570}1571#endif1572}15731574void EditorPropertyInteger::setup(const EditorPropertyRangeHint &p_range_hint) {1575spin->set_min(p_range_hint.min);1576spin->set_max(p_range_hint.max);1577spin->set_step(Math::round(p_range_hint.step));1578if (p_range_hint.hide_control) {1579spin->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);1580} else {1581spin->set_control_state(p_range_hint.prefer_slider ? EditorSpinSlider::CONTROL_STATE_PREFER_SLIDER : EditorSpinSlider::CONTROL_STATE_DEFAULT);1582}1583spin->set_allow_greater(p_range_hint.or_greater);1584spin->set_allow_lesser(p_range_hint.or_less);1585spin->set_suffix(p_range_hint.suffix);1586}15871588EditorPropertyInteger::EditorPropertyInteger() {1589spin = memnew(EditorSpinSlider);1590spin->set_flat(true);1591spin->set_editing_integer(true);1592add_child(spin);1593add_focusable(spin);1594spin->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyInteger::_value_changed));1595}15961597///////////////////// OBJECT ID /////////////////////////15981599void EditorPropertyObjectID::_set_read_only(bool p_read_only) {1600edit->set_disabled(p_read_only);1601}16021603void EditorPropertyObjectID::_edit_pressed() {1604emit_signal(SNAME("object_id_selected"), get_edited_property(), get_edited_property_value());1605}16061607void EditorPropertyObjectID::update_property() {1608String type = base_type;1609if (type.is_empty()) {1610type = "Object";1611}16121613ObjectID id = get_edited_property_value();1614if (id.is_valid()) {1615edit->set_text(type + " ID: " + uitos(id));1616edit->set_tooltip_text(type + " ID: " + uitos(id));1617edit->set_disabled(false);1618edit->set_button_icon(EditorNode::get_singleton()->get_class_icon(type));1619} else {1620edit->set_text(TTR("<empty>"));1621edit->set_tooltip_text("");1622edit->set_disabled(true);1623edit->set_button_icon(Ref<Texture2D>());1624}1625}16261627void EditorPropertyObjectID::setup(const String &p_base_type) {1628base_type = p_base_type;1629}16301631EditorPropertyObjectID::EditorPropertyObjectID() {1632edit = memnew(Button);1633edit->set_theme_type_variation(SNAME("EditorInspectorButton"));1634edit->set_accessibility_name(TTRC("Edit"));1635add_child(edit);1636add_focusable(edit);1637edit->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);1638edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyObjectID::_edit_pressed));1639}16401641///////////////////// SIGNAL /////////////////////////16421643void EditorPropertySignal::_edit_pressed() {1644Signal signal = get_edited_property_value();1645emit_signal(SNAME("object_id_selected"), get_edited_property(), signal.get_object_id());1646}16471648void EditorPropertySignal::update_property() {1649String type = base_type;16501651Signal signal = get_edited_property_value();16521653edit->set_text("Signal: " + signal.get_name());1654edit->set_disabled(false);1655edit->set_button_icon(get_editor_theme_icon(SNAME("Signals")));1656}16571658EditorPropertySignal::EditorPropertySignal() {1659edit = memnew(Button);1660edit->set_theme_type_variation(SNAME("EditorInspectorButton"));1661edit->set_accessibility_name(TTRC("Edit"));1662add_child(edit);1663add_focusable(edit);1664edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertySignal::_edit_pressed));1665}16661667///////////////////// CALLABLE /////////////////////////16681669void EditorPropertyCallable::update_property() {1670String type = base_type;16711672Callable callable = get_edited_property_value();16731674edit->set_text("Callable");1675edit->set_disabled(true);1676edit->set_button_icon(get_editor_theme_icon(SNAME("Callable")));1677}16781679EditorPropertyCallable::EditorPropertyCallable() {1680edit = memnew(Button);1681edit->set_theme_type_variation(SNAME("EditorInspectorButton"));1682edit->set_accessibility_name(TTRC("Edit"));1683add_child(edit);1684add_focusable(edit);1685}16861687///////////////////// FLOAT /////////////////////////16881689void EditorPropertyFloat::_set_read_only(bool p_read_only) {1690spin->set_read_only(p_read_only);1691}16921693void EditorPropertyFloat::_value_changed(double val) {1694if (radians_as_degrees) {1695val = Math::deg_to_rad(val);1696}1697emit_changed(get_edited_property(), val);1698}16991700void EditorPropertyFloat::update_property() {1701double val = get_edited_property_value();1702if (radians_as_degrees) {1703val = Math::rad_to_deg(val);1704}1705spin->set_value_no_signal(val);1706}17071708void EditorPropertyFloat::setup(const EditorPropertyRangeHint &p_range_hint) {1709radians_as_degrees = p_range_hint.radians_as_degrees;1710spin->set_min(p_range_hint.min);1711spin->set_max(p_range_hint.max);1712spin->set_step(p_range_hint.step);1713if (p_range_hint.hide_control) {1714spin->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);1715}1716spin->set_exp_ratio(p_range_hint.exp_range);1717spin->set_allow_greater(p_range_hint.or_greater);1718spin->set_allow_lesser(p_range_hint.or_less);1719spin->set_suffix(p_range_hint.suffix);1720}17211722EditorPropertyFloat::EditorPropertyFloat() {1723spin = memnew(EditorSpinSlider);1724spin->set_flat(true);1725add_child(spin);1726add_focusable(spin);1727spin->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyFloat::_value_changed));1728}17291730///////////////////// EASING /////////////////////////17311732void EditorPropertyEasing::_set_read_only(bool p_read_only) {1733spin->set_read_only(p_read_only);1734}17351736void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) {1737if (is_read_only()) {1738return;1739}1740const Ref<InputEventMouseButton> mb = p_ev;1741if (mb.is_valid()) {1742if (mb->is_double_click() && mb->get_button_index() == MouseButton::LEFT) {1743_setup_spin();1744}17451746if (mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {1747preset->set_position(easing_draw->get_screen_position() + mb->get_position());1748preset->reset_size();1749preset->popup();17501751// Ensure the easing doesn't appear as being dragged1752dragging = false;1753easing_draw->queue_redraw();1754}17551756if (mb->get_button_index() == MouseButton::LEFT) {1757dragging = mb->is_pressed();1758// Update to display the correct dragging color1759easing_draw->queue_redraw();1760}1761}17621763const Ref<InputEventMouseMotion> mm = p_ev;17641765if (dragging && mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {1766float rel = mm->get_relative().x;1767if (rel == 0) {1768return;1769}17701771if (flip) {1772rel = -rel;1773}17741775float val = get_edited_property_value();1776bool sg = val < 0;1777val = Math::abs(val);17781779val = Math::log(val) / Math::log((float)2.0);1780// Logarithmic space.1781val += rel * 0.05;17821783val = Math::pow(2.0f, val);1784if (sg) {1785val = -val;1786}17871788// 0 is a singularity, but both positive and negative values1789// are otherwise allowed. Enforce 0+ as workaround.1790if (Math::is_zero_approx(val)) {1791val = 0.00001;1792}17931794// Limit to a reasonable value to prevent the curve going into infinity,1795// which can cause crashes and other issues.1796val = CLAMP(val, -1'000'000, 1'000'000);17971798emit_changed(get_edited_property(), val);1799}1800}18011802void EditorPropertyEasing::_draw_easing() {1803RID ci = easing_draw->get_canvas_item();18041805Size2 s = easing_draw->get_size();18061807const int point_count = 48;18081809const float exp = get_edited_property_value();18101811const Ref<Font> f = get_theme_font(SceneStringName(font), SNAME("Label"));1812int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));1813const Color font_color = get_theme_color(is_read_only() ? SNAME("font_uneditable_color") : SceneStringName(font_color), SNAME("LineEdit"));1814Color line_color;1815if (dragging) {1816line_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));1817} else {1818line_color = get_theme_color(is_read_only() ? SNAME("font_uneditable_color") : SceneStringName(font_color), SNAME("LineEdit")) * Color(1, 1, 1, 0.9);1819}18201821Vector<Point2> points;1822for (int i = 0; i <= point_count; i++) {1823float ifl = i / float(point_count);18241825const float h = 1.0 - Math::ease(ifl, exp);18261827if (flip) {1828ifl = 1.0 - ifl;1829}18301831points.push_back(Point2(ifl * s.width, h * s.height));1832}18331834easing_draw->draw_polyline(points, line_color, 1.0, true);1835// Draw more decimals for small numbers since higher precision is usually required for fine adjustments.1836int decimals;1837if (Math::abs(exp) < 0.1 - CMP_EPSILON) {1838decimals = 4;1839} else if (Math::abs(exp) < 1 - CMP_EPSILON) {1840decimals = 3;1841} else if (Math::abs(exp) < 10 - CMP_EPSILON) {1842decimals = 2;1843} else {1844decimals = 1;1845}18461847const String &formatted = TranslationServer::get_singleton()->format_number(rtos(exp).pad_decimals(decimals), _get_locale());1848f->draw_string(ci, Point2(10, 10 + f->get_ascent(font_size)), formatted, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);1849}18501851void EditorPropertyEasing::update_property() {1852float val = get_edited_property_value();1853spin->set_value_no_signal(val);18541855easing_draw->queue_redraw();1856}18571858void EditorPropertyEasing::_set_preset(int p_preset) {1859static const float preset_value[EASING_MAX] = { 0.0, 1.0, 2.0, 0.5, -2.0, -0.5 };18601861emit_changed(get_edited_property(), preset_value[p_preset]);1862}18631864void EditorPropertyEasing::_setup_spin() {1865spin->setup_and_show();1866spin->get_line_edit()->set_text(TranslationServer::get_singleton()->format_number(rtos(get_edited_property_value()), _get_locale()));1867spin->show();1868}18691870void EditorPropertyEasing::_spin_value_changed(double p_value) {1871// Limit to a reasonable value to prevent the curve going into infinity,1872// which can cause crashes and other issues.1873p_value = CLAMP(p_value, -1'000'000, 1'000'000);18741875if (positive_only) {1876// Force a positive or zero value if a negative value was manually entered by double-clicking.1877p_value = MAX(0.0, p_value);1878}18791880emit_changed(get_edited_property(), p_value);1881_spin_focus_exited();1882}18831884void EditorPropertyEasing::_spin_focus_exited() {1885spin->hide();1886// Ensure the easing doesn't appear as being dragged1887dragging = false;1888easing_draw->queue_redraw();1889}18901891void EditorPropertyEasing::setup(bool p_positive_only, bool p_flip) {1892flip = p_flip;1893positive_only = p_positive_only;18941895// Names need translation context, so they are set in NOTIFICATION_TRANSLATION_CHANGED.1896preset->add_item("", EASING_LINEAR);1897preset->add_item("", EASING_IN);1898preset->add_item("", EASING_OUT);1899preset->add_item("", EASING_ZERO);1900if (!positive_only) {1901preset->add_item("", EASING_IN_OUT);1902preset->add_item("", EASING_OUT_IN);1903}1904}19051906void EditorPropertyEasing::_notification(int p_what) {1907switch (p_what) {1908case NOTIFICATION_THEME_CHANGED: {1909preset->set_item_icon(preset->get_item_index(EASING_LINEAR), get_editor_theme_icon(SNAME("CurveLinear")));1910preset->set_item_icon(preset->get_item_index(EASING_IN), get_editor_theme_icon(SNAME("CurveIn")));1911preset->set_item_icon(preset->get_item_index(EASING_OUT), get_editor_theme_icon(SNAME("CurveOut")));1912preset->set_item_icon(preset->get_item_index(EASING_ZERO), get_editor_theme_icon(SNAME("CurveConstant")));1913if (!positive_only) {1914preset->set_item_icon(preset->get_item_index(EASING_IN_OUT), get_editor_theme_icon(SNAME("CurveInOut")));1915preset->set_item_icon(preset->get_item_index(EASING_OUT_IN), get_editor_theme_icon(SNAME("CurveOutIn")));1916}1917easing_draw->set_custom_minimum_size(Size2(0, get_theme_font(SceneStringName(font), SNAME("Label"))->get_height(get_theme_font_size(SceneStringName(font_size), SNAME("Label"))) * 2));1918} break;19191920case NOTIFICATION_TRANSLATION_CHANGED: {1921preset->set_item_text(preset->get_item_index(EASING_LINEAR), TTR("Linear", "Ease Type"));1922preset->set_item_text(preset->get_item_index(EASING_IN), TTR("Ease In", "Ease Type"));1923preset->set_item_text(preset->get_item_index(EASING_OUT), TTR("Ease Out", "Ease Type"));1924preset->set_item_text(preset->get_item_index(EASING_ZERO), TTR("Zero", "Ease Type"));1925if (!positive_only) {1926preset->set_item_text(preset->get_item_index(EASING_IN_OUT), TTR("Ease In-Out", "Ease Type"));1927preset->set_item_text(preset->get_item_index(EASING_OUT_IN), TTR("Ease Out-In", "Ease Type"));1928}1929} break;1930}1931}19321933EditorPropertyEasing::EditorPropertyEasing() {1934easing_draw = memnew(Control);1935easing_draw->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyEasing::_draw_easing));1936easing_draw->connect(SceneStringName(gui_input), callable_mp(this, &EditorPropertyEasing::_drag_easing));1937easing_draw->set_default_cursor_shape(Control::CURSOR_MOVE);1938add_child(easing_draw);19391940preset = memnew(PopupMenu);1941preset->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);1942add_child(preset);1943preset->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyEasing::_set_preset));19441945spin = memnew(EditorSpinSlider);1946spin->set_flat(true);1947spin->set_min(-100);1948spin->set_max(100);1949spin->set_step(0);1950spin->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);1951spin->set_allow_lesser(true);1952spin->set_allow_greater(true);1953spin->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyEasing::_spin_value_changed));1954spin->get_line_edit()->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyEasing::_spin_focus_exited));1955spin->hide();1956add_child(spin);1957}19581959///////////////////// RECT2 /////////////////////////19601961void EditorPropertyRect2::_set_read_only(bool p_read_only) {1962for (int i = 0; i < 4; i++) {1963spin[i]->set_read_only(p_read_only);1964}1965}19661967void EditorPropertyRect2::_value_changed(double val, const String &p_name) {1968Rect2 r2;1969r2.position.x = spin[0]->get_value();1970r2.position.y = spin[1]->get_value();1971r2.size.x = spin[2]->get_value();1972r2.size.y = spin[3]->get_value();1973emit_changed(get_edited_property(), r2, p_name);1974}19751976void EditorPropertyRect2::update_property() {1977Rect2 val = get_edited_property_value();1978spin[0]->set_value_no_signal(val.position.x);1979spin[1]->set_value_no_signal(val.position.y);1980spin[2]->set_value_no_signal(val.size.x);1981spin[3]->set_value_no_signal(val.size.y);1982}19831984void EditorPropertyRect2::_notification(int p_what) {1985switch (p_what) {1986case NOTIFICATION_THEME_CHANGED: {1987const Color *colors = _get_property_colors();1988for (int i = 0; i < 4; i++) {1989spin[i]->add_theme_color_override("label_color", colors[i % 2]);1990}1991} break;1992}1993}19941995void EditorPropertyRect2::setup(const EditorPropertyRangeHint &p_range_hint) {1996for (int i = 0; i < 4; i++) {1997spin[i]->set_min(p_range_hint.min);1998spin[i]->set_max(p_range_hint.max);1999spin[i]->set_step(p_range_hint.step);2000if (p_range_hint.hide_control) {2001spin[i]->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);2002}2003spin[i]->set_allow_greater(true);2004spin[i]->set_allow_lesser(true);2005spin[i]->set_suffix(p_range_hint.suffix);2006}2007}20082009EditorPropertyRect2::EditorPropertyRect2(bool p_force_wide) {2010bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));2011bool grid = false;2012BoxContainer *bc;20132014if (p_force_wide) {2015bc = memnew(HBoxContainer);2016add_child(bc);2017} else if (horizontal) {2018bc = memnew(VBoxContainer);2019add_child(bc);2020set_bottom_editor(bc);20212022bc->add_child(memnew(HBoxContainer));2023bc->add_child(memnew(HBoxContainer));2024grid = true;2025} else {2026bc = memnew(VBoxContainer);2027add_child(bc);2028}20292030static const char *desc[4] = { "x", "y", "w", "h" };2031for (int i = 0; i < 4; i++) {2032spin[i] = memnew(EditorSpinSlider);2033spin[i]->set_label(desc[i]);2034spin[i]->set_accessibility_name(desc[i]);2035spin[i]->set_flat(true);20362037if (grid) {2038bc->get_child(i / 2)->add_child(spin[i]);2039} else {2040bc->add_child(spin[i]);2041}20422043add_focusable(spin[i]);2044spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyRect2::_value_changed).bind(desc[i]));2045if (horizontal) {2046spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2047}2048}20492050if (!horizontal) {2051set_label_reference(spin[0]); //show text and buttons around this2052}2053}20542055///////////////////// RECT2i /////////////////////////20562057void EditorPropertyRect2i::_set_read_only(bool p_read_only) {2058for (int i = 0; i < 4; i++) {2059spin[i]->set_read_only(p_read_only);2060}2061}20622063void EditorPropertyRect2i::_value_changed(double val, const String &p_name) {2064Rect2i r2;2065r2.position.x = spin[0]->get_value();2066r2.position.y = spin[1]->get_value();2067r2.size.x = spin[2]->get_value();2068r2.size.y = spin[3]->get_value();2069emit_changed(get_edited_property(), r2, p_name);2070}20712072void EditorPropertyRect2i::update_property() {2073Rect2i val = get_edited_property_value();2074spin[0]->set_value_no_signal(val.position.x);2075spin[1]->set_value_no_signal(val.position.y);2076spin[2]->set_value_no_signal(val.size.x);2077spin[3]->set_value_no_signal(val.size.y);2078}20792080void EditorPropertyRect2i::_notification(int p_what) {2081switch (p_what) {2082case NOTIFICATION_THEME_CHANGED: {2083const Color *colors = _get_property_colors();2084for (int i = 0; i < 4; i++) {2085spin[i]->add_theme_color_override("label_color", colors[i % 2]);2086}2087} break;2088}2089}20902091void EditorPropertyRect2i::setup(const EditorPropertyRangeHint &p_range_hint) {2092for (int i = 0; i < 4; i++) {2093spin[i]->set_min(p_range_hint.min);2094spin[i]->set_max(p_range_hint.max);2095spin[i]->set_step(1);2096spin[i]->set_allow_greater(true);2097spin[i]->set_allow_lesser(true);2098spin[i]->set_suffix(p_range_hint.suffix);2099spin[i]->set_editing_integer(true);2100}2101}21022103EditorPropertyRect2i::EditorPropertyRect2i(bool p_force_wide) {2104bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));2105bool grid = false;2106BoxContainer *bc;21072108if (p_force_wide) {2109bc = memnew(HBoxContainer);2110add_child(bc);2111} else if (horizontal) {2112bc = memnew(VBoxContainer);2113add_child(bc);2114set_bottom_editor(bc);21152116bc->add_child(memnew(HBoxContainer));2117bc->add_child(memnew(HBoxContainer));2118grid = true;2119} else {2120bc = memnew(VBoxContainer);2121add_child(bc);2122}21232124static const char *desc[4] = { "x", "y", "w", "h" };2125for (int i = 0; i < 4; i++) {2126spin[i] = memnew(EditorSpinSlider);2127spin[i]->set_label(desc[i]);2128spin[i]->set_accessibility_name(desc[i]);2129spin[i]->set_flat(true);21302131if (grid) {2132bc->get_child(i / 2)->add_child(spin[i]);2133} else {2134bc->add_child(spin[i]);2135}21362137add_focusable(spin[i]);2138spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyRect2i::_value_changed).bind(desc[i]));2139if (horizontal) {2140spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2141}2142}21432144if (!horizontal) {2145set_label_reference(spin[0]); //show text and buttons around this2146}2147}21482149///////////////////// PLANE /////////////////////////21502151void EditorPropertyPlane::_set_read_only(bool p_read_only) {2152for (int i = 0; i < 4; i++) {2153spin[i]->set_read_only(p_read_only);2154}2155}21562157void EditorPropertyPlane::_value_changed(double val, const String &p_name) {2158Plane p;2159p.normal.x = spin[0]->get_value();2160p.normal.y = spin[1]->get_value();2161p.normal.z = spin[2]->get_value();2162p.d = spin[3]->get_value();2163emit_changed(get_edited_property(), p, p_name);2164}21652166void EditorPropertyPlane::update_property() {2167Plane val = get_edited_property_value();2168spin[0]->set_value_no_signal(val.normal.x);2169spin[1]->set_value_no_signal(val.normal.y);2170spin[2]->set_value_no_signal(val.normal.z);2171spin[3]->set_value_no_signal(val.d);2172}21732174void EditorPropertyPlane::_notification(int p_what) {2175switch (p_what) {2176case NOTIFICATION_THEME_CHANGED: {2177const Color *colors = _get_property_colors();2178for (int i = 0; i < 4; i++) {2179spin[i]->add_theme_color_override("label_color", colors[i]);2180}2181} break;2182}2183}21842185void EditorPropertyPlane::setup(const EditorPropertyRangeHint &p_range_hint) {2186for (int i = 0; i < 4; i++) {2187spin[i]->set_min(p_range_hint.min);2188spin[i]->set_max(p_range_hint.max);2189spin[i]->set_step(p_range_hint.step);2190if (p_range_hint.hide_control) {2191spin[i]->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);2192}2193spin[i]->set_allow_greater(true);2194spin[i]->set_allow_lesser(true);2195}2196spin[3]->set_suffix(p_range_hint.suffix);2197}21982199EditorPropertyPlane::EditorPropertyPlane(bool p_force_wide) {2200bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));22012202BoxContainer *bc;22032204if (p_force_wide) {2205bc = memnew(HBoxContainer);2206add_child(bc);2207} else if (horizontal) {2208bc = memnew(HBoxContainer);2209add_child(bc);2210set_bottom_editor(bc);2211} else {2212bc = memnew(VBoxContainer);2213add_child(bc);2214}22152216static const char *desc[4] = { "x", "y", "z", "d" };2217for (int i = 0; i < 4; i++) {2218spin[i] = memnew(EditorSpinSlider);2219spin[i]->set_flat(true);2220spin[i]->set_label(desc[i]);2221spin[i]->set_accessibility_name(desc[i]);2222bc->add_child(spin[i]);2223add_focusable(spin[i]);2224spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyPlane::_value_changed).bind(desc[i]));2225if (horizontal) {2226spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2227}2228}22292230if (!horizontal) {2231set_label_reference(spin[0]); //show text and buttons around this2232}2233}22342235///////////////////// QUATERNION /////////////////////////22362237void EditorPropertyQuaternion::_set_read_only(bool p_read_only) {2238for (int i = 0; i < 4; i++) {2239spin[i]->set_read_only(p_read_only);2240}2241for (int i = 0; i < 3; i++) {2242euler[i]->set_read_only(p_read_only);2243}2244}22452246void EditorPropertyQuaternion::_edit_custom_value() {2247if (edit_button->is_pressed()) {2248edit_custom_bc->show();2249for (int i = 0; i < 3; i++) {2250euler[i]->grab_focus();2251}2252} else {2253edit_custom_bc->hide();2254for (int i = 0; i < 4; i++) {2255spin[i]->grab_focus();2256}2257}2258update_property();2259}22602261void EditorPropertyQuaternion::_custom_value_changed(double val) {2262edit_euler.x = euler[0]->get_value();2263edit_euler.y = euler[1]->get_value();2264edit_euler.z = euler[2]->get_value();22652266Vector3 v;2267v.x = Math::deg_to_rad(edit_euler.x);2268v.y = Math::deg_to_rad(edit_euler.y);2269v.z = Math::deg_to_rad(edit_euler.z);22702271Quaternion temp_q = Quaternion::from_euler(v);2272spin[0]->set_value_no_signal(temp_q.x);2273spin[1]->set_value_no_signal(temp_q.y);2274spin[2]->set_value_no_signal(temp_q.z);2275spin[3]->set_value_no_signal(temp_q.w);2276_value_changed(-1, "");2277}22782279void EditorPropertyQuaternion::_value_changed(double val, const String &p_name) {2280Quaternion p;2281p.x = spin[0]->get_value();2282p.y = spin[1]->get_value();2283p.z = spin[2]->get_value();2284p.w = spin[3]->get_value();22852286emit_changed(get_edited_property(), p, p_name);2287}22882289bool EditorPropertyQuaternion::is_grabbing_euler() {2290bool is_grabbing = false;2291for (int i = 0; i < 3; i++) {2292is_grabbing |= euler[i]->is_grabbing();2293}2294return is_grabbing;2295}22962297void EditorPropertyQuaternion::update_property() {2298Quaternion val = get_edited_property_value();2299spin[0]->set_value_no_signal(val.x);2300spin[1]->set_value_no_signal(val.y);2301spin[2]->set_value_no_signal(val.z);2302spin[3]->set_value_no_signal(val.w);2303if (!is_grabbing_euler()) {2304Vector3 v = val.normalized().get_euler();2305edit_euler.x = Math::rad_to_deg(v.x);2306edit_euler.y = Math::rad_to_deg(v.y);2307edit_euler.z = Math::rad_to_deg(v.z);2308euler[0]->set_value_no_signal(edit_euler.x);2309euler[1]->set_value_no_signal(edit_euler.y);2310euler[2]->set_value_no_signal(edit_euler.z);2311}2312}23132314void EditorPropertyQuaternion::_warning_pressed() {2315warning_dialog->popup_centered();2316}23172318void EditorPropertyQuaternion::_notification(int p_what) {2319switch (p_what) {2320case NOTIFICATION_THEME_CHANGED: {2321const Color *colors = _get_property_colors();2322for (int i = 0; i < 4; i++) {2323spin[i]->add_theme_color_override("label_color", colors[i]);2324}2325for (int i = 0; i < 3; i++) {2326euler[i]->add_theme_color_override("label_color", colors[i]);2327}2328edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));2329euler_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("property_color"), SNAME("EditorProperty")));2330warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));2331warning->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));2332} break;2333}2334}23352336void EditorPropertyQuaternion::setup(const EditorPropertyRangeHint &p_range_hint, bool p_hide_editor) {2337for (int i = 0; i < 4; i++) {2338spin[i]->set_min(p_range_hint.min);2339spin[i]->set_max(p_range_hint.max);2340spin[i]->set_step(p_range_hint.step);2341if (p_range_hint.hide_control) {2342spin[i]->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);2343}2344spin[i]->set_allow_greater(true);2345spin[i]->set_allow_lesser(true);2346// Quaternion is inherently unitless, however someone may want to use it as2347// a generic way to store 4 values, so we'll still respect the suffix.2348spin[i]->set_suffix(p_range_hint.suffix);2349}23502351for (int i = 0; i < 3; i++) {2352euler[i]->set_min(-360);2353euler[i]->set_max(360);2354euler[i]->set_step(0.1);2355euler[i]->set_allow_greater(true);2356euler[i]->set_allow_lesser(true);2357euler[i]->set_suffix(U"\u00B0");2358}23592360if (p_hide_editor) {2361edit_button->hide();2362}2363}23642365EditorPropertyQuaternion::EditorPropertyQuaternion() {2366bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing");23672368VBoxContainer *bc = memnew(VBoxContainer);2369edit_custom_bc = memnew(VBoxContainer);2370BoxContainer *edit_custom_layout;2371if (horizontal) {2372default_layout = memnew(HBoxContainer);2373edit_custom_layout = memnew(HBoxContainer);2374set_bottom_editor(bc);2375} else {2376default_layout = memnew(VBoxContainer);2377edit_custom_layout = memnew(VBoxContainer);2378}2379edit_custom_bc->hide();2380add_child(bc);2381edit_custom_bc->set_h_size_flags(SIZE_EXPAND_FILL);2382default_layout->set_h_size_flags(SIZE_EXPAND_FILL);2383edit_custom_layout->set_h_size_flags(SIZE_EXPAND_FILL);2384bc->add_child(default_layout);2385bc->add_child(edit_custom_bc);23862387static const char *desc[4] = { "x", "y", "z", "w" };2388for (int i = 0; i < 4; i++) {2389spin[i] = memnew(EditorSpinSlider);2390spin[i]->set_flat(true);2391spin[i]->set_label(desc[i]);2392spin[i]->set_accessibility_name(desc[i]);2393default_layout->add_child(spin[i]);2394add_focusable(spin[i]);2395spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyQuaternion::_value_changed).bind(desc[i]));2396if (horizontal) {2397spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2398}2399}24002401warning = memnew(Button);2402warning->set_text(TTR("Temporary Euler may be changed implicitly!"));2403warning->set_clip_text(true);2404warning->set_theme_type_variation(SNAME("EditorInspectorButton"));2405warning->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyQuaternion::_warning_pressed));2406warning_dialog = memnew(AcceptDialog);2407add_child(warning_dialog);2408warning_dialog->set_text(TTR("Temporary Euler will not be stored in the object with the original value. Instead, it will be stored as Quaternion with irreversible conversion.\nThis is due to the fact that the result of Euler->Quaternion can be determined uniquely, but the result of Quaternion->Euler can be multi-existent."));24092410euler_label = memnew(Label);2411euler_label->set_text(TTR("Temporary Euler"));24122413edit_custom_bc->add_child(warning);2414edit_custom_bc->add_child(edit_custom_layout);2415edit_custom_layout->add_child(euler_label);24162417for (int i = 0; i < 3; i++) {2418euler[i] = memnew(EditorSpinSlider);2419euler[i]->set_flat(true);2420euler[i]->set_label(desc[i]);2421euler[i]->set_accessibility_name(vformat(TTR("Temporary Euler %s"), desc[i]));2422edit_custom_layout->add_child(euler[i]);2423add_focusable(euler[i]);2424euler[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyQuaternion::_custom_value_changed));2425if (horizontal) {2426euler[i]->set_h_size_flags(SIZE_EXPAND_FILL);2427}2428}24292430edit_button = memnew(Button);2431edit_button->set_accessibility_name(TTRC("Edit"));2432edit_button->set_flat(true);2433edit_button->set_toggle_mode(true);2434edit_button->set_theme_type_variation(SNAME("EditorInspectorButton"));2435default_layout->add_child(edit_button);2436edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyQuaternion::_edit_custom_value));24372438add_focusable(edit_button);24392440if (!horizontal) {2441set_label_reference(spin[0]); //show text and buttons around this2442}2443}24442445///////////////////// AABB /////////////////////////24462447void EditorPropertyAABB::_set_read_only(bool p_read_only) {2448for (int i = 0; i < 6; i++) {2449spin[i]->set_read_only(p_read_only);2450}2451}24522453void EditorPropertyAABB::_value_changed(double val, const String &p_name) {2454AABB p;2455p.position.x = spin[0]->get_value();2456p.position.y = spin[1]->get_value();2457p.position.z = spin[2]->get_value();2458p.size.x = spin[3]->get_value();2459p.size.y = spin[4]->get_value();2460p.size.z = spin[5]->get_value();2461emit_changed(get_edited_property(), p, p_name);2462}24632464void EditorPropertyAABB::update_property() {2465AABB val = get_edited_property_value();2466spin[0]->set_value_no_signal(val.position.x);2467spin[1]->set_value_no_signal(val.position.y);2468spin[2]->set_value_no_signal(val.position.z);2469spin[3]->set_value_no_signal(val.size.x);2470spin[4]->set_value_no_signal(val.size.y);2471spin[5]->set_value_no_signal(val.size.z);2472}24732474void EditorPropertyAABB::_notification(int p_what) {2475switch (p_what) {2476case NOTIFICATION_THEME_CHANGED: {2477const Color *colors = _get_property_colors();2478for (int i = 0; i < 6; i++) {2479spin[i]->add_theme_color_override("label_color", colors[i % 3]);2480}2481} break;2482}2483}24842485void EditorPropertyAABB::setup(const EditorPropertyRangeHint &p_range_hint) {2486for (int i = 0; i < 6; i++) {2487spin[i]->set_min(p_range_hint.min);2488spin[i]->set_max(p_range_hint.max);2489spin[i]->set_step(p_range_hint.step);2490if (p_range_hint.hide_control) {2491spin[i]->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);2492}2493spin[i]->set_allow_greater(true);2494spin[i]->set_allow_lesser(true);2495spin[i]->set_suffix(p_range_hint.suffix);2496}2497}24982499EditorPropertyAABB::EditorPropertyAABB() {2500GridContainer *g = memnew(GridContainer);2501g->set_columns(3);2502add_child(g);25032504static const char *desc[6] = { "x", "y", "z", "w", "h", "d" };2505for (int i = 0; i < 6; i++) {2506spin[i] = memnew(EditorSpinSlider);2507spin[i]->set_label(desc[i]);2508spin[i]->set_accessibility_name(desc[i]);2509spin[i]->set_flat(true);25102511g->add_child(spin[i]);2512spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2513add_focusable(spin[i]);2514spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyAABB::_value_changed).bind(desc[i]));2515}2516set_bottom_editor(g);2517}25182519///////////////////// TRANSFORM2D /////////////////////////25202521void EditorPropertyTransform2D::_set_read_only(bool p_read_only) {2522for (int i = 0; i < 6; i++) {2523spin[i]->set_read_only(p_read_only);2524}2525}25262527void EditorPropertyTransform2D::_value_changed(double val, const String &p_name) {2528Transform2D p;2529p[0][0] = spin[0]->get_value();2530p[1][0] = spin[1]->get_value();2531p[2][0] = spin[2]->get_value();2532p[0][1] = spin[3]->get_value();2533p[1][1] = spin[4]->get_value();2534p[2][1] = spin[5]->get_value();25352536emit_changed(get_edited_property(), p, p_name);2537}25382539void EditorPropertyTransform2D::update_property() {2540Transform2D val = get_edited_property_value();2541spin[0]->set_value_no_signal(val[0][0]);2542spin[1]->set_value_no_signal(val[1][0]);2543spin[2]->set_value_no_signal(val[2][0]);2544spin[3]->set_value_no_signal(val[0][1]);2545spin[4]->set_value_no_signal(val[1][1]);2546spin[5]->set_value_no_signal(val[2][1]);2547}25482549void EditorPropertyTransform2D::_notification(int p_what) {2550switch (p_what) {2551case NOTIFICATION_THEME_CHANGED: {2552const Color *colors = _get_property_colors();2553for (int i = 0; i < 6; i++) {2554// For Transform2D, use the 4th color (cyan) for the origin vector.2555if (i % 3 == 2) {2556spin[i]->add_theme_color_override("label_color", colors[3]);2557} else {2558spin[i]->add_theme_color_override("label_color", colors[i % 3]);2559}2560}2561} break;2562}2563}25642565void EditorPropertyTransform2D::setup(const EditorPropertyRangeHint &p_range_hint) {2566for (int i = 0; i < 6; i++) {2567spin[i]->set_min(p_range_hint.min);2568spin[i]->set_max(p_range_hint.max);2569spin[i]->set_step(p_range_hint.step);2570if (p_range_hint.hide_control) {2571spin[i]->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);2572}2573spin[i]->set_allow_greater(true);2574spin[i]->set_allow_lesser(true);2575if (i % 3 == 2) {2576spin[i]->set_suffix(p_range_hint.suffix);2577}2578}2579}25802581EditorPropertyTransform2D::EditorPropertyTransform2D(bool p_include_origin) {2582GridContainer *g = memnew(GridContainer);2583g->set_columns(p_include_origin ? 3 : 2);2584add_child(g);25852586static const char *desc[6] = { "xx", "xy", "xo", "yx", "yy", "yo" };2587for (int i = 0; i < 6; i++) {2588spin[i] = memnew(EditorSpinSlider);2589spin[i]->set_label(desc[i]);2590spin[i]->set_accessibility_name(desc[i]);2591spin[i]->set_flat(true);2592if (p_include_origin || i % 3 != 2) {2593g->add_child(spin[i]);2594}2595spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2596add_focusable(spin[i]);2597spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyTransform2D::_value_changed).bind(desc[i]));2598}2599set_bottom_editor(g);2600}26012602///////////////////// BASIS /////////////////////////26032604void EditorPropertyBasis::_set_read_only(bool p_read_only) {2605for (int i = 0; i < 9; i++) {2606spin[i]->set_read_only(p_read_only);2607}2608}26092610void EditorPropertyBasis::_value_changed(double val, const String &p_name) {2611Basis p;2612p[0][0] = spin[0]->get_value();2613p[0][1] = spin[1]->get_value();2614p[0][2] = spin[2]->get_value();2615p[1][0] = spin[3]->get_value();2616p[1][1] = spin[4]->get_value();2617p[1][2] = spin[5]->get_value();2618p[2][0] = spin[6]->get_value();2619p[2][1] = spin[7]->get_value();2620p[2][2] = spin[8]->get_value();26212622emit_changed(get_edited_property(), p, p_name);2623}26242625void EditorPropertyBasis::update_property() {2626Basis val = get_edited_property_value();2627spin[0]->set_value_no_signal(val[0][0]);2628spin[1]->set_value_no_signal(val[0][1]);2629spin[2]->set_value_no_signal(val[0][2]);2630spin[3]->set_value_no_signal(val[1][0]);2631spin[4]->set_value_no_signal(val[1][1]);2632spin[5]->set_value_no_signal(val[1][2]);2633spin[6]->set_value_no_signal(val[2][0]);2634spin[7]->set_value_no_signal(val[2][1]);2635spin[8]->set_value_no_signal(val[2][2]);2636}26372638void EditorPropertyBasis::_notification(int p_what) {2639switch (p_what) {2640case NOTIFICATION_THEME_CHANGED: {2641const Color *colors = _get_property_colors();2642for (int i = 0; i < 9; i++) {2643spin[i]->add_theme_color_override("label_color", colors[i % 3]);2644}2645} break;2646}2647}26482649void EditorPropertyBasis::setup(const EditorPropertyRangeHint &p_range_hint) {2650for (int i = 0; i < 9; i++) {2651spin[i]->set_min(p_range_hint.min);2652spin[i]->set_max(p_range_hint.max);2653spin[i]->set_step(p_range_hint.step);2654if (p_range_hint.hide_control) {2655spin[i]->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);2656}2657spin[i]->set_allow_greater(true);2658spin[i]->set_allow_lesser(true);2659// Basis is inherently unitless, however someone may want to use it as2660// a generic way to store 9 values, so we'll still respect the suffix.2661spin[i]->set_suffix(p_range_hint.suffix);2662}2663}26642665EditorPropertyBasis::EditorPropertyBasis() {2666GridContainer *g = memnew(GridContainer);2667g->set_columns(3);2668add_child(g);26692670static const char *desc[9] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz" };2671for (int i = 0; i < 9; i++) {2672spin[i] = memnew(EditorSpinSlider);2673spin[i]->set_label(desc[i]);2674spin[i]->set_accessibility_name(desc[i]);2675spin[i]->set_flat(true);2676g->add_child(spin[i]);2677spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2678add_focusable(spin[i]);2679spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyBasis::_value_changed).bind(desc[i]));2680}2681set_bottom_editor(g);2682}26832684///////////////////// TRANSFORM3D /////////////////////////26852686void EditorPropertyTransform3D::_set_read_only(bool p_read_only) {2687for (int i = 0; i < 12; i++) {2688spin[i]->set_read_only(p_read_only);2689}2690}26912692void EditorPropertyTransform3D::_value_changed(double val, const String &p_name) {2693Transform3D p;2694p.basis[0][0] = spin[0]->get_value();2695p.basis[0][1] = spin[1]->get_value();2696p.basis[0][2] = spin[2]->get_value();2697p.origin[0] = spin[3]->get_value();2698p.basis[1][0] = spin[4]->get_value();2699p.basis[1][1] = spin[5]->get_value();2700p.basis[1][2] = spin[6]->get_value();2701p.origin[1] = spin[7]->get_value();2702p.basis[2][0] = spin[8]->get_value();2703p.basis[2][1] = spin[9]->get_value();2704p.basis[2][2] = spin[10]->get_value();2705p.origin[2] = spin[11]->get_value();27062707emit_changed(get_edited_property(), p, p_name);2708}27092710void EditorPropertyTransform3D::update_property() {2711update_using_transform(get_edited_property_value());2712}27132714void EditorPropertyTransform3D::update_using_transform(Transform3D p_transform) {2715spin[0]->set_value_no_signal(p_transform.basis[0][0]);2716spin[1]->set_value_no_signal(p_transform.basis[0][1]);2717spin[2]->set_value_no_signal(p_transform.basis[0][2]);2718spin[3]->set_value_no_signal(p_transform.origin[0]);2719spin[4]->set_value_no_signal(p_transform.basis[1][0]);2720spin[5]->set_value_no_signal(p_transform.basis[1][1]);2721spin[6]->set_value_no_signal(p_transform.basis[1][2]);2722spin[7]->set_value_no_signal(p_transform.origin[1]);2723spin[8]->set_value_no_signal(p_transform.basis[2][0]);2724spin[9]->set_value_no_signal(p_transform.basis[2][1]);2725spin[10]->set_value_no_signal(p_transform.basis[2][2]);2726spin[11]->set_value_no_signal(p_transform.origin[2]);2727}27282729void EditorPropertyTransform3D::_notification(int p_what) {2730switch (p_what) {2731case NOTIFICATION_THEME_CHANGED: {2732const Color *colors = _get_property_colors();2733for (int i = 0; i < 12; i++) {2734spin[i]->add_theme_color_override("label_color", colors[i % 4]);2735}2736} break;2737}2738}27392740void EditorPropertyTransform3D::setup(const EditorPropertyRangeHint &p_range_hint) {2741for (int i = 0; i < 12; i++) {2742spin[i]->set_min(p_range_hint.min);2743spin[i]->set_max(p_range_hint.max);2744spin[i]->set_step(p_range_hint.step);2745if (p_range_hint.hide_control) {2746spin[i]->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);2747}2748spin[i]->set_allow_greater(true);2749spin[i]->set_allow_lesser(true);2750if (i % 4 == 3) {2751spin[i]->set_suffix(p_range_hint.suffix);2752}2753}2754}27552756EditorPropertyTransform3D::EditorPropertyTransform3D() {2757GridContainer *g = memnew(GridContainer);2758g->set_columns(4);2759add_child(g);27602761static const char *desc[12] = { "xx", "xy", "xz", "xo", "yx", "yy", "yz", "yo", "zx", "zy", "zz", "zo" };2762for (int i = 0; i < 12; i++) {2763spin[i] = memnew(EditorSpinSlider);2764spin[i]->set_label(desc[i]);2765spin[i]->set_accessibility_name(desc[i]);2766spin[i]->set_flat(true);2767g->add_child(spin[i]);2768spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2769add_focusable(spin[i]);2770spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyTransform3D::_value_changed).bind(desc[i]));2771}2772set_bottom_editor(g);2773}27742775///////////////////// PROJECTION /////////////////////////27762777void EditorPropertyProjection::_set_read_only(bool p_read_only) {2778for (int i = 0; i < 12; i++) {2779spin[i]->set_read_only(p_read_only);2780}2781}27822783void EditorPropertyProjection::_value_changed(double val, const String &p_name) {2784Projection p;2785p.columns[0][0] = spin[0]->get_value();2786p.columns[0][1] = spin[1]->get_value();2787p.columns[0][2] = spin[2]->get_value();2788p.columns[0][3] = spin[3]->get_value();2789p.columns[1][0] = spin[4]->get_value();2790p.columns[1][1] = spin[5]->get_value();2791p.columns[1][2] = spin[6]->get_value();2792p.columns[1][3] = spin[7]->get_value();2793p.columns[2][0] = spin[8]->get_value();2794p.columns[2][1] = spin[9]->get_value();2795p.columns[2][2] = spin[10]->get_value();2796p.columns[2][3] = spin[11]->get_value();2797p.columns[3][0] = spin[12]->get_value();2798p.columns[3][1] = spin[13]->get_value();2799p.columns[3][2] = spin[14]->get_value();2800p.columns[3][3] = spin[15]->get_value();28012802emit_changed(get_edited_property(), p, p_name);2803}28042805void EditorPropertyProjection::update_property() {2806update_using_transform(get_edited_property_value());2807}28082809void EditorPropertyProjection::update_using_transform(Projection p_transform) {2810spin[0]->set_value_no_signal(p_transform.columns[0][0]);2811spin[1]->set_value_no_signal(p_transform.columns[0][1]);2812spin[2]->set_value_no_signal(p_transform.columns[0][2]);2813spin[3]->set_value_no_signal(p_transform.columns[0][3]);2814spin[4]->set_value_no_signal(p_transform.columns[1][0]);2815spin[5]->set_value_no_signal(p_transform.columns[1][1]);2816spin[6]->set_value_no_signal(p_transform.columns[1][2]);2817spin[7]->set_value_no_signal(p_transform.columns[1][3]);2818spin[8]->set_value_no_signal(p_transform.columns[2][0]);2819spin[9]->set_value_no_signal(p_transform.columns[2][1]);2820spin[10]->set_value_no_signal(p_transform.columns[2][2]);2821spin[11]->set_value_no_signal(p_transform.columns[2][3]);2822spin[12]->set_value_no_signal(p_transform.columns[3][0]);2823spin[13]->set_value_no_signal(p_transform.columns[3][1]);2824spin[14]->set_value_no_signal(p_transform.columns[3][2]);2825spin[15]->set_value_no_signal(p_transform.columns[3][3]);2826}28272828void EditorPropertyProjection::_notification(int p_what) {2829switch (p_what) {2830case NOTIFICATION_THEME_CHANGED: {2831const Color *colors = _get_property_colors();2832for (int i = 0; i < 16; i++) {2833spin[i]->add_theme_color_override("label_color", colors[i % 4]);2834}2835} break;2836}2837}28382839void EditorPropertyProjection::setup(const EditorPropertyRangeHint &p_range_hint) {2840for (int i = 0; i < 16; i++) {2841spin[i]->set_min(p_range_hint.min);2842spin[i]->set_max(p_range_hint.max);2843spin[i]->set_step(p_range_hint.step);2844if (p_range_hint.hide_control) {2845spin[i]->set_control_state(EditorSpinSlider::CONTROL_STATE_HIDE);2846}2847spin[i]->set_allow_greater(true);2848spin[i]->set_allow_lesser(true);2849if (i % 4 == 3) {2850spin[i]->set_suffix(p_range_hint.suffix);2851}2852}2853}28542855EditorPropertyProjection::EditorPropertyProjection() {2856GridContainer *g = memnew(GridContainer);2857g->set_columns(4);2858add_child(g);28592860static const char *desc[16] = { "xx", "xy", "xz", "xw", "yx", "yy", "yz", "yw", "zx", "zy", "zz", "zw", "wx", "wy", "wz", "ww" };2861for (int i = 0; i < 16; i++) {2862spin[i] = memnew(EditorSpinSlider);2863spin[i]->set_label(desc[i]);2864spin[i]->set_accessibility_name(desc[i]);2865spin[i]->set_flat(true);2866g->add_child(spin[i]);2867spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2868add_focusable(spin[i]);2869spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyProjection::_value_changed).bind(desc[i]));2870}2871set_bottom_editor(g);2872}2873////////////// COLOR PICKER //////////////////////28742875void EditorPropertyColor::_set_read_only(bool p_read_only) {2876picker->set_disabled(p_read_only);2877}28782879void EditorPropertyColor::_color_changed(const Color &p_color) {2880if (!live_changes_enabled) {2881return;2882}28832884// Cancel the color change if the current color is identical to the new one.2885if (((Color)get_edited_property_value()).is_equal_approx(p_color)) {2886return;2887}28882889// Preview color change, bypassing undo/redo.2890get_edited_object()->set(get_edited_property(), p_color);2891}28922893void EditorPropertyColor::_picker_created() {2894picker->get_popup()->connect("about_to_popup", callable_mp(this, &EditorPropertyColor::_popup_opening));2895picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed), CONNECT_DEFERRED);2896}28972898void EditorPropertyColor::_popup_opening() {2899if (EditorNode::get_singleton()) {2900EditorNode::get_singleton()->setup_color_picker(picker->get_picker());2901}2902last_color = picker->get_pick_color();2903was_checked = !is_checkable() || is_checked();2904}29052906void EditorPropertyColor::_popup_closed() {2907get_edited_object()->set(get_edited_property(), was_checked ? Variant(last_color) : Variant());2908if (!picker->get_pick_color().is_equal_approx(last_color)) {2909emit_changed(get_edited_property(), picker->get_pick_color(), "", false);2910}2911}29122913void EditorPropertyColor::update_property() {2914picker->set_pick_color(get_edited_property_display_value());2915const Color color = picker->get_pick_color();29162917// Add a tooltip to display each channel's values without having to click the ColorPickerButton2918if (picker->is_editing_alpha()) {2919picker->set_tooltip_text(vformat(2920"R: %s\nG: %s\nB: %s\nA: %s",2921rtos(color.r).pad_decimals(2),2922rtos(color.g).pad_decimals(2),2923rtos(color.b).pad_decimals(2),2924rtos(color.a).pad_decimals(2)));2925} else {2926picker->set_tooltip_text(vformat(2927"R: %s\nG: %s\nB: %s",2928rtos(color.r).pad_decimals(2),2929rtos(color.g).pad_decimals(2),2930rtos(color.b).pad_decimals(2)));2931}2932}29332934void EditorPropertyColor::setup(bool p_show_alpha) {2935picker->set_edit_alpha(p_show_alpha);2936}29372938void EditorPropertyColor::set_live_changes_enabled(bool p_enabled) {2939live_changes_enabled = p_enabled;2940}29412942EditorPropertyColor::EditorPropertyColor() {2943picker = memnew(ColorPickerButton);2944add_child(picker);2945picker->set_flat(true);2946picker->set_theme_type_variation(SNAME("EditorInspectorButton"));2947picker->connect("color_changed", callable_mp(this, &EditorPropertyColor::_color_changed));2948picker->connect("picker_created", callable_mp(this, &EditorPropertyColor::_picker_created), CONNECT_ONE_SHOT);2949}29502951////////////// NODE PATH //////////////////////29522953void EditorPropertyNodePath::_set_read_only(bool p_read_only) {2954assign->set_disabled(p_read_only);2955menu->set_disabled(p_read_only);2956}29572958Variant EditorPropertyNodePath::_get_cache_value(const StringName &p_prop, bool &r_valid) const {2959if (p_prop == get_edited_property()) {2960r_valid = true;2961return const_cast<EditorPropertyNodePath *>(this)->get_edited_object()->get(get_edited_property(), &r_valid);2962}2963return Variant();2964}29652966void EditorPropertyNodePath::_node_selected(const NodePath &p_path, bool p_absolute) {2967NodePath path = p_path;2968Node *base_node = get_base_node();29692970if (!base_node && Object::cast_to<RefCounted>(get_edited_object())) {2971Node *to_node = get_node(p_path);2972ERR_FAIL_NULL(to_node);2973path = get_tree()->get_edited_scene_root()->get_path_to(to_node);2974}29752976if (p_absolute && base_node) { // for AnimationTrackKeyEdit2977path = base_node->get_path().rel_path_to(p_path);2978}29792980if (editing_node) {2981if (!base_node) {2982emit_changed(get_edited_property(), get_tree()->get_edited_scene_root()->get_node(path));2983} else {2984emit_changed(get_edited_property(), base_node->get_node(path));2985}2986} else {2987emit_changed(get_edited_property(), path);2988}2989update_property();2990}29912992void EditorPropertyNodePath::_node_assign() {2993if (!scene_tree) {2994scene_tree = memnew(SceneTreeDialog);2995scene_tree->get_scene_tree()->set_show_enabled_subscene(true);2996scene_tree->set_valid_types(valid_types);2997add_child(scene_tree);2998scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected).bind(true));2999}30003001Variant val = get_edited_property_value();3002Node *n = nullptr;3003if (val.get_type() == Variant::Type::NODE_PATH) {3004Node *base_node = get_base_node();3005n = base_node == nullptr ? nullptr : base_node->get_node_or_null(val);3006} else {3007n = Object::cast_to<Node>(val);3008}3009scene_tree->popup_scenetree_dialog(n, get_base_node());3010}30113012void EditorPropertyNodePath::_assign_draw() {3013if (dropping) {3014Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));3015assign->draw_rect(Rect2(Point2(), assign->get_size()), color, false);3016}3017}30183019void EditorPropertyNodePath::_update_menu() {3020const NodePath &np = _get_node_path();30213022menu->get_popup()->set_item_disabled(ACTION_CLEAR, np.is_empty());3023menu->get_popup()->set_item_disabled(ACTION_COPY, np.is_empty());30243025Node *edited_node = Object::cast_to<Node>(get_edited_object());3026menu->get_popup()->set_item_disabled(ACTION_SELECT, !edited_node || !edited_node->has_node(np));3027}30283029void EditorPropertyNodePath::_menu_option(int p_idx) {3030switch (p_idx) {3031case ACTION_CLEAR: {3032if (editing_node) {3033emit_changed(get_edited_property(), Variant());3034} else {3035emit_changed(get_edited_property(), NodePath());3036}3037update_property();3038} break;30393040case ACTION_COPY: {3041DisplayServer::get_singleton()->clipboard_set(String(_get_node_path()));3042} break;30433044case ACTION_EDIT: {3045assign->hide();3046menu->hide();30473048const NodePath &np = _get_node_path();3049edit->set_text(String(np));3050edit->show();3051callable_mp((Control *)edit, &Control::grab_focus).call_deferred(false);3052} break;30533054case ACTION_SELECT: {3055const Node *edited_node = get_base_node();3056ERR_FAIL_NULL(edited_node);30573058const NodePath &np = _get_node_path();3059Node *target_node = edited_node->get_node_or_null(np);3060ERR_FAIL_NULL(target_node);30613062SceneTreeDock::get_singleton()->set_selected(target_node);3063} break;3064}3065}30663067void EditorPropertyNodePath::_accept_text() {3068_text_submitted(edit->get_text());3069}30703071void EditorPropertyNodePath::_text_submitted(const String &p_text) {3072NodePath np = p_text;3073_node_selected(np, false);3074edit->hide();3075assign->show();3076menu->show();3077}30783079const NodePath EditorPropertyNodePath::_get_node_path() const {3080const Node *base_node = const_cast<EditorPropertyNodePath *>(this)->get_base_node();30813082Variant val = get_edited_property_value();3083Node *n = Object::cast_to<Node>(val);3084if (n) {3085if (!n->is_inside_tree()) {3086return NodePath();3087}3088if (base_node) {3089return base_node->get_path_to(n);3090} else {3091return get_tree()->get_edited_scene_root()->get_path_to(n);3092}3093} else {3094return val;3095}3096}30973098bool EditorPropertyNodePath::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {3099return !is_read_only() && is_drop_valid(p_data);3100}31013102void EditorPropertyNodePath::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {3103ERR_FAIL_COND(!is_drop_valid(p_data));3104Dictionary data_dict = p_data;3105Array nodes = data_dict["nodes"];3106Node *node = get_tree()->get_edited_scene_root()->get_node(nodes[0]);31073108if (node) {3109_node_selected(node->get_path());3110}3111}31123113bool EditorPropertyNodePath::is_drop_valid(const Dictionary &p_drag_data) const {3114if (!p_drag_data.has("type") || p_drag_data["type"] != "nodes") {3115return false;3116}3117Array nodes = p_drag_data["nodes"];3118if (nodes.size() != 1) {3119return false;3120}31213122Object *data_root = p_drag_data.get("scene_root", (Object *)nullptr);3123if (data_root && get_tree()->get_edited_scene_root() != data_root) {3124return false;3125}31263127Node *dropped_node = get_tree()->get_edited_scene_root()->get_node(nodes[0]);3128ERR_FAIL_NULL_V(dropped_node, false);31293130if (valid_types.is_empty()) {3131// No type requirements specified so any type is valid.3132return true;3133}31343135for (const StringName &E : valid_types) {3136if (dropped_node->is_class(E) ||3137EditorNode::get_singleton()->is_object_of_custom_type(dropped_node, E)) {3138return true;3139} else {3140Ref<Script> dropped_node_script = dropped_node->get_script();3141while (dropped_node_script.is_valid()) {3142if (dropped_node_script->get_path() == E) {3143return true;3144}3145dropped_node_script = dropped_node_script->get_base_script();3146}3147}3148}31493150return false;3151}31523153void EditorPropertyNodePath::update_property() {3154const Node *base_node = get_base_node();3155const NodePath &p = _get_node_path();3156assign->set_tooltip_text(String(p));31573158if (p.is_empty()) {3159assign->set_button_icon(Ref<Texture2D>());3160assign->set_text(TTR("Assign..."));3161assign->set_flat(false);3162return;3163}3164assign->set_flat(true);31653166if (!base_node || !base_node->has_node(p)) {3167assign->set_button_icon(Ref<Texture2D>());3168assign->set_text(String(p));3169return;3170}31713172const Node *target_node = base_node->get_node(p);3173ERR_FAIL_NULL(target_node);31743175String new_text = target_node->get_name();3176if (new_text.contains_char('@')) {3177assign->set_button_icon(Ref<Texture2D>());3178assign->set_text(String(p));3179return;3180}31813182if (p.get_subname_count() > 0) {3183new_text += ":" + p.get_concatenated_subnames();3184}3185assign->set_text(new_text);3186assign->set_button_icon(EditorNode::get_singleton()->get_object_icon(target_node));3187}31883189void EditorPropertyNodePath::setup(const Vector<StringName> &p_valid_types, bool p_use_path_from_scene_root, bool p_editing_node) {3190valid_types = p_valid_types;3191editing_node = p_editing_node;3192use_path_from_scene_root = p_use_path_from_scene_root;3193}31943195void EditorPropertyNodePath::_notification(int p_what) {3196switch (p_what) {3197case NOTIFICATION_THEME_CHANGED: {3198menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));3199menu->get_popup()->set_item_icon(ACTION_CLEAR, get_editor_theme_icon(SNAME("Clear")));3200menu->get_popup()->set_item_icon(ACTION_COPY, get_editor_theme_icon(SNAME("ActionCopy")));3201menu->get_popup()->set_item_icon(ACTION_EDIT, get_editor_theme_icon(SNAME("Edit")));3202menu->get_popup()->set_item_icon(ACTION_SELECT, get_editor_theme_icon(SNAME("ExternalLink")));32033204// Use a constant width for the icon to avoid sizing issues or blurry icons.3205assign->add_theme_constant_override("icon_max_width", get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor)));3206} break;32073208case NOTIFICATION_DRAG_BEGIN: {3209if (!is_read_only() && is_drop_valid(get_viewport()->gui_get_drag_data())) {3210dropping = true;3211assign->queue_redraw();3212}3213} break;32143215case NOTIFICATION_DRAG_END: {3216if (dropping) {3217dropping = false;3218assign->queue_redraw();3219}3220} break;3221}3222}32233224Node *EditorPropertyNodePath::get_base_node() {3225Node *base_node = Object::cast_to<Node>(get_edited_object());32263227// For proxy objects, specifies the node to which the path is relative.3228if (!base_node && get_edited_object()->has_meta("__base_node_relative")) {3229base_node = Object::cast_to<Node>(get_edited_object()->get_meta("__base_node_relative"));3230}32313232if (!base_node) {3233base_node = Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object());3234}3235if (!base_node) {3236// Try a base node within history.3237if (EditorNode::get_singleton()->get_editor_selection_history()->get_path_size() > 0) {3238Object *base = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_path_object(0));3239if (base) {3240base_node = Object::cast_to<Node>(base);3241}3242}3243}32443245if (!use_path_from_scene_root) {3246return base_node;3247}32483249if (get_edited_object()->has_method("get_root_path")) {3250return Object::cast_to<Node>(get_edited_object()->call("get_root_path"));3251}32523253if (!base_node) {3254return nullptr; // Editing external resources.3255}32563257if (base_node->is_instance()) {3258return base_node; // Known scene root.3259}32603261base_node = base_node->get_owner();3262if (base_node) {3263return base_node; // Node in known scene.3264}32653266return get_tree()->get_edited_scene_root(); // Treat as a node in the main scene.3267}32683269EditorPropertyNodePath::EditorPropertyNodePath() {3270HBoxContainer *hbc = memnew(HBoxContainer);3271hbc->add_theme_constant_override("separation", 0);3272add_child(hbc);3273assign = memnew(Button);3274assign->set_accessibility_name(TTRC("Assign Node"));3275assign->set_flat(true);3276assign->set_theme_type_variation(SNAME("EditorInspectorButton"));3277assign->set_h_size_flags(SIZE_EXPAND_FILL);3278assign->set_clip_text(true);3279assign->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);3280assign->set_expand_icon(true);3281assign->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyNodePath::_node_assign));3282assign->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyNodePath::_assign_draw));3283SET_DRAG_FORWARDING_CD(assign, EditorPropertyNodePath);3284hbc->add_child(assign);32853286menu = memnew(MenuButton);3287menu->set_flat(true);3288menu->connect(SNAME("about_to_popup"), callable_mp(this, &EditorPropertyNodePath::_update_menu));3289hbc->add_child(menu);32903291menu->get_popup()->add_item(TTR("Clear"), ACTION_CLEAR);3292menu->get_popup()->add_item(TTR("Copy as Text"), ACTION_COPY);3293menu->get_popup()->add_item(TTR("Edit"), ACTION_EDIT);3294menu->get_popup()->add_item(TTR("Show Node in Tree"), ACTION_SELECT);3295menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyNodePath::_menu_option));32963297edit = memnew(LineEdit);3298edit->set_accessibility_name(TTRC("Node Path"));3299edit->set_h_size_flags(SIZE_EXPAND_FILL);3300edit->hide();3301edit->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyNodePath::_accept_text));3302edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyNodePath::_text_submitted));3303hbc->add_child(edit);3304}33053306///////////////////// RID /////////////////////////33073308void EditorPropertyRID::update_property() {3309RID rid = get_edited_property_value();3310if (rid.is_valid()) {3311uint64_t id = rid.get_id();3312label->set_text("RID: " + uitos(id));3313} else {3314label->set_text(TTR("Invalid RID"));3315}3316}33173318EditorPropertyRID::EditorPropertyRID() {3319label = memnew(Label);3320add_child(label);3321}33223323////////////// RESOURCE //////////////////////33243325void EditorPropertyResource::_set_read_only(bool p_read_only) {3326resource_picker->set_editable(!p_read_only);3327}33283329void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_inspect) {3330if (p_resource->is_built_in() && !p_resource->get_path().is_empty()) {3331String parent = p_resource->get_path().get_slice("::", 0);3332List<String> extensions;3333ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);33343335if (p_inspect) {3336if (extensions.find(parent.get_extension()) && (!EditorNode::get_singleton()->get_edited_scene() || EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path() != parent)) {3337// If the resource belongs to another (non-imported) scene, edit it in that scene instead.3338if (!FileAccess::exists(parent + ".import")) {3339callable_mp(EditorNode::get_singleton(), &EditorNode::edit_foreign_resource).call_deferred(p_resource);3340return;3341}3342}3343}3344}33453346if (!p_inspect && use_sub_inspector) {3347bool unfold = !get_edited_object()->editor_is_section_unfolded(get_edited_property());3348get_edited_object()->editor_set_section_unfold(get_edited_property(), unfold);3349update_property();3350} else if (!is_checkable() || is_checked()) {3351emit_signal(SNAME("resource_selected"), get_edited_property(), p_resource);3352}3353}33543355void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource) {3356Resource *r = Object::cast_to<Resource>(get_edited_object());3357if (r) {3358// Check for recursive setting of resource3359HashSet<Resource *> resources_found;3360resources_found.insert(r);3361bool found = EditorNode::find_recursive_resources(p_resource, resources_found);3362if (found) {3363callable_mp(EditorNode::get_singleton(), &EditorNode::show_warning).call_deferred(TTR("Recursion detected, unable to assign resource to property."), TTR("Warning!"));3364emit_changed(get_edited_property(), Ref<Resource>());3365update_property();3366return;3367}3368}33693370if (p_resource.is_valid() && p_resource->is_local_to_scene()) {3371// Attempting to configure the local scene.3372Node *local_scene = _get_base_node();3373if (local_scene) {3374HashMap<Ref<Resource>, Ref<Resource>> remap;3375p_resource->configure_for_local_scene(local_scene, remap);3376} else {3377WARN_PRINT("You are attempting to assign a local-to-scene resource outside the scene.");3378}3379}33803381// The bool is_script applies only to an object's main script.3382// Changing the value of Script-type exported variables of the main script should not trigger saving/reloading properties.3383bool is_script = false;3384Ref<Script> s = p_resource;3385if (get_edited_object() && s.is_valid() && get_edited_property() == CoreStringName(script)) {3386is_script = true;3387InspectorDock::get_singleton()->store_script_properties(get_edited_object());3388s->call("set_instance_base_type", get_edited_object()->get_class());3389}33903391// Prevent the creation of invalid ViewportTextures when possible.3392Ref<ViewportTexture> vpt = p_resource;3393if (vpt.is_valid()) {3394r = Object::cast_to<Resource>(get_edited_object());3395if (Object::cast_to<VisualShaderNodeTexture>(r)) {3396EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture in a Texture2D node because the texture will not be bound to a scene.\nUse a Texture2DParameter node instead and set the texture in the \"Shader Parameters\" tab."));3397emit_changed(get_edited_property(), Ref<Resource>());3398update_property();3399return;3400}34013402if (r && r->get_path().is_resource_file()) {3403EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on resources saved as a file.\nResource needs to belong to a scene."));3404emit_changed(get_edited_property(), Ref<Resource>());3405update_property();3406return;3407}34083409if (r && !r->is_local_to_scene()) {3410EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on this resource because it's not set as local to scene.\nPlease switch on the 'local to scene' property on it (and all resources containing it up to a node)."));3411emit_changed(get_edited_property(), Ref<Resource>());3412update_property();3413return;3414}3415}34163417emit_changed(get_edited_property(), p_resource);3418update_property();34193420if (is_script) {3421// Restore properties if script was changed.3422InspectorDock::get_singleton()->apply_script_properties(get_edited_object());3423}34243425// Automatically suggest setting up the path for a ViewportTexture.3426if (vpt.is_valid() && vpt->get_viewport_path_in_scene().is_empty()) {3427if (!scene_tree) {3428scene_tree = memnew(SceneTreeDialog);3429scene_tree->set_title(TTR("Pick a Viewport"));34303431Vector<StringName> valid_types;3432valid_types.push_back("Viewport");3433scene_tree->set_valid_types(valid_types);3434scene_tree->get_scene_tree()->set_show_enabled_subscene(true);34353436add_child(scene_tree);3437scene_tree->connect("selected", callable_mp(this, &EditorPropertyResource::_viewport_selected));3438}3439scene_tree->popup_scenetree_dialog();3440}3441}34423443void EditorPropertyResource::_sub_inspector_property_keyed(const String &p_property, const Variant &p_value, bool p_advance) {3444// The second parameter could be null, causing the event to fire with less arguments, so use the pointer call which preserves it.3445const Variant args[3] = { String(get_edited_property()) + ":" + p_property, p_value, p_advance };3446const Variant *argp[3] = { &args[0], &args[1], &args[2] };3447emit_signalp(SNAME("property_keyed_with_value"), argp, 3);3448}34493450void EditorPropertyResource::_sub_inspector_resource_selected(const Ref<Resource> &p_resource, const String &p_property) {3451emit_signal(SNAME("resource_selected"), String(get_edited_property()) + ":" + p_property, p_resource);3452}34533454void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) {3455emit_signal(SNAME("object_id_selected"), get_edited_property(), p_id);3456}34573458void EditorPropertyResource::_open_editor_pressed() {3459Ref<Resource> res = get_edited_property_value();3460if (res.is_valid()) {3461EditorNode::get_singleton()->edit_item(res.ptr(), this);3462}3463}34643465void EditorPropertyResource::_update_preferred_shader() {3466Node *parent = get_parent();3467EditorProperty *parent_property = nullptr;34683469while (parent && !parent_property) {3470parent_property = Object::cast_to<EditorProperty>(parent);3471parent = parent->get_parent();3472}34733474if (parent_property) {3475EditorShaderPicker *shader_picker = Object::cast_to<EditorShaderPicker>(resource_picker);3476Object *ed_object = parent_property->get_edited_object();3477const StringName &ed_property = parent_property->get_edited_property();34783479// Set preferred shader based on edited parent type.3480if ((Object::cast_to<GPUParticles2D>(ed_object) || Object::cast_to<GPUParticles3D>(ed_object)) && ed_property == SNAME("process_material")) {3481shader_picker->set_preferred_mode(Shader::MODE_PARTICLES);3482} else if (Object::cast_to<FogVolume>(ed_object)) {3483shader_picker->set_preferred_mode(Shader::MODE_FOG);3484} else if (Object::cast_to<CanvasItem>(ed_object)) {3485shader_picker->set_preferred_mode(Shader::MODE_CANVAS_ITEM);3486} else if (Object::cast_to<Node3D>(ed_object) || Object::cast_to<Mesh>(ed_object)) {3487shader_picker->set_preferred_mode(Shader::MODE_SPATIAL);3488} else if (Object::cast_to<Sky>(ed_object)) {3489shader_picker->set_preferred_mode(Shader::MODE_SKY);3490}3491}3492}34933494bool EditorPropertyResource::_should_stop_editing() const {3495return !resource_picker->is_toggle_pressed();3496}34973498Node *EditorPropertyResource::_get_base_node() {3499Node *base_node = Object::cast_to<Node>(get_edited_object());35003501if (!base_node) {3502base_node = Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object());3503}35043505if (!base_node) {3506// Try a base node within history.3507if (EditorNode::get_singleton()->get_editor_selection_history()->get_path_size() > 0) {3508Object *base = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_path_object(0));3509if (base) {3510base_node = Object::cast_to<Node>(base);3511}3512}3513}35143515if (!base_node) {3516return nullptr; // Editing external resources.3517}35183519if (!base_node->get_scene_file_path().is_empty()) {3520return base_node; // Known scene root.3521}35223523base_node = base_node->get_owner();3524if (base_node) {3525return base_node; // Node in known scene.3526}35273528return get_tree()->get_edited_scene_root(); // Treat as a node in the main scene.3529}35303531void EditorPropertyResource::_viewport_selected(const NodePath &p_path) {3532Node *to_node = get_node(p_path);3533if (!Object::cast_to<Viewport>(to_node)) {3534EditorNode::get_singleton()->show_warning(TTR("Selected node is not a Viewport!"));3535return;3536}35373538Ref<ViewportTexture> vt = get_edited_property_value();3539ERR_FAIL_COND(vt.is_null());35403541Node *local_scene = _get_base_node();3542ERR_FAIL_NULL(local_scene);3543vt->set_viewport_path_in_scene(local_scene->get_path_to(to_node));35443545emit_changed(get_edited_property(), vt);3546update_property();3547}35483549void EditorPropertyResource::setup(Object *p_object, const String &p_path, const String &p_base_type) {3550if (resource_picker) {3551memdelete(resource_picker);3552resource_picker = nullptr;3553}35543555if (p_path == "script" && p_base_type == "Script" && Object::cast_to<Node>(p_object)) {3556EditorScriptPicker *script_picker = memnew(EditorScriptPicker);3557script_picker->set_script_owner(Object::cast_to<Node>(p_object));3558resource_picker = script_picker;3559} else if (p_path == "shader" && p_base_type == "Shader" && Object::cast_to<ShaderMaterial>(p_object)) {3560EditorShaderPicker *shader_picker = memnew(EditorShaderPicker);3561shader_picker->set_edited_material(Object::cast_to<ShaderMaterial>(p_object));3562resource_picker = shader_picker;3563connect(SceneStringName(ready), callable_mp(this, &EditorPropertyResource::_update_preferred_shader));3564} else if (ClassDB::is_parent_class(p_base_type, "AudioStream")) {3565EditorAudioStreamPicker *astream_picker = memnew(EditorAudioStreamPicker);3566resource_picker = astream_picker;3567} else {3568resource_picker = memnew(EditorResourcePicker);3569}35703571resource_picker->set_base_type(p_base_type);3572resource_picker->set_resource_owner(p_object);3573resource_picker->set_property_path(p_path);3574resource_picker->set_editable(true);3575resource_picker->set_h_size_flags(SIZE_EXPAND_FILL);3576add_child(resource_picker);35773578resource_picker->connect("resource_selected", callable_mp(this, &EditorPropertyResource::_resource_selected));3579resource_picker->connect("resource_changed", callable_mp(this, &EditorPropertyResource::_resource_changed));35803581for (int i = 0; i < resource_picker->get_child_count(); i++) {3582Button *b = Object::cast_to<Button>(resource_picker->get_child(i));3583if (b) {3584add_focusable(b);3585}3586}3587}35883589void EditorPropertyResource::update_property() {3590Ref<Resource> res = get_edited_property_display_value();35913592if (use_sub_inspector) {3593if (res.is_valid() != resource_picker->is_toggle_mode()) {3594resource_picker->set_toggle_mode(res.is_valid());3595}35963597if (res.is_valid() && get_edited_object()->editor_is_section_unfolded(get_edited_property())) {3598if (!sub_inspector) {3599sub_inspector = memnew(EditorInspector);3600sub_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);3601sub_inspector->set_use_doc_hints(true);36023603EditorInspector *parent_inspector = get_parent_inspector();3604if (parent_inspector) {3605sub_inspector->set_root_inspector(parent_inspector->get_root_inspector());3606sub_inspector->register_text_enter(parent_inspector->search_box);3607}36083609sub_inspector->set_property_name_style(InspectorDock::get_singleton()->get_property_name_style());36103611sub_inspector->connect("property_keyed", callable_mp(this, &EditorPropertyResource::_sub_inspector_property_keyed));3612sub_inspector->connect("resource_selected", callable_mp(this, &EditorPropertyResource::_sub_inspector_resource_selected));3613sub_inspector->connect("object_id_selected", callable_mp(this, &EditorPropertyResource::_sub_inspector_object_id_selected));3614sub_inspector->set_keying(is_keying());3615sub_inspector->set_read_only(is_read_only());3616sub_inspector->set_use_folding(is_using_folding());36173618sub_inspector->set_draw_focus_border(false);3619sub_inspector->set_focus_mode(FocusMode::FOCUS_NONE);36203621sub_inspector->set_use_filter(use_filter);36223623add_child(sub_inspector);3624set_bottom_editor(sub_inspector);36253626resource_picker->set_toggle_pressed(true);36273628Array editor_list;3629for (int i = 0; i < EditorNode::get_editor_data().get_editor_plugin_count(); i++) {3630EditorPlugin *ep = EditorNode::get_editor_data().get_editor_plugin(i);3631if (ep->handles(res.ptr())) {3632editor_list.push_back(ep);3633}3634}36353636if (!editor_list.is_empty()) {3637// Open editor directly.3638_open_editor_pressed();3639opened_editor = true;3640}3641}36423643sub_inspector->set_read_only(is_checkable() && !is_checked());36443645if (res.ptr() != sub_inspector->get_edited_object()) {3646sub_inspector->edit(res.ptr());3647_update_property_bg();3648}36493650} else if (sub_inspector) {3651set_bottom_editor(nullptr);3652memdelete(sub_inspector);3653sub_inspector = nullptr;36543655if (opened_editor) {3656EditorNode::get_singleton()->hide_unused_editors();3657opened_editor = false;3658}3659}3660}36613662resource_picker->set_edited_resource_no_check(res);3663const Ref<Resource> &real_res = get_edited_property_value();3664resource_picker->set_force_allow_unique(real_res.is_null() && res.is_valid());3665}36663667void EditorPropertyResource::collapse_all_folding() {3668if (sub_inspector) {3669sub_inspector->collapse_all_folding();3670}3671}36723673void EditorPropertyResource::expand_all_folding() {3674if (sub_inspector) {3675sub_inspector->expand_all_folding();3676}3677}36783679void EditorPropertyResource::expand_revertable() {3680if (sub_inspector) {3681sub_inspector->expand_revertable();3682}3683}36843685void EditorPropertyResource::set_use_sub_inspector(bool p_enable) {3686use_sub_inspector = p_enable;3687}36883689void EditorPropertyResource::set_use_filter(bool p_use) {3690use_filter = p_use;3691if (sub_inspector) {3692update_property();3693}3694}36953696void EditorPropertyResource::fold_resource() {3697bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());3698if (unfolded) {3699resource_picker->set_toggle_pressed(false);3700get_edited_object()->editor_set_section_unfold(get_edited_property(), false);3701update_property();3702}3703}37043705bool EditorPropertyResource::is_colored(ColorationMode p_mode) {3706switch (p_mode) {3707case COLORATION_CONTAINER_RESOURCE:3708return sub_inspector != nullptr;3709case COLORATION_RESOURCE:3710return true;3711case COLORATION_EXTERNAL:3712if (sub_inspector) {3713Resource *edited_resource = Object::cast_to<Resource>(sub_inspector->get_edited_object());3714return edited_resource && !edited_resource->is_built_in();3715}3716break;3717}3718return false;3719}37203721void EditorPropertyResource::_notification(int p_what) {3722switch (p_what) {3723case NOTIFICATION_EXIT_TREE: {3724const EditorInspector *ei = get_parent_inspector();3725const EditorInspector *main_ei = InspectorDock::get_inspector_singleton();3726if (ei && main_ei && ei != main_ei && !main_ei->is_ancestor_of(ei)) {3727fold_resource();3728}3729} break;3730}3731}37323733void EditorPropertyResource::_bind_methods() {3734ClassDB::bind_method(D_METHOD("_should_stop_editing"), &EditorPropertyResource::_should_stop_editing);3735}37363737EditorPropertyResource::EditorPropertyResource() {3738use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector"));3739has_borders = true;3740}37413742////////////// DEFAULT PLUGIN //////////////////////37433744bool EditorInspectorDefaultPlugin::can_handle(Object *p_object) {3745return true; // Can handle everything.3746}37473748bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {3749Control *editor = EditorInspectorDefaultPlugin::get_editor_for_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage, p_wide);3750if (editor) {3751add_property_editor(p_path, editor);3752}3753return false;3754}37553756static EditorPropertyRangeHint _parse_range_hint(PropertyHint p_hint, const String &p_hint_text, double p_default_step, bool is_int = false) {3757EditorPropertyRangeHint hint;3758hint.step = p_default_step;3759if (is_int) {3760hint.hide_control = false; // Always show controls for ints, unless specified in hint range.3761}3762Vector<String> slices = p_hint_text.split(",");3763if (p_hint == PROPERTY_HINT_RANGE) {3764ERR_FAIL_COND_V_MSG(slices.size() < 2, hint,3765vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": Missing required min and/or max values.", p_hint_text));37663767hint.or_greater = false; // If using ranged, assume false by default.3768hint.or_less = false;37693770hint.min = slices[0].to_float();3771hint.max = slices[1].to_float();37723773if (slices.size() >= 3 && slices[2].is_valid_float()) {3774// Step is optional, could be something else if not a number.3775hint.step = slices[2].to_float();3776}3777hint.hide_control = false;3778for (int i = 2; i < slices.size(); i++) {3779String slice = slices[i].strip_edges();3780if (slice == "or_greater") {3781hint.or_greater = true;3782} else if (slice == "or_less") {3783hint.or_less = true;3784} else if (slice == "prefer_slider") {3785hint.prefer_slider = true;3786} else if (slice == "hide_control") {3787hint.hide_control = true;3788#ifndef DISABLE_DEPRECATED3789} else if (slice == "hide_slider") {3790hint.hide_control = true;3791#endif3792} else if (slice == "exp") {3793hint.exp_range = true;3794}3795}3796}3797bool degrees = false;3798for (int i = 0; i < slices.size(); i++) {3799String slice = slices[i].strip_edges();3800if (slice == "radians_as_degrees"3801#ifndef DISABLE_DEPRECATED3802|| slice == "radians"3803#endif // DISABLE_DEPRECATED3804) {3805hint.radians_as_degrees = true;3806} else if (slice == "degrees") {3807degrees = true;3808} else if (slice.begins_with("suffix:")) {3809hint.suffix = " " + slice.replace_first("suffix:", "").strip_edges();3810}3811}38123813if ((hint.radians_as_degrees || degrees) && hint.suffix.is_empty()) {3814hint.suffix = U"\u00B0";3815}38163817ERR_FAIL_COND_V_MSG(hint.step == 0, hint,3818vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": Step cannot be 0.", p_hint_text));38193820return hint;3821}38223823static EditorProperty *get_input_action_editor(const String &p_hint_text, bool is_string_name) {3824// TODO: Should probably use a better editor GUI with a search bar.3825// Said GUI could also handle showing builtin options, requiring 1 less hint.3826EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);3827Vector<String> options;3828Vector<String> builtin_options;3829List<PropertyInfo> pinfo;3830ProjectSettings::get_singleton()->get_property_list(&pinfo);3831Vector<String> hints = p_hint_text.remove_char(' ').split(",", false);38323833HashMap<String, List<Ref<InputEvent>>> builtins(InputMap::get_singleton()->get_builtins());3834bool show_builtin = hints.has("show_builtin");38353836for (const PropertyInfo &pi : pinfo) {3837if (!pi.name.begins_with("input/")) {3838continue;3839}38403841const String action_name = pi.name.get_slicec('/', 1);3842if (builtins.has(action_name)) {3843if (show_builtin) {3844builtin_options.append(action_name);3845}3846} else {3847options.append(action_name);3848}3849}3850options.append_array(builtin_options);3851editor->setup(options, Vector<String>(), is_string_name, hints.has("loose_mode"));3852return editor;3853}38543855EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_object, const Variant::Type p_type, const String &p_path, const PropertyHint p_hint, const String &p_hint_text, const BitField<PropertyUsageFlags> p_usage, const bool p_wide) {3856double default_float_step = EDITOR_GET("interface/inspector/default_float_step");38573858switch (p_type) {3859// atomic types3860case Variant::NIL: {3861if (p_usage & PROPERTY_USAGE_NIL_IS_VARIANT) {3862return memnew(EditorPropertyVariant);3863} else {3864return memnew(EditorPropertyNil);3865}3866} break;3867case Variant::BOOL: {3868EditorPropertyCheck *editor = memnew(EditorPropertyCheck);3869return editor;3870} break;3871case Variant::INT: {3872if (p_hint == PROPERTY_HINT_ENUM) {3873EditorPropertyEnum *editor = memnew(EditorPropertyEnum);3874Vector<String> options = p_hint_text.split(",");3875editor->setup(options);3876return editor;38773878} else if (p_hint == PROPERTY_HINT_FLAGS) {3879EditorPropertyFlags *editor = memnew(EditorPropertyFlags);3880Vector<String> options = p_hint_text.split(",");3881editor->setup(options);3882return editor;38833884} else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS ||3885p_hint == PROPERTY_HINT_LAYERS_2D_RENDER ||3886p_hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION ||3887p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS ||3888p_hint == PROPERTY_HINT_LAYERS_3D_RENDER ||3889p_hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION ||3890p_hint == PROPERTY_HINT_LAYERS_AVOIDANCE) {3891EditorPropertyLayers::LayerType lt = EditorPropertyLayers::LAYER_RENDER_2D;3892switch (p_hint) {3893case PROPERTY_HINT_LAYERS_2D_RENDER:3894lt = EditorPropertyLayers::LAYER_RENDER_2D;3895break;3896case PROPERTY_HINT_LAYERS_2D_PHYSICS:3897lt = EditorPropertyLayers::LAYER_PHYSICS_2D;3898break;3899case PROPERTY_HINT_LAYERS_2D_NAVIGATION:3900lt = EditorPropertyLayers::LAYER_NAVIGATION_2D;3901break;3902case PROPERTY_HINT_LAYERS_3D_RENDER:3903lt = EditorPropertyLayers::LAYER_RENDER_3D;3904break;3905case PROPERTY_HINT_LAYERS_3D_PHYSICS:3906lt = EditorPropertyLayers::LAYER_PHYSICS_3D;3907break;3908case PROPERTY_HINT_LAYERS_3D_NAVIGATION:3909lt = EditorPropertyLayers::LAYER_NAVIGATION_3D;3910break;3911case PROPERTY_HINT_LAYERS_AVOIDANCE:3912lt = EditorPropertyLayers::LAYER_AVOIDANCE;3913break;3914default: {3915} //compiler could be smarter here and realize this can't happen3916}3917EditorPropertyLayers *editor = memnew(EditorPropertyLayers);3918editor->setup(lt);3919return editor;3920} else if (p_hint == PROPERTY_HINT_OBJECT_ID) {3921EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);3922editor->setup(p_hint_text);3923return editor;39243925} else {3926EditorPropertyInteger *editor = memnew(EditorPropertyInteger);3927editor->setup(_parse_range_hint(p_hint, p_hint_text, 1, true));3928return editor;3929}3930} break;3931case Variant::FLOAT: {3932if (p_hint == PROPERTY_HINT_EXP_EASING) {3933EditorPropertyEasing *editor = memnew(EditorPropertyEasing);3934bool positive_only = false;3935bool flip = false;3936const Vector<String> hints = p_hint_text.split(",");3937for (int i = 0; i < hints.size(); i++) {3938const String hint = hints[i].strip_edges();3939if (hint == "attenuation") {3940flip = true;3941}3942if (hint == "positive_only") {3943positive_only = true;3944}3945}39463947editor->setup(positive_only, flip);3948return editor;39493950} else {3951EditorPropertyFloat *editor = memnew(EditorPropertyFloat);3952editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step));3953return editor;3954}3955} break;3956case Variant::STRING: {3957if (p_hint == PROPERTY_HINT_ENUM || p_hint == PROPERTY_HINT_ENUM_SUGGESTION) {3958EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);3959Vector<String> options;3960Vector<String> option_names;3961if (p_hint_text.begins_with(";")) {3962// This is not supported officially. Only for `interface/editor/editor_language`.3963for (const String &option : p_hint_text.split(";", false)) {3964options.append(option.get_slicec('/', 0));3965option_names.append(option.get_slicec('/', 1));3966}3967} else {3968options = p_hint_text.split(",", false);3969}3970editor->setup(options, option_names, false, (p_hint == PROPERTY_HINT_ENUM_SUGGESTION));3971return editor;3972} else if (p_hint == PROPERTY_HINT_INPUT_NAME) {3973return get_input_action_editor(p_hint_text, false);3974} else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) {3975Vector<String> options = p_hint_text.split(",", false);3976EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText(false));3977if (options.has("monospace")) {3978editor->set_monospaced(true);3979}3980if (options.has("no_wrap")) {3981editor->set_wrap_lines(false);3982}3983return editor;3984} else if (p_hint == PROPERTY_HINT_EXPRESSION) {3985EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText(true));3986return editor;3987} else if (p_hint == PROPERTY_HINT_TYPE_STRING) {3988EditorPropertyClassName *editor = memnew(EditorPropertyClassName);3989editor->setup(p_hint_text, p_hint_text);3990return editor;3991} else if (p_hint == PROPERTY_HINT_LOCALE_ID) {3992EditorPropertyLocale *editor = memnew(EditorPropertyLocale);3993editor->setup(p_hint_text);3994return editor;3995} else if (p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_FILE || p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE || p_hint == PROPERTY_HINT_FILE_PATH) {3996Vector<String> extensions = p_hint_text.split(",");3997bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE;3998bool folder = p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_GLOBAL_DIR;3999bool save = p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE;4000bool enable_uid = p_hint == PROPERTY_HINT_FILE;4001EditorPropertyPath *editor = memnew(EditorPropertyPath);4002editor->setup(extensions, folder, global, enable_uid);4003if (save) {4004editor->set_save_mode();4005}4006return editor;4007} else {4008EditorPropertyText *editor = memnew(EditorPropertyText);40094010Vector<String> hints = p_hint_text.split(",");4011if (hints.has("monospace")) {4012editor->set_monospaced(true);4013}40144015if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) {4016editor->set_placeholder(p_hint_text);4017} else if (p_hint == PROPERTY_HINT_PASSWORD) {4018editor->set_secret(true);4019editor->set_placeholder(p_hint_text);4020}4021return editor;4022}4023} break;40244025// math types40264027case Variant::VECTOR2: {4028EditorPropertyVector2 *editor = memnew(EditorPropertyVector2(p_wide));4029editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step), p_hint == PROPERTY_HINT_LINK);4030return editor;40314032} break;4033case Variant::VECTOR2I: {4034EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide));4035EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);4036hint.step = Math::round(hint.step);4037editor->setup(hint, p_hint == PROPERTY_HINT_LINK, true);4038return editor;40394040} break;4041case Variant::RECT2: {4042EditorPropertyRect2 *editor = memnew(EditorPropertyRect2(p_wide));4043editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step));4044return editor;4045} break;4046case Variant::RECT2I: {4047EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i(p_wide));4048editor->setup(_parse_range_hint(p_hint, p_hint_text, 1, true));4049return editor;4050} break;4051case Variant::VECTOR3: {4052EditorPropertyVector3 *editor = memnew(EditorPropertyVector3(p_wide));4053editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step), p_hint == PROPERTY_HINT_LINK);4054return editor;40554056} break;4057case Variant::VECTOR3I: {4058EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide));4059EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);4060hint.step = Math::round(hint.step);4061editor->setup(hint, p_hint == PROPERTY_HINT_LINK, true);4062return editor;40634064} break;4065case Variant::VECTOR4: {4066EditorPropertyVector4 *editor = memnew(EditorPropertyVector4);4067editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step), p_hint == PROPERTY_HINT_LINK);4068return editor;40694070} break;4071case Variant::VECTOR4I: {4072EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);4073EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);4074hint.step = Math::round(hint.step);4075editor->setup(hint, p_hint == PROPERTY_HINT_LINK, true);4076return editor;40774078} break;4079case Variant::TRANSFORM2D: {4080EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D);4081editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step));4082return editor;4083} break;4084case Variant::PLANE: {4085EditorPropertyPlane *editor = memnew(EditorPropertyPlane(p_wide));4086editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step));4087return editor;4088} break;4089case Variant::QUATERNION: {4090EditorPropertyQuaternion *editor = memnew(EditorPropertyQuaternion);4091// Quaternions are almost never used for human-readable values that need stepifying,4092// so we should be more precise with their step, as much as the float precision allows.4093#ifdef REAL_T_IS_DOUBLE4094constexpr double QUATERNION_STEP = 1e-14;4095#else4096constexpr double QUATERNION_STEP = 1e-6;4097#endif4098editor->setup(_parse_range_hint(p_hint, p_hint_text, QUATERNION_STEP), p_hint == PROPERTY_HINT_HIDE_QUATERNION_EDIT);4099return editor;4100} break;4101case Variant::AABB: {4102EditorPropertyAABB *editor = memnew(EditorPropertyAABB);4103editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step));4104return editor;4105} break;4106case Variant::BASIS: {4107EditorPropertyBasis *editor = memnew(EditorPropertyBasis);4108editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step));4109return editor;4110} break;4111case Variant::TRANSFORM3D: {4112EditorPropertyTransform3D *editor = memnew(EditorPropertyTransform3D);4113editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step));4114return editor;41154116} break;4117case Variant::PROJECTION: {4118EditorPropertyProjection *editor = memnew(EditorPropertyProjection);4119editor->setup(_parse_range_hint(p_hint, p_hint_text, default_float_step));4120return editor;41214122} break;41234124// misc types4125case Variant::COLOR: {4126EditorPropertyColor *editor = memnew(EditorPropertyColor);4127editor->setup(p_hint != PROPERTY_HINT_COLOR_NO_ALPHA);4128return editor;4129} break;4130case Variant::STRING_NAME: {4131if (p_hint == PROPERTY_HINT_ENUM || p_hint == PROPERTY_HINT_ENUM_SUGGESTION) {4132EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);4133Vector<String> options = p_hint_text.split(",", false);4134editor->setup(options, Vector<String>(), true, (p_hint == PROPERTY_HINT_ENUM_SUGGESTION));4135return editor;4136} else if (p_hint == PROPERTY_HINT_INPUT_NAME) {4137return get_input_action_editor(p_hint_text, true);4138} else {4139EditorPropertyText *editor = memnew(EditorPropertyText);4140if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) {4141editor->set_placeholder(p_hint_text);4142} else if (p_hint == PROPERTY_HINT_PASSWORD) {4143editor->set_secret(true);4144editor->set_placeholder(p_hint_text);4145}4146editor->set_string_name(true);4147return editor;4148}4149} break;4150case Variant::NODE_PATH: {4151EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath);4152if (p_hint == PROPERTY_HINT_NODE_PATH_VALID_TYPES && !p_hint_text.is_empty()) {4153Vector<String> types = p_hint_text.split(",", false);4154Vector<StringName> sn = Variant(types); //convert via variant4155editor->setup(sn, (p_usage & PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT));4156}4157return editor;41584159} break;4160case Variant::RID: {4161EditorPropertyRID *editor = memnew(EditorPropertyRID);4162return editor;4163} break;4164case Variant::OBJECT: {4165if (p_hint == PROPERTY_HINT_NODE_TYPE) {4166EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath);4167Vector<String> types = p_hint_text.split(",", false);4168Vector<StringName> sn = Variant(types); //convert via variant4169editor->setup(sn, false, true);4170return editor;4171} else {4172EditorPropertyResource *editor = memnew(EditorPropertyResource);4173editor->setup(p_object, p_path, p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource");41744175if (p_hint == PROPERTY_HINT_RESOURCE_TYPE) {4176const PackedStringArray open_in_new_inspector = EDITOR_GET("interface/inspector/resources_to_open_in_new_inspector");41774178for (const String &type : open_in_new_inspector) {4179for (int j = 0; j < p_hint_text.get_slice_count(","); j++) {4180const String inherits = p_hint_text.get_slicec(',', j);4181if (ClassDB::is_parent_class(inherits, type)) {4182editor->set_use_sub_inspector(false);4183}4184}4185}4186}41874188return editor;4189}41904191} break;4192case Variant::CALLABLE: {4193EditorPropertyCallable *editor = memnew(EditorPropertyCallable);4194return editor;4195} break;4196case Variant::SIGNAL: {4197EditorPropertySignal *editor = memnew(EditorPropertySignal);4198return editor;4199} break;4200case Variant::DICTIONARY: {4201if (p_hint == PROPERTY_HINT_LOCALIZABLE_STRING) {4202EditorPropertyLocalizableString *editor = memnew(EditorPropertyLocalizableString);4203return editor;4204} else {4205EditorPropertyDictionary *editor = memnew(EditorPropertyDictionary);4206editor->setup(p_hint, p_hint_text);4207return editor;4208}4209} break;4210case Variant::ARRAY: {4211EditorPropertyArray *editor = memnew(EditorPropertyArray);4212editor->setup(Variant::ARRAY, p_hint_text);4213return editor;4214} break;4215case Variant::PACKED_BYTE_ARRAY: {4216EditorPropertyArray *editor = memnew(EditorPropertyArray);4217editor->setup(Variant::PACKED_BYTE_ARRAY, p_hint_text);4218return editor;4219} break;4220case Variant::PACKED_INT32_ARRAY: {4221EditorPropertyArray *editor = memnew(EditorPropertyArray);4222editor->setup(Variant::PACKED_INT32_ARRAY, p_hint_text);4223return editor;4224} break;4225case Variant::PACKED_INT64_ARRAY: {4226EditorPropertyArray *editor = memnew(EditorPropertyArray);4227editor->setup(Variant::PACKED_INT64_ARRAY, p_hint_text);4228return editor;4229} break;4230case Variant::PACKED_FLOAT32_ARRAY: {4231EditorPropertyArray *editor = memnew(EditorPropertyArray);4232editor->setup(Variant::PACKED_FLOAT32_ARRAY, p_hint_text);4233return editor;4234} break;4235case Variant::PACKED_FLOAT64_ARRAY: {4236EditorPropertyArray *editor = memnew(EditorPropertyArray);4237editor->setup(Variant::PACKED_FLOAT64_ARRAY, p_hint_text);4238return editor;4239} break;4240case Variant::PACKED_STRING_ARRAY: {4241EditorPropertyArray *editor = memnew(EditorPropertyArray);4242editor->setup(Variant::PACKED_STRING_ARRAY, p_hint_text);4243return editor;4244} break;4245case Variant::PACKED_VECTOR2_ARRAY: {4246EditorPropertyArray *editor = memnew(EditorPropertyArray);4247editor->setup(Variant::PACKED_VECTOR2_ARRAY, p_hint_text);4248return editor;4249} break;4250case Variant::PACKED_VECTOR3_ARRAY: {4251EditorPropertyArray *editor = memnew(EditorPropertyArray);4252editor->setup(Variant::PACKED_VECTOR3_ARRAY, p_hint_text);4253return editor;4254} break;4255case Variant::PACKED_COLOR_ARRAY: {4256EditorPropertyArray *editor = memnew(EditorPropertyArray);4257editor->setup(Variant::PACKED_COLOR_ARRAY, p_hint_text);4258return editor;4259} break;4260case Variant::PACKED_VECTOR4_ARRAY: {4261EditorPropertyArray *editor = memnew(EditorPropertyArray);4262editor->setup(Variant::PACKED_VECTOR4_ARRAY, p_hint_text);4263return editor;4264} break;4265default: {4266}4267}42684269return nullptr;4270}427142724273