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