2018-05-16 19:19:33 +02:00
/*************************************************************************/
/* editor_inspector.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
2021-01-01 20:13:46 +01:00
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
2018-05-16 19:19:33 +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. */
/*************************************************************************/
2018-05-15 22:12:35 +02:00
# include "editor_inspector.h"
2020-03-05 15:33:01 +01:00
2018-05-15 22:12:35 +02:00
# include "array_property_edit.h"
2020-06-08 15:25:52 +02:00
# include "core/os/keyboard.h"
2018-05-15 22:12:35 +02:00
# include "dictionary_property_edit.h"
2020-11-29 04:42:06 +01:00
# include "editor/doc_tools.h"
2019-12-24 08:17:23 +01:00
# include "editor_feature_profile.h"
2018-05-15 22:12:35 +02:00
# include "editor_node.h"
# include "editor_scale.h"
2020-06-08 15:25:52 +02:00
# include "editor_settings.h"
2018-05-15 22:12:35 +02:00
# include "multi_node_edit.h"
# include "scene/resources/packed_scene.h"
Size2 EditorProperty : : get_minimum_size ( ) const {
Size2 ms ;
2021-07-17 23:22:52 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " font " ) , SNAME ( " Tree " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " font_size " ) , SNAME ( " Tree " ) ) ;
2020-09-03 13:22:16 +02:00
ms . height = font - > get_height ( font_size ) ;
2018-08-07 17:19:19 +02:00
2018-05-15 22:12:35 +02:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2020-05-14 16:41:43 +02:00
if ( ! c ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2020-10-01 09:17:33 +02:00
if ( c - > is_set_as_top_level ( ) ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
if ( ! c - > is_visible ( ) ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
if ( c = = bottom_editor ) {
2018-05-17 23:02:16 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-05-17 23:02:16 +02:00
2018-05-15 22:12:35 +02:00
Size2 minsize = c - > get_combined_minimum_size ( ) ;
ms . width = MAX ( ms . width , minsize . width ) ;
ms . height = MAX ( ms . height , minsize . height ) ;
}
if ( keying ) {
2021-07-17 23:22:52 +02:00
Ref < Texture2D > key = get_theme_icon ( SNAME ( " Key " ) , SNAME ( " EditorIcons " ) ) ;
ms . width + = key - > get_width ( ) + get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) ;
2018-05-15 22:12:35 +02:00
}
2020-04-17 04:52:00 +02:00
if ( deletable ) {
2021-07-17 23:22:52 +02:00
Ref < Texture2D > key = get_theme_icon ( SNAME ( " Close " ) , SNAME ( " EditorIcons " ) ) ;
ms . width + = key - > get_width ( ) + get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) ;
2020-04-17 04:52:00 +02:00
}
2018-05-15 22:12:35 +02:00
if ( checkable ) {
2021-07-17 23:22:52 +02:00
Ref < Texture2D > check = get_theme_icon ( SNAME ( " checked " ) , SNAME ( " CheckBox " ) ) ;
ms . width + = check - > get_width ( ) + get_theme_constant ( SNAME ( " hseparation " ) , SNAME ( " CheckBox " ) ) + get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) ;
2018-05-15 22:12:35 +02:00
}
2020-04-02 01:20:12 +02:00
if ( bottom_editor ! = nullptr & & bottom_editor - > is_visible ( ) ) {
2021-07-17 23:22:52 +02:00
ms . height + = get_theme_constant ( SNAME ( " vseparation " ) ) ;
2018-05-17 23:02:16 +02:00
Size2 bems = bottom_editor - > get_combined_minimum_size ( ) ;
2018-08-07 17:19:19 +02:00
//bems.width += get_constant("item_margin", "Tree");
2018-05-17 23:02:16 +02:00
ms . height + = bems . height ;
ms . width = MAX ( ms . width , bems . width ) ;
2018-05-15 22:12:35 +02:00
}
return ms ;
}
2019-01-18 17:01:24 +01:00
void EditorProperty : : emit_changed ( const StringName & p_property , const Variant & p_value , const StringName & p_field , bool p_changing ) {
2019-03-06 13:24:59 +01:00
Variant args [ 4 ] = { p_property , p_value , p_field , p_changing } ;
const Variant * argptrs [ 4 ] = { & args [ 0 ] , & args [ 1 ] , & args [ 2 ] , & args [ 3 ] } ;
2021-02-10 21:18:45 +01:00
cache [ p_property ] = p_value ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " property_changed " ) , ( const Variant * * ) argptrs , 4 ) ;
2019-01-18 17:01:24 +01:00
}
2018-05-15 22:12:35 +02:00
void EditorProperty : : _notification ( int p_what ) {
if ( p_what = = NOTIFICATION_SORT_CHILDREN ) {
Size2 size = get_size ( ) ;
Rect2 rect ;
2018-05-17 23:02:16 +02:00
Rect2 bottom_rect ;
2018-05-15 22:12:35 +02:00
2018-07-19 00:37:17 +02:00
right_child_rect = Rect2 ( ) ;
bottom_child_rect = Rect2 ( ) ;
2018-05-17 23:02:16 +02:00
{
2018-07-14 23:15:42 +02:00
int child_room = size . width * ( 1.0 - split_ratio ) ;
2021-07-17 23:22:52 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " font " ) , SNAME ( " Tree " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " font_size " ) , SNAME ( " Tree " ) ) ;
2020-09-03 13:22:16 +02:00
int height = font - > get_height ( font_size ) ;
2018-08-07 17:19:19 +02:00
bool no_children = true ;
2018-05-15 22:12:35 +02:00
//compute room needed
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2020-05-14 16:41:43 +02:00
if ( ! c ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2020-10-01 09:17:33 +02:00
if ( c - > is_set_as_top_level ( ) ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
if ( c = = bottom_editor ) {
2018-05-17 23:02:16 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
Size2 minsize = c - > get_combined_minimum_size ( ) ;
child_room = MAX ( child_room , minsize . width ) ;
2018-05-17 23:02:16 +02:00
height = MAX ( height , minsize . height ) ;
2018-08-07 17:19:19 +02:00
no_children = false ;
2018-05-15 22:12:35 +02:00
}
2018-08-07 17:19:19 +02:00
if ( no_children ) {
text_size = size . width ;
rect = Rect2 ( size . width - 1 , 0 , 1 , height ) ;
} else {
text_size = MAX ( 0 , size . width - ( child_room + 4 * EDSCALE ) ) ;
2020-09-03 13:22:16 +02:00
if ( is_layout_rtl ( ) ) {
rect = Rect2 ( 1 , 0 , child_room , height ) ;
} else {
rect = Rect2 ( size . width - child_room , 0 , child_room , height ) ;
}
2018-08-07 17:19:19 +02:00
}
2018-05-17 23:02:16 +02:00
if ( bottom_editor ) {
2018-07-19 00:37:17 +02:00
int m = 0 ; //get_constant("item_margin", "Tree");
2021-07-17 23:22:52 +02:00
bottom_rect = Rect2 ( m , rect . size . height + get_theme_constant ( SNAME ( " vseparation " ) ) , size . width - m , bottom_editor - > get_combined_minimum_size ( ) . height ) ;
2018-05-17 23:02:16 +02:00
}
2018-05-15 22:12:35 +02:00
2018-11-30 17:00:04 +01:00
if ( keying ) {
2019-06-11 20:43:37 +02:00
Ref < Texture2D > key ;
2018-05-15 22:12:35 +02:00
2018-11-30 17:00:04 +01:00
if ( use_keying_next ( ) ) {
2021-07-17 23:22:52 +02:00
key = get_theme_icon ( SNAME ( " KeyNext " ) , SNAME ( " EditorIcons " ) ) ;
2018-11-30 17:00:04 +01:00
} else {
2021-07-17 23:22:52 +02:00
key = get_theme_icon ( SNAME ( " Key " ) , SNAME ( " EditorIcons " ) ) ;
2018-11-30 17:00:04 +01:00
}
2018-05-15 22:12:35 +02:00
2021-07-17 23:22:52 +02:00
rect . size . x - = key - > get_width ( ) + get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) ;
2020-09-03 13:22:16 +02:00
if ( is_layout_rtl ( ) ) {
2021-07-17 23:22:52 +02:00
rect . position . x + = key - > get_width ( ) + get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) ;
2020-09-03 13:22:16 +02:00
}
2018-11-30 17:00:04 +01:00
if ( no_children ) {
text_size - = key - > get_width ( ) + 4 * EDSCALE ;
}
}
2020-04-17 04:52:00 +02:00
if ( deletable ) {
Ref < Texture2D > close ;
2021-07-17 23:22:52 +02:00
close = get_theme_icon ( SNAME ( " Close " ) , SNAME ( " EditorIcons " ) ) ;
2020-04-17 04:52:00 +02:00
2021-07-17 23:22:52 +02:00
rect . size . x - = close - > get_width ( ) + get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) ;
2020-04-17 04:52:00 +02:00
2020-09-03 13:22:16 +02:00
if ( is_layout_rtl ( ) ) {
2021-07-17 23:22:52 +02:00
rect . position . x + = close - > get_width ( ) + get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) ;
2020-09-03 13:22:16 +02:00
}
2020-04-17 04:52:00 +02:00
if ( no_children ) {
text_size - = close - > get_width ( ) + 4 * EDSCALE ;
}
}
2018-05-15 22:12:35 +02:00
}
//set children
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2020-05-14 16:41:43 +02:00
if ( ! c ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2020-10-01 09:17:33 +02:00
if ( c - > is_set_as_top_level ( ) ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
if ( c = = bottom_editor ) {
2018-05-17 23:02:16 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
fit_child_in_rect ( c , rect ) ;
2018-07-19 00:37:17 +02:00
right_child_rect = rect ;
2018-05-15 22:12:35 +02:00
}
2018-05-17 23:02:16 +02:00
if ( bottom_editor ) {
fit_child_in_rect ( bottom_editor , bottom_rect ) ;
2018-07-19 00:37:17 +02:00
bottom_child_rect = bottom_rect ;
2018-05-17 23:02:16 +02:00
}
2018-05-15 22:12:35 +02:00
update ( ) ; //need to redraw text
}
if ( p_what = = NOTIFICATION_DRAW ) {
2021-07-17 23:22:52 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " font " ) , SNAME ( " Tree " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " font_size " ) , SNAME ( " Tree " ) ) ;
Color dark_color = get_theme_color ( SNAME ( " dark_color_2 " ) , SNAME ( " Editor " ) ) ;
2020-09-03 13:22:16 +02:00
bool rtl = is_layout_rtl ( ) ;
2018-05-15 22:12:35 +02:00
Size2 size = get_size ( ) ;
2018-05-17 23:02:16 +02:00
if ( bottom_editor ) {
2020-12-22 17:24:29 +01:00
size . height = bottom_editor - > get_offset ( SIDE_TOP ) ;
2018-05-15 22:12:35 +02:00
} else if ( label_reference ) {
size . height = label_reference - > get_size ( ) . height ;
}
2021-02-11 22:01:56 +01:00
Ref < StyleBox > sb ;
2018-05-15 22:12:35 +02:00
if ( selected ) {
2021-07-17 23:22:52 +02:00
sb = get_theme_stylebox ( SNAME ( " bg_selected " ) ) ;
2021-02-11 22:01:56 +01:00
} else {
2021-07-17 23:22:52 +02:00
sb = get_theme_stylebox ( SNAME ( " bg " ) ) ;
2018-05-15 22:12:35 +02:00
}
2021-02-11 22:01:56 +01:00
draw_style_box ( sb , Rect2 ( Vector2 ( ) , size ) ) ;
2018-08-07 17:19:19 +02:00
if ( draw_top_bg & & right_child_rect ! = Rect2 ( ) ) {
2018-07-19 00:37:17 +02:00
draw_rect ( right_child_rect , dark_color ) ;
}
if ( bottom_child_rect ! = Rect2 ( ) ) {
draw_rect ( bottom_child_rect , dark_color ) ;
}
2018-05-15 22:12:35 +02:00
Color color ;
if ( draw_red ) {
2021-08-16 04:42:24 +02:00
color = get_theme_color ( is_read_only ( ) ? SNAME ( " readonly_error_color " ) : SNAME ( " error_color " ) ) ;
2018-05-15 22:12:35 +02:00
} else {
2021-08-16 04:42:24 +02:00
color = get_theme_color ( is_read_only ( ) ? SNAME ( " readonly_color " ) : SNAME ( " property_color " ) ) ;
2018-05-15 22:12:35 +02:00
}
if ( label . find ( " . " ) ! = - 1 ) {
color . a = 0.5 ; //this should be un-hacked honestly, as it's used for editor overrides
}
2021-07-17 23:22:52 +02:00
int ofs = get_theme_constant ( SNAME ( " font_offset " ) ) ;
2019-04-23 03:33:53 +02:00
int text_limit = text_size ;
2018-05-15 22:12:35 +02:00
if ( checkable ) {
2019-06-11 20:43:37 +02:00
Ref < Texture2D > checkbox ;
2020-05-14 16:41:43 +02:00
if ( checked ) {
2021-07-17 23:22:52 +02:00
checkbox = get_theme_icon ( SNAME ( " GuiChecked " ) , SNAME ( " EditorIcons " ) ) ;
2020-05-14 16:41:43 +02:00
} else {
2021-07-17 23:22:52 +02:00
checkbox = get_theme_icon ( SNAME ( " GuiUnchecked " ) , SNAME ( " EditorIcons " ) ) ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2019-02-12 21:10:08 +01:00
Color color2 ( 1 , 1 , 1 ) ;
2018-05-15 22:12:35 +02:00
if ( check_hover ) {
2019-02-12 21:10:08 +01:00
color2 . r * = 1.2 ;
color2 . g * = 1.2 ;
color2 . b * = 1.2 ;
2018-05-15 22:12:35 +02:00
}
check_rect = Rect2 ( ofs , ( ( size . height - checkbox - > get_height ( ) ) / 2 ) , checkbox - > get_width ( ) , checkbox - > get_height ( ) ) ;
2020-09-03 13:22:16 +02:00
if ( rtl ) {
draw_texture ( checkbox , Vector2 ( size . width - check_rect . position . x - checkbox - > get_width ( ) , check_rect . position . y ) , color2 ) ;
} else {
draw_texture ( checkbox , check_rect . position , color2 ) ;
}
2021-07-17 23:22:52 +02:00
ofs + = get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) + checkbox - > get_width ( ) + get_theme_constant ( SNAME ( " hseparation " ) , SNAME ( " CheckBox " ) ) ;
2019-04-23 03:33:53 +02:00
text_limit - = ofs ;
2018-05-15 22:12:35 +02:00
} else {
check_rect = Rect2 ( ) ;
}
2021-08-16 04:42:24 +02:00
if ( can_revert & & ! is_read_only ( ) ) {
2021-07-17 23:22:52 +02:00
Ref < Texture2D > reload_icon = get_theme_icon ( SNAME ( " ReloadSmall " ) , SNAME ( " EditorIcons " ) ) ;
text_limit - = reload_icon - > get_width ( ) + get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) * 2 ;
revert_rect = Rect2 ( text_limit + get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) , ( size . height - reload_icon - > get_height ( ) ) / 2 , reload_icon - > get_width ( ) , reload_icon - > get_height ( ) ) ;
2018-05-15 22:12:35 +02:00
2019-02-12 21:10:08 +01:00
Color color2 ( 1 , 1 , 1 ) ;
2018-05-15 22:12:35 +02:00
if ( revert_hover ) {
2019-02-12 21:10:08 +01:00
color2 . r * = 1.2 ;
color2 . g * = 1.2 ;
color2 . b * = 1.2 ;
2018-05-15 22:12:35 +02:00
}
2020-09-03 13:22:16 +02:00
if ( rtl ) {
draw_texture ( reload_icon , Vector2 ( size . width - revert_rect . position . x - reload_icon - > get_width ( ) , revert_rect . position . y ) , color2 ) ;
} else {
draw_texture ( reload_icon , revert_rect . position , color2 ) ;
}
2018-05-15 22:12:35 +02:00
} else {
revert_rect = Rect2 ( ) ;
}
2020-09-03 13:22:16 +02:00
int v_ofs = ( size . height - font - > get_height ( font_size ) ) / 2 ;
if ( rtl ) {
draw_string ( font , Point2 ( size . width - ofs - text_limit , v_ofs + font - > get_ascent ( font_size ) ) , label , HALIGN_RIGHT , text_limit , font_size , color ) ;
} else {
draw_string ( font , Point2 ( ofs , v_ofs + font - > get_ascent ( font_size ) ) , label , HALIGN_LEFT , text_limit , font_size , color ) ;
}
2018-05-15 22:12:35 +02:00
if ( keying ) {
2019-06-11 20:43:37 +02:00
Ref < Texture2D > key ;
2018-05-15 22:12:35 +02:00
if ( use_keying_next ( ) ) {
2021-07-17 23:22:52 +02:00
key = get_theme_icon ( SNAME ( " KeyNext " ) , SNAME ( " EditorIcons " ) ) ;
2018-05-15 22:12:35 +02:00
} else {
2021-07-17 23:22:52 +02:00
key = get_theme_icon ( SNAME ( " Key " ) , SNAME ( " EditorIcons " ) ) ;
2018-05-15 22:12:35 +02:00
}
2021-07-17 23:22:52 +02:00
ofs = size . width - key - > get_width ( ) - get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) ;
2018-05-15 22:12:35 +02:00
2019-02-12 21:10:08 +01:00
Color color2 ( 1 , 1 , 1 ) ;
2018-05-15 22:12:35 +02:00
if ( keying_hover ) {
2019-02-12 21:10:08 +01:00
color2 . r * = 1.2 ;
color2 . g * = 1.2 ;
color2 . b * = 1.2 ;
2018-05-15 22:12:35 +02:00
}
keying_rect = Rect2 ( ofs , ( ( size . height - key - > get_height ( ) ) / 2 ) , key - > get_width ( ) , key - > get_height ( ) ) ;
2020-09-03 13:22:16 +02:00
if ( rtl ) {
draw_texture ( key , Vector2 ( size . width - keying_rect . position . x - key - > get_width ( ) , keying_rect . position . y ) , color2 ) ;
} else {
draw_texture ( key , keying_rect . position , color2 ) ;
}
2018-05-15 22:12:35 +02:00
} else {
keying_rect = Rect2 ( ) ;
}
2020-04-17 04:52:00 +02:00
if ( deletable ) {
Ref < Texture2D > close ;
2021-07-17 23:22:52 +02:00
close = get_theme_icon ( SNAME ( " Close " ) , SNAME ( " EditorIcons " ) ) ;
2020-04-17 04:52:00 +02:00
2021-07-17 23:22:52 +02:00
ofs = size . width - close - > get_width ( ) - get_theme_constant ( SNAME ( " hseparator " ) , SNAME ( " Tree " ) ) ;
2020-04-17 04:52:00 +02:00
Color color2 ( 1 , 1 , 1 ) ;
if ( delete_hover ) {
color2 . r * = 1.2 ;
color2 . g * = 1.2 ;
color2 . b * = 1.2 ;
}
delete_rect = Rect2 ( ofs , ( ( size . height - close - > get_height ( ) ) / 2 ) , close - > get_width ( ) , close - > get_height ( ) ) ;
2020-09-03 13:22:16 +02:00
if ( rtl ) {
draw_texture ( close , Vector2 ( size . width - delete_rect . position . x - close - > get_width ( ) , delete_rect . position . y ) , color2 ) ;
} else {
draw_texture ( close , delete_rect . position , color2 ) ;
}
2020-04-17 04:52:00 +02:00
} else {
delete_rect = Rect2 ( ) ;
}
2018-05-15 22:12:35 +02:00
}
}
void EditorProperty : : set_label ( const String & p_label ) {
label = p_label ;
update ( ) ;
}
String EditorProperty : : get_label ( ) const {
return label ;
}
Object * EditorProperty : : get_edited_object ( ) {
return object ;
}
StringName EditorProperty : : get_edited_property ( ) {
return property ;
}
void EditorProperty : : update_property ( ) {
2021-08-22 03:52:44 +02:00
GDVIRTUAL_CALL ( _update_property ) ;
2018-05-15 22:12:35 +02:00
}
2021-08-16 04:42:24 +02:00
void EditorProperty : : _set_read_only ( bool p_read_only ) {
}
2018-05-15 22:12:35 +02:00
void EditorProperty : : set_read_only ( bool p_read_only ) {
read_only = p_read_only ;
2021-08-16 04:42:24 +02:00
_set_read_only ( p_read_only ) ;
2018-05-15 22:12:35 +02:00
}
bool EditorProperty : : is_read_only ( ) const {
return read_only ;
}
2018-11-25 14:46:26 +01:00
bool EditorPropertyRevert : : may_node_be_in_instance ( Node * p_node ) {
2018-05-15 22:12:35 +02:00
Node * edited_scene = EditorNode : : get_singleton ( ) - > get_edited_scene ( ) ;
bool might_be = false ;
2018-11-25 14:46:26 +01:00
Node * node = p_node ;
2018-05-15 22:12:35 +02:00
while ( node ) {
if ( node = = edited_scene ) {
if ( node - > get_scene_inherited_state ( ) . is_valid ( ) ) {
might_be = true ;
break ;
}
might_be = false ;
break ;
}
2020-10-26 17:07:09 +01:00
if ( node - > get_scene_instance_state ( ) . is_valid ( ) ) {
might_be = true ;
break ;
}
2018-05-15 22:12:35 +02:00
node = node - > get_owner ( ) ;
}
return might_be ; // or might not be
}
2021-08-02 13:58:02 +02:00
bool EditorPropertyRevert : : get_instantiated_node_original_property ( Node * p_node , const StringName & p_prop , Variant & value , bool p_check_class_default ) {
2018-11-25 14:46:26 +01:00
Node * node = p_node ;
2018-05-15 22:12:35 +02:00
Node * orig = node ;
Node * edited_scene = EditorNode : : get_singleton ( ) - > get_edited_scene ( ) ;
bool found = false ;
while ( node ) {
Ref < SceneState > ss ;
if ( node = = edited_scene ) {
ss = node - > get_scene_inherited_state ( ) ;
} else {
ss = node - > get_scene_instance_state ( ) ;
}
if ( ss . is_valid ( ) ) {
NodePath np = node - > get_path_to ( orig ) ;
int node_idx = ss - > find_node_by_path ( np ) ;
if ( node_idx > = 0 ) {
bool lfound = false ;
Variant lvar ;
lvar = ss - > get_property_value ( node_idx , p_prop , lfound ) ;
if ( lfound ) {
found = true ;
value = lvar ;
}
}
}
if ( node = = edited_scene ) {
//just in case
break ;
}
node = node - > get_owner ( ) ;
}
2021-08-02 13:58:02 +02:00
if ( p_check_class_default & & ! found & & p_node ) {
2018-11-08 15:30:02 +01:00
//if not found, try default class value
2020-10-27 03:39:07 +01:00
Variant attempt = ClassDB : : class_get_default_property_value ( p_node - > get_class_name ( ) , p_prop ) ;
2018-11-08 15:30:02 +01:00
if ( attempt . get_type ( ) ! = Variant : : NIL ) {
found = true ;
value = attempt ;
}
}
2018-05-15 22:12:35 +02:00
return found ;
}
2018-11-25 14:46:26 +01:00
bool EditorPropertyRevert : : is_node_property_different ( Node * p_node , const Variant & p_current , const Variant & p_orig ) {
2018-05-15 22:12:35 +02:00
// this is a pretty difficult function, because a property may not be saved but may have
// the flag to not save if one or if zero
2018-11-08 15:30:02 +01:00
//make sure there is an actual state
2018-05-15 22:12:35 +02:00
{
2018-11-25 14:46:26 +01:00
Node * node = p_node ;
2020-05-14 16:41:43 +02:00
if ( ! node ) {
2018-05-15 22:12:35 +02:00
return false ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
Node * edited_scene = EditorNode : : get_singleton ( ) - > get_edited_scene ( ) ;
bool found_state = false ;
while ( node ) {
Ref < SceneState > ss ;
if ( node = = edited_scene ) {
ss = node - > get_scene_inherited_state ( ) ;
} else {
ss = node - > get_scene_instance_state ( ) ;
}
if ( ss . is_valid ( ) ) {
found_state = true ;
2021-02-21 03:03:07 +01:00
break ;
2018-05-15 22:12:35 +02:00
}
if ( node = = edited_scene ) {
//just in case
break ;
}
node = node - > get_owner ( ) ;
}
2020-05-14 16:41:43 +02:00
if ( ! found_state ) {
2018-05-15 22:12:35 +02:00
return false ; //pointless to check if we are not comparing against anything.
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
}
2021-02-21 03:03:07 +01:00
return is_property_value_different ( p_current , p_orig ) ;
}
2018-05-15 22:12:35 +02:00
2021-02-21 03:03:07 +01:00
bool EditorPropertyRevert : : is_property_value_different ( const Variant & p_a , const Variant & p_b ) {
if ( p_a . get_type ( ) = = Variant : : FLOAT & & p_b . get_type ( ) = = Variant : : FLOAT ) {
//this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error
return ! Math : : is_equal_approx ( ( float ) p_a , ( float ) p_b ) ;
} else {
return p_a ! = p_b ;
2018-05-15 22:12:35 +02:00
}
}
2021-02-21 03:03:07 +01:00
Variant EditorPropertyRevert : : get_property_revert_value ( Object * p_object , const StringName & p_property ) {
// If the object implements property_can_revert, rely on that completely
// (i.e. don't then try to revert to default value - the property_get_revert implementation
// can do that if so desired)
if ( p_object - > has_method ( " property_can_revert " ) & & p_object - > call ( " property_can_revert " , p_property ) ) {
return p_object - > call ( " property_get_revert " , p_property ) ;
}
2018-05-15 22:12:35 +02:00
2021-02-21 03:03:07 +01:00
Ref < Script > scr = p_object - > get_script ( ) ;
2018-11-25 14:46:26 +01:00
Node * node = Object : : cast_to < Node > ( p_object ) ;
if ( node & & EditorPropertyRevert : : may_node_be_in_instance ( node ) ) {
2021-02-21 03:03:07 +01:00
//if this node is an instance or inherits, but it has a script attached which is unrelated
//to the one set for the parent and also has a default value for the property, consider that
//has precedence over the value from the parent, because that is an explicit source of defaults
//closer in the tree to the current node
bool ignore_parent = false ;
if ( scr . is_valid ( ) ) {
Variant sorig ;
if ( EditorPropertyRevert : : get_instantiated_node_original_property ( node , " script " , sorig ) & & ! scr - > inherits_script ( sorig ) ) {
Variant dummy ;
if ( scr - > get_property_default_value ( p_property , dummy ) ) {
ignore_parent = true ;
}
2018-11-08 15:30:02 +01:00
}
}
2021-02-21 03:03:07 +01:00
if ( ! ignore_parent ) {
//check for difference including instantiation
Variant vorig ;
2021-08-02 13:58:02 +02:00
if ( EditorPropertyRevert : : get_instantiated_node_original_property ( node , p_property , vorig , false ) ) {
2021-02-21 03:03:07 +01:00
return vorig ;
}
2018-10-29 16:25:31 +01:00
}
}
2018-05-15 22:12:35 +02:00
2021-02-21 03:03:07 +01:00
if ( scr . is_valid ( ) ) {
Variant orig_value ;
if ( scr - > get_property_default_value ( p_property , orig_value ) ) {
return orig_value ;
2018-05-15 22:12:35 +02:00
}
}
2021-02-21 03:03:07 +01:00
//report default class value instead
return ClassDB : : class_get_default_property_value ( p_object - > get_class_name ( ) , p_property ) ;
}
bool EditorPropertyRevert : : can_property_revert ( Object * p_object , const StringName & p_property ) {
Variant revert_value = EditorPropertyRevert : : get_property_revert_value ( p_object , p_property ) ;
if ( revert_value . get_type ( ) = = Variant : : NIL ) {
return false ;
}
Variant current_value = p_object - > get ( p_property ) ;
return EditorPropertyRevert : : is_property_value_different ( current_value , revert_value ) ;
2018-11-25 14:46:26 +01:00
}
void EditorProperty : : update_reload_status ( ) {
2020-05-14 16:41:43 +02:00
if ( property = = StringName ( ) ) {
2018-11-25 14:46:26 +01:00
return ; //no property, so nothing to do
2020-05-14 16:41:43 +02:00
}
2018-11-25 14:46:26 +01:00
bool has_reload = EditorPropertyRevert : : can_property_revert ( object , property ) ;
2018-05-15 22:12:35 +02:00
if ( has_reload ! = can_revert ) {
can_revert = has_reload ;
update ( ) ;
}
}
bool EditorProperty : : use_keying_next ( ) const {
2018-10-01 21:44:57 +02:00
List < PropertyInfo > plist ;
object - > get_property_list ( & plist , true ) ;
for ( List < PropertyInfo > : : Element * I = plist . front ( ) ; I ; I = I - > next ( ) ) {
PropertyInfo & p = I - > get ( ) ;
if ( p . name = = property ) {
2019-07-25 09:11:41 +02:00
return ( p . usage & PROPERTY_USAGE_KEYING_INCREMENTS ) ;
2018-10-01 21:44:57 +02:00
}
}
2018-05-15 22:12:35 +02:00
return false ;
}
2020-05-14 14:29:06 +02:00
2018-05-15 22:12:35 +02:00
void EditorProperty : : set_checkable ( bool p_checkable ) {
checkable = p_checkable ;
update ( ) ;
queue_sort ( ) ;
}
bool EditorProperty : : is_checkable ( ) const {
return checkable ;
}
void EditorProperty : : set_checked ( bool p_checked ) {
checked = p_checked ;
update ( ) ;
}
bool EditorProperty : : is_checked ( ) const {
return checked ;
}
void EditorProperty : : set_draw_red ( bool p_draw_red ) {
draw_red = p_draw_red ;
update ( ) ;
}
void EditorProperty : : set_keying ( bool p_keying ) {
keying = p_keying ;
update ( ) ;
queue_sort ( ) ;
}
2020-04-17 04:52:00 +02:00
void EditorProperty : : set_deletable ( bool p_deletable ) {
deletable = p_deletable ;
update ( ) ;
queue_sort ( ) ;
}
bool EditorProperty : : is_deletable ( ) const {
return deletable ;
}
2018-05-15 22:12:35 +02:00
bool EditorProperty : : is_keying ( ) const {
return keying ;
}
bool EditorProperty : : is_draw_red ( ) const {
return draw_red ;
}
void EditorProperty : : _focusable_focused ( int p_index ) {
2020-05-14 16:41:43 +02:00
if ( ! selectable ) {
2018-05-19 21:09:38 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
bool already_selected = selected ;
selected = true ;
selected_focusable = p_index ;
update ( ) ;
if ( ! already_selected & & selected ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " selected " ) , property , selected_focusable ) ;
2018-05-15 22:12:35 +02:00
}
}
void EditorProperty : : add_focusable ( Control * p_control ) {
2020-02-21 18:28:45 +01:00
p_control - > connect ( " focus_entered " , callable_mp ( this , & EditorProperty : : _focusable_focused ) , varray ( focusables . size ( ) ) ) ;
2018-05-15 22:12:35 +02:00
focusables . push_back ( p_control ) ;
}
void EditorProperty : : select ( int p_focusable ) {
bool already_selected = selected ;
if ( p_focusable > = 0 ) {
ERR_FAIL_INDEX ( p_focusable , focusables . size ( ) ) ;
focusables [ p_focusable ] - > grab_focus ( ) ;
} else {
selected = true ;
update ( ) ;
}
if ( ! already_selected & & selected ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " selected " ) , property , selected_focusable ) ;
2018-05-15 22:12:35 +02:00
}
}
void EditorProperty : : deselect ( ) {
selected = false ;
selected_focusable = - 1 ;
update ( ) ;
}
bool EditorProperty : : is_selected ( ) const {
return selected ;
}
2021-08-22 17:37:22 +02:00
void EditorProperty : : gui_input ( const Ref < InputEvent > & p_event ) {
2021-04-05 08:52:21 +02:00
ERR_FAIL_COND ( p_event . is_null ( ) ) ;
2020-05-14 16:41:43 +02:00
if ( property = = StringName ( ) ) {
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
Ref < InputEventMouse > me = p_event ;
if ( me . is_valid ( ) ) {
2020-09-03 13:22:16 +02:00
Vector2 mpos = me - > get_position ( ) ;
if ( is_layout_rtl ( ) ) {
mpos . x = get_size ( ) . x - mpos . x ;
}
2021-01-08 04:37:37 +01:00
bool button_left = me - > get_button_mask ( ) & MOUSE_BUTTON_MASK_LEFT ;
2018-05-15 22:12:35 +02:00
2020-09-03 13:22:16 +02:00
bool new_keying_hover = keying_rect . has_point ( mpos ) & & ! button_left ;
2018-05-15 22:12:35 +02:00
if ( new_keying_hover ! = keying_hover ) {
keying_hover = new_keying_hover ;
update ( ) ;
}
2020-09-03 13:22:16 +02:00
bool new_delete_hover = delete_rect . has_point ( mpos ) & & ! button_left ;
2020-04-17 04:52:00 +02:00
if ( new_delete_hover ! = delete_hover ) {
delete_hover = new_delete_hover ;
update ( ) ;
}
2020-09-03 13:22:16 +02:00
bool new_revert_hover = revert_rect . has_point ( mpos ) & & ! button_left ;
2018-05-15 22:12:35 +02:00
if ( new_revert_hover ! = revert_hover ) {
revert_hover = new_revert_hover ;
update ( ) ;
}
2020-09-03 13:22:16 +02:00
bool new_check_hover = check_rect . has_point ( mpos ) & & ! button_left ;
2018-05-15 22:12:35 +02:00
if ( new_check_hover ! = check_hover ) {
check_hover = new_check_hover ;
update ( ) ;
}
}
Ref < InputEventMouseButton > mb = p_event ;
2021-01-08 04:37:37 +01:00
if ( mb . is_valid ( ) & & mb - > is_pressed ( ) & & mb - > get_button_index ( ) = = MOUSE_BUTTON_LEFT ) {
2020-09-03 13:22:16 +02:00
Vector2 mpos = mb - > get_position ( ) ;
if ( is_layout_rtl ( ) ) {
mpos . x = get_size ( ) . x - mpos . x ;
}
2018-05-19 21:09:38 +02:00
if ( ! selected & & selectable ) {
2018-05-15 22:12:35 +02:00
selected = true ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " selected " ) , property , - 1 ) ;
2018-05-15 22:12:35 +02:00
update ( ) ;
}
2020-09-03 13:22:16 +02:00
if ( keying_rect . has_point ( mpos ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " property_keyed " ) , property , use_keying_next ( ) ) ;
2018-10-01 21:44:57 +02:00
if ( use_keying_next ( ) ) {
2020-03-26 22:49:16 +01:00
if ( property = = " frame_coords " & & ( object - > is_class ( " Sprite2D " ) | | object - > is_class ( " Sprite3D " ) ) ) {
2021-05-23 18:46:39 +02:00
Vector2i new_coords = object - > get ( property ) ;
2019-10-22 19:01:23 +02:00
new_coords . x + + ;
if ( new_coords . x > = object - > get ( " hframes " ) . operator int64_t ( ) ) {
new_coords . x = 0 ;
new_coords . y + + ;
}
2021-07-17 23:22:52 +02:00
call_deferred ( SNAME ( " emit_changed " ) , property , new_coords , " " , false ) ;
2019-10-22 19:01:23 +02:00
} else {
2021-07-17 23:22:52 +02:00
call_deferred ( SNAME ( " emit_changed " ) , property , object - > get ( property ) . operator int64_t ( ) + 1 , " " , false ) ;
2019-10-22 19:01:23 +02:00
}
2021-08-22 03:52:44 +02:00
call_deferred ( SNAME ( " update_property " ) ) ;
2018-10-01 21:44:57 +02:00
}
2018-05-15 22:12:35 +02:00
}
2020-09-03 13:22:16 +02:00
if ( delete_rect . has_point ( mpos ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " property_deleted " ) , property ) ;
2020-04-17 04:52:00 +02:00
}
2018-05-15 22:12:35 +02:00
2020-09-03 13:22:16 +02:00
if ( revert_rect . has_point ( mpos ) ) {
2021-02-21 03:03:07 +01:00
Variant revert_value = EditorPropertyRevert : : get_property_revert_value ( object , property ) ;
emit_changed ( property , revert_value ) ;
update_property ( ) ;
2018-05-15 22:12:35 +02:00
}
2021-02-21 03:03:07 +01:00
2020-09-03 13:22:16 +02:00
if ( check_rect . has_point ( mpos ) ) {
2018-05-15 22:12:35 +02:00
checked = ! checked ;
update ( ) ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " property_checked " ) , property , checked ) ;
2018-05-15 22:12:35 +02:00
}
2020-06-08 15:25:52 +02:00
} else if ( mb . is_valid ( ) & & mb - > is_pressed ( ) & & mb - > get_button_index ( ) = = MOUSE_BUTTON_RIGHT ) {
_ensure_popup ( ) ;
menu - > set_position ( get_screen_position ( ) + get_local_mouse_position ( ) ) ;
menu - > set_size ( Vector2 ( 1 , 1 ) ) ;
menu - > popup ( ) ;
select ( ) ;
return ;
}
}
void EditorProperty : : unhandled_key_input ( const Ref < InputEvent > & p_event ) {
2021-08-30 17:33:01 +02:00
if ( ! selected | | ! p_event - > is_pressed ( ) ) {
2020-06-08 15:25:52 +02:00
return ;
}
2021-09-03 14:33:02 +02:00
const Ref < InputEventKey > k = p_event ;
if ( k . is_valid ( ) & & k - > is_pressed ( ) ) {
if ( ED_IS_SHORTCUT ( " property_editor/copy_property " , p_event ) ) {
menu_option ( MENU_COPY_PROPERTY ) ;
accept_event ( ) ;
} else if ( ED_IS_SHORTCUT ( " property_editor/paste_property " , p_event ) & & ! is_read_only ( ) ) {
menu_option ( MENU_PASTE_PROPERTY ) ;
accept_event ( ) ;
} else if ( ED_IS_SHORTCUT ( " property_editor/copy_property_path " , p_event ) ) {
menu_option ( MENU_COPY_PROPERTY_PATH ) ;
accept_event ( ) ;
}
2018-05-15 22:12:35 +02:00
}
}
2021-03-19 06:42:56 +01:00
const Color * EditorProperty : : _get_property_colors ( ) {
const Color base = get_theme_color ( SNAME ( " accent_color " ) , SNAME ( " Editor " ) ) ;
const float saturation = base . get_s ( ) * 0.75 ;
const float value = base . get_v ( ) ;
static Color c [ 4 ] ;
c [ 0 ] . set_hsv ( 0.0 / 3.0 + 0.05 , saturation , value ) ;
c [ 1 ] . set_hsv ( 1.0 / 3.0 + 0.05 , saturation , value ) ;
c [ 2 ] . set_hsv ( 2.0 / 3.0 + 0.05 , saturation , value ) ;
c [ 3 ] . set_hsv ( 1.5 / 3.0 + 0.05 , saturation , value ) ;
return c ;
}
2018-05-15 22:12:35 +02:00
void EditorProperty : : set_label_reference ( Control * p_control ) {
label_reference = p_control ;
}
2020-05-14 14:29:06 +02:00
2018-05-17 23:02:16 +02:00
void EditorProperty : : set_bottom_editor ( Control * p_control ) {
bottom_editor = p_control ;
}
2020-05-14 14:29:06 +02:00
2021-02-10 21:18:45 +01:00
bool EditorProperty : : is_cache_valid ( ) const {
if ( object ) {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , Variant > & E : cache ) {
2021-02-10 21:18:45 +01:00
bool valid ;
2021-08-09 22:13:42 +02:00
Variant value = object - > get ( E . key , & valid ) ;
if ( ! valid | | value ! = E . value ) {
2021-02-10 21:18:45 +01:00
return false ;
}
}
}
return true ;
}
void EditorProperty : : update_cache ( ) {
cache . clear ( ) ;
if ( object & & property ! = StringName ( ) ) {
bool valid ;
Variant value = object - > get ( property , & valid ) ;
if ( valid ) {
cache [ property ] = value ;
}
}
}
2018-05-15 22:12:35 +02:00
Variant EditorProperty : : get_drag_data ( const Point2 & p_point ) {
2020-05-14 16:41:43 +02:00
if ( property = = StringName ( ) ) {
2018-05-15 22:12:35 +02:00
return Variant ( ) ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
Dictionary dp ;
dp [ " type " ] = " obj_property " ;
dp [ " object " ] = object ;
dp [ " property " ] = property ;
dp [ " value " ] = object - > get ( property ) ;
Label * label = memnew ( Label ) ;
label - > set_text ( property ) ;
set_drag_preview ( label ) ;
return dp ;
}
2018-05-17 23:02:16 +02:00
void EditorProperty : : set_use_folding ( bool p_use_folding ) {
use_folding = p_use_folding ;
}
bool EditorProperty : : is_using_folding ( ) const {
return use_folding ;
}
void EditorProperty : : expand_all_folding ( ) {
}
void EditorProperty : : collapse_all_folding ( ) {
2018-05-15 22:12:35 +02:00
}
2018-05-19 21:09:38 +02:00
void EditorProperty : : set_selectable ( bool p_selectable ) {
selectable = p_selectable ;
}
bool EditorProperty : : is_selectable ( ) const {
return selectable ;
}
2018-07-14 23:15:42 +02:00
void EditorProperty : : set_name_split_ratio ( float p_ratio ) {
split_ratio = p_ratio ;
}
float EditorProperty : : get_name_split_ratio ( ) const {
return split_ratio ;
}
2018-05-19 21:09:38 +02:00
void EditorProperty : : set_object_and_property ( Object * p_object , const StringName & p_property ) {
object = p_object ;
property = p_property ;
}
2018-07-20 23:14:33 +02:00
Control * EditorProperty : : make_custom_tooltip ( const String & p_text ) const {
tooltip_text = p_text ;
EditorHelpBit * help_bit = memnew ( EditorHelpBit ) ;
2021-07-17 23:22:52 +02:00
//help_bit->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel")));
2018-10-24 20:00:22 +02:00
help_bit - > get_rich_text ( ) - > set_fixed_size_to_width ( 360 * EDSCALE ) ;
2018-07-20 23:14:33 +02:00
2020-03-20 21:51:53 +01:00
String text ;
2020-02-26 04:22:18 +01:00
PackedStringArray slices = p_text . split ( " :: " , false ) ;
2020-12-15 13:04:21 +01:00
if ( ! slices . is_empty ( ) ) {
2020-02-26 04:22:18 +01:00
String property_name = slices [ 0 ] . strip_edges ( ) ;
2020-03-20 21:51:53 +01:00
text = TTR ( " Property: " ) + " [u][b] " + property_name + " [/b][/u] " ;
2020-02-26 04:22:18 +01:00
if ( slices . size ( ) > 1 ) {
String property_doc = slices [ 1 ] . strip_edges ( ) ;
if ( property_name ! = property_doc ) {
text + = " \n " + property_doc ;
}
}
2021-08-13 12:47:14 +02:00
help_bit - > call_deferred ( SNAME ( " set_text " ) , text ) ; //hack so it uses proper theme once inside scene
2020-02-26 04:22:18 +01:00
}
2018-07-20 23:14:33 +02:00
return help_bit ;
}
String EditorProperty : : get_tooltip_text ( ) const {
return tooltip_text ;
}
2020-06-08 15:25:52 +02:00
void EditorProperty : : menu_option ( int p_option ) {
switch ( p_option ) {
case MENU_COPY_PROPERTY : {
EditorNode : : get_singleton ( ) - > get_inspector ( ) - > set_property_clipboard ( object - > get ( property ) ) ;
} break ;
case MENU_PASTE_PROPERTY : {
emit_changed ( property , EditorNode : : get_singleton ( ) - > get_inspector ( ) - > get_property_clipboard ( ) ) ;
} break ;
case MENU_COPY_PROPERTY_PATH : {
DisplayServer : : get_singleton ( ) - > clipboard_set ( property ) ;
} break ;
}
}
2018-05-15 22:12:35 +02:00
void EditorProperty : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " set_label " , " text " ) , & EditorProperty : : set_label ) ;
ClassDB : : bind_method ( D_METHOD ( " get_label " ) , & EditorProperty : : get_label ) ;
ClassDB : : bind_method ( D_METHOD ( " set_read_only " , " read_only " ) , & EditorProperty : : set_read_only ) ;
ClassDB : : bind_method ( D_METHOD ( " is_read_only " ) , & EditorProperty : : is_read_only ) ;
ClassDB : : bind_method ( D_METHOD ( " set_checkable " , " checkable " ) , & EditorProperty : : set_checkable ) ;
ClassDB : : bind_method ( D_METHOD ( " is_checkable " ) , & EditorProperty : : is_checkable ) ;
ClassDB : : bind_method ( D_METHOD ( " set_checked " , " checked " ) , & EditorProperty : : set_checked ) ;
ClassDB : : bind_method ( D_METHOD ( " is_checked " ) , & EditorProperty : : is_checked ) ;
ClassDB : : bind_method ( D_METHOD ( " set_draw_red " , " draw_red " ) , & EditorProperty : : set_draw_red ) ;
ClassDB : : bind_method ( D_METHOD ( " is_draw_red " ) , & EditorProperty : : is_draw_red ) ;
ClassDB : : bind_method ( D_METHOD ( " set_keying " , " keying " ) , & EditorProperty : : set_keying ) ;
ClassDB : : bind_method ( D_METHOD ( " is_keying " ) , & EditorProperty : : is_keying ) ;
2020-04-17 04:52:00 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_deletable " , " deletable " ) , & EditorProperty : : set_deletable ) ;
ClassDB : : bind_method ( D_METHOD ( " is_deletable " ) , & EditorProperty : : is_deletable ) ;
2018-05-15 22:12:35 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_edited_property " ) , & EditorProperty : : get_edited_property ) ;
ClassDB : : bind_method ( D_METHOD ( " get_edited_object " ) , & EditorProperty : : get_edited_object ) ;
2018-07-20 23:14:33 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_tooltip_text " ) , & EditorProperty : : get_tooltip_text ) ;
2021-08-22 03:52:44 +02:00
ClassDB : : bind_method ( D_METHOD ( " update_property " ) , & EditorProperty : : update_property ) ;
2018-07-20 23:14:33 +02:00
2019-04-23 21:39:09 +02:00
ClassDB : : bind_method ( D_METHOD ( " add_focusable " , " control " ) , & EditorProperty : : add_focusable ) ;
ClassDB : : bind_method ( D_METHOD ( " set_bottom_editor " , " editor " ) , & EditorProperty : : set_bottom_editor ) ;
2019-01-18 17:01:24 +01:00
ClassDB : : bind_method ( D_METHOD ( " emit_changed " , " property " , " value " , " field " , " changing " ) , & EditorProperty : : emit_changed , DEFVAL ( StringName ( ) ) , DEFVAL ( false ) ) ;
2018-05-15 22:12:35 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " label " ) , " set_label " , " get_label " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " read_only " ) , " set_read_only " , " is_read_only " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " checkable " ) , " set_checkable " , " is_checkable " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " checked " ) , " set_checked " , " is_checked " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " draw_red " ) , " set_draw_red " , " is_draw_red " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " keying " ) , " set_keying " , " is_keying " ) ;
2020-04-17 04:52:00 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " deletable " ) , " set_deletable " , " is_deletable " ) ;
2020-02-20 22:58:05 +01:00
ADD_SIGNAL ( MethodInfo ( " property_changed " , PropertyInfo ( Variant : : STRING_NAME , " property " ) , PropertyInfo ( Variant : : NIL , " value " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NIL_IS_VARIANT ) ) ) ;
2020-02-17 22:06:54 +01:00
ADD_SIGNAL ( MethodInfo ( " multiple_properties_changed " , PropertyInfo ( Variant : : PACKED_STRING_ARRAY , " properties " ) , PropertyInfo ( Variant : : ARRAY , " value " ) ) ) ;
2020-02-20 22:58:05 +01:00
ADD_SIGNAL ( MethodInfo ( " property_keyed " , PropertyInfo ( Variant : : STRING_NAME , " property " ) ) ) ;
2020-04-17 04:52:00 +02:00
ADD_SIGNAL ( MethodInfo ( " property_deleted " , PropertyInfo ( Variant : : STRING_NAME , " property " ) ) ) ;
2020-02-20 22:58:05 +01:00
ADD_SIGNAL ( MethodInfo ( " property_keyed_with_value " , PropertyInfo ( Variant : : STRING_NAME , " property " ) , PropertyInfo ( Variant : : NIL , " value " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NIL_IS_VARIANT ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " property_checked " , PropertyInfo ( Variant : : STRING_NAME , " property " ) , PropertyInfo ( Variant : : STRING , " bool " ) ) ) ;
2018-05-15 22:12:35 +02:00
ADD_SIGNAL ( MethodInfo ( " resource_selected " , PropertyInfo ( Variant : : STRING , " path " ) , PropertyInfo ( Variant : : OBJECT , " resource " , PROPERTY_HINT_RESOURCE_TYPE , " Resource " ) ) ) ;
2020-02-20 22:58:05 +01:00
ADD_SIGNAL ( MethodInfo ( " object_id_selected " , PropertyInfo ( Variant : : STRING_NAME , " property " ) , PropertyInfo ( Variant : : INT , " id " ) ) ) ;
2018-05-15 22:12:35 +02:00
ADD_SIGNAL ( MethodInfo ( " selected " , PropertyInfo ( Variant : : STRING , " path " ) , PropertyInfo ( Variant : : INT , " focusable_idx " ) ) ) ;
2021-08-22 03:52:44 +02:00
GDVIRTUAL_BIND ( _update_property )
2018-05-15 22:12:35 +02:00
}
EditorProperty : : EditorProperty ( ) {
2018-08-07 17:19:19 +02:00
draw_top_bg = true ;
2020-04-02 01:20:12 +02:00
object = nullptr ;
2018-07-14 23:15:42 +02:00
split_ratio = 0.5 ;
2018-05-19 21:09:38 +02:00
selectable = true ;
2018-05-15 22:12:35 +02:00
text_size = 0 ;
read_only = false ;
checkable = false ;
checked = false ;
draw_red = false ;
keying = false ;
2020-04-17 04:52:00 +02:00
deletable = false ;
2018-05-15 22:12:35 +02:00
keying_hover = false ;
revert_hover = false ;
check_hover = false ;
can_revert = false ;
2018-05-17 23:02:16 +02:00
use_folding = false ;
2018-05-15 22:12:35 +02:00
property_usage = 0 ;
selected = false ;
selected_focusable = - 1 ;
2020-04-02 01:20:12 +02:00
label_reference = nullptr ;
bottom_editor = nullptr ;
2020-11-24 10:12:55 +01:00
delete_hover = false ;
2020-06-08 15:25:52 +02:00
menu = nullptr ;
set_process_unhandled_key_input ( true ) ;
}
void EditorProperty : : _ensure_popup ( ) {
if ( menu ) {
return ;
}
menu = memnew ( PopupMenu ) ;
menu - > add_shortcut ( ED_GET_SHORTCUT ( " property_editor/copy_property " ) , MENU_COPY_PROPERTY ) ;
menu - > add_shortcut ( ED_GET_SHORTCUT ( " property_editor/paste_property " ) , MENU_PASTE_PROPERTY ) ;
menu - > add_shortcut ( ED_GET_SHORTCUT ( " property_editor/copy_property_path " ) , MENU_COPY_PROPERTY_PATH ) ;
menu - > connect ( " id_pressed " , callable_mp ( this , & EditorProperty : : menu_option ) ) ;
menu - > set_item_disabled ( MENU_PASTE_PROPERTY , is_read_only ( ) ) ;
add_child ( menu ) ;
2018-05-15 22:12:35 +02:00
}
2020-05-14 14:29:06 +02:00
2018-05-15 22:12:35 +02:00
////////////////////////////////////////////////
////////////////////////////////////////////////
void EditorInspectorPlugin : : add_custom_control ( Control * control ) {
AddedEditor ae ;
ae . property_editor = control ;
added_editors . push_back ( ae ) ;
}
void EditorInspectorPlugin : : add_property_editor ( const String & p_for_property , Control * p_prop ) {
2020-04-02 01:20:12 +02:00
ERR_FAIL_COND ( Object : : cast_to < EditorProperty > ( p_prop ) = = nullptr ) ;
2018-05-15 22:12:35 +02:00
AddedEditor ae ;
ae . properties . push_back ( p_for_property ) ;
ae . property_editor = p_prop ;
added_editors . push_back ( ae ) ;
}
void EditorInspectorPlugin : : add_property_editor_for_multiple_properties ( const String & p_label , const Vector < String > & p_properties , Control * p_prop ) {
AddedEditor ae ;
ae . properties = p_properties ;
ae . property_editor = p_prop ;
ae . label = p_label ;
added_editors . push_back ( ae ) ;
}
bool EditorInspectorPlugin : : can_handle ( Object * p_object ) {
2021-08-22 03:52:44 +02:00
bool success ;
if ( GDVIRTUAL_CALL ( _can_handle , p_object , success ) ) {
return success ;
2018-05-15 22:12:35 +02:00
}
return false ;
}
2020-05-14 14:29:06 +02:00
2018-05-15 22:12:35 +02:00
void EditorInspectorPlugin : : parse_begin ( Object * p_object ) {
2021-08-22 03:52:44 +02:00
GDVIRTUAL_CALL ( _parse_begin ) ;
2018-05-15 22:12:35 +02:00
}
2018-05-17 23:02:16 +02:00
void EditorInspectorPlugin : : parse_category ( Object * p_object , const String & p_parse_category ) {
2021-08-22 03:52:44 +02:00
GDVIRTUAL_CALL ( _parse_category , p_object , p_parse_category ) ;
2018-05-17 23:02:16 +02:00
}
2021-07-01 03:24:34 +02:00
bool EditorInspectorPlugin : : parse_property ( Object * p_object , const Variant : : Type p_type , const String & p_path , const PropertyHint p_hint , const String & p_hint_text , const uint32_t p_usage , const bool p_wide ) {
2021-08-22 03:52:44 +02:00
bool ret ;
if ( GDVIRTUAL_CALL ( _parse_property , p_object , p_type , p_path , p_hint , p_hint_text , p_usage , p_wide , ret ) ) {
return ret ;
2018-05-15 22:12:35 +02:00
}
return false ;
}
2020-05-14 14:29:06 +02:00
2018-05-15 22:12:35 +02:00
void EditorInspectorPlugin : : parse_end ( ) {
2021-08-22 03:52:44 +02:00
GDVIRTUAL_CALL ( _parse_end ) ;
2018-05-15 22:12:35 +02:00
}
void EditorInspectorPlugin : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " add_custom_control " , " control " ) , & EditorInspectorPlugin : : add_custom_control ) ;
ClassDB : : bind_method ( D_METHOD ( " add_property_editor " , " property " , " editor " ) , & EditorInspectorPlugin : : add_property_editor ) ;
ClassDB : : bind_method ( D_METHOD ( " add_property_editor_for_multiple_properties " , " label " , " properties " , " editor " ) , & EditorInspectorPlugin : : add_property_editor_for_multiple_properties ) ;
2021-08-22 03:52:44 +02:00
GDVIRTUAL_BIND ( _can_handle , " object " )
GDVIRTUAL_BIND ( _parse_begin )
GDVIRTUAL_BIND ( _parse_category , " object " , " category " )
GDVIRTUAL_BIND ( _parse_property , " object " , " type " , " name " , " hint_type " , " hint_string " , " usage_flags " , " wide " ) ;
GDVIRTUAL_BIND ( _parse_end )
2018-05-15 22:12:35 +02:00
}
////////////////////////////////////////////////
////////////////////////////////////////////////
void EditorInspectorCategory : : _notification ( int p_what ) {
if ( p_what = = NOTIFICATION_DRAW ) {
2021-07-17 23:22:52 +02:00
Ref < StyleBox > sb = get_theme_stylebox ( SNAME ( " prop_category_style " ) , SNAME ( " Editor " ) ) ;
2021-05-27 17:32:30 +02:00
draw_style_box ( sb , Rect2 ( Vector2 ( ) , get_size ( ) ) ) ;
2021-07-17 23:22:52 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " bold " ) , SNAME ( " EditorFonts " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " bold_size " ) , SNAME ( " EditorFonts " ) ) ;
2018-05-15 22:12:35 +02:00
2021-07-17 23:22:52 +02:00
int hs = get_theme_constant ( SNAME ( " hseparation " ) , SNAME ( " Tree " ) ) ;
2018-05-15 22:12:35 +02:00
2020-09-03 13:22:16 +02:00
int w = font - > get_string_size ( label , font_size ) . width ;
2018-05-15 22:12:35 +02:00
if ( icon . is_valid ( ) ) {
w + = hs + icon - > get_width ( ) ;
}
int ofs = ( get_size ( ) . width - w ) / 2 ;
if ( icon . is_valid ( ) ) {
draw_texture ( icon , Point2 ( ofs , ( get_size ( ) . height - icon - > get_height ( ) ) / 2 ) . floor ( ) ) ;
ofs + = hs + icon - > get_width ( ) ;
}
2021-07-17 23:22:52 +02:00
Color color = get_theme_color ( SNAME ( " font_color " ) , SNAME ( " Tree " ) ) ;
2020-09-03 13:22:16 +02:00
draw_string ( font , Point2 ( ofs , font - > get_ascent ( font_size ) + ( get_size ( ) . height - font - > get_height ( font_size ) ) / 2 ) . floor ( ) , label , HALIGN_LEFT , get_size ( ) . width , font_size , color ) ;
2018-05-15 22:12:35 +02:00
}
}
2018-07-20 23:14:33 +02:00
Control * EditorInspectorCategory : : make_custom_tooltip ( const String & p_text ) const {
tooltip_text = p_text ;
EditorHelpBit * help_bit = memnew ( EditorHelpBit ) ;
2021-07-17 23:22:52 +02:00
help_bit - > add_theme_style_override ( " panel " , get_theme_stylebox ( SNAME ( " panel " ) , SNAME ( " TooltipPanel " ) ) ) ;
2018-10-24 20:00:22 +02:00
help_bit - > get_rich_text ( ) - > set_fixed_size_to_width ( 360 * EDSCALE ) ;
2018-07-20 23:14:33 +02:00
2020-02-26 04:22:18 +01:00
PackedStringArray slices = p_text . split ( " :: " , false ) ;
2020-12-15 13:04:21 +01:00
if ( ! slices . is_empty ( ) ) {
2020-02-26 04:22:18 +01:00
String property_name = slices [ 0 ] . strip_edges ( ) ;
String text = " [u][b] " + property_name + " [/b][/u] " ;
if ( slices . size ( ) > 1 ) {
String property_doc = slices [ 1 ] . strip_edges ( ) ;
if ( property_name ! = property_doc ) {
text + = " \n " + property_doc ;
}
}
2021-08-13 12:47:14 +02:00
help_bit - > call_deferred ( SNAME ( " set_text " ) , text ) ; //hack so it uses proper theme once inside scene
2020-02-26 04:22:18 +01:00
}
2018-07-20 23:14:33 +02:00
return help_bit ;
}
2018-05-15 22:12:35 +02:00
Size2 EditorInspectorCategory : : get_minimum_size ( ) const {
2021-07-17 23:22:52 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " font " ) , SNAME ( " Tree " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " font_size " ) , SNAME ( " Tree " ) ) ;
2018-05-15 22:12:35 +02:00
Size2 ms ;
ms . width = 1 ;
2020-09-03 13:22:16 +02:00
ms . height = font - > get_height ( font_size ) ;
2018-05-15 22:12:35 +02:00
if ( icon . is_valid ( ) ) {
ms . height = MAX ( icon - > get_height ( ) , ms . height ) ;
}
2021-07-17 23:22:52 +02:00
ms . height + = get_theme_constant ( SNAME ( " vseparation " ) , SNAME ( " Tree " ) ) ;
2018-05-15 22:12:35 +02:00
return ms ;
}
2018-07-20 23:14:33 +02:00
void EditorInspectorCategory : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " get_tooltip_text " ) , & EditorInspectorCategory : : get_tooltip_text ) ;
}
String EditorInspectorCategory : : get_tooltip_text ( ) const {
return tooltip_text ;
}
2018-05-15 22:12:35 +02:00
EditorInspectorCategory : : EditorInspectorCategory ( ) {
}
////////////////////////////////////////////////
////////////////////////////////////////////////
2018-07-19 00:37:17 +02:00
void EditorInspectorSection : : _test_unfold ( ) {
if ( ! vbox_added ) {
add_child ( vbox ) ;
2021-08-31 10:48:45 +02:00
move_child ( vbox , 0 ) ;
2018-07-19 00:37:17 +02:00
vbox_added = true ;
}
}
2018-05-15 22:12:35 +02:00
void EditorInspectorSection : : _notification ( int p_what ) {
2021-08-31 10:48:45 +02:00
switch ( p_what ) {
case NOTIFICATION_THEME_CHANGED : {
minimum_size_changed ( ) ;
} break ;
case NOTIFICATION_SORT_CHILDREN : {
if ( ! vbox_added ) {
return ;
}
// Get the section header font.
Ref < Font > font = get_theme_font ( SNAME ( " bold " ) , SNAME ( " EditorFonts " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " bold_size " ) , SNAME ( " EditorFonts " ) ) ;
// Get the right direction arrow texture, if the section is foldable.
Ref < Texture2D > arrow ;
if ( foldable ) {
if ( object - > editor_is_section_unfolded ( section ) ) {
arrow = get_theme_icon ( SNAME ( " arrow " ) , SNAME ( " Tree " ) ) ;
2020-09-03 13:22:16 +02:00
} else {
2021-08-31 10:48:45 +02:00
if ( is_layout_rtl ( ) ) {
arrow = get_theme_icon ( SNAME ( " arrow_collapsed_mirrored " ) , SNAME ( " Tree " ) ) ;
} else {
arrow = get_theme_icon ( SNAME ( " arrow_collapsed " ) , SNAME ( " Tree " ) ) ;
}
2020-09-03 13:22:16 +02:00
}
2018-05-15 22:12:35 +02:00
}
2021-08-31 10:48:45 +02:00
// Compute the height of the section header.
int header_height = font - > get_height ( font_size ) ;
if ( arrow . is_valid ( ) ) {
header_height = MAX ( header_height , arrow - > get_height ( ) ) ;
2020-05-14 16:41:43 +02:00
}
2021-08-31 10:48:45 +02:00
header_height + = get_theme_constant ( SNAME ( " vseparation " ) , SNAME ( " Tree " ) ) ;
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
int inspector_margin = get_theme_constant ( SNAME ( " inspector_margin " ) , SNAME ( " Editor " ) ) ;
Size2 size = get_size ( ) - Vector2 ( inspector_margin , 0 ) ;
Vector2 offset = Vector2 ( is_layout_rtl ( ) ? 0 : inspector_margin , header_height ) ;
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
if ( ! c ) {
continue ;
}
if ( c - > is_set_as_top_level ( ) ) {
continue ;
}
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
fit_child_in_rect ( c , Rect2 ( offset , size ) ) ;
}
} break ;
case NOTIFICATION_DRAW : {
// Get the section header font.
Ref < Font > font = get_theme_font ( SNAME ( " bold " ) , SNAME ( " EditorFonts " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " bold_size " ) , SNAME ( " EditorFonts " ) ) ;
// Get the right direction arrow texture, if the section is foldable.
Ref < Texture2D > arrow ;
if ( foldable ) {
if ( object - > editor_is_section_unfolded ( section ) ) {
arrow = get_theme_icon ( SNAME ( " arrow " ) , SNAME ( " Tree " ) ) ;
2021-05-27 17:32:30 +02:00
} else {
2021-08-31 10:48:45 +02:00
if ( is_layout_rtl ( ) ) {
arrow = get_theme_icon ( SNAME ( " arrow_collapsed_mirrored " ) , SNAME ( " Tree " ) ) ;
} else {
arrow = get_theme_icon ( SNAME ( " arrow_collapsed " ) , SNAME ( " Tree " ) ) ;
}
2021-05-27 17:32:30 +02:00
}
2018-05-15 22:12:35 +02:00
}
2021-08-31 10:48:45 +02:00
bool rtl = is_layout_rtl ( ) ;
2018-07-19 00:37:17 +02:00
2021-08-31 10:48:45 +02:00
// Compute the height of the section header.
int header_height = font - > get_height ( font_size ) ;
if ( arrow . is_valid ( ) ) {
header_height = MAX ( header_height , arrow - > get_height ( ) ) ;
2020-09-03 13:22:16 +02:00
}
2021-08-31 10:48:45 +02:00
header_height + = get_theme_constant ( SNAME ( " vseparation " ) , SNAME ( " Tree " ) ) ;
2020-08-09 10:34:04 +02:00
2021-09-13 18:53:59 +02:00
Rect2 header_rect = Rect2 ( Vector2 ( ) , Vector2 ( get_size ( ) . width , header_height ) ) ;
2021-08-31 10:48:45 +02:00
Color c = bg_color ;
c . a * = 0.4 ;
2021-09-13 18:53:59 +02:00
if ( foldable & & header_rect . has_point ( get_local_mouse_position ( ) ) ) {
c = c . lightened ( Input : : get_singleton ( ) - > is_mouse_button_pressed ( MOUSE_BUTTON_LEFT ) ? - 0.05 : 0.2 ) ;
}
draw_rect ( header_rect , c ) ;
2020-08-09 10:34:04 +02:00
2021-08-31 10:48:45 +02:00
const int arrow_margin = 2 ;
const int arrow_width = arrow . is_valid ( ) ? arrow - > get_width ( ) : 0 ;
Color color = get_theme_color ( SNAME ( " font_color " ) ) ;
float text_width = get_size ( ) . width - Math : : round ( arrow_width + arrow_margin * EDSCALE ) ;
draw_string ( font , Point2 ( rtl ? 0 : Math : : round ( arrow_width + arrow_margin * EDSCALE ) , font - > get_ascent ( font_size ) + ( header_height - font - > get_height ( font_size ) ) / 2 ) . floor ( ) , label , rtl ? HALIGN_RIGHT : HALIGN_LEFT , text_width , font_size , color ) ;
2020-08-09 10:34:04 +02:00
2021-08-31 10:48:45 +02:00
if ( arrow . is_valid ( ) ) {
if ( rtl ) {
draw_texture ( arrow , Point2 ( get_size ( ) . width - arrow - > get_width ( ) - Math : : round ( arrow_margin * EDSCALE ) , ( header_height - arrow - > get_height ( ) ) / 2 ) . floor ( ) ) ;
} else {
draw_texture ( arrow , Point2 ( Math : : round ( arrow_margin * EDSCALE ) , ( header_height - arrow - > get_height ( ) ) / 2 ) . floor ( ) ) ;
}
}
2020-08-09 10:34:04 +02:00
2021-08-31 10:48:45 +02:00
if ( dropping & & ! vbox - > is_visible_in_tree ( ) ) {
Color accent_color = get_theme_color ( SNAME ( " accent_color " ) , SNAME ( " Editor " ) ) ;
draw_rect ( Rect2 ( Point2 ( ) , get_size ( ) ) , accent_color , false ) ;
2020-08-09 10:34:04 +02:00
}
2021-08-31 10:48:45 +02:00
} break ;
case NOTIFICATION_DRAG_BEGIN : {
Dictionary dd = get_viewport ( ) - > gui_get_drag_data ( ) ;
2020-08-09 10:34:04 +02:00
2021-08-31 10:48:45 +02:00
// Only allow dropping if the section contains properties which can take the dragged data.
bool children_can_drop = false ;
for ( int child_idx = 0 ; child_idx < vbox - > get_child_count ( ) ; child_idx + + ) {
Control * editor_property = Object : : cast_to < Control > ( vbox - > get_child ( child_idx ) ) ;
2020-08-09 10:34:04 +02:00
2021-08-31 10:48:45 +02:00
// Test can_drop_data and can_drop_data_fw, since can_drop_data only works if set up with forwarding or if script attached.
if ( editor_property & & ( editor_property - > can_drop_data ( Point2 ( ) , dd ) | | editor_property - > call ( " _can_drop_data_fw " , Point2 ( ) , dd , this ) ) ) {
children_can_drop = true ;
break ;
}
}
2020-08-09 10:34:04 +02:00
2021-08-31 10:48:45 +02:00
dropping = children_can_drop ;
update ( ) ;
} break ;
case NOTIFICATION_DRAG_END : {
dropping = false ;
update ( ) ;
} break ;
case NOTIFICATION_MOUSE_ENTER : {
if ( dropping ) {
dropping_unfold_timer - > start ( ) ;
}
2021-09-13 18:53:59 +02:00
update ( ) ;
2021-08-31 10:48:45 +02:00
} break ;
2020-08-09 10:34:04 +02:00
2021-08-31 10:48:45 +02:00
case NOTIFICATION_MOUSE_EXIT : {
if ( dropping ) {
dropping_unfold_timer - > stop ( ) ;
}
2021-09-13 18:53:59 +02:00
update ( ) ;
2021-08-31 10:48:45 +02:00
} break ;
2018-05-15 22:12:35 +02:00
}
}
Size2 EditorInspectorSection : : get_minimum_size ( ) const {
Size2 ms ;
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2020-05-14 16:41:43 +02:00
if ( ! c ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2020-10-01 09:17:33 +02:00
if ( c - > is_set_as_top_level ( ) ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
if ( ! c - > is_visible ( ) ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
Size2 minsize = c - > get_combined_minimum_size ( ) ;
ms . width = MAX ( ms . width , minsize . width ) ;
ms . height = MAX ( ms . height , minsize . height ) ;
}
2021-07-17 23:22:52 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " font " ) , SNAME ( " Tree " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " font_size " ) , SNAME ( " Tree " ) ) ;
ms . height + = font - > get_height ( font_size ) + get_theme_constant ( SNAME ( " vseparation " ) , SNAME ( " Tree " ) ) ;
ms . width + = get_theme_constant ( SNAME ( " inspector_margin " ) , SNAME ( " Editor " ) ) ;
2018-05-15 22:12:35 +02:00
return ms ;
}
void EditorInspectorSection : : setup ( const String & p_section , const String & p_label , Object * p_object , const Color & p_bg_color , bool p_foldable ) {
section = p_section ;
label = p_label ;
object = p_object ;
bg_color = p_bg_color ;
foldable = p_foldable ;
2018-07-19 00:37:17 +02:00
if ( ! foldable & & ! vbox_added ) {
add_child ( vbox ) ;
2021-08-31 10:48:45 +02:00
move_child ( vbox , 0 ) ;
2018-07-19 00:37:17 +02:00
vbox_added = true ;
}
2018-05-15 22:12:35 +02:00
if ( foldable ) {
2018-07-19 00:37:17 +02:00
_test_unfold ( ) ;
2018-05-15 22:12:35 +02:00
if ( object - > editor_is_section_unfolded ( section ) ) {
vbox - > show ( ) ;
} else {
vbox - > hide ( ) ;
}
}
}
2021-08-22 17:37:22 +02:00
void EditorInspectorSection : : gui_input ( const Ref < InputEvent > & p_event ) {
2021-04-05 08:52:21 +02:00
ERR_FAIL_COND ( p_event . is_null ( ) ) ;
2020-05-14 16:41:43 +02:00
if ( ! foldable ) {
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
Ref < InputEventMouseButton > mb = p_event ;
2021-01-08 04:37:37 +01:00
if ( mb . is_valid ( ) & & mb - > is_pressed ( ) & & mb - > get_button_index ( ) = = MOUSE_BUTTON_LEFT ) {
2021-07-17 23:22:52 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " font " ) , SNAME ( " Tree " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " font_size " ) , SNAME ( " Tree " ) ) ;
2020-09-03 13:22:16 +02:00
if ( mb - > get_position ( ) . y > font - > get_height ( font_size ) ) { //clicked outside
2018-11-19 01:52:01 +01:00
return ;
}
2020-08-09 10:34:04 +02:00
bool should_unfold = ! object - > editor_is_section_unfolded ( section ) ;
if ( should_unfold ) {
unfold ( ) ;
2018-05-15 22:12:35 +02:00
} else {
2020-08-09 10:34:04 +02:00
fold ( ) ;
2018-05-15 22:12:35 +02:00
}
2021-09-13 18:53:59 +02:00
} else if ( mb . is_valid ( ) & & ! mb - > is_pressed ( ) ) {
update ( ) ;
2018-05-15 22:12:35 +02:00
}
}
VBoxContainer * EditorInspectorSection : : get_vbox ( ) {
return vbox ;
}
void EditorInspectorSection : : unfold ( ) {
2020-05-14 16:41:43 +02:00
if ( ! foldable ) {
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-07-19 00:37:17 +02:00
_test_unfold ( ) ;
2018-05-15 22:12:35 +02:00
object - > editor_set_section_unfold ( section , true ) ;
vbox - > show ( ) ;
update ( ) ;
}
void EditorInspectorSection : : fold ( ) {
2020-05-14 16:41:43 +02:00
if ( ! foldable ) {
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2020-05-14 16:41:43 +02:00
if ( ! vbox_added ) {
2021-08-31 10:48:45 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
object - > editor_set_section_unfold ( section , false ) ;
vbox - > hide ( ) ;
update ( ) ;
}
void EditorInspectorSection : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " setup " , " section " , " label " , " object " , " bg_color " , " foldable " ) , & EditorInspectorSection : : setup ) ;
ClassDB : : bind_method ( D_METHOD ( " get_vbox " ) , & EditorInspectorSection : : get_vbox ) ;
ClassDB : : bind_method ( D_METHOD ( " unfold " ) , & EditorInspectorSection : : unfold ) ;
ClassDB : : bind_method ( D_METHOD ( " fold " ) , & EditorInspectorSection : : fold ) ;
}
EditorInspectorSection : : EditorInspectorSection ( ) {
2020-04-02 01:20:12 +02:00
object = nullptr ;
2018-05-15 22:12:35 +02:00
foldable = false ;
vbox = memnew ( VBoxContainer ) ;
2018-07-19 00:37:17 +02:00
vbox_added = false ;
2020-08-09 10:34:04 +02:00
dropping = false ;
dropping_unfold_timer = memnew ( Timer ) ;
dropping_unfold_timer - > set_wait_time ( 0.6 ) ;
dropping_unfold_timer - > set_one_shot ( true ) ;
add_child ( dropping_unfold_timer ) ;
dropping_unfold_timer - > connect ( " timeout " , callable_mp ( this , & EditorInspectorSection : : unfold ) ) ;
2018-07-19 00:37:17 +02:00
}
EditorInspectorSection : : ~ EditorInspectorSection ( ) {
if ( ! vbox_added ) {
memdelete ( vbox ) ;
}
2018-05-15 22:12:35 +02:00
}
2021-08-31 10:48:45 +02:00
////////////////////////////////////////////////
////////////////////////////////////////////////
int EditorInspectorArray : : _get_array_count ( ) {
if ( mode = = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION ) {
List < PropertyInfo > object_property_list ;
object - > get_property_list ( & object_property_list ) ;
return _extract_properties_as_array ( object_property_list ) . size ( ) ;
} else if ( mode = = MODE_USE_COUNT_PROPERTY ) {
bool valid ;
int count = object - > get ( count_property , & valid ) ;
ERR_FAIL_COND_V_MSG ( ! valid , 0 , vformat ( " %s is not a valid property to be used as array count. " , count_property ) ) ;
return count ;
}
return 0 ;
}
void EditorInspectorArray : : _add_button_pressed ( ) {
_move_element ( - 1 , - 1 ) ;
}
void EditorInspectorArray : : _first_page_button_pressed ( ) {
emit_signal ( " page_change_request " , 0 ) ;
}
void EditorInspectorArray : : _prev_page_button_pressed ( ) {
emit_signal ( " page_change_request " , MAX ( 0 , page - 1 ) ) ;
}
void EditorInspectorArray : : _page_line_edit_text_submitted ( String p_text ) {
if ( p_text . is_valid_int ( ) ) {
int new_page = p_text . to_int ( ) - 1 ;
new_page = MIN ( MAX ( 0 , new_page ) , max_page ) ;
page_line_edit - > set_text ( Variant ( new_page ) ) ;
emit_signal ( " page_change_request " , new_page ) ;
} else {
page_line_edit - > set_text ( Variant ( page ) ) ;
}
}
void EditorInspectorArray : : _next_page_button_pressed ( ) {
emit_signal ( " page_change_request " , MIN ( max_page , page + 1 ) ) ;
}
void EditorInspectorArray : : _last_page_button_pressed ( ) {
emit_signal ( " page_change_request " , max_page ) ;
}
void EditorInspectorArray : : _rmb_popup_id_pressed ( int p_id ) {
switch ( p_id ) {
case OPTION_MOVE_UP :
if ( popup_array_index_pressed > 0 ) {
_move_element ( popup_array_index_pressed , popup_array_index_pressed - 1 ) ;
}
break ;
case OPTION_MOVE_DOWN :
if ( popup_array_index_pressed < count - 1 ) {
_move_element ( popup_array_index_pressed , popup_array_index_pressed + 2 ) ;
}
break ;
case OPTION_NEW_BEFORE :
_move_element ( - 1 , popup_array_index_pressed ) ;
break ;
case OPTION_NEW_AFTER :
_move_element ( - 1 , popup_array_index_pressed + 1 ) ;
break ;
case OPTION_REMOVE :
_move_element ( popup_array_index_pressed , - 1 ) ;
break ;
case OPTION_CLEAR_ARRAY :
_clear_array ( ) ;
break ;
case OPTION_RESIZE_ARRAY :
new_size = count ;
new_size_line_edit - > set_text ( Variant ( new_size ) ) ;
resize_dialog - > get_ok_button ( ) - > set_disabled ( true ) ;
resize_dialog - > popup_centered ( ) ;
new_size_line_edit - > grab_focus ( ) ;
new_size_line_edit - > select_all ( ) ;
break ;
default :
break ;
}
}
void EditorInspectorArray : : _control_dropping_draw ( ) {
int drop_position = _drop_position ( ) ;
if ( dropping & & drop_position > = 0 ) {
Vector2 from ;
Vector2 to ;
if ( drop_position < elements_vbox - > get_child_count ( ) ) {
Transform2D xform = Object : : cast_to < Control > ( elements_vbox - > get_child ( drop_position ) ) - > get_transform ( ) ;
from = xform . xform ( Vector2 ( ) ) ;
to = xform . xform ( Vector2 ( elements_vbox - > get_size ( ) . x , 0 ) ) ;
} else {
Control * child = Object : : cast_to < Control > ( elements_vbox - > get_child ( drop_position - 1 ) ) ;
Transform2D xform = child - > get_transform ( ) ;
from = xform . xform ( Vector2 ( 0 , child - > get_size ( ) . y ) ) ;
to = xform . xform ( Vector2 ( elements_vbox - > get_size ( ) . x , child - > get_size ( ) . y ) ) ;
}
Color color = get_theme_color ( SNAME ( " accent_color " ) , SNAME ( " Editor " ) ) ;
control_dropping - > draw_line ( from , to , color , 2 ) ;
}
}
void EditorInspectorArray : : _vbox_visibility_changed ( ) {
control_dropping - > set_visible ( vbox - > is_visible_in_tree ( ) ) ;
}
void EditorInspectorArray : : _panel_draw ( int p_index ) {
ERR_FAIL_INDEX ( p_index , ( int ) array_elements . size ( ) ) ;
Ref < StyleBox > style = get_theme_stylebox ( " Focus " , " EditorStyles " ) ;
if ( ! style . is_valid ( ) ) {
return ;
}
if ( array_elements [ p_index ] . panel - > has_focus ( ) ) {
array_elements [ p_index ] . panel - > draw_style_box ( style , Rect2 ( Vector2 ( ) , array_elements [ p_index ] . panel - > get_size ( ) ) ) ;
}
}
void EditorInspectorArray : : _panel_gui_input ( Ref < InputEvent > p_event , int p_index ) {
ERR_FAIL_INDEX ( p_index , ( int ) array_elements . size ( ) ) ;
Ref < InputEventKey > key_ref = p_event ;
if ( key_ref . is_valid ( ) ) {
const InputEventKey & key = * * key_ref ;
if ( array_elements [ p_index ] . panel - > has_focus ( ) & & key . is_pressed ( ) & & key . get_keycode ( ) = = KEY_DELETE ) {
_move_element ( begin_array_index + p_index , - 1 ) ;
array_elements [ p_index ] . panel - > accept_event ( ) ;
}
}
Ref < InputEventMouseButton > mb = p_event ;
if ( mb . is_valid ( ) ) {
if ( mb - > get_button_index ( ) = = MOUSE_BUTTON_RIGHT ) {
popup_array_index_pressed = begin_array_index + p_index ;
rmb_popup - > set_item_disabled ( OPTION_MOVE_UP , popup_array_index_pressed = = 0 ) ;
rmb_popup - > set_item_disabled ( OPTION_MOVE_DOWN , popup_array_index_pressed = = count - 1 ) ;
rmb_popup - > set_position ( mb - > get_global_position ( ) ) ;
rmb_popup - > set_size ( Vector2 ( ) ) ;
rmb_popup - > popup ( ) ;
}
}
}
void EditorInspectorArray : : _move_element ( int p_element_index , int p_to_pos ) {
String action_name ;
if ( p_element_index < 0 ) {
action_name = vformat ( " Add element to property array with prefix %s. " , array_element_prefix ) ;
} else if ( p_to_pos < 0 ) {
action_name = vformat ( " Remove element %d from property array with prefix %s. " , p_element_index , array_element_prefix ) ;
} else {
action_name = vformat ( " Move element %d to position %d in property array with prefix %s. " , p_element_index , p_to_pos , array_element_prefix ) ;
}
undo_redo - > create_action ( action_name ) ;
if ( mode = = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION ) {
// Call the function.
Callable move_function = EditorNode : : get_singleton ( ) - > get_editor_data ( ) . get_move_array_element_function ( object - > get_class_name ( ) ) ;
if ( move_function . is_valid ( ) ) {
Variant args [ ] = { ( Object * ) undo_redo , object , array_element_prefix , p_element_index , p_to_pos } ;
const Variant * args_p [ ] = { & args [ 0 ] , & args [ 1 ] , & args [ 2 ] , & args [ 3 ] , & args [ 4 ] } ;
Variant return_value ;
Callable : : CallError call_error ;
move_function . call ( args_p , 5 , return_value , call_error ) ;
} else {
WARN_PRINT ( vformat ( " Could not find a function to move arrays elements for class %s. Register a move element function using EditorData::add_move_array_element_function " , object - > get_class_name ( ) ) ) ;
}
} else if ( mode = = MODE_USE_COUNT_PROPERTY ) {
ERR_FAIL_COND ( p_to_pos < - 1 | | p_to_pos > count ) ;
List < PropertyInfo > object_property_list ;
object - > get_property_list ( & object_property_list ) ;
Array properties_as_array = _extract_properties_as_array ( object_property_list ) ;
properties_as_array . resize ( count ) ;
// For undoing things
undo_redo - > add_undo_property ( object , count_property , properties_as_array . size ( ) ) ;
for ( int i = 0 ; i < ( int ) properties_as_array . size ( ) ; i + + ) {
Dictionary d = Dictionary ( properties_as_array [ i ] ) ;
Array keys = d . keys ( ) ;
for ( int j = 0 ; j < keys . size ( ) ; j + + ) {
String key = keys [ j ] ;
undo_redo - > add_undo_property ( object , vformat ( key , i ) , d [ key ] ) ;
}
}
if ( p_element_index < 0 ) {
// Add an element.
properties_as_array . insert ( p_to_pos < 0 ? properties_as_array . size ( ) : p_to_pos , Dictionary ( ) ) ;
} else if ( p_to_pos < 0 ) {
// Delete the element.
properties_as_array . remove ( p_element_index ) ;
} else {
// Move the element.
properties_as_array . insert ( p_to_pos , properties_as_array [ p_element_index ] . duplicate ( ) ) ;
properties_as_array . remove ( p_to_pos < p_element_index ? p_element_index + 1 : p_element_index ) ;
}
// Change the array size then set the properties.
undo_redo - > add_do_property ( object , count_property , properties_as_array . size ( ) ) ;
for ( int i = 0 ; i < ( int ) properties_as_array . size ( ) ; i + + ) {
Dictionary d = properties_as_array [ i ] ;
Array keys = d . keys ( ) ;
for ( int j = 0 ; j < keys . size ( ) ; j + + ) {
String key = keys [ j ] ;
undo_redo - > add_do_property ( object , vformat ( key , i ) , d [ key ] ) ;
}
}
}
undo_redo - > commit_action ( ) ;
// Handle page change and update counts.
if ( p_element_index < 0 ) {
int added_index = p_to_pos < 0 ? count : p_to_pos ;
emit_signal ( " page_change_request " , added_index / page_lenght ) ;
count + = 1 ;
} else if ( p_to_pos < 0 ) {
count - = 1 ;
if ( page = = max_page & & ( MAX ( 0 , count - 1 ) / page_lenght ! = max_page ) ) {
emit_signal ( " page_change_request " , max_page - 1 ) ;
}
}
begin_array_index = page * page_lenght ;
end_array_index = MIN ( count , ( page + 1 ) * page_lenght ) ;
max_page = MAX ( 0 , count - 1 ) / page_lenght ;
}
void EditorInspectorArray : : _clear_array ( ) {
undo_redo - > create_action ( vformat ( " Clear property array with prefix %s. " , array_element_prefix ) ) ;
if ( mode = = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION ) {
for ( int i = count - 1 ; i > = 0 ; i - - ) {
// Call the function.
Callable move_function = EditorNode : : get_singleton ( ) - > get_editor_data ( ) . get_move_array_element_function ( object - > get_class_name ( ) ) ;
if ( move_function . is_valid ( ) ) {
Variant args [ ] = { ( Object * ) undo_redo , object , array_element_prefix , i , - 1 } ;
const Variant * args_p [ ] = { & args [ 0 ] , & args [ 1 ] , & args [ 2 ] , & args [ 3 ] , & args [ 4 ] } ;
Variant return_value ;
Callable : : CallError call_error ;
move_function . call ( args_p , 5 , return_value , call_error ) ;
} else {
WARN_PRINT ( vformat ( " Could not find a function to move arrays elements for class %s. Register a move element function using EditorData::add_move_array_element_function " , object - > get_class_name ( ) ) ) ;
}
}
} else if ( mode = = MODE_USE_COUNT_PROPERTY ) {
List < PropertyInfo > object_property_list ;
object - > get_property_list ( & object_property_list ) ;
Array properties_as_array = _extract_properties_as_array ( object_property_list ) ;
properties_as_array . resize ( count ) ;
// For undoing things
undo_redo - > add_undo_property ( object , count_property , count ) ;
for ( int i = 0 ; i < ( int ) properties_as_array . size ( ) ; i + + ) {
Dictionary d = Dictionary ( properties_as_array [ i ] ) ;
Array keys = d . keys ( ) ;
for ( int j = 0 ; j < keys . size ( ) ; j + + ) {
String key = keys [ j ] ;
undo_redo - > add_undo_property ( object , vformat ( key , i ) , d [ key ] ) ;
}
}
// Change the array size then set the properties.
undo_redo - > add_do_property ( object , count_property , 0 ) ;
}
undo_redo - > commit_action ( ) ;
// Handle page change and update counts.
emit_signal ( " page_change_request " , 0 ) ;
count = 0 ;
begin_array_index = 0 ;
end_array_index = 0 ;
max_page = 0 ;
}
void EditorInspectorArray : : _resize_array ( int p_size ) {
ERR_FAIL_COND ( p_size < 0 ) ;
if ( p_size = = count ) {
return ;
}
undo_redo - > create_action ( vformat ( " Resize property array with prefix %s. " , array_element_prefix ) ) ;
if ( p_size > count ) {
if ( mode = = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION ) {
for ( int i = count ; i < p_size ; i + + ) {
// Call the function.
Callable move_function = EditorNode : : get_singleton ( ) - > get_editor_data ( ) . get_move_array_element_function ( object - > get_class_name ( ) ) ;
if ( move_function . is_valid ( ) ) {
Variant args [ ] = { ( Object * ) undo_redo , object , array_element_prefix , - 1 , - 1 } ;
const Variant * args_p [ ] = { & args [ 0 ] , & args [ 1 ] , & args [ 2 ] , & args [ 3 ] , & args [ 4 ] } ;
Variant return_value ;
Callable : : CallError call_error ;
move_function . call ( args_p , 5 , return_value , call_error ) ;
} else {
WARN_PRINT ( vformat ( " Could not find a function to move arrays elements for class %s. Register a move element function using EditorData::add_move_array_element_function " , object - > get_class_name ( ) ) ) ;
}
}
} else if ( mode = = MODE_USE_COUNT_PROPERTY ) {
undo_redo - > add_undo_property ( object , count_property , count ) ;
undo_redo - > add_do_property ( object , count_property , p_size ) ;
}
} else {
if ( mode = = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION ) {
for ( int i = count - 1 ; i > p_size - 1 ; i - - ) {
// Call the function.
Callable move_function = EditorNode : : get_singleton ( ) - > get_editor_data ( ) . get_move_array_element_function ( object - > get_class_name ( ) ) ;
if ( move_function . is_valid ( ) ) {
Variant args [ ] = { ( Object * ) undo_redo , object , array_element_prefix , i , - 1 } ;
const Variant * args_p [ ] = { & args [ 0 ] , & args [ 1 ] , & args [ 2 ] , & args [ 3 ] , & args [ 4 ] } ;
Variant return_value ;
Callable : : CallError call_error ;
move_function . call ( args_p , 5 , return_value , call_error ) ;
} else {
WARN_PRINT ( vformat ( " Could not find a function to move arrays elements for class %s. Register a move element function using EditorData::add_move_array_element_function " , object - > get_class_name ( ) ) ) ;
}
}
} else if ( mode = = MODE_USE_COUNT_PROPERTY ) {
List < PropertyInfo > object_property_list ;
object - > get_property_list ( & object_property_list ) ;
Array properties_as_array = _extract_properties_as_array ( object_property_list ) ;
properties_as_array . resize ( count ) ;
// For undoing things
undo_redo - > add_undo_property ( object , count_property , count ) ;
for ( int i = count - 1 ; i > p_size - 1 ; i - - ) {
Dictionary d = Dictionary ( properties_as_array [ i ] ) ;
Array keys = d . keys ( ) ;
for ( int j = 0 ; j < keys . size ( ) ; j + + ) {
String key = keys [ j ] ;
undo_redo - > add_undo_property ( object , vformat ( key , i ) , d [ key ] ) ;
}
}
// Change the array size then set the properties.
undo_redo - > add_do_property ( object , count_property , p_size ) ;
}
}
undo_redo - > commit_action ( ) ;
// Handle page change and update counts.
emit_signal ( " page_change_request " , 0 ) ;
/*
count = 0 ;
begin_array_index = 0 ;
end_array_index = 0 ;
max_page = 0 ;
*/
}
Array EditorInspectorArray : : _extract_properties_as_array ( const List < PropertyInfo > & p_list ) {
Array output ;
for ( const PropertyInfo & pi : p_list ) {
if ( pi . name . begins_with ( array_element_prefix ) ) {
String str = pi . name . trim_prefix ( array_element_prefix ) ;
int to_char_index = 0 ;
while ( to_char_index < str . length ( ) ) {
if ( str [ to_char_index ] < ' 0 ' | | str [ to_char_index ] > ' 9 ' ) {
break ;
}
to_char_index + + ;
}
if ( to_char_index > 0 ) {
int array_index = str . left ( to_char_index ) . to_int ( ) ;
Error error = OK ;
if ( array_index > = output . size ( ) ) {
error = output . resize ( array_index + 1 ) ;
}
if ( error = = OK ) {
String format_string = String ( array_element_prefix ) + " %d " + str . substr ( to_char_index ) ;
Dictionary dict = output [ array_index ] ;
dict [ format_string ] = object - > get ( pi . name ) ;
output [ array_index ] = dict ;
} else {
WARN_PRINT ( vformat ( " Array element %s has an index too high. Array allocaiton failed. " , pi . name ) ) ;
}
}
}
}
return output ;
}
int EditorInspectorArray : : _drop_position ( ) const {
for ( int i = 0 ; i < ( int ) array_elements . size ( ) ; i + + ) {
const ArrayElement & ae = array_elements [ i ] ;
Size2 size = ae . panel - > get_size ( ) ;
Vector2 mp = ae . panel - > get_local_mouse_position ( ) ;
if ( Rect2 ( Vector2 ( ) , size ) . has_point ( mp ) ) {
if ( mp . y < size . y / 2 ) {
return i ;
} else {
return i + 1 ;
}
}
}
return - 1 ;
}
void EditorInspectorArray : : _new_size_line_edit_text_changed ( String p_text ) {
bool valid = false ;
;
if ( p_text . is_valid_int ( ) ) {
int val = p_text . to_int ( ) ;
if ( val > 0 & & val ! = count ) {
valid = true ;
}
}
resize_dialog - > get_ok_button ( ) - > set_disabled ( ! valid ) ;
}
void EditorInspectorArray : : _new_size_line_edit_text_submitted ( String p_text ) {
bool valid = false ;
;
if ( p_text . is_valid_int ( ) ) {
int val = p_text . to_int ( ) ;
if ( val > 0 & & val ! = count ) {
new_size = val ;
valid = true ;
}
}
if ( valid ) {
resize_dialog - > hide ( ) ;
_resize_array ( new_size ) ;
} else {
new_size_line_edit - > set_text ( Variant ( new_size ) ) ;
}
}
void EditorInspectorArray : : _resize_dialog_confirmed ( ) {
_new_size_line_edit_text_submitted ( new_size_line_edit - > get_text ( ) ) ;
}
void EditorInspectorArray : : _setup ( ) {
// Setup counts.
count = _get_array_count ( ) ;
begin_array_index = page * page_lenght ;
end_array_index = MIN ( count , ( page + 1 ) * page_lenght ) ;
max_page = MAX ( 0 , count - 1 ) / page_lenght ;
array_elements . resize ( MAX ( 0 , end_array_index - begin_array_index ) ) ;
if ( page < 0 | | page > max_page ) {
WARN_PRINT ( vformat ( " Invalid page number %d " , page ) ) ;
page = CLAMP ( page , 0 , max_page ) ;
}
for ( int i = 0 ; i < ( int ) array_elements . size ( ) ; i + + ) {
ArrayElement & ae = array_elements [ i ] ;
// Panel and its hbox.
ae . panel = memnew ( PanelContainer ) ;
ae . panel - > set_focus_mode ( FOCUS_ALL ) ;
ae . panel - > set_mouse_filter ( MOUSE_FILTER_PASS ) ;
ae . panel - > set_drag_forwarding ( this ) ;
ae . panel - > set_meta ( " index " , begin_array_index + i ) ;
ae . panel - > set_tooltip ( vformat ( TTR ( " Element %d: %s%d* " ) , i , array_element_prefix , i ) ) ;
ae . panel - > connect ( " focus_entered " , callable_mp ( ( CanvasItem * ) ae . panel , & PanelContainer : : update ) ) ;
ae . panel - > connect ( " focus_exited " , callable_mp ( ( CanvasItem * ) ae . panel , & PanelContainer : : update ) ) ;
ae . panel - > connect ( " draw " , callable_bind ( callable_mp ( this , & EditorInspectorArray : : _panel_draw ) , i ) ) ;
ae . panel - > connect ( " gui_input " , callable_bind ( callable_mp ( this , & EditorInspectorArray : : _panel_gui_input ) , i ) ) ;
ae . panel - > add_theme_style_override ( SNAME ( " panel " ) , i % 2 ? odd_style : even_style ) ;
elements_vbox - > add_child ( ae . panel ) ;
ae . margin = memnew ( MarginContainer ) ;
ae . margin - > set_mouse_filter ( MOUSE_FILTER_PASS ) ;
if ( is_inside_tree ( ) ) {
Size2 min_size = get_theme_stylebox ( " Focus " , " EditorStyles " ) - > get_minimum_size ( ) ;
ae . margin - > add_theme_constant_override ( " margin_left " , min_size . x / 2 ) ;
ae . margin - > add_theme_constant_override ( " margin_top " , min_size . y / 2 ) ;
ae . margin - > add_theme_constant_override ( " margin_right " , min_size . x / 2 ) ;
ae . margin - > add_theme_constant_override ( " margin_bottom " , min_size . y / 2 ) ;
}
ae . panel - > add_child ( ae . margin ) ;
ae . hbox = memnew ( HBoxContainer ) ;
ae . hbox - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
ae . hbox - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
ae . margin - > add_child ( ae . hbox ) ;
// Move button.
ae . move_texture_rect = memnew ( TextureRect ) ;
ae . move_texture_rect - > set_stretch_mode ( TextureRect : : STRETCH_KEEP_CENTERED ) ;
if ( is_inside_tree ( ) ) {
ae . move_texture_rect - > set_texture ( get_theme_icon ( SNAME ( " TripleBar " ) , SNAME ( " EditorIcons " ) ) ) ;
}
ae . hbox - > add_child ( ae . move_texture_rect ) ;
// Right vbox.
ae . vbox = memnew ( VBoxContainer ) ;
ae . vbox - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
ae . vbox - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
ae . hbox - > add_child ( ae . vbox ) ;
}
// Hide/show the add button.
add_button - > set_visible ( page = = max_page ) ;
if ( max_page = = 0 ) {
hbox_pagination - > hide ( ) ;
} else {
// Update buttons.
first_page_button - > set_disabled ( page = = 0 ) ;
prev_page_button - > set_disabled ( page = = 0 ) ;
next_page_button - > set_disabled ( page = = max_page ) ;
last_page_button - > set_disabled ( page = = max_page ) ;
// Update page number and page count.
page_line_edit - > set_text ( vformat ( " %d " , page + 1 ) ) ;
page_count_label - > set_text ( vformat ( " / %d " , max_page + 1 ) ) ;
}
}
Variant EditorInspectorArray : : get_drag_data_fw ( const Point2 & p_point , Control * p_from ) {
int index = p_from - > get_meta ( " index " ) ;
Dictionary dict ;
dict [ " type " ] = " property_array_element " ;
dict [ " property_array_prefix " ] = array_element_prefix ;
dict [ " index " ] = index ;
return dict ;
}
void EditorInspectorArray : : drop_data_fw ( const Point2 & p_point , const Variant & p_data , Control * p_from ) {
Dictionary dict = p_data ;
int to_drop = dict [ " index " ] ;
int drop_position = _drop_position ( ) ;
if ( drop_position < 0 ) {
return ;
}
_move_element ( to_drop , begin_array_index + drop_position ) ;
}
bool EditorInspectorArray : : can_drop_data_fw ( const Point2 & p_point , const Variant & p_data , Control * p_from ) const {
// First, update drawing.
control_dropping - > update ( ) ;
if ( p_data . get_type ( ) ! = Variant : : DICTIONARY ) {
return false ;
}
Dictionary dict = p_data ;
int drop_position = _drop_position ( ) ;
if ( ! dict . has ( " type " ) | | dict [ " type " ] ! = " property_array_element " | | String ( dict [ " property_array_prefix " ] ) ! = array_element_prefix | | drop_position < 0 ) {
return false ;
}
// Check in dropping at the given index does indeed move the item.
int moved_array_index = ( int ) dict [ " index " ] ;
int drop_array_index = begin_array_index + drop_position ;
return drop_array_index ! = moved_array_index & & drop_array_index - 1 ! = moved_array_index ;
}
void EditorInspectorArray : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE :
case NOTIFICATION_THEME_CHANGED : {
Color color = get_theme_color ( SNAME ( " dark_color_1 " ) , SNAME ( " Editor " ) ) ;
odd_style - > set_bg_color ( color . lightened ( 0.15 ) ) ;
even_style - > set_bg_color ( color . darkened ( 0.15 ) ) ;
for ( int i = 0 ; i < ( int ) array_elements . size ( ) ; i + + ) {
ArrayElement & ae = array_elements [ i ] ;
ae . move_texture_rect - > set_texture ( get_theme_icon ( SNAME ( " TripleBar " ) , SNAME ( " EditorIcons " ) ) ) ;
Size2 min_size = get_theme_stylebox ( " Focus " , " EditorStyles " ) - > get_minimum_size ( ) ;
ae . margin - > add_theme_constant_override ( " margin_left " , min_size . x / 2 ) ;
ae . margin - > add_theme_constant_override ( " margin_top " , min_size . y / 2 ) ;
ae . margin - > add_theme_constant_override ( " margin_right " , min_size . x / 2 ) ;
ae . margin - > add_theme_constant_override ( " margin_bottom " , min_size . y / 2 ) ;
}
add_button - > set_icon ( get_theme_icon ( SNAME ( " Add " ) , SNAME ( " EditorIcons " ) ) ) ;
first_page_button - > set_icon ( get_theme_icon ( SNAME ( " PageFirst " ) , SNAME ( " EditorIcons " ) ) ) ;
prev_page_button - > set_icon ( get_theme_icon ( SNAME ( " PagePrevious " ) , SNAME ( " EditorIcons " ) ) ) ;
next_page_button - > set_icon ( get_theme_icon ( SNAME ( " PageNext " ) , SNAME ( " EditorIcons " ) ) ) ;
last_page_button - > set_icon ( get_theme_icon ( SNAME ( " PageLast " ) , SNAME ( " EditorIcons " ) ) ) ;
minimum_size_changed ( ) ;
} break ;
case NOTIFICATION_DRAG_BEGIN : {
Dictionary dict = get_viewport ( ) - > gui_get_drag_data ( ) ;
if ( dict . has ( " type " ) & & dict [ " type " ] = = " property_array_element " & & String ( dict [ " property_array_prefix " ] ) = = array_element_prefix ) {
dropping = true ;
control_dropping - > update ( ) ;
}
} break ;
case NOTIFICATION_DRAG_END : {
if ( dropping ) {
dropping = false ;
control_dropping - > update ( ) ;
}
} break ;
}
}
void EditorInspectorArray : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " _get_drag_data_fw " ) , & EditorInspectorArray : : get_drag_data_fw ) ;
ClassDB : : bind_method ( D_METHOD ( " _can_drop_data_fw " ) , & EditorInspectorArray : : can_drop_data_fw ) ;
ClassDB : : bind_method ( D_METHOD ( " _drop_data_fw " ) , & EditorInspectorArray : : drop_data_fw ) ;
ADD_SIGNAL ( MethodInfo ( " page_change_request " ) ) ;
}
void EditorInspectorArray : : set_undo_redo ( UndoRedo * p_undo_redo ) {
undo_redo = p_undo_redo ;
}
void EditorInspectorArray : : setup_with_move_element_function ( Object * p_object , String p_label , const StringName & p_array_element_prefix , int p_page , const Color & p_bg_color , bool p_foldable ) {
count_property = " " ;
mode = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION ;
array_element_prefix = p_array_element_prefix ;
page = p_page ;
EditorInspectorSection : : setup ( String ( p_array_element_prefix ) + " _array " , p_label , p_object , p_bg_color , p_foldable ) ;
_setup ( ) ;
}
void EditorInspectorArray : : setup_with_count_property ( Object * p_object , String p_label , const StringName & p_count_property , const StringName & p_array_element_prefix , int p_page , const Color & p_bg_color , bool p_foldable ) {
count_property = p_count_property ;
mode = MODE_USE_COUNT_PROPERTY ;
array_element_prefix = p_array_element_prefix ;
page = p_page ;
EditorInspectorSection : : setup ( String ( count_property ) + " _array " , p_label , p_object , p_bg_color , p_foldable ) ;
_setup ( ) ;
}
VBoxContainer * EditorInspectorArray : : get_vbox ( int p_index ) {
if ( p_index > = begin_array_index & & p_index < end_array_index ) {
return array_elements [ p_index - begin_array_index ] . vbox ;
} else if ( p_index < 0 ) {
return vbox ;
} else {
return nullptr ;
}
}
EditorInspectorArray : : EditorInspectorArray ( ) {
set_mouse_filter ( Control : : MOUSE_FILTER_STOP ) ;
odd_style . instantiate ( ) ;
even_style . instantiate ( ) ;
rmb_popup = memnew ( PopupMenu ) ;
rmb_popup - > add_item ( TTR ( " Move Up " ) , OPTION_MOVE_UP ) ;
rmb_popup - > add_item ( TTR ( " Move Down " ) , OPTION_MOVE_DOWN ) ;
rmb_popup - > add_separator ( ) ;
rmb_popup - > add_item ( TTR ( " Insert New Before " ) , OPTION_NEW_BEFORE ) ;
rmb_popup - > add_item ( TTR ( " Insert New After " ) , OPTION_NEW_AFTER ) ;
rmb_popup - > add_separator ( ) ;
rmb_popup - > add_item ( TTR ( " Remove " ) , OPTION_REMOVE ) ;
rmb_popup - > add_separator ( ) ;
rmb_popup - > add_item ( TTR ( " Clear Array " ) , OPTION_CLEAR_ARRAY ) ;
rmb_popup - > add_item ( TTR ( " Resize Array... " ) , OPTION_RESIZE_ARRAY ) ;
rmb_popup - > connect ( " id_pressed " , callable_mp ( this , & EditorInspectorArray : : _rmb_popup_id_pressed ) ) ;
add_child ( rmb_popup ) ;
elements_vbox = memnew ( VBoxContainer ) ;
elements_vbox - > add_theme_constant_override ( " separation " , 0 ) ;
vbox - > add_child ( elements_vbox ) ;
add_button = memnew ( Button ) ;
add_button - > set_text ( TTR ( " Add Element " ) ) ;
add_button - > set_text_align ( Button : : ALIGN_CENTER ) ;
add_button - > connect ( " pressed " , callable_mp ( this , & EditorInspectorArray : : _add_button_pressed ) ) ;
vbox - > add_child ( add_button ) ;
hbox_pagination = memnew ( HBoxContainer ) ;
hbox_pagination - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
hbox_pagination - > set_alignment ( HBoxContainer : : ALIGN_CENTER ) ;
vbox - > add_child ( hbox_pagination ) ;
first_page_button = memnew ( Button ) ;
first_page_button - > set_flat ( true ) ;
first_page_button - > connect ( " pressed " , callable_mp ( this , & EditorInspectorArray : : _first_page_button_pressed ) ) ;
hbox_pagination - > add_child ( first_page_button ) ;
prev_page_button = memnew ( Button ) ;
prev_page_button - > set_flat ( true ) ;
prev_page_button - > connect ( " pressed " , callable_mp ( this , & EditorInspectorArray : : _prev_page_button_pressed ) ) ;
hbox_pagination - > add_child ( prev_page_button ) ;
page_line_edit = memnew ( LineEdit ) ;
page_line_edit - > connect ( " text_submitted " , callable_mp ( this , & EditorInspectorArray : : _page_line_edit_text_submitted ) ) ;
page_line_edit - > add_theme_constant_override ( " minimum_character_width " , 2 ) ;
hbox_pagination - > add_child ( page_line_edit ) ;
page_count_label = memnew ( Label ) ;
hbox_pagination - > add_child ( page_count_label ) ;
next_page_button = memnew ( Button ) ;
next_page_button - > set_flat ( true ) ;
next_page_button - > connect ( " pressed " , callable_mp ( this , & EditorInspectorArray : : _next_page_button_pressed ) ) ;
hbox_pagination - > add_child ( next_page_button ) ;
last_page_button = memnew ( Button ) ;
last_page_button - > set_flat ( true ) ;
last_page_button - > connect ( " pressed " , callable_mp ( this , & EditorInspectorArray : : _last_page_button_pressed ) ) ;
hbox_pagination - > add_child ( last_page_button ) ;
control_dropping = memnew ( Control ) ;
control_dropping - > connect ( " draw " , callable_mp ( this , & EditorInspectorArray : : _control_dropping_draw ) ) ;
control_dropping - > set_mouse_filter ( Control : : MOUSE_FILTER_IGNORE ) ;
add_child ( control_dropping ) ;
resize_dialog = memnew ( AcceptDialog ) ;
resize_dialog - > set_title ( TTRC ( " Resize Array " ) ) ;
resize_dialog - > add_cancel_button ( ) ;
resize_dialog - > connect ( " confirmed " , callable_mp ( this , & EditorInspectorArray : : _resize_dialog_confirmed ) ) ;
add_child ( resize_dialog ) ;
VBoxContainer * resize_dialog_vbox = memnew ( VBoxContainer ) ;
resize_dialog - > add_child ( resize_dialog_vbox ) ;
new_size_line_edit = memnew ( LineEdit ) ;
new_size_line_edit - > connect ( " text_changed " , callable_mp ( this , & EditorInspectorArray : : _new_size_line_edit_text_changed ) ) ;
new_size_line_edit - > connect ( " text_submitted " , callable_mp ( this , & EditorInspectorArray : : _new_size_line_edit_text_submitted ) ) ;
resize_dialog_vbox - > add_margin_child ( TTRC ( " New Size: " ) , new_size_line_edit ) ;
vbox - > connect ( " visibility_changed " , callable_mp ( this , & EditorInspectorArray : : _vbox_visibility_changed ) ) ;
}
2018-05-15 22:12:35 +02:00
////////////////////////////////////////////////
////////////////////////////////////////////////
Ref < EditorInspectorPlugin > EditorInspector : : inspector_plugins [ MAX_PLUGINS ] ;
int EditorInspector : : inspector_plugin_count = 0 ;
2021-07-01 03:24:34 +02:00
EditorProperty * EditorInspector : : instantiate_property_editor ( Object * p_object , const Variant : : Type p_type , const String & p_path , PropertyHint p_hint , const String & p_hint_text , const uint32_t p_usage , const bool p_wide ) {
2018-07-14 23:15:42 +02:00
for ( int i = inspector_plugin_count - 1 ; i > = 0 ; i - - ) {
2020-04-17 04:52:00 +02:00
inspector_plugins [ i ] - > parse_property ( p_object , p_type , p_path , p_hint , p_hint_text , p_usage , p_wide ) ;
2018-07-14 23:15:42 +02:00
if ( inspector_plugins [ i ] - > added_editors . size ( ) ) {
for ( int j = 1 ; j < inspector_plugins [ i ] - > added_editors . size ( ) ; j + + ) { //only keep first one
memdelete ( inspector_plugins [ i ] - > added_editors [ j ] . property_editor ) ;
}
EditorProperty * prop = Object : : cast_to < EditorProperty > ( inspector_plugins [ i ] - > added_editors [ 0 ] . property_editor ) ;
if ( prop ) {
inspector_plugins [ i ] - > added_editors . clear ( ) ;
return prop ;
} else {
memdelete ( inspector_plugins [ i ] - > added_editors [ 0 ] . property_editor ) ;
inspector_plugins [ i ] - > added_editors . clear ( ) ;
}
}
}
2020-04-02 01:20:12 +02:00
return nullptr ;
2018-07-14 23:15:42 +02:00
}
2018-05-15 22:12:35 +02:00
void EditorInspector : : add_inspector_plugin ( const Ref < EditorInspectorPlugin > & p_plugin ) {
ERR_FAIL_COND ( inspector_plugin_count = = MAX_PLUGINS ) ;
for ( int i = 0 ; i < inspector_plugin_count ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( inspector_plugins [ i ] = = p_plugin ) {
2018-05-15 22:12:35 +02:00
return ; //already exists
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
}
inspector_plugins [ inspector_plugin_count + + ] = p_plugin ;
}
void EditorInspector : : remove_inspector_plugin ( const Ref < EditorInspectorPlugin > & p_plugin ) {
ERR_FAIL_COND ( inspector_plugin_count = = MAX_PLUGINS ) ;
int idx = - 1 ;
for ( int i = 0 ; i < inspector_plugin_count ; i + + ) {
if ( inspector_plugins [ i ] = = p_plugin ) {
idx = i ;
break ;
}
}
2019-09-25 10:28:50 +02:00
ERR_FAIL_COND_MSG ( idx = = - 1 , " Trying to remove nonexistent inspector plugin. " ) ;
2018-05-15 22:12:35 +02:00
for ( int i = idx ; i < inspector_plugin_count - 1 ; i + + ) {
inspector_plugins [ i ] = inspector_plugins [ i + 1 ] ;
}
2019-03-06 00:03:38 +01:00
2020-05-14 16:41:43 +02:00
if ( idx = = inspector_plugin_count - 1 ) {
2019-03-06 00:03:38 +01:00
inspector_plugins [ idx ] = Ref < EditorInspectorPlugin > ( ) ;
2020-05-14 16:41:43 +02:00
}
2019-03-06 00:03:38 +01:00
2018-05-15 22:12:35 +02:00
inspector_plugin_count - - ;
}
void EditorInspector : : cleanup_plugins ( ) {
for ( int i = 0 ; i < inspector_plugin_count ; i + + ) {
inspector_plugins [ i ] . unref ( ) ;
}
inspector_plugin_count = 0 ;
}
void EditorInspector : : set_undo_redo ( UndoRedo * p_undo_redo ) {
undo_redo = p_undo_redo ;
}
String EditorInspector : : get_selected_path ( ) const {
return property_selected ;
}
2018-05-17 23:02:16 +02:00
void EditorInspector : : _parse_added_editors ( VBoxContainer * current_vbox , Ref < EditorInspectorPlugin > ped ) {
2021-07-24 15:46:25 +02:00
for ( const EditorInspectorPlugin : : AddedEditor & F : ped - > added_editors ) {
2021-07-16 05:45:57 +02:00
EditorProperty * ep = Object : : cast_to < EditorProperty > ( F . property_editor ) ;
current_vbox - > add_child ( F . property_editor ) ;
2018-05-17 23:02:16 +02:00
if ( ep ) {
ep - > object = object ;
2020-03-05 15:33:01 +01:00
ep - > connect ( " property_changed " , callable_mp ( this , & EditorInspector : : _property_changed ) ) ;
2020-02-21 18:28:45 +01:00
ep - > connect ( " property_keyed " , callable_mp ( this , & EditorInspector : : _property_keyed ) ) ;
2020-04-17 04:52:00 +02:00
ep - > connect ( " property_deleted " , callable_mp ( this , & EditorInspector : : _property_deleted ) , varray ( ) , CONNECT_DEFERRED ) ;
2020-02-21 18:28:45 +01:00
ep - > connect ( " property_keyed_with_value " , callable_mp ( this , & EditorInspector : : _property_keyed_with_value ) ) ;
ep - > connect ( " property_checked " , callable_mp ( this , & EditorInspector : : _property_checked ) ) ;
ep - > connect ( " selected " , callable_mp ( this , & EditorInspector : : _property_selected ) ) ;
ep - > connect ( " multiple_properties_changed " , callable_mp ( this , & EditorInspector : : _multiple_properties_changed ) ) ;
ep - > connect ( " resource_selected " , callable_mp ( this , & EditorInspector : : _resource_selected ) , varray ( ) , CONNECT_DEFERRED ) ;
ep - > connect ( " object_id_selected " , callable_mp ( this , & EditorInspector : : _object_id_selected ) , varray ( ) , CONNECT_DEFERRED ) ;
2018-05-17 23:02:16 +02:00
2021-07-16 05:45:57 +02:00
if ( F . properties . size ( ) ) {
if ( F . properties . size ( ) = = 1 ) {
2018-05-17 23:02:16 +02:00
//since it's one, associate:
2021-07-16 05:45:57 +02:00
ep - > property = F . properties [ 0 ] ;
2018-05-17 23:02:16 +02:00
ep - > property_usage = 0 ;
}
2021-07-16 05:45:57 +02:00
if ( F . label ! = String ( ) ) {
ep - > set_label ( F . label ) ;
2018-05-17 23:02:16 +02:00
}
2021-07-16 05:45:57 +02:00
for ( int i = 0 ; i < F . properties . size ( ) ; i + + ) {
String prop = F . properties [ i ] ;
2018-05-17 23:02:16 +02:00
if ( ! editor_property_map . has ( prop ) ) {
editor_property_map [ prop ] = List < EditorProperty * > ( ) ;
}
editor_property_map [ prop ] . push_back ( ep ) ;
}
}
ep - > set_read_only ( read_only ) ;
ep - > update_property ( ) ;
ep - > update_reload_status ( ) ;
2020-04-17 04:52:00 +02:00
ep - > set_deletable ( deletable_properties ) ;
2021-02-10 21:18:45 +01:00
ep - > update_cache ( ) ;
2018-05-17 23:02:16 +02:00
}
}
ped - > added_editors . clear ( ) ;
}
2019-04-09 00:18:03 +02:00
bool EditorInspector : : _is_property_disabled_by_feature_profile ( const StringName & p_property ) {
Ref < EditorFeatureProfile > profile = EditorFeatureProfileManager : : get_singleton ( ) - > get_current_profile ( ) ;
if ( profile . is_null ( ) ) {
return false ;
}
StringName class_name = object - > get_class ( ) ;
while ( class_name ! = StringName ( ) ) {
if ( profile - > is_class_property_disabled ( class_name , p_property ) ) {
return true ;
}
if ( profile - > is_class_disabled ( class_name ) ) {
//won't see properties of a disabled class
return true ;
}
class_name = ClassDB : : get_parent_class ( class_name ) ;
}
return false ;
}
2018-05-15 22:12:35 +02:00
void EditorInspector : : update_tree ( ) {
//to update properly if all is refreshed
StringName current_selected = property_selected ;
2019-01-22 16:29:26 +01:00
int current_focusable = - 1 ;
if ( property_focusable ! = - 1 ) {
//check focusable is really focusable
bool restore_focus = false ;
Control * focused = get_focus_owner ( ) ;
if ( focused ) {
Node * parent = focused - > get_parent ( ) ;
while ( parent ) {
EditorInspector * inspector = Object : : cast_to < EditorInspector > ( parent ) ;
if ( inspector ) {
restore_focus = inspector = = this ; //may be owned by another inspector
break ; //exit after the first inspector is found, since there may be nested ones
}
parent = parent - > get_parent ( ) ;
}
}
if ( restore_focus ) {
current_focusable = property_focusable ;
}
}
2018-05-15 22:12:35 +02:00
_clear ( ) ;
2020-05-14 16:41:43 +02:00
if ( ! object ) {
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2020-03-17 07:33:00 +01:00
List < Ref < EditorInspectorPlugin > > valid_plugins ;
2018-05-15 22:12:35 +02:00
for ( int i = inspector_plugin_count - 1 ; i > = 0 ; i - - ) { //start by last, so lastly added can override newly added
2020-05-14 16:41:43 +02:00
if ( ! inspector_plugins [ i ] - > can_handle ( object ) ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
valid_plugins . push_back ( inspector_plugins [ i ] ) ;
}
2021-08-31 10:48:45 +02:00
// Decide if properties should be drawn in red.
2018-05-15 22:12:35 +02:00
bool draw_red = false ;
2021-05-26 18:28:38 +02:00
if ( is_inside_tree ( ) ) {
2018-05-15 22:12:35 +02:00
Node * nod = Object : : cast_to < Node > ( object ) ;
Node * es = EditorNode : : get_singleton ( ) - > get_edited_scene ( ) ;
if ( nod & & es ! = nod & & nod - > get_owner ( ) ! = es ) {
2021-08-31 10:48:45 +02:00
// Draw in red edited nodes that are not in the currently edited scene.
2018-05-15 22:12:35 +02:00
draw_red = true ;
}
}
String filter = search_box ? search_box - > get_text ( ) : " " ;
String group ;
String group_base ;
2020-04-08 03:51:52 +02:00
String subgroup ;
String subgroup_base ;
2020-04-02 01:20:12 +02:00
VBoxContainer * category_vbox = nullptr ;
2018-05-15 22:12:35 +02:00
2019-09-29 05:27:10 +02:00
List < PropertyInfo > plist ;
2018-05-15 22:12:35 +02:00
object - > get_property_list ( & plist , true ) ;
2019-09-29 05:27:10 +02:00
_update_script_class_properties ( * object , plist ) ;
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
Map < VBoxContainer * , HashMap < String , VBoxContainer * > > vbox_per_path ;
Map < String , EditorInspectorArray * > editor_inspector_array_per_prefix ;
2018-05-15 22:12:35 +02:00
2021-07-17 23:22:52 +02:00
Color sscolor = get_theme_color ( SNAME ( " prop_subsection " ) , SNAME ( " Editor " ) ) ;
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
// Get the lists of editors to add the beginning.
2021-07-26 17:50:35 +02:00
for ( Ref < EditorInspectorPlugin > & ped : valid_plugins ) {
2018-05-17 23:02:16 +02:00
ped - > parse_begin ( object ) ;
_parse_added_editors ( main_vbox , ped ) ;
}
2021-08-31 10:48:45 +02:00
// Get the lists of editors for properties.
for ( List < PropertyInfo > : : Element * E_property = plist . front ( ) ; E_property ; E_property = E_property - > next ( ) ) {
PropertyInfo & p = E_property - > get ( ) ;
2018-05-15 22:12:35 +02:00
2020-04-08 03:51:52 +02:00
if ( p . usage & PROPERTY_USAGE_SUBGROUP ) {
2021-08-31 10:48:45 +02:00
// Setup a property sub-group.
2020-04-08 03:51:52 +02:00
subgroup = p . name ;
subgroup_base = p . hint_string ;
continue ;
} else if ( p . usage & PROPERTY_USAGE_GROUP ) {
2021-08-31 10:48:45 +02:00
// Setup a property group.
2018-05-15 22:12:35 +02:00
group = p . name ;
group_base = p . hint_string ;
2020-04-08 03:51:52 +02:00
subgroup = " " ;
subgroup_base = " " ;
2018-05-15 22:12:35 +02:00
continue ;
} else if ( p . usage & PROPERTY_USAGE_CATEGORY ) {
2021-08-31 10:48:45 +02:00
// Setup a property category.
2018-05-15 22:12:35 +02:00
group = " " ;
group_base = " " ;
2020-04-08 03:51:52 +02:00
subgroup = " " ;
subgroup_base = " " ;
2018-05-15 22:12:35 +02:00
2020-05-14 16:41:43 +02:00
if ( ! show_categories ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
// Iterate over remaining properties. If no properties in category, skip the category.
List < PropertyInfo > : : Element * N = E_property - > next ( ) ;
2018-05-15 22:12:35 +02:00
bool valid = true ;
while ( N ) {
2021-02-17 17:44:49 +01:00
if ( N - > get ( ) . usage & PROPERTY_USAGE_EDITOR & & ( ! restrict_to_basic | | ( N - > get ( ) . usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING ) ) ) {
2018-05-15 22:12:35 +02:00
break ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
if ( N - > get ( ) . usage & PROPERTY_USAGE_CATEGORY ) {
valid = false ;
break ;
}
N = N - > next ( ) ;
}
2020-05-14 16:41:43 +02:00
if ( ! valid ) {
2021-08-31 10:48:45 +02:00
continue ; // Empty, ignore it.
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
// Create an EditorInspectorCategory and add it to the inspector.
2018-05-15 22:12:35 +02:00
EditorInspectorCategory * category = memnew ( EditorInspectorCategory ) ;
main_vbox - > add_child ( category ) ;
2020-04-02 01:20:12 +02:00
category_vbox = nullptr ; //reset
2018-05-15 22:12:35 +02:00
String type = p . name ;
2021-08-31 10:48:45 +02:00
// Set the category icon.
2019-09-29 05:27:10 +02:00
if ( ! ClassDB : : class_exists ( type ) & & ! ScriptServer : : is_global_class ( type ) & & p . hint_string . length ( ) & & FileAccess : : exists ( p . hint_string ) ) {
2021-08-31 10:48:45 +02:00
// If we have a category inside a script, search for the first script with a valid icon.
Ref < Script > script = ResourceLoader : : load ( p . hint_string , " Script " ) ;
2019-09-29 05:27:10 +02:00
String base_type ;
2021-08-31 10:48:45 +02:00
if ( script . is_valid ( ) ) {
base_type = script - > get_instance_base_type ( ) ;
2019-09-29 05:27:10 +02:00
}
2021-08-31 10:48:45 +02:00
while ( script . is_valid ( ) ) {
StringName name = EditorNode : : get_editor_data ( ) . script_class_get_name ( script - > get_path ( ) ) ;
2019-09-29 05:27:10 +02:00
String icon_path = EditorNode : : get_editor_data ( ) . script_class_get_icon_path ( name ) ;
if ( name ! = StringName ( ) & & icon_path . length ( ) ) {
category - > icon = ResourceLoader : : load ( icon_path , " Texture " ) ;
break ;
}
2021-08-31 10:48:45 +02:00
script = script - > get_base_script ( ) ;
2019-09-29 05:27:10 +02:00
}
2021-07-17 23:22:52 +02:00
if ( category - > icon . is_null ( ) & & has_theme_icon ( base_type , SNAME ( " EditorIcons " ) ) ) {
category - > icon = get_theme_icon ( base_type , SNAME ( " EditorIcons " ) ) ;
2019-09-29 05:27:10 +02:00
}
}
if ( category - > icon . is_null ( ) ) {
2020-07-06 00:06:37 +02:00
if ( type ! = String ( ) ) { // Can happen for built-in scripts.
category - > icon = EditorNode : : get_singleton ( ) - > get_class_icon ( type , " Object " ) ;
}
2019-09-29 05:27:10 +02:00
}
2021-08-31 10:48:45 +02:00
// Set the category label.
2018-05-15 22:12:35 +02:00
category - > label = type ;
if ( use_doc_hints ) {
2021-08-31 10:48:45 +02:00
// Sets the category tooltip to show documentation.
2019-02-12 21:10:08 +01:00
StringName type2 = p . name ;
if ( ! class_descr_cache . has ( type2 ) ) {
2018-05-15 22:12:35 +02:00
String descr ;
2020-11-29 04:42:06 +01:00
DocTools * dd = EditorHelp : : get_doc_data ( ) ;
2019-02-12 21:10:08 +01:00
Map < String , DocData : : ClassDoc > : : Element * E = dd - > class_list . find ( type2 ) ;
2018-05-15 22:12:35 +02:00
if ( E ) {
2020-05-28 12:02:12 +02:00
descr = DTR ( E - > get ( ) . brief_description ) ;
2018-05-15 22:12:35 +02:00
}
2020-05-28 12:02:12 +02:00
class_descr_cache [ type2 ] = descr ;
2018-05-15 22:12:35 +02:00
}
2019-02-12 21:10:08 +01:00
category - > set_tooltip ( p . name + " :: " + ( class_descr_cache [ type2 ] = = " " ? " " : class_descr_cache [ type2 ] ) ) ;
2018-05-15 22:12:35 +02:00
}
2018-05-17 23:02:16 +02:00
2021-08-31 10:48:45 +02:00
// Add editors at the start of a category.
2021-07-26 17:50:35 +02:00
for ( Ref < EditorInspectorPlugin > & ped : valid_plugins ) {
2018-05-17 23:02:16 +02:00
ped - > parse_category ( object , p . name ) ;
_parse_added_editors ( main_vbox , ped ) ;
}
2018-05-15 22:12:35 +02:00
continue ;
2021-02-17 17:44:49 +01:00
} else if ( ! ( p . usage & PROPERTY_USAGE_EDITOR ) | | _is_property_disabled_by_feature_profile ( p . name ) | | ( restrict_to_basic & & ! ( p . usage & PROPERTY_USAGE_EDITOR_BASIC_SETTING ) ) ) {
2021-08-31 10:48:45 +02:00
// Ignore properties that are not supposed to be in the inspector.
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2021-02-18 19:52:29 +01:00
if ( p . name = = " script " ) {
2021-08-31 10:48:45 +02:00
// Script should go into its own category.
category_vbox = nullptr ;
2021-02-18 19:52:29 +01:00
}
2020-05-14 16:41:43 +02:00
if ( p . usage & PROPERTY_USAGE_HIGH_END_GFX & & RS : : get_singleton ( ) - > is_low_end ( ) ) {
2021-08-31 10:48:45 +02:00
// Do not show this property in low end gfx.
continue ;
2020-05-14 16:41:43 +02:00
}
2018-09-29 01:32:40 +02:00
2019-07-22 12:03:57 +02:00
if ( p . name = = " script " & & ( hide_script | | bool ( object - > call ( " _hide_script_from_inspector " ) ) ) ) {
2021-08-31 10:48:45 +02:00
// Hide script variables from inspector if required.
2018-05-15 22:12:35 +02:00
continue ;
2018-06-07 17:46:14 +02:00
}
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
// Get the path for property.
String path = p . name ;
2020-04-08 03:51:52 +02:00
2021-08-31 10:48:45 +02:00
// First check if we have an array that fits the prefix.
String array_prefix = " " ;
int array_index = - 1 ;
for ( Map < String , EditorInspectorArray * > : : Element * E = editor_inspector_array_per_prefix . front ( ) ; E ; E = E - > next ( ) ) {
if ( p . name . begins_with ( E - > key ( ) ) & & E - > key ( ) . length ( ) > array_prefix . length ( ) ) {
array_prefix = E - > key ( ) ;
}
}
if ( ! array_prefix . is_empty ( ) ) {
// If we have an array element, find the according index in array.
String str = p . name . trim_prefix ( array_prefix ) ;
int to_char_index = 0 ;
while ( to_char_index < str . length ( ) ) {
if ( str [ to_char_index ] < ' 0 ' | | str [ to_char_index ] > ' 9 ' ) {
break ;
2020-04-08 03:51:52 +02:00
}
2021-08-31 10:48:45 +02:00
to_char_index + + ;
}
if ( to_char_index > 0 ) {
array_index = str . left ( to_char_index ) . to_int ( ) ;
} else {
array_prefix = " " ;
2020-04-08 03:51:52 +02:00
}
}
2021-08-31 10:48:45 +02:00
if ( ! array_prefix . is_empty ( ) ) {
path = path . trim_prefix ( array_prefix ) ;
int char_index = path . find ( " / " ) ;
if ( char_index > = 0 ) {
path = path . right ( - char_index - 1 ) ;
} else {
path = vformat ( TTR ( " Element %s " ) , array_index ) ;
}
} else {
// Check if we exit or not a subgroup. If there is a prefix, remove it from the property label string.
if ( subgroup ! = " " & & subgroup_base ! = " " ) {
if ( path . begins_with ( subgroup_base ) ) {
path = path . trim_prefix ( subgroup_base ) ;
} else if ( subgroup_base . begins_with ( path ) ) {
// Keep it, this is used pretty often.
} else {
subgroup = " " ; // The prefix changed, we are no longer in the subgroup.
}
}
// Check if we exit or not a group. If there is a prefix, remove it from the property label string.
if ( group ! = " " & & group_base ! = " " & & subgroup = = " " ) {
if ( path . begins_with ( group_base ) ) {
path = path . trim_prefix ( group_base ) ;
} else if ( group_base . begins_with ( path ) ) {
// Keep it, this is used pretty often.
2018-05-15 22:12:35 +02:00
} else {
2021-08-31 10:48:45 +02:00
group = " " ; // The prefix changed, we are no longer in the group.
2020-04-08 03:51:52 +02:00
subgroup = " " ;
2018-05-15 22:12:35 +02:00
}
}
2021-08-31 10:48:45 +02:00
// Add the group and subgroup to the path.
if ( subgroup ! = " " ) {
path = subgroup + " / " + path ;
}
if ( group ! = " " ) {
path = group + " / " + path ;
}
}
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
// Get the property label's string.
String property_label_string = ( path . find ( " / " ) ! = - 1 ) ? path . substr ( path . rfind ( " / " ) + 1 ) : path ;
2018-05-15 22:12:35 +02:00
if ( capitalize_paths ) {
2021-08-31 10:48:45 +02:00
// Capitalize paths.
int dot = property_label_string . find ( " . " ) ;
2018-05-15 22:12:35 +02:00
if ( dot ! = - 1 ) {
2021-08-31 10:48:45 +02:00
String ov = property_label_string . substr ( dot ) ;
property_label_string = property_label_string . substr ( 0 , dot ) ;
property_label_string = property_label_string . capitalize ( ) ;
property_label_string + = ov ;
2018-05-15 22:12:35 +02:00
} else {
2021-08-31 10:48:45 +02:00
property_label_string = property_label_string . capitalize ( ) ;
2018-05-15 22:12:35 +02:00
}
}
2021-08-31 10:48:45 +02:00
// Remove the property from the path.
int idx = path . rfind ( " / " ) ;
if ( idx > - 1 ) {
path = path . left ( idx ) ;
} else {
path = " " ;
2021-05-21 15:17:43 +02:00
}
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
// Ignore properties that do not fit the filter.
2018-05-15 22:12:35 +02:00
if ( use_filter & & filter ! = " " ) {
2021-08-31 10:48:45 +02:00
if ( ! filter . is_subsequence_ofi ( path ) & & ! filter . is_subsequence_ofi ( property_label_string ) & & property_prefix . to_lower ( ) . find ( filter . to_lower ( ) ) = = - 1 ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
}
2021-08-31 10:48:45 +02:00
// Recreate the category vbox if it was reset.
2020-04-02 01:20:12 +02:00
if ( category_vbox = = nullptr ) {
2018-07-19 00:37:17 +02:00
category_vbox = memnew ( VBoxContainer ) ;
main_vbox - > add_child ( category_vbox ) ;
}
2021-08-31 10:48:45 +02:00
// Find the correct section/vbox to add the property editor to.
VBoxContainer * root_vbox = array_prefix . is_empty ( ) ? main_vbox : editor_inspector_array_per_prefix [ array_prefix ] - > get_vbox ( array_index ) ;
if ( ! root_vbox ) {
continue ;
}
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
if ( ! vbox_per_path . has ( root_vbox ) ) {
vbox_per_path [ root_vbox ] = HashMap < String , VBoxContainer * > ( ) ;
vbox_per_path [ root_vbox ] [ " " ] = root_vbox ;
}
VBoxContainer * current_vbox = root_vbox ;
String acc_path = " " ;
int level = 1 ;
Vector < String > components = path . split ( " / " ) ;
for ( int i = 0 ; i < components . size ( ) ; i + + ) {
String component = components [ i ] ;
acc_path + = ( i > 0 ) ? " / " + component : component ;
2018-08-20 18:38:18 +02:00
2021-08-31 10:48:45 +02:00
if ( ! vbox_per_path [ root_vbox ] . has ( acc_path ) ) {
// If the section does not exists, create it.
EditorInspectorSection * section = memnew ( EditorInspectorSection ) ;
current_vbox - > add_child ( section ) ;
sections . push_back ( section ) ;
2018-05-15 22:12:35 +02:00
2021-08-31 10:48:45 +02:00
if ( capitalize_paths ) {
component = component . capitalize ( ) ;
2018-05-15 22:12:35 +02:00
}
2018-07-19 00:37:17 +02:00
2021-08-31 10:48:45 +02:00
Color c = sscolor ;
c . a / = level ;
section - > setup ( acc_path , component , object , c , use_folding ) ;
vbox_per_path [ root_vbox ] [ acc_path ] = section - > get_vbox ( ) ;
}
current_vbox = vbox_per_path [ root_vbox ] [ acc_path ] ;
level = ( MIN ( level + 1 , 4 ) ) ;
}
// If we did not find a section to add the property to, add it to the category vbox instead (the category vbox handles margins correctly).
if ( current_vbox = = main_vbox ) {
current_vbox = category_vbox ;
}
// Check if the property is an array counter, if so create a dedicated array editor for the array.
if ( p . usage & PROPERTY_USAGE_ARRAY ) {
EditorInspectorArray * editor_inspector_array = nullptr ;
StringName array_element_prefix ;
Color c = sscolor ;
c . a / = level ;
if ( p . type = = Variant : : NIL ) {
// Setup the array to use a method to create/move/delete elements.
array_element_prefix = p . class_name ;
editor_inspector_array = memnew ( EditorInspectorArray ) ;
String array_label = ( path . find ( " / " ) ! = - 1 ) ? path . substr ( path . rfind ( " / " ) + 1 ) : path ;
array_label = property_label_string . capitalize ( ) ;
int page = per_array_page . has ( array_element_prefix ) ? per_array_page [ array_element_prefix ] : 0 ;
editor_inspector_array - > setup_with_move_element_function ( object , array_label , array_element_prefix , page , c , use_folding ) ;
editor_inspector_array - > connect ( " page_change_request " , callable_mp ( this , & EditorInspector : : _page_change_request ) , varray ( array_element_prefix ) ) ;
editor_inspector_array - > set_undo_redo ( undo_redo ) ;
} else if ( p . type = = Variant : : INT ) {
// Setup the array to use the count property and built-in functions to create/move/delete elements.
Vector < String > class_name_components = String ( p . class_name ) . split ( " , " ) ;
if ( class_name_components . size ( ) = = 2 ) {
array_element_prefix = class_name_components [ 1 ] ;
editor_inspector_array = memnew ( EditorInspectorArray ) ;
int page = per_array_page . has ( array_element_prefix ) ? per_array_page [ array_element_prefix ] : 0 ;
editor_inspector_array - > setup_with_count_property ( object , class_name_components [ 0 ] , p . name , array_element_prefix , page , c , use_folding ) ;
editor_inspector_array - > connect ( " page_change_request " , callable_mp ( this , & EditorInspector : : _page_change_request ) , varray ( array_element_prefix ) ) ;
editor_inspector_array - > set_undo_redo ( undo_redo ) ;
2018-07-19 00:37:17 +02:00
}
}
2021-08-31 10:48:45 +02:00
if ( editor_inspector_array ) {
current_vbox - > add_child ( editor_inspector_array ) ;
editor_inspector_array_per_prefix [ array_element_prefix ] = editor_inspector_array ;
}
continue ;
2018-05-15 22:12:35 +02:00
}
2021-08-31 10:48:45 +02:00
// Checkable and checked properties.
2018-05-15 22:12:35 +02:00
bool checkable = false ;
bool checked = false ;
if ( p . usage & PROPERTY_USAGE_CHECKABLE ) {
checkable = true ;
checked = p . usage & PROPERTY_USAGE_CHECKED ;
}
2021-08-16 04:42:24 +02:00
bool property_read_only = ( p . usage & PROPERTY_USAGE_READ_ONLY ) | | read_only ;
2021-08-31 10:48:45 +02:00
// Mark properties that would require an editor restart (mostly when editing editor settings).
2018-07-19 23:58:15 +02:00
if ( p . usage & PROPERTY_USAGE_RESTART_IF_CHANGED ) {
restart_request_props . insert ( p . name ) ;
}
2018-05-15 22:12:35 +02:00
String doc_hint ;
if ( use_doc_hints ) {
2021-08-31 10:48:45 +02:00
// Build the doc hint, to use as tooltip.
// Get the class name.
2018-05-15 22:12:35 +02:00
StringName classname = object - > get_class_name ( ) ;
2018-07-19 23:58:15 +02:00
if ( object_class ! = String ( ) ) {
classname = object_class ;
}
2021-08-31 10:48:45 +02:00
2018-07-19 23:58:15 +02:00
StringName propname = property_prefix + p . name ;
2018-05-15 22:12:35 +02:00
String descr ;
bool found = false ;
2021-08-31 10:48:45 +02:00
// Search for the property description in the cache.
2020-03-17 07:33:00 +01:00
Map < StringName , Map < StringName , String > > : : Element * E = descr_cache . find ( classname ) ;
2018-05-15 22:12:35 +02:00
if ( E ) {
Map < StringName , String > : : Element * F = E - > get ( ) . find ( propname ) ;
if ( F ) {
found = true ;
descr = F - > get ( ) ;
}
}
if ( ! found ) {
2021-08-31 10:48:45 +02:00
// Build the property description String and add it to the cache.
2020-11-29 04:42:06 +01:00
DocTools * dd = EditorHelp : : get_doc_data ( ) ;
2019-02-12 21:10:08 +01:00
Map < String , DocData : : ClassDoc > : : Element * F = dd - > class_list . find ( classname ) ;
while ( F & & descr = = String ( ) ) {
for ( int i = 0 ; i < F - > get ( ) . properties . size ( ) ; i + + ) {
if ( F - > get ( ) . properties [ i ] . name = = propname . operator String ( ) ) {
2020-05-28 12:02:12 +02:00
descr = DTR ( F - > get ( ) . properties [ i ] . description ) ;
2018-05-15 22:12:35 +02:00
break ;
}
}
2020-01-29 19:56:03 +01:00
Vector < String > slices = propname . operator String ( ) . split ( " / " ) ;
2021-08-06 16:12:43 +02:00
if ( slices . size ( ) = = 2 & & slices [ 0 ] . begins_with ( " theme_override_ " ) ) {
2020-01-29 19:56:03 +01:00
for ( int i = 0 ; i < F - > get ( ) . theme_properties . size ( ) ; i + + ) {
if ( F - > get ( ) . theme_properties [ i ] . name = = slices [ 1 ] ) {
2020-05-28 12:02:12 +02:00
descr = DTR ( F - > get ( ) . theme_properties [ i ] . description ) ;
2020-01-29 19:56:03 +01:00
break ;
}
}
}
2020-12-15 13:04:21 +01:00
if ( ! F - > get ( ) . inherits . is_empty ( ) ) {
2019-02-12 21:10:08 +01:00
F = dd - > class_list . find ( F - > get ( ) . inherits ) ;
2018-05-15 22:12:35 +02:00
} else {
break ;
}
}
descr_cache [ classname ] [ propname ] = descr ;
}
doc_hint = descr ;
}
2021-08-31 10:48:45 +02:00
// Seach for the inspector plugin that will handle the properties. Then add the correct property editor to it.
2021-07-26 17:50:35 +02:00
for ( Ref < EditorInspectorPlugin > & ped : valid_plugins ) {
2020-04-17 04:52:00 +02:00
bool exclusive = ped - > parse_property ( object , p . type , p . name , p . hint , p . hint_string , p . usage , wide_editors ) ;
2018-06-27 00:05:11 +02:00
2021-08-31 10:48:45 +02:00
List < EditorInspectorPlugin : : AddedEditor > editors = ped - > added_editors ; // Make a copy, since plugins may be used again in a sub-inspector.
2018-05-17 23:02:16 +02:00
ped - > added_editors . clear ( ) ;
2021-07-24 15:46:25 +02:00
for ( const EditorInspectorPlugin : : AddedEditor & F : editors ) {
2021-07-16 05:45:57 +02:00
EditorProperty * ep = Object : : cast_to < EditorProperty > ( F . property_editor ) ;
2018-05-15 22:12:35 +02:00
if ( ep ) {
2021-08-31 10:48:45 +02:00
// Set all this before the control gets the ENTER_TREE notification.
2018-05-15 22:12:35 +02:00
ep - > object = object ;
2021-07-16 05:45:57 +02:00
if ( F . properties . size ( ) ) {
if ( F . properties . size ( ) = = 1 ) {
2018-05-15 22:12:35 +02:00
//since it's one, associate:
2021-07-16 05:45:57 +02:00
ep - > property = F . properties [ 0 ] ;
2018-05-15 22:12:35 +02:00
ep - > property_usage = p . usage ;
//and set label?
}
2021-07-16 05:45:57 +02:00
if ( F . label ! = String ( ) ) {
ep - > set_label ( F . label ) ;
2018-05-15 22:12:35 +02:00
} else {
Fix various typos with codespell
Found via `codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,fave,findn,hist,inout,leapyear,lod,nd,numer,ois,ony,paket,seeked,sinc,switchs,te,uint`
2021-07-07 17:17:32 +02:00
// Use the existing one.
2021-08-31 10:48:45 +02:00
ep - > set_label ( property_label_string ) ;
2018-05-15 22:12:35 +02:00
}
2021-07-16 05:45:57 +02:00
for ( int i = 0 ; i < F . properties . size ( ) ; i + + ) {
String prop = F . properties [ i ] ;
2018-05-15 22:12:35 +02:00
if ( ! editor_property_map . has ( prop ) ) {
editor_property_map [ prop ] = List < EditorProperty * > ( ) ;
}
editor_property_map [ prop ] . push_back ( ep ) ;
}
}
2018-08-07 17:19:19 +02:00
ep - > set_draw_red ( draw_red ) ;
ep - > set_use_folding ( use_folding ) ;
ep - > set_checkable ( checkable ) ;
ep - > set_checked ( checked ) ;
ep - > set_keying ( keying ) ;
2021-08-16 04:42:24 +02:00
ep - > set_read_only ( property_read_only ) ;
2020-04-17 04:52:00 +02:00
ep - > set_deletable ( deletable_properties ) ;
2018-08-07 17:19:19 +02:00
}
2021-07-16 05:45:57 +02:00
current_vbox - > add_child ( F . property_editor ) ;
2018-08-07 17:19:19 +02:00
if ( ep ) {
2021-08-31 10:48:45 +02:00
// Eventually, set other properties/signals after the property editor got added to the tree.
2021-08-30 18:59:45 +02:00
bool update_all = ( p . usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED ) ;
ep - > connect ( " property_changed " , callable_mp ( this , & EditorInspector : : _property_changed ) , varray ( update_all ) ) ;
2020-02-21 18:28:45 +01:00
ep - > connect ( " property_keyed " , callable_mp ( this , & EditorInspector : : _property_keyed ) ) ;
2020-04-17 04:52:00 +02:00
ep - > connect ( " property_deleted " , callable_mp ( this , & EditorInspector : : _property_deleted ) , varray ( ) , CONNECT_DEFERRED ) ;
2020-02-21 18:28:45 +01:00
ep - > connect ( " property_keyed_with_value " , callable_mp ( this , & EditorInspector : : _property_keyed_with_value ) ) ;
ep - > connect ( " property_checked " , callable_mp ( this , & EditorInspector : : _property_checked ) ) ;
ep - > connect ( " selected " , callable_mp ( this , & EditorInspector : : _property_selected ) ) ;
ep - > connect ( " multiple_properties_changed " , callable_mp ( this , & EditorInspector : : _multiple_properties_changed ) ) ;
ep - > connect ( " resource_selected " , callable_mp ( this , & EditorInspector : : _resource_selected ) , varray ( ) , CONNECT_DEFERRED ) ;
ep - > connect ( " object_id_selected " , callable_mp ( this , & EditorInspector : : _object_id_selected ) , varray ( ) , CONNECT_DEFERRED ) ;
2018-08-07 17:19:19 +02:00
if ( doc_hint ! = String ( ) ) {
ep - > set_tooltip ( property_prefix + p . name + " :: " + doc_hint ) ;
} else {
ep - > set_tooltip ( property_prefix + p . name ) ;
}
2018-05-15 22:12:35 +02:00
ep - > update_property ( ) ;
ep - > update_reload_status ( ) ;
2021-02-10 21:18:45 +01:00
ep - > update_cache ( ) ;
2018-05-15 22:12:35 +02:00
if ( current_selected & & ep - > property = = current_selected ) {
ep - > select ( current_focusable ) ;
}
}
}
2018-06-27 00:05:11 +02:00
if ( exclusive ) {
2021-08-31 10:48:45 +02:00
// If we know the plugin is exclusive, we don't need to go through other plugins.
2018-06-27 00:05:11 +02:00
break ;
}
2018-05-15 22:12:35 +02:00
}
}
2021-08-31 10:48:45 +02:00
// Get the lists of to add at the end.
2021-07-26 17:50:35 +02:00
for ( Ref < EditorInspectorPlugin > & ped : valid_plugins ) {
2018-05-17 23:02:16 +02:00
ped - > parse_end ( ) ;
_parse_added_editors ( main_vbox , ped ) ;
}
2018-05-15 22:12:35 +02:00
}
2020-05-14 14:29:06 +02:00
2018-05-15 22:12:35 +02:00
void EditorInspector : : update_property ( const String & p_prop ) {
2020-05-14 16:41:43 +02:00
if ( ! editor_property_map . has ( p_prop ) ) {
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2021-07-16 05:45:57 +02:00
for ( EditorProperty * E : editor_property_map [ p_prop ] ) {
E - > update_property ( ) ;
E - > update_reload_status ( ) ;
E - > update_cache ( ) ;
2018-05-15 22:12:35 +02:00
}
}
void EditorInspector : : _clear ( ) {
while ( main_vbox - > get_child_count ( ) ) {
memdelete ( main_vbox - > get_child ( 0 ) ) ;
}
2018-05-17 23:02:16 +02:00
property_selected = StringName ( ) ;
property_focusable = - 1 ;
editor_property_map . clear ( ) ;
sections . clear ( ) ;
pending . clear ( ) ;
2018-07-19 23:58:15 +02:00
restart_request_props . clear ( ) ;
2018-05-15 22:12:35 +02:00
}
2018-05-17 23:02:16 +02:00
Object * EditorInspector : : get_edited_object ( ) {
return object ;
}
2018-05-15 22:12:35 +02:00
2018-05-17 23:02:16 +02:00
void EditorInspector : : edit ( Object * p_object ) {
2020-05-14 16:41:43 +02:00
if ( object = = p_object ) {
2018-05-17 23:02:16 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
if ( object ) {
2018-05-17 23:02:16 +02:00
_clear ( ) ;
2021-02-10 21:18:45 +01:00
object - > disconnect ( " property_list_changed " , callable_mp ( this , & EditorInspector : : _changed_callback ) ) ;
2018-05-15 22:12:35 +02:00
}
2021-08-31 10:48:45 +02:00
per_array_page . clear ( ) ;
2018-05-15 22:12:35 +02:00
object = p_object ;
2018-05-17 23:02:16 +02:00
2018-05-15 22:12:35 +02:00
if ( object ) {
2018-07-19 00:37:17 +02:00
update_scroll_request = 0 ; //reset
if ( scroll_cache . has ( object - > get_instance_id ( ) ) ) { //if exists, set something else
2018-09-13 03:38:39 +02:00
update_scroll_request = scroll_cache [ object - > get_instance_id ( ) ] ; //done this way because wait until full size is accommodated
2018-07-19 00:37:17 +02:00
}
2021-02-10 21:18:45 +01:00
object - > connect ( " property_list_changed " , callable_mp ( this , & EditorInspector : : _changed_callback ) ) ;
2018-05-15 22:12:35 +02:00
update_tree ( ) ;
}
}
void EditorInspector : : set_keying ( bool p_active ) {
2020-05-14 16:41:43 +02:00
if ( keying = = p_active ) {
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
keying = p_active ;
update_tree ( ) ;
}
2020-05-14 14:29:06 +02:00
2018-05-15 22:12:35 +02:00
void EditorInspector : : set_read_only ( bool p_read_only ) {
read_only = p_read_only ;
update_tree ( ) ;
}
bool EditorInspector : : is_capitalize_paths_enabled ( ) const {
return capitalize_paths ;
}
2020-05-14 14:29:06 +02:00
2018-05-15 22:12:35 +02:00
void EditorInspector : : set_enable_capitalize_paths ( bool p_capitalize ) {
capitalize_paths = p_capitalize ;
update_tree ( ) ;
}
void EditorInspector : : set_autoclear ( bool p_enable ) {
autoclear = p_enable ;
}
void EditorInspector : : set_show_categories ( bool p_show ) {
show_categories = p_show ;
update_tree ( ) ;
}
void EditorInspector : : set_use_doc_hints ( bool p_enable ) {
use_doc_hints = p_enable ;
update_tree ( ) ;
}
2020-05-14 14:29:06 +02:00
2019-07-22 12:03:57 +02:00
void EditorInspector : : set_hide_script ( bool p_hide ) {
hide_script = p_hide ;
2018-05-15 22:12:35 +02:00
update_tree ( ) ;
}
2020-05-14 14:29:06 +02:00
2018-05-15 22:12:35 +02:00
void EditorInspector : : set_use_filter ( bool p_use ) {
use_filter = p_use ;
update_tree ( ) ;
}
2020-05-14 14:29:06 +02:00
2018-05-15 22:12:35 +02:00
void EditorInspector : : register_text_enter ( Node * p_line_edit ) {
search_box = Object : : cast_to < LineEdit > ( p_line_edit ) ;
2020-05-14 16:41:43 +02:00
if ( search_box ) {
2020-02-21 18:28:45 +01:00
search_box - > connect ( " text_changed " , callable_mp ( this , & EditorInspector : : _filter_changed ) ) ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
}
void EditorInspector : : _filter_changed ( const String & p_text ) {
2018-06-05 19:12:01 +02:00
_clear ( ) ;
2018-05-15 22:12:35 +02:00
update_tree ( ) ;
}
void EditorInspector : : set_use_folding ( bool p_enable ) {
use_folding = p_enable ;
update_tree ( ) ;
}
2018-06-14 20:36:38 +02:00
bool EditorInspector : : is_using_folding ( ) {
return use_folding ;
}
2018-05-15 22:12:35 +02:00
void EditorInspector : : collapse_all_folding ( ) {
2021-07-16 05:45:57 +02:00
for ( EditorInspectorSection * E : sections ) {
E - > fold ( ) ;
2018-05-15 22:12:35 +02:00
}
2018-05-17 23:02:16 +02:00
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , List < EditorProperty * > > & F : editor_property_map ) {
for ( EditorProperty * E : F . value ) {
2021-07-16 05:45:57 +02:00
E - > collapse_all_folding ( ) ;
2018-05-17 23:02:16 +02:00
}
}
2018-05-15 22:12:35 +02:00
}
void EditorInspector : : expand_all_folding ( ) {
2021-07-16 05:45:57 +02:00
for ( EditorInspectorSection * E : sections ) {
E - > unfold ( ) ;
2018-05-15 22:12:35 +02:00
}
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , List < EditorProperty * > > & F : editor_property_map ) {
for ( EditorProperty * E : F . value ) {
2021-07-16 05:45:57 +02:00
E - > expand_all_folding ( ) ;
2018-05-17 23:02:16 +02:00
}
}
2018-05-15 22:12:35 +02:00
}
void EditorInspector : : set_scroll_offset ( int p_offset ) {
set_v_scroll ( p_offset ) ;
}
int EditorInspector : : get_scroll_offset ( ) const {
return get_v_scroll ( ) ;
}
2020-04-17 04:52:00 +02:00
void EditorInspector : : set_use_wide_editors ( bool p_enable ) {
wide_editors = p_enable ;
}
2021-02-11 22:01:56 +01:00
void EditorInspector : : _update_inspector_bg ( ) {
if ( sub_inspector ) {
int count_subinspectors = 0 ;
Node * n = get_parent ( ) ;
while ( n ) {
EditorInspector * ei = Object : : cast_to < EditorInspector > ( n ) ;
if ( ei & & ei - > sub_inspector ) {
count_subinspectors + + ;
}
n = n - > get_parent ( ) ;
}
count_subinspectors = MIN ( 15 , count_subinspectors ) ;
add_theme_style_override ( " bg " , get_theme_stylebox ( " sub_inspector_bg " + itos ( count_subinspectors ) , " Editor " ) ) ;
} else {
2021-07-17 23:22:52 +02:00
add_theme_style_override ( " bg " , get_theme_stylebox ( SNAME ( " bg " ) , SNAME ( " Tree " ) ) ) ;
2021-02-11 22:01:56 +01:00
}
}
2019-01-25 19:14:56 +01:00
void EditorInspector : : set_sub_inspector ( bool p_enable ) {
sub_inspector = p_enable ;
2020-05-14 16:41:43 +02:00
if ( ! is_inside_tree ( ) ) {
2018-07-19 00:37:17 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-07-19 00:37:17 +02:00
2021-02-11 22:01:56 +01:00
_update_inspector_bg ( ) ;
2018-07-19 00:37:17 +02:00
}
2020-04-17 04:52:00 +02:00
void EditorInspector : : set_use_deletable_properties ( bool p_enabled ) {
deletable_properties = p_enabled ;
}
2021-08-31 10:48:45 +02:00
void EditorInspector : : _page_change_request ( int p_new_page , const StringName & p_array_prefix ) {
int prev_page = per_array_page . has ( p_array_prefix ) ? per_array_page [ p_array_prefix ] : 0 ;
int new_page = MAX ( 0 , p_new_page ) ;
if ( new_page ! = prev_page ) {
per_array_page [ p_array_prefix ] = new_page ;
update_tree_pending = true ;
}
}
2018-05-15 22:12:35 +02:00
void EditorInspector : : _edit_request_change ( Object * p_object , const String & p_property ) {
2020-05-14 16:41:43 +02:00
if ( object ! = p_object ) { //may be undoing/redoing for a non edited object, so ignore
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2020-05-14 16:41:43 +02:00
if ( changing ) {
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2020-05-14 16:41:43 +02:00
if ( p_property = = String ( ) ) {
2018-05-15 22:12:35 +02:00
update_tree_pending = true ;
2020-05-14 16:41:43 +02:00
} else {
2018-05-15 22:12:35 +02:00
pending . insert ( p_property ) ;
}
}
void EditorInspector : : _edit_set ( const String & p_name , const Variant & p_value , bool p_refresh_all , const String & p_changed_field ) {
if ( autoclear & & editor_property_map . has ( p_name ) ) {
2021-07-16 05:45:57 +02:00
for ( EditorProperty * E : editor_property_map [ p_name ] ) {
if ( E - > is_checkable ( ) ) {
E - > set_checked ( true ) ;
2018-05-15 22:12:35 +02:00
}
}
}
2019-02-14 14:19:03 +01:00
if ( ! undo_redo | | bool ( object - > call ( " _dont_undo_redo " ) ) ) {
2018-05-15 22:12:35 +02:00
object - > set ( p_name , p_value ) ;
2020-05-14 16:41:43 +02:00
if ( p_refresh_all ) {
2018-05-15 22:12:35 +02:00
_edit_request_change ( object , " " ) ;
2020-05-14 16:41:43 +02:00
} else {
2018-05-15 22:12:35 +02:00
_edit_request_change ( object , p_name ) ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
emit_signal ( _prop_edited , p_name ) ;
} else if ( Object : : cast_to < MultiNodeEdit > ( object ) ) {
Object : : cast_to < MultiNodeEdit > ( object ) - > set_property_field ( p_name , p_value , p_changed_field ) ;
_edit_request_change ( object , p_name ) ;
emit_signal ( _prop_edited , p_name ) ;
} else {
2021-07-16 16:27:59 +02:00
undo_redo - > create_action ( vformat ( TTR ( " Set %s " ) , p_name ) , UndoRedo : : MERGE_ENDS ) ;
2018-05-15 22:12:35 +02:00
undo_redo - > add_do_property ( object , p_name , p_value ) ;
undo_redo - > add_undo_property ( object , p_name , object - > get ( p_name ) ) ;
2021-08-12 20:26:47 +02:00
PropertyInfo prop_info ;
if ( ClassDB : : get_property_info ( object - > get_class_name ( ) , p_name , & prop_info ) ) {
for ( const String & linked_prop : prop_info . linked_properties ) {
undo_redo - > add_undo_property ( object , linked_prop , object - > get ( linked_prop ) ) ;
}
}
2021-04-28 17:39:57 +02:00
Variant v_undo_redo = ( Object * ) undo_redo ;
Variant v_object = object ;
Variant v_name = p_name ;
for ( int i = 0 ; i < EditorNode : : get_singleton ( ) - > get_editor_data ( ) . get_undo_redo_inspector_hook_callback ( ) . size ( ) ; i + + ) {
const Callable & callback = EditorNode : : get_singleton ( ) - > get_editor_data ( ) . get_undo_redo_inspector_hook_callback ( ) [ i ] ;
const Variant * p_arguments [ ] = { & v_undo_redo , & v_object , & v_name , & p_value } ;
Variant return_value ;
Callable : : CallError call_error ;
callback . call ( p_arguments , 4 , return_value , call_error ) ;
if ( call_error . error ! = Callable : : CallError : : CALL_OK ) {
ERR_PRINT ( " Invalid UndoRedo callback. " ) ;
}
}
2018-05-15 22:12:35 +02:00
if ( p_refresh_all ) {
undo_redo - > add_do_method ( this , " _edit_request_change " , object , " " ) ;
undo_redo - > add_undo_method ( this , " _edit_request_change " , object , " " ) ;
} else {
undo_redo - > add_do_method ( this , " _edit_request_change " , object , p_name ) ;
undo_redo - > add_undo_method ( this , " _edit_request_change " , object , p_name ) ;
}
Resource * r = Object : : cast_to < Resource > ( object ) ;
if ( r ) {
if ( String ( p_name ) = = " resource_local_to_scene " ) {
bool prev = object - > get ( p_name ) ;
bool next = p_value ;
if ( next ) {
undo_redo - > add_do_method ( r , " setup_local_to_scene " ) ;
}
if ( prev ) {
undo_redo - > add_undo_method ( r , " setup_local_to_scene " ) ;
}
}
}
undo_redo - > add_do_method ( this , " emit_signal " , _prop_edited , p_name ) ;
undo_redo - > add_undo_method ( this , " emit_signal " , _prop_edited , p_name ) ;
undo_redo - > commit_action ( ) ;
}
if ( editor_property_map . has ( p_name ) ) {
2021-07-16 05:45:57 +02:00
for ( EditorProperty * E : editor_property_map [ p_name ] ) {
E - > update_reload_status ( ) ;
2018-05-15 22:12:35 +02:00
}
}
}
2021-08-30 18:59:45 +02:00
void EditorInspector : : _property_changed ( const String & p_path , const Variant & p_value , const String & p_name , bool p_changing , bool p_update_all ) {
2018-07-05 01:08:45 +02:00
// The "changing" variable must be true for properties that trigger events as typing occurs,
2020-03-03 10:46:03 +01:00
// like "text_changed" signal. E.g. text property of Label, Button, RichTextLabel, etc.
2020-05-14 16:41:43 +02:00
if ( p_changing ) {
2018-07-05 01:08:45 +02:00
this - > changing + + ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2021-08-30 18:59:45 +02:00
_edit_set ( p_path , p_value , p_update_all , p_name ) ;
2018-07-05 01:08:45 +02:00
2020-05-14 16:41:43 +02:00
if ( p_changing ) {
2018-07-05 01:08:45 +02:00
this - > changing - - ;
2020-05-14 16:41:43 +02:00
}
2018-07-19 23:58:15 +02:00
if ( restart_request_props . has ( p_path ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " restart_requested " ) ) ;
2018-07-19 23:58:15 +02:00
}
2018-05-15 22:12:35 +02:00
}
2021-08-31 10:48:45 +02:00
void EditorInspector : : _multiple_properties_changed ( Vector < String > p_paths , Array p_values , bool p_changing ) {
2018-05-15 22:12:35 +02:00
ERR_FAIL_COND ( p_paths . size ( ) = = 0 | | p_values . size ( ) = = 0 ) ;
ERR_FAIL_COND ( p_paths . size ( ) ! = p_values . size ( ) ) ;
String names ;
for ( int i = 0 ; i < p_paths . size ( ) ; i + + ) {
2020-05-14 16:41:43 +02:00
if ( i > 0 ) {
2018-05-15 22:12:35 +02:00
names + = " , " ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
names + = p_paths [ i ] ;
}
undo_redo - > create_action ( TTR ( " Set Multiple: " ) + " " + names , UndoRedo : : MERGE_ENDS ) ;
for ( int i = 0 ; i < p_paths . size ( ) ; i + + ) {
_edit_set ( p_paths [ i ] , p_values [ i ] , false , " " ) ;
2018-07-19 23:58:15 +02:00
if ( restart_request_props . has ( p_paths [ i ] ) ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " restart_requested " ) ) ;
2018-07-19 23:58:15 +02:00
}
2018-05-15 22:12:35 +02:00
}
2021-08-31 10:48:45 +02:00
if ( p_changing ) {
changing + + ;
}
2018-05-15 22:12:35 +02:00
undo_redo - > commit_action ( ) ;
2021-08-31 10:48:45 +02:00
if ( p_changing ) {
changing - - ;
}
2018-05-15 22:12:35 +02:00
}
2018-11-08 21:46:34 +01:00
void EditorInspector : : _property_keyed ( const String & p_path , bool p_advance ) {
2020-05-14 16:41:43 +02:00
if ( ! object ) {
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " property_keyed " ) , p_path , object - > get ( p_path ) , p_advance ) ; //second param is deprecated
2018-05-15 22:12:35 +02:00
}
2020-04-17 04:52:00 +02:00
void EditorInspector : : _property_deleted ( const String & p_path ) {
2020-05-14 16:41:43 +02:00
if ( ! object ) {
2020-04-17 04:52:00 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2020-04-17 04:52:00 +02:00
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " property_deleted " ) , p_path ) ; //second param is deprecated
2020-04-17 04:52:00 +02:00
}
2018-11-08 21:46:34 +01:00
void EditorInspector : : _property_keyed_with_value ( const String & p_path , const Variant & p_value , bool p_advance ) {
2020-05-14 16:41:43 +02:00
if ( ! object ) {
2018-05-17 23:02:16 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-17 23:02:16 +02:00
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " property_keyed " ) , p_path , p_value , p_advance ) ; //second param is deprecated
2018-05-17 23:02:16 +02:00
}
2018-05-15 22:12:35 +02:00
void EditorInspector : : _property_checked ( const String & p_path , bool p_checked ) {
2020-05-14 16:41:43 +02:00
if ( ! object ) {
2018-05-15 22:12:35 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
//property checked
if ( autoclear ) {
if ( ! p_checked ) {
object - > set ( p_path , Variant ( ) ) ;
} else {
Variant to_create ;
List < PropertyInfo > pinfo ;
object - > get_property_list ( & pinfo ) ;
2021-07-24 15:46:25 +02:00
for ( const PropertyInfo & E : pinfo ) {
2021-07-16 05:45:57 +02:00
if ( E . name = = p_path ) {
2020-02-19 20:27:19 +01:00
Callable : : CallError ce ;
2021-07-16 05:45:57 +02:00
Variant : : construct ( E . type , to_create , nullptr , 0 , ce ) ;
2018-05-15 22:12:35 +02:00
break ;
}
}
object - > set ( p_path , to_create ) ;
}
if ( editor_property_map . has ( p_path ) ) {
2021-07-16 05:45:57 +02:00
for ( EditorProperty * E : editor_property_map [ p_path ] ) {
E - > update_property ( ) ;
E - > update_reload_status ( ) ;
E - > update_cache ( ) ;
2018-05-15 22:12:35 +02:00
}
}
} else {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " property_toggled " ) , p_path , p_checked ) ;
2018-05-15 22:12:35 +02:00
}
}
void EditorInspector : : _property_selected ( const String & p_path , int p_focusable ) {
property_selected = p_path ;
property_focusable = p_focusable ;
//deselect the others
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , List < EditorProperty * > > & F : editor_property_map ) {
if ( F . key = = property_selected ) {
2018-05-15 22:12:35 +02:00
continue ;
2020-05-14 16:41:43 +02:00
}
2021-08-09 22:13:42 +02:00
for ( EditorProperty * E : F . value ) {
2021-07-16 05:45:57 +02:00
if ( E - > is_selected ( ) ) {
E - > deselect ( ) ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
}
}
2018-07-19 23:58:15 +02:00
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " property_selected " ) , p_path ) ;
2018-05-15 22:12:35 +02:00
}
void EditorInspector : : _object_id_selected ( const String & p_path , ObjectID p_id ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " object_id_selected " ) , p_id ) ;
2018-05-15 22:12:35 +02:00
}
void EditorInspector : : _resource_selected ( const String & p_path , RES p_resource ) {
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " resource_selected " ) , p_resource , p_path ) ;
2018-05-15 22:12:35 +02:00
}
void EditorInspector : : _node_removed ( Node * p_node ) {
if ( p_node = = object ) {
2020-04-02 01:20:12 +02:00
edit ( nullptr ) ;
2018-05-15 22:12:35 +02:00
}
}
void EditorInspector : : _notification ( int p_what ) {
2019-04-09 00:18:03 +02:00
if ( p_what = = NOTIFICATION_READY ) {
2020-02-21 18:28:45 +01:00
EditorFeatureProfileManager : : get_singleton ( ) - > connect ( " current_feature_profile_changed " , callable_mp ( this , & EditorInspector : : _feature_profile_changed ) ) ;
2021-02-10 21:18:45 +01:00
set_process ( is_visible_in_tree ( ) ) ;
2021-03-13 19:08:16 +01:00
_update_inspector_bg ( ) ;
2019-04-09 00:18:03 +02:00
}
2018-05-15 22:12:35 +02:00
if ( p_what = = NOTIFICATION_ENTER_TREE ) {
2021-02-11 22:01:56 +01:00
if ( ! sub_inspector ) {
2020-02-21 18:28:45 +01:00
get_tree ( ) - > connect ( " node_removed " , callable_mp ( this , & EditorInspector : : _node_removed ) ) ;
2018-07-19 00:37:17 +02:00
}
2018-05-15 22:12:35 +02:00
}
2019-03-07 16:39:53 +01:00
if ( p_what = = NOTIFICATION_PREDELETE ) {
2020-04-02 01:20:12 +02:00
edit ( nullptr ) ; //just in case
2019-03-07 16:39:53 +01:00
}
2018-05-15 22:12:35 +02:00
if ( p_what = = NOTIFICATION_EXIT_TREE ) {
2019-01-25 19:14:56 +01:00
if ( ! sub_inspector ) {
2020-02-21 18:28:45 +01:00
get_tree ( ) - > disconnect ( " node_removed " , callable_mp ( this , & EditorInspector : : _node_removed ) ) ;
2019-01-25 19:14:56 +01:00
}
2020-04-02 01:20:12 +02:00
edit ( nullptr ) ;
2018-05-15 22:12:35 +02:00
}
2021-02-10 21:18:45 +01:00
if ( p_what = = NOTIFICATION_VISIBILITY_CHANGED ) {
set_process ( is_visible_in_tree ( ) ) ;
}
2018-05-15 22:12:35 +02:00
if ( p_what = = NOTIFICATION_PROCESS ) {
2018-07-19 00:37:17 +02:00
if ( update_scroll_request > = 0 ) {
2021-07-17 23:22:52 +02:00
get_v_scrollbar ( ) - > call_deferred ( SNAME ( " set_value " ) , update_scroll_request ) ;
2018-07-19 00:37:17 +02:00
update_scroll_request = - 1 ;
}
2018-05-15 22:12:35 +02:00
if ( refresh_countdown > 0 ) {
refresh_countdown - = get_process_delta_time ( ) ;
if ( refresh_countdown < = 0 ) {
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , List < EditorProperty * > > & F : editor_property_map ) {
for ( EditorProperty * E : F . value ) {
2021-07-16 05:45:57 +02:00
if ( ! E - > is_cache_valid ( ) ) {
E - > update_property ( ) ;
E - > update_reload_status ( ) ;
E - > update_cache ( ) ;
2021-02-10 21:18:45 +01:00
}
2018-05-15 22:12:35 +02:00
}
}
2021-02-10 21:18:45 +01:00
refresh_countdown = float ( EditorSettings : : get_singleton ( ) - > get ( " docks/property_editor/auto_refresh_interval " ) ) ;
2018-05-15 22:12:35 +02:00
}
}
changing + + ;
if ( update_tree_pending ) {
update_tree ( ) ;
update_tree_pending = false ;
pending . clear ( ) ;
} else {
while ( pending . size ( ) ) {
StringName prop = pending . front ( ) - > get ( ) ;
if ( editor_property_map . has ( prop ) ) {
2021-07-16 05:45:57 +02:00
for ( EditorProperty * E : editor_property_map [ prop ] ) {
E - > update_property ( ) ;
E - > update_reload_status ( ) ;
E - > update_cache ( ) ;
2018-05-15 22:12:35 +02:00
}
}
pending . erase ( pending . front ( ) ) ;
}
}
changing - - ;
}
if ( p_what = = EditorSettings : : NOTIFICATION_EDITOR_SETTINGS_CHANGED ) {
2021-02-11 22:01:56 +01:00
_update_inspector_bg ( ) ;
2018-10-05 20:37:26 +02:00
2018-05-15 22:12:35 +02:00
update_tree ( ) ;
}
}
2021-02-10 21:18:45 +01:00
void EditorInspector : : _changed_callback ( ) {
//this is called when property change is notified via notify_property_list_changed()
if ( object ! = nullptr ) {
_edit_request_change ( object , String ( ) ) ;
}
2018-05-15 22:12:35 +02:00
}
2018-07-19 00:37:17 +02:00
void EditorInspector : : _vscroll_changed ( double p_offset ) {
2020-05-14 16:41:43 +02:00
if ( update_scroll_request > = 0 ) { //waiting, do nothing
2018-07-19 00:37:17 +02:00
return ;
2020-05-14 16:41:43 +02:00
}
2018-07-19 00:37:17 +02:00
if ( object ) {
scroll_cache [ object - > get_instance_id ( ) ] = p_offset ;
}
}
2018-07-20 08:37:10 +02:00
2018-07-19 23:58:15 +02:00
void EditorInspector : : set_property_prefix ( const String & p_prefix ) {
property_prefix = p_prefix ;
}
String EditorInspector : : get_property_prefix ( ) const {
return property_prefix ;
}
void EditorInspector : : set_object_class ( const String & p_class ) {
object_class = p_class ;
}
String EditorInspector : : get_object_class ( ) const {
return object_class ;
}
2018-07-19 00:37:17 +02:00
2019-04-09 00:18:03 +02:00
void EditorInspector : : _feature_profile_changed ( ) {
update_tree ( ) ;
}
2019-09-29 05:27:10 +02:00
void EditorInspector : : _update_script_class_properties ( const Object & p_object , List < PropertyInfo > & r_list ) const {
Ref < Script > script = p_object . get_script ( ) ;
if ( script . is_null ( ) ) {
return ;
}
2021-02-01 02:14:26 +01:00
List < Ref < Script > > classes ;
2019-09-29 05:27:10 +02:00
// NodeC -> NodeB -> NodeA
while ( script . is_valid ( ) ) {
2021-02-01 02:14:26 +01:00
classes . push_front ( script ) ;
2019-09-29 05:27:10 +02:00
script = script - > get_base_script ( ) ;
}
2020-12-15 13:04:21 +01:00
if ( classes . is_empty ( ) ) {
2019-09-29 05:27:10 +02:00
return ;
}
// Script Variables -> to insert: NodeC..B..A -> bottom (insert_here)
2021-04-05 14:02:50 +02:00
List < PropertyInfo > : : Element * script_variables = nullptr ;
List < PropertyInfo > : : Element * bottom = nullptr ;
List < PropertyInfo > : : Element * insert_here = nullptr ;
2019-09-29 05:27:10 +02:00
for ( List < PropertyInfo > : : Element * E = r_list . front ( ) ; E ; E = E - > next ( ) ) {
PropertyInfo & pi = E - > get ( ) ;
if ( pi . name ! = " Script Variables " ) {
continue ;
}
script_variables = E ;
bottom = r_list . insert_after ( script_variables , PropertyInfo ( ) ) ;
insert_here = bottom ;
break ;
}
Set < StringName > added ;
2021-07-26 17:50:35 +02:00
for ( const Ref < Script > & s : classes ) {
2021-02-01 02:14:26 +01:00
String path = s - > get_path ( ) ;
String name = EditorNode : : get_editor_data ( ) . script_class_get_name ( path ) ;
if ( name . is_empty ( ) ) {
if ( ! path . is_empty ( ) & & path . find ( " :: " ) = = - 1 ) {
name = path . get_file ( ) ;
} else {
name = TTR ( " Built-in script " ) ;
}
2020-07-06 00:06:37 +02:00
}
2021-02-01 02:14:26 +01:00
2019-09-29 05:27:10 +02:00
List < PropertyInfo > props ;
s - > get_script_property_list ( & props ) ;
// Script Variables -> NodeA -> bottom (insert_here)
List < PropertyInfo > : : Element * category = r_list . insert_before ( insert_here , PropertyInfo ( Variant : : NIL , name , PROPERTY_HINT_NONE , path , PROPERTY_USAGE_CATEGORY ) ) ;
// Script Variables -> NodeA -> A props... -> bottom (insert_here)
for ( List < PropertyInfo > : : Element * P = props . front ( ) ; P ; P = P - > next ( ) ) {
PropertyInfo & pi = P - > get ( ) ;
if ( added . has ( pi . name ) ) {
continue ;
}
added . insert ( pi . name ) ;
r_list . insert_before ( insert_here , pi ) ;
}
// Script Variables -> NodeA (insert_here) -> A props... -> bottom
insert_here = category ;
}
// NodeC -> C props... -> NodeB..C..
2021-08-27 15:32:28 +02:00
if ( script_variables ) {
r_list . erase ( script_variables ) ;
List < PropertyInfo > : : Element * to_delete = bottom - > next ( ) ;
while ( to_delete & & ! ( to_delete - > get ( ) . usage & PROPERTY_USAGE_CATEGORY ) ) {
r_list . erase ( to_delete ) ;
to_delete = bottom - > next ( ) ;
}
r_list . erase ( bottom ) ;
2019-09-29 05:27:10 +02:00
}
}
2021-02-17 17:44:49 +01:00
void EditorInspector : : set_restrict_to_basic_settings ( bool p_restrict ) {
restrict_to_basic = p_restrict ;
update_tree ( ) ;
}
2020-06-08 15:25:52 +02:00
void EditorInspector : : set_property_clipboard ( const Variant & p_value ) {
property_clipboard = p_value ;
}
Variant EditorInspector : : get_property_clipboard ( ) const {
return property_clipboard ;
}
2018-05-15 22:12:35 +02:00
void EditorInspector : : _bind_methods ( ) {
ClassDB : : bind_method ( " _edit_request_change " , & EditorInspector : : _edit_request_change ) ;
2018-07-19 00:37:17 +02:00
2018-07-19 23:58:15 +02:00
ADD_SIGNAL ( MethodInfo ( " property_selected " , PropertyInfo ( Variant : : STRING , " property " ) ) ) ;
2018-05-15 22:12:35 +02:00
ADD_SIGNAL ( MethodInfo ( " property_keyed " , PropertyInfo ( Variant : : STRING , " property " ) ) ) ;
2020-04-17 04:52:00 +02:00
ADD_SIGNAL ( MethodInfo ( " property_deleted " , PropertyInfo ( Variant : : STRING , " property " ) ) ) ;
2018-05-15 22:12:35 +02:00
ADD_SIGNAL ( MethodInfo ( " resource_selected " , PropertyInfo ( Variant : : OBJECT , " res " ) , PropertyInfo ( Variant : : STRING , " prop " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " object_id_selected " , PropertyInfo ( Variant : : INT , " id " ) ) ) ;
2018-07-19 23:58:15 +02:00
ADD_SIGNAL ( MethodInfo ( " property_edited " , PropertyInfo ( Variant : : STRING , " property " ) ) ) ;
2018-10-30 17:11:54 +01:00
ADD_SIGNAL ( MethodInfo ( " property_toggled " , PropertyInfo ( Variant : : STRING , " property " ) , PropertyInfo ( Variant : : BOOL , " checked " ) ) ) ;
2018-07-19 23:58:15 +02:00
ADD_SIGNAL ( MethodInfo ( " restart_requested " ) ) ;
2018-05-15 22:12:35 +02:00
}
EditorInspector : : EditorInspector ( ) {
2020-04-02 01:20:12 +02:00
object = nullptr ;
undo_redo = nullptr ;
2018-05-15 22:12:35 +02:00
main_vbox = memnew ( VBoxContainer ) ;
main_vbox - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
2020-03-12 13:37:40 +01:00
main_vbox - > add_theme_constant_override ( " separation " , 0 ) ;
2018-05-15 22:12:35 +02:00
add_child ( main_vbox ) ;
2018-05-17 23:02:16 +02:00
set_enable_h_scroll ( false ) ;
set_enable_v_scroll ( true ) ;
2018-05-15 22:12:35 +02:00
2020-04-17 04:52:00 +02:00
wide_editors = false ;
2018-05-15 22:12:35 +02:00
show_categories = false ;
2019-07-22 12:03:57 +02:00
hide_script = true ;
2018-05-15 22:12:35 +02:00
use_doc_hints = false ;
2018-08-20 22:07:02 +02:00
capitalize_paths = true ;
2018-05-15 22:12:35 +02:00
use_filter = false ;
autoclear = false ;
changing = 0 ;
use_folding = false ;
update_all_pending = false ;
update_tree_pending = false ;
read_only = false ;
2020-04-02 01:20:12 +02:00
search_box = nullptr ;
2018-05-15 22:12:35 +02:00
keying = false ;
_prop_edited = " property_edited " ;
2021-02-10 21:18:45 +01:00
set_process ( false ) ;
2018-05-15 22:12:35 +02:00
property_focusable = - 1 ;
2019-01-25 19:14:56 +01:00
sub_inspector = false ;
2020-04-17 04:52:00 +02:00
deletable_properties = false ;
2020-06-08 15:25:52 +02:00
property_clipboard = Variant ( ) ;
2018-07-19 00:37:17 +02:00
2020-02-21 18:28:45 +01:00
get_v_scrollbar ( ) - > connect ( " value_changed " , callable_mp ( this , & EditorInspector : : _vscroll_changed ) ) ;
2018-07-19 00:37:17 +02:00
update_scroll_request = - 1 ;
2021-02-10 21:18:45 +01:00
if ( EditorSettings : : get_singleton ( ) ) {
refresh_countdown = float ( EditorSettings : : get_singleton ( ) - > get ( " docks/property_editor/auto_refresh_interval " ) ) ;
} else {
//used when class is created by the docgen to dump default values of everything bindable, editorsettings may not be created
refresh_countdown = 0.33 ;
}
2020-06-08 15:25:52 +02:00
ED_SHORTCUT ( " property_editor/copy_property " , TTR ( " Copy Property " ) , KEY_MASK_CMD | KEY_C ) ;
ED_SHORTCUT ( " property_editor/paste_property " , TTR ( " Paste Property " ) , KEY_MASK_CMD | KEY_V ) ;
ED_SHORTCUT ( " property_editor/copy_property_path " , TTR ( " Copy Property Path " ) , KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_C ) ;
2018-05-15 22:12:35 +02:00
}