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 |