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