Path: blob/master/editor/inspector/editor_sectioned_inspector.cpp
9896 views
/**************************************************************************/1/* editor_sectioned_inspector.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_sectioned_inspector.h"3132#include "editor/editor_string_names.h"33#include "editor/inspector/editor_inspector.h"34#include "editor/inspector/editor_property_name_processor.h"35#include "editor/themes/editor_scale.h"36#include "scene/gui/check_button.h"37#include "scene/gui/line_edit.h"38#include "scene/gui/tree.h"3940static bool _property_path_matches(const String &p_property_path, const String &p_filter, EditorPropertyNameProcessor::Style p_style) {41if (p_property_path.containsn(p_filter)) {42return true;43}4445const Vector<String> sections = p_property_path.split("/");46for (int i = 0; i < sections.size(); i++) {47if (p_filter.is_subsequence_ofn(EditorPropertyNameProcessor::get_singleton()->process_name(sections[i], p_style, p_property_path))) {48return true;49}50}51return false;52}5354class SectionedInspectorFilter : public Object {55GDCLASS(SectionedInspectorFilter, Object);5657Object *edited = nullptr;58String section;59bool allow_sub = false;6061bool _set(const StringName &p_name, const Variant &p_value) {62if (!edited) {63return false;64}6566String name = p_name;67if (!section.is_empty()) {68name = section + "/" + name;69}7071bool valid;72edited->set(name, p_value, &valid);73return valid;74}7576bool _get(const StringName &p_name, Variant &r_ret) const {77if (!edited) {78return false;79}8081String name = p_name;82if (!section.is_empty()) {83name = section + "/" + name;84}8586bool valid = false;8788r_ret = edited->get(name, &valid);89return valid;90}91void _get_property_list(List<PropertyInfo> *p_list) const {92if (!edited) {93return;94}9596List<PropertyInfo> pinfo;97edited->get_property_list(&pinfo);98for (PropertyInfo &pi : pinfo) {99int sp = pi.name.find_char('/');100101if (pi.name == "resource_path" || pi.name == "resource_name" || pi.name == "resource_local_to_scene" || pi.name.begins_with("script/") || pi.name.begins_with("_global_script")) { //skip resource stuff102continue;103}104105if (sp == -1) {106pi.name = "global/" + pi.name;107}108109if (pi.name.begins_with(section + "/")) {110pi.name = pi.name.replace_first(section + "/", "");111if (!allow_sub && pi.name.contains_char('/')) {112continue;113}114p_list->push_back(pi);115}116}117}118119bool _property_can_revert(const StringName &p_name) const {120return edited->property_can_revert(section + "/" + p_name);121}122123bool _property_get_revert(const StringName &p_name, Variant &r_property) const {124r_property = edited->property_get_revert(section + "/" + p_name);125return true;126}127128public:129void set_section(const String &p_section, bool p_allow_sub) {130section = p_section;131allow_sub = p_allow_sub;132notify_property_list_changed();133}134135void set_edited(Object *p_edited) {136edited = p_edited;137notify_property_list_changed();138}139};140141void SectionedInspector::_bind_methods() {142ClassDB::bind_method("update_category_list", &SectionedInspector::update_category_list);143ADD_SIGNAL(MethodInfo("category_changed", PropertyInfo(Variant::STRING, "new_category")));144}145146void SectionedInspector::_section_selected() {147if (!sections->get_selected()) {148return;149}150151selected_category = sections->get_selected()->get_metadata(0);152filter->set_section(selected_category, sections->get_selected()->get_first_child() == nullptr);153inspector->set_property_prefix(selected_category + "/");154inspector->set_scroll_offset(0);155emit_signal(SNAME("category_changed"), selected_category);156}157158void SectionedInspector::set_current_section(const String &p_section) {159if (section_map.has(p_section)) {160TreeItem *item = section_map[p_section];161item->select(0);162sections->scroll_to_item(item);163}164}165166String SectionedInspector::get_current_section() const {167if (sections->get_selected()) {168return sections->get_selected()->get_metadata(0);169} else {170return "";171}172}173174String SectionedInspector::get_full_item_path(const String &p_item) {175String base = get_current_section();176177if (!base.is_empty()) {178return base + "/" + p_item;179} else {180return p_item;181}182}183184void SectionedInspector::edit(Object *p_object) {185if (!p_object) {186obj = ObjectID();187sections->clear();188189filter->set_edited(nullptr);190inspector->edit(nullptr);191192return;193}194195ObjectID id = p_object->get_instance_id();196197inspector->set_object_class(p_object->get_class());198199if (obj != id) {200obj = id;201update_category_list();202203filter->set_edited(p_object);204inspector->edit(filter);205206TreeItem *first_item = sections->get_root();207if (first_item) {208while (first_item->get_first_child()) {209first_item = first_item->get_first_child();210}211212first_item->select(0);213selected_category = first_item->get_metadata(0);214}215} else {216update_category_list();217}218}219220void SectionedInspector::update_category_list() {221sections->clear();222223Object *o = ObjectDB::get_instance(obj);224225if (!o) {226return;227}228229List<PropertyInfo> pinfo;230o->get_property_list(&pinfo);231232section_map.clear();233234TreeItem *root = sections->create_item();235section_map[""] = root;236237String filter_text;238if (search_box) {239filter_text = search_box->get_text();240}241242const EditorPropertyNameProcessor::Style name_style = EditorPropertyNameProcessor::get_settings_style();243const EditorPropertyNameProcessor::Style tooltip_style = EditorPropertyNameProcessor::get_tooltip_style(name_style);244245for (PropertyInfo &pi : pinfo) {246if (pi.usage & PROPERTY_USAGE_CATEGORY) {247continue;248} else if (!(pi.usage & PROPERTY_USAGE_EDITOR) ||249(filter_text.is_empty() && restrict_to_basic && !(pi.usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING))) {250continue;251}252253if (pi.name.contains_char(':') || pi.name == "script" || pi.name == "resource_name" || pi.name == "resource_path" || pi.name == "resource_local_to_scene" || pi.name.begins_with("_global_script")) {254continue;255}256257if (!filter_text.is_empty() && !_property_path_matches(pi.name, filter_text, name_style)) {258continue;259}260261int sp = pi.name.find_char('/');262if (sp == -1) {263pi.name = "global/" + pi.name;264}265266Vector<String> sectionarr = pi.name.split("/");267String metasection;268269int sc = MIN(2, sectionarr.size() - 1);270271for (int i = 0; i < sc; i++) {272TreeItem *parent = section_map[metasection];273//parent->set_custom_bg_color(0, get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor)));274parent->set_custom_font(0, get_theme_font(SNAME("bold"), EditorStringName(EditorFonts)));275276if (i > 0) {277metasection += "/" + sectionarr[i];278} else {279metasection = sectionarr[i];280}281282if (!section_map.has(metasection)) {283TreeItem *ms = sections->create_item(parent);284section_map[metasection] = ms;285286const String text = EditorPropertyNameProcessor::get_singleton()->process_name(sectionarr[i], name_style, pi.name);287const String tooltip = EditorPropertyNameProcessor::get_singleton()->process_name(sectionarr[i], tooltip_style, pi.name);288289ms->set_text(0, text);290ms->set_tooltip_text(0, tooltip);291ms->set_metadata(0, metasection);292ms->set_selectable(0, false);293}294295if (i == sc - 1) {296//if it has children, make selectable297section_map[metasection]->set_selectable(0, true);298}299}300}301302if (section_map.has(selected_category)) {303section_map[selected_category]->select(0);304}305306inspector->update_tree();307}308309void SectionedInspector::register_search_box(LineEdit *p_box) {310search_box = p_box;311inspector->register_text_enter(p_box);312search_box->connect(SceneStringName(text_changed), callable_mp(this, &SectionedInspector::_search_changed));313}314315void SectionedInspector::register_advanced_toggle(CheckButton *p_toggle) {316advanced_toggle = p_toggle;317advanced_toggle->connect(SceneStringName(toggled), callable_mp(this, &SectionedInspector::_advanced_toggled));318_advanced_toggled(advanced_toggle->is_pressed());319}320321void SectionedInspector::_search_changed(const String &p_what) {322if (advanced_toggle) {323if (p_what.is_empty()) {324advanced_toggle->set_pressed_no_signal(!restrict_to_basic);325advanced_toggle->set_disabled(false);326advanced_toggle->set_tooltip_text(String());327} else {328advanced_toggle->set_pressed_no_signal(true);329advanced_toggle->set_disabled(true);330advanced_toggle->set_tooltip_text(TTRC("Advanced settings are always shown when searching."));331}332}333update_category_list();334}335336void SectionedInspector::_advanced_toggled(bool p_toggled_on) {337restrict_to_basic = !p_toggled_on;338update_category_list();339inspector->set_restrict_to_basic_settings(restrict_to_basic);340}341342void SectionedInspector::_notification(int p_notification) {343if (p_notification == NOTIFICATION_TRANSLATION_CHANGED) {344if (sections->get_root()) {345// Only update when initialized.346callable_mp(this, &SectionedInspector::update_category_list).call_deferred();347}348}349}350351EditorInspector *SectionedInspector::get_inspector() {352return inspector;353}354355SectionedInspector::SectionedInspector() :356sections(memnew(Tree)),357filter(memnew(SectionedInspectorFilter)),358inspector(memnew(EditorInspector)) {359add_theme_constant_override("autohide", 1); // Fixes the dragger always showing up360361VBoxContainer *left_vb = memnew(VBoxContainer);362left_vb->set_custom_minimum_size(Size2(190, 0) * EDSCALE);363add_child(left_vb);364365sections->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);366sections->set_v_size_flags(SIZE_EXPAND_FILL);367sections->set_hide_root(true);368sections->set_theme_type_variation("TreeSecondary");369370left_vb->add_child(sections, true);371372VBoxContainer *right_vb = memnew(VBoxContainer);373right_vb->set_custom_minimum_size(Size2(300, 0) * EDSCALE);374right_vb->set_h_size_flags(SIZE_EXPAND_FILL);375add_child(right_vb);376377inspector->set_v_size_flags(SIZE_EXPAND_FILL);378right_vb->add_child(inspector, true);379inspector->set_use_doc_hints(true);380381sections->connect("cell_selected", callable_mp(this, &SectionedInspector::_section_selected));382}383384SectionedInspector::~SectionedInspector() {385memdelete(filter);386}387388389