Browse Source

Extract BottomPanel from EditorNode

kit 1 year ago
parent
commit
eb6ca91ba6

+ 4 - 3
editor/debugger/editor_debugger_node.cpp

@@ -38,6 +38,7 @@
 #include "editor/editor_settings.h"
 #include "editor/editor_string_names.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_run_bar.h"
 #include "editor/inspector_dock.h"
 #include "editor/plugins/editor_debugger_plugin.h"
@@ -268,9 +269,9 @@ Error EditorDebuggerNode::start(const String &p_uri) {
 	stop(true);
 	current_uri = p_uri;
 	if (EDITOR_GET("run/output/always_open_output_on_play")) {
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(EditorNode::get_log());
+		EditorNode::get_bottom_panel()->make_item_visible(EditorNode::get_log());
 	} else {
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+		EditorNode::get_bottom_panel()->make_item_visible(this);
 	}
 	server = Ref<EditorDebuggerServer>(EditorDebuggerServer::create(p_uri.substr(0, p_uri.find("://") + 3)));
 	const Error err = server->start(p_uri);
@@ -502,7 +503,7 @@ void EditorDebuggerNode::_break_state_changed() {
 	const bool breaked = get_current_debugger()->is_breaked();
 	const bool can_debug = get_current_debugger()->is_debuggable();
 	if (breaked) { // Show debugger.
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+		EditorNode::get_bottom_panel()->make_item_visible(this);
 	}
 
 	// Update script menu.

+ 3 - 2
editor/editor_audio_buses.cpp

@@ -39,6 +39,7 @@
 #include "editor/editor_string_names.h"
 #include "editor/editor_undo_redo_manager.h"
 #include "editor/filesystem_dock.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/themes/editor_scale.h"
 #include "editor/themes/editor_theme_manager.h"
@@ -1040,7 +1041,7 @@ void EditorAudioBuses::_rebuild_buses() {
 
 EditorAudioBuses *EditorAudioBuses::register_editor() {
 	EditorAudioBuses *audio_buses = memnew(EditorAudioBuses);
-	EditorNode::get_singleton()->add_bottom_panel_item(TTR("Audio"), audio_buses);
+	EditorNode::get_bottom_panel()->add_item(TTR("Audio"), audio_buses);
 	return audio_buses;
 }
 
@@ -1357,7 +1358,7 @@ EditorAudioBuses::EditorAudioBuses() {
 }
 
 void EditorAudioBuses::open_layout(const String &p_path) {
-	EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+	EditorNode::get_bottom_panel()->make_item_visible(this);
 
 	Ref<AudioBusLayout> state = ResourceLoader::load(p_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE);
 	if (state.is_null()) {

+ 6 - 5
editor/editor_dock_manager.cpp

@@ -42,6 +42,7 @@
 #include "editor/editor_settings.h"
 #include "editor/editor_string_names.h"
 #include "editor/filesystem_dock.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/themes/editor_scale.h"
 #include "editor/window_wrapper.h"
 
@@ -196,7 +197,7 @@ void EditorDockManager::_dock_select_input(const Ref<InputEvent> &p_input) {
 
 		if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
 			if (dock_bottom_selected_idx != -1) {
-				EditorNode::get_singleton()->remove_bottom_panel_item(bottom_docks[dock_bottom_selected_idx]);
+				EditorNode::get_bottom_panel()->remove_item(bottom_docks[dock_bottom_selected_idx]);
 
 				bottom_docks[dock_bottom_selected_idx]->call("_set_dock_horizontal", false);
 
@@ -391,13 +392,13 @@ void EditorDockManager::_dock_move_selected_to_bottom() {
 	dock->call("_set_dock_horizontal", true);
 
 	bottom_docks.push_back(dock);
-	EditorNode::get_singleton()->add_bottom_panel_item(dock->get_name(), dock, true);
+	EditorNode::get_bottom_panel()->add_item(dock->get_name(), dock, true);
 	dock_select_popup->hide();
 	update_dock_slots_visibility(true);
 	_edit_current();
 	emit_signal(SNAME("layout_changed"));
 
-	EditorNode::get_singleton()->make_bottom_panel_item_visible(dock);
+	EditorNode::get_bottom_panel()->make_item_visible(dock);
 }
 
 void EditorDockManager::_dock_make_float(Control *p_dock, int p_slot_index, bool p_show_window) {
@@ -601,7 +602,7 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
 				dock_slot[atidx]->set_block_signals(false);
 			} else if (bottom_idx != -1) {
 				bottom_docks.erase(node);
-				EditorNode::get_singleton()->remove_bottom_panel_item(node);
+				EditorNode::get_bottom_panel()->remove_item(node);
 				dock_slot[i]->add_child(node);
 				node->call("_set_dock_horizontal", false);
 			}
@@ -662,7 +663,7 @@ void EditorDockManager::load_docks_from_config(Ref<ConfigFile> p_layout, const S
 			node->call("_set_dock_horizontal", true);
 
 			bottom_docks.push_back(node);
-			EditorNode::get_singleton()->add_bottom_panel_item(node->get_name(), node, true);
+			EditorNode::get_bottom_panel()->add_item(node->get_name(), node, true);
 		}
 	}
 

+ 8 - 234
editor/editor_node.cpp

@@ -106,6 +106,7 @@
 #include "editor/export/project_export.h"
 #include "editor/fbx_importer_manager.h"
 #include "editor/filesystem_dock.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/gui/editor_run_bar.h"
 #include "editor/gui/editor_scene_tabs.h"
@@ -166,9 +167,6 @@
 
 EditorNode *EditorNode::singleton = nullptr;
 
-// The metadata key used to store and retrieve the version text to copy to the clipboard.
-static const String META_TEXT_TO_COPY = "text_to_copy";
-
 static const String EDITOR_NODE_CONFIG_SECTION = "EditorNode";
 
 static const String REMOVE_ANDROID_BUILD_TEMPLATE_MESSAGE = "The Android build template is already installed in this project and it won't be overwritten.\nRemove the \"%s\" directory manually before attempting this operation again.";
@@ -518,7 +516,6 @@ void EditorNode::_update_theme(bool p_skip_creation) {
 		main_menu->add_theme_style_override("pressed", theme->get_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
 		distraction_free->set_icon(theme->get_icon(SNAME("DistractionFree"), EditorStringName(EditorIcons)));
 		distraction_free->add_theme_style_override("pressed", theme->get_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
-		bottom_panel_raise->set_icon(theme->get_icon(SNAME("ExpandBottomDock"), EditorStringName(EditorIcons)));
 
 		help_menu->set_item_icon(help_menu->get_item_index(HELP_SEARCH), theme->get_icon(SNAME("HelpSearch"), EditorStringName(EditorIcons)));
 		help_menu->set_item_icon(help_menu->get_item_index(HELP_COPY_SYSTEM_INFO), theme->get_icon(SNAME("ActionCopy"), EditorStringName(EditorIcons)));
@@ -529,11 +526,6 @@ void EditorNode::_update_theme(bool p_skip_creation) {
 			bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles)));
 		}
 
-		for (int i = 0; i < bottom_panel_items.size(); i++) {
-			bottom_panel_items.write[i].button->add_theme_style_override("pressed", theme->get_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
-			bottom_panel_items.write[i].button->add_theme_style_override("hover_pressed", theme->get_stylebox(SNAME("MenuHover"), EditorStringName(EditorStyles)));
-		}
-
 		for (int i = 0; i < main_editor_buttons.size(); i++) {
 			Button *tb = main_editor_buttons[i];
 			EditorPlugin *p_editor = editor_table[i];
@@ -623,8 +615,6 @@ void EditorNode::_notification(int p_what) {
 
 			ResourceImporterTexture::get_singleton()->update_imports();
 
-			bottom_panel_updating = false;
-
 			if (requested_first_scan) {
 				requested_first_scan = false;
 
@@ -1169,10 +1159,6 @@ void EditorNode::_titlebar_resized() {
 	}
 }
 
-void EditorNode::_version_button_pressed() {
-	DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY));
-}
-
 void EditorNode::_update_undo_redo_allowed() {
 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
 	file_menu->set_item_disabled(file_menu->get_item_index(EDIT_UNDO), !undo_redo->has_undo());
@@ -4301,7 +4287,7 @@ void EditorNode::_project_run_started() {
 	}
 
 	if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) {
-		make_bottom_panel_item_visible(log);
+		bottom_panel->make_item_visible(log);
 	}
 }
 
@@ -4310,12 +4296,7 @@ void EditorNode::_project_run_stopped() {
 		return;
 	}
 
-	for (int i = 0; i < bottom_panel_items.size(); i++) {
-		if (bottom_panel_items[i].control == log) {
-			_bottom_panel_switch(false, i);
-			break;
-		}
-	}
+	bottom_panel->make_item_visible(log, false);
 }
 
 void EditorNode::notify_all_debug_sessions_exited() {
@@ -4897,18 +4878,7 @@ void EditorNode::_save_central_editor_layout_to_config(Ref<ConfigFile> p_config_
 	int center_split_offset = center_split->get_split_offset();
 	p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "center_split_offset", center_split_offset);
 
-	int selected_bottom_panel_item_idx = -1;
-	for (int i = 0; i < bottom_panel_items.size(); i++) {
-		if (bottom_panel_items[i].button->is_pressed()) {
-			selected_bottom_panel_item_idx = i;
-			break;
-		}
-	}
-	if (selected_bottom_panel_item_idx != -1) {
-		p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item", selected_bottom_panel_item_idx);
-	} else {
-		p_config_file->set_value(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item", Variant());
-	}
+	bottom_panel->save_layout_to_config(p_config_file, EDITOR_NODE_CONFIG_SECTION);
 
 	// Debugger tab.
 
@@ -4934,27 +4904,11 @@ void EditorNode::_save_central_editor_layout_to_config(Ref<ConfigFile> p_config_
 void EditorNode::_load_central_editor_layout_from_config(Ref<ConfigFile> p_config_file) {
 	// Bottom panel.
 
-	bool has_active_tab = false;
-	if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item")) {
-		int selected_bottom_panel_item_idx = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "selected_bottom_panel_item");
-		if (selected_bottom_panel_item_idx >= 0 && selected_bottom_panel_item_idx < bottom_panel_items.size()) {
-			// Make sure we don't try to open contextual editors which are not enabled in the current context.
-			if (bottom_panel_items[selected_bottom_panel_item_idx].button->is_visible()) {
-				_bottom_panel_switch(true, selected_bottom_panel_item_idx);
-				has_active_tab = true;
-			}
-		}
-	}
+	bottom_panel->load_layout_from_config(p_config_file, EDITOR_NODE_CONFIG_SECTION);
 
 	if (p_config_file->has_section_key(EDITOR_NODE_CONFIG_SECTION, "center_split_offset")) {
 		int center_split_offset = p_config_file->get_value(EDITOR_NODE_CONFIG_SECTION, "center_split_offset");
 		center_split->set_split_offset(center_split_offset);
-
-		// If there is no active tab we need to collapse the panel.
-		if (!has_active_tab) {
-			bottom_panel_items[0].control->show(); // _bottom_panel_switch() can collapse only visible tabs.
-			_bottom_panel_switch(false, 0);
-		}
 	}
 
 	// Debugger tab.
@@ -5244,125 +5198,6 @@ void EditorNode::_scene_tab_closed(int p_tab) {
 	scene_tabs->update_scene_tabs();
 }
 
-Button *EditorNode::add_bottom_panel_item(String p_text, Control *p_item, bool p_at_front) {
-	Button *tb = memnew(Button);
-	tb->set_theme_type_variation("FlatMenuButton");
-	tb->connect("toggled", callable_mp(this, &EditorNode::_bottom_panel_switch_by_control).bind(p_item));
-	tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorNode::_bottom_panel_drag_hover).bind(tb, p_item), Callable());
-	tb->set_text(p_text);
-	tb->set_toggle_mode(true);
-	tb->set_focus_mode(Control::FOCUS_NONE);
-	bottom_panel_vb->add_child(p_item);
-	bottom_panel_hb->move_to_front();
-	bottom_panel_hb_editors->add_child(tb);
-	if (p_at_front) {
-		bottom_panel_hb_editors->move_child(tb, 0);
-	}
-	p_item->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-	p_item->hide();
-	BottomPanelItem bpi;
-	bpi.button = tb;
-	bpi.control = p_item;
-	bpi.name = p_text;
-	bottom_panel_items.push_back(bpi);
-
-	return tb;
-}
-
-void EditorNode::hide_bottom_panel() {
-	for (int i = 0; i < bottom_panel_items.size(); i++) {
-		if (bottom_panel_items[i].control->is_visible()) {
-			_bottom_panel_switch(false, i);
-			break;
-		}
-	}
-}
-
-void EditorNode::make_bottom_panel_item_visible(Control *p_item) {
-	for (int i = 0; i < bottom_panel_items.size(); i++) {
-		if (bottom_panel_items[i].control == p_item) {
-			_bottom_panel_switch(true, i);
-			break;
-		}
-	}
-}
-
-void EditorNode::raise_bottom_panel_item(Control *p_item) {
-	for (int i = 0; i < bottom_panel_items.size(); i++) {
-		if (bottom_panel_items[i].control == p_item) {
-			bottom_panel_items[i].button->move_to_front();
-			SWAP(bottom_panel_items.write[i], bottom_panel_items.write[bottom_panel_items.size() - 1]);
-			break;
-		}
-	}
-}
-
-void EditorNode::remove_bottom_panel_item(Control *p_item) {
-	for (int i = 0; i < bottom_panel_items.size(); i++) {
-		if (bottom_panel_items[i].control == p_item) {
-			if (p_item->is_visible_in_tree()) {
-				_bottom_panel_switch(false, i);
-			}
-			bottom_panel_vb->remove_child(bottom_panel_items[i].control);
-			bottom_panel_hb_editors->remove_child(bottom_panel_items[i].button);
-			memdelete(bottom_panel_items[i].button);
-			bottom_panel_items.remove_at(i);
-			break;
-		}
-	}
-}
-
-void EditorNode::_bottom_panel_switch_by_control(bool p_enable, Control *p_control) {
-	for (int i = 0; i < bottom_panel_items.size(); i++) {
-		if (bottom_panel_items[i].control == p_control) {
-			_bottom_panel_switch(p_enable, i);
-			return;
-		}
-	}
-}
-
-void EditorNode::_bottom_panel_switch(bool p_enable, int p_idx) {
-	if (bottom_panel_updating) {
-		return;
-	}
-	ERR_FAIL_INDEX(p_idx, bottom_panel_items.size());
-
-	if (bottom_panel_items[p_idx].control->is_visible() == p_enable) {
-		return;
-	}
-
-	if (p_enable) {
-		bottom_panel_updating = true;
-
-		for (int i = 0; i < bottom_panel_items.size(); i++) {
-			bottom_panel_items[i].button->set_pressed(i == p_idx);
-			bottom_panel_items[i].control->set_visible(i == p_idx);
-		}
-		if (EditorDebuggerNode::get_singleton() == bottom_panel_items[p_idx].control) {
-			// This is the debug panel which uses tabs, so the top section should be smaller.
-			bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles)));
-		} else {
-			bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
-		}
-		center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
-		center_split->set_collapsed(false);
-		if (bottom_panel_raise->is_pressed()) {
-			top_split->hide();
-		}
-		bottom_panel_raise->show();
-	} else {
-		bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
-		bottom_panel_items[p_idx].button->set_pressed(false);
-		bottom_panel_items[p_idx].control->set_visible(false);
-		center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
-		center_split->set_collapsed(true);
-		bottom_panel_raise->hide();
-		if (bottom_panel_raise->is_pressed()) {
-			top_split->show();
-		}
-	}
-}
-
 void EditorNode::_toggle_distraction_free_mode() {
 	if (EDITOR_GET("interface/editor/separate_distraction_mode")) {
 		int screen = -1;
@@ -6079,17 +5914,6 @@ Vector<Ref<EditorResourceConversionPlugin>> EditorNode::find_resource_conversion
 	return ret;
 }
 
-void EditorNode::_bottom_panel_raise_toggled(bool p_pressed) {
-	top_split->set_visible(!p_pressed);
-}
-
-bool EditorNode::_bottom_panel_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control) {
-	if (!p_button->is_pressed()) {
-		_bottom_panel_switch_by_control(true, p_control);
-	}
-	return false;
-}
-
 void EditorNode::_update_renderer_color() {
 	String rendering_method = renderer->get_selected_metadata();
 
@@ -7149,62 +6973,12 @@ EditorNode::EditorNode() {
 
 	// Bottom panels.
 
-	bottom_panel = memnew(PanelContainer);
-	bottom_panel->add_theme_style_override("panel", theme->get_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
+	bottom_panel = memnew(EditorBottomPanel);
 	center_split->add_child(bottom_panel);
 	center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
 
-	bottom_panel_vb = memnew(VBoxContainer);
-	bottom_panel->add_child(bottom_panel_vb);
-
-	bottom_panel_hb = memnew(HBoxContainer);
-	bottom_panel_hb->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); // Adjust for the height of the "Expand Bottom Dock" icon.
-	bottom_panel_vb->add_child(bottom_panel_hb);
-
-	bottom_panel_hb_editors = memnew(HBoxContainer);
-	bottom_panel_hb_editors->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	bottom_panel_hb->add_child(bottom_panel_hb_editors);
-
-	editor_toaster = memnew(EditorToaster);
-	bottom_panel_hb->add_child(editor_toaster);
-
-	VBoxContainer *version_info_vbc = memnew(VBoxContainer);
-	bottom_panel_hb->add_child(version_info_vbc);
-
-	// Add a dummy control node for vertical spacing.
-	Control *v_spacer = memnew(Control);
-	version_info_vbc->add_child(v_spacer);
-
-	version_btn = memnew(LinkButton);
-	version_btn->set_text(VERSION_FULL_CONFIG);
-	String hash = String(VERSION_HASH);
-	if (hash.length() != 0) {
-		hash = " " + vformat("[%s]", hash.left(9));
-	}
-	// Set the text to copy in metadata as it slightly differs from the button's text.
-	version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash);
-	// Fade out the version label to be less prominent, but still readable.
-	version_btn->set_self_modulate(Color(1, 1, 1, 0.65));
-	version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
-	version_btn->set_tooltip_text(TTR("Click to copy."));
-	version_btn->connect("pressed", callable_mp(this, &EditorNode::_version_button_pressed));
-	version_info_vbc->add_child(version_btn);
-
-	// Add a dummy control node for horizontal spacing.
-	Control *h_spacer = memnew(Control);
-	bottom_panel_hb->add_child(h_spacer);
-
-	bottom_panel_raise = memnew(Button);
-	bottom_panel_hb->add_child(bottom_panel_raise);
-	bottom_panel_raise->hide();
-	bottom_panel_raise->set_flat(false);
-	bottom_panel_raise->set_theme_type_variation("FlatMenuButton");
-	bottom_panel_raise->set_toggle_mode(true);
-	bottom_panel_raise->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12));
-	bottom_panel_raise->connect("toggled", callable_mp(this, &EditorNode::_bottom_panel_raise_toggled));
-
 	log = memnew(EditorLog);
-	Button *output_button = add_bottom_panel_item(TTR("Output"), log);
+	Button *output_button = bottom_panel->add_item(TTR("Output"), log);
 	log->set_tool_button(output_button);
 
 	center_split->connect("resized", callable_mp(this, &EditorNode::_vp_resized));
@@ -7365,7 +7139,7 @@ EditorNode::EditorNode() {
 	}
 
 	// More visually meaningful to have this later.
-	raise_bottom_panel_item(AnimationPlayerEditor::get_singleton());
+	bottom_panel->move_item_to_end(AnimationPlayerEditor::get_singleton());
 
 	add_editor_plugin(VersionControlEditorPlugin::get_singleton());
 

+ 3 - 28
editor/editor_node.h

@@ -77,6 +77,7 @@ class DockSplitContainer;
 class DynamicFontImportSettingsDialog;
 class EditorAbout;
 class EditorBuildProfileManager;
+class EditorBottomPanel;
 class EditorCommandPalette;
 class EditorDockManager;
 class EditorExport;
@@ -244,12 +245,6 @@ private:
 		MAX_BUILD_CALLBACKS = 128
 	};
 
-	struct BottomPanelItem {
-		String name;
-		Control *control = nullptr;
-		Button *button = nullptr;
-	};
-
 	struct ExportDefer {
 		String preset;
 		String path;
@@ -324,7 +319,6 @@ private:
 	DisplayServer::WindowMode prev_mode = DisplayServer::WINDOW_MODE_MAXIMIZED;
 	int old_split_ofs = 0;
 	VSplitContainer *top_split = nullptr;
-	HBoxContainer *bottom_hb = nullptr;
 	Control *vp_base = nullptr;
 
 	Label *project_title = nullptr;
@@ -421,15 +415,7 @@ private:
 	Timer *editor_layout_save_delay_timer = nullptr;
 	Button *distraction_free = nullptr;
 
-	Vector<BottomPanelItem> bottom_panel_items;
-	PanelContainer *bottom_panel = nullptr;
-	HBoxContainer *bottom_panel_hb = nullptr;
-	HBoxContainer *bottom_panel_hb_editors = nullptr;
-	VBoxContainer *bottom_panel_vb = nullptr;
-	EditorToaster *editor_toaster = nullptr;
-	LinkButton *version_btn = nullptr;
-	Button *bottom_panel_raise = nullptr;
-	bool bottom_panel_updating = false;
+	EditorBottomPanel *bottom_panel = nullptr;
 
 	Tree *disk_changed_list = nullptr;
 	ConfirmationDialog *disk_changed = nullptr;
@@ -562,7 +548,6 @@ private:
 	void _show_messages();
 	void _vp_resized();
 	void _titlebar_resized();
-	void _version_button_pressed();
 
 	void _update_undo_redo_allowed();
 
@@ -663,11 +648,6 @@ private:
 	void _immediate_dialog_confirmed();
 	void _select_default_main_screen_plugin();
 
-	void _bottom_panel_switch_by_control(bool p_enable, Control *p_control);
-	void _bottom_panel_switch(bool p_enable, int p_idx);
-	void _bottom_panel_raise_toggled(bool);
-	bool _bottom_panel_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control);
-
 	void _begin_first_scan();
 
 	void _notify_scene_updated(Node *p_node);
@@ -698,6 +678,7 @@ public:
 
 	static EditorTitleBar *get_title_bar() { return singleton->title_bar; }
 	static VSplitContainer *get_top_split() { return singleton->top_split; }
+	static EditorBottomPanel *get_bottom_panel() { return singleton->bottom_panel; }
 
 	static String adjust_scene_name_casing(const String &root_name);
 
@@ -878,12 +859,6 @@ public:
 
 	bool is_exiting() const { return exiting; }
 
-	Button *add_bottom_panel_item(String p_text, Control *p_item, bool p_at_front = false);
-	void make_bottom_panel_item_visible(Control *p_item);
-	void raise_bottom_panel_item(Control *p_item);
-	void hide_bottom_panel();
-	void remove_bottom_panel_item(Control *p_item);
-
 	Variant drag_resource(const Ref<Resource> &p_res, Control *p_from);
 	Variant drag_files_and_dirs(const Vector<String> &p_paths, Control *p_from);
 

+ 5 - 4
editor/editor_plugin.cpp

@@ -39,6 +39,7 @@
 #include "editor/editor_translation_parser.h"
 #include "editor/editor_undo_redo_manager.h"
 #include "editor/export/editor_export.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_title_bar.h"
 #include "editor/import/3d/resource_importer_scene.h"
 #include "editor/import/editor_import_plugin.h"
@@ -80,7 +81,7 @@ void EditorPlugin::remove_autoload_singleton(const String &p_name) {
 
 Button *EditorPlugin::add_control_to_bottom_panel(Control *p_control, const String &p_title) {
 	ERR_FAIL_NULL_V(p_control, nullptr);
-	return EditorNode::get_singleton()->add_bottom_panel_item(p_title, p_control);
+	return EditorNode::get_bottom_panel()->add_item(p_title, p_control);
 }
 
 void EditorPlugin::add_control_to_dock(DockSlot p_slot, Control *p_control) {
@@ -95,7 +96,7 @@ void EditorPlugin::remove_control_from_docks(Control *p_control) {
 
 void EditorPlugin::remove_control_from_bottom_panel(Control *p_control) {
 	ERR_FAIL_NULL(p_control);
-	EditorNode::get_singleton()->remove_bottom_panel_item(p_control);
+	EditorNode::get_bottom_panel()->remove_item(p_control);
 }
 
 void EditorPlugin::add_control_to_container(CustomControlContainer p_location, Control *p_control) {
@@ -505,11 +506,11 @@ void EditorPlugin::queue_save_layout() {
 }
 
 void EditorPlugin::make_bottom_panel_item_visible(Control *p_item) {
-	EditorNode::get_singleton()->make_bottom_panel_item_visible(p_item);
+	EditorNode::get_bottom_panel()->make_item_visible(p_item);
 }
 
 void EditorPlugin::hide_bottom_panel() {
-	EditorNode::get_singleton()->hide_bottom_panel();
+	EditorNode::get_bottom_panel()->hide_bottom_panel();
 }
 
 EditorInterface *EditorPlugin::get_editor_interface() {

+ 272 - 0
editor/gui/editor_bottom_panel.cpp

@@ -0,0 +1,272 @@
+/**************************************************************************/
+/*  editor_bottom_panel.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_bottom_panel.h"
+
+#include "core/version.h"
+#include "editor/debugger/editor_debugger_node.h"
+#include "editor/editor_about.h"
+#include "editor/editor_command_palette.h"
+#include "editor/editor_node.h"
+#include "editor/editor_string_names.h"
+#include "editor/gui/editor_toaster.h"
+#include "editor/themes/editor_scale.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/link_button.h"
+
+// The metadata key used to store and retrieve the version text to copy to the clipboard.
+static const String META_TEXT_TO_COPY = "text_to_copy";
+
+void EditorBottomPanel::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_THEME_CHANGED: {
+			expand_button->set_icon(get_editor_theme_icon(SNAME("ExpandBottomDock")));
+			for (int i = 0; i < items.size(); i++) {
+				items.write[i].button->add_theme_style_override("pressed", get_theme_stylebox(SNAME("MenuTransparent"), EditorStringName(EditorStyles)));
+				items.write[i].button->add_theme_style_override("hover_pressed", get_theme_stylebox(SNAME("MenuHover"), EditorStringName(EditorStyles)));
+			}
+		} break;
+	}
+}
+
+void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control) {
+	for (int i = 0; i < items.size(); i++) {
+		if (items[i].control == p_control) {
+			_switch_to_item(p_visible, i);
+			return;
+		}
+	}
+}
+
+void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) {
+	ERR_FAIL_INDEX(p_idx, items.size());
+
+	if (items[p_idx].control->is_visible() == p_visible) {
+		return;
+	}
+
+	SplitContainer *center_split = Object::cast_to<SplitContainer>(get_parent());
+	ERR_FAIL_NULL(center_split);
+
+	if (p_visible) {
+		for (int i = 0; i < items.size(); i++) {
+			items[i].button->set_pressed_no_signal(i == p_idx);
+			items[i].control->set_visible(i == p_idx);
+		}
+		if (EditorDebuggerNode::get_singleton() == items[p_idx].control) {
+			// This is the debug panel which uses tabs, so the top section should be smaller.
+			add_theme_style_override("panel", get_theme_stylebox(SNAME("BottomPanelDebuggerOverride"), EditorStringName(EditorStyles)));
+		} else {
+			add_theme_style_override("panel", get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
+		}
+		center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
+		center_split->set_collapsed(false);
+		if (expand_button->is_pressed()) {
+			EditorNode::get_top_split()->hide();
+		}
+		expand_button->show();
+	} else {
+		add_theme_style_override("panel", get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
+		items[p_idx].button->set_pressed_no_signal(false);
+		items[p_idx].control->set_visible(false);
+		center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
+		center_split->set_collapsed(true);
+		expand_button->hide();
+		if (expand_button->is_pressed()) {
+			EditorNode::get_top_split()->show();
+		}
+	}
+}
+
+void EditorBottomPanel::_expand_button_toggled(bool p_pressed) {
+	EditorNode::get_top_split()->set_visible(!p_pressed);
+}
+
+void EditorBottomPanel::_version_button_pressed() {
+	DisplayServer::get_singleton()->clipboard_set(version_btn->get_meta(META_TEXT_TO_COPY));
+}
+
+bool EditorBottomPanel::_button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control) {
+	if (!p_button->is_pressed()) {
+		_switch_by_control(true, p_control);
+	}
+	return false;
+}
+
+void EditorBottomPanel::save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const {
+	int selected_item_idx = -1;
+	for (int i = 0; i < items.size(); i++) {
+		if (items[i].button->is_pressed()) {
+			selected_item_idx = i;
+			break;
+		}
+	}
+	if (selected_item_idx != -1) {
+		p_config_file->set_value(p_section, "selected_bottom_panel_item", selected_item_idx);
+	} else {
+		p_config_file->set_value(p_section, "selected_bottom_panel_item", Variant());
+	}
+}
+
+void EditorBottomPanel::load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section) {
+	bool has_active_tab = false;
+	if (p_config_file->has_section_key(p_section, "selected_bottom_panel_item")) {
+		int selected_item_idx = p_config_file->get_value(p_section, "selected_bottom_panel_item");
+		if (selected_item_idx >= 0 && selected_item_idx < items.size()) {
+			// Make sure we don't try to open contextual editors which are not enabled in the current context.
+			if (items[selected_item_idx].button->is_visible()) {
+				_switch_to_item(true, selected_item_idx);
+				has_active_tab = true;
+			}
+		}
+	}
+	// If there is no active tab we need to collapse the panel.
+	if (!has_active_tab) {
+		items[0].control->show(); // _switch_to_item() can collapse only visible tabs.
+		_switch_to_item(false, 0);
+	}
+}
+
+Button *EditorBottomPanel::add_item(String p_text, Control *p_item, bool p_at_front) {
+	Button *tb = memnew(Button);
+	tb->set_theme_type_variation("FlatMenuButton");
+	tb->connect("toggled", callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item));
+	tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorBottomPanel::_button_drag_hover).bind(tb, p_item), Callable());
+	tb->set_text(p_text);
+	tb->set_toggle_mode(true);
+	tb->set_focus_mode(Control::FOCUS_NONE);
+	item_vbox->add_child(p_item);
+
+	bottom_hbox->move_to_front();
+	button_hbox->add_child(tb);
+	if (p_at_front) {
+		button_hbox->move_child(tb, 0);
+	}
+	p_item->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+	p_item->hide();
+
+	BottomPanelItem bpi;
+	bpi.button = tb;
+	bpi.control = p_item;
+	bpi.name = p_text;
+	items.push_back(bpi);
+
+	return tb;
+}
+
+void EditorBottomPanel::remove_item(Control *p_item) {
+	for (int i = 0; i < items.size(); i++) {
+		if (items[i].control == p_item) {
+			if (p_item->is_visible_in_tree()) {
+				_switch_to_item(false, i);
+			}
+			item_vbox->remove_child(items[i].control);
+			button_hbox->remove_child(items[i].button);
+			memdelete(items[i].button);
+			items.remove_at(i);
+			break;
+		}
+	}
+}
+
+void EditorBottomPanel::make_item_visible(Control *p_item, bool p_visible) {
+	_switch_by_control(p_visible, p_item);
+}
+
+void EditorBottomPanel::move_item_to_end(Control *p_item) {
+	for (int i = 0; i < items.size(); i++) {
+		if (items[i].control == p_item) {
+			items[i].button->move_to_front();
+			SWAP(items.write[i], items.write[items.size() - 1]);
+			break;
+		}
+	}
+}
+
+void EditorBottomPanel::hide_bottom_panel() {
+	for (int i = 0; i < items.size(); i++) {
+		if (items[i].control->is_visible()) {
+			_switch_to_item(false, i);
+			break;
+		}
+	}
+}
+
+EditorBottomPanel::EditorBottomPanel() {
+	item_vbox = memnew(VBoxContainer);
+	add_child(item_vbox);
+
+	bottom_hbox = memnew(HBoxContainer);
+	bottom_hbox->set_custom_minimum_size(Size2(0, 24 * EDSCALE)); // Adjust for the height of the "Expand Bottom Dock" icon.
+	item_vbox->add_child(bottom_hbox);
+
+	button_hbox = memnew(HBoxContainer);
+	button_hbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+	bottom_hbox->add_child(button_hbox);
+
+	editor_toaster = memnew(EditorToaster);
+	bottom_hbox->add_child(editor_toaster);
+
+	VBoxContainer *version_info_vbox = memnew(VBoxContainer);
+	bottom_hbox->add_child(version_info_vbox);
+
+	// Add a dummy control node for vertical spacing.
+	Control *v_spacer = memnew(Control);
+	version_info_vbox->add_child(v_spacer);
+
+	version_btn = memnew(LinkButton);
+	version_btn->set_text(VERSION_FULL_CONFIG);
+	String hash = String(VERSION_HASH);
+	if (hash.length() != 0) {
+		hash = " " + vformat("[%s]", hash.left(9));
+	}
+	// Set the text to copy in metadata as it slightly differs from the button's text.
+	version_btn->set_meta(META_TEXT_TO_COPY, "v" VERSION_FULL_BUILD + hash);
+	// Fade out the version label to be less prominent, but still readable.
+	version_btn->set_self_modulate(Color(1, 1, 1, 0.65));
+	version_btn->set_underline_mode(LinkButton::UNDERLINE_MODE_ON_HOVER);
+	version_btn->set_tooltip_text(TTR("Click to copy."));
+	version_btn->connect("pressed", callable_mp(this, &EditorBottomPanel::_version_button_pressed));
+	version_info_vbox->add_child(version_btn);
+
+	// Add a dummy control node for horizontal spacing.
+	Control *h_spacer = memnew(Control);
+	bottom_hbox->add_child(h_spacer);
+
+	expand_button = memnew(Button);
+	bottom_hbox->add_child(expand_button);
+	expand_button->hide();
+	expand_button->set_flat(false);
+	expand_button->set_theme_type_variation("FlatMenuButton");
+	expand_button->set_toggle_mode(true);
+	expand_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12));
+	expand_button->connect("toggled", callable_mp(this, &EditorBottomPanel::_expand_button_toggled));
+}

+ 84 - 0
editor/gui/editor_bottom_panel.h

@@ -0,0 +1,84 @@
+/**************************************************************************/
+/*  editor_bottom_panel.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_BOTTOM_PANEL_H
+#define EDITOR_BOTTOM_PANEL_H
+
+#include "scene/gui/panel_container.h"
+
+class Button;
+class ConfigFile;
+class EditorToaster;
+class HBoxContainer;
+class LinkButton;
+class VBoxContainer;
+
+class EditorBottomPanel : public PanelContainer {
+	GDCLASS(EditorBottomPanel, PanelContainer);
+
+	struct BottomPanelItem {
+		String name;
+		Control *control = nullptr;
+		Button *button = nullptr;
+	};
+
+	Vector<BottomPanelItem> items;
+
+	VBoxContainer *item_vbox = nullptr;
+	HBoxContainer *bottom_hbox = nullptr;
+	HBoxContainer *button_hbox = nullptr;
+	EditorToaster *editor_toaster = nullptr;
+	LinkButton *version_btn = nullptr;
+	Button *expand_button = nullptr;
+
+	void _switch_by_control(bool p_visible, Control *p_control);
+	void _switch_to_item(bool p_visible, int p_idx);
+	void _expand_button_toggled(bool p_pressed);
+	void _version_button_pressed();
+
+	bool _button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control);
+
+protected:
+	void _notification(int p_what);
+
+public:
+	void save_layout_to_config(Ref<ConfigFile> p_config_file, const String &p_section) const;
+	void load_layout_from_config(Ref<ConfigFile> p_config_file, const String &p_section);
+
+	Button *add_item(String p_text, Control *p_item, bool p_at_front = false);
+	void remove_item(Control *p_item);
+	void make_item_visible(Control *p_item, bool p_visible = true);
+	void move_item_to_end(Control *p_item);
+	void hide_bottom_panel();
+
+	EditorBottomPanel();
+};
+
+#endif // EDITOR_BOTTOM_PANEL_H

+ 4 - 3
editor/plugins/animation_player_editor_plugin.cpp

@@ -38,6 +38,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/inspector_dock.h"
 #include "editor/plugins/canvas_item_editor_plugin.h" // For onion skinning.
@@ -790,7 +791,7 @@ void AnimationPlayerEditor::set_state(const Dictionary &p_state) {
 			}
 
 			_update_player();
-			EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+			EditorNode::get_bottom_panel()->make_item_visible(this);
 			set_process(true);
 			ensure_visibility();
 
@@ -2192,7 +2193,7 @@ bool AnimationPlayerEditorPlugin::handles(Object *p_object) const {
 
 void AnimationPlayerEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(anim_editor);
+		EditorNode::get_bottom_panel()->make_item_visible(anim_editor);
 		anim_editor->set_process(true);
 		anim_editor->ensure_visibility();
 	}
@@ -2200,7 +2201,7 @@ void AnimationPlayerEditorPlugin::make_visible(bool p_visible) {
 
 AnimationPlayerEditorPlugin::AnimationPlayerEditorPlugin() {
 	anim_editor = memnew(AnimationPlayerEditor(this));
-	EditorNode::get_singleton()->add_bottom_panel_item(TTR("Animation"), anim_editor);
+	EditorNode::get_bottom_panel()->add_item(TTR("Animation"), anim_editor);
 }
 
 AnimationPlayerEditorPlugin::~AnimationPlayerEditorPlugin() {

+ 4 - 3
editor/plugins/animation_tree_editor_plugin.cpp

@@ -40,6 +40,7 @@
 #include "core/math/delaunay_2d.h"
 #include "core/os/keyboard.h"
 #include "editor/editor_node.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/animation/animation_blend_tree.h"
@@ -299,11 +300,11 @@ void AnimationTreeEditorPlugin::make_visible(bool p_visible) {
 		//editor->hide_animation_player_editors();
 		//editor->animation_panel_make_visible(true);
 		button->show();
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(anim_tree_editor);
+		EditorNode::get_bottom_panel()->make_item_visible(anim_tree_editor);
 		anim_tree_editor->set_process(true);
 	} else {
 		if (anim_tree_editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
+			EditorNode::get_bottom_panel()->hide_bottom_panel();
 		}
 		button->hide();
 		anim_tree_editor->set_process(false);
@@ -314,7 +315,7 @@ AnimationTreeEditorPlugin::AnimationTreeEditorPlugin() {
 	anim_tree_editor = memnew(AnimationTreeEditor);
 	anim_tree_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
 
-	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("AnimationTree"), anim_tree_editor);
+	button = EditorNode::get_bottom_panel()->add_item(TTR("AnimationTree"), anim_tree_editor);
 	button->hide();
 }
 

+ 2 - 1
editor/plugins/debugger_editor_plugin.cpp

@@ -36,6 +36,7 @@
 #include "editor/debugger/editor_file_server.h"
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/plugins/script_editor_plugin.h"
 #include "editor/run_instances_dialog.h"
 #include "editor/themes/editor_scale.h"
@@ -54,7 +55,7 @@ DebuggerEditorPlugin::DebuggerEditorPlugin(PopupMenu *p_debug_menu) {
 	file_server = memnew(EditorFileServer);
 
 	EditorDebuggerNode *debugger = memnew(EditorDebuggerNode);
-	Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
+	Button *db = EditorNode::get_bottom_panel()->add_item(TTR("Debugger"), debugger);
 	debugger->set_tool_button(db);
 
 	// Main editor debug menu.

+ 4 - 3
editor/plugins/resource_preloader_editor_plugin.cpp

@@ -36,6 +36,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/themes/editor_scale.h"
 
@@ -406,11 +407,11 @@ void ResourcePreloaderEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
 		//preloader_editor->show();
 		button->show();
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(preloader_editor);
+		EditorNode::get_bottom_panel()->make_item_visible(preloader_editor);
 		//preloader_editor->set_process(true);
 	} else {
 		if (preloader_editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
+			EditorNode::get_bottom_panel()->hide_bottom_panel();
 		}
 		button->hide();
 		//preloader_editor->hide();
@@ -422,7 +423,7 @@ ResourcePreloaderEditorPlugin::ResourcePreloaderEditorPlugin() {
 	preloader_editor = memnew(ResourcePreloaderEditor);
 	preloader_editor->set_custom_minimum_size(Size2(0, 250) * EDSCALE);
 
-	button = EditorNode::get_singleton()->add_bottom_panel_item("ResourcePreloader", preloader_editor);
+	button = EditorNode::get_bottom_panel()->add_item("ResourcePreloader", preloader_editor);
 	button->hide();
 }
 

+ 4 - 3
editor/plugins/script_editor_plugin.cpp

@@ -50,6 +50,7 @@
 #include "editor/editor_string_names.h"
 #include "editor/filesystem_dock.h"
 #include "editor/find_in_files.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/gui/editor_run_bar.h"
 #include "editor/gui/editor_toaster.h"
@@ -1712,7 +1713,7 @@ void ScriptEditor::_notification(int p_what) {
 				find_in_files_button->show();
 			} else {
 				if (find_in_files->is_visible_in_tree()) {
-					EditorNode::get_singleton()->hide_bottom_panel();
+					EditorNode::get_bottom_panel()->hide_bottom_panel();
 				}
 				find_in_files_button->hide();
 			}
@@ -3798,7 +3799,7 @@ void ScriptEditor::_start_find_in_files(bool with_replace) {
 	find_in_files->set_replace_text(find_in_files_dialog->get_replace_text());
 	find_in_files->start_search();
 
-	EditorNode::get_singleton()->make_bottom_panel_item_visible(find_in_files);
+	EditorNode::get_bottom_panel()->make_item_visible(find_in_files);
 }
 
 void ScriptEditor::_on_find_in_files_modified_files(PackedStringArray paths) {
@@ -4159,7 +4160,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
 	find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, callable_mp(this, &ScriptEditor::_start_find_in_files).bind(true));
 	add_child(find_in_files_dialog);
 	find_in_files = memnew(FindInFilesPanel);
-	find_in_files_button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Search Results"), find_in_files);
+	find_in_files_button = EditorNode::get_bottom_panel()->add_item(TTR("Search Results"), find_in_files);
 	find_in_files->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
 	find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, callable_mp(this, &ScriptEditor::_on_find_in_files_result_selected));
 	find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, callable_mp(this, &ScriptEditor::_on_find_in_files_modified_files));

+ 3 - 2
editor/plugins/shader_editor_plugin.cpp

@@ -35,6 +35,7 @@
 #include "editor/editor_string_names.h"
 #include "editor/editor_undo_redo_manager.h"
 #include "editor/filesystem_dock.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/inspector_dock.h"
 #include "editor/plugins/text_shader_editor.h"
 #include "editor/plugins/visual_shader_editor_plugin.h"
@@ -178,7 +179,7 @@ bool ShaderEditorPlugin::handles(Object *p_object) const {
 
 void ShaderEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(window_wrapper);
+		EditorNode::get_bottom_panel()->make_item_visible(window_wrapper);
 	}
 }
 
@@ -677,7 +678,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() {
 	empty.instantiate();
 	shader_tabs->add_theme_style_override("panel", empty);
 
-	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Shader Editor"), window_wrapper);
+	button = EditorNode::get_bottom_panel()->add_item(TTR("Shader Editor"), window_wrapper);
 
 	shader_create_dialog = memnew(ShaderCreateDialog);
 	vb->add_child(shader_create_dialog);

+ 4 - 3
editor/plugins/shader_file_editor_plugin.cpp

@@ -37,6 +37,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_string_names.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/gui/item_list.h"
 #include "scene/gui/split_container.h"
@@ -308,12 +309,12 @@ bool ShaderFileEditorPlugin::handles(Object *p_object) const {
 void ShaderFileEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
 		button->show();
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(shader_editor);
+		EditorNode::get_bottom_panel()->make_item_visible(shader_editor);
 
 	} else {
 		button->hide();
 		if (shader_editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
+			EditorNode::get_bottom_panel()->hide_bottom_panel();
 		}
 	}
 }
@@ -322,7 +323,7 @@ ShaderFileEditorPlugin::ShaderFileEditorPlugin() {
 	shader_editor = memnew(ShaderFileEditor);
 
 	shader_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
-	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("ShaderFile"), shader_editor);
+	button = EditorNode::get_bottom_panel()->add_item(TTR("ShaderFile"), shader_editor);
 	button->hide();
 }
 

+ 3 - 2
editor/plugins/sprite_frames_editor_plugin.cpp

@@ -38,6 +38,7 @@
 #include "editor/editor_settings.h"
 #include "editor/editor_string_names.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/scene_tree_dock.h"
 #include "editor/themes/editor_scale.h"
@@ -2219,7 +2220,7 @@ bool SpriteFramesEditorPlugin::handles(Object *p_object) const {
 void SpriteFramesEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
 		button->show();
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(frames_editor);
+		EditorNode::get_bottom_panel()->make_item_visible(frames_editor);
 	} else {
 		button->hide();
 		frames_editor->edit(Ref<SpriteFrames>());
@@ -2229,7 +2230,7 @@ void SpriteFramesEditorPlugin::make_visible(bool p_visible) {
 SpriteFramesEditorPlugin::SpriteFramesEditorPlugin() {
 	frames_editor = memnew(SpriteFramesEditor);
 	frames_editor->set_custom_minimum_size(Size2(0, 300) * EDSCALE);
-	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("SpriteFrames"), frames_editor);
+	button = EditorNode::get_bottom_panel()->add_item(TTR("SpriteFrames"), frames_editor);
 	button->hide();
 }
 

+ 4 - 3
editor/plugins/theme_editor_plugin.cpp

@@ -36,6 +36,7 @@
 #include "editor/editor_resource_picker.h"
 #include "editor/editor_string_names.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/inspector_dock.h"
 #include "editor/progress_dialog.h"
@@ -3760,10 +3761,10 @@ bool ThemeEditorPlugin::handles(Object *p_object) const {
 void ThemeEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
 		button->show();
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(theme_editor);
+		EditorNode::get_bottom_panel()->make_item_visible(theme_editor);
 	} else {
 		if (theme_editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
+			EditorNode::get_bottom_panel()->hide_bottom_panel();
 		}
 
 		button->hide();
@@ -3843,6 +3844,6 @@ ThemeEditorPlugin::ThemeEditorPlugin() {
 	theme_editor->plugin = this;
 	theme_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
 
-	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Theme"), theme_editor);
+	button = EditorNode::get_bottom_panel()->add_item(TTR("Theme"), theme_editor);
 	button->hide();
 }

+ 8 - 7
editor/plugins/tiles/tiles_editor_plugin.cpp

@@ -38,6 +38,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_string_names.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/plugins/canvas_item_editor_plugin.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/2d/tile_map.h"
@@ -479,11 +480,11 @@ bool TileMapEditorPlugin::handles(Object *p_object) const {
 void TileMapEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
 		button->show();
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(editor);
+		EditorNode::get_bottom_panel()->make_item_visible(editor);
 	} else {
 		button->hide();
 		if (editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
+			EditorNode::get_bottom_panel()->hide_bottom_panel();
 		}
 	}
 }
@@ -498,7 +499,7 @@ void TileMapEditorPlugin::forward_canvas_draw_over_viewport(Control *p_overlay)
 
 void TileMapEditorPlugin::hide_editor() {
 	if (editor->is_visible_in_tree()) {
-		EditorNode::get_singleton()->hide_bottom_panel();
+		EditorNode::get_bottom_panel()->hide_bottom_panel();
 	}
 }
 
@@ -519,7 +520,7 @@ TileMapEditorPlugin::TileMapEditorPlugin() {
 	editor->connect("change_selected_layer_request", callable_mp(this, &TileMapEditorPlugin::_select_layer));
 	editor->hide();
 
-	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TileMap"), editor);
+	button = EditorNode::get_bottom_panel()->add_item(TTR("TileMap"), editor);
 	button->hide();
 }
 
@@ -544,12 +545,12 @@ void TileSetEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
 		button->show();
 		if (!tile_map_plugin_singleton->is_editor_visible()) {
-			EditorNode::get_singleton()->make_bottom_panel_item_visible(editor);
+			EditorNode::get_bottom_panel()->make_item_visible(editor);
 		}
 	} else {
 		button->hide();
 		if (editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
+			EditorNode::get_bottom_panel()->hide_bottom_panel();
 		}
 	}
 }
@@ -570,7 +571,7 @@ TileSetEditorPlugin::TileSetEditorPlugin() {
 	editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
 	editor->hide();
 
-	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("TileSet"), editor);
+	button = EditorNode::get_bottom_panel()->add_item(TTR("TileSet"), editor);
 	button->hide();
 }
 

+ 3 - 2
editor/plugins/version_control_editor_plugin.cpp

@@ -40,6 +40,7 @@
 #include "editor/editor_settings.h"
 #include "editor/editor_string_names.h"
 #include "editor/filesystem_dock.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/plugins/script_editor_plugin.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/gui/separator.h"
@@ -912,7 +913,7 @@ void VersionControlEditorPlugin::fetch_available_vcs_plugin_names() {
 void VersionControlEditorPlugin::register_editor() {
 	EditorDockManager::get_singleton()->add_control_to_dock(EditorDockManager::DOCK_SLOT_RIGHT_UL, version_commit_dock);
 
-	version_control_dock_button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Version Control"), version_control_dock);
+	version_control_dock_button = EditorNode::get_bottom_panel()->add_item(TTR("Version Control"), version_control_dock);
 
 	_set_vcs_ui_state(true);
 }
@@ -931,7 +932,7 @@ void VersionControlEditorPlugin::shut_down() {
 	EditorVCSInterface::set_singleton(nullptr);
 
 	EditorDockManager::get_singleton()->remove_control_from_dock(version_commit_dock);
-	EditorNode::get_singleton()->remove_bottom_panel_item(version_control_dock);
+	EditorNode::get_bottom_panel()->remove_item(version_control_dock);
 
 	_set_vcs_ui_state(false);
 }

+ 6 - 5
modules/multiplayer/editor/multiplayer_editor_plugin.cpp

@@ -36,6 +36,7 @@
 
 #include "editor/editor_interface.h"
 #include "editor/editor_node.h"
+#include "editor/gui/editor_bottom_panel.h"
 
 void MultiplayerEditorDebugger::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("open_request", PropertyInfo(Variant::STRING, "path")));
@@ -112,7 +113,7 @@ void MultiplayerEditorDebugger::setup_session(int p_session_id) {
 
 MultiplayerEditorPlugin::MultiplayerEditorPlugin() {
 	repl_editor = memnew(ReplicationEditor);
-	button = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Replication"), repl_editor);
+	button = EditorNode::get_bottom_panel()->add_item(TTR("Replication"), repl_editor);
 	button->hide();
 	repl_editor->get_pin()->connect("pressed", callable_mp(this, &MultiplayerEditorPlugin::_pinned));
 	debugger.instantiate();
@@ -139,7 +140,7 @@ void MultiplayerEditorPlugin::_node_removed(Node *p_node) {
 	if (p_node && p_node == repl_editor->get_current()) {
 		repl_editor->edit(nullptr);
 		if (repl_editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
+			EditorNode::get_bottom_panel()->hide_bottom_panel();
 		}
 		button->hide();
 		repl_editor->get_pin()->set_pressed(false);
@@ -149,7 +150,7 @@ void MultiplayerEditorPlugin::_node_removed(Node *p_node) {
 void MultiplayerEditorPlugin::_pinned() {
 	if (!repl_editor->get_pin()->is_pressed()) {
 		if (repl_editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
+			EditorNode::get_bottom_panel()->hide_bottom_panel();
 		}
 		button->hide();
 	}
@@ -166,10 +167,10 @@ bool MultiplayerEditorPlugin::handles(Object *p_object) const {
 void MultiplayerEditorPlugin::make_visible(bool p_visible) {
 	if (p_visible) {
 		button->show();
-		EditorNode::get_singleton()->make_bottom_panel_item_visible(repl_editor);
+		EditorNode::get_bottom_panel()->make_item_visible(repl_editor);
 	} else if (!repl_editor->get_pin()->is_pressed()) {
 		if (repl_editor->is_visible_in_tree()) {
-			EditorNode::get_singleton()->hide_bottom_panel();
+			EditorNode::get_bottom_panel()->hide_bottom_panel();
 		}
 		button->hide();
 	}

+ 2 - 1
modules/openxr/editor/openxr_action_map_editor.cpp

@@ -33,6 +33,7 @@
 #include "core/config/project_settings.h"
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
+#include "editor/gui/editor_bottom_panel.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/themes/editor_scale.h"
 
@@ -356,7 +357,7 @@ void OpenXRActionMapEditor::_do_remove_interaction_profile_editor(OpenXRInteract
 }
 
 void OpenXRActionMapEditor::open_action_map(String p_path) {
-	EditorNode::get_singleton()->make_bottom_panel_item_visible(this);
+	EditorNode::get_bottom_panel()->make_item_visible(this);
 
 	// out with the old...
 	_clear_action_map();

+ 2 - 1
modules/openxr/editor/openxr_editor_plugin.cpp

@@ -33,6 +33,7 @@
 #include "../action_map/openxr_action_map.h"
 
 #include "editor/editor_node.h"
+#include "editor/gui/editor_bottom_panel.h"
 
 void OpenXREditorPlugin::edit(Object *p_node) {
 	if (Object::cast_to<OpenXRActionMap>(p_node)) {
@@ -52,7 +53,7 @@ void OpenXREditorPlugin::make_visible(bool p_visible) {
 
 OpenXREditorPlugin::OpenXREditorPlugin() {
 	action_map_editor = memnew(OpenXRActionMapEditor);
-	EditorNode::get_singleton()->add_bottom_panel_item(TTR("OpenXR Action Map"), action_map_editor);
+	EditorNode::get_bottom_panel()->add_item(TTR("OpenXR Action Map"), action_map_editor);
 
 #ifndef ANDROID_ENABLED
 	select_runtime = memnew(OpenXRSelectRuntime);