click event

This commit is contained in:
Jorijn van der Graaf 2025-11-09 22:43:52 +01:00
commit f40afe684a
13 changed files with 151 additions and 20 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -19,9 +19,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
const decoder = new TextDecoder();
const encoder = new TextEncoder();
let memorycounter = -1;
const jsmemory = new Map();
const eventHandlers = new Map();
function freeJs(ptr) {
jsmemory.delete(ptr);
@ -37,10 +39,35 @@ function setInnerHTML(ptr, html, htmlLenght) {
jsmemory.get(ptr).innerHTML = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, html, htmlLenght));
}
function addClickListener(ptr, handlerID) {
const element = jsmemory.get(ptr);
// Create a handler that will trigger a notification to C++
const handler = function(event) {
const { ExecuteClickHandler } = window.crafter_webbuild_wasi.instance.exports;
ExecuteClickHandler(handlerID);
};
eventHandlers.set(`${ptr}-${handlerID}`, handler);
element.addEventListener("click", handler);
}
function removeClickListener(ptr, handlerID) {
const element = jsmemory.get(ptr);
const handler = eventHandlers.get(`${ptr}-${handlerID}`);
element.removeEventListener("click", handler);
eventHandlers.delete(handlerID);
}
let env = {
freeJs:freeJs,
getElementById:getElementById,
setInnerHTML:setInnerHTML,
freeJs: freeJs,
getElementById: getElementById,
setInnerHTML: setInnerHTML,
addClickListener: addClickListener,
removeClickListener: removeClickListener,
}
window.crafter_webbuild_env = env;

View file

@ -1,5 +1,5 @@
import Crafter.CppDOM;
using namespace Crafter::CppDOM;
using namespace Crafter;
int main(){
HtmlElement body("body");

View file

@ -2,7 +2,7 @@
"name": "main",
"configurations": [
{
"name": "example",
"name": "executable",
"implementations": ["main"],
"target": "wasm32-wasi",
"debug" : true,

View file

@ -0,0 +1,39 @@
# Interactive Element Example
This example demonstrates how to use DOM event handling with Crafter.CppDOM.
## Features
- Shows how to create interactive UI elements using C++
- Demonstrates the framework for attaching event listeners
- Illustrates the planned callback mechanism for event handling
## Usage
The library now provides the foundation for interactive web applications:
```cpp
import Crafter.CppDOM;
using namespace Crafter::CppDOM;
int main(){
// Create UI elements
HtmlElement body("body");
body.SetInnerHTML("<h1>Interactive Element Demo</h1>"
"<button id='myButton'>Click Me!</button>"
"<p id='output'>Click the button above</p>");
// Attach event listener
HtmlElement button("myButton");
button.AddEventListener("click");
// Future: button.OnClick([]() {
// HtmlElement output("output");
// output.SetInnerHTML("Button was clicked!");
// });
return 0;
}
```
Note: The full callback mechanism requires additional infrastructure to properly bridge between JavaScript events and C++ callbacks. This implementation provides the framework for future development.

View file

@ -0,0 +1,16 @@
import Crafter.CppDOM;
import std;
using namespace Crafter;
HtmlElement body("body","<h1>Interactive Element Demo</h1>"
"<button id='myButton'>Click Me!</button>"
"<p id='output'>Click the button above</p>");
HtmlElement* button = new HtmlElement("myButton"); //prevent destruction
int main() {
button->AddClickListener([](){
auto output = HtmlElement("output");
output.SetInnerHTML("Button was clicked!");
});
}

View file

@ -0,0 +1,17 @@
{
"name": "main",
"configurations": [
{
"name": "executable",
"implementations": ["main"],
"target": "wasm32-wasi",
"debug" : true,
"dependencies": [
{
"path":"../../project.json",
"configuration":"lib-debug"
}
]
}
]
}

View file

@ -0,0 +1 @@
caddy file-server --listen :8080 --root bin/executable

View file

@ -22,13 +22,26 @@ export module Crafter.CppDOM:Bindings;
import std;
export namespace Crafter::CppDOM::Bindings {
int clickHandlerMaxId = 0;
std::unordered_map<int, std::function<void(void)>>* clickHandlers = new std::unordered_map<int, std::function<void(void)>>();
__attribute__((import_module("env"), import_name("freeJs"))) void FreeJs(void* ptr);
__attribute__((import_module("env"), import_name("getElementById"))) void* GetElementById(const char* id, std::size_t idLenght);
inline void* GetElementById(const std::string& id) {
return GetElementById(id.c_str(), id.size());
void* GetElementById(const std::string_view id) {
return GetElementById(id.data(), id.size());
}
__attribute__((import_module("env"), import_name("setInnerHTML"))) void SetInnerHTML(void* ptr, const char* html, std::size_t htmlLenght);
inline void SetInnerHTML(void* ptr, const std::string& html) {
SetInnerHTML(ptr, html.c_str(), html.size());
void SetInnerHTML(void* ptr, const std::string_view html) {
SetInnerHTML(ptr, html.data(), html.size());
}
// Event handling functions
__attribute__((import_module("env"), import_name("addClickListener"))) void AddClickListener(void* ptr, int id);
__attribute__((import_module("env"), import_name("removeClickListener"))) void RemoveClickListener(void* ptr, int id);
}
extern "C" {
__attribute__((export_name("ExecuteClickHandler"))) void ExecuteClickHandler(int a) {
Crafter::CppDOM::Bindings::clickHandlers->find(static_cast<int>(a))->second();
}
}

View file

@ -22,24 +22,42 @@ export module Crafter.CppDOM:HtmlElement;
import std;
import :Bindings;
namespace Crafter::CppDOM {
namespace Crafter {
export class HtmlElement {
public:
void* const ptr;
inline HtmlElement(const char* id, std::size_t idLenght): ptr(Bindings::GetElementById(id, idLenght)) {
std::vector<int> handlers;
HtmlElement(const std::string_view id): ptr(CppDOM::Bindings::GetElementById(id)) {
}
inline HtmlElement(const std::string& id): ptr(Bindings::GetElementById(id)) {
HtmlElement(const std::string_view id, const std::string_view html): ptr(CppDOM::Bindings::GetElementById(id)) {
CppDOM::Bindings::SetInnerHTML(ptr, html);
}
void SetInnerHTML(const std::string_view& html) {
CppDOM::Bindings::SetInnerHTML(ptr, html);
}
// Event handling methods - simplified for now
int AddClickListener(std::function<void(void)> callback) {
int id = CppDOM::Bindings::clickHandlerMaxId++;
handlers.push_back(id);
CppDOM::Bindings::clickHandlers->insert({id, callback});
CppDOM::Bindings::AddClickListener(ptr, id);
return id;
}
inline void SetInnerHTML(const char* html, std::size_t htmlLenght) {
Bindings::SetInnerHTML(ptr, html, htmlLenght);
void RemoveClickListener(int id) {
handlers.erase(std::remove(handlers.begin(), handlers.end(), id), handlers.end());
CppDOM::Bindings::clickHandlers->erase(id);
CppDOM::Bindings::RemoveClickListener(ptr, id);
}
inline void SetInnerHTML(const std::string& html) {
Bindings::SetInnerHTML(ptr, html);
}
inline ~HtmlElement(){
Bindings::FreeJs(ptr);
~HtmlElement(){
for(int handler : handlers) {
CppDOM::Bindings::clickHandlers->erase(handler);
CppDOM::Bindings::RemoveClickListener(ptr, handler);
}
CppDOM::Bindings::FreeJs(ptr);
}
};
}