diff --git a/.gitignore b/.gitignore index f49eff3..797d0bc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build .vs out example/*/dist +temp.log \ No newline at end of file diff --git a/example/hello_world/CMakeLists.txt b/example/hello_world/CMakeLists.txt index 535ab64..28905b2 100644 --- a/example/hello_world/CMakeLists.txt +++ b/example/hello_world/CMakeLists.txt @@ -6,6 +6,8 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_PREFIX_PATH "C:\\Users\\agniv\\Documents\\sourcemeta\\native\\build\\dist") + find_package(Native REQUIRED) native_add_app( diff --git a/example/hello_world/hello_world.cc b/example/hello_world/hello_world.cc index 7da341d..c268fb3 100644 --- a/example/hello_world/hello_world.cc +++ b/example/hello_world/hello_world.cc @@ -1,3 +1,21 @@ +////////////// temporary block -- for logging purpose /////////////////// +#include +#include + +// Open a log file in append mode +static std::ofstream log_file("temp.log", std::ios::app); + +// Helper struct to redirect std::cout and std::cerr +struct LogRedirector { + LogRedirector() { + std::cout.rdbuf(log_file.rdbuf()); + std::cerr.rdbuf(log_file.rdbuf()); + } +}; +// Create a static instance so redirection happens at startup. +static LogRedirector logRedirector; +////////////// temporary block -- for logging purpose /////////////////// + #include #include #include @@ -19,7 +37,7 @@ class App : public sourcemeta::native::Application { // webview.load_url("https://sourcemeta.com"); window.add(webview); - this->exit(); + // this->exit(); } auto on_error(std::exception_ptr) noexcept -> void override {} diff --git a/example/hello_world/index.html b/example/hello_world/index.html index e488877..f0e629b 100644 --- a/example/hello_world/index.html +++ b/example/hello_world/index.html @@ -11,6 +11,23 @@

Welcome to Native

This is a simple example to demonstrate loading HTML with CSS styling.

+ +

Waiting for message...

+ + + + + + diff --git a/src/ui/webview/include/sourcemeta/native/webview.h b/src/ui/webview/include/sourcemeta/native/webview.h index 0d66f30..7470317 100644 --- a/src/ui/webview/include/sourcemeta/native/webview.h +++ b/src/ui/webview/include/sourcemeta/native/webview.h @@ -24,9 +24,8 @@ class WebView { auto load_html(const std::string &html_path) -> void; // IPC messaging - // auto send_message(const std::string& channel, const std::string& message) - // -> void; auto on_message(const std::string& channel, void (*callback)(const - // std::string&)) -> void; + auto send_message(const std::string& channel, const std::string& message) -> void; + auto on_message(const std::string& channel, void (*callback)(const std::string&)) -> void; // Size control auto resize() -> void; diff --git a/src/ui/webview/win32/webview_win32.cc b/src/ui/webview/win32/webview_win32.cc index 789a0ab..0473c3a 100644 --- a/src/ui/webview/win32/webview_win32.cc +++ b/src/ui/webview/win32/webview_win32.cc @@ -37,6 +37,9 @@ class WebView::Internal { std::optional url; std::optional html_filename; + std::unordered_map> message_callbacks; + bool message_handler_registered{false}; + // Idealy, we would prefer to have a generic type as parent as in the future // WebView could be the child of a Container class, mixed with others UI // classes. @@ -83,6 +86,12 @@ class WebView::Internal { // Set internals to ready this->ready = true; + // The following setting steps is redundant since the values are the default settings + ComPtr settings; + this->webview->get_Settings(&settings); + // settings->put_IsScriptEnabled(TRUE); + // settings->put_AreDefaultScriptDialogsEnabled(TRUE); + settings->put_IsWebMessageEnabled(TRUE); callback(); return S_OK; }) @@ -104,6 +113,52 @@ class WebView::Internal { .Get()); } + // Registers the WebMessageReceived event handler + auto register_message_handler() -> void { + if (message_handler_registered) return; + message_handler_registered = true; + this->webview->add_WebMessageReceived(Callback( + [](ICoreWebView2 *sender, ICoreWebView2WebMessageReceivedEventArgs *args) -> HRESULT { + LPWSTR message_raw; + + args->TryGetWebMessageAsString(&message_raw); + std::wstring w_message(message_raw); + + std::string message_str(w_message.begin(), w_message.end()); + std::cout << "Value of message_str: " << message_str << std::endl; + + try { + // Parse message_str here + } catch (std::exception &e) { + std::cerr << "IPC JSON parse error: " << e.what() << std::endl; + } + sender->PostWebMessageAsString(w_message.c_str()); + return S_OK; + }).Get(), nullptr); + } + + auto send_message(const std::string &channel, const std::string &message) -> void { + if (!this->webview) { + std::cout << "webview is not ready yet!" << std::endl; + return; + } + std::wstring w_channel(channel.begin(), channel.end()); + std::wstring w_message(message.begin(), message.end()); + std::wstring json_data = L"{\"channel\": \"" + w_channel + L"\", \"message\": \"" + w_message + L"\"}"; + + this->webview->PostWebMessageAsString(json_data.c_str()); + } + + auto on_message(const std::string &channel, void (*callback)(const std::string &)) -> void { + message_callbacks[channel] = callback; + + if (!this->webview) { + std::cout << "webview is not ready yet!" << std::endl; + return; + } + register_message_handler(); + } + private: HWND *parent_; }; @@ -154,6 +209,13 @@ auto WebView::attach_to(sourcemeta::native::Window &window) -> void { } else if (internal->html_filename.has_value()) { internal->navigate_to_html(); } + std::cout << "internally calling send_message" << std::endl; + internal->send_message("test_channel", "this is data from native code"); + + std::cout << "internally calling on_message" << std::endl; + internal->on_message("test_channel", [](const std::string& msg){ + std::cout << "inside on_message" << std::endl; + }); }); }