2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2025-10-09 08:16:41 +00:00

feat: add nlohmann json based wrappers for webview binds,notify

This commit is contained in:
Jan Laupetin
2025-10-07 23:26:46 +01:00
parent cf584fe3eb
commit 49295448c8
11 changed files with 241 additions and 87 deletions

View File

@@ -46,8 +46,11 @@ function ModMan:project()
filter {}
self:include(includes)
Utils:include(includes)
json:include(includes)
webview:include(includes)
links:linkto(Utils)
links:linkto(webview)
links:linkall()
end

View File

@@ -2,11 +2,6 @@
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
#pragma warning(push, 0)
#include <webview/macros.h>
#include <webview/webview.h>
#pragma warning(pop)
#include "Web/UiAssets.h"
#include <Windows.h>
@@ -55,7 +50,7 @@ namespace
std::wstringstream wss;
wss << std::format(L"Content-Length: {}\n", contentLength);
wss << L"Content-Type: " << StringToWideString(GetMimeTypeForFileName(assetName));
wss << L"Content-Type: " << StringToWideString(ui::GetMimeTypeForFileName(assetName));
return wss.str();
}
@@ -151,7 +146,7 @@ namespace edge
{
void InstallCustomProtocolHandler(webview::webview& wv)
{
assetLookup = BuildUiFileLookup();
assetLookup = ui::BuildUiFileLookup();
const auto controller = static_cast<ICoreWebView2Controller*>(wv.browser_controller().value());
Microsoft::WRL::ComPtr<ICoreWebView2> core;

View File

@@ -4,9 +4,7 @@
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
#pragma warning(push, 0)
#include <webview/webview.h>
#pragma warning(pop)
#include "Web/WebViewLib.h"
namespace edge
{

View File

@@ -2,12 +2,6 @@
#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include <webview/macros.h>
#include <webview/webview.h>
#pragma GCC diagnostic pop
#include "Web/UiAssets.h"
#include <format>
@@ -27,7 +21,7 @@ namespace
gsize stream_length = foundUiFile->second.dataSize;
GInputStream* stream = g_memory_input_stream_new_from_data(foundUiFile->second.data, foundUiFile->second.dataSize, nullptr);
webkit_uri_scheme_request_finish(request, stream, stream_length, GetMimeTypeForFileName(foundUiFile->second.filename));
webkit_uri_scheme_request_finish(request, stream, stream_length, ui::GetMimeTypeForFileName(foundUiFile->second.filename));
g_object_unref(stream);
}
else
@@ -48,7 +42,7 @@ namespace gtk
const auto webView = WEBKIT_WEB_VIEW(widget);
const auto context = webkit_web_view_get_context(webView);
assetLookup = BuildUiFileLookup();
assetLookup = ui::BuildUiFileLookup();
webkit_web_context_register_uri_scheme(context, "modman", ModManUriSchemeRequestCb, NULL, nullptr);
}

View File

@@ -4,10 +4,7 @@
#if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include <webview/webview.h>
#pragma GCC diagnostic pop
#include "Web/WebViewLib.h"
namespace gtk
{

View File

@@ -2,30 +2,33 @@
#include <format>
std::unordered_map<std::string, UiFile> BuildUiFileLookup()
namespace ui
{
std::unordered_map<std::string, UiFile> result;
for (const auto& asset : MOD_MAN_UI_FILES)
std::unordered_map<std::string, UiFile> BuildUiFileLookup()
{
result.emplace(std::format("/{}", asset.filename), asset);
std::unordered_map<std::string, UiFile> result;
for (const auto& asset : MOD_MAN_UI_FILES)
{
result.emplace(std::format("/{}", asset.filename), asset);
}
return result;
}
return result;
}
const char* GetMimeTypeForFileName(const std::string& fileName)
{
const char* mimeType;
const char* GetMimeTypeForFileName(const std::string& fileName)
{
const char* mimeType;
if (fileName.ends_with(".html"))
mimeType = "text/html";
else if (fileName.ends_with(".js"))
mimeType = "text/javascript";
else if (fileName.ends_with(".css"))
mimeType = "text/css";
else
mimeType = "application/octet-stream";
if (fileName.ends_with(".html"))
mimeType = "text/html";
else if (fileName.ends_with(".js"))
mimeType = "text/javascript";
else if (fileName.ends_with(".css"))
mimeType = "text/css";
else
mimeType = "application/octet-stream";
return mimeType;
}
return mimeType;
}
} // namespace ui

View File

@@ -5,5 +5,8 @@
#include <string>
#include <unordered_map>
std::unordered_map<std::string, UiFile> BuildUiFileLookup();
const char* GetMimeTypeForFileName(const std::string& fileName);
namespace ui
{
std::unordered_map<std::string, UiFile> BuildUiFileLookup();
const char* GetMimeTypeForFileName(const std::string& fileName);
} // namespace ui

View File

View File

@@ -0,0 +1,142 @@
#pragma once
#include "Utils/Logging/Log.h"
#include "WebViewLib.h"
#pragma warning(push, 0)
#include <nlohmann/json.hpp>
#pragma warning(pop)
namespace ui
{
inline void Bind(webview::webview& wv, const std::string& name, std::function<void()> fn)
{
wv.bind(name,
[fn](const std::string& req) -> std::string
{
fn();
return "";
});
}
template<typename TInput> void Bind(webview::webview& wv, const std::string& name, std::function<void(TInput)> fn)
{
wv.bind(name,
[fn](const std::string& req) -> std::string
{
TInput param;
try
{
const auto json = nlohmann::json::parse(req);
if (!json.is_array())
{
con::error("Webview params are not an array: {}", req);
return "";
}
param = json.at(0).get<TInput>();
}
catch (const nlohmann::json::exception& e)
{
con::error("Failed to parse json of webview call: {}", e.what());
return "";
}
fn(std::move(param));
return "";
});
}
template<typename TReturn> void BindRetOnly(webview::webview& wv, const std::string& name, std::function<TReturn()> fn)
{
wv.bind(name,
[fn](const std::string& req) -> std::string
{
auto result = fn();
return nlohmann::json(result).dump();
});
}
template<typename TInput, typename TReturn> void Bind(webview::webview& wv, const std::string& name, std::function<TReturn(TInput)> fn)
{
wv.bind(name,
[fn](const std::string& req) -> std::string
{
TInput param;
try
{
const auto json = nlohmann::json::parse(req);
if (!json.is_array())
{
con::error("Webview params are not an array: {}", req);
return "";
}
param = json.at(0).get<TInput>();
}
catch (const nlohmann::json::exception& e)
{
con::error("Failed to parse json of webview call: {}", e.what());
return "";
}
auto result = fn(std::move(param));
return nlohmann::json(result).dump();
});
}
inline void BindAsync(webview::webview& wv, const std::string& name, std::function<void(const std::string& id)> fn)
{
wv.bind(
name,
[fn](const std::string& id, const std::string& req, void* /* arg */)
{
fn(id);
},
nullptr);
}
template<typename TInput> void BindAsync(webview::webview& wv, const std::string& name, std::function<void(const std::string& id, TInput)> fn)
{
wv.bind(
name,
[fn](const std::string& id, const std::string& req, void* /* arg */)
{
TInput param;
try
{
const auto json = nlohmann::json::parse(req);
if (!json.is_array())
{
con::error("Webview params are not an array: {}", req);
return "";
}
param = json.at(0).get<TInput>();
}
catch (const nlohmann::json::exception& e)
{
con::error("Failed to parse json of webview call: {}", e.what());
return "";
}
fn(id, std::move(param));
},
nullptr);
}
template<typename TPayload> void PromiseResolve(webview::webview& wv, const std::string& id, const TPayload& payload)
{
wv.resolve(id, 0, nlohmann::json(payload).dump());
}
template<typename TPayload> void PromiseReject(webview::webview& wv, const std::string& id, const TPayload& payload)
{
wv.resolve(id, 1, nlohmann::json(payload).dump());
}
template<typename TPayload> void Notify(webview::webview& wv, const std::string& eventKey, const TPayload& payload)
{
wv.notify(eventKey, nlohmann::json(payload).dump());
}
} // namespace ui

View File

@@ -0,0 +1,20 @@
#pragma once
#ifdef _MSC_VER
#pragma warning(push, 0)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include <webview/webview.h>
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
#ifdef ERROR
#undef ERROR
#endif

View File

@@ -1,21 +1,9 @@
#ifdef _MSC_VER
#pragma warning(push, 0)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
#include "webview/webview.h"
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
#include "GitVersion.h"
#include "Web/Edge/AssetHandlerEdge.h"
#include "Web/Gtk/AssetHandlerGtk.h"
#include "Web/UiCommunication.h"
#include "Web/ViteAssets.h"
#include "Web/WebViewLib.h"
#include <chrono>
#include <format>
@@ -31,8 +19,10 @@ namespace
#ifdef _DEBUG
std::optional<webview::webview> devToolWindow;
void RunDevToolsWindow(webview::webview& parent)
void RunDevToolsWindow()
{
con::debug("Creating dev tools window");
try
{
auto& newWindow = devToolWindow.emplace(false, nullptr);
@@ -50,6 +40,8 @@ namespace
int RunMainWindow()
{
con::debug("Creating main window");
try
{
webview::webview w(
@@ -64,32 +56,37 @@ namespace
w.set_size(480, 320, WEBVIEW_HINT_MIN);
// A binding that counts up or down and immediately returns the new value.
w.bind("greet",
[&](const std::string& req) -> std::string
{
const auto name = req.substr(2, req.size() - 4);
w.notify("greeting", webview::json_escape(name));
return webview::json_escape(std::format("Hello from C++ {}!", name));
});
ui::Bind<std::string, std::string>(w,
"greet",
[&w](std::string name) -> std::string
{
ui::Notify(w, "greeting", 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.
w.bind(
"compute",
[&](const std::string& id, const std::string& req, void* /*arg*/)
{
// Create a thread and forget about it for the sake of simplicity.
std::thread(
[&, id, req]
{
// Simulate load.
std::this_thread::sleep_for(std::chrono::seconds(1));
// Imagine that req is properly parsed or use your own JSON parser.
const auto* result = "42";
w.resolve(id, 0, result);
})
.detach();
},
nullptr);
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)
edge::InstallCustomProtocolHandler(w);
@@ -107,9 +104,9 @@ namespace
if (VITE_DEV_SERVER)
{
w.dispatch(
[&w]
[]
{
RunDevToolsWindow(w);
RunDevToolsWindow();
});
}
#else
@@ -135,6 +132,8 @@ int main()
{
#endif
con::info("Starting ModMan " GIT_VERSION);
const auto result = RunMainWindow();
return result;