mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-10-20 21:45:21 +00:00
feat: add dialog handler for ModMan
This commit is contained in:
123
src/ModMan/Web/Binds/DialogBinds.cpp
Normal file
123
src/ModMan/Web/Binds/DialogBinds.cpp
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
#include "DialogBinds.h"
|
||||||
|
|
||||||
|
#include "Web/Platform/DialogHandler.h"
|
||||||
|
#include "Web/UiCommunication.h"
|
||||||
|
|
||||||
|
#include "Json/JsonExtension.h"
|
||||||
|
|
||||||
|
using namespace PLATFORM_NAMESPACE;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
class FileDialogFilterDto
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string name;
|
||||||
|
std::string filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
NLOHMANN_DEFINE_TYPE_EXTENSION(FileDialogFilterDto, name, filter);
|
||||||
|
|
||||||
|
class OpenFileDialogDto
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::optional<std::vector<FileDialogFilterDto>> filters;
|
||||||
|
};
|
||||||
|
|
||||||
|
NLOHMANN_DEFINE_TYPE_EXTENSION(OpenFileDialogDto, filters);
|
||||||
|
|
||||||
|
class SaveFileDialogDto
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::optional<std::vector<FileDialogFilterDto>> filters;
|
||||||
|
};
|
||||||
|
|
||||||
|
NLOHMANN_DEFINE_TYPE_EXTENSION(SaveFileDialogDto, filters);
|
||||||
|
|
||||||
|
void ReplyWithDialogResult(webview::webview& wv, const std::string& id, const DialogCallbackResultType resultType, const std::optional<std::string>& result)
|
||||||
|
{
|
||||||
|
if (resultType == FAILED)
|
||||||
|
ui::PromiseReject(wv, id, result);
|
||||||
|
else
|
||||||
|
ui::PromiseResolve(wv, id, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenFileDialogBind(webview::webview& wv, const std::string& id, const std::optional<OpenFileDialogDto>& dto)
|
||||||
|
{
|
||||||
|
OpenFileDialog dialog;
|
||||||
|
dialog.SetCallback(
|
||||||
|
[&wv, id](const DialogCallbackResultType resultType, const std::optional<std::string>& result)
|
||||||
|
{
|
||||||
|
ReplyWithDialogResult(wv, id, resultType, result);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dto && dto->filters)
|
||||||
|
{
|
||||||
|
for (auto& filter : *dto->filters)
|
||||||
|
{
|
||||||
|
dialog.AddFilter(filter.name, filter.filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.OpenAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SaveFileDialogBind(webview::webview& wv, const std::string& id, const std::optional<SaveFileDialogDto>& dto)
|
||||||
|
{
|
||||||
|
SaveFileDialog dialog;
|
||||||
|
dialog.SetCallback(
|
||||||
|
[&wv, id](const DialogCallbackResultType resultType, const std::optional<std::string>& result)
|
||||||
|
{
|
||||||
|
ReplyWithDialogResult(wv, id, resultType, result);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dto && dto->filters)
|
||||||
|
{
|
||||||
|
for (auto& filter : *dto->filters)
|
||||||
|
{
|
||||||
|
dialog.AddFilter(filter.name, filter.filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.OpenAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FolderSelectDialogBind(webview::webview& wv, const std::string& id)
|
||||||
|
{
|
||||||
|
FolderSelectDialog dialog;
|
||||||
|
dialog.SetCallback(
|
||||||
|
[&wv, id](const DialogCallbackResultType resultType, const std::optional<std::string>& result)
|
||||||
|
{
|
||||||
|
ReplyWithDialogResult(wv, id, resultType, result);
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.OpenAsync();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
void RegisterDialogHandlerBinds(webview::webview& wv)
|
||||||
|
{
|
||||||
|
BindAsync<std::optional<OpenFileDialogDto>>(wv,
|
||||||
|
"openFileDialog",
|
||||||
|
[&wv](const std::string& id, const std::optional<OpenFileDialogDto>& dto)
|
||||||
|
{
|
||||||
|
OpenFileDialogBind(wv, id, dto);
|
||||||
|
});
|
||||||
|
|
||||||
|
BindAsync<std::optional<SaveFileDialogDto>>(wv,
|
||||||
|
"saveFileDialog",
|
||||||
|
[&wv](const std::string& id, const std::optional<SaveFileDialogDto>& dto)
|
||||||
|
{
|
||||||
|
SaveFileDialogBind(wv, id, dto);
|
||||||
|
});
|
||||||
|
|
||||||
|
BindAsync(wv,
|
||||||
|
"folderSelectDialog",
|
||||||
|
[&wv](const std::string& id)
|
||||||
|
{
|
||||||
|
FolderSelectDialogBind(wv, id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} // namespace ui
|
8
src/ModMan/Web/Binds/DialogBinds.h
Normal file
8
src/ModMan/Web/Binds/DialogBinds.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Web/WebViewLib.h"
|
||||||
|
|
||||||
|
namespace ui
|
||||||
|
{
|
||||||
|
void RegisterDialogHandlerBinds(webview::webview& wv);
|
||||||
|
}
|
7
src/ModMan/Web/Platform/AssetHandler.h
Normal file
7
src/ModMan/Web/Platform/AssetHandler.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "Windows/AssetHandlerWindows.h"
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#include "Linux/AssetHandlerLinux.h"
|
||||||
|
#endif
|
7
src/ModMan/Web/Platform/DialogHandler.h
Normal file
7
src/ModMan/Web/Platform/DialogHandler.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "Windows/DialogHandlerWindows.h"
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#include "Linux/DialogHandlerLinux.h"
|
||||||
|
#endif
|
@@ -1,4 +1,4 @@
|
|||||||
#include "AssetHandlerGtk.h"
|
#include "AssetHandlerLinux.h"
|
||||||
|
|
||||||
#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
|
#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
|
||||||
|
|
||||||
@@ -6,6 +6,9 @@
|
|||||||
|
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
using namespace PLATFORM_NAMESPACE_LINUX;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -34,9 +37,9 @@ namespace
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace gtk
|
namespace PLATFORM_NAMESPACE_LINUX
|
||||||
{
|
{
|
||||||
void InstallCustomProtocolHandler(webview::webview& wv)
|
void InstallAssetHandler(webview::webview& wv)
|
||||||
{
|
{
|
||||||
const auto widget = static_cast<GtkWidget*>(wv.browser_controller().value());
|
const auto widget = static_cast<GtkWidget*>(wv.browser_controller().value());
|
||||||
const auto webView = WEBKIT_WEB_VIEW(widget);
|
const auto webView = WEBKIT_WEB_VIEW(widget);
|
||||||
@@ -46,6 +49,6 @@ namespace gtk
|
|||||||
|
|
||||||
webkit_web_context_register_uri_scheme(context, "modman", ModManUriSchemeRequestCb, NULL, nullptr);
|
webkit_web_context_register_uri_scheme(context, "modman", ModManUriSchemeRequestCb, NULL, nullptr);
|
||||||
}
|
}
|
||||||
} // namespace gtk
|
} // namespace PLATFORM_NAMESPACE_LINUX
|
||||||
|
|
||||||
#endif
|
#endif
|
@@ -1,16 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Web/Platform/Platform.h"
|
||||||
|
|
||||||
#include <webview/macros.h>
|
#include <webview/macros.h>
|
||||||
|
|
||||||
#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
|
#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
|
||||||
|
|
||||||
#include "Web/WebViewLib.h"
|
#include "Web/WebViewLib.h"
|
||||||
|
|
||||||
namespace gtk
|
namespace PLATFORM_NAMESPACE_LINUX
|
||||||
{
|
{
|
||||||
constexpr auto URL_PREFIX = "modman://localhost/";
|
constexpr auto URL_PREFIX = "modman://localhost/";
|
||||||
|
|
||||||
void InstallCustomProtocolHandler(webview::webview& wv);
|
void InstallAssetHandler(webview::webview& wv);
|
||||||
} // namespace gtk
|
} // namespace PLATFORM_NAMESPACE_LINUX
|
||||||
|
|
||||||
#endif
|
#endif
|
92
src/ModMan/Web/Platform/Linux/DialogHandlerLinux.cpp
Normal file
92
src/ModMan/Web/Platform/Linux/DialogHandlerLinux.cpp
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
#include "DialogHandlerLinux.h"
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
using namespace PLATFORM_NAMESPACE_LINUX;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool InitGtk()
|
||||||
|
{
|
||||||
|
#if GTK_MAJOR_VERSION >= 4
|
||||||
|
return gtk_init_check();
|
||||||
|
#else
|
||||||
|
return gtk_init_check(nullptr, nullptr);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenFileDialog()
|
||||||
|
{
|
||||||
|
#ifdef GDK_AVAILABLE_IN_4_10
|
||||||
|
auto* dialog = gtk_file_dialog_new();
|
||||||
|
|
||||||
|
gtk_file_dialog_open(dialog, nullptr, nullptr, [](GObject *source,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data
|
||||||
|
) -> void {
|
||||||
|
|
||||||
|
}, nullptr
|
||||||
|
);
|
||||||
|
|
||||||
|
g_object_unref(dialog);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetFilters(void* pFileOpen, const std::vector<FileDialogFilter>& filters)
|
||||||
|
{
|
||||||
|
if (filters.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> ShowFileDialog()
|
||||||
|
{
|
||||||
|
std::optional<std::string> result = std::nullopt;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace PLATFORM_NAMESPACE_LINUX
|
||||||
|
{
|
||||||
|
FileDialogFilter::FileDialogFilter(std::string name, std::string filter)
|
||||||
|
: m_name(std::move(name)),
|
||||||
|
m_filter(std::move(filter))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDialog::FileDialog() = default;
|
||||||
|
|
||||||
|
void FileDialog::AddFilter(std::string name, std::string filter)
|
||||||
|
{
|
||||||
|
m_filters.emplace_back(std::move(name), std::move(filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenFileDialog::OpenFileDialog() = default;
|
||||||
|
|
||||||
|
std::optional<std::string> OpenFileDialog::Open() const
|
||||||
|
{
|
||||||
|
if (!InitGtk())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveFileDialog::SaveFileDialog() = default;
|
||||||
|
|
||||||
|
std::optional<std::string> SaveFileDialog::Open() const
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
FolderSelectDialog::FolderSelectDialog() = default;
|
||||||
|
|
||||||
|
std::optional<std::string> FolderSelectDialog::Open() const
|
||||||
|
{
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
} // namespace PLATFORM_NAMESPACE_LINUX
|
||||||
|
#endif
|
70
src/ModMan/Web/Platform/Linux/DialogHandlerLinux.h
Normal file
70
src/ModMan/Web/Platform/Linux/DialogHandlerLinux.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
|
||||||
|
#include "Web/Platform/Platform.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace PLATFORM_NAMESPACE_LINUX
|
||||||
|
{
|
||||||
|
class FileDialogFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FileDialogFilter(std::string name, std::string filter);
|
||||||
|
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DialogWithCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DialogWithCallback();
|
||||||
|
|
||||||
|
void SetCallback(std::function<void(std::optional<std::string> result)> callback);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::function<void(std::optional<std::string> result)> m_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileDialog : public DialogWithCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FileDialog();
|
||||||
|
|
||||||
|
void AddFilter(std::string name, std::string filter);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<FileDialogFilter> m_filters;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpenFileDialog : public FileDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenFileDialog();
|
||||||
|
|
||||||
|
void OpenAsync() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SaveFileDialog : public FileDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SaveFileDialog();
|
||||||
|
|
||||||
|
void OpenAsync() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FolderSelectDialog : public DialogWithCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FolderSelectDialog();
|
||||||
|
|
||||||
|
void OpenAsync() const;
|
||||||
|
};
|
||||||
|
} // namespace PLATFORM_NAMESPACE_LINUX
|
||||||
|
|
||||||
|
#endif
|
10
src/ModMan/Web/Platform/Platform.h
Normal file
10
src/ModMan/Web/Platform/Platform.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define PLATFORM_NAMESPACE_WINDOWS windows
|
||||||
|
#define PLATFORM_NAMESPACE_LINUX linux
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define PLATFORM_NAMESPACE PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#define PLATFORM_NAMESPACE PLATFORM_NAMESPACE_LINUX
|
||||||
|
#endif
|
@@ -1,7 +1,8 @@
|
|||||||
#include "AssetHandlerEdge.h"
|
#include "AssetHandlerWindows.h"
|
||||||
|
|
||||||
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
|
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
|
||||||
|
|
||||||
|
#include "PlatformUtilsWindows.h"
|
||||||
#include "Web/UiAssets.h"
|
#include "Web/UiAssets.h"
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@@ -11,40 +12,14 @@
|
|||||||
#include <webview/detail/backends/win32_edge.hh>
|
#include <webview/detail/backends/win32_edge.hh>
|
||||||
#include <wrl/event.h>
|
#include <wrl/event.h>
|
||||||
|
|
||||||
|
using namespace PLATFORM_NAMESPACE_WINDOWS;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
constexpr auto LOCALHOST_PREFIX = "http://localhost:";
|
constexpr auto LOCALHOST_PREFIX = "http://localhost:";
|
||||||
|
|
||||||
std::unordered_map<std::string, UiFile> assetLookup;
|
std::unordered_map<std::string, UiFile> assetLookup;
|
||||||
|
|
||||||
std::string WideStringToString(const std::wstring& wideString)
|
|
||||||
{
|
|
||||||
if (wideString.empty())
|
|
||||||
return "";
|
|
||||||
|
|
||||||
const auto sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, wideString.data(), static_cast<int>(wideString.size()), nullptr, 0, nullptr, nullptr);
|
|
||||||
if (sizeNeeded <= 0)
|
|
||||||
throw std::runtime_error(std::format("WideCharToMultiByte() failed: {}", sizeNeeded));
|
|
||||||
|
|
||||||
std::string result(sizeNeeded, 0);
|
|
||||||
WideCharToMultiByte(CP_UTF8, 0, wideString.data(), static_cast<int>(wideString.size()), result.data(), sizeNeeded, nullptr, nullptr);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring StringToWideString(const std::string& string)
|
|
||||||
{
|
|
||||||
if (string.empty())
|
|
||||||
return L"";
|
|
||||||
|
|
||||||
const auto sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, string.data(), static_cast<int>(string.size()), nullptr, 0);
|
|
||||||
if (sizeNeeded <= 0)
|
|
||||||
throw std::runtime_error(std::format("MultiByteToWideChar() failed: {}", sizeNeeded));
|
|
||||||
|
|
||||||
std::wstring result(sizeNeeded, 0);
|
|
||||||
MultiByteToWideChar(CP_UTF8, 0, string.data(), static_cast<int>(string.size()), result.data(), sizeNeeded);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring HeadersForAssetName(const std::string& assetName, const size_t contentLength)
|
std::wstring HeadersForAssetName(const std::string& assetName, const size_t contentLength)
|
||||||
{
|
{
|
||||||
std::wstringstream wss;
|
std::wstringstream wss;
|
||||||
@@ -99,9 +74,9 @@ namespace
|
|||||||
return S_OK;
|
return S_OK;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (uri.starts_with(edge::URL_PREFIX))
|
if (uri.starts_with(URL_PREFIX))
|
||||||
{
|
{
|
||||||
const auto asset = uri.substr(std::char_traits<char>::length(edge::URL_PREFIX) - 1);
|
const auto asset = uri.substr(std::char_traits<char>::length(URL_PREFIX) - 1);
|
||||||
|
|
||||||
const auto foundUiFile = assetLookup.find(asset);
|
const auto foundUiFile = assetLookup.find(asset);
|
||||||
if (foundUiFile != assetLookup.end())
|
if (foundUiFile != assetLookup.end())
|
||||||
@@ -142,9 +117,9 @@ namespace
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
namespace edge
|
namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
{
|
{
|
||||||
void InstallCustomProtocolHandler(webview::webview& wv)
|
void InstallAssetHandler(webview::webview& wv)
|
||||||
{
|
{
|
||||||
assetLookup = ui::BuildUiFileLookup();
|
assetLookup = ui::BuildUiFileLookup();
|
||||||
|
|
||||||
@@ -182,6 +157,6 @@ namespace edge
|
|||||||
std::cerr << "Failed to add resource requested filter\n";
|
std::cerr << "Failed to add resource requested filter\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace edge
|
} // namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
|
||||||
#endif
|
#endif
|
@@ -1,16 +1,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Web/Platform/Platform.h"
|
||||||
|
|
||||||
#include <webview/macros.h>
|
#include <webview/macros.h>
|
||||||
|
|
||||||
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
|
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
|
||||||
|
|
||||||
#include "Web/WebViewLib.h"
|
#include "Web/WebViewLib.h"
|
||||||
|
|
||||||
namespace edge
|
namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
{
|
{
|
||||||
constexpr auto URL_PREFIX = "http://modman.local/";
|
constexpr auto URL_PREFIX = "http://modman.local/";
|
||||||
|
|
||||||
void InstallCustomProtocolHandler(webview::webview& wv);
|
void InstallAssetHandler(webview::webview& wv);
|
||||||
} // namespace edge
|
} // namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
|
||||||
#endif
|
#endif
|
239
src/ModMan/Web/Platform/Windows/DialogHandlerWindows.cpp
Normal file
239
src/ModMan/Web/Platform/Windows/DialogHandlerWindows.cpp
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
#include "DialogHandlerWindows.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include "PlatformUtilsWindows.h"
|
||||||
|
|
||||||
|
#include <shobjidl.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
using namespace PLATFORM_NAMESPACE_WINDOWS;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool SetFilters(IFileDialog* pFileOpen, const std::vector<FileDialogFilter>& filters)
|
||||||
|
{
|
||||||
|
if (filters.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const auto filterCount = filters.size();
|
||||||
|
std::vector<COMDLG_FILTERSPEC> filterSpecs;
|
||||||
|
filterSpecs.reserve(filterCount + 1);
|
||||||
|
|
||||||
|
std::vector<std::wstring> filterStrings;
|
||||||
|
filterStrings.reserve(filterCount * 2);
|
||||||
|
|
||||||
|
for (auto i = 0u; i < filterCount; i++)
|
||||||
|
{
|
||||||
|
const auto& filter = filters[i];
|
||||||
|
COMDLG_FILTERSPEC filterSpec;
|
||||||
|
|
||||||
|
const auto& wName = filterStrings.emplace_back(StringToWideString(filter.m_name));
|
||||||
|
const auto& wSpec = filterStrings.emplace_back(StringToWideString(filter.m_filter));
|
||||||
|
|
||||||
|
filterSpec.pszName = wName.c_str();
|
||||||
|
filterSpec.pszSpec = wSpec.c_str();
|
||||||
|
|
||||||
|
filterSpecs.emplace_back(filterSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
COMDLG_FILTERSPEC wildCardSpec;
|
||||||
|
wildCardSpec.pszName = L"*.*";
|
||||||
|
wildCardSpec.pszSpec = L"*.*";
|
||||||
|
filterSpecs.emplace_back(wildCardSpec);
|
||||||
|
|
||||||
|
const auto result = pFileOpen->SetFileTypes(static_cast<UINT>(filterCount + 1), filterSpecs.data());
|
||||||
|
|
||||||
|
return SUCCEEDED(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogCallbackResultType ShowFileDialog(IFileDialog* pFileDialog, std::optional<std::string>& result)
|
||||||
|
{
|
||||||
|
DialogCallbackResultType resultType = FAILED;
|
||||||
|
|
||||||
|
auto hr = pFileDialog->Show(nullptr);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
IShellItem* pItem;
|
||||||
|
hr = pFileDialog->GetResult(&pItem);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
PWSTR pszFilePath;
|
||||||
|
hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
|
||||||
|
|
||||||
|
// Display the file name to the user.
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
result = WideStringToString(pszFilePath);
|
||||||
|
CoTaskMemFree(pszFilePath);
|
||||||
|
|
||||||
|
resultType = SUCCESSFUL;
|
||||||
|
}
|
||||||
|
pItem->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (HRESULT_FROM_WIN32(ERROR_CANCELLED) == hr)
|
||||||
|
{
|
||||||
|
resultType = CANCELLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultType;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
{
|
||||||
|
DialogWithCallback::DialogWithCallback() = default;
|
||||||
|
|
||||||
|
void DialogWithCallback::SetCallback(callback_t callback)
|
||||||
|
{
|
||||||
|
m_callback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDialogFilter::FileDialogFilter(std::string name, std::string filter)
|
||||||
|
: m_name(std::move(name)),
|
||||||
|
m_filter(std::move(filter))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDialog::FileDialog() = default;
|
||||||
|
|
||||||
|
void FileDialog::AddFilter(std::string name, std::string filter)
|
||||||
|
{
|
||||||
|
m_filters.emplace_back(std::move(name), std::move(filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
OpenFileDialog::OpenFileDialog() = default;
|
||||||
|
|
||||||
|
void OpenFileDialog::OpenAsync()
|
||||||
|
{
|
||||||
|
// Move data out of the dialog object since it may be destroyed
|
||||||
|
callback_t callback(std::move(m_callback));
|
||||||
|
std::vector filters(std::move(m_filters));
|
||||||
|
|
||||||
|
// Windows dialogs are not asynchronous -> Spawn another thread
|
||||||
|
std::thread(
|
||||||
|
[callback, filters]
|
||||||
|
{
|
||||||
|
const auto initResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||||
|
if (!SUCCEEDED(initResult))
|
||||||
|
{
|
||||||
|
callback(FAILED, std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogCallbackResultType resultType = FAILED;
|
||||||
|
std::optional<std::string> result = std::nullopt;
|
||||||
|
IFileOpenDialog* pFileOpen;
|
||||||
|
|
||||||
|
const auto hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr) && SetFilters(pFileOpen, filters))
|
||||||
|
{
|
||||||
|
resultType = ShowFileDialog(pFileOpen, result);
|
||||||
|
pFileOpen->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
CoUninitialize();
|
||||||
|
|
||||||
|
callback(resultType, result);
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveFileDialog::SaveFileDialog() = default;
|
||||||
|
|
||||||
|
void SaveFileDialog::OpenAsync()
|
||||||
|
{
|
||||||
|
// Move data out of the dialog object since it may be destroyed
|
||||||
|
callback_t callback(std::move(m_callback));
|
||||||
|
std::vector filters(std::move(m_filters));
|
||||||
|
|
||||||
|
// Windows dialogs are not asynchronous -> Spawn another thread
|
||||||
|
std::thread(
|
||||||
|
[callback, filters]
|
||||||
|
{
|
||||||
|
const auto initResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||||
|
if (!SUCCEEDED(initResult))
|
||||||
|
{
|
||||||
|
callback(FAILED, std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogCallbackResultType resultType = FAILED;
|
||||||
|
std::optional<std::string> result = std::nullopt;
|
||||||
|
IFileSaveDialog* pFileSave;
|
||||||
|
|
||||||
|
const auto hr = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_ALL, IID_IFileSaveDialog, reinterpret_cast<void**>(&pFileSave));
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr) && SetFilters(pFileSave, filters))
|
||||||
|
{
|
||||||
|
resultType = ShowFileDialog(pFileSave, result);
|
||||||
|
pFileSave->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
CoUninitialize();
|
||||||
|
|
||||||
|
callback(resultType, result);
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
FolderSelectDialog::FolderSelectDialog() = default;
|
||||||
|
|
||||||
|
void FolderSelectDialog::OpenAsync()
|
||||||
|
{
|
||||||
|
// Move data out of the dialog object since it may be destroyed
|
||||||
|
callback_t callback(std::move(m_callback));
|
||||||
|
|
||||||
|
// Windows dialogs are not asynchronous -> Spawn another thread
|
||||||
|
std::thread(
|
||||||
|
[callback]
|
||||||
|
{
|
||||||
|
const auto initResult = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
|
||||||
|
if (!SUCCEEDED(initResult))
|
||||||
|
{
|
||||||
|
callback(FAILED, std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> result = std::nullopt;
|
||||||
|
IFileOpenDialog* pFileOpen;
|
||||||
|
|
||||||
|
const auto hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_ALL, IID_IFileOpenDialog, reinterpret_cast<void**>(&pFileOpen));
|
||||||
|
if (!SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
CoUninitialize();
|
||||||
|
callback(FAILED, std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD dwOptions = 0;
|
||||||
|
if (!SUCCEEDED(pFileOpen->GetOptions(&dwOptions)))
|
||||||
|
{
|
||||||
|
pFileOpen->Release();
|
||||||
|
CoUninitialize();
|
||||||
|
callback(FAILED, std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SUCCEEDED(pFileOpen->SetOptions(dwOptions | FOS_PICKFOLDERS)))
|
||||||
|
{
|
||||||
|
pFileOpen->Release();
|
||||||
|
CoUninitialize();
|
||||||
|
callback(FAILED, std::nullopt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto resultType = ShowFileDialog(pFileOpen, result);
|
||||||
|
|
||||||
|
pFileOpen->Release();
|
||||||
|
CoUninitialize();
|
||||||
|
|
||||||
|
callback(resultType, result);
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
}
|
||||||
|
} // namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
#endif
|
80
src/ModMan/Web/Platform/Windows/DialogHandlerWindows.h
Normal file
80
src/ModMan/Web/Platform/Windows/DialogHandlerWindows.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include "Web/Platform/Platform.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
{
|
||||||
|
enum DialogCallbackResultType : std::uint8_t
|
||||||
|
{
|
||||||
|
SUCCESSFUL,
|
||||||
|
CANCELLED,
|
||||||
|
FAILED
|
||||||
|
};
|
||||||
|
|
||||||
|
class DialogWithCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using callback_t = std::function<void(DialogCallbackResultType resultType, std::optional<std::string> result)>;
|
||||||
|
|
||||||
|
DialogWithCallback();
|
||||||
|
|
||||||
|
void SetCallback(callback_t callback);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
callback_t m_callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileDialogFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FileDialogFilter(std::string name, std::string filter);
|
||||||
|
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FileDialog : public DialogWithCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FileDialog();
|
||||||
|
|
||||||
|
void AddFilter(std::string name, std::string filter);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<FileDialogFilter> m_filters;
|
||||||
|
};
|
||||||
|
|
||||||
|
class OpenFileDialog : public FileDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OpenFileDialog();
|
||||||
|
|
||||||
|
void OpenAsync();
|
||||||
|
};
|
||||||
|
|
||||||
|
class SaveFileDialog : public FileDialog
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SaveFileDialog();
|
||||||
|
|
||||||
|
void OpenAsync();
|
||||||
|
};
|
||||||
|
|
||||||
|
class FolderSelectDialog : public DialogWithCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FolderSelectDialog();
|
||||||
|
|
||||||
|
void OpenAsync();
|
||||||
|
};
|
||||||
|
} // namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
|
||||||
|
#endif
|
40
src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.cpp
Normal file
40
src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.cpp
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
#include "PlatformUtilsWindows.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <format>
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
{
|
||||||
|
std::string WideStringToString(const std::wstring& wideString)
|
||||||
|
{
|
||||||
|
if (wideString.empty())
|
||||||
|
return "";
|
||||||
|
|
||||||
|
const auto sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, wideString.data(), static_cast<int>(wideString.size()), nullptr, 0, nullptr, nullptr);
|
||||||
|
if (sizeNeeded <= 0)
|
||||||
|
throw std::runtime_error(std::format("WideCharToMultiByte() failed: {}", sizeNeeded));
|
||||||
|
|
||||||
|
std::string result(sizeNeeded, 0);
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, wideString.data(), static_cast<int>(wideString.size()), result.data(), sizeNeeded, nullptr, nullptr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring StringToWideString(const std::string& string)
|
||||||
|
{
|
||||||
|
if (string.empty())
|
||||||
|
return L"";
|
||||||
|
|
||||||
|
const auto sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, string.data(), static_cast<int>(string.size()), nullptr, 0);
|
||||||
|
if (sizeNeeded <= 0)
|
||||||
|
throw std::runtime_error(std::format("MultiByteToWideChar() failed: {}", sizeNeeded));
|
||||||
|
|
||||||
|
std::wstring result(sizeNeeded, 0);
|
||||||
|
MultiByteToWideChar(CP_UTF8, 0, string.data(), static_cast<int>(string.size()), result.data(), sizeNeeded);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
} // namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
|
||||||
|
#endif
|
16
src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.h
Normal file
16
src/ModMan/Web/Platform/Windows/PlatformUtilsWindows.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include "Web/Platform/Platform.h"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
{
|
||||||
|
std::string WideStringToString(const std::wstring& wideString);
|
||||||
|
std::wstring StringToWideString(const std::string& string);
|
||||||
|
} // namespace PLATFORM_NAMESPACE_WINDOWS
|
||||||
|
|
||||||
|
#endif
|
@@ -42,7 +42,6 @@ namespace ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn(std::move(param));
|
fn(std::move(param));
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -81,7 +80,6 @@ namespace ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto result = fn(std::move(param));
|
auto result = fn(std::move(param));
|
||||||
|
|
||||||
return nlohmann::json(result).dump();
|
return nlohmann::json(result).dump();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -121,6 +119,7 @@ namespace ui
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn(id, std::move(param));
|
fn(id, std::move(param));
|
||||||
|
return "";
|
||||||
},
|
},
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
#include "GitVersion.h"
|
#include "GitVersion.h"
|
||||||
#include "Web/Edge/AssetHandlerEdge.h"
|
#include "Web/Platform/AssetHandler.h"
|
||||||
#include "Web/Gtk/AssetHandlerGtk.h"
|
|
||||||
#include "Web/UiCommunication.h"
|
#include "Web/UiCommunication.h"
|
||||||
|
#include "Web/Binds/DialogBinds.h"
|
||||||
#include "Web/ViteAssets.h"
|
#include "Web/ViteAssets.h"
|
||||||
#include "Web/WebViewLib.h"
|
#include "Web/WebViewLib.h"
|
||||||
|
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
|
using namespace PLATFORM_NAMESPACE;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@@ -55,6 +56,8 @@ namespace
|
|||||||
w.set_size(1280, 640, WEBVIEW_HINT_NONE);
|
w.set_size(1280, 640, WEBVIEW_HINT_NONE);
|
||||||
w.set_size(480, 320, WEBVIEW_HINT_MIN);
|
w.set_size(480, 320, WEBVIEW_HINT_MIN);
|
||||||
|
|
||||||
|
ui::RegisterDialogHandlerBinds(w);
|
||||||
|
|
||||||
// A binding that counts up or down and immediately returns the new value.
|
// A binding that counts up or down and immediately returns the new value.
|
||||||
ui::Bind<std::string, std::string>(w,
|
ui::Bind<std::string, std::string>(w,
|
||||||
"greet",
|
"greet",
|
||||||
@@ -64,36 +67,12 @@ namespace
|
|||||||
return std::format("Hello from C++ {}!", name);
|
return std::format("Hello from C++ {}!", name);
|
||||||
});
|
});
|
||||||
|
|
||||||
// A binding that counts up or down and immediately returns the new value.
|
|
||||||
ui::Bind(w,
|
|
||||||
"debug",
|
|
||||||
[]()
|
|
||||||
{
|
|
||||||
con::info("Debug");
|
|
||||||
});
|
|
||||||
|
|
||||||
// A binding that creates a new thread and returns the result at a later time.
|
|
||||||
ui::BindAsync(w,
|
|
||||||
"compute",
|
|
||||||
[&](const std::string& id)
|
|
||||||
{
|
|
||||||
// Create a thread and forget about it for the sake of simplicity.
|
|
||||||
std::thread(
|
|
||||||
[&, id]
|
|
||||||
{
|
|
||||||
// Simulate load.
|
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|
||||||
ui::PromiseResolve(w, id, 42);
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
});
|
|
||||||
|
|
||||||
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
|
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
|
||||||
edge::InstallCustomProtocolHandler(w);
|
InstallAssetHandler(w);
|
||||||
constexpr auto urlPrefix = edge::URL_PREFIX;
|
constexpr auto urlPrefix = URL_PREFIX;
|
||||||
#elif defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
|
#elif defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
|
||||||
gtk::InstallCustomProtocolHandler(w);
|
InstallAssetHandler(w);
|
||||||
constexpr auto urlPrefix = gtk::URL_PREFIX;
|
constexpr auto urlPrefix = URL_PREFIX;
|
||||||
#else
|
#else
|
||||||
#error Unsupported platform
|
#error Unsupported platform
|
||||||
#endif
|
#endif
|
||||||
|
@@ -4,6 +4,7 @@ import { webviewBinds, webviewAddEventListener, webviewRemoveEventListener } fro
|
|||||||
|
|
||||||
const greetMsg = ref("");
|
const greetMsg = ref("");
|
||||||
const lastPersonGreeted = ref("");
|
const lastPersonGreeted = ref("");
|
||||||
|
const lastPath = ref("");
|
||||||
const name = ref("");
|
const name = ref("");
|
||||||
|
|
||||||
async function greet() {
|
async function greet() {
|
||||||
@@ -14,6 +15,11 @@ function onPersonGreeted(person: string) {
|
|||||||
lastPersonGreeted.value = person;
|
lastPersonGreeted.value = person;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function onOpenFastfileClick() {
|
||||||
|
lastPath.value =
|
||||||
|
(await webviewBinds.openFileDialog({ filters: [{ name: "Fastfiles", filter: "*.ff" }] })) ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
webviewAddEventListener("greeting", onPersonGreeted);
|
webviewAddEventListener("greeting", onPersonGreeted);
|
||||||
|
|
||||||
onUnmounted(() => webviewRemoveEventListener("greeting", onPersonGreeted));
|
onUnmounted(() => webviewRemoveEventListener("greeting", onPersonGreeted));
|
||||||
@@ -30,6 +36,10 @@ onUnmounted(() => webviewRemoveEventListener("greeting", onPersonGreeted));
|
|||||||
</form>
|
</form>
|
||||||
<p>{{ greetMsg }}</p>
|
<p>{{ greetMsg }}</p>
|
||||||
<p>The last person greeted is: {{ lastPersonGreeted }}</p>
|
<p>The last person greeted is: {{ lastPersonGreeted }}</p>
|
||||||
|
<p>
|
||||||
|
<button @click="onOpenFastfileClick">Open fastfile</button>
|
||||||
|
<span>The last path: {{ lastPath }}</span>
|
||||||
|
</p>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
18
src/ModManUi/src/native/DialogBinds.ts
Normal file
18
src/ModManUi/src/native/DialogBinds.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export interface FileDialogFilterDto {
|
||||||
|
name: string;
|
||||||
|
filter: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OpenFileDialogDto {
|
||||||
|
filters?: FileDialogFilterDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SaveFileDialogDto {
|
||||||
|
filters?: FileDialogFilterDto[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DialogBinds {
|
||||||
|
openFileDialog(options?: OpenFileDialogDto): string | null;
|
||||||
|
saveFileDialog(options?: SaveFileDialogDto): string | null;
|
||||||
|
folderSelectDialog(): string | null;
|
||||||
|
}
|
@@ -1,6 +1,9 @@
|
|||||||
export interface NativeMethods {
|
import type { DialogBinds } from "./DialogBinds";
|
||||||
greet: (name: string) => Promise<string>;
|
|
||||||
}
|
|
||||||
|
export type NativeMethods = {
|
||||||
|
greet(name: string): Promise<string>;
|
||||||
|
} & DialogBinds;
|
||||||
|
|
||||||
interface NativeEventMap {
|
interface NativeEventMap {
|
||||||
greeting: string;
|
greeting: string;
|
@@ -3,7 +3,12 @@
|
|||||||
// Credits to
|
// Credits to
|
||||||
// https://www.kdab.com/jsonify-with-nlohmann-json/
|
// https://www.kdab.com/jsonify-with-nlohmann-json/
|
||||||
|
|
||||||
|
#ifdef HAS_NLOHMANN_JSON
|
||||||
|
|
||||||
|
#pragma warning(push, 0)
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
#pragma warning(pop)
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
// partial specialization (full specialization works too)
|
// partial specialization (full specialization works too)
|
||||||
@@ -56,3 +61,5 @@ namespace nlohmann
|
|||||||
{ \
|
{ \
|
||||||
NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(EXTEND_JSON_FROM, __VA_ARGS__)) \
|
NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(EXTEND_JSON_FROM, __VA_ARGS__)) \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
3
thirdparty/json.lua
vendored
3
thirdparty/json.lua
vendored
@@ -3,7 +3,8 @@ json = {}
|
|||||||
function json:include(includes)
|
function json:include(includes)
|
||||||
if includes:handle(self:name()) then
|
if includes:handle(self:name()) then
|
||||||
defines {
|
defines {
|
||||||
"JSON_DIAGNOSTICS=1"
|
"JSON_DIAGNOSTICS=1",
|
||||||
|
"HAS_NLOHMANN_JSON"
|
||||||
}
|
}
|
||||||
includedirs {
|
includedirs {
|
||||||
path.join(ThirdPartyFolder(), "json", "single_include")
|
path.join(ThirdPartyFolder(), "json", "single_include")
|
||||||
|
Reference in New Issue
Block a user