0.3.0
Loading...
Searching...
No Matches
NodeEditorApplication.cpp
Go to the documentation of this file.
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
10
11#include <imgui_node_editor.h>
12#include <imgui_node_editor_internal.h>
13namespace ed = ax::NodeEditor;
14
15#include <imgui_internal.h>
16#include <imgui_stdlib.h>
17
18#include "ImGuiFileDialog.h"
19
20#include "implot.h"
21#include <implot_internal.h>
22
26
29
31
33
38
40
41#include "internal/Node/Pin.hpp"
43
45namespace nm = NAV::NodeManager;
46#include "NodeRegistry.hpp"
47
51
52#include "util/Json.hpp"
53#include "util/StringUtil.hpp"
54
55#include <string>
56#include <array>
57#include <vector>
58#include <map>
59#include <algorithm>
60#include <utility>
61#include <filesystem>
62
63#include "internal/CMakeRC.hpp"
64
65namespace NAV::gui
66{
67namespace
68{
69
70ax::NodeEditor::EditorContext* m_Editor = nullptr;
71
72} // namespace
73} // namespace NAV::gui
74
76{
77 LOG_TRACE("called");
78
79 ed::Config config;
80
81 // config.SettingsFile = "INSTINCT.json";
82 // config.UserPointer = this;
83
84 // Stops the Editor from creating a log file, as we do it ourselves
85 // clang-format off
86 config.SaveSettings = [](const char* /*data*/, size_t /*size*/,
87 ed::SaveReasonFlags /*reason*/, void* /*userPointer*/) -> bool {
88 // clang-format on
89
90 return false;
91 };
92
93 // Trigger the changed bar on the node overview list when a node is moved
94 // clang-format off
95 config.SaveNodeSettings = [](ed::NodeId nodeId, const char* /*data*/, size_t /*size*/,
96 ed::SaveReasonFlags /*reason*/, void* /*userPointer*/) -> bool {
97 // clang-format on
98
99 // auto* self = static_cast<NodeEditorApplication*>(userPointer);
100
102 gui::TouchNode(nodeId);
103
104 return true;
105 };
106
107 m_Editor = ed::CreateEditor(&config);
108 ed::SetCurrentEditor(m_Editor);
109
110 ImGui::GetStyle().FrameRounding = 4.0F;
111 ed::GetStyle().FlowDuration = 1.0F;
112
113 ImPlot::CreateContext();
114 ImPlot::GetStyle().Use24HourClock = true;
115
116 imPlotReferenceStyle = ImPlot::GetStyle();
117
118 std::filesystem::path imPlotConfigFilepath = flow::GetConfigPath();
119 if (std::filesystem::path inputPath{ ConfigManager::Get<std::string>("implot-config") };
120 inputPath.is_relative())
121 {
122 imPlotConfigFilepath /= inputPath;
123 }
124 else
125 {
126 imPlotConfigFilepath = inputPath;
127 }
128 std::ifstream imPlotConfigFilestream(imPlotConfigFilepath);
129
130 if (!imPlotConfigFilestream.good())
131 {
132 LOG_ERROR("The ImPlot style config file could not be loaded: {}", imPlotConfigFilepath);
133 }
134 else
135 {
136 json j;
137 imPlotConfigFilestream >> j;
138
139 if (j.contains("implot") && j.at("implot").contains("style"))
140 {
141 j.at("implot").at("style").get_to(ImPlot::GetStyle());
142 }
143 }
144
145 // Add custom colormap and set as default. ConfigManager::LoadGlobalSettings then overrides this default selection if it is saved
146 ImPlotContext& gp = *ImPlot::GetCurrentContext();
147 ImVector<ImVec4> custom;
148 for (int c = 0; c < gp.ColormapData.GetKeyCount(ImPlotColormap_Deep); ++c)
149 {
150 custom.push_back(ImGui::ColorConvertU32ToFloat4(gp.ColormapData.GetKeyColor(ImPlotColormap_Deep, c)));
151 }
152 custom.push_back(ImColor(37, 109, 227));
153 custom.push_back(ImColor(239, 100, 21));
154 custom.push_back(ImColor(21, 228, 69));
155 custom.push_back(ImColor(239, 20, 28));
156 custom.push_back(ImColor(100, 64, 217));
157 custom.push_back(ImColor(164, 78, 2));
158 custom.push_back(ImColor(232, 39, 176));
159 custom.push_back(ImColor(255, 207, 31));
160 custom.push_back(ImColor(54, 228, 174));
161 ImPlot::AddColormap("DeepEx", custom.Data, custom.Size, true);
162 ImPlot::BustItemCache();
163 gp.Style.Colormap = gp.ColormapData.Count - 1;
164 ImPlot::BustItemCache();
165
167
168 auto fs = cmrc::instinct::get_filesystem();
169
170 if (fs.is_file("resources/images/BlueprintBackground.png"))
171 {
172 auto fd = fs.open("resources/images/BlueprintBackground.png");
173
174 LOG_DEBUG("Generating Texture for Blueprint Background ({} byte)", fd.size());
175
176 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
177 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
178
179 std::vector<char> buffer;
180 buffer.resize(fd.size(), '\0');
181
182 is.read(buffer.data(),
183 static_cast<std::streamsize>(buffer.size()));
184
185 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
186 m_HeaderBackground = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
187 static_cast<int>(fd.size()));
188 }
189 else
190 {
191 m_HeaderBackground = LoadTexture("resources/images/BlueprintBackground.png");
192 }
193
194 if (fs.is_file("resources/images/INSTINCT_Logo_Text_white_small.png"))
195 {
196 auto fd = fs.open("resources/images/INSTINCT_Logo_Text_white_small.png");
197
198 LOG_DEBUG("Generating Texture for INSTINCT Logo (white) ({} byte)", fd.size());
199
200 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
201 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
202
203 std::vector<char> buffer;
204 buffer.resize(fd.size(), '\0');
205
206 is.read(buffer.data(),
207 static_cast<std::streamsize>(buffer.size()));
208
209 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
210 m_InstinctLogo.at(0) = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
211 static_cast<int>(fd.size()));
212 }
213 else
214 {
215 m_InstinctLogo.at(0) = LoadTexture("resources/images/INSTINCT_Logo_Text_white_small.png");
216 }
217
218 if (fs.is_file("resources/images/INSTINCT_Logo_Text_black_small.png"))
219 {
220 auto fd = fs.open("resources/images/INSTINCT_Logo_Text_black_small.png");
221
222 LOG_DEBUG("Generating Texture for INSTINCT Logo (black) ({} byte)", fd.size());
223
224 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
225 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
226
227 std::vector<char> buffer;
228 buffer.resize(fd.size(), '\0');
229
230 is.read(buffer.data(),
231 static_cast<std::streamsize>(buffer.size()));
232
233 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
234 m_InstinctLogo.at(1) = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
235 static_cast<int>(fd.size()));
236 }
237 else
238 {
239 m_InstinctLogo.at(1) = LoadTexture("resources/images/INSTINCT_Logo_Text_black_small.png");
240 }
241
242 if (fs.is_file("resources/images/INS_logo_rectangular_white_small.png"))
243 {
244 auto fd = fs.open("resources/images/INS_logo_rectangular_white_small.png");
245
246 LOG_DEBUG("Generating Texture for INS Logo (white) ({} byte)", fd.size());
247
248 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
249 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
250
251 std::vector<char> buffer;
252 buffer.resize(fd.size(), '\0');
253
254 is.read(buffer.data(),
255 static_cast<std::streamsize>(buffer.size()));
256
257 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
258 m_InsLogo.at(0) = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
259 static_cast<int>(fd.size()));
260 }
261 else
262 {
263 m_InsLogo.at(0) = LoadTexture("resources/images/INS_logo_rectangular_white_small.png");
264 }
265
266 if (fs.is_file("resources/images/INS_logo_rectangular_black_small.png"))
267 {
268 auto fd = fs.open("resources/images/INS_logo_rectangular_black_small.png");
269
270 LOG_DEBUG("Generating Texture for INS Logo (black) ({} byte)", fd.size());
271
272 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
273 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
274
275 std::vector<char> buffer;
276 buffer.resize(fd.size(), '\0');
277
278 is.read(buffer.data(),
279 static_cast<std::streamsize>(buffer.size()));
280
281 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
282 m_InsLogo.at(1) = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
283 static_cast<int>(fd.size()));
284 }
285 else
286 {
287 m_InsLogo.at(1) = LoadTexture("resources/images/INS_logo_rectangular_black_small.png");
288 }
289
290 if (fs.is_file("resources/images/ic_save_white_24dp.png"))
291 {
292 auto fd = fs.open("resources/images/ic_save_white_24dp.png");
293
294 LOG_DEBUG("Generating Texture for Save icon ({} byte)", fd.size());
295
296 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
297 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
298
299 std::vector<char> buffer;
300 buffer.resize(fd.size(), '\0');
301
302 is.read(buffer.data(),
303 static_cast<std::streamsize>(buffer.size()));
304
305 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
306 m_SaveButtonImage = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
307 static_cast<int>(fd.size()));
308 }
309 else
310 {
311 m_SaveButtonImage = LoadTexture("resources/images/ic_save_white_24dp.png");
312 }
313
314 if (fs.is_file("resources/images/Rose-rhodonea-curve-7x9-chart-improved.jpg"))
315 {
316 auto fd = fs.open("resources/images/Rose-rhodonea-curve-7x9-chart-improved.jpg");
317
318 LOG_DEBUG("Generating Texture for Rose figure ({} byte)", fd.size());
319
320 auto is = cmrc::memstream(const_cast<char*>(fd.begin()), // NOLINT(cppcoreguidelines-pro-type-const-cast)
321 const_cast<char*>(fd.end())); // NOLINT(cppcoreguidelines-pro-type-const-cast)
322
323 std::vector<char> buffer;
324 buffer.resize(fd.size(), '\0');
325
326 is.read(buffer.data(),
327 static_cast<std::streamsize>(buffer.size()));
328
329 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
330 m_RoseFigure = LoadTexture(reinterpret_cast<const void*>(const_cast<const char*>(buffer.data())),
331 static_cast<int>(fd.size()));
332 }
333 else
334 {
335 m_RoseFigure = LoadTexture("resources/images/Rose-rhodonea-curve-7x9-chart-improved.jpg");
336 }
337}
338
340{
341 LOG_TRACE("called");
342
344
346
348
349 auto releaseTexture = [this](ImTextureID& id) {
350 if (id)
351 {
352 DestroyTexture(id);
353 id = nullptr;
354 }
355 };
356
357 releaseTexture(m_InsLogo.at(0));
358 releaseTexture(m_InsLogo.at(1));
359 releaseTexture(m_InstinctLogo.at(0));
360 releaseTexture(m_InstinctLogo.at(1));
361 releaseTexture(m_HeaderBackground);
362 releaseTexture(m_SaveButtonImage);
363 releaseTexture(m_RoseFigure);
364
365 if (m_Editor)
366 {
367 ed::DestroyEditor(m_Editor);
368 m_Editor = nullptr;
369 }
370 if (ImPlotContext* ctx = ImPlot::GetCurrentContext())
371 {
372 ImPlot::DestroyContext(ctx);
373 }
374}
375
377{
378 LOG_TRACE("called");
379
381 {
383 return false;
384 }
385
386 return true;
387}
388
390{
391 const auto& io = ImGui::GetIO();
392 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
393 {
394 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
395 {
397 return;
398 }
399 }
400
401 ImGui::PushFont(WindowFont());
402 ImGui::OpenPopup("Quit with unsaved changes?");
403 if (ImGui::BeginPopupModal("Quit with unsaved changes?", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
404 {
405 ImGui::Text("Do you want to save your changes or discard them?");
406 if (ImGui::Button("Save"))
407 {
408 if (flow::GetCurrentFilename().empty())
409 {
410 ImGuiFileDialog::Instance()->OpenModal("Save Flow", "Save Flow", ".flow", (flow::GetFlowPath() / ".").string(), 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite);
411 ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtention, ".flow", ImVec4(0.0F, 1.0F, 0.0F, 0.9F));
412 }
413 else
414 {
418 Quit();
419 ImGui::CloseCurrentPopup();
420 }
421 }
422
423 if (ImGuiFileDialog::Instance()->Display("Save Flow", ImGuiWindowFlags_NoCollapse, ImVec2(600, 400)))
424 {
425 if (ImGuiFileDialog::Instance()->IsOk())
426 {
427 flow::SetCurrentFilename(ImGuiFileDialog::Instance()->GetFilePathName());
429 }
430
432 ImGuiFileDialog::Instance()->Close();
433 Quit();
434 ImGui::CloseCurrentPopup();
435 }
436 ImGui::SameLine();
437 if (ImGui::Button("Discard"))
438 {
441 Quit();
442 ImGui::CloseCurrentPopup();
443 }
444 ImGui::SameLine();
445 if (ImGui::Button("Cancel"))
446 {
448 ImGui::CloseCurrentPopup();
449 }
450 ImGui::EndPopup();
451 }
452 ImGui::PopFont();
453}
454
456{
457 ImGui::PushFont(WindowFont());
458 ImGuiFileDialog::Instance()->OpenDialog("Save Flow", "Save Flow", ".flow", (flow::GetFlowPath() / ".").string(), 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite);
459 ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtention, ".flow", ImVec4(0.0F, 1.0F, 0.0F, 0.9F));
460
461 const auto& io = ImGui::GetIO();
462 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
463 {
464 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
465 {
467 ImGuiFileDialog::Instance()->Close();
468 ImGui::PopFont();
469 return;
470 }
471 }
472
473 if (ImGuiFileDialog::Instance()->Display("Save Flow", ImGuiWindowFlags_NoCollapse, ImVec2(600, 400)))
474 {
475 if (ImGuiFileDialog::Instance()->IsOk())
476 {
477 flow::SetCurrentFilename(ImGuiFileDialog::Instance()->GetFilePathName());
479 }
480
482 ImGuiFileDialog::Instance()->Close();
483 }
484 ImGui::PopFont();
485}
486
488{
489 const auto& io = ImGui::GetIO();
490 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
491 {
492 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
493 {
495 return;
496 }
497 }
498
499 ImGui::PushFont(WindowFont());
500 ImGui::OpenPopup("Clear Nodes and discard unsaved changes?");
501 if (ImGui::BeginPopupModal("Clear Nodes and discard unsaved changes?", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
502 {
503 ImGui::Text("Do you want to save your changes before clearing the nodes?");
504 if (ImGui::Button("Save"))
505 {
506 if (flow::GetCurrentFilename().empty())
507 {
508 ImGuiFileDialog::Instance()->OpenModal("Save Flow", "Save Flow", ".flow", (flow::GetFlowPath() / ".").string(), 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite);
509 ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtention, ".flow", ImVec4(0.0F, 1.0F, 0.0F, 0.9F));
510 }
511 else
512 {
518 ImGui::CloseCurrentPopup();
519 }
520 }
521
522 if (ImGuiFileDialog::Instance()->Display("Save Flow", ImGuiWindowFlags_NoCollapse, ImVec2(600, 400)))
523 {
524 if (ImGuiFileDialog::Instance()->IsOk())
525 {
526 flow::SetCurrentFilename(ImGuiFileDialog::Instance()->GetFilePathName());
528
529 ImGuiFileDialog::Instance()->Close();
533 }
534
536
537 ImGui::CloseCurrentPopup();
538 }
539 ImGui::SameLine();
540 if (ImGui::Button("Discard"))
541 {
546 ImGui::CloseCurrentPopup();
547 }
548 ImGui::SameLine();
549 if (ImGui::Button("Cancel"))
550 {
552 ImGui::CloseCurrentPopup();
553 }
554 ImGui::EndPopup();
555 }
556 ImGui::PopFont();
557}
558
560{
561 ImGui::PushFont(WindowFont());
563 {
564 ImGui::OpenPopup("Discard unsaved changes?");
565 if (ImGui::BeginPopupModal("Discard unsaved changes?", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
566 {
567 ImGui::Text("Do you want to save your changes or discard them before loading?");
568 if (ImGui::Button("Save"))
569 {
570 if (flow::GetCurrentFilename().empty())
571 {
572 ImGuiFileDialog::Instance()->OpenModal("Save Flow##Load", "Save Flow", ".flow", (flow::GetFlowPath() / ".").string(), 1, nullptr, ImGuiFileDialogFlags_ConfirmOverwrite);
573 ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtention, ".flow", ImVec4(0.0F, 1.0F, 0.0F, 0.9F));
574 }
575 else
576 {
579 ImGui::CloseCurrentPopup();
580 }
581 }
582
583 if (ImGuiFileDialog::Instance()->Display("Save Flow##Load", ImGuiWindowFlags_NoCollapse, ImVec2(600, 400)))
584 {
585 if (ImGuiFileDialog::Instance()->IsOk())
586 {
587 flow::SetCurrentFilename(ImGuiFileDialog::Instance()->GetFilePathName());
589 }
590
592 ImGuiFileDialog::Instance()->Close();
593 ImGui::CloseCurrentPopup();
594 }
595 ImGui::SameLine();
596 if (ImGui::Button("Discard"))
597 {
599 ImGui::CloseCurrentPopup();
600 }
601 ImGui::SameLine();
602 if (ImGui::Button("Cancel"))
603 {
605 ImGui::CloseCurrentPopup();
606 }
607 ImGui::EndPopup();
608 }
609 }
610 else
611 {
612 ImGuiFileDialog::Instance()->OpenDialog("Load Flow", "Load Flow", ".flow", (flow::GetFlowPath() / ".").string(), 1, nullptr);
613 ImGuiFileDialog::Instance()->SetFileStyle(IGFD_FileStyleByExtention, ".flow", ImVec4(0.0F, 1.0F, 0.0F, 0.9F));
614
615 static bool loadSuccessful = true;
616
617 auto& io = ImGui::GetIO();
618 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
619 {
620 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
621 {
623 loadSuccessful = true;
624 ImGuiFileDialog::Instance()->Close();
625 ImGui::PopFont();
626 return;
627 }
628 }
629
630 if (ImGuiFileDialog::Instance()->Display("Load Flow", ImGuiWindowFlags_NoCollapse, ImVec2(600, 400)))
631 {
632 if (ImGuiFileDialog::Instance()->IsOk())
633 {
634 loadSuccessful = flow::LoadFlow(ImGuiFileDialog::Instance()->GetFilePathName());
635 if (loadSuccessful)
636 {
638 frameCountNavigate = ImGui::GetFrameCount();
639 ImGuiFileDialog::Instance()->Close();
640 }
641 }
642 else
643 {
645 ImGuiFileDialog::Instance()->Close();
646 }
647 }
648 if (!loadSuccessful)
649 {
650 ImGui::OpenPopup("Could not open file");
651 }
652
653 if (ImGui::BeginPopupModal("Could not open file", nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize))
654 {
655 ImGui::Text("Could not open the flow file. This can have the following reasons:");
656 ImGui::Text("- the filename specified is invalid");
657 ImGui::Text("- the program has insufficient permissions to access the file");
658 ImGui::Text("- the provided file was not a valid flow file");
659 ImGui::Text("Read the log output for further information");
660 if (ImGui::Button("Close"))
661 {
662 loadSuccessful = true;
663 ImGui::CloseCurrentPopup();
664 }
665 ImGui::EndPopup();
666 }
667 }
668 ImGui::PopFont();
669}
670
672{
673 ImGui::PushFont(WindowFont());
674 const char* title = renameNode->kind == Node::Kind::GroupBox ? "Rename Group Box" : "Rename Node";
675 ImGui::OpenPopup(title);
676 if (ImGui::BeginPopupModal(title, nullptr, ImGuiWindowFlags_AlwaysAutoResize))
677 {
678 static std::string nameBackup = renameNode->name;
679 if (nameBackup.empty())
680 {
681 nameBackup = renameNode->name;
682 }
683
684 auto& io = ImGui::GetIO();
685 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
686 {
687 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
688 {
689 if (renameNode)
690 {
691 renameNode->name = nameBackup;
692 }
693 nameBackup.clear();
694 renameNode = nullptr;
695 ImGui::CloseCurrentPopup();
696 ImGui::EndPopup();
697 ImGui::PopFont();
698 return;
699 }
700 }
701
702 if (ImGui::InputTextMultiline(fmt::format("##{}", size_t(renameNode->id)).c_str(), &renameNode->name, ImVec2(0, 65), ImGuiInputTextFlags_CtrlEnterForNewLine | ImGuiInputTextFlags_EnterReturnsTrue))
703 {
704 nameBackup.clear();
705 renameNode = nullptr;
706 ImGui::CloseCurrentPopup();
707 }
708 ImGui::SameLine();
709 gui::widgets::HelpMarker("Hold SHIFT or use mouse to select text.\n"
710 "CTRL+Left/Right to word jump.\n"
711 "CTRL+A or double-click to select all.\n"
712 "CTRL+X,CTRL+C,CTRL+V clipboard.\n"
713 "CTRL+Z,CTRL+Y undo/redo.\n"
714 "ESCAPE to revert.");
715 if (ImGui::Button("Accept"))
716 {
717 nameBackup.clear();
718 renameNode = nullptr;
720 ImGui::CloseCurrentPopup();
721 }
722 ImGui::SameLine();
723 if (ImGui::Button("Cancel"))
724 {
725 if (renameNode)
726 {
727 renameNode->name = nameBackup;
728 }
729 nameBackup.clear();
730 renameNode = nullptr;
731 ImGui::CloseCurrentPopup();
732 }
733 ImGui::EndPopup();
734 }
735 ImGui::PopFont();
736}
737
739{
740 ImGui::PushFont(WindowFont());
741 const char* title = "Rename Pin";
742 ImGui::OpenPopup(title);
743 if (ImGui::BeginPopupModal(title, nullptr, ImGuiWindowFlags_AlwaysAutoResize))
744 {
745 static std::string nameBackup = renamePin->name;
746 if (nameBackup.empty())
747 {
748 nameBackup = renamePin->name;
749 }
750
751 auto& io = ImGui::GetIO();
752 if (!io.KeyCtrl && !io.KeyAlt && !io.KeyShift && !io.KeySuper)
753 {
754 if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
755 {
756 if (renamePin)
757 {
758 renamePin->name = nameBackup;
759 }
760 nameBackup.clear();
761 renamePin = nullptr;
762 ImGui::CloseCurrentPopup();
763 ImGui::EndPopup();
764 ImGui::PopFont();
765 return;
766 }
767 }
768
769 if (ImGui::InputTextMultiline(fmt::format("##{}", size_t(renamePin->id)).c_str(), &renamePin->name, ImVec2(0, 65), ImGuiInputTextFlags_CtrlEnterForNewLine | ImGuiInputTextFlags_EnterReturnsTrue))
770 {
771 nameBackup.clear();
772 renamePin = nullptr;
773 ImGui::CloseCurrentPopup();
774 }
775 ImGui::SameLine();
776 gui::widgets::HelpMarker("Hold SHIFT or use mouse to select text.\n"
777 "CTRL+Left/Right to word jump.\n"
778 "CTRL+A or double-click to select all.\n"
779 "CTRL+X,CTRL+C,CTRL+V clipboard.\n"
780 "CTRL+Z,CTRL+Y undo/redo.\n"
781 "ESCAPE to revert.");
782 if (ImGui::Button("Accept"))
783 {
784 nameBackup.clear();
785 renamePin = nullptr;
787 ImGui::CloseCurrentPopup();
788 }
789 ImGui::SameLine();
790 if (ImGui::Button("Cancel"))
791 {
792 if (renamePin)
793 {
794 renamePin->name = nameBackup;
795 }
796 nameBackup.clear();
797 renamePin = nullptr;
798 ImGui::CloseCurrentPopup();
799 }
800 ImGui::EndPopup();
801 }
802 ImGui::PopFont();
803}
804
806{
807 bool firstFrame = ImGui::GetFrameCount() == 1;
808
809 if (frameCountNavigate && ImGui::GetFrameCount() - frameCountNavigate > 3)
810 {
812 ed::NavigateToContent();
813 }
814
815 switch (globalAction)
816 {
818 Quit();
819 break;
822 break;
825 break;
828 break;
831 break;
832 default:
833 break;
834 }
835
836 gui::UpdateTouch(deltaTime);
837
838 if (ed::AreShortcutsEnabled())
839 {
841 }
842
843 ImGui::PushFont(PanelFont());
845 menuBarHeight = ImGui::GetCursorPosY();
846 ImGui::PopFont();
847
848 ed::SetCurrentEditor(m_Editor);
849
850 static ed::NodeId contextNodeId = 0;
851 static ed::LinkId contextLinkId = 0;
852 static ed::PinId contextPinId = 0;
853 static bool createNewNode = false;
854 static Pin* newNodeLinkPin = nullptr;
855
856 bool leftPaneActive = false;
857 if (!hideLeftPane)
858 {
859 gui::widgets::Splitter("Main Splitter", true, SPLITTER_THICKNESS, &leftPaneWidth, &rightPaneWidth, 25.0F, 50.0F);
860
861 ImGui::PushFont(PanelFont());
863 ImGui::PopFont();
864
865 ImGui::SameLine(0.0F, 12.0F);
866 }
867
868 // ToolTips have to be shown outside of the NodeEditor Context, so save the Tooltip and push it afterwards
869 std::string tooltipText;
870
871 ImGui::BeginGroup();
872
873 bool darkMode = ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_Bg].x
874 + ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_Bg].y
875 + ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_Bg].z
876 > 2.0F;
877
879 {
880 float blueprintHeight = ImGui::GetContentRegionAvail().y - bottomViewHeight + (isUsingBigPanelFont() ? 48.5F : 28.5F);
881 ImGui::PushStyleColor(ImGuiCol_Separator, IM_COL32_BLACK_TRANS);
882 gui::widgets::Splitter("Log Splitter", false, 6.0F, &blueprintHeight, &bottomViewHeight, 400.0F, BOTTOM_VIEW_UNCOLLAPSED_MIN_HEIGHT);
883 ImGui::PopStyleColor();
884 }
885
886 ed::Begin("Node editor", ImVec2(0, ImGui::GetContentRegionAvail().y - bottomViewHeight + SPLITTER_THICKNESS));
887 {
888 static Pin* newLinkPin = nullptr;
889
890 auto cursorTopLeft = ImGui::GetCursorScreenPos();
891
892 static util::BlueprintNodeBuilder builder(m_HeaderBackground, GetTextureWidth(m_HeaderBackground), GetTextureHeight(m_HeaderBackground));
893
894 auto textColor = ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_NodeBg].x
895 + ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_NodeBg].y
896 + ax::NodeEditor::GetStyle().Colors[ax::NodeEditor::StyleColor_NodeBg].z
897 > 2.0F
898 ? IM_COL32(0, 0, 0, 255)
899 : IM_COL32(255, 255, 255, 255);
900
901 for (auto* node : nm::m_Nodes()) // Blueprint || Simple
902 {
903 if (node->kind != Node::Kind::Blueprint && node->kind != Node::Kind::Simple) // NOLINT(misc-redundant-expression) - false positive warning
904 {
905 continue;
906 }
907
908 const auto isSimple = node->kind == Node::Kind::Simple;
909
910 bool hasOutputDelegates = false;
911 for (const auto& output : node->outputPins)
912 {
913 if (output.type == Pin::Type::Delegate)
914 {
915 hasOutputDelegates = true;
916 }
917 }
918
919 builder.Begin(node->id);
920
921 if (!isSimple) // Header Text for Blueprint Nodes
922 {
923 if (node->isDisabled()) // Node disabled
924 {
925 builder.Header(ImColor(192, 192, 192)); // Silver
926 }
927 else if (node->isInitialized())
928 {
929 builder.Header(ImColor(128, 255, 128)); // Light green
930 }
931 else
932 {
933 builder.Header(ImColor(255, 128, 128)); // Light red
934 }
935 ImGui::Spring(0);
936 ImGui::PushStyleColor(ImGuiCol_Text, textColor);
937 ImGui::TextUnformatted(node->name.c_str());
938 ImGui::PopStyleColor();
939 ImGui::Spring(1);
940 if (node->getState() == Node::State::DoInitialize)
941 {
942 gui::widgets::Spinner(("##Spinner " + node->nameId()).c_str(), ImColor(144, 202, 238), 10.0F, 1.0F);
943 }
944 else if (node->getState() == Node::State::Initializing)
945 {
946 gui::widgets::Spinner(("##Spinner " + node->nameId()).c_str(), ImColor(144, 238, 144), 10.0F, 1.0F);
947 }
948 else if (node->getState() == Node::State::DoDeinitialize)
949 {
950 gui::widgets::Spinner(("##Spinner " + node->nameId()).c_str(), ImColor(255, 222, 122), 10.0F, 1.0F);
951 }
952 else if (node->getState() == Node::State::Deinitializing)
953 {
954 gui::widgets::Spinner(("##Spinner " + node->nameId()).c_str(), ImColor(255, 160, 122), 10.0F, 1.0F);
955 }
956 else
957 {
958 ImGui::Dummy(ImVec2(ImGui::GetStyle().ItemSpacing.x + 12.0F + ImGui::GetStyle().FramePadding.x, 26));
959 }
960 if (hasOutputDelegates)
961 {
962 ImGui::BeginVertical("delegates", ImVec2(0, 26));
963 ImGui::Spring(1, 0);
964 for (const auto& output : node->outputPins)
965 {
966 if (output.type != Pin::Type::Delegate)
967 {
968 continue;
969 }
970
971 auto alpha = ImGui::GetStyle().Alpha;
972 if (newLinkPin && newLinkPin->kind == Pin::Kind::Input && &output != newLinkPin
973 && !reinterpret_cast<InputPin*>(newLinkPin)->canCreateLink(output))
974 {
975 alpha = alpha * (48.0F / 255.0F);
976 }
977
978 ed::BeginPin(output.id, ed::PinKind::Output);
979 ed::PinPivotAlignment(ImVec2(1.0F, 0.5F));
980 ed::PinPivotSize(ImVec2(0, 0));
981 ImGui::BeginHorizontal(output.id.AsPointer());
982 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
983 // if (!output.name.empty())
984 // {
985 // ImGui::PushStyleColor(ImGuiCol_Text, textColor);
986 // ImGui::TextUnformatted(output.name.c_str());
987 // ImGui::PopStyleColor();
988 // ImGui::Spring(0);
989 // }
990 output.drawPinIcon(output.isPinLinked(), static_cast<int>(alpha * 255));
991 ImGui::Spring(0, ImGui::GetStyle().ItemSpacing.x / 2);
992 ImGui::EndHorizontal();
993 ImGui::PopStyleVar();
994 ed::EndPin();
995
996 // DrawItemRect(ImColor(255, 0, 0));
997 }
998 ImGui::Spring(1, 0);
999 ImGui::EndVertical();
1000 ImGui::Spring(0, ImGui::GetStyle().ItemSpacing.x / 2);
1001 }
1002 else
1003 {
1004 ImGui::Spring(0);
1005 }
1006 builder.EndHeader();
1007 }
1008
1009 for (auto& input : node->inputPins) // Input Pins
1010 {
1011 auto alpha = ImGui::GetStyle().Alpha;
1012 if (newLinkPin && newLinkPin->kind == Pin::Kind::Output && &input != newLinkPin
1013 && !reinterpret_cast<OutputPin*>(newLinkPin)->canCreateLink(input))
1014 {
1015 alpha = alpha * (48.0F / 255.0F);
1016 }
1017
1018 builder.Input(input.id);
1019 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
1020 input.drawPinIcon(input.isPinLinked(), static_cast<int>(alpha * 255));
1021 if (ImGui::IsItemHovered()) { tooltipText = fmt::format("{}", fmt::join(input.dataIdentifier, "\n")); }
1022 if (_showQueueSizeOnPins && input.type == Pin::Type::Flow && input.isPinLinked())
1023 {
1024 auto cursor = ImGui::GetCursorPos();
1025 std::string text = fmt::format("{}{}", input.queue.size(), input.queueBlocked ? "*" : "");
1026 auto* drawList = ImGui::GetWindowDrawList();
1027 drawList->AddText(ImVec2(cursor.x - 26.0F, cursor.y + 2.F), IM_COL32(255, 0, 0, 255), text.c_str());
1028 }
1029
1030 ImGui::Spring(0);
1031 if (!input.name.empty())
1032 {
1033 bool noneType = input.type == Pin::Type::None;
1034 if (noneType)
1035 {
1036 ImGui::BeginDisabled();
1037 }
1038 ImGui::PushStyleColor(ImGuiCol_Text, textColor);
1039 ImGui::TextUnformatted(input.name.c_str());
1040 ImGui::PopStyleColor();
1041 if (noneType)
1042 {
1043 ImGui::EndDisabled();
1044 }
1045 ImGui::Spring(0);
1046 }
1047 ImGui::PopStyleVar();
1049 }
1050
1051 if (isSimple) // Middle Text for Simple Nodes
1052 {
1053 builder.Middle();
1054
1055 ImGui::Spring(1, 0);
1056 ImGui::PushStyleColor(ImGuiCol_Text, textColor);
1057 ImGui::TextUnformatted(node->name.c_str());
1058 ImGui::PopStyleColor();
1059 ImGui::Spring(1, 0);
1060 }
1061
1062 for (const auto& output : node->outputPins) // Output Pins
1063 {
1064 if (!isSimple && output.type == Pin::Type::Delegate)
1065 {
1066 continue;
1067 }
1068
1069 auto alpha = ImGui::GetStyle().Alpha;
1070 if (newLinkPin && newLinkPin->kind == Pin::Kind::Input && &output != newLinkPin
1071 && !reinterpret_cast<InputPin*>(newLinkPin)->canCreateLink(output))
1072 {
1073 alpha = alpha * (48.0F / 255.0F);
1074 }
1075
1076 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, alpha);
1077 builder.Output(output.id);
1078 if (!output.name.empty())
1079 {
1080 ImGui::Spring(0);
1081 bool noneType = output.type == Pin::Type::None;
1082 if (noneType)
1083 {
1084 ImGui::BeginDisabled();
1085 }
1086 ImGui::PushStyleColor(ImGuiCol_Text, textColor);
1087 ImGui::TextUnformatted(output.name.c_str());
1088 ImGui::PopStyleColor();
1089 if (noneType)
1090 {
1091 ImGui::EndDisabled();
1092 }
1093 }
1094 ImGui::Spring(0);
1095 output.drawPinIcon(output.isPinLinked(), static_cast<int>(alpha * 255));
1096 if (ImGui::IsItemHovered()) { tooltipText = fmt::format("{}", fmt::join(output.dataIdentifier, "\n")); }
1097 if (_showQueueSizeOnPins && output.type == Pin::Type::Flow && output.isPinLinked())
1098 {
1099 auto cursor = ImGui::GetCursorPos();
1100 std::string text = fmt::format("{}", output.noMoreDataAvailable ? "F" : "P");
1101 auto* drawList = ImGui::GetWindowDrawList();
1102 drawList->AddText(ImVec2(cursor.x - 26.0F, cursor.y + 2.F), IM_COL32(255, 0, 0, 255), text.c_str());
1103 }
1104
1105 ImGui::PopStyleVar();
1107 }
1108
1109 builder.End();
1110 }
1111
1112 for (const auto* node : nm::m_Nodes()) // GroupBox
1113 {
1114 if (node->kind != Node::Kind::GroupBox)
1115 {
1116 continue;
1117 }
1118
1119 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.75F);
1120 ed::PushStyleColor(ed::StyleColor_NodeBg, m_colors[COLOR_GROUP_HEADER_BG]);
1121 ed::PushStyleColor(ed::StyleColor_NodeBorder, m_colors[COLOR_GROUP_OUTER_BORDER]);
1122 ed::BeginNode(node->id);
1123 ImGui::PushID(node->id.AsPointer());
1124 ImGui::BeginVertical("content");
1125 ImGui::BeginHorizontal("horizontal");
1126 ImGui::Spring(1);
1127 ImGui::PushStyleColor(ImGuiCol_Text, m_colors[COLOR_GROUP_HEADER_TEXT]);
1128 ImGui::TextUnformatted(node->name.c_str());
1129 ImGui::PopStyleColor();
1130 ImGui::Spring(1);
1131 ImGui::EndHorizontal();
1132 ed::Group(node->_size);
1133 ImGui::EndVertical();
1134 ImGui::PopID();
1135 ed::EndNode();
1136 ed::PopStyleColor(2);
1137 ImGui::PopStyleVar();
1138 }
1139
1140 for (const auto* node : nm::m_Nodes())
1141 {
1142 for (const auto& output : node->outputPins) // Output Pins
1143 {
1144 auto color = output.getIconColor();
1145 if (output.type == Pin::Type::Flow)
1146 {
1147 color = darkMode ? ImColor{ 0, 0, 0 } : ImColor{ 255, 255, 255 };
1148 }
1149
1150 for (const auto& link : output.links)
1151 {
1152 ed::Link(link.linkId, output.id, link.connectedPinId, color, 2.0F * defaultFontRatio());
1153 }
1154 }
1155 }
1156
1157 if (!createNewNode)
1158 {
1159 if (ed::BeginCreate(ImColor(255, 255, 255), 2.0F))
1160 {
1161 auto showLabel = [](const char* label, ImColor color) {
1162 ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetTextLineHeight());
1163 auto size = ImGui::CalcTextSize(label);
1164
1165 auto padding = ImGui::GetStyle().FramePadding;
1166 auto spacing = ImGui::GetStyle().ItemSpacing;
1167
1168 ImGui::SetCursorPos(ImGui::GetCursorPos() + ImVec2(spacing.x, -spacing.y));
1169
1170 auto rectMin = ImGui::GetCursorScreenPos() - padding;
1171 auto rectMax = ImGui::GetCursorScreenPos() + size + padding;
1172
1173 auto* drawList = ImGui::GetWindowDrawList();
1174 drawList->AddRectFilled(rectMin, rectMax, color, size.y * 0.15F);
1175 ImGui::TextUnformatted(label);
1176 };
1177
1178 ed::PinId startPinId = 0;
1179 ed::PinId endPinId = 0;
1180 if (ed::QueryNewLink(&startPinId, &endPinId))
1181 {
1182 Pin* startPin = nm::FindInputPin(startPinId);
1183 Pin* endPin = nm::FindInputPin(endPinId);
1184 if (!startPin) { startPin = nm::FindOutputPin(startPinId); }
1185 if (!endPin) { endPin = nm::FindOutputPin(endPinId); }
1186
1187 newLinkPin = startPin ? startPin : endPin;
1188
1189 if (startPin && startPin->kind == Pin::Kind::Input)
1190 {
1191 std::swap(startPin, endPin);
1192 std::swap(startPinId, endPinId);
1193 }
1194
1195 if (startPin && endPin)
1196 {
1197 if (endPin == startPin)
1198 {
1199 ed::RejectNewItem(ImColor(255, 0, 0), 2.0F);
1200 }
1201 else if (endPin->kind == startPin->kind)
1202 {
1203 showLabel("x Incompatible Pin Kind", ImColor(45, 32, 32, 180));
1204 ed::RejectNewItem(ImColor(255, 0, 0), 2.0F);
1205 }
1206 else if (endPin->parentNode == startPin->parentNode)
1207 {
1208 showLabel("x Cannot connect to self", ImColor(45, 32, 32, 180));
1209 ed::RejectNewItem(ImColor(255, 0, 0), 1.0F);
1210 }
1211 else if (endPin->type != startPin->type)
1212 {
1213 showLabel("x Incompatible Pin Type", ImColor(45, 32, 32, 180));
1214 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1215 }
1216 else if (reinterpret_cast<InputPin*>(endPin)->isPinLinked())
1217 {
1218 showLabel("End Pin already linked", ImColor(45, 32, 32, 180));
1219 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1220 }
1221 else if (startPin->type == Pin::Type::Flow
1223 {
1224 showLabel(fmt::format("The data type [{}]\ncan't be linked to [{}]",
1225 fmt::join(startPin->dataIdentifier, ","),
1226 fmt::join(endPin->dataIdentifier, ","))
1227 .c_str(),
1228 ImColor(45, 32, 32, 180));
1229 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1230 }
1231 else if (startPin->type == Pin::Type::Delegate
1232 && (startPin->parentNode == nullptr
1233 || std::ranges::find(endPin->dataIdentifier, startPin->parentNode->type()) == endPin->dataIdentifier.end()))
1234 {
1235 if (startPin->parentNode != nullptr)
1236 {
1237 showLabel(fmt::format("The delegate type [{}]\ncan't be linked to [{}]",
1238 startPin->parentNode->type(),
1239 fmt::join(endPin->dataIdentifier, ","))
1240 .c_str(),
1241 ImColor(45, 32, 32, 180));
1242 }
1243
1244 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1245 }
1246 else if ((startPin->type == Pin::Type::Object || startPin->type == Pin::Type::Matrix) // NOLINT(misc-redundant-expression) - false positive warning
1248 {
1249 showLabel(fmt::format("The data type [{}]\ncan't be linked to [{}]",
1250 fmt::join(startPin->dataIdentifier, ","),
1251 fmt::join(endPin->dataIdentifier, ","))
1252 .c_str(),
1253 ImColor(45, 32, 32, 180));
1254 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1255 }
1256 else
1257 {
1258 showLabel("+ Create Link", ImColor(32, 45, 32, 180));
1259 if (ed::AcceptNewItem(ImColor(128, 255, 128), 4.0F))
1260 {
1261 reinterpret_cast<OutputPin*>(startPin)->createLink(*reinterpret_cast<InputPin*>(endPin));
1262 }
1263 }
1264 }
1265 }
1266
1267 ed::PinId pinId = 0;
1268 if (ed::QueryNewNode(&pinId))
1269 {
1270 newLinkPin = nm::FindInputPin(pinId);
1271 if (!newLinkPin) { newLinkPin = nm::FindOutputPin(pinId); }
1272
1273 if (newLinkPin && newLinkPin->kind == Pin::Kind::Input && reinterpret_cast<InputPin*>(newLinkPin)->isPinLinked())
1274 {
1275 showLabel("End Pin already linked", ImColor(45, 32, 32, 180));
1276 ed::RejectNewItem(ImColor(255, 128, 128), 1.0F);
1277 }
1278 else
1279 {
1280 if (newLinkPin)
1281 {
1282 showLabel("+ Create Node", ImColor(32, 45, 32, 180));
1283 }
1284
1285 if (ed::AcceptNewItem())
1286 {
1287 createNewNode = true;
1288 newNodeLinkPin = nm::FindInputPin(pinId);
1289 if (!newNodeLinkPin) { newNodeLinkPin = nm::FindOutputPin(pinId); }
1290 newLinkPin = nullptr;
1291 ed::Suspend();
1292 ImGui::OpenPopup("Create New Node");
1293 ed::Resume();
1294 }
1295 }
1296 }
1297 }
1298 else
1299 {
1300 newLinkPin = nullptr;
1301 }
1302
1303 ed::EndCreate();
1304
1305 if (ed::BeginDelete())
1306 {
1307 ed::LinkId linkId = 0;
1308 while (ed::QueryDeletedLink(&linkId))
1309 {
1310 if (ed::AcceptDeletedItem())
1311 {
1312 bool deleted = false;
1313 for (auto* node : nm::m_Nodes())
1314 {
1315 if (deleted) { break; }
1316 for (auto& output : node->outputPins)
1317 {
1318 if (deleted) { break; }
1319 for (const auto& link : output.links)
1320 {
1321 if (link.linkId == linkId)
1322 {
1323 output.deleteLink(*link.getConnectedPin());
1324 deleted = true;
1325 break;
1326 }
1327 }
1328 }
1329 }
1330 }
1331 }
1332
1333 ed::NodeId nodeId = 0;
1334 while (ed::QueryDeletedNode(&nodeId))
1335 {
1336 if (Node* node = nm::FindNode(nodeId))
1337 {
1338 if (node->isTransient())
1339 {
1340 break;
1341 }
1342 }
1343 if (ed::AcceptDeletedItem())
1344 {
1345 nm::DeleteNode(nodeId);
1346 }
1347 }
1348 }
1349 ed::EndDelete();
1350 }
1351
1352 ImGui::SetCursorScreenPos(cursorTopLeft);
1353 }
1354
1355 // Shortcut enable/disable
1356 ax::NodeEditor::EnableShortcuts(ed::IsActive() || leftPaneActive);
1357
1358 // auto openPopupPosition = ImGui::GetMousePos();
1359 ed::Suspend();
1360 if (ed::ShowNodeContextMenu(&contextNodeId))
1361 {
1362 ImGui::OpenPopup("Node Context Menu");
1363 }
1364 else if (ed::ShowPinContextMenu(&contextPinId))
1365 {
1366 ImGui::OpenPopup("Pin Context Menu");
1367 }
1368 else if (ed::ShowLinkContextMenu(&contextLinkId) && ed::IsActive())
1369 {
1370 ImGui::OpenPopup("Link Context Menu");
1371 }
1372 else if (ed::ShowBackgroundContextMenu() && ed::IsActive())
1373 {
1374 ImGui::OpenPopup("Create New Node");
1375 newNodeLinkPin = nullptr;
1376 }
1377 else if (ed::NodeId doubleClickedNodeId = ed::GetDoubleClickedNode())
1378 {
1379 Node* node = nm::FindNode(doubleClickedNodeId);
1380 node->_showConfig = true;
1381 node->_configWindowFocus = true;
1382 }
1383 ed::Resume();
1384
1385 ed::Suspend();
1386 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8, 8));
1387 static Node* renameNode = nullptr;
1388 static Pin* renamePin = nullptr;
1389 if (ImGui::BeginPopup("Node Context Menu"))
1390 {
1391 auto* node = nm::FindNode(contextNodeId);
1392
1393 ImGui::TextUnformatted("Node Context Menu");
1394 ImGui::Separator();
1395 if (node)
1396 {
1397 ImGui::Text("ID: %lu", size_t(node->id));
1398 ImGui::Text("Type: %s", node->type().c_str());
1399 ImGui::Text("Kind: %s", std::string(node->kind).c_str());
1400 ImGui::Text("Inputs: %lu", node->inputPins.size());
1401 ImGui::Text("Outputs: %lu", node->outputPins.size());
1402 ImGui::Text("State: %s", Node::toString(node->getState()).c_str());
1403 ImGui::Text("Mode: %s", node->getMode() == Node::Mode::POST_PROCESSING ? "Post-processing" : "Real-time");
1404 ImGui::Separator();
1405 if (node->kind != Node::Kind::GroupBox)
1406 {
1407 if (ImGui::MenuItem(node->isInitialized() ? "Reinitialize" : "Initialize", "",
1408 false, node->isInitialized() || node->getState() == Node::State::Deinitialized))
1409 {
1410 if (node->isInitialized()) { node->doReinitialize(); }
1411 else { node->doInitialize(); }
1412 }
1413 if (ImGui::MenuItem("Deinitialize", "", false, node->isInitialized()))
1414 {
1415 node->doDeinitialize();
1416 }
1417 if (ImGui::MenuItem("Wake Worker"))
1418 {
1419 node->wakeWorker();
1420 }
1421 ImGui::Separator();
1422 if (node->_hasConfig && ImGui::MenuItem("Configure", "", false))
1423 {
1424 node->_showConfig = true;
1425 node->_configWindowFocus = true;
1426 }
1427 if (ImGui::MenuItem(node->isDisabled() ? "Enable" : "Disable", "", false, !node->isTransient()))
1428 {
1429 if (node->isDisabled())
1430 {
1431 node->doEnable();
1432 }
1433 else
1434 {
1435 node->doDisable();
1436 }
1438 }
1439 }
1440 if (ImGui::MenuItem("Rename"))
1441 {
1442 renameNode = node;
1443 }
1444 ImGui::Separator();
1445 if (ImGui::MenuItem("Delete", "", false, !node->isTransient()))
1446 {
1447 ed::DeleteNode(contextNodeId);
1448 }
1449 }
1450 else
1451 {
1452 ImGui::Text("Unknown node: %lu", size_t(contextNodeId));
1453 }
1454
1455 ImGui::EndPopup();
1456 }
1457
1458 if (renameNode) // Popup for renaming a node
1459 {
1460 ShowRenameNodeRequest(renameNode);
1461 }
1462
1463 if (ImGui::BeginPopup("Pin Context Menu"))
1464 {
1465 ImGui::TextUnformatted("Pin Context Menu");
1466 ImGui::Separator();
1467 if (auto* pin = nm::FindInputPin(contextPinId))
1468 {
1469 ImGui::Text("ID: %lu", size_t(pin->id));
1470 ImGui::Text("Node: %s", pin->parentNode ? std::to_string(size_t(pin->parentNode->id)).c_str() : "<none>");
1471 ImGui::Text("Type: %s", std::string(pin->type).c_str());
1472 ImGui::Separator();
1473 if (pin->isPinLinked())
1474 {
1475 const auto& link = pin->link;
1476 ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
1477 if (ImGui::TreeNode(fmt::format("LinkId: {}", size_t(link.linkId)).c_str()))
1478 {
1479 ImGui::BulletText("Connected Node: %s", link.connectedNode->nameId().c_str());
1480 ImGui::BulletText("Connected Pin: %s (%zu)", link.getConnectedPin()->name.c_str(),
1481 size_t(link.getConnectedPin()->id));
1482 ImGui::TreePop();
1483 }
1484 }
1485 else { ImGui::TextUnformatted("Link: Not linked"); }
1486 ImGui::Separator();
1487 if (ImGui::TreeNode(fmt::format("Queue: {}", pin->queue.size()).c_str()))
1488 {
1489 ImGui::BeginChild("QueueItems", ImVec2(ImGui::GetContentRegionAvail().x, 150), false, ImGuiWindowFlags_None);
1490 for (size_t i = 0; i < pin->queue.size(); i++) // NOLINT(modernize-loop-convert)
1491 {
1492 ImGui::Text("%s", std::string(pin->queue.at(i)->insTime.toYMDHMS()).c_str());
1493 }
1494 ImGui::EndChild();
1495 ImGui::TreePop();
1496 }
1497
1498 ImGui::Text("Queue blocked: %s", pin->queueBlocked ? "true" : "false");
1499 ImGui::Text("Temporal check: %s", pin->neededForTemporalQueueCheck ? "true" : "false");
1500 ImGui::Text("Drop queue: %s", pin->dropQueueIfNotFirable ? "true" : "false");
1501 ImGui::Separator();
1502 if (ImGui::MenuItem("Rename")) { renamePin = pin; }
1503 }
1504 else if (auto* pin = nm::FindOutputPin(contextPinId))
1505 {
1506 ImGui::Text("ID: %lu", size_t(pin->id));
1507 ImGui::Text("Node: %s", pin->parentNode ? std::to_string(size_t(pin->parentNode->id)).c_str() : "<none>");
1508 ImGui::Text("Type: %s", std::string(pin->type).c_str());
1509 ImGui::Text("Data available: %s", pin->noMoreDataAvailable ? "No" : "Yes");
1510 if (!pin->blocksConnectedNodeFromFinishing)
1511 {
1512 ImGui::TextUnformatted("Does not block connected pin from finishing!!!");
1513 }
1514 ImGui::Separator();
1515 if (pin->isPinLinked())
1516 {
1517 ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
1518 if (ImGui::TreeNode(fmt::format("Links: {}", pin->links.size()).c_str()))
1519 {
1520 for (const auto& link : pin->links)
1521 {
1522 ImGui::SetNextItemOpen(true, ImGuiCond_Appearing);
1523 if (ImGui::TreeNode(fmt::format("LinkId: {}", size_t(link.linkId)).c_str()))
1524 {
1525 ImGui::BulletText("Connected Node: %s", link.connectedNode->nameId().c_str());
1526 ImGui::BulletText("Connected Pin: %s (%zu)", link.getConnectedPin()->name.c_str(),
1527 size_t(link.getConnectedPin()->id));
1528 ImGui::TreePop();
1529 }
1530 }
1531 ImGui::TreePop();
1532 }
1533 }
1534 else { ImGui::TextUnformatted("Link: Not linked"); }
1535 ImGui::Separator();
1536 if (ImGui::MenuItem("Rename"))
1537 {
1538 renamePin = pin;
1539 }
1540 }
1541 else
1542 {
1543 ImGui::Text("Unknown pin: %lu", size_t(contextPinId));
1544 }
1545
1546 ImGui::EndPopup();
1547 }
1548
1549 if (renamePin) // Popup for renaming a pin
1550 {
1551 ShowRenamePinRequest(renamePin);
1552 }
1553
1554 if (ImGui::BeginPopup("Link Context Menu"))
1555 {
1556 ax::NodeEditor::PinId startPinId = 0;
1557 ax::NodeEditor::PinId endPinId = 0;
1558 for (const auto* node : nm::m_Nodes())
1559 {
1560 if (startPinId) { break; }
1561 for (const auto& output : node->outputPins)
1562 {
1563 if (startPinId) { break; }
1564 for (const auto& link : output.links)
1565 {
1566 if (link.linkId == contextLinkId)
1567 {
1568 startPinId = output.id;
1569 endPinId = link.connectedPinId;
1570 break;
1571 }
1572 }
1573 }
1574 }
1575
1576 ImGui::TextUnformatted("Link Context Menu");
1577 ImGui::Separator();
1578 if (startPinId)
1579 {
1580 ImGui::Text("ID: %lu", size_t(contextLinkId));
1581 ImGui::Text("From: %lu", size_t(startPinId));
1582 ImGui::Text("To: %lu", size_t(endPinId));
1583 }
1584 else
1585 {
1586 ImGui::Text("Unknown link: %lu", size_t(contextLinkId));
1587 }
1588 ImGui::Separator();
1589 if (ImGui::MenuItem("Delete"))
1590 {
1591 ed::DeleteLink(contextLinkId);
1592 }
1593 ImGui::EndPopup();
1594 }
1595
1596 static bool setKeyboardFocus = true;
1597 static ImVec2 newNodeSpawnPos{ -1, -1 };
1598 if (ImGui::BeginPopup("Create New Node"))
1599 {
1600 if (newNodeSpawnPos.x == -1 || newNodeSpawnPos.y == -1)
1601 {
1602 auto viewRect = reinterpret_cast<ax::NodeEditor::Detail::EditorContext*>(ed::GetCurrentEditor())->GetViewRect();
1603 newNodeSpawnPos = ImGui::GetMousePos();
1604 newNodeSpawnPos.x -= leftPaneWidth + SPLITTER_THICKNESS + 10.0F;
1605 newNodeSpawnPos.y -= menuBarHeight;
1606
1607 newNodeSpawnPos *= ed::GetCurrentZoom();
1608
1609 newNodeSpawnPos += viewRect.GetTL();
1610
1611 LOG_DEBUG("New Node will spawn at {}x{} - Zoom {}", newNodeSpawnPos.x, newNodeSpawnPos.y, ed::GetCurrentZoom());
1612 }
1613
1614 if (setKeyboardFocus)
1615 {
1616 ImGui::SetKeyboardFocusHere(0);
1617 }
1618 static ImGuiTextFilter filter;
1619
1620 filter.Draw("##NewNodeFilter");
1621
1622 if (setKeyboardFocus)
1623 {
1624 filter.Clear();
1625 setKeyboardFocus = false;
1626 }
1627
1628 Node* node = nullptr;
1629 for (const auto& [category, nodeInfoList] : NAV::NodeRegistry::RegisteredNodes())
1630 {
1631 // Prevent category from showing, if it is empty
1632 bool categoryHasItems = false;
1633 for (const auto& nodeInfo : nodeInfoList)
1634 {
1635 if (nodeInfo.hasCompatiblePin(newNodeLinkPin)
1636 && (filter.PassFilter(nodeInfo.type.c_str()) || filter.PassFilter(category.c_str())))
1637 {
1638 categoryHasItems = true;
1639 break;
1640 }
1641 }
1642 if (categoryHasItems)
1643 {
1644 ImGui::SetNextItemOpen(true, ImGuiCond_Once);
1645 if (ImGui::TreeNode((category + "##NewNodeTree").c_str()))
1646 {
1647 for (const auto& nodeInfo : nodeInfoList)
1648 {
1649 const auto& displayName = nodeInfo.type;
1650 const auto& constructor = nodeInfo.constructor;
1651 ImGui::Indent();
1652 if (nodeInfo.hasCompatiblePin(newNodeLinkPin)
1653 && (filter.PassFilter(nodeInfo.type.c_str()) || filter.PassFilter(category.c_str()))
1654 && ImGui::MenuItem(displayName.c_str()))
1655 {
1656 filter.Clear();
1657 node = constructor();
1658 nm::AddNode(node);
1659 }
1660 ImGui::Unindent();
1661 }
1662 ImGui::TreePop();
1663 }
1664 }
1665 }
1666
1667 if (node)
1668 {
1669 createNewNode = false;
1670
1671 ed::SetNodePosition(node->id, newNodeSpawnPos);
1672 newNodeSpawnPos = { -1, -1 };
1673
1674 if (auto* startPin = newNodeLinkPin)
1675 {
1676 if (startPin->kind == Pin::Kind::Input)
1677 {
1678 for (auto& pin : node->outputPins)
1679 {
1680 if (reinterpret_cast<InputPin*>(startPin)->canCreateLink(pin))
1681 {
1682 pin.createLink(*reinterpret_cast<InputPin*>(startPin));
1683 break;
1684 }
1685 }
1686 }
1687 else // if (startPin->kind == Pin::Kind::Output)
1688 {
1689 for (auto& pin : node->inputPins)
1690 {
1691 if (reinterpret_cast<OutputPin*>(startPin)->canCreateLink(pin))
1692 {
1693 reinterpret_cast<OutputPin*>(startPin)->createLink(pin);
1694 break;
1695 }
1696 }
1697 }
1698 }
1699 }
1700
1701 ImGui::EndPopup();
1702 }
1703 else
1704 {
1705 setKeyboardFocus = true;
1706 createNewNode = false;
1707 newNodeSpawnPos = { -1, -1 };
1708 }
1709 ImGui::PopStyleVar();
1710
1711 for (auto* node : nm::m_Nodes()) // Config Windows for nodes
1712 {
1713 if (node->_hasConfig && node->_showConfig)
1714 {
1715 ImVec2 center(ImGui::GetIO().DisplaySize.x * 0.5F, ImGui::GetIO().DisplaySize.y * 0.5F);
1716 if (node->_configWindowFocus)
1717 {
1718 ImGui::SetNextWindowCollapsed(false);
1719 ImGui::SetNextWindowFocus();
1720 node->_configWindowFocus = false;
1721 }
1722 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5F, 0.5F));
1723 ImGui::SetNextWindowSize(node->_guiConfigDefaultWindowSize, ImGuiCond_FirstUseEver);
1724 if (!node->_configWindowMutex.try_lock())
1725 {
1726 ImGui::SetNextWindowCollapsed(true, ImGuiCond_Always);
1727 node->_configWindowForceCollapse = true;
1728 }
1729 else
1730 {
1731 node->_configWindowMutex.unlock();
1732 if (node->_configWindowForceCollapse)
1733 {
1734 LOG_TRACE("Setting next window collapsed: {}", node->_configWindowIsCollapsed);
1735 ImGui::SetNextWindowCollapsed(node->_configWindowIsCollapsed);
1736 node->_configWindowForceCollapse = false;
1737 }
1738 }
1739 if (ImGui::Begin(fmt::format("{} ({})", node->nameId(), node->type()).c_str(), &(node->_showConfig),
1740 ImGuiWindowFlags_None))
1741 {
1742 ImGui::PushFont(WindowFont());
1743 bool locked = node->_lockConfigDuringRun && (node->callbacksEnabled || FlowExecutor::isRunning());
1744 if (locked) { ImGui::BeginDisabled(); }
1745 if (!node->_configWindowForceCollapse)
1746 {
1747 node->_configWindowIsCollapsed = false;
1748 node->guiConfig();
1749 }
1750 if (locked) { ImGui::EndDisabled(); }
1751 ImGui::PopFont();
1752 }
1753 else // Window is collapsed
1754 {
1755 if (!node->_configWindowForceCollapse) { node->_configWindowIsCollapsed = true; }
1756 if (ImGui::IsWindowFocused())
1757 {
1758 ed::EnableShortcuts(true);
1759 }
1760 }
1761
1762 ImGui::End();
1763 }
1764 }
1765 ed::Resume();
1766
1767 ed::End();
1768
1769 ImGui::PushFont(PanelFont());
1770 ImGui::Indent(SPLITTER_THICKNESS);
1771 ImGui::SetNextItemOpen(true, ImGuiCond_Once);
1772 if (ImGui::BeginTabBar("BottomViewTabBar"))
1773 {
1774 bool noItemSelected = bottomViewSelectedTab == BottomViewTabItem::None;
1775 if (noItemSelected)
1776 {
1777 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.0F);
1778 }
1779 if (ImGui::BeginTabItem("▼"))
1780 {
1783 ImGui::EndTabItem();
1784 }
1785 else
1786 {
1788 }
1789 if (noItemSelected)
1790 {
1791 ImGui::PopStyleVar();
1792 }
1793
1794 if (ImGui::BeginTabItem("Log Output", nullptr, firstFrame ? ImGuiTabItemFlags_SetSelected : ImGuiTabItemFlags_None))
1795 {
1796 static int scrollToBottom = 0;
1798 {
1799 scrollToBottom = 2;
1800 }
1802
1803 static bool autoScroll = true;
1804 static ImGuiTextFilter textFilter;
1805
1806 // Options menu
1807 if (ImGui::BeginPopup("Options"))
1808 {
1809 ImGui::Checkbox("Auto-scroll", &autoScroll);
1810 ImGui::EndPopup();
1811 }
1812
1813 // Main window
1814 if (ImGui::Button("Options"))
1815 {
1816 ImGui::OpenPopup("Options");
1817 }
1818 ImGui::SameLine();
1819 ImGui::SetNextItemWidth(100.0F * panelFontRatio());
1820 static int logLevelFilterSelected = spdlog::level::info;
1821 if (ImGui::BeginCombo("##LogLevelCombo", spdlog::level::to_string_view(static_cast<spdlog::level::level_enum>(logLevelFilterSelected)).begin()))
1822 {
1823 for (int n = spdlog::level::debug; n < spdlog::level::critical; n++)
1824 {
1825 const bool is_selected = (logLevelFilterSelected == n);
1826 if (ImGui::Selectable(spdlog::level::to_string_view(static_cast<spdlog::level::level_enum>(n)).begin(), is_selected))
1827 {
1828 logLevelFilterSelected = n;
1829 }
1830
1831 // Set the initial focus when opening the combo (scrolling + keyboard navigation focus)
1832 if (is_selected)
1833 {
1834 ImGui::SetItemDefaultFocus();
1835 }
1836 }
1837 ImGui::EndCombo();
1838 }
1839 ImGui::SameLine();
1840 textFilter.Draw("Filter", -100.0F);
1841
1842 ImGui::Separator();
1843 ImGui::BeginChild("scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
1844 {
1845 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
1846 ImGui::PushFont(MonoFont());
1847
1848 auto logMessages = Logger::GetRingBufferSink()->last_formatted();
1849
1850 for (auto& logLine : logMessages)
1851 {
1852 for (int n = logLevelFilterSelected; n < spdlog::level::n_levels; n++)
1853 {
1854 if (logLine.find(fmt::format("] [{}]", spdlog::level::to_short_c_str(static_cast<spdlog::level::level_enum>(n)))) != std::string::npos)
1855 {
1856 if (!textFilter.IsActive() || textFilter.PassFilter(logLine.c_str()))
1857 {
1858 // str::replace(logLine, "[T]", "\033[30m[T]\033[0m");
1859 str::replace(logLine, "[D]", "\033[36m[D]\033[0m");
1860 str::replace(logLine, "[I]", "\033[32m[I]\033[0m");
1861 str::replace(logLine, "[W]", "\033[33m[W]\033[0m");
1862 str::replace(logLine, "[E]", "\033[31m[E]\033[0m");
1863 ImGui::TextAnsiUnformatted(logLine.c_str());
1864 // ImGui::TextAnsiUnformatted("\033[31;1;4mHello\033[0mtt");
1865 }
1866 break;
1867 }
1868 }
1869 }
1870 ImGui::PopFont();
1871 ImGui::PopStyleVar();
1872
1873 if (scrollToBottom)
1874 {
1875 ImGui::SetScrollHereY(1.0F);
1876 scrollToBottom--;
1877 }
1878 else if (autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
1879 {
1880 ImGui::SetScrollHereY(1.0F);
1881 }
1882 }
1883 ImGui::EndChild();
1884
1885 ImGui::EndTabItem();
1886 }
1887 ImGui::EndTabBar();
1888 }
1889 ImGui::Unindent();
1890 ImGui::PopFont();
1891
1892 ImGui::EndGroup();
1893
1895
1896 // Push the Tooltip on the stack if needed
1897 if (!tooltipText.empty()) { ImGui::SetTooltip("%s", tooltipText.c_str()); }
1898
1899 ImGui::PushFont(WindowFont());
1901 ImGui::PopFont();
1902
1903 std::string title = (flow::HasUnsavedChanges() ? "● " : "")
1904 + (flow::GetCurrentFilename().empty() ? "" : flow::GetCurrentFilename() + " - ")
1905 + "INSTINCT";
1906 SetTitle(title.c_str());
1907}
1908
1910{
1911 return defaultFontSize.at(isUsingBigDefaultFont() ? 1 : 0) / defaultFontSize[0];
1912}
1913
1915{
1916 return windowFontSize.at(isUsingBigWindowFont() ? 1 : 0) / windowFontSize[0];
1917}
1918
1920{
1921 return panelFontSize.at(isUsingBigPanelFont() ? 1 : 0) / panelFontSize[0];
1922}
1923
1925{
1926 return monoFontSize.at(isUsingBigMonoFont() ? 1 : 0) / monoFontSize[0];
1927}
1928
1930{
1931 return headerFontSize.at(isUsingBigHeaderFont() ? 1 : 0) / headerFontSize[0];
1932}
Handles compiling resources into the binary.
Config management for the Project.
Handles Flow animations.
Flow Executor Thread.
Save/Load the Nodes.
nlohmann::json json
json namespace
@ Clear
Clear the flow.
@ QuitUnsaved
Quit the program without saving.
@ Quit
Quit the program.
@ Load
Load a flow.
@ None
None.
@ SaveAs
Save the flow as filename.
Global windows.
Text Help Marker (?) with Tooltip.
Defines how to save certain datatypes to json.
Left Pane where Nodes and Selection is shown.
#define LOG_DEBUG
Debug information. Should not be called on functions which receive observations (spamming)
Definition Logger.hpp:67
#define LOG_ERROR
Error occurred, which stops part of the program to work, but not everything.
Definition Logger.hpp:73
#define LOG_TRACE
Detailled info to trace the execution of the program. Should not be called on functions which receive...
Definition Logger.hpp:65
Main Menu Bar.
Manages all Nodes.
Utility class which specifies available nodes.
Node Class.
Pin class.
Defines all available shortcuts.
Spinner to show that something is done.
Screen Divider.
Utility functions for working with std::strings.
Text which can be colored by Ansi codes.
Touch Event Tracker.
static const std::shared_ptr< spdlog::sinks::ringbuffer_sink_mt > & GetRingBufferSink()
Returns the ring buffer sink.
Definition Logger.cpp:181
Input pins of nodes.
Definition Pin.hpp:491
bool isPinLinked() const
Checks if the pin is linked.
Definition Pin.cpp:355
bool canCreateLink(const OutputPin &other) const
Checks if this pin can connect to the provided pin.
Definition Pin.cpp:350
Abstract parent class for all nodes.
Definition Node.hpp:92
@ DoInitialize
Node should be initialized.
Definition Node.hpp:179
@ Initializing
Node is currently initializing.
Definition Node.hpp:180
@ DoDeinitialize
Node should be deinitialized.
Definition Node.hpp:182
@ Deinitializing
Node is currently deinitializing.
Definition Node.hpp:183
@ Deinitialized
Node is deinitialized (red)
Definition Node.hpp:178
std::vector< OutputPin > outputPins
List of output pins.
Definition Node.hpp:399
Kind kind
Kind of the Node.
Definition Node.hpp:393
std::vector< InputPin > inputPins
List of input pins.
Definition Node.hpp:397
@ POST_PROCESSING
Node running in post-processing mode.
Definition Node.hpp:192
bool _configWindowFocus
Flag if the config window should be focused.
Definition Node.hpp:445
bool _showConfig
Flag if the config window is shown.
Definition Node.hpp:435
std::string name
Name of the Node.
Definition Node.hpp:395
static std::string toString(State state)
Converts the state into a printable text.
Definition Node.cpp:263
ax::NodeEditor::NodeId id
Unique Id of the Node.
Definition Node.hpp:391
virtual std::string type() const =0
String representation of the Class Type.
Output pins of nodes.
Definition Pin.hpp:338
bool canCreateLink(const InputPin &other) const
Checks if this pin can connect to the provided pin.
Definition Pin.cpp:239
Pins in the GUI for information exchange.
Definition Pin.hpp:43
Node * parentNode
Reference to the parent node.
Definition Pin.hpp:307
std::string name
Name of the Pin.
Definition Pin.hpp:299
ax::NodeEditor::PinId id
Unique Id of the Pin.
Definition Pin.hpp:297
std::vector< std::string > dataIdentifier
One or multiple Data Identifiers (Unique name which is used for data flows)
Definition Pin.hpp:305
Type type
Type of the Pin.
Definition Pin.hpp:301
static bool dataIdentifierHaveCommon(const std::vector< std::string > &a, const std::vector< std::string > &b)
Checks if the first list of data identifiers has a common entry with the second.
Definition Pin.cpp:59
Kind kind
Kind of the Pin (Input/Output)
Definition Pin.hpp:303
static ImPlotStyle imPlotReferenceStyle
Default style of the ImPlot library to compare changes against.
static float bottomViewHeight
Height of the log viewer.
static float windowFontRatio()
Ratio to multiply for GUI window elements.
static std::array< ImTextureID, 2 > m_InstinctLogo
Pointer to the texture for the instinct logo.
static float leftPaneWidth
Width of the left pane.
static constexpr float SPLITTER_THICKNESS
Thickness of the splitter between left and right pane.
GlobalActions globalAction
Global action to execute.
bool OnQuitRequest() override
Called when the user request the application to close.
static void ShowRenameNodeRequest(Node *&renameNode)
Shows a PopupModal where the user can rename the node.
static std::vector< ImVec4 > m_colors
Color settings.
static float defaultFontRatio()
Ratio to multiply for default GUI elements.
static bool hideLeftPane
Hide left pane.
static float monoFontRatio()
Ratio to multiply for log output GUI elements.
static ImTextureID m_RoseFigure
Pointer to the texture for the rose figure (ImuSimulator node)
static std::array< ImTextureID, 2 > m_InsLogo
Pointer to the texture for the INS logo.
void OnStart() override
Called when the application is started.
void ShowSaveAsRequested()
Shows a PopupModel where the user can select a path to save his flow to.
static bool _showQueueSizeOnPins
Shows the queue size on the pins (every frame the queue mutex will be locked)
void ShowLoadRequested()
Shows a PopupModel loading a new flow.
void ShowQuitRequested()
Shows a PopupModal asking the user if he wants to quit with unsaved changes.
void OnStop() override
Called when the application is stopped.
ImTextureID m_HeaderBackground
Pointer to the texture for the node headers.
static void ShowRenamePinRequest(Pin *&renamePin)
Shows a PopupModal where the user can rename the pin.
void ShowClearNodesRequested()
Shows a PopupModel to clear the current flow.
static std::vector< const char * > m_colorsNames
Color names.
void OnFrame(float deltaTime) override
Called on every frame.
static float rightPaneWidth
Width of the right pane.
static constexpr float BOTTOM_VIEW_COLLAPSED_MIN_HEIGHT
Minimal height of the bottom view if it is collapsed.
static float panelFontRatio()
Ratio to multiply for GUI editor elements.
static constexpr float BOTTOM_VIEW_UNCOLLAPSED_MIN_HEIGHT
Minimal height of the bottom view if it is not collapsed.
static ImTextureID m_SaveButtonImage
Pointer to the texture for the save button.
int frameCountNavigate
Frame counter to block the navigate to content function till nodes are correctly loaded.
BottomViewTabItem bottomViewSelectedTab
Selected Tab item in the bottom view.
static float headerFontRatio()
Ratio to multiply for node header elements.
@ COLOR_GROUP_OUTER_BORDER
Color of the group outer border.
@ COLOR_GROUP_HEADER_BG
Color of the group header background.
@ COLOR_GROUP_HEADER_TEXT
Color of the group header text.
static float menuBarHeight
Height of the menu bar on top.
static void EndOutput()
Ends building the output pin.
void Output(ax::NodeEditor::PinId id)
Begins building an output pin.
void Begin(ax::NodeEditor::NodeId id)
Begins building a node.
void Middle()
Begins building of the middle of the node.
void Input(ax::NodeEditor::PinId id)
Begins building an input pin.
void Header(const ImVec4 &color=ImVec4(1, 1, 1, 1))
Begins building the header.
static void EndInput()
Ends building the input pin.
void TextAnsiUnformatted(const char *text, const char *text_end=nullptr)
Displays an unformatted ansi text.
void LoadGlobalSettings()
Loads the global settings.
const T & Get(const std::string &key, const T &&defaultValue)
Retrieves the value of a corresponding key from the configuration, if one exists.
void SaveGlobalSettings()
Saves the global settings.
void ProcessQueue()
Triggers the queued flow animations.
bool isRunning() noexcept
Checks if the thread is running.
void stop()
Stops the Thread.
bool DeleteNode(ax::NodeEditor::NodeId nodeId)
Delete the node provided by id.
InputPin * FindInputPin(ax::NodeEditor::PinId id)
Finds the Pin for the PinId.
OutputPin * FindOutputPin(ax::NodeEditor::PinId id)
Finds the Pin for the PinId.
void DeleteAllNodes()
Delete all nodes.
const std::vector< Node * > & m_Nodes()
List of all registered Nodes.
void AddNode(Node *node)
Add the provided node object to the list of nodes.
Node * FindNode(ax::NodeEditor::NodeId id)
Finds the Node for the NodeId.
bool NodeDataTypeAnyIsChildOf(const std::vector< std::string > &childTypes, const std::vector< std::string > &parentTypes)
Checks if any of the provided child types is a child of any of the provided parent types.
const std::map< std::string, std::vector< NodeInfo > > & RegisteredNodes()
Reference to List of all registered Nodes.
void SetCurrentFilename(const std::string &newFilename)
Set the current filename of the open flow.
bool LoadFlow(const std::string &filepath)
Loads the flow from the specified file.
std::filesystem::path GetConfigPath()
Get the path where config files are searched.
void SaveFlowAs(const std::string &filepath)
Saves the current flow into the specified file.
void DiscardChanges()
Discards the unsaved changes flag. Does not really discard the changes.
std::string GetCurrentFilename()
Get the current filename of the open flow.
void ApplyChanges()
Signals that there have been changes to the flow.
std::filesystem::path GetFlowPath()
Get the path where flow files are searched.
bool HasUnsavedChanges()
Checks if the currently open flow has unsaved changes.
void ShowMainMenuBar(GlobalActions &globalAction)
Shows the main menu bar and moves down the cursor.
bool ShowLeftPane(float paneWidth)
Shows the left overview pane.
Definition LeftPane.cpp:27
void Spinner(const char *label, const ImU32 &color, float radius, float thickness=1.0F)
Shows a Spinner to signal that work is done.
Definition Spinner.cpp:15
bool Splitter(const char *str_id, bool split_vertically, float thickness, float *size1, float *size2, float min_size1, float min_size2, float splitter_long_axis_size=-1.0F)
Vertical or horizontal Screen Divider.
Definition Splitter.cpp:16
void HelpMarker(const char *desc, const char *symbol="(?)")
Text Help Marker, e.g. '(?)', with Tooltip.
void renderGlobalWindows(std::vector< ImVec4 > &colors, const std::vector< const char * > &colorNames)
Called every frame to render global windows.
Definition Global.cpp:32
void checkShortcuts(GlobalActions &globalAction)
Checks if a shortcut was pressed.
Definition Shortcuts.cpp:23
void TouchNode(ax::NodeEditor::NodeId id)
Trigger a touch event on the specified node.
void UpdateTouch(float deltaTime)
Updates the touch events for all nodes.
static bool replace(std::string &str, const std::string &from, const std::string &to, CaseSensitivity cs=RespectCase)
Replaces the first occurrence of a search pattern with another sequence.
@ Blueprint
Node with header.
Definition Node.hpp:100
@ GroupBox
Group box which can group other nodes and drag them together.
Definition Node.hpp:102
@ Simple
Node without header, which displays its name in the center of the content.
Definition Node.hpp:101
@ Input
Input Pin.
Definition Pin.hpp:171
@ Output
Output Pin.
Definition Pin.hpp:170
@ Delegate
Reference to the Node object.
Definition Pin.hpp:59
@ None
Not initialized.
Definition Pin.hpp:51
@ Matrix
Matrix Object.
Definition Pin.hpp:58
@ Flow
NodeData Trigger.
Definition Pin.hpp:52
@ Object
Generic Object.
Definition Pin.hpp:57