From 224dc563e976d780099cc8446c3d15034b494bc0 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Mon, 10 Nov 2025 22:06:27 +0100 Subject: [PATCH] style --- README.md | 86 +++++++++---------- additional/env.js | 24 ++++++ examples/StyleExample/main.cpp | 31 +++++++ examples/StyleExample/project.json | 17 ++++ examples/StyleExample/run.sh | 1 + .../Crafter.CppDOM-HtmlElement.cpp | 24 ++++++ interfaces/Crafter.CppDOM-BindingsExport.cppm | 26 ++++++ interfaces/Crafter.CppDOM-BindingsImport.cppm | 31 +++++++ interfaces/Crafter.CppDOM-HtmlElement.cppm | 6 ++ 9 files changed, 200 insertions(+), 46 deletions(-) create mode 100644 examples/StyleExample/main.cpp create mode 100644 examples/StyleExample/project.json create mode 100755 examples/StyleExample/run.sh diff --git a/README.md b/README.md index 20a4f00..5891544 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,53 @@ -# About +# Crafter.CppDOM -![alt text](https://github.com/Catcrafts/Crafter.Web/blob/master/hello.png?raw=true) +A C++ DOM library for web applications that allows you to manipulate HTML elements directly from C++. -Crafter.CppDOM is a C++ library that exposes the browser DOM api's to C++ WebAssembly. +## New Styling Features -# How to use -Please view the examples folder, this is a snippit from the HelloElement example: +This library now supports comprehensive styling capabilities beyond just inline HTML: + +### Style Methods + +- `SetStyle(const std::string_view style)` - Sets multiple CSS properties at once +- `SetProperty(const std::string_view property, const std::string_view value)` - Sets a single CSS property +- `AddClass(const std::string_view className)` - Adds a CSS class to the element +- `RemoveClass(const std::string_view className)` - Removes a CSS class from the element +- `ToggleClass(const std::string_view className)` - Toggles a CSS class on the element +- `HasClass(const std::string_view className)` - Checks if the element has a specific CSS class + +### Example Usage ```cpp -import Crafter.CppDOM; -using namespace Crafter::CppDOM; +HtmlElement div("myDiv"); -int main(){ - HtmlElement body("body"); - body.SetInnerHTML("Hello World!"); -} -``` -You can also view the wiki for more detailed information. +// Set multiple styles at once +div.SetStyle("color: blue; font-size: 20px; background-color: lightgray;"); -It is highly recommended to use this with [Crafter.Build](https://forgejo.catcrafts.net/Catcrafts/Crafter.Build), but it is not strictly required if the same way of injecting the env is followed. The following instructions will be for Crafter.Build. +// Set individual properties +div.SetProperty("border", "2px solid red"); +div.SetProperty("padding", "10px"); -## Quickstart -create a ``project.json`` in an empty folder, open it in your preferred text editor. -Create a basic project file, that describes your web project. -```JSON -{ - "name": "main", - "configurations": [ - { - "name": "executable", - "implementations": ["main"], - "target": "wasm32-wasi", - "debug" : true, - "dependencies": [ - { - "path":"https://forgejo.catcrafts.net/Catcrafts/Crafter.CppDOM.git", - "configuration":"lib-debug" - } - ], - } - ] -} +// Work with CSS classes +div.AddClass("highlight"); +div.AddClass("container"); +div.ToggleClass("active"); +bool isActive = div.HasClass("active"); +div.RemoveClass("highlight"); ``` -Save and close the file, create a ``main.cpp`` -```cpp -import Crafter.CppDOM; -using namespace Crafter::CppDOM; +### Benefits -int main(){ - HtmlElement body("body"); - body.SetInnerHTML("Hello World!"); -} -``` +1. **Type Safety**: Compile-time checking of method names and parameters +2. **Performance**: More efficient than constructing HTML strings +3. **Maintainability**: Clear separation between content and styling logic +4. **Flexibility**: Support for both inline styles and CSS classes +5. **Developer Experience**: Intuitive API similar to JavaScript DOM -Save and close, then run ``crafter-build build executable && caddy file-server --listen :8080 --root bin/executable``. if you have caddy installed, if not use your favorite static file server instead. Now you can open the browser at ``http://localhost:8080`` and ``Hello World!`` will appear in the browser. +## Examples -This sample can also be viewed in the [HelloElement example](https://forgejo.catcrafts.net/Catcrafts/Crafter.CppDOM/src/branch/master/examples) \ No newline at end of file +Check the examples directory for usage demonstrations: +- `HelloWorld` - Basic usage +- `HelloElement` - Creating and manipulating elements +- `InteractiveElement` - Interactive elements with event handling +- `AllEventHandling` - Complete event handling example +- `StyleExample` - Demonstrates new styling features \ No newline at end of file diff --git a/additional/env.js b/additional/env.js index 84248c4..a379805 100644 --- a/additional/env.js +++ b/additional/env.js @@ -600,6 +600,30 @@ let env = { freeJs: freeJs, getElementById: getElementById, setInnerHTML: setInnerHTML, + setStyle: function(ptr, style, styleLength) { + jsmemory.get(ptr).style.cssText = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, style, styleLength)); + }, + setProperty: function(ptr, property, propertyLength, value, valueLength) { + const prop = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, property, propertyLength)); + const val = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, value, valueLength)); + jsmemory.get(ptr).style.setProperty(prop, val); + }, + addClass: function(ptr, className, classNameLength) { + const cls = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, className, classNameLength)); + jsmemory.get(ptr).classList.add(cls); + }, + removeClass: function(ptr, className, classNameLength) { + const cls = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, className, classNameLength)); + jsmemory.get(ptr).classList.remove(cls); + }, + toggleClass: function(ptr, className, classNameLength) { + const cls = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, className, classNameLength)); + jsmemory.get(ptr).classList.toggle(cls); + }, + hasClass: function(ptr, className, classNameLength) { + const cls = decoder.decode(new Int8Array(window.crafter_webbuild_wasi.instance.exports.memory.buffer, className, classNameLength)); + return jsmemory.get(ptr).classList.contains(cls); + }, addClickListener: addClickListener, removeClickListener: removeClickListener, addMouseOverListener: addMouseOverListener, diff --git a/examples/StyleExample/main.cpp b/examples/StyleExample/main.cpp new file mode 100644 index 0000000..251fe24 --- /dev/null +++ b/examples/StyleExample/main.cpp @@ -0,0 +1,31 @@ +import Crafter.CppDOM; +using namespace Crafter; + +int main(){ + HtmlElement body("body","
"); + // Create a div element + HtmlElement div("myDiv"); + + // Set some initial content + div.SetInnerHTML("

This is a styled paragraph

"); + + // Apply styles using different methods + div.SetStyle("color: blue; font-size: 20px; background-color: lightgray;"); + + // Or apply individual properties + div.SetProperty("border", "2px solid red"); + div.SetProperty("padding", "10px"); + + // Add CSS classes + div.AddClass("highlight"); + div.AddClass("container"); + + // Demonstrate class toggling + div.ToggleClass("active"); + + // Check if class exists + bool hasActiveClass = div.HasClass("active"); + + // Remove a class + div.RemoveClass("highlight"); +} \ No newline at end of file diff --git a/examples/StyleExample/project.json b/examples/StyleExample/project.json new file mode 100644 index 0000000..9750e20 --- /dev/null +++ b/examples/StyleExample/project.json @@ -0,0 +1,17 @@ +{ + "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/StyleExample/run.sh b/examples/StyleExample/run.sh new file mode 100755 index 0000000..e706621 --- /dev/null +++ b/examples/StyleExample/run.sh @@ -0,0 +1 @@ +caddy file-server --listen :8080 --root bin/executable \ No newline at end of file diff --git a/implementations/Crafter.CppDOM-HtmlElement.cpp b/implementations/Crafter.CppDOM-HtmlElement.cpp index 72d863d..01ed75a 100644 --- a/implementations/Crafter.CppDOM-HtmlElement.cpp +++ b/implementations/Crafter.CppDOM-HtmlElement.cpp @@ -38,6 +38,30 @@ namespace Crafter { CppDOMBindings::SetInnerHTML(ptr, html); } + void HtmlElement::SetStyle(const std::string_view style) { + CppDOMBindings::SetStyle(ptr, style); + } + + void HtmlElement::SetProperty(const std::string_view property, const std::string_view value) { + CppDOMBindings::SetProperty(ptr, property, value); + } + + void HtmlElement::AddClass(const std::string_view className) { + CppDOMBindings::AddClass(ptr, className); + } + + void HtmlElement::RemoveClass(const std::string_view className) { + CppDOMBindings::RemoveClass(ptr, className); + } + + void HtmlElement::ToggleClass(const std::string_view className) { + CppDOMBindings::ToggleClass(ptr, className); + } + + bool HtmlElement::HasClass(const std::string_view className) { + return CppDOMBindings::HasClass(ptr, className); + } + std::int32_t HtmlElement::AddClickListener(std::function callback) { std::int32_t id = CppDOMBindings::clickHandlerMaxId++; clickHandlers.push_back(id); diff --git a/interfaces/Crafter.CppDOM-BindingsExport.cppm b/interfaces/Crafter.CppDOM-BindingsExport.cppm index 4aa6e82..f6647da 100644 --- a/interfaces/Crafter.CppDOM-BindingsExport.cppm +++ b/interfaces/Crafter.CppDOM-BindingsExport.cppm @@ -200,4 +200,30 @@ extern "C" { __attribute__((export_name("ExecuteDragLeaveHandler"))) void ExecuteDragLeaveHandler(std::int32_t handlerID, double clientX, double clientY, double screenX, double screenY, std::int32_t button, std::int32_t buttons, bool altKey, bool ctrlKey, bool shiftKey, bool metaKey) { Crafter::CppDOMBindings::dragLeaveHandlers->find(handlerID)->second(Crafter::MouseEvent(clientX, clientY, screenX, screenY, button, buttons, altKey, ctrlKey, shiftKey, metaKey)); } + + // Style functions + __attribute__((export_name("setStyle"))) void SetStyle(void* ptr, const char* style, std::size_t styleLength) { + // This will be implemented in JavaScript + } + + __attribute__((export_name("setProperty"))) void SetProperty(void* ptr, const char* property, std::size_t propertyLength, const char* value, std::size_t valueLength) { + // This will be implemented in JavaScript + } + + __attribute__((export_name("addClass"))) void AddClass(void* ptr, const char* className, std::size_t classNameLength) { + // This will be implemented in JavaScript + } + + __attribute__((export_name("removeClass"))) void RemoveClass(void* ptr, const char* className, std::size_t classNameLength) { + // This will be implemented in JavaScript + } + + __attribute__((export_name("toggleClass"))) void ToggleClass(void* ptr, const char* className, std::size_t classNameLength) { + // This will be implemented in JavaScript + } + + __attribute__((export_name("hasClass"))) bool HasClass(void* ptr, const char* className, std::size_t classNameLength) { + // This will be implemented in JavaScript + return false; + } } \ No newline at end of file diff --git a/interfaces/Crafter.CppDOM-BindingsImport.cppm b/interfaces/Crafter.CppDOM-BindingsImport.cppm index 32ea9ec..56db1fb 100644 --- a/interfaces/Crafter.CppDOM-BindingsImport.cppm +++ b/interfaces/Crafter.CppDOM-BindingsImport.cppm @@ -105,4 +105,35 @@ export namespace Crafter::CppDOMBindings { __attribute__((import_module("env"), import_name("addDragLeaveListener"))) void AddDragLeaveListener(void* ptr, int id); __attribute__((import_module("env"), import_name("removeDragLeaveListener"))) void RemoveDragLeaveListener(void* ptr, int id); + + // Style functions + __attribute__((import_module("env"), import_name("setStyle"))) void SetStyle(void* ptr, const char* style, std::size_t styleLength); + void SetStyle(void* ptr, const std::string_view style) { + SetStyle(ptr, style.data(), style.size()); + } + + __attribute__((import_module("env"), import_name("setProperty"))) void SetProperty(void* ptr, const char* property, std::size_t propertyLength, const char* value, std::size_t valueLength); + void SetProperty(void* ptr, const std::string_view property, const std::string_view value) { + SetProperty(ptr, property.data(), property.size(), value.data(), value.size()); + } + + __attribute__((import_module("env"), import_name("addClass"))) void AddClass(void* ptr, const char* className, std::size_t classNameLength); + void AddClass(void* ptr, const std::string_view className) { + AddClass(ptr, className.data(), className.size()); + } + + __attribute__((import_module("env"), import_name("removeClass"))) void RemoveClass(void* ptr, const char* className, std::size_t classNameLength); + void RemoveClass(void* ptr, const std::string_view className) { + RemoveClass(ptr, className.data(), className.size()); + } + + __attribute__((import_module("env"), import_name("toggleClass"))) void ToggleClass(void* ptr, const char* className, std::size_t classNameLength); + void ToggleClass(void* ptr, const std::string_view className) { + ToggleClass(ptr, className.data(), className.size()); + } + + __attribute__((import_module("env"), import_name("hasClass"))) bool HasClass(void* ptr, const char* className, std::size_t classNameLength); + bool HasClass(void* ptr, const std::string_view className) { + return HasClass(ptr, className.data(), className.size()); + } } \ No newline at end of file diff --git a/interfaces/Crafter.CppDOM-HtmlElement.cppm b/interfaces/Crafter.CppDOM-HtmlElement.cppm index 561b6a3..fe7bf8f 100644 --- a/interfaces/Crafter.CppDOM-HtmlElement.cppm +++ b/interfaces/Crafter.CppDOM-HtmlElement.cppm @@ -56,6 +56,12 @@ namespace Crafter { HtmlElement(const std::string_view id); HtmlElement(const std::string_view id, const std::string_view html); void SetInnerHTML(const std::string_view html); + void SetStyle(const std::string_view style); + void SetProperty(const std::string_view property, const std::string_view value); + void AddClass(const std::string_view className); + void RemoveClass(const std::string_view className); + void ToggleClass(const std::string_view className); + bool HasClass(const std::string_view className); std::int32_t AddClickListener(std::function callback); void RemoveClickListener(std::int32_t id);