2019-06-11 15:31:23 +02:00
/*************************************************************************/
/* editor_feature_profile.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2020-01-01 11:16:22 +01:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2019-06-11 15:31:23 +02:00
/* */
/* 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. */
/*************************************************************************/
2019-04-09 00:18:03 +02:00
# include "editor_feature_profile.h"
2020-05-25 13:10:28 +02:00
2019-04-09 00:18:03 +02:00
# include "core/io/json.h"
# include "core/os/dir_access.h"
# include "editor/editor_settings.h"
# include "editor_node.h"
# include "editor_scale.h"
const char * EditorFeatureProfile : : feature_names [ FEATURE_MAX ] = {
TTRC ( " 3D Editor " ) ,
TTRC ( " Script Editor " ) ,
TTRC ( " Asset Library " ) ,
TTRC ( " Scene Tree Editing " ) ,
TTRC ( " Node Dock " ) ,
2020-05-08 21:51:42 +02:00
TTRC ( " FileSystem Dock " ) ,
TTRC ( " Import Dock " ) ,
2019-04-09 00:18:03 +02:00
} ;
const char * EditorFeatureProfile : : feature_identifiers [ FEATURE_MAX ] = {
" 3d " ,
" script " ,
" asset_lib " ,
" scene_tree " ,
" node_dock " ,
2020-05-08 21:51:42 +02:00
" filesystem_dock " ,
" import_dock " ,
2019-04-09 00:18:03 +02:00
} ;
void EditorFeatureProfile : : set_disable_class ( const StringName & p_class , bool p_disabled ) {
if ( p_disabled ) {
disabled_classes . insert ( p_class ) ;
} else {
disabled_classes . erase ( p_class ) ;
}
}
bool EditorFeatureProfile : : is_class_disabled ( const StringName & p_class ) const {
2019-11-07 15:08:01 +01:00
if ( p_class = = StringName ( ) ) {
return false ;
}
return disabled_classes . has ( p_class ) | | is_class_disabled ( ClassDB : : get_parent_class_nocheck ( p_class ) ) ;
2019-04-09 00:18:03 +02:00
}
void EditorFeatureProfile : : set_disable_class_editor ( const StringName & p_class , bool p_disabled ) {
if ( p_disabled ) {
disabled_editors . insert ( p_class ) ;
} else {
disabled_editors . erase ( p_class ) ;
}
}
bool EditorFeatureProfile : : is_class_editor_disabled ( const StringName & p_class ) const {
2019-11-07 15:08:01 +01:00
if ( p_class = = StringName ( ) ) {
return false ;
}
return disabled_editors . has ( p_class ) | | is_class_editor_disabled ( ClassDB : : get_parent_class_nocheck ( p_class ) ) ;
2019-04-09 00:18:03 +02:00
}
void EditorFeatureProfile : : set_disable_class_property ( const StringName & p_class , const StringName & p_property , bool p_disabled ) {
if ( p_disabled ) {
if ( ! disabled_properties . has ( p_class ) ) {
disabled_properties [ p_class ] = Set < StringName > ( ) ;
}
disabled_properties [ p_class ] . insert ( p_property ) ;
} else {
ERR_FAIL_COND ( ! disabled_properties . has ( p_class ) ) ;
disabled_properties [ p_class ] . erase ( p_property ) ;
if ( disabled_properties [ p_class ] . empty ( ) ) {
disabled_properties . erase ( p_class ) ;
}
}
}
2020-05-14 14:29:06 +02:00
2019-04-09 00:18:03 +02:00
bool EditorFeatureProfile : : is_class_property_disabled ( const StringName & p_class , const StringName & p_property ) const {
if ( ! disabled_properties . has ( p_class ) ) {
return false ;
}
if ( ! disabled_properties [ p_class ] . has ( p_property ) ) {
return false ;
}
return true ;
}
bool EditorFeatureProfile : : has_class_properties_disabled ( const StringName & p_class ) const {
return disabled_properties . has ( p_class ) ;
}
void EditorFeatureProfile : : set_disable_feature ( Feature p_feature , bool p_disable ) {
ERR_FAIL_INDEX ( p_feature , FEATURE_MAX ) ;
features_disabled [ p_feature ] = p_disable ;
}
2020-05-14 14:29:06 +02:00
2019-04-09 00:18:03 +02:00
bool EditorFeatureProfile : : is_feature_disabled ( Feature p_feature ) const {
ERR_FAIL_INDEX_V ( p_feature , FEATURE_MAX , false ) ;
return features_disabled [ p_feature ] ;
}
String EditorFeatureProfile : : get_feature_name ( Feature p_feature ) {
ERR_FAIL_INDEX_V ( p_feature , FEATURE_MAX , String ( ) ) ;
return feature_names [ p_feature ] ;
}
Error EditorFeatureProfile : : save_to_file ( const String & p_path ) {
Dictionary json ;
json [ " type " ] = " feature_profile " ;
Array dis_classes ;
for ( Set < StringName > : : Element * E = disabled_classes . front ( ) ; E ; E = E - > next ( ) ) {
dis_classes . push_back ( String ( E - > get ( ) ) ) ;
}
dis_classes . sort ( ) ;
json [ " disabled_classes " ] = dis_classes ;
Array dis_editors ;
for ( Set < StringName > : : Element * E = disabled_editors . front ( ) ; E ; E = E - > next ( ) ) {
dis_editors . push_back ( String ( E - > get ( ) ) ) ;
}
dis_editors . sort ( ) ;
json [ " disabled_editors " ] = dis_editors ;
Array dis_props ;
2020-03-17 07:33:00 +01:00
for ( Map < StringName , Set < StringName > > : : Element * E = disabled_properties . front ( ) ; E ; E = E - > next ( ) ) {
2019-04-09 00:18:03 +02:00
for ( Set < StringName > : : Element * F = E - > get ( ) . front ( ) ; F ; F = F - > next ( ) ) {
dis_props . push_back ( String ( E - > key ( ) ) + " : " + String ( F - > get ( ) ) ) ;
}
}
json [ " disabled_properties " ] = dis_props ;
Array dis_features ;
for ( int i = 0 ; i < FEATURE_MAX ; i + + ) {
if ( features_disabled [ i ] ) {
dis_features . push_back ( feature_identifiers [ i ] ) ;
}
}
json [ " disabled_features " ] = dis_features ;
FileAccessRef f = FileAccess : : open ( p_path , FileAccess : : WRITE ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_V_MSG ( ! f , ERR_CANT_CREATE , " Cannot create file ' " + p_path + " '. " ) ;
2019-04-09 00:18:03 +02:00
String text = JSON : : print ( json , " \t " ) ;
f - > store_string ( text ) ;
f - > close ( ) ;
return OK ;
}
Error EditorFeatureProfile : : load_from_file ( const String & p_path ) {
Error err ;
String text = FileAccess : : get_file_as_string ( p_path , & err ) ;
if ( err ! = OK ) {
return err ;
}
String err_str ;
int err_line ;
Variant v ;
err = JSON : : parse ( text , v , err_str , err_line ) ;
if ( err ! = OK ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Error parsing ' " + p_path + " ' on line " + itos ( err_line ) + " : " + err_str ) ;
2019-04-09 00:18:03 +02:00
return ERR_PARSE_ERROR ;
}
Dictionary json = v ;
if ( ! json . has ( " type " ) | | String ( json [ " type " ] ) ! = " feature_profile " ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Error parsing ' " + p_path + " ', it's not a feature profile. " ) ;
2019-04-09 00:18:03 +02:00
return ERR_PARSE_ERROR ;
}
disabled_classes . clear ( ) ;
if ( json . has ( " disabled_classes " ) ) {
Array disabled_classes_arr = json [ " disabled_classes " ] ;
for ( int i = 0 ; i < disabled_classes_arr . size ( ) ; i + + ) {
disabled_classes . insert ( disabled_classes_arr [ i ] ) ;
}
}
disabled_editors . clear ( ) ;
if ( json . has ( " disabled_editors " ) ) {
Array disabled_editors_arr = json [ " disabled_editors " ] ;
for ( int i = 0 ; i < disabled_editors_arr . size ( ) ; i + + ) {
disabled_editors . insert ( disabled_editors_arr [ i ] ) ;
}
}
disabled_properties . clear ( ) ;
if ( json . has ( " disabled_properties " ) ) {
Array disabled_properties_arr = json [ " disabled_properties " ] ;
for ( int i = 0 ; i < disabled_properties_arr . size ( ) ; i + + ) {
String s = disabled_properties_arr [ i ] ;
set_disable_class_property ( s . get_slice ( " : " , 0 ) , s . get_slice ( " : " , 1 ) , true ) ;
}
}
if ( json . has ( " disabled_features " ) ) {
Array disabled_features_arr = json [ " disabled_features " ] ;
for ( int i = 0 ; i < FEATURE_MAX ; i + + ) {
bool found = false ;
String f = feature_identifiers [ i ] ;
for ( int j = 0 ; j < disabled_features_arr . size ( ) ; j + + ) {
String fd = disabled_features_arr [ j ] ;
if ( fd = = f ) {
found = true ;
break ;
}
}
features_disabled [ i ] = found ;
}
}
return OK ;
}
void EditorFeatureProfile : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " set_disable_class " , " class_name " , " disable " ) , & EditorFeatureProfile : : set_disable_class ) ;
ClassDB : : bind_method ( D_METHOD ( " is_class_disabled " , " class_name " ) , & EditorFeatureProfile : : is_class_disabled ) ;
ClassDB : : bind_method ( D_METHOD ( " set_disable_class_editor " , " class_name " , " disable " ) , & EditorFeatureProfile : : set_disable_class_editor ) ;
ClassDB : : bind_method ( D_METHOD ( " is_class_editor_disabled " , " class_name " ) , & EditorFeatureProfile : : is_class_editor_disabled ) ;
2019-09-24 11:44:48 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_disable_class_property " , " class_name " , " property " , " disable " ) , & EditorFeatureProfile : : set_disable_class_property ) ;
ClassDB : : bind_method ( D_METHOD ( " is_class_property_disabled " , " class_name " , " property " ) , & EditorFeatureProfile : : is_class_property_disabled ) ;
2019-04-09 00:18:03 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_disable_feature " , " feature " , " disable " ) , & EditorFeatureProfile : : set_disable_feature ) ;
ClassDB : : bind_method ( D_METHOD ( " is_feature_disabled " , " feature " ) , & EditorFeatureProfile : : is_feature_disabled ) ;
ClassDB : : bind_method ( D_METHOD ( " get_feature_name " , " feature " ) , & EditorFeatureProfile : : _get_feature_name ) ;
ClassDB : : bind_method ( D_METHOD ( " save_to_file " , " path " ) , & EditorFeatureProfile : : save_to_file ) ;
ClassDB : : bind_method ( D_METHOD ( " load_from_file " , " path " ) , & EditorFeatureProfile : : load_from_file ) ;
BIND_ENUM_CONSTANT ( FEATURE_3D ) ;
BIND_ENUM_CONSTANT ( FEATURE_SCRIPT ) ;
BIND_ENUM_CONSTANT ( FEATURE_ASSET_LIB ) ;
BIND_ENUM_CONSTANT ( FEATURE_SCENE_TREE ) ;
BIND_ENUM_CONSTANT ( FEATURE_NODE_DOCK ) ;
BIND_ENUM_CONSTANT ( FEATURE_FILESYSTEM_DOCK ) ;
2020-09-04 10:43:11 +02:00
BIND_ENUM_CONSTANT ( FEATURE_IMPORT_DOCK ) ;
2019-04-09 00:18:03 +02:00
BIND_ENUM_CONSTANT ( FEATURE_MAX ) ;
}
EditorFeatureProfile : : EditorFeatureProfile ( ) {
for ( int i = 0 ; i < FEATURE_MAX ; i + + ) {
features_disabled [ i ] = false ;
}
}
//////////////////////////
void EditorFeatureProfileManager : : _notification ( int p_what ) {
if ( p_what = = NOTIFICATION_READY ) {
current_profile = EDITOR_GET ( " _default_feature_profile " ) ;
if ( current_profile ! = String ( ) ) {
current . instance ( ) ;
Error err = current - > load_from_file ( EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) . plus_file ( current_profile + " .profile " ) ) ;
if ( err ! = OK ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( " Error loading default feature profile: " + current_profile ) ;
2019-04-09 00:18:03 +02:00
current_profile = String ( ) ;
current . unref ( ) ;
}
}
_update_profile_list ( current_profile ) ;
}
}
String EditorFeatureProfileManager : : _get_selected_profile ( ) {
int idx = profile_list - > get_selected ( ) ;
if ( idx < 0 ) {
return String ( ) ;
}
return profile_list - > get_item_metadata ( idx ) ;
}
void EditorFeatureProfileManager : : _update_profile_list ( const String & p_select_profile ) {
String selected_profile ;
if ( p_select_profile = = String ( ) ) { //default, keep
if ( profile_list - > get_selected ( ) > = 0 ) {
selected_profile = profile_list - > get_item_metadata ( profile_list - > get_selected ( ) ) ;
if ( ! FileAccess : : exists ( EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) . plus_file ( selected_profile + " .profile " ) ) ) {
selected_profile = String ( ) ; //does not exist
}
}
} else {
selected_profile = p_select_profile ;
}
Vector < String > profiles ;
DirAccessRef d = DirAccess : : open ( EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! d , " Cannot open directory ' " + EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) + " '. " ) ;
2019-04-09 00:18:03 +02:00
d - > list_dir_begin ( ) ;
while ( true ) {
String f = d - > get_next ( ) ;
if ( f = = String ( ) ) {
break ;
}
if ( ! d - > current_is_dir ( ) ) {
2020-07-03 15:26:22 +02:00
int last_pos = f . rfind ( " .profile " ) ;
2019-04-09 00:18:03 +02:00
if ( last_pos ! = - 1 ) {
profiles . push_back ( f . substr ( 0 , last_pos ) ) ;
}
}
}
profiles . sort ( ) ;
profile_list - > clear ( ) ;
for ( int i = 0 ; i < profiles . size ( ) ; i + + ) {
String name = profiles [ i ] ;
if ( i = = 0 & & selected_profile = = String ( ) ) {
selected_profile = name ;
}
if ( name = = current_profile ) {
2020-03-22 21:52:06 +01:00
name + = " " + TTR ( " (current) " ) ;
2019-04-09 00:18:03 +02:00
}
profile_list - > add_item ( name ) ;
int index = profile_list - > get_item_count ( ) - 1 ;
profile_list - > set_item_metadata ( index , profiles [ i ] ) ;
if ( profiles [ i ] = = selected_profile ) {
profile_list - > select ( index ) ;
}
}
2020-03-22 21:52:06 +01:00
class_list_vbc - > set_visible ( selected_profile ! = String ( ) ) ;
property_list_vbc - > set_visible ( selected_profile ! = String ( ) ) ;
no_profile_selected_help - > set_visible ( selected_profile = = String ( ) ) ;
2019-04-09 00:18:03 +02:00
profile_actions [ PROFILE_CLEAR ] - > set_disabled ( current_profile = = String ( ) ) ;
profile_actions [ PROFILE_ERASE ] - > set_disabled ( selected_profile = = String ( ) ) ;
profile_actions [ PROFILE_EXPORT ] - > set_disabled ( selected_profile = = String ( ) ) ;
profile_actions [ PROFILE_SET ] - > set_disabled ( selected_profile = = String ( ) ) ;
2020-03-22 21:52:06 +01:00
current_profile_name - > set_text ( current_profile ! = String ( ) ? current_profile : TTR ( " (none) " ) ) ;
2019-04-09 00:18:03 +02:00
_update_selected_profile ( ) ;
}
void EditorFeatureProfileManager : : _profile_action ( int p_action ) {
switch ( p_action ) {
case PROFILE_CLEAR : {
EditorSettings : : get_singleton ( ) - > set ( " _default_feature_profile " , " " ) ;
EditorSettings : : get_singleton ( ) - > save ( ) ;
current_profile = " " ;
current . unref ( ) ;
2019-06-05 06:12:08 +02:00
2019-04-09 00:18:03 +02:00
_update_profile_list ( ) ;
2019-06-05 06:12:08 +02:00
_emit_current_profile_changed ( ) ;
2019-04-09 00:18:03 +02:00
} break ;
case PROFILE_SET : {
String selected = _get_selected_profile ( ) ;
ERR_FAIL_COND ( selected = = String ( ) ) ;
if ( selected = = current_profile ) {
2019-06-05 06:12:08 +02:00
return ; // Nothing to do here.
2019-04-09 00:18:03 +02:00
}
EditorSettings : : get_singleton ( ) - > set ( " _default_feature_profile " , selected ) ;
EditorSettings : : get_singleton ( ) - > save ( ) ;
current_profile = selected ;
current = edited ;
_update_profile_list ( ) ;
2019-06-05 06:12:08 +02:00
_emit_current_profile_changed ( ) ;
2019-04-09 00:18:03 +02:00
} break ;
case PROFILE_IMPORT : {
2020-07-11 18:45:19 +02:00
import_profiles - > popup_file_dialog ( ) ;
2019-04-09 00:18:03 +02:00
} break ;
case PROFILE_EXPORT : {
2020-07-11 18:45:19 +02:00
export_profile - > popup_file_dialog ( ) ;
2019-04-09 00:18:03 +02:00
export_profile - > set_current_file ( _get_selected_profile ( ) + " .profile " ) ;
} break ;
case PROFILE_NEW : {
2020-03-06 18:00:16 +01:00
new_profile_dialog - > popup_centered ( ) ;
2019-04-09 00:18:03 +02:00
new_profile_name - > clear ( ) ;
new_profile_name - > grab_focus ( ) ;
} break ;
case PROFILE_ERASE : {
String selected = _get_selected_profile ( ) ;
ERR_FAIL_COND ( selected = = String ( ) ) ;
erase_profile_dialog - > set_text ( vformat ( TTR ( " Erase profile '%s'? (no undo) " ) , selected ) ) ;
2020-03-06 18:00:16 +01:00
erase_profile_dialog - > popup_centered ( ) ;
2019-04-09 00:18:03 +02:00
} break ;
}
}
void EditorFeatureProfileManager : : _erase_selected_profile ( ) {
String selected = _get_selected_profile ( ) ;
ERR_FAIL_COND ( selected = = String ( ) ) ;
DirAccessRef da = DirAccess : : open ( EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( ! da , " Cannot open directory ' " + EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) + " '. " ) ;
2019-04-09 00:18:03 +02:00
da - > remove ( selected + " .profile " ) ;
if ( selected = = current_profile ) {
_profile_action ( PROFILE_CLEAR ) ;
} else {
_update_profile_list ( ) ;
}
}
void EditorFeatureProfileManager : : _create_new_profile ( ) {
String name = new_profile_name - > get_text ( ) . strip_edges ( ) ;
if ( ! name . is_valid_filename ( ) | | name . find ( " . " ) ! = - 1 ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Profile must be a valid filename and must not contain '.' " ) ) ;
return ;
}
String file = EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) . plus_file ( name + " .profile " ) ;
if ( FileAccess : : exists ( file ) ) {
EditorNode : : get_singleton ( ) - > show_warning ( TTR ( " Profile with this name already exists. " ) ) ;
return ;
}
Ref < EditorFeatureProfile > new_profile ;
new_profile . instance ( ) ;
new_profile - > save_to_file ( file ) ;
_update_profile_list ( name ) ;
2020-03-22 21:52:06 +01:00
// The newly created profile is the first one, make it the current profile automatically.
if ( profile_list - > get_item_count ( ) = = 1 ) {
_profile_action ( PROFILE_SET ) ;
}
2019-04-09 00:18:03 +02:00
}
void EditorFeatureProfileManager : : _profile_selected ( int p_what ) {
_update_selected_profile ( ) ;
}
void EditorFeatureProfileManager : : _fill_classes_from ( TreeItem * p_parent , const String & p_class , const String & p_selected ) {
TreeItem * class_item = class_list - > create_item ( p_parent ) ;
class_item - > set_cell_mode ( 0 , TreeItem : : CELL_MODE_CHECK ) ;
class_item - > set_icon ( 0 , EditorNode : : get_singleton ( ) - > get_class_icon ( p_class , " Node " ) ) ;
String text = p_class ;
bool disabled = edited - > is_class_disabled ( p_class ) ;
bool disabled_editor = edited - > is_class_editor_disabled ( p_class ) ;
bool disabled_properties = edited - > has_class_properties_disabled ( p_class ) ;
if ( disabled ) {
2020-03-12 13:37:40 +01:00
class_item - > set_custom_color ( 0 , class_list - > get_theme_color ( " disabled_font_color " , " Editor " ) ) ;
2019-04-09 00:18:03 +02:00
} else if ( disabled_editor & & disabled_properties ) {
text + = " " + TTR ( " (Editor Disabled, Properties Disabled) " ) ;
} else if ( disabled_properties ) {
text + = " " + TTR ( " (Properties Disabled) " ) ;
} else if ( disabled_editor ) {
text + = " " + TTR ( " (Editor Disabled) " ) ;
}
class_item - > set_text ( 0 , text ) ;
class_item - > set_editable ( 0 , true ) ;
class_item - > set_selectable ( 0 , true ) ;
class_item - > set_metadata ( 0 , p_class ) ;
if ( p_class = = p_selected ) {
class_item - > select ( 0 ) ;
}
if ( disabled ) {
//class disabled, do nothing else (do not show further)
return ;
}
class_item - > set_checked ( 0 , true ) ; // if its not disabled, its checked
List < StringName > child_classes ;
ClassDB : : get_direct_inheriters_from_class ( p_class , & child_classes ) ;
child_classes . sort_custom < StringName : : AlphCompare > ( ) ;
for ( List < StringName > : : Element * E = child_classes . front ( ) ; E ; E = E - > next ( ) ) {
String name = E - > get ( ) ;
if ( name . begins_with ( " Editor " ) | | ClassDB : : get_api_type ( name ) ! = ClassDB : : API_CORE ) {
continue ;
}
_fill_classes_from ( class_item , name , p_selected ) ;
}
}
void EditorFeatureProfileManager : : _class_list_item_selected ( ) {
2020-05-14 16:41:43 +02:00
if ( updating_features ) {
2019-04-09 00:18:03 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2019-04-09 00:18:03 +02:00
property_list - > clear ( ) ;
TreeItem * item = class_list - > get_selected ( ) ;
if ( ! item ) {
return ;
}
Variant md = item - > get_metadata ( 0 ) ;
2020-02-20 22:58:05 +01:00
if ( md . get_type ( ) ! = Variant : : STRING & & md . get_type ( ) ! = Variant : : STRING_NAME ) {
2019-04-09 00:18:03 +02:00
return ;
}
String class_name = md ;
if ( edited - > is_class_disabled ( class_name ) ) {
return ;
}
updating_features = true ;
TreeItem * root = property_list - > create_item ( ) ;
TreeItem * options = property_list - > create_item ( root ) ;
options - > set_text ( 0 , TTR ( " Class Options: " ) ) ;
{
TreeItem * option = property_list - > create_item ( options ) ;
option - > set_cell_mode ( 0 , TreeItem : : CELL_MODE_CHECK ) ;
option - > set_editable ( 0 , true ) ;
option - > set_selectable ( 0 , true ) ;
2019-04-09 01:20:20 +02:00
option - > set_checked ( 0 , ! edited - > is_class_editor_disabled ( class_name ) ) ;
option - > set_text ( 0 , TTR ( " Enable Contextual Editor " ) ) ;
2019-04-09 00:18:03 +02:00
option - > set_metadata ( 0 , CLASS_OPTION_DISABLE_EDITOR ) ;
}
TreeItem * properties = property_list - > create_item ( root ) ;
properties - > set_text ( 0 , TTR ( " Enabled Properties: " ) ) ;
List < PropertyInfo > props ;
ClassDB : : get_property_list ( class_name , & props , true ) ;
for ( List < PropertyInfo > : : Element * E = props . front ( ) ; E ; E = E - > next ( ) ) {
String name = E - > get ( ) . name ;
2020-05-14 16:41:43 +02:00
if ( ! ( E - > get ( ) . usage & PROPERTY_USAGE_EDITOR ) ) {
2019-04-09 00:18:03 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2019-04-09 00:18:03 +02:00
TreeItem * property = property_list - > create_item ( properties ) ;
property - > set_cell_mode ( 0 , TreeItem : : CELL_MODE_CHECK ) ;
property - > set_editable ( 0 , true ) ;
property - > set_selectable ( 0 , true ) ;
property - > set_checked ( 0 , ! edited - > is_class_property_disabled ( class_name , name ) ) ;
property - > set_text ( 0 , name . capitalize ( ) ) ;
property - > set_metadata ( 0 , name ) ;
String icon_type = Variant : : get_type_name ( E - > get ( ) . type ) ;
property - > set_icon ( 0 , EditorNode : : get_singleton ( ) - > get_class_icon ( icon_type ) ) ;
}
updating_features = false ;
}
void EditorFeatureProfileManager : : _class_list_item_edited ( ) {
2020-05-14 16:41:43 +02:00
if ( updating_features ) {
2019-04-09 00:18:03 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2019-04-09 00:18:03 +02:00
TreeItem * item = class_list - > get_edited ( ) ;
if ( ! item ) {
return ;
}
bool checked = item - > is_checked ( 0 ) ;
Variant md = item - > get_metadata ( 0 ) ;
2020-02-20 22:58:05 +01:00
if ( md . get_type ( ) = = Variant : : STRING | | md . get_type ( ) = = Variant : : STRING_NAME ) {
2019-04-09 00:18:03 +02:00
String class_selected = md ;
edited - > set_disable_class ( class_selected , ! checked ) ;
_save_and_update ( ) ;
_update_selected_profile ( ) ;
} else if ( md . get_type ( ) = = Variant : : INT ) {
int feature_selected = md ;
edited - > set_disable_feature ( EditorFeatureProfile : : Feature ( feature_selected ) , ! checked ) ;
_save_and_update ( ) ;
}
}
void EditorFeatureProfileManager : : _property_item_edited ( ) {
2020-05-14 16:41:43 +02:00
if ( updating_features ) {
2019-04-09 00:18:03 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2019-04-09 00:18:03 +02:00
TreeItem * class_item = class_list - > get_selected ( ) ;
if ( ! class_item ) {
return ;
}
Variant md = class_item - > get_metadata ( 0 ) ;
2020-02-20 22:58:05 +01:00
if ( md . get_type ( ) ! = Variant : : STRING & & md . get_type ( ) ! = Variant : : STRING_NAME ) {
2019-04-09 00:18:03 +02:00
return ;
}
String class_name = md ;
TreeItem * item = property_list - > get_edited ( ) ;
if ( ! item ) {
return ;
}
bool checked = item - > is_checked ( 0 ) ;
md = item - > get_metadata ( 0 ) ;
2020-02-20 22:58:05 +01:00
if ( md . get_type ( ) = = Variant : : STRING | | md . get_type ( ) = = Variant : : STRING_NAME ) {
2019-04-09 00:18:03 +02:00
String property_selected = md ;
edited - > set_disable_class_property ( class_name , property_selected , ! checked ) ;
_save_and_update ( ) ;
_update_selected_profile ( ) ;
} else if ( md . get_type ( ) = = Variant : : INT ) {
int feature_selected = md ;
switch ( feature_selected ) {
case CLASS_OPTION_DISABLE_EDITOR : {
2019-04-09 01:20:20 +02:00
edited - > set_disable_class_editor ( class_name , ! checked ) ;
2019-04-09 00:18:03 +02:00
_save_and_update ( ) ;
_update_selected_profile ( ) ;
} break ;
}
}
}
void EditorFeatureProfileManager : : _update_selected_profile ( ) {
String class_selected ;
int feature_selected = - 1 ;
if ( class_list - > get_selected ( ) ) {
Variant md = class_list - > get_selected ( ) - > get_metadata ( 0 ) ;
2020-02-20 22:58:05 +01:00
if ( md . get_type ( ) = = Variant : : STRING | | md . get_type ( ) = = Variant : : STRING_NAME ) {
2019-04-09 00:18:03 +02:00
class_selected = md ;
} else if ( md . get_type ( ) = = Variant : : INT ) {
feature_selected = md ;
}
}
class_list - > clear ( ) ;
String profile = _get_selected_profile ( ) ;
if ( profile = = String ( ) ) { //nothing selected, nothing edited
property_list - > clear ( ) ;
edited . unref ( ) ;
return ;
}
if ( profile = = current_profile ) {
edited = current ; //reuse current profile (which is what editor uses)
ERR_FAIL_COND ( current . is_null ( ) ) ; //nothing selected, current should never be null
} else {
//reload edited, if different from current
edited . instance ( ) ;
Error err = edited - > load_from_file ( EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) . plus_file ( profile + " .profile " ) ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( err ! = OK , " Error when loading EditorSettings from file ' " + EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) . plus_file ( profile + " .profile " ) + " '. " ) ;
2019-04-09 00:18:03 +02:00
}
updating_features = true ;
TreeItem * root = class_list - > create_item ( ) ;
TreeItem * features = class_list - > create_item ( root ) ;
2020-05-08 21:51:42 +02:00
TreeItem * last_feature ;
2019-04-09 00:18:03 +02:00
features - > set_text ( 0 , TTR ( " Enabled Features: " ) ) ;
for ( int i = 0 ; i < EditorFeatureProfile : : FEATURE_MAX ; i + + ) {
2020-05-08 21:51:42 +02:00
TreeItem * feature ;
if ( i = = EditorFeatureProfile : : FEATURE_IMPORT_DOCK ) {
feature = class_list - > create_item ( last_feature ) ;
} else {
feature = class_list - > create_item ( features ) ;
last_feature = feature ;
}
2019-04-09 00:18:03 +02:00
feature - > set_cell_mode ( 0 , TreeItem : : CELL_MODE_CHECK ) ;
feature - > set_text ( 0 , TTRGET ( EditorFeatureProfile : : get_feature_name ( EditorFeatureProfile : : Feature ( i ) ) ) ) ;
feature - > set_selectable ( 0 , true ) ;
feature - > set_editable ( 0 , true ) ;
feature - > set_metadata ( 0 , i ) ;
if ( ! edited - > is_feature_disabled ( EditorFeatureProfile : : Feature ( i ) ) ) {
feature - > set_checked ( 0 , true ) ;
}
if ( i = = feature_selected ) {
feature - > select ( 0 ) ;
}
}
TreeItem * classes = class_list - > create_item ( root ) ;
classes - > set_text ( 0 , TTR ( " Enabled Classes: " ) ) ;
_fill_classes_from ( classes , " Node " , class_selected ) ;
_fill_classes_from ( classes , " Resource " , class_selected ) ;
updating_features = false ;
_class_list_item_selected ( ) ;
}
void EditorFeatureProfileManager : : _import_profiles ( const Vector < String > & p_paths ) {
//test it first
for ( int i = 0 ; i < p_paths . size ( ) ; i + + ) {
Ref < EditorFeatureProfile > profile ;
profile . instance ( ) ;
Error err = profile - > load_from_file ( p_paths [ i ] ) ;
String basefile = p_paths [ i ] . get_file ( ) ;
if ( err ! = OK ) {
EditorNode : : get_singleton ( ) - > show_warning ( vformat ( TTR ( " File '%s' format is invalid, import aborted. " ) , basefile ) ) ;
return ;
}
String dst_file = EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) . plus_file ( basefile ) ;
if ( FileAccess : : exists ( dst_file ) ) {
2019-06-17 14:11:49 +02:00
EditorNode : : get_singleton ( ) - > show_warning ( vformat ( TTR ( " Profile '%s' already exists. Remove it first before importing, import aborted. " ) , basefile . get_basename ( ) ) ) ;
2019-04-09 00:18:03 +02:00
return ;
}
}
//do it second
for ( int i = 0 ; i < p_paths . size ( ) ; i + + ) {
Ref < EditorFeatureProfile > profile ;
profile . instance ( ) ;
Error err = profile - > load_from_file ( p_paths [ i ] ) ;
ERR_CONTINUE ( err ! = OK ) ;
String basefile = p_paths [ i ] . get_file ( ) ;
String dst_file = EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) . plus_file ( basefile ) ;
profile - > save_to_file ( dst_file ) ;
}
_update_profile_list ( ) ;
2020-03-22 21:52:06 +01:00
// The newly imported profile is the first one, make it the current profile automatically.
if ( profile_list - > get_item_count ( ) = = 1 ) {
_profile_action ( PROFILE_SET ) ;
}
2019-04-09 00:18:03 +02:00
}
void EditorFeatureProfileManager : : _export_profile ( const String & p_path ) {
ERR_FAIL_COND ( edited . is_null ( ) ) ;
Error err = edited - > save_to_file ( p_path ) ;
if ( err ! = OK ) {
EditorNode : : get_singleton ( ) - > show_warning ( vformat ( TTR ( " Error saving profile to path: '%s'. " ) , p_path ) ) ;
}
}
void EditorFeatureProfileManager : : _save_and_update ( ) {
String edited_path = _get_selected_profile ( ) ;
ERR_FAIL_COND ( edited_path = = String ( ) ) ;
ERR_FAIL_COND ( edited . is_null ( ) ) ;
edited - > save_to_file ( EditorSettings : : get_singleton ( ) - > get_feature_profiles_dir ( ) . plus_file ( edited_path + " .profile " ) ) ;
if ( edited = = current ) {
update_timer - > start ( ) ;
}
}
void EditorFeatureProfileManager : : _emit_current_profile_changed ( ) {
emit_signal ( " current_feature_profile_changed " ) ;
}
void EditorFeatureProfileManager : : notify_changed ( ) {
_emit_current_profile_changed ( ) ;
}
Ref < EditorFeatureProfile > EditorFeatureProfileManager : : get_current_profile ( ) {
return current ;
}
2020-04-02 01:20:12 +02:00
EditorFeatureProfileManager * EditorFeatureProfileManager : : singleton = nullptr ;
2019-04-09 00:18:03 +02:00
void EditorFeatureProfileManager : : _bind_methods ( ) {
ClassDB : : bind_method ( " _update_selected_profile " , & EditorFeatureProfileManager : : _update_selected_profile ) ;
ADD_SIGNAL ( MethodInfo ( " current_feature_profile_changed " ) ) ;
}
EditorFeatureProfileManager : : EditorFeatureProfileManager ( ) {
VBoxContainer * main_vbc = memnew ( VBoxContainer ) ;
add_child ( main_vbc ) ;
HBoxContainer * name_hbc = memnew ( HBoxContainer ) ;
current_profile_name = memnew ( LineEdit ) ;
name_hbc - > add_child ( current_profile_name ) ;
2020-03-22 21:52:06 +01:00
current_profile_name - > set_text ( TTR ( " (none) " ) ) ;
2019-04-09 00:18:03 +02:00
current_profile_name - > set_editable ( false ) ;
2020-03-06 18:00:16 +01:00
current_profile_name - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2019-04-09 00:18:03 +02:00
profile_actions [ PROFILE_CLEAR ] = memnew ( Button ( TTR ( " Unset " ) ) ) ;
name_hbc - > add_child ( profile_actions [ PROFILE_CLEAR ] ) ;
profile_actions [ PROFILE_CLEAR ] - > set_disabled ( true ) ;
2020-02-21 18:28:45 +01:00
profile_actions [ PROFILE_CLEAR ] - > connect ( " pressed " , callable_mp ( this , & EditorFeatureProfileManager : : _profile_action ) , varray ( PROFILE_CLEAR ) ) ;
2019-04-09 00:18:03 +02:00
2019-06-05 06:12:08 +02:00
main_vbc - > add_margin_child ( TTR ( " Current Profile: " ) , name_hbc ) ;
2019-04-09 00:18:03 +02:00
HBoxContainer * profiles_hbc = memnew ( HBoxContainer ) ;
profile_list = memnew ( OptionButton ) ;
2020-03-06 18:00:16 +01:00
profile_list - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2019-04-09 00:18:03 +02:00
profiles_hbc - > add_child ( profile_list ) ;
2020-02-21 18:28:45 +01:00
profile_list - > connect ( " item_selected " , callable_mp ( this , & EditorFeatureProfileManager : : _profile_selected ) ) ;
2019-04-09 00:18:03 +02:00
profile_actions [ PROFILE_SET ] = memnew ( Button ( TTR ( " Make Current " ) ) ) ;
profiles_hbc - > add_child ( profile_actions [ PROFILE_SET ] ) ;
profile_actions [ PROFILE_SET ] - > set_disabled ( true ) ;
2020-02-21 18:28:45 +01:00
profile_actions [ PROFILE_SET ] - > connect ( " pressed " , callable_mp ( this , & EditorFeatureProfileManager : : _profile_action ) , varray ( PROFILE_SET ) ) ;
2019-04-09 00:18:03 +02:00
profile_actions [ PROFILE_ERASE ] = memnew ( Button ( TTR ( " Remove " ) ) ) ;
profiles_hbc - > add_child ( profile_actions [ PROFILE_ERASE ] ) ;
profile_actions [ PROFILE_ERASE ] - > set_disabled ( true ) ;
2020-02-21 18:28:45 +01:00
profile_actions [ PROFILE_ERASE ] - > connect ( " pressed " , callable_mp ( this , & EditorFeatureProfileManager : : _profile_action ) , varray ( PROFILE_ERASE ) ) ;
2019-04-09 00:18:03 +02:00
profiles_hbc - > add_child ( memnew ( VSeparator ) ) ;
profile_actions [ PROFILE_NEW ] = memnew ( Button ( TTR ( " New " ) ) ) ;
profiles_hbc - > add_child ( profile_actions [ PROFILE_NEW ] ) ;
2020-02-21 18:28:45 +01:00
profile_actions [ PROFILE_NEW ] - > connect ( " pressed " , callable_mp ( this , & EditorFeatureProfileManager : : _profile_action ) , varray ( PROFILE_NEW ) ) ;
2019-04-09 00:18:03 +02:00
profiles_hbc - > add_child ( memnew ( VSeparator ) ) ;
profile_actions [ PROFILE_IMPORT ] = memnew ( Button ( TTR ( " Import " ) ) ) ;
profiles_hbc - > add_child ( profile_actions [ PROFILE_IMPORT ] ) ;
2020-02-21 18:28:45 +01:00
profile_actions [ PROFILE_IMPORT ] - > connect ( " pressed " , callable_mp ( this , & EditorFeatureProfileManager : : _profile_action ) , varray ( PROFILE_IMPORT ) ) ;
2019-04-09 00:18:03 +02:00
profile_actions [ PROFILE_EXPORT ] = memnew ( Button ( TTR ( " Export " ) ) ) ;
profiles_hbc - > add_child ( profile_actions [ PROFILE_EXPORT ] ) ;
profile_actions [ PROFILE_EXPORT ] - > set_disabled ( true ) ;
2020-02-21 18:28:45 +01:00
profile_actions [ PROFILE_EXPORT ] - > connect ( " pressed " , callable_mp ( this , & EditorFeatureProfileManager : : _profile_action ) , varray ( PROFILE_EXPORT ) ) ;
2019-04-09 00:18:03 +02:00
2019-06-05 06:12:08 +02:00
main_vbc - > add_margin_child ( TTR ( " Available Profiles: " ) , profiles_hbc ) ;
2019-04-09 00:18:03 +02:00
h_split = memnew ( HSplitContainer ) ;
2020-03-06 18:00:16 +01:00
h_split - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2019-04-09 00:18:03 +02:00
main_vbc - > add_child ( h_split ) ;
2020-03-22 21:52:06 +01:00
class_list_vbc = memnew ( VBoxContainer ) ;
2019-04-09 00:18:03 +02:00
h_split - > add_child ( class_list_vbc ) ;
2020-03-06 18:00:16 +01:00
class_list_vbc - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2019-04-09 00:18:03 +02:00
class_list = memnew ( Tree ) ;
2019-06-05 06:12:08 +02:00
class_list_vbc - > add_margin_child ( TTR ( " Enabled Classes: " ) , class_list , true ) ;
2019-04-09 00:18:03 +02:00
class_list - > set_hide_root ( true ) ;
class_list - > set_edit_checkbox_cell_only_when_checkbox_is_pressed ( true ) ;
2020-02-21 18:28:45 +01:00
class_list - > connect ( " cell_selected " , callable_mp ( this , & EditorFeatureProfileManager : : _class_list_item_selected ) ) ;
class_list - > connect ( " item_edited " , callable_mp ( this , & EditorFeatureProfileManager : : _class_list_item_edited ) , varray ( ) , CONNECT_DEFERRED ) ;
2020-03-22 21:52:06 +01:00
// It will be displayed once the user creates or chooses a profile.
class_list_vbc - > hide ( ) ;
2019-04-09 00:18:03 +02:00
2020-03-22 21:52:06 +01:00
property_list_vbc = memnew ( VBoxContainer ) ;
2019-04-09 00:18:03 +02:00
h_split - > add_child ( property_list_vbc ) ;
2020-03-06 18:00:16 +01:00
property_list_vbc - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2019-04-09 00:18:03 +02:00
property_list = memnew ( Tree ) ;
2020-03-22 21:52:06 +01:00
property_list_vbc - > add_margin_child ( TTR ( " Class Options: " ) , property_list , true ) ;
2019-04-09 00:18:03 +02:00
property_list - > set_hide_root ( true ) ;
property_list - > set_hide_folding ( true ) ;
property_list - > set_edit_checkbox_cell_only_when_checkbox_is_pressed ( true ) ;
2020-02-21 18:28:45 +01:00
property_list - > connect ( " item_edited " , callable_mp ( this , & EditorFeatureProfileManager : : _property_item_edited ) , varray ( ) , CONNECT_DEFERRED ) ;
2020-03-22 21:52:06 +01:00
// It will be displayed once the user creates or chooses a profile.
property_list_vbc - > hide ( ) ;
no_profile_selected_help = memnew ( Label ( TTR ( " Create or import a profile to edit available classes and properties. " ) ) ) ;
// Add some spacing above the help label.
Ref < StyleBoxEmpty > sb = memnew ( StyleBoxEmpty ) ;
sb - > set_default_margin ( MARGIN_TOP , 20 * EDSCALE ) ;
2020-05-25 13:10:28 +02:00
no_profile_selected_help - > add_theme_style_override ( " normal " , sb ) ;
2020-03-22 21:52:06 +01:00
no_profile_selected_help - > set_align ( Label : : ALIGN_CENTER ) ;
2020-05-25 13:10:28 +02:00
no_profile_selected_help - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2020-03-22 21:52:06 +01:00
h_split - > add_child ( no_profile_selected_help ) ;
2019-04-09 00:18:03 +02:00
new_profile_dialog = memnew ( ConfirmationDialog ) ;
new_profile_dialog - > set_title ( TTR ( " New profile name: " ) ) ;
new_profile_name = memnew ( LineEdit ) ;
new_profile_dialog - > add_child ( new_profile_name ) ;
new_profile_name - > set_custom_minimum_size ( Size2 ( 300 * EDSCALE , 1 ) ) ;
add_child ( new_profile_dialog ) ;
2020-02-21 18:28:45 +01:00
new_profile_dialog - > connect ( " confirmed " , callable_mp ( this , & EditorFeatureProfileManager : : _create_new_profile ) ) ;
2019-04-09 00:18:03 +02:00
new_profile_dialog - > register_text_enter ( new_profile_name ) ;
new_profile_dialog - > get_ok ( ) - > set_text ( TTR ( " Create " ) ) ;
erase_profile_dialog = memnew ( ConfirmationDialog ) ;
add_child ( erase_profile_dialog ) ;
erase_profile_dialog - > set_title ( TTR ( " Erase Profile " ) ) ;
2020-02-21 18:28:45 +01:00
erase_profile_dialog - > connect ( " confirmed " , callable_mp ( this , & EditorFeatureProfileManager : : _erase_selected_profile ) ) ;
2019-04-09 00:18:03 +02:00
import_profiles = memnew ( EditorFileDialog ) ;
add_child ( import_profiles ) ;
2020-03-06 18:00:16 +01:00
import_profiles - > set_file_mode ( EditorFileDialog : : FILE_MODE_OPEN_FILES ) ;
2019-12-16 06:18:44 +01:00
import_profiles - > add_filter ( " *.profile; " + TTR ( " Godot Feature Profile " ) ) ;
2020-02-21 18:28:45 +01:00
import_profiles - > connect ( " files_selected " , callable_mp ( this , & EditorFeatureProfileManager : : _import_profiles ) ) ;
2019-04-09 00:18:03 +02:00
import_profiles - > set_title ( TTR ( " Import Profile(s) " ) ) ;
import_profiles - > set_access ( EditorFileDialog : : ACCESS_FILESYSTEM ) ;
export_profile = memnew ( EditorFileDialog ) ;
add_child ( export_profile ) ;
2020-03-06 18:00:16 +01:00
export_profile - > set_file_mode ( EditorFileDialog : : FILE_MODE_SAVE_FILE ) ;
2019-12-16 06:18:44 +01:00
export_profile - > add_filter ( " *.profile; " + TTR ( " Godot Feature Profile " ) ) ;
2020-02-21 18:28:45 +01:00
export_profile - > connect ( " file_selected " , callable_mp ( this , & EditorFeatureProfileManager : : _export_profile ) ) ;
2019-04-09 00:18:03 +02:00
export_profile - > set_title ( TTR ( " Export Profile " ) ) ;
export_profile - > set_access ( EditorFileDialog : : ACCESS_FILESYSTEM ) ;
set_title ( TTR ( " Manage Editor Feature Profiles " ) ) ;
EDITOR_DEF ( " _default_feature_profile " , " " ) ;
update_timer = memnew ( Timer ) ;
update_timer - > set_wait_time ( 1 ) ; //wait a second before updating editor
add_child ( update_timer ) ;
2020-02-21 18:28:45 +01:00
update_timer - > connect ( " timeout " , callable_mp ( this , & EditorFeatureProfileManager : : _emit_current_profile_changed ) ) ;
2019-04-09 00:18:03 +02:00
update_timer - > set_one_shot ( true ) ;
updating_features = false ;
singleton = this ;
}