diff --git a/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h b/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h index b6a31ac86..c3c74c2b4 100644 --- a/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h +++ b/Sources/Overload/OvEditor/include/OvEditor/Settings/EditorSettings.h @@ -6,6 +6,7 @@ #pragma once +#include #include namespace OvEditor::Settings @@ -92,5 +93,7 @@ namespace OvEditor::Settings inline static Property TranslationSnapUnit = { 1.0f }; inline static Property RotationSnapUnit = { 15.0f }; inline static Property ScalingSnapUnit = { 1.0f }; + inline static Property FolderExternalToolName = std::string{ Utils::ExternalTools.front().name}; + inline static Property FolderExternalToolCommand = std::string{ Utils::ExternalTools.front().command}; }; } diff --git a/Sources/Overload/OvEditor/include/OvEditor/Utils/ExternalTools.h b/Sources/Overload/OvEditor/include/OvEditor/Utils/ExternalTools.h new file mode 100644 index 000000000..0421a756f --- /dev/null +++ b/Sources/Overload/OvEditor/include/OvEditor/Utils/ExternalTools.h @@ -0,0 +1,26 @@ +/** +* @project: Overload +* @author: Overload Tech. +* @licence: MIT +*/ + +#pragma once + +#include +#include +#include + +namespace OvEditor::Utils +{ + struct ExternalTool + { + const std::string_view name; + const std::string_view command; + }; + + constexpr auto ExternalTools = std::to_array({ + ExternalTool{ "Visual Studio Code", "code {}" }, + ExternalTool{ "Sublime Text", "subl {}" }, + ExternalTool{ "Atom", "atom {}" }, + }); +} diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp index ed991b063..5d7672a0f 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/AssetBrowser.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include #include #include @@ -201,9 +203,27 @@ namespace class FolderContextualMenu : public BrowserItemContextualMenu { + private: + OvTools::Utils::OptRef m_openInExternalTool; + public: FolderContextualMenu(const std::string& p_filePath, bool p_protected = false) : BrowserItemContextualMenu(p_filePath, p_protected) {} + auto GetOpenInExternalToolName() const + { + return std::format( + "Open in {}", + OvEditor::Settings::EditorSettings::FolderExternalToolName.Get() + ); + } + virtual void Execute(OvUI::Plugins::EPluginExecutionContext p_context) override + { + BrowserItemContextualMenu::Execute(p_context); + + // Keep the "Open In External Tool" menu item label up to date + m_openInExternalTool->name = GetOpenInExternalToolName(); + } + virtual void CreateList() override { auto& showInExplorer = CreateWidget("Show in explorer"); @@ -212,6 +232,23 @@ namespace OvTools::Utils::SystemCalls::ShowInExplorer(filePath); }; + m_openInExternalTool = CreateWidget(GetOpenInExternalToolName()); + m_openInExternalTool->ClickedEvent += [this] + { + const auto command = std::vformat( + OvEditor::Settings::EditorSettings::FolderExternalToolCommand.Get(), + std::make_format_args(filePath) + ); + + if (!OvTools::Utils::SystemCalls::ExecuteCommand(command)) + { + OVLOG_ERROR(std::format( + "Failed to open {}", + OvEditor::Settings::EditorSettings::FolderExternalToolName.Get() + )); + } + }; + if (!m_protected) { auto& importAssetHere = CreateWidget("Import Here..."); diff --git a/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp b/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp index 3850dcc0b..f95fbe9c7 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Panels/MenuBar.cpp @@ -18,18 +18,20 @@ #include #include +#include #include #include #include #include #include -#include "OvEditor/Panels/MenuBar.h" -#include "OvEditor/Panels/SceneView.h" -#include "OvEditor/Panels/AssetView.h" -#include "OvEditor/Core/EditorActions.h" -#include "OvEditor/Settings/EditorSettings.h" -#include "OvEditor/Utils/ActorCreationMenu.h" +#include +#include +#include +#include +#include +#include +#include using namespace OvUI::Panels; using namespace OvUI::Widgets; @@ -69,6 +71,38 @@ void OvEditor::Panels::MenuBar::HandleShortcuts(float p_deltaTime) void OvEditor::Panels::MenuBar::InitializeSettingsMenu() { + using namespace OvEditor::Settings; + + auto& folderInExternalToolSettings = m_settingsMenu->CreateWidget("Folder External Tool..."); + auto& folderExternalToolName = folderInExternalToolSettings.CreateWidget("", "Name"); + auto& folderExternalToolCommand = folderInExternalToolSettings.CreateWidget("", "Command"); + folderExternalToolCommand.selectAllOnClick = true; + folderInExternalToolSettings.ClickedEvent += [&folderExternalToolCommand, &folderExternalToolName] + { + folderExternalToolName.content = EditorSettings::FolderExternalToolName; + folderExternalToolCommand.content = EditorSettings::FolderExternalToolCommand; + }; + folderExternalToolName.ContentChangedEvent += [](const auto& p_content) + { + EditorSettings::FolderExternalToolName = p_content; + }; + folderExternalToolCommand.ContentChangedEvent += [](const auto& p_content) + { + EditorSettings::FolderExternalToolCommand = p_content; + }; + folderInExternalToolSettings.CreateWidget(); + folderInExternalToolSettings.CreateWidget("Load External Tool Preset:"); + for (auto& tool : Utils::ExternalTools) + { + folderInExternalToolSettings.CreateWidget(std::string{ tool.name }, 300.0f).ClickedEvent += [&folderExternalToolName, &folderExternalToolCommand, &tool] + { + EditorSettings::FolderExternalToolName = std::string{ tool.name }; + EditorSettings::FolderExternalToolCommand = std::string{ tool.command }; + folderExternalToolName.content = tool.name; + folderExternalToolCommand.content = tool.command; + }; + } + m_settingsMenu->CreateWidget("Spawn actors at origin", "", true, true).ValueChangedEvent += EDITOR_BIND(SetActorSpawnAtOrigin, std::placeholders::_1); m_settingsMenu->CreateWidget("Vertical Synchronization", "", true, true).ValueChangedEvent += [this](bool p_value) { EDITOR_CONTEXT(device)->SetVsync(p_value); }; auto& cameraSpeedMenu = m_settingsMenu->CreateWidget("Camera Speed"); diff --git a/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp b/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp index f648cd257..db55be211 100644 --- a/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp +++ b/Sources/Overload/OvEditor/src/OvEditor/Settings/EditorSettings.cpp @@ -42,6 +42,8 @@ void OvEditor::Settings::EditorSettings::Save() iniFile.Add("translation_snap_unit", TranslationSnapUnit.Get()); iniFile.Add("rotation_snap_unit", RotationSnapUnit.Get()); iniFile.Add("scaling_snap_unit", ScalingSnapUnit.Get()); + iniFile.Add("folder_external_tool_name", FolderExternalToolName.Get()); + iniFile.Add("folder_external_tool_command", FolderExternalToolCommand.Get()); iniFile.Rewrite(); } @@ -57,4 +59,6 @@ void OvEditor::Settings::EditorSettings::Load() LoadIniEntry(iniFile, "translation_snap_unit", TranslationSnapUnit); LoadIniEntry(iniFile, "rotation_snap_unit", RotationSnapUnit); LoadIniEntry(iniFile, "scaling_snap_unit", ScalingSnapUnit); + LoadIniEntry(iniFile, "folder_external_tool_name", FolderExternalToolName); + LoadIniEntry(iniFile, "folder_external_tool_command", FolderExternalToolCommand); } diff --git a/Sources/Overload/OvTools/include/OvTools/Utils/SystemCalls.h b/Sources/Overload/OvTools/include/OvTools/Utils/SystemCalls.h index e97ea1698..3b5600f19 100644 --- a/Sources/Overload/OvTools/include/OvTools/Utils/SystemCalls.h +++ b/Sources/Overload/OvTools/include/OvTools/Utils/SystemCalls.h @@ -38,6 +38,12 @@ namespace OvTools::Utils */ static void EditFile(const std::string& p_file); + /** + * Execute a custom command. Returns true if the command invocation succeeded + * @param p_command + */ + static bool ExecuteCommand(const std::string_view p_command); + /** * Open the given url with the default browser * @param p_url diff --git a/Sources/Overload/OvTools/src/OvTools/Utils/SystemCalls.cpp b/Sources/Overload/OvTools/src/OvTools/Utils/SystemCalls.cpp index d59904d7e..cf0d595b6 100644 --- a/Sources/Overload/OvTools/src/OvTools/Utils/SystemCalls.cpp +++ b/Sources/Overload/OvTools/src/OvTools/Utils/SystemCalls.cpp @@ -4,13 +4,16 @@ * @licence: MIT */ -#include "OvTools/Utils/PathParser.h" -#include "OvTools/Utils/SystemCalls.h" +#include +#include +#include +#include + +#include +#include -#include #include -#include -#include +#include void OvTools::Utils::SystemCalls::ShowInExplorer(const std::string & p_path) { @@ -26,9 +29,44 @@ void OvTools::Utils::SystemCalls::OpenFile(const std::string & p_file, const std void OvTools::Utils::SystemCalls::EditFile(const std::string & p_file) { - ShellExecuteW(NULL, NULL, std::wstring(p_file.begin(), p_file.end()).c_str(), NULL, NULL, SW_NORMAL); + ShellExecuteW(NULL, NULL, std::wstring(p_file.begin(), p_file.end()).c_str(), NULL, NULL, SW_SHOWNORMAL); } +bool OvTools::Utils::SystemCalls::ExecuteCommand(const std::string_view p_command) +{ + STARTUPINFO startupInfo; + PROCESS_INFORMATION processInfo; + + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + ZeroMemory(&processInfo, sizeof(processInfo)); + + std::string command = std::format("cmd.exe /c {}", p_command); + + bool success = CreateProcess( + nullptr, // Application name (nullptr uses command line) + command.data(), // Command to execute + nullptr, // Process security attributes + nullptr, // Thread security attributes + FALSE, // Do not inherit handles + CREATE_NO_WINDOW, // Run the process without a window + nullptr, // Environment variables + nullptr, // Current directory + &startupInfo, // STARTUPINFO structure + &processInfo // PROCESS_INFORMATION structure + ); + + // Wait until child process exits. + WaitForSingleObject(processInfo.hProcess, INFINITE); + + // Close the process and thread handles + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + + return success; +} + + void OvTools::Utils::SystemCalls::OpenURL(const std::string& p_url) { ShellExecute(0, 0, p_url.c_str(), 0, 0, SW_SHOW);