From e3baa65c3be280e56dcd11248f6bec3d131358fe Mon Sep 17 00:00:00 2001 From: Martin Felis Date: Wed, 1 May 2024 10:58:33 +0200 Subject: [PATCH] Added breadcrumb navigation for embedded graphs. --- src/AnimGraph/AnimGraphEditor.cc | 141 ++++++++++++++++++++++--------- 1 file changed, 100 insertions(+), 41 deletions(-) diff --git a/src/AnimGraph/AnimGraphEditor.cc b/src/AnimGraph/AnimGraphEditor.cc index da02a6f..480d40f 100644 --- a/src/AnimGraph/AnimGraphEditor.cc +++ b/src/AnimGraph/AnimGraphEditor.cc @@ -15,7 +15,10 @@ struct EditorState { AnimGraphResource* rootGraphResource = nullptr; - AnimGraphResource* currentGraphResource = nullptr; + + std::vector hierarchyStack; + size_t hierarchyStackIndex = 0; + bool isGraphLoadedThisFrame = false; }; @@ -259,18 +262,25 @@ void AnimGraphEditorRenderSidebar( } void AnimGraphEditorClear() { - sEditorState.currentGraphResource = nullptr; + sEditorState.hierarchyStack.clear(); delete sEditorState.rootGraphResource; sEditorState.rootGraphResource = new AnimGraphResource(); + sEditorState.rootGraphResource->m_name = "Root"; sEditorState.rootGraphResource->m_graph_type_name = "BlendTree"; sEditorState.rootGraphResource->m_blend_tree_resource.InitGraphConnectors(); - sEditorState.currentGraphResource = sEditorState.rootGraphResource; + + sEditorState.hierarchyStack.push_back(sEditorState.rootGraphResource); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] = + sEditorState.hierarchyStack.back(); } void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { ax::NodeEditor::SetCurrentEditor(context); + // + // Menu bar + // ImGui::BeginMenuBar(); if (ImGui::Button("Save")) { sEditorState.rootGraphResource->SaveToFile("editor_graph.json"); @@ -289,14 +299,47 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { memset(graph_name_buffer, 0, sizeof(graph_name_buffer)); strncpy( graph_name_buffer, - sEditorState.currentGraphResource->m_name.c_str(), + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_name.c_str(), sizeof(graph_name_buffer)); if (ImGui::InputText("Name", graph_name_buffer, sizeof(graph_name_buffer))) { - sEditorState.currentGraphResource->m_name = graph_name_buffer; + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex]->m_name = + graph_name_buffer; } ImGui::EndMenuBar(); + // + // Breadcrumb navigation + // + for (size_t i = 0, n = sEditorState.hierarchyStack.size(); i < n; i++) { + AnimGraphResource* graph_resource = + dynamic_cast(sEditorState.hierarchyStack[i]); + ImGui::PushID(graph_resource); + + bool highlight_button = i == sEditorState.hierarchyStackIndex; + + if (highlight_button) { + ImGui::PushStyleColor( + ImGuiCol_Button, + (ImVec4)ImColor::HSV(1. / 7.0f, 0.6f, 0.6f)); + } + + if (ImGui::Button(graph_resource->m_name.c_str())) { + sEditorState.hierarchyStackIndex = i; + } + + if (highlight_button) { + ImGui::PopStyleColor(1); + } + + ImGui::PopID(); + + if (i < n - 1) { + ImGui::SameLine(); + } + } + ImGui::Columns(2); // @@ -305,13 +348,13 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { ax::NodeEditor::Begin("Graph Editor"); for (size_t node_id = 0, - n = sEditorState.currentGraphResource->m_blend_tree_resource - .GetNumNodes(); + n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNumNodes(); node_id < n; node_id++) { AnimNodeResource* node_resource = - sEditorState.currentGraphResource->m_blend_tree_resource.GetNode( - node_id); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNode(node_id); if (node_id == 0 || node_id == 1) { // continue; @@ -327,8 +370,8 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { // Inputs std::vector node_inputs = - sEditorState.currentGraphResource->m_blend_tree_resource - .GetNodeInputSockets(node_resource); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNodeInputSockets(node_resource); for (size_t j = 0, ni = node_inputs.size(); j < ni; j++) { Socket& socket = node_inputs[j]; ax::NodeEditor::BeginPin( @@ -342,8 +385,8 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { // Outputs std::vector node_outputs = - sEditorState.currentGraphResource->m_blend_tree_resource - .GetNodeOutputSockets(node_resource); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNodeOutputSockets(node_resource); for (size_t j = 0, ni = node_outputs.size(); j < ni; j++) { Socket& socket = node_outputs[j]; ax::NodeEditor::BeginPin( @@ -364,24 +407,26 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { int link_id = 0; for (size_t connection_id = 0, - n = sEditorState.currentGraphResource->m_blend_tree_resource - .GetNumConnections(); + n = sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNumConnections(); connection_id < n; connection_id++) { const BlendTreeConnectionResource* connection_resource = - sEditorState.currentGraphResource->m_blend_tree_resource.GetConnection( - connection_id); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetConnection(connection_id); const AnimNodeResource* source_node_resource = - sEditorState.currentGraphResource->m_blend_tree_resource.GetNode( - connection_resource->source_node_index); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNode( + connection_resource->source_node_index); int source_socket_index = source_node_resource->m_socket_accessor->GetOutputIndex( connection_resource->source_socket_name.c_str()); const AnimNodeResource* target_node_resource = - sEditorState.currentGraphResource->m_blend_tree_resource.GetNode( - connection_resource->target_node_index); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNode( + connection_resource->target_node_index); int target_socket_index = target_node_resource->m_socket_accessor->GetInputIndex( connection_resource->target_socket_name.c_str()); @@ -411,8 +456,8 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { &source_node_socket_index); const AnimNodeResource* source_node = - sEditorState.currentGraphResource->m_blend_tree_resource.GetNode( - source_node_index); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNode(source_node_index); if (source_node->m_socket_accessor->m_outputs.size() < source_node_socket_index) { source_node_socket_index = -1; @@ -427,21 +472,21 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { &target_node_socket_index); const AnimNodeResource* target_node = - sEditorState.currentGraphResource->m_blend_tree_resource.GetNode( - target_node_index); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNode(target_node_index); if (target_node->m_socket_accessor->m_inputs.size() < target_node_socket_index) { target_node_socket_index = -1; } const Socket* source_socket = - sEditorState.currentGraphResource->m_blend_tree_resource - .GetNodeOutputSocketByIndex( + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNodeOutputSocketByIndex( source_node, source_node_socket_index); const Socket* target_socket = - sEditorState.currentGraphResource->m_blend_tree_resource - .GetNodeInputSocketByIndex( + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNodeInputSocketByIndex( target_node, target_node_socket_index); @@ -458,8 +503,8 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { ->m_inputs[target_node_socket_index] .m_name; - sEditorState.currentGraphResource->m_blend_tree_resource - .ConnectSockets( + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.ConnectSockets( source_node, source_socket_name, target_node, @@ -522,11 +567,12 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { if (!node_type_name.empty()) { AnimNodeResource* node_resource = AnimNodeResourceFactory(node_type_name); - size_t node_id = sEditorState.currentGraphResource - ->m_blend_tree_resource.GetNumNodes(); + size_t node_id = + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNumNodes(); ax::NodeEditor::SetNodePosition(node_id, popup_mouse_position); - sEditorState.currentGraphResource->m_blend_tree_resource.AddNode( - node_resource); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.AddNode(node_resource); } ImGui::EndPopup(); @@ -549,9 +595,10 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { if (selected_node_id.Get() != 0) { AnimGraphEditorRenderSidebar( - sEditorState.currentGraphResource->m_blend_tree_resource, - sEditorState.currentGraphResource->m_blend_tree_resource.GetNode( - selected_node_id.Get())); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource, + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNode(selected_node_id.Get())); } } @@ -561,11 +608,23 @@ void AnimGraphEditorUpdate(ax::NodeEditor::EditorContext* context) { ax::NodeEditor::GetDoubleClickedNode(); if (!double_clicked_node_id.Invalid) { AnimNodeResource* clicked_node_resource = - sEditorState.currentGraphResource->m_blend_tree_resource.GetNode( - double_clicked_node_id.Get()); + sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex] + ->m_blend_tree_resource.GetNode(double_clicked_node_id.Get()); if (clicked_node_resource->m_node_type_name == "BlendTree") { - sEditorState.currentGraphResource = + AnimGraphResource* clicked_graph_resource = dynamic_cast(clicked_node_resource); + if (sEditorState.hierarchyStack.size() + > sEditorState.hierarchyStackIndex + 1 + && sEditorState.hierarchyStack[sEditorState.hierarchyStackIndex + 1] + == clicked_graph_resource) { + sEditorState.hierarchyStackIndex++; + } else { + sEditorState.hierarchyStack.resize( + sEditorState.hierarchyStackIndex + 1); + sEditorState.hierarchyStack.push_back(clicked_graph_resource); + sEditorState.hierarchyStackIndex++; + } + ax::NodeEditor::ClearSelection(); } }