INSTINCT Code Coverage Report


Directory: src/
File: internal/gui/panels/LeftPane.cpp
Date: 2025-11-25 23:34:18
Exec Total Coverage
Lines: 0 107 0.0%
Functions: 0 1 0.0%
Branches: 0 282 0.0%

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