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