3535#include " editor/debugger/editor_debugger_inspector.h"
3636#include " editor/doc/doc_tools.h"
3737#include " editor/docks/inspector_dock.h"
38+ #include " editor/editor_interface.h"
3839#include " editor/editor_main_screen.h"
3940#include " editor/editor_node.h"
4041#include " editor/editor_string_names.h"
@@ -1757,8 +1758,68 @@ Size2 EditorInspectorCategory::get_minimum_size() const {
17571758 return ms;
17581759}
17591760
1761+ void EditorInspectorCategory::_collect_properties (const Object *p_object, Vector<String> &p_properties) const {
1762+ List<PropertyInfo> property_list;
1763+ p_object->get_property_list (&property_list, true );
1764+
1765+ String current_category = " " ;
1766+ for (const PropertyInfo &prop_info : property_list) {
1767+ if (prop_info.usage & PROPERTY_USAGE_GROUP) {
1768+ continue ;
1769+ }
1770+ if (prop_info.usage & PROPERTY_USAGE_CATEGORY) {
1771+ current_category = prop_info.name ;
1772+ continue ;
1773+ }
1774+ if (!(prop_info.usage & PROPERTY_USAGE_EDITOR)) {
1775+ continue ;
1776+ }
1777+ if (current_category != label && !current_category.ends_with (" .gd" )) {
1778+ continue ;
1779+ }
1780+
1781+ p_properties.push_back (prop_info.name );
1782+ }
1783+ }
1784+
17601785void EditorInspectorCategory::_handle_menu_option (int p_option) {
17611786 switch (p_option) {
1787+ case MENU_COPY_VALUE: {
1788+ Object *object = EditorInterface::get_singleton ()->get_inspector ()->get_edited_object ();
1789+ Dictionary clipboard;
1790+ Vector<String> properties;
1791+ _collect_properties (object, properties);
1792+
1793+ clipboard[" path" ] = doc_class_name;
1794+ for (String property_name : properties) {
1795+ clipboard[property_name] = object->get (property_name);
1796+ }
1797+ InspectorDock::get_inspector_singleton ()->set_property_clipboard (clipboard);
1798+ } break ;
1799+
1800+ case MENU_PASTE_VALUE: {
1801+ Object *object = EditorInterface::get_singleton ()->get_inspector ()->get_edited_object ();
1802+ Dictionary clipboard = InspectorDock::get_inspector_singleton ()->get_property_clipboard ();
1803+ String category_name = clipboard[" path" ];
1804+
1805+ if (category_name != doc_class_name) {
1806+ break ;
1807+ }
1808+
1809+ String action_name = " Set category " + category_name;
1810+
1811+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton ();
1812+ undo_redo->create_action (action_name);
1813+
1814+ for (String property_name : clipboard.keys ()) {
1815+ Variant value = clipboard[property_name];
1816+ undo_redo->add_do_property (object, property_name, value);
1817+ undo_redo->add_undo_property (object, property_name, object->get (property_name));
1818+ }
1819+
1820+ undo_redo->commit_action ();
1821+ } break ;
1822+
17621823 case MENU_OPEN_DOCS: {
17631824 ScriptEditor::get_singleton ()->goto_help (" class:" + doc_class_name);
17641825 EditorNode::get_singleton ()->get_editor_main_screen ()->select (EditorMainScreen::EDITOR_SCRIPT);
@@ -1781,6 +1842,8 @@ void EditorInspectorCategory::_popup_context_menu(const Point2i &p_position) {
17811842 if (is_favorite) {
17821843 menu->add_item (TTRC (" Unfavorite All" ), MENU_UNFAVORITE_ALL);
17831844 } else {
1845+ menu->add_icon_shortcut (get_editor_theme_icon (SNAME (" ActionCopy" )), ED_GET_SHORTCUT (" property_editor/copy_category_values" ), MENU_COPY_VALUE);
1846+ menu->add_icon_shortcut (get_editor_theme_icon (SNAME (" ActionPaste" )), ED_GET_SHORTCUT (" property_editor/paste_category_values" ), MENU_PASTE_VALUE);
17841847 menu->add_item (TTRC (" Open Documentation" ), MENU_OPEN_DOCS);
17851848 menu->set_item_disabled (-1 , !EditorHelp::get_doc_data ()->class_list .has (doc_class_name));
17861849 }
@@ -2214,9 +2277,10 @@ Control *EditorInspectorSection::make_custom_tooltip(const String &p_text) const
22142277 return nullptr ;
22152278}
22162279
2217- 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, int p_level) {
2280+ void EditorInspectorSection::setup (const String &p_inspector_path, const String & p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable, int p_indent_depth, int p_level) {
22182281 section = p_section;
22192282 label = p_label;
2283+ inspector_path = p_inspector_path;
22202284 object = p_object;
22212285 bg_color = p_bg_color;
22222286 foldable = p_foldable;
@@ -2307,6 +2371,12 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) {
23072371 fold ();
23082372 }
23092373 }
2374+ } else if ((!checkable || checked) && inspector_path != " " && mb.is_valid () && mb->is_pressed () && mb->get_button_index () == MouseButton::RIGHT) {
2375+ accept_event ();
2376+ _update_popup ();
2377+ menu->set_position (get_screen_position () + get_local_mouse_position ());
2378+ menu->reset_size ();
2379+ menu->popup ();
23102380 } else if (mb.is_valid () && !mb->is_pressed ()) {
23112381 queue_redraw ();
23122382 }
@@ -2450,6 +2520,103 @@ void EditorInspectorSection::update_property() {
24502520 }
24512521}
24522522
2523+ void EditorInspectorSection::_update_popup () {
2524+ if (menu) {
2525+ menu->clear ();
2526+ } else {
2527+ menu = memnew (PopupMenu);
2528+ add_child (menu);
2529+ menu->connect (SceneStringName (id_pressed), callable_mp (this , &EditorInspectorSection::menu_option));
2530+ }
2531+
2532+ menu->add_icon_shortcut (get_editor_theme_icon (SNAME (" ActionCopy" )), ED_GET_SHORTCUT (" property_editor/copy_group_values" ), MENU_COPY_VALUE);
2533+ menu->add_icon_shortcut (get_editor_theme_icon (SNAME (" ActionPaste" )), ED_GET_SHORTCUT (" property_editor/paste_group_values" ), MENU_PASTE_VALUE);
2534+ }
2535+
2536+ void EditorInspectorSection::_collect_properties (Vector<String> &p_properties) const {
2537+ List<PropertyInfo> property_list;
2538+ object->get_property_list (&property_list, true );
2539+
2540+ String current_category = " " ;
2541+ String current_group = " " ;
2542+ String current_subgroup = " " ;
2543+ for (const PropertyInfo &prop_info : property_list) {
2544+ if (prop_info.usage & PROPERTY_USAGE_GROUP) {
2545+ current_group = prop_info.name ;
2546+ continue ;
2547+ }
2548+ if (prop_info.usage & PROPERTY_USAGE_SUBGROUP) {
2549+ current_subgroup = prop_info.name ;
2550+ continue ;
2551+ }
2552+ if (prop_info.usage & PROPERTY_USAGE_CATEGORY) {
2553+ current_category = prop_info.name ;
2554+ current_group = " " ;
2555+ current_subgroup = " " ;
2556+ continue ;
2557+ }
2558+ if (!(prop_info.usage & PROPERTY_USAGE_EDITOR)) {
2559+ continue ;
2560+ }
2561+ if (prop_info.name .split (" /" ).size () > 1 ) {
2562+ current_group = prop_info.name .split (" /" )[0 ];
2563+ }
2564+ if (!(current_category + " /" + current_group + " /" + current_subgroup).begins_with (get_inspector_path ())) {
2565+ if (current_category.ends_with (" .gd" )) {
2566+ String section_path = inspector_path;
2567+ section_path = section_path.replace (section_path.split (" /" )[0 ] + " /" , " " );
2568+ if (!(current_group + " /" + current_subgroup).begins_with (section_path)) {
2569+ continue ;
2570+ }
2571+ } else {
2572+ continue ;
2573+ }
2574+ }
2575+
2576+ p_properties.push_back (prop_info.name );
2577+ }
2578+ }
2579+
2580+ void EditorInspectorSection::menu_option (int p_option) const {
2581+ switch (p_option) {
2582+ case MENU_COPY_VALUE: {
2583+ Vector<String> properties;
2584+ Dictionary clipboard;
2585+ _collect_properties (properties);
2586+
2587+ clipboard[" path" ] = get_inspector_path ();
2588+ for (String property_name : properties) {
2589+ clipboard[property_name] = object->get (property_name);
2590+ }
2591+ InspectorDock::get_inspector_singleton ()->set_property_clipboard (clipboard);
2592+ } break ;
2593+ case MENU_PASTE_VALUE: {
2594+ Dictionary clipboard = InspectorDock::get_inspector_singleton ()->get_property_clipboard ();
2595+ String group_name = clipboard[" path" ];
2596+
2597+ if (!get_inspector_path ().begins_with (group_name)) {
2598+ break ;
2599+ }
2600+
2601+ Vector<String> properties;
2602+ String action_name = " Set group " + group_name;
2603+
2604+ _collect_properties (properties);
2605+
2606+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton ();
2607+ undo_redo->create_action (action_name);
2608+
2609+ for (String property_name : properties) {
2610+ Variant value = clipboard[property_name];
2611+ undo_redo->add_do_property (object, property_name, value);
2612+ undo_redo->add_undo_property (object, property_name, object->get (property_name));
2613+ }
2614+
2615+ undo_redo->commit_action ();
2616+ } break ;
2617+ }
2618+ }
2619+
24532620void EditorInspectorSection::_bind_methods () {
24542621 ClassDB::bind_method (D_METHOD (" setup" , " section" , " label" , " object" , " bg_color" , " foldable" , " indent_depth" , " level" ), &EditorInspectorSection::setup, DEFVAL (0 ), DEFVAL (1 ));
24552622 ClassDB::bind_method (D_METHOD (" get_vbox" ), &EditorInspectorSection::get_vbox);
@@ -3240,7 +3407,7 @@ void EditorInspectorArray::_bind_methods() {
32403407 ADD_SIGNAL (MethodInfo (" page_change_request" ));
32413408}
32423409
3243- 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_is_const, bool p_numbered, int p_page_length, const String &p_add_item_text) {
3410+ void EditorInspectorArray::setup_with_move_element_function (Object *p_object, const String &p_category, 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_is_const, bool p_numbered, int p_page_length, const String &p_add_item_text) {
32443411 count_property = " " ;
32453412 mode = MODE_USE_MOVE_ARRAY_ELEMENT_FUNCTION;
32463413 array_element_prefix = p_array_element_prefix;
@@ -3250,12 +3417,12 @@ void EditorInspectorArray::setup_with_move_element_function(Object *p_object, co
32503417 page_length = p_page_length;
32513418 numbered = p_numbered;
32523419
3253- EditorInspectorSection::setup (String (p_array_element_prefix) + " _array" , p_label, p_object, p_bg_color, p_foldable, 0 );
3420+ EditorInspectorSection::setup (p_category + " / " + p_label. to_lower (), String (p_array_element_prefix) + " _array" , p_label, p_object, p_bg_color, p_foldable, 0 );
32543421
32553422 _setup ();
32563423}
32573424
3258- 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_is_const, bool p_numbered, int p_page_length, const String &p_add_item_text, const String &p_swap_method) {
3425+ void EditorInspectorArray::setup_with_count_property (Object *p_object, const String &p_category, 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_is_const, bool p_numbered, int p_page_length, const String &p_add_item_text, const String &p_swap_method) {
32593426 count_property = p_count_property;
32603427 mode = MODE_USE_COUNT_PROPERTY;
32613428 array_element_prefix = p_array_element_prefix;
@@ -3267,7 +3434,7 @@ void EditorInspectorArray::setup_with_count_property(Object *p_object, const Str
32673434 swap_method = p_swap_method;
32683435
32693436 add_button->set_text (p_add_item_text);
3270- EditorInspectorSection::setup (String (count_property) + " _array" , p_label, p_object, p_bg_color, p_foldable, 0 );
3437+ EditorInspectorSection::setup (p_category + " / " + p_label. to_lower (), String (count_property) + " _array" , p_label, p_object, p_bg_color, p_foldable, 0 );
32713438
32723439 _setup ();
32733440}
@@ -4135,7 +4302,7 @@ void EditorInspector::update_tree() {
41354302
41364303 Color c = sscolor;
41374304 c.a /= level;
4138- section->setup (acc_path, label, object, c, use_folding, section_depth, level);
4305+ section->setup ((doc_name. is_empty () ? acc_path : String (doc_name) + (acc_path. is_empty () ? " " : " / " + acc_path)), acc_path, label, object, c, use_folding, section_depth, level);
41394306 section->set_tooltip_text (tooltip);
41404307
41414308 section->connect (" section_toggled_by_user" , callable_mp (this , &EditorInspector::_section_toggled_by_user));
@@ -4202,7 +4369,7 @@ void EditorInspector::update_tree() {
42024369 String array_label = path.contains_char (' /' ) ? path.substr (path.rfind_char (' /' ) + 1 ) : path;
42034370 array_label = EditorPropertyNameProcessor::get_singleton ()->process_name (property_label_string, property_name_style, p.name , doc_name);
42044371 int page = per_array_page.has (array_element_prefix) ? per_array_page[array_element_prefix] : 0 ;
4205- editor_inspector_array->setup_with_move_element_function (object, array_label, array_element_prefix, page, c, use_folding);
4372+ editor_inspector_array->setup_with_move_element_function (object, doc_name, array_label, array_element_prefix, page, c, use_folding);
42064373 editor_inspector_array->connect (" page_change_request" , callable_mp (this , &EditorInspector::_page_change_request).bind (array_element_prefix));
42074374 } else if (p.type == Variant::INT) {
42084375 // Setup the array to use the count property and built-in functions to create/move/delete elements.
@@ -4211,7 +4378,7 @@ void EditorInspector::update_tree() {
42114378 editor_inspector_array = memnew (EditorInspectorArray (all_read_only));
42124379 int page = per_array_page.has (array_element_prefix) ? per_array_page[array_element_prefix] : 0 ;
42134380
4214- editor_inspector_array->setup_with_count_property (object, class_name_components[0 ], p.name , array_element_prefix, page, c, foldable, movable, is_const, numbered, page_size, add_button_text, swap_method);
4381+ editor_inspector_array->setup_with_count_property (object, doc_name, class_name_components[0 ], p.name , array_element_prefix, page, c, foldable, movable, is_const, numbered, page_size, add_button_text, swap_method);
42154382 editor_inspector_array->connect (" page_change_request" , callable_mp (this , &EditorInspector::_page_change_request).bind (array_element_prefix));
42164383 }
42174384 }
@@ -4559,7 +4726,8 @@ void EditorInspector::update_tree() {
45594726 get_root_inspector ()->get_v_scroll_bar ()->connect (SceneStringName (value_changed), callable_mp (section, &EditorInspectorSection::reset_timer).unbind (1 ));
45604727 favorites_groups_vbox->add_child (section);
45614728 parent_vbox = section->get_vbox ();
4562- section->setup (" " , section_name, object, sscolor, false );
4729+
4730+ section->setup (" " , " " , section_name, object, sscolor, false );
45634731 section->set_tooltip_text (tooltip);
45644732
45654733 if (togglable_editor_inspector_sections.has (section_name)) {
@@ -4598,7 +4766,7 @@ void EditorInspector::update_tree() {
45984766 get_root_inspector ()->get_v_scroll_bar ()->connect (SceneStringName (value_changed), callable_mp (section, &EditorInspectorSection::reset_timer).unbind (1 ));
45994767 vbox->add_child (section);
46004768 vbox = section->get_vbox ();
4601- section->setup (" " , section_name, object, sscolor, false );
4769+ section->setup (" " , " " , section_name, object, sscolor, false );
46024770 section->set_tooltip_text (tooltip);
46034771
46044772 if (togglable_editor_inspector_sections.has (KV.key + " /" + section_name)) {
@@ -5782,6 +5950,10 @@ EditorInspector::EditorInspector() {
57825950
57835951 ED_SHORTCUT (" property_editor/copy_value" , TTRC (" Copy Value" ), KeyModifierMask::CMD_OR_CTRL | Key::C);
57845952 ED_SHORTCUT (" property_editor/paste_value" , TTRC (" Paste Value" ), KeyModifierMask::CMD_OR_CTRL | Key::V);
5953+ ED_SHORTCUT (" property_editor/copy_category_values" , TTRC (" Copy Category Values" ), KeyModifierMask::CMD_OR_CTRL | Key::C);
5954+ ED_SHORTCUT (" property_editor/paste_category_values" , TTRC (" Paste Category Values" ), KeyModifierMask::CMD_OR_CTRL | Key::V);
5955+ ED_SHORTCUT (" property_editor/copy_group_values" , TTRC (" Copy Group Values" ), KeyModifierMask::CMD_OR_CTRL | Key::C);
5956+ ED_SHORTCUT (" property_editor/paste_group_values" , TTRC (" Paste Group Values" ), KeyModifierMask::CMD_OR_CTRL | Key::V);
57855957 ED_SHORTCUT (" property_editor/copy_property_path" , TTRC (" Copy Property Path" ), KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::SHIFT | Key::C);
57865958
57875959 // `use_settings_name_style` is true by default, set the name style accordingly.
0 commit comments