Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
BlueprintEditorGUI.cpp
Go to the documentation of this file.
1/*
2 * Olympe Blueprint Editor GUI - Frontend Implementation
3 * All data operations delegate to BlueprintEditor backend
4 */
5
7#include "BlueprintEditor.h"
9#include "HistoryPanel.h"
12#include "DebugPanel.h"
13#include "ProfilerPanel.h"
14#include "BTtoVSMigrator.h"
15#include "TabManager.h"
16#include "../TaskSystem/TaskGraphLoader.h"
17#include "../Core/FontManager.h"
18#include "../third_party/imgui/imgui.h"
19#include "../third_party/imnodes/imnodes.h"
20#include "../third_party/nlohmann/json.hpp"
21#include "../system/system_utils.h"
22#include <fstream>
23#include <sstream>
24#include <iostream>
25
26using namespace Olympe::Blueprint;
27
28namespace Olympe
29{
31 : m_SelectedComponentIndex(-1)
32 , m_NextNodeId(0)
33 , m_ShowDemoWindow(false)
34 , m_ShowAddComponentDialog(false)
35 , m_ShowAboutDialog(false)
36 , m_SelectedComponentType(0)
37 , m_ShowAssetBrowser(true) // Main panel 1
38 , m_ShowAssetInfo(false) // Deprecated - merged into Inspector
39 , m_ShowInspector(true) // Main panel 3
40 , m_ShowNodeGraph(true) // Main panel 2
41 , m_ShowEntities(false) // Deprecated - merged into Asset Browser tab
42 , m_ShowEntityProperties(false) // Deprecated - merged into Inspector
43 , m_ShowComponentGraph(false) // Deprecated
44 , m_ShowPropertyPanel(false) // Deprecated - merged into Inspector
45 , m_ShowTemplateBrowser(false)
46 , m_ShowHistory(false)
47 , m_ShowDebugger(false)
48 , m_ShowProfiler(false)
49 , m_ShowVSEditor(false)
50 , m_ShowPreferences(false)
51 , m_ShowShortcuts(false)
52 , m_TemplateBrowserPanel(nullptr)
53 , m_HistoryPanel(nullptr)
54 , m_VSEditorPanel(nullptr)
55 , m_DebugPanel(nullptr)
56 , m_ProfilerPanel(nullptr)
57 , m_AssetBrowserWidth(400.0f)
58 , m_InspectorWidth(400.0f)
59 , m_MinPanelWidth(200.0f)
60 , m_SplitterSize(8.0f)
61 , m_LeftPanelSplitHeight(0.0f) // 0 = use default 40 % on first frame
62 , m_InspectorPanelHeight(0.0f) // 0 = use default 40 % on first frame
63 , m_VerificationLogsPanelHeight(0.0f) // 0 = use default 20 % on first frame
64 {
66 m_FilepathBuffer[0] = '\0';
67 }
68
70 {
72 {
73 delete m_ProfilerPanel;
74 m_ProfilerPanel = nullptr;
75 }
76
77 if (m_DebugPanel)
78 {
79 delete m_DebugPanel;
80 m_DebugPanel = nullptr;
81 }
82
84 {
85 delete m_VSEditorPanel;
86 m_VSEditorPanel = nullptr;
87 }
88
90 {
91 delete m_HistoryPanel;
92 m_HistoryPanel = nullptr;
93 }
94
96 {
98 m_TemplateBrowserPanel = nullptr;
99 }
100 }
101
103 {
104 // Initialize ImNodes
105 ImNodes::CreateContext();
106 ImNodes::StyleColorsDark();
107
108 // Configure ImNodes style
109 ImNodesStyle& style = ImNodes::GetStyle();
110 style.Flags |= ImNodesStyleFlags_GridLines;
111
112 // Initialize Font Manager and load Font Awesome
114 FontManager::Get().LoadFontAwesome("Assets/Fonts/fa-solid-900.otf", 16.0f);
115
116 // Initialize Asset Browser with Blueprints directory
117 m_AssetBrowser.Initialize("Blueprints");
118
119 // Set up callback for asset selection
120 m_AssetBrowser.SetAssetOpenCallback([this](const std::string& path) {
121 LoadBlueprint(path);
122 });
123
124 // Initialize new panels
128
129 // Initialize template browser panel
132
133 // Initialize history panel
136
137 // Phase 5: Initialize VS editor, debugger, and profiler panels
140
141 m_DebugPanel = new DebugPanel();
143
146
147 // Load layout configuration from backend
149 const auto& config = backend.GetConfig();
150 if (config.contains("layout"))
151 {
152 const auto& layout = config["layout"];
153 if (layout.contains("asset_browser_width"))
154 m_AssetBrowserWidth = layout["asset_browser_width"].get<float>();
155 if (layout.contains("inspector_width"))
156 m_InspectorWidth = layout["inspector_width"].get<float>();
157 if (layout.contains("min_panel_width"))
158 m_MinPanelWidth = layout["min_panel_width"].get<float>();
159 if (layout.contains("splitter_size"))
160 m_SplitterSize = layout["splitter_size"].get<float>();
161 }
162 }
163
165 {
166 // Save layout configuration to backend
168 auto& config = backend.GetConfigMutable();
169 if (!config.contains("layout"))
170 config["layout"] = json::object();
171
172 config["layout"]["asset_browser_width"] = m_AssetBrowserWidth;
173 config["layout"]["inspector_width"] = m_InspectorWidth;
174 config["layout"]["min_panel_width"] = m_MinPanelWidth;
175 config["layout"]["splitter_size"] = m_SplitterSize;
176
177 // Phase 5: Shutdown VS editor, debugger, and profiler panels
178 if (m_ProfilerPanel)
179 {
181 delete m_ProfilerPanel;
182 m_ProfilerPanel = nullptr;
183 }
184
185 if (m_DebugPanel)
186 {
188 delete m_DebugPanel;
189 m_DebugPanel = nullptr;
190 }
191
192 if (m_VSEditorPanel)
193 {
195 delete m_VSEditorPanel;
196 m_VSEditorPanel = nullptr;
197 }
198
199 // Shutdown panels
200 if (m_HistoryPanel)
201 {
203 delete m_HistoryPanel;
204 m_HistoryPanel = nullptr;
205 }
206
208 {
211 m_TemplateBrowserPanel = nullptr;
212 }
213
217
218 ImNodes::DestroyContext();
219 }
220
222 {
223 // Only render if the backend is active
224 if (!BlueprintEditor::Get().IsActive())
225 {
226 return;
227 }
228
229 // Handle keyboard shortcuts
231
232 // Get backend reference for data access
234 const auto& blueprint = backend.GetCurrentBlueprint();
235
236 // Render menu bar in main viewport
237 if (ImGui::BeginMainMenuBar())
238 {
239 // ===== D) FILE MENU =====
240 if (ImGui::BeginMenu(ICON_FA_FILE " File"))
241 {
242 // Tab-based graph creation
243 if (ImGui::MenuItem(ICON_FA_CODE " New Visual Script", "Ctrl+N"))
244 TabManager::Get().CreateNewTab("VisualScript");
245
246 if (ImGui::MenuItem(ICON_FA_BRAIN " New Behavior Tree", "Ctrl+Shift+N"))
247 TabManager::Get().CreateNewTab("BehaviorTree");
248
249 if (ImGui::MenuItem(ICON_FA_CUBES " New Entity Prefab", "Ctrl+Alt+N"))
250 TabManager::Get().CreateNewTab("EntityPrefab");
251
252 if (ImGui::MenuItem(ICON_FA_FOLDER_OPEN " Open Blueprint...", "Ctrl+O"))
253 {
254 // Legacy fallback when no file dialog exists
255 LoadBlueprint("Blueprints/AI/guard_patrol.json");
256 }
257
258 ImGui::Separator();
259
261 if (ImGui::MenuItem(ICON_FA_FLOPPY_DISK " Save", "Ctrl+S", false, hasActiveTab))
263
264 if (ImGui::MenuItem(ICON_FA_FLOPPY_DISK " Save As...", "Ctrl+Shift+S", false, hasActiveTab))
266
267 ImGui::Separator();
268
269 if (ImGui::MenuItem(ICON_FA_XMARK " Close Tab", "Ctrl+W", false, hasActiveTab))
270 TabManager::Get().CloseTab(TabManager::Get().GetActiveTabID());
271
272 if (ImGui::MenuItem(ICON_FA_XMARK " Close All Tabs", "Ctrl+Shift+W", false, hasActiveTab))
274
275 ImGui::Separator();
276
277 // Legacy submenu (collapsed by default)
278 if (ImGui::BeginMenu(ICON_FA_CLOCK " Legacy Blueprints"))
279 {
280 if (ImGui::MenuItem(ICON_FA_FILE_CODE " New Entity Blueprint (Legacy)"))
281 NewBlueprint();
282
283 ImGui::Separator();
284 ImGui::TextDisabled("Old BehaviorTree v2 system");
285 ImGui::TextDisabled("Use 'New Visual Script' instead");
286
287 ImGui::EndMenu();
288 }
289
290 ImGui::Separator();
291
292 // Phase 5: Template menu items
293 if (ImGui::MenuItem(ICON_FA_BOOKMARK " Save as Template...", "Ctrl+Shift+T", false, backend.HasBlueprint()))
294 {
297 }
298
299 if (ImGui::MenuItem(ICON_FA_BOOK " Template Browser", nullptr, &m_ShowTemplateBrowser))
300 {
301 // Toggle template browser visibility
302 }
303
304 ImGui::Separator();
305
306 if (ImGui::MenuItem(ICON_FA_ARROW_ROTATE_RIGHT " Reload Assets"))
307 backend.RefreshAssets();
308
309 ImGui::Separator();
310
311 if (ImGui::MenuItem(ICON_FA_ARROW_RIGHT " Exit Editor", "F2"))
312 backend.SetActive(false);
313
314 ImGui::EndMenu();
315 }
316
317 // ===== D) EDIT MENU =====
318 if (ImGui::BeginMenu(ICON_FA_PEN_TO_SQUARE " Edit"))
319 {
320 // Phase 6: Undo/Redo
321 bool canUndo = backend.CanUndo();
322 bool canRedo = backend.CanRedo();
323
324 std::string undoLabel = ICON_FA_ROTATE_LEFT " Undo";
325 if (canUndo)
326 {
327 undoLabel += ": " + backend.GetLastCommandDescription();
328 }
329
330 if (ImGui::MenuItem(undoLabel.c_str(), "Ctrl+Z", false, canUndo))
331 {
332 backend.Undo();
333 }
334
335 std::string redoLabel = ICON_FA_ROTATE_RIGHT " Redo";
336 if (canRedo)
337 {
338 redoLabel += ": " + backend.GetNextRedoDescription();
339 }
340
341 if (ImGui::MenuItem(redoLabel.c_str(), "Ctrl+Y", false, canRedo))
342 {
343 backend.Redo();
344 }
345
346 ImGui::Separator();
347
348 // Component operations
349 if (ImGui::MenuItem(ICON_FA_PLUS " Add Component", "Insert", false, backend.HasBlueprint()))
351
352 if (ImGui::MenuItem(ICON_FA_TRASH_CAN " Remove Component", "Delete", false,
354 {
356 }
357
358 ImGui::Separator();
359
360 if (ImGui::MenuItem(ICON_FA_GEAR " Preferences..."))
361 {
362 m_ShowPreferences = true;
363 }
364
365 ImGui::EndMenu();
366 }
367
368 // ===== TOOLS MENU =====
369 if (ImGui::BeginMenu(ICON_FA_WRENCH " Tools"))
370 {
371 if (ImGui::MenuItem(ICON_FA_ARROW_RIGHT " Migrate Blueprints v1 -> v2"))
372 {
373 backend.SetShowMigrationDialog(true);
374 }
375
376 ImGui::Separator();
377
378 if (ImGui::MenuItem(ICON_FA_CHECK " Validate All Blueprints"))
379 {
380 std::cout << "Validating all blueprints..." << std::endl;
381 }
382
383 ImGui::EndMenu();
384 }
385
386 // ===== D) VIEW MENU =====
387 if (ImGui::BeginMenu(ICON_FA_EYE " View"))
388 {
389 ImGui::Text("Main Panels:");
390 ImGui::Separator();
391
392 // Three main panels only
393 ImGui::MenuItem(ICON_FA_FOLDER " Asset Browser", nullptr, &m_ShowAssetBrowser);
394 ImGui::MenuItem(ICON_FA_DIAGRAM_PROJECT " Node Graph Editor", nullptr, &m_ShowNodeGraph);
395 ImGui::MenuItem(ICON_FA_LIST " Inspector", nullptr, &m_ShowInspector);
396
397 ImGui::Separator();
398
399 ImGui::Text("Additional:");
400 ImGui::Separator();
401 ImGui::MenuItem(ICON_FA_BOOK " Template Browser", nullptr, &m_ShowTemplateBrowser); // Phase 5
402 ImGui::MenuItem(ICON_FA_CLOCK " History", nullptr, &m_ShowHistory); // Phase 6
403 ImGui::MenuItem(ICON_FA_CODE " VS Graph Editor", nullptr, &m_ShowVSEditor); // Phase 5 (new)
404 ImGui::MenuItem(ICON_FA_BUG " Debugger", nullptr, &m_ShowDebugger); // Phase 5 (new)
405 ImGui::MenuItem(ICON_FA_GAUGE " Profiler", nullptr, &m_ShowProfiler); // Phase 5 (new)
406
407 ImGui::Separator();
408
409 ImGui::Text("Debug:");
410 ImGui::Separator();
411 ImGui::MenuItem(ICON_FA_LIGHTBULB " ImGui Demo", nullptr, &m_ShowDemoWindow);
412
413 ImGui::Separator();
414
415 if (ImGui::MenuItem(ICON_FA_WINDOW_RESTORE " Reset Layout"))
416 {
417 // Reset main panels to visible
418 m_ShowAssetBrowser = true;
419 m_ShowNodeGraph = true;
420 m_ShowInspector = true;
421 // Keep optional panels in their current state
422 }
423
424 ImGui::EndMenu();
425 }
426
427 // ===== D) HELP MENU =====
428 if (ImGui::BeginMenu(ICON_FA_CIRCLE_QUESTION " Help"))
429 {
430 if (ImGui::MenuItem(ICON_FA_BOOK " Documentation"))
431 {
432 // TODO: Open documentation URL or local help
433 std::cout << "Opening documentation..." << std::endl;
434 }
435
436 if (ImGui::MenuItem(ICON_FA_SLIDERS " Keyboard Shortcuts"))
437 {
438 m_ShowShortcuts = true;
439 }
440
441 ImGui::Separator();
442
443 if (ImGui::MenuItem(ICON_FA_CIRCLE_INFO " About Olympe Engine"))
444 m_ShowAboutDialog = true;
445
446 ImGui::EndMenu();
447 }
448
449 ImGui::EndMainMenuBar();
450 }
451
452 // Render fixed docked layout (like BehaviorTreeDebugWindow)
454
455 // Status bar at bottom - DISABLED (not needed in standalone editor)
456 // RenderStatusBar();
457
458 // Dialogs
461
464
465 if (m_ShowShortcuts)
467
468 // Migration dialog
469 if (backend.ShowMigrationDialog())
471
472 // About dialog
474 {
475 ImGui::OpenPopup("About");
476 if (ImGui::BeginPopupModal("About", &m_ShowAboutDialog, ImGuiWindowFlags_AlwaysAutoResize))
477 {
478 ImGui::Text("Olympe Blueprint Editor");
479 ImGui::Separator();
480 ImGui::Text("Visual node-based editor for entity blueprints");
481 ImGui::Text("Version: 2.0");
482 ImGui::Text("Status: Interactive panels, entity synchronization, full menus");
483 ImGui::Separator();
484 ImGui::Text("Libraries:");
485 ImGui::BulletText("ImGui for UI");
486 ImGui::BulletText("ImNodes for node editing");
487 ImGui::BulletText("SDL3 for window/rendering");
488 ImGui::Separator();
489 ImGui::Text("Features:");
490 ImGui::BulletText("Interactive, dockable panels");
491 ImGui::BulletText("Runtime entity tracking");
492 ImGui::BulletText("Synchronized panel selection");
493 ImGui::BulletText("Full menu system");
494 if (ImGui::Button("Close", ImVec2(120, 0)))
495 m_ShowAboutDialog = false;
496 ImGui::EndPopup();
497 }
498 }
499
500 // Demo window for testing
502 ImGui::ShowDemoWindow(&m_ShowDemoWindow);
503 }
504
506 {
507 // Create a fullscreen window for the fixed layout
508 ImGui::SetNextWindowPos(ImVec2(0, ImGui::GetFrameHeight())); // Below menu bar
509 ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x,
510 ImGui::GetIO().DisplaySize.y - ImGui::GetFrameHeight()));
511
519
520 if (!ImGui::Begin("##FixedLayoutWindow", nullptr, windowFlags))
521 {
522 ImGui::End();
523 return;
524 }
525
526 float windowWidth = ImGui::GetContentRegionAvail().x;
527 float windowHeight = ImGui::GetContentRegionAvail().y;
528
529 // Ensure minimum left panel width
532
536
537 // =====================================================================
538 // LEFT COLUMN: Asset Browser (top 25%) + Inspector (bottom 10%) + Verification Logs (70%)
539 // =====================================================================
540 ImGui::BeginChild("LeftColumn", ImVec2(m_AssetBrowserWidth, windowHeight), false);
541 {
542 float leftHeight = ImGui::GetContentRegionAvail().y;
543
544 // Default heights on first frame: 25% Asset Browser, 10% Inspector, 70% Verification Logs
545 if (m_LeftPanelSplitHeight <= 0.0f)
547 if (m_InspectorPanelHeight <= 0.0f)
549
550 const float kMinSectionHeight = 60.0f;
551 const float kSplitterHeight = 4.0f;
552
553 // Clamp asset-browser height
558
559 // Clamp inspector height
564
568
569 // ---- Part 1: Asset Browser (top) ----
570 ImGui::BeginChild("AssetBrowserSection",
574 ImGui::EndChild();
575
576 // ---- Splitter 1 (between Asset Browser and Inspector) ----
577 ImGui::Button("##LeftHSplitter1", ImVec2(-1.0f, kSplitterHeight));
578 if (ImGui::IsItemActive())
579 m_LeftPanelSplitHeight += ImGui::GetIO().MouseDelta.y;
580 if (ImGui::IsItemHovered())
581 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
582
583 // ---- Part 2: Inspector (middle) ----
584 ImGui::BeginChild("InspectorSection", ImVec2(0, m_InspectorPanelHeight), true);
585 if (m_ShowInspector)
587 ImGui::EndChild();
588
589 // ---- Splitter 2 (between Inspector and Verification Logs) ----
590 ImGui::Button("##LeftHSplitter2", ImVec2(-1.0f, kSplitterHeight));
591 if (ImGui::IsItemActive())
592 m_InspectorPanelHeight += ImGui::GetIO().MouseDelta.y;
593 if (ImGui::IsItemHovered())
594 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS);
595
596 // ---- Part 3: Verification Logs (bottom) ----
597 ImGui::BeginChild("VerificationLogsSection", ImVec2(0, logsHeight), true);
598 {
599 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "Verification Output");
600 ImGui::Separator();
601
602 // Get the active tab renderer and cast to VisualScriptRenderer
604 EditorTab* activeTab = tabMgr.GetActiveTab();
605
606 if (activeTab && activeTab->graphType == "VisualScript" && activeTab->renderer)
607 {
608 // Safe cast to VisualScriptRenderer (which wraps VisualScriptEditorPanel)
610 dynamic_cast<VisualScriptRenderer*>(activeTab->renderer);
611
612 if (vsRenderer)
613 {
614 // Access the wrapped panel via GetPanel() and render the verification logs
616 }
617 else
618 {
619 ImGui::TextDisabled("(Unable to retrieve verification logs - renderer mismatch)");
620 }
621 }
622 else
623 {
624 ImGui::TextDisabled("(Open a VisualScript graph to see verification logs)");
625 }
626 }
627 ImGui::EndChild();
628 }
629 ImGui::EndChild();
630
631 // Vertical splitter between left column and center
632 ImGui::SameLine();
633 ImGui::Button("##LeftSplitter", ImVec2(m_SplitterSize, windowHeight));
634 if (ImGui::IsItemActive())
635 {
636 m_AssetBrowserWidth += ImGui::GetIO().MouseDelta.x;
642 }
643 if (ImGui::IsItemHovered())
644 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
645
646 // =====================================================================
647 // CENTER COLUMN: Tab bar + Active graph canvas
648 // =====================================================================
649 ImGui::SameLine();
650 ImGui::BeginChild("CenterColumn", ImVec2(0, windowHeight), false);
651 {
652 // Tab bar (handles unsaved dialogs and tab switching)
654
655 ImGui::Separator();
656
657 // Active graph canvas
659 }
660 ImGui::EndChild();
661
662 ImGui::End();
663
664 // Render floating/optional panels separately (they can be closed)
667
670
673
676 }
677
678 // Menu bar is now integrated in the main Render() function
679
683
685 {
686 // Get backend reference
688 const auto& blueprint = backend.GetCurrentBlueprint();
689
690 ImGui::Begin("Entity Properties");
691
692 if (backend.HasBlueprint())
693 {
694 ImGui::Text("Blueprint: %s", blueprint.name.c_str());
695 if (backend.HasUnsavedChanges())
696 {
697 ImGui::SameLine();
698 ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.0f, 1.0f), "*");
699 }
700
701 ImGui::Separator();
702
703 // Description
704 ImGui::Text("Description:");
705 ImGui::TextWrapped("%s", blueprint.description.c_str());
706
707 ImGui::Separator();
708
709 // Component list
710 ImGui::Text("Components (%zu)", blueprint.components.size());
711
712 for (size_t i = 0; i < blueprint.components.size(); ++i)
713 {
714 const auto& comp = blueprint.components[i];
715 bool selected = (m_SelectedComponentIndex == (int)i);
716
717 if (ImGui::Selectable(comp.type.c_str(), selected))
718 {
720 }
721
722 // Right-click context menu
723 if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(1))
724 {
725 ImGui::OpenPopup("component_context");
727 }
728 }
729
730 // Context menu
731 if (ImGui::BeginPopup("component_context"))
732 {
733 if (ImGui::MenuItem("Remove"))
734 {
736 }
737 ImGui::EndPopup();
738 }
739
740 ImGui::Separator();
741
742 if (ImGui::Button("Add Component", ImVec2(-1, 0)))
743 {
745 }
746 }
747 else
748 {
749 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "No blueprint loaded");
750 ImGui::Text("");
751 ImGui::Text("Use File > New or File > Open");
752 ImGui::Text("to get started");
753 }
754
755 ImGui::End();
756 }
757
759 {
760 // Get backend reference
762 const auto& blueprint = backend.GetCurrentBlueprint();
763
764 ImGui::Begin("Component Graph");
765
766 if (backend.HasBlueprint())
767 {
768 ImNodes::BeginNodeEditor();
769
770 // Render each component as a node
771 for (size_t i = 0; i < blueprint.components.size(); ++i)
772 {
773 const auto& comp = blueprint.components[i];
774 int node_id = (int)i;
775
776 ImNodes::BeginNode(node_id);
777
778 ImNodes::BeginNodeTitleBar();
779 ImGui::TextUnformatted(comp.type.c_str());
780 ImNodes::EndNodeTitleBar();
781
782 // Node content - show key properties
783 ImGui::PushItemWidth(120.0f);
784
785 // Display type only (minimal JSON doesn't support iteration)
786 ImGui::TextDisabled("Type: %s", comp.type.c_str());
787 ImGui::TextDisabled("Click to view properties");
788
789 ImGui::PopItemWidth();
790
791 ImNodes::EndNode();
792
793 // Store or initialize node position
794 if (m_NodePositions.find(node_id) == m_NodePositions.end())
795 {
796 // Initial positioning in a grid
797 float x = 100.0f + (i % 3) * 250.0f;
798 float y = 100.0f + (i / 3) * 150.0f;
800 ImNodes::SetNodeGridSpacePos(node_id, m_NodePositions[node_id]);
801 }
802 }
803
804 ImNodes::EndNodeEditor();
805
806 // Handle node selection
807 int num_selected = ImNodes::NumSelectedNodes();
808 if (num_selected > 0)
809 {
810 int* selected_nodes = new int[num_selected];
811 ImNodes::GetSelectedNodes(selected_nodes);
813 delete[] selected_nodes;
814 }
815 }
816 else
817 {
818 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f), "No blueprint loaded");
819 ImGui::Text("");
820 ImGui::Text("Components will appear as nodes here");
821 }
822
823 ImGui::End();
824 }
825
827 {
828 // Get backend reference
830 const auto& blueprint = backend.GetCurrentBlueprint();
831
832 ImGui::Begin("Properties");
833
834 if (m_SelectedComponentIndex >= 0 &&
835 m_SelectedComponentIndex < (int)blueprint.components.size())
836 {
837 const auto& comp = blueprint.components[m_SelectedComponentIndex];
838
839 ImGui::Text("Component: %s", comp.type.c_str());
840 ImGui::Separator();
841
842 // Display properties as JSON (minimal JSON doesn't support iteration)
843 ImGui::Text("Properties:");
844 std::string props_json = comp.properties.dump(2);
845 ImGui::TextWrapped("%s", props_json.c_str());
846
847 ImGui::Separator();
848 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.0f, 1.0f),
849 "Note: Use console editor (Phase 1) for property editing");
850 ImGui::Text("Phase 2 property editing requires full JSON library");
851 }
852 else
853 {
854 ImGui::TextColored(ImVec4(0.7f, 0.7f, 0.7f, 1.0f),
855 "Select a component to view properties");
856 }
857
858 ImGui::End();
859 }
860
862 {
863 // Get backend reference
865 const auto& blueprint = backend.GetCurrentBlueprint();
866
867 ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0));
868 ImGui::Begin("##StatusBar", nullptr,
871
872 if (backend.HasBlueprint())
873 {
874 ImGui::Text("Blueprint: %s", blueprint.name.c_str());
875 ImGui::SameLine();
876 ImGui::Text(" | Components: %zu", blueprint.components.size());
877
878 if (backend.HasUnsavedChanges())
879 {
880 ImGui::SameLine();
881 ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.0f, 1.0f), "| Modified");
882 }
883
884 const std::string& filepath = backend.GetCurrentFilepath();
885 if (!filepath.empty())
886 {
887 ImGui::SameLine();
888 ImGui::TextDisabled("| %s", filepath.c_str());
889 }
890 }
891 else
892 {
893 ImGui::Text("Ready | No blueprint loaded");
894 }
895
896 ImGui::End();
897 ImGui::PopStyleVar();
898 }
899
901 {
902 ImGui::OpenPopup("Add Component");
903
904 ImVec2 center = ImGui::GetMainViewport()->GetCenter();
905 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
906
907 if (ImGui::BeginPopupModal("Add Component", &m_ShowAddComponentDialog,
909 {
910 ImGui::Text("Select component type:");
911 ImGui::Separator();
912
913 const char* component_types[] = {
914 "Position", "BoundingBox", "VisualSprite", "Movement",
915 "PhysicsBody", "Health", "AIBehavior", "TriggerZone",
916 "Animation", "AudioSource", "Inventory", "PlayerController"
917 };
918
919 ImGui::ListBox("##component_types", &m_SelectedComponentType,
921
922 ImGui::Separator();
923
924 if (ImGui::Button("Add", ImVec2(120, 0)))
925 {
928 }
929
930 ImGui::SameLine();
931
932 if (ImGui::Button("Cancel", ImVec2(120, 0)))
933 {
935 }
936
937 ImGui::EndPopup();
938 }
939 }
940
942 {
943 // Delegate to backend
944 BlueprintEditor::Get().NewBlueprint("NewBlueprint", "A new entity blueprint");
945
946 // Reset UI state
948 m_NodePositions.clear();
949 }
950
952 {
953 // Create a new tab via TabManager (replaces direct panel creation)
954 TabManager::Get().CreateNewTab("VisualScript");
955 SYSTEM_LOG << "[BlueprintEditorGUI] Created new Visual Script graph via TabManager\n";
956 }
957
958 void BlueprintEditorGUI::LoadBlueprint(const std::string& filepath)
959 {
960 // ------------------------------------------------------------------
961 // Route graph files through TabManager; fall back to legacy backend
962 // for entity blueprints and other non-graph files.
963 // ------------------------------------------------------------------
964 std::string tabID = TabManager::Get().OpenFileInTab(filepath);
965 if (!tabID.empty())
966 {
967 // Successfully opened in TabManager
968 return;
969 }
970
971 // Fallback: delegate to legacy backend (entity blueprints, etc.)
972 if (BlueprintEditor::Get().LoadBlueprint(filepath))
973 {
974 // Reset UI state on successful load
976 m_NodePositions.clear();
977 }
978 }
979
981 {
983
984 if (backend.GetCurrentFilepath().empty())
985 {
986 // Default save location if no filepath set
987 const std::string& name = backend.GetCurrentBlueprint().name;
988 std::string filepath = "../Blueprints/" + name + ".json";
989 backend.SaveBlueprintAs(filepath);
990 }
991 else
992 {
993 backend.SaveBlueprint();
994 }
995 }
996
998 {
1000 std::string savePath = backend.GetCurrentFilepath();
1001 if (savePath.empty()) {
1002 savePath = "Blueprints/";
1003 }
1004
1005 // TODO: Implement save as dialog
1006 SYSTEM_LOG << "[BlueprintEditorGUI] SaveBlueprintAs not yet implemented" << std::endl;
1007 }
1008
1009 void BlueprintEditorGUI::AddComponent(const std::string& type)
1010 {
1011 // Get mutable blueprint from backend
1013
1015 newComp.type = type;
1016
1017 // Create default properties based on type
1018 if (type == "Position")
1019 {
1021 }
1022 else if (type == "BoundingBox")
1023 {
1024 newComp = CreateBoundingBoxComponent(0, 0, 32, 32);
1025 }
1026 else if (type == "VisualSprite")
1027 {
1028 newComp = CreateVisualSpriteComponent("Resources/sprite.png", 0, 0, 32, 32);
1029 }
1030 else if (type == "Movement")
1031 {
1032 newComp = CreateMovementComponent(1, 0, 0, 0);
1033 }
1034 else if (type == "PhysicsBody")
1035 {
1036 newComp = CreatePhysicsBodyComponent(1.0f, 100.0f);
1037 }
1038 else if (type == "Health")
1039 {
1040 newComp = CreateHealthComponent(100, 100);
1041 }
1042 else if (type == "AIBehavior")
1043 {
1045 }
1046 else
1047 {
1048 // Generic component
1049 newComp.type = type;
1050 newComp.properties = json::object();
1051 }
1052
1053 // Add to backend blueprint
1054 blueprint.AddComponent(newComp.type, newComp.properties);
1055
1056 // Mark as modified in backend
1058 }
1059
1061 {
1062 // Get mutable blueprint from backend
1064
1065 if (index >= 0 && index < (int)blueprint.components.size())
1066 {
1067 blueprint.components.erase(
1068 blueprint.components.begin() + index
1069 );
1071
1072 // Mark as modified in backend
1074 }
1075 }
1076
1077 // D) Additional dialog implementations
1079 {
1080 ImGui::OpenPopup("Preferences");
1081
1082 ImVec2 center = ImGui::GetMainViewport()->GetCenter();
1083 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1084 ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_Appearing);
1085
1086 if (ImGui::BeginPopupModal("Preferences", &m_ShowPreferences))
1087 {
1088 ImGui::Text("Editor Preferences");
1089 ImGui::Separator();
1090
1091 ImGui::TextWrapped("Preferences coming soon...");
1092 ImGui::Spacing();
1093
1094 ImGui::Text("Planned settings:");
1095 ImGui::BulletText("Auto-save interval");
1096 ImGui::BulletText("Theme selection");
1097 ImGui::BulletText("Grid snap settings");
1098 ImGui::BulletText("Default component properties");
1099
1100 ImGui::Separator();
1101
1102 if (ImGui::Button("Close", ImVec2(120, 0)))
1103 {
1104 m_ShowPreferences = false;
1105 }
1106
1107 ImGui::EndPopup();
1108 }
1109 }
1110
1112 {
1113 ImGui::OpenPopup("Keyboard Shortcuts");
1114
1115 ImVec2 center = ImGui::GetMainViewport()->GetCenter();
1116 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1117 ImGui::SetNextWindowSize(ImVec2(450, 400), ImGuiCond_Appearing);
1118
1119 if (ImGui::BeginPopupModal("Keyboard Shortcuts", &m_ShowShortcuts))
1120 {
1121 ImGui::Text("Keyboard Shortcuts");
1122 ImGui::Separator();
1123
1124 ImGui::Columns(2, "shortcuts");
1125 ImGui::SetColumnWidth(0, 200);
1126
1127 ImGui::Text("Editor Control:");
1128 ImGui::Separator();
1129 ImGui::Text("F2"); ImGui::NextColumn(); ImGui::Text("Toggle Blueprint Editor"); ImGui::NextColumn();
1130 ImGui::Text("Escape"); ImGui::NextColumn(); ImGui::Text("Exit Application"); ImGui::NextColumn();
1131
1132 ImGui::Spacing();
1133 ImGui::Text("File Operations:");
1134 ImGui::Separator();
1135 ImGui::Text("Ctrl+N"); ImGui::NextColumn(); ImGui::Text("New Blueprint"); ImGui::NextColumn();
1136 ImGui::Text("Ctrl+O"); ImGui::NextColumn(); ImGui::Text("Open Blueprint"); ImGui::NextColumn();
1137 ImGui::Text("Ctrl+S"); ImGui::NextColumn(); ImGui::Text("Save"); ImGui::NextColumn();
1138 ImGui::Text("Ctrl+Shift+S"); ImGui::NextColumn(); ImGui::Text("Save As"); ImGui::NextColumn();
1139
1140 ImGui::Spacing();
1141 ImGui::Text("Edit Operations:");
1142 ImGui::Separator();
1143 ImGui::Text("Ctrl+Z"); ImGui::NextColumn(); ImGui::Text("Undo"); ImGui::NextColumn();
1144 ImGui::Text("Ctrl+Y"); ImGui::NextColumn(); ImGui::Text("Redo"); ImGui::NextColumn();
1145 ImGui::Text("Insert"); ImGui::NextColumn(); ImGui::Text("Add Component"); ImGui::NextColumn();
1146 ImGui::Text("Delete"); ImGui::NextColumn(); ImGui::Text("Remove Component"); ImGui::NextColumn();
1147
1148 ImGui::Columns(1);
1149 ImGui::Separator();
1150
1151 if (ImGui::Button("Close", ImVec2(120, 0)))
1152 {
1153 m_ShowShortcuts = false;
1154 }
1155
1156 ImGui::EndPopup();
1157 }
1158 }
1159
1161 {
1162 auto& backend = BlueprintEditor::Get();
1163
1164 ImGui::OpenPopup("Migrate Blueprints");
1165 ImVec2 center = ImGui::GetMainViewport()->GetCenter();
1166 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
1167 ImGui::SetNextWindowSize(ImVec2(500, 300), ImGuiCond_Appearing);
1168
1169 bool isOpen = true;
1170 if (ImGui::BeginPopupModal("Migrate Blueprints", &isOpen, ImGuiWindowFlags_AlwaysAutoResize))
1171 {
1172 ImGui::TextWrapped("This will migrate all v1 blueprints to v2 format:");
1173 ImGui::Spacing();
1174
1175 ImGui::BulletText("Add schema_version and blueprintType fields");
1176 ImGui::BulletText("Calculate and save node positions");
1177 ImGui::BulletText("Unify parameters structure");
1178 ImGui::BulletText("Create .v1.backup files");
1179
1180 ImGui::Spacing();
1181 ImGui::Separator();
1182 ImGui::Spacing();
1183
1184 ImGui::TextWrapped("Scanning: Blueprints/");
1185 ImGui::TextColored(ImVec4(1.0f, 0.7f, 0.0f, 1.0f),
1186 "Warning: This will modify your blueprint files!");
1187
1188 ImGui::Spacing();
1189 ImGui::Separator();
1190
1191 if (ImGui::Button("Migrate All", ImVec2(150, 0)))
1192 {
1193 backend.MigrateAllBlueprints();
1194 backend.SetShowMigrationDialog(false);
1195 }
1196
1197 ImGui::SameLine();
1198
1199 if (ImGui::Button("Cancel", ImVec2(150, 0)))
1200 {
1201 backend.SetShowMigrationDialog(false);
1202 }
1203
1204 ImGui::EndPopup();
1205 }
1206
1207 if (!isOpen)
1208 {
1209 backend.SetShowMigrationDialog(false);
1210 }
1211 }
1212
1213 // Phase 6: Keyboard shortcuts handler
1215 {
1216 ImGuiIO& io = ImGui::GetIO();
1217 auto& backend = BlueprintEditor::Get();
1218
1219 // Don't process shortcuts if typing in a text field
1220 if (io.WantTextInput)
1221 return;
1222
1223 // Ctrl+Z : Undo
1224 if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_Z) && !io.KeyShift)
1225 {
1226 if (backend.CanUndo())
1227 backend.Undo();
1228 }
1229
1230 // Ctrl+Y or Ctrl+Shift+Z : Redo
1231 if ((io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_Y)) ||
1232 (io.KeyCtrl && io.KeyShift && ImGui::IsKeyPressed(ImGuiKey_Z)))
1233 {
1234 if (backend.CanRedo())
1235 backend.Redo();
1236 }
1237
1238 // Ctrl+S : Save active tab
1239 if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_S) && !io.KeyShift)
1240 {
1241 if (!TabManager::Get().IsEmpty())
1243 else if (backend.HasBlueprint())
1244 SaveBlueprint();
1245 }
1246
1247 // Ctrl+Shift+S : Save As
1248 if (io.KeyCtrl && io.KeyShift && ImGui::IsKeyPressed(ImGuiKey_S))
1249 {
1250 if (!TabManager::Get().IsEmpty())
1252 else if (backend.HasBlueprint())
1254 }
1255
1256 // Ctrl+N : New Visual Script tab
1257 if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_N) && !io.KeyShift && !io.KeyAlt)
1258 TabManager::Get().CreateNewTab("VisualScript");
1259
1260 // Ctrl+Shift+N : New Behavior Tree tab
1261 if (io.KeyCtrl && io.KeyShift && ImGui::IsKeyPressed(ImGuiKey_N))
1262 TabManager::Get().CreateNewTab("BehaviorTree");
1263
1264 // Ctrl+Alt+N : New Entity Prefab tab
1265 if (io.KeyCtrl && io.KeyAlt && ImGui::IsKeyPressed(ImGuiKey_N) && !io.KeyShift)
1266 TabManager::Get().CreateNewTab("EntityPrefab");
1267
1268 // Ctrl+W : Close active tab
1269 if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_W) && !io.KeyShift)
1270 {
1271 if (!TabManager::Get().IsEmpty())
1272 TabManager::Get().CloseTab(TabManager::Get().GetActiveTabID());
1273 }
1274
1275 // Ctrl+Shift+W : Close all tabs
1276 if (io.KeyCtrl && io.KeyShift && ImGui::IsKeyPressed(ImGuiKey_W))
1278
1279 // Ctrl+O : Open Blueprint
1280 if (io.KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_O))
1281 {
1282 // TODO: File dialog
1283 LoadBlueprint("../Blueprints/example_entity_simple.json");
1284 }
1285
1286 // Ctrl+Shift+T : Save as Template
1287 if (io.KeyCtrl && io.KeyShift && ImGui::IsKeyPressed(ImGuiKey_T))
1288 {
1289 if (backend.HasBlueprint())
1290 m_ShowTemplateBrowser = true;
1291 }
1292 }
1293}
Converts legacy BT v2 JSON graphs to ATS VS v4 JSON format (Phase 6).
ImGui debug panel for ATS VS runtime debugging (Phase 5).
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
#define ICON_FA_FOLDER
#define ICON_FA_PEN_TO_SQUARE
#define ICON_FA_ARROW_ROTATE_RIGHT
#define ICON_FA_BOOK
#define ICON_FA_CUBES
#define ICON_FA_LIGHTBULB
#define ICON_FA_FILE
#define ICON_FA_PLUS
#define ICON_FA_DIAGRAM_PROJECT
#define ICON_FA_BOOKMARK
#define ICON_FA_ROTATE_RIGHT
#define ICON_FA_EYE
#define ICON_FA_TRASH_CAN
#define ICON_FA_GAUGE
#define ICON_FA_WINDOW_RESTORE
#define ICON_FA_CIRCLE_INFO
#define ICON_FA_WRENCH
#define ICON_FA_CODE
#define ICON_FA_SLIDERS
#define ICON_FA_GEAR
#define ICON_FA_FOLDER_OPEN
#define ICON_FA_LIST
#define ICON_FA_FILE_CODE
#define ICON_FA_CIRCLE_QUESTION
#define ICON_FA_BRAIN
#define ICON_FA_CLOCK
#define ICON_FA_XMARK
#define ICON_FA_FLOPPY_DISK
#define ICON_FA_ROTATE_LEFT
#define ICON_FA_BUG
#define ICON_FA_ARROW_RIGHT
#define ICON_FA_CHECK
ImGui profiler panel for ATS VS node execution metrics (Phase 5).
Central manager for the multi-graph tab system.
ImNodes-based graph editor for ATS Visual Script graphs (Phase 5).
IGraphRenderer adapter that wraps VisualScriptEditorPanel.
void Initialize(const std::string &assetsRootPath)
void SetAssetOpenCallback(std::function< void(const std::string &)> callback)
void AddComponent(const std::string &type)
class HistoryPanel * m_HistoryPanel
void LoadBlueprint(const std::string &filepath)
std::map< int, ImVec2 > m_NodePositions
class VisualScriptEditorPanel * m_VSEditorPanel
class TemplateBrowserPanel * m_TemplateBrowserPanel
class ProfilerPanel * m_ProfilerPanel
void NewBlueprint(const std::string &name, const std::string &description="")
Blueprint::EntityBlueprint & GetCurrentBlueprintMutable()
static BlueprintEditor & Get()
ImGui panel that exposes DebugController UI.
Definition DebugPanel.h:24
void Render()
Renders the debug panel window.
void Initialize()
Initialize font system Must be called after ImGui context creation but before first frame.
static FontManager & Get()
Get singleton instance.
bool LoadFontAwesome(const std::string &fontPath, float fontSize=16.0f)
Load Font Awesome font.
HistoryPanel - ImGui panel for command history visualization Shows undo/redo stacks with command desc...
ImGui panel that visualizes PerformanceProfiler data.
void Render()
Renders the profiler panel window.
Singleton that owns and manages all open graph editor tabs.
Definition TabManager.h:65
bool SaveActiveTabAs(const std::string &path)
Saves the active tab to a specific path.
static TabManager & Get()
Returns the global singleton instance.
std::string CreateNewTab(const std::string &graphType)
Creates a new empty tab of the given graph type.
bool CloseAllTabs()
Attempts to close all tabs.
bool SaveActiveTab()
Saves the active tab.
bool IsEmpty() const
Returns true when there are no open tabs.
Definition TabManager.h:150
bool CloseTab(const std::string &tabID)
Closes the given tab.
void RenderTabBar()
Renders the horizontal tab bar (call before RenderActiveCanvas).
void RenderActiveCanvas()
Renders the graph canvas of the active tab.
std::string OpenFileInTab(const std::string &filePath)
Opens a file in a new tab.
TemplateBrowserPanel - ImGui panel for template management Provides interface for browsing,...
ImNodes graph editor for ATS Visual Script v4 graphs.
void Shutdown()
Shutdown the editor panel and release all resources.
void Initialize()
Initialize the editor panel with ImNodes context and UI helpers.
void RenderVerificationLogsPanel()
Public render method for verification logs panel.
Adapts the existing VisualScriptEditorPanel to the IGraphRenderer interface.
VisualScriptEditorPanel & GetPanel()
Direct access to the wrapped panel (for advanced operations).
ComponentData CreateVisualSpriteComponent(const std::string &spritePath, float srcX, float srcY, float srcWidth, float srcHeight, float hotSpotX, float hotSpotY)
ComponentData CreateBoundingBoxComponent(float x, float y, float width, float height)
ComponentData CreateMovementComponent(float dirX, float dirY, float velX, float velY)
ComponentData CreateAIBehaviorComponent(const std::string &behaviorType)
ComponentData CreatePositionComponent(float x, float y)
ComponentData CreatePhysicsBodyComponent(float mass, float speed)
ComponentData CreateHealthComponent(int current, int max)
< Provides AssetID and INVALID_ASSET_ID
std::vector< ComponentData > components
Represents a single open graph in the editor.
Definition TabManager.h:39
#define SYSTEM_LOG