diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp index 5273b61f374..ce2c1a5a164 100644 --- a/editor/connections_dialog.cpp +++ b/editor/connections_dialog.cpp @@ -34,6 +34,7 @@ #include "core/templates/hash_set.h" #include "editor/editor_help.h" #include "editor/editor_inspector.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -1191,7 +1192,7 @@ void ConnectionsDock::_go_to_method(TreeItem &p_item) { } if (scr.is_valid() && ScriptEditor::get_singleton()->script_goto_method(scr, cd.method)) { - EditorNode::get_singleton()->editor_select(EditorNode::EDITOR_SCRIPT); + EditorNode::get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); } } @@ -1199,7 +1200,7 @@ void ConnectionsDock::_handle_class_menu_option(int p_option) { switch (p_option) { case CLASS_MENU_OPEN_DOCS: ScriptEditor::get_singleton()->goto_help("class:" + class_menu_doc_class_name); - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); break; } } @@ -1229,7 +1230,7 @@ void ConnectionsDock::_handle_signal_menu_option(int p_option) { } break; case SIGNAL_MENU_OPEN_DOCS: { ScriptEditor::get_singleton()->goto_help("class_signal:" + String(meta["class"]) + ":" + String(meta["name"])); - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); } break; } } diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index fe758bf99b7..f5f7b8f51cf 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -39,6 +39,7 @@ #include "core/string/string_builder.h" #include "core/version_generated.gen.h" #include "editor/doc_data_compressed.gen.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_property_name_processor.h" @@ -2310,7 +2311,7 @@ void EditorHelp::_update_doc() { void EditorHelp::_request_help(const String &p_string) { Error err = _goto_desc(p_string); if (err == OK) { - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); } } @@ -3605,7 +3606,7 @@ void EditorHelpBit::_update_labels() { } void EditorHelpBit::_go_to_help(const String &p_what) { - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); ScriptEditor::get_singleton()->goto_help(p_what); emit_signal(SNAME("request_hide")); } diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp index 987c7f9c31d..11be765bc2b 100644 --- a/editor/editor_help_search.cpp +++ b/editor/editor_help_search.cpp @@ -32,6 +32,7 @@ #include "core/os/keyboard.h" #include "editor/editor_feature_profile.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" #include "editor/editor_string_names.h" @@ -201,7 +202,7 @@ void EditorHelpSearch::_confirmed() { } // Activate the script editor and emit the signal with the documentation link to display. - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); emit_signal(SNAME("go_to_help"), item->get_metadata(0)); diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index a1cae374aa8..4a2c41bc9c2 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -34,6 +34,7 @@ #include "core/os/keyboard.h" #include "editor/doc_tools.h" #include "editor/editor_feature_profile.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_property_name_processor.h" #include "editor/editor_settings.h" @@ -1037,7 +1038,7 @@ void EditorProperty::menu_option(int p_option) { } break; case MENU_OPEN_DOCUMENTATION: { ScriptEditor::get_singleton()->goto_help(doc_path); - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); } break; } } @@ -1297,7 +1298,7 @@ void EditorInspectorCategory::_handle_menu_option(int p_option) { switch (p_option) { case MENU_OPEN_DOCS: ScriptEditor::get_singleton()->goto_help("class:" + doc_class_name); - EditorNode::get_singleton()->set_visible_editor(EditorNode::EDITOR_SCRIPT); + EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_SCRIPT); break; } } diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp index 86b66ef410e..fa6198f6950 100644 --- a/editor/editor_interface.cpp +++ b/editor/editor_interface.cpp @@ -33,6 +33,7 @@ #include "editor/editor_command_palette.h" #include "editor/editor_feature_profile.h" +#include "editor/editor_main_screen.h" #include "editor/editor_node.h" #include "editor/editor_paths.h" #include "editor/editor_resource_preview.h" @@ -215,7 +216,7 @@ Control *EditorInterface::get_base_control() const { } VBoxContainer *EditorInterface::get_editor_main_screen() const { - return EditorNode::get_singleton()->get_main_screen_control(); + return EditorNode::get_singleton()->get_editor_main_screen()->get_control(); } ScriptEditor *EditorInterface::get_script_editor() const { @@ -232,7 +233,7 @@ SubViewport *EditorInterface::get_editor_viewport_3d(int p_idx) const { } void EditorInterface::set_main_screen_editor(const String &p_name) { - EditorNode::get_singleton()->select_editor_by_name(p_name); + EditorNode::get_singleton()->get_editor_main_screen()->select_by_name(p_name); } void EditorInterface::set_distraction_free_mode(bool p_enter) { diff --git a/editor/editor_main_screen.cpp b/editor/editor_main_screen.cpp new file mode 100644 index 00000000000..77bbee5a7ff --- /dev/null +++ b/editor/editor_main_screen.cpp @@ -0,0 +1,291 @@ +/**************************************************************************/ +/* editor_main_screen.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "editor_main_screen.h" + +#include "core/io/config_file.h" +#include "editor/editor_node.h" +#include "editor/editor_settings.h" +#include "editor/editor_string_names.h" +#include "editor/plugins/editor_plugin.h" +#include "scene/gui/box_container.h" +#include "scene/gui/button.h" + +void EditorMainScreen::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_READY: { + if (EDITOR_3D < buttons.size() && buttons[EDITOR_3D]->is_visible()) { + // If the 3D editor is enabled, use this as the default. + select(EDITOR_3D); + return; + } + + // Switch to the first main screen plugin that is enabled. Usually this is + // 2D, but may be subsequent ones if 2D is disabled in the feature profile. + for (int i = 0; i < buttons.size(); i++) { + Button *editor_button = buttons[i]; + if (editor_button->is_visible()) { + select(i); + return; + } + } + + select(-1); + } break; + case NOTIFICATION_THEME_CHANGED: { + for (int i = 0; i < buttons.size(); i++) { + Button *tb = buttons[i]; + EditorPlugin *p_editor = editor_table[i]; + Ref icon = p_editor->get_icon(); + + if (icon.is_valid()) { + tb->set_icon(icon); + } else if (has_theme_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { + tb->set_icon(get_theme_icon(p_editor->get_name(), EditorStringName(EditorIcons))); + } + } + } break; + } +} + +void EditorMainScreen::set_button_container(HBoxContainer *p_button_hb) { + button_hb = p_button_hb; +} + +void EditorMainScreen::save_layout_to_config(Ref p_config_file, const String &p_section) const { + int selected_main_editor_idx = -1; + for (int i = 0; i < buttons.size(); i++) { + if (buttons[i]->is_pressed()) { + selected_main_editor_idx = i; + break; + } + } + if (selected_main_editor_idx != -1) { + p_config_file->set_value(p_section, "selected_main_editor_idx", selected_main_editor_idx); + } else { + p_config_file->set_value(p_section, "selected_main_editor_idx", Variant()); + } +} + +void EditorMainScreen::load_layout_from_config(Ref p_config_file, const String &p_section) { + int selected_main_editor_idx = p_config_file->get_value(p_section, "selected_main_editor_idx", -1); + if (selected_main_editor_idx >= 0 && selected_main_editor_idx < buttons.size()) { + callable_mp(this, &EditorMainScreen::select).call_deferred(selected_main_editor_idx); + } +} + +void EditorMainScreen::set_button_enabled(int p_index, bool p_enabled) { + ERR_FAIL_INDEX(p_index, buttons.size()); + buttons[p_index]->set_visible(p_enabled); + if (!p_enabled && buttons[p_index]->is_pressed()) { + select(EDITOR_2D); + } +} + +bool EditorMainScreen::is_button_enabled(int p_index) const { + ERR_FAIL_INDEX_V(p_index, buttons.size(), false); + return buttons[p_index]->is_visible(); +} + +int EditorMainScreen::_get_current_main_editor() const { + for (int i = 0; i < editor_table.size(); i++) { + if (editor_table[i] == selected_plugin) { + return i; + } + } + + return 0; +} + +void EditorMainScreen::select_next() { + int editor = _get_current_main_editor(); + + do { + if (editor == editor_table.size() - 1) { + editor = 0; + } else { + editor++; + } + } while (!buttons[editor]->is_visible()); + + select(editor); +} + +void EditorMainScreen::select_prev() { + int editor = _get_current_main_editor(); + + do { + if (editor == 0) { + editor = editor_table.size() - 1; + } else { + editor--; + } + } while (!buttons[editor]->is_visible()); + + select(editor); +} + +void EditorMainScreen::select_by_name(const String &p_name) { + ERR_FAIL_COND(p_name.is_empty()); + + for (int i = 0; i < buttons.size(); i++) { + if (buttons[i]->get_text() == p_name) { + select(i); + return; + } + } + + ERR_FAIL_MSG("The editor name '" + p_name + "' was not found."); +} + +void EditorMainScreen::select(int p_index) { + if (EditorNode::get_singleton()->is_changing_scene()) { + return; + } + + ERR_FAIL_INDEX(p_index, editor_table.size()); + + if (!buttons[p_index]->is_visible()) { // Button hidden, no editor. + return; + } + + for (int i = 0; i < buttons.size(); i++) { + buttons[i]->set_pressed_no_signal(i == p_index); + } + + EditorPlugin *new_editor = editor_table[p_index]; + ERR_FAIL_NULL(new_editor); + + if (selected_plugin == new_editor) { + return; + } + + if (selected_plugin) { + selected_plugin->make_visible(false); + } + + selected_plugin = new_editor; + selected_plugin->make_visible(true); + selected_plugin->selected_notify(); + + EditorData &editor_data = EditorNode::get_editor_data(); + int plugin_count = editor_data.get_editor_plugin_count(); + for (int i = 0; i < plugin_count; i++) { + editor_data.get_editor_plugin(i)->notify_main_screen_changed(selected_plugin->get_name()); + } + + EditorNode::get_singleton()->update_distraction_free_mode(); +} + +int EditorMainScreen::get_selected_index() const { + for (int i = 0; i < editor_table.size(); i++) { + if (selected_plugin == editor_table[i]) { + return i; + } + } + return -1; +} + +int EditorMainScreen::get_plugin_index(EditorPlugin *p_editor) const { + int screen = -1; + for (int i = 0; i < editor_table.size(); i++) { + if (p_editor == editor_table[i]) { + screen = i; + break; + } + } + return screen; +} + +EditorPlugin *EditorMainScreen::get_selected_plugin() const { + return selected_plugin; +} + +VBoxContainer *EditorMainScreen::get_control() const { + return main_screen_vbox; +} + +void EditorMainScreen::add_main_plugin(EditorPlugin *p_editor) { + Button *tb = memnew(Button); + tb->set_toggle_mode(true); + tb->set_theme_type_variation("MainScreenButton"); + tb->set_name(p_editor->get_name()); + tb->set_text(p_editor->get_name()); + + Ref icon = p_editor->get_icon(); + if (icon.is_null() && has_theme_icon(p_editor->get_name(), EditorStringName(EditorIcons))) { + icon = get_editor_theme_icon(p_editor->get_name()); + } + if (icon.is_valid()) { + tb->set_icon(icon); + // Make sure the control is updated if the icon is reimported. + icon->connect_changed(callable_mp((Control *)tb, &Control::update_minimum_size)); + } + + tb->connect(SceneStringName(pressed), callable_mp(this, &EditorMainScreen::select).bind(buttons.size())); + + buttons.push_back(tb); + button_hb->add_child(tb); + editor_table.push_back(p_editor); +} + +void EditorMainScreen::remove_main_plugin(EditorPlugin *p_editor) { + // Remove the main editor button and update the bindings of + // all buttons behind it to point to the correct main window. + for (int i = buttons.size() - 1; i >= 0; i--) { + if (p_editor->get_name() == buttons[i]->get_text()) { + if (buttons[i]->is_pressed()) { + select(EDITOR_SCRIPT); + } + + memdelete(buttons[i]); + buttons.remove_at(i); + + break; + } else { + buttons[i]->disconnect(SceneStringName(pressed), callable_mp(this, &EditorMainScreen::select)); + buttons[i]->connect(SceneStringName(pressed), callable_mp(this, &EditorMainScreen::select).bind(i - 1)); + } + } + + if (selected_plugin == p_editor) { + selected_plugin = nullptr; + } + + editor_table.erase(p_editor); +} + +EditorMainScreen::EditorMainScreen() { + main_screen_vbox = memnew(VBoxContainer); + main_screen_vbox->set_name("MainScreen"); + main_screen_vbox->set_v_size_flags(Control::SIZE_EXPAND_FILL); + main_screen_vbox->add_theme_constant_override("separation", 0); + add_child(main_screen_vbox); +} diff --git a/editor/editor_main_screen.h b/editor/editor_main_screen.h new file mode 100644 index 00000000000..153a182bc21 --- /dev/null +++ b/editor/editor_main_screen.h @@ -0,0 +1,91 @@ +/**************************************************************************/ +/* editor_main_screen.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef EDITOR_MAIN_SCREEN_H +#define EDITOR_MAIN_SCREEN_H + +#include "scene/gui/panel_container.h" + +class Button; +class ConfigFile; +class EditorPlugin; +class HBoxContainer; +class VBoxContainer; + +class EditorMainScreen : public PanelContainer { + GDCLASS(EditorMainScreen, PanelContainer); + +public: + enum EditorTable { + EDITOR_2D = 0, + EDITOR_3D, + EDITOR_SCRIPT, + EDITOR_ASSETLIB, + }; + +private: + VBoxContainer *main_screen_vbox = nullptr; + EditorPlugin *selected_plugin = nullptr; + + HBoxContainer *button_hb = nullptr; + Vector