diff --git a/README.md b/README.md index 18318fb..c7bcc99 100644 --- a/README.md +++ b/README.md @@ -102,63 +102,4 @@ int main(){ } ``` -This feature allows you to make HTTP requests directly from C++ code running in WebAssembly, which can be useful for communicating with APIs or backend services. - -# Location Pathname Access - -The library now provides access to the current page's pathname through the `GetPathNameString()` function: - -```cpp -import Crafter.CppDOM; -using namespace Crafter::CppDOMBindings; - -int main(){ - // Get the current page's pathname - std::string path = GetPathNameString(); - - // Use the path - HtmlElementView body("body"); - body.SetInnerHTML("Current path: " + path); -} -``` - -# PopState Event Handling - -The library supports handling the `popstate` event for navigation history changes: - -```cpp -import Crafter.CppDOM; -using namespace Crafter::CppDOMBindings; - -int main(){ - // Add a listener for popstate events - auto popStateId = AddPopStateListener([]() { - // This will be called when the user navigates back/forward - std::string path = GetPathNameString(); - HtmlElementView body("body"); - body.SetInnerHTML("Navigated to: " + path); - }); - - // Later, remove the listener if needed - // RemovePopStateListener(popStateId); -} -``` - -# History PushState Functionality - -The library provides access to the `history.pushState` API for manipulating browser history: - -```cpp -import Crafter.CppDOM; -using namespace Crafter::CppDOMBindings; - -int main(){ - // Push a new state to the browser history - PushState("{\"page\": \"about\"}", "About Page", "/about"); - - // This will modify the browser URL without reloading the page - // and can be combined with popstate event handling -} -``` - -These features allow you to build single-page applications with proper URL handling and navigation state management. \ No newline at end of file +This feature allows you to make HTTP requests directly from C++ code running in WebAssembly, which can be useful for communicating with APIs or backend services. \ No newline at end of file diff --git a/additional/env.js b/additional/env.js index c6500f8..8e84382 100644 --- a/additional/env.js +++ b/additional/env.js @@ -661,47 +661,6 @@ let env = { const element = jsmemory.get(ptr); const val = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, value, valueLength)); element.value = val; - }, getValue: function(ptr) { - const element = jsmemory.get(ptr); - if(element.value) { - return writeStringToWasm(element.value || ""); - } else { - return 0; - } - }, - setValue: function(ptr, value, valueLength) { - const element = jsmemory.get(ptr); - const val = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, value, valueLength)); - element.value = val; - }, - getPathName: function() { - return writeStringToWasm(window.location.pathname); - }, - addPopStateListener: function(handlerID) { - const handler = function(event) { - const { ExecutePopStateHandler } = window.crafter_webbuild_wasi.instance.exports; - ExecutePopStateHandler(handlerID); - }; - - window.addEventListener('popstate', handler); - - // Store handler for potential cleanup - eventHandlers.set(`popstate-${handlerID}`, handler); - }, - removePopStateListener: function(handlerID) { - const handler = eventHandlers.get(`popstate-${handlerID}`); - - if (handler) { - window.removeEventListener('popstate', handler); - eventHandlers.delete(`popstate-${handlerID}`); - } - }, - pushState: function(data, dataLength, title, titleLength, url, urlLength) { - const dataStr = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, data, dataLength)); - const titleStr = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, title, titleLength)); - const urlStr = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, url, urlLength)); - - window.history.pushState(JSON.parse(dataStr), titleStr, urlStr); }, addClickListener: addClickListener, removeClickListener: removeClickListener, diff --git a/examples/AllEventHandling/main.cpp b/examples/AllEventHandling/main.cpp index 5c2baf7..8a8778a 100644 --- a/examples/AllEventHandling/main.cpp +++ b/examples/AllEventHandling/main.cpp @@ -1,26 +1,193 @@ import Crafter.CppDOM; -using namespace Crafter::CppDOM; -using namespace Crafter::CppDOMBindings; +using namespace Crafter; import std; +// Create the main container element +HtmlElementView* body = new HtmlElementView("body", "
Click me!
"); - div.SetStyle("padding: 20px; background-color: lightblue; cursor: pointer;"); - - // Add click handler - div.AddClickListener([](Crafter::MouseEvent e) { - std::cout << "Clicked!" << std::endl; + // Mouse Events + mouseButton->AddClickListener([&](MouseEvent event) { + mouseOutput->SetInnerHTML(std::format("Click: X={}, Y={}
", event.clientX, event.clientY)); }); - - body.AddChild(div); - - // Demonstrate new bindings - std::string path = GetPathNameString(); - std::cout << "Current path: " << path << std::endl; - + + mouseButton->AddMouseOverListener([&](MouseEvent event) { + mouseOutput->SetInnerHTML(std::format("Mouse Over: X={}, Y={}
", event.clientX, event.clientY)); + }); + + mouseButton->AddMouseOutListener([&](MouseEvent event) { + mouseOutput->SetInnerHTML(std::format("Mouse Out: X={}, Y={}
", event.clientX, event.clientY)); + }); + + mouseButton->AddMouseMoveListener([&](MouseEvent event) { + mouseOutput->SetInnerHTML(std::format("Mouse Move: X={}, Y={}
", event.clientX, event.clientY)); + }); + + mouseButton->AddMouseDownListener([&](MouseEvent event) { + mouseOutput->SetInnerHTML(std::format("Mouse Down: Button={}, X={}, Y={}
", event.button, event.clientX, event.clientY)); + }); + + mouseButton->AddMouseUpListener([&](MouseEvent event) { + mouseOutput->SetInnerHTML(std::format("Mouse Up: Button={}, X={}, Y={}
", event.button, event.clientX, event.clientY)); + }); + + // Keyboard Events + keyInput->AddKeyDownListener([&](KeyboardEvent event) { + std::string keyInfo = std::format("Key Down: Key='{}', Code={}, Ctrl={}, Shift={}, Alt={}
", + event.key, event.keyCode, event.ctrlKey, event.shiftKey, event.altKey); + keyOutput->SetInnerHTML(keyInfo); + }); + + keyInput->AddKeyUpListener([&](KeyboardEvent event) { + std::string keyInfo = std::format("Key Up: Key='{}', Code={}, Ctrl={}, Shift={}, Alt={}
", + event.key, event.keyCode, event.ctrlKey, event.shiftKey, event.altKey); + keyOutput->SetInnerHTML(keyInfo); + }); + + keyInput->AddKeyPressListener([&](KeyboardEvent event) { + std::string keyInfo = std::format("Key Press: Key='{}', Code={}, Ctrl={}, Shift={}, Alt={}
", + event.key, event.keyCode, event.ctrlKey, event.shiftKey, event.altKey); + keyOutput->SetInnerHTML(keyInfo); + }); + + // Focus Events + focusInput->AddFocusListener([&](FocusEvent event) { + focusOutput->SetInnerHTML("Focus: Element gained focus
"); + }); + + focusInput->AddBlurListener([&](FocusEvent event) { + focusOutput->SetInnerHTML("Blur: Element lost focus
"); + }); + + // Form Events + formInput->AddInputListener([&](InputEvent event) { + formOutput->SetInnerHTML(std::format("Input: Value='{}'
", event.data)); + }); + + formInput->AddChangeListener([&](ChangeEvent event) { + formOutput->SetInnerHTML(std::format("Change: Value='{}'
", event.value)); + }); + + formSelect->AddChangeListener([&](ChangeEvent event) { + formOutput->SetInnerHTML(std::format("Select Change: Value='{}'
", event.value)); + }); + + // Submit Event + formElement->AddSubmitListener([&]() { + formOutput->SetInnerHTML("Submit: Form submitted successfully!
"); + }); + + // Window Events + // Resize event + body->AddResizeListener([&](ResizeEvent event) { + windowOutput->SetInnerHTML(std::format("Resize: Width={}, Height={}
", event.width, event.height)); + }); + + // Scroll event + body->AddScrollListener([&](ScrollEvent event) { + windowOutput->SetInnerHTML(std::format("Scroll: X={}, Y={}
", event.scrollX, event.scrollY)); + }); + + // Context Menu Event + body->AddContextMenuListener([&](MouseEvent event) { + windowOutput->SetInnerHTML(std::format("Context Menu: X={}, Y={}
", event.clientX, event.clientY)); + }); + + // Drag and Drop Events + dragSource->AddDragStartListener([&](MouseEvent event) { + dragOutput->SetInnerHTML("Drag Start: Dragging started
"); + }); + + dragSource->AddDragEndListener([&](MouseEvent event) { + dragOutput->SetInnerHTML("Drag End: Dragging ended
"); + }); + + dropTarget->AddDragOverListener([&](MouseEvent event) { + dragOutput->SetInnerHTML("Drag Over: Dragging over drop target
"); + }); + + dropTarget->AddDragEnterListener([&](MouseEvent event) { + dragOutput->SetInnerHTML("Drag Enter: Drag entered drop target
"); + }); + + dropTarget->AddDragLeaveListener([&](MouseEvent event) { + dragOutput->SetInnerHTML("Drag Leave: Drag left drop target
"); + }); + + dropTarget->AddDropListener([&](MouseEvent event) { + dragOutput->SetInnerHTML("Drop: Item dropped
"); + }); + + // Wheel Event + wheelContainer->AddWheelListener([&](WheelEvent event) { + wheelOutput->SetInnerHTML(std::format("Wheel: DeltaX={}, DeltaY={}, DeltaZ={}
", + event.deltaX, event.deltaY, event.deltaZ)); + }); + return 0; } \ No newline at end of file diff --git a/examples/SPAExample/README.md b/examples/SPAExample/README.md deleted file mode 100644 index 6f9687e..0000000 --- a/examples/SPAExample/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# SPAExample - -This example demonstrates how to create a Single Page Application (SPA) using Crafter.CppDOM with dynamic content updating and browser history management. - -## Features - -- Shows how to implement SPA navigation without page reloads -- Demonstrates usage of `PushState` and `PopState` events -- Illustrates dynamic content updating based on navigation -- Shows how to handle browser back/forward buttons -- Uses modern web techniques for seamless navigation - -## Usage - -```cpp -import Crafter.CppDOM; -import std; -using namespace Crafter; -using namespace Crafter::CppDOMBindings; - -HtmlElementView* body = new HtmlElementView("body", R"( - - -)"); -HtmlElementView* home = new HtmlElementView("home"); -HtmlElementView* about = new HtmlElementView("about"); -HtmlElementView* contact = new HtmlElementView("contact"); -HtmlElementView* content = new HtmlElementView("content"); - -void UpdateContent(const std::string_view page) { - if (page == "home") { - content->SetInnerHTML("Welcome to the Home page of our Single Page Application!
This demo shows how to use the new history.pushState and popstate event handling features.
"); - } else if (page == "about") { - content->SetInnerHTML("This is the About page.
Notice how the URL changes without reloading the page.
"); - } else if (page == "contact") { - content->SetInnerHTML("This is the Contact page.
You can navigate back and forth using the browser's back/forward buttons.
"); - } -} - -int main() { - body->SetStyle("font-family: Arial, sans-serif; margin: 0; padding: 20px;"); - - // Add click handlers for navigation - home->AddClickListener([](Crafter::MouseEvent e) { - PushState("{\"page\": \"home\"}", "Home", "/"); - UpdateContent("home"); - }); - - about->AddClickListener([](Crafter::MouseEvent e) { - PushState("{\"page\": \"about\"}", "About", "/about"); - UpdateContent("about"); - }); - - contact->AddClickListener([](Crafter::MouseEvent e) { - PushState("{\"page\": \"contact\"}", "Contact", "/contact"); - UpdateContent("contact"); - }); - - // Add popstate listener to handle browser back/forward buttons - auto popStateId = AddPopStateListener([]() { - std::string path = GetPathNameString(); - if (path == "/") { - UpdateContent("home"); - } else if (path == "/about") { - UpdateContent("about"); - } else if (path == "/contact") { - UpdateContent("contact"); - } else { - UpdateContent("home"); - } - }); - - // Initialize with home page - UpdateContent("home"); - - return 0; -} -``` - -## Building and Running - -```bash -crafter-build build executable -run.sh -``` - -Then navigate to `http://localhost:8080/` in your browser. - -If caddy is not installed, you can use your favorite static file server instead. \ No newline at end of file diff --git a/examples/SPAExample/main.cpp b/examples/SPAExample/main.cpp deleted file mode 100644 index cd39dd3..0000000 --- a/examples/SPAExample/main.cpp +++ /dev/null @@ -1,67 +0,0 @@ -import Crafter.CppDOM; -import std; -using namespace Crafter; -using namespace Crafter::CppDOMBindings; - -HtmlElementView* body = new HtmlElementView("body", R"( - - -)"); -HtmlElementView* home = new HtmlElementView("home"); -HtmlElementView* about = new HtmlElementView("about"); -HtmlElementView* contact = new HtmlElementView("contact"); -HtmlElementView* content = new HtmlElementView("content"); - -void UpdateContent(const std::string_view page) { - if (page == "home") { - content->SetInnerHTML("Welcome to the Home page of our Single Page Application!
This demo shows how to use the new history.pushState and popstate event handling features.
"); - } else if (page == "about") { - content->SetInnerHTML("This is the About page.
Notice how the URL changes without reloading the page.
"); - } else if (page == "contact") { - content->SetInnerHTML("This is the Contact page.
You can navigate back and forth using the browser's back/forward buttons.
"); - } -} - -int main() { - body->SetStyle("font-family: Arial, sans-serif; margin: 0; padding: 20px;"); - - // Add click handlers for navigation - home->AddClickListener([](Crafter::MouseEvent e) { - PushState("{\"page\": \"home\"}", "Home", "/"); - UpdateContent("home"); - }); - - about->AddClickListener([](Crafter::MouseEvent e) { - PushState("{\"page\": \"about\"}", "About", "/about"); - UpdateContent("about"); - }); - - contact->AddClickListener([](Crafter::MouseEvent e) { - PushState("{\"page\": \"contact\"}", "Contact", "/contact"); - UpdateContent("contact"); - }); - - // Add popstate listener to handle browser back/forward buttons - auto popStateId = AddPopStateListener([]() { - std::string path = GetPathNameString(); - if (path == "/") { - UpdateContent("home"); - } else if (path == "/about") { - UpdateContent("about"); - } else if (path == "/contact") { - UpdateContent("contact"); - } else { - UpdateContent("home"); - } - }); - - // Initialize with home page - UpdateContent("home"); - - return 0; -} \ No newline at end of file diff --git a/examples/SPAExample/project.json b/examples/SPAExample/project.json deleted file mode 100644 index 9750e20..0000000 --- a/examples/SPAExample/project.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "main", - "configurations": [ - { - "name": "executable", - "implementations": ["main"], - "target": "wasm32-wasi", - "debug" : true, - "dependencies": [ - { - "path":"../../project.json", - "configuration":"lib-debug" - } - ] - } - ] -} \ No newline at end of file diff --git a/examples/SPAExample/run.sh b/examples/SPAExample/run.sh deleted file mode 100755 index e706621..0000000 --- a/examples/SPAExample/run.sh +++ /dev/null @@ -1 +0,0 @@ -caddy file-server --listen :8080 --root bin/executable \ No newline at end of file diff --git a/interfaces/Crafter.CppDOM-BindingsExport.cppm b/interfaces/Crafter.CppDOM-BindingsExport.cppm index e1d4280..15b59c5 100644 --- a/interfaces/Crafter.CppDOM-BindingsExport.cppm +++ b/interfaces/Crafter.CppDOM-BindingsExport.cppm @@ -1,7 +1,26 @@ +/* +Crafter.CppDOM +Copyright (C) 2025 Catcrafts +Catcrafts.net + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + export module Crafter.CppDOM:BindingsExport; import std; import :EventTypes; -import :BindingsImport; export namespace Crafter::CppDOMBindings { std::int32_t clickHandlerMaxId = 0; @@ -76,9 +95,6 @@ export namespace Crafter::CppDOMBindings { std::int32_t dragLeaveHandlerMaxId = 0; std::unordered_map