2018-05-16 19:19:33 +02:00
/**************************************************************************/
/* editor_inspector.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-05-15 22:12:35 +02:00
# include "editor_inspector.h"
2024-05-24 18:18:24 +02:00
# include "editor_inspector.compat.inc"
2020-03-05 15:33:01 +01:00
2020-06-08 15:25:52 +02:00
# include "core/os/keyboard.h"
2020-11-29 04:42:06 +01:00
# include "editor/doc_tools.h"
2022-02-12 02:46:22 +01:00
# include "editor/editor_feature_profile.h"
# include "editor/editor_node.h"
2022-03-03 11:25:11 +01:00
# include "editor/editor_property_name_processor.h"
2022-02-12 02:46:22 +01:00
# include "editor/editor_settings.h"
2023-08-13 02:33:39 +02:00
# include "editor/editor_string_names.h"
2022-08-29 12:10:32 +02:00
# include "editor/editor_undo_redo_manager.h"
2023-06-26 19:18:27 +02:00
# include "editor/gui/editor_validation_panel.h"
2022-11-19 12:45:49 +01:00
# include "editor/inspector_dock.h"
2024-01-15 13:14:55 +01:00
# include "editor/multi_node_edit.h"
2022-03-28 22:37:01 +02:00
# include "editor/plugins/script_editor_plugin.h"
2024-01-15 13:14:55 +01:00
# include "editor/themes/editor_scale.h"
2022-11-23 00:14:08 +01:00
# include "editor/themes/editor_theme_manager.h"
2024-04-11 10:21:44 +02:00
# include "scene/gui/margin_container.h"
2022-11-19 12:45:49 +01:00
# include "scene/gui/spin_box.h"
# include "scene/gui/texture_rect.h"
2021-09-14 13:05:54 +02:00
# include "scene/property_utils.h"
2018-05-15 22:12:35 +02:00
# include "scene/resources/packed_scene.h"
2023-07-14 22:35:39 +02:00
# include "scene/resources/style_box_flat.h"
2024-05-12 21:35:57 +02:00
# include "scene/scene_string_names.h"
2018-05-15 22:12:35 +02:00
2023-05-17 17:22:26 +02:00
bool EditorInspector : : _property_path_matches ( const String & p_property_path , const String & p_filter , EditorPropertyNameProcessor : : Style p_style ) {
2024-05-06 10:26:10 +02:00
if ( p_property_path . containsn ( p_filter ) ) {
2022-03-17 18:16:25 +01:00
return true ;
}
2023-05-17 17:22:26 +02:00
const Vector < String > prop_sections = p_property_path . split ( " / " ) ;
for ( int i = 0 ; i < prop_sections . size ( ) ; i + + ) {
2023-10-05 15:12:10 +02:00
if ( p_filter . is_subsequence_ofn ( EditorPropertyNameProcessor : : get_singleton ( ) - > process_name ( prop_sections [ i ] , p_style , p_property_path ) ) ) {
2022-03-17 18:16:25 +01:00
return true ;
}
}
return false ;
}
2018-05-15 22:12:35 +02:00
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 " ) ) ;
2024-03-05 13:27:33 +01:00
ms . height = label . is_empty ( ) ? 0 : font - > get_height ( font_size ) + 4 * EDSCALE ;
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 + + ) {
2024-05-06 12:38:51 +02:00
Control * c = as_sortable_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
}
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 ( ) ;
2024-03-03 12:49:08 +01:00
ms = ms . max ( minsize ) ;
2018-05-15 22:12:35 +02:00
}
if ( keying ) {
2023-08-13 02:33:39 +02:00
Ref < Texture2D > key = get_editor_theme_icon ( SNAME ( " Key " ) ) ;
2024-06-12 16:02:24 +02:00
ms . width + = key - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2018-05-15 22:12:35 +02:00
}
2020-04-17 04:52:00 +02:00
if ( deletable ) {
2023-08-13 02:33:39 +02:00
Ref < Texture2D > key = get_editor_theme_icon ( SNAME ( " Close " ) ) ;
2024-06-12 16:02:24 +02:00
ms . width + = key - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , 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 " ) ) ;
2024-06-12 16:02:24 +02:00
ms . width + = check - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , 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 ( ) ) {
2024-03-05 13:27:33 +01:00
ms . height + = label . is_empty ( ) ? 0 : get_theme_constant ( SNAME ( " v_separation " ) ) ;
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 ;
2022-03-09 14:58:40 +01:00
emit_signalp ( 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 ) {
2022-02-16 00:52:32 +01:00
switch ( p_what ) {
case NOTIFICATION_SORT_CHILDREN : {
Size2 size = get_size ( ) ;
Rect2 rect ;
Rect2 bottom_rect ;
right_child_rect = Rect2 ( ) ;
bottom_child_rect = Rect2 ( ) ;
{
int child_room = size . width * ( 1.0 - split_ratio ) ;
Ref < Font > font = get_theme_font ( SNAME ( " font " ) , SNAME ( " Tree " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " font_size " ) , SNAME ( " Tree " ) ) ;
2024-03-05 13:27:33 +01:00
int height = label . is_empty ( ) ? 0 : font - > get_height ( font_size ) + 4 * EDSCALE ;
2022-02-16 00:52:32 +01:00
bool no_children = true ;
//compute room needed
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2024-05-06 12:38:51 +02:00
Control * c = as_sortable_control ( get_child ( i ) ) ;
2022-02-16 00:52:32 +01:00
if ( ! c ) {
continue ;
}
if ( c = = bottom_editor ) {
continue ;
}
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
Size2 minsize = c - > get_combined_minimum_size ( ) ;
child_room = MAX ( child_room , minsize . width ) ;
height = MAX ( height , minsize . height ) ;
no_children = false ;
}
2018-07-19 00:37:17 +02:00
2022-02-16 00:52:32 +01: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 ) ) ;
if ( is_layout_rtl ( ) ) {
rect = Rect2 ( 1 , 0 , child_room , height ) ;
} else {
rect = Rect2 ( size . width - child_room , 0 , child_room , height ) ;
}
}
if ( bottom_editor ) {
2024-03-05 13:27:33 +01:00
int v_offset = label . is_empty ( ) ? 0 : get_theme_constant ( SNAME ( " v_separation " ) ) ;
bottom_rect = Rect2 ( 0 , rect . size . height + v_offset , size . width , bottom_editor - > get_combined_minimum_size ( ) . height ) ;
2022-02-16 00:52:32 +01:00
}
if ( keying ) {
Ref < Texture2D > key ;
if ( use_keying_next ( ) ) {
2023-08-13 02:33:39 +02:00
key = get_editor_theme_icon ( SNAME ( " KeyNext " ) ) ;
2022-02-16 00:52:32 +01:00
} else {
2023-08-13 02:33:39 +02:00
key = get_editor_theme_icon ( SNAME ( " Key " ) ) ;
2022-02-16 00:52:32 +01:00
}
2024-06-12 16:02:24 +02:00
rect . size . x - = key - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2022-02-16 00:52:32 +01:00
if ( is_layout_rtl ( ) ) {
2024-06-12 16:02:24 +02:00
rect . position . x + = key - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2022-02-16 00:52:32 +01:00
}
if ( no_children ) {
text_size - = key - > get_width ( ) + 4 * EDSCALE ;
}
}
if ( deletable ) {
Ref < Texture2D > close ;
2023-08-13 02:33:39 +02:00
close = get_editor_theme_icon ( SNAME ( " Close " ) ) ;
2022-02-16 00:52:32 +01:00
2024-06-12 16:02:24 +02:00
rect . size . x - = close - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2022-02-16 00:52:32 +01:00
if ( is_layout_rtl ( ) ) {
2024-06-12 16:02:24 +02:00
rect . position . x + = close - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2022-02-16 00:52:32 +01:00
}
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
if ( no_children ) {
text_size - = close - > get_width ( ) + 4 * EDSCALE ;
}
}
2024-06-12 16:02:24 +02:00
// Account for the space needed on the outer side
// when any of the icons are visible.
if ( keying | | deletable ) {
int separation = get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
rect . size . x - = separation ;
if ( is_layout_rtl ( ) ) {
rect . position . x + = separation ;
}
}
2022-02-16 00:52:32 +01:00
}
//set children
2018-05-15 22:12:35 +02:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2024-05-06 12:38:51 +02:00
Control * c = as_sortable_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
}
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
2022-02-16 00:52:32 +01:00
fit_child_in_rect ( c , rect ) ;
right_child_rect = rect ;
2018-08-07 17:19:19 +02:00
}
2018-05-17 23:02:16 +02:00
if ( bottom_editor ) {
2022-02-16 00:52:32 +01:00
fit_child_in_rect ( bottom_editor , bottom_rect ) ;
bottom_child_rect = bottom_rect ;
2018-05-17 23:02:16 +02:00
}
2018-05-15 22:12:35 +02:00
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ; //need to redraw text
2022-02-16 00:52:32 +01:00
} break ;
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
case NOTIFICATION_DRAW : {
Ref < Font > font = get_theme_font ( SNAME ( " font " ) , SNAME ( " Tree " ) ) ;
int font_size = get_theme_font_size ( SNAME ( " font_size " ) , SNAME ( " Tree " ) ) ;
bool rtl = is_layout_rtl ( ) ;
2018-11-30 17:00:04 +01:00
2022-02-16 00:52:32 +01:00
Size2 size = get_size ( ) ;
if ( bottom_editor ) {
2022-04-01 01:55:51 +02:00
size . height = bottom_editor - > get_offset ( SIDE_TOP ) - get_theme_constant ( SNAME ( " v_separation " ) ) ;
2022-02-16 00:52:32 +01:00
} else if ( label_reference ) {
size . height = label_reference - > get_size ( ) . height ;
2018-11-30 17:00:04 +01:00
}
2020-04-17 04:52:00 +02:00
2024-03-05 13:27:33 +01:00
// Only draw the label if it's not empty.
if ( label . is_empty ( ) ) {
size . height = 0 ;
} else {
Ref < StyleBox > sb = get_theme_stylebox ( selected ? SNAME ( " bg_selected " ) : SNAME ( " bg " ) ) ;
draw_style_box ( sb , Rect2 ( Vector2 ( ) , size ) ) ;
}
2020-04-17 04:52:00 +02:00
2022-04-01 01:55:51 +02:00
Ref < StyleBox > bg_stylebox = get_theme_stylebox ( SNAME ( " child_bg " ) ) ;
2022-02-16 00:52:32 +01:00
if ( draw_top_bg & & right_child_rect ! = Rect2 ( ) ) {
2022-04-01 01:55:51 +02:00
draw_style_box ( bg_stylebox , right_child_rect ) ;
2020-04-17 04:52:00 +02:00
}
2022-02-16 00:52:32 +01:00
if ( bottom_child_rect ! = Rect2 ( ) ) {
2022-04-01 01:55:51 +02:00
draw_style_box ( bg_stylebox , bottom_child_rect ) ;
2020-05-14 16:41:43 +02:00
}
2022-02-16 00:52:32 +01:00
Color color ;
2023-03-09 09:41:52 +01:00
if ( draw_warning | | draw_prop_warning ) {
2022-02-16 00:52:32 +01:00
color = get_theme_color ( is_read_only ( ) ? SNAME ( " readonly_warning_color " ) : SNAME ( " warning_color " ) ) ;
} else {
color = get_theme_color ( is_read_only ( ) ? SNAME ( " readonly_color " ) : SNAME ( " property_color " ) ) ;
2020-05-14 16:41:43 +02:00
}
2022-02-16 00:52:32 +01:00
if ( label . contains ( " . " ) ) {
// FIXME: Move this to the project settings editor, as this is only used
// for project settings feature tag overrides.
color . a = 0.5 ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
int ofs = get_theme_constant ( SNAME ( " font_offset " ) ) ;
int text_limit = text_size - ofs ;
2018-07-19 00:37:17 +02:00
2022-02-16 00:52:32 +01:00
if ( checkable ) {
Ref < Texture2D > checkbox ;
if ( checked ) {
2023-08-13 02:33:39 +02:00
checkbox = get_editor_theme_icon ( SNAME ( " GuiChecked " ) ) ;
2022-02-16 00:52:32 +01:00
} else {
2023-08-13 02:33:39 +02:00
checkbox = get_editor_theme_icon ( SNAME ( " GuiUnchecked " ) ) ;
2022-02-16 00:52:32 +01:00
}
2019-04-23 03:33:53 +02:00
2022-02-16 00:52:32 +01:00
Color color2 ( 1 , 1 , 1 ) ;
if ( check_hover ) {
color2 . r * = 1.2 ;
color2 . g * = 1.2 ;
color2 . b * = 1.2 ;
}
check_rect = Rect2 ( ofs , ( ( size . height - checkbox - > get_height ( ) ) / 2 ) , checkbox - > get_width ( ) , checkbox - > get_height ( ) ) ;
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 ) ;
}
2024-06-12 16:02:24 +02:00
int check_ofs = checkbox - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2022-02-16 00:52:32 +01:00
ofs + = check_ofs ;
text_limit - = check_ofs ;
2020-05-14 16:41:43 +02:00
} else {
2022-02-16 00:52:32 +01:00
check_rect = Rect2 ( ) ;
2020-05-14 16:41:43 +02:00
}
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
if ( can_revert & & ! is_read_only ( ) ) {
2023-08-13 02:33:39 +02:00
Ref < Texture2D > reload_icon = get_editor_theme_icon ( SNAME ( " ReloadSmall " ) ) ;
2024-06-12 16:02:24 +02:00
text_limit - = reload_icon - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2022-02-16 00:52:32 +01:00
revert_rect = Rect2 ( ofs + text_limit , ( size . height - reload_icon - > get_height ( ) ) / 2 , reload_icon - > get_width ( ) , reload_icon - > get_height ( ) ) ;
Color color2 ( 1 , 1 , 1 ) ;
if ( revert_hover ) {
color2 . r * = 1.2 ;
color2 . g * = 1.2 ;
color2 . b * = 1.2 ;
}
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 ) ;
}
2020-09-03 13:22:16 +02:00
} else {
2022-02-16 00:52:32 +01:00
revert_rect = Rect2 ( ) ;
2020-09-03 13:22:16 +02:00
}
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
if ( ! pin_hidden & & pinned ) {
2023-08-13 02:33:39 +02:00
Ref < Texture2D > pinned_icon = get_editor_theme_icon ( SNAME ( " Pin " ) ) ;
2024-06-12 16:02:24 +02:00
int margin_w = get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2022-02-16 00:52:32 +01:00
int total_icon_w = margin_w + pinned_icon - > get_width ( ) ;
2022-05-09 11:47:10 +02:00
int text_w = font - > get_string_size ( label , rtl ? HORIZONTAL_ALIGNMENT_RIGHT : HORIZONTAL_ALIGNMENT_LEFT , text_limit - total_icon_w , font_size ) . x ;
2022-02-16 00:52:32 +01:00
int y = ( size . height - pinned_icon - > get_height ( ) ) / 2 ;
if ( rtl ) {
draw_texture ( pinned_icon , Vector2 ( size . width - ofs - text_w - total_icon_w , y ) , color ) ;
} else {
draw_texture ( pinned_icon , Vector2 ( ofs + text_w + margin_w , y ) , color ) ;
}
text_limit - = total_icon_w ;
2020-09-03 13:22:16 +02:00
}
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
int v_ofs = ( size . height - font - > get_height ( font_size ) ) / 2 ;
2021-10-26 21:12:25 +02:00
if ( rtl ) {
2022-02-16 00:52:32 +01:00
draw_string ( font , Point2 ( size . width - ofs - text_limit , v_ofs + font - > get_ascent ( font_size ) ) , label , HORIZONTAL_ALIGNMENT_RIGHT , text_limit , font_size , color ) ;
2021-10-26 21:12:25 +02:00
} else {
2022-02-16 00:52:32 +01:00
draw_string ( font , Point2 ( ofs , v_ofs + font - > get_ascent ( font_size ) ) , label , HORIZONTAL_ALIGNMENT_LEFT , text_limit , font_size , color ) ;
2021-10-26 21:12:25 +02:00
}
2022-08-11 11:59:27 +02:00
ofs = size . width ;
2022-02-16 00:52:32 +01:00
if ( keying ) {
Ref < Texture2D > key ;
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
if ( use_keying_next ( ) ) {
2023-08-13 02:33:39 +02:00
key = get_editor_theme_icon ( SNAME ( " KeyNext " ) ) ;
2022-02-16 00:52:32 +01:00
} else {
2023-08-13 02:33:39 +02:00
key = get_editor_theme_icon ( SNAME ( " Key " ) ) ;
2022-02-16 00:52:32 +01:00
}
2018-05-15 22:12:35 +02:00
2024-06-12 16:02:24 +02:00
ofs - = key - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
Color color2 ( 1 , 1 , 1 ) ;
if ( keying_hover ) {
color2 . r * = 1.2 ;
color2 . g * = 1.2 ;
color2 . b * = 1.2 ;
}
keying_rect = Rect2 ( ofs , ( ( size . height - key - > get_height ( ) ) / 2 ) , key - > get_width ( ) , key - > get_height ( ) ) ;
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
2020-09-03 13:22:16 +02:00
} else {
2022-02-16 00:52:32 +01:00
keying_rect = Rect2 ( ) ;
2020-09-03 13:22:16 +02:00
}
2022-02-16 00:52:32 +01:00
if ( deletable ) {
Ref < Texture2D > close ;
2020-04-17 04:52:00 +02:00
2023-08-13 02:33:39 +02:00
close = get_editor_theme_icon ( SNAME ( " Close " ) ) ;
2020-04-17 04:52:00 +02:00
2024-06-12 16:02:24 +02:00
ofs - = close - > get_width ( ) + get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2020-04-17 04:52:00 +02:00
2022-02-16 00:52:32 +01: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 ( ) ) ;
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-09-03 13:22:16 +02:00
} else {
2022-02-16 00:52:32 +01:00
delete_rect = Rect2 ( ) ;
2020-09-03 13:22:16 +02:00
}
2022-02-16 00:52:32 +01:00
} break ;
2024-05-12 21:35:57 +02:00
case NOTIFICATION_ENTER_TREE : {
if ( has_borders ) {
get_parent ( ) - > connect ( SceneStringName ( theme_changed ) , callable_mp ( this , & EditorProperty : : _update_property_bg ) ) ;
_update_property_bg ( ) ;
}
} break ;
case NOTIFICATION_EXIT_TREE : {
if ( has_borders ) {
get_parent ( ) - > disconnect ( SceneStringName ( theme_changed ) , callable_mp ( this , & EditorProperty : : _update_property_bg ) ) ;
}
}
2018-05-15 22:12:35 +02:00
}
}
void EditorProperty : : set_label ( const String & p_label ) {
label = p_label ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
String EditorProperty : : get_label ( ) const {
return label ;
}
Object * EditorProperty : : get_edited_object ( ) {
return object ;
}
2022-06-19 00:42:02 +02:00
StringName EditorProperty : : get_edited_property ( ) const {
2018-05-15 22:12:35 +02:00
return property ;
}
2023-02-15 18:03:12 +01:00
EditorInspector * EditorProperty : : get_parent_inspector ( ) const {
Node * parent = get_parent ( ) ;
while ( parent ) {
EditorInspector * ei = Object : : cast_to < EditorInspector > ( parent ) ;
if ( ei ) {
return ei ;
}
parent = parent - > get_parent ( ) ;
}
2024-02-20 14:53:16 +01:00
return nullptr ;
2023-02-15 18:03:12 +01:00
}
2022-03-28 22:37:01 +02:00
void EditorProperty : : set_doc_path ( const String & p_doc_path ) {
doc_path = p_doc_path ;
}
2024-01-26 20:42:20 +01:00
void EditorProperty : : set_internal ( bool p_internal ) {
internal = p_internal ;
}
2018-05-15 22:12:35 +02:00
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 ;
2022-09-19 01:21:55 +02:00
if ( GDVIRTUAL_CALL ( _set_read_only , p_read_only ) ) {
return ;
}
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 ;
}
2021-12-11 14:03:48 +01:00
Variant EditorPropertyRevert : : get_property_revert_value ( Object * p_object , const StringName & p_property , bool * r_is_valid ) {
2022-08-12 20:43:14 +02:00
if ( p_object - > property_can_revert ( p_property ) ) {
2021-12-11 14:03:48 +01:00
if ( r_is_valid ) {
* r_is_valid = true ;
}
2022-08-12 20:43:14 +02:00
return p_object - > property_get_revert ( p_property ) ;
2021-02-21 03:03:07 +01:00
}
2018-05-15 22:12:35 +02:00
2021-12-11 14:03:48 +01:00
return PropertyUtils : : get_property_default_value ( p_object , p_property , r_is_valid ) ;
2021-02-21 03:03:07 +01:00
}
2022-06-19 00:42:02 +02:00
bool EditorPropertyRevert : : can_property_revert ( Object * p_object , const StringName & p_property , const Variant * p_custom_current_value ) {
2021-12-11 14:03:48 +01:00
bool is_valid_revert = false ;
Variant revert_value = EditorPropertyRevert : : get_property_revert_value ( p_object , p_property , & is_valid_revert ) ;
if ( ! is_valid_revert ) {
2021-02-21 03:03:07 +01:00
return false ;
}
2022-06-19 00:42:02 +02:00
Variant current_value = p_custom_current_value ? * p_custom_current_value : p_object - > get ( p_property ) ;
2024-05-19 00:17:34 +02:00
return PropertyUtils : : is_property_value_different ( p_object , current_value , revert_value ) ;
2018-11-25 14:46:26 +01:00
}
2022-06-19 00:42:02 +02:00
StringName EditorProperty : : _get_revert_property ( ) const {
return property ;
}
2024-04-25 23:16:18 +02:00
void EditorProperty : : _update_property_bg ( ) {
// This function is to be called on EditorPropertyResource, EditorPropertyArray, and EditorPropertyDictionary.
// Behavior is undetermined on any other EditorProperty.
if ( ! is_inside_tree ( ) ) {
return ;
}
begin_bulk_theme_override ( ) ;
if ( bottom_editor ) {
ColorationMode nested_color_mode = ( ColorationMode ) ( int ) EDITOR_GET ( " interface/inspector/nested_color_mode " ) ;
bool delimitate_all_container_and_resources = EDITOR_GET ( " interface/inspector/delimitate_all_container_and_resources " ) ;
int count_subinspectors = 0 ;
if ( is_colored ( nested_color_mode ) ) {
Node * n = this ;
while ( n ) {
EditorProperty * ep = Object : : cast_to < EditorProperty > ( n ) ;
if ( ep & & ep - > is_colored ( nested_color_mode ) ) {
count_subinspectors + + ;
}
n = n - > get_parent ( ) ;
}
count_subinspectors = MIN ( 16 , count_subinspectors ) ;
}
add_theme_style_override ( SNAME ( " DictionaryAddItem " ) , get_theme_stylebox ( " DictionaryAddItem " + itos ( count_subinspectors ) , EditorStringName ( EditorStyles ) ) ) ;
add_theme_constant_override ( " v_separation " , 0 ) ;
if ( delimitate_all_container_and_resources | | is_colored ( nested_color_mode ) ) {
add_theme_style_override ( " bg_selected " , get_theme_stylebox ( " sub_inspector_property_bg " + itos ( count_subinspectors ) , EditorStringName ( EditorStyles ) ) ) ;
add_theme_style_override ( " bg " , get_theme_stylebox ( " sub_inspector_property_bg " + itos ( count_subinspectors ) , EditorStringName ( EditorStyles ) ) ) ;
add_theme_color_override ( " property_color " , get_theme_color ( SNAME ( " sub_inspector_property_color " ) , EditorStringName ( EditorStyles ) ) ) ;
2024-05-14 15:50:53 +02:00
bottom_editor - > add_theme_style_override ( SceneStringName ( panel ) , get_theme_stylebox ( " sub_inspector_bg " + itos ( count_subinspectors ) , EditorStringName ( EditorStyles ) ) ) ;
2024-04-25 23:16:18 +02:00
} else {
2024-05-14 15:50:53 +02:00
bottom_editor - > add_theme_style_override ( SceneStringName ( panel ) , get_theme_stylebox ( " sub_inspector_bg_no_border " , EditorStringName ( EditorStyles ) ) ) ;
2024-04-25 23:16:18 +02:00
}
} else {
remove_theme_style_override ( " bg_selected " ) ;
remove_theme_style_override ( " bg " ) ;
remove_theme_color_override ( " property_color " ) ;
}
end_bulk_theme_override ( ) ;
queue_redraw ( ) ;
}
2022-05-01 03:24:45 +02:00
void EditorProperty : : update_editor_property_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
2021-10-26 21:12:25 +02:00
bool new_pinned = false ;
if ( can_pin ) {
Node * node = Object : : cast_to < Node > ( object ) ;
CRASH_COND ( ! node ) ;
new_pinned = node - > is_property_pinned ( property ) ;
}
2022-05-01 03:24:45 +02:00
2023-03-09 09:41:52 +01:00
bool new_warning = false ;
if ( object - > has_method ( " _get_property_warning " ) ) {
new_warning = ! String ( object - > call ( " _get_property_warning " , property ) ) . is_empty ( ) ;
}
2022-06-19 00:42:02 +02:00
Variant current = object - > get ( _get_revert_property ( ) ) ;
bool new_can_revert = EditorPropertyRevert : : can_property_revert ( object , property , & current ) & & ! is_read_only ( ) ;
2018-11-25 14:46:26 +01:00
2022-05-01 03:24:45 +02:00
bool new_checked = checked ;
if ( checkable ) { // for properties like theme overrides.
bool valid = false ;
Variant value = object - > get ( property , & valid ) ;
if ( valid ) {
new_checked = value . get_type ( ) ! = Variant : : NIL ;
}
}
2023-03-09 09:41:52 +01:00
if ( new_can_revert ! = can_revert | | new_pinned ! = pinned | | new_checked ! = checked | | new_warning ! = draw_prop_warning ) {
2022-07-30 15:12:51 +02:00
if ( new_can_revert ! = can_revert ) {
emit_signal ( SNAME ( " property_can_revert_changed " ) , property , new_can_revert ) ;
}
2023-03-09 09:41:52 +01:00
draw_prop_warning = new_warning ;
2021-09-14 13:05:54 +02:00
can_revert = new_can_revert ;
2021-10-26 21:12:25 +02:00
pinned = new_pinned ;
2022-05-01 03:24:45 +02:00
checked = new_checked ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
}
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 ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
queue_sort ( ) ;
}
bool EditorProperty : : is_checkable ( ) const {
return checkable ;
}
void EditorProperty : : set_checked ( bool p_checked ) {
checked = p_checked ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
bool EditorProperty : : is_checked ( ) const {
return checked ;
}
2021-09-30 17:08:04 +02:00
void EditorProperty : : set_draw_warning ( bool p_draw_warning ) {
draw_warning = p_draw_warning ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
void EditorProperty : : set_keying ( bool p_keying ) {
keying = p_keying ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
queue_sort ( ) ;
}
2020-04-17 04:52:00 +02:00
void EditorProperty : : set_deletable ( bool p_deletable ) {
deletable = p_deletable ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-04-17 04:52:00 +02:00
queue_sort ( ) ;
}
bool EditorProperty : : is_deletable ( ) const {
return deletable ;
}
2018-05-15 22:12:35 +02:00
bool EditorProperty : : is_keying ( ) const {
return keying ;
}
2021-09-30 17:08:04 +02:00
bool EditorProperty : : is_draw_warning ( ) const {
return draw_warning ;
2018-05-15 22:12:35 +02:00
}
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 ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
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 ) {
2024-05-13 16:56:03 +02:00
p_control - > connect ( SceneStringName ( focus_entered ) , callable_mp ( this , & EditorProperty : : _focusable_focused ) . bind ( focusables . size ( ) ) ) ;
2018-05-15 22:12:35 +02:00
focusables . push_back ( p_control ) ;
}
2024-02-14 14:14:21 +01:00
void EditorProperty : : grab_focus ( int p_focusable ) {
if ( focusables . is_empty ( ) ) {
return ;
}
if ( p_focusable > = 0 ) {
ERR_FAIL_INDEX ( p_focusable , focusables . size ( ) ) ;
focusables [ p_focusable ] - > grab_focus ( ) ;
} else {
focusables [ 0 ] - > grab_focus ( ) ;
}
}
2018-05-15 22:12:35 +02:00
void EditorProperty : : select ( int p_focusable ) {
bool already_selected = selected ;
2023-05-17 05:56:24 +02:00
if ( ! selectable ) {
return ;
}
2018-05-15 22:12:35 +02:00
if ( p_focusable > = 0 ) {
ERR_FAIL_INDEX ( p_focusable , focusables . size ( ) ) ;
focusables [ p_focusable ] - > grab_focus ( ) ;
} else {
selected = true ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
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 ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
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 ;
}
2023-01-08 00:55:54 +01:00
bool button_left = me - > get_button_mask ( ) . has_flag ( MouseButtonMask : : 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 ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
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 ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2020-04-17 04:52:00 +02:00
}
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 ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
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 ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
}
Ref < InputEventMouseButton > mb = p_event ;
2021-08-13 23:31:57 +02:00
if ( mb . is_valid ( ) & & mb - > is_pressed ( ) & & mb - > get_button_index ( ) = = MouseButton : : 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 ;
}
2023-05-17 05:56:24 +02:00
select ( ) ;
2018-05-15 22:12:35 +02:00
2020-09-03 13:22:16 +02:00
if ( keying_rect . has_point ( mpos ) ) {
2022-10-23 19:03:47 +02:00
accept_event ( ) ;
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 + + ;
2022-01-04 05:05:39 +01:00
if ( new_coords . x > = int64_t ( object - > get ( " hframes " ) ) ) {
2019-10-22 19:01:23 +02:00
new_coords . x = 0 ;
new_coords . y + + ;
}
2022-01-04 05:05:39 +01:00
if ( new_coords . x < int64_t ( object - > get ( " hframes " ) ) & & new_coords . y < int64_t ( object - > get ( " vframes " ) ) ) {
2023-12-18 15:46:56 +01:00
callable_mp ( this , & EditorProperty : : emit_changed ) . call_deferred ( property , new_coords , " " , false ) ;
2022-01-04 05:05:39 +01:00
}
2019-10-22 19:01:23 +02:00
} else {
2022-01-04 05:05:39 +01:00
if ( int64_t ( object - > get ( property ) ) + 1 < ( int64_t ( object - > get ( " hframes " ) ) * int64_t ( object - > get ( " vframes " ) ) ) ) {
2023-12-18 15:46:56 +01:00
callable_mp ( this , & EditorProperty : : emit_changed ) . call_deferred ( property , object - > get ( property ) . operator int64_t ( ) + 1 , " " , false ) ;
2022-01-04 05:05:39 +01:00
}
2019-10-22 19:01:23 +02:00
}
2023-12-18 15:46:56 +01:00
callable_mp ( this , & EditorProperty : : update_property ) . call_deferred ( ) ;
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 ) ) {
2022-10-23 19:03:47 +02:00
accept_event ( ) ;
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 ) ) {
2022-10-23 19:03:47 +02:00
accept_event ( ) ;
2023-01-14 15:39:21 +01:00
get_viewport ( ) - > gui_release_focus ( ) ;
2021-12-11 14:03:48 +01:00
bool is_valid_revert = false ;
Variant revert_value = EditorPropertyRevert : : get_property_revert_value ( object , property , & is_valid_revert ) ;
ERR_FAIL_COND ( ! is_valid_revert ) ;
2023-02-15 12:40:00 +01:00
emit_changed ( _get_revert_property ( ) , revert_value ) ;
2021-02-21 03:03:07 +01:00
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 ) ) {
2022-10-23 19:03:47 +02:00
accept_event ( ) ;
2018-05-15 22:12:35 +02:00
checked = ! checked ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2021-07-17 23:22:52 +02:00
emit_signal ( SNAME ( " property_checked " ) , property , checked ) ;
2018-05-15 22:12:35 +02:00
}
2021-08-13 23:31:57 +02:00
} else if ( mb . is_valid ( ) & & mb - > is_pressed ( ) & & mb - > get_button_index ( ) = = MouseButton : : RIGHT ) {
2022-10-23 19:03:47 +02:00
accept_event ( ) ;
2021-10-26 21:12:25 +02:00
_update_popup ( ) ;
2020-06-08 15:25:52 +02:00
menu - > set_position ( get_screen_position ( ) + get_local_mouse_position ( ) ) ;
2021-11-20 09:04:57 +01:00
menu - > reset_size ( ) ;
2020-06-08 15:25:52 +02:00
menu - > popup ( ) ;
select ( ) ;
return ;
}
}
2022-01-11 14:59:52 +01:00
void EditorProperty : : shortcut_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 ( ) ) {
2022-09-30 16:44:27 +02:00
if ( ED_IS_SHORTCUT ( " property_editor/copy_value " , p_event ) ) {
menu_option ( MENU_COPY_VALUE ) ;
2021-09-03 14:33:02 +02:00
accept_event ( ) ;
2024-01-26 20:42:20 +01:00
} else if ( ! is_read_only ( ) & & ED_IS_SHORTCUT ( " property_editor/paste_value " , p_event ) ) {
2022-09-30 16:44:27 +02:00
menu_option ( MENU_PASTE_VALUE ) ;
2021-09-03 14:33:02 +02:00
accept_event ( ) ;
2024-01-26 20:42:20 +01:00
} else if ( ! internal & & ED_IS_SHORTCUT ( " property_editor/copy_property_path " , p_event ) ) {
2021-09-03 14:33:02 +02:00
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 ( ) {
static Color c [ 4 ] ;
2023-08-13 02:33:39 +02:00
c [ 0 ] = get_theme_color ( SNAME ( " property_color_x " ) , EditorStringName ( Editor ) ) ;
c [ 1 ] = get_theme_color ( SNAME ( " property_color_y " ) , EditorStringName ( Editor ) ) ;
c [ 2 ] = get_theme_color ( SNAME ( " property_color_z " ) , EditorStringName ( Editor ) ) ;
c [ 3 ] = get_theme_color ( SNAME ( " property_color_w " ) , EditorStringName ( Editor ) ) ;
2021-03-19 06:42:56 +01:00
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 ;
2024-05-12 21:35:57 +02:00
if ( has_borders ) {
_update_property_bg ( ) ;
}
2018-05-17 23:02:16 +02:00
}
2020-05-14 14:29:06 +02:00
2022-06-19 00:42:02 +02:00
Variant EditorProperty : : _get_cache_value ( const StringName & p_prop , bool & r_valid ) const {
return object - > get ( p_prop , & r_valid ) ;
}
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 ;
2022-06-19 00:42:02 +02:00
Variant value = _get_cache_value ( E . key , valid ) ;
2021-08-09 22:13:42 +02:00
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 ;
2022-06-19 00:42:02 +02:00
Variant value = _get_cache_value ( property , valid ) ;
2021-02-10 21:18:45 +01:00
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 ) ;
2022-09-29 11:53:28 +02:00
Label * drag_label = memnew ( Label ) ;
drag_label - > set_text ( property ) ;
set_drag_preview ( drag_label ) ;
2018-05-15 22:12:35 +02:00
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
}
2022-07-30 15:12:51 +02:00
void EditorProperty : : expand_revertable ( ) {
}
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 ;
2021-10-26 21:12:25 +02:00
_update_pin_flags ( ) ;
}
static bool _is_value_potential_override ( Node * p_node , const String & p_property ) {
// Consider a value is potentially overriding another if either of the following is true:
// a) The node is foreign (inheriting or an instance), so the original value may come from another scene.
// b) The node belongs to the scene, but the original value comes from somewhere but the builtin class (i.e., a script).
Node * edited_scene = EditorNode : : get_singleton ( ) - > get_edited_scene ( ) ;
Vector < SceneState : : PackState > states_stack = PropertyUtils : : get_node_states_stack ( p_node , edited_scene ) ;
if ( states_stack . size ( ) ) {
return true ;
} else {
2021-12-11 14:03:48 +01:00
bool is_valid_default = false ;
2021-10-26 21:12:25 +02:00
bool is_class_default = false ;
2021-12-11 14:03:48 +01:00
PropertyUtils : : get_property_default_value ( p_node , p_property , & is_valid_default , & states_stack , false , nullptr , & is_class_default ) ;
2021-10-26 21:12:25 +02:00
return ! is_class_default ;
}
}
void EditorProperty : : _update_pin_flags ( ) {
can_pin = false ;
pin_hidden = true ;
if ( read_only ) {
return ;
}
if ( Node * node = Object : : cast_to < Node > ( object ) ) {
// Avoid errors down the road by ignoring nodes which are not part of a scene
if ( ! node - > get_owner ( ) ) {
bool is_scene_root = false ;
2023-08-11 15:55:47 +02:00
for ( int i = 0 ; i < EditorNode : : get_editor_data ( ) . get_edited_scene_count ( ) ; + + i ) {
if ( EditorNode : : get_editor_data ( ) . get_edited_scene_root ( i ) = = node ) {
2021-10-26 21:12:25 +02:00
is_scene_root = true ;
break ;
}
}
if ( ! is_scene_root ) {
return ;
}
}
if ( ! _is_value_potential_override ( node , property ) ) {
return ;
}
pin_hidden = false ;
{
2022-05-19 17:00:06 +02:00
HashSet < StringName > storable_properties ;
2021-10-26 21:12:25 +02:00
node - > get_storable_properties ( storable_properties ) ;
if ( storable_properties . has ( node - > get_property_store_alias ( property ) ) ) {
can_pin = true ;
}
}
}
2018-05-19 21:09:38 +02:00
}
2022-02-02 14:10:15 +01:00
Control * EditorProperty : : make_custom_tooltip ( const String & p_text ) const {
2024-04-11 10:21:44 +02:00
String custom_warning ;
if ( object - > has_method ( " _get_property_warning " ) ) {
custom_warning = object - > call ( " _get_property_warning " , property ) ;
}
2023-10-10 10:30:36 +02:00
2024-04-11 10:21:44 +02:00
if ( has_doc_tooltip | | ! custom_warning . is_empty ( ) ) {
EditorHelpBit * help_bit = memnew ( EditorHelpBit ) ;
2023-12-06 23:10:32 +01:00
2024-04-11 10:21:44 +02:00
if ( has_doc_tooltip ) {
help_bit - > parse_symbol ( p_text ) ;
2023-09-21 04:54:51 +02:00
2024-04-11 10:21:44 +02:00
const EditorInspector * inspector = get_parent_inspector ( ) ;
if ( inspector ) {
const String custom_description = inspector - > get_custom_property_description ( p_text ) ;
if ( ! custom_description . is_empty ( ) ) {
2024-05-14 09:23:14 +02:00
help_bit - > set_description ( custom_description ) ;
2024-04-11 10:21:44 +02:00
}
2023-10-10 10:30:36 +02:00
}
2023-09-21 04:54:51 +02:00
}
2024-04-11 10:21:44 +02:00
if ( ! custom_warning . is_empty ( ) ) {
2024-05-14 09:23:14 +02:00
String description = " [b][color= " + get_theme_color ( SNAME ( " warning_color " ) ) . to_html ( false ) + " ] " + custom_warning + " [/color][/b] " ;
if ( ! help_bit - > get_description ( ) . is_empty ( ) ) {
description + = " \n " + help_bit - > get_description ( ) ;
}
help_bit - > set_description ( description ) ;
2024-04-11 10:21:44 +02:00
}
EditorHelpBitTooltip : : show_tooltip ( help_bit , const_cast < EditorProperty * > ( this ) ) ;
return memnew ( Control ) ; // Make the standard tooltip invisible.
2023-03-09 09:41:52 +01:00
}
2023-09-21 04:54:51 +02:00
2024-04-11 10:21:44 +02:00
return nullptr ;
2022-02-02 14:10:15 +01:00
}
2020-06-08 15:25:52 +02:00
void EditorProperty : : menu_option ( int p_option ) {
switch ( p_option ) {
2022-09-30 16:44:27 +02:00
case MENU_COPY_VALUE : {
2021-11-17 21:08:55 +01:00
InspectorDock : : get_inspector_singleton ( ) - > set_property_clipboard ( object - > get ( property ) ) ;
2020-06-08 15:25:52 +02:00
} break ;
2022-09-30 16:44:27 +02:00
case MENU_PASTE_VALUE : {
2021-11-17 21:08:55 +01:00
emit_changed ( property , InspectorDock : : get_inspector_singleton ( ) - > get_property_clipboard ( ) ) ;
2020-06-08 15:25:52 +02:00
} break ;
case MENU_COPY_PROPERTY_PATH : {
2022-03-13 14:23:44 +01:00
DisplayServer : : get_singleton ( ) - > clipboard_set ( property_path ) ;
2020-06-08 15:25:52 +02:00
} break ;
2021-10-26 21:12:25 +02:00
case MENU_PIN_VALUE : {
emit_signal ( SNAME ( " property_pinned " ) , property , ! pinned ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2021-10-26 21:12:25 +02:00
} break ;
2022-03-28 22:37:01 +02:00
case MENU_OPEN_DOCUMENTATION : {
ScriptEditor : : get_singleton ( ) - > goto_help ( doc_path ) ;
EditorNode : : get_singleton ( ) - > set_visible_editor ( EditorNode : : EDITOR_SCRIPT ) ;
} break ;
2020-06-08 15:25:52 +02:00
}
}
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 ) ;
2021-09-30 17:08:04 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_draw_warning " , " draw_warning " ) , & EditorProperty : : set_draw_warning ) ;
ClassDB : : bind_method ( D_METHOD ( " is_draw_warning " ) , & EditorProperty : : is_draw_warning ) ;
2018-05-15 22:12:35 +02:00
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 ) ;
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 " ) ;
2021-09-30 17:08:04 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " draw_warning " ) , " set_draw_warning " , " is_draw_warning " ) ;
2018-05-15 22:12:35 +02:00
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 " ) ;
2022-01-28 15:35:25 +01:00
2022-08-30 00:27:43 +02:00
ADD_SIGNAL ( MethodInfo ( " property_changed " , PropertyInfo ( Variant : : STRING_NAME , " property " ) , PropertyInfo ( Variant : : NIL , " value " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NIL_IS_VARIANT ) , PropertyInfo ( Variant : : STRING_NAME , " field " ) , PropertyInfo ( Variant : : BOOL , " changing " ) ) ) ;
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 ) ) ) ;
2021-10-26 21:12:25 +02:00
ADD_SIGNAL ( MethodInfo ( " property_checked " , PropertyInfo ( Variant : : STRING_NAME , " property " ) , PropertyInfo ( Variant : : BOOL , " checked " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " property_pinned " , PropertyInfo ( Variant : : STRING_NAME , " property " ) , PropertyInfo ( Variant : : BOOL , " pinned " ) ) ) ;
2022-07-30 15:12:51 +02:00
ADD_SIGNAL ( MethodInfo ( " property_can_revert_changed " , PropertyInfo ( Variant : : STRING_NAME , " property " ) , PropertyInfo ( Variant : : BOOL , " can_revert " ) ) ) ;
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 )
2022-09-19 01:21:55 +02:00
GDVIRTUAL_BIND ( _set_read_only , " read_only " )
2022-05-01 03:24:45 +02:00
ClassDB : : bind_method ( D_METHOD ( " _update_editor_property_status " ) , & EditorProperty : : update_editor_property_status ) ;
2018-05-15 22:12:35 +02:00
}
EditorProperty : : EditorProperty ( ) {
2020-04-02 01:20:12 +02:00
object = nullptr ;
2018-07-14 23:15:42 +02:00
split_ratio = 0.5 ;
2018-05-15 22:12:35 +02:00
text_size = 0 ;
property_usage = 0 ;
selected_focusable = - 1 ;
2020-04-02 01:20:12 +02:00
label_reference = nullptr ;
bottom_editor = nullptr ;
2020-06-08 15:25:52 +02:00
menu = nullptr ;
2022-01-11 14:59:52 +01:00
set_process_shortcut_input ( true ) ;
2020-06-08 15:25:52 +02:00
}
2021-10-26 21:12:25 +02:00
void EditorProperty : : _update_popup ( ) {
2020-06-08 15:25:52 +02:00
if ( menu ) {
2021-10-26 21:12:25 +02:00
menu - > clear ( ) ;
} else {
menu = memnew ( PopupMenu ) ;
add_child ( menu ) ;
2024-05-14 14:13:31 +02:00
menu - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & EditorProperty : : menu_option ) ) ;
2020-06-08 15:25:52 +02:00
}
2023-08-13 02:33:39 +02:00
menu - > add_icon_shortcut ( get_editor_theme_icon ( SNAME ( " ActionCopy " ) ) , ED_GET_SHORTCUT ( " property_editor/copy_value " ) , MENU_COPY_VALUE ) ;
menu - > add_icon_shortcut ( get_editor_theme_icon ( SNAME ( " ActionPaste " ) ) , ED_GET_SHORTCUT ( " property_editor/paste_value " ) , MENU_PASTE_VALUE ) ;
menu - > add_icon_shortcut ( get_editor_theme_icon ( SNAME ( " CopyNodePath " ) ) , ED_GET_SHORTCUT ( " property_editor/copy_property_path " ) , MENU_COPY_PROPERTY_PATH ) ;
2022-09-30 16:44:27 +02:00
menu - > set_item_disabled ( MENU_PASTE_VALUE , is_read_only ( ) ) ;
2024-01-26 20:42:20 +01:00
menu - > set_item_disabled ( MENU_COPY_PROPERTY_PATH , internal ) ;
2021-10-26 21:12:25 +02:00
if ( ! pin_hidden ) {
menu - > add_separator ( ) ;
if ( can_pin ) {
2023-08-13 02:33:39 +02:00
menu - > add_icon_check_item ( get_editor_theme_icon ( SNAME ( " Pin " ) ) , TTR ( " Pin Value " ) , MENU_PIN_VALUE ) ;
2021-10-26 21:12:25 +02:00
menu - > set_item_checked ( menu - > get_item_index ( MENU_PIN_VALUE ) , pinned ) ;
} else {
2023-08-13 02:33:39 +02:00
menu - > add_icon_check_item ( get_editor_theme_icon ( SNAME ( " Pin " ) ) , vformat ( TTR ( " Pin Value [Disabled because '%s' is editor-only] " ) , property ) , MENU_PIN_VALUE ) ;
2021-10-26 21:12:25 +02:00
menu - > set_item_disabled ( menu - > get_item_index ( MENU_PIN_VALUE ) , true ) ;
}
2022-03-28 22:37:01 +02:00
menu - > set_item_tooltip ( menu - > get_item_index ( MENU_PIN_VALUE ) , TTR ( " Pinning a value forces it to be saved even if it's equal to the default. " ) ) ;
}
if ( ! doc_path . is_empty ( ) ) {
menu - > add_separator ( ) ;
2023-08-13 02:33:39 +02:00
menu - > add_icon_item ( get_editor_theme_icon ( SNAME ( " Help " ) ) , TTR ( " Open Documentation " ) , MENU_OPEN_DOCUMENTATION ) ;
2021-10-26 21:12:25 +02:00
}
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 ) ;
}
2024-05-24 18:18:24 +02:00
void EditorInspectorPlugin : : add_property_editor ( const String & p_for_property , Control * p_prop , bool p_add_to_end , const String & p_label ) {
2018-05-15 22:12:35 +02:00
AddedEditor ae ;
ae . properties . push_back ( p_for_property ) ;
ae . property_editor = p_prop ;
2022-05-24 00:35:01 +02:00
ae . add_to_end = p_add_to_end ;
2024-05-24 18:18:24 +02:00
ae . label = p_label ;
2018-05-15 22:12:35 +02:00
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 ) {
2022-09-29 11:53:28 +02:00
bool success = false ;
2022-10-18 18:47:44 +02:00
GDVIRTUAL_CALL ( _can_handle , p_object , success ) ;
return success ;
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 : : parse_begin ( Object * p_object ) {
2021-11-10 15:49:19 +01:00
GDVIRTUAL_CALL ( _parse_begin , p_object ) ;
2018-05-15 22:12:35 +02:00
}
2018-05-17 23:02:16 +02:00
2021-11-10 15:49:19 +01:00
void EditorInspectorPlugin : : parse_category ( Object * p_object , const String & p_category ) {
GDVIRTUAL_CALL ( _parse_category , p_object , p_category ) ;
}
void EditorInspectorPlugin : : parse_group ( Object * p_object , const String & p_group ) {
GDVIRTUAL_CALL ( _parse_group , p_object , p_group ) ;
2018-05-17 23:02:16 +02:00
}
2023-01-31 19:08:46 +01: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 BitField < PropertyUsageFlags > p_usage , const bool p_wide ) {
2022-09-29 11:53:28 +02:00
bool ret = false ;
2022-10-18 18:47:44 +02:00
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
}
2020-05-14 14:29:06 +02:00
2021-11-10 15:49:19 +01:00
void EditorInspectorPlugin : : parse_end ( Object * p_object ) {
GDVIRTUAL_CALL ( _parse_end , p_object ) ;
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 ) ;
2024-05-24 18:18:24 +02:00
ClassDB : : bind_method ( D_METHOD ( " add_property_editor " , " property " , " editor " , " add_to_end " , " label " ) , & EditorInspectorPlugin : : add_property_editor , DEFVAL ( false ) , DEFVAL ( String ( ) ) ) ;
2018-05-15 22:12:35 +02:00
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 " )
2021-11-10 15:49:19 +01:00
GDVIRTUAL_BIND ( _parse_begin , " object " )
2021-08-22 03:52:44 +02:00
GDVIRTUAL_BIND ( _parse_category , " object " , " category " )
2021-11-10 15:49:19 +01:00
GDVIRTUAL_BIND ( _parse_group , " object " , " group " )
2021-08-22 03:52:44 +02:00
GDVIRTUAL_BIND ( _parse_property , " object " , " type " , " name " , " hint_type " , " hint_string " , " usage_flags " , " wide " ) ;
2021-11-10 15:49:19 +01:00
GDVIRTUAL_BIND ( _parse_end , " object " )
2018-05-15 22:12:35 +02:00
}
////////////////////////////////////////////////
////////////////////////////////////////////////
void EditorInspectorCategory : : _notification ( int p_what ) {
2022-02-16 00:52:32 +01:00
switch ( p_what ) {
2023-08-31 21:20:39 +02:00
case NOTIFICATION_ENTER_TREE :
case NOTIFICATION_THEME_CHANGED : {
2023-08-13 02:33:39 +02:00
menu - > set_item_icon ( menu - > get_item_index ( MENU_OPEN_DOCS ) , get_editor_theme_icon ( SNAME ( " Help " ) ) ) ;
2023-08-31 21:20:39 +02:00
} break ;
2022-02-16 00:52:32 +01:00
case NOTIFICATION_DRAW : {
2022-04-01 01:55:51 +02:00
Ref < StyleBox > sb = get_theme_stylebox ( SNAME ( " bg " ) ) ;
2021-05-27 17:32:30 +02:00
2022-02-16 00:52:32 +01:00
draw_style_box ( sb , Rect2 ( Vector2 ( ) , get_size ( ) ) ) ;
2021-05-27 17:32:30 +02:00
2023-08-13 02:33:39 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " bold " ) , EditorStringName ( EditorFonts ) ) ;
int font_size = get_theme_font_size ( SNAME ( " bold_size " ) , EditorStringName ( EditorFonts ) ) ;
2018-05-15 22:12:35 +02:00
2022-04-14 23:20:28 +02:00
int hs = get_theme_constant ( SNAME ( " h_separation " ) , SNAME ( " Tree " ) ) ;
2023-08-13 02:33:39 +02:00
int icon_size = get_theme_constant ( SNAME ( " class_icon_size " ) , EditorStringName ( Editor ) ) ;
2018-05-15 22:12:35 +02:00
2022-05-09 11:47:10 +02:00
int w = font - > get_string_size ( label , HORIZONTAL_ALIGNMENT_LEFT , - 1 , font_size ) . width ;
2022-02-16 00:52:32 +01:00
if ( icon . is_valid ( ) ) {
2023-03-31 21:17:59 +02:00
w + = hs + icon_size ;
2022-02-16 00:52:32 +01:00
}
2024-05-11 03:51:32 +02:00
w = MIN ( w , get_size ( ) . width - sb - > get_minimum_size ( ) . width ) ;
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
int ofs = ( get_size ( ) . width - w ) / 2 ;
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
if ( icon . is_valid ( ) ) {
2023-03-31 21:17:59 +02:00
Size2 rect_size = Size2 ( icon_size , icon_size ) ;
Point2 rect_pos = Point2 ( ofs , ( get_size ( ) . height - icon_size ) / 2 ) . floor ( ) ;
2024-05-11 03:51:32 +02:00
if ( is_layout_rtl ( ) ) {
rect_pos . x = get_size ( ) . width - rect_pos . x - icon_size ;
}
2023-03-31 21:17:59 +02:00
draw_texture_rect ( icon , Rect2 ( rect_pos , rect_size ) ) ;
ofs + = hs + icon_size ;
2024-05-11 03:51:32 +02:00
w - = hs + icon_size ;
2022-02-16 00:52:32 +01:00
}
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
Color color = get_theme_color ( SNAME ( " font_color " ) , SNAME ( " Tree " ) ) ;
2024-05-11 03:51:32 +02:00
if ( is_layout_rtl ( ) ) {
ofs = get_size ( ) . width - ofs - w ;
}
draw_string ( font , Point2 ( ofs , font - > get_ascent ( font_size ) + ( get_size ( ) . height - font - > get_height ( font_size ) ) / 2 ) . floor ( ) , label , HORIZONTAL_ALIGNMENT_LEFT , w , font_size , color ) ;
2022-02-16 00:52:32 +01:00
} break ;
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 {
2024-04-11 10:21:44 +02:00
// If it's not a doc tooltip, fallback to the default one.
if ( doc_class_name . is_empty ( ) ) {
return nullptr ;
}
EditorHelpBit * help_bit = memnew ( EditorHelpBit ( p_text ) ) ;
EditorHelpBitTooltip : : show_tooltip ( help_bit , const_cast < EditorInspectorCategory * > ( this ) ) ;
return memnew ( Control ) ; // Make the standard tooltip invisible.
2018-07-20 23:14:33 +02:00
}
2018-05-15 22:12:35 +02:00
Size2 EditorInspectorCategory : : get_minimum_size ( ) const {
2023-08-13 02:33:39 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " bold " ) , EditorStringName ( EditorFonts ) ) ;
int font_size = get_theme_font_size ( SNAME ( " bold_size " ) , EditorStringName ( EditorFonts ) ) ;
2018-05-15 22:12:35 +02:00
Size2 ms ;
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 ( ) ) {
2023-09-29 19:49:25 +02:00
int icon_size = get_theme_constant ( SNAME ( " class_icon_size " ) , EditorStringName ( Editor ) ) ;
ms . height = MAX ( icon_size , ms . height ) ;
2018-05-15 22:12:35 +02:00
}
2022-04-14 23:20:28 +02:00
ms . height + = get_theme_constant ( SNAME ( " v_separation " ) , SNAME ( " Tree " ) ) ;
2018-05-15 22:12:35 +02:00
return ms ;
}
2023-08-31 21:20:39 +02:00
void EditorInspectorCategory : : _handle_menu_option ( int p_option ) {
switch ( p_option ) {
case MENU_OPEN_DOCS :
ScriptEditor : : get_singleton ( ) - > goto_help ( " class: " + doc_class_name ) ;
EditorNode : : get_singleton ( ) - > set_visible_editor ( EditorNode : : EDITOR_SCRIPT ) ;
break ;
}
}
void EditorInspectorCategory : : gui_input ( const Ref < InputEvent > & p_event ) {
if ( doc_class_name . is_empty ( ) ) {
return ;
}
const Ref < InputEventMouseButton > & mb_event = p_event ;
if ( mb_event . is_null ( ) | | ! mb_event - > is_pressed ( ) | | mb_event - > get_button_index ( ) ! = MouseButton : : RIGHT ) {
return ;
}
menu - > set_item_disabled ( menu - > get_item_index ( MENU_OPEN_DOCS ) , ! EditorHelp : : get_doc_data ( ) - > class_list . has ( doc_class_name ) ) ;
menu - > set_position ( get_screen_position ( ) + mb_event - > get_position ( ) ) ;
menu - > reset_size ( ) ;
menu - > popup ( ) ;
}
2018-05-15 22:12:35 +02:00
EditorInspectorCategory : : EditorInspectorCategory ( ) {
2023-08-31 21:20:39 +02:00
menu = memnew ( PopupMenu ) ;
2024-05-14 14:13:31 +02:00
menu - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & EditorInspectorCategory : : _handle_menu_option ) ) ;
2023-08-31 21:20:39 +02:00
menu - > add_item ( TTR ( " Open Documentation " ) , MENU_OPEN_DOCS ) ;
add_child ( menu ) ;
2018-05-15 22:12:35 +02:00
}
////////////////////////////////////////////////
////////////////////////////////////////////////
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 ;
}
}
2022-12-18 15:37:08 +01:00
Ref < Texture2D > EditorInspectorSection : : _get_arrow ( ) {
Ref < Texture2D > arrow ;
if ( foldable ) {
if ( object - > editor_is_section_unfolded ( section ) ) {
arrow = get_theme_icon ( SNAME ( " arrow " ) , SNAME ( " Tree " ) ) ;
} else {
if ( is_layout_rtl ( ) ) {
arrow = get_theme_icon ( SNAME ( " arrow_collapsed_mirrored " ) , SNAME ( " Tree " ) ) ;
} else {
arrow = get_theme_icon ( SNAME ( " arrow_collapsed " ) , SNAME ( " Tree " ) ) ;
}
}
}
return arrow ;
}
int EditorInspectorSection : : _get_header_height ( ) {
2023-08-13 02:33:39 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " bold " ) , EditorStringName ( EditorFonts ) ) ;
int font_size = get_theme_font_size ( SNAME ( " bold_size " ) , EditorStringName ( EditorFonts ) ) ;
2022-12-18 15:37:08 +01:00
int header_height = font - > get_height ( font_size ) ;
Ref < Texture2D > arrow = _get_arrow ( ) ;
if ( arrow . is_valid ( ) ) {
header_height = MAX ( header_height , arrow - > get_height ( ) ) ;
}
header_height + = get_theme_constant ( SNAME ( " v_separation " ) , SNAME ( " Tree " ) ) ;
return header_height ;
}
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 : {
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2021-08-31 10:48:45 +02:00
} break ;
2022-02-16 00:52:32 +01:00
2021-08-31 10:48:45 +02:00
case NOTIFICATION_SORT_CHILDREN : {
if ( ! vbox_added ) {
return ;
}
2018-05-15 22:12:35 +02:00
2023-08-13 02:33:39 +02:00
int inspector_margin = get_theme_constant ( SNAME ( " inspector_margin " ) , EditorStringName ( Editor ) ) ;
2021-11-08 21:53:41 +01:00
int section_indent_size = get_theme_constant ( SNAME ( " indent_size " ) , SNAME ( " EditorInspectorSection " ) ) ;
if ( indent_depth > 0 & & section_indent_size > 0 ) {
inspector_margin + = indent_depth * section_indent_size ;
}
Ref < StyleBoxFlat > section_indent_style = get_theme_stylebox ( SNAME ( " indent_box " ) , SNAME ( " EditorInspectorSection " ) ) ;
if ( indent_depth > 0 & & section_indent_style . is_valid ( ) ) {
inspector_margin + = section_indent_style - > get_margin ( SIDE_LEFT ) + section_indent_style - > get_margin ( SIDE_RIGHT ) ;
}
2021-08-31 10:48:45 +02:00
Size2 size = get_size ( ) - Vector2 ( inspector_margin , 0 ) ;
2022-12-18 15:37:08 +01:00
int header_height = _get_header_height ( ) ;
2021-08-31 10:48:45 +02:00
Vector2 offset = Vector2 ( is_layout_rtl ( ) ? 0 : inspector_margin , header_height ) ;
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2024-05-06 12:38:51 +02:00
Control * c = as_sortable_control ( get_child ( i ) ) ;
2021-08-31 10:48:45 +02:00
if ( ! c ) {
continue ;
}
fit_child_in_rect ( c , Rect2 ( offset , size ) ) ;
}
} break ;
2022-02-16 00:52:32 +01:00
2021-08-31 10:48:45 +02:00
case NOTIFICATION_DRAW : {
2021-11-08 21:53:41 +01:00
int section_indent = 0 ;
int section_indent_size = get_theme_constant ( SNAME ( " indent_size " ) , SNAME ( " EditorInspectorSection " ) ) ;
if ( indent_depth > 0 & & section_indent_size > 0 ) {
section_indent = indent_depth * section_indent_size ;
}
Ref < StyleBoxFlat > section_indent_style = get_theme_stylebox ( SNAME ( " indent_box " ) , SNAME ( " EditorInspectorSection " ) ) ;
if ( indent_depth > 0 & & section_indent_style . is_valid ( ) ) {
section_indent + = section_indent_style - > get_margin ( SIDE_LEFT ) + section_indent_style - > get_margin ( SIDE_RIGHT ) ;
}
int header_width = get_size ( ) . width - section_indent ;
int header_offset_x = 0.0 ;
2022-12-18 15:37:08 +01:00
bool rtl = is_layout_rtl ( ) ;
2021-11-08 21:53:41 +01:00
if ( ! rtl ) {
header_offset_x + = section_indent ;
}
// Draw header area.
2022-12-18 15:37:08 +01:00
int header_height = _get_header_height ( ) ;
2021-11-08 21:53:41 +01:00
Rect2 header_rect = Rect2 ( Vector2 ( header_offset_x , 0.0 ) , Vector2 ( header_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 ( ) ) ) {
2021-08-13 23:31:57 +02:00
c = c . lightened ( Input : : get_singleton ( ) - > is_mouse_button_pressed ( MouseButton : : LEFT ) ? - 0.05 : 0.2 ) ;
2021-09-13 18:53:59 +02:00
}
draw_rect ( header_rect , c ) ;
2020-08-09 10:34:04 +02:00
2022-12-18 15:37:08 +01:00
// Draw header title, folding arrow and count of revertable properties.
2022-07-30 15:12:51 +02:00
{
int separation = Math : : round ( 2 * EDSCALE ) ;
2020-08-09 10:34:04 +02:00
2022-07-30 15:12:51 +02:00
int margin_start = section_indent + separation ;
int margin_end = separation ;
// - Arrow.
2022-12-18 15:37:08 +01:00
Ref < Texture2D > arrow = _get_arrow ( ) ;
2022-07-30 15:12:51 +02:00
if ( arrow . is_valid ( ) ) {
Point2 arrow_position ;
if ( rtl ) {
arrow_position . x = get_size ( ) . width - ( margin_start + arrow - > get_width ( ) ) ;
} else {
arrow_position . x = margin_start ;
}
arrow_position . y = ( header_height - arrow - > get_height ( ) ) / 2 ;
draw_texture ( arrow , arrow_position ) ;
margin_start + = arrow - > get_width ( ) ;
}
int available = get_size ( ) . width - ( margin_start + margin_end ) ;
// - Count of revertable properties.
String num_revertable_str ;
int num_revertable_width = 0 ;
2022-12-18 15:37:08 +01:00
bool folded = foldable & & ! object - > editor_is_section_unfolded ( section ) ;
2023-08-13 02:33:39 +02:00
Ref < Font > font = get_theme_font ( SNAME ( " bold " ) , EditorStringName ( EditorFonts ) ) ;
int font_size = get_theme_font_size ( SNAME ( " bold_size " ) , EditorStringName ( EditorFonts ) ) ;
Color font_color = get_theme_color ( SNAME ( " font_color " ) , EditorStringName ( Editor ) ) ;
2022-12-18 15:37:08 +01:00
2022-07-30 15:12:51 +02:00
if ( folded & & revertable_properties . size ( ) ) {
int label_width = font - > get_string_size ( label , HORIZONTAL_ALIGNMENT_LEFT , available , font_size , TextServer : : JUSTIFICATION_KASHIDA | TextServer : : JUSTIFICATION_CONSTRAIN_ELLIPSIS ) . x ;
2023-08-13 02:33:39 +02:00
Ref < Font > light_font = get_theme_font ( SNAME ( " main " ) , EditorStringName ( EditorFonts ) ) ;
int light_font_size = get_theme_font_size ( SNAME ( " main_size " ) , EditorStringName ( EditorFonts ) ) ;
2024-01-15 13:14:55 +01:00
Color light_font_color = get_theme_color ( SNAME ( " font_disabled_color " ) , EditorStringName ( Editor ) ) ;
2022-07-30 15:12:51 +02:00
// Can we fit the long version of the revertable count text?
2023-05-26 15:55:51 +02:00
num_revertable_str = vformat ( TTRN ( " (%d change) " , " (%d changes) " , revertable_properties . size ( ) ) , revertable_properties . size ( ) ) ;
2022-07-30 15:12:51 +02:00
num_revertable_width = light_font - > get_string_size ( num_revertable_str , HORIZONTAL_ALIGNMENT_LEFT , - 1.0f , light_font_size , TextServer : : JUSTIFICATION_NONE ) . x ;
if ( label_width + separation + num_revertable_width > available ) {
// We'll have to use the short version.
num_revertable_str = vformat ( " (%d) " , revertable_properties . size ( ) ) ;
num_revertable_width = light_font - > get_string_size ( num_revertable_str , HORIZONTAL_ALIGNMENT_LEFT , - 1.0f , light_font_size , TextServer : : JUSTIFICATION_NONE ) . x ;
}
Point2 text_offset = Point2 (
margin_end ,
light_font - > get_ascent ( light_font_size ) + ( header_height - light_font - > get_height ( light_font_size ) ) / 2 ) ;
if ( ! rtl ) {
text_offset . x = get_size ( ) . width - ( text_offset . x + num_revertable_width ) ;
}
draw_string ( light_font , text_offset , num_revertable_str , HORIZONTAL_ALIGNMENT_LEFT , - 1.0f , light_font_size , light_font_color , TextServer : : JUSTIFICATION_NONE ) ;
margin_end + = num_revertable_width + separation ;
available - = num_revertable_width + separation ;
}
// - Label.
Point2 text_offset = Point2 (
margin_start ,
font - > get_ascent ( font_size ) + ( header_height - font - > get_height ( font_size ) ) / 2 ) ;
2021-08-31 10:48:45 +02:00
if ( rtl ) {
2022-07-30 15:12:51 +02:00
text_offset . x = margin_end ;
2021-08-31 10:48:45 +02:00
}
2022-07-30 15:12:51 +02:00
HorizontalAlignment text_align = rtl ? HORIZONTAL_ALIGNMENT_RIGHT : HORIZONTAL_ALIGNMENT_LEFT ;
draw_string ( font , text_offset , label , text_align , available , font_size , font_color , TextServer : : JUSTIFICATION_KASHIDA | TextServer : : JUSTIFICATION_CONSTRAIN_ELLIPSIS ) ;
2021-08-31 10:48:45 +02:00
}
2020-08-09 10:34:04 +02:00
2021-11-08 21:53:41 +01:00
// Draw dropping highlight.
2021-08-31 10:48:45 +02:00
if ( dropping & & ! vbox - > is_visible_in_tree ( ) ) {
2023-08-13 02:33:39 +02:00
Color accent_color = get_theme_color ( SNAME ( " accent_color " ) , EditorStringName ( Editor ) ) ;
2021-08-31 10:48:45 +02:00
draw_rect ( Rect2 ( Point2 ( ) , get_size ( ) ) , accent_color , false ) ;
2020-08-09 10:34:04 +02:00
}
2021-11-08 21:53:41 +01:00
// Draw section indentation.
if ( section_indent_style . is_valid ( ) & & section_indent > 0 ) {
Rect2 indent_rect = Rect2 ( Vector2 ( ) , Vector2 ( indent_depth * section_indent_size , get_size ( ) . height ) ) ;
if ( rtl ) {
indent_rect . position . x = get_size ( ) . width - section_indent + section_indent_style - > get_margin ( SIDE_RIGHT ) ;
} else {
indent_rect . position . x = section_indent_style - > get_margin ( SIDE_LEFT ) ;
}
draw_style_box ( section_indent_style , indent_rect ) ;
}
2021-08-31 10:48:45 +02:00
} break ;
2022-02-16 00:52:32 +01:00
2021-08-31 10:48:45 +02:00
case NOTIFICATION_DRAG_BEGIN : {
2022-12-16 15:14:50 +01:00
dropping_for_unfold = true ;
2021-08-31 10:48:45 +02:00
} break ;
2022-02-16 00:52:32 +01:00
2021-08-31 10:48:45 +02:00
case NOTIFICATION_DRAG_END : {
2022-12-16 15:14:50 +01:00
dropping_for_unfold = false ;
2021-08-31 10:48:45 +02:00
} break ;
2022-02-16 00:52:32 +01:00
2021-08-31 10:48:45 +02:00
case NOTIFICATION_MOUSE_ENTER : {
2022-12-16 15:14:50 +01:00
if ( dropping | | dropping_for_unfold ) {
2021-08-31 10:48:45 +02:00
dropping_unfold_timer - > start ( ) ;
}
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
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 : {
2022-12-16 15:14:50 +01:00
if ( dropping | | dropping_for_unfold ) {
2021-08-31 10:48:45 +02:00
dropping_unfold_timer - > stop ( ) ;
}
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
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 + + ) {
2024-05-06 12:38:51 +02:00
Control * c = as_sortable_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
}
2018-05-15 22:12:35 +02:00
Size2 minsize = c - > get_combined_minimum_size ( ) ;
2024-03-03 12:49:08 +01:00
ms = ms . max ( minsize ) ;
2018-05-15 22:12:35 +02:00
}
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 " ) ) ;
2022-04-14 23:20:28 +02:00
ms . height + = font - > get_height ( font_size ) + get_theme_constant ( SNAME ( " v_separation " ) , SNAME ( " Tree " ) ) ;
2023-08-13 02:33:39 +02:00
ms . width + = get_theme_constant ( SNAME ( " inspector_margin " ) , EditorStringName ( Editor ) ) ;
2018-05-15 22:12:35 +02:00
2021-11-08 21:53:41 +01:00
int section_indent_size = get_theme_constant ( SNAME ( " indent_size " ) , SNAME ( " EditorInspectorSection " ) ) ;
if ( indent_depth > 0 & & section_indent_size > 0 ) {
ms . width + = indent_depth * section_indent_size ;
}
Ref < StyleBoxFlat > section_indent_style = get_theme_stylebox ( SNAME ( " indent_box " ) , SNAME ( " EditorInspectorSection " ) ) ;
if ( indent_depth > 0 & & section_indent_style . is_valid ( ) ) {
ms . width + = section_indent_style - > get_margin ( SIDE_LEFT ) + section_indent_style - > get_margin ( SIDE_RIGHT ) ;
}
2018-05-15 22:12:35 +02:00
return ms ;
}
2021-11-08 21:53:41 +01:00
void EditorInspectorSection : : setup ( const String & p_section , const String & p_label , Object * p_object , const Color & p_bg_color , bool p_foldable , int p_indent_depth ) {
2018-05-15 22:12:35 +02:00
section = p_section ;
label = p_label ;
object = p_object ;
bg_color = p_bg_color ;
foldable = p_foldable ;
2021-11-08 21:53:41 +01:00
indent_depth = p_indent_depth ;
2018-05-15 22:12:35 +02:00
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-08-13 23:31:57 +02:00
if ( mb . is_valid ( ) & & mb - > is_pressed ( ) & & mb - > get_button_index ( ) = = MouseButton : : LEFT ) {
2022-12-18 15:37:08 +01:00
if ( object - > editor_is_section_unfolded ( section ) ) {
int header_height = _get_header_height ( ) ;
if ( mb - > get_position ( ) . y > = header_height ) {
return ;
}
2018-11-19 01:52:01 +01:00
}
2022-10-23 19:03:47 +02:00
accept_event ( ) ;
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 ( ) ) {
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
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 ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
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 ( ) ;
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2018-05-15 22:12:35 +02:00
}
2023-05-14 19:40:45 +02:00
void EditorInspectorSection : : set_bg_color ( const Color & p_bg_color ) {
bg_color = p_bg_color ;
queue_redraw ( ) ;
}
2022-07-30 15:12:51 +02:00
bool EditorInspectorSection : : has_revertable_properties ( ) const {
return ! revertable_properties . is_empty ( ) ;
}
void EditorInspectorSection : : property_can_revert_changed ( const String & p_path , bool p_can_revert ) {
bool had_revertable_properties = has_revertable_properties ( ) ;
if ( p_can_revert ) {
revertable_properties . insert ( p_path ) ;
} else {
revertable_properties . erase ( p_path ) ;
}
if ( has_revertable_properties ( ) ! = had_revertable_properties ) {
2022-08-13 23:21:24 +02:00
queue_redraw ( ) ;
2022-07-30 15:12:51 +02:00
}
}
2018-05-15 22:12:35 +02:00
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 ( ) {
vbox = memnew ( VBoxContainer ) ;
2020-08-09 10:34:04 +02:00
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
////////////////////////////////////////////////
////////////////////////////////////////////////
2022-02-06 01:11:15 +01: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 ;
2022-09-29 11:53:28 +02:00
int count_val = object - > get ( count_property , & valid ) ;
2021-08-31 10:48:45 +02:00
ERR_FAIL_COND_V_MSG ( ! valid , 0 , vformat ( " %s is not a valid property to be used as array count. " , count_property ) ) ;
2022-09-29 11:53:28 +02:00
return count_val ;
2021-08-31 10:48:45 +02:00
}
return 0 ;
}
void EditorInspectorArray : : _add_button_pressed ( ) {
_move_element ( - 1 , - 1 ) ;
}
2022-02-06 01:11:15 +01:00
void EditorInspectorArray : : _paginator_page_changed ( int p_page ) {
emit_signal ( " page_change_request " , p_page ) ;
2021-08-31 10:48:45 +02:00
}
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 :
2022-07-09 21:51:34 +02:00
new_size_spin_box - > set_value ( count ) ;
2021-08-31 10:48:45 +02:00
resize_dialog - > get_ok_button ( ) - > set_disabled ( true ) ;
2022-08-13 19:22:07 +02:00
resize_dialog - > popup_centered ( Size2 ( 250 , 0 ) * EDSCALE ) ;
2022-07-09 21:51:34 +02:00
new_size_spin_box - > get_line_edit ( ) - > grab_focus ( ) ;
new_size_spin_box - > get_line_edit ( ) - > select_all ( ) ;
2021-08-31 10:48:45 +02:00
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 ) ) ;
}
2023-08-13 02:33:39 +02:00
Color color = get_theme_color ( SNAME ( " accent_color " ) , EditorStringName ( Editor ) ) ;
2021-08-31 10:48:45 +02:00
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 ( ) ) ;
2023-08-13 02:33:39 +02:00
Ref < StyleBox > style = get_theme_stylebox ( SNAME ( " Focus " ) , EditorStringName ( EditorStyles ) ) ;
2021-08-31 10:48:45 +02:00
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 ( ) ) ;
2022-09-06 17:59:36 +02:00
if ( read_only ) {
return ;
}
2021-08-31 10:48:45 +02:00
Ref < InputEventKey > key_ref = p_event ;
if ( key_ref . is_valid ( ) ) {
const InputEventKey & key = * * key_ref ;
2021-08-13 23:31:57 +02:00
if ( array_elements [ p_index ] . panel - > has_focus ( ) & & key . is_pressed ( ) & & key . get_keycode ( ) = = Key : : KEY_DELETE ) {
2021-08-31 10:48:45 +02:00
_move_element ( begin_array_index + p_index , - 1 ) ;
array_elements [ p_index ] . panel - > accept_event ( ) ;
}
}
Ref < InputEventMouseButton > mb = p_event ;
if ( mb . is_valid ( ) ) {
2022-07-21 01:18:14 +02:00
if ( movable & & mb - > get_button_index ( ) = = MouseButton : : RIGHT ) {
2022-10-23 19:03:47 +02:00
array_elements [ p_index ] . panel - > accept_event ( ) ;
2021-08-31 10:48:45 +02:00
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 ) ;
2021-08-31 17:43:35 +02:00
rmb_popup - > set_position ( get_screen_position ( ) + mb - > get_position ( ) ) ;
2021-11-20 09:04:57 +01:00
rmb_popup - > reset_size ( ) ;
2021-08-31 10:48:45 +02:00
rmb_popup - > popup ( ) ;
}
}
}
void EditorInspectorArray : : _move_element ( int p_element_index , int p_to_pos ) {
String action_name ;
if ( p_element_index < 0 ) {
2023-03-09 07:37:41 +01:00
action_name = vformat ( TTR ( " Add element to property array with prefix %s. " ) , array_element_prefix ) ;
2021-08-31 10:48:45 +02:00
} else if ( p_to_pos < 0 ) {
2023-03-09 07:37:41 +01:00
action_name = vformat ( TTR ( " Remove element %d from property array with prefix %s. " ) , p_element_index , array_element_prefix ) ;
2021-08-31 10:48:45 +02:00
} else {
2023-03-09 07:37:41 +01:00
action_name = vformat ( TTR ( " Move element %d to position %d in property array with prefix %s. " ) , p_element_index , p_to_pos , array_element_prefix ) ;
2021-08-31 10:48:45 +02:00
}
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2021-08-31 10:48:45 +02:00
undo_redo - > create_action ( action_name ) ;
if ( mode = = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION ) {
// Call the function.
2023-08-11 15:55:47 +02:00
Callable move_function = EditorNode : : get_editor_data ( ) . get_move_array_element_function ( object - > get_class_name ( ) ) ;
2021-08-31 10:48:45 +02:00
if ( move_function . is_valid ( ) ) {
2023-07-11 16:18:10 +02:00
move_function . call ( undo_redo , object , array_element_prefix , p_element_index , p_to_pos ) ;
2021-08-31 10:48:45 +02:00
} 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 ) ;
2022-07-21 01:18:14 +02:00
if ( ! swap_method . is_empty ( ) ) {
ERR_FAIL_COND ( ! object - > has_method ( swap_method ) ) ;
2021-08-31 10:48:45 +02:00
2022-07-21 01:18:14 +02:00
// Swap method was provided, use it.
if ( p_element_index < 0 ) {
// Add an element at position
undo_redo - > add_do_property ( object , count_property , count + 1 ) ;
if ( p_to_pos > = 0 ) {
for ( int i = count ; i > p_to_pos ; i - - ) {
undo_redo - > add_do_method ( object , swap_method , i , i - 1 ) ;
}
for ( int i = p_to_pos ; i < count ; i + + ) {
undo_redo - > add_undo_method ( object , swap_method , i , i + 1 ) ;
}
}
undo_redo - > add_undo_property ( object , count_property , count ) ;
} else if ( p_to_pos < 0 ) {
if ( count > 0 ) {
// Remove element at position
undo_redo - > add_undo_property ( object , count_property , count ) ;
List < PropertyInfo > object_property_list ;
object - > get_property_list ( & object_property_list ) ;
for ( int i = p_element_index ; i < count - 1 ; i + + ) {
undo_redo - > add_do_method ( object , swap_method , i , i + 1 ) ;
}
for ( int i = count ; i > p_element_index ; i - - ) {
undo_redo - > add_undo_method ( object , swap_method , i , i - 1 ) ;
}
String erase_prefix = String ( array_element_prefix ) + itos ( p_element_index ) ;
for ( const PropertyInfo & E : object_property_list ) {
if ( E . name . begins_with ( erase_prefix ) ) {
undo_redo - > add_undo_property ( object , E . name , object - > get ( E . name ) ) ;
}
}
2021-08-31 10:48:45 +02:00
2022-07-21 01:18:14 +02:00
undo_redo - > add_do_property ( object , count_property , count - 1 ) ;
}
} else {
if ( p_to_pos > p_element_index ) {
p_to_pos - - ;
}
if ( p_to_pos < p_element_index ) {
for ( int i = p_element_index ; i > p_to_pos ; i - - ) {
undo_redo - > add_do_method ( object , swap_method , i , i - 1 ) ;
}
for ( int i = p_to_pos ; i < p_element_index ; i + + ) {
undo_redo - > add_undo_method ( object , swap_method , i , i + 1 ) ;
}
} else if ( p_to_pos > p_element_index ) {
for ( int i = p_element_index ; i < p_to_pos ; i + + ) {
undo_redo - > add_do_method ( object , swap_method , i , i + 1 ) ;
}
for ( int i = p_to_pos ; i > p_element_index ; i - - ) {
undo_redo - > add_undo_method ( object , swap_method , i , i - 1 ) ;
}
}
}
2021-08-31 10:48:45 +02:00
} else {
2022-07-21 01:18:14 +02:00
// Use standard properties.
List < PropertyInfo > object_property_list ;
object - > get_property_list ( & object_property_list ) ;
2021-08-31 10:48:45 +02:00
2022-07-21 01:18:14 +02:00
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_at ( 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_at ( 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 ] ) ;
}
2021-08-31 10:48:45 +02:00
}
}
}
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 ;
2022-02-06 01:11:15 +01:00
emit_signal ( SNAME ( " page_change_request " ) , added_index / page_length ) ;
2021-08-31 10:48:45 +02:00
count + = 1 ;
} else if ( p_to_pos < 0 ) {
count - = 1 ;
2022-02-06 01:11:15 +01:00
if ( page = = max_page & & ( MAX ( 0 , count - 1 ) / page_length ! = max_page ) ) {
2022-02-06 15:53:53 +01:00
emit_signal ( SNAME ( " page_change_request " ) , max_page - 1 ) ;
2021-08-31 10:48:45 +02:00
}
2023-08-13 00:17:45 +02:00
} else if ( p_to_pos = = begin_array_index - 1 ) {
emit_signal ( SNAME ( " page_change_request " ) , page - 1 ) ;
} else if ( p_to_pos > end_array_index ) {
emit_signal ( SNAME ( " page_change_request " ) , page + 1 ) ;
2021-08-31 10:48:45 +02:00
}
2022-02-06 01:11:15 +01:00
begin_array_index = page * page_length ;
end_array_index = MIN ( count , ( page + 1 ) * page_length ) ;
max_page = MAX ( 0 , count - 1 ) / page_length ;
2021-08-31 10:48:45 +02:00
}
void EditorInspectorArray : : _clear_array ( ) {
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2023-09-12 12:15:44 +02:00
undo_redo - > create_action ( vformat ( TTR ( " Clear Property Array with Prefix %s " ) , array_element_prefix ) ) ;
2021-08-31 10:48:45 +02:00
if ( mode = = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION ) {
for ( int i = count - 1 ; i > = 0 ; i - - ) {
// Call the function.
2023-08-11 15:55:47 +02:00
Callable move_function = EditorNode : : get_editor_data ( ) . get_move_array_element_function ( object - > get_class_name ( ) ) ;
2021-08-31 10:48:45 +02:00
if ( move_function . is_valid ( ) ) {
2023-07-11 16:18:10 +02:00
move_function . call ( undo_redo , object , array_element_prefix , i , - 1 ) ;
2021-08-31 10:48:45 +02:00
} 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.
2022-02-06 15:53:53 +01:00
emit_signal ( SNAME ( " page_change_request " ) , 0 ) ;
2021-08-31 10:48:45 +02:00
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 ;
}
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2023-09-12 12:15:44 +02:00
undo_redo - > create_action ( vformat ( TTR ( " Resize Property Array with Prefix %s " ) , array_element_prefix ) ) ;
2021-08-31 10:48:45 +02:00
if ( p_size > count ) {
if ( mode = = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION ) {
for ( int i = count ; i < p_size ; i + + ) {
// Call the function.
2023-08-11 15:55:47 +02:00
Callable move_function = EditorNode : : get_editor_data ( ) . get_move_array_element_function ( object - > get_class_name ( ) ) ;
2021-08-31 10:48:45 +02:00
if ( move_function . is_valid ( ) ) {
2023-07-11 16:18:10 +02:00
move_function . call ( undo_redo , object , array_element_prefix , - 1 , - 1 ) ;
2021-08-31 10:48:45 +02:00
} 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.
2023-08-11 15:55:47 +02:00
Callable move_function = EditorNode : : get_editor_data ( ) . get_move_array_element_function ( object - > get_class_name ( ) ) ;
2021-08-31 10:48:45 +02:00
if ( move_function . is_valid ( ) ) {
2023-07-11 16:18:10 +02:00
move_function . call ( undo_redo , object , array_element_prefix , i , - 1 ) ;
2021-08-31 10:48:45 +02:00
} 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.
2022-02-06 15:53:53 +01:00
emit_signal ( SNAME ( " page_change_request " ) , 0 ) ;
2021-08-31 10:48:45 +02:00
/*
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 ) {
2024-03-07 17:45:13 +01:00
if ( ! ( pi . usage & PROPERTY_USAGE_EDITOR ) ) {
continue ;
}
2021-08-31 10:48:45 +02:00
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 ( ) ) {
2022-02-04 09:32:20 +01:00
if ( ! is_digit ( str [ to_char_index ] ) ) {
2021-08-31 10:48:45 +02:00
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 {
Fix various typos
Found via ` codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,expct,fave,findn,gird,hist,inout,leapyear,lod,nd,numer,ois,ony,paket,seeked,sinc,switchs,te,uint,varn`
Update editor/import/resource_importer_layered_texture.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update doc/classes/TileSetScenesCollectionSource.xml
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/gui/graph_edit.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/resources/animation.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/resources/animation.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/resources/animation.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/gui/rich_text_label.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Revert previously committed change
2022-01-02 07:03:58 +01:00
WARN_PRINT ( vformat ( " Array element %s has an index too high. Array allocation failed. " , pi . name ) ) ;
2021-08-31 10:48:45 +02:00
}
}
}
}
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 ;
}
2022-07-09 21:51:34 +02:00
void EditorInspectorArray : : _resize_dialog_confirmed ( ) {
if ( int ( new_size_spin_box - > get_value ( ) ) = = count ) {
return ;
2021-08-31 10:48:45 +02:00
}
2022-07-09 21:51:34 +02:00
resize_dialog - > hide ( ) ;
_resize_array ( int ( new_size_spin_box - > get_value ( ) ) ) ;
2021-08-31 10:48:45 +02:00
}
2022-07-09 21:51:34 +02:00
void EditorInspectorArray : : _new_size_spin_box_value_changed ( float p_value ) {
resize_dialog - > get_ok_button ( ) - > set_disabled ( int ( p_value ) = = count ) ;
2021-08-31 10:48:45 +02:00
}
2024-02-15 17:25:58 +01:00
void EditorInspectorArray : : _new_size_spin_box_text_submitted ( const String & p_text ) {
2022-07-09 21:51:34 +02:00
_resize_dialog_confirmed ( ) ;
2021-08-31 10:48:45 +02:00
}
void EditorInspectorArray : : _setup ( ) {
// Setup counts.
count = _get_array_count ( ) ;
2022-02-06 01:11:15 +01:00
begin_array_index = page * page_length ;
end_array_index = MIN ( count , ( page + 1 ) * page_length ) ;
max_page = MAX ( 0 , count - 1 ) / page_length ;
2021-08-31 10:48:45 +02:00
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 ) ;
}
2022-07-21 01:18:14 +02:00
Ref < Font > numbers_font ;
int numbers_min_w = 0 ;
if ( numbered ) {
2023-08-13 02:33:39 +02:00
numbers_font = get_theme_font ( SNAME ( " bold " ) , EditorStringName ( EditorFonts ) ) ;
2022-07-21 01:18:14 +02:00
int digits_found = count ;
String test ;
while ( digits_found ) {
test + = " 8 " ;
digits_found / = 10 ;
}
numbers_min_w = numbers_font - > get_string_size ( test ) . width ;
}
2021-08-31 10:48:45 +02:00
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 ) ;
2023-01-14 03:37:19 +01:00
SET_DRAG_FORWARDING_GCD ( ae . panel , EditorInspectorArray ) ;
2023-09-07 13:04:28 +02:00
int element_position = begin_array_index + i ;
ae . panel - > set_meta ( " index " , element_position ) ;
ae . panel - > set_tooltip_text ( vformat ( TTR ( " Element %d: %s%d* " ) , element_position , array_element_prefix , element_position ) ) ;
2024-05-13 16:56:03 +02:00
ae . panel - > connect ( SceneStringName ( focus_entered ) , callable_mp ( ( CanvasItem * ) ae . panel , & PanelContainer : : queue_redraw ) ) ;
ae . panel - > connect ( SceneStringName ( focus_exited ) , callable_mp ( ( CanvasItem * ) ae . panel , & PanelContainer : : queue_redraw ) ) ;
ae . panel - > connect ( SceneStringName ( draw ) , callable_mp ( this , & EditorInspectorArray : : _panel_draw ) . bind ( i ) ) ;
ae . panel - > connect ( SceneStringName ( gui_input ) , callable_mp ( this , & EditorInspectorArray : : _panel_gui_input ) . bind ( i ) ) ;
2024-05-14 15:50:53 +02:00
ae . panel - > add_theme_style_override ( SceneStringName ( panel ) , i % 2 ? odd_style : even_style ) ;
2021-08-31 10:48:45 +02:00
elements_vbox - > add_child ( ae . panel ) ;
ae . margin = memnew ( MarginContainer ) ;
ae . margin - > set_mouse_filter ( MOUSE_FILTER_PASS ) ;
if ( is_inside_tree ( ) ) {
2023-08-13 02:33:39 +02:00
Size2 min_size = get_theme_stylebox ( SNAME ( " Focus " ) , EditorStringName ( EditorStyles ) ) - > get_minimum_size ( ) ;
2023-10-19 18:05:19 +02:00
ae . margin - > begin_bulk_theme_override ( ) ;
2022-02-08 10:14:58 +01:00
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 ) ;
2023-10-19 18:05:19 +02:00
ae . margin - > end_bulk_theme_override ( ) ;
2021-08-31 10:48:45 +02:00
}
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.
2022-07-21 01:18:14 +02:00
if ( movable ) {
2023-08-13 00:17:45 +02:00
VBoxContainer * move_vbox = memnew ( VBoxContainer ) ;
move_vbox - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
move_vbox - > set_alignment ( BoxContainer : : ALIGNMENT_CENTER ) ;
ae . hbox - > add_child ( move_vbox ) ;
if ( element_position > 0 ) {
ae . move_up = memnew ( Button ) ;
2023-08-13 02:33:39 +02:00
ae . move_up - > set_icon ( get_editor_theme_icon ( SNAME ( " MoveUp " ) ) ) ;
2024-05-14 09:40:21 +02:00
ae . move_up - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorInspectorArray : : _move_element ) . bind ( element_position , element_position - 1 ) ) ;
2023-08-13 00:17:45 +02:00
move_vbox - > add_child ( ae . move_up ) ;
}
2022-07-21 01:18:14 +02:00
ae . move_texture_rect = memnew ( TextureRect ) ;
ae . move_texture_rect - > set_stretch_mode ( TextureRect : : STRETCH_KEEP_CENTERED ) ;
ae . move_texture_rect - > set_default_cursor_shape ( Control : : CURSOR_MOVE ) ;
if ( is_inside_tree ( ) ) {
2023-08-13 02:33:39 +02:00
ae . move_texture_rect - > set_texture ( get_editor_theme_icon ( SNAME ( " TripleBar " ) ) ) ;
2022-07-21 01:18:14 +02:00
}
2023-08-13 00:17:45 +02:00
move_vbox - > add_child ( ae . move_texture_rect ) ;
if ( element_position < _get_array_count ( ) - 1 ) {
ae . move_down = memnew ( Button ) ;
2023-08-13 02:33:39 +02:00
ae . move_down - > set_icon ( get_editor_theme_icon ( SNAME ( " MoveDown " ) ) ) ;
2024-05-14 09:40:21 +02:00
ae . move_down - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorInspectorArray : : _move_element ) . bind ( element_position , element_position + 2 ) ) ;
2023-08-13 00:17:45 +02:00
move_vbox - > add_child ( ae . move_down ) ;
}
2022-07-21 01:18:14 +02:00
}
if ( numbered ) {
ae . number = memnew ( Label ) ;
ae . number - > add_theme_font_override ( " font " , numbers_font ) ;
ae . number - > set_custom_minimum_size ( Size2 ( numbers_min_w , 0 ) ) ;
ae . number - > set_horizontal_alignment ( HORIZONTAL_ALIGNMENT_RIGHT ) ;
ae . number - > set_vertical_alignment ( VERTICAL_ALIGNMENT_CENTER ) ;
2023-09-07 13:04:28 +02:00
ae . number - > set_text ( itos ( element_position ) ) ;
2022-07-21 01:18:14 +02:00
ae . hbox - > add_child ( ae . number ) ;
2021-08-31 10:48:45 +02:00
}
// 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 ) ;
2022-07-21 01:18:14 +02:00
ae . erase = memnew ( Button ) ;
2023-08-13 02:33:39 +02:00
ae . erase - > set_icon ( get_editor_theme_icon ( SNAME ( " Remove " ) ) ) ;
2022-07-21 01:18:14 +02:00
ae . erase - > set_v_size_flags ( SIZE_SHRINK_CENTER ) ;
2024-05-14 09:40:21 +02:00
ae . erase - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorInspectorArray : : _remove_item ) . bind ( element_position ) ) ;
2022-07-21 01:18:14 +02:00
ae . hbox - > add_child ( ae . erase ) ;
2021-08-31 10:48:45 +02:00
}
// Hide/show the add button.
add_button - > set_visible ( page = = max_page ) ;
2022-02-06 01:11:15 +01:00
// Add paginator if there's more than 1 page.
if ( max_page > 0 ) {
EditorPaginator * paginator = memnew ( EditorPaginator ) ;
paginator - > update ( page , max_page ) ;
paginator - > connect ( " page_changed " , callable_mp ( this , & EditorInspectorArray : : _paginator_page_changed ) ) ;
vbox - > add_child ( paginator ) ;
2021-08-31 10:48:45 +02:00
}
}
2022-07-21 01:18:14 +02:00
void EditorInspectorArray : : _remove_item ( int p_index ) {
_move_element ( p_index , - 1 ) ;
}
2021-08-31 10:48:45 +02:00
Variant EditorInspectorArray : : get_drag_data_fw ( const Point2 & p_point , Control * p_from ) {
2022-07-21 01:18:14 +02:00
if ( ! movable ) {
return Variant ( ) ;
}
2021-08-31 10:48:45 +02:00
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 {
2022-09-06 17:59:36 +02:00
if ( ! movable | | read_only ) {
2022-07-21 01:18:14 +02:00
return false ;
}
2021-08-31 10:48:45 +02:00
// First, update drawing.
2022-08-13 23:21:24 +02:00
control_dropping - > queue_redraw ( ) ;
2021-08-31 10:48:45 +02:00
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 ) {
2022-08-29 11:04:31 +02:00
case NOTIFICATION_ENTER_TREE :
2021-08-31 10:48:45 +02:00
case NOTIFICATION_THEME_CHANGED : {
2023-08-13 02:33:39 +02:00
Color color = get_theme_color ( SNAME ( " dark_color_1 " ) , EditorStringName ( Editor ) ) ;
2022-07-23 00:11:03 +02:00
odd_style - > set_bg_color ( color . darkened ( - 0.08 ) ) ;
even_style - > set_bg_color ( color . darkened ( 0.08 ) ) ;
2021-08-31 10:48:45 +02:00
2022-12-29 01:24:45 +01:00
for ( ArrayElement & ae : array_elements ) {
2022-07-21 01:18:14 +02:00
if ( ae . move_texture_rect ) {
2023-08-13 02:33:39 +02:00
ae . move_texture_rect - > set_texture ( get_editor_theme_icon ( SNAME ( " TripleBar " ) ) ) ;
2022-07-21 01:18:14 +02:00
}
2023-08-13 00:17:45 +02:00
if ( ae . move_up ) {
2023-08-13 02:33:39 +02:00
ae . move_up - > set_icon ( get_editor_theme_icon ( SNAME ( " MoveUp " ) ) ) ;
2023-08-13 00:17:45 +02:00
}
if ( ae . move_down ) {
2023-08-13 02:33:39 +02:00
ae . move_down - > set_icon ( get_editor_theme_icon ( SNAME ( " MoveDown " ) ) ) ;
2023-08-13 00:17:45 +02:00
}
2023-08-13 02:33:39 +02:00
Size2 min_size = get_theme_stylebox ( SNAME ( " Focus " ) , EditorStringName ( EditorStyles ) ) - > get_minimum_size ( ) ;
2023-10-19 18:05:19 +02:00
ae . margin - > begin_bulk_theme_override ( ) ;
2022-02-08 10:14:58 +01:00
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 ) ;
2023-10-19 18:05:19 +02:00
ae . margin - > end_bulk_theme_override ( ) ;
2022-07-21 01:18:14 +02:00
if ( ae . erase ) {
2023-08-13 02:33:39 +02:00
ae . erase - > set_icon ( get_editor_theme_icon ( SNAME ( " Remove " ) ) ) ;
2022-07-21 01:18:14 +02:00
}
2021-08-31 10:48:45 +02:00
}
2023-08-13 02:33:39 +02:00
add_button - > set_icon ( get_editor_theme_icon ( SNAME ( " Add " ) ) ) ;
2021-12-06 14:02:34 +01:00
update_minimum_size ( ) ;
2021-08-31 10:48:45 +02:00
} break ;
2022-02-16 00:52:32 +01:00
2021-08-31 10:48:45 +02:00
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 ;
2022-08-13 23:21:24 +02:00
control_dropping - > queue_redraw ( ) ;
2021-08-31 10:48:45 +02:00
}
} break ;
2022-02-16 00:52:32 +01:00
2021-08-31 10:48:45 +02:00
case NOTIFICATION_DRAG_END : {
if ( dropping ) {
dropping = false ;
2022-08-13 23:21:24 +02:00
control_dropping - > queue_redraw ( ) ;
2021-08-31 10:48:45 +02:00
}
} break ;
}
}
void EditorInspectorArray : : _bind_methods ( ) {
ADD_SIGNAL ( MethodInfo ( " page_change_request " ) ) ;
}
2024-02-15 17:25:58 +01:00
void EditorInspectorArray : : setup_with_move_element_function ( Object * p_object , const String & p_label , const StringName & p_array_element_prefix , int p_page , const Color & p_bg_color , bool p_foldable , bool p_movable , bool p_numbered , int p_page_length , const String & p_add_item_text ) {
2021-08-31 10:48:45 +02:00
count_property = " " ;
mode = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION ;
array_element_prefix = p_array_element_prefix ;
page = p_page ;
2022-07-21 01:18:14 +02:00
movable = p_movable ;
page_length = p_page_length ;
numbered = p_numbered ;
2021-08-31 10:48:45 +02:00
2021-11-08 21:53:41 +01:00
EditorInspectorSection : : setup ( String ( p_array_element_prefix ) + " _array " , p_label , p_object , p_bg_color , p_foldable , 0 ) ;
2021-08-31 10:48:45 +02:00
_setup ( ) ;
}
2024-02-15 17:25:58 +01:00
void EditorInspectorArray : : setup_with_count_property ( Object * p_object , const 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 , bool p_movable , bool p_numbered , int p_page_length , const String & p_add_item_text , const String & p_swap_method ) {
2021-08-31 10:48:45 +02:00
count_property = p_count_property ;
mode = MODE_USE_COUNT_PROPERTY ;
array_element_prefix = p_array_element_prefix ;
page = p_page ;
2022-07-21 01:18:14 +02:00
movable = p_movable ;
page_length = p_page_length ;
numbered = p_numbered ;
swap_method = p_swap_method ;
2021-08-31 10:48:45 +02:00
2022-07-21 01:18:14 +02:00
add_button - > set_text ( p_add_item_text ) ;
2021-11-08 21:53:41 +01:00
EditorInspectorSection : : setup ( String ( count_property ) + " _array " , p_label , p_object , p_bg_color , p_foldable , 0 ) ;
2021-08-31 10:48:45 +02:00
_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 ;
}
}
2022-09-06 17:59:36 +02:00
EditorInspectorArray : : EditorInspectorArray ( bool p_read_only ) {
read_only = p_read_only ;
2021-08-31 10:48:45 +02:00
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 ) ;
2024-05-14 14:13:31 +02:00
rmb_popup - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & EditorInspectorArray : : _rmb_popup_id_pressed ) ) ;
2021-08-31 10:48:45 +02:00
add_child ( rmb_popup ) ;
elements_vbox = memnew ( VBoxContainer ) ;
2022-02-08 10:14:58 +01:00
elements_vbox - > add_theme_constant_override ( " separation " , 0 ) ;
2021-08-31 10:48:45 +02:00
vbox - > add_child ( elements_vbox ) ;
2022-05-25 01:38:13 +02:00
add_button = EditorInspector : : create_inspector_action_button ( TTR ( " Add Element " ) ) ;
2024-05-14 09:40:21 +02:00
add_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorInspectorArray : : _add_button_pressed ) ) ;
2022-09-06 17:59:36 +02:00
add_button - > set_disabled ( read_only ) ;
2021-08-31 10:48:45 +02:00
vbox - > add_child ( add_button ) ;
control_dropping = memnew ( Control ) ;
2024-05-13 16:56:03 +02:00
control_dropping - > connect ( SceneStringName ( draw ) , callable_mp ( this , & EditorInspectorArray : : _control_dropping_draw ) ) ;
2021-08-31 10:48:45 +02:00
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 ) ;
2022-07-09 21:51:34 +02:00
new_size_spin_box = memnew ( SpinBox ) ;
new_size_spin_box - > set_max ( 16384 ) ;
new_size_spin_box - > connect ( " value_changed " , callable_mp ( this , & EditorInspectorArray : : _new_size_spin_box_value_changed ) ) ;
new_size_spin_box - > get_line_edit ( ) - > connect ( " text_submitted " , callable_mp ( this , & EditorInspectorArray : : _new_size_spin_box_text_submitted ) ) ;
2022-09-06 17:59:36 +02:00
new_size_spin_box - > set_editable ( ! read_only ) ;
2022-07-09 21:51:34 +02:00
resize_dialog_vbox - > add_margin_child ( TTRC ( " New Size: " ) , new_size_spin_box ) ;
2021-08-31 10:48:45 +02:00
2024-05-13 16:56:03 +02:00
vbox - > connect ( SceneStringName ( visibility_changed ) , callable_mp ( this , & EditorInspectorArray : : _vbox_visibility_changed ) ) ;
2021-08-31 10:48:45 +02:00
}
2018-05-15 22:12:35 +02:00
////////////////////////////////////////////////
////////////////////////////////////////////////
2022-02-06 01:11:15 +01:00
void EditorPaginator : : _first_page_button_pressed ( ) {
emit_signal ( " page_changed " , 0 ) ;
}
void EditorPaginator : : _prev_page_button_pressed ( ) {
emit_signal ( " page_changed " , MAX ( 0 , page - 1 ) ) ;
}
2024-02-15 17:25:58 +01:00
void EditorPaginator : : _page_line_edit_text_submitted ( const String & p_text ) {
2022-02-06 01:11:15 +01:00
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_changed " , new_page ) ;
} else {
page_line_edit - > set_text ( Variant ( page ) ) ;
}
}
void EditorPaginator : : _next_page_button_pressed ( ) {
emit_signal ( " page_changed " , MIN ( max_page , page + 1 ) ) ;
}
void EditorPaginator : : _last_page_button_pressed ( ) {
emit_signal ( " page_changed " , max_page ) ;
}
void EditorPaginator : : update ( int p_page , int p_max_page ) {
page = p_page ;
max_page = p_max_page ;
// 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 ) ) ;
}
void EditorPaginator : : _notification ( int p_what ) {
2022-02-16 00:52:32 +01:00
switch ( p_what ) {
2022-08-29 11:04:31 +02:00
case NOTIFICATION_ENTER_TREE :
2022-02-16 00:52:32 +01:00
case NOTIFICATION_THEME_CHANGED : {
2023-08-13 02:33:39 +02:00
first_page_button - > set_icon ( get_editor_theme_icon ( SNAME ( " PageFirst " ) ) ) ;
prev_page_button - > set_icon ( get_editor_theme_icon ( SNAME ( " PagePrevious " ) ) ) ;
next_page_button - > set_icon ( get_editor_theme_icon ( SNAME ( " PageNext " ) ) ) ;
last_page_button - > set_icon ( get_editor_theme_icon ( SNAME ( " PageLast " ) ) ) ;
2022-02-16 00:52:32 +01:00
} break ;
2022-02-06 01:11:15 +01:00
}
}
void EditorPaginator : : _bind_methods ( ) {
ADD_SIGNAL ( MethodInfo ( " page_changed " , PropertyInfo ( Variant : : INT , " page " ) ) ) ;
}
EditorPaginator : : EditorPaginator ( ) {
set_h_size_flags ( SIZE_EXPAND_FILL ) ;
set_alignment ( ALIGNMENT_CENTER ) ;
first_page_button = memnew ( Button ) ;
first_page_button - > set_flat ( true ) ;
2024-05-14 09:40:21 +02:00
first_page_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorPaginator : : _first_page_button_pressed ) ) ;
2022-02-06 01:11:15 +01:00
add_child ( first_page_button ) ;
prev_page_button = memnew ( Button ) ;
prev_page_button - > set_flat ( true ) ;
2024-05-14 09:40:21 +02:00
prev_page_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorPaginator : : _prev_page_button_pressed ) ) ;
2022-02-06 01:11:15 +01:00
add_child ( prev_page_button ) ;
page_line_edit = memnew ( LineEdit ) ;
page_line_edit - > connect ( " text_submitted " , callable_mp ( this , & EditorPaginator : : _page_line_edit_text_submitted ) ) ;
page_line_edit - > add_theme_constant_override ( " minimum_character_width " , 2 ) ;
add_child ( page_line_edit ) ;
page_count_label = memnew ( Label ) ;
add_child ( page_count_label ) ;
next_page_button = memnew ( Button ) ;
next_page_button - > set_flat ( true ) ;
2024-05-14 09:40:21 +02:00
next_page_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorPaginator : : _next_page_button_pressed ) ) ;
2022-02-06 01:11:15 +01:00
add_child ( next_page_button ) ;
last_page_button = memnew ( Button ) ;
last_page_button - > set_flat ( true ) ;
2024-05-14 09:40:21 +02:00
last_page_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorPaginator : : _last_page_button_pressed ) ) ;
2022-02-06 01:11:15 +01:00
add_child ( last_page_button ) ;
}
////////////////////////////////////////////////
////////////////////////////////////////////////
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 ( ) ) {
2024-04-15 15:18:34 +02:00
for ( List < EditorInspectorPlugin : : AddedEditor > : : Element * E = inspector_plugins [ i ] - > added_editors . front ( ) - > next ( ) ; E ; E = E - > next ( ) ) { //only keep first one
memdelete ( E - > get ( ) . property_editor ) ;
2018-07-14 23:15:42 +02:00
}
2024-04-15 15:18:34 +02:00
EditorProperty * prop = Object : : cast_to < EditorProperty > ( inspector_plugins [ i ] - > added_editors . front ( ) - > get ( ) . property_editor ) ;
2018-07-14 23:15:42 +02:00
if ( prop ) {
inspector_plugins [ i ] - > added_editors . clear ( ) ;
return prop ;
} else {
2024-04-15 15:18:34 +02:00
memdelete ( inspector_plugins [ i ] - > added_editors . front ( ) - > get ( ) . property_editor ) ;
2018-07-14 23:15:42 +02:00
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 ] ;
}
2021-12-06 03:25:01 +01:00
inspector_plugins [ inspector_plugin_count - 1 ] = Ref < EditorInspectorPlugin > ( ) ;
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 ;
}
2022-05-25 01:38:13 +02:00
Button * EditorInspector : : create_inspector_action_button ( const String & p_text ) {
Button * button = memnew ( Button ) ;
button - > set_text ( p_text ) ;
button - > set_theme_type_variation ( SNAME ( " InspectorActionButton " ) ) ;
button - > set_h_size_flags ( SIZE_SHRINK_CENTER ) ;
return button ;
}
2023-02-15 18:03:12 +01:00
bool EditorInspector : : is_main_editor_inspector ( ) const {
return InspectorDock : : get_singleton ( ) & & InspectorDock : : get_inspector_singleton ( ) = = this ;
}
2018-05-15 22:12:35 +02:00
String EditorInspector : : get_selected_path ( ) const {
return property_selected ;
}
2022-07-30 15:12:51 +02:00
void EditorInspector : : _parse_added_editors ( VBoxContainer * current_vbox , EditorInspectorSection * p_section , 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 ;
2022-08-01 11:26:56 +02:00
ep - > connect ( " property_changed " , callable_mp ( this , & EditorInspector : : _property_changed ) . bind ( false ) ) ;
2020-02-21 18:28:45 +01:00
ep - > connect ( " property_keyed " , callable_mp ( this , & EditorInspector : : _property_keyed ) ) ;
2022-07-28 22:56:41 +02:00
ep - > connect ( " property_deleted " , callable_mp ( this , & EditorInspector : : _property_deleted ) , 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 ) ) ;
2021-10-26 21:12:25 +02:00
ep - > connect ( " property_pinned " , callable_mp ( this , & EditorInspector : : _property_pinned ) ) ;
2020-02-21 18:28:45 +01:00
ep - > connect ( " selected " , callable_mp ( this , & EditorInspector : : _property_selected ) ) ;
ep - > connect ( " multiple_properties_changed " , callable_mp ( this , & EditorInspector : : _multiple_properties_changed ) ) ;
2022-07-28 22:56:41 +02:00
ep - > connect ( " resource_selected " , callable_mp ( this , & EditorInspector : : _resource_selected ) , CONNECT_DEFERRED ) ;
ep - > connect ( " object_id_selected " , callable_mp ( this , & EditorInspector : : _object_id_selected ) , 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 ] ;
2022-03-13 14:23:44 +01:00
ep - > property_path = property_prefix + F . properties [ 0 ] ;
2018-05-17 23:02:16 +02:00
ep - > property_usage = 0 ;
}
2021-12-09 10:42:46 +01:00
if ( ! F . label . is_empty ( ) ) {
2021-07-16 05:45:57 +02:00
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 ) ;
}
}
2024-02-25 13:38:44 +01:00
Node * section_search = p_section ;
while ( section_search ) {
EditorInspectorSection * section = Object : : cast_to < EditorInspectorSection > ( section_search ) ;
if ( section ) {
ep - > connect ( " property_can_revert_changed " , callable_mp ( section , & EditorInspectorSection : : property_can_revert_changed ) ) ;
}
section_search = section_search - > get_parent ( ) ;
if ( Object : : cast_to < EditorInspector > ( section_search ) ) {
// Skip sub-resource inspectors.
break ;
}
2022-07-30 15:12:51 +02:00
}
2018-05-17 23:02:16 +02:00
ep - > set_read_only ( read_only ) ;
ep - > update_property ( ) ;
2021-10-26 21:12:25 +02:00
ep - > _update_pin_flags ( ) ;
2022-05-01 03:24:45 +02:00
ep - > update_editor_property_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 ;
}
2022-12-28 06:54:40 +01:00
void EditorInspector : : update_tree ( ) {
2023-02-11 14:08:39 +01:00
// Store currently selected and focused elements to restore after the update.
// TODO: Can be useful to store more context for the focusable, such as the caret position in LineEdit.
2018-05-15 22:12:35 +02:00
StringName current_selected = property_selected ;
2019-01-22 16:29:26 +01:00
int current_focusable = - 1 ;
2024-05-19 11:30:49 +02:00
// Temporarily disable focus following to avoid jumping while the inspector is updating.
set_follow_focus ( false ) ;
2019-01-22 16:29:26 +01:00
if ( property_focusable ! = - 1 ) {
2023-02-11 14:08:39 +01:00
// Check that focusable is actually focusable.
2019-01-22 16:29:26 +01:00
bool restore_focus = false ;
2022-02-03 11:59:32 +01:00
Control * focused = get_viewport ( ) ? get_viewport ( ) - > gui_get_focus_owner ( ) : nullptr ;
2019-01-22 16:29:26 +01:00
if ( focused ) {
Node * parent = focused - > get_parent ( ) ;
while ( parent ) {
EditorInspector * inspector = Object : : cast_to < EditorInspector > ( parent ) ;
if ( inspector ) {
2023-02-11 14:08:39 +01:00
restore_focus = inspector = = this ; // May be owned by another inspector.
break ; // Exit after the first inspector is found, since there may be nested ones.
2019-01-22 16:29:26 +01:00
}
parent = parent - > get_parent ( ) ;
}
}
if ( restore_focus ) {
current_focusable = property_focusable ;
}
}
2018-05-15 22:12:35 +02:00
2023-02-11 14:08:39 +01:00
// Only hide plugins if we are not editing any object.
// This should be handled outside of the update_tree call anyway (see EditorInspector::edit), but might as well keep it safe.
_clear ( ! object ) ;
2018-05-15 22:12:35 +02:00
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 ] ) ;
}
2022-04-29 08:06:48 +02:00
// Decide if properties should be drawn with the warning color (yellow),
// or if the whole object should be considered read-only.
2021-09-30 17:08:04 +02:00
bool draw_warning = false ;
2022-04-29 08:06:48 +02:00
bool all_read_only = false ;
2021-05-26 18:28:38 +02:00
if ( is_inside_tree ( ) ) {
2022-04-29 08:06:48 +02:00
if ( object - > has_method ( " _is_read_only " ) ) {
all_read_only = object - > call ( " _is_read_only " ) ;
}
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-09-30 17:08:04 +02:00
// Draw in warning color edited nodes that are not in the currently edited scene,
// as changes may be lost in the future.
draw_warning = true ;
2022-04-29 08:06:48 +02:00
} else {
if ( ! all_read_only ) {
Resource * res = Object : : cast_to < Resource > ( object ) ;
if ( res ) {
all_read_only = EditorNode : : get_singleton ( ) - > is_resource_read_only ( res ) ;
}
}
2018-05-15 22:12:35 +02:00
}
}
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 ;
2021-11-08 21:53:41 +01:00
int section_depth = 0 ;
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 ) ;
2022-05-13 15:04:37 +02:00
HashMap < VBoxContainer * , HashMap < String , VBoxContainer * > > vbox_per_path ;
HashMap < String , EditorInspectorArray * > editor_inspector_array_per_prefix ;
2018-05-15 22:12:35 +02:00
2023-08-13 02:33:39 +02:00
Color sscolor = get_theme_color ( SNAME ( " prop_subsection " ) , EditorStringName ( 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 ) ;
2022-07-30 15:12:51 +02:00
_parse_added_editors ( main_vbox , nullptr , ped ) ;
2018-05-17 23:02:16 +02:00
}
2022-08-10 17:23:13 +02:00
StringName doc_name ;
2022-07-12 06:47:06 +02:00
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 ;
2021-11-08 21:53:41 +01:00
Vector < String > hint_parts = p . hint_string . split ( " , " ) ;
subgroup_base = hint_parts [ 0 ] ;
if ( hint_parts . size ( ) > 1 ) {
section_depth = hint_parts [ 1 ] . to_int ( ) ;
} else {
section_depth = 0 ;
}
2020-04-08 03:51:52 +02:00
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 ;
2021-11-08 21:53:41 +01:00
Vector < String > hint_parts = p . hint_string . split ( " , " ) ;
group_base = hint_parts [ 0 ] ;
if ( hint_parts . size ( ) > 1 ) {
section_depth = hint_parts [ 1 ] . to_int ( ) ;
} else {
section_depth = 0 ;
}
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 = " " ;
2021-11-08 21:53:41 +01:00
section_depth = 0 ;
2018-05-15 22:12:35 +02:00
2024-03-18 07:49:21 +01:00
vbox_per_path . clear ( ) ;
editor_inspector_array_per_prefix . clear ( ) ;
2024-05-14 09:23:14 +02:00
// `hint_script` should contain a native class name or a script path.
// Otherwise the category was probably added via `@export_category` or `_get_property_list()`.
const bool is_custom_category = p . hint_string . is_empty ( ) ;
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 ) {
2022-11-29 02:42:19 +01:00
if ( ! N - > get ( ) . name . begins_with ( " metadata/_ " ) & & N - > get ( ) . usage & PROPERTY_USAGE_EDITOR & &
( ! filter . is_empty ( ) | | ! 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
}
2024-02-14 09:28:53 +01:00
// Treat custom categories as second-level ones. Do not skip a normal category if it is followed by a custom one.
// Skip in the other 3 cases (normal -> normal, custom -> custom, custom -> normal).
2024-05-14 09:23:14 +02:00
if ( ( N - > get ( ) . usage & PROPERTY_USAGE_CATEGORY ) & & ( is_custom_category | | ! N - > get ( ) . hint_string . is_empty ( ) ) ) {
2018-05-15 22:12:35 +02:00
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
2024-05-28 07:33:27 +02:00
String category_label ;
String category_tooltip ;
Ref < Texture > category_icon ;
2018-05-15 22:12:35 +02:00
2024-02-03 21:30:45 +01:00
// Do not add an icon, do not change the current class (`doc_name`) for custom categories.
2024-05-14 09:23:14 +02:00
if ( is_custom_category ) {
2024-05-28 07:33:27 +02:00
category_label = p . name ;
category_tooltip = p . name ;
2024-02-03 21:30:45 +01:00
} else {
doc_name = p . name ;
2024-05-28 07:33:27 +02:00
category_label = p . name ;
2024-02-03 21:30:45 +01:00
// Use category's owner script to update some of its information.
2024-05-28 07:33:27 +02:00
if ( ! EditorNode : : get_editor_data ( ) . is_type_recognized ( p . name ) & & ResourceLoader : : exists ( p . hint_string ) ) {
2024-02-03 21:30:45 +01:00
Ref < Script > scr = ResourceLoader : : load ( p . hint_string , " Script " ) ;
if ( scr . is_valid ( ) ) {
StringName script_name = EditorNode : : get_editor_data ( ) . script_class_get_name ( scr - > get_path ( ) ) ;
// Update the docs reference and the label based on the script.
Vector < DocData : : ClassDoc > docs = scr - > get_documentation ( ) ;
if ( ! docs . is_empty ( ) ) {
// The documentation of a GDScript's main class is at the end of the array.
// Hacky because this isn't necessarily always guaranteed.
doc_name = docs [ docs . size ( ) - 1 ] . name ;
}
if ( script_name ! = StringName ( ) ) {
2024-05-28 07:33:27 +02:00
category_label = script_name ;
2024-02-03 21:30:45 +01:00
}
2023-04-11 16:51:20 +02:00
2024-02-03 21:30:45 +01:00
// Find the icon corresponding to the script.
if ( script_name ! = StringName ( ) ) {
2024-05-28 07:33:27 +02:00
category_icon = EditorNode : : get_singleton ( ) - > get_class_icon ( script_name ) ;
2024-02-03 21:30:45 +01:00
} else {
2024-05-28 07:33:27 +02:00
category_icon = EditorNode : : get_singleton ( ) - > get_object_icon ( scr . ptr ( ) , " Object " ) ;
2024-02-03 21:30:45 +01:00
}
2023-04-11 16:51:20 +02:00
}
2019-09-29 05:27:10 +02:00
}
2022-02-28 01:57:34 +01:00
2024-05-28 07:33:27 +02:00
if ( category_icon . is_null ( ) & & ! p . name . is_empty ( ) ) {
category_icon = EditorNode : : get_singleton ( ) - > get_class_icon ( p . name ) ;
2024-02-03 21:30:45 +01:00
}
2021-08-31 10:48:45 +02:00
2024-02-03 21:30:45 +01:00
if ( use_doc_hints ) {
2024-04-11 10:21:44 +02:00
// `|` separators used in `EditorHelpBit`.
2024-05-28 07:33:27 +02:00
category_tooltip = " class| " + doc_name + " | " ;
2024-02-03 21:30:45 +01:00
}
2018-05-15 22:12:35 +02:00
}
2018-05-17 23:02:16 +02:00
2024-05-28 07:33:27 +02:00
if ( ( is_custom_category & & ! show_custom_categories ) | | ( ! is_custom_category & & ! show_standard_categories ) ) {
continue ;
}
// Hide the "MultiNodeEdit" category for MultiNodeEdit.
if ( Object : : cast_to < MultiNodeEdit > ( object ) & & p . name = = " MultiNodeEdit " ) {
continue ;
}
// Create an EditorInspectorCategory and add it to the inspector.
EditorInspectorCategory * category = memnew ( EditorInspectorCategory ) ;
main_vbox - > add_child ( category ) ;
category_vbox = nullptr ; // Reset.
// Set the category info.
category - > label = category_label ;
category - > set_tooltip_text ( category_tooltip ) ;
category - > icon = category_icon ;
if ( ! is_custom_category ) {
category - > doc_class_name = doc_name ;
}
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 ) ;
2022-07-30 15:12:51 +02:00
_parse_added_editors ( main_vbox , nullptr , ped ) ;
2018-05-17 23:02:16 +02:00
}
2018-05-15 22:12:35 +02:00
continue ;
2022-11-29 02:42:19 +01:00
} else if ( p . name . begins_with ( " metadata/_ " ) | | ! ( p . usage & PROPERTY_USAGE_EDITOR ) | | _is_property_disabled_by_feature_profile ( p . name ) | |
( filter . is_empty ( ) & & 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
2022-06-14 20:34:17 +02:00
if ( p . name . begins_with ( " metadata/ " ) & & bool ( object - > call ( " _hide_metadata_from_inspector " ) ) ) {
// Hide metadata from inspector if required.
continue ;
}
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 ;
2022-05-13 15:04:37 +02:00
for ( KeyValue < String , EditorInspectorArray * > & E : editor_inspector_array_per_prefix ) {
if ( p . name . begins_with ( E . key ) & & E . key . length ( ) > array_prefix . length ( ) ) {
array_prefix = E . key ;
2021-08-31 10:48:45 +02:00
}
}
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 ( ) ) {
2022-02-04 09:32:20 +01:00
if ( ! is_digit ( str [ to_char_index ] ) ) {
2021-08-31 10:48:45 +02:00
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.
2021-12-09 10:42:46 +01:00
if ( ! subgroup . is_empty ( ) & & ! subgroup_base . is_empty ( ) ) {
2021-08-31 10:48:45 +02:00
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.
2021-12-09 10:42:46 +01:00
if ( ! group . is_empty ( ) & & ! group_base . is_empty ( ) & & subgroup . is_empty ( ) ) {
2021-08-31 10:48:45 +02:00
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.
2021-12-09 10:42:46 +01:00
if ( ! subgroup . is_empty ( ) ) {
2021-08-31 10:48:45 +02:00
path = subgroup + " / " + path ;
}
2021-12-09 10:42:46 +01:00
if ( ! group . is_empty ( ) ) {
2021-08-31 10:48:45 +02:00
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.
2022-03-17 18:16:25 +01:00
String name_override = ( path . contains ( " / " ) ) ? path . substr ( path . rfind ( " / " ) + 1 ) : path ;
2022-03-23 02:46:59 +01:00
String feature_tag ;
{
const int dot = name_override . find ( " . " ) ;
2018-05-15 22:12:35 +02:00
if ( dot ! = - 1 ) {
2022-04-19 02:31:57 +02:00
feature_tag = name_override . substr ( dot ) ;
2022-03-17 18:16:25 +01:00
name_override = name_override . substr ( 0 , dot ) ;
2018-05-15 22:12:35 +02:00
}
}
2022-04-29 11:47:42 +02:00
// Don't localize script variables.
2022-03-23 02:46:59 +01:00
EditorPropertyNameProcessor : : Style name_style = property_name_style ;
2022-04-29 11:47:42 +02:00
if ( ( p . usage & PROPERTY_USAGE_SCRIPT_VARIABLE ) & & name_style = = EditorPropertyNameProcessor : : STYLE_LOCALIZED ) {
2022-03-23 02:46:59 +01:00
name_style = EditorPropertyNameProcessor : : STYLE_CAPITALIZED ;
}
2023-10-05 15:12:10 +02:00
const String property_label_string = EditorPropertyNameProcessor : : get_singleton ( ) - > process_name ( name_override , name_style , p . name , doc_name ) + feature_tag ;
2022-03-23 02:46:59 +01: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.
2021-12-09 10:42:46 +01:00
if ( use_filter & & ! filter . is_empty ( ) ) {
2022-03-17 18:16:25 +01:00
const String property_path = property_prefix + ( path . is_empty ( ) ? " " : path + " / " ) + name_override ;
2022-03-23 02:46:59 +01:00
if ( ! _property_path_matches ( property_path , filter , property_name_style ) ) {
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 + + ) {
2023-11-18 23:40:56 +01:00
const String & component = components [ i ] ;
2021-08-31 10:48:45 +02:00
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
2022-03-26 03:22:46 +01:00
String label ;
String tooltip ;
2022-04-29 11:47:42 +02:00
// Don't localize groups for script variables.
EditorPropertyNameProcessor : : Style section_name_style = property_name_style ;
if ( ( p . usage & PROPERTY_USAGE_SCRIPT_VARIABLE ) & & section_name_style = = EditorPropertyNameProcessor : : STYLE_LOCALIZED ) {
section_name_style = EditorPropertyNameProcessor : : STYLE_CAPITALIZED ;
}
2022-03-26 03:22:46 +01:00
// Only process group label if this is not the group or subgroup.
if ( ( i = = 0 & & component = = group ) | | ( i = = 1 & & component = = subgroup ) ) {
2022-04-29 11:47:42 +02:00
if ( section_name_style = = EditorPropertyNameProcessor : : STYLE_LOCALIZED ) {
2022-12-27 03:58:01 +01:00
label = EditorPropertyNameProcessor : : get_singleton ( ) - > translate_group_name ( component ) ;
2022-03-26 03:22:46 +01:00
tooltip = component ;
} else {
label = component ;
2022-12-27 03:58:01 +01:00
tooltip = EditorPropertyNameProcessor : : get_singleton ( ) - > translate_group_name ( component ) ;
2022-03-26 03:22:46 +01:00
}
} else {
2023-10-05 15:12:10 +02:00
label = EditorPropertyNameProcessor : : get_singleton ( ) - > process_name ( component , section_name_style , p . name , doc_name ) ;
tooltip = EditorPropertyNameProcessor : : get_singleton ( ) - > process_name ( component , EditorPropertyNameProcessor : : get_tooltip_style ( section_name_style ) , p . name , doc_name ) ;
2022-03-26 03:22:46 +01:00
}
2018-07-19 00:37:17 +02:00
2021-08-31 10:48:45 +02:00
Color c = sscolor ;
c . a / = level ;
2022-03-03 11:25:11 +01:00
section - > setup ( acc_path , label , object , c , use_folding , section_depth ) ;
2022-08-25 12:42:17 +02:00
section - > set_tooltip_text ( tooltip ) ;
2021-08-31 10:48:45 +02:00
2021-11-10 15:49:19 +01:00
// Add editors at the start of a group.
for ( Ref < EditorInspectorPlugin > & ped : valid_plugins ) {
ped - > parse_group ( object , path ) ;
2022-07-30 15:12:51 +02:00
_parse_added_editors ( section - > get_vbox ( ) , section , ped ) ;
2021-11-10 15:49:19 +01:00
}
2021-08-31 10:48:45 +02:00
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 ;
2022-07-21 01:18:14 +02:00
Vector < String > class_name_components = String ( p . class_name ) . split ( " , " ) ;
int page_size = 5 ;
bool movable = true ;
bool numbered = false ;
bool foldable = use_folding ;
2022-09-16 01:49:14 +02:00
String add_button_text = TTR ( " Add Element " ) ;
2022-07-21 01:18:14 +02:00
String swap_method ;
for ( int i = ( p . type = = Variant : : NIL ? 1 : 2 ) ; i < class_name_components . size ( ) ; i + + ) {
if ( class_name_components [ i ] . begins_with ( " page_size " ) & & class_name_components [ i ] . get_slice_count ( " = " ) = = 2 ) {
page_size = class_name_components [ i ] . get_slice ( " = " , 1 ) . to_int ( ) ;
} else if ( class_name_components [ i ] . begins_with ( " add_button_text " ) & & class_name_components [ i ] . get_slice_count ( " = " ) = = 2 ) {
add_button_text = class_name_components [ i ] . get_slice ( " = " , 1 ) . strip_edges ( ) ;
} else if ( class_name_components [ i ] = = " static " ) {
movable = false ;
} else if ( class_name_components [ i ] = = " numbered " ) {
numbered = true ;
} else if ( class_name_components [ i ] = = " unfoldable " ) {
foldable = false ;
} else if ( class_name_components [ i ] . begins_with ( " swap_method " ) & & class_name_components [ i ] . get_slice_count ( " = " ) = = 2 ) {
swap_method = class_name_components [ i ] . get_slice ( " = " , 1 ) . strip_edges ( ) ;
}
}
2021-08-31 10:48:45 +02:00
if ( p . type = = Variant : : NIL ) {
// Setup the array to use a method to create/move/delete elements.
2022-07-21 01:18:14 +02:00
array_element_prefix = class_name_components [ 0 ] ;
2022-09-06 17:59:36 +02:00
editor_inspector_array = memnew ( EditorInspectorArray ( all_read_only ) ) ;
2021-08-31 10:48:45 +02:00
2022-02-03 17:03:38 +01:00
String array_label = path . contains ( " / " ) ? path . substr ( path . rfind ( " / " ) + 1 ) : path ;
2023-10-05 15:12:10 +02:00
array_label = EditorPropertyNameProcessor : : get_singleton ( ) - > process_name ( property_label_string , property_name_style , p . name , doc_name ) ;
2021-08-31 10:48:45 +02:00
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 ) ;
2022-07-28 22:56:41 +02:00
editor_inspector_array - > connect ( " page_change_request " , callable_mp ( this , & EditorInspector : : _page_change_request ) . bind ( array_element_prefix ) ) ;
2021-08-31 10:48:45 +02:00
} else if ( p . type = = Variant : : INT ) {
// Setup the array to use the count property and built-in functions to create/move/delete elements.
2022-08-04 16:21:09 +02:00
if ( class_name_components . size ( ) > = 2 ) {
array_element_prefix = class_name_components [ 1 ] ;
2022-09-06 17:59:36 +02:00
editor_inspector_array = memnew ( EditorInspectorArray ( all_read_only ) ) ;
2021-08-31 10:48:45 +02:00
int page = per_array_page . has ( array_element_prefix ) ? per_array_page [ array_element_prefix ] : 0 ;
2022-07-21 01:18:14 +02:00
editor_inspector_array - > setup_with_count_property ( object , class_name_components [ 0 ] , p . name , array_element_prefix , page , c , foldable , movable , numbered , page_size , add_button_text , swap_method ) ;
2022-07-28 22:56:41 +02:00
editor_inspector_array - > connect ( " page_change_request " , callable_mp ( this , & EditorInspector : : _page_change_request ) . bind ( array_element_prefix ) ) ;
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 ;
}
2022-07-21 01:18:14 +02:00
2021-08-31 10:48:45 +02:00
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 ) ;
}
2023-09-21 04:54:51 +02:00
String doc_path ;
String theme_item_name ;
StringName classname = doc_name ;
2018-05-15 22:12:35 +02:00
2023-09-21 04:54:51 +02:00
// Build the doc hint, to use as tooltip.
2018-05-15 22:12:35 +02:00
if ( use_doc_hints ) {
2021-12-09 10:42:46 +01:00
if ( ! object_class . is_empty ( ) ) {
2018-07-19 23:58:15 +02:00
classname = object_class ;
2022-06-14 20:34:17 +02:00
} else if ( Object : : cast_to < MultiNodeEdit > ( object ) ) {
classname = Object : : cast_to < MultiNodeEdit > ( object ) - > get_edited_class_name ( ) ;
2022-11-20 23:35:48 +01:00
} else if ( classname = = " " ) {
classname = object - > get_class_name ( ) ;
Resource * res = Object : : cast_to < Resource > ( object ) ;
if ( res & & ! res - > get_script ( ) . is_null ( ) ) {
// Grab the script of this resource to get the evaluated script class.
Ref < Script > scr = res - > get_script ( ) ;
if ( scr . is_valid ( ) ) {
Vector < DocData : : ClassDoc > docs = scr - > get_documentation ( ) ;
if ( ! docs . is_empty ( ) ) {
2023-04-08 22:26:41 +02:00
// The documentation of a GDScript's main class is at the end of the array.
// Hacky because this isn't necessarily always guaranteed.
classname = docs [ docs . size ( ) - 1 ] . name ;
2022-11-20 23:35:48 +01:00
}
}
}
2018-07-19 23:58:15 +02:00
}
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
bool found = false ;
2023-04-01 11:53:01 +02:00
// Small hack for theme_overrides. They are listed under Control, but come from another class.
if ( classname = = " Control " & & p . name . begins_with ( " theme_override_ " ) ) {
classname = get_edited_object ( ) - > get_class ( ) ;
}
2023-09-21 04:54:51 +02:00
// Search for the doc path in the cache.
2024-04-09 20:31:44 +02:00
HashMap < StringName , HashMap < StringName , DocCacheInfo > > : : Iterator E = doc_cache . find ( classname ) ;
2018-05-15 22:12:35 +02:00
if ( E ) {
2024-04-09 20:31:44 +02:00
HashMap < StringName , DocCacheInfo > : : Iterator F = E - > value . find ( propname ) ;
2018-05-15 22:12:35 +02:00
if ( F ) {
found = true ;
2024-04-09 20:31:44 +02:00
doc_path = F - > value . doc_path ;
theme_item_name = F - > value . theme_item_name ;
2018-05-15 22:12:35 +02:00
}
}
if ( ! found ) {
2023-09-21 04:54:51 +02:00
DocTools * dd = EditorHelp : : get_doc_data ( ) ;
// Do not cache the doc path information of scripts.
2023-08-31 21:20:39 +02:00
bool is_native_class = ClassDB : : class_exists ( classname ) ;
2023-04-19 02:00:13 +02:00
HashMap < String , DocData : : ClassDoc > : : ConstIterator F = dd - > class_list . find ( classname ) ;
2023-09-21 04:54:51 +02:00
while ( F ) {
2020-01-29 19:56:03 +01:00
Vector < String > slices = propname . operator String ( ) . split ( " / " ) ;
2023-09-21 04:54:51 +02:00
// Check if it's a theme item first.
2021-08-06 16:12:43 +02:00
if ( slices . size ( ) = = 2 & & slices [ 0 ] . begins_with ( " theme_override_ " ) ) {
2022-05-13 15:04:37 +02:00
for ( int i = 0 ; i < F - > value . theme_properties . size ( ) ; i + + ) {
2023-09-21 04:54:51 +02:00
String doc_path_current = " class_theme_item: " + F - > value . name + " : " + F - > value . theme_properties [ i ] . name ;
2022-05-13 15:04:37 +02:00
if ( F - > value . theme_properties [ i ] . name = = slices [ 1 ] ) {
2023-09-21 04:54:51 +02:00
doc_path = doc_path_current ;
theme_item_name = F - > value . theme_properties [ i ] . name ;
2020-01-29 19:56:03 +01:00
}
}
2018-05-15 22:12:35 +02:00
} else {
2023-09-21 04:54:51 +02:00
for ( int i = 0 ; i < F - > value . properties . size ( ) ; i + + ) {
String doc_path_current = " class_property: " + F - > value . name + " : " + F - > value . properties [ i ] . name ;
if ( F - > value . properties [ i ] . name = = propname . operator String ( ) ) {
doc_path = doc_path_current ;
}
}
2018-05-15 22:12:35 +02:00
}
2024-04-09 20:31:44 +02:00
if ( is_native_class ) {
DocCacheInfo cache_info ;
cache_info . doc_path = doc_path ;
cache_info . theme_item_name = theme_item_name ;
doc_cache [ classname ] [ propname ] = cache_info ;
}
2023-09-21 04:54:51 +02:00
if ( ! doc_path . is_empty ( ) | | F - > value . inherits . is_empty ( ) ) {
break ;
}
// Couldn't find the doc path in the class itself, try its super class.
F = dd - > class_list . find ( F - > value . inherits ) ;
2023-01-22 09:12:56 +01:00
}
2022-03-28 22:37:01 +02:00
}
2018-05-15 22:12:35 +02:00
}
2022-05-24 00:35:01 +02:00
Vector < EditorInspectorPlugin : : AddedEditor > editors ;
Vector < EditorInspectorPlugin : : AddedEditor > late_editors ;
Fix various typos
Found via ` codespell -q 3 -S ./thirdparty,*.po,./DONORS.md -L ackward,ang,ans,ba,beng,cas,childs,childrens,dof,doubleclick,expct,fave,findn,gird,hist,inout,leapyear,lod,nd,numer,ois,ony,paket,seeked,sinc,switchs,te,uint,varn`
Update editor/import/resource_importer_layered_texture.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update doc/classes/TileSetScenesCollectionSource.xml
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/gui/graph_edit.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/resources/animation.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/resources/animation.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/resources/animation.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Update scene/gui/rich_text_label.cpp
Co-authored-by: Raul Santos <raulsntos@gmail.com>
Revert previously committed change
2022-01-02 07:03:58 +01:00
// Search 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
2022-05-24 00:35:01 +02:00
for ( const EditorInspectorPlugin : : AddedEditor & F : ped - > added_editors ) {
if ( F . add_to_end ) {
late_editors . push_back ( F ) ;
} else {
editors . push_back ( F ) ;
}
}
2018-05-17 23:02:16 +02:00
ped - > added_editors . clear ( ) ;
2022-05-24 00:35:01 +02:00
if ( exclusive ) {
break ;
}
}
2018-05-15 22:12:35 +02:00
2022-05-24 00:35:01 +02:00
editors . append_array ( late_editors ) ;
2018-05-15 22:12:35 +02:00
2022-05-24 00:35:01 +02:00
for ( int i = 0 ; i < editors . size ( ) ; i + + ) {
EditorProperty * ep = Object : : cast_to < EditorProperty > ( editors [ i ] . property_editor ) ;
const Vector < String > & properties = editors [ i ] . properties ;
2018-05-15 22:12:35 +02:00
2022-05-24 00:35:01 +02:00
if ( ep ) {
// Set all this before the control gets the ENTER_TREE notification.
ep - > object = object ;
2018-05-15 22:12:35 +02:00
2022-05-24 00:35:01 +02:00
if ( properties . size ( ) ) {
if ( properties . size ( ) = = 1 ) {
2023-09-21 04:54:51 +02:00
// Since it's one, associate:
2022-05-24 00:35:01 +02:00
ep - > property = properties [ 0 ] ;
ep - > property_path = property_prefix + properties [ 0 ] ;
ep - > property_usage = p . usage ;
2023-09-21 04:54:51 +02:00
// And set label?
2018-05-15 22:12:35 +02:00
}
2022-05-24 00:35:01 +02:00
if ( ! editors [ i ] . label . is_empty ( ) ) {
ep - > set_label ( editors [ i ] . label ) ;
2018-08-07 17:19:19 +02:00
} else {
2022-05-24 00:35:01 +02:00
// Use the existing one.
ep - > set_label ( property_label_string ) ;
2018-08-07 17:19:19 +02:00
}
2022-05-01 03:24:45 +02:00
2022-05-24 00:35:01 +02:00
for ( int j = 0 ; j < properties . size ( ) ; j + + ) {
String prop = properties [ j ] ;
2018-05-15 22:12:35 +02:00
2022-05-24 00:35:01 +02:00
if ( ! editor_property_map . has ( prop ) ) {
editor_property_map [ prop ] = List < EditorProperty * > ( ) ;
}
editor_property_map [ prop ] . push_back ( ep ) ;
2018-05-15 22:12:35 +02:00
}
}
2022-07-30 15:12:51 +02:00
2024-02-25 13:38:44 +01:00
Node * section_search = current_vbox - > get_parent ( ) ;
while ( section_search ) {
EditorInspectorSection * section = Object : : cast_to < EditorInspectorSection > ( section_search ) ;
if ( section ) {
ep - > connect ( " property_can_revert_changed " , callable_mp ( section , & EditorInspectorSection : : property_can_revert_changed ) ) ;
}
section_search = section_search - > get_parent ( ) ;
if ( Object : : cast_to < EditorInspector > ( section_search ) ) {
// Skip sub-resource inspectors.
break ;
}
2022-07-30 15:12:51 +02:00
}
2022-05-24 00:35:01 +02:00
ep - > set_draw_warning ( draw_warning ) ;
ep - > set_use_folding ( use_folding ) ;
ep - > set_checkable ( checkable ) ;
ep - > set_checked ( checked ) ;
ep - > set_keying ( keying ) ;
2022-04-29 08:06:48 +02:00
ep - > set_read_only ( property_read_only | | all_read_only ) ;
2022-05-24 00:35:01 +02:00
ep - > set_deletable ( deletable_properties | | p . name . begins_with ( " metadata/ " ) ) ;
}
current_vbox - > add_child ( editors [ i ] . property_editor ) ;
if ( ep ) {
// Eventually, set other properties/signals after the property editor got added to the tree.
bool update_all = ( p . usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED ) ;
2022-07-28 22:56:41 +02:00
ep - > connect ( " property_changed " , callable_mp ( this , & EditorInspector : : _property_changed ) . bind ( update_all ) ) ;
2022-05-24 00:35:01 +02:00
ep - > connect ( " property_keyed " , callable_mp ( this , & EditorInspector : : _property_keyed ) ) ;
2022-07-28 22:56:41 +02:00
ep - > connect ( " property_deleted " , callable_mp ( this , & EditorInspector : : _property_deleted ) , CONNECT_DEFERRED ) ;
2022-05-24 00:35:01 +02: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 ( " property_pinned " , callable_mp ( this , & EditorInspector : : _property_pinned ) ) ;
ep - > connect ( " selected " , callable_mp ( this , & EditorInspector : : _property_selected ) ) ;
ep - > connect ( " multiple_properties_changed " , callable_mp ( this , & EditorInspector : : _multiple_properties_changed ) ) ;
2022-07-28 22:56:41 +02:00
ep - > connect ( " resource_selected " , callable_mp ( this , & EditorInspector : : _resource_selected ) , CONNECT_DEFERRED ) ;
ep - > connect ( " object_id_selected " , callable_mp ( this , & EditorInspector : : _object_id_selected ) , CONNECT_DEFERRED ) ;
2023-09-21 04:54:51 +02:00
if ( use_doc_hints ) {
2024-04-11 10:21:44 +02:00
// `|` separators used in `EditorHelpBit`.
2023-09-21 04:54:51 +02:00
if ( theme_item_name . is_empty ( ) ) {
2024-04-26 11:48:11 +02:00
if ( p . name . contains ( " shader_parameter/ " ) ) {
ShaderMaterial * shader_material = Object : : cast_to < ShaderMaterial > ( object ) ;
if ( shader_material ) {
ep - > set_tooltip_text ( " property| " + shader_material - > get_shader ( ) - > get_path ( ) + " | " + property_prefix + p . name ) ;
}
} else if ( p . usage & PROPERTY_USAGE_INTERNAL ) {
2024-04-11 10:21:44 +02:00
ep - > set_tooltip_text ( " internal_property| " + classname + " | " + property_prefix + p . name ) ;
2024-01-26 20:42:20 +01:00
} else {
2024-04-11 10:21:44 +02:00
ep - > set_tooltip_text ( " property| " + classname + " | " + property_prefix + p . name ) ;
2024-01-26 20:42:20 +01:00
}
2023-09-21 04:54:51 +02:00
} else {
2024-04-11 10:21:44 +02:00
ep - > set_tooltip_text ( " theme_item| " + classname + " | " + theme_item_name ) ;
2023-09-21 04:54:51 +02:00
}
2023-10-10 10:30:36 +02:00
ep - > has_doc_tooltip = true ;
2023-09-21 04:54:51 +02:00
}
ep - > set_doc_path ( doc_path ) ;
2024-01-26 20:42:20 +01:00
ep - > set_internal ( p . usage & PROPERTY_USAGE_INTERNAL ) ;
2022-05-24 00:35:01 +02:00
ep - > update_property ( ) ;
ep - > _update_pin_flags ( ) ;
2022-05-01 03:24:45 +02:00
ep - > update_editor_property_status ( ) ;
2022-05-24 00:35:01 +02:00
ep - > update_cache ( ) ;
2018-06-27 00:05:11 +02:00
2022-05-24 00:35:01 +02:00
if ( current_selected & & ep - > property = = current_selected ) {
ep - > select ( current_focusable ) ;
}
2018-06-27 00:05:11 +02:00
}
2018-05-15 22:12:35 +02:00
}
}
2022-06-14 20:34:17 +02:00
if ( ! hide_metadata & & ! object - > call ( " _hide_metadata_from_inspector " ) ) {
2022-08-03 00:39:08 +02:00
// Add 4px of spacing between the "Add Metadata" button and the content above it.
Control * spacer = memnew ( Control ) ;
spacer - > set_custom_minimum_size ( Size2 ( 0 , 4 ) * EDSCALE ) ;
main_vbox - > add_child ( spacer ) ;
2022-05-25 01:38:13 +02:00
Button * add_md = EditorInspector : : create_inspector_action_button ( TTR ( " Add Metadata " ) ) ;
2023-08-13 02:33:39 +02:00
add_md - > set_icon ( get_editor_theme_icon ( SNAME ( " Add " ) ) ) ;
2024-05-14 09:40:21 +02:00
add_md - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & EditorInspector : : _show_add_meta_dialog ) ) ;
2022-03-23 21:08:54 +01:00
main_vbox - > add_child ( add_md ) ;
2022-04-29 08:06:48 +02:00
if ( all_read_only ) {
add_md - > set_disabled ( true ) ;
}
2022-03-23 21:08:54 +01: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 ) {
2021-11-10 15:49:19 +01:00
ped - > parse_end ( object ) ;
2022-07-30 15:12:51 +02:00
_parse_added_editors ( main_vbox , nullptr , ped ) ;
2018-05-17 23:02:16 +02:00
}
2023-01-21 22:49:06 +01:00
2023-02-15 18:03:12 +01:00
if ( is_main_editor_inspector ( ) ) {
2023-01-21 22:49:06 +01:00
// Updating inspector might invalidate some editing owners.
EditorNode : : get_singleton ( ) - > hide_unused_editors ( ) ;
}
2024-05-19 11:30:49 +02:00
set_follow_focus ( true ) ;
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 ( ) ;
2022-05-01 03:24:45 +02:00
E - > update_editor_property_status ( ) ;
2021-07-16 05:45:57 +02:00
E - > update_cache ( ) ;
2018-05-15 22:12:35 +02:00
}
}
2023-02-11 14:08:39 +01:00
void EditorInspector : : _clear ( bool p_hide_plugins ) {
2018-05-15 22:12:35 +02:00
while ( main_vbox - > get_child_count ( ) ) {
memdelete ( main_vbox - > get_child ( 0 ) ) ;
}
2023-02-11 14:08:39 +01:00
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 ( ) ;
2023-02-11 14:08:39 +01:00
2023-02-15 18:03:12 +01:00
if ( p_hide_plugins & & is_main_editor_inspector ( ) ) {
2023-01-21 22:49:06 +01:00
EditorNode : : get_singleton ( ) - > hide_unused_editors ( this ) ;
}
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
2023-09-10 21:29:28 +02:00
Object * EditorInspector : : get_next_edited_object ( ) {
return next_object ;
}
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
}
2023-08-11 15:55:47 +02:00
2023-09-10 21:29:28 +02:00
next_object = p_object ; // Some plugins need to know the next edited object when clearing the inspector.
2018-05-15 22:12:35 +02:00
if ( object ) {
2024-05-13 16:56:03 +02:00
object - > disconnect ( CoreStringName ( property_list_changed ) , callable_mp ( this , & EditorInspector : : _changed_callback ) ) ;
2024-04-25 23:16:18 +02:00
_clear ( ) ;
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
}
2024-05-13 16:56:03 +02:00
object - > connect ( CoreStringName ( property_list_changed ) , callable_mp ( this , & EditorInspector : : _changed_callback ) ) ;
2018-05-15 22:12:35 +02:00
update_tree ( ) ;
}
2023-09-10 21:29:28 +02:00
// Keep it available until the end so it works with both main and sub inspectors.
next_object = nullptr ;
2022-02-06 15:53:53 +01:00
emit_signal ( SNAME ( " edited_object_changed " ) ) ;
2018-05-15 22:12:35 +02:00
}
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 ;
2023-03-07 18:41:54 +01:00
_keying_changed ( ) ;
}
void EditorInspector : : _keying_changed ( ) {
for ( const KeyValue < StringName , List < EditorProperty * > > & F : editor_property_map ) {
for ( EditorProperty * E : F . value ) {
if ( E ) {
E - > set_keying ( keying ) ;
}
}
}
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 : : set_read_only ( bool p_read_only ) {
2023-02-19 23:07:23 +01:00
if ( p_read_only = = read_only ) {
return ;
}
2018-05-15 22:12:35 +02:00
read_only = p_read_only ;
update_tree ( ) ;
}
2022-03-23 02:46:59 +01:00
EditorPropertyNameProcessor : : Style EditorInspector : : get_property_name_style ( ) const {
return property_name_style ;
2018-05-15 22:12:35 +02:00
}
2020-05-14 14:29:06 +02:00
2022-03-23 02:46:59 +01:00
void EditorInspector : : set_property_name_style ( EditorPropertyNameProcessor : : Style p_style ) {
if ( property_name_style = = p_style ) {
return ;
}
property_name_style = p_style ;
2018-05-15 22:12:35 +02:00
update_tree ( ) ;
}
2023-03-02 13:37:02 +01:00
void EditorInspector : : set_use_settings_name_style ( bool p_enable ) {
if ( use_settings_name_style = = p_enable ) {
return ;
}
use_settings_name_style = p_enable ;
if ( use_settings_name_style ) {
set_property_name_style ( EditorPropertyNameProcessor : : get_singleton ( ) - > get_settings_style ( ) ) ;
}
}
2018-05-15 22:12:35 +02:00
void EditorInspector : : set_autoclear ( bool p_enable ) {
autoclear = p_enable ;
}
2024-05-14 09:23:14 +02:00
void EditorInspector : : set_show_categories ( bool p_show_standard , bool p_show_custom ) {
show_standard_categories = p_show_standard ;
show_custom_categories = p_show_custom ;
2018-05-15 22:12:35 +02:00
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
2022-03-23 21:08:54 +01:00
void EditorInspector : : set_hide_metadata ( bool p_hide ) {
hide_metadata = p_hide ;
update_tree ( ) ;
}
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 ) {
update_tree ( ) ;
}
2023-08-11 15:55:47 +02:00
void EditorInspector : : set_use_folding ( bool p_use_folding , bool p_update_tree ) {
use_folding = p_use_folding ;
if ( p_update_tree ) {
update_tree ( ) ;
}
2018-05-15 22:12:35 +02:00
}
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
}
2022-07-30 15:12:51 +02:00
void EditorInspector : : expand_revertable ( ) {
HashSet < EditorInspectorSection * > sections_to_unfold [ 2 ] ;
for ( EditorInspectorSection * E : sections ) {
if ( E - > has_revertable_properties ( ) ) {
sections_to_unfold [ 0 ] . insert ( E ) ;
}
}
2022-09-30 14:23:36 +02:00
// Climb up the hierarchy doing double buffering with the sets.
2022-07-30 15:12:51 +02:00
int a = 0 ;
int b = 1 ;
while ( sections_to_unfold [ a ] . size ( ) ) {
for ( EditorInspectorSection * E : sections_to_unfold [ a ] ) {
E - > unfold ( ) ;
Node * n = E - > get_parent ( ) ;
while ( n ) {
if ( Object : : cast_to < EditorInspector > ( n ) ) {
break ;
}
if ( Object : : cast_to < EditorInspectorSection > ( n ) & & ! sections_to_unfold [ a ] . has ( ( EditorInspectorSection * ) n ) ) {
sections_to_unfold [ b ] . insert ( ( EditorInspectorSection * ) n ) ;
}
n = n - > get_parent ( ) ;
}
}
sections_to_unfold [ a ] . clear ( ) ;
SWAP ( a , b ) ;
}
for ( const KeyValue < StringName , List < EditorProperty * > > & F : editor_property_map ) {
for ( EditorProperty * E : F . value ) {
E - > expand_revertable ( ) ;
}
}
}
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 ;
}
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
}
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
2021-12-09 10:42:46 +01:00
if ( p_property . is_empty ( ) ) {
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
}
}
}
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
if ( 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 ) ;
2021-10-19 11:40:46 +02:00
bool valid = false ;
Variant value = object - > get ( p_name , & valid ) ;
if ( valid ) {
undo_redo - > add_undo_property ( object , p_name , value ) ;
}
2018-05-15 22:12:35 +02:00
2022-07-21 01:18:14 +02:00
List < StringName > linked_properties ;
ClassDB : : get_linked_properties_info ( object - > get_class_name ( ) , p_name , & linked_properties ) ;
for ( const StringName & linked_prop : linked_properties ) {
valid = false ;
Variant undo_value = object - > get ( linked_prop , & valid ) ;
if ( valid ) {
undo_redo - > add_undo_property ( object , linked_prop , undo_value ) ;
}
}
PackedStringArray linked_properties_dynamic = object - > call ( " _get_linked_undo_properties " , p_name , p_value ) ;
for ( int i = 0 ; i < linked_properties_dynamic . size ( ) ; i + + ) {
valid = false ;
Variant undo_value = object - > get ( linked_properties_dynamic [ i ] , & valid ) ;
if ( valid ) {
undo_redo - > add_undo_property ( object , linked_properties_dynamic [ i ] , undo_value ) ;
2021-08-12 20:26:47 +02:00
}
}
2022-03-25 18:06:46 +01:00
Variant v_undo_redo = undo_redo ;
2021-04-28 17:39:57 +02:00
Variant v_object = object ;
Variant v_name = p_name ;
2023-08-11 15:55:47 +02:00
const Vector < Callable > & callbacks = EditorNode : : get_editor_data ( ) . get_undo_redo_inspector_hook_callback ( ) ;
2023-06-16 19:24:04 +02:00
for ( int i = 0 ; i < callbacks . size ( ) ; i + + ) {
const Callable & callback = callbacks [ i ] ;
2021-04-28 17:39:57 +02:00
const Variant * p_arguments [ ] = { & v_undo_redo , & v_object , & v_name , & p_value } ;
Variant return_value ;
Callable : : CallError call_error ;
2022-07-28 22:56:41 +02:00
callback . callp ( p_arguments , 4 , return_value , call_error ) ;
2021-04-28 17:39:57 +02:00
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 ] ) {
2022-05-01 03:24:45 +02:00
E - > update_editor_property_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 ) {
2024-01-28 21:51:39 +01:00
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 ) {
2024-01-28 21:51:39 +01:00
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
}
2024-02-15 17:25:58 +01:00
void EditorInspector : : _multiple_properties_changed ( const Vector < String > & p_paths , const Array & p_values , bool p_changing ) {
2024-01-19 13:21:39 +01:00
ERR_FAIL_COND ( p_paths . is_empty ( ) | | p_values . is_empty ( ) ) ;
2018-05-15 22:12:35 +02:00
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 ] ;
}
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2023-09-12 12:15:44 +02:00
// TRANSLATORS: This is describing a change to multiple properties at once. The parameter is a list of property names.
undo_redo - > create_action ( vformat ( TTR ( " Set Multiple: %s " ) , names ) , UndoRedo : : MERGE_ENDS ) ;
2018-05-15 22:12:35 +02:00
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-12-03 16:23:32 +01:00
// The second parameter could be null, causing the event to fire with less arguments, so use the pointer call which preserves it.
const Variant args [ 3 ] = { p_path , object - > get ( p_path ) , p_advance } ;
const Variant * argp [ 3 ] = { & args [ 0 ] , & args [ 1 ] , & args [ 2 ] } ;
2022-03-09 14:58:40 +01:00
emit_signalp ( SNAME ( " property_keyed " ) , argp , 3 ) ;
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
2022-03-23 21:08:54 +01:00
if ( p_path . begins_with ( " metadata/ " ) ) {
String name = p_path . replace_first ( " metadata/ " , " " ) ;
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2022-03-23 21:08:54 +01:00
undo_redo - > create_action ( vformat ( TTR ( " Remove metadata %s " ) , name ) ) ;
undo_redo - > add_do_method ( object , " remove_meta " , name ) ;
undo_redo - > add_undo_method ( object , " set_meta " , name , object - > get_meta ( name ) ) ;
undo_redo - > commit_action ( ) ;
}
2021-12-03 16:23:32 +01:00
emit_signal ( SNAME ( " property_deleted " ) , p_path ) ;
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-12-03 16:23:32 +01:00
// The second parameter could be null, causing the event to fire with less arguments, so use the pointer call which preserves it.
const Variant args [ 3 ] = { p_path , p_value , p_advance } ;
const Variant * argp [ 3 ] = { & args [ 0 ] , & args [ 1 ] , & args [ 2 ] } ;
2022-03-09 14:58:40 +01:00
emit_signalp ( SNAME ( " property_keyed " ) , argp , 3 ) ;
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 ) {
2024-05-21 17:05:15 +02:00
_edit_set ( p_path , Variant ( ) , false , " " ) ;
2018-05-15 22:12:35 +02:00
} 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 ;
}
}
2024-05-21 17:05:15 +02:00
_edit_set ( p_path , to_create , false , " " ) ;
2018-05-15 22:12:35 +02:00
}
if ( editor_property_map . has ( p_path ) ) {
2021-07-16 05:45:57 +02:00
for ( EditorProperty * E : editor_property_map [ p_path ] ) {
2022-05-09 11:47:10 +02:00
E - > set_checked ( p_checked ) ;
2021-07-16 05:45:57 +02:00
E - > update_property ( ) ;
2022-05-01 03:24:45 +02:00
E - > update_editor_property_status ( ) ;
2021-07-16 05:45:57 +02:00
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
}
}
2021-10-26 21:12:25 +02:00
void EditorInspector : : _property_pinned ( const String & p_path , bool p_pinned ) {
if ( ! object ) {
return ;
}
Node * node = Object : : cast_to < Node > ( object ) ;
2023-09-09 17:24:40 +02:00
ERR_FAIL_NULL ( node ) ;
2021-10-26 21:12:25 +02:00
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
undo_redo - > create_action ( vformat ( p_pinned ? TTR ( " Pinned %s " ) : TTR ( " Unpinned %s " ) , p_path ) ) ;
undo_redo - > add_do_method ( node , " _set_property_pinned " , p_path , p_pinned ) ;
undo_redo - > add_undo_method ( node , " _set_property_pinned " , p_path , ! p_pinned ) ;
if ( editor_property_map . has ( p_path ) ) {
for ( List < EditorProperty * > : : Element * E = editor_property_map [ p_path ] . front ( ) ; E ; E = E - > next ( ) ) {
undo_redo - > add_do_method ( E - > get ( ) , " _update_editor_property_status " ) ;
undo_redo - > add_undo_method ( E - > get ( ) , " _update_editor_property_status " ) ;
2021-10-26 21:12:25 +02:00
}
}
2022-12-23 23:53:16 +01:00
undo_redo - > commit_action ( ) ;
2021-10-26 21:12:25 +02:00
}
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 ;
2022-03-28 22:37:01 +02:00
// 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
}
2022-05-03 01:43:50 +02:00
void EditorInspector : : _resource_selected ( const String & p_path , Ref < Resource > 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 ) {
2022-02-16 00:52:32 +01:00
switch ( p_what ) {
case NOTIFICATION_READY : {
EditorFeatureProfileManager : : get_singleton ( ) - > connect ( " current_feature_profile_changed " , callable_mp ( this , & EditorInspector : : _feature_profile_changed ) ) ;
set_process ( is_visible_in_tree ( ) ) ;
2024-05-14 15:50:53 +02:00
add_theme_style_override ( SceneStringName ( panel ) , get_theme_stylebox ( SceneStringName ( panel ) , SNAME ( " Tree " ) ) ) ;
2022-02-16 00:52:32 +01:00
if ( ! sub_inspector ) {
get_tree ( ) - > connect ( " node_removed " , callable_mp ( this , & EditorInspector : : _node_removed ) ) ;
}
} break ;
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
case NOTIFICATION_PREDELETE : {
2024-05-03 22:27:59 +02:00
if ( ! sub_inspector & & is_inside_tree ( ) ) {
2022-02-16 00:52:32 +01:00
get_tree ( ) - > disconnect ( " node_removed " , callable_mp ( this , & EditorInspector : : _node_removed ) ) ;
}
2024-05-03 22:27:59 +02:00
edit ( nullptr ) ;
2022-02-16 00:52:32 +01:00
} break ;
case NOTIFICATION_VISIBILITY_CHANGED : {
set_process ( is_visible_in_tree ( ) ) ;
} break ;
case NOTIFICATION_PROCESS : {
if ( update_scroll_request > = 0 ) {
2023-12-18 15:46:56 +01:00
callable_mp ( ( Range * ) get_v_scroll_bar ( ) , & Range : : set_value ) . call_deferred ( update_scroll_request ) ;
2022-02-16 00:52:32 +01:00
update_scroll_request = - 1 ;
}
2022-04-19 08:42:16 +02:00
if ( update_tree_pending ) {
2022-10-18 16:43:37 +02:00
refresh_countdown = float ( EDITOR_GET ( " docks/property_editor/auto_refresh_interval " ) ) ;
2022-04-19 08:42:16 +02:00
} else if ( refresh_countdown > 0 ) {
2022-02-16 00:52:32 +01:00
refresh_countdown - = get_process_delta_time ( ) ;
if ( refresh_countdown < = 0 ) {
for ( const KeyValue < StringName , List < EditorProperty * > > & F : editor_property_map ) {
for ( EditorProperty * E : F . value ) {
2022-07-25 02:46:02 +02:00
if ( E & & ! E - > is_cache_valid ( ) ) {
2022-02-16 00:52:32 +01:00
E - > update_property ( ) ;
2022-05-01 03:24:45 +02:00
E - > update_editor_property_status ( ) ;
2022-02-16 00:52:32 +01:00
E - > update_cache ( ) ;
}
2021-02-10 21:18:45 +01:00
}
2018-05-15 22:12:35 +02:00
}
2022-10-18 16:43:37 +02:00
refresh_countdown = float ( EDITOR_GET ( " docks/property_editor/auto_refresh_interval " ) ) ;
2018-05-15 22:12:35 +02:00
}
}
2022-02-16 00:52:32 +01:00
changing + + ;
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
if ( update_tree_pending ) {
2022-12-28 06:54:40 +01:00
update_tree ( ) ;
2022-02-16 00:52:32 +01:00
update_tree_pending = false ;
pending . clear ( ) ;
2022-12-28 06:54:40 +01:00
2022-02-16 00:52:32 +01:00
} else {
while ( pending . size ( ) ) {
2022-05-19 17:00:06 +02:00
StringName prop = * pending . begin ( ) ;
2022-02-16 00:52:32 +01:00
if ( editor_property_map . has ( prop ) ) {
for ( EditorProperty * E : editor_property_map [ prop ] ) {
E - > update_property ( ) ;
2022-05-01 03:24:45 +02:00
E - > update_editor_property_status ( ) ;
2022-02-16 00:52:32 +01:00
E - > update_cache ( ) ;
}
2018-05-15 22:12:35 +02:00
}
2022-05-19 17:00:06 +02:00
pending . remove ( pending . begin ( ) ) ;
2018-05-15 22:12:35 +02:00
}
}
2022-02-16 00:52:32 +01:00
changing - - ;
} break ;
2018-05-15 22:12:35 +02:00
2022-02-16 00:52:32 +01:00
case EditorSettings : : NOTIFICATION_EDITOR_SETTINGS_CHANGED : {
2023-03-02 13:37:02 +01:00
bool needs_update = false ;
2024-05-12 21:35:57 +02:00
if ( EditorThemeManager : : is_generated_theme_outdated ( ) & & ! sub_inspector ) {
2024-05-14 15:50:53 +02:00
add_theme_style_override ( SceneStringName ( panel ) , get_theme_stylebox ( SceneStringName ( panel ) , SNAME ( " Tree " ) ) ) ;
2024-05-12 21:35:57 +02:00
}
2023-03-02 13:37:02 +01:00
if ( use_settings_name_style & & EditorSettings : : get_singleton ( ) - > check_changed_settings_in_group ( " interface/editor/localize_settings " ) ) {
EditorPropertyNameProcessor : : Style style = EditorPropertyNameProcessor : : get_settings_style ( ) ;
if ( property_name_style ! = style ) {
property_name_style = style ;
needs_update = true ;
}
}
2024-04-25 23:16:18 +02:00
2022-03-05 13:25:32 +01:00
if ( EditorSettings : : get_singleton ( ) - > check_changed_settings_in_group ( " interface/inspector " ) ) {
2023-03-02 13:37:02 +01:00
needs_update = true ;
}
if ( needs_update ) {
2022-03-05 13:25:32 +01:00
update_tree ( ) ;
}
2022-02-16 00:52:32 +01:00
} break ;
2018-05-15 22:12:35 +02:00
}
}
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 ;
}
2023-12-06 23:10:32 +01:00
void EditorInspector : : add_custom_property_description ( const String & p_class , const String & p_property , const String & p_description ) {
2024-05-14 09:23:14 +02:00
const String key = vformat ( " property|%s|%s " , p_class , p_property ) ;
2023-12-06 23:10:32 +01:00
custom_property_descriptions [ key ] = p_description ;
}
String EditorInspector : : get_custom_property_description ( const String & p_property ) const {
HashMap < String , String > : : ConstIterator E = custom_property_descriptions . find ( p_property ) ;
if ( E ) {
return E - > value ;
}
return " " ;
}
2018-07-19 23:58:15 +02:00
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 ( ) ;
}
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 ;
}
2022-03-23 21:08:54 +01:00
void EditorInspector : : _add_meta_confirm ( ) {
String name = add_meta_name - > get_text ( ) ;
object - > editor_set_section_unfold ( " metadata " , true ) ; // Ensure metadata is unfolded when adding a new metadata.
Variant defval ;
Callable : : CallError ce ;
Variant : : construct ( Variant : : Type ( add_meta_type - > get_selected_id ( ) ) , defval , nullptr , 0 , ce ) ;
2022-12-23 23:53:16 +01:00
EditorUndoRedoManager * undo_redo = EditorUndoRedoManager : : get_singleton ( ) ;
2022-03-23 21:08:54 +01:00
undo_redo - > create_action ( vformat ( TTR ( " Add metadata %s " ) , name ) ) ;
undo_redo - > add_do_method ( object , " set_meta " , name , defval ) ;
undo_redo - > add_undo_method ( object , " remove_meta " , name ) ;
undo_redo - > commit_action ( ) ;
}
2023-06-26 19:18:27 +02:00
void EditorInspector : : _check_meta_name ( ) {
const String meta_name = add_meta_name - > get_text ( ) ;
2022-03-23 21:08:54 +01:00
2023-06-26 19:18:27 +02:00
if ( meta_name . is_empty ( ) ) {
validation_panel - > set_message ( EditorValidationPanel : : MSG_ID_DEFAULT , TTR ( " Metadata name can't be empty. " ) , EditorValidationPanel : : MSG_ERROR ) ;
} else if ( ! meta_name . is_valid_identifier ( ) ) {
validation_panel - > set_message ( EditorValidationPanel : : MSG_ID_DEFAULT , TTR ( " Metadata name must be a valid identifier. " ) , EditorValidationPanel : : MSG_ERROR ) ;
} else if ( object - > has_meta ( meta_name ) ) {
validation_panel - > set_message ( EditorValidationPanel : : MSG_ID_DEFAULT , vformat ( TTR ( " Metadata with name \" %s \" already exists. " ) , meta_name ) , EditorValidationPanel : : MSG_ERROR ) ;
} else if ( meta_name [ 0 ] = = ' _ ' ) {
validation_panel - > set_message ( EditorValidationPanel : : MSG_ID_DEFAULT , TTR ( " Names starting with _ are reserved for editor-only metadata. " ) , EditorValidationPanel : : MSG_ERROR ) ;
2022-03-23 21:08:54 +01:00
}
}
void EditorInspector : : _show_add_meta_dialog ( ) {
if ( ! add_meta_dialog ) {
add_meta_dialog = memnew ( ConfirmationDialog ) ;
2022-03-27 23:38:00 +02:00
2022-03-23 21:08:54 +01:00
VBoxContainer * vbc = memnew ( VBoxContainer ) ;
add_meta_dialog - > add_child ( vbc ) ;
2023-05-02 23:59:00 +02:00
2022-03-23 21:08:54 +01:00
HBoxContainer * hbc = memnew ( HBoxContainer ) ;
vbc - > add_child ( hbc ) ;
hbc - > add_child ( memnew ( Label ( TTR ( " Name: " ) ) ) ) ;
2023-05-02 23:59:00 +02:00
2022-03-23 21:08:54 +01:00
add_meta_name = memnew ( LineEdit ) ;
add_meta_name - > set_custom_minimum_size ( Size2 ( 200 * EDSCALE , 1 ) ) ;
hbc - > add_child ( add_meta_name ) ;
hbc - > add_child ( memnew ( Label ( TTR ( " Type: " ) ) ) ) ;
2023-05-02 23:59:00 +02:00
2022-03-23 21:08:54 +01:00
add_meta_type = memnew ( OptionButton ) ;
for ( int i = 0 ; i < Variant : : VARIANT_MAX ; i + + ) {
if ( i = = Variant : : NIL | | i = = Variant : : RID | | i = = Variant : : CALLABLE | | i = = Variant : : SIGNAL ) {
continue ; //not editable by inspector.
}
String type = i = = Variant : : OBJECT ? String ( " Resource " ) : Variant : : get_type_name ( Variant : : Type ( i ) ) ;
2023-08-13 02:33:39 +02:00
add_meta_type - > add_icon_item ( get_editor_theme_icon ( type ) , type , i ) ;
2022-03-23 21:08:54 +01:00
}
hbc - > add_child ( add_meta_type ) ;
2023-05-02 23:59:00 +02:00
Control * spacing = memnew ( Control ) ;
vbc - > add_child ( spacing ) ;
spacing - > set_custom_minimum_size ( Size2 ( 0 , 10 * EDSCALE ) ) ;
2022-07-08 02:31:19 +02:00
add_meta_dialog - > set_ok_button_text ( TTR ( " Add " ) ) ;
2022-03-23 21:08:54 +01:00
add_child ( add_meta_dialog ) ;
add_meta_dialog - > register_text_enter ( add_meta_name ) ;
add_meta_dialog - > connect ( " confirmed " , callable_mp ( this , & EditorInspector : : _add_meta_confirm ) ) ;
2023-05-02 23:59:00 +02:00
2023-06-26 19:18:27 +02:00
validation_panel = memnew ( EditorValidationPanel ) ;
vbc - > add_child ( validation_panel ) ;
validation_panel - > add_line ( EditorValidationPanel : : MSG_ID_DEFAULT , TTR ( " Metadata name is valid. " ) ) ;
validation_panel - > set_update_callback ( callable_mp ( this , & EditorInspector : : _check_meta_name ) ) ;
validation_panel - > set_accept_button ( add_meta_dialog - > get_ok_button ( ) ) ;
2022-03-23 21:08:54 +01:00
2023-06-26 19:18:27 +02:00
add_meta_name - > connect ( " text_changed " , callable_mp ( validation_panel , & EditorValidationPanel : : update ) . unbind ( 1 ) ) ;
2022-03-23 21:08:54 +01:00
}
2022-09-16 05:07:36 +02:00
Node * node = Object : : cast_to < Node > ( object ) ;
if ( node ) {
add_meta_dialog - > set_title ( vformat ( TTR ( " Add Metadata Property for \" %s \" " ) , node - > get_name ( ) ) ) ;
} else {
// This should normally be reached when the object is derived from Resource.
add_meta_dialog - > set_title ( vformat ( TTR ( " Add Metadata Property for \" %s \" " ) , object - > get_class ( ) ) ) ;
}
2022-03-23 21:08:54 +01:00
add_meta_dialog - > popup_centered ( ) ;
add_meta_name - > grab_focus ( ) ;
2023-06-26 19:18:27 +02:00
add_meta_name - > set_text ( " " ) ;
validation_panel - > update ( ) ;
2022-03-23 21:08:54 +01:00
}
2018-05-15 22:12:35 +02:00
void EditorInspector : : _bind_methods ( ) {
ClassDB : : bind_method ( " _edit_request_change " , & EditorInspector : : _edit_request_change ) ;
2022-09-19 16:20:34 +02:00
ClassDB : : bind_method ( " get_selected_path " , & EditorInspector : : get_selected_path ) ;
2023-09-07 18:39:05 +02:00
ClassDB : : bind_method ( " get_edited_object " , & EditorInspector : : get_edited_object ) ;
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 " ) ) ) ;
2021-12-03 16:23:32 +01:00
ADD_SIGNAL ( MethodInfo ( " property_keyed " , PropertyInfo ( Variant : : STRING , " property " ) , PropertyInfo ( Variant : : NIL , " value " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NIL_IS_VARIANT ) , PropertyInfo ( Variant : : BOOL , " advance " ) ) ) ;
2020-04-17 04:52:00 +02:00
ADD_SIGNAL ( MethodInfo ( " property_deleted " , PropertyInfo ( Variant : : STRING , " property " ) ) ) ;
2022-01-28 15:35:25 +01:00
ADD_SIGNAL ( MethodInfo ( " resource_selected " , PropertyInfo ( Variant : : OBJECT , " resource " , PROPERTY_HINT_RESOURCE_TYPE , " Resource " ) , PropertyInfo ( Variant : : STRING , " path " ) ) ) ;
2018-05-15 22:12:35 +02:00
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 " ) ) ) ;
2021-12-02 12:01:38 +01:00
ADD_SIGNAL ( MethodInfo ( " edited_object_changed " ) ) ;
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 ;
2018-05-15 22:12:35 +02:00
main_vbox = memnew ( VBoxContainer ) ;
main_vbox - > set_h_size_flags ( SIZE_EXPAND_FILL ) ;
2022-02-08 10:14:58 +01:00
main_vbox - > add_theme_constant_override ( " separation " , 0 ) ;
2018-05-15 22:12:35 +02:00
add_child ( main_vbox ) ;
2021-12-07 17:15:18 +01:00
set_horizontal_scroll_mode ( SCROLL_MODE_DISABLED ) ;
2023-07-02 20:44:42 +02:00
set_follow_focus ( true ) ;
2018-05-15 22:12:35 +02:00
changing = 0 ;
2020-04-02 01:20:12 +02:00
search_box = nullptr ;
2018-05-15 22:12:35 +02:00
_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 ;
2020-06-08 15:25:52 +02:00
property_clipboard = Variant ( ) ;
2018-07-19 00:37:17 +02:00
2021-11-30 17:46:36 +01:00
get_v_scroll_bar ( ) - > 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 ( ) ) {
2022-10-18 16:43:37 +02:00
refresh_countdown = float ( EDITOR_GET ( " docks/property_editor/auto_refresh_interval " ) ) ;
2021-02-10 21:18:45 +01:00
} 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
2022-09-30 16:44:27 +02:00
ED_SHORTCUT ( " property_editor/copy_value " , TTR ( " Copy Value " ) , KeyModifierMask : : CMD_OR_CTRL | Key : : C ) ;
ED_SHORTCUT ( " property_editor/paste_value " , TTR ( " Paste Value " ) , KeyModifierMask : : CMD_OR_CTRL | Key : : V ) ;
2022-09-02 11:37:48 +02:00
ED_SHORTCUT ( " property_editor/copy_property_path " , TTR ( " Copy Property Path " ) , KeyModifierMask : : CMD_OR_CTRL | KeyModifierMask : : SHIFT | Key : : C ) ;
2023-03-02 13:37:02 +01:00
// `use_settings_name_style` is true by default, set the name style accordingly.
set_property_name_style ( EditorPropertyNameProcessor : : get_singleton ( ) - > get_settings_style ( ) ) ;
2018-05-15 22:12:35 +02:00
}