INSTINCT Code Coverage Report


Directory: src/
File: internal/gui/widgets/DynamicInputPins.cpp
Date: 2025-02-07 16:54:41
Exec Total Coverage
Lines: 22 103 21.4%
Functions: 4 7 57.1%
Branches: 10 166 6.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 /// @file DynamicInputPins.cpp
10 /// @brief Inputs pins which can be added dynamically
11 /// @author T. Topp (topp@ins.uni-stuttgart.de)
12 /// @date 2023-12-21
13
14 #include "DynamicInputPins.hpp"
15
16 #include <optional>
17 #include <imgui.h>
18
19 #include "util/Container/Vector.hpp"
20 #include "util/Logger.hpp"
21
22 namespace NAV::gui::widgets
23 {
24
25 614 DynamicInputPins::DynamicInputPins(size_t firstDynamicPin,
26 Node* node,
27 std::function<void(Node*)> pinAddCallback,
28 std::function<void(Node*, size_t)> pinDeleteCallback,
29 614 size_t defaultInputPins)
30 614 : _firstDynamicPinIdx(firstDynamicPin), _pinAddCallback(std::move(pinAddCallback)), _pinDeleteCallback(std::move(pinDeleteCallback))
31 {
32
2/2
✓ Branch 0 taken 257 times.
✓ Branch 1 taken 614 times.
871 while (_nDynamicInputPins < defaultInputPins)
33 {
34
1/2
✓ Branch 1 taken 257 times.
✗ Branch 2 not taken.
257 _pinAddCallback(node);
35 257 _nDynamicInputPins++;
36 }
37 614 }
38
39 bool DynamicInputPins::ShowGuiWidgets(size_t id, std::vector<InputPin>& inputPins, Node* node, const std::vector<ExtraColumn>& extraColumns)
40 {
41 bool changed = false;
42
43 int nExtraColumns = static_cast<int>(extraColumns.size());
44 if (ImGui::BeginTable(fmt::format("Pin Settings##{}", id).c_str(),
45 inputPins.size() > _firstDynamicPinIdx + 1 ? 2 + nExtraColumns : 1 + nExtraColumns,
46 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX, ImVec2(0.0F, 0.0F)))
47 {
48 ImGui::TableSetupColumn("Pin");
49 for (const auto& column : extraColumns)
50 {
51 ImGui::TableSetupColumn(column.header.c_str());
52 }
53 if (inputPins.size() > _firstDynamicPinIdx + 1)
54 {
55 ImGui::TableSetupColumn(""); // Delete Button column
56 }
57 ImGui::TableHeadersRow();
58
59 // Used to reset the member variabel _dragAndDropPinIndex in case no plot does a drag and drop action
60 bool dragAndDropPinStillInProgress = false;
61
62 auto showDragDropTargetPin = [&](size_t pinIdxTarget) {
63 ImGui::Dummy(ImVec2(-1.F, 2.F));
64
65 bool selectableDummy = true;
66 ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5F, 0.5F));
67 ImGui::PushStyleColor(ImGuiCol_Header, IM_COL32(16, 173, 44, 79));
68 ImGui::Selectable(fmt::format("[drop here]").c_str(), &selectableDummy, ImGuiSelectableFlags_None,
69 ImVec2(std::max(ImGui::GetColumnWidth(0), ImGui::CalcTextSize("[drop here]").x), 20.F));
70 ImGui::PopStyleColor();
71 ImGui::PopStyleVar();
72
73 if (ImGui::BeginDragDropTarget())
74 {
75 if (const ImGuiPayload* payloadData = ImGui::AcceptDragDropPayload(fmt::format("DND Pin {}", id).c_str()))
76 {
77 auto pinIdxSource = *static_cast<size_t*>(payloadData->Data);
78
79 if (pinIdxSource < pinIdxTarget)
80 {
81 --pinIdxTarget;
82 }
83
84 move(inputPins, pinIdxSource, pinIdxTarget);
85 changed = true;
86 }
87 ImGui::EndDragDropTarget();
88 }
89 ImGui::Dummy(ImVec2(-1.F, 2.F));
90 };
91
92 std::optional<size_t> deletePinIdx;
93 for (size_t pinIndex = 0; pinIndex < inputPins.size(); pinIndex++)
94 {
95 ImGui::TableNextRow();
96 ImGui::TableNextColumn(); // Pin
97
98 if (pinIndex == _firstDynamicPinIdx && _dragAndDropPinIndex > static_cast<int>(_firstDynamicPinIdx))
99 {
100 showDragDropTargetPin(_firstDynamicPinIdx);
101 }
102
103 bool selectablePinDummy = false;
104 ImGui::Selectable(fmt::format("{}##{}", inputPins.at(pinIndex).name, id).c_str(), &selectablePinDummy);
105 if (pinIndex >= _firstDynamicPinIdx && ImGui::BeginDragDropSource(ImGuiDragDropFlags_None))
106 {
107 dragAndDropPinStillInProgress = true;
108 _dragAndDropPinIndex = static_cast<int>(pinIndex);
109 // Data is copied into heap inside the drag and drop
110 ImGui::SetDragDropPayload(fmt::format("DND Pin {}", id).c_str(), &pinIndex, sizeof(pinIndex));
111 ImGui::TextUnformatted(inputPins.at(pinIndex).name.c_str());
112 ImGui::EndDragDropSource();
113 }
114 if (_dragAndDropPinIndex >= 0 && pinIndex >= _firstDynamicPinIdx
115 && pinIndex != static_cast<size_t>(_dragAndDropPinIndex - 1)
116 && pinIndex != static_cast<size_t>(_dragAndDropPinIndex))
117 {
118 showDragDropTargetPin(pinIndex + 1);
119 }
120 if (pinIndex >= _firstDynamicPinIdx && ImGui::IsItemHovered())
121 {
122 ImGui::SetTooltip("This item can be dragged to reorder the pins");
123 }
124
125 for (const auto& column : extraColumns)
126 {
127 ImGui::TableNextColumn();
128 changed |= column.content(pinIndex);
129 }
130
131 if (pinIndex >= _firstDynamicPinIdx && inputPins.size() > _firstDynamicPinIdx + 1)
132 {
133 ImGui::TableNextColumn(); // Delete
134 if (ImGui::Button(fmt::format("x##{} - {}", id, pinIndex).c_str()))
135 {
136 deletePinIdx = pinIndex;
137 }
138 if (ImGui::IsItemHovered())
139 {
140 ImGui::SetTooltip("Delete the pin");
141 }
142 }
143 }
144 if (deletePinIdx)
145 {
146 LOG_TRACE("{}: Deleting pin with index {}", id, *deletePinIdx);
147 changed = true;
148 _pinDeleteCallback(node, *deletePinIdx);
149 _nDynamicInputPins--;
150 }
151
152 if (!dragAndDropPinStillInProgress)
153 {
154 _dragAndDropPinIndex = -1;
155 }
156
157 ImGui::TableNextRow();
158 ImGui::TableNextColumn(); // Pin
159 if (ImGui::Button(fmt::format("Add Pin##{}", id).c_str()))
160 {
161 LOG_TRACE("{}: Adding a new pin", id);
162 changed = true;
163 _pinAddCallback(node);
164 _nDynamicInputPins++;
165 }
166
167 ImGui::EndTable();
168 }
169
170 return changed;
171 }
172
173 1304 size_t DynamicInputPins::getNumberOfDynamicPins() const
174 {
175 1304 return _nDynamicInputPins;
176 }
177
178 470 void DynamicInputPins::addPin(Node* node)
179 {
180 470 _pinAddCallback(node);
181 470 _nDynamicInputPins++;
182 470 }
183
184 void to_json(json& j, const DynamicInputPins& obj)
185 {
186 j = json{
187 { "nDynamicInputPins", obj._nDynamicInputPins },
188 };
189 }
190 36 void from_json(const json& j, DynamicInputPins& obj, Node* node)
191 {
192
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 if (j.contains("nDynamicInputPins"))
193 {
194 36 size_t nPins = 0;
195
2/4
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
36 j.at("nDynamicInputPins").get_to(nPins);
196
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 36 times.
80 while (obj._nDynamicInputPins < nPins)
197 {
198
1/2
✓ Branch 1 taken 44 times.
✗ Branch 2 not taken.
44 obj._pinAddCallback(node);
199 44 obj._nDynamicInputPins++;
200 }
201
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 while (obj._nDynamicInputPins > nPins)
202 {
203 obj._pinDeleteCallback(node, --obj._nDynamicInputPins);
204 }
205 }
206 36 }
207
208 } // namespace NAV::gui::widgets
209