node_view.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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. #include "node_view.h"
  31. #include "editor/editor_node.h"
  32. #include "editor/themes/editor_scale.h"
  33. #include "scene/gui/check_button.h"
  34. #include "scene/gui/popup_menu.h"
  35. #include "scene/gui/split_container.h"
  36. SnapshotNodeView::SnapshotNodeView() {
  37. set_name(TTRC("Nodes"));
  38. }
  39. void SnapshotNodeView::show_snapshot(GameStateSnapshot *p_data, GameStateSnapshot *p_diff_data) {
  40. SnapshotView::show_snapshot(p_data, p_diff_data);
  41. set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);
  42. set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);
  43. HSplitContainer *diff_sides = memnew(HSplitContainer);
  44. diff_sides->set_anchors_preset(LayoutPreset::PRESET_FULL_RECT);
  45. add_child(diff_sides);
  46. main_tree = _make_node_tree(diff_data && !combined_diff_view ? TTRC("A Nodes") : TTRC("Nodes"));
  47. diff_sides->add_child(main_tree.root);
  48. _add_snapshot_to_tree(main_tree.tree, snapshot_data, diff_data && combined_diff_view ? DIFF_GROUP_REMOVED : DIFF_GROUP_NONE);
  49. if (diff_data) {
  50. CheckButton *diff_mode_toggle = memnew(CheckButton(TTRC("Combine Diff")));
  51. diff_mode_toggle->set_pressed(combined_diff_view);
  52. diff_mode_toggle->connect(SceneStringName(toggled), callable_mp(this, &SnapshotNodeView::_toggle_diff_mode));
  53. main_tree.filter_bar->add_child(diff_mode_toggle);
  54. main_tree.filter_bar->move_child(diff_mode_toggle, 0);
  55. if (combined_diff_view) {
  56. // Merge the snapshots together and add a diff.
  57. _add_snapshot_to_tree(main_tree.tree, diff_data, DIFF_GROUP_ADDED);
  58. } else {
  59. // Add a second column with the diff snapshot.
  60. diff_tree = _make_node_tree(TTRC("B Nodes"));
  61. diff_sides->add_child(diff_tree.root);
  62. _add_snapshot_to_tree(diff_tree.tree, diff_data, DIFF_GROUP_NONE);
  63. }
  64. }
  65. _refresh_icons();
  66. main_tree.filter_bar->apply();
  67. if (diff_tree.filter_bar) {
  68. diff_tree.filter_bar->apply();
  69. diff_sides->set_split_offset(diff_sides->get_size().x * 0.5);
  70. }
  71. choose_object_menu = memnew(PopupMenu);
  72. add_child(choose_object_menu);
  73. choose_object_menu->connect(SceneStringName(id_pressed), callable_mp(this, &SnapshotNodeView::_choose_object_pressed).bind(false));
  74. }
  75. NodeTreeElements SnapshotNodeView::_make_node_tree(const String &p_tree_name) {
  76. NodeTreeElements elements;
  77. elements.root = memnew(VBoxContainer);
  78. elements.root->set_anchors_preset(LayoutPreset::PRESET_FULL_RECT);
  79. elements.tree = memnew(Tree);
  80. elements.filter_bar = memnew(TreeSortAndFilterBar(elements.tree, TTRC("Filter Nodes")));
  81. elements.root->add_child(elements.filter_bar);
  82. elements.tree->set_select_mode(Tree::SelectMode::SELECT_ROW);
  83. elements.tree->set_custom_minimum_size(Size2(150, 0) * EDSCALE);
  84. elements.tree->set_hide_folding(false);
  85. elements.root->add_child(elements.tree);
  86. elements.tree->set_hide_root(true);
  87. elements.tree->set_allow_reselect(true);
  88. elements.tree->set_columns(1);
  89. elements.tree->set_column_titles_visible(true);
  90. elements.tree->set_column_title(0, p_tree_name);
  91. elements.tree->set_column_expand(0, true);
  92. elements.tree->set_column_clip_content(0, false);
  93. elements.tree->set_column_custom_minimum_width(0, 150 * EDSCALE);
  94. elements.tree->set_theme_type_variation("TreeSecondary");
  95. elements.tree->connect(SceneStringName(item_selected), callable_mp(this, &SnapshotNodeView::_node_selected).bind(elements.tree));
  96. elements.tree->set_h_size_flags(SizeFlags::SIZE_EXPAND_FILL);
  97. elements.tree->set_v_size_flags(SizeFlags::SIZE_EXPAND_FILL);
  98. elements.tree->set_anchors_preset(LayoutPreset::PRESET_FULL_RECT);
  99. elements.tree->create_item();
  100. return elements;
  101. }
  102. void SnapshotNodeView::_node_selected(Tree *p_tree_selected_from) {
  103. active_tree = p_tree_selected_from;
  104. if (diff_tree.tree) {
  105. // Deselect nodes in non-active tree, if needed.
  106. if (active_tree == main_tree.tree) {
  107. diff_tree.tree->deselect_all();
  108. }
  109. if (active_tree == diff_tree.tree) {
  110. main_tree.tree->deselect_all();
  111. }
  112. }
  113. const LocalVector<SnapshotDataObject *> &item_data = tree_item_data[p_tree_selected_from->get_selected()];
  114. if (item_data.is_empty()) {
  115. return;
  116. } else if (item_data.size() == 1) {
  117. EditorNode::get_singleton()->push_item(static_cast<Object *>(item_data[0]));
  118. } else if (item_data.size() == 2) {
  119. // This happens if we're in the combined diff view and the node exists in both trees
  120. // The user has to specify which version of the node they want to see in the inspector.
  121. _show_choose_object_menu();
  122. }
  123. }
  124. void SnapshotNodeView::_toggle_diff_mode(bool p_state) {
  125. combined_diff_view = p_state;
  126. show_snapshot(snapshot_data, diff_data); // Redraw everything when we toggle views.
  127. }
  128. void SnapshotNodeView::_notification(int p_what) {
  129. if (p_what == NOTIFICATION_THEME_CHANGED) {
  130. _refresh_icons();
  131. }
  132. }
  133. void SnapshotNodeView::_add_snapshot_to_tree(Tree *p_tree, GameStateSnapshot *p_snapshot, DiffGroup p_diff_group) {
  134. SnapshotDataObject *scene_root = nullptr;
  135. LocalVector<SnapshotDataObject *> orphan_nodes;
  136. for (const KeyValue<ObjectID, SnapshotDataObject *> &kv : p_snapshot->objects) {
  137. if (kv.value->is_node() && !kv.value->extra_debug_data.has("node_parent")) {
  138. if (kv.value->extra_debug_data["node_is_scene_root"]) {
  139. scene_root = kv.value;
  140. } else {
  141. orphan_nodes.push_back(kv.value);
  142. }
  143. }
  144. }
  145. if (scene_root != nullptr) {
  146. TreeItem *root_item = _add_item_to_tree(p_tree, p_tree->get_root(), scene_root, p_diff_group);
  147. _add_children_to_tree(root_item, scene_root, p_diff_group);
  148. }
  149. if (!orphan_nodes.is_empty()) {
  150. TreeItem *orphans_item = _add_item_to_tree(p_tree, p_tree->get_root(), TTRC("Orphan Nodes"), p_diff_group);
  151. for (SnapshotDataObject *orphan_node : orphan_nodes) {
  152. TreeItem *orphan_item = _add_item_to_tree(p_tree, orphans_item, orphan_node, p_diff_group);
  153. _add_children_to_tree(orphan_item, orphan_node, p_diff_group);
  154. }
  155. }
  156. }
  157. void SnapshotNodeView::_add_children_to_tree(TreeItem *p_parent_item, SnapshotDataObject *p_data, DiffGroup p_diff_group) {
  158. for (const Variant &child_id : (Array)p_data->extra_debug_data["node_children"]) {
  159. SnapshotDataObject *child_object = p_data->snapshot->objects[ObjectID((uint64_t)child_id)];
  160. TreeItem *child_item = _add_item_to_tree(p_parent_item->get_tree(), p_parent_item, child_object, p_diff_group);
  161. _add_children_to_tree(child_item, child_object, p_diff_group);
  162. }
  163. }
  164. TreeItem *SnapshotNodeView::_add_item_to_tree(Tree *p_tree, TreeItem *p_parent, const String &p_item_name, DiffGroup p_diff_group) {
  165. // Find out if this node already exists.
  166. TreeItem *item = nullptr;
  167. if (p_diff_group != DIFF_GROUP_NONE) {
  168. for (int idx = 0; idx < p_parent->get_child_count(); idx++) {
  169. TreeItem *child = p_parent->get_child(idx);
  170. if (child->get_text(0) == p_item_name) {
  171. item = child;
  172. break;
  173. }
  174. }
  175. }
  176. if (item) {
  177. // If it exists, clear the background color because we now know it exists in both trees.
  178. item->clear_custom_bg_color(0);
  179. } else {
  180. // Add the new node and set its background color to green or red depending on which snapshot it's a part of.
  181. item = p_tree->create_item(p_parent);
  182. if (p_diff_group == DIFF_GROUP_ADDED) {
  183. item->set_custom_bg_color(0, Color(0, 1, 0, 0.1));
  184. } else if (p_diff_group == DIFF_GROUP_REMOVED) {
  185. item->set_custom_bg_color(0, Color(1, 0, 0, 0.1));
  186. }
  187. }
  188. item->set_text(0, p_item_name);
  189. item->set_auto_translate_mode(0, AUTO_TRANSLATE_MODE_DISABLED);
  190. return item;
  191. }
  192. TreeItem *SnapshotNodeView::_add_item_to_tree(Tree *p_tree, TreeItem *p_parent, SnapshotDataObject *p_data, DiffGroup p_diff_group) {
  193. String node_name = p_data->extra_debug_data["node_name"];
  194. TreeItem *child_item = _add_item_to_tree(p_tree, p_parent, node_name, p_diff_group);
  195. tree_item_data[child_item].push_back(p_data);
  196. return child_item;
  197. }
  198. void SnapshotNodeView::_refresh_icons() {
  199. for (TreeItem *item : _get_children_recursive(main_tree.tree)) {
  200. HashMap<TreeItem *, LocalVector<SnapshotDataObject *>>::Iterator E = tree_item_data.find(item);
  201. if (E && !E->value.is_empty()) {
  202. item->set_icon(0, EditorNode::get_singleton()->get_class_icon(E->value[0]->type_name));
  203. } else {
  204. item->set_icon(0, EditorNode::get_singleton()->get_class_icon("MissingNode"));
  205. }
  206. }
  207. if (diff_tree.tree) {
  208. for (TreeItem *item : _get_children_recursive(diff_tree.tree)) {
  209. HashMap<TreeItem *, LocalVector<SnapshotDataObject *>>::Iterator E = tree_item_data.find(item);
  210. if (E && !E->value.is_empty()) {
  211. item->set_icon(0, EditorNode::get_singleton()->get_class_icon(E->value[0]->type_name));
  212. } else {
  213. item->set_icon(0, EditorNode::get_singleton()->get_class_icon("MissingNode"));
  214. }
  215. }
  216. }
  217. }
  218. void SnapshotNodeView::clear_snapshot() {
  219. SnapshotView::clear_snapshot();
  220. tree_item_data.clear();
  221. main_tree.tree = nullptr;
  222. main_tree.filter_bar = nullptr;
  223. main_tree.root = nullptr;
  224. diff_tree.tree = nullptr;
  225. diff_tree.filter_bar = nullptr;
  226. diff_tree.root = nullptr;
  227. active_tree = nullptr;
  228. }
  229. void SnapshotNodeView::_choose_object_pressed(int p_object_idx, bool p_confirm_override) {
  230. EditorNode::get_singleton()->push_item(static_cast<Object *>(tree_item_data[active_tree->get_selected()][p_object_idx]));
  231. }
  232. void SnapshotNodeView::_show_choose_object_menu() {
  233. remove_child(choose_object_menu);
  234. add_child(choose_object_menu);
  235. choose_object_menu->clear(false);
  236. choose_object_menu->add_item(TTRC("Snapshot A"), 0);
  237. choose_object_menu->add_item(TTRC("Snapshot B"), 1);
  238. choose_object_menu->reset_size();
  239. choose_object_menu->set_position(get_screen_position() + get_local_mouse_position());
  240. choose_object_menu->popup();
  241. }