2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* script_create_dialog.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-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "script_create_dialog.h"
2017-01-16 08:04:19 +01:00
2020-11-07 23:33:38 +01:00
# include "core/config/project_settings.h"
2021-06-11 14:51:48 +02:00
# include "core/io/file_access.h"
2018-09-11 18:13:45 +02:00
# include "core/io/resource_saver.h"
2020-11-07 23:33:38 +01:00
# include "core/string/string_builder.h"
2019-02-18 16:45:26 +01:00
# include "editor/create_dialog.h"
2022-02-12 02:46:22 +01:00
# include "editor/editor_file_system.h"
2022-02-20 11:20:58 +01:00
# include "editor/editor_node.h"
2022-07-29 02:36:26 +02:00
# include "editor/editor_paths.h"
2017-05-03 20:41:02 +02:00
# include "editor/editor_scale.h"
2022-02-14 14:00:03 +01:00
# include "editor/editor_settings.h"
2023-04-07 18:59:49 +02:00
# include "editor/gui/editor_file_dialog.h"
2023-06-26 19:18:27 +02:00
# include "editor/gui/editor_validation_panel.h"
2014-02-10 02:10:30 +01:00
2022-02-20 11:20:58 +01:00
static String _get_parent_class_of_script ( String p_path ) {
if ( ! ResourceLoader : : exists ( p_path , " Script " ) ) {
return " Object " ; // A script eventually inherits from Object.
}
Ref < Script > script = ResourceLoader : : load ( p_path , " Script " ) ;
ERR_FAIL_COND_V ( script . is_null ( ) , " Object " ) ;
String class_name ;
Ref < Script > base = script - > get_base_script ( ) ;
// Inherits from a built-in class.
if ( base . is_null ( ) ) {
script - > get_language ( ) - > get_global_class_name ( script - > get_path ( ) , & class_name ) ;
return class_name ;
}
// Inherits from a script that has class_name.
class_name = script - > get_language ( ) - > get_global_class_name ( base - > get_path ( ) ) ;
if ( ! class_name . is_empty ( ) ) {
return class_name ;
}
// Inherits from a plain script.
return _get_parent_class_of_script ( base - > get_path ( ) ) ;
}
static Vector < String > _get_hierarchy ( String p_class_name ) {
Vector < String > hierarchy ;
String class_name = p_class_name ;
while ( true ) {
// A registered class.
if ( ClassDB : : class_exists ( class_name ) ) {
hierarchy . push_back ( class_name ) ;
class_name = ClassDB : : get_parent_class ( class_name ) ;
continue ;
}
// A class defined in script with class_name.
if ( ScriptServer : : is_global_class ( class_name ) ) {
hierarchy . push_back ( class_name ) ;
Ref < Script > script = EditorNode : : get_editor_data ( ) . script_class_load_script ( class_name ) ;
ERR_BREAK ( script . is_null ( ) ) ;
class_name = _get_parent_class_of_script ( script - > get_path ( ) ) ;
continue ;
}
break ;
}
if ( hierarchy . is_empty ( ) ) {
if ( p_class_name . is_valid_identifier ( ) ) {
hierarchy . push_back ( p_class_name ) ;
}
hierarchy . push_back ( " Object " ) ;
}
return hierarchy ;
}
2021-07-04 04:47:43 +02:00
void ScriptCreateDialog : : _notification ( int p_what ) {
switch ( p_what ) {
2022-12-01 15:30:16 +01:00
case NOTIFICATION_ENTER_TREE :
case NOTIFICATION_THEME_CHANGED : {
for ( int i = 0 ; i < ScriptServer : : get_language_count ( ) ; i + + ) {
Ref < Texture2D > language_icon = get_theme_icon ( ScriptServer : : get_language ( i ) - > get_type ( ) , SNAME ( " EditorIcons " ) ) ;
if ( language_icon . is_valid ( ) ) {
language_menu - > set_item_icon ( i , language_icon ) ;
}
}
2021-10-11 11:30:59 +02:00
String last_language = EditorSettings : : get_singleton ( ) - > get_project_metadata ( " script_setup " , " last_selected_language " , " " ) ;
if ( ! last_language . is_empty ( ) ) {
2021-07-04 04:47:43 +02:00
for ( int i = 0 ; i < language_menu - > get_item_count ( ) ; i + + ) {
2021-10-11 11:30:59 +02:00
if ( language_menu - > get_item_text ( i ) = = last_language ) {
2021-07-04 04:47:43 +02:00
language_menu - > select ( i ) ;
current_language = i ;
break ;
}
}
} else {
language_menu - > select ( default_language ) ;
}
2022-12-01 15:32:41 +01:00
if ( EditorSettings : : get_singleton ( ) - > has_meta ( " script_setup_use_script_templates " ) ) {
is_using_templates = bool ( EditorSettings : : get_singleton ( ) - > get_meta ( " script_setup_use_script_templates " ) ) ;
2022-12-01 15:30:16 +01:00
use_templates - > set_pressed ( is_using_templates ) ;
2021-10-11 11:30:59 +02:00
}
2020-05-14 14:29:06 +02:00
2021-07-17 23:22:52 +02:00
path_button - > set_icon ( get_theme_icon ( SNAME ( " Folder " ) , SNAME ( " EditorIcons " ) ) ) ;
parent_browse_button - > set_icon ( get_theme_icon ( SNAME ( " Folder " ) , SNAME ( " EditorIcons " ) ) ) ;
parent_search_button - > set_icon ( get_theme_icon ( SNAME ( " ClassList " ) , SNAME ( " EditorIcons " ) ) ) ;
2018-02-25 17:04:16 +01:00
} break ;
2017-05-03 20:41:02 +02:00
}
}
2019-02-08 18:42:05 +01:00
void ScriptCreateDialog : : _path_hbox_sorted ( ) {
if ( is_visible ( ) ) {
2020-07-03 15:26:22 +02:00
int filename_start_pos = initial_bp . rfind ( " / " ) + 1 ;
2019-02-08 18:42:05 +01:00
int filename_end_pos = initial_bp . length ( ) ;
2020-02-02 18:08:22 +01:00
if ( ! is_built_in ) {
file_path - > select ( filename_start_pos , filename_end_pos ) ;
}
2019-02-08 18:42:05 +01:00
// First set cursor to the end of line to scroll LineEdit view
// to the right and then set the actual cursor position.
2021-03-28 20:31:25 +02:00
file_path - > set_caret_column ( file_path - > get_text ( ) . length ( ) ) ;
file_path - > set_caret_column ( filename_start_pos ) ;
2019-02-08 18:42:05 +01:00
file_path - > grab_focus ( ) ;
}
}
2018-09-23 00:29:24 +02:00
bool ScriptCreateDialog : : _can_be_built_in ( ) {
return ( supports_built_in & & built_in_enabled ) ;
}
2020-01-08 23:43:55 +01:00
void ScriptCreateDialog : : config ( const String & p_base_name , const String & p_base_path , bool p_built_in_enabled , bool p_load_enabled ) {
2014-02-10 02:10:30 +01:00
class_name - > set_text ( " " ) ;
2018-01-11 00:33:12 +01:00
class_name - > deselect ( ) ;
2014-02-10 02:10:30 +01:00
parent_name - > set_text ( p_base_name ) ;
2018-01-11 00:33:12 +01:00
parent_name - > deselect ( ) ;
2022-08-18 15:58:08 +02:00
internal_name - > set_text ( " " ) ;
2018-09-12 12:29:50 +02:00
2021-12-09 10:42:46 +01:00
if ( ! p_base_path . is_empty ( ) ) {
2017-01-14 04:51:09 +01:00
initial_bp = p_base_path . get_basename ( ) ;
2014-02-10 02:10:30 +01:00
file_path - > set_text ( initial_bp + " . " + ScriptServer : : get_language ( language_menu - > get_selected ( ) ) - > get_extension ( ) ) ;
2020-01-01 01:09:50 +01:00
current_language = language_menu - > get_selected ( ) ;
2014-02-10 02:10:30 +01:00
} else {
initial_bp = " " ;
file_path - > set_text ( " " ) ;
}
2018-01-11 00:33:12 +01:00
file_path - > deselect ( ) ;
2018-09-23 00:29:24 +02:00
built_in_enabled = p_built_in_enabled ;
2020-01-08 23:43:55 +01:00
load_enabled = p_load_enabled ;
2018-09-23 00:29:24 +02:00
2021-10-11 11:30:59 +02:00
_language_changed ( current_language ) ;
2014-02-10 02:10:30 +01:00
_class_name_changed ( " " ) ;
_path_changed ( file_path - > get_text ( ) ) ;
}
2019-02-18 16:45:26 +01:00
void ScriptCreateDialog : : set_inheritance_base_type ( const String & p_base ) {
base_type = p_base ;
}
2019-06-11 23:05:24 +02:00
bool ScriptCreateDialog : : _validate_parent ( const String & p_string ) {
2020-05-14 16:41:43 +02:00
if ( p_string . length ( ) = = 0 ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2019-06-11 23:05:24 +02:00
if ( can_inherit_from_file & & p_string . is_quoted ( ) ) {
2019-04-30 15:58:02 +02:00
String p = p_string . substr ( 1 , p_string . length ( ) - 2 ) ;
2020-05-14 16:41:43 +02:00
if ( _validate_path ( p , true ) = = " " ) {
2019-04-30 15:58:02 +02:00
return true ;
2020-05-14 16:41:43 +02:00
}
2019-04-30 15:58:02 +02:00
}
2022-02-28 01:57:34 +01:00
return EditorNode : : get_editor_data ( ) . is_type_recognized ( p_string ) ;
2019-06-11 23:05:24 +02:00
}
bool ScriptCreateDialog : : _validate_class ( const String & p_string ) {
2020-05-14 16:41:43 +02:00
if ( p_string . length ( ) = = 0 ) {
2019-06-11 23:05:24 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2019-06-11 23:05:24 +02:00
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < p_string . length ( ) ; i + + ) {
if ( i = = 0 ) {
2021-10-11 11:30:59 +02:00
// Cannot start with a number.
2020-05-14 16:41:43 +02:00
if ( p_string [ 0 ] > = ' 0 ' & & p_string [ 0 ] < = ' 9 ' ) {
2021-10-11 11:30:59 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2022-02-04 09:32:20 +01:00
bool valid_char = is_ascii_identifier_char ( p_string [ i ] ) | | p_string [ i ] = = ' . ' ;
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( ! valid_char ) {
2014-02-10 02:10:30 +01:00
return false ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
return true ;
}
2019-04-30 15:58:02 +02:00
String ScriptCreateDialog : : _validate_path ( const String & p_path , bool p_file_must_exist ) {
String p = p_path . strip_edges ( ) ;
2021-12-09 10:42:46 +01:00
if ( p . is_empty ( ) ) {
2019-04-30 15:58:02 +02:00
return TTR ( " Path is empty. " ) ;
2020-05-14 16:41:43 +02:00
}
2021-12-09 10:42:46 +01:00
if ( p . get_file ( ) . get_basename ( ) . is_empty ( ) ) {
2019-04-30 15:58:02 +02:00
return TTR ( " Filename is empty. " ) ;
2020-05-14 16:41:43 +02:00
}
2019-04-30 15:58:02 +02:00
2021-10-11 11:30:59 +02:00
if ( ! p . get_file ( ) . get_basename ( ) . is_valid_filename ( ) ) {
return TTR ( " Filename is invalid. " ) ;
}
2019-04-30 15:58:02 +02:00
p = ProjectSettings : : get_singleton ( ) - > localize_path ( p ) ;
2020-05-14 16:41:43 +02:00
if ( ! p . begins_with ( " res:// " ) ) {
2019-04-30 15:58:02 +02:00
return TTR ( " Path is not local. " ) ;
2020-05-14 16:41:43 +02:00
}
2019-04-30 15:58:02 +02:00
2022-03-10 15:27:09 +01:00
{
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
2022-03-10 15:27:09 +01:00
if ( da - > change_dir ( p . get_base_dir ( ) ) ! = OK ) {
return TTR ( " Base path is invalid. " ) ;
}
2019-04-30 15:58:02 +02:00
}
2022-03-10 15:27:09 +01:00
{
// Check if file exists.
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
2022-03-10 15:27:09 +01:00
if ( da - > dir_exists ( p ) ) {
return TTR ( " A directory with the same name exists. " ) ;
} else if ( p_file_must_exist & & ! da - > file_exists ( p ) ) {
return TTR ( " File does not exist. " ) ;
}
2019-04-30 15:58:02 +02:00
}
2021-10-11 11:30:59 +02:00
// Check file extension.
2019-04-30 15:58:02 +02:00
String extension = p . get_extension ( ) ;
List < String > extensions ;
2021-10-11 11:30:59 +02:00
// Get all possible extensions for script.
2019-04-30 15:58:02 +02:00
for ( int l = 0 ; l < language_menu - > get_item_count ( ) ; l + + ) {
ScriptServer : : get_language ( l ) - > get_recognized_extensions ( & extensions ) ;
}
bool found = false ;
bool match = false ;
2021-07-24 15:46:25 +02:00
for ( const String & E : extensions ) {
2021-07-16 05:45:57 +02:00
if ( E . nocasecmp_to ( extension ) = = 0 ) {
2019-04-30 15:58:02 +02:00
found = true ;
2021-07-16 05:45:57 +02:00
if ( E = = ScriptServer : : get_language ( language_menu - > get_selected ( ) ) - > get_extension ( ) ) {
2019-04-30 15:58:02 +02:00
match = true ;
}
break ;
}
}
2020-05-14 16:41:43 +02:00
if ( ! found ) {
2019-04-30 15:58:02 +02:00
return TTR ( " Invalid extension. " ) ;
2020-05-14 16:41:43 +02:00
}
if ( ! match ) {
2021-10-11 11:30:59 +02:00
return TTR ( " Extension doesn't match chosen language. " ) ;
2020-05-14 16:41:43 +02:00
}
2019-04-30 15:58:02 +02:00
2021-10-11 11:30:59 +02:00
// Let ScriptLanguage do custom validation.
2023-06-26 19:18:27 +02:00
return ScriptServer : : get_language ( language_menu - > get_selected ( ) ) - > validate_path ( p ) ;
2019-04-30 15:58:02 +02:00
}
2020-09-29 07:31:41 +02:00
String ScriptCreateDialog : : _get_class_name ( ) const {
if ( has_named_classes ) {
return class_name - > get_text ( ) ;
} else {
return ProjectSettings : : get_singleton ( ) - > localize_path ( file_path - > get_text ( ) ) . get_file ( ) . get_basename ( ) ;
}
}
2014-02-10 02:10:30 +01:00
void ScriptCreateDialog : : _class_name_changed ( const String & p_name ) {
2021-10-11 11:30:59 +02:00
is_class_name_valid = _validate_class ( class_name - > get_text ( ) ) ;
2023-06-26 19:18:27 +02:00
validation_panel - > update ( ) ;
2017-05-03 20:41:02 +02:00
}
2014-02-10 02:10:30 +01:00
2017-05-03 20:41:02 +02:00
void ScriptCreateDialog : : _parent_name_changed ( const String & p_parent ) {
2021-10-11 11:30:59 +02:00
is_parent_name_valid = _validate_parent ( parent_name - > get_text ( ) ) ;
2023-06-26 19:18:27 +02:00
validation_panel - > update ( ) ;
2014-02-10 02:10:30 +01:00
}
2017-06-13 22:03:08 +02:00
void ScriptCreateDialog : : _template_changed ( int p_template ) {
2021-10-11 11:30:59 +02:00
const ScriptLanguage : : ScriptTemplate & sinfo = _get_current_template ( ) ;
// Update last used dictionaries
if ( is_using_templates & & ! parent_name - > get_text ( ) . begins_with ( " \" res: " ) ) {
if ( sinfo . origin = = ScriptLanguage : : TemplateLocation : : TEMPLATE_PROJECT ) {
// Save the last used template for this node into the project dictionary.
Dictionary dic_templates_project = EditorSettings : : get_singleton ( ) - > get_project_metadata ( " script_setup " , " templates_dictionary " , Dictionary ( ) ) ;
dic_templates_project [ parent_name - > get_text ( ) ] = sinfo . get_hash ( ) ;
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " script_setup " , " templates_dictionary " , dic_templates_project ) ;
} else {
2022-12-01 15:32:41 +01:00
// Save template info to editor dictionary (not a project template).
2022-12-01 15:30:16 +01:00
Dictionary dic_templates ;
2022-12-01 15:32:41 +01:00
if ( EditorSettings : : get_singleton ( ) - > has_meta ( " script_setup_templates_dictionary " ) ) {
dic_templates = ( Dictionary ) EditorSettings : : get_singleton ( ) - > get_meta ( " script_setup_templates_dictionary " ) ;
2022-12-01 15:30:16 +01:00
}
dic_templates [ parent_name - > get_text ( ) ] = sinfo . get_hash ( ) ;
2022-12-01 15:32:41 +01:00
EditorSettings : : get_singleton ( ) - > set_meta ( " script_setup_templates_dictionary " , dic_templates ) ;
2021-10-11 11:30:59 +02:00
// Remove template from project dictionary as we last used an editor level template.
Dictionary dic_templates_project = EditorSettings : : get_singleton ( ) - > get_project_metadata ( " script_setup " , " templates_dictionary " , Dictionary ( ) ) ;
if ( dic_templates_project . has ( parent_name - > get_text ( ) ) ) {
dic_templates_project . erase ( parent_name - > get_text ( ) ) ;
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " script_setup " , " templates_dictionary " , dic_templates_project ) ;
}
2019-08-22 17:59:43 +02:00
}
}
2023-06-26 19:18:27 +02:00
2021-10-11 11:30:59 +02:00
// Update template label information.
2023-08-07 10:37:41 +02:00
String template_info = U " • " ;
2021-10-11 11:30:59 +02:00
template_info + = TTR ( " Template: " ) ;
template_info + = " " + sinfo . name ;
if ( ! sinfo . description . is_empty ( ) ) {
template_info + = " - " + sinfo . description ;
}
2023-06-26 19:18:27 +02:00
validation_panel - > set_message ( MSG_ID_TEMPLATE , template_info , EditorValidationPanel : : MSG_INFO , false ) ;
2017-06-13 22:03:08 +02:00
}
2014-02-10 02:10:30 +01:00
void ScriptCreateDialog : : ok_pressed ( ) {
2017-05-03 20:41:02 +02:00
if ( is_new_script_created ) {
2016-11-09 17:29:15 +01:00
_create_new ( ) ;
} else {
_load_exist ( ) ;
}
2021-10-11 11:30:59 +02:00
EditorSettings : : get_singleton ( ) - > save ( ) ;
2017-05-03 20:41:02 +02:00
is_new_script_created = true ;
2023-06-26 19:18:27 +02:00
validation_panel - > update ( ) ;
2016-11-09 17:29:15 +01:00
}
void ScriptCreateDialog : : _create_new ( ) {
2020-09-29 07:31:41 +02:00
String cname_param = _get_class_name ( ) ;
2014-02-10 02:10:30 +01:00
2017-06-13 22:03:08 +02:00
Ref < Script > scr ;
2021-10-11 11:30:59 +02:00
const ScriptLanguage : : ScriptTemplate sinfo = _get_current_template ( ) ;
2022-02-28 01:57:34 +01:00
String parent_class = parent_name - > get_text ( ) ;
2022-09-15 15:50:35 +02:00
if ( ! parent_name - > get_text ( ) . is_quoted ( ) & & ! ClassDB : : class_exists ( parent_class ) & & ! ScriptServer : : is_global_class ( parent_class ) ) {
2022-02-28 01:57:34 +01:00
// If base is a custom type, replace with script path instead.
const EditorData : : CustomType * type = EditorNode : : get_editor_data ( ) . get_custom_type_by_name ( parent_class ) ;
ERR_FAIL_NULL ( type ) ;
parent_class = " \" " + type - > script - > get_path ( ) + " \" " ;
}
scr = ScriptServer : : get_language ( language_menu - > get_selected ( ) ) - > make_template ( sinfo . content , cname_param , parent_class ) ;
2016-08-06 03:46:45 +02:00
2017-10-24 01:54:47 +02:00
if ( has_named_classes ) {
String cname = class_name - > get_text ( ) ;
2020-05-14 16:41:43 +02:00
if ( cname . length ( ) ) {
2017-10-24 01:54:47 +02:00
scr - > set_name ( cname ) ;
2020-05-14 16:41:43 +02:00
}
2017-10-24 01:54:47 +02:00
}
2014-02-10 02:10:30 +01:00
2021-11-04 02:34:35 +01:00
if ( is_built_in ) {
scr - > set_name ( internal_name - > get_text ( ) ) ;
} else {
2017-07-19 22:00:46 +02:00
String lpath = ProjectSettings : : get_singleton ( ) - > localize_path ( file_path - > get_text ( ) ) ;
2016-08-06 03:46:45 +02:00
scr - > set_path ( lpath ) ;
2022-06-03 01:33:42 +02:00
Error err = ResourceSaver : : save ( scr , lpath , ResourceSaver : : FLAG_CHANGE_PATH ) ;
2014-02-10 02:10:30 +01:00
if ( err ! = OK ) {
2017-05-03 20:41:02 +02:00
alert - > set_text ( TTR ( " Error - Could not create script in filesystem. " ) ) ;
alert - > popup_centered ( ) ;
2014-02-10 02:10:30 +01:00
return ;
}
2016-11-09 17:29:15 +01:00
}
2014-02-10 02:10:30 +01:00
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " script_created " ) , scr ) ;
2019-06-29 23:20:10 +02:00
hide ( ) ;
2016-11-09 17:29:15 +01:00
}
2014-02-10 02:10:30 +01:00
2016-11-09 17:29:15 +01:00
void ScriptCreateDialog : : _load_exist ( ) {
String path = file_path - > get_text ( ) ;
2022-05-03 01:43:50 +02:00
Ref < Resource > p_script = ResourceLoader : : load ( path , " Script " ) ;
2016-11-09 17:29:15 +01:00
if ( p_script . is_null ( ) ) {
alert - > set_text ( vformat ( TTR ( " Error loading script from %s " ) , path ) ) ;
2017-05-03 20:41:02 +02:00
alert - > popup_centered ( ) ;
2016-11-09 17:29:15 +01:00
return ;
2014-02-10 02:10:30 +01:00
}
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " script_created " ) , p_script ) ;
2019-06-29 23:20:10 +02:00
hide ( ) ;
2014-02-10 02:10:30 +01:00
}
2021-10-11 11:30:59 +02:00
void ScriptCreateDialog : : _language_changed ( int l ) {
language = ScriptServer : : get_language ( l ) ;
2017-10-24 01:54:47 +02:00
2019-06-11 23:05:24 +02:00
has_named_classes = language - > has_named_classes ( ) ;
can_inherit_from_file = language - > can_inherit_from_file ( ) ;
supports_built_in = language - > supports_builtin_mode ( ) ;
2020-05-14 16:41:43 +02:00
if ( ! supports_built_in ) {
2017-10-24 01:54:47 +02:00
is_built_in = false ;
2020-05-14 16:41:43 +02:00
}
2017-04-15 17:35:06 +02:00
2017-06-13 22:03:08 +02:00
String selected_ext = " . " + language - > get_extension ( ) ;
2016-11-09 17:29:15 +01:00
String path = file_path - > get_text ( ) ;
String extension = " " ;
2021-12-09 10:42:46 +01:00
if ( ! path . is_empty ( ) ) {
2022-02-03 17:03:38 +01:00
if ( path . contains ( " . " ) ) {
2017-05-03 20:41:02 +02:00
extension = path . get_extension ( ) ;
2016-11-09 17:29:15 +01:00
}
2017-05-03 20:41:02 +02:00
if ( extension . length ( ) = = 0 ) {
2021-10-11 11:30:59 +02:00
// Add extension if none.
2017-05-03 20:41:02 +02:00
path + = selected_ext ;
_path_changed ( path ) ;
} else {
2021-10-11 11:30:59 +02:00
// Change extension by selected language.
2017-05-03 20:41:02 +02:00
List < String > extensions ;
2021-10-11 11:30:59 +02:00
// Get all possible extensions for script.
2019-02-12 21:10:08 +01:00
for ( int m = 0 ; m < language_menu - > get_item_count ( ) ; m + + ) {
ScriptServer : : get_language ( m ) - > get_recognized_extensions ( & extensions ) ;
2017-05-03 20:41:02 +02:00
}
2021-07-24 15:46:25 +02:00
for ( const String & E : extensions ) {
2021-07-16 05:45:57 +02:00
if ( E . nocasecmp_to ( extension ) = = 0 ) {
2017-05-03 20:41:02 +02:00
path = path . get_basename ( ) + selected_ext ;
_path_changed ( path ) ;
break ;
}
2016-11-09 17:29:15 +01:00
}
}
2017-07-31 20:19:45 +02:00
} else {
path = " class " + selected_ext ;
_path_changed ( path ) ;
2014-02-10 02:10:30 +01:00
}
2017-07-31 20:19:45 +02:00
file_path - > set_text ( path ) ;
2017-05-03 20:41:02 +02:00
2017-07-08 13:33:25 +02:00
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " script_setup " , " last_selected_language " , language_menu - > get_item_text ( language_menu - > get_selected ( ) ) ) ;
2018-01-11 00:33:12 +01:00
_parent_name_changed ( parent_name - > get_text ( ) ) ;
2023-06-26 19:18:27 +02:00
validation_panel - > update ( ) ;
2014-02-10 02:10:30 +01:00
}
void ScriptCreateDialog : : _built_in_pressed ( ) {
if ( internal - > is_pressed ( ) ) {
2017-05-03 20:41:02 +02:00
is_built_in = true ;
2018-12-16 10:31:43 +01:00
is_new_script_created = true ;
2014-02-10 02:10:30 +01:00
} else {
2017-05-03 20:41:02 +02:00
is_built_in = false ;
2018-12-16 10:31:43 +01:00
_path_changed ( file_path - > get_text ( ) ) ;
2014-02-10 02:10:30 +01:00
}
2023-06-26 19:18:27 +02:00
validation_panel - > update ( ) ;
2014-02-10 02:10:30 +01:00
}
2021-10-11 11:30:59 +02:00
void ScriptCreateDialog : : _use_template_pressed ( ) {
is_using_templates = use_templates - > is_pressed ( ) ;
2022-12-01 15:32:41 +01:00
EditorSettings : : get_singleton ( ) - > set_meta ( " script_setup_use_script_templates " , is_using_templates ) ;
2023-06-26 19:18:27 +02:00
validation_panel - > update ( ) ;
2021-10-11 11:30:59 +02:00
}
2018-01-10 03:10:55 +01:00
void ScriptCreateDialog : : _browse_path ( bool browse_parent , bool p_save ) {
2017-04-15 17:35:06 +02:00
is_browsing_parent = browse_parent ;
2014-02-10 02:10:30 +01:00
2018-01-10 03:10:55 +01:00
if ( p_save ) {
2020-03-06 18:00:16 +01:00
file_browse - > set_file_mode ( EditorFileDialog : : FILE_MODE_SAVE_FILE ) ;
2019-03-25 01:54:29 +01:00
file_browse - > set_title ( TTR ( " Open Script / Choose Location " ) ) ;
2022-07-08 02:31:19 +02:00
file_browse - > set_ok_button_text ( TTR ( " Open " ) ) ;
2018-01-10 03:10:55 +01:00
} else {
2020-03-06 18:00:16 +01:00
file_browse - > set_file_mode ( EditorFileDialog : : FILE_MODE_OPEN_FILE ) ;
2018-01-10 03:10:55 +01:00
file_browse - > set_title ( TTR ( " Open Script " ) ) ;
}
2016-06-20 05:38:13 +02:00
file_browse - > set_disable_overwrite_warning ( true ) ;
2014-02-10 02:10:30 +01:00
file_browse - > clear_filters ( ) ;
List < String > extensions ;
2017-07-01 15:18:55 +02:00
int lang = language_menu - > get_selected ( ) ;
ScriptServer : : get_language ( lang ) - > get_recognized_extensions ( & extensions ) ;
2014-02-10 02:10:30 +01:00
2021-07-24 15:46:25 +02:00
for ( const String & E : extensions ) {
2021-07-16 05:45:57 +02:00
file_browse - > add_filter ( " *. " + E ) ;
2014-02-10 02:10:30 +01:00
}
2016-03-09 00:00:52 +01:00
file_browse - > set_current_path ( file_path - > get_text ( ) ) ;
2020-07-11 18:45:19 +02:00
file_browse - > popup_file_dialog ( ) ;
2014-02-10 02:10:30 +01:00
}
void ScriptCreateDialog : : _file_selected ( const String & p_file ) {
2021-10-11 11:30:59 +02:00
String path = ProjectSettings : : get_singleton ( ) - > localize_path ( p_file ) ;
2017-04-15 17:35:06 +02:00
if ( is_browsing_parent ) {
2021-10-11 11:30:59 +02:00
parent_name - > set_text ( " \" " + path + " \" " ) ;
2019-06-11 23:05:24 +02:00
_parent_name_changed ( parent_name - > get_text ( ) ) ;
2017-04-15 17:35:06 +02:00
} else {
2021-10-11 11:30:59 +02:00
file_path - > set_text ( path ) ;
_path_changed ( path ) ;
2017-12-14 03:25:00 +01:00
2021-10-11 11:30:59 +02:00
String filename = path . get_file ( ) . get_basename ( ) ;
int select_start = path . rfind ( filename ) ;
2017-12-14 03:25:00 +01:00
file_path - > select ( select_start , select_start + filename . length ( ) ) ;
2021-03-28 20:31:25 +02:00
file_path - > set_caret_column ( select_start + filename . length ( ) ) ;
2017-12-14 03:25:00 +01:00
file_path - > grab_focus ( ) ;
2017-04-15 17:35:06 +02:00
}
2014-02-10 02:10:30 +01:00
}
2019-02-18 16:45:26 +01:00
void ScriptCreateDialog : : _create ( ) {
2019-06-11 23:05:24 +02:00
parent_name - > set_text ( select_class - > get_selected_type ( ) . split ( " " ) [ 0 ] ) ;
_parent_name_changed ( parent_name - > get_text ( ) ) ;
2019-02-18 16:45:26 +01:00
}
void ScriptCreateDialog : : _browse_class_in_tree ( ) {
select_class - > set_base_type ( base_type ) ;
select_class - > popup_create ( true ) ;
2020-12-26 15:58:17 +01:00
select_class - > set_title ( vformat ( TTR ( " Inherit %s " ) , base_type ) ) ;
2022-07-08 02:31:19 +02:00
select_class - > set_ok_button_text ( TTR ( " Inherit " ) ) ;
2019-02-18 16:45:26 +01:00
}
2014-02-10 02:10:30 +01:00
void ScriptCreateDialog : : _path_changed ( const String & p_path ) {
2020-02-02 18:08:22 +01:00
if ( is_built_in ) {
return ;
}
2017-05-03 20:41:02 +02:00
is_path_valid = false ;
is_new_script_created = true ;
2014-02-10 02:10:30 +01:00
2023-06-26 19:18:27 +02:00
path_error = _validate_path ( p_path , false ) ;
2021-12-09 10:42:46 +01:00
if ( ! path_error . is_empty ( ) ) {
2023-06-26 19:18:27 +02:00
validation_panel - > update ( ) ;
2018-01-04 01:00:11 +01:00
return ;
2014-02-10 02:10:30 +01:00
}
2021-10-11 11:30:59 +02:00
// Check if file exists.
2022-03-23 10:08:58 +01:00
Ref < DirAccess > da = DirAccess : : create ( DirAccess : : ACCESS_RESOURCES ) ;
2019-04-30 15:58:02 +02:00
String p = ProjectSettings : : get_singleton ( ) - > localize_path ( p_path . strip_edges ( ) ) ;
2022-03-10 15:27:09 +01:00
if ( da - > file_exists ( p ) ) {
2017-09-01 19:25:01 +02:00
is_new_script_created = false ;
2017-05-03 20:41:02 +02:00
}
is_path_valid = true ;
2023-06-26 19:18:27 +02:00
validation_panel - > update ( ) ;
2017-05-03 20:41:02 +02:00
}
2021-06-16 18:43:34 +02:00
void ScriptCreateDialog : : _path_submitted ( const String & p_path ) {
2023-06-26 19:18:27 +02:00
if ( ! get_ok_button ( ) - > is_disabled ( ) ) {
ok_pressed ( ) ;
2017-05-03 20:41:02 +02:00
}
2014-02-10 02:10:30 +01:00
}
2021-10-11 11:30:59 +02:00
void ScriptCreateDialog : : _update_template_menu ( ) {
bool is_language_using_templates = language - > is_using_templates ( ) ;
template_menu - > set_disabled ( false ) ;
template_menu - > clear ( ) ;
template_list . clear ( ) ;
2020-01-08 23:43:55 +01:00
2021-10-11 11:30:59 +02:00
if ( is_language_using_templates ) {
2022-01-07 00:08:56 +01:00
// Get the latest templates used for each type of node from project settings then global settings.
2021-10-11 11:30:59 +02:00
Dictionary last_local_templates = EditorSettings : : get_singleton ( ) - > get_project_metadata ( " script_setup " , " templates_dictionary " , Dictionary ( ) ) ;
2022-12-01 15:30:16 +01:00
Dictionary last_global_templates ;
2022-12-01 15:32:41 +01:00
if ( EditorSettings : : get_singleton ( ) - > has_meta ( " script_setup_templates_dictionary " ) ) {
last_global_templates = ( Dictionary ) EditorSettings : : get_singleton ( ) - > get_meta ( " script_setup_templates_dictionary " ) ;
2022-12-01 15:30:16 +01:00
}
2021-10-11 11:30:59 +02:00
String inherits_base_type = parent_name - > get_text ( ) ;
2022-02-20 11:20:58 +01:00
// If it inherits from a script, get its parent class first.
2021-10-11 11:30:59 +02:00
if ( inherits_base_type [ 0 ] = = ' " ' ) {
2022-02-20 11:20:58 +01:00
inherits_base_type = _get_parent_class_of_script ( inherits_base_type . unquote ( ) ) ;
2021-10-11 11:30:59 +02:00
}
// Get all ancestor node for selected base node.
// There templates will also fit the base node.
2022-02-20 11:20:58 +01:00
Vector < String > hierarchy = _get_hierarchy ( inherits_base_type ) ;
2021-10-11 11:30:59 +02:00
int last_used_template = - 1 ;
int preselected_template = - 1 ;
int previous_ancestor_level = - 1 ;
// Templates can be stored in tree different locations.
Vector < ScriptLanguage : : TemplateLocation > template_locations ;
template_locations . append ( ScriptLanguage : : TEMPLATE_PROJECT ) ;
template_locations . append ( ScriptLanguage : : TEMPLATE_EDITOR ) ;
template_locations . append ( ScriptLanguage : : TEMPLATE_BUILT_IN ) ;
for ( const ScriptLanguage : : TemplateLocation & template_location : template_locations ) {
String display_name = _get_script_origin_label ( template_location ) ;
bool separator = false ;
int ancestor_level = 0 ;
for ( const String & current_node : hierarchy ) {
Vector < ScriptLanguage : : ScriptTemplate > templates_found ;
if ( template_location = = ScriptLanguage : : TEMPLATE_BUILT_IN ) {
templates_found = language - > get_built_in_templates ( current_node ) ;
} else {
String template_directory ;
if ( template_location = = ScriptLanguage : : TEMPLATE_PROJECT ) {
2022-07-29 02:36:26 +02:00
template_directory = EditorPaths : : get_singleton ( ) - > get_project_script_templates_dir ( ) ;
2021-10-11 11:30:59 +02:00
} else {
2022-07-29 02:36:26 +02:00
template_directory = EditorPaths : : get_singleton ( ) - > get_script_templates_dir ( ) ;
2021-10-11 11:30:59 +02:00
}
templates_found = _get_user_templates ( language , current_node , template_directory , template_location ) ;
}
if ( ! templates_found . is_empty ( ) ) {
if ( ! separator ) {
template_menu - > add_separator ( ) ;
2022-03-12 01:06:45 +01:00
template_menu - > set_item_text ( - 1 , display_name ) ;
2021-10-11 11:30:59 +02:00
separator = true ;
}
for ( ScriptLanguage : : ScriptTemplate & t : templates_found ) {
template_menu - > add_item ( t . inherit + " : " + t . name ) ;
int id = template_menu - > get_item_count ( ) - 1 ;
// Check if this template should be preselected if node isn't in the last used dictionary.
if ( ancestor_level < previous_ancestor_level | | previous_ancestor_level = = - 1 ) {
previous_ancestor_level = ancestor_level ;
preselected_template = id ;
}
// Check for last used template for this node in project settings then in global settings.
if ( last_local_templates . has ( parent_name - > get_text ( ) ) & & t . get_hash ( ) = = String ( last_local_templates [ parent_name - > get_text ( ) ] ) ) {
last_used_template = id ;
2022-12-01 15:30:16 +01:00
} else if ( last_used_template = = - 1 & & last_global_templates . has ( parent_name - > get_text ( ) ) & & t . get_hash ( ) = = String ( last_global_templates [ parent_name - > get_text ( ) ] ) ) {
2021-10-11 11:30:59 +02:00
last_used_template = id ;
}
t . id = id ;
template_list . push_back ( t ) ;
String icon = has_theme_icon ( t . inherit , SNAME ( " EditorIcons " ) ) ? t . inherit : " Object " ;
template_menu - > set_item_icon ( id , get_theme_icon ( icon , SNAME ( " EditorIcons " ) ) ) ;
}
}
ancestor_level + + ;
}
}
if ( last_used_template ! = - 1 ) {
template_menu - > select ( last_used_template ) ;
} else if ( preselected_template ! = - 1 ) {
template_menu - > select ( preselected_template ) ;
}
}
_template_changed ( template_menu - > get_selected ( ) ) ;
}
void ScriptCreateDialog : : _update_dialog ( ) {
// "Add Script Dialog" GUI logic and script checks.
_update_template_menu ( ) ;
2017-05-03 20:41:02 +02:00
2020-01-08 23:43:55 +01:00
// Is script path/name valid (order from top to bottom)?
2017-05-03 20:41:02 +02:00
2019-05-01 16:35:11 +02:00
if ( ! is_built_in & & ! is_path_valid ) {
2023-06-26 19:18:27 +02:00
validation_panel - > set_message ( MSG_ID_SCRIPT , TTR ( " Invalid path. " ) , EditorValidationPanel : : MSG_ERROR ) ;
2017-05-03 20:41:02 +02:00
}
2017-12-15 19:40:08 +01:00
if ( has_named_classes & & ( is_new_script_created & & ! is_class_name_valid ) ) {
2023-06-26 19:18:27 +02:00
validation_panel - > set_message ( MSG_ID_SCRIPT , TTR ( " Invalid class name. " ) , EditorValidationPanel : : MSG_ERROR ) ;
2017-05-03 20:41:02 +02:00
}
2019-05-01 16:35:11 +02:00
if ( ! is_parent_name_valid & & is_new_script_created ) {
2023-06-26 19:18:27 +02:00
validation_panel - > set_message ( MSG_ID_SCRIPT , TTR ( " Invalid inherited parent name or path. " ) , EditorValidationPanel : : MSG_ERROR ) ;
2017-05-03 20:41:02 +02:00
}
2020-01-08 23:43:55 +01:00
2023-06-26 19:18:27 +02:00
if ( validation_panel - > is_valid ( ) & & ! is_new_script_created ) {
validation_panel - > set_message ( MSG_ID_SCRIPT , TTR ( " File exists, it will be reused. " ) , EditorValidationPanel : : MSG_OK ) ;
}
if ( ! path_error . is_empty ( ) ) {
validation_panel - > set_message ( MSG_ID_PATH , path_error , EditorValidationPanel : : MSG_ERROR ) ;
2017-05-03 20:41:02 +02:00
}
2020-01-08 23:43:55 +01:00
// Does script have named classes?
2016-11-09 17:29:15 +01:00
2017-05-03 20:41:02 +02:00
if ( has_named_classes ) {
if ( is_new_script_created ) {
class_name - > set_editable ( true ) ;
2019-07-10 14:44:52 +02:00
class_name - > set_placeholder ( TTR ( " Allowed: a-z, A-Z, 0-9, _ and . " ) ) ;
2022-02-06 15:53:53 +01:00
Color placeholder_color = class_name - > get_theme_color ( SNAME ( " font_placeholder_color " ) ) ;
2022-01-30 18:56:23 +01:00
placeholder_color . a = 0.3 ;
2022-02-08 10:14:58 +01:00
class_name - > add_theme_color_override ( " font_placeholder_color " , placeholder_color ) ;
2017-05-03 20:41:02 +02:00
} else {
class_name - > set_editable ( false ) ;
}
} else {
class_name - > set_editable ( false ) ;
class_name - > set_placeholder ( TTR ( " N/A " ) ) ;
2022-02-06 15:53:53 +01:00
Color placeholder_color = class_name - > get_theme_color ( SNAME ( " font_placeholder_color " ) ) ;
2022-01-30 18:56:23 +01:00
placeholder_color . a = 1 ;
2022-02-08 10:14:58 +01:00
class_name - > add_theme_color_override ( " font_placeholder_color " , placeholder_color ) ;
2019-06-11 23:05:24 +02:00
class_name - > set_text ( " " ) ;
2017-05-03 20:41:02 +02:00
}
2020-01-08 23:43:55 +01:00
// Is script Built-in?
2017-05-03 20:41:02 +02:00
if ( is_built_in ) {
file_path - > set_editable ( false ) ;
path_button - > set_disabled ( true ) ;
re_check_path = true ;
} else {
file_path - > set_editable ( true ) ;
path_button - > set_disabled ( false ) ;
if ( re_check_path ) {
re_check_path = false ;
_path_changed ( file_path - > get_text ( ) ) ;
}
}
2020-01-08 05:15:05 +01:00
if ( ! _can_be_built_in ( ) ) {
internal - > set_pressed ( false ) ;
}
internal - > set_disabled ( ! _can_be_built_in ( ) ) ;
2020-01-08 23:43:55 +01:00
// Is Script created or loaded from existing file?
2017-05-03 20:41:02 +02:00
2023-06-26 19:18:27 +02:00
if ( is_built_in ) {
validation_panel - > set_message ( MSG_ID_BUILT_IN , TTR ( " Note: Built-in scripts have some limitations and can't be edited using an external editor. " ) , EditorValidationPanel : : MSG_INFO , false ) ;
} else if ( _get_class_name ( ) = = parent_name - > get_text ( ) ) {
validation_panel - > set_message ( MSG_ID_BUILT_IN , TTR ( " Warning: Having the script name be the same as a built-in type is usually not desired. " ) , EditorValidationPanel : : MSG_WARNING , false ) ;
}
2020-04-03 23:56:57 +02:00
2021-11-04 02:34:35 +01:00
path_controls [ 0 ] - > set_visible ( ! is_built_in ) ;
path_controls [ 1 ] - > set_visible ( ! is_built_in ) ;
name_controls [ 0 ] - > set_visible ( is_built_in ) ;
name_controls [ 1 ] - > set_visible ( is_built_in ) ;
2020-09-29 07:31:41 +02:00
// Check if the script name is the same as the parent class.
// This warning isn't relevant if the script is built-in.
2021-10-11 11:30:59 +02:00
bool is_new_file = is_built_in | | is_new_script_created ;
parent_name - > set_editable ( is_new_file ) ;
parent_search_button - > set_disabled ( ! is_new_file ) ;
parent_browse_button - > set_disabled ( ! is_new_file | | ! can_inherit_from_file ) ;
template_inactive_message = " " ;
String button_text = is_new_file ? TTR ( " Create " ) : TTR ( " Load " ) ;
2022-07-08 02:31:19 +02:00
set_ok_button_text ( button_text ) ;
2021-10-11 11:30:59 +02:00
if ( is_new_file ) {
if ( is_built_in ) {
2023-06-26 19:18:27 +02:00
validation_panel - > set_message ( MSG_ID_PATH , TTR ( " Built-in script (into scene file). " ) , EditorValidationPanel : : MSG_OK ) ;
2017-05-03 20:41:02 +02:00
}
2021-10-11 11:30:59 +02:00
} else {
2023-06-26 19:18:27 +02:00
template_inactive_message = TTR ( " Using existing script file. " ) ;
2021-10-11 11:30:59 +02:00
if ( load_enabled ) {
if ( is_path_valid ) {
2023-06-26 19:18:27 +02:00
validation_panel - > set_message ( MSG_ID_PATH , TTR ( " Will load an existing script file. " ) , EditorValidationPanel : : MSG_OK ) ;
2021-10-11 11:30:59 +02:00
}
} else {
2023-06-26 19:18:27 +02:00
validation_panel - > set_message ( MSG_ID_PATH , TTR ( " Script file already exists. " ) , EditorValidationPanel : : MSG_ERROR ) ;
2021-10-11 11:30:59 +02:00
}
}
// Show templates list if needed.
if ( is_using_templates ) {
// Check if at least one suitable template has been found.
if ( template_menu - > get_item_count ( ) = = 0 & & template_inactive_message . is_empty ( ) ) {
template_inactive_message = TTR ( " No suitable template. " ) ;
2017-05-03 20:41:02 +02:00
}
2020-01-08 23:43:55 +01:00
} else {
2021-10-11 11:30:59 +02:00
template_inactive_message = TTR ( " Empty " ) ;
}
2020-01-08 23:43:55 +01:00
2021-10-11 11:30:59 +02:00
if ( ! template_inactive_message . is_empty ( ) ) {
template_menu - > set_disabled ( true ) ;
template_menu - > clear ( ) ;
template_menu - > add_item ( template_inactive_message ) ;
2023-06-26 19:18:27 +02:00
validation_panel - > set_message ( MSG_ID_TEMPLATE , " " , EditorValidationPanel : : MSG_INFO ) ;
2020-09-01 03:24:09 +02:00
}
2016-11-09 17:29:15 +01:00
}
2021-10-11 11:30:59 +02:00
ScriptLanguage : : ScriptTemplate ScriptCreateDialog : : _get_current_template ( ) const {
2022-02-04 19:08:10 +01:00
int selected_index = template_menu - > get_selected ( ) ;
2021-10-11 11:30:59 +02:00
for ( const ScriptLanguage : : ScriptTemplate & t : template_list ) {
if ( is_using_templates ) {
2022-02-04 19:08:10 +01:00
if ( t . id = = selected_index ) {
2021-10-11 11:30:59 +02:00
return t ;
}
} else {
// Using empty built-in template if templates are disabled.
if ( t . origin = = ScriptLanguage : : TemplateLocation : : TEMPLATE_BUILT_IN & & t . name = = " Empty " ) {
return t ;
}
}
}
return ScriptLanguage : : ScriptTemplate ( ) ;
}
2022-09-29 11:53:28 +02:00
Vector < ScriptLanguage : : ScriptTemplate > ScriptCreateDialog : : _get_user_templates ( const ScriptLanguage * p_language , const StringName & p_object , const String & p_dir , const ScriptLanguage : : TemplateLocation & p_origin ) const {
2021-10-11 11:30:59 +02:00
Vector < ScriptLanguage : : ScriptTemplate > user_templates ;
2022-09-29 11:53:28 +02:00
String extension = p_language - > get_extension ( ) ;
2021-10-11 11:30:59 +02:00
2022-08-30 02:34:01 +02:00
String dir_path = p_dir . path_join ( p_object ) ;
2021-10-11 11:30:59 +02:00
2022-03-23 10:08:58 +01:00
Ref < DirAccess > d = DirAccess : : open ( dir_path ) ;
if ( d . is_valid ( ) ) {
2021-10-11 11:30:59 +02:00
d - > list_dir_begin ( ) ;
String file = d - > get_next ( ) ;
while ( file ! = String ( ) ) {
if ( file . get_extension ( ) = = extension ) {
2022-09-29 11:53:28 +02:00
user_templates . append ( _parse_template ( p_language , dir_path , file , p_origin , p_object ) ) ;
2021-10-11 11:30:59 +02:00
}
file = d - > get_next ( ) ;
}
d - > list_dir_end ( ) ;
}
return user_templates ;
}
2022-09-29 11:53:28 +02:00
ScriptLanguage : : ScriptTemplate ScriptCreateDialog : : _parse_template ( const ScriptLanguage * p_language , const String & p_path , const String & p_filename , const ScriptLanguage : : TemplateLocation & p_origin , const String & p_inherits ) const {
2021-10-11 11:30:59 +02:00
ScriptLanguage : : ScriptTemplate script_template = ScriptLanguage : : ScriptTemplate ( ) ;
script_template . origin = p_origin ;
script_template . inherit = p_inherits ;
2023-06-25 18:18:58 +02:00
int space_indent_size = 4 ;
2021-10-11 11:30:59 +02:00
// Get meta delimiter
2022-11-14 18:21:06 +01:00
String meta_delimiter ;
2021-10-11 11:30:59 +02:00
List < String > comment_delimiters ;
2022-09-29 11:53:28 +02:00
p_language - > get_comment_delimiters ( & comment_delimiters ) ;
2021-10-11 11:30:59 +02:00
for ( const String & script_delimiter : comment_delimiters ) {
2022-02-03 17:03:38 +01:00
if ( ! script_delimiter . contains ( " " ) ) {
2021-10-11 11:30:59 +02:00
meta_delimiter = script_delimiter ;
break ;
}
}
String meta_prefix = meta_delimiter + " meta- " ;
// Parse file for meta-information and script content
Error err ;
2022-08-30 02:34:01 +02:00
Ref < FileAccess > file = FileAccess : : open ( p_path . path_join ( p_filename ) , FileAccess : : READ , & err ) ;
2021-10-11 11:30:59 +02:00
if ( ! err ) {
while ( ! file - > eof_reached ( ) ) {
String line = file - > get_line ( ) ;
if ( line . begins_with ( meta_prefix ) ) {
// Store meta information
2023-06-25 18:18:58 +02:00
line = line . substr ( meta_prefix . length ( ) ) ;
if ( line . begins_with ( " name: " ) ) {
script_template . name = line . substr ( 5 ) . strip_edges ( ) ;
} else if ( line . begins_with ( " description: " ) ) {
script_template . description = line . substr ( 12 ) . strip_edges ( ) ;
} else if ( line . begins_with ( " space-indent: " ) ) {
String indent_value = line . substr ( 13 ) . strip_edges ( ) ;
2021-10-11 11:30:59 +02:00
if ( indent_value . is_valid_int ( ) ) {
2023-01-29 00:52:05 +01:00
int indent_size = indent_value . to_int ( ) ;
if ( indent_size > = 0 ) {
2023-06-25 18:18:58 +02:00
space_indent_size = indent_size ;
} else {
WARN_PRINT ( vformat ( " Template meta-space-indent need to be a non-negative integer value. Found %s. " , indent_value ) ) ;
2021-10-11 11:30:59 +02:00
}
} else {
2023-06-25 18:18:58 +02:00
WARN_PRINT ( vformat ( " Template meta-space-indent need to be a valid integer value. Found %s. " , indent_value ) ) ;
2021-10-11 11:30:59 +02:00
}
}
} else {
2023-06-25 18:18:58 +02:00
// Replace indentation.
int i = 0 ;
int space_count = 0 ;
for ( ; i < line . length ( ) ; i + + ) {
if ( line [ i ] = = ' \t ' ) {
if ( space_count ) {
script_template . content + = String ( " " ) . repeat ( space_count ) ;
space_count = 0 ;
}
script_template . content + = " _TS_ " ;
} else if ( line [ i ] = = ' ' ) {
space_count + + ;
if ( space_count = = space_indent_size ) {
script_template . content + = " _TS_ " ;
space_count = 0 ;
}
} else {
break ;
}
}
if ( space_count ) {
script_template . content + = String ( " " ) . repeat ( space_count ) ;
2021-10-11 11:30:59 +02:00
}
2023-06-25 18:18:58 +02:00
script_template . content + = line . substr ( i ) + " \n " ;
2021-10-11 11:30:59 +02:00
}
}
}
script_template . content = script_template . content . lstrip ( " \n " ) ;
// Get name from file name if no name in meta information
if ( script_template . name = = String ( ) ) {
2022-08-30 11:36:24 +02:00
script_template . name = p_filename . get_basename ( ) . capitalize ( ) ;
2021-10-11 11:30:59 +02:00
}
return script_template ;
}
String ScriptCreateDialog : : _get_script_origin_label ( const ScriptLanguage : : TemplateLocation & p_origin ) const {
switch ( p_origin ) {
case ScriptLanguage : : TEMPLATE_BUILT_IN :
return TTR ( " Built-in " ) ;
case ScriptLanguage : : TEMPLATE_EDITOR :
return TTR ( " Editor " ) ;
case ScriptLanguage : : TEMPLATE_PROJECT :
return TTR ( " Project " ) ;
}
return " " ;
}
2014-02-10 02:10:30 +01:00
void ScriptCreateDialog : : _bind_methods ( ) {
2020-01-08 23:43:55 +01:00
ClassDB : : bind_method ( D_METHOD ( " config " , " inherits " , " path " , " built_in_enabled " , " load_enabled " ) , & ScriptCreateDialog : : config , DEFVAL ( true ) , DEFVAL ( true ) ) ;
2018-06-30 05:08:28 +02:00
2014-02-10 02:10:30 +01:00
ADD_SIGNAL ( MethodInfo ( " script_created " , PropertyInfo ( Variant : : OBJECT , " script " , PROPERTY_HINT_RESOURCE_TYPE , " Script " ) ) ) ;
}
ScriptCreateDialog : : ScriptCreateDialog ( ) {
2017-05-03 20:41:02 +02:00
/* Main Controls */
2021-07-04 04:47:43 +02:00
GridContainer * gc = memnew ( GridContainer ) ;
2017-05-03 20:41:02 +02:00
gc - > set_columns ( 2 ) ;
2021-10-11 11:30:59 +02:00
/* Information Messages Field */
2017-05-03 20:41:02 +02:00
2023-06-26 19:18:27 +02:00
validation_panel = memnew ( EditorValidationPanel ) ;
validation_panel - > add_line ( MSG_ID_SCRIPT , TTR ( " Script path/name is valid. " ) ) ;
validation_panel - > add_line ( MSG_ID_PATH , TTR ( " Will create a new script file. " ) ) ;
validation_panel - > add_line ( MSG_ID_BUILT_IN ) ;
validation_panel - > add_line ( MSG_ID_TEMPLATE ) ;
validation_panel - > set_update_callback ( callable_mp ( this , & ScriptCreateDialog : : _update_dialog ) ) ;
validation_panel - > set_accept_button ( get_ok_button ( ) ) ;
2017-05-03 20:41:02 +02:00
2019-05-01 19:06:45 +02:00
/* Spacing */
Control * spacing = memnew ( Control ) ;
spacing - > set_custom_minimum_size ( Size2 ( 0 , 10 * EDSCALE ) ) ;
2017-05-03 20:41:02 +02:00
2023-06-26 19:18:27 +02:00
VBoxContainer * vb = memnew ( VBoxContainer ) ;
2017-05-03 20:41:02 +02:00
vb - > add_child ( gc ) ;
2019-05-01 19:06:45 +02:00
vb - > add_child ( spacing ) ;
2023-06-26 19:18:27 +02:00
vb - > add_child ( validation_panel ) ;
2021-06-30 01:20:34 +02:00
add_child ( vb ) ;
2017-05-03 20:41:02 +02:00
/* Language */
2014-02-10 02:10:30 +01:00
language_menu = memnew ( OptionButton ) ;
2021-10-11 11:30:59 +02:00
language_menu - > set_custom_minimum_size ( Size2 ( 350 , 0 ) * EDSCALE ) ;
2020-03-06 18:00:16 +01:00
language_menu - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2019-09-27 05:49:20 +02:00
gc - > add_child ( memnew ( Label ( TTR ( " Language: " ) ) ) ) ;
2017-05-03 20:41:02 +02:00
gc - > add_child ( language_menu ) ;
2014-02-10 02:10:30 +01:00
2020-05-31 19:31:45 +02:00
default_language = - 1 ;
2014-02-10 02:10:30 +01:00
for ( int i = 0 ; i < ScriptServer : : get_language_count ( ) ; i + + ) {
2017-04-06 13:40:00 +02:00
String lang = ScriptServer : : get_language ( i ) - > get_name ( ) ;
language_menu - > add_item ( lang ) ;
if ( lang = = " GDScript " ) {
2019-08-12 13:41:24 +02:00
default_language = i ;
2017-04-06 13:40:00 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-05-31 19:31:45 +02:00
if ( default_language > = 0 ) {
language_menu - > select ( default_language ) ;
}
2019-09-03 12:42:34 +02:00
current_language = default_language ;
2016-10-12 22:23:48 +02:00
2021-10-11 11:30:59 +02:00
language_menu - > connect ( " item_selected " , callable_mp ( this , & ScriptCreateDialog : : _language_changed ) ) ;
2014-02-10 02:10:30 +01:00
2017-05-03 20:41:02 +02:00
/* Inherits */
2014-02-10 02:10:30 +01:00
2019-02-18 16:45:26 +01:00
base_type = " Object " ;
2021-06-30 01:20:34 +02:00
HBoxContainer * hb = memnew ( HBoxContainer ) ;
2020-03-06 18:00:16 +01:00
hb - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2017-05-03 20:41:02 +02:00
parent_name = memnew ( LineEdit ) ;
2020-02-21 18:28:45 +01:00
parent_name - > connect ( " text_changed " , callable_mp ( this , & ScriptCreateDialog : : _parent_name_changed ) ) ;
2020-03-06 18:00:16 +01:00
parent_name - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2017-05-03 20:41:02 +02:00
hb - > add_child ( parent_name ) ;
2019-02-18 16:45:26 +01:00
parent_search_button = memnew ( Button ) ;
2020-02-21 18:28:45 +01:00
parent_search_button - > connect ( " pressed " , callable_mp ( this , & ScriptCreateDialog : : _browse_class_in_tree ) ) ;
2019-02-18 16:45:26 +01:00
hb - > add_child ( parent_search_button ) ;
2017-05-03 20:41:02 +02:00
parent_browse_button = memnew ( Button ) ;
2022-07-28 22:56:41 +02:00
parent_browse_button - > connect ( " pressed " , callable_mp ( this , & ScriptCreateDialog : : _browse_path ) . bind ( true , false ) ) ;
2017-05-03 20:41:02 +02:00
hb - > add_child ( parent_browse_button ) ;
2019-09-27 05:49:20 +02:00
gc - > add_child ( memnew ( Label ( TTR ( " Inherits: " ) ) ) ) ;
2017-05-03 20:41:02 +02:00
gc - > add_child ( hb ) ;
2014-02-10 02:10:30 +01:00
2017-05-03 20:41:02 +02:00
/* Class Name */
2014-02-10 02:10:30 +01:00
2017-05-03 20:41:02 +02:00
class_name = memnew ( LineEdit ) ;
2020-02-21 18:28:45 +01:00
class_name - > connect ( " text_changed " , callable_mp ( this , & ScriptCreateDialog : : _class_name_changed ) ) ;
2020-03-06 18:00:16 +01:00
class_name - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2019-09-27 05:49:20 +02:00
gc - > add_child ( memnew ( Label ( TTR ( " Class Name: " ) ) ) ) ;
2017-05-03 20:41:02 +02:00
gc - > add_child ( class_name ) ;
2014-02-10 02:10:30 +01:00
2017-06-13 22:03:08 +02:00
/* Templates */
2019-09-27 05:49:20 +02:00
gc - > add_child ( memnew ( Label ( TTR ( " Template: " ) ) ) ) ;
2021-10-11 11:30:59 +02:00
HBoxContainer * template_hb = memnew ( HBoxContainer ) ;
template_hb - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
use_templates = memnew ( CheckBox ) ;
use_templates - > set_pressed ( is_using_templates ) ;
use_templates - > connect ( " pressed " , callable_mp ( this , & ScriptCreateDialog : : _use_template_pressed ) ) ;
template_hb - > add_child ( use_templates ) ;
template_inactive_message = " " ;
template_menu = memnew ( OptionButton ) ;
template_menu - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2020-02-21 18:28:45 +01:00
template_menu - > connect ( " item_selected " , callable_mp ( this , & ScriptCreateDialog : : _template_changed ) ) ;
2021-10-11 11:30:59 +02:00
template_hb - > add_child ( template_menu ) ;
gc - > add_child ( template_hb ) ;
2017-06-13 22:03:08 +02:00
2017-05-03 20:41:02 +02:00
/* Built-in Script */
2014-02-10 02:10:30 +01:00
2019-07-04 17:30:44 +02:00
internal = memnew ( CheckBox ) ;
internal - > set_text ( TTR ( " On " ) ) ;
2020-02-21 18:28:45 +01:00
internal - > connect ( " pressed " , callable_mp ( this , & ScriptCreateDialog : : _built_in_pressed ) ) ;
2020-01-08 05:15:05 +01:00
gc - > add_child ( memnew ( Label ( TTR ( " Built-in Script: " ) ) ) ) ;
2019-04-30 21:18:08 +02:00
gc - > add_child ( internal ) ;
2017-05-03 20:41:02 +02:00
/* Path */
hb = memnew ( HBoxContainer ) ;
2020-02-21 18:28:45 +01:00
hb - > connect ( " sort_children " , callable_mp ( this , & ScriptCreateDialog : : _path_hbox_sorted ) ) ;
2017-05-03 20:41:02 +02:00
file_path = memnew ( LineEdit ) ;
2020-02-21 18:28:45 +01:00
file_path - > connect ( " text_changed " , callable_mp ( this , & ScriptCreateDialog : : _path_changed ) ) ;
2020-03-06 18:00:16 +01:00
file_path - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2017-05-03 20:41:02 +02:00
hb - > add_child ( file_path ) ;
path_button = memnew ( Button ) ;
2022-07-28 22:56:41 +02:00
path_button - > connect ( " pressed " , callable_mp ( this , & ScriptCreateDialog : : _browse_path ) . bind ( false , true ) ) ;
2017-05-03 20:41:02 +02:00
hb - > add_child ( path_button ) ;
2021-11-04 02:34:35 +01:00
Label * label = memnew ( Label ( TTR ( " Path: " ) ) ) ;
gc - > add_child ( label ) ;
2017-05-03 20:41:02 +02:00
gc - > add_child ( hb ) ;
2021-11-04 02:34:35 +01:00
path_controls [ 0 ] = label ;
path_controls [ 1 ] = hb ;
/* Name */
internal_name = memnew ( LineEdit ) ;
internal_name - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2022-02-17 17:03:41 +01:00
internal_name - > connect ( " text_submitted " , callable_mp ( this , & ScriptCreateDialog : : _path_submitted ) ) ;
2021-11-04 02:34:35 +01:00
label = memnew ( Label ( TTR ( " Name: " ) ) ) ;
gc - > add_child ( label ) ;
gc - > add_child ( internal_name ) ;
name_controls [ 0 ] = label ;
name_controls [ 1 ] = internal_name ;
label - > hide ( ) ;
internal_name - > hide ( ) ;
2017-05-03 20:41:02 +02:00
/* Dialog Setup */
2014-02-10 02:10:30 +01:00
2019-02-18 16:45:26 +01:00
select_class = memnew ( CreateDialog ) ;
2020-02-21 18:28:45 +01:00
select_class - > connect ( " create " , callable_mp ( this , & ScriptCreateDialog : : _create ) ) ;
2019-02-18 16:45:26 +01:00
add_child ( select_class ) ;
2015-06-06 14:44:38 +02:00
file_browse = memnew ( EditorFileDialog ) ;
2020-02-21 18:28:45 +01:00
file_browse - > connect ( " file_selected " , callable_mp ( this , & ScriptCreateDialog : : _file_selected ) ) ;
2020-03-06 18:00:16 +01:00
file_browse - > set_file_mode ( EditorFileDialog : : FILE_MODE_OPEN_FILE ) ;
2014-02-10 02:10:30 +01:00
add_child ( file_browse ) ;
2022-07-08 02:31:19 +02:00
set_ok_button_text ( TTR ( " Create " ) ) ;
2014-02-10 02:10:30 +01:00
alert = memnew ( AcceptDialog ) ;
2022-06-15 10:01:45 +02:00
alert - > get_label ( ) - > set_autowrap_mode ( TextServer : : AUTOWRAP_WORD_SMART ) ;
2021-11-25 03:58:47 +01:00
alert - > get_label ( ) - > set_horizontal_alignment ( HORIZONTAL_ALIGNMENT_CENTER ) ;
alert - > get_label ( ) - > set_vertical_alignment ( VERTICAL_ALIGNMENT_CENTER ) ;
2017-05-03 20:41:02 +02:00
alert - > get_label ( ) - > set_custom_minimum_size ( Size2 ( 325 , 60 ) * EDSCALE ) ;
2014-02-10 02:10:30 +01:00
add_child ( alert ) ;
2016-11-09 17:29:15 +01:00
2017-05-03 20:41:02 +02:00
set_hide_on_ok ( false ) ;
set_title ( TTR ( " Attach Node Script " ) ) ;
2014-02-10 02:10:30 +01:00
}