| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // This file is part of INSTINCT, the INS Toolkit for Integrated | ||
| 2 | // Navigation Concepts and Training by the Institute of Navigation of | ||
| 3 | // the University of Stuttgart, Germany. | ||
| 4 | // | ||
| 5 | // This Source Code Form is subject to the terms of the Mozilla Public | ||
| 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| 7 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
| 8 | |||
| 9 | #include "LeftPane.hpp" | ||
| 10 | |||
| 11 | #include <imgui_node_editor.h> | ||
| 12 | #include <imgui_internal.h> | ||
| 13 | |||
| 14 | #include "internal/FlowManager.hpp" | ||
| 15 | |||
| 16 | #include "internal/gui/TouchTracker.hpp" | ||
| 17 | #include "internal/gui/NodeEditorApplication.hpp" | ||
| 18 | |||
| 19 | #include "util/StringUtil.hpp" | ||
| 20 | |||
| 21 | #include <string> | ||
| 22 | #include <algorithm> | ||
| 23 | |||
| 24 | namespace ed = ax::NodeEditor; | ||
| 25 | |||
| 26 | ✗ | bool NAV::gui::panels::ShowLeftPane(float paneWidth) | |
| 27 | { | ||
| 28 | ✗ | auto& io = ImGui::GetIO(); | |
| 29 | |||
| 30 | ✗ | bool paneActive = false; | |
| 31 | |||
| 32 | ✗ | float insLogoWidth = paneWidth < 150 ? paneWidth : 150; | |
| 33 | |||
| 34 | ✗ | float insLogoHeight = insLogoWidth * 2967.0F / 4073.0F; | |
| 35 | ✗ | float childHeight = ImGui::GetContentRegionAvail().y - insLogoHeight; | |
| 36 | |||
| 37 | ✗ | ImGui::BeginGroup(); | |
| 38 | |||
| 39 | ✗ | ImGui::BeginChild("Selection", ImVec2(paneWidth, childHeight)); | |
| 40 | |||
| 41 | ✗ | paneWidth = ImGui::GetContentRegionAvail().x; | |
| 42 | |||
| 43 | ✗ | float colSum = ImGui::GetStyle().Colors[ImGuiCol_WindowBg].x + ImGui::GetStyle().Colors[ImGuiCol_WindowBg].y + ImGui::GetStyle().Colors[ImGuiCol_WindowBg].z; | |
| 44 | ✗ | ImTextureID instinctLogo = NodeEditorApplication::m_InstinctLogo.at(colSum > 2.0F ? 1 : 0); | |
| 45 | ✗ | ImTextureID insLogo = NodeEditorApplication::m_InsLogo.at(colSum > 2.0F ? 1 : 0); | |
| 46 | |||
| 47 | ✗ | ImGui::Image(instinctLogo, ImVec2(paneWidth, paneWidth * 3000.0F / 13070.0F)); | |
| 48 | ✗ | ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5.0F); | |
| 49 | |||
| 50 | { // Control Buttons | ||
| 51 | ✗ | ImGui::BeginHorizontal("Control Buttons", ImVec2(paneWidth, 0)); | |
| 52 | ✗ | ImGui::Spring(0.0F, 0.0F); | |
| 53 | ✗ | if (ImGui::Button("Zoom to Content")) | |
| 54 | { | ||
| 55 | ✗ | ed::NavigateToContent(); | |
| 56 | } | ||
| 57 | ✗ | if (ImGui::IsItemHovered()) | |
| 58 | { | ||
| 59 | ✗ | ImGui::SetTooltip("Shortcut: F"); | |
| 60 | } | ||
| 61 | ✗ | ImGui::Spring(); | |
| 62 | ✗ | ImGui::EndHorizontal(); | |
| 63 | } | ||
| 64 | |||
| 65 | ✗ | std::vector<ed::NodeId> selectedNodes; | |
| 66 | ✗ | std::vector<ed::LinkId> selectedLinks; | |
| 67 | ✗ | selectedNodes.resize(static_cast<size_t>(ed::GetSelectedObjectCount())); | |
| 68 | ✗ | selectedLinks.resize(static_cast<size_t>(ed::GetSelectedObjectCount())); | |
| 69 | |||
| 70 | ✗ | auto nodeCount = static_cast<size_t>(ed::GetSelectedNodes(selectedNodes.data(), static_cast<int>(selectedNodes.size()))); | |
| 71 | ✗ | auto linkCount = static_cast<size_t>(ed::GetSelectedLinks(selectedLinks.data(), static_cast<int>(selectedLinks.size()))); | |
| 72 | |||
| 73 | ✗ | selectedNodes.resize(nodeCount); | |
| 74 | ✗ | selectedLinks.resize(linkCount); | |
| 75 | |||
| 76 | { // Node List | ||
| 77 | ✗ | ImGui::GetWindowDrawList()->AddRectFilled( | |
| 78 | ✗ | ImGui::GetCursorScreenPos(), | |
| 79 | ✗ | ImGui::GetCursorScreenPos() + ImVec2(paneWidth, ImGui::GetTextLineHeight()), | |
| 80 | ✗ | ImColor(ImGui::GetStyle().Colors[ImGuiCol_HeaderActive]), ImGui::GetTextLineHeight() * 0.25F); | |
| 81 | ✗ | ImGui::Spacing(); | |
| 82 | ✗ | ImGui::SameLine(); | |
| 83 | ✗ | ImGui::TextUnformatted("Nodes"); | |
| 84 | ✗ | ImGui::Indent(); | |
| 85 | ✗ | for (const auto* node : flow::m_Nodes()) | |
| 86 | { | ||
| 87 | ✗ | ImGui::PushID(node->id.AsPointer()); | |
| 88 | ✗ | auto start = ImGui::GetCursorScreenPos(); | |
| 89 | |||
| 90 | ✗ | if (const auto progress = gui::GetTouchProgress(node->id); progress != 0.0F) | |
| 91 | { | ||
| 92 | ✗ | ImGui::GetWindowDrawList()->AddLine( | |
| 93 | ✗ | start + ImVec2(-18, 0), | |
| 94 | ✗ | start + ImVec2(-18, ImGui::GetTextLineHeight()), | |
| 95 | ✗ | IM_COL32(255, 0, 0, 255 - static_cast<int>(255 * progress)), 4.0F); | |
| 96 | } | ||
| 97 | |||
| 98 | ✗ | if (node->kind == Node::Kind::GroupBox) | |
| 99 | { | ||
| 100 | ✗ | ImGui::PopID(); | |
| 101 | ✗ | continue; | |
| 102 | } | ||
| 103 | |||
| 104 | // Circle to show init status | ||
| 105 | ✗ | ImU32 circleCol = 0; | |
| 106 | ✗ | if (node->isDisabled()) | |
| 107 | { | ||
| 108 | ✗ | circleCol = IM_COL32(192, 192, 192, 255); | |
| 109 | } | ||
| 110 | ✗ | else if (node->getState() == Node::State::DoInitialize) | |
| 111 | { | ||
| 112 | ✗ | circleCol = IM_COL32(144, 202, 238, 255); | |
| 113 | } | ||
| 114 | ✗ | else if (node->getState() == Node::State::Initializing) | |
| 115 | { | ||
| 116 | ✗ | circleCol = IM_COL32(143, 188, 143, 255); | |
| 117 | } | ||
| 118 | ✗ | else if (node->getState() == Node::State::DoDeinitialize) | |
| 119 | { | ||
| 120 | ✗ | circleCol = IM_COL32(255, 222, 122, 255); | |
| 121 | } | ||
| 122 | ✗ | else if (node->getState() == Node::State::Deinitializing) | |
| 123 | { | ||
| 124 | ✗ | circleCol = IM_COL32(240, 128, 128, 255); | |
| 125 | } | ||
| 126 | ✗ | else if (node->getState() == Node::State::Initialized) | |
| 127 | { | ||
| 128 | ✗ | circleCol = IM_COL32(0, 255, 0, 255); | |
| 129 | } | ||
| 130 | else // if (!node->getState() == Node::State::Deinitialized) | ||
| 131 | { | ||
| 132 | ✗ | circleCol = IM_COL32(255, 0, 0, 255); | |
| 133 | } | ||
| 134 | ✗ | ImGui::GetWindowDrawList()->AddCircleFilled(start + ImVec2(-8 + (NodeEditorApplication::panelFontRatio() - 1.F) * 5.F, ImGui::GetTextLineHeight() / 2.0F + 1.0F), | |
| 135 | ✗ | 5.0F * NodeEditorApplication::panelFontRatio(), circleCol); | |
| 136 | |||
| 137 | ✗ | bool isSelected = std::ranges::find(selectedNodes, node->id) != selectedNodes.end(); | |
| 138 | ✗ | if (NodeEditorApplication::panelFontRatio() != 1.F) { ImGui::Indent((NodeEditorApplication::panelFontRatio() - 1.F) * 15.F); } | |
| 139 | ✗ | if (ImGui::Selectable((str::replaceAll_copy(node->name, "\n", "") + "##" + std::to_string(size_t(node->id))).c_str(), &isSelected)) | |
| 140 | { | ||
| 141 | ✗ | if (io.KeyCtrl) | |
| 142 | { | ||
| 143 | ✗ | if (isSelected) | |
| 144 | { | ||
| 145 | ✗ | ed::SelectNode(node->id, true); | |
| 146 | } | ||
| 147 | else | ||
| 148 | { | ||
| 149 | ✗ | ed::DeselectNode(node->id); | |
| 150 | } | ||
| 151 | } | ||
| 152 | else | ||
| 153 | { | ||
| 154 | ✗ | ed::SelectNode(node->id, false); | |
| 155 | } | ||
| 156 | |||
| 157 | ✗ | ed::NavigateToSelection(); | |
| 158 | } | ||
| 159 | ✗ | if (ImGui::IsItemHovered()) | |
| 160 | { | ||
| 161 | ✗ | ImGui::SetTooltip("Type: %s", node->type().c_str()); | |
| 162 | } | ||
| 163 | ✗ | if (NodeEditorApplication::panelFontRatio() != 1.F) { ImGui::Unindent((NodeEditorApplication::panelFontRatio() - 1.F) * 15.F); } | |
| 164 | |||
| 165 | // auto id = fmt::format("({})", size_t(node->id)); | ||
| 166 | // auto textSize = ImGui::CalcTextSize(id.c_str(), nullptr); | ||
| 167 | // auto iconPanelPos = start + ImVec2(std::max(paneWidth, 150.F) - ImGui::GetStyle().FramePadding.x - ImGui::GetStyle().IndentSpacing - ImGui::GetStyle().ItemInnerSpacing.x * 1, ImGui::GetTextLineHeight() / 2); | ||
| 168 | // ImGui::GetWindowDrawList()->AddText( | ||
| 169 | // ImVec2(iconPanelPos.x - textSize.x - ImGui::GetStyle().ItemInnerSpacing.x, start.y), | ||
| 170 | // colSum > 2.0F ? IM_COL32(0, 0, 0, 255) : IM_COL32(255, 255, 255, 255), id.c_str(), nullptr); | ||
| 171 | |||
| 172 | ✗ | ImGui::PopID(); | |
| 173 | } | ||
| 174 | ✗ | ImGui::Unindent(); | |
| 175 | } | ||
| 176 | |||
| 177 | { // Selection List | ||
| 178 | ✗ | ImGui::GetWindowDrawList()->AddRectFilled( | |
| 179 | ✗ | ImGui::GetCursorScreenPos(), | |
| 180 | ✗ | ImGui::GetCursorScreenPos() + ImVec2(paneWidth, ImGui::GetTextLineHeight()), | |
| 181 | ✗ | ImColor(ImGui::GetStyle().Colors[ImGuiCol_HeaderActive]), ImGui::GetTextLineHeight() * 0.25F); | |
| 182 | ✗ | ImGui::Spacing(); | |
| 183 | ✗ | ImGui::SameLine(); | |
| 184 | ✗ | ImGui::TextUnformatted("Selection"); | |
| 185 | |||
| 186 | ✗ | ImGui::BeginHorizontal("Selection Stats", ImVec2(paneWidth, 0)); | |
| 187 | ✗ | size_t selectedCount = selectedNodes.size() + selectedLinks.size(); | |
| 188 | ✗ | ImGui::Text("%lu item%s selected", selectedCount, (selectedCount > 1 || selectedCount == 0) ? "s" : ""); | |
| 189 | ✗ | ImGui::Spring(); | |
| 190 | ✗ | if (ImGui::Button("Deselect")) | |
| 191 | { | ||
| 192 | ✗ | ed::ClearSelection(); | |
| 193 | } | ||
| 194 | ✗ | ImGui::EndHorizontal(); | |
| 195 | ✗ | ImGui::Indent(); | |
| 196 | ✗ | for (size_t i = 0; i < nodeCount; ++i) | |
| 197 | { | ||
| 198 | ✗ | auto* node = flow::FindNode(selectedNodes[i]); | |
| 199 | ✗ | ImGui::Text("%s (%lu)", node != nullptr ? node->name.c_str() : "", size_t(selectedNodes[i])); | |
| 200 | } | ||
| 201 | ✗ | for (size_t i = 0; i < linkCount; ++i) | |
| 202 | { | ||
| 203 | ✗ | ImGui::Text("Link (%lu)", size_t(selectedLinks[i])); | |
| 204 | } | ||
| 205 | ✗ | ImGui::Unindent(); | |
| 206 | } | ||
| 207 | |||
| 208 | ✗ | if (ImGui::IsWindowFocused()) | |
| 209 | { | ||
| 210 | ✗ | paneActive = true; | |
| 211 | } | ||
| 212 | |||
| 213 | ✗ | ImGui::EndChild(); | |
| 214 | |||
| 215 | ✗ | ImGui::Image(insLogo, ImVec2(insLogoWidth, insLogoHeight)); | |
| 216 | |||
| 217 | ✗ | ImGui::EndGroup(); | |
| 218 | |||
| 219 | ✗ | return paneActive; | |
| 220 | ✗ | } | |
| 221 |