Skip to content

Commit 79b3fa4

Browse files
committed
Added Copy/Paste Properties For Groups
Added Copy/Paste Properties For Categories Fixed SubGroup Not Showing PopupMenu Disables popmenu on unchecked groups Format code Changed _collect_properties for EditorInspectorCategory and EditorInspectorSection Fixed coding style Fixes copy/paste for EditorInspectorArray Fixes check for EditorInspectorArray Fixes empty paste null reference + Allow to copy/paste group using hierarchy
1 parent d61cd91 commit 79b3fa4

File tree

3 files changed

+205
-17
lines changed

3 files changed

+205
-17
lines changed

editor/inspector/editor_inspector.cpp

Lines changed: 182 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
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+
17601785
void 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+
24532620
void 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.

editor/inspector/editor_inspector.h

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@ class EditorInspectorCategory : public Control {
320320

321321
// Right-click context menu options.
322322
enum ClassMenuOption {
323+
MENU_COPY_VALUE,
324+
MENU_PASTE_VALUE,
323325
MENU_OPEN_DOCS,
324326
MENU_UNFAVORITE_ALL,
325327
};
@@ -350,6 +352,7 @@ class EditorInspectorCategory : public Control {
350352
bool is_favorite = false;
351353
bool menu_icon_dirty = true;
352354

355+
void _collect_properties(const Object *p_object, Vector<String> &p_properties) const;
353356
void _handle_menu_option(int p_option);
354357
void _popup_context_menu(const Point2i &p_position);
355358
void _update_icon();
@@ -378,8 +381,14 @@ class EditorInspectorSection : public Container {
378381

379382
friend class EditorInspector;
380383

384+
enum MenuItems {
385+
MENU_COPY_VALUE,
386+
MENU_PASTE_VALUE,
387+
};
388+
381389
String label;
382390
String section;
391+
String inspector_path;
383392
Color bg_color;
384393
bool vbox_added = false; // Optimization.
385394
bool foldable = false;
@@ -401,6 +410,8 @@ class EditorInspectorSection : public Container {
401410

402411
bool checkbox_only = false;
403412

413+
PopupMenu *menu = nullptr;
414+
404415
HashSet<StringName> revertable_properties;
405416

406417
void _test_unfold();
@@ -458,9 +469,10 @@ class EditorInspectorSection : public Container {
458469
virtual Size2 get_minimum_size() const override;
459470
virtual Control *make_custom_tooltip(const String &p_text) const override;
460471

461-
void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable, int p_indent_depth = 0, int p_level = 1);
472+
void 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 = 0, int p_level = 1);
462473
String get_section() const;
463474
String get_label() const { return label; }
475+
String get_inspector_path() const { return inspector_path; }
464476
VBoxContainer *get_vbox();
465477
void unfold();
466478
void fold();
@@ -476,6 +488,10 @@ class EditorInspectorSection : public Container {
476488
void _property_edited(const String &p_property);
477489
void update_property();
478490

491+
void _update_popup();
492+
void _collect_properties(Vector<String> &p_properties) const;
493+
void menu_option(int p_option) const;
494+
479495
EditorInspectorSection();
480496
~EditorInspectorSection();
481497
};
@@ -595,8 +611,8 @@ class EditorInspectorArray : public EditorInspectorSection {
595611
static void _bind_methods();
596612

597613
public:
598-
void 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 = true, bool p_is_const = false, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "");
599-
void 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 = true, bool p_is_const = false, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = "");
614+
void 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 = true, bool p_is_const = false, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "");
615+
void 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 = true, bool p_is_const = false, bool p_numbered = false, int p_page_length = 5, const String &p_add_item_text = "", const String &p_swap_method = "");
600616
VBoxContainer *get_vbox(int p_index);
601617

602618
void show_menu(int p_index, const Vector2 &p_offset);

0 commit comments

Comments
 (0)