From 49295448c87519ee767afdfc72233ccf9af5e8fc Mon Sep 17 00:00:00 2001 From: Jan Laupetin Date: Tue, 7 Oct 2025 23:26:46 +0100 Subject: [PATCH] feat: add nlohmann json based wrappers for webview binds,notify --- src/ModMan.lua | 3 + src/ModMan/Web/Edge/AssetHandlerEdge.cpp | 9 +- src/ModMan/Web/Edge/AssetHandlerEdge.h | 4 +- src/ModMan/Web/Gtk/AssetHandlerGtk.cpp | 10 +- src/ModMan/Web/Gtk/AssetHandlerGtk.h | 5 +- src/ModMan/Web/UiAssets.cpp | 45 +++---- src/ModMan/Web/UiAssets.h | 7 +- src/ModMan/Web/UiCommunication.cpp | 0 src/ModMan/Web/UiCommunication.h | 142 +++++++++++++++++++++++ src/ModMan/Web/WebViewLib.h | 20 ++++ src/ModMan/main.cpp | 83 +++++++------ 11 files changed, 241 insertions(+), 87 deletions(-) create mode 100644 src/ModMan/Web/UiCommunication.cpp create mode 100644 src/ModMan/Web/UiCommunication.h create mode 100644 src/ModMan/Web/WebViewLib.h diff --git a/src/ModMan.lua b/src/ModMan.lua index 1de3aa1a..c160f700 100644 --- a/src/ModMan.lua +++ b/src/ModMan.lua @@ -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 diff --git a/src/ModMan/Web/Edge/AssetHandlerEdge.cpp b/src/ModMan/Web/Edge/AssetHandlerEdge.cpp index a1e7b4b8..7cbdf961 100644 --- a/src/ModMan/Web/Edge/AssetHandlerEdge.cpp +++ b/src/ModMan/Web/Edge/AssetHandlerEdge.cpp @@ -2,11 +2,6 @@ #if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) -#pragma warning(push, 0) -#include -#include -#pragma warning(pop) - #include "Web/UiAssets.h" #include @@ -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(wv.browser_controller().value()); Microsoft::WRL::ComPtr core; diff --git a/src/ModMan/Web/Edge/AssetHandlerEdge.h b/src/ModMan/Web/Edge/AssetHandlerEdge.h index 5154ff63..fb01197b 100644 --- a/src/ModMan/Web/Edge/AssetHandlerEdge.h +++ b/src/ModMan/Web/Edge/AssetHandlerEdge.h @@ -4,9 +4,7 @@ #if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE) -#pragma warning(push, 0) -#include -#pragma warning(pop) +#include "Web/WebViewLib.h" namespace edge { diff --git a/src/ModMan/Web/Gtk/AssetHandlerGtk.cpp b/src/ModMan/Web/Gtk/AssetHandlerGtk.cpp index e02cc85f..3cec4a45 100644 --- a/src/ModMan/Web/Gtk/AssetHandlerGtk.cpp +++ b/src/ModMan/Web/Gtk/AssetHandlerGtk.cpp @@ -2,12 +2,6 @@ #if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#include -#include -#pragma GCC diagnostic pop - #include "Web/UiAssets.h" #include @@ -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); } diff --git a/src/ModMan/Web/Gtk/AssetHandlerGtk.h b/src/ModMan/Web/Gtk/AssetHandlerGtk.h index 6f41d01f..8a399dff 100644 --- a/src/ModMan/Web/Gtk/AssetHandlerGtk.h +++ b/src/ModMan/Web/Gtk/AssetHandlerGtk.h @@ -4,10 +4,7 @@ #if defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#include -#pragma GCC diagnostic pop +#include "Web/WebViewLib.h" namespace gtk { diff --git a/src/ModMan/Web/UiAssets.cpp b/src/ModMan/Web/UiAssets.cpp index cd34c030..bf5c658d 100644 --- a/src/ModMan/Web/UiAssets.cpp +++ b/src/ModMan/Web/UiAssets.cpp @@ -2,30 +2,33 @@ #include -std::unordered_map BuildUiFileLookup() +namespace ui { - std::unordered_map result; - - for (const auto& asset : MOD_MAN_UI_FILES) + std::unordered_map BuildUiFileLookup() { - result.emplace(std::format("/{}", asset.filename), asset); + std::unordered_map 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 diff --git a/src/ModMan/Web/UiAssets.h b/src/ModMan/Web/UiAssets.h index 13e838a2..ea7e185b 100644 --- a/src/ModMan/Web/UiAssets.h +++ b/src/ModMan/Web/UiAssets.h @@ -5,5 +5,8 @@ #include #include -std::unordered_map BuildUiFileLookup(); -const char* GetMimeTypeForFileName(const std::string& fileName); +namespace ui +{ + std::unordered_map BuildUiFileLookup(); + const char* GetMimeTypeForFileName(const std::string& fileName); +} // namespace ui diff --git a/src/ModMan/Web/UiCommunication.cpp b/src/ModMan/Web/UiCommunication.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/ModMan/Web/UiCommunication.h b/src/ModMan/Web/UiCommunication.h new file mode 100644 index 00000000..df0935ba --- /dev/null +++ b/src/ModMan/Web/UiCommunication.h @@ -0,0 +1,142 @@ +#pragma once + +#include "Utils/Logging/Log.h" +#include "WebViewLib.h" + +#pragma warning(push, 0) +#include +#pragma warning(pop) + +namespace ui +{ + inline void Bind(webview::webview& wv, const std::string& name, std::function fn) + { + wv.bind(name, + [fn](const std::string& req) -> std::string + { + fn(); + return ""; + }); + } + + template void Bind(webview::webview& wv, const std::string& name, std::function 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(); + } + catch (const nlohmann::json::exception& e) + { + con::error("Failed to parse json of webview call: {}", e.what()); + return ""; + } + + fn(std::move(param)); + + return ""; + }); + } + + template void BindRetOnly(webview::webview& wv, const std::string& name, std::function fn) + { + wv.bind(name, + [fn](const std::string& req) -> std::string + { + auto result = fn(); + + return nlohmann::json(result).dump(); + }); + } + + template void Bind(webview::webview& wv, const std::string& name, std::function 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(); + } + 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 fn) + { + wv.bind( + name, + [fn](const std::string& id, const std::string& req, void* /* arg */) + { + fn(id); + }, + nullptr); + } + + template void BindAsync(webview::webview& wv, const std::string& name, std::function 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(); + } + 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 void PromiseResolve(webview::webview& wv, const std::string& id, const TPayload& payload) + { + wv.resolve(id, 0, nlohmann::json(payload).dump()); + } + + template void PromiseReject(webview::webview& wv, const std::string& id, const TPayload& payload) + { + wv.resolve(id, 1, nlohmann::json(payload).dump()); + } + + template void Notify(webview::webview& wv, const std::string& eventKey, const TPayload& payload) + { + wv.notify(eventKey, nlohmann::json(payload).dump()); + } +} // namespace ui diff --git a/src/ModMan/Web/WebViewLib.h b/src/ModMan/Web/WebViewLib.h new file mode 100644 index 00000000..5a8fd0b0 --- /dev/null +++ b/src/ModMan/Web/WebViewLib.h @@ -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 + +#ifdef _MSC_VER +#pragma warning(pop) +#else +#pragma GCC diagnostic pop +#endif + +#ifdef ERROR +#undef ERROR +#endif diff --git a/src/ModMan/main.cpp b/src/ModMan/main.cpp index bf39ad4d..1929ef9c 100644 --- a/src/ModMan/main.cpp +++ b/src/ModMan/main.cpp @@ -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 #include @@ -31,8 +19,10 @@ namespace #ifdef _DEBUG std::optional 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(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;