2023-04-13 03:02:28 +08:00
/**************************************************************************/
/* 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 ) {
2023-04-28 00:49:16 +08:00
case NOTIFICATION_POSTINITIALIZE : {
_reset_play_buttons ( ) ;
} break ;
2023-04-13 03:02:28 +08:00
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 ( ) ) {
2023-04-26 19:57:02 +08:00
// TODO: Provide options to directly resolve the issue with a custom dialog.
2023-04-13 03:02:28 +08:00
EditorNode : : get_singleton ( ) - > show_accept ( TTR ( " Movie Maker mode is enabled, but no movie file path has been specified. \n A default movie file path can be specified in the project settings under the Editor > Movie Writer category. \n Alternatively, for running single scenes, a `movie_file` string metadata can be added to the root node, \n specifying 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. \n The 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 ) ) ;
quick_run = memnew ( EditorQuickOpen ) ;
add_child ( quick_run ) ;
quick_run - > connect ( " quick_open " , callable_mp ( this , & EditorRunBar : : _quick_run_selected ) ) ;
}