From 8a74d8438f5dee92927d953a7954ad3c3022cbd9 Mon Sep 17 00:00:00 2001
From: Yuri Sizov <yuris@humnom.net>
Date: Wed, 12 Apr 2023 21:02:28 +0200
Subject: [PATCH] Extract editor run toolbar into its own component

- Simplify and update its logic.
- Simplify EditorScript.
- Improve EditorNode and other relevant includes.
- Fix scene-based path in the movie writer when
reloading a scene.
---
 doc/classes/EditorScript.xml                  |   4 +-
 .../debug_adapter/debug_adapter_parser.cpp    |  17 +-
 .../debug_adapter/debug_adapter_protocol.cpp  |   6 +-
 editor/debugger/editor_debugger_node.cpp      |  16 +-
 editor/editor_data.cpp                        |  10 +
 editor/editor_data.h                          |   3 +
 editor/editor_interface.cpp                   |  17 +-
 editor/editor_node.cpp                        | 498 ++++--------------
 editor/editor_node.h                          |  59 +--
 editor/editor_plugin.cpp                      |   4 +-
 editor/editor_plugin_settings.cpp             |   1 +
 editor/editor_run.h                           |   1 +
 editor/editor_run_native.cpp                  |   7 +-
 editor/editor_run_native.h                    |   6 +-
 ...ditor_run_script.cpp => editor_script.cpp} |  33 +-
 .../{editor_run_script.h => editor_script.h}  |  21 +-
 editor/editor_settings.h                      |   3 +-
 editor/export/editor_export_platform.cpp      |   1 +
 editor/gui/editor_run_bar.cpp                 | 442 ++++++++++++++++
 editor/gui/editor_run_bar.h                   | 115 ++++
 editor/plugins/canvas_item_editor_plugin.cpp  |   5 +-
 editor/plugins/curve_editor_plugin.cpp        |   1 +
 editor/plugins/curve_editor_plugin.h          |   2 +
 editor/plugins/material_editor_plugin.cpp     |   1 +
 editor/plugins/node_3d_editor_plugin.cpp      |   5 +-
 editor/plugins/script_editor_plugin.cpp       |  11 +-
 editor/plugins/script_text_editor.cpp         |   1 +
 editor/project_settings_editor.cpp            |   1 +
 editor/register_editor_types.cpp              |   6 +-
 .../editor_scene_exporter_gltf_plugin.cpp     |   1 +
 modules/mono/editor/editor_internal_calls.cpp |   5 +-
 platform/ios/export/export_plugin.cpp         |   1 +
 platform/linuxbsd/export/export_plugin.cpp    |   1 +
 platform/uwp/export/export.cpp                |   1 +
 platform/web/export/export.cpp                |   1 +
 platform/web/export/export_plugin.cpp         |   1 +
 platform/windows/export/export_plugin.cpp     |   1 +
 37 files changed, 771 insertions(+), 538 deletions(-)
 rename editor/{editor_run_script.cpp => editor_script.cpp} (87%)
 rename editor/{editor_run_script.h => editor_script.h} (88%)
 create mode 100644 editor/gui/editor_run_bar.cpp
 create mode 100644 editor/gui/editor_run_bar.h

diff --git a/doc/classes/EditorScript.xml b/doc/classes/EditorScript.xml
index ecaa21efb3c..d77d11857b5 100644
--- a/doc/classes/EditorScript.xml
+++ b/doc/classes/EditorScript.xml
@@ -48,13 +48,13 @@
 				[b]Warning:[/b] The implementation of this method is currently disabled.
 			</description>
 		</method>
-		<method name="get_editor_interface">
+		<method name="get_editor_interface" qualifiers="const">
 			<return type="EditorInterface" />
 			<description>
 				Returns the [EditorInterface] singleton instance.
 			</description>
 		</method>
-		<method name="get_scene">
+		<method name="get_scene" qualifiers="const">
 			<return type="Node" />
 			<description>
 				Returns the Editor's currently active scene.
diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.cpp b/editor/debugger/debug_adapter/debug_adapter_parser.cpp
index fc806ded5ec..e3686a0217d 100644
--- a/editor/debugger/debug_adapter/debug_adapter_parser.cpp
+++ b/editor/debugger/debug_adapter/debug_adapter_parser.cpp
@@ -32,9 +32,8 @@
 
 #include "editor/debugger/editor_debugger_node.h"
 #include "editor/debugger/script_editor_debugger.h"
-#include "editor/editor_node.h"
-#include "editor/editor_run_native.h"
 #include "editor/export/editor_export_platform.h"
+#include "editor/gui/editor_run_bar.h"
 #include "editor/plugins/script_editor_plugin.h"
 
 void DebugAdapterParser::_bind_methods() {
@@ -162,7 +161,7 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const
 
 Dictionary DebugAdapterParser::req_disconnect(const Dictionary &p_params) const {
 	if (!DebugAdapterProtocol::get_singleton()->get_current_peer()->attached) {
-		EditorNode::get_singleton()->run_stop();
+		EditorRunBar::get_singleton()->stop_playing();
 	}
 
 	return prepare_success_response(p_params);
@@ -188,7 +187,7 @@ Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) const {
 
 	String platform_string = args.get("platform", "host");
 	if (platform_string == "host") {
-		EditorNode::get_singleton()->run_play();
+		EditorRunBar::get_singleton()->play_main_scene();
 	} else {
 		int device = args.get("device", -1);
 		int idx = -1;
@@ -212,8 +211,8 @@ Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) const {
 			return prepare_error_response(p_params, DAP::ErrorType::UNKNOWN_PLATFORM);
 		}
 
-		EditorNode *editor = EditorNode::get_singleton();
-		Error err = platform_string == "android" ? editor->run_play_native(device * 10000 + idx) : editor->run_play_native(idx);
+		EditorRunBar *run_bar = EditorRunBar::get_singleton();
+		Error err = platform_string == "android" ? run_bar->start_native_device(device * 10000 + idx) : run_bar->start_native_device(idx);
 		if (err) {
 			if (err == ERR_INVALID_PARAMETER && platform_string == "android") {
 				return prepare_error_response(p_params, DAP::ErrorType::MISSING_DEVICE);
@@ -257,13 +256,13 @@ Dictionary DebugAdapterParser::req_restart(const Dictionary &p_params) const {
 }
 
 Dictionary DebugAdapterParser::req_terminate(const Dictionary &p_params) const {
-	EditorNode::get_singleton()->run_stop();
+	EditorRunBar::get_singleton()->stop_playing();
 
 	return prepare_success_response(p_params);
 }
 
 Dictionary DebugAdapterParser::req_pause(const Dictionary &p_params) const {
-	EditorNode::get_singleton()->get_pause_button()->set_pressed(true);
+	EditorRunBar::get_singleton()->get_pause_button()->set_pressed(true);
 	EditorDebuggerNode::get_singleton()->_paused();
 
 	DebugAdapterProtocol::get_singleton()->notify_stopped_paused();
@@ -272,7 +271,7 @@ Dictionary DebugAdapterParser::req_pause(const Dictionary &p_params) const {
 }
 
 Dictionary DebugAdapterParser::req_continue(const Dictionary &p_params) const {
-	EditorNode::get_singleton()->get_pause_button()->set_pressed(false);
+	EditorRunBar::get_singleton()->get_pause_button()->set_pressed(false);
 	EditorDebuggerNode::get_singleton()->_paused();
 
 	DebugAdapterProtocol::get_singleton()->notify_continued();
diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp
index f85163cd6ae..26fb73570e4 100644
--- a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp
+++ b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp
@@ -38,6 +38,7 @@
 #include "editor/editor_log.h"
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
+#include "editor/gui/editor_run_bar.h"
 
 DebugAdapterProtocol *DebugAdapterProtocol::singleton = nullptr;
 
@@ -812,7 +813,7 @@ Array DebugAdapterProtocol::update_breakpoints(const String &p_path, const Array
 }
 
 void DebugAdapterProtocol::on_debug_paused() {
-	if (EditorNode::get_singleton()->get_pause_button()->is_pressed()) {
+	if (EditorRunBar::get_singleton()->get_pause_button()->is_pressed()) {
 		notify_stopped_paused();
 	} else {
 		notify_continued();
@@ -1017,8 +1018,7 @@ DebugAdapterProtocol::DebugAdapterProtocol() {
 
 	reset_ids();
 
-	EditorNode *node = EditorNode::get_singleton();
-	node->get_pause_button()->connect("pressed", callable_mp(this, &DebugAdapterProtocol::on_debug_paused));
+	EditorRunBar::get_singleton()->get_pause_button()->connect("pressed", callable_mp(this, &DebugAdapterProtocol::on_debug_paused));
 
 	EditorDebuggerNode *debugger_node = EditorDebuggerNode::get_singleton();
 	debugger_node->connect("breakpoint_toggled", callable_mp(this, &DebugAdapterProtocol::on_debug_breakpoint_toggled));
diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp
index a368cacf56a..7083640b247 100644
--- a/editor/debugger/editor_debugger_node.cpp
+++ b/editor/debugger/editor_debugger_node.cpp
@@ -37,6 +37,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_run_bar.h"
 #include "editor/inspector_dock.h"
 #include "editor/plugins/editor_debugger_plugin.h"
 #include "editor/plugins/script_editor_plugin.h"
@@ -87,8 +88,7 @@ EditorDebuggerNode::EditorDebuggerNode() {
 	remote_scene_tree_timeout = EDITOR_DEF("debugger/remote_scene_tree_refresh_interval", 1.0);
 	inspect_edited_object_timeout = EDITOR_DEF("debugger/remote_inspect_refresh_interval", 0.2);
 
-	EditorNode *editor = EditorNode::get_singleton();
-	editor->get_pause_button()->connect("pressed", callable_mp(this, &EditorDebuggerNode::_paused));
+	EditorRunBar::get_singleton()->get_pause_button()->connect("pressed", callable_mp(this, &EditorDebuggerNode::_paused));
 }
 
 ScriptEditorDebugger *EditorDebuggerNode::_add_debugger() {
@@ -260,7 +260,7 @@ void EditorDebuggerNode::stop(bool p_force) {
 		server->stop();
 		EditorNode::get_log()->add_message("--- Debugging process stopped ---", EditorLog::MSG_TYPE_EDITOR);
 
-		if (EditorNode::get_singleton()->is_movie_maker_enabled()) {
+		if (EditorRunBar::get_singleton()->is_movie_maker_enabled()) {
 			// Request attention in case the user was doing something else when movie recording is finished.
 			DisplayServer::get_singleton()->window_request_attention();
 		}
@@ -344,7 +344,7 @@ void EditorDebuggerNode::_notification(int p_what) {
 					}
 				}
 
-				EditorNode::get_singleton()->get_pause_button()->set_disabled(false);
+				EditorRunBar::get_singleton()->get_pause_button()->set_disabled(false);
 				// Switch to remote tree view if so desired.
 				auto_switch_remote_scene_tree = (bool)EDITOR_GET("debugger/auto_switch_to_remote_scene_tree");
 				if (auto_switch_remote_scene_tree) {
@@ -413,8 +413,8 @@ void EditorDebuggerNode::_debugger_stopped(int p_id) {
 		}
 	});
 	if (!found) {
-		EditorNode::get_singleton()->get_pause_button()->set_pressed(false);
-		EditorNode::get_singleton()->get_pause_button()->set_disabled(true);
+		EditorRunBar::get_singleton()->get_pause_button()->set_pressed(false);
+		EditorRunBar::get_singleton()->get_pause_button()->set_disabled(true);
 		SceneTreeDock::get_singleton()->hide_remote_tree();
 		SceneTreeDock::get_singleton()->hide_tab_buttons();
 		EditorNode::get_singleton()->notify_all_debug_sessions_exited();
@@ -509,7 +509,7 @@ void EditorDebuggerNode::_update_debug_options() {
 }
 
 void EditorDebuggerNode::_paused() {
-	const bool paused = EditorNode::get_singleton()->get_pause_button()->is_pressed();
+	const bool paused = EditorRunBar::get_singleton()->get_pause_button()->is_pressed();
 	_for_all(tabs, [&](ScriptEditorDebugger *dbg) {
 		if (paused && !dbg->is_breaked()) {
 			dbg->debug_break();
@@ -527,7 +527,7 @@ void EditorDebuggerNode::_breaked(bool p_breaked, bool p_can_debug, String p_mes
 		tabs->set_current_tab(p_debugger);
 	}
 	_break_state_changed();
-	EditorNode::get_singleton()->get_pause_button()->set_pressed(p_breaked);
+	EditorRunBar::get_singleton()->get_pause_button()->set_pressed(p_breaked);
 	emit_signal(SNAME("breaked"), p_breaked, p_can_debug);
 }
 
diff --git a/editor/editor_data.cpp b/editor/editor_data.cpp
index 1421db42ec7..1f9d7541855 100644
--- a/editor/editor_data.cpp
+++ b/editor/editor_data.cpp
@@ -713,6 +713,16 @@ int EditorData::get_edited_scene() const {
 	return current_edited_scene;
 }
 
+int EditorData::get_edited_scene_from_path(const String &p_path) const {
+	for (int i = 0; i < edited_scene.size(); i++) {
+		if (edited_scene[i].path == p_path) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
 void EditorData::set_edited_scene(int p_idx) {
 	ERR_FAIL_INDEX(p_idx, edited_scene.size());
 	current_edited_scene = p_idx;
diff --git a/editor/editor_data.h b/editor/editor_data.h
index 370963074c5..d4a2f534cdb 100644
--- a/editor/editor_data.h
+++ b/editor/editor_data.h
@@ -196,9 +196,11 @@ public:
 	void set_edited_scene(int p_idx);
 	void set_edited_scene_root(Node *p_root);
 	int get_edited_scene() const;
+	int get_edited_scene_from_path(const String &p_path) const;
 	Node *get_edited_scene_root(int p_idx = -1);
 	int get_edited_scene_count() const;
 	Vector<EditedScene> get_edited_scenes() const;
+
 	String get_scene_title(int p_idx, bool p_always_strip_extension = false) const;
 	String get_scene_path(int p_idx) const;
 	String get_scene_type(int p_idx) const;
@@ -211,6 +213,7 @@ public:
 	NodePath get_edited_scene_live_edit_root();
 	bool check_and_update_scene(int p_idx);
 	void move_edited_scene_to_index(int p_idx);
+
 	bool call_build();
 
 	void set_scene_as_saved(int p_idx);
diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp
index 99803fd82da..167a5a3dba3 100644
--- a/editor/editor_interface.cpp
+++ b/editor/editor_interface.cpp
@@ -37,6 +37,7 @@
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/filesystem_dock.h"
+#include "editor/gui/editor_run_bar.h"
 #include "editor/inspector_dock.h"
 #include "main/main.h"
 #include "scene/gui/box_container.h"
@@ -317,35 +318,35 @@ void EditorInterface::save_scene_as(const String &p_scene, bool p_with_preview)
 // Scene playback.
 
 void EditorInterface::play_main_scene() {
-	EditorNode::get_singleton()->run_play();
+	EditorRunBar::get_singleton()->play_main_scene();
 }
 
 void EditorInterface::play_current_scene() {
-	EditorNode::get_singleton()->run_play_current();
+	EditorRunBar::get_singleton()->play_current_scene();
 }
 
 void EditorInterface::play_custom_scene(const String &scene_path) {
-	EditorNode::get_singleton()->run_play_custom(scene_path);
+	EditorRunBar::get_singleton()->play_custom_scene(scene_path);
 }
 
 void EditorInterface::stop_playing_scene() {
-	EditorNode::get_singleton()->run_stop();
+	EditorRunBar::get_singleton()->stop_playing();
 }
 
 bool EditorInterface::is_playing_scene() const {
-	return EditorNode::get_singleton()->is_run_playing();
+	return EditorRunBar::get_singleton()->is_playing();
 }
 
 String EditorInterface::get_playing_scene() const {
-	return EditorNode::get_singleton()->get_run_playing_scene();
+	return EditorRunBar::get_singleton()->get_playing_scene();
 }
 
 void EditorInterface::set_movie_maker_enabled(bool p_enabled) {
-	EditorNode::get_singleton()->set_movie_maker_enabled(p_enabled);
+	EditorRunBar::get_singleton()->set_movie_maker_enabled(p_enabled);
 }
 
 bool EditorInterface::is_movie_maker_enabled() const {
-	return EditorNode::get_singleton()->is_movie_maker_enabled();
+	return EditorRunBar::get_singleton()->is_movie_maker_enabled();
 }
 
 // Base.
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index f2f12857999..fc1ab9ef18c 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -53,6 +53,8 @@
 #include "scene/gui/menu_button.h"
 #include "scene/gui/panel.h"
 #include "scene/gui/panel_container.h"
+#include "scene/gui/popup.h"
+#include "scene/gui/rich_text_label.h"
 #include "scene/gui/split_container.h"
 #include "scene/gui/tab_bar.h"
 #include "scene/gui/tab_container.h"
@@ -99,6 +101,7 @@
 #include "editor/fbx_importer_manager.h"
 #include "editor/filesystem_dock.h"
 #include "editor/gui/editor_file_dialog.h"
+#include "editor/gui/editor_run_bar.h"
 #include "editor/gui/editor_title_bar.h"
 #include "editor/gui/editor_toaster.h"
 #include "editor/history_dock.h"
@@ -802,20 +805,6 @@ void EditorNode::_notification(int p_what) {
 
 			_build_icon_type_cache();
 
-			if (write_movie_button->is_pressed()) {
-				launch_pad->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("LaunchPadMovieMode"), SNAME("EditorStyles")));
-				write_movie_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("MovieWriterButtonPressed"), SNAME("EditorStyles")));
-			} else {
-				launch_pad->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
-				write_movie_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
-			}
-
-			play_button->set_icon(gui_base->get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
-			play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons")));
-			play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons")));
-			pause_button->set_icon(gui_base->get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
-			stop_button->set_icon(gui_base->get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
-
 			prev_scene->set_icon(gui_base->get_theme_icon(SNAME("PrevScene"), SNAME("EditorIcons")));
 			distraction_free->set_icon(gui_base->get_theme_icon(SNAME("DistractionFree"), SNAME("EditorIcons")));
 			scene_tab_add->set_icon(gui_base->get_theme_icon(SNAME("Add"), SNAME("EditorIcons")));
@@ -1194,7 +1183,7 @@ void EditorNode::_vp_resized() {
 }
 
 void EditorNode::_titlebar_resized() {
-	DisplayServer::get_singleton()->window_set_window_buttons_offset(Vector2i(menu_hb->get_global_position().y + menu_hb->get_size().y / 2, menu_hb->get_global_position().y + menu_hb->get_size().y / 2), DisplayServer::MAIN_WINDOW_ID);
+	DisplayServer::get_singleton()->window_set_window_buttons_offset(Vector2i(title_bar->get_global_position().y + title_bar->get_size().y / 2, title_bar->get_global_position().y + title_bar->get_size().y / 2), DisplayServer::MAIN_WINDOW_ID);
 	const Vector3i &margin = DisplayServer::get_singleton()->window_get_safe_title_margins(DisplayServer::MAIN_WINDOW_ID);
 	if (left_menu_spacer) {
 		int w = (gui_base->is_layout_rtl()) ? margin.y : margin.x;
@@ -1204,8 +1193,8 @@ void EditorNode::_titlebar_resized() {
 		int w = (gui_base->is_layout_rtl()) ? margin.x : margin.y;
 		right_menu_spacer->set_custom_minimum_size(Size2(w, 0));
 	}
-	if (menu_hb) {
-		menu_hb->set_custom_minimum_size(Size2(0, margin.z - menu_hb->get_global_position().y));
+	if (title_bar) {
+		title_bar->set_custom_minimum_size(Size2(0, margin.z - title_bar->get_global_position().y));
 	}
 }
 
@@ -1814,7 +1803,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
 	editor_data.apply_changes_in_editors();
 	List<Ref<AnimatedValuesBackup>> anim_backups;
 	_reset_animation_players(scene, &anim_backups);
-	_save_default_environment();
+	save_default_environment();
 
 	_set_scene_metadata(p_file, idx);
 
@@ -1876,7 +1865,7 @@ void EditorNode::_save_scene(String p_file, int idx) {
 }
 
 void EditorNode::save_all_scenes() {
-	_menu_option_confirm(RUN_STOP, true);
+	project_run_bar->stop_playing();
 	_save_all_scenes();
 }
 
@@ -1890,11 +1879,33 @@ void EditorNode::save_scene_list(Vector<String> p_scene_filenames) {
 	}
 }
 
+void EditorNode::save_before_run() {
+	current_menu_option = FILE_SAVE_AND_RUN;
+	_menu_option_confirm(FILE_SAVE_AS_SCENE, true);
+	file->set_title(TTR("Save scene before running..."));
+}
+
+void EditorNode::try_autosave() {
+	if (!bool(EDITOR_GET("run/auto_save/save_before_running"))) {
+		return;
+	}
+
+	if (unsaved_cache) {
+		Node *scene = editor_data.get_edited_scene_root();
+
+		if (scene && !scene->get_scene_file_path().is_empty()) { // Only autosave if there is a scene and if it has a path.
+			_save_scene_with_preview(scene->get_scene_file_path());
+		}
+	}
+	_menu_option(FILE_SAVE_ALL_SCENES);
+	editor_data.save_editor_external_data();
+}
+
 void EditorNode::restart_editor() {
 	exiting = true;
 
-	if (editor_run.get_status() != EditorRun::STATUS_STOP) {
-		editor_run.stop();
+	if (project_run_bar->is_playing()) {
+		project_run_bar->stop_playing();
 	}
 
 	String to_reopen;
@@ -1942,7 +1953,7 @@ void EditorNode::_save_all_scenes() {
 	if (!all_saved) {
 		show_warning(TTR("Could not save one or more scenes!"), TTR("Save All Scenes"));
 	}
-	_save_default_environment();
+	save_default_environment();
 }
 
 void EditorNode::_mark_unsaved_scenes() {
@@ -1982,11 +1993,7 @@ void EditorNode::_dialog_action(String p_file) {
 			ProjectSettings::get_singleton()->save();
 			// TODO: Would be nice to show the project manager opened with the highlighted field.
 
-			if ((bool)pick_main_scene->get_meta("from_native", false)) {
-				run_native->resume_run_native();
-			} else {
-				_run(false, ""); // Automatically run the project.
-			}
+			project_run_bar->play_main_scene((bool)pick_main_scene->get_meta("from_native", false));
 		} break;
 		case FILE_CLOSE:
 		case SCENE_TAB_CLOSE:
@@ -2007,7 +2014,7 @@ void EditorNode::_dialog_action(String p_file) {
 					return;
 				}
 
-				_save_default_environment();
+				save_default_environment();
 				_save_scene_with_preview(p_file, scene_idx);
 				_add_to_recent_scenes(p_file);
 				save_layout();
@@ -2021,9 +2028,9 @@ void EditorNode::_dialog_action(String p_file) {
 
 		case FILE_SAVE_AND_RUN: {
 			if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
-				_save_default_environment();
+				save_default_environment();
 				_save_scene_with_preview(p_file);
-				_run(false, p_file);
+				project_run_bar->play_custom_scene(p_file);
 			}
 		} break;
 
@@ -2032,13 +2039,9 @@ void EditorNode::_dialog_action(String p_file) {
 			ProjectSettings::get_singleton()->save();
 
 			if (file->get_file_mode() == EditorFileDialog::FILE_MODE_SAVE_FILE) {
-				_save_default_environment();
+				save_default_environment();
 				_save_scene_with_preview(p_file);
-				if ((bool)pick_main_scene->get_meta("from_native", false)) {
-					run_native->resume_run_native();
-				} else {
-					_run(false, p_file);
-				}
+				project_run_bar->play_main_scene((bool)pick_main_scene->get_meta("from_native", false));
 			}
 		} break;
 
@@ -2250,7 +2253,7 @@ void EditorNode::push_item(Object *p_object, const String &p_property, bool p_in
 	_edit_current();
 }
 
-void EditorNode::_save_default_environment() {
+void EditorNode::save_default_environment() {
 	Ref<Environment> fallback = get_tree()->get_root()->get_world_3d()->get_fallback_environment();
 
 	if (fallback.is_valid() && fallback->get_path().is_resource_file()) {
@@ -2496,157 +2499,6 @@ void EditorNode::_edit_current(bool p_skip_foreign) {
 	InspectorDock::get_singleton()->update(current_obj);
 }
 
-void EditorNode::_write_movie_toggled(bool p_enabled) {
-	if (p_enabled) {
-		launch_pad->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("LaunchPadMovieMode"), SNAME("EditorStyles")));
-		write_movie_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("MovieWriterButtonPressed"), SNAME("EditorStyles")));
-	} else {
-		launch_pad->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
-		write_movie_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
-	}
-}
-
-void EditorNode::_run(bool p_current, const String &p_custom) {
-	if (editor_run.get_status() == EditorRun::STATUS_PLAY) {
-		play_button->set_pressed(!_playing_edited);
-		play_scene_button->set_pressed(_playing_edited);
-		return;
-	}
-
-	play_button->set_pressed(false);
-	play_button->set_icon(gui_base->get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
-	play_scene_button->set_pressed(false);
-	play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons")));
-	play_custom_scene_button->set_pressed(false);
-	play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons")));
-
-	String write_movie_file;
-	if (write_movie_button->is_pressed()) {
-		if (p_current && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->has_meta("movie_file")) {
-			// If the scene file has a movie_file metadata set, use this as file. Quick workaround if you want to have multiple scenes that write to multiple movies.
-			write_movie_file = get_tree()->get_edited_scene_root()->get_meta("movie_file");
-		} else {
-			write_movie_file = GLOBAL_GET("editor/movie_writer/movie_file");
-		}
-		if (write_movie_file == String()) {
-			show_accept(TTR("Movie Maker mode is enabled, but no movie file path has been specified.\nA default movie file path can be specified in the project settings under the Editor > Movie Writer category.\nAlternatively, for running single scenes, a `movie_file` string metadata can be added to the root node,\nspecifying the path to a movie file that will be used when recording that scene."), TTR("OK"));
-			return;
-		}
-	}
-
-	String run_filename;
-
-	if ((p_current && p_custom.is_empty()) || (editor_data.get_edited_scene_root() && !p_custom.is_empty() && p_custom == editor_data.get_edited_scene_root()->get_scene_file_path())) {
-		Node *scene = editor_data.get_edited_scene_root();
-
-		if (!scene) {
-			show_accept(TTR("There is no defined scene to run."), TTR("OK"));
-			return;
-		}
-
-		if (scene->get_scene_file_path().is_empty()) {
-			current_menu_option = FILE_SAVE_AND_RUN;
-			_menu_option_confirm(FILE_SAVE_AS_SCENE, true);
-			file->set_title(TTR("Save scene before running..."));
-			return;
-		}
-
-		run_filename = scene->get_scene_file_path();
-	} else if (!p_custom.is_empty()) {
-		run_filename = p_custom;
-	}
-
-	if (run_filename.is_empty()) {
-		// Evidently, run the scene.
-		if (!ensure_main_scene(false)) {
-			return;
-		}
-		run_filename = GLOBAL_DEF_BASIC("application/run/main_scene", "");
-	}
-
-	if (bool(EDITOR_GET("run/auto_save/save_before_running"))) {
-		if (unsaved_cache) {
-			Node *scene = editor_data.get_edited_scene_root();
-
-			if (scene && !scene->get_scene_file_path().is_empty()) { // Only autosave if there is a scene and if it has a path.
-				_save_scene_with_preview(scene->get_scene_file_path());
-			}
-		}
-		_menu_option(FILE_SAVE_ALL_SCENES);
-		editor_data.save_editor_external_data();
-	}
-
-	if (!call_build()) {
-		return;
-	}
-
-	if (bool(EDITOR_GET("run/output/always_clear_output_on_play"))) {
-		log->clear();
-	}
-
-	if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) {
-		make_bottom_panel_item_visible(log);
-	}
-
-	EditorDebuggerNode::get_singleton()->start();
-	Error error = editor_run.run(run_filename, write_movie_file);
-	if (error != OK) {
-		EditorDebuggerNode::get_singleton()->stop();
-		show_accept(TTR("Could not start subprocess(es)!"), TTR("OK"));
-		return;
-	}
-
-	emit_signal(SNAME("play_pressed"));
-	if (p_current) {
-		run_current_filename = run_filename;
-		play_scene_button->set_pressed(true);
-		play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
-		play_scene_button->set_tooltip_text(TTR("Reload the played scene."));
-	} else if (!p_custom.is_empty()) {
-		run_custom_filename = p_custom;
-		play_custom_scene_button->set_pressed(true);
-		play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
-		play_custom_scene_button->set_tooltip_text(TTR("Reload the played scene."));
-	} else {
-		play_button->set_pressed(true);
-		play_button->set_icon(gui_base->get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
-		play_button->set_tooltip_text(TTR("Reload the played scene."));
-	}
-	stop_button->set_disabled(false);
-
-	_playing_edited = p_current;
-}
-
-void EditorNode::_run_native(const Ref<EditorExportPreset> &p_preset) {
-	bool autosave = EDITOR_GET("run/auto_save/save_before_running");
-	if (autosave) {
-		_menu_option_confirm(FILE_SAVE_ALL_SCENES, false);
-	}
-	if (run_native->is_deploy_debug_remote_enabled()) {
-		_menu_option_confirm(RUN_STOP, true);
-
-		if (!call_build()) {
-			return; // Build failed.
-		}
-
-		EditorDebuggerNode::get_singleton()->start(p_preset->get_platform()->get_debug_protocol());
-		emit_signal(SNAME("play_pressed"));
-		editor_run.run_native_notify();
-	}
-}
-
-void EditorNode::_reset_play_buttons() {
-	play_button->set_pressed(false);
-	play_button->set_icon(gui_base->get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
-	play_button->set_tooltip_text(TTR("Play the project."));
-	play_scene_button->set_pressed(false);
-	play_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons")));
-	play_scene_button->set_tooltip_text(TTR("Play the edited scene."));
-	play_custom_scene_button->set_pressed(false);
-	play_custom_scene_button->set_icon(gui_base->get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons")));
-	play_custom_scene_button->set_tooltip_text(TTR("Play a custom scene."));
-}
-
 void EditorNode::_android_build_source_selected(const String &p_file) {
 	export_template_manager->install_android_template_from_file(p_file);
 }
@@ -2813,6 +2665,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 			_save_all_scenes();
 		} break;
 
+		case FILE_RUN_SCENE: {
+			project_run_bar->play_current_scene();
+		} break;
+
 		case FILE_EXPORT_PROJECT: {
 			project_export->popup_export();
 		} break;
@@ -2916,45 +2772,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 			EditorUndoRedoManager::get_singleton()->clear_history(false, editor_data.get_current_edited_scene_history_id());
 			scene_tabs->set_current_tab(cur_idx);
 
-		} break;
-		case RUN_PLAY: {
-			run_play();
-
-		} break;
-		case RUN_PLAY_CUSTOM_SCENE: {
-			if (run_custom_filename.is_empty() || editor_run.get_status() == EditorRun::STATUS_STOP) {
-				_menu_option_confirm(RUN_STOP, true);
-				quick_run->popup_dialog("PackedScene", true);
-				quick_run->set_title(TTR("Quick Run Scene..."));
-				play_custom_scene_button->set_pressed(false);
-			} else {
-				String last_custom_scene = run_custom_filename; // This is necessary to have a copy of the string.
-				run_play_custom(last_custom_scene);
-			}
-
-		} break;
-		case RUN_STOP: {
-			if (editor_run.get_status() == EditorRun::STATUS_STOP) {
-				break;
-			}
-
-			editor_run.stop();
-			run_custom_filename.clear();
-			run_current_filename.clear();
-			stop_button->set_disabled(true);
-			_reset_play_buttons();
-
-			if (bool(EDITOR_GET("run/output/always_close_output_on_stop"))) {
-				for (int i = 0; i < bottom_panel_items.size(); i++) {
-					if (bottom_panel_items[i].control == log) {
-						_bottom_panel_switch(false, i);
-						break;
-					}
-				}
-			}
-			EditorDebuggerNode::get_singleton()->stop();
-			emit_signal(SNAME("stop_pressed"));
-
 		} break;
 
 		case FILE_SHOW_IN_FILESYSTEM: {
@@ -2964,15 +2781,6 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
 			}
 		} break;
 
-		case RUN_PLAY_SCENE: {
-			if (run_current_filename.is_empty() || editor_run.get_status() == EditorRun::STATUS_STOP) {
-				run_play_current();
-			} else {
-				String last_current_scene = run_current_filename; // This is necessary to have a copy of the string.
-				run_play_custom(last_current_scene);
-			}
-
-		} break;
 		case RUN_SETTINGS: {
 			project_settings_editor->popup_project_settings();
 		} break;
@@ -3296,12 +3104,12 @@ void EditorNode::_discard_changes(const String &p_str) {
 			_proceed_closing_scene_tabs();
 		} break;
 		case FILE_QUIT: {
-			_menu_option_confirm(RUN_STOP, true);
+			project_run_bar->stop_playing();
 			_exit_editor(EXIT_SUCCESS);
 
 		} break;
 		case RUN_PROJECT_MANAGER: {
-			_menu_option_confirm(RUN_STOP, true);
+			project_run_bar->stop_playing();
 			_exit_editor(EXIT_SUCCESS);
 			String exec = OS::get_singleton()->get_executable_path();
 
@@ -3584,14 +3392,6 @@ bool EditorNode::is_addon_plugin_enabled(const String &p_addon) const {
 	return addon_name_to_plugin.has("res://addons/" + p_addon + "/plugin.cfg");
 }
 
-void EditorNode::set_movie_maker_enabled(bool p_enabled) {
-	write_movie_button->set_pressed(p_enabled);
-}
-
-bool EditorNode::is_movie_maker_enabled() const {
-	return write_movie_button->is_pressed();
-}
-
 void EditorNode::_remove_edited_scene(bool p_change_tab) {
 	int new_index = editor_data.get_edited_scene();
 	int old_index = new_index;
@@ -4260,14 +4060,31 @@ void EditorNode::_quick_opened() {
 	}
 }
 
-void EditorNode::_quick_run() {
-	_run(false, quick_run->get_selected());
+void EditorNode::_project_run_started() {
+	if (bool(EDITOR_GET("run/output/always_clear_output_on_play"))) {
+		log->clear();
+	}
+
+	if (bool(EDITOR_GET("run/output/always_open_output_on_play"))) {
+		make_bottom_panel_item_visible(log);
+	}
+}
+
+void EditorNode::_project_run_stopped() {
+	if (!bool(EDITOR_GET("run/output/always_close_output_on_stop"))) {
+		return;
+	}
+
+	for (int i = 0; i < bottom_panel_items.size(); i++) {
+		if (bottom_panel_items[i].control == log) {
+			_bottom_panel_switch(false, i);
+			break;
+		}
+	}
 }
 
 void EditorNode::notify_all_debug_sessions_exited() {
-	_menu_option_confirm(RUN_STOP, false);
-	stop_button->set_pressed(false);
-	editor_run.stop();
+	project_run_bar->stop_playing();
 }
 
 void EditorNode::add_io_error(const String &p_error) {
@@ -4305,13 +4122,12 @@ bool EditorNode::is_scene_in_use(const String &p_path) {
 	return false;
 }
 
+OS::ProcessID EditorNode::has_child_process(OS::ProcessID p_pid) const {
+	return project_run_bar->has_child_process(p_pid);
+}
+
 void EditorNode::stop_child_process(OS::ProcessID p_pid) {
-	if (has_child_process(p_pid)) {
-		editor_run.stop_child_process(p_pid);
-		if (!editor_run.get_child_process_count()) { // All children stopped. Closing.
-			_menu_option_confirm(RUN_STOP, false);
-		}
-	}
+	project_run_bar->stop_child_process(p_pid);
 }
 
 Ref<Script> EditorNode::get_object_custom_type_base(const Object *p_object) const {
@@ -5274,45 +5090,6 @@ bool EditorNode::ensure_main_scene(bool p_from_native) {
 	return true;
 }
 
-Error EditorNode::run_play_native(int p_id) {
-	return run_native->run_native(p_id);
-}
-
-void EditorNode::run_play() {
-	_menu_option_confirm(RUN_STOP, true);
-	_run(false);
-}
-
-void EditorNode::run_play_current() {
-	_save_default_environment();
-	_menu_option_confirm(RUN_STOP, true);
-	_run(true);
-}
-
-void EditorNode::run_play_custom(const String &p_custom) {
-	bool is_current = !run_current_filename.is_empty();
-	_menu_option_confirm(RUN_STOP, true);
-	_run(is_current, p_custom);
-}
-
-void EditorNode::run_stop() {
-	_menu_option_confirm(RUN_STOP, false);
-}
-
-bool EditorNode::is_run_playing() const {
-	EditorRun::Status status = editor_run.get_status();
-	return (status == EditorRun::STATUS_PLAY || status == EditorRun::STATUS_PAUSED);
-}
-
-String EditorNode::get_run_playing_scene() const {
-	String run_filename = editor_run.get_running_scene();
-	if (run_filename.is_empty() && is_run_playing()) {
-		run_filename = GLOBAL_GET("application/run/main_scene"); // Must be the main scene then.
-	}
-
-	return run_filename;
-}
-
 void EditorNode::_immediate_dialog_confirmed() {
 	immediate_dialog_confirmed = true;
 }
@@ -5533,7 +5310,7 @@ void EditorNode::_scene_tab_input(const Ref<InputEvent> &p_input) {
 			if (scene_tabs->get_hovered_tab() >= 0) {
 				scene_tabs_context_menu->add_separator();
 				scene_tabs_context_menu->add_item(TTR("Show in FileSystem"), FILE_SHOW_IN_FILESYSTEM);
-				scene_tabs_context_menu->add_item(TTR("Play This Scene"), RUN_PLAY_SCENE);
+				scene_tabs_context_menu->add_item(TTR("Play This Scene"), FILE_RUN_SCENE);
 
 				scene_tabs_context_menu->add_separator();
 				scene_tabs_context_menu->add_shortcut(ED_GET_SHORTCUT("editor/close_scene"), FILE_CLOSE);
@@ -6546,8 +6323,6 @@ void EditorNode::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("get_gui_base"), &EditorNode::get_gui_base);
 
-	ADD_SIGNAL(MethodInfo("play_pressed"));
-	ADD_SIGNAL(MethodInfo("stop_pressed"));
 	ADD_SIGNAL(MethodInfo("request_help_search"));
 	ADD_SIGNAL(MethodInfo("script_add_function_request", PropertyInfo(Variant::OBJECT, "obj"), PropertyInfo(Variant::STRING, "function"), PropertyInfo(Variant::PACKED_STRING_ARRAY, "args")));
 	ADD_SIGNAL(MethodInfo("resource_saved", PropertyInfo(Variant::OBJECT, "obj")));
@@ -6931,8 +6706,8 @@ EditorNode::EditorNode() {
 	main_vbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT, Control::PRESET_MODE_MINSIZE, 8);
 	main_vbox->add_theme_constant_override("separation", 8 * EDSCALE);
 
-	menu_hb = memnew(EditorTitleBar);
-	main_vbox->add_child(menu_hb);
+	title_bar = memnew(EditorTitleBar);
+	main_vbox->add_child(title_bar);
 
 	left_l_hsplit = memnew(HSplitContainer);
 	main_vbox->add_child(left_l_hsplit);
@@ -7172,11 +6947,11 @@ EditorNode::EditorNode() {
 		// Add spacer to avoid other controls under window minimize/maximize/close buttons (left side).
 		left_menu_spacer = memnew(Control);
 		left_menu_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
-		menu_hb->add_child(left_menu_spacer);
+		title_bar->add_child(left_menu_spacer);
 	}
 
 	main_menu = memnew(MenuBar);
-	menu_hb->add_child(main_menu);
+	title_bar->add_child(main_menu);
 
 	main_menu->add_theme_style_override("hover", gui_base->get_theme_stylebox(SNAME("MenuHover"), SNAME("EditorStyles")));
 	main_menu->set_flat(true);
@@ -7352,7 +7127,7 @@ EditorNode::EditorNode() {
 	HBoxContainer *left_spacer = memnew(HBoxContainer);
 	left_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
 	left_spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	menu_hb->add_child(left_spacer);
+	title_bar->add_child(left_spacer);
 
 	if (can_expand && global_menu) {
 		project_title = memnew(Label);
@@ -7367,7 +7142,7 @@ EditorNode::EditorNode() {
 	}
 
 	main_editor_button_hb = memnew(HBoxContainer);
-	menu_hb->add_child(main_editor_button_hb);
+	title_bar->add_child(main_editor_button_hb);
 
 	// Options are added and handled by DebuggerEditorPlugin.
 	debug_menu = memnew(PopupMenu);
@@ -7452,104 +7227,15 @@ EditorNode::EditorNode() {
 	Control *right_spacer = memnew(Control);
 	right_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
 	right_spacer->set_h_size_flags(Control::SIZE_EXPAND_FILL);
-	menu_hb->add_child(right_spacer);
+	title_bar->add_child(right_spacer);
 
-	launch_pad = memnew(PanelContainer);
-	launch_pad->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
-	menu_hb->add_child(launch_pad);
-
-	HBoxContainer *launch_pad_hb = memnew(HBoxContainer);
-	launch_pad->add_child(launch_pad_hb);
-
-	play_button = memnew(Button);
-	play_button->set_flat(true);
-	launch_pad_hb->add_child(play_button);
-	play_button->set_toggle_mode(true);
-	play_button->set_focus_mode(Control::FOCUS_NONE);
-	play_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_PLAY));
-	play_button->set_tooltip_text(TTR("Run the project's default scene."));
-
-	ED_SHORTCUT_AND_COMMAND("editor/run_project", TTR("Run Project"), Key::F5);
-	ED_SHORTCUT_OVERRIDE("editor/run_project", "macos", KeyModifierMask::META | Key::B);
-	play_button->set_shortcut(ED_GET_SHORTCUT("editor/run_project"));
-
-	pause_button = memnew(Button);
-	pause_button->set_flat(true);
-	pause_button->set_toggle_mode(true);
-	pause_button->set_icon(gui_base->get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
-	pause_button->set_focus_mode(Control::FOCUS_NONE);
-	pause_button->set_tooltip_text(TTR("Pause the running project's execution for debugging."));
-	pause_button->set_disabled(true);
-	launch_pad_hb->add_child(pause_button);
-
-	ED_SHORTCUT("editor/pause_running_project", TTR("Pause Running Project"), Key::F7);
-	ED_SHORTCUT_OVERRIDE("editor/pause_running_project", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::Y);
-	pause_button->set_shortcut(ED_GET_SHORTCUT("editor/pause_running_project"));
-
-	stop_button = memnew(Button);
-	stop_button->set_flat(true);
-	launch_pad_hb->add_child(stop_button);
-	stop_button->set_focus_mode(Control::FOCUS_NONE);
-	stop_button->set_icon(gui_base->get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
-	stop_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_STOP));
-	stop_button->set_tooltip_text(TTR("Stop the currently running project."));
-	stop_button->set_disabled(true);
-
-	ED_SHORTCUT("editor/stop_running_project", TTR("Stop Running Project"), Key::F8);
-	ED_SHORTCUT_OVERRIDE("editor/stop_running_project", "macos", KeyModifierMask::META | Key::PERIOD);
-	stop_button->set_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));
-
-	run_native = memnew(EditorRunNative);
-	launch_pad_hb->add_child(run_native);
-	run_native->connect("native_run", callable_mp(this, &EditorNode::_run_native));
-
-	play_scene_button = memnew(Button);
-	play_scene_button->set_flat(true);
-	launch_pad_hb->add_child(play_scene_button);
-	play_scene_button->set_toggle_mode(true);
-	play_scene_button->set_focus_mode(Control::FOCUS_NONE);
-	play_scene_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_PLAY_SCENE));
-	play_scene_button->set_tooltip_text(TTR("Run the currently edited scene."));
-
-	ED_SHORTCUT_AND_COMMAND("editor/run_current_scene", TTR("Run Current Scene"), Key::F6);
-	ED_SHORTCUT_OVERRIDE("editor/run_current_scene", "macos", KeyModifierMask::META | Key::R);
-	play_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_current_scene"));
-
-	play_custom_scene_button = memnew(Button);
-	play_custom_scene_button->set_flat(true);
-	launch_pad_hb->add_child(play_custom_scene_button);
-	play_custom_scene_button->set_toggle_mode(true);
-	play_custom_scene_button->set_focus_mode(Control::FOCUS_NONE);
-	play_custom_scene_button->connect("pressed", callable_mp(this, &EditorNode::_menu_option).bind(RUN_PLAY_CUSTOM_SCENE));
-	play_custom_scene_button->set_tooltip_text(TTR("Run a specific scene."));
-
-	_reset_play_buttons();
-
-	ED_SHORTCUT_AND_COMMAND("editor/run_specific_scene", TTR("Run Specific Scene"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F5);
-	ED_SHORTCUT_OVERRIDE("editor/run_specific_scene", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::R);
-	play_custom_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_specific_scene"));
-
-	write_movie_panel = memnew(PanelContainer);
-	write_movie_panel->add_theme_style_override("panel", gui_base->get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
-	launch_pad_hb->add_child(write_movie_panel);
-
-	write_movie_button = memnew(Button);
-	write_movie_button->set_flat(true);
-	write_movie_button->set_toggle_mode(true);
-	write_movie_panel->add_child(write_movie_button);
-	write_movie_button->set_pressed(false);
-	write_movie_button->set_icon(gui_base->get_theme_icon(SNAME("MainMovieWrite"), SNAME("EditorIcons")));
-	write_movie_button->set_focus_mode(Control::FOCUS_NONE);
-	write_movie_button->connect("toggled", callable_mp(this, &EditorNode::_write_movie_toggled));
-	write_movie_button->set_tooltip_text(TTR("Enable Movie Maker mode.\nThe project will run at stable FPS and the visual and audio output will be recorded to a video file."));
-
-	// This button behaves differently, so color it as such.
-	write_movie_button->add_theme_color_override("icon_normal_color", Color(1, 1, 1, 0.7));
-	write_movie_button->add_theme_color_override("icon_pressed_color", Color(0, 0, 0, 0.84));
-	write_movie_button->add_theme_color_override("icon_hover_color", Color(1, 1, 1, 0.9));
+	project_run_bar = memnew(EditorRunBar);
+	title_bar->add_child(project_run_bar);
+	project_run_bar->connect("play_pressed", callable_mp(this, &EditorNode::_project_run_started));
+	project_run_bar->connect("stop_pressed", callable_mp(this, &EditorNode::_project_run_stopped));
 
 	HBoxContainer *right_menu_hb = memnew(HBoxContainer);
-	menu_hb->add_child(right_menu_hb);
+	title_bar->add_child(right_menu_hb);
 
 	renderer = memnew(OptionButton);
 	renderer->set_visible(true);
@@ -7567,7 +7253,7 @@ EditorNode::EditorNode() {
 		// Add spacer to avoid other controls under the window minimize/maximize/close buttons (right side).
 		right_menu_spacer = memnew(Control);
 		right_menu_spacer->set_mouse_filter(Control::MOUSE_FILTER_PASS);
-		menu_hb->add_child(right_menu_spacer);
+		title_bar->add_child(right_menu_spacer);
 	}
 
 	String current_renderer = GLOBAL_GET("rendering/renderer/rendering_method");
@@ -8004,10 +7690,6 @@ EditorNode::EditorNode() {
 	gui_base->add_child(quick_open);
 	quick_open->connect("quick_open", callable_mp(this, &EditorNode::_quick_opened));
 
-	quick_run = memnew(EditorQuickOpen);
-	gui_base->add_child(quick_run);
-	quick_run->connect("quick_open", callable_mp(this, &EditorNode::_quick_run));
-
 	_update_recent_scenes();
 
 	set_process_shortcut_input(true);
@@ -8079,14 +7761,14 @@ EditorNode::EditorNode() {
 	screenshot_timer->set_owner(get_owner());
 
 	// Adjust spacers to center 2D / 3D / Script buttons.
-	int max_w = MAX(launch_pad->get_minimum_size().x + right_menu_hb->get_minimum_size().x, main_menu->get_minimum_size().x);
+	int max_w = MAX(project_run_bar->get_minimum_size().x + right_menu_hb->get_minimum_size().x, main_menu->get_minimum_size().x);
 	left_spacer->set_custom_minimum_size(Size2(MAX(0, max_w - main_menu->get_minimum_size().x), 0));
-	right_spacer->set_custom_minimum_size(Size2(MAX(0, max_w - launch_pad->get_minimum_size().x - right_menu_hb->get_minimum_size().x), 0));
+	right_spacer->set_custom_minimum_size(Size2(MAX(0, max_w - project_run_bar->get_minimum_size().x - right_menu_hb->get_minimum_size().x), 0));
 
 	// Extend menu bar to window title.
 	if (can_expand) {
 		DisplayServer::get_singleton()->window_set_flag(DisplayServer::WINDOW_FLAG_EXTEND_TO_TITLE, true, DisplayServer::MAIN_WINDOW_ID);
-		menu_hb->set_can_move_window(true);
+		title_bar->set_can_move_window(true);
 	}
 
 	String exec = OS::get_singleton()->get_executable_path();
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 831e2989f59..0003fee3014 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -35,8 +35,6 @@
 #include "editor/editor_data.h"
 #include "editor/editor_folding.h"
 #include "editor/editor_plugin.h"
-#include "editor/editor_run.h"
-#include "editor/export/editor_export.h"
 
 typedef void (*EditorNodeInitCallback)();
 typedef void (*EditorPluginInitializeCallback)();
@@ -59,6 +57,8 @@ class Node2D;
 class OptionButton;
 class Panel;
 class PanelContainer;
+class PopupPanel;
+class RichTextLabel;
 class SubViewport;
 class TabBar;
 class TabContainer;
@@ -92,6 +92,7 @@ class EditorQuickOpen;
 class EditorPropertyResource;
 class EditorResourcePreview;
 class EditorResourceConversionPlugin;
+class EditorRunBar;
 class EditorRunNative;
 class EditorSelectionHistory;
 class EditorSettingsDialog;
@@ -162,6 +163,7 @@ private:
 		FILE_SAVE_ALL_SCENES,
 		FILE_SAVE_AND_RUN,
 		FILE_SAVE_AND_RUN_MAIN_SCENE,
+		FILE_RUN_SCENE,
 		FILE_SHOW_IN_FILESYSTEM,
 		FILE_EXPORT_PROJECT,
 		FILE_EXPORT_MESH_LIBRARY,
@@ -188,11 +190,7 @@ private:
 		TOOLS_CUSTOM,
 		RESOURCE_SAVE,
 		RESOURCE_SAVE_AS,
-		RUN_PLAY,
 
-		RUN_STOP,
-		RUN_PLAY_SCENE,
-		RUN_PLAY_CUSTOM_SCENE,
 		RUN_SETTINGS,
 		RUN_USER_DATA_FOLDER,
 		RELOAD_CURRENT_PROJECT,
@@ -265,7 +263,6 @@ private:
 
 	EditorData editor_data;
 	EditorFolding editor_folding;
-	EditorRun editor_run;
 	EditorSelectionHistory editor_history;
 
 	EditorCommandPalette *command_palette = nullptr;
@@ -277,9 +274,7 @@ private:
 	EditorPluginList *editor_plugins_force_over = nullptr;
 	EditorPluginList *editor_plugins_over = nullptr;
 	EditorQuickOpen *quick_open = nullptr;
-	EditorQuickOpen *quick_run = nullptr;
 	EditorResourcePreview *resource_preview = nullptr;
-	EditorRunNative *run_native = nullptr;
 	EditorSelection *editor_selection = nullptr;
 	EditorSettingsDialog *editor_settings_dialog = nullptr;
 	HistoryDock *history_dock = nullptr;
@@ -342,7 +337,8 @@ private:
 	Label *project_title = nullptr;
 	Control *left_menu_spacer = nullptr;
 	Control *right_menu_spacer = nullptr;
-	EditorTitleBar *menu_hb = nullptr;
+	EditorTitleBar *title_bar = nullptr;
+	EditorRunBar *project_run_bar = nullptr;
 	VBoxContainer *main_screen_vbox = nullptr;
 	MenuBar *main_menu = nullptr;
 	PopupMenu *file_menu = nullptr;
@@ -357,15 +353,6 @@ private:
 	Button *search_button = nullptr;
 	TextureProgressBar *audio_vu = nullptr;
 
-	PanelContainer *launch_pad = nullptr;
-	Button *play_button = nullptr;
-	Button *pause_button = nullptr;
-	Button *stop_button = nullptr;
-	Button *play_scene_button = nullptr;
-	Button *play_custom_scene_button = nullptr;
-	PanelContainer *write_movie_panel = nullptr;
-	Button *write_movie_button = nullptr;
-
 	Timer *screenshot_timer = nullptr;
 
 	PluginConfigDialog *plugin_config_dialog = nullptr;
@@ -469,7 +456,6 @@ private:
 	bool scene_distraction_free = false;
 	bool script_distraction_free = false;
 
-	bool _playing_edited = false;
 	bool changing_scene = false;
 	bool cmdline_export_mode = false;
 	bool convert_old = false;
@@ -496,9 +482,6 @@ private:
 	String external_file;
 	String open_navigate;
 
-	String run_custom_filename;
-	String run_current_filename;
-
 	DynamicFontImportSettings *fontdata_import_settings = nullptr;
 	SceneImportSettings *scene_import_settings = nullptr;
 	AudioStreamImportSettings *audio_stream_import_settings = nullptr;
@@ -600,14 +583,10 @@ private:
 	void _instantiate_request(const Vector<String> &p_files);
 
 	void _quick_opened();
-	void _quick_run();
 	void _open_command_palette();
 
-	void _write_movie_toggled(bool p_enabled);
-
-	void _run(bool p_current = false, const String &p_custom = "");
-	void _run_native(const Ref<EditorExportPreset> &p_preset);
-	void _reset_play_buttons();
+	void _project_run_started();
+	void _project_run_stopped();
 
 	void _add_to_recent_scenes(const String &p_scene);
 	void _update_recent_scenes();
@@ -687,7 +666,6 @@ private:
 	void _inherit_imported(const String &p_action);
 	void _open_imported();
 
-	void _save_default_environment();
 	void _update_update_spinner();
 
 	void _resources_changed(const Vector<String> &p_resources);
@@ -717,7 +695,6 @@ protected:
 	friend class FileSystemDock;
 
 	static void _bind_methods();
-
 	void _notification(int p_what);
 
 	int get_current_tab();
@@ -738,7 +715,7 @@ public:
 	static EditorData &get_editor_data() { return singleton->editor_data; }
 	static EditorFolding &get_editor_folding() { return singleton->editor_folding; }
 
-	static EditorTitleBar *get_menu_hb() { return singleton->menu_hb; }
+	static EditorTitleBar *get_title_bar() { return singleton->title_bar; }
 	static VSplitContainer *get_top_split() { return singleton->top_split; }
 
 	static String adjust_scene_name_casing(const String &root_name);
@@ -789,9 +766,6 @@ public:
 	void set_addon_plugin_enabled(const String &p_addon, bool p_enabled, bool p_config_changed = false);
 	bool is_addon_plugin_enabled(const String &p_addon) const;
 
-	void set_movie_maker_enabled(bool p_enabled);
-	bool is_movie_maker_enabled() const;
-
 	void edit_node(Node *p_node);
 	void edit_resource(const Ref<Resource> &p_resource);
 
@@ -874,7 +848,7 @@ public:
 
 	void notify_all_debug_sessions_exited();
 
-	OS::ProcessID has_child_process(OS::ProcessID p_pid) const { return editor_run.has_child_process(p_pid); }
+	OS::ProcessID has_child_process(OS::ProcessID p_pid) const;
 	void stop_child_process(OS::ProcessID p_pid);
 
 	Ref<Theme> get_editor_theme() const { return theme; }
@@ -908,6 +882,7 @@ public:
 	bool is_scene_in_use(const String &p_path);
 
 	void save_layout();
+	void save_default_environment();
 
 	void open_export_template_manager();
 
@@ -918,8 +893,6 @@ public:
 
 	bool is_exiting() const { return exiting; }
 
-	Button *get_pause_button() { return pause_button; }
-
 	Button *add_bottom_panel_item(String p_text, Control *p_item);
 	void make_bottom_panel_item_visible(Control *p_item);
 	void raise_bottom_panel_item(Control *p_item);
@@ -937,6 +910,8 @@ public:
 
 	void save_all_scenes();
 	void save_scene_list(Vector<String> p_scene_filenames);
+	void save_before_run();
+	void try_autosave();
 	void restart_editor();
 
 	void notify_settings_changed();
@@ -958,14 +933,6 @@ public:
 	Vector<Ref<EditorResourceConversionPlugin>> find_resource_conversion_plugin(const Ref<Resource> &p_for_resource);
 
 	bool ensure_main_scene(bool p_from_native);
-
-	Error run_play_native(int p_id);
-	void run_play();
-	void run_play_current();
-	void run_play_custom(const String &p_custom);
-	void run_stop();
-	bool is_run_playing() const;
-	String get_run_playing_scene() const;
 };
 
 struct EditorProgress {
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 8ac5e77d351..442524d5792 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -94,7 +94,7 @@ void EditorPlugin::add_control_to_container(CustomControlContainer p_location, C
 
 	switch (p_location) {
 		case CONTAINER_TOOLBAR: {
-			EditorNode::get_menu_hb()->add_child(p_control);
+			EditorNode::get_title_bar()->add_child(p_control);
 		} break;
 
 		case CONTAINER_SPATIAL_EDITOR_MENU: {
@@ -147,7 +147,7 @@ void EditorPlugin::remove_control_from_container(CustomControlContainer p_locati
 
 	switch (p_location) {
 		case CONTAINER_TOOLBAR: {
-			EditorNode::get_menu_hb()->remove_child(p_control);
+			EditorNode::get_title_bar()->remove_child(p_control);
 		} break;
 
 		case CONTAINER_SPATIAL_EDITOR_MENU: {
diff --git a/editor/editor_plugin_settings.cpp b/editor/editor_plugin_settings.cpp
index 9bcb25e9c03..1e582992d18 100644
--- a/editor/editor_plugin_settings.cpp
+++ b/editor/editor_plugin_settings.cpp
@@ -32,6 +32,7 @@
 
 #include "core/config/project_settings.h"
 #include "core/io/config_file.h"
+#include "core/io/dir_access.h"
 #include "core/io/file_access.h"
 #include "core/os/main_loop.h"
 #include "editor/editor_node.h"
diff --git a/editor/editor_run.h b/editor/editor_run.h
index 68c8742f790..bd6770ae3db 100644
--- a/editor/editor_run.h
+++ b/editor/editor_run.h
@@ -50,6 +50,7 @@ private:
 public:
 	Status get_status() const;
 	String get_running_scene() const;
+
 	Error run(const String &p_scene, const String &p_write_movie = "");
 	void run_native_notify() { status = STATUS_PLAY; }
 	void stop();
diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp
index 815d4fab9c4..beccf0f2ec9 100644
--- a/editor/editor_run_native.cpp
+++ b/editor/editor_run_native.cpp
@@ -33,6 +33,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
+#include "editor/export/editor_export.h"
 #include "editor/export/editor_export_platform.h"
 
 void EditorRunNative::_notification(int p_what) {
@@ -77,7 +78,7 @@ void EditorRunNative::_notification(int p_what) {
 	}
 }
 
-Error EditorRunNative::run_native(int p_id) {
+Error EditorRunNative::start_run_native(int p_id) {
 	if (p_id < 0) {
 		return OK;
 	}
@@ -142,7 +143,7 @@ Error EditorRunNative::run_native(int p_id) {
 }
 
 void EditorRunNative::resume_run_native() {
-	run_native(resume_id);
+	start_run_native(resume_id);
 }
 
 void EditorRunNative::_bind_methods() {
@@ -155,7 +156,7 @@ bool EditorRunNative::is_deploy_debug_remote_enabled() const {
 
 EditorRunNative::EditorRunNative() {
 	remote_debug = memnew(MenuButton);
-	remote_debug->get_popup()->connect("id_pressed", callable_mp(this, &EditorRunNative::run_native));
+	remote_debug->get_popup()->connect("id_pressed", callable_mp(this, &EditorRunNative::start_run_native));
 	remote_debug->set_tooltip_text(TTR("Remote Debug"));
 	remote_debug->set_disabled(true);
 
diff --git a/editor/editor_run_native.h b/editor/editor_run_native.h
index 2a5431e54b9..f52a455bb2a 100644
--- a/editor/editor_run_native.h
+++ b/editor/editor_run_native.h
@@ -52,11 +52,11 @@ protected:
 	void _notification(int p_what);
 
 public:
-	Error run_native(int p_id);
-	bool is_deploy_debug_remote_enabled() const;
-
+	Error start_run_native(int p_id);
 	void resume_run_native();
 
+	bool is_deploy_debug_remote_enabled() const;
+
 	EditorRunNative();
 };
 
diff --git a/editor/editor_run_script.cpp b/editor/editor_script.cpp
similarity index 87%
rename from editor/editor_run_script.cpp
rename to editor/editor_script.cpp
index a459943656a..4e8c5ad8b58 100644
--- a/editor/editor_run_script.cpp
+++ b/editor/editor_script.cpp
@@ -1,5 +1,5 @@
 /**************************************************************************/
-/*  editor_run_script.cpp                                                 */
+/*  editor_script.cpp                                                     */
 /**************************************************************************/
 /*                         This file is part of:                          */
 /*                             GODOT ENGINE                               */
@@ -28,18 +28,18 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /**************************************************************************/
 
-#include "editor_run_script.h"
+#include "editor_script.h"
 
 #include "editor/editor_interface.h"
 #include "editor/editor_node.h"
 
 void EditorScript::add_root_node(Node *p_node) {
-	if (!editor) {
+	if (!EditorNode::get_singleton()) {
 		EditorNode::add_io_error("EditorScript::add_root_node: " + TTR("Write your logic in the _run() method."));
 		return;
 	}
 
-	if (editor->get_edited_scene()) {
+	if (EditorNode::get_singleton()->get_edited_scene()) {
 		EditorNode::add_io_error("EditorScript::add_root_node: " + TTR("There is an edited scene already."));
 		return;
 	}
@@ -47,36 +47,29 @@ void EditorScript::add_root_node(Node *p_node) {
 	//editor->set_edited_scene(p_node);
 }
 
-EditorInterface *EditorScript::get_editor_interface() {
-	return EditorInterface::get_singleton();
-}
-
-Node *EditorScript::get_scene() {
-	if (!editor) {
+Node *EditorScript::get_scene() const {
+	if (!EditorNode::get_singleton()) {
 		EditorNode::add_io_error("EditorScript::get_scene: " + TTR("Write your logic in the _run() method."));
 		return nullptr;
 	}
 
-	return editor->get_edited_scene();
+	return EditorNode::get_singleton()->get_edited_scene();
 }
 
-void EditorScript::_run() {
+EditorInterface *EditorScript::get_editor_interface() const {
+	return EditorInterface::get_singleton();
+}
+
+void EditorScript::run() {
 	if (!GDVIRTUAL_CALL(_run)) {
 		EditorNode::add_io_error(TTR("Couldn't run editor script, did you forget to override the '_run' method?"));
 	}
 }
 
-void EditorScript::set_editor(EditorNode *p_editor) {
-	editor = p_editor;
-}
-
 void EditorScript::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("add_root_node", "node"), &EditorScript::add_root_node);
 	ClassDB::bind_method(D_METHOD("get_scene"), &EditorScript::get_scene);
 	ClassDB::bind_method(D_METHOD("get_editor_interface"), &EditorScript::get_editor_interface);
+
 	GDVIRTUAL_BIND(_run);
 }
-
-EditorScript::EditorScript() {
-	editor = nullptr;
-}
diff --git a/editor/editor_run_script.h b/editor/editor_script.h
similarity index 88%
rename from editor/editor_run_script.h
rename to editor/editor_script.h
index 8284d59110c..d7c813261d3 100644
--- a/editor/editor_run_script.h
+++ b/editor/editor_script.h
@@ -1,5 +1,5 @@
 /**************************************************************************/
-/*  editor_run_script.h                                                   */
+/*  editor_script.h                                                       */
 /**************************************************************************/
 /*                         This file is part of:                          */
 /*                             GODOT ENGINE                               */
@@ -28,8 +28,8 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
 /**************************************************************************/
 
-#ifndef EDITOR_RUN_SCRIPT_H
-#define EDITOR_RUN_SCRIPT_H
+#ifndef EDITOR_SCRIPT_H
+#define EDITOR_SCRIPT_H
 
 #include "core/object/gdvirtual.gen.inc"
 #include "core/object/ref_counted.h"
@@ -41,20 +41,19 @@ class EditorNode;
 class EditorScript : public RefCounted {
 	GDCLASS(EditorScript, RefCounted);
 
-	EditorNode *editor = nullptr;
-
 protected:
 	static void _bind_methods();
+
 	GDVIRTUAL0(_run)
 
 public:
 	void add_root_node(Node *p_node);
-	Node *get_scene();
-	EditorInterface *get_editor_interface();
-	virtual void _run();
+	Node *get_scene() const;
+	EditorInterface *get_editor_interface() const;
 
-	void set_editor(EditorNode *p_editor);
-	EditorScript();
+	virtual void run();
+
+	EditorScript() {}
 };
 
-#endif // EDITOR_RUN_SCRIPT_H
+#endif // EDITOR_SCRIPT_H
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index a21fb9fdfb3..660a9501a27 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -31,14 +31,13 @@
 #ifndef EDITOR_SETTINGS_H
 #define EDITOR_SETTINGS_H
 
+#include "core/input/shortcut.h"
 #include "core/io/config_file.h"
 #include "core/io/resource.h"
 #include "core/os/thread_safe.h"
 #include "core/templates/rb_set.h"
 
 class EditorPlugin;
-class InputEvent;
-class Shortcut;
 
 class EditorSettings : public Resource {
 	GDCLASS(EditorSettings, Resource);
diff --git a/editor/export/editor_export_platform.cpp b/editor/export/editor_export_platform.cpp
index 4e173799115..121088f27a5 100644
--- a/editor/export/editor_export_platform.cpp
+++ b/editor/export/editor_export_platform.cpp
@@ -42,6 +42,7 @@
 #include "editor/editor_paths.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
+#include "editor/export/editor_export.h"
 #include "editor/plugins/script_editor_plugin.h"
 #include "editor_export_plugin.h"
 #include "scene/resources/packed_scene.h"
diff --git a/editor/gui/editor_run_bar.cpp b/editor/gui/editor_run_bar.cpp
new file mode 100644
index 00000000000..3da4514d998
--- /dev/null
+++ b/editor/gui/editor_run_bar.cpp
@@ -0,0 +1,442 @@
+/**************************************************************************/
+/*  editor_run_bar.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_run_bar.h"
+
+#include "core/config/project_settings.h"
+#include "editor/debugger/editor_debugger_node.h"
+#include "editor/editor_command_palette.h"
+#include "editor/editor_node.h"
+#include "editor/editor_quick_open.h"
+#include "editor/editor_run_native.h"
+#include "editor/editor_settings.h"
+#include "scene/gui/box_container.h"
+#include "scene/gui/button.h"
+#include "scene/gui/panel_container.h"
+
+EditorRunBar *EditorRunBar::singleton = nullptr;
+
+void EditorRunBar::_notification(int p_what) {
+	switch (p_what) {
+		case NOTIFICATION_THEME_CHANGED: {
+			_update_play_buttons();
+			pause_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
+			stop_button->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
+
+			if (is_movie_maker_enabled()) {
+				main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadMovieMode"), SNAME("EditorStyles")));
+				write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonPressed"), SNAME("EditorStyles")));
+			} else {
+				main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
+				write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
+			}
+
+			write_movie_button->set_icon(get_theme_icon(SNAME("MainMovieWrite"), SNAME("EditorIcons")));
+			// This button behaves differently, so color it as such.
+			write_movie_button->add_theme_color_override("icon_normal_color", Color(1, 1, 1, 0.7));
+			write_movie_button->add_theme_color_override("icon_pressed_color", Color(0, 0, 0, 0.84));
+			write_movie_button->add_theme_color_override("icon_hover_color", Color(1, 1, 1, 0.9));
+		} break;
+	}
+}
+
+void EditorRunBar::_reset_play_buttons() {
+	play_button->set_pressed(false);
+	play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
+	play_button->set_tooltip_text(TTR("Play the project."));
+
+	play_scene_button->set_pressed(false);
+	play_scene_button->set_icon(get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons")));
+	play_scene_button->set_tooltip_text(TTR("Play the edited scene."));
+
+	play_custom_scene_button->set_pressed(false);
+	play_custom_scene_button->set_icon(get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons")));
+	play_custom_scene_button->set_tooltip_text(TTR("Play a custom scene."));
+}
+
+void EditorRunBar::_update_play_buttons() {
+	_reset_play_buttons();
+	if (!is_playing()) {
+		return;
+	}
+
+	Button *active_button = nullptr;
+	if (current_mode == RUN_CURRENT) {
+		active_button = play_scene_button;
+	} else if (current_mode == RUN_CUSTOM) {
+		active_button = play_custom_scene_button;
+	} else {
+		active_button = play_button;
+	}
+
+	if (active_button) {
+		active_button->set_pressed(true);
+		active_button->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
+		active_button->set_tooltip_text(TTR("Reload the played scene."));
+	}
+}
+
+void EditorRunBar::_write_movie_toggled(bool p_enabled) {
+	if (p_enabled) {
+		add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadMovieMode"), SNAME("EditorStyles")));
+		write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonPressed"), SNAME("EditorStyles")));
+	} else {
+		add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
+		write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
+	}
+}
+
+void EditorRunBar::_quick_run_selected() {
+	play_custom_scene(quick_run->get_selected());
+}
+
+void EditorRunBar::_play_custom_pressed() {
+	if (editor_run.get_status() == EditorRun::STATUS_STOP || current_mode != RunMode::RUN_CUSTOM) {
+		stop_playing();
+
+		quick_run->popup_dialog("PackedScene", true);
+		quick_run->set_title(TTR("Quick Run Scene..."));
+		play_custom_scene_button->set_pressed(false);
+	} else {
+		// Reload if already running a custom scene.
+		String last_custom_scene = run_custom_filename; // This is necessary to have a copy of the string.
+		play_custom_scene(last_custom_scene);
+	}
+}
+
+void EditorRunBar::_play_current_pressed() {
+	if (editor_run.get_status() == EditorRun::STATUS_STOP || current_mode != RunMode::RUN_CURRENT) {
+		play_current_scene();
+	} else {
+		// Reload if already running the current scene.
+		play_current_scene(true);
+	}
+}
+
+void EditorRunBar::_run_scene(const String &p_scene_path) {
+	ERR_FAIL_COND_MSG(current_mode == RUN_CUSTOM && p_scene_path.is_empty(), "Attempting to run a custom scene with an empty path.");
+
+	if (editor_run.get_status() == EditorRun::STATUS_PLAY) {
+		return;
+	}
+
+	_reset_play_buttons();
+
+	String write_movie_file;
+	if (is_movie_maker_enabled()) {
+		if (current_mode == RUN_CURRENT) {
+			Node *scene_root = nullptr;
+			if (p_scene_path.is_empty()) {
+				scene_root = get_tree()->get_edited_scene_root();
+			} else {
+				int scene_index = EditorNode::get_editor_data().get_edited_scene_from_path(p_scene_path);
+				if (scene_index >= 0) {
+					scene_root = EditorNode::get_editor_data().get_edited_scene_root(scene_index);
+				}
+			}
+
+			if (scene_root && scene_root->has_meta("movie_file")) {
+				// If the scene file has a movie_file metadata set, use this as file.
+				// Quick workaround if you want to have multiple scenes that write to
+				// multiple movies.
+				write_movie_file = scene_root->get_meta("movie_file");
+			}
+		}
+
+		if (write_movie_file.is_empty()) {
+			write_movie_file = GLOBAL_GET("editor/movie_writer/movie_file");
+		}
+
+		if (write_movie_file.is_empty()) {
+			// TODO: Provide options to diretly resolve the issue with a custom dialog.
+			EditorNode::get_singleton()->show_accept(TTR("Movie Maker mode is enabled, but no movie file path has been specified.\nA default movie file path can be specified in the project settings under the Editor > Movie Writer category.\nAlternatively, for running single scenes, a `movie_file` string metadata can be added to the root node,\nspecifying the path to a movie file that will be used when recording that scene."), TTR("OK"));
+			return;
+		}
+	}
+
+	String run_filename;
+	switch (current_mode) {
+		case RUN_CUSTOM: {
+			run_filename = p_scene_path;
+			run_custom_filename = run_filename;
+		} break;
+
+		case RUN_CURRENT: {
+			if (!p_scene_path.is_empty()) {
+				run_filename = p_scene_path;
+				run_current_filename = run_filename;
+				break;
+			}
+
+			Node *scene_root = get_tree()->get_edited_scene_root();
+			if (!scene_root) {
+				EditorNode::get_singleton()->show_accept(TTR("There is no defined scene to run."), TTR("OK"));
+				return;
+			}
+
+			if (scene_root->get_scene_file_path().is_empty()) {
+				EditorNode::get_singleton()->save_before_run();
+				return;
+			}
+
+			run_filename = scene_root->get_scene_file_path();
+			run_current_filename = run_filename;
+		} break;
+
+		default: {
+			if (!EditorNode::get_singleton()->ensure_main_scene(false)) {
+				return;
+			}
+
+			run_filename = GLOBAL_DEF_BASIC("application/run/main_scene", "");
+		} break;
+	}
+
+	EditorNode::get_singleton()->try_autosave();
+	if (!EditorNode::get_singleton()->call_build()) {
+		return;
+	}
+
+	EditorDebuggerNode::get_singleton()->start();
+	Error error = editor_run.run(run_filename, write_movie_file);
+	if (error != OK) {
+		EditorDebuggerNode::get_singleton()->stop();
+		EditorNode::get_singleton()->show_accept(TTR("Could not start subprocess(es)!"), TTR("OK"));
+		return;
+	}
+
+	_update_play_buttons();
+	stop_button->set_disabled(false);
+
+	emit_signal(SNAME("play_pressed"));
+}
+
+void EditorRunBar::_run_native(const Ref<EditorExportPreset> &p_preset) {
+	EditorNode::get_singleton()->try_autosave();
+
+	if (run_native->is_deploy_debug_remote_enabled()) {
+		stop_playing();
+
+		if (!EditorNode::get_singleton()->call_build()) {
+			return; // Build failed.
+		}
+
+		EditorDebuggerNode::get_singleton()->start(p_preset->get_platform()->get_debug_protocol());
+		emit_signal(SNAME("play_pressed"));
+		editor_run.run_native_notify();
+	}
+}
+
+void EditorRunBar::play_main_scene(bool p_from_native) {
+	if (p_from_native) {
+		run_native->resume_run_native();
+	} else {
+		stop_playing();
+
+		current_mode = RunMode::RUN_MAIN;
+		_run_scene();
+	}
+}
+
+void EditorRunBar::play_current_scene(bool p_reload) {
+	EditorNode::get_singleton()->save_default_environment();
+	stop_playing();
+
+	current_mode = RunMode::RUN_CURRENT;
+	if (p_reload) {
+		String last_current_scene = run_current_filename; // This is necessary to have a copy of the string.
+		_run_scene(last_current_scene);
+	} else {
+		_run_scene();
+	}
+}
+
+void EditorRunBar::play_custom_scene(const String &p_custom) {
+	stop_playing();
+
+	current_mode = RunMode::RUN_CUSTOM;
+	_run_scene(p_custom);
+}
+
+void EditorRunBar::stop_playing() {
+	if (editor_run.get_status() == EditorRun::STATUS_STOP) {
+		return;
+	}
+
+	current_mode = RunMode::STOPPED;
+	editor_run.stop();
+	EditorDebuggerNode::get_singleton()->stop();
+
+	run_custom_filename.clear();
+	run_current_filename.clear();
+	stop_button->set_pressed(false);
+	stop_button->set_disabled(true);
+	_reset_play_buttons();
+
+	emit_signal(SNAME("stop_pressed"));
+}
+
+bool EditorRunBar::is_playing() const {
+	EditorRun::Status status = editor_run.get_status();
+	return (status == EditorRun::STATUS_PLAY || status == EditorRun::STATUS_PAUSED);
+}
+
+String EditorRunBar::get_playing_scene() const {
+	String run_filename = editor_run.get_running_scene();
+	if (run_filename.is_empty() && is_playing()) {
+		run_filename = GLOBAL_GET("application/run/main_scene"); // Must be the main scene then.
+	}
+
+	return run_filename;
+}
+
+Error EditorRunBar::start_native_device(int p_device_id) {
+	return run_native->start_run_native(p_device_id);
+}
+
+OS::ProcessID EditorRunBar::has_child_process(OS::ProcessID p_pid) const {
+	return editor_run.has_child_process(p_pid);
+}
+
+void EditorRunBar::stop_child_process(OS::ProcessID p_pid) {
+	if (!has_child_process(p_pid)) {
+		return;
+	}
+
+	editor_run.stop_child_process(p_pid);
+	if (!editor_run.get_child_process_count()) { // All children stopped. Closing.
+		stop_playing();
+	}
+}
+
+void EditorRunBar::set_movie_maker_enabled(bool p_enabled) {
+	write_movie_button->set_pressed(p_enabled);
+}
+
+bool EditorRunBar::is_movie_maker_enabled() const {
+	return write_movie_button->is_pressed();
+}
+
+void EditorRunBar::_bind_methods() {
+	ADD_SIGNAL(MethodInfo("play_pressed"));
+	ADD_SIGNAL(MethodInfo("stop_pressed"));
+}
+
+EditorRunBar::EditorRunBar() {
+	singleton = this;
+
+	main_panel = memnew(PanelContainer);
+	add_child(main_panel);
+
+	HBoxContainer *main_hbox = memnew(HBoxContainer);
+	main_panel->add_child(main_hbox);
+
+	play_button = memnew(Button);
+	main_hbox->add_child(play_button);
+	play_button->set_flat(true);
+	play_button->set_toggle_mode(true);
+	play_button->set_focus_mode(Control::FOCUS_NONE);
+	play_button->set_tooltip_text(TTR("Run the project's default scene."));
+	play_button->connect("pressed", callable_mp(this, &EditorRunBar::play_main_scene).bind(false));
+
+	ED_SHORTCUT_AND_COMMAND("editor/run_project", TTR("Run Project"), Key::F5);
+	ED_SHORTCUT_OVERRIDE("editor/run_project", "macos", KeyModifierMask::META | Key::B);
+	play_button->set_shortcut(ED_GET_SHORTCUT("editor/run_project"));
+
+	pause_button = memnew(Button);
+	main_hbox->add_child(pause_button);
+	pause_button->set_flat(true);
+	pause_button->set_toggle_mode(true);
+	pause_button->set_focus_mode(Control::FOCUS_NONE);
+	pause_button->set_tooltip_text(TTR("Pause the running project's execution for debugging."));
+	pause_button->set_disabled(true);
+
+	ED_SHORTCUT("editor/pause_running_project", TTR("Pause Running Project"), Key::F7);
+	ED_SHORTCUT_OVERRIDE("editor/pause_running_project", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::Y);
+	pause_button->set_shortcut(ED_GET_SHORTCUT("editor/pause_running_project"));
+
+	stop_button = memnew(Button);
+	main_hbox->add_child(stop_button);
+	stop_button->set_flat(true);
+	stop_button->set_focus_mode(Control::FOCUS_NONE);
+	stop_button->set_tooltip_text(TTR("Stop the currently running project."));
+	stop_button->set_disabled(true);
+	stop_button->connect("pressed", callable_mp(this, &EditorRunBar::stop_playing));
+
+	ED_SHORTCUT("editor/stop_running_project", TTR("Stop Running Project"), Key::F8);
+	ED_SHORTCUT_OVERRIDE("editor/stop_running_project", "macos", KeyModifierMask::META | Key::PERIOD);
+	stop_button->set_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));
+
+	run_native = memnew(EditorRunNative);
+	main_hbox->add_child(run_native);
+	run_native->connect("native_run", callable_mp(this, &EditorRunBar::_run_native));
+
+	play_scene_button = memnew(Button);
+	main_hbox->add_child(play_scene_button);
+	play_scene_button->set_flat(true);
+	play_scene_button->set_toggle_mode(true);
+	play_scene_button->set_focus_mode(Control::FOCUS_NONE);
+	play_scene_button->set_tooltip_text(TTR("Run the currently edited scene."));
+	play_scene_button->connect("pressed", callable_mp(this, &EditorRunBar::_play_current_pressed));
+
+	ED_SHORTCUT_AND_COMMAND("editor/run_current_scene", TTR("Run Current Scene"), Key::F6);
+	ED_SHORTCUT_OVERRIDE("editor/run_current_scene", "macos", KeyModifierMask::META | Key::R);
+	play_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_current_scene"));
+
+	play_custom_scene_button = memnew(Button);
+	main_hbox->add_child(play_custom_scene_button);
+	play_custom_scene_button->set_flat(true);
+	play_custom_scene_button->set_toggle_mode(true);
+	play_custom_scene_button->set_focus_mode(Control::FOCUS_NONE);
+	play_custom_scene_button->set_tooltip_text(TTR("Run a specific scene."));
+	play_custom_scene_button->connect("pressed", callable_mp(this, &EditorRunBar::_play_custom_pressed));
+
+	ED_SHORTCUT_AND_COMMAND("editor/run_specific_scene", TTR("Run Specific Scene"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F5);
+	ED_SHORTCUT_OVERRIDE("editor/run_specific_scene", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::R);
+	play_custom_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_specific_scene"));
+
+	write_movie_panel = memnew(PanelContainer);
+	main_hbox->add_child(write_movie_panel);
+
+	write_movie_button = memnew(Button);
+	write_movie_panel->add_child(write_movie_button);
+	write_movie_button->set_flat(true);
+	write_movie_button->set_toggle_mode(true);
+	write_movie_button->set_pressed(false);
+	write_movie_button->set_focus_mode(Control::FOCUS_NONE);
+	write_movie_button->set_tooltip_text(TTR("Enable Movie Maker mode.\nThe project will run at stable FPS and the visual and audio output will be recorded to a video file."));
+	write_movie_button->connect("toggled", callable_mp(this, &EditorRunBar::_write_movie_toggled));
+
+	_reset_play_buttons();
+
+	quick_run = memnew(EditorQuickOpen);
+	add_child(quick_run);
+	quick_run->connect("quick_open", callable_mp(this, &EditorRunBar::_quick_run_selected));
+}
diff --git a/editor/gui/editor_run_bar.h b/editor/gui/editor_run_bar.h
new file mode 100644
index 00000000000..b7e7db2bd6a
--- /dev/null
+++ b/editor/gui/editor_run_bar.h
@@ -0,0 +1,115 @@
+/**************************************************************************/
+/*  editor_run_bar.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_RUN_BAR_H
+#define EDITOR_RUN_BAR_H
+
+#include "editor/editor_run.h"
+#include "editor/export/editor_export.h"
+#include "scene/gui/margin_container.h"
+
+class Button;
+class EditorRunNative;
+class EditorQuickOpen;
+class PanelContainer;
+
+class EditorRunBar : public MarginContainer {
+	GDCLASS(EditorRunBar, MarginContainer);
+
+	static EditorRunBar *singleton;
+
+	enum RunMode {
+		STOPPED = 0,
+		RUN_MAIN,
+		RUN_CURRENT,
+		RUN_CUSTOM,
+	};
+
+	PanelContainer *main_panel = nullptr;
+
+	Button *play_button = nullptr;
+	Button *pause_button = nullptr;
+	Button *stop_button = nullptr;
+	Button *play_scene_button = nullptr;
+	Button *play_custom_scene_button = nullptr;
+
+	EditorRun editor_run;
+	EditorRunNative *run_native = nullptr;
+
+	PanelContainer *write_movie_panel = nullptr;
+	Button *write_movie_button = nullptr;
+
+	EditorQuickOpen *quick_run = nullptr;
+
+	RunMode current_mode = RunMode::STOPPED;
+	String run_custom_filename;
+	String run_current_filename;
+
+	void _reset_play_buttons();
+	void _update_play_buttons();
+
+	void _write_movie_toggled(bool p_enabled);
+	void _quick_run_selected();
+
+	void _play_current_pressed();
+	void _play_custom_pressed();
+
+	void _run_scene(const String &p_scene_path = "");
+	void _run_native(const Ref<EditorExportPreset> &p_preset);
+
+protected:
+	void _notification(int p_what);
+	static void _bind_methods();
+
+public:
+	static EditorRunBar *get_singleton() { return singleton; }
+
+	void play_main_scene(bool p_from_native = false);
+	void play_current_scene(bool p_reload = false);
+	void play_custom_scene(const String &p_custom);
+
+	void stop_playing();
+	bool is_playing() const;
+	String get_playing_scene() const;
+
+	Error start_native_device(int p_device_id);
+
+	OS::ProcessID has_child_process(OS::ProcessID p_pid) const;
+	void stop_child_process(OS::ProcessID p_pid);
+
+	void set_movie_maker_enabled(bool p_enabled);
+	bool is_movie_maker_enabled() const;
+
+	Button *get_pause_button() { return pause_button; }
+
+	EditorRunBar();
+};
+
+#endif // EDITOR_RUN_BAR_H
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index eef1fea11be..f9ab37dce22 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -38,6 +38,7 @@
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_run_bar.h"
 #include "editor/gui/editor_toaster.h"
 #include "editor/gui/editor_zoom_widget.h"
 #include "editor/plugins/animation_player_editor_plugin.h"
@@ -4975,8 +4976,8 @@ CanvasItemEditor::CanvasItemEditor() {
 	SceneTreeDock::get_singleton()->connect("node_created", callable_mp(this, &CanvasItemEditor::_node_created));
 	SceneTreeDock::get_singleton()->connect("add_node_used", callable_mp(this, &CanvasItemEditor::_reset_create_position));
 
-	EditorNode::get_singleton()->call_deferred(SNAME("connect"), "play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true));
-	EditorNode::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false));
+	EditorRunBar::get_singleton()->call_deferred(SNAME("connect"), "play_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(true));
+	EditorRunBar::get_singleton()->call_deferred(SNAME("connect"), "stop_pressed", callable_mp(this, &CanvasItemEditor::_update_override_camera_button).bind(false));
 
 	// A fluid container for all toolbars.
 	HFlowContainer *main_flow = memnew(HFlowContainer);
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index a7ec4bfe33e..ba2d7e67bf0 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -39,6 +39,7 @@
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "scene/gui/popup_menu.h"
 
 CurveEditor::CurveEditor() {
 	_selected_point = -1;
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index d5cc0cb66a6..ca1a824f0c7 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -36,6 +36,8 @@
 #include "editor/editor_resource_preview.h"
 #include "scene/resources/curve.h"
 
+class PopupMenu;
+
 // Edits a y(x) curve
 class CurveEditor : public Control {
 	GDCLASS(CurveEditor, Control);
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
index 328fe9b9508..2fdebc7c7fe 100644
--- a/editor/plugins/material_editor_plugin.cpp
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -42,6 +42,7 @@
 #include "scene/gui/color_rect.h"
 #include "scene/gui/subviewport_container.h"
 #include "scene/gui/texture_button.h"
+#include "scene/main/viewport.h"
 #include "scene/resources/fog_material.h"
 #include "scene/resources/particle_process_material.h"
 #include "scene/resources/sky_material.h"
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 7225c1b6581..4fc3c5588d5 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -40,6 +40,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_run_bar.h"
 #include "editor/gui/editor_spin_slider.h"
 #include "editor/plugins/animation_player_editor_plugin.h"
 #include "editor/plugins/node_3d_editor_gizmos.h"
@@ -7487,8 +7488,8 @@ void Node3DEditor::_notification(int p_what) {
 			SceneTreeDock::get_singleton()->get_tree_editor()->connect("node_changed", callable_mp(this, &Node3DEditor::_refresh_menu_icons));
 			editor_selection->connect("selection_changed", callable_mp(this, &Node3DEditor::_selection_changed));
 
-			EditorNode::get_singleton()->connect("stop_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(false));
-			EditorNode::get_singleton()->connect("play_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(true));
+			EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(false));
+			EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &Node3DEditor::_update_camera_override_button).bind(true));
 
 			_update_preview_environment();
 
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 736826c231c..7be4d76f1e2 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -43,12 +43,13 @@
 #include "editor/editor_help_search.h"
 #include "editor/editor_node.h"
 #include "editor/editor_paths.h"
-#include "editor/editor_run_script.h"
 #include "editor/editor_scale.h"
+#include "editor/editor_script.h"
 #include "editor/editor_settings.h"
 #include "editor/filesystem_dock.h"
 #include "editor/find_in_files.h"
 #include "editor/gui/editor_file_dialog.h"
+#include "editor/gui/editor_run_bar.h"
 #include "editor/inspector_dock.h"
 #include "editor/node_dock.h"
 #include "editor/plugins/shader_editor_plugin.h"
@@ -1375,9 +1376,7 @@ void ScriptEditor::_menu_option(int p_option) {
 
 				Ref<EditorScript> es = memnew(EditorScript);
 				es->set_script(scr);
-				es->set_editor(EditorNode::get_singleton());
-
-				es->_run();
+				es->run();
 			} break;
 			case FILE_CLOSE: {
 				if (current->is_unsaved()) {
@@ -1592,7 +1591,7 @@ void ScriptEditor::_tab_changed(int p_which) {
 void ScriptEditor::_notification(int p_what) {
 	switch (p_what) {
 		case NOTIFICATION_ENTER_TREE: {
-			EditorNode::get_singleton()->connect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
+			EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
 			EditorNode::get_singleton()->connect("script_add_function_request", callable_mp(this, &ScriptEditor::_add_callback));
 			EditorNode::get_singleton()->connect("resource_saved", callable_mp(this, &ScriptEditor::_res_saved_callback));
 			EditorNode::get_singleton()->connect("scene_saved", callable_mp(this, &ScriptEditor::_scene_saved_callback));
@@ -1646,7 +1645,7 @@ void ScriptEditor::_notification(int p_what) {
 		} break;
 
 		case NOTIFICATION_EXIT_TREE: {
-			EditorNode::get_singleton()->disconnect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
+			EditorRunBar::get_singleton()->disconnect("stop_pressed", callable_mp(this, &ScriptEditor::_editor_stop));
 		} break;
 
 		case NOTIFICATION_APPLICATION_FOCUS_IN: {
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 9b84c33ff27..63b63090842 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -38,6 +38,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
+#include "scene/gui/rich_text_label.h"
 #include "scene/gui/split_container.h"
 
 void ConnectionInfoDialog::ok_pressed() {
diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp
index 5c159d54ac1..671be80ed3e 100644
--- a/editor/project_settings_editor.cpp
+++ b/editor/project_settings_editor.cpp
@@ -36,6 +36,7 @@
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/export/editor_export.h"
 #include "scene/gui/check_button.h"
 #include "servers/movie_writer/movie_writer.h"
 
diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp
index 724d435f68e..1f35d63564e 100644
--- a/editor/register_editor_types.cpp
+++ b/editor/register_editor_types.cpp
@@ -40,11 +40,13 @@
 #include "editor/editor_paths.h"
 #include "editor/editor_resource_picker.h"
 #include "editor/editor_resource_preview.h"
-#include "editor/editor_run_script.h"
+#include "editor/editor_script.h"
 #include "editor/editor_settings.h"
 #include "editor/editor_translation_parser.h"
 #include "editor/editor_undo_redo_manager.h"
+#include "editor/export/editor_export_platform.h"
 #include "editor/export/editor_export_platform_pc.h"
+#include "editor/export/editor_export_plugin.h"
 #include "editor/filesystem_dock.h"
 #include "editor/gui/editor_file_dialog.h"
 #include "editor/gui/editor_spin_slider.h"
@@ -137,7 +139,9 @@ void register_editor_types() {
 	GDREGISTER_CLASS(EditorExportPlugin);
 	GDREGISTER_ABSTRACT_CLASS(EditorExportPlatform);
 	GDREGISTER_ABSTRACT_CLASS(EditorExportPlatformPC);
+
 	register_exporter_types();
+
 	GDREGISTER_CLASS(EditorResourceConversionPlugin);
 	GDREGISTER_CLASS(EditorSceneFormatImporter);
 	GDREGISTER_CLASS(EditorScenePostImportPlugin);
diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
index 5f6ec5904cf..554fe5a4221 100644
--- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
+++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp
@@ -37,6 +37,7 @@
 #include "editor/editor_file_system.h"
 #include "editor/editor_node.h"
 #include "editor/gui/editor_file_dialog.h"
+#include "scene/gui/popup_menu.h"
 
 String SceneExporterGLTFPlugin::get_name() const {
 	return "ConvertGLTF2";
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index ad9ab66cc91..34b9974d102 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -42,6 +42,7 @@
 #include "editor/editor_paths.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
+#include "editor/gui/editor_run_bar.h"
 #include "editor/plugins/script_editor_plugin.h"
 #include "main/main.h"
 
@@ -156,11 +157,11 @@ void godot_icall_Internal_EditorNodeShowScriptScreen() {
 }
 
 void godot_icall_Internal_EditorRunPlay() {
-	EditorNode::get_singleton()->run_play();
+	EditorRunBar::get_singleton()->play_main_scene();
 }
 
 void godot_icall_Internal_EditorRunStop() {
-	EditorNode::get_singleton()->run_stop();
+	EditorRunBar::get_singleton()->stop_playing();
 }
 
 void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index b6d70048e36..92ba4dba4e7 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -33,6 +33,7 @@
 #include "core/string/translation.h"
 #include "editor/editor_node.h"
 #include "editor/editor_scale.h"
+#include "editor/export/editor_export.h"
 #include "platform/ios/logo_svg.gen.h"
 
 #include "modules/modules_enabled.gen.h" // For svg.
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index 317c6575b33..9544cc761df 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -34,6 +34,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_paths.h"
 #include "editor/editor_scale.h"
+#include "editor/export/editor_export.h"
 #include "platform/linuxbsd/logo_svg.gen.h"
 #include "platform/linuxbsd/run_icon_svg.gen.h"
 
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index 85964c53e6c..c20e3316a5a 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -31,6 +31,7 @@
 #include "export.h"
 
 #include "editor/editor_settings.h"
+#include "editor/export/editor_export.h"
 #include "export_plugin.h"
 
 void register_uwp_exporter_types() {
diff --git a/platform/web/export/export.cpp b/platform/web/export/export.cpp
index 54d9774da58..80c29024a84 100644
--- a/platform/web/export/export.cpp
+++ b/platform/web/export/export.cpp
@@ -31,6 +31,7 @@
 #include "export.h"
 
 #include "editor/editor_settings.h"
+#include "editor/export/editor_export.h"
 #include "export_plugin.h"
 
 void register_web_exporter_types() {
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index 7a62cd2a4ad..876efdf8643 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -33,6 +33,7 @@
 #include "core/config/project_settings.h"
 #include "editor/editor_scale.h"
 #include "editor/editor_settings.h"
+#include "editor/export/editor_export.h"
 #include "platform/web/logo_svg.gen.h"
 #include "platform/web/run_icon_svg.gen.h"
 
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 05b25eae031..1863a3083b7 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -35,6 +35,7 @@
 #include "editor/editor_node.h"
 #include "editor/editor_paths.h"
 #include "editor/editor_scale.h"
+#include "editor/export/editor_export.h"
 #include "platform/windows/logo_svg.gen.h"
 #include "platform/windows/run_icon_svg.gen.h"