VEX Engine
Retro looking game engine made in vulkan mostly because i wanted to understand it better.
Loading...
Searching...
No Matches
EditorMenuBar.cpp
1#include "EditorMenuBar.hpp"
2#include "AssetBrowser.hpp"
3
4#include "../Editor.hpp"
5#include "../DialogWindow.hpp"
6#include "../EditorProperties.hpp"
7#include "../ProjectProperties.hpp"
8#include "../Execute.hpp"
9
11#include "components/ErrorUtils.hpp"
12#include "components/PathUtils.hpp"
13#include "components/Scene.hpp"
15#include "imgui.h"
16
17#include <nlohmann/json.hpp>
18#include <filesystem>
19#include <ImReflect.hpp>
20
21#ifdef _WIN32
22 #include <windows.h>
23 #include <shellapi.h>
24#else
25 #include <unistd.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28#endif
29
31 std::erase_if(m_Windows, [](const std::shared_ptr<BasicEditorWindow>& window) {
32 return !window->isOpen;
33 });
34
35 static enum { ACTION_NONE, ACTION_QUIT, ACTION_TO_SELECTOR, ACTION_RUN_DEBUG, ACTION_RUN_RELEASE } pendingQuitAction = ACTION_NONE;
36 bool openSavePopup = false;
37
38 if (ImGui::BeginMenuBar()) {
39 if (ImGui::BeginMenu("File")) {
40 if (ImGui::MenuItem("New Scene")) {
41 NewScene();
42 }
43 if (ImGui::MenuItem("Open Scene")) {
44 OpenScene();
45 }
46 if (ImGui::MenuItem("Save Scene")) {
47 std::string sceneName = vex::GetAssetPath(m_editor.getSceneManager()->getLastSceneName());
48 m_editor.getSceneManager()->GetScene(sceneName)->Save(sceneName);
49 }
50 if (ImGui::MenuItem("Save Scene As")) {
52 }
53 if (ImGui::MenuItem("Quit to Project Selector")) {
54 pendingQuitAction = ACTION_TO_SELECTOR;
55 openSavePopup = true;
56 }
57 if (ImGui::MenuItem("Quit Project")) {
58 pendingQuitAction = ACTION_QUIT;
59 openSavePopup = true;
60 }
61 ImGui::EndMenu();
62 }
63 if (ImGui::BeginMenu("Build")) {
64 if (ImGui::MenuItem("Build Debug")) {
65 RunBuild(true, false);
66 }
67 if (ImGui::MenuItem("Build Release")) {
68 RunBuild(false, false);
69 }
70 if (ImGui::MenuItem("Build Distribution (Shipping)")) {
71 BuildDist();
72 }
73 ImGui::EndMenu();
74 }
75 if (ImGui::BeginMenu("Preferences")) {
76 if (ImGui::MenuItem("Editor Settings")) {
78 }
79 if (ImGui::MenuItem("Project Settings")) {
81 }
82 ImGui::EndMenu();
83 }
84 if (ImGui::BeginMenu("Window")) {
85 if (ImGui::MenuItem("Console")){
86 m_editor.ShowConsole();
87 }
88 ImGui::EndMenu();
89 }
90
91 float runMenuWidth = ImGui::CalcTextSize("Run").x + 40.0f;
92 float currentPos = ImGui::GetCursorPosX();
93 float rightPos = ImGui::GetWindowWidth() - runMenuWidth;
94
95 if (rightPos > currentPos) {
96 ImGui::SetCursorPosX(rightPos);
97 }
98
99 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.00f, 0.23f, 0.01f, 1.0f));
100 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.47f, 0.05f, 0.05f, 1.0f));
101 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.30f, 0.03f, 0.03f, 1.0f));
102 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.94f, 0.85f, 0.85f, 1.0f));
103
104 ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0.0f);
105
106 if (ImGui::Button("Run", ImVec2(runMenuWidth - 10, 0))) {
107 ImGui::OpenPopup("RunPopup");
108 }
109
110 ImGui::PopStyleVar();
111 ImGui::PopStyleColor(4);
112
113 if (ImGui::BeginPopup("RunPopup")) {
114 if (ImGui::MenuItem("Debug")) {
115 pendingQuitAction = ACTION_RUN_DEBUG;
116 openSavePopup = true;
117 }
118 if (ImGui::MenuItem("Release")) {
119 pendingQuitAction = ACTION_RUN_RELEASE;
120 openSavePopup = true;
121 }
122 ImGui::EndPopup();
123 }
124 ImGui::EndMenuBar();
125 }
126
127 if (openSavePopup) {
128 ImGui::OpenPopup("Save Changes?");
129 }
130
131 ImVec2 center = ImGui::GetMainViewport()->GetCenter();
132 ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
133
134 if (ImGui::BeginPopupModal("Save Changes?", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
135 ImGui::Text("Do you want to save changes to the current scene before quitting?");
136 ImGui::Separator();
137 ImGui::Spacing();
138
139 if (ImGui::Button("Save", ImVec2(120, 0))) {
140 std::string sceneName = vex::GetAssetPath(m_editor.getSceneManager()->getLastSceneName());
141 m_editor.getSceneManager()->GetScene(sceneName)->Save(sceneName);
142 ImGui::CloseCurrentPopup();
143
144 if (pendingQuitAction == ACTION_TO_SELECTOR) {
146 m_editor.quit();
147 } else if (pendingQuitAction == ACTION_QUIT) {
148 m_editor.quit();
149 }else if (pendingQuitAction == ACTION_RUN_DEBUG) {
150 RunBuild(true, true);
151 }else if (pendingQuitAction == ACTION_RUN_RELEASE) {
152 RunBuild(false, true);
153 }
154 pendingQuitAction = ACTION_NONE;
155 }
156
157 ImGui::SameLine();
158
159 if (ImGui::Button("Don't Save", ImVec2(120, 0))) {
160 ImGui::CloseCurrentPopup();
161
162 if (pendingQuitAction == ACTION_TO_SELECTOR) {
164 m_editor.quit();
165 } else if (pendingQuitAction == ACTION_QUIT) {
166 m_editor.quit();
167 }else if (pendingQuitAction == ACTION_RUN_DEBUG) {
168 RunBuild(true, true);
169 }else if (pendingQuitAction == ACTION_RUN_RELEASE) {
170 RunBuild(false, true);
171 }
172 pendingQuitAction = ACTION_NONE;
173 }
174
175 ImGui::SameLine();
176
177 if (ImGui::Button("Cancel", ImVec2(120, 0))) {
178 ImGui::CloseCurrentPopup();
179 pendingQuitAction = ACTION_NONE;
180 }
181
182 ImGui::EndPopup();
183 }
184}
185
187 std::filesystem::path binDir = vex::GetExecutableDir();
188 std::filesystem::path selectorPath = binDir / "VexProjectSelector";
189
190 #ifdef _WIN32
191 selectorPath += ".exe";
192 #endif
193
194 if (!std::filesystem::exists(selectorPath)) {
195 vex::log("Error: Could not find ProjectSelector at %s", selectorPath.string().c_str());
196 return;
197 }
198
199 std::string pathStr = selectorPath.string();
200
201 #ifdef _WIN32
202 ShellExecuteA(NULL, "open", pathStr.c_str(), NULL, NULL, SW_SHOW);
203 #else
204 pid_t pid = fork();
205
206 if (pid == 0) {
207 if (setsid() < 0) {
208 exit(EXIT_FAILURE);
209 }
210 execl(pathStr.c_str(), pathStr.c_str(), (char*)NULL);
211 exit(EXIT_FAILURE);
212 }
213 #endif
214}
215
217 std::shared_ptr<BasicEditorWindow> openSceneWindow = std::make_shared<BasicEditorWindow>();
218 std::weak_ptr<BasicEditorWindow> weakWindow = openSceneWindow;
219
220 EditorProperties* editorProperties = m_editor.getEditorProperties();
221
222 openSceneWindow->Create = [this, weakWindow, editorProperties](vex::ImGUIWrapper& wrapper){
223 wrapper.addUIFunction([=, this](){
224 auto window = weakWindow.lock(); if (!window || !window->isOpen) return;
225 ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
226
227 if (ImGui::Begin("Editor Settings", &window->isOpen)) {
228 ImReflect::Input("##data", editorProperties);
229 }
230 ImGui::End();
231 });
232 };
233
234 m_Windows.push_back(openSceneWindow);
235 openSceneWindow->Create(m_ImGUIWrapper);
236}
237
239 std::shared_ptr<BasicEditorWindow> openSceneWindow = std::make_shared<BasicEditorWindow>();
240 std::weak_ptr<BasicEditorWindow> weakWindow = openSceneWindow;
241
242 ProjectProperties* projectProperties = m_editor.getProjectProperties();
243
244 openSceneWindow->Create = [this, weakWindow, projectProperties](vex::ImGUIWrapper& wrapper){
245 wrapper.addUIFunction([=, this](){
246 auto window = weakWindow.lock(); if (!window || !window->isOpen) return;
247 ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
248
249 if (ImGui::Begin("Project Settings", &window->isOpen)) {
250 ImReflect::Input("##data", projectProperties);
251 }
252 ImGui::End();
253 });
254 };
255
256 m_Windows.push_back(openSceneWindow);
257 openSceneWindow->Create(m_ImGUIWrapper);
258}
259
261 std::shared_ptr<BasicEditorWindow> openSceneWindow = std::make_shared<BasicEditorWindow>();
262 std::weak_ptr<BasicEditorWindow> weakWindow = openSceneWindow;
263 std::string startPath = m_editor.getProjectBinaryPath() + "/Assets";
264 auto dialogBrowser = std::make_shared<vex::AssetBrowser>(startPath);
265 auto showError = std::make_shared<bool>(false);
266
267 openSceneWindow->Create = [this, weakWindow, dialogBrowser, showError](vex::ImGUIWrapper& wrapper){
268 wrapper.addUIFunction([=, this](){
269 auto window = weakWindow.lock(); if (!window || !window->isOpen) return;
270 ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
271
272 if (ImGui::Begin("Open Scene File", &window->isOpen)) {
273 std::string selectedFile = dialogBrowser->Draw(m_editor.GetEditorIcons(), m_editor.getEditorProperties()->assetBrowserThumbnailSize);
274 if (!selectedFile.empty() && !dialogBrowser->GetExtension(selectedFile).empty()) {
275
276 if (dialogBrowser->GetExtension(selectedFile) == ".json") {
277
278 int type = dialogBrowser->GetJSONAssetType(selectedFile);
279 if (type == 1) {
280 vex::log("Menu Bar requesting load: %s", selectedFile.c_str());
281 m_editor.requestSceneReload(selectedFile);
282 window->isOpen = false;
283 } else {
284 *showError = true;
285 }
286 } else {
287 *showError = true;
288 }
289 }
290
291 if (*showError) {
292 ImGui::OpenPopup("Invalid Scene File");
293 *showError = false;
294 }
295
296 if (ImGui::BeginPopupModal("Invalid Scene File", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
297 ImGui::Text("The selected file is not a valid VEX Scene file.");
298 ImGui::Text("Please select a .json file with scene data.");
299
300 ImGui::Separator();
301
302 if (ImGui::Button("OK", ImVec2(120, 0))) {
303 ImGui::CloseCurrentPopup();
304 }
305 ImGui::EndPopup();
306 }
307 }
308 ImGui::End();
309 });
310 };
311
312 m_Windows.push_back(openSceneWindow);
313 openSceneWindow->Create(m_ImGUIWrapper);
314}
315
317 std::shared_ptr<BasicEditorWindow> openSceneWindow = std::make_shared<BasicEditorWindow>();
318 std::weak_ptr<BasicEditorWindow> weakWindow = openSceneWindow;
319 std::string startPath = m_editor.getProjectBinaryPath() + "/Assets";
320 auto dialogBrowser = std::make_shared<vex::AssetBrowser>(startPath);
321 auto showError = std::make_shared<bool>(false);
322
323 auto saveFolder = std::make_shared<std::string>(startPath);
324 auto fileName = std::make_shared<std::string>("NewScene.json");
325
326 auto* scene = m_editor.getSceneManager()->GetScene(m_editor.getSceneManager()->getLastSceneName());
327
328 openSceneWindow->Create = [this, weakWindow, dialogBrowser, showError, scene, saveFolder, fileName](vex::ImGUIWrapper& wrapper) {
329 wrapper.addUIFunction([=, this]() {
330 auto window = weakWindow.lock(); if (!window || !window->isOpen) return;
331 ImGui::SetNextWindowSize(ImVec2(600, 450), ImGuiCond_FirstUseEver);
332
333 if (ImGui::Begin("Save Scene As", &window->isOpen)) {
334
335 std::string selectedFile = dialogBrowser->Draw(m_editor.GetEditorIcons(), m_editor.getEditorProperties()->assetBrowserThumbnailSize);
336
337 if (!selectedFile.empty()) {
338 std::filesystem::path p(selectedFile);
339
340 if (p.has_extension()) {
341 *saveFolder = p.parent_path().string();
342 *fileName = p.filename().string();
343 } else {
344 *saveFolder = selectedFile;
345 }
346 }
347 else if (dialogBrowser->getCurrentPath() != *saveFolder) {
348 *saveFolder = dialogBrowser->getCurrentPath();
349 }
350
351 ImGui::Separator();
352 ImGui::Text("Save Options");
353
354 ImReflect::Input("Filename", *fileName);
355
356 if (ImGui::Button("Save", ImVec2(100, 0))) {
357 if (saveFolder->empty() || fileName->empty()) {
358 vex::log("Error: Folder or Filename cannot be empty.");
359 }
360 else {
361 std::filesystem::path finalPath(*saveFolder);
362 finalPath /= *fileName;
363
364 if (!finalPath.has_extension()) {
365 finalPath += ".json";
366 }
367
368 scene->Save(finalPath.string());
369 vex::log("Saved scene to: %s", finalPath.string().c_str());
370 window->isOpen = false;
371 }
372 }
373 }
374 ImGui::End();
375 });
376 };
377
378 m_Windows.push_back(openSceneWindow);
379 openSceneWindow->Create(m_ImGUIWrapper);
380}
381
383 std::filesystem::path projectFile = std::filesystem::path(m_editor.getProjectBinaryPath()) / "VexProject.json";
384
385 std::ifstream file(projectFile);
386 if (file.is_open()) {
387 try {
388 nlohmann::json j;
389 file >> j;
390 if (j.contains("project_name")) {
391 return j["project_name"].get<std::string>();
392 }
393 } catch (...) {
394 vex::log("Error: Failed to parse VexProject.json");
395 }
396 }
397 return "";
398}
399
400void EditorMenuBar::RunBuild(bool isDebug, bool runAfter) {
401 struct BuildState {
402 std::string m_logs = "";
403 bool m_isFinished = false;
404 std::mutex m_logMutex;
405 bool m_autoScroll = true;
406 };
407 auto state = std::make_shared<BuildState>();
408
409 std::string projectPath = m_editor.getProjectBinaryPath();
410 std::string configFlag = isDebug ? " -d" : " -r";
411 std::string command;
412
413 #ifdef _WIN32
414 command = "..\\..\\BuildTools\\build\\ProjectBuilder.exe \"" + projectPath + "\"" + configFlag;
415 #else
416 command = "../../BuildTools/build/ProjectBuilder \"" + projectPath + "\"" + configFlag;
417 #endif
418
419 vex::log("Starting Build: %s", command.c_str());
420
421 std::shared_ptr<BasicEditorWindow> buildWindow = std::make_shared<BasicEditorWindow>();
422 std::weak_ptr<BasicEditorWindow> weakWindow = buildWindow;
423
424 buildWindow->Create = [this, weakWindow, state, isDebug](vex::ImGUIWrapper& wrapper) {
425 wrapper.addUIFunction([=]() {
426 auto window = weakWindow.lock(); if (!window || !window->isOpen) return;
427
428 ImGui::SetNextWindowSize(ImVec2(600, 400), ImGuiCond_FirstUseEver);
429
430 std::string title = isDebug ? "Building Debug..." : "Building Release...";
431 if (state->m_isFinished) title += " (Finished)";
432
433 if (ImGui::Begin(title.c_str(), &window->isOpen)) {
434
435 if(ImGui::Button("Clear")) {
436 std::lock_guard<std::mutex> lock(state->m_logMutex);
437 state->m_logs.clear();
438 }
439 ImGui::SameLine();
440 ImGui::Checkbox("Auto-scroll", &state->m_autoScroll);
441
442 ImGui::Separator();
443
444 ImGui::BeginChild("BuildLogRegion", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
445
446 {
447 std::lock_guard<std::mutex> lock(state->m_logMutex);
448 ImGui::TextUnformatted(state->m_logs.c_str());
449 }
450
451 if (state->m_autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
452 ImGui::SetScrollHereY(1.0f);
453 }
454
455 ImGui::EndChild();
456 }
457 ImGui::End();
458 });
459 };
460
461 m_Windows.push_back(buildWindow);
462 buildWindow->Create(m_ImGUIWrapper);
463
464 std::thread([command, state, this, isDebug, runAfter]() {
465 executeCommandRealTime(command,
466 [state](const std::string& line) {
467 std::lock_guard<std::mutex> lock(state->m_logMutex);
468 state->m_logs += line;
469 }
470 );
471
472 if (runAfter) {
473 std::string projectName = GetProjectName();
474 if (projectName.empty()) {
475 std::lock_guard<std::mutex> lock(state->m_logMutex);
476 state->m_logs += "\n[Error] Could not retrieve Project Name from VexProject.json. Cannot run.\n";
477 } else {
478 std::filesystem::path runPath(m_editor.getProjectBinaryPath());
479 runPath = runPath / "Build" / (isDebug ? "Debug" : "Release");
480 runPath = runPath / projectName;
481
482 #ifdef _WIN32
483 runPath += ".exe";
484 #endif
485
486 if (std::filesystem::exists(runPath)) {
487 {
488 std::lock_guard<std::mutex> lock(state->m_logMutex);
489 state->m_logs += "\n=== Build Complete. Launching: " + runPath.string() + " ===\n";
490 }
491
492 std::string runCmd = "\"" + runPath.string() + "\"";
493
494 executeCommandRealTime(runCmd,
495 [state](const std::string& line) {
496 std::lock_guard<std::mutex> lock(state->m_logMutex);
497 state->m_logs += line;
498 }
499 );
500 } else {
501 std::lock_guard<std::mutex> lock(state->m_logMutex);
502 state->m_logs += "\n[Error] Executable not found at: " + runPath.string() + "\n";
503 }
504 }
505 }
506
507 {
508 std::lock_guard<std::mutex> lock(state->m_logMutex);
509 state->m_logs += "\n=== Build Finished ===\n";
510 state->m_isFinished = true;
511 }
512 }).detach();
513}
514
516 struct BuildState {
517 std::string m_logs = "";
518 bool m_isFinished = false;
519 std::mutex m_logMutex;
520 bool m_autoScroll = true;
521 };
522 auto state = std::make_shared<BuildState>();
523
524 std::string projectPath;
525 std::string enginePath;
526
527 try {
528 projectPath = std::filesystem::canonical(m_editor.getProjectBinaryPath()).string();
529 enginePath = std::filesystem::canonical(vex::GetExecutableDir() / ".." / "..").string();
530 } catch (const std::exception& e) {
531 vex::log("Error resolving paths for Dist Build: %s", e.what());
532 return;
533 }
534
535 std::string command;
536
537 #ifdef _WIN32
538 command = "..\\..\\BuildTools\\build\\ProjectBuilder.exe \"" + projectPath + "\" -dist";
539 vex::log("Starting Windows Distribution Build: %s", command.c_str());
540
541 #else
542 std::string image = "vex-builder:latest";
543 std::string containerEnginePath = "/VexEngine";
544 std::string containerProjectPath = "/VexProject";
545
546 std::string shellCmd =
547 "echo '>> Compiling ProjectBuilder (Container)...' && "
548 "cd " + containerEnginePath + "/BuildTools && "
549 "chmod +x rebuild-buildtools-linux.sh && "
550 "./rebuild-buildtools-linux.sh build_dist && "
551 "echo '>> Starting Project Distribution Build...' && "
552 "./build_dist/ProjectBuilder " + containerProjectPath + " -dist";
553
554 std::stringstream cmdBuilder;
555
556 bool isPodman = (system("which podman > /dev/null 2>&1") == 0);
557 if (isPodman) cmdBuilder << "podman";
558 else cmdBuilder << "docker";
559
560 std::string mountOpts = ":rw,z";
561
562 cmdBuilder << " run --rm ";
563
564 if (isPodman) {
565 cmdBuilder << "--userns=keep-id ";
566 } else {
567 cmdBuilder << "--user $(id -u):$(id -g) ";
568 }
569
570 cmdBuilder << "-v \"" << enginePath << ":" << containerEnginePath << mountOpts << "\" "
571 << "-v \"" << projectPath << ":" << containerProjectPath << mountOpts << "\" "
572 << "-w " << containerEnginePath << " "
573 << image << " "
574 << "/bin/bash -c \"" << shellCmd << "\"";
575
576 command = cmdBuilder.str() + " 2>&1";
577
578 vex::log("Starting Linux (Sniper) Distribution Build...");
579 #endif
580
581 std::shared_ptr<BasicEditorWindow> buildWindow = std::make_shared<BasicEditorWindow>();
582 std::weak_ptr<BasicEditorWindow> weakWindow = buildWindow;
583
584 buildWindow->Create = [this, weakWindow, state](vex::ImGUIWrapper& wrapper) {
585 wrapper.addUIFunction([=]() {
586 auto window = weakWindow.lock(); if (!window || !window->isOpen) return;
587 ImGui::SetNextWindowSize(ImVec2(800, 500), ImGuiCond_FirstUseEver);
588
589 if (ImGui::Begin("Distribution Build (Shipping)", &window->isOpen)) {
590 if(ImGui::Button("Clear")) {
591 std::lock_guard<std::mutex> lock(state->m_logMutex);
592 state->m_logs.clear();
593 }
594 ImGui::SameLine();
595 ImGui::Checkbox("Auto-scroll", &state->m_autoScroll);
596 ImGui::Separator();
597
598 #ifndef _WIN32
599 ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.0f, 1.0f), "[Linux] Building in Special Container.");
600 #endif
601
602 if (state->m_isFinished) {
603 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f), "Status: Finished");
604 } else {
605 ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "Status: Working...");
606 }
607
608 ImGui::BeginChild("BuildLogRegion", ImVec2(0, 0), false, ImGuiWindowFlags_HorizontalScrollbar);
609 {
610 std::lock_guard<std::mutex> lock(state->m_logMutex);
611 ImGui::TextUnformatted(state->m_logs.c_str());
612 }
613 if (state->m_autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
614 ImGui::SetScrollHereY(1.0f);
615 }
616 ImGui::EndChild();
617 }
618 ImGui::End();
619 });
620 };
621
622 m_Windows.push_back(buildWindow);
623 buildWindow->Create(m_ImGUIWrapper);
624
625 std::thread([command, state]() {
626 try {
627 executeCommandRealTime(command,
628 [state](const std::string& line) {
629 {
630 std::lock_guard<std::mutex> lock(state->m_logMutex);
631 state->m_logs += line;
632 }
633
634 if (line.length() > 1) {
635 vex::log("%s", line.c_str());
636 }
637 }
638 );
639 } catch (const std::exception& e) {
640 std::lock_guard<std::mutex> lock(state->m_logMutex);
641 state->m_logs += "\n[CRITICAL ERROR] Execution failed: ";
642 state->m_logs += e.what();
643 }
644
645 {
646 std::lock_guard<std::mutex> lock(state->m_logMutex);
647 state->m_logs += "\n=== Distribution Build Finished ===\n";
648 state->m_isFinished = true;
649 }
650 }).detach();
651}
652
654 std::shared_ptr<BasicEditorWindow> newSceneWindow = std::make_shared<BasicEditorWindow>();
655 std::weak_ptr<BasicEditorWindow> weakWindow = newSceneWindow;
656 std::string startPath = m_editor.getProjectBinaryPath() + "/Assets";
657 auto dialogBrowser = std::make_shared<vex::AssetBrowser>(startPath);
658
659 auto saveFolder = std::make_shared<std::string>(startPath);
660 auto fileName = std::make_shared<std::string>("NewScene.json");
661
662 newSceneWindow->Create = [this, weakWindow, dialogBrowser, saveFolder, fileName](vex::ImGUIWrapper& wrapper) {
663 wrapper.addUIFunction([=, this]() {
664 auto window = weakWindow.lock(); if (!window || !window->isOpen) return;
665 ImGui::SetNextWindowSize(ImVec2(600, 450), ImGuiCond_FirstUseEver);
666
667 if (ImGui::Begin("Create New Scene", &window->isOpen)) {
668
669 std::string selectedFile = dialogBrowser->Draw(m_editor.GetEditorIcons(), m_editor.getEditorProperties()->assetBrowserThumbnailSize);
670
671 if (!selectedFile.empty()) {
672 std::filesystem::path p(selectedFile);
673 if (p.has_extension()) {
674 *saveFolder = p.parent_path().string();
675 *fileName = p.filename().string();
676 } else {
677 *saveFolder = selectedFile;
678 }
679 }
680 else if (dialogBrowser->getCurrentPath() != *saveFolder) {
681 *saveFolder = dialogBrowser->getCurrentPath();
682 }
683
684 ImGui::Separator();
685 ImGui::Text("New Scene Options");
686
687 ImReflect::Input("Filename", *fileName);
688
689 if (ImGui::Button("Create & Open", ImVec2(120, 0))) {
690 if (saveFolder->empty() || fileName->empty()) {
691 vex::log("Error: Folder or Filename cannot be empty.");
692 }
693 else {
694 std::filesystem::path destPath(*saveFolder);
695 destPath /= *fileName;
696
697 if (!destPath.has_extension()) {
698 destPath += ".json";
699 }
700
701 std::filesystem::path sourcePath = std::filesystem::path("../Assets/default/default.json");
702
703 try {
704 if (std::filesystem::exists(sourcePath)) {
705 std::filesystem::copy_file(sourcePath, destPath, std::filesystem::copy_options::overwrite_existing);
706 vex::log("New scene created: %s", destPath.string().c_str());
707
708 m_editor.requestSceneReload(destPath.string());
709 window->isOpen = false;
710 }
711 else {
712 vex::log("Error: Could not find default scene template at: %s", sourcePath.string().c_str());
713 }
714 }
715 catch (const std::filesystem::filesystem_error& e) {
716 vex::log("Filesystem Error: %s", e.what());
717 }
718 }
719 }
720 }
721 ImGui::End();
722 });
723 };
724
725 m_Windows.push_back(newSceneWindow);
726 newSceneWindow->Create(m_ImGUIWrapper);
727}
Class responsible for displaying and navigating project assets within an ImGUI window.
A specialized Engine class for creating and managing modal dialog windows.
ImGUI component responsible for drawing the main editor menu bar and handling its actions.
The main Editor class, inheriting from Engine and providing editor-specific functionality and UI.
This file defines struct GameInfo.
This file defines SceneManager class.
This file defines SceneManager class.
void DrawBar()
Draws the main menu bar and processes user input.
void RunBuild(bool isDebug, bool runAfter=false)
Executes the project build process.
void OpenProjectSettings()
Opens the project settings window/menu.
void OpenProjectSelector()
Opens the project selector window.
void SaveSceneAs()
Saves the current scene to a new file path.
void NewScene()
Copies empty scene to assets folders and opens it.
void OpenEditorSettings()
Opens the editor settings window/menu.
void BuildDist()
Builds the project distribution package.
void OpenScene()
Handles the logic for opening an existing scene file.
std::string GetProjectName()
Retrieves the name of the current project.
This class provides an interface template for ImGui Implementation. Every backend should implement th...
std::filesystem::path VEX_EXPORT GetExecutableDir()
Retrieves the directory containing the current executable.
Definition PathUtils.cpp:37
void VEX_EXPORT log(const char *fmt,...)
Logs a formatted message.
std::string VEX_EXPORT GetAssetPath(const std::string &relativePath)
Resolves the absolute path of an asset based on the current build configuration.
Definition PathUtils.cpp:73
Structure to hold all configurable editor settings and properties.
Structure to hold all configurable project settings and properties.