Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/modules/objectdb_profiler/editor/data_viewers/node_view.cpp
20984 views
1
/**************************************************************************/
2
/* node_view.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "node_view.h"
32
33
#include "editor/editor_node.h"
34
#include "editor/themes/editor_scale.h"
35
#include "scene/gui/check_button.h"
36
#include "scene/gui/popup_menu.h"
37
#include "scene/gui/split_container.h"
38
39
SnapshotNodeView::SnapshotNodeView() {
40
set_name(TTRC("Nodes"));
41
}
42
43
void SnapshotNodeView::show_snapshot(GameStateSnapshot *p_data, GameStateSnapshot *p_diff_data) {
44
SnapshotView::show_snapshot(p_data, p_diff_data);
45
46
set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);
47
set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);
48
49
HSplitContainer *diff_sides = memnew(HSplitContainer);
50
diff_sides->set_anchors_preset(LayoutPreset::PRESET_FULL_RECT);
51
add_child(diff_sides);
52
53
main_tree = _make_node_tree(diff_data && !combined_diff_view ? TTRC("A Nodes") : TTRC("Nodes"));
54
diff_sides->add_child(main_tree.root);
55
_add_snapshot_to_tree(main_tree.tree, snapshot_data, diff_data && combined_diff_view ? DIFF_GROUP_REMOVED : DIFF_GROUP_NONE);
56
57
if (diff_data) {
58
CheckButton *diff_mode_toggle = memnew(CheckButton(TTRC("Combine Diff")));
59
diff_mode_toggle->set_pressed(combined_diff_view);
60
diff_mode_toggle->connect(SceneStringName(toggled), callable_mp(this, &SnapshotNodeView::_toggle_diff_mode));
61
main_tree.filter_bar->add_child(diff_mode_toggle);
62
main_tree.filter_bar->move_child(diff_mode_toggle, 0);
63
64
if (combined_diff_view) {
65
// Merge the snapshots together and add a diff.
66
_add_snapshot_to_tree(main_tree.tree, diff_data, DIFF_GROUP_ADDED);
67
} else {
68
// Add a second column with the diff snapshot.
69
diff_tree = _make_node_tree(TTRC("B Nodes"));
70
diff_sides->add_child(diff_tree.root);
71
_add_snapshot_to_tree(diff_tree.tree, diff_data, DIFF_GROUP_NONE);
72
}
73
}
74
75
_refresh_icons();
76
main_tree.filter_bar->apply();
77
if (diff_tree.filter_bar) {
78
diff_tree.filter_bar->apply();
79
diff_sides->set_split_offset(diff_sides->get_size().x * 0.5);
80
}
81
82
choose_object_menu = memnew(PopupMenu);
83
add_child(choose_object_menu);
84
choose_object_menu->connect(SceneStringName(id_pressed), callable_mp(this, &SnapshotNodeView::_choose_object_pressed).bind(false));
85
}
86
87
NodeTreeElements SnapshotNodeView::_make_node_tree(const String &p_tree_name) {
88
NodeTreeElements elements;
89
elements.root = memnew(VBoxContainer);
90
elements.root->set_anchors_preset(LayoutPreset::PRESET_FULL_RECT);
91
elements.tree = memnew(Tree);
92
elements.filter_bar = memnew(TreeSortAndFilterBar(elements.tree, TTRC("Filter Nodes")));
93
elements.root->add_child(elements.filter_bar);
94
elements.tree->set_select_mode(Tree::SelectMode::SELECT_ROW);
95
elements.tree->set_custom_minimum_size(Size2(150, 0) * EDSCALE);
96
elements.tree->set_hide_folding(false);
97
elements.root->add_child(elements.tree);
98
elements.tree->set_hide_root(true);
99
elements.tree->set_allow_reselect(true);
100
elements.tree->set_columns(1);
101
elements.tree->set_column_titles_visible(true);
102
elements.tree->set_column_title(0, p_tree_name);
103
elements.tree->set_column_expand(0, true);
104
elements.tree->set_column_clip_content(0, false);
105
elements.tree->set_column_custom_minimum_width(0, 150 * EDSCALE);
106
elements.tree->set_theme_type_variation("TreeSecondary");
107
elements.tree->connect(SceneStringName(item_selected), callable_mp(this, &SnapshotNodeView::_node_selected).bind(elements.tree));
108
elements.tree->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);
109
elements.tree->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);
110
elements.tree->set_anchors_preset(LayoutPreset::PRESET_FULL_RECT);
111
112
elements.tree->create_item();
113
114
return elements;
115
}
116
117
void SnapshotNodeView::_node_selected(Tree *p_tree_selected_from) {
118
active_tree = p_tree_selected_from;
119
if (diff_tree.tree) {
120
// Deselect nodes in non-active tree, if needed.
121
if (active_tree == main_tree.tree) {
122
diff_tree.tree->deselect_all();
123
}
124
if (active_tree == diff_tree.tree) {
125
main_tree.tree->deselect_all();
126
}
127
}
128
129
const LocalVector<SnapshotDataObject *> &item_data = tree_item_data[p_tree_selected_from->get_selected()];
130
if (item_data.is_empty()) {
131
return;
132
} else if (item_data.size() == 1) {
133
EditorNode::get_singleton()->push_item(static_cast<Object *>(item_data[0]));
134
} else if (item_data.size() == 2) {
135
// This happens if we're in the combined diff view and the node exists in both trees
136
// The user has to specify which version of the node they want to see in the inspector.
137
_show_choose_object_menu();
138
}
139
}
140
141
void SnapshotNodeView::_toggle_diff_mode(bool p_state) {
142
combined_diff_view = p_state;
143
show_snapshot(snapshot_data, diff_data); // Redraw everything when we toggle views.
144
}
145
146
void SnapshotNodeView::_notification(int p_what) {
147
if (p_what == NOTIFICATION_THEME_CHANGED) {
148
_refresh_icons();
149
}
150
}
151
152
void SnapshotNodeView::_add_snapshot_to_tree(Tree *p_tree, GameStateSnapshot *p_snapshot, DiffGroup p_diff_group) {
153
SnapshotDataObject *scene_root = nullptr;
154
LocalVector<SnapshotDataObject *> orphan_nodes;
155
156
for (const KeyValue<ObjectID, SnapshotDataObject *> &kv : p_snapshot->objects) {
157
if (kv.value->is_node() && !kv.value->extra_debug_data.has("node_parent")) {
158
if (kv.value->extra_debug_data["node_is_scene_root"]) {
159
scene_root = kv.value;
160
} else {
161
orphan_nodes.push_back(kv.value);
162
}
163
}
164
}
165
166
if (scene_root != nullptr) {
167
TreeItem *root_item = _add_item_to_tree(p_tree, p_tree->get_root(), scene_root, p_diff_group);
168
_add_children_to_tree(root_item, scene_root, p_diff_group);
169
}
170
171
if (!orphan_nodes.is_empty()) {
172
TreeItem *orphans_item = _add_item_to_tree(p_tree, p_tree->get_root(), TTRC("Orphan Nodes"), p_diff_group);
173
for (SnapshotDataObject *orphan_node : orphan_nodes) {
174
TreeItem *orphan_item = _add_item_to_tree(p_tree, orphans_item, orphan_node, p_diff_group);
175
_add_children_to_tree(orphan_item, orphan_node, p_diff_group);
176
}
177
}
178
}
179
180
void SnapshotNodeView::_add_children_to_tree(TreeItem *p_parent_item, SnapshotDataObject *p_data, DiffGroup p_diff_group) {
181
for (const Variant &child_id : (Array)p_data->extra_debug_data["node_children"]) {
182
SnapshotDataObject *child_object = p_data->snapshot->objects[ObjectID((uint64_t)child_id)];
183
TreeItem *child_item = _add_item_to_tree(p_parent_item->get_tree(), p_parent_item, child_object, p_diff_group);
184
_add_children_to_tree(child_item, child_object, p_diff_group);
185
}
186
}
187
188
TreeItem *SnapshotNodeView::_add_item_to_tree(Tree *p_tree, TreeItem *p_parent, const String &p_item_name, DiffGroup p_diff_group) {
189
// Find out if this node already exists.
190
TreeItem *item = nullptr;
191
if (p_diff_group != DIFF_GROUP_NONE) {
192
for (int idx = 0; idx < p_parent->get_child_count(); idx++) {
193
TreeItem *child = p_parent->get_child(idx);
194
if (child->get_text(0) == p_item_name) {
195
item = child;
196
break;
197
}
198
}
199
}
200
201
if (item) {
202
// If it exists, clear the background color because we now know it exists in both trees.
203
item->clear_custom_bg_color(0);
204
} else {
205
// Add the new node and set its background color to green or red depending on which snapshot it's a part of.
206
item = p_tree->create_item(p_parent);
207
208
if (p_diff_group == DIFF_GROUP_ADDED) {
209
item->set_custom_bg_color(0, Color(0, 1, 0, 0.1));
210
} else if (p_diff_group == DIFF_GROUP_REMOVED) {
211
item->set_custom_bg_color(0, Color(1, 0, 0, 0.1));
212
}
213
}
214
215
item->set_text(0, p_item_name);
216
item->set_auto_translate_mode(0, AUTO_TRANSLATE_MODE_DISABLED);
217
218
return item;
219
}
220
221
TreeItem *SnapshotNodeView::_add_item_to_tree(Tree *p_tree, TreeItem *p_parent, SnapshotDataObject *p_data, DiffGroup p_diff_group) {
222
String node_name = p_data->extra_debug_data["node_name"];
223
TreeItem *child_item = _add_item_to_tree(p_tree, p_parent, node_name, p_diff_group);
224
tree_item_data[child_item].push_back(p_data);
225
return child_item;
226
}
227
228
void SnapshotNodeView::_refresh_icons() {
229
for (TreeItem *item : _get_children_recursive(main_tree.tree)) {
230
HashMap<TreeItem *, LocalVector<SnapshotDataObject *>>::Iterator E = tree_item_data.find(item);
231
if (E && !E->value.is_empty()) {
232
item->set_icon(0, EditorNode::get_singleton()->get_class_icon(E->value[0]->type_name));
233
} else {
234
item->set_icon(0, EditorNode::get_singleton()->get_class_icon("MissingNode"));
235
}
236
}
237
238
if (diff_tree.tree) {
239
for (TreeItem *item : _get_children_recursive(diff_tree.tree)) {
240
HashMap<TreeItem *, LocalVector<SnapshotDataObject *>>::Iterator E = tree_item_data.find(item);
241
if (E && !E->value.is_empty()) {
242
item->set_icon(0, EditorNode::get_singleton()->get_class_icon(E->value[0]->type_name));
243
} else {
244
item->set_icon(0, EditorNode::get_singleton()->get_class_icon("MissingNode"));
245
}
246
}
247
}
248
}
249
250
void SnapshotNodeView::clear_snapshot() {
251
SnapshotView::clear_snapshot();
252
253
tree_item_data.clear();
254
main_tree.tree = nullptr;
255
main_tree.filter_bar = nullptr;
256
main_tree.root = nullptr;
257
diff_tree.tree = nullptr;
258
diff_tree.filter_bar = nullptr;
259
diff_tree.root = nullptr;
260
active_tree = nullptr;
261
}
262
263
void SnapshotNodeView::_choose_object_pressed(int p_object_idx, bool p_confirm_override) {
264
EditorNode::get_singleton()->push_item(static_cast<Object *>(tree_item_data[active_tree->get_selected()][p_object_idx]));
265
}
266
267
void SnapshotNodeView::_show_choose_object_menu() {
268
remove_child(choose_object_menu);
269
add_child(choose_object_menu);
270
choose_object_menu->clear(false);
271
choose_object_menu->add_item(TTRC("Snapshot A"), 0);
272
choose_object_menu->add_item(TTRC("Snapshot B"), 1);
273
choose_object_menu->reset_size();
274
choose_object_menu->set_position(get_screen_position() + get_local_mouse_position());
275
choose_object_menu->popup();
276
}
277
278