2014-02-10 01:10:30 +00:00
/**************************************************************************/
/* shader_editor_plugin.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. */
/**************************************************************************/
2018-01-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "shader_editor_plugin.h"
2016-03-08 23:00:52 +00:00
2022-11-02 14:23:25 +00:00
# include "editor/editor_command_palette.h"
2017-03-05 13:21:25 +00:00
# include "editor/editor_node.h"
2023-08-13 00:33:39 +00:00
# include "editor/editor_string_names.h"
2022-08-29 10:10:32 +00:00
# include "editor/editor_undo_redo_manager.h"
2022-05-27 09:02:55 +00:00
# include "editor/filesystem_dock.h"
2024-01-30 22:22:22 +00:00
# include "editor/gui/editor_bottom_panel.h"
2022-11-19 11:45:49 +00:00
# include "editor/inspector_dock.h"
2022-09-03 04:15:21 +00:00
# include "editor/plugins/text_shader_editor.h"
2022-05-27 09:02:55 +00:00
# include "editor/plugins/visual_shader_editor_plugin.h"
# include "editor/shader_create_dialog.h"
2024-01-15 12:14:55 +00:00
# include "editor/themes/editor_scale.h"
2022-11-02 14:23:25 +00:00
# include "editor/window_wrapper.h"
2022-11-19 11:45:49 +00:00
# include "scene/gui/item_list.h"
# include "scene/gui/texture_rect.h"
2014-02-10 01:10:30 +00:00
2024-08-18 04:54:58 +00:00
Ref < Resource > ShaderEditorPlugin : : _get_current_shader ( ) {
int index = shader_tabs - > get_current_tab ( ) ;
ERR_FAIL_INDEX_V ( index , shader_tabs - > get_tab_count ( ) , Ref < Resource > ( ) ) ;
if ( edited_shaders [ index ] . shader . is_valid ( ) ) {
return edited_shaders [ index ] . shader ;
} else {
return edited_shaders [ index ] . shader_inc ;
}
}
2022-05-27 09:02:55 +00:00
void ShaderEditorPlugin : : _update_shader_list ( ) {
shader_list - > clear ( ) ;
2022-12-29 00:24:45 +00:00
for ( EditedShader & edited_shader : edited_shaders ) {
Ref < Resource > shader = edited_shader . shader ;
2022-07-28 16:28:38 +00:00
if ( shader . is_null ( ) ) {
2022-12-29 00:24:45 +00:00
shader = edited_shader . shader_inc ;
2022-03-08 10:39:16 +00:00
}
2022-05-27 09:02:55 +00:00
2022-07-28 16:28:38 +00:00
String path = shader - > get_path ( ) ;
String text = path . get_file ( ) ;
if ( text . is_empty ( ) ) {
// This appears for newly created built-in shaders before saving the scene.
text = TTR ( " [unsaved] " ) ;
} else if ( shader - > is_built_in ( ) ) {
const String & shader_name = shader - > get_name ( ) ;
if ( ! shader_name . is_empty ( ) ) {
text = vformat ( " %s (%s) " , shader_name , text . get_slice ( " :: " , 0 ) ) ;
2022-03-08 10:39:16 +00:00
}
2022-05-27 09:02:55 +00:00
}
2023-10-11 09:49:29 +00:00
// When shader is deleted in filesystem dock, need this to correctly close shader editor.
2023-11-08 18:14:01 +00:00
edited_shader . path = path ;
2023-10-11 09:49:29 +00:00
2022-07-28 16:28:38 +00:00
bool unsaved = false ;
2022-12-29 00:24:45 +00:00
if ( edited_shader . shader_editor ) {
unsaved = edited_shader . shader_editor - > is_unsaved ( ) ;
2022-07-28 16:28:38 +00:00
}
// TODO: Handle visual shaders too.
if ( unsaved ) {
text + = " (*) " ;
}
String _class = shader - > get_class ( ) ;
2023-08-13 00:33:39 +00:00
if ( ! shader_list - > has_theme_icon ( _class , EditorStringName ( EditorIcons ) ) ) {
2022-03-08 10:39:16 +00:00
_class = " TextFile " ;
2022-05-27 09:02:55 +00:00
}
2023-08-13 00:33:39 +00:00
Ref < Texture2D > icon = shader_list - > get_editor_theme_icon ( _class ) ;
2022-05-27 09:02:55 +00:00
shader_list - > add_item ( text , icon ) ;
2024-01-02 15:00:11 +00:00
shader_list - > set_item_tooltip ( - 1 , path ) ;
edited_shader . name = text ;
2022-05-27 09:02:55 +00:00
}
if ( shader_tabs - > get_tab_count ( ) ) {
shader_list - > select ( shader_tabs - > get_current_tab ( ) ) ;
}
2024-08-18 04:54:58 +00:00
_set_file_specific_items_disabled ( edited_shaders . is_empty ( ) ) ;
2022-06-29 09:31:18 +00:00
_update_shader_list_status ( ) ;
}
void ShaderEditorPlugin : : _update_shader_list_status ( ) {
for ( int i = 0 ; i < shader_list - > get_item_count ( ) ; i + + ) {
2022-09-03 03:54:40 +00:00
TextShaderEditor * se = Object : : cast_to < TextShaderEditor > ( shader_tabs - > get_tab_control ( i ) ) ;
2022-06-29 09:31:18 +00:00
if ( se ) {
if ( se - > was_compilation_successful ( ) ) {
shader_list - > set_item_tag_icon ( i , Ref < Texture2D > ( ) ) ;
} else {
2023-08-13 00:33:39 +00:00
shader_list - > set_item_tag_icon ( i , shader_list - > get_editor_theme_icon ( SNAME ( " Error " ) ) ) ;
2022-06-29 09:31:18 +00:00
}
}
}
2022-05-27 09:02:55 +00:00
}
2022-08-10 02:12:31 +00:00
void ShaderEditorPlugin : : _move_shader_tab ( int p_from , int p_to ) {
if ( p_from = = p_to ) {
return ;
}
EditedShader es = edited_shaders [ p_from ] ;
edited_shaders . remove_at ( p_from ) ;
edited_shaders . insert ( p_to , es ) ;
shader_tabs - > move_child ( shader_tabs - > get_tab_control ( p_from ) , p_to ) ;
_update_shader_list ( ) ;
}
2014-02-10 01:10:30 +00:00
void ShaderEditorPlugin : : edit ( Object * p_object ) {
2023-01-21 21:49:06 +00:00
if ( ! p_object ) {
return ;
}
2022-05-27 09:02:55 +00:00
EditedShader es ;
2022-03-08 10:39:16 +00:00
ShaderInclude * si = Object : : cast_to < ShaderInclude > ( p_object ) ;
if ( si ! = nullptr ) {
for ( uint32_t i = 0 ; i < edited_shaders . size ( ) ; i + + ) {
if ( edited_shaders [ i ] . shader_inc . ptr ( ) = = si ) {
shader_tabs - > set_current_tab ( i ) ;
shader_list - > select ( i ) ;
return ;
}
}
es . shader_inc = Ref < ShaderInclude > ( si ) ;
2022-09-03 03:54:40 +00:00
es . shader_editor = memnew ( TextShaderEditor ) ;
2024-07-12 18:56:49 +00:00
es . shader_editor - > edit_shader_include ( si ) ;
2022-05-27 09:02:55 +00:00
shader_tabs - > add_child ( es . shader_editor ) ;
2022-03-08 10:39:16 +00:00
} else {
Shader * s = Object : : cast_to < Shader > ( p_object ) ;
for ( uint32_t i = 0 ; i < edited_shaders . size ( ) ; i + + ) {
if ( edited_shaders [ i ] . shader . ptr ( ) = = s ) {
shader_tabs - > set_current_tab ( i ) ;
shader_list - > select ( i ) ;
return ;
}
}
es . shader = Ref < Shader > ( s ) ;
Ref < VisualShader > vs = es . shader ;
if ( vs . is_valid ( ) ) {
2024-07-12 18:56:49 +00:00
es . shader_editor = memnew ( VisualShaderEditor ) ;
2022-03-08 10:39:16 +00:00
} else {
2022-09-03 03:54:40 +00:00
es . shader_editor = memnew ( TextShaderEditor ) ;
2024-02-18 02:16:58 +00:00
}
2024-07-12 18:56:49 +00:00
shader_tabs - > add_child ( es . shader_editor ) ;
es . shader_editor - > edit_shader ( es . shader ) ;
2024-02-18 02:16:58 +00:00
}
2024-07-12 18:56:49 +00:00
TextShaderEditor * text_shader_editor = Object : : cast_to < TextShaderEditor > ( es . shader_editor ) ;
if ( text_shader_editor ) {
text_shader_editor - > connect ( " validation_changed " , callable_mp ( this , & ShaderEditorPlugin : : _update_shader_list ) ) ;
CodeTextEditor * cte = text_shader_editor - > get_code_editor ( ) ;
2024-02-18 02:16:58 +00:00
if ( cte ) {
cte - > set_zoom_factor ( text_shader_zoom_factor ) ;
cte - > connect ( " zoomed " , callable_mp ( this , & ShaderEditorPlugin : : _set_text_shader_zoom_factor ) ) ;
2022-03-08 10:39:16 +00:00
}
2022-05-27 09:02:55 +00:00
}
2022-03-08 10:39:16 +00:00
2022-05-27 09:02:55 +00:00
shader_tabs - > set_current_tab ( shader_tabs - > get_tab_count ( ) - 1 ) ;
edited_shaders . push_back ( es ) ;
_update_shader_list ( ) ;
2014-02-10 01:10:30 +00:00
}
bool ShaderEditorPlugin : : handles ( Object * p_object ) const {
2022-03-08 10:39:16 +00:00
return Object : : cast_to < Shader > ( p_object ) ! = nullptr | | Object : : cast_to < ShaderInclude > ( p_object ) ! = nullptr ;
2014-02-10 01:10:30 +00:00
}
void ShaderEditorPlugin : : make_visible ( bool p_visible ) {
if ( p_visible ) {
2024-01-30 22:22:22 +00:00
EditorNode : : get_bottom_panel ( ) - > make_item_visible ( window_wrapper ) ;
2014-02-10 01:10:30 +00:00
}
}
void ShaderEditorPlugin : : selected_notify ( ) {
2022-05-27 09:02:55 +00:00
}
2024-07-12 18:56:49 +00:00
ShaderEditor * ShaderEditorPlugin : : get_shader_editor ( const Ref < Shader > & p_for_shader ) {
2023-05-11 02:17:03 +00:00
for ( EditedShader & edited_shader : edited_shaders ) {
if ( edited_shader . shader = = p_for_shader ) {
return edited_shader . shader_editor ;
}
}
return nullptr ;
}
2022-11-02 14:23:25 +00:00
void ShaderEditorPlugin : : set_window_layout ( Ref < ConfigFile > p_layout ) {
if ( EDITOR_GET ( " interface/multi_window/restore_windows_on_load " ) & & window_wrapper - > is_window_available ( ) & & p_layout - > has_section_key ( " ShaderEditor " , " window_rect " ) ) {
window_wrapper - > restore_window_from_saved_position (
p_layout - > get_value ( " ShaderEditor " , " window_rect " , Rect2i ( ) ) ,
p_layout - > get_value ( " ShaderEditor " , " window_screen " , - 1 ) ,
p_layout - > get_value ( " ShaderEditor " , " window_screen_rect " , Rect2i ( ) ) ) ;
} else {
window_wrapper - > set_window_enabled ( false ) ;
}
2023-05-11 02:17:03 +00:00
if ( ! bool ( EDITOR_GET ( " editors/shader_editor/behavior/files/restore_shaders_on_load " ) ) ) {
return ;
}
if ( ! p_layout - > has_section ( " ShaderEditor " ) ) {
return ;
}
if ( ! p_layout - > has_section_key ( " ShaderEditor " , " open_shaders " ) | |
! p_layout - > has_section_key ( " ShaderEditor " , " selected_shader " ) ) {
return ;
}
Array shaders = p_layout - > get_value ( " ShaderEditor " , " open_shaders " ) ;
int selected_shader_idx = 0 ;
String selected_shader = p_layout - > get_value ( " ShaderEditor " , " selected_shader " ) ;
for ( int i = 0 ; i < shaders . size ( ) ; i + + ) {
String path = shaders [ i ] ;
Ref < Resource > res = ResourceLoader : : load ( path ) ;
if ( res . is_valid ( ) ) {
edit ( res . ptr ( ) ) ;
}
if ( selected_shader = = path ) {
selected_shader_idx = i ;
}
}
if ( p_layout - > has_section_key ( " ShaderEditor " , " split_offset " ) ) {
main_split - > set_split_offset ( p_layout - > get_value ( " ShaderEditor " , " split_offset " ) ) ;
}
_update_shader_list ( ) ;
_shader_selected ( selected_shader_idx ) ;
2024-02-18 02:16:58 +00:00
_set_text_shader_zoom_factor ( p_layout - > get_value ( " ShaderEditor " , " text_shader_zoom_factor " , 1.0f ) ) ;
2022-11-02 14:23:25 +00:00
}
void ShaderEditorPlugin : : get_window_layout ( Ref < ConfigFile > p_layout ) {
if ( window_wrapper - > get_window_enabled ( ) ) {
p_layout - > set_value ( " ShaderEditor " , " window_rect " , window_wrapper - > get_window_rect ( ) ) ;
int screen = window_wrapper - > get_window_screen ( ) ;
p_layout - > set_value ( " ShaderEditor " , " window_screen " , screen ) ;
p_layout - > set_value ( " ShaderEditor " , " window_screen_rect " , DisplayServer : : get_singleton ( ) - > screen_get_usable_rect ( screen ) ) ;
} else {
if ( p_layout - > has_section_key ( " ShaderEditor " , " window_rect " ) ) {
p_layout - > erase_section_key ( " ShaderEditor " , " window_rect " ) ;
}
if ( p_layout - > has_section_key ( " ShaderEditor " , " window_screen " ) ) {
p_layout - > erase_section_key ( " ShaderEditor " , " window_screen " ) ;
}
if ( p_layout - > has_section_key ( " ShaderEditor " , " window_screen_rect " ) ) {
p_layout - > erase_section_key ( " ShaderEditor " , " window_screen_rect " ) ;
}
}
2023-05-11 02:17:03 +00:00
Array shaders ;
String selected_shader ;
for ( int i = 0 ; i < shader_tabs - > get_tab_count ( ) ; i + + ) {
EditedShader edited_shader = edited_shaders [ i ] ;
2024-07-12 18:56:49 +00:00
if ( edited_shader . shader_editor ) {
2023-05-25 07:39:25 +00:00
String shader_path ;
if ( edited_shader . shader . is_valid ( ) ) {
shader_path = edited_shader . shader - > get_path ( ) ;
} else {
DEV_ASSERT ( edited_shader . shader_inc . is_valid ( ) ) ;
shader_path = edited_shader . shader_inc - > get_path ( ) ;
}
shaders . push_back ( shader_path ) ;
2014-02-10 01:10:30 +00:00
2024-07-12 18:56:49 +00:00
ShaderEditor * shader_editor = Object : : cast_to < ShaderEditor > ( shader_tabs - > get_current_tab_control ( ) ) ;
2023-05-11 02:17:03 +00:00
2024-07-12 18:56:49 +00:00
if ( shader_editor & & edited_shader . shader_editor = = shader_editor ) {
2023-05-25 07:39:25 +00:00
selected_shader = shader_path ;
2023-05-11 02:17:03 +00:00
}
2022-07-16 06:59:20 +00:00
}
}
2023-05-11 02:17:03 +00:00
p_layout - > set_value ( " ShaderEditor " , " open_shaders " , shaders ) ;
p_layout - > set_value ( " ShaderEditor " , " split_offset " , main_split - > get_split_offset ( ) ) ;
p_layout - > set_value ( " ShaderEditor " , " selected_shader " , selected_shader ) ;
2024-02-18 02:16:58 +00:00
p_layout - > set_value ( " ShaderEditor " , " text_shader_zoom_factor " , text_shader_zoom_factor ) ;
2022-07-16 06:59:20 +00:00
}
2023-04-05 13:45:41 +00:00
String ShaderEditorPlugin : : get_unsaved_status ( const String & p_for_scene ) const {
2022-10-16 17:30:38 +00:00
// TODO: This should also include visual shaders and shader includes, but save_external_data() doesn't seem to save them...
PackedStringArray unsaved_shaders ;
for ( uint32_t i = 0 ; i < edited_shaders . size ( ) ; i + + ) {
if ( edited_shaders [ i ] . shader_editor ) {
if ( edited_shaders [ i ] . shader_editor - > is_unsaved ( ) ) {
if ( unsaved_shaders . is_empty ( ) ) {
2023-04-05 13:45:41 +00:00
unsaved_shaders . append ( TTR ( " Save changes to the following shaders(s) before quitting? " ) ) ;
2022-10-16 17:30:38 +00:00
}
2024-01-02 15:00:11 +00:00
unsaved_shaders . append ( edited_shaders [ i ] . name . trim_suffix ( " (*) " ) ) ;
2022-10-16 17:30:38 +00:00
}
}
}
2024-01-02 15:00:11 +00:00
if ( ! p_for_scene . is_empty ( ) ) {
PackedStringArray unsaved_built_in_shaders ;
const String scene_file = p_for_scene . get_file ( ) ;
for ( const String & E : unsaved_shaders ) {
if ( ! E . is_resource_file ( ) & & E . contains ( scene_file ) ) {
if ( unsaved_built_in_shaders . is_empty ( ) ) {
unsaved_built_in_shaders . append ( TTR ( " There are unsaved changes in the following built-in shaders(s): " ) ) ;
}
unsaved_built_in_shaders . append ( E ) ;
}
}
if ( ! unsaved_built_in_shaders . is_empty ( ) ) {
return String ( " \n " ) . join ( unsaved_built_in_shaders ) ;
}
return String ( ) ;
}
2022-10-16 17:30:38 +00:00
return String ( " \n " ) . join ( unsaved_shaders ) ;
}
2014-02-10 01:10:30 +00:00
void ShaderEditorPlugin : : save_external_data ( ) {
2022-12-29 00:24:45 +00:00
for ( EditedShader & edited_shader : edited_shaders ) {
if ( edited_shader . shader_editor ) {
edited_shader . shader_editor - > save_external_data ( ) ;
2022-05-27 09:02:55 +00:00
}
}
2022-07-28 16:28:38 +00:00
_update_shader_list ( ) ;
2014-02-10 01:10:30 +00:00
}
void ShaderEditorPlugin : : apply_changes ( ) {
2022-12-29 00:24:45 +00:00
for ( EditedShader & edited_shader : edited_shaders ) {
if ( edited_shader . shader_editor ) {
edited_shader . shader_editor - > apply_shaders ( ) ;
2022-05-27 09:02:55 +00:00
}
}
}
void ShaderEditorPlugin : : _shader_selected ( int p_index ) {
2023-05-11 02:17:03 +00:00
if ( p_index > = ( int ) edited_shaders . size ( ) ) {
return ;
}
2022-03-08 10:39:16 +00:00
if ( edited_shaders [ p_index ] . shader_editor ) {
edited_shaders [ p_index ] . shader_editor - > validate_script ( ) ;
}
2024-06-20 10:37:16 +00:00
2022-05-27 09:02:55 +00:00
shader_tabs - > set_current_tab ( p_index ) ;
2022-09-17 15:41:13 +00:00
shader_list - > select ( p_index ) ;
2022-05-27 09:02:55 +00:00
}
2022-07-28 16:28:38 +00:00
void ShaderEditorPlugin : : _shader_list_clicked ( int p_item , Vector2 p_local_mouse_pos , MouseButton p_mouse_button_index ) {
if ( p_mouse_button_index = = MouseButton : : MIDDLE ) {
_close_shader ( p_item ) ;
}
2024-08-18 04:54:58 +00:00
if ( p_mouse_button_index = = MouseButton : : RIGHT ) {
_make_script_list_context_menu ( ) ;
}
}
void ShaderEditorPlugin : : _setup_popup_menu ( PopupMenuType p_type , PopupMenu * p_menu ) {
if ( p_type = = FILE ) {
p_menu - > add_shortcut ( ED_SHORTCUT ( " shader_editor/new " , TTR ( " New Shader... " ) , KeyModifierMask : : CMD_OR_CTRL | Key : : N ) , FILE_NEW ) ;
p_menu - > add_shortcut ( ED_SHORTCUT ( " shader_editor/new_include " , TTR ( " New Shader Include... " ) , KeyModifierMask : : CMD_OR_CTRL | KeyModifierMask : : SHIFT | Key : : N ) , FILE_NEW_INCLUDE ) ;
p_menu - > add_separator ( ) ;
p_menu - > add_shortcut ( ED_SHORTCUT ( " shader_editor/open " , TTR ( " Load Shader File... " ) , KeyModifierMask : : CMD_OR_CTRL | Key : : O ) , FILE_OPEN ) ;
p_menu - > add_shortcut ( ED_SHORTCUT ( " shader_editor/open_include " , TTR ( " Load Shader Include File... " ) , KeyModifierMask : : CMD_OR_CTRL | KeyModifierMask : : SHIFT | Key : : O ) , FILE_OPEN_INCLUDE ) ;
}
if ( p_type = = FILE | | p_type = = CONTEXT_VALID_ITEM ) {
p_menu - > add_shortcut ( ED_SHORTCUT ( " shader_editor/save " , TTR ( " Save File " ) , KeyModifierMask : : ALT | KeyModifierMask : : CMD_OR_CTRL | Key : : S ) , FILE_SAVE ) ;
p_menu - > add_shortcut ( ED_SHORTCUT ( " shader_editor/save_as " , TTR ( " Save File As... " ) ) , FILE_SAVE_AS ) ;
}
if ( p_type = = FILE ) {
p_menu - > add_separator ( ) ;
p_menu - > add_item ( TTR ( " Open File in Inspector " ) , FILE_INSPECT ) ;
p_menu - > add_separator ( ) ;
p_menu - > add_shortcut ( ED_SHORTCUT ( " shader_editor/close_file " , TTR ( " Close File " ) , KeyModifierMask : : CMD_OR_CTRL | Key : : W ) , FILE_CLOSE ) ;
} else {
p_menu - > add_shortcut ( ED_SHORTCUT ( " shader_editor/close_file " , TTR ( " Close File " ) , KeyModifierMask : : CMD_OR_CTRL | Key : : W ) , FILE_CLOSE ) ;
p_menu - > add_item ( TTR ( " Close All " ) , CLOSE_ALL ) ;
p_menu - > add_item ( TTR ( " Close Other Tabs " ) , CLOSE_OTHER_TABS ) ;
if ( p_type = = CONTEXT_VALID_ITEM ) {
p_menu - > add_separator ( ) ;
p_menu - > add_item ( TTR ( " Copy Script Path " ) , COPY_PATH ) ;
p_menu - > add_item ( TTR ( " Show in File System " ) , SHOW_IN_FILE_SYSTEM ) ;
}
}
}
void ShaderEditorPlugin : : _make_script_list_context_menu ( ) {
context_menu - > clear ( ) ;
int selected = shader_tabs - > get_current_tab ( ) ;
if ( selected < 0 | | selected > = shader_tabs - > get_tab_count ( ) ) {
return ;
}
Control * control = shader_tabs - > get_tab_control ( selected ) ;
bool is_valid_editor_control = Object : : cast_to < TextShaderEditor > ( control ) | | Object : : cast_to < VisualShaderEditor > ( control ) ;
_setup_popup_menu ( is_valid_editor_control ? CONTEXT_VALID_ITEM : CONTEXT , context_menu ) ;
context_menu - > set_item_disabled ( context_menu - > get_item_index ( CLOSE_ALL ) , shader_tabs - > get_tab_count ( ) < = 0 ) ;
context_menu - > set_item_disabled ( context_menu - > get_item_index ( CLOSE_OTHER_TABS ) , shader_tabs - > get_tab_count ( ) < = 1 ) ;
context_menu - > set_position ( main_split - > get_screen_position ( ) + main_split - > get_local_mouse_position ( ) ) ;
context_menu - > reset_size ( ) ;
context_menu - > popup ( ) ;
2022-07-28 16:28:38 +00:00
}
2022-05-27 09:02:55 +00:00
void ShaderEditorPlugin : : _close_shader ( int p_index ) {
2022-09-18 11:05:47 +00:00
ERR_FAIL_INDEX ( p_index , shader_tabs - > get_tab_count ( ) ) ;
Control * c = shader_tabs - > get_tab_control ( p_index ) ;
2022-05-27 09:02:55 +00:00
memdelete ( c ) ;
2022-09-18 11:05:47 +00:00
edited_shaders . remove_at ( p_index ) ;
2022-05-27 09:02:55 +00:00
_update_shader_list ( ) ;
2022-12-23 22:53:16 +00:00
EditorUndoRedoManager : : get_singleton ( ) - > clear_history ( ) ; // To prevent undo on deleted graphs.
2022-05-27 09:02:55 +00:00
}
2023-04-09 18:43:55 +00:00
void ShaderEditorPlugin : : _close_builtin_shaders_from_scene ( const String & p_scene ) {
for ( uint32_t i = 0 ; i < edited_shaders . size ( ) ; ) {
Ref < Shader > & shader = edited_shaders [ i ] . shader ;
if ( shader . is_valid ( ) ) {
if ( shader - > is_built_in ( ) & & shader - > get_path ( ) . begins_with ( p_scene ) ) {
_close_shader ( i ) ;
continue ;
}
}
Ref < ShaderInclude > & include = edited_shaders [ i ] . shader_inc ;
if ( include . is_valid ( ) ) {
if ( include - > is_built_in ( ) & & include - > get_path ( ) . begins_with ( p_scene ) ) {
_close_shader ( i ) ;
continue ;
}
}
i + + ;
}
}
2022-05-27 09:02:55 +00:00
void ShaderEditorPlugin : : _resource_saved ( Object * obj ) {
// May have been renamed on save.
2022-12-29 00:24:45 +00:00
for ( EditedShader & edited_shader : edited_shaders ) {
2024-07-26 10:07:00 +00:00
if ( edited_shader . shader . ptr ( ) = = obj | | edited_shader . shader_inc . ptr ( ) = = obj ) {
2022-05-27 09:02:55 +00:00
_update_shader_list ( ) ;
return ;
}
}
}
void ShaderEditorPlugin : : _menu_item_pressed ( int p_index ) {
switch ( p_index ) {
case FILE_NEW : {
2022-03-08 10:39:16 +00:00
String base_path = FileSystemDock : : get_singleton ( ) - > get_current_path ( ) . get_base_dir ( ) ;
2022-08-30 00:34:01 +00:00
shader_create_dialog - > config ( base_path . path_join ( " new_shader " ) , false , false , 0 ) ;
2022-05-27 09:02:55 +00:00
shader_create_dialog - > popup_centered ( ) ;
} break ;
2022-03-08 10:39:16 +00:00
case FILE_NEW_INCLUDE : {
String base_path = FileSystemDock : : get_singleton ( ) - > get_current_path ( ) . get_base_dir ( ) ;
2022-08-30 00:34:01 +00:00
shader_create_dialog - > config ( base_path . path_join ( " new_shader " ) , false , false , 2 ) ;
2022-03-08 10:39:16 +00:00
shader_create_dialog - > popup_centered ( ) ;
} break ;
2022-05-27 09:02:55 +00:00
case FILE_OPEN : {
InspectorDock : : get_singleton ( ) - > open_resource ( " Shader " ) ;
} break ;
2022-03-08 10:39:16 +00:00
case FILE_OPEN_INCLUDE : {
InspectorDock : : get_singleton ( ) - > open_resource ( " ShaderInclude " ) ;
} break ;
2022-05-27 09:02:55 +00:00
case FILE_SAVE : {
int index = shader_tabs - > get_current_tab ( ) ;
ERR_FAIL_INDEX ( index , shader_tabs - > get_tab_count ( ) ) ;
2024-07-12 18:56:49 +00:00
TextShaderEditor * editor = Object : : cast_to < TextShaderEditor > ( edited_shaders [ index ] . shader_editor ) ;
2024-01-12 05:32:13 +00:00
if ( editor ) {
if ( editor - > get_trim_trailing_whitespace_on_save ( ) ) {
editor - > trim_trailing_whitespace ( ) ;
}
if ( editor - > get_trim_final_newlines_on_save ( ) ) {
editor - > trim_final_newlines ( ) ;
}
2023-03-09 16:27:23 +00:00
}
2022-03-08 10:39:16 +00:00
if ( edited_shaders [ index ] . shader . is_valid ( ) ) {
EditorNode : : get_singleton ( ) - > save_resource ( edited_shaders [ index ] . shader ) ;
} else {
EditorNode : : get_singleton ( ) - > save_resource ( edited_shaders [ index ] . shader_inc ) ;
}
2023-03-09 16:27:23 +00:00
if ( editor ) {
editor - > tag_saved_version ( ) ;
2022-09-25 00:37:04 +00:00
}
2022-05-27 09:02:55 +00:00
} break ;
case FILE_SAVE_AS : {
int index = shader_tabs - > get_current_tab ( ) ;
ERR_FAIL_INDEX ( index , shader_tabs - > get_tab_count ( ) ) ;
2024-07-12 18:56:49 +00:00
TextShaderEditor * editor = Object : : cast_to < TextShaderEditor > ( edited_shaders [ index ] . shader_editor ) ;
2024-01-12 05:32:13 +00:00
if ( editor ) {
if ( editor - > get_trim_trailing_whitespace_on_save ( ) ) {
editor - > trim_trailing_whitespace ( ) ;
}
if ( editor - > get_trim_final_newlines_on_save ( ) ) {
editor - > trim_final_newlines ( ) ;
}
2023-03-09 16:27:23 +00:00
}
2022-03-08 10:39:16 +00:00
String path ;
if ( edited_shaders [ index ] . shader . is_valid ( ) ) {
path = edited_shaders [ index ] . shader - > get_path ( ) ;
if ( ! path . is_resource_file ( ) ) {
path = " " ;
}
EditorNode : : get_singleton ( ) - > save_resource_as ( edited_shaders [ index ] . shader , path ) ;
} else {
path = edited_shaders [ index ] . shader_inc - > get_path ( ) ;
if ( ! path . is_resource_file ( ) ) {
path = " " ;
}
EditorNode : : get_singleton ( ) - > save_resource_as ( edited_shaders [ index ] . shader_inc , path ) ;
2022-05-27 09:02:55 +00:00
}
2023-03-09 16:27:23 +00:00
if ( editor ) {
editor - > tag_saved_version ( ) ;
2022-09-25 00:37:04 +00:00
}
2022-05-27 09:02:55 +00:00
} break ;
case FILE_INSPECT : {
int index = shader_tabs - > get_current_tab ( ) ;
ERR_FAIL_INDEX ( index , shader_tabs - > get_tab_count ( ) ) ;
2022-03-08 10:39:16 +00:00
if ( edited_shaders [ index ] . shader . is_valid ( ) ) {
EditorNode : : get_singleton ( ) - > push_item ( edited_shaders [ index ] . shader . ptr ( ) ) ;
} else {
EditorNode : : get_singleton ( ) - > push_item ( edited_shaders [ index ] . shader_inc . ptr ( ) ) ;
}
2022-05-27 09:02:55 +00:00
} break ;
case FILE_CLOSE : {
_close_shader ( shader_tabs - > get_current_tab ( ) ) ;
} break ;
2024-08-18 04:54:58 +00:00
case CLOSE_ALL : {
while ( shader_tabs - > get_tab_count ( ) > 0 ) {
_close_shader ( 0 ) ;
}
} break ;
case CLOSE_OTHER_TABS : {
int index = shader_tabs - > get_current_tab ( ) ;
for ( int i = 0 ; i < index ; i + + ) {
_close_shader ( 0 ) ;
}
while ( shader_tabs - > get_tab_count ( ) > 1 ) {
_close_shader ( 1 ) ;
}
} break ;
case SHOW_IN_FILE_SYSTEM : {
Ref < Resource > shader = _get_current_shader ( ) ;
String path = shader - > get_path ( ) ;
if ( ! path . is_empty ( ) ) {
FileSystemDock : : get_singleton ( ) - > navigate_to_path ( path ) ;
}
} break ;
case COPY_PATH : {
Ref < Resource > shader = _get_current_shader ( ) ;
DisplayServer : : get_singleton ( ) - > clipboard_set ( shader - > get_path ( ) ) ;
} break ;
2022-05-27 09:02:55 +00:00
}
}
void ShaderEditorPlugin : : _shader_created ( Ref < Shader > p_shader ) {
EditorNode : : get_singleton ( ) - > push_item ( p_shader . ptr ( ) ) ;
2014-02-10 01:10:30 +00:00
}
2022-03-08 10:39:16 +00:00
void ShaderEditorPlugin : : _shader_include_created ( Ref < ShaderInclude > p_shader_inc ) {
EditorNode : : get_singleton ( ) - > push_item ( p_shader_inc . ptr ( ) ) ;
}
2022-08-10 02:12:31 +00:00
Variant ShaderEditorPlugin : : get_drag_data_fw ( const Point2 & p_point , Control * p_from ) {
if ( shader_list - > get_item_count ( ) = = 0 ) {
return Variant ( ) ;
}
int idx = shader_list - > get_item_at_position ( p_point ) ;
if ( idx < 0 ) {
return Variant ( ) ;
}
HBoxContainer * drag_preview = memnew ( HBoxContainer ) ;
String preview_name = shader_list - > get_item_text ( idx ) ;
Ref < Texture2D > preview_icon = shader_list - > get_item_icon ( idx ) ;
if ( ! preview_icon . is_null ( ) ) {
TextureRect * tf = memnew ( TextureRect ) ;
tf - > set_texture ( preview_icon ) ;
tf - > set_stretch_mode ( TextureRect : : STRETCH_KEEP_CENTERED ) ;
drag_preview - > add_child ( tf ) ;
}
Label * label = memnew ( Label ( preview_name ) ) ;
drag_preview - > add_child ( label ) ;
main_split - > set_drag_preview ( drag_preview ) ;
Dictionary drag_data ;
drag_data [ " type " ] = " shader_list_element " ;
drag_data [ " shader_list_element " ] = idx ;
return drag_data ;
}
bool ShaderEditorPlugin : : can_drop_data_fw ( const Point2 & p_point , const Variant & p_data , Control * p_from ) const {
Dictionary d = p_data ;
if ( ! d . has ( " type " ) ) {
return false ;
}
if ( String ( d [ " type " ] ) = = " shader_list_element " ) {
return true ;
}
if ( String ( d [ " type " ] ) = = " files " ) {
Vector < String > files = d [ " files " ] ;
if ( files . size ( ) = = 0 ) {
return false ;
}
for ( int i = 0 ; i < files . size ( ) ; i + + ) {
2023-11-18 22:40:56 +00:00
const String & file = files [ i ] ;
2022-08-10 02:12:31 +00:00
if ( ResourceLoader : : exists ( file , " Shader " ) ) {
Ref < Shader > shader = ResourceLoader : : load ( file ) ;
if ( shader . is_valid ( ) ) {
return true ;
}
}
2023-03-13 16:21:26 +00:00
if ( ResourceLoader : : exists ( file , " ShaderInclude " ) ) {
Ref < ShaderInclude > sinclude = ResourceLoader : : load ( file ) ;
if ( sinclude . is_valid ( ) ) {
return true ;
}
}
2022-08-10 02:12:31 +00:00
}
return false ;
}
return false ;
}
void ShaderEditorPlugin : : drop_data_fw ( const Point2 & p_point , const Variant & p_data , Control * p_from ) {
if ( ! can_drop_data_fw ( p_point , p_data , p_from ) ) {
return ;
}
Dictionary d = p_data ;
if ( ! d . has ( " type " ) ) {
return ;
}
if ( String ( d [ " type " ] ) = = " shader_list_element " ) {
int idx = d [ " shader_list_element " ] ;
int new_idx = shader_list - > get_item_at_position ( p_point ) ;
_move_shader_tab ( idx , new_idx ) ;
return ;
}
if ( String ( d [ " type " ] ) = = " files " ) {
Vector < String > files = d [ " files " ] ;
for ( int i = 0 ; i < files . size ( ) ; i + + ) {
2023-11-18 22:40:56 +00:00
const String & file = files [ i ] ;
2023-03-13 16:21:26 +00:00
Ref < Resource > res ;
if ( ResourceLoader : : exists ( file , " Shader " ) | | ResourceLoader : : exists ( file , " ShaderInclude " ) ) {
res = ResourceLoader : : load ( file ) ;
2022-08-10 02:12:31 +00:00
}
if ( res . is_valid ( ) ) {
edit ( res . ptr ( ) ) ;
}
}
}
}
2022-11-02 14:23:25 +00:00
void ShaderEditorPlugin : : _window_changed ( bool p_visible ) {
make_floating - > set_visible ( ! p_visible ) ;
}
2024-02-18 02:16:58 +00:00
void ShaderEditorPlugin : : _set_text_shader_zoom_factor ( float p_zoom_factor ) {
if ( text_shader_zoom_factor ! = p_zoom_factor ) {
text_shader_zoom_factor = p_zoom_factor ;
for ( const EditedShader & edited_shader : edited_shaders ) {
2024-07-12 18:56:49 +00:00
TextShaderEditor * text_shader_editor = Object : : cast_to < TextShaderEditor > ( edited_shader . shader_editor ) ;
if ( text_shader_editor ) {
CodeTextEditor * cte = text_shader_editor - > get_code_editor ( ) ;
2024-02-18 02:16:58 +00:00
if ( cte & & cte - > get_zoom_factor ( ) ! = text_shader_zoom_factor ) {
cte - > set_zoom_factor ( text_shader_zoom_factor ) ;
}
}
}
}
}
2023-10-11 09:49:29 +00:00
void ShaderEditorPlugin : : _file_removed ( const String & p_removed_file ) {
for ( uint32_t i = 0 ; i < edited_shaders . size ( ) ; i + + ) {
2023-11-08 18:14:01 +00:00
if ( edited_shaders [ i ] . path = = p_removed_file ) {
2023-10-11 09:49:29 +00:00
_close_shader ( i ) ;
2023-11-08 18:14:01 +00:00
break ;
2023-10-11 09:49:29 +00:00
}
}
}
2024-01-02 11:53:18 +00:00
void ShaderEditorPlugin : : _res_saved_callback ( const Ref < Resource > & p_res ) {
if ( p_res . is_null ( ) ) {
return ;
}
const String & path = p_res - > get_path ( ) ;
for ( EditedShader & edited : edited_shaders ) {
Ref < Resource > shader_res = edited . shader ;
if ( shader_res . is_null ( ) ) {
shader_res = edited . shader_inc ;
}
ERR_FAIL_COND ( shader_res . is_null ( ) ) ;
2024-07-12 18:56:49 +00:00
TextShaderEditor * text_shader_editor = Object : : cast_to < TextShaderEditor > ( edited . shader_editor ) ;
if ( ! text_shader_editor | | ! shader_res - > is_built_in ( ) ) {
2024-01-02 11:53:18 +00:00
continue ;
}
if ( shader_res - > get_path ( ) . get_slice ( " :: " , 0 ) = = path ) {
2024-07-12 18:56:49 +00:00
text_shader_editor - > tag_saved_version ( ) ;
2024-01-02 11:53:18 +00:00
_update_shader_list ( ) ;
}
}
}
2024-08-18 04:54:58 +00:00
void ShaderEditorPlugin : : _set_file_specific_items_disabled ( bool p_disabled ) {
PopupMenu * file_popup_menu = file_menu - > get_popup ( ) ;
file_popup_menu - > set_item_disabled ( file_popup_menu - > get_item_index ( FILE_SAVE ) , p_disabled ) ;
file_popup_menu - > set_item_disabled ( file_popup_menu - > get_item_index ( FILE_SAVE_AS ) , p_disabled ) ;
file_popup_menu - > set_item_disabled ( file_popup_menu - > get_item_index ( FILE_INSPECT ) , p_disabled ) ;
file_popup_menu - > set_item_disabled ( file_popup_menu - > get_item_index ( FILE_CLOSE ) , p_disabled ) ;
}
2023-04-09 18:43:55 +00:00
void ShaderEditorPlugin : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_READY : {
2023-12-18 14:46:56 +00:00
EditorNode : : get_singleton ( ) - > connect ( " resource_saved " , callable_mp ( this , & ShaderEditorPlugin : : _resource_saved ) , CONNECT_DEFERRED ) ;
2023-04-09 18:43:55 +00:00
EditorNode : : get_singleton ( ) - > connect ( " scene_closed " , callable_mp ( this , & ShaderEditorPlugin : : _close_builtin_shaders_from_scene ) ) ;
2023-10-11 09:49:29 +00:00
FileSystemDock : : get_singleton ( ) - > connect ( " file_removed " , callable_mp ( this , & ShaderEditorPlugin : : _file_removed ) ) ;
2024-01-02 11:53:18 +00:00
EditorNode : : get_singleton ( ) - > connect ( " resource_saved " , callable_mp ( this , & ShaderEditorPlugin : : _res_saved_callback ) ) ;
2023-04-09 18:43:55 +00:00
} break ;
}
}
2022-01-27 09:36:51 +00:00
ShaderEditorPlugin : : ShaderEditorPlugin ( ) {
2022-11-02 14:23:25 +00:00
window_wrapper = memnew ( WindowWrapper ) ;
2023-06-23 10:21:56 +00:00
window_wrapper - > set_window_title ( vformat ( TTR ( " %s - Godot Engine " ) , TTR ( " Shader Editor " ) ) ) ;
2022-11-02 14:23:25 +00:00
window_wrapper - > set_margins_enabled ( true ) ;
2022-05-27 09:02:55 +00:00
main_split = memnew ( HSplitContainer ) ;
2022-11-02 14:23:25 +00:00
Ref < Shortcut > make_floating_shortcut = ED_SHORTCUT_AND_COMMAND ( " shader_editor/make_floating " , TTR ( " Make Floating " ) ) ;
window_wrapper - > set_wrapped_control ( main_split , make_floating_shortcut ) ;
2022-05-27 09:02:55 +00:00
VBoxContainer * vb = memnew ( VBoxContainer ) ;
2022-11-02 14:23:25 +00:00
HBoxContainer * menu_hb = memnew ( HBoxContainer ) ;
vb - > add_child ( menu_hb ) ;
2022-05-27 09:02:55 +00:00
file_menu = memnew ( MenuButton ) ;
file_menu - > set_text ( TTR ( " File " ) ) ;
2023-11-08 12:07:31 +00:00
file_menu - > set_shortcut_context ( main_split ) ;
2024-08-18 04:54:58 +00:00
_setup_popup_menu ( FILE , file_menu - > get_popup ( ) ) ;
2024-05-14 12:13:31 +00:00
file_menu - > get_popup ( ) - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & ShaderEditorPlugin : : _menu_item_pressed ) ) ;
2022-11-02 14:23:25 +00:00
menu_hb - > add_child ( file_menu ) ;
2022-05-27 09:02:55 +00:00
2024-08-18 04:54:58 +00:00
_set_file_specific_items_disabled ( true ) ;
context_menu = memnew ( PopupMenu ) ;
add_child ( context_menu ) ;
context_menu - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & ShaderEditorPlugin : : _menu_item_pressed ) ) ;
2022-05-27 09:02:55 +00:00
2023-11-06 11:41:08 +00:00
Control * padding = memnew ( Control ) ;
padding - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
menu_hb - > add_child ( padding ) ;
make_floating = memnew ( ScreenSelect ) ;
make_floating - > set_flat ( true ) ;
make_floating - > connect ( " request_open_in_screen " , callable_mp ( window_wrapper , & WindowWrapper : : enable_window_on_screen ) . bind ( true ) ) ;
if ( ! make_floating - > is_disabled ( ) ) {
// Override default ScreenSelect tooltip if multi-window support is available.
2022-11-02 14:23:25 +00:00
make_floating - > set_tooltip_text ( TTR ( " Make the shader editor floating. " ) ) ;
}
2023-11-06 11:41:08 +00:00
menu_hb - > add_child ( make_floating ) ;
window_wrapper - > connect ( " window_visibility_changed " , callable_mp ( this , & ShaderEditorPlugin : : _window_changed ) ) ;
2022-05-27 09:02:55 +00:00
shader_list = memnew ( ItemList ) ;
2024-01-23 21:29:45 +00:00
shader_list - > set_auto_translate_mode ( AUTO_TRANSLATE_MODE_DISABLED ) ;
2022-05-27 09:02:55 +00:00
shader_list - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
vb - > add_child ( shader_list ) ;
2024-05-14 12:21:31 +00:00
shader_list - > connect ( SceneStringName ( item_selected ) , callable_mp ( this , & ShaderEditorPlugin : : _shader_selected ) ) ;
2022-07-28 16:28:38 +00:00
shader_list - > connect ( " item_clicked " , callable_mp ( this , & ShaderEditorPlugin : : _shader_list_clicked ) ) ;
2024-08-18 04:54:58 +00:00
shader_list - > set_allow_rmb_select ( true ) ;
2023-01-14 02:37:19 +00:00
SET_DRAG_FORWARDING_GCD ( shader_list , ShaderEditorPlugin ) ;
2022-05-27 09:02:55 +00:00
main_split - > add_child ( vb ) ;
vb - > set_custom_minimum_size ( Size2 ( 200 , 300 ) * EDSCALE ) ;
shader_tabs = memnew ( TabContainer ) ;
shader_tabs - > set_tabs_visible ( false ) ;
shader_tabs - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
main_split - > add_child ( shader_tabs ) ;
Ref < StyleBoxEmpty > empty ;
empty . instantiate ( ) ;
2024-05-14 13:50:53 +00:00
shader_tabs - > add_theme_style_override ( SceneStringName ( panel ) , empty ) ;
2022-05-27 09:02:55 +00:00
2024-02-07 23:08:07 +00:00
button = EditorNode : : get_bottom_panel ( ) - > add_item ( TTR ( " Shader Editor " ) , window_wrapper , ED_SHORTCUT_AND_COMMAND ( " bottom_panels/toggle_shader_editor_bottom_panel " , TTR ( " Toggle Shader Editor Bottom Panel " ) , KeyModifierMask : : ALT | Key : : S ) ) ;
2014-02-10 01:10:30 +00:00
2022-05-27 09:02:55 +00:00
shader_create_dialog = memnew ( ShaderCreateDialog ) ;
vb - > add_child ( shader_create_dialog ) ;
shader_create_dialog - > connect ( " shader_created " , callable_mp ( this , & ShaderEditorPlugin : : _shader_created ) ) ;
2022-03-08 10:39:16 +00:00
shader_create_dialog - > connect ( " shader_include_created " , callable_mp ( this , & ShaderEditorPlugin : : _shader_include_created ) ) ;
2014-02-10 01:10:30 +00:00
}
ShaderEditorPlugin : : ~ ShaderEditorPlugin ( ) {
}