Path: blob/master/editor/inspector/editor_properties.cpp
9903 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 "editor/docks/inspector_dock.h"35#include "editor/docks/scene_tree_dock.h"36#include "editor/editor_node.h"37#include "editor/editor_string_names.h"38#include "editor/gui/create_dialog.h"39#include "editor/gui/editor_file_dialog.h"40#include "editor/gui/editor_spin_slider.h"41#include "editor/gui/editor_variant_type_selectors.h"42#include "editor/inspector/editor_properties_array_dict.h"43#include "editor/inspector/editor_properties_vector.h"44#include "editor/inspector/editor_resource_picker.h"45#include "editor/inspector/property_selector.h"46#include "editor/scene/scene_tree_editor.h"47#include "editor/script/script_editor_plugin.h"48#include "editor/settings/editor_settings.h"49#include "editor/settings/project_settings_editor.h"50#include "editor/themes/editor_scale.h"51#include "scene/2d/gpu_particles_2d.h"52#include "scene/3d/fog_volume.h"53#include "scene/3d/gpu_particles_3d.h"54#include "scene/gui/color_picker.h"55#include "scene/gui/grid_container.h"56#include "scene/main/window.h"57#include "scene/resources/font.h"58#include "scene/resources/mesh.h"59#include "scene/resources/visual_shader_nodes.h"6061///////////////////// NIL /////////////////////////6263void EditorPropertyNil::update_property() {64}6566EditorPropertyNil::EditorPropertyNil() {67Label *prop_label = memnew(Label);68prop_label->set_text("<null>");69add_child(prop_label);70}7172//////////////////// VARIANT ///////////////////////7374void EditorPropertyVariant::_change_type(int p_to_type) {75new_type = Variant::Type(p_to_type);7677Variant zero;78Callable::CallError ce;79Variant::construct(new_type, zero, nullptr, 0, ce);80emit_changed(get_edited_property(), zero);81}8283void EditorPropertyVariant::_popup_edit_menu() {84if (change_type == nullptr) {85change_type = memnew(EditorVariantTypePopupMenu(false));86change_type->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyVariant::_change_type));87content->add_child(change_type);88}8990Rect2 rect = edit_button->get_screen_rect();91change_type->set_position(rect.get_end() - Vector2(change_type->get_contents_minimum_size().x, 0));92change_type->popup();93}9495void EditorPropertyVariant::_set_read_only(bool p_read_only) {96edit_button->set_disabled(p_read_only);97if (sub_property) {98sub_property->set_read_only(p_read_only);99}100}101102void EditorPropertyVariant::_notification(int p_what) {103if (p_what == NOTIFICATION_THEME_CHANGED) {104edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));105}106}107108void EditorPropertyVariant::update_property() {109const Variant &value = get_edited_property_value();110if (new_type == Variant::VARIANT_MAX) {111new_type = value.get_type();112}113114if (new_type != current_type) {115current_type = new_type;116117if (sub_property) {118memdelete(sub_property);119sub_property = nullptr;120}121122if (current_type == Variant::OBJECT) {123sub_property = EditorInspector::instantiate_property_editor(nullptr, current_type, "", PROPERTY_HINT_RESOURCE_TYPE, "Resource", PROPERTY_USAGE_NONE);124} else {125sub_property = EditorInspector::instantiate_property_editor(nullptr, current_type, "", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE);126}127ERR_FAIL_NULL(sub_property);128129sub_property->set_object_and_property(get_edited_object(), get_edited_property());130sub_property->set_name_split_ratio(0);131sub_property->set_selectable(false);132sub_property->set_use_folding(is_using_folding());133sub_property->set_read_only(is_read_only());134sub_property->set_h_size_flags(SIZE_EXPAND_FILL);135sub_property->connect(SNAME("property_changed"), callable_mp((EditorProperty *)this, &EditorProperty::emit_changed));136content->add_child(sub_property);137content->move_child(sub_property, 0);138sub_property->update_property();139} else if (sub_property) {140sub_property->update_property();141}142new_type = Variant::VARIANT_MAX;143}144145EditorPropertyVariant::EditorPropertyVariant() {146content = memnew(HBoxContainer);147add_child(content);148149edit_button = memnew(Button);150edit_button->set_flat(true);151edit_button->set_accessibility_name(TTRC("Edit"));152edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyVariant::_popup_edit_menu));153content->add_child(edit_button);154}155156///////////////////// TEXT /////////////////////////157158void EditorPropertyText::_set_read_only(bool p_read_only) {159text->set_editable(!p_read_only);160}161162void EditorPropertyText::_text_submitted(const String &p_string) {163if (updating) {164return;165}166167if (text->has_focus()) {168_text_changed(p_string);169}170}171172void EditorPropertyText::_text_changed(const String &p_string) {173if (updating) {174return;175}176177// Set tooltip so that the full text is displayed in a tooltip if hovered.178// This is useful when using a narrow inspector, as the text can be trimmed otherwise.179text->set_tooltip_text(get_tooltip_string(text->get_text()));180181if (string_name) {182emit_changed(get_edited_property(), StringName(p_string));183} else {184emit_changed(get_edited_property(), p_string);185}186}187188void EditorPropertyText::update_property() {189String s = get_edited_property_value();190updating = true;191if (text->get_text() != s) {192int caret = text->get_caret_column();193text->set_text(s);194text->set_tooltip_text(get_tooltip_string(s));195text->set_caret_column(caret);196}197text->set_editable(!is_read_only());198updating = false;199}200201void EditorPropertyText::set_string_name(bool p_enabled) {202string_name = p_enabled;203if (p_enabled) {204Label *prefix = memnew(Label("&"));205prefix->set_tooltip_text("StringName");206prefix->set_mouse_filter(MOUSE_FILTER_STOP);207text->get_parent()->add_child(prefix);208text->get_parent()->move_child(prefix, 0);209}210}211212void EditorPropertyText::set_secret(bool p_enabled) {213text->set_secret(p_enabled);214}215216void EditorPropertyText::set_placeholder(const String &p_string) {217text->set_placeholder(p_string);218}219220EditorPropertyText::EditorPropertyText() {221HBoxContainer *hb = memnew(HBoxContainer);222add_child(hb);223224text = memnew(LineEdit);225text->set_h_size_flags(SIZE_EXPAND_FILL);226text->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED); // Prevents translating placeholder.227hb->add_child(text);228add_focusable(text);229text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyText::_text_changed));230text->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyText::_text_submitted));231}232233///////////////////// MULTILINE TEXT /////////////////////////234235void EditorPropertyMultilineText::_set_read_only(bool p_read_only) {236text->set_editable(!p_read_only);237open_big_text->set_disabled(p_read_only);238}239240void EditorPropertyMultilineText::_big_text_changed() {241text->set_text(big_text->get_text());242// Set tooltip so that the full text is displayed in a tooltip if hovered.243// This is useful when using a narrow inspector, as the text can be trimmed otherwise.244text->set_tooltip_text(get_tooltip_string(big_text->get_text()));245emit_changed(get_edited_property(), big_text->get_text(), "", true);246}247248void EditorPropertyMultilineText::_text_changed() {249text->set_tooltip_text(get_tooltip_string(text->get_text()));250emit_changed(get_edited_property(), text->get_text(), "", true);251}252253void EditorPropertyMultilineText::_open_big_text() {254if (!big_text_dialog) {255big_text = memnew(TextEdit);256if (expression) {257big_text->set_syntax_highlighter(text->get_syntax_highlighter());258big_text->add_theme_font_override(SceneStringName(font), get_theme_font(SNAME("expression"), EditorStringName(EditorFonts)));259big_text->add_theme_font_size_override(SceneStringName(font_size), get_theme_font_size(SNAME("expression_size"), EditorStringName(EditorFonts)));260}261big_text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyMultilineText::_big_text_changed));262big_text->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);263big_text_dialog = memnew(AcceptDialog);264big_text_dialog->add_child(big_text);265big_text_dialog->set_title(TTR("Edit Text:"));266add_child(big_text_dialog);267}268269big_text_dialog->popup_centered_clamped(Size2(1000, 900) * EDSCALE, 0.8);270big_text->set_text(text->get_text());271big_text->grab_focus();272}273274void EditorPropertyMultilineText::update_property() {275String t = get_edited_property_value();276if (text->get_text() != t) {277text->set_text(t);278text->set_tooltip_text(get_tooltip_string(t));279if (big_text && big_text->is_visible_in_tree()) {280big_text->set_text(t);281}282}283}284285void EditorPropertyMultilineText::_notification(int p_what) {286switch (p_what) {287case NOTIFICATION_THEME_CHANGED: {288Ref<Texture2D> df = get_editor_theme_icon(SNAME("DistractionFree"));289open_big_text->set_button_icon(df);290291Ref<Font> font;292int font_size;293if (expression) {294font = get_theme_font(SNAME("expression"), EditorStringName(EditorFonts));295font_size = get_theme_font_size(SNAME("expression_size"), EditorStringName(EditorFonts));296297text->add_theme_font_override(SceneStringName(font), font);298text->add_theme_font_size_override(SceneStringName(font_size), font_size);299if (big_text) {300big_text->add_theme_font_override(SceneStringName(font), font);301big_text->add_theme_font_size_override(SceneStringName(font_size), font_size);302}303} else {304font = get_theme_font(SceneStringName(font), SNAME("TextEdit"));305font_size = get_theme_font_size(SceneStringName(font_size), SNAME("TextEdit"));306}307text->set_custom_minimum_size(Vector2(0, font->get_height(font_size) * 6));308} break;309}310}311312EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) {313HBoxContainer *hb = memnew(HBoxContainer);314hb->add_theme_constant_override("separation", 0);315add_child(hb);316set_bottom_editor(hb);317text = memnew(TextEdit);318text->connect(SceneStringName(text_changed), callable_mp(this, &EditorPropertyMultilineText::_text_changed));319text->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);320add_focusable(text);321hb->add_child(text);322text->set_h_size_flags(SIZE_EXPAND_FILL);323open_big_text = memnew(Button);324open_big_text->set_accessibility_name(TTRC("Open Text Edit Dialog"));325open_big_text->set_flat(true);326open_big_text->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyMultilineText::_open_big_text));327hb->add_child(open_big_text);328big_text_dialog = nullptr;329big_text = nullptr;330if (p_expression) {331expression = true;332Ref<EditorStandardSyntaxHighlighter> highlighter;333highlighter.instantiate();334text->set_syntax_highlighter(highlighter);335}336}337338///////////////////// TEXT ENUM /////////////////////////339340void EditorPropertyTextEnum::_set_read_only(bool p_read_only) {341option_button->set_disabled(p_read_only);342edit_button->set_disabled(p_read_only);343}344345void EditorPropertyTextEnum::_emit_changed_value(const String &p_string) {346if (string_name) {347emit_changed(get_edited_property(), StringName(p_string));348} else {349emit_changed(get_edited_property(), p_string);350}351}352353void EditorPropertyTextEnum::_option_selected(int p_which) {354_emit_changed_value(option_button->get_item_text(p_which));355}356357void EditorPropertyTextEnum::_edit_custom_value() {358default_layout->hide();359edit_custom_layout->show();360custom_value_edit->grab_focus();361}362363void EditorPropertyTextEnum::_custom_value_submitted(const String &p_value) {364edit_custom_layout->hide();365default_layout->show();366367_emit_changed_value(p_value.strip_edges());368}369370void EditorPropertyTextEnum::_custom_value_accepted() {371String new_value = custom_value_edit->get_text().strip_edges();372_custom_value_submitted(new_value);373}374375void EditorPropertyTextEnum::_custom_value_canceled() {376custom_value_edit->set_text(get_edited_property_value());377378edit_custom_layout->hide();379default_layout->show();380}381382void EditorPropertyTextEnum::update_property() {383String current_value = get_edited_property_value();384int default_option = options.find(current_value);385386// The list can change in the loose mode.387if (loose_mode) {388custom_value_edit->set_text(current_value);389option_button->clear();390391// Manually entered value.392if (default_option < 0 && !current_value.is_empty()) {393option_button->add_item(current_value, options.size() + 1001);394option_button->select(0);395396option_button->add_separator();397}398399// Add an explicit empty value for clearing the property.400option_button->add_item("", options.size() + 1000);401402for (int i = 0; i < options.size(); i++) {403option_button->add_item(options[i], i);404if (options[i] == current_value) {405option_button->select(option_button->get_item_count() - 1);406}407}408} else {409option_button->select(default_option);410if (default_option < 0) {411option_button->set_text(current_value);412}413}414}415416void EditorPropertyTextEnum::setup(const Vector<String> &p_options, bool p_string_name, bool p_loose_mode) {417string_name = p_string_name;418loose_mode = p_loose_mode;419420options.clear();421422if (loose_mode) {423// Add an explicit empty value for clearing the property in the loose mode.424option_button->add_item("", options.size() + 1000);425}426427for (int i = 0; i < p_options.size(); i++) {428options.append(p_options[i]);429option_button->add_item(p_options[i], i);430}431432if (loose_mode) {433edit_button->show();434}435}436437void EditorPropertyTextEnum::_notification(int p_what) {438switch (p_what) {439case NOTIFICATION_THEME_CHANGED: {440edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));441accept_button->set_button_icon(get_editor_theme_icon(SNAME("ImportCheck")));442cancel_button->set_button_icon(get_editor_theme_icon(SNAME("ImportFail")));443} break;444}445}446447EditorPropertyTextEnum::EditorPropertyTextEnum() {448HBoxContainer *hb = memnew(HBoxContainer);449add_child(hb);450451default_layout = memnew(HBoxContainer);452default_layout->set_h_size_flags(SIZE_EXPAND_FILL);453hb->add_child(default_layout);454455edit_custom_layout = memnew(HBoxContainer);456edit_custom_layout->set_h_size_flags(SIZE_EXPAND_FILL);457edit_custom_layout->hide();458hb->add_child(edit_custom_layout);459460option_button = memnew(OptionButton);461option_button->set_accessibility_name(TTRC("Enum Options"));462option_button->set_h_size_flags(SIZE_EXPAND_FILL);463option_button->set_clip_text(true);464option_button->set_flat(true);465option_button->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);466default_layout->add_child(option_button);467option_button->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyTextEnum::_option_selected));468469edit_button = memnew(Button);470edit_button->set_accessibility_name(TTRC("Edit"));471edit_button->set_flat(true);472edit_button->hide();473default_layout->add_child(edit_button);474edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_edit_custom_value));475476custom_value_edit = memnew(LineEdit);477custom_value_edit->set_accessibility_name(TTRC("Custom Value"));478custom_value_edit->set_h_size_flags(SIZE_EXPAND_FILL);479edit_custom_layout->add_child(custom_value_edit);480custom_value_edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyTextEnum::_custom_value_submitted));481482accept_button = memnew(Button);483accept_button->set_accessibility_name(TTRC("Accept Custom Value Edit"));484accept_button->set_flat(true);485edit_custom_layout->add_child(accept_button);486accept_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_custom_value_accepted));487488cancel_button = memnew(Button);489cancel_button->set_accessibility_name(TTRC("Cancel Custom Value Edit"));490cancel_button->set_flat(true);491edit_custom_layout->add_child(cancel_button);492cancel_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyTextEnum::_custom_value_canceled));493494add_focusable(option_button);495add_focusable(edit_button);496add_focusable(custom_value_edit);497add_focusable(accept_button);498add_focusable(cancel_button);499}500501//////////////////// LOCALE ////////////////////////502503void EditorPropertyLocale::_locale_selected(const String &p_locale) {504emit_changed(get_edited_property(), p_locale);505update_property();506}507508void EditorPropertyLocale::_locale_pressed() {509if (!dialog) {510dialog = memnew(EditorLocaleDialog);511dialog->connect("locale_selected", callable_mp(this, &EditorPropertyLocale::_locale_selected));512add_child(dialog);513}514515String locale_code = get_edited_property_value();516dialog->set_locale(locale_code);517dialog->popup_locale_dialog();518}519520void EditorPropertyLocale::update_property() {521String locale_code = get_edited_property_value();522locale->set_text(locale_code);523locale->set_tooltip_text(locale_code);524}525526void EditorPropertyLocale::setup(const String &p_hint_text) {527}528529void EditorPropertyLocale::_notification(int p_what) {530switch (p_what) {531case NOTIFICATION_THEME_CHANGED: {532locale_edit->set_button_icon(get_editor_theme_icon(SNAME("Translation")));533} break;534}535}536537void EditorPropertyLocale::_locale_focus_exited() {538_locale_selected(locale->get_text());539}540541EditorPropertyLocale::EditorPropertyLocale() {542HBoxContainer *locale_hb = memnew(HBoxContainer);543add_child(locale_hb);544locale = memnew(LineEdit);545locale->set_accessibility_name(TTRC("Locale"));546locale_hb->add_child(locale);547locale->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyLocale::_locale_selected));548locale->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyLocale::_locale_focus_exited));549locale->set_h_size_flags(SIZE_EXPAND_FILL);550551locale_edit = memnew(Button);552locale_edit->set_accessibility_name(TTRC("Edit"));553locale_edit->set_clip_text(true);554locale_hb->add_child(locale_edit);555add_focusable(locale);556dialog = nullptr;557locale_edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLocale::_locale_pressed));558}559560///////////////////// PATH /////////////////////////561562void EditorPropertyPath::_set_read_only(bool p_read_only) {563path->set_editable(!p_read_only);564path_edit->set_disabled(p_read_only);565}566567void EditorPropertyPath::_path_selected(const String &p_path) {568String full_path = p_path;569570if (enable_uid) {571const ResourceUID::ID id = ResourceLoader::get_resource_uid(full_path);572if (id != ResourceUID::INVALID_ID) {573full_path = ResourceUID::get_singleton()->id_to_text(id);574}575}576577emit_changed(get_edited_property(), full_path);578update_property();579}580581String EditorPropertyPath::_get_path_text(bool p_allow_uid) {582String full_path = get_edited_property_value();583if (!p_allow_uid && full_path.begins_with("uid://")) {584full_path = ResourceUID::uid_to_path(full_path);585}586587return full_path;588}589590void EditorPropertyPath::_path_pressed() {591if (!dialog) {592dialog = memnew(EditorFileDialog);593dialog->connect("file_selected", callable_mp(this, &EditorPropertyPath::_path_selected));594dialog->connect("dir_selected", callable_mp(this, &EditorPropertyPath::_path_selected));595add_child(dialog);596}597598String full_path = _get_path_text();599600dialog->clear_filters();601602if (global) {603dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);604} else {605dialog->set_access(EditorFileDialog::ACCESS_RESOURCES);606}607608if (folder) {609dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_DIR);610dialog->set_current_dir(full_path);611} else {612dialog->set_file_mode(save_mode ? EditorFileDialog::FILE_MODE_SAVE_FILE : EditorFileDialog::FILE_MODE_OPEN_FILE);613for (int i = 0; i < extensions.size(); i++) {614String e = extensions[i].strip_edges();615if (!e.is_empty()) {616dialog->add_filter(extensions[i].strip_edges());617}618}619dialog->set_current_path(full_path);620}621622dialog->popup_file_dialog();623}624625void EditorPropertyPath::update_property() {626String full_path = _get_path_text(display_uid);627path->set_text(full_path);628path->set_tooltip_text(full_path);629630toggle_uid->set_visible(get_edited_property_value().operator String().begins_with("uid://"));631}632633void EditorPropertyPath::setup(const Vector<String> &p_extensions, bool p_folder, bool p_global, bool p_enable_uid) {634extensions = p_extensions;635folder = p_folder;636global = p_global;637enable_uid = p_enable_uid;638}639640void EditorPropertyPath::set_save_mode() {641save_mode = true;642}643644void EditorPropertyPath::_notification(int p_what) {645switch (p_what) {646case NOTIFICATION_THEME_CHANGED: {647if (folder) {648path_edit->set_button_icon(get_editor_theme_icon(SNAME("FolderBrowse")));649} else {650path_edit->set_button_icon(get_editor_theme_icon(SNAME("FileBrowse")));651}652_update_uid_icon();653} break;654}655}656657void EditorPropertyPath::_path_focus_exited() {658_path_selected(path->get_text());659}660661void EditorPropertyPath::_toggle_uid_display() {662display_uid = !display_uid;663_update_uid_icon();664update_property();665}666667void EditorPropertyPath::_update_uid_icon() {668toggle_uid->set_button_icon(get_editor_theme_icon(display_uid ? SNAME("UID") : SNAME("NodePath")));669}670671void EditorPropertyPath::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {672const Dictionary drag_data = p_data;673if (!drag_data.has("type")) {674return;675}676if (String(drag_data["type"]) != "files") {677return;678}679const Vector<String> filesPaths = drag_data["files"];680if (filesPaths.is_empty()) {681return;682}683684_path_selected(filesPaths[0]);685}686687bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {688const Dictionary drag_data = p_data;689if (!drag_data.has("type")) {690return false;691}692if (String(drag_data["type"]) != "files") {693return false;694}695const Vector<String> filesPaths = drag_data["files"];696if (filesPaths.is_empty()) {697return false;698}699700for (const String &extension : extensions) {701if (filesPaths[0].ends_with(extension.substr(1))) {702return true;703}704}705706return false;707}708709EditorPropertyPath::EditorPropertyPath() {710HBoxContainer *path_hb = memnew(HBoxContainer);711add_child(path_hb);712path = memnew(LineEdit);713path->set_accessibility_name(TTRC("Path"));714SET_DRAG_FORWARDING_CDU(path, EditorPropertyPath);715path->set_structured_text_bidi_override(TextServer::STRUCTURED_TEXT_FILE);716path_hb->add_child(path);717path->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyPath::_path_selected));718path->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyPath::_path_focus_exited));719path->set_h_size_flags(SIZE_EXPAND_FILL);720721toggle_uid = memnew(Button);722toggle_uid->set_accessibility_name(TTRC("Toggle Display UID"));723toggle_uid->set_tooltip_text(TTRC("Toggles displaying between path and UID.\nThe UID is the actual value of this property."));724toggle_uid->set_pressed(false);725path_hb->add_child(toggle_uid);726add_focusable(toggle_uid);727toggle_uid->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyPath::_toggle_uid_display));728729path_edit = memnew(Button);730path_edit->set_accessibility_name(TTRC("Edit"));731path_hb->add_child(path_edit);732add_focusable(path);733path_edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyPath::_path_pressed));734}735736///////////////////// CLASS NAME /////////////////////////737738void EditorPropertyClassName::_set_read_only(bool p_read_only) {739property->set_disabled(p_read_only);740}741742void EditorPropertyClassName::setup(const String &p_base_type, const String &p_selected_type) {743base_type = p_base_type;744dialog->set_base_type(base_type);745selected_type = p_selected_type;746property->set_text(selected_type);747}748749void EditorPropertyClassName::update_property() {750String s = get_edited_property_value();751property->set_text(s);752selected_type = s;753}754755void EditorPropertyClassName::_property_selected() {756dialog->popup_create(true, true, get_edited_property_value(), get_edited_property());757}758759void EditorPropertyClassName::_dialog_created() {760selected_type = dialog->get_selected_type();761emit_changed(get_edited_property(), selected_type);762update_property();763}764765EditorPropertyClassName::EditorPropertyClassName() {766property = memnew(Button);767property->set_clip_text(true);768add_child(property);769add_focusable(property);770property->set_text(selected_type);771property->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyClassName::_property_selected));772dialog = memnew(CreateDialog);773dialog->set_base_type(base_type);774dialog->connect("create", callable_mp(this, &EditorPropertyClassName::_dialog_created));775add_child(dialog);776}777778///////////////////// CHECK /////////////////////////779780void EditorPropertyCheck::_set_read_only(bool p_read_only) {781checkbox->set_disabled(p_read_only);782}783784void EditorPropertyCheck::_checkbox_pressed() {785emit_changed(get_edited_property(), checkbox->is_pressed());786}787788void EditorPropertyCheck::update_property() {789bool c = get_edited_property_value();790checkbox->set_pressed(c);791checkbox->set_disabled(is_read_only());792}793794EditorPropertyCheck::EditorPropertyCheck() {795checkbox = memnew(CheckBox);796checkbox->set_text(TTR("On"));797add_child(checkbox);798add_focusable(checkbox);799checkbox->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyCheck::_checkbox_pressed));800}801802///////////////////// ENUM /////////////////////////803804void EditorPropertyEnum::_set_read_only(bool p_read_only) {805options->set_disabled(p_read_only);806}807808void EditorPropertyEnum::_option_selected(int p_which) {809int64_t val = options->get_item_metadata(p_which);810emit_changed(get_edited_property(), val);811}812813void EditorPropertyEnum::update_property() {814Variant current = get_edited_property_value();815if (current.get_type() == Variant::NIL) {816options->select(-1);817options->set_text("<null>");818return;819}820821int64_t which = current;822for (int i = 0; i < options->get_item_count(); i++) {823if (which == (int64_t)options->get_item_metadata(i)) {824options->select(i);825return;826}827}828options->select(-1);829options->set_text(itos(which));830}831832void EditorPropertyEnum::setup(const Vector<String> &p_options) {833options->clear();834HashMap<int64_t, Vector<String>> items;835int64_t current_val = 0;836for (const String &option : p_options) {837Vector<String> text_split = option.split(":");838if (text_split.size() != 1) {839current_val = text_split[1].to_int();840}841items[current_val].push_back(text_split[0]);842current_val += 1;843}844845for (const KeyValue<int64_t, Vector<String>> &K : items) {846options->add_item(String(", ").join(K.value));847options->set_item_metadata(-1, K.key);848}849}850851void EditorPropertyEnum::set_option_button_clip(bool p_enable) {852options->set_clip_text(p_enable);853}854855EditorPropertyEnum::EditorPropertyEnum() {856options = memnew(OptionButton);857options->set_clip_text(true);858options->set_flat(true);859options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);860add_child(options);861add_focusable(options);862options->connect(SceneStringName(item_selected), callable_mp(this, &EditorPropertyEnum::_option_selected));863}864865///////////////////// FLAGS /////////////////////////866867void EditorPropertyFlags::_set_read_only(bool p_read_only) {868for (CheckBox *check : flags) {869check->set_disabled(p_read_only);870}871}872873void EditorPropertyFlags::_flag_toggled(int p_index) {874uint32_t value = get_edited_property_value();875if (flags[p_index]->is_pressed()) {876value |= flag_values[p_index];877} else {878value &= ~flag_values[p_index];879}880881emit_changed(get_edited_property(), value);882}883884void EditorPropertyFlags::update_property() {885uint32_t value = get_edited_property_value();886887for (int i = 0; i < flags.size(); i++) {888flags[i]->set_pressed((value & flag_values[i]) == flag_values[i]);889}890}891892void EditorPropertyFlags::setup(const Vector<String> &p_options) {893ERR_FAIL_COND(flags.size());894895bool first = true;896uint32_t current_val;897for (int i = 0; i < p_options.size(); i++) {898// An empty option is not considered a "flag".899String option = p_options[i].strip_edges();900if (option.is_empty()) {901continue;902}903const int flag_index = flags.size(); // Index of the next element (added by the code below).904905// Value for a flag can be explicitly overridden.906Vector<String> text_split = option.split(":");907if (text_split.size() != 1) {908current_val = text_split[1].to_int();909} else {910current_val = 1 << i;911}912flag_values.push_back(current_val);913914// Create a CheckBox for the current flag.915CheckBox *cb = memnew(CheckBox);916cb->set_text(text_split[0]);917cb->set_clip_text(true);918cb->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyFlags::_flag_toggled).bind(flag_index));919add_focusable(cb);920vbox->add_child(cb);921flags.push_back(cb);922923// Can't use `i == 0` because we want to find the first none-empty option.924if (first) {925set_label_reference(cb);926first = false;927}928}929}930931EditorPropertyFlags::EditorPropertyFlags() {932vbox = memnew(VBoxContainer);933vbox->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);934add_child(vbox);935}936937///////////////////// LAYERS /////////////////////////938939void EditorPropertyLayersGrid::_rename_pressed(int p_menu) {940// Show rename popup for active layer.941if (renamed_layer_index == INT32_MAX) {942return;943}944String name = names[renamed_layer_index];945rename_dialog->set_title(vformat(TTR("Renaming layer %d:"), renamed_layer_index + 1));946rename_dialog_text->set_text(name);947rename_dialog_text->select(0, name.length());948rename_dialog->popup_centered(Size2(300, 80) * EDSCALE);949rename_dialog_text->grab_focus();950}951952void EditorPropertyLayersGrid::_rename_operation_confirm() {953String new_name = rename_dialog_text->get_text().strip_edges();954if (new_name.length() == 0) {955EditorNode::get_singleton()->show_warning(TTR("No name provided."));956return;957} else if (new_name.contains_char('/') || new_name.contains_char('\\') || new_name.contains_char(':')) {958EditorNode::get_singleton()->show_warning(TTR("Name contains invalid characters."));959return;960}961names.set(renamed_layer_index, new_name);962tooltips.set(renamed_layer_index, new_name + "\n" + vformat(TTR("Bit %d, value %d"), renamed_layer_index, 1 << renamed_layer_index));963emit_signal(SNAME("rename_confirmed"), renamed_layer_index, new_name);964}965966EditorPropertyLayersGrid::EditorPropertyLayersGrid() {967rename_dialog = memnew(ConfirmationDialog);968VBoxContainer *rename_dialog_vb = memnew(VBoxContainer);969rename_dialog->add_child(rename_dialog_vb);970rename_dialog_text = memnew(LineEdit);971rename_dialog_vb->add_margin_child(TTR("Name:"), rename_dialog_text);972rename_dialog->set_ok_button_text(TTR("Rename"));973add_child(rename_dialog);974rename_dialog->register_text_enter(rename_dialog_text);975rename_dialog->connect(SceneStringName(confirmed), callable_mp(this, &EditorPropertyLayersGrid::_rename_operation_confirm));976layer_rename = memnew(PopupMenu);977layer_rename->add_item(TTR("Rename layer"), 0);978add_child(layer_rename);979layer_rename->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyLayersGrid::_rename_pressed));980}981982Size2 EditorPropertyLayersGrid::get_grid_size() const {983Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));984int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));985return Vector2(0, font->get_height(font_size) * 3);986}987988void EditorPropertyLayersGrid::set_read_only(bool p_read_only) {989read_only = p_read_only;990}991992Size2 EditorPropertyLayersGrid::get_minimum_size() const {993Size2 min_size = get_grid_size();994995// Add extra rows when expanded.996if (expanded) {997const int bsize = (min_size.height * 80 / 100) / 2;998for (int i = 0; i < expansion_rows; ++i) {999min_size.y += 2 * (bsize + 1) + 3;1000}1001}10021003return min_size;1004}10051006String EditorPropertyLayersGrid::get_tooltip(const Point2 &p_pos) const {1007for (int i = 0; i < flag_rects.size(); i++) {1008if (i < tooltips.size() && flag_rects[i].has_point(p_pos)) {1009return tooltips[i];1010}1011}1012return String();1013}10141015void EditorPropertyLayersGrid::_update_hovered(const Vector2 &p_position) {1016bool expand_was_hovered = expand_hovered;1017expand_hovered = expand_rect.has_point(p_position);1018if (expand_hovered != expand_was_hovered) {1019queue_redraw();1020}10211022if (!expand_hovered) {1023for (int i = 0; i < flag_rects.size(); i++) {1024if (flag_rects[i].has_point(p_position)) {1025// Used to highlight the hovered flag in the layers grid.1026hovered_index = i;1027queue_redraw();1028return;1029}1030}1031}10321033// Remove highlight when no square is hovered.1034if (hovered_index != INT32_MAX) {1035hovered_index = INT32_MAX;1036queue_redraw();1037}1038}10391040void EditorPropertyLayersGrid::_on_hover_exit() {1041if (expand_hovered) {1042expand_hovered = false;1043queue_redraw();1044}1045if (hovered_index != INT32_MAX) {1046hovered_index = INT32_MAX;1047queue_redraw();1048}1049}10501051void EditorPropertyLayersGrid::_update_flag(bool p_replace) {1052if (hovered_index != INT32_MAX) {1053// Toggle the flag.1054// We base our choice on the hovered flag, so that it always matches the hovered flag.1055if (p_replace) {1056// Replace all flags with the hovered flag ("solo mode"),1057// instead of toggling the hovered flags while preserving other flags' state.1058if (value == uint32_t(1 << hovered_index)) {1059// If the flag is already enabled, enable all other items and disable the current flag.1060// This allows for quicker toggling.1061value = INT32_MAX - (1 << hovered_index);1062} else {1063value = 1 << hovered_index;1064}1065} else {1066if (value & (1 << hovered_index)) {1067value &= ~(1 << hovered_index);1068} else {1069value |= (1 << hovered_index);1070}1071}10721073emit_signal(SNAME("flag_changed"), value);1074queue_redraw();1075} else if (expand_hovered) {1076expanded = !expanded;1077update_minimum_size();1078queue_redraw();1079}1080}10811082void EditorPropertyLayersGrid::gui_input(const Ref<InputEvent> &p_ev) {1083if (read_only) {1084return;1085}1086const Ref<InputEventMouseMotion> mm = p_ev;1087if (mm.is_valid()) {1088_update_hovered(mm->get_position());1089return;1090}10911092const Ref<InputEventMouseButton> mb = p_ev;1093if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {1094_update_hovered(mb->get_position());1095_update_flag(mb->is_command_or_control_pressed());1096}1097if (mb.is_valid() && mb->get_button_index() == MouseButton::RIGHT && mb->is_pressed()) {1098if (hovered_index != INT32_MAX) {1099renamed_layer_index = hovered_index;1100layer_rename->set_position(get_screen_position() + mb->get_position());1101layer_rename->reset_size();1102layer_rename->popup();1103}1104}1105}11061107void EditorPropertyLayersGrid::_notification(int p_what) {1108switch (p_what) {1109case NOTIFICATION_ACCESSIBILITY_UPDATE: {1110RID ae = get_accessibility_element();1111ERR_FAIL_COND(ae.is_null());11121113//TODO1114DisplayServer::get_singleton()->accessibility_update_set_role(ae, DisplayServer::AccessibilityRole::ROLE_STATIC_TEXT);1115DisplayServer::get_singleton()->accessibility_update_set_value(ae, TTR(vformat("The %s is not accessible at this time.", "Layers grid property editor")));1116} break;11171118case NOTIFICATION_DRAW: {1119Size2 grid_size = get_grid_size();1120grid_size.x = get_size().x;11211122flag_rects.clear();11231124int prev_expansion_rows = expansion_rows;1125expansion_rows = 0;11261127const int bsize = (grid_size.height * 80 / 100) / 2;1128const int h = bsize * 2 + 1;11291130Color color = get_theme_color(read_only ? SNAME("highlight_disabled_color") : SNAME("highlight_color"), EditorStringName(Editor));11311132Color text_color = get_theme_color(read_only ? SNAME("font_disabled_color") : SceneStringName(font_color), EditorStringName(Editor));1133text_color.a *= 0.5;11341135Color text_color_on = get_theme_color(read_only ? SNAME("font_disabled_color") : SNAME("font_hover_color"), EditorStringName(Editor));1136text_color_on.a *= 0.7;11371138const int vofs = (grid_size.height - h) / 2;11391140uint32_t layer_index = 0;11411142Point2 arrow_pos;11431144Point2 block_ofs(4, vofs);11451146while (true) {1147Point2 ofs = block_ofs;11481149for (int i = 0; i < 2; i++) {1150for (int j = 0; j < layer_group_size; j++) {1151const bool on = value & (1 << layer_index);1152Rect2 rect2 = Rect2(ofs, Size2(bsize, bsize));11531154color.a = on ? 0.6 : 0.2;1155if (layer_index == hovered_index) {1156// Add visual feedback when hovering a flag.1157color.a += 0.15;1158}11591160draw_rect(rect2, color);1161flag_rects.push_back(rect2);11621163Ref<Font> font = get_theme_font(SceneStringName(font), SNAME("Label"));1164int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));1165Vector2 offset;1166offset.y = rect2.size.y * 0.75;11671168draw_string(font, rect2.position + offset, itos(layer_index + 1), HORIZONTAL_ALIGNMENT_CENTER, rect2.size.x, font_size, on ? text_color_on : text_color);11691170ofs.x += bsize + 1;11711172++layer_index;1173}11741175ofs.x = block_ofs.x;1176ofs.y += bsize + 1;1177}11781179if (layer_index >= layer_count) {1180if (!flag_rects.is_empty() && (expansion_rows == 0)) {1181const Rect2 &last_rect = flag_rects[flag_rects.size() - 1];1182arrow_pos = last_rect.get_end();1183}1184break;1185}11861187int block_size_x = layer_group_size * (bsize + 1);1188block_ofs.x += block_size_x + 3;11891190if (block_ofs.x + block_size_x + 12 > grid_size.width) {1191// Keep last valid cell position for the expansion icon.1192if (!flag_rects.is_empty() && (expansion_rows == 0)) {1193const Rect2 &last_rect = flag_rects[flag_rects.size() - 1];1194arrow_pos = last_rect.get_end();1195}1196++expansion_rows;11971198if (expanded) {1199// Expand grid to next line.1200block_ofs.x = 4;1201block_ofs.y += 2 * (bsize + 1) + 3;1202} else {1203// Skip remaining blocks.1204break;1205}1206}1207}12081209if ((expansion_rows != prev_expansion_rows) && expanded) {1210update_minimum_size();1211}12121213if ((expansion_rows == 0) && (layer_index == layer_count)) {1214// Whole grid was drawn, no need for expansion icon.1215break;1216}12171218Ref<Texture2D> arrow = get_theme_icon(SNAME("arrow"), SNAME("Tree"));1219ERR_FAIL_COND(arrow.is_null());12201221Color arrow_color = get_theme_color(SNAME("highlight_color"), EditorStringName(Editor));1222arrow_color.a = expand_hovered ? 1.0 : 0.6;12231224arrow_pos.x += 2.0;1225arrow_pos.y -= arrow->get_height();12261227Rect2 arrow_draw_rect(arrow_pos, arrow->get_size());1228expand_rect = arrow_draw_rect;1229if (expanded) {1230arrow_draw_rect.size.y *= -1.0; // Flip arrow vertically when expanded.1231}12321233RID ci = get_canvas_item();1234arrow->draw_rect(ci, arrow_draw_rect, false, arrow_color);12351236} break;12371238case NOTIFICATION_MOUSE_EXIT: {1239_on_hover_exit();1240} break;1241}1242}12431244void EditorPropertyLayersGrid::set_flag(uint32_t p_flag) {1245value = p_flag;1246queue_redraw();1247}12481249void EditorPropertyLayersGrid::_bind_methods() {1250ADD_SIGNAL(MethodInfo("flag_changed", PropertyInfo(Variant::INT, "flag")));1251ADD_SIGNAL(MethodInfo("rename_confirmed", PropertyInfo(Variant::INT, "layer_id"), PropertyInfo(Variant::STRING, "new_name")));1252}12531254void EditorPropertyLayers::_notification(int p_what) {1255switch (p_what) {1256case NOTIFICATION_THEME_CHANGED: {1257button->set_texture_normal(get_editor_theme_icon(SNAME("GuiTabMenuHl")));1258button->set_texture_pressed(get_editor_theme_icon(SNAME("GuiTabMenuHl")));1259button->set_texture_disabled(get_editor_theme_icon(SNAME("GuiTabMenu")));1260} break;1261}1262}12631264void EditorPropertyLayers::_set_read_only(bool p_read_only) {1265button->set_disabled(p_read_only);1266grid->set_read_only(p_read_only);1267}12681269void EditorPropertyLayers::_grid_changed(uint32_t p_grid) {1270emit_changed(get_edited_property(), p_grid);1271}12721273void EditorPropertyLayers::update_property() {1274uint32_t value = get_edited_property_value();12751276grid->set_flag(value);1277}12781279void EditorPropertyLayers::setup(LayerType p_layer_type) {1280layer_type = p_layer_type;1281int layer_group_size = 0;1282int layer_count = 0;1283switch (p_layer_type) {1284case LAYER_RENDER_2D: {1285basename = "layer_names/2d_render";1286layer_group_size = 5;1287layer_count = 20;1288} break;12891290case LAYER_PHYSICS_2D: {1291basename = "layer_names/2d_physics";1292layer_group_size = 4;1293layer_count = 32;1294} break;12951296case LAYER_NAVIGATION_2D: {1297basename = "layer_names/2d_navigation";1298layer_group_size = 4;1299layer_count = 32;1300} break;13011302case LAYER_RENDER_3D: {1303basename = "layer_names/3d_render";1304layer_group_size = 5;1305layer_count = 20;1306} break;13071308case LAYER_PHYSICS_3D: {1309basename = "layer_names/3d_physics";1310layer_group_size = 4;1311layer_count = 32;1312} break;13131314case LAYER_NAVIGATION_3D: {1315basename = "layer_names/3d_navigation";1316layer_group_size = 4;1317layer_count = 32;1318} break;13191320case LAYER_AVOIDANCE: {1321basename = "layer_names/avoidance";1322layer_group_size = 4;1323layer_count = 32;1324} break;1325}13261327Vector<String> names;1328Vector<String> tooltips;1329for (int i = 0; i < layer_count; i++) {1330String name;13311332if (ProjectSettings::get_singleton()->has_setting(basename + vformat("/layer_%d", i + 1))) {1333name = GLOBAL_GET(basename + vformat("/layer_%d", i + 1));1334}13351336if (name.is_empty()) {1337name = vformat(TTR("Layer %d"), i + 1);1338}13391340names.push_back(name);1341tooltips.push_back(name + "\n" + vformat(TTR("Bit %d, value %d"), i, 1 << i));1342}13431344grid->names = names;1345grid->tooltips = tooltips;1346grid->layer_group_size = layer_group_size;1347grid->layer_count = layer_count;1348}13491350void EditorPropertyLayers::set_layer_name(int p_index, const String &p_name) {1351const String property_name = basename + vformat("/layer_%d", p_index + 1);1352if (ProjectSettings::get_singleton()->has_setting(property_name)) {1353ProjectSettings::get_singleton()->set(property_name, p_name);1354ProjectSettings::get_singleton()->save();1355}1356}13571358String EditorPropertyLayers::get_layer_name(int p_index) const {1359const String property_name = basename + vformat("/layer_%d", p_index + 1);1360if (ProjectSettings::get_singleton()->has_setting(property_name)) {1361return GLOBAL_GET(property_name);1362}1363return String();1364}13651366void EditorPropertyLayers::_button_pressed() {1367int layer_count = grid->layer_count;1368layers->clear();1369for (int i = 0; i < layer_count; i++) {1370const String name = get_layer_name(i);1371if (name.is_empty()) {1372continue;1373}1374layers->add_check_item(name, i);1375int idx = layers->get_item_index(i);1376layers->set_item_checked(idx, grid->value & (1 << i));1377}13781379if (layers->get_item_count() == 0) {1380layers->add_item(TTR("No Named Layers"));1381layers->set_item_disabled(0, true);1382}1383layers->add_separator();1384layers->add_icon_item(get_editor_theme_icon("Edit"), TTR("Edit Layer Names"), grid->layer_count);13851386Rect2 gp = button->get_screen_rect();1387layers->reset_size();1388Vector2 popup_pos = gp.position - Vector2(layers->get_contents_minimum_size().x, 0);1389layers->set_position(popup_pos);1390layers->popup();1391}13921393void EditorPropertyLayers::_menu_pressed(int p_menu) {1394if (uint32_t(p_menu) == grid->layer_count) {1395ProjectSettingsEditor::get_singleton()->popup_project_settings(true);1396ProjectSettingsEditor::get_singleton()->set_general_page(basename);1397} else {1398if (grid->value & (1 << p_menu)) {1399grid->value &= ~(1 << p_menu);1400} else {1401grid->value |= (1 << p_menu);1402}1403grid->queue_redraw();1404layers->set_item_checked(layers->get_item_index(p_menu), grid->value & (1 << p_menu));1405_grid_changed(grid->value);1406}1407}14081409void EditorPropertyLayers::_refresh_names() {1410setup(layer_type);1411}14121413EditorPropertyLayers::EditorPropertyLayers() {1414HBoxContainer *hb = memnew(HBoxContainer);1415hb->set_clip_contents(true);1416add_child(hb);1417grid = memnew(EditorPropertyLayersGrid);1418grid->connect("flag_changed", callable_mp(this, &EditorPropertyLayers::_grid_changed));1419grid->connect("rename_confirmed", callable_mp(this, &EditorPropertyLayers::set_layer_name));1420grid->set_h_size_flags(SIZE_EXPAND_FILL);1421hb->add_child(grid);14221423button = memnew(TextureButton);1424button->set_accessibility_name(TTRC("Layers"));1425button->set_stretch_mode(TextureButton::STRETCH_KEEP_CENTERED);1426button->set_toggle_mode(true);1427button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyLayers::_button_pressed));1428hb->add_child(button);14291430set_bottom_editor(hb);14311432layers = memnew(PopupMenu);1433add_child(layers);1434layers->set_hide_on_checkable_item_selection(false);1435layers->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyLayers::_menu_pressed));1436layers->connect("popup_hide", callable_mp((BaseButton *)button, &BaseButton::set_pressed).bind(false));1437ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorPropertyLayers::_refresh_names));1438}14391440///////////////////// INT /////////////////////////14411442void EditorPropertyInteger::_set_read_only(bool p_read_only) {1443spin->set_read_only(p_read_only);1444}14451446void EditorPropertyInteger::_value_changed(int64_t val) {1447emit_changed(get_edited_property(), val);1448}14491450void EditorPropertyInteger::update_property() {1451int64_t val = get_edited_property_display_value();1452spin->set_value_no_signal(val);1453#ifdef DEBUG_ENABLED1454// If spin (currently EditorSplinSlider : Range) is changed so that it can use int64_t, then the below warning wouldn't be a problem.1455if (val != (int64_t)(double)(val)) {1456WARN_PRINT("Cannot reliably represent '" + itos(val) + "' in the inspector, value is too large.");1457}1458#endif1459}14601461void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) {1462spin->set_min(p_min);1463spin->set_max(p_max);1464spin->set_step(p_step);1465spin->set_hide_slider(p_hide_slider);1466spin->set_allow_greater(p_allow_greater);1467spin->set_allow_lesser(p_allow_lesser);1468spin->set_suffix(p_suffix);1469}14701471EditorPropertyInteger::EditorPropertyInteger() {1472spin = memnew(EditorSpinSlider);1473spin->set_flat(true);1474spin->set_editing_integer(true);1475add_child(spin);1476add_focusable(spin);1477spin->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyInteger::_value_changed));1478}14791480///////////////////// OBJECT ID /////////////////////////14811482void EditorPropertyObjectID::_set_read_only(bool p_read_only) {1483edit->set_disabled(p_read_only);1484}14851486void EditorPropertyObjectID::_edit_pressed() {1487emit_signal(SNAME("object_id_selected"), get_edited_property(), get_edited_property_value());1488}14891490void EditorPropertyObjectID::update_property() {1491String type = base_type;1492if (type.is_empty()) {1493type = "Object";1494}14951496ObjectID id = get_edited_property_value();1497if (id.is_valid()) {1498edit->set_text(type + " ID: " + uitos(id));1499edit->set_tooltip_text(type + " ID: " + uitos(id));1500edit->set_disabled(false);1501edit->set_button_icon(EditorNode::get_singleton()->get_class_icon(type));1502} else {1503edit->set_text(TTR("<empty>"));1504edit->set_tooltip_text("");1505edit->set_disabled(true);1506edit->set_button_icon(Ref<Texture2D>());1507}1508}15091510void EditorPropertyObjectID::setup(const String &p_base_type) {1511base_type = p_base_type;1512}15131514EditorPropertyObjectID::EditorPropertyObjectID() {1515edit = memnew(Button);1516edit->set_accessibility_name(TTRC("Edit"));1517add_child(edit);1518add_focusable(edit);1519edit->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_ELLIPSIS);1520edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyObjectID::_edit_pressed));1521}15221523///////////////////// SIGNAL /////////////////////////15241525void EditorPropertySignal::_edit_pressed() {1526Signal signal = get_edited_property_value();1527emit_signal(SNAME("object_id_selected"), get_edited_property(), signal.get_object_id());1528}15291530void EditorPropertySignal::update_property() {1531String type = base_type;15321533Signal signal = get_edited_property_value();15341535edit->set_text("Signal: " + signal.get_name());1536edit->set_disabled(false);1537edit->set_button_icon(get_editor_theme_icon(SNAME("Signals")));1538}15391540EditorPropertySignal::EditorPropertySignal() {1541edit = memnew(Button);1542edit->set_accessibility_name(TTRC("Edit"));1543add_child(edit);1544add_focusable(edit);1545edit->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertySignal::_edit_pressed));1546}15471548///////////////////// CALLABLE /////////////////////////15491550void EditorPropertyCallable::update_property() {1551String type = base_type;15521553Callable callable = get_edited_property_value();15541555edit->set_text("Callable");1556edit->set_disabled(true);1557edit->set_button_icon(get_editor_theme_icon(SNAME("Callable")));1558}15591560EditorPropertyCallable::EditorPropertyCallable() {1561edit = memnew(Button);1562edit->set_accessibility_name(TTRC("Edit"));1563add_child(edit);1564add_focusable(edit);1565}15661567///////////////////// FLOAT /////////////////////////15681569void EditorPropertyFloat::_set_read_only(bool p_read_only) {1570spin->set_read_only(p_read_only);1571}15721573void EditorPropertyFloat::_value_changed(double val) {1574if (radians_as_degrees) {1575val = Math::deg_to_rad(val);1576}1577emit_changed(get_edited_property(), val);1578}15791580void EditorPropertyFloat::update_property() {1581double val = get_edited_property_value();1582if (radians_as_degrees) {1583val = Math::rad_to_deg(val);1584}1585spin->set_value_no_signal(val);1586}15871588void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_exp_range, bool p_greater, bool p_lesser, const String &p_suffix, bool p_radians_as_degrees) {1589radians_as_degrees = p_radians_as_degrees;1590spin->set_min(p_min);1591spin->set_max(p_max);1592spin->set_step(p_step);1593spin->set_hide_slider(p_hide_slider);1594spin->set_exp_ratio(p_exp_range);1595spin->set_allow_greater(p_greater);1596spin->set_allow_lesser(p_lesser);1597spin->set_suffix(p_suffix);1598}15991600EditorPropertyFloat::EditorPropertyFloat() {1601spin = memnew(EditorSpinSlider);1602spin->set_flat(true);1603add_child(spin);1604add_focusable(spin);1605spin->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyFloat::_value_changed));1606}16071608///////////////////// EASING /////////////////////////16091610void EditorPropertyEasing::_set_read_only(bool p_read_only) {1611spin->set_read_only(p_read_only);1612}16131614void EditorPropertyEasing::_drag_easing(const Ref<InputEvent> &p_ev) {1615if (is_read_only()) {1616return;1617}1618const Ref<InputEventMouseButton> mb = p_ev;1619if (mb.is_valid()) {1620if (mb->is_double_click() && mb->get_button_index() == MouseButton::LEFT) {1621_setup_spin();1622}16231624if (mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {1625preset->set_position(easing_draw->get_screen_position() + mb->get_position());1626preset->reset_size();1627preset->popup();16281629// Ensure the easing doesn't appear as being dragged1630dragging = false;1631easing_draw->queue_redraw();1632}16331634if (mb->get_button_index() == MouseButton::LEFT) {1635dragging = mb->is_pressed();1636// Update to display the correct dragging color1637easing_draw->queue_redraw();1638}1639}16401641const Ref<InputEventMouseMotion> mm = p_ev;16421643if (dragging && mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {1644float rel = mm->get_relative().x;1645if (rel == 0) {1646return;1647}16481649if (flip) {1650rel = -rel;1651}16521653float val = get_edited_property_value();1654bool sg = val < 0;1655val = Math::abs(val);16561657val = Math::log(val) / Math::log((float)2.0);1658// Logarithmic space.1659val += rel * 0.05;16601661val = Math::pow(2.0f, val);1662if (sg) {1663val = -val;1664}16651666// 0 is a singularity, but both positive and negative values1667// are otherwise allowed. Enforce 0+ as workaround.1668if (Math::is_zero_approx(val)) {1669val = 0.00001;1670}16711672// Limit to a reasonable value to prevent the curve going into infinity,1673// which can cause crashes and other issues.1674val = CLAMP(val, -1'000'000, 1'000'000);16751676emit_changed(get_edited_property(), val);1677easing_draw->queue_redraw();1678}1679}16801681void EditorPropertyEasing::_draw_easing() {1682RID ci = easing_draw->get_canvas_item();16831684Size2 s = easing_draw->get_size();16851686const int point_count = 48;16871688const float exp = get_edited_property_value();16891690const Ref<Font> f = get_theme_font(SceneStringName(font), SNAME("Label"));1691int font_size = get_theme_font_size(SceneStringName(font_size), SNAME("Label"));1692const Color font_color = get_theme_color(is_read_only() ? SNAME("font_uneditable_color") : SceneStringName(font_color), SNAME("LineEdit"));1693Color line_color;1694if (dragging) {1695line_color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));1696} else {1697line_color = get_theme_color(is_read_only() ? SNAME("font_uneditable_color") : SceneStringName(font_color), SNAME("LineEdit")) * Color(1, 1, 1, 0.9);1698}16991700Vector<Point2> points;1701for (int i = 0; i <= point_count; i++) {1702float ifl = i / float(point_count);17031704const float h = 1.0 - Math::ease(ifl, exp);17051706if (flip) {1707ifl = 1.0 - ifl;1708}17091710points.push_back(Point2(ifl * s.width, h * s.height));1711}17121713easing_draw->draw_polyline(points, line_color, 1.0, true);1714// Draw more decimals for small numbers since higher precision is usually required for fine adjustments.1715int decimals;1716if (Math::abs(exp) < 0.1 - CMP_EPSILON) {1717decimals = 4;1718} else if (Math::abs(exp) < 1 - CMP_EPSILON) {1719decimals = 3;1720} else if (Math::abs(exp) < 10 - CMP_EPSILON) {1721decimals = 2;1722} else {1723decimals = 1;1724}1725f->draw_string(ci, Point2(10, 10 + f->get_ascent(font_size)), TS->format_number(rtos(exp).pad_decimals(decimals)), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);1726}17271728void EditorPropertyEasing::update_property() {1729easing_draw->queue_redraw();1730}17311732void EditorPropertyEasing::_set_preset(int p_preset) {1733static const float preset_value[EASING_MAX] = { 0.0, 1.0, 2.0, 0.5, -2.0, -0.5 };17341735emit_changed(get_edited_property(), preset_value[p_preset]);1736easing_draw->queue_redraw();1737}17381739void EditorPropertyEasing::_setup_spin() {1740spin->setup_and_show();1741spin->get_line_edit()->set_text(TS->format_number(rtos(get_edited_property_value())));1742spin->show();1743}17441745void EditorPropertyEasing::_spin_value_changed(double p_value) {1746// 0 is a singularity, but both positive and negative values1747// are otherwise allowed. Enforce 0+ as workaround.1748if (Math::is_zero_approx(p_value)) {1749p_value = 0.00001;1750}17511752// Limit to a reasonable value to prevent the curve going into infinity,1753// which can cause crashes and other issues.1754p_value = CLAMP(p_value, -1'000'000, 1'000'000);17551756if (positive_only) {1757// Force a positive or zero value if a negative value was manually entered by double-clicking.1758p_value = MAX(0.0, p_value);1759}17601761emit_changed(get_edited_property(), p_value);1762_spin_focus_exited();1763}17641765void EditorPropertyEasing::_spin_focus_exited() {1766spin->hide();1767// Ensure the easing doesn't appear as being dragged1768dragging = false;1769easing_draw->queue_redraw();1770}17711772void EditorPropertyEasing::setup(bool p_positive_only, bool p_flip) {1773flip = p_flip;1774positive_only = p_positive_only;1775}17761777void EditorPropertyEasing::_notification(int p_what) {1778switch (p_what) {1779case NOTIFICATION_THEME_CHANGED: {1780preset->clear();1781preset->add_icon_item(get_editor_theme_icon(SNAME("CurveLinear")), "Linear", EASING_LINEAR);1782preset->add_icon_item(get_editor_theme_icon(SNAME("CurveIn")), "Ease In", EASING_IN);1783preset->add_icon_item(get_editor_theme_icon(SNAME("CurveOut")), "Ease Out", EASING_OUT);1784preset->add_icon_item(get_editor_theme_icon(SNAME("CurveConstant")), "Zero", EASING_ZERO);1785if (!positive_only) {1786preset->add_icon_item(get_editor_theme_icon(SNAME("CurveInOut")), "Ease In-Out", EASING_IN_OUT);1787preset->add_icon_item(get_editor_theme_icon(SNAME("CurveOutIn")), "Ease Out-In", EASING_OUT_IN);1788}1789easing_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));1790} break;1791}1792}17931794EditorPropertyEasing::EditorPropertyEasing() {1795easing_draw = memnew(Control);1796easing_draw->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyEasing::_draw_easing));1797easing_draw->connect(SceneStringName(gui_input), callable_mp(this, &EditorPropertyEasing::_drag_easing));1798easing_draw->set_default_cursor_shape(Control::CURSOR_MOVE);1799add_child(easing_draw);18001801preset = memnew(PopupMenu);1802add_child(preset);1803preset->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyEasing::_set_preset));18041805spin = memnew(EditorSpinSlider);1806spin->set_flat(true);1807spin->set_min(-100);1808spin->set_max(100);1809spin->set_step(0);1810spin->set_hide_slider(true);1811spin->set_allow_lesser(true);1812spin->set_allow_greater(true);1813spin->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyEasing::_spin_value_changed));1814spin->get_line_edit()->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyEasing::_spin_focus_exited));1815spin->hide();1816add_child(spin);1817}18181819///////////////////// RECT2 /////////////////////////18201821void EditorPropertyRect2::_set_read_only(bool p_read_only) {1822for (int i = 0; i < 4; i++) {1823spin[i]->set_read_only(p_read_only);1824}1825}18261827void EditorPropertyRect2::_value_changed(double val, const String &p_name) {1828Rect2 r2;1829r2.position.x = spin[0]->get_value();1830r2.position.y = spin[1]->get_value();1831r2.size.x = spin[2]->get_value();1832r2.size.y = spin[3]->get_value();1833emit_changed(get_edited_property(), r2, p_name);1834}18351836void EditorPropertyRect2::update_property() {1837Rect2 val = get_edited_property_value();1838spin[0]->set_value_no_signal(val.position.x);1839spin[1]->set_value_no_signal(val.position.y);1840spin[2]->set_value_no_signal(val.size.x);1841spin[3]->set_value_no_signal(val.size.y);1842}18431844void EditorPropertyRect2::_notification(int p_what) {1845switch (p_what) {1846case NOTIFICATION_THEME_CHANGED: {1847const Color *colors = _get_property_colors();1848for (int i = 0; i < 4; i++) {1849spin[i]->add_theme_color_override("label_color", colors[i % 2]);1850}1851} break;1852}1853}18541855void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {1856for (int i = 0; i < 4; i++) {1857spin[i]->set_min(p_min);1858spin[i]->set_max(p_max);1859spin[i]->set_step(p_step);1860spin[i]->set_hide_slider(p_hide_slider);1861spin[i]->set_allow_greater(true);1862spin[i]->set_allow_lesser(true);1863spin[i]->set_suffix(p_suffix);1864}1865}18661867EditorPropertyRect2::EditorPropertyRect2(bool p_force_wide) {1868bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));1869bool grid = false;1870BoxContainer *bc;18711872if (p_force_wide) {1873bc = memnew(HBoxContainer);1874add_child(bc);1875} else if (horizontal) {1876bc = memnew(VBoxContainer);1877add_child(bc);1878set_bottom_editor(bc);18791880bc->add_child(memnew(HBoxContainer));1881bc->add_child(memnew(HBoxContainer));1882grid = true;1883} else {1884bc = memnew(VBoxContainer);1885add_child(bc);1886}18871888static const char *desc[4] = { "x", "y", "w", "h" };1889for (int i = 0; i < 4; i++) {1890spin[i] = memnew(EditorSpinSlider);1891spin[i]->set_label(desc[i]);1892spin[i]->set_accessibility_name(desc[i]);1893spin[i]->set_flat(true);18941895if (grid) {1896bc->get_child(i / 2)->add_child(spin[i]);1897} else {1898bc->add_child(spin[i]);1899}19001901add_focusable(spin[i]);1902spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyRect2::_value_changed).bind(desc[i]));1903if (horizontal) {1904spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);1905}1906}19071908if (!horizontal) {1909set_label_reference(spin[0]); //show text and buttons around this1910}1911}19121913///////////////////// RECT2i /////////////////////////19141915void EditorPropertyRect2i::_set_read_only(bool p_read_only) {1916for (int i = 0; i < 4; i++) {1917spin[i]->set_read_only(p_read_only);1918}1919}19201921void EditorPropertyRect2i::_value_changed(double val, const String &p_name) {1922Rect2i r2;1923r2.position.x = spin[0]->get_value();1924r2.position.y = spin[1]->get_value();1925r2.size.x = spin[2]->get_value();1926r2.size.y = spin[3]->get_value();1927emit_changed(get_edited_property(), r2, p_name);1928}19291930void EditorPropertyRect2i::update_property() {1931Rect2i val = get_edited_property_value();1932spin[0]->set_value_no_signal(val.position.x);1933spin[1]->set_value_no_signal(val.position.y);1934spin[2]->set_value_no_signal(val.size.x);1935spin[3]->set_value_no_signal(val.size.y);1936}19371938void EditorPropertyRect2i::_notification(int p_what) {1939switch (p_what) {1940case NOTIFICATION_THEME_CHANGED: {1941const Color *colors = _get_property_colors();1942for (int i = 0; i < 4; i++) {1943spin[i]->add_theme_color_override("label_color", colors[i % 2]);1944}1945} break;1946}1947}19481949void EditorPropertyRect2i::setup(int p_min, int p_max, const String &p_suffix) {1950for (int i = 0; i < 4; i++) {1951spin[i]->set_min(p_min);1952spin[i]->set_max(p_max);1953spin[i]->set_step(1);1954spin[i]->set_allow_greater(true);1955spin[i]->set_allow_lesser(true);1956spin[i]->set_suffix(p_suffix);1957spin[i]->set_editing_integer(true);1958}1959}19601961EditorPropertyRect2i::EditorPropertyRect2i(bool p_force_wide) {1962bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));1963bool grid = false;1964BoxContainer *bc;19651966if (p_force_wide) {1967bc = memnew(HBoxContainer);1968add_child(bc);1969} else if (horizontal) {1970bc = memnew(VBoxContainer);1971add_child(bc);1972set_bottom_editor(bc);19731974bc->add_child(memnew(HBoxContainer));1975bc->add_child(memnew(HBoxContainer));1976grid = true;1977} else {1978bc = memnew(VBoxContainer);1979add_child(bc);1980}19811982static const char *desc[4] = { "x", "y", "w", "h" };1983for (int i = 0; i < 4; i++) {1984spin[i] = memnew(EditorSpinSlider);1985spin[i]->set_label(desc[i]);1986spin[i]->set_accessibility_name(desc[i]);1987spin[i]->set_flat(true);19881989if (grid) {1990bc->get_child(i / 2)->add_child(spin[i]);1991} else {1992bc->add_child(spin[i]);1993}19941995add_focusable(spin[i]);1996spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyRect2i::_value_changed).bind(desc[i]));1997if (horizontal) {1998spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);1999}2000}20012002if (!horizontal) {2003set_label_reference(spin[0]); //show text and buttons around this2004}2005}20062007///////////////////// PLANE /////////////////////////20082009void EditorPropertyPlane::_set_read_only(bool p_read_only) {2010for (int i = 0; i < 4; i++) {2011spin[i]->set_read_only(p_read_only);2012}2013}20142015void EditorPropertyPlane::_value_changed(double val, const String &p_name) {2016Plane p;2017p.normal.x = spin[0]->get_value();2018p.normal.y = spin[1]->get_value();2019p.normal.z = spin[2]->get_value();2020p.d = spin[3]->get_value();2021emit_changed(get_edited_property(), p, p_name);2022}20232024void EditorPropertyPlane::update_property() {2025Plane val = get_edited_property_value();2026spin[0]->set_value_no_signal(val.normal.x);2027spin[1]->set_value_no_signal(val.normal.y);2028spin[2]->set_value_no_signal(val.normal.z);2029spin[3]->set_value_no_signal(val.d);2030}20312032void EditorPropertyPlane::_notification(int p_what) {2033switch (p_what) {2034case NOTIFICATION_THEME_CHANGED: {2035const Color *colors = _get_property_colors();2036for (int i = 0; i < 4; i++) {2037spin[i]->add_theme_color_override("label_color", colors[i]);2038}2039} break;2040}2041}20422043void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {2044for (int i = 0; i < 4; i++) {2045spin[i]->set_min(p_min);2046spin[i]->set_max(p_max);2047spin[i]->set_step(p_step);2048spin[i]->set_hide_slider(p_hide_slider);2049spin[i]->set_allow_greater(true);2050spin[i]->set_allow_lesser(true);2051}2052spin[3]->set_suffix(p_suffix);2053}20542055EditorPropertyPlane::EditorPropertyPlane(bool p_force_wide) {2056bool horizontal = p_force_wide || bool(EDITOR_GET("interface/inspector/horizontal_vector_types_editing"));20572058BoxContainer *bc;20592060if (p_force_wide) {2061bc = memnew(HBoxContainer);2062add_child(bc);2063} else if (horizontal) {2064bc = memnew(HBoxContainer);2065add_child(bc);2066set_bottom_editor(bc);2067} else {2068bc = memnew(VBoxContainer);2069add_child(bc);2070}20712072static const char *desc[4] = { "x", "y", "z", "d" };2073for (int i = 0; i < 4; i++) {2074spin[i] = memnew(EditorSpinSlider);2075spin[i]->set_flat(true);2076spin[i]->set_label(desc[i]);2077spin[i]->set_accessibility_name(desc[i]);2078bc->add_child(spin[i]);2079add_focusable(spin[i]);2080spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyPlane::_value_changed).bind(desc[i]));2081if (horizontal) {2082spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2083}2084}20852086if (!horizontal) {2087set_label_reference(spin[0]); //show text and buttons around this2088}2089}20902091///////////////////// QUATERNION /////////////////////////20922093void EditorPropertyQuaternion::_set_read_only(bool p_read_only) {2094for (int i = 0; i < 4; i++) {2095spin[i]->set_read_only(p_read_only);2096}2097for (int i = 0; i < 3; i++) {2098euler[i]->set_read_only(p_read_only);2099}2100}21012102void EditorPropertyQuaternion::_edit_custom_value() {2103if (edit_button->is_pressed()) {2104edit_custom_bc->show();2105for (int i = 0; i < 3; i++) {2106euler[i]->grab_focus();2107}2108} else {2109edit_custom_bc->hide();2110for (int i = 0; i < 4; i++) {2111spin[i]->grab_focus();2112}2113}2114update_property();2115}21162117void EditorPropertyQuaternion::_custom_value_changed(double val) {2118edit_euler.x = euler[0]->get_value();2119edit_euler.y = euler[1]->get_value();2120edit_euler.z = euler[2]->get_value();21212122Vector3 v;2123v.x = Math::deg_to_rad(edit_euler.x);2124v.y = Math::deg_to_rad(edit_euler.y);2125v.z = Math::deg_to_rad(edit_euler.z);21262127Quaternion temp_q = Quaternion::from_euler(v);2128spin[0]->set_value_no_signal(temp_q.x);2129spin[1]->set_value_no_signal(temp_q.y);2130spin[2]->set_value_no_signal(temp_q.z);2131spin[3]->set_value_no_signal(temp_q.w);2132_value_changed(-1, "");2133}21342135void EditorPropertyQuaternion::_value_changed(double val, const String &p_name) {2136Quaternion p;2137p.x = spin[0]->get_value();2138p.y = spin[1]->get_value();2139p.z = spin[2]->get_value();2140p.w = spin[3]->get_value();21412142emit_changed(get_edited_property(), p, p_name);2143}21442145bool EditorPropertyQuaternion::is_grabbing_euler() {2146bool is_grabbing = false;2147for (int i = 0; i < 3; i++) {2148is_grabbing |= euler[i]->is_grabbing();2149}2150return is_grabbing;2151}21522153void EditorPropertyQuaternion::update_property() {2154Quaternion val = get_edited_property_value();2155spin[0]->set_value_no_signal(val.x);2156spin[1]->set_value_no_signal(val.y);2157spin[2]->set_value_no_signal(val.z);2158spin[3]->set_value_no_signal(val.w);2159if (!is_grabbing_euler()) {2160Vector3 v = val.normalized().get_euler();2161edit_euler.x = Math::rad_to_deg(v.x);2162edit_euler.y = Math::rad_to_deg(v.y);2163edit_euler.z = Math::rad_to_deg(v.z);2164euler[0]->set_value_no_signal(edit_euler.x);2165euler[1]->set_value_no_signal(edit_euler.y);2166euler[2]->set_value_no_signal(edit_euler.z);2167}2168}21692170void EditorPropertyQuaternion::_warning_pressed() {2171warning_dialog->popup_centered();2172}21732174void EditorPropertyQuaternion::_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}2181for (int i = 0; i < 3; i++) {2182euler[i]->add_theme_color_override("label_color", colors[i]);2183}2184edit_button->set_button_icon(get_editor_theme_icon(SNAME("Edit")));2185euler_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("property_color"), SNAME("EditorProperty")));2186warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));2187warning->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));2188} break;2189}2190}21912192void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix, bool p_hide_editor) {2193for (int i = 0; i < 4; i++) {2194spin[i]->set_min(p_min);2195spin[i]->set_max(p_max);2196spin[i]->set_step(p_step);2197spin[i]->set_hide_slider(p_hide_slider);2198spin[i]->set_allow_greater(true);2199spin[i]->set_allow_lesser(true);2200// Quaternion is inherently unitless, however someone may want to use it as2201// a generic way to store 4 values, so we'll still respect the suffix.2202spin[i]->set_suffix(p_suffix);2203}22042205for (int i = 0; i < 3; i++) {2206euler[i]->set_min(-360);2207euler[i]->set_max(360);2208euler[i]->set_step(0.1);2209euler[i]->set_hide_slider(false);2210euler[i]->set_allow_greater(true);2211euler[i]->set_allow_lesser(true);2212euler[i]->set_suffix(U"\u00B0");2213}22142215if (p_hide_editor) {2216edit_button->hide();2217}2218}22192220EditorPropertyQuaternion::EditorPropertyQuaternion() {2221bool horizontal = EDITOR_GET("interface/inspector/horizontal_vector_types_editing");22222223VBoxContainer *bc = memnew(VBoxContainer);2224edit_custom_bc = memnew(VBoxContainer);2225BoxContainer *edit_custom_layout;2226if (horizontal) {2227default_layout = memnew(HBoxContainer);2228edit_custom_layout = memnew(HBoxContainer);2229set_bottom_editor(bc);2230} else {2231default_layout = memnew(VBoxContainer);2232edit_custom_layout = memnew(VBoxContainer);2233}2234edit_custom_bc->hide();2235add_child(bc);2236edit_custom_bc->set_h_size_flags(SIZE_EXPAND_FILL);2237default_layout->set_h_size_flags(SIZE_EXPAND_FILL);2238edit_custom_layout->set_h_size_flags(SIZE_EXPAND_FILL);2239bc->add_child(default_layout);2240bc->add_child(edit_custom_bc);22412242static const char *desc[4] = { "x", "y", "z", "w" };2243for (int i = 0; i < 4; i++) {2244spin[i] = memnew(EditorSpinSlider);2245spin[i]->set_flat(true);2246spin[i]->set_label(desc[i]);2247spin[i]->set_accessibility_name(desc[i]);2248default_layout->add_child(spin[i]);2249add_focusable(spin[i]);2250spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyQuaternion::_value_changed).bind(desc[i]));2251if (horizontal) {2252spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2253}2254}22552256warning = memnew(Button);2257warning->set_text(TTR("Temporary Euler may be changed implicitly!"));2258warning->set_clip_text(true);2259warning->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyQuaternion::_warning_pressed));2260warning_dialog = memnew(AcceptDialog);2261add_child(warning_dialog);2262warning_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."));22632264euler_label = memnew(Label);2265euler_label->set_text(TTR("Temporary Euler"));22662267edit_custom_bc->add_child(warning);2268edit_custom_bc->add_child(edit_custom_layout);2269edit_custom_layout->add_child(euler_label);22702271for (int i = 0; i < 3; i++) {2272euler[i] = memnew(EditorSpinSlider);2273euler[i]->set_flat(true);2274euler[i]->set_label(desc[i]);2275euler[i]->set_accessibility_name(vformat(TTR("Temporary Euler %s"), desc[i]));2276edit_custom_layout->add_child(euler[i]);2277add_focusable(euler[i]);2278euler[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyQuaternion::_custom_value_changed));2279if (horizontal) {2280euler[i]->set_h_size_flags(SIZE_EXPAND_FILL);2281}2282}22832284edit_button = memnew(Button);2285edit_button->set_accessibility_name(TTRC("Edit"));2286edit_button->set_flat(true);2287edit_button->set_toggle_mode(true);2288default_layout->add_child(edit_button);2289edit_button->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyQuaternion::_edit_custom_value));22902291add_focusable(edit_button);22922293if (!horizontal) {2294set_label_reference(spin[0]); //show text and buttons around this2295}2296}22972298///////////////////// AABB /////////////////////////22992300void EditorPropertyAABB::_set_read_only(bool p_read_only) {2301for (int i = 0; i < 6; i++) {2302spin[i]->set_read_only(p_read_only);2303}2304}23052306void EditorPropertyAABB::_value_changed(double val, const String &p_name) {2307AABB p;2308p.position.x = spin[0]->get_value();2309p.position.y = spin[1]->get_value();2310p.position.z = spin[2]->get_value();2311p.size.x = spin[3]->get_value();2312p.size.y = spin[4]->get_value();2313p.size.z = spin[5]->get_value();2314emit_changed(get_edited_property(), p, p_name);2315}23162317void EditorPropertyAABB::update_property() {2318AABB val = get_edited_property_value();2319spin[0]->set_value_no_signal(val.position.x);2320spin[1]->set_value_no_signal(val.position.y);2321spin[2]->set_value_no_signal(val.position.z);2322spin[3]->set_value_no_signal(val.size.x);2323spin[4]->set_value_no_signal(val.size.y);2324spin[5]->set_value_no_signal(val.size.z);2325}23262327void EditorPropertyAABB::_notification(int p_what) {2328switch (p_what) {2329case NOTIFICATION_THEME_CHANGED: {2330const Color *colors = _get_property_colors();2331for (int i = 0; i < 6; i++) {2332spin[i]->add_theme_color_override("label_color", colors[i % 3]);2333}2334} break;2335}2336}23372338void EditorPropertyAABB::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {2339for (int i = 0; i < 6; i++) {2340spin[i]->set_min(p_min);2341spin[i]->set_max(p_max);2342spin[i]->set_step(p_step);2343spin[i]->set_hide_slider(p_hide_slider);2344spin[i]->set_allow_greater(true);2345spin[i]->set_allow_lesser(true);2346spin[i]->set_suffix(p_suffix);2347}2348}23492350EditorPropertyAABB::EditorPropertyAABB() {2351GridContainer *g = memnew(GridContainer);2352g->set_columns(3);2353add_child(g);23542355static const char *desc[6] = { "x", "y", "z", "w", "h", "d" };2356for (int i = 0; i < 6; i++) {2357spin[i] = memnew(EditorSpinSlider);2358spin[i]->set_label(desc[i]);2359spin[i]->set_accessibility_name(desc[i]);2360spin[i]->set_flat(true);23612362g->add_child(spin[i]);2363spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2364add_focusable(spin[i]);2365spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyAABB::_value_changed).bind(desc[i]));2366}2367set_bottom_editor(g);2368}23692370///////////////////// TRANSFORM2D /////////////////////////23712372void EditorPropertyTransform2D::_set_read_only(bool p_read_only) {2373for (int i = 0; i < 6; i++) {2374spin[i]->set_read_only(p_read_only);2375}2376}23772378void EditorPropertyTransform2D::_value_changed(double val, const String &p_name) {2379Transform2D p;2380p[0][0] = spin[0]->get_value();2381p[1][0] = spin[1]->get_value();2382p[2][0] = spin[2]->get_value();2383p[0][1] = spin[3]->get_value();2384p[1][1] = spin[4]->get_value();2385p[2][1] = spin[5]->get_value();23862387emit_changed(get_edited_property(), p, p_name);2388}23892390void EditorPropertyTransform2D::update_property() {2391Transform2D val = get_edited_property_value();2392spin[0]->set_value_no_signal(val[0][0]);2393spin[1]->set_value_no_signal(val[1][0]);2394spin[2]->set_value_no_signal(val[2][0]);2395spin[3]->set_value_no_signal(val[0][1]);2396spin[4]->set_value_no_signal(val[1][1]);2397spin[5]->set_value_no_signal(val[2][1]);2398}23992400void EditorPropertyTransform2D::_notification(int p_what) {2401switch (p_what) {2402case NOTIFICATION_THEME_CHANGED: {2403const Color *colors = _get_property_colors();2404for (int i = 0; i < 6; i++) {2405// For Transform2D, use the 4th color (cyan) for the origin vector.2406if (i % 3 == 2) {2407spin[i]->add_theme_color_override("label_color", colors[3]);2408} else {2409spin[i]->add_theme_color_override("label_color", colors[i % 3]);2410}2411}2412} break;2413}2414}24152416void EditorPropertyTransform2D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {2417for (int i = 0; i < 6; i++) {2418spin[i]->set_min(p_min);2419spin[i]->set_max(p_max);2420spin[i]->set_step(p_step);2421spin[i]->set_hide_slider(p_hide_slider);2422spin[i]->set_allow_greater(true);2423spin[i]->set_allow_lesser(true);2424if (i % 3 == 2) {2425spin[i]->set_suffix(p_suffix);2426}2427}2428}24292430EditorPropertyTransform2D::EditorPropertyTransform2D(bool p_include_origin) {2431GridContainer *g = memnew(GridContainer);2432g->set_columns(p_include_origin ? 3 : 2);2433add_child(g);24342435static const char *desc[6] = { "xx", "xy", "xo", "yx", "yy", "yo" };2436for (int i = 0; i < 6; i++) {2437spin[i] = memnew(EditorSpinSlider);2438spin[i]->set_label(desc[i]);2439spin[i]->set_accessibility_name(desc[i]);2440spin[i]->set_flat(true);2441if (p_include_origin || i % 3 != 2) {2442g->add_child(spin[i]);2443}2444spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2445add_focusable(spin[i]);2446spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyTransform2D::_value_changed).bind(desc[i]));2447}2448set_bottom_editor(g);2449}24502451///////////////////// BASIS /////////////////////////24522453void EditorPropertyBasis::_set_read_only(bool p_read_only) {2454for (int i = 0; i < 9; i++) {2455spin[i]->set_read_only(p_read_only);2456}2457}24582459void EditorPropertyBasis::_value_changed(double val, const String &p_name) {2460Basis p;2461p[0][0] = spin[0]->get_value();2462p[0][1] = spin[1]->get_value();2463p[0][2] = spin[2]->get_value();2464p[1][0] = spin[3]->get_value();2465p[1][1] = spin[4]->get_value();2466p[1][2] = spin[5]->get_value();2467p[2][0] = spin[6]->get_value();2468p[2][1] = spin[7]->get_value();2469p[2][2] = spin[8]->get_value();24702471emit_changed(get_edited_property(), p, p_name);2472}24732474void EditorPropertyBasis::update_property() {2475Basis val = get_edited_property_value();2476spin[0]->set_value_no_signal(val[0][0]);2477spin[1]->set_value_no_signal(val[0][1]);2478spin[2]->set_value_no_signal(val[0][2]);2479spin[3]->set_value_no_signal(val[1][0]);2480spin[4]->set_value_no_signal(val[1][1]);2481spin[5]->set_value_no_signal(val[1][2]);2482spin[6]->set_value_no_signal(val[2][0]);2483spin[7]->set_value_no_signal(val[2][1]);2484spin[8]->set_value_no_signal(val[2][2]);2485}24862487void EditorPropertyBasis::_notification(int p_what) {2488switch (p_what) {2489case NOTIFICATION_THEME_CHANGED: {2490const Color *colors = _get_property_colors();2491for (int i = 0; i < 9; i++) {2492spin[i]->add_theme_color_override("label_color", colors[i % 3]);2493}2494} break;2495}2496}24972498void EditorPropertyBasis::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {2499for (int i = 0; i < 9; i++) {2500spin[i]->set_min(p_min);2501spin[i]->set_max(p_max);2502spin[i]->set_step(p_step);2503spin[i]->set_hide_slider(p_hide_slider);2504spin[i]->set_allow_greater(true);2505spin[i]->set_allow_lesser(true);2506// Basis is inherently unitless, however someone may want to use it as2507// a generic way to store 9 values, so we'll still respect the suffix.2508spin[i]->set_suffix(p_suffix);2509}2510}25112512EditorPropertyBasis::EditorPropertyBasis() {2513GridContainer *g = memnew(GridContainer);2514g->set_columns(3);2515add_child(g);25162517static const char *desc[9] = { "xx", "xy", "xz", "yx", "yy", "yz", "zx", "zy", "zz" };2518for (int i = 0; i < 9; i++) {2519spin[i] = memnew(EditorSpinSlider);2520spin[i]->set_label(desc[i]);2521spin[i]->set_accessibility_name(desc[i]);2522spin[i]->set_flat(true);2523g->add_child(spin[i]);2524spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2525add_focusable(spin[i]);2526spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyBasis::_value_changed).bind(desc[i]));2527}2528set_bottom_editor(g);2529}25302531///////////////////// TRANSFORM3D /////////////////////////25322533void EditorPropertyTransform3D::_set_read_only(bool p_read_only) {2534for (int i = 0; i < 12; i++) {2535spin[i]->set_read_only(p_read_only);2536}2537}25382539void EditorPropertyTransform3D::_value_changed(double val, const String &p_name) {2540Transform3D p;2541p.basis[0][0] = spin[0]->get_value();2542p.basis[0][1] = spin[1]->get_value();2543p.basis[0][2] = spin[2]->get_value();2544p.origin[0] = spin[3]->get_value();2545p.basis[1][0] = spin[4]->get_value();2546p.basis[1][1] = spin[5]->get_value();2547p.basis[1][2] = spin[6]->get_value();2548p.origin[1] = spin[7]->get_value();2549p.basis[2][0] = spin[8]->get_value();2550p.basis[2][1] = spin[9]->get_value();2551p.basis[2][2] = spin[10]->get_value();2552p.origin[2] = spin[11]->get_value();25532554emit_changed(get_edited_property(), p, p_name);2555}25562557void EditorPropertyTransform3D::update_property() {2558update_using_transform(get_edited_property_value());2559}25602561void EditorPropertyTransform3D::update_using_transform(Transform3D p_transform) {2562spin[0]->set_value_no_signal(p_transform.basis[0][0]);2563spin[1]->set_value_no_signal(p_transform.basis[0][1]);2564spin[2]->set_value_no_signal(p_transform.basis[0][2]);2565spin[3]->set_value_no_signal(p_transform.origin[0]);2566spin[4]->set_value_no_signal(p_transform.basis[1][0]);2567spin[5]->set_value_no_signal(p_transform.basis[1][1]);2568spin[6]->set_value_no_signal(p_transform.basis[1][2]);2569spin[7]->set_value_no_signal(p_transform.origin[1]);2570spin[8]->set_value_no_signal(p_transform.basis[2][0]);2571spin[9]->set_value_no_signal(p_transform.basis[2][1]);2572spin[10]->set_value_no_signal(p_transform.basis[2][2]);2573spin[11]->set_value_no_signal(p_transform.origin[2]);2574}25752576void EditorPropertyTransform3D::_notification(int p_what) {2577switch (p_what) {2578case NOTIFICATION_THEME_CHANGED: {2579const Color *colors = _get_property_colors();2580for (int i = 0; i < 12; i++) {2581spin[i]->add_theme_color_override("label_color", colors[i % 4]);2582}2583} break;2584}2585}25862587void EditorPropertyTransform3D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {2588for (int i = 0; i < 12; i++) {2589spin[i]->set_min(p_min);2590spin[i]->set_max(p_max);2591spin[i]->set_step(p_step);2592spin[i]->set_hide_slider(p_hide_slider);2593spin[i]->set_allow_greater(true);2594spin[i]->set_allow_lesser(true);2595if (i % 4 == 3) {2596spin[i]->set_suffix(p_suffix);2597}2598}2599}26002601EditorPropertyTransform3D::EditorPropertyTransform3D() {2602GridContainer *g = memnew(GridContainer);2603g->set_columns(4);2604add_child(g);26052606static const char *desc[12] = { "xx", "xy", "xz", "xo", "yx", "yy", "yz", "yo", "zx", "zy", "zz", "zo" };2607for (int i = 0; i < 12; i++) {2608spin[i] = memnew(EditorSpinSlider);2609spin[i]->set_label(desc[i]);2610spin[i]->set_accessibility_name(desc[i]);2611spin[i]->set_flat(true);2612g->add_child(spin[i]);2613spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2614add_focusable(spin[i]);2615spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyTransform3D::_value_changed).bind(desc[i]));2616}2617set_bottom_editor(g);2618}26192620///////////////////// PROJECTION /////////////////////////26212622void EditorPropertyProjection::_set_read_only(bool p_read_only) {2623for (int i = 0; i < 12; i++) {2624spin[i]->set_read_only(p_read_only);2625}2626}26272628void EditorPropertyProjection::_value_changed(double val, const String &p_name) {2629Projection p;2630p.columns[0][0] = spin[0]->get_value();2631p.columns[0][1] = spin[1]->get_value();2632p.columns[0][2] = spin[2]->get_value();2633p.columns[0][3] = spin[3]->get_value();2634p.columns[1][0] = spin[4]->get_value();2635p.columns[1][1] = spin[5]->get_value();2636p.columns[1][2] = spin[6]->get_value();2637p.columns[1][3] = spin[7]->get_value();2638p.columns[2][0] = spin[8]->get_value();2639p.columns[2][1] = spin[9]->get_value();2640p.columns[2][2] = spin[10]->get_value();2641p.columns[2][3] = spin[11]->get_value();2642p.columns[3][0] = spin[12]->get_value();2643p.columns[3][1] = spin[13]->get_value();2644p.columns[3][2] = spin[14]->get_value();2645p.columns[3][3] = spin[15]->get_value();26462647emit_changed(get_edited_property(), p, p_name);2648}26492650void EditorPropertyProjection::update_property() {2651update_using_transform(get_edited_property_value());2652}26532654void EditorPropertyProjection::update_using_transform(Projection p_transform) {2655spin[0]->set_value_no_signal(p_transform.columns[0][0]);2656spin[1]->set_value_no_signal(p_transform.columns[0][1]);2657spin[2]->set_value_no_signal(p_transform.columns[0][2]);2658spin[3]->set_value_no_signal(p_transform.columns[0][3]);2659spin[4]->set_value_no_signal(p_transform.columns[1][0]);2660spin[5]->set_value_no_signal(p_transform.columns[1][1]);2661spin[6]->set_value_no_signal(p_transform.columns[1][2]);2662spin[7]->set_value_no_signal(p_transform.columns[1][3]);2663spin[8]->set_value_no_signal(p_transform.columns[2][0]);2664spin[9]->set_value_no_signal(p_transform.columns[2][1]);2665spin[10]->set_value_no_signal(p_transform.columns[2][2]);2666spin[11]->set_value_no_signal(p_transform.columns[2][3]);2667spin[12]->set_value_no_signal(p_transform.columns[3][0]);2668spin[13]->set_value_no_signal(p_transform.columns[3][1]);2669spin[14]->set_value_no_signal(p_transform.columns[3][2]);2670spin[15]->set_value_no_signal(p_transform.columns[3][3]);2671}26722673void EditorPropertyProjection::_notification(int p_what) {2674switch (p_what) {2675case NOTIFICATION_THEME_CHANGED: {2676const Color *colors = _get_property_colors();2677for (int i = 0; i < 16; i++) {2678spin[i]->add_theme_color_override("label_color", colors[i % 4]);2679}2680} break;2681}2682}26832684void EditorPropertyProjection::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {2685for (int i = 0; i < 16; i++) {2686spin[i]->set_min(p_min);2687spin[i]->set_max(p_max);2688spin[i]->set_step(p_step);2689spin[i]->set_hide_slider(p_hide_slider);2690spin[i]->set_allow_greater(true);2691spin[i]->set_allow_lesser(true);2692if (i % 4 == 3) {2693spin[i]->set_suffix(p_suffix);2694}2695}2696}26972698EditorPropertyProjection::EditorPropertyProjection() {2699GridContainer *g = memnew(GridContainer);2700g->set_columns(4);2701add_child(g);27022703static const char *desc[16] = { "xx", "xy", "xz", "xw", "yx", "yy", "yz", "yw", "zx", "zy", "zz", "zw", "wx", "wy", "wz", "ww" };2704for (int i = 0; i < 16; i++) {2705spin[i] = memnew(EditorSpinSlider);2706spin[i]->set_label(desc[i]);2707spin[i]->set_accessibility_name(desc[i]);2708spin[i]->set_flat(true);2709g->add_child(spin[i]);2710spin[i]->set_h_size_flags(SIZE_EXPAND_FILL);2711add_focusable(spin[i]);2712spin[i]->connect(SceneStringName(value_changed), callable_mp(this, &EditorPropertyProjection::_value_changed).bind(desc[i]));2713}2714set_bottom_editor(g);2715}2716////////////// COLOR PICKER //////////////////////27172718void EditorPropertyColor::_set_read_only(bool p_read_only) {2719picker->set_disabled(p_read_only);2720}27212722void EditorPropertyColor::_color_changed(const Color &p_color) {2723if (!live_changes_enabled) {2724return;2725}27262727// Cancel the color change if the current color is identical to the new one.2728if (((Color)get_edited_property_value()).is_equal_approx(p_color)) {2729return;2730}27312732// Preview color change, bypassing undo/redo.2733get_edited_object()->set(get_edited_property(), p_color);2734}27352736void EditorPropertyColor::_picker_created() {2737picker->get_popup()->connect("about_to_popup", callable_mp(this, &EditorPropertyColor::_popup_opening));2738picker->connect("popup_closed", callable_mp(this, &EditorPropertyColor::_popup_closed), CONNECT_DEFERRED);2739}27402741void EditorPropertyColor::_popup_opening() {2742EditorNode::get_singleton()->setup_color_picker(picker->get_picker());2743last_color = picker->get_pick_color();2744was_checked = !is_checkable() || is_checked();2745}27462747void EditorPropertyColor::_popup_closed() {2748get_edited_object()->set(get_edited_property(), was_checked ? Variant(last_color) : Variant());2749if (!picker->get_pick_color().is_equal_approx(last_color)) {2750emit_changed(get_edited_property(), picker->get_pick_color(), "", false);2751}2752}27532754void EditorPropertyColor::_notification(int p_what) {2755switch (p_what) {2756case NOTIFICATION_THEME_CHANGED: {2757picker->set_custom_minimum_size(Size2(0, get_theme_constant(SNAME("color_picker_button_height"), EditorStringName(Editor))));2758} break;2759}2760}27612762void EditorPropertyColor::update_property() {2763picker->set_pick_color(get_edited_property_display_value());2764const Color color = picker->get_pick_color();27652766// Add a tooltip to display each channel's values without having to click the ColorPickerButton2767if (picker->is_editing_alpha()) {2768picker->set_tooltip_text(vformat(2769"R: %s\nG: %s\nB: %s\nA: %s",2770rtos(color.r).pad_decimals(2),2771rtos(color.g).pad_decimals(2),2772rtos(color.b).pad_decimals(2),2773rtos(color.a).pad_decimals(2)));2774} else {2775picker->set_tooltip_text(vformat(2776"R: %s\nG: %s\nB: %s",2777rtos(color.r).pad_decimals(2),2778rtos(color.g).pad_decimals(2),2779rtos(color.b).pad_decimals(2)));2780}2781}27822783void EditorPropertyColor::setup(bool p_show_alpha) {2784picker->set_edit_alpha(p_show_alpha);2785}27862787void EditorPropertyColor::set_live_changes_enabled(bool p_enabled) {2788live_changes_enabled = p_enabled;2789}27902791EditorPropertyColor::EditorPropertyColor() {2792picker = memnew(ColorPickerButton);2793add_child(picker);2794picker->set_flat(true);2795picker->connect("color_changed", callable_mp(this, &EditorPropertyColor::_color_changed));2796picker->connect("picker_created", callable_mp(this, &EditorPropertyColor::_picker_created), CONNECT_ONE_SHOT);2797}27982799////////////// NODE PATH //////////////////////28002801void EditorPropertyNodePath::_set_read_only(bool p_read_only) {2802assign->set_disabled(p_read_only);2803menu->set_disabled(p_read_only);2804}28052806Variant EditorPropertyNodePath::_get_cache_value(const StringName &p_prop, bool &r_valid) const {2807if (p_prop == get_edited_property()) {2808r_valid = true;2809return const_cast<EditorPropertyNodePath *>(this)->get_edited_object()->get(get_edited_property(), &r_valid);2810}2811return Variant();2812}28132814void EditorPropertyNodePath::_node_selected(const NodePath &p_path, bool p_absolute) {2815NodePath path = p_path;2816Node *base_node = get_base_node();28172818if (!base_node && Object::cast_to<RefCounted>(get_edited_object())) {2819Node *to_node = get_node(p_path);2820ERR_FAIL_NULL(to_node);2821path = get_tree()->get_edited_scene_root()->get_path_to(to_node);2822}28232824if (p_absolute && base_node) { // for AnimationTrackKeyEdit2825path = base_node->get_path().rel_path_to(p_path);2826}28272828if (editing_node) {2829if (!base_node) {2830emit_changed(get_edited_property(), get_tree()->get_edited_scene_root()->get_node(path));2831} else {2832emit_changed(get_edited_property(), base_node->get_node(path));2833}2834} else {2835emit_changed(get_edited_property(), path);2836}2837update_property();2838}28392840void EditorPropertyNodePath::_node_assign() {2841if (!scene_tree) {2842scene_tree = memnew(SceneTreeDialog);2843scene_tree->get_scene_tree()->set_show_enabled_subscene(true);2844scene_tree->set_valid_types(valid_types);2845add_child(scene_tree);2846scene_tree->connect("selected", callable_mp(this, &EditorPropertyNodePath::_node_selected).bind(true));2847}28482849Variant val = get_edited_property_value();2850Node *n = nullptr;2851if (val.get_type() == Variant::Type::NODE_PATH) {2852Node *base_node = get_base_node();2853n = base_node == nullptr ? nullptr : base_node->get_node_or_null(val);2854} else {2855n = Object::cast_to<Node>(val);2856}2857scene_tree->popup_scenetree_dialog(n, get_base_node());2858}28592860void EditorPropertyNodePath::_assign_draw() {2861if (dropping) {2862Color color = get_theme_color(SNAME("accent_color"), EditorStringName(Editor));2863assign->draw_rect(Rect2(Point2(), assign->get_size()), color, false);2864}2865}28662867void EditorPropertyNodePath::_update_menu() {2868const NodePath &np = _get_node_path();28692870menu->get_popup()->set_item_disabled(ACTION_CLEAR, np.is_empty());2871menu->get_popup()->set_item_disabled(ACTION_COPY, np.is_empty());28722873Node *edited_node = Object::cast_to<Node>(get_edited_object());2874menu->get_popup()->set_item_disabled(ACTION_SELECT, !edited_node || !edited_node->has_node(np));2875}28762877void EditorPropertyNodePath::_menu_option(int p_idx) {2878switch (p_idx) {2879case ACTION_CLEAR: {2880if (editing_node) {2881emit_changed(get_edited_property(), Variant());2882} else {2883emit_changed(get_edited_property(), NodePath());2884}2885update_property();2886} break;28872888case ACTION_COPY: {2889DisplayServer::get_singleton()->clipboard_set(String(_get_node_path()));2890} break;28912892case ACTION_EDIT: {2893assign->hide();2894menu->hide();28952896const NodePath &np = _get_node_path();2897edit->set_text(String(np));2898edit->show();2899callable_mp((Control *)edit, &Control::grab_focus).call_deferred();2900} break;29012902case ACTION_SELECT: {2903const Node *edited_node = get_base_node();2904ERR_FAIL_NULL(edited_node);29052906const NodePath &np = _get_node_path();2907Node *target_node = edited_node->get_node_or_null(np);2908ERR_FAIL_NULL(target_node);29092910SceneTreeDock::get_singleton()->set_selected(target_node);2911} break;2912}2913}29142915void EditorPropertyNodePath::_accept_text() {2916_text_submitted(edit->get_text());2917}29182919void EditorPropertyNodePath::_text_submitted(const String &p_text) {2920NodePath np = p_text;2921_node_selected(np, false);2922edit->hide();2923assign->show();2924menu->show();2925}29262927const NodePath EditorPropertyNodePath::_get_node_path() const {2928const Node *base_node = const_cast<EditorPropertyNodePath *>(this)->get_base_node();29292930Variant val = get_edited_property_value();2931Node *n = Object::cast_to<Node>(val);2932if (n) {2933if (!n->is_inside_tree()) {2934return NodePath();2935}2936if (base_node) {2937return base_node->get_path_to(n);2938} else {2939return get_tree()->get_edited_scene_root()->get_path_to(n);2940}2941} else {2942return val;2943}2944}29452946bool EditorPropertyNodePath::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {2947return !is_read_only() && is_drop_valid(p_data);2948}29492950void EditorPropertyNodePath::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {2951ERR_FAIL_COND(!is_drop_valid(p_data));2952Dictionary data_dict = p_data;2953Array nodes = data_dict["nodes"];2954Node *node = get_tree()->get_edited_scene_root()->get_node(nodes[0]);29552956if (node) {2957_node_selected(node->get_path());2958}2959}29602961bool EditorPropertyNodePath::is_drop_valid(const Dictionary &p_drag_data) const {2962if (p_drag_data["type"] != "nodes") {2963return false;2964}2965Array nodes = p_drag_data["nodes"];2966if (nodes.size() != 1) {2967return false;2968}29692970Node *dropped_node = get_tree()->get_edited_scene_root()->get_node(nodes[0]);2971ERR_FAIL_NULL_V(dropped_node, false);29722973if (valid_types.is_empty()) {2974// No type requirements specified so any type is valid.2975return true;2976}29772978for (const StringName &E : valid_types) {2979if (dropped_node->is_class(E) ||2980EditorNode::get_singleton()->is_object_of_custom_type(dropped_node, E)) {2981return true;2982} else {2983Ref<Script> dropped_node_script = dropped_node->get_script();2984while (dropped_node_script.is_valid()) {2985if (dropped_node_script->get_path() == E) {2986return true;2987}2988dropped_node_script = dropped_node_script->get_base_script();2989}2990}2991}29922993return false;2994}29952996void EditorPropertyNodePath::update_property() {2997const Node *base_node = get_base_node();2998const NodePath &p = _get_node_path();2999assign->set_tooltip_text(String(p));30003001if (p.is_empty()) {3002assign->set_button_icon(Ref<Texture2D>());3003assign->set_text(TTR("Assign..."));3004assign->set_flat(false);3005return;3006}3007assign->set_flat(true);30083009if (!base_node || !base_node->has_node(p)) {3010assign->set_button_icon(Ref<Texture2D>());3011assign->set_text(String(p));3012return;3013}30143015const Node *target_node = base_node->get_node(p);3016ERR_FAIL_NULL(target_node);30173018if (String(target_node->get_name()).contains_char('@')) {3019assign->set_button_icon(Ref<Texture2D>());3020assign->set_text(String(p));3021return;3022}30233024assign->set_text(target_node->get_name());3025assign->set_button_icon(EditorNode::get_singleton()->get_object_icon(target_node, "Node"));3026}30273028void EditorPropertyNodePath::setup(const Vector<StringName> &p_valid_types, bool p_use_path_from_scene_root, bool p_editing_node) {3029valid_types = p_valid_types;3030editing_node = p_editing_node;3031use_path_from_scene_root = p_use_path_from_scene_root;3032}30333034void EditorPropertyNodePath::_notification(int p_what) {3035switch (p_what) {3036case NOTIFICATION_THEME_CHANGED: {3037menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));3038menu->get_popup()->set_item_icon(ACTION_CLEAR, get_editor_theme_icon(SNAME("Clear")));3039menu->get_popup()->set_item_icon(ACTION_COPY, get_editor_theme_icon(SNAME("ActionCopy")));3040menu->get_popup()->set_item_icon(ACTION_EDIT, get_editor_theme_icon(SNAME("Edit")));3041menu->get_popup()->set_item_icon(ACTION_SELECT, get_editor_theme_icon(SNAME("ExternalLink")));3042} break;30433044case NOTIFICATION_DRAG_BEGIN: {3045if (!is_read_only() && is_drop_valid(get_viewport()->gui_get_drag_data())) {3046dropping = true;3047assign->queue_redraw();3048}3049} break;30503051case NOTIFICATION_DRAG_END: {3052if (dropping) {3053dropping = false;3054assign->queue_redraw();3055}3056} break;3057}3058}30593060Node *EditorPropertyNodePath::get_base_node() {3061Node *base_node = Object::cast_to<Node>(get_edited_object());30623063if (!base_node) {3064base_node = Object::cast_to<Node>(InspectorDock::get_inspector_singleton()->get_edited_object());3065}3066if (!base_node) {3067// Try a base node within history.3068if (EditorNode::get_singleton()->get_editor_selection_history()->get_path_size() > 0) {3069Object *base = ObjectDB::get_instance(EditorNode::get_singleton()->get_editor_selection_history()->get_path_object(0));3070if (base) {3071base_node = Object::cast_to<Node>(base);3072}3073}3074}3075if (use_path_from_scene_root) {3076if (get_edited_object()->has_method("get_root_path")) {3077base_node = Object::cast_to<Node>(get_edited_object()->call("get_root_path"));3078} else {3079base_node = get_tree()->get_edited_scene_root();3080}3081}30823083return base_node;3084}30853086EditorPropertyNodePath::EditorPropertyNodePath() {3087HBoxContainer *hbc = memnew(HBoxContainer);3088hbc->add_theme_constant_override("separation", 0);3089add_child(hbc);3090assign = memnew(Button);3091assign->set_accessibility_name(TTRC("Assign Node"));3092assign->set_flat(true);3093assign->set_h_size_flags(SIZE_EXPAND_FILL);3094assign->set_clip_text(true);3095assign->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);3096assign->set_expand_icon(true);3097assign->connect(SceneStringName(pressed), callable_mp(this, &EditorPropertyNodePath::_node_assign));3098assign->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyNodePath::_assign_draw));3099SET_DRAG_FORWARDING_CD(assign, EditorPropertyNodePath);3100hbc->add_child(assign);31013102menu = memnew(MenuButton);3103menu->set_flat(true);3104menu->connect(SNAME("about_to_popup"), callable_mp(this, &EditorPropertyNodePath::_update_menu));3105hbc->add_child(menu);31063107menu->get_popup()->add_item(TTR("Clear"), ACTION_CLEAR);3108menu->get_popup()->add_item(TTR("Copy as Text"), ACTION_COPY);3109menu->get_popup()->add_item(TTR("Edit"), ACTION_EDIT);3110menu->get_popup()->add_item(TTR("Show Node in Tree"), ACTION_SELECT);3111menu->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &EditorPropertyNodePath::_menu_option));31123113edit = memnew(LineEdit);3114edit->set_accessibility_name(TTRC("Node Path"));3115edit->set_h_size_flags(SIZE_EXPAND_FILL);3116edit->hide();3117edit->connect(SceneStringName(focus_exited), callable_mp(this, &EditorPropertyNodePath::_accept_text));3118edit->connect(SceneStringName(text_submitted), callable_mp(this, &EditorPropertyNodePath::_text_submitted));3119hbc->add_child(edit);3120}31213122///////////////////// RID /////////////////////////31233124void EditorPropertyRID::update_property() {3125RID rid = get_edited_property_value();3126if (rid.is_valid()) {3127uint64_t id = rid.get_id();3128label->set_text("RID: " + uitos(id));3129} else {3130label->set_text(TTR("Invalid RID"));3131}3132}31333134EditorPropertyRID::EditorPropertyRID() {3135label = memnew(Label);3136add_child(label);3137}31383139////////////// RESOURCE //////////////////////31403141void EditorPropertyResource::_set_read_only(bool p_read_only) {3142resource_picker->set_editable(!p_read_only);3143}31443145void EditorPropertyResource::_resource_selected(const Ref<Resource> &p_resource, bool p_inspect) {3146if (p_resource->is_built_in() && !p_resource->get_path().is_empty()) {3147String parent = p_resource->get_path().get_slice("::", 0);3148List<String> extensions;3149ResourceLoader::get_recognized_extensions_for_type("PackedScene", &extensions);31503151if (p_inspect) {3152if (extensions.find(parent.get_extension()) && (!EditorNode::get_singleton()->get_edited_scene() || EditorNode::get_singleton()->get_edited_scene()->get_scene_file_path() != parent)) {3153// If the resource belongs to another (non-imported) scene, edit it in that scene instead.3154if (!FileAccess::exists(parent + ".import")) {3155callable_mp(EditorNode::get_singleton(), &EditorNode::edit_foreign_resource).call_deferred(p_resource);3156return;3157}3158}3159}3160}31613162if (!p_inspect && use_sub_inspector) {3163bool unfold = !get_edited_object()->editor_is_section_unfolded(get_edited_property());3164get_edited_object()->editor_set_section_unfold(get_edited_property(), unfold);3165update_property();3166} else if (!is_checkable() || is_checked()) {3167emit_signal(SNAME("resource_selected"), get_edited_property(), p_resource);3168}3169}31703171static bool _find_recursive_resources(const Variant &v, HashSet<Resource *> &resources_found) {3172switch (v.get_type()) {3173case Variant::ARRAY: {3174Array a = v;3175for (int i = 0; i < a.size(); i++) {3176Variant v2 = a[i];3177if (v2.get_type() != Variant::ARRAY && v2.get_type() != Variant::DICTIONARY && v2.get_type() != Variant::OBJECT) {3178continue;3179}3180if (_find_recursive_resources(v2, resources_found)) {3181return true;3182}3183}3184} break;3185case Variant::DICTIONARY: {3186Dictionary d = v;3187for (const KeyValue<Variant, Variant> &kv : d) {3188const Variant &k = kv.key;3189const Variant &v2 = kv.value;3190if (k.get_type() == Variant::ARRAY || k.get_type() == Variant::DICTIONARY || k.get_type() == Variant::OBJECT) {3191if (_find_recursive_resources(k, resources_found)) {3192return true;3193}3194}3195if (v2.get_type() == Variant::ARRAY || v2.get_type() == Variant::DICTIONARY || v2.get_type() == Variant::OBJECT) {3196if (_find_recursive_resources(v2, resources_found)) {3197return true;3198}3199}3200}3201} break;3202case Variant::OBJECT: {3203Ref<Resource> r = v;32043205if (r.is_null()) {3206return false;3207}32083209if (resources_found.has(r.ptr())) {3210return true;3211}32123213resources_found.insert(r.ptr());32143215List<PropertyInfo> plist;3216r->get_property_list(&plist);3217for (const PropertyInfo &pinfo : plist) {3218if (!(pinfo.usage & PROPERTY_USAGE_STORAGE)) {3219continue;3220}32213222if (pinfo.type != Variant::ARRAY && pinfo.type != Variant::DICTIONARY && pinfo.type != Variant::OBJECT) {3223continue;3224}3225if (_find_recursive_resources(r->get(pinfo.name), resources_found)) {3226return true;3227}3228}32293230resources_found.erase(r.ptr());3231} break;3232default: {3233}3234}3235return false;3236}32373238void EditorPropertyResource::_resource_changed(const Ref<Resource> &p_resource) {3239Resource *r = Object::cast_to<Resource>(get_edited_object());3240if (r) {3241// Check for recursive setting of resource3242HashSet<Resource *> resources_found;3243resources_found.insert(r);3244bool found = _find_recursive_resources(p_resource, resources_found);3245if (found) {3246EditorNode::get_singleton()->show_warning(TTR("Recursion detected, unable to assign resource to property."));3247emit_changed(get_edited_property(), Ref<Resource>());3248update_property();3249return;3250}3251}32523253// The bool is_script applies only to an object's main script.3254// Changing the value of Script-type exported variables of the main script should not trigger saving/reloading properties.3255bool is_script = false;3256Ref<Script> s = p_resource;3257if (get_edited_object() && s.is_valid() && get_edited_property() == CoreStringName(script)) {3258is_script = true;3259InspectorDock::get_singleton()->store_script_properties(get_edited_object());3260s->call("set_instance_base_type", get_edited_object()->get_class());3261}32623263// Prevent the creation of invalid ViewportTextures when possible.3264Ref<ViewportTexture> vpt = p_resource;3265if (vpt.is_valid()) {3266r = Object::cast_to<Resource>(get_edited_object());3267if (Object::cast_to<VisualShaderNodeTexture>(r)) {3268EditorNode::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."));3269emit_changed(get_edited_property(), Ref<Resource>());3270update_property();3271return;3272}32733274if (r && r->get_path().is_resource_file()) {3275EditorNode::get_singleton()->show_warning(TTR("Can't create a ViewportTexture on resources saved as a file.\nResource needs to belong to a scene."));3276emit_changed(get_edited_property(), Ref<Resource>());3277update_property();3278return;3279}32803281if (r && !r->is_local_to_scene()) {3282EditorNode::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)."));3283emit_changed(get_edited_property(), Ref<Resource>());3284update_property();3285return;3286}3287}32883289emit_changed(get_edited_property(), p_resource);3290update_property();32913292if (is_script) {3293// Restore properties if script was changed.3294InspectorDock::get_singleton()->apply_script_properties(get_edited_object());3295}32963297// Automatically suggest setting up the path for a ViewportTexture.3298if (vpt.is_valid() && vpt->get_viewport_path_in_scene().is_empty()) {3299if (!scene_tree) {3300scene_tree = memnew(SceneTreeDialog);3301scene_tree->set_title(TTR("Pick a Viewport"));33023303Vector<StringName> valid_types;3304valid_types.push_back("Viewport");3305scene_tree->set_valid_types(valid_types);3306scene_tree->get_scene_tree()->set_show_enabled_subscene(true);33073308add_child(scene_tree);3309scene_tree->connect("selected", callable_mp(this, &EditorPropertyResource::_viewport_selected));3310}3311scene_tree->popup_scenetree_dialog();3312}3313}33143315void EditorPropertyResource::_sub_inspector_property_keyed(const String &p_property, const Variant &p_value, bool p_advance) {3316// The second parameter could be null, causing the event to fire with less arguments, so use the pointer call which preserves it.3317const Variant args[3] = { String(get_edited_property()) + ":" + p_property, p_value, p_advance };3318const Variant *argp[3] = { &args[0], &args[1], &args[2] };3319emit_signalp(SNAME("property_keyed_with_value"), argp, 3);3320}33213322void EditorPropertyResource::_sub_inspector_resource_selected(const Ref<Resource> &p_resource, const String &p_property) {3323emit_signal(SNAME("resource_selected"), String(get_edited_property()) + ":" + p_property, p_resource);3324}33253326void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) {3327emit_signal(SNAME("object_id_selected"), get_edited_property(), p_id);3328}33293330void EditorPropertyResource::_open_editor_pressed() {3331Ref<Resource> res = get_edited_property_value();3332if (res.is_valid()) {3333EditorNode::get_singleton()->edit_item(res.ptr(), this);3334}3335}33363337void EditorPropertyResource::_update_preferred_shader() {3338Node *parent = get_parent();3339EditorProperty *parent_property = nullptr;33403341while (parent && !parent_property) {3342parent_property = Object::cast_to<EditorProperty>(parent);3343parent = parent->get_parent();3344}33453346if (parent_property) {3347EditorShaderPicker *shader_picker = Object::cast_to<EditorShaderPicker>(resource_picker);3348Object *ed_object = parent_property->get_edited_object();3349const StringName &ed_property = parent_property->get_edited_property();33503351// Set preferred shader based on edited parent type.3352if ((Object::cast_to<GPUParticles2D>(ed_object) || Object::cast_to<GPUParticles3D>(ed_object)) && ed_property == SNAME("process_material")) {3353shader_picker->set_preferred_mode(Shader::MODE_PARTICLES);3354} else if (Object::cast_to<FogVolume>(ed_object)) {3355shader_picker->set_preferred_mode(Shader::MODE_FOG);3356} else if (Object::cast_to<CanvasItem>(ed_object)) {3357shader_picker->set_preferred_mode(Shader::MODE_CANVAS_ITEM);3358} else if (Object::cast_to<Node3D>(ed_object) || Object::cast_to<Mesh>(ed_object)) {3359shader_picker->set_preferred_mode(Shader::MODE_SPATIAL);3360} else if (Object::cast_to<Sky>(ed_object)) {3361shader_picker->set_preferred_mode(Shader::MODE_SKY);3362}3363}3364}33653366bool EditorPropertyResource::_should_stop_editing() const {3367return !resource_picker->is_toggle_pressed();3368}33693370void EditorPropertyResource::_viewport_selected(const NodePath &p_path) {3371Node *to_node = get_node(p_path);3372if (!Object::cast_to<Viewport>(to_node)) {3373EditorNode::get_singleton()->show_warning(TTR("Selected node is not a Viewport!"));3374return;3375}33763377Ref<ViewportTexture> vt = get_edited_property_value();3378ERR_FAIL_COND(vt.is_null());33793380vt->set_viewport_path_in_scene(get_tree()->get_edited_scene_root()->get_path_to(to_node));33813382emit_changed(get_edited_property(), vt);3383update_property();3384}33853386void EditorPropertyResource::setup(Object *p_object, const String &p_path, const String &p_base_type) {3387if (resource_picker) {3388memdelete(resource_picker);3389resource_picker = nullptr;3390}33913392if (p_path == "script" && p_base_type == "Script" && Object::cast_to<Node>(p_object)) {3393EditorScriptPicker *script_picker = memnew(EditorScriptPicker);3394script_picker->set_script_owner(Object::cast_to<Node>(p_object));3395resource_picker = script_picker;3396} else if (p_path == "shader" && p_base_type == "Shader" && Object::cast_to<ShaderMaterial>(p_object)) {3397EditorShaderPicker *shader_picker = memnew(EditorShaderPicker);3398shader_picker->set_edited_material(Object::cast_to<ShaderMaterial>(p_object));3399resource_picker = shader_picker;3400connect(SceneStringName(ready), callable_mp(this, &EditorPropertyResource::_update_preferred_shader));3401} else if (ClassDB::is_parent_class(p_base_type, "AudioStream")) {3402EditorAudioStreamPicker *astream_picker = memnew(EditorAudioStreamPicker);3403resource_picker = astream_picker;3404} else {3405resource_picker = memnew(EditorResourcePicker);3406}34073408resource_picker->set_base_type(p_base_type);3409resource_picker->set_resource_owner(p_object);3410resource_picker->set_editable(true);3411resource_picker->set_h_size_flags(SIZE_EXPAND_FILL);3412add_child(resource_picker);34133414resource_picker->connect("resource_selected", callable_mp(this, &EditorPropertyResource::_resource_selected));3415resource_picker->connect("resource_changed", callable_mp(this, &EditorPropertyResource::_resource_changed));34163417for (int i = 0; i < resource_picker->get_child_count(); i++) {3418Button *b = Object::cast_to<Button>(resource_picker->get_child(i));3419if (b) {3420add_focusable(b);3421}3422}3423}34243425void EditorPropertyResource::update_property() {3426Ref<Resource> res = get_edited_property_display_value();34273428if (use_sub_inspector) {3429if (res.is_valid() != resource_picker->is_toggle_mode()) {3430resource_picker->set_toggle_mode(res.is_valid());3431}34323433if (res.is_valid() && get_edited_object()->editor_is_section_unfolded(get_edited_property())) {3434if (!sub_inspector) {3435sub_inspector = memnew(EditorInspector);3436sub_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);3437sub_inspector->set_use_doc_hints(true);34383439EditorInspector *parent_inspector = get_parent_inspector();3440if (parent_inspector) {3441sub_inspector->set_root_inspector(parent_inspector->get_root_inspector());3442sub_inspector->register_text_enter(parent_inspector->search_box);3443}34443445sub_inspector->set_property_name_style(InspectorDock::get_singleton()->get_property_name_style());34463447sub_inspector->connect("property_keyed", callable_mp(this, &EditorPropertyResource::_sub_inspector_property_keyed));3448sub_inspector->connect("resource_selected", callable_mp(this, &EditorPropertyResource::_sub_inspector_resource_selected));3449sub_inspector->connect("object_id_selected", callable_mp(this, &EditorPropertyResource::_sub_inspector_object_id_selected));3450sub_inspector->set_keying(is_keying());3451sub_inspector->set_read_only(is_read_only());3452sub_inspector->set_use_folding(is_using_folding());34533454sub_inspector->set_draw_focus_border(false);3455sub_inspector->set_focus_mode(FocusMode::FOCUS_NONE);34563457sub_inspector->set_use_filter(use_filter);34583459add_child(sub_inspector);3460set_bottom_editor(sub_inspector);34613462resource_picker->set_toggle_pressed(true);34633464Array editor_list;3465for (int i = 0; i < EditorNode::get_editor_data().get_editor_plugin_count(); i++) {3466EditorPlugin *ep = EditorNode::get_editor_data().get_editor_plugin(i);3467if (ep->handles(res.ptr())) {3468editor_list.push_back(ep);3469}3470}34713472if (!editor_list.is_empty()) {3473// Open editor directly.3474_open_editor_pressed();3475opened_editor = true;3476}3477}34783479sub_inspector->set_read_only(is_checkable() && !is_checked());34803481if (res.ptr() != sub_inspector->get_edited_object()) {3482sub_inspector->edit(res.ptr());3483_update_property_bg();3484}34853486} else if (sub_inspector) {3487set_bottom_editor(nullptr);3488memdelete(sub_inspector);3489sub_inspector = nullptr;34903491if (opened_editor) {3492EditorNode::get_singleton()->hide_unused_editors();3493opened_editor = false;3494}3495}3496}34973498resource_picker->set_edited_resource_no_check(res);3499}35003501void EditorPropertyResource::collapse_all_folding() {3502if (sub_inspector) {3503sub_inspector->collapse_all_folding();3504}3505}35063507void EditorPropertyResource::expand_all_folding() {3508if (sub_inspector) {3509sub_inspector->expand_all_folding();3510}3511}35123513void EditorPropertyResource::expand_revertable() {3514if (sub_inspector) {3515sub_inspector->expand_revertable();3516}3517}35183519void EditorPropertyResource::set_use_sub_inspector(bool p_enable) {3520use_sub_inspector = p_enable;3521}35223523void EditorPropertyResource::set_use_filter(bool p_use) {3524use_filter = p_use;3525if (sub_inspector) {3526update_property();3527}3528}35293530void EditorPropertyResource::fold_resource() {3531bool unfolded = get_edited_object()->editor_is_section_unfolded(get_edited_property());3532if (unfolded) {3533resource_picker->set_toggle_pressed(false);3534get_edited_object()->editor_set_section_unfold(get_edited_property(), false);3535update_property();3536}3537}35383539bool EditorPropertyResource::is_colored(ColorationMode p_mode) {3540switch (p_mode) {3541case COLORATION_CONTAINER_RESOURCE:3542return sub_inspector != nullptr;3543case COLORATION_RESOURCE:3544return true;3545case COLORATION_EXTERNAL:3546if (sub_inspector) {3547Resource *edited_resource = Object::cast_to<Resource>(sub_inspector->get_edited_object());3548return edited_resource && !edited_resource->is_built_in();3549}3550break;3551}3552return false;3553}35543555void EditorPropertyResource::_notification(int p_what) {3556switch (p_what) {3557case NOTIFICATION_EXIT_TREE: {3558const EditorInspector *ei = get_parent_inspector();3559const EditorInspector *main_ei = InspectorDock::get_inspector_singleton();3560if (ei && main_ei && ei != main_ei && !main_ei->is_ancestor_of(ei)) {3561fold_resource();3562}3563} break;3564}3565}35663567void EditorPropertyResource::_bind_methods() {3568ClassDB::bind_method(D_METHOD("_should_stop_editing"), &EditorPropertyResource::_should_stop_editing);3569}35703571EditorPropertyResource::EditorPropertyResource() {3572use_sub_inspector = bool(EDITOR_GET("interface/inspector/open_resources_in_current_inspector"));3573has_borders = true;3574}35753576////////////// DEFAULT PLUGIN //////////////////////35773578bool EditorInspectorDefaultPlugin::can_handle(Object *p_object) {3579return true; // Can handle everything.3580}35813582bool 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) {3583Control *editor = EditorInspectorDefaultPlugin::get_editor_for_property(p_object, p_type, p_path, p_hint, p_hint_text, p_usage, p_wide);3584if (editor) {3585add_property_editor(p_path, editor);3586}3587return false;3588}35893590struct EditorPropertyRangeHint {3591bool or_greater = true;3592bool or_less = true;3593double min = 0.0;3594double max = 0.0;3595double step = 1.0;3596String suffix;3597bool exp_range = false;3598bool hide_slider = true;3599bool radians_as_degrees = false;3600};36013602static EditorPropertyRangeHint _parse_range_hint(PropertyHint p_hint, const String &p_hint_text, double p_default_step, bool is_int = false) {3603EditorPropertyRangeHint hint;3604hint.step = p_default_step;3605if (is_int) {3606hint.hide_slider = false; // Always show slider for ints, unless specified in hint range.3607}3608Vector<String> slices = p_hint_text.split(",");3609if (p_hint == PROPERTY_HINT_RANGE) {3610ERR_FAIL_COND_V_MSG(slices.size() < 2, hint,3611vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": Missing required min and/or max values.", p_hint_text));36123613hint.or_greater = false; // If using ranged, assume false by default.3614hint.or_less = false;36153616hint.min = slices[0].to_float();3617hint.max = slices[1].to_float();36183619if (slices.size() >= 3 && slices[2].is_valid_float()) {3620// Step is optional, could be something else if not a number.3621hint.step = slices[2].to_float();3622}3623hint.hide_slider = false;3624for (int i = 2; i < slices.size(); i++) {3625String slice = slices[i].strip_edges();3626if (slice == "or_greater") {3627hint.or_greater = true;3628} else if (slice == "or_less") {3629hint.or_less = true;3630} else if (slice == "hide_slider") {3631hint.hide_slider = true;3632} else if (slice == "exp") {3633hint.exp_range = true;3634}3635}3636}3637bool degrees = false;3638for (int i = 0; i < slices.size(); i++) {3639String slice = slices[i].strip_edges();3640if (slice == "radians_as_degrees"3641#ifndef DISABLE_DEPRECATED3642|| slice == "radians"3643#endif // DISABLE_DEPRECATED3644) {3645hint.radians_as_degrees = true;3646} else if (slice == "degrees") {3647degrees = true;3648} else if (slice.begins_with("suffix:")) {3649hint.suffix = " " + slice.replace_first("suffix:", "").strip_edges();3650}3651}36523653if ((hint.radians_as_degrees || degrees) && hint.suffix.is_empty()) {3654hint.suffix = U"\u00B0";3655}36563657ERR_FAIL_COND_V_MSG(hint.step == 0, hint,3658vformat("Invalid PROPERTY_HINT_RANGE with hint \"%s\": Step cannot be 0.", p_hint_text));36593660return hint;3661}36623663static EditorProperty *get_input_action_editor(const String &p_hint_text, bool is_string_name) {3664// TODO: Should probably use a better editor GUI with a search bar.3665// Said GUI could also handle showing builtin options, requiring 1 less hint.3666EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);3667Vector<String> options;3668Vector<String> builtin_options;3669List<PropertyInfo> pinfo;3670ProjectSettings::get_singleton()->get_property_list(&pinfo);3671Vector<String> hints = p_hint_text.remove_char(' ').split(",", false);36723673HashMap<String, List<Ref<InputEvent>>> builtins = InputMap::get_singleton()->get_builtins();3674bool show_builtin = hints.has("show_builtin");36753676for (const PropertyInfo &pi : pinfo) {3677if (!pi.name.begins_with("input/")) {3678continue;3679}36803681const String action_name = pi.name.get_slicec('/', 1);3682if (builtins.has(action_name)) {3683if (show_builtin) {3684builtin_options.append(action_name);3685}3686} else {3687options.append(action_name);3688}3689}3690options.append_array(builtin_options);3691editor->setup(options, is_string_name, hints.has("loose_mode"));3692return editor;3693}36943695EditorProperty *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) {3696double default_float_step = EDITOR_GET("interface/inspector/default_float_step");36973698switch (p_type) {3699// atomic types3700case Variant::NIL: {3701if (p_usage & PROPERTY_USAGE_NIL_IS_VARIANT) {3702return memnew(EditorPropertyVariant);3703} else {3704return memnew(EditorPropertyNil);3705}3706} break;3707case Variant::BOOL: {3708EditorPropertyCheck *editor = memnew(EditorPropertyCheck);3709return editor;3710} break;3711case Variant::INT: {3712if (p_hint == PROPERTY_HINT_ENUM) {3713EditorPropertyEnum *editor = memnew(EditorPropertyEnum);3714Vector<String> options = p_hint_text.split(",");3715editor->setup(options);3716return editor;37173718} else if (p_hint == PROPERTY_HINT_FLAGS) {3719EditorPropertyFlags *editor = memnew(EditorPropertyFlags);3720Vector<String> options = p_hint_text.split(",");3721editor->setup(options);3722return editor;37233724} else if (p_hint == PROPERTY_HINT_LAYERS_2D_PHYSICS ||3725p_hint == PROPERTY_HINT_LAYERS_2D_RENDER ||3726p_hint == PROPERTY_HINT_LAYERS_2D_NAVIGATION ||3727p_hint == PROPERTY_HINT_LAYERS_3D_PHYSICS ||3728p_hint == PROPERTY_HINT_LAYERS_3D_RENDER ||3729p_hint == PROPERTY_HINT_LAYERS_3D_NAVIGATION ||3730p_hint == PROPERTY_HINT_LAYERS_AVOIDANCE) {3731EditorPropertyLayers::LayerType lt = EditorPropertyLayers::LAYER_RENDER_2D;3732switch (p_hint) {3733case PROPERTY_HINT_LAYERS_2D_RENDER:3734lt = EditorPropertyLayers::LAYER_RENDER_2D;3735break;3736case PROPERTY_HINT_LAYERS_2D_PHYSICS:3737lt = EditorPropertyLayers::LAYER_PHYSICS_2D;3738break;3739case PROPERTY_HINT_LAYERS_2D_NAVIGATION:3740lt = EditorPropertyLayers::LAYER_NAVIGATION_2D;3741break;3742case PROPERTY_HINT_LAYERS_3D_RENDER:3743lt = EditorPropertyLayers::LAYER_RENDER_3D;3744break;3745case PROPERTY_HINT_LAYERS_3D_PHYSICS:3746lt = EditorPropertyLayers::LAYER_PHYSICS_3D;3747break;3748case PROPERTY_HINT_LAYERS_3D_NAVIGATION:3749lt = EditorPropertyLayers::LAYER_NAVIGATION_3D;3750break;3751case PROPERTY_HINT_LAYERS_AVOIDANCE:3752lt = EditorPropertyLayers::LAYER_AVOIDANCE;3753break;3754default: {3755} //compiler could be smarter here and realize this can't happen3756}3757EditorPropertyLayers *editor = memnew(EditorPropertyLayers);3758editor->setup(lt);3759return editor;3760} else if (p_hint == PROPERTY_HINT_OBJECT_ID) {3761EditorPropertyObjectID *editor = memnew(EditorPropertyObjectID);3762editor->setup(p_hint_text);3763return editor;37643765} else {3766EditorPropertyInteger *editor = memnew(EditorPropertyInteger);37673768EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);3769editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.or_greater, hint.or_less, hint.suffix);37703771return editor;3772}3773} break;3774case Variant::FLOAT: {3775if (p_hint == PROPERTY_HINT_EXP_EASING) {3776EditorPropertyEasing *editor = memnew(EditorPropertyEasing);3777bool positive_only = false;3778bool flip = false;3779const Vector<String> hints = p_hint_text.split(",");3780for (int i = 0; i < hints.size(); i++) {3781const String hint = hints[i].strip_edges();3782if (hint == "attenuation") {3783flip = true;3784}3785if (hint == "positive_only") {3786positive_only = true;3787}3788}37893790editor->setup(positive_only, flip);3791return editor;37923793} else {3794EditorPropertyFloat *editor = memnew(EditorPropertyFloat);37953796EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3797editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.exp_range, hint.or_greater, hint.or_less, hint.suffix, hint.radians_as_degrees);37983799return editor;3800}3801} break;3802case Variant::STRING: {3803if (p_hint == PROPERTY_HINT_ENUM || p_hint == PROPERTY_HINT_ENUM_SUGGESTION) {3804EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);3805Vector<String> options = p_hint_text.split(",", false);3806editor->setup(options, false, (p_hint == PROPERTY_HINT_ENUM_SUGGESTION));3807return editor;3808} else if (p_hint == PROPERTY_HINT_INPUT_NAME) {3809return get_input_action_editor(p_hint_text, false);3810} else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) {3811EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText);3812return editor;3813} else if (p_hint == PROPERTY_HINT_EXPRESSION) {3814EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText(true));3815return editor;3816} else if (p_hint == PROPERTY_HINT_TYPE_STRING) {3817EditorPropertyClassName *editor = memnew(EditorPropertyClassName);3818editor->setup(p_hint_text, p_hint_text);3819return editor;3820} else if (p_hint == PROPERTY_HINT_LOCALE_ID) {3821EditorPropertyLocale *editor = memnew(EditorPropertyLocale);3822editor->setup(p_hint_text);3823return editor;3824} 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) {3825Vector<String> extensions = p_hint_text.split(",");3826bool global = p_hint == PROPERTY_HINT_GLOBAL_DIR || p_hint == PROPERTY_HINT_GLOBAL_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE;3827bool folder = p_hint == PROPERTY_HINT_DIR || p_hint == PROPERTY_HINT_GLOBAL_DIR;3828bool save = p_hint == PROPERTY_HINT_SAVE_FILE || p_hint == PROPERTY_HINT_GLOBAL_SAVE_FILE;3829bool enable_uid = p_hint == PROPERTY_HINT_FILE;3830EditorPropertyPath *editor = memnew(EditorPropertyPath);3831editor->setup(extensions, folder, global, enable_uid);3832if (save) {3833editor->set_save_mode();3834}3835return editor;3836} else {3837EditorPropertyText *editor = memnew(EditorPropertyText);3838if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) {3839editor->set_placeholder(p_hint_text);3840} else if (p_hint == PROPERTY_HINT_PASSWORD) {3841editor->set_secret(true);3842editor->set_placeholder(p_hint_text);3843}3844return editor;3845}3846} break;38473848// math types38493850case Variant::VECTOR2: {3851EditorPropertyVector2 *editor = memnew(EditorPropertyVector2(p_wide));38523853EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3854editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, hint.radians_as_degrees);3855return editor;38563857} break;3858case Variant::VECTOR2I: {3859EditorPropertyVector2i *editor = memnew(EditorPropertyVector2i(p_wide));3860EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);3861editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, true);3862return editor;38633864} break;3865case Variant::RECT2: {3866EditorPropertyRect2 *editor = memnew(EditorPropertyRect2(p_wide));3867EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3868editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);3869return editor;3870} break;3871case Variant::RECT2I: {3872EditorPropertyRect2i *editor = memnew(EditorPropertyRect2i(p_wide));3873EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);3874editor->setup(hint.min, hint.max, hint.suffix);38753876return editor;3877} break;3878case Variant::VECTOR3: {3879EditorPropertyVector3 *editor = memnew(EditorPropertyVector3(p_wide));3880EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3881editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, hint.radians_as_degrees);3882return editor;38833884} break;3885case Variant::VECTOR3I: {3886EditorPropertyVector3i *editor = memnew(EditorPropertyVector3i(p_wide));3887EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);3888editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, true);3889return editor;38903891} break;3892case Variant::VECTOR4: {3893EditorPropertyVector4 *editor = memnew(EditorPropertyVector4);3894EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3895editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, p_hint == PROPERTY_HINT_LINK, hint.suffix, hint.radians_as_degrees);3896return editor;38973898} break;3899case Variant::VECTOR4I: {3900EditorPropertyVector4i *editor = memnew(EditorPropertyVector4i);3901EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, 1, true);3902editor->setup(hint.min, hint.max, 1, false, p_hint == PROPERTY_HINT_LINK, hint.suffix, false, true);3903return editor;39043905} break;3906case Variant::TRANSFORM2D: {3907EditorPropertyTransform2D *editor = memnew(EditorPropertyTransform2D);3908EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3909editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);3910return editor;3911} break;3912case Variant::PLANE: {3913EditorPropertyPlane *editor = memnew(EditorPropertyPlane(p_wide));3914EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3915editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);3916return editor;3917} break;3918case Variant::QUATERNION: {3919EditorPropertyQuaternion *editor = memnew(EditorPropertyQuaternion);3920EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3921editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix, p_hint == PROPERTY_HINT_HIDE_QUATERNION_EDIT);3922return editor;3923} break;3924case Variant::AABB: {3925EditorPropertyAABB *editor = memnew(EditorPropertyAABB);3926EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3927editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);3928return editor;3929} break;3930case Variant::BASIS: {3931EditorPropertyBasis *editor = memnew(EditorPropertyBasis);3932EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3933editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);3934return editor;3935} break;3936case Variant::TRANSFORM3D: {3937EditorPropertyTransform3D *editor = memnew(EditorPropertyTransform3D);3938EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3939editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);3940return editor;39413942} break;3943case Variant::PROJECTION: {3944EditorPropertyProjection *editor = memnew(EditorPropertyProjection);3945EditorPropertyRangeHint hint = _parse_range_hint(p_hint, p_hint_text, default_float_step);3946editor->setup(hint.min, hint.max, hint.step, hint.hide_slider, hint.suffix);3947return editor;39483949} break;39503951// misc types3952case Variant::COLOR: {3953EditorPropertyColor *editor = memnew(EditorPropertyColor);3954editor->setup(p_hint != PROPERTY_HINT_COLOR_NO_ALPHA);3955return editor;3956} break;3957case Variant::STRING_NAME: {3958if (p_hint == PROPERTY_HINT_ENUM || p_hint == PROPERTY_HINT_ENUM_SUGGESTION) {3959EditorPropertyTextEnum *editor = memnew(EditorPropertyTextEnum);3960Vector<String> options = p_hint_text.split(",", false);3961editor->setup(options, true, (p_hint == PROPERTY_HINT_ENUM_SUGGESTION));3962return editor;3963} else if (p_hint == PROPERTY_HINT_INPUT_NAME) {3964return get_input_action_editor(p_hint_text, true);3965} else {3966EditorPropertyText *editor = memnew(EditorPropertyText);3967if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) {3968editor->set_placeholder(p_hint_text);3969} else if (p_hint == PROPERTY_HINT_PASSWORD) {3970editor->set_secret(true);3971editor->set_placeholder(p_hint_text);3972}3973editor->set_string_name(true);3974return editor;3975}3976} break;3977case Variant::NODE_PATH: {3978EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath);3979if (p_hint == PROPERTY_HINT_NODE_PATH_VALID_TYPES && !p_hint_text.is_empty()) {3980Vector<String> types = p_hint_text.split(",", false);3981Vector<StringName> sn = Variant(types); //convert via variant3982editor->setup(sn, (p_usage & PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT));3983}3984return editor;39853986} break;3987case Variant::RID: {3988EditorPropertyRID *editor = memnew(EditorPropertyRID);3989return editor;3990} break;3991case Variant::OBJECT: {3992if (p_hint == PROPERTY_HINT_NODE_TYPE) {3993EditorPropertyNodePath *editor = memnew(EditorPropertyNodePath);3994Vector<String> types = p_hint_text.split(",", false);3995Vector<StringName> sn = Variant(types); //convert via variant3996editor->setup(sn, false, true);3997return editor;3998} else {3999EditorPropertyResource *editor = memnew(EditorPropertyResource);4000editor->setup(p_object, p_path, p_hint == PROPERTY_HINT_RESOURCE_TYPE ? p_hint_text : "Resource");40014002if (p_hint == PROPERTY_HINT_RESOURCE_TYPE) {4003const PackedStringArray open_in_new_inspector = EDITOR_GET("interface/inspector/resources_to_open_in_new_inspector");40044005for (const String &type : open_in_new_inspector) {4006for (int j = 0; j < p_hint_text.get_slice_count(","); j++) {4007const String inherits = p_hint_text.get_slicec(',', j);4008if (ClassDB::is_parent_class(inherits, type)) {4009editor->set_use_sub_inspector(false);4010}4011}4012}4013}40144015return editor;4016}40174018} break;4019case Variant::CALLABLE: {4020EditorPropertyCallable *editor = memnew(EditorPropertyCallable);4021return editor;4022} break;4023case Variant::SIGNAL: {4024EditorPropertySignal *editor = memnew(EditorPropertySignal);4025return editor;4026} break;4027case Variant::DICTIONARY: {4028if (p_hint == PROPERTY_HINT_LOCALIZABLE_STRING) {4029EditorPropertyLocalizableString *editor = memnew(EditorPropertyLocalizableString);4030return editor;4031} else {4032EditorPropertyDictionary *editor = memnew(EditorPropertyDictionary);4033editor->setup(p_hint, p_hint_text);4034return editor;4035}4036} break;4037case Variant::ARRAY: {4038EditorPropertyArray *editor = memnew(EditorPropertyArray);4039editor->setup(Variant::ARRAY, p_hint_text);4040return editor;4041} break;4042case Variant::PACKED_BYTE_ARRAY: {4043EditorPropertyArray *editor = memnew(EditorPropertyArray);4044editor->setup(Variant::PACKED_BYTE_ARRAY, p_hint_text);4045return editor;4046} break;4047case Variant::PACKED_INT32_ARRAY: {4048EditorPropertyArray *editor = memnew(EditorPropertyArray);4049editor->setup(Variant::PACKED_INT32_ARRAY, p_hint_text);4050return editor;4051} break;4052case Variant::PACKED_INT64_ARRAY: {4053EditorPropertyArray *editor = memnew(EditorPropertyArray);4054editor->setup(Variant::PACKED_INT64_ARRAY, p_hint_text);4055return editor;4056} break;4057case Variant::PACKED_FLOAT32_ARRAY: {4058EditorPropertyArray *editor = memnew(EditorPropertyArray);4059editor->setup(Variant::PACKED_FLOAT32_ARRAY, p_hint_text);4060return editor;4061} break;4062case Variant::PACKED_FLOAT64_ARRAY: {4063EditorPropertyArray *editor = memnew(EditorPropertyArray);4064editor->setup(Variant::PACKED_FLOAT64_ARRAY, p_hint_text);4065return editor;4066} break;4067case Variant::PACKED_STRING_ARRAY: {4068EditorPropertyArray *editor = memnew(EditorPropertyArray);4069editor->setup(Variant::PACKED_STRING_ARRAY, p_hint_text);4070return editor;4071} break;4072case Variant::PACKED_VECTOR2_ARRAY: {4073EditorPropertyArray *editor = memnew(EditorPropertyArray);4074editor->setup(Variant::PACKED_VECTOR2_ARRAY, p_hint_text);4075return editor;4076} break;4077case Variant::PACKED_VECTOR3_ARRAY: {4078EditorPropertyArray *editor = memnew(EditorPropertyArray);4079editor->setup(Variant::PACKED_VECTOR3_ARRAY, p_hint_text);4080return editor;4081} break;4082case Variant::PACKED_COLOR_ARRAY: {4083EditorPropertyArray *editor = memnew(EditorPropertyArray);4084editor->setup(Variant::PACKED_COLOR_ARRAY, p_hint_text);4085return editor;4086} break;4087case Variant::PACKED_VECTOR4_ARRAY: {4088EditorPropertyArray *editor = memnew(EditorPropertyArray);4089editor->setup(Variant::PACKED_VECTOR4_ARRAY, p_hint_text);4090return editor;4091} break;4092default: {4093}4094}40954096return nullptr;4097}409840994100