lifecycle update

This commit is contained in:
Jorijn van der Graaf 2025-11-14 18:40:13 +01:00
commit 0b7a43efbd
14 changed files with 950 additions and 490 deletions

View file

@ -1,26 +1,180 @@
import Crafter.CppDOM;
using namespace Crafter::CppDOM;
using namespace Crafter::CppDOMBindings;
using namespace Crafter;
import std;
HtmlElementPtr body("body", "<div id='container'>"
"<h1>Enhanced Event Handling Demo</h1>"
"<div id='events-container'>"
"<div class='event-section'>"
"<h2>Mouse Events</h2>"
"<button id='mouseButton'>Click Me!</button>"
"<div id='mouseOutput'></div>"
"</div>"
"<div class='event-section'>"
"<h2>Keyboard Events</h2>"
"<input type='text' id='keyInput' placeholder='Press keys here'>"
"<div id='keyOutput'></div>"
"</div>"
"<div class='event-section'>"
"<h2>Focus Events</h2>"
"<input type='text' id='focusInput' placeholder='Focus me!'>"
"<div id='focusOutput'></div>"
"</div>"
"<div class='event-section'>"
"<h2>Form Events</h2>"
"<form id='formElement'>"
"<input type='text' id='formInput' placeholder='Type something'>"
"<select id='formSelect'>"
"<option value='option1'>Option 1</option>"
"<option value='option2'>Option 2</option>"
"<option value='option3'>Option 3</option>"
"</select>"
"<button type='submit'>Submit Form</button>"
"</form>"
"<div id='formOutput'></div>"
"</div>"
"<div class='event-section'>"
"<h2>Window Events</h2>"
"<div id='windowOutput'></div>"
"</div>"
"<div class='event-section'>"
"<h2>Drag & Drop Events</h2>"
"<div id='dragSource' draggable='true'>Drag Me!</div>"
"<div id='dropTarget'>Drop Here</div>"
"<div id='dragOutput'></div>"
"</div>"
"<div class='event-section'>"
"<h2>Wheel Events</h2>"
"<div id='wheelContainer'>Scroll here</div>"
"<div id='wheelOutput'></div>"
"</div>"
"</div>"
"</div>");
HtmlElementPtr mouseButton("mouseButton");
HtmlElementPtr mouseOutput("mouseOutput");
HtmlElementPtr keyInput("keyInput");
HtmlElementPtr keyOutput("keyOutput");
HtmlElementPtr focusInput("focusInput");
HtmlElementPtr focusOutput("focusOutput");
HtmlElementPtr formInput("formInput");
HtmlElementPtr formSelect("formSelect");
HtmlElementPtr formElement("formElement");
HtmlElementPtr formOutput("formOutput");
HtmlElementPtr windowOutput("windowOutput");
HtmlElementPtr dragSource("dragSource");
HtmlElementPtr dropTarget("dropTarget");
HtmlElementPtr dragOutput("dragOutput");
HtmlElementPtr wheelContainer("wheelContainer");
HtmlElementPtr wheelOutput("wheelOutput");
int main() {
HtmlElementView body("body");
body.SetInnerHTML("<h1>All Event Handling</h1>");
HtmlElementView div("div");
div.SetInnerHTML("<p>Click me!</p>");
div.SetStyle("padding: 20px; background-color: lightblue; cursor: pointer;");
// Add click handler
div.AddClickListener([](Crafter::MouseEvent e) {
std::cout << "Clicked!" << std::endl;
mouseButton.AddClickListener([&](MouseEvent event) {
mouseOutput.SetInnerHTML(std::format("<p><strong>Click:</strong> X={}, Y={}</p>", event.clientX, event.clientY));
});
mouseButton.AddMouseOverListener([&](MouseEvent event) {
mouseOutput.SetInnerHTML(std::format("<p><strong>Mouse Over:</strong> X={}, Y={}</p>", event.clientX, event.clientY));
});
mouseButton.AddMouseOutListener([&](MouseEvent event) {
mouseOutput.SetInnerHTML(std::format("<p><strong>Mouse Out:</strong> X={}, Y={}</p>", event.clientX, event.clientY));
});
mouseButton.AddMouseMoveListener([&](MouseEvent event) {
mouseOutput.SetInnerHTML(std::format("<p><strong>Mouse Move:</strong> X={}, Y={}</p>", event.clientX, event.clientY));
});
mouseButton.AddMouseDownListener([&](MouseEvent event) {
mouseOutput.SetInnerHTML(std::format("<p><strong>Mouse Down:</strong> Button={}, X={}, Y={}</p>", event.button, event.clientX, event.clientY));
});
mouseButton.AddMouseUpListener([&](MouseEvent event) {
mouseOutput.SetInnerHTML(std::format("<p><strong>Mouse Up:</strong> Button={}, X={}, Y={}</p>", event.button, event.clientX, event.clientY));
});
keyInput.AddKeyDownListener([&](KeyboardEvent event) {
std::string keyInfo = std::format("<p><strong>Key Down:</strong> Key='{}', Code={}, Ctrl={}, Shift={}, Alt={}</p>",
event.key, event.keyCode, event.ctrlKey, event.shiftKey, event.altKey);
keyOutput.SetInnerHTML(keyInfo);
});
keyInput.AddKeyUpListener([&](KeyboardEvent event) {
std::string keyInfo = std::format("<p><strong>Key Up:</strong> Key='{}', Code={}, Ctrl={}, Shift={}, Alt={}</p>",
event.key, event.keyCode, event.ctrlKey, event.shiftKey, event.altKey);
keyOutput.SetInnerHTML(keyInfo);
});
keyInput.AddKeyPressListener([&](KeyboardEvent event) {
std::string keyInfo = std::format("<p><strong>Key Press:</strong> Key='{}', Code={}, Ctrl={}, Shift={}, Alt={}</p>",
event.key, event.keyCode, event.ctrlKey, event.shiftKey, event.altKey);
keyOutput.SetInnerHTML(keyInfo);
});
// Focus Events
focusInput.AddFocusListener([&](FocusEvent event) {
focusOutput.SetInnerHTML("<p><strong>Focus:</strong> Element gained focus</p>");
});
focusInput.AddBlurListener([&](FocusEvent event) {
focusOutput.SetInnerHTML("<p><strong>Blur:</strong> Element lost focus</p>");
});
// Form Events
formInput.AddInputListener([&](InputEvent event) {
formOutput.SetInnerHTML(std::format("<p><strong>Input:</strong> Value='{}'</p>", event.data));
});
formInput.AddChangeListener([&](ChangeEvent event) {
formOutput.SetInnerHTML(std::format("<p><strong>Change:</strong> Value='{}'</p>", event.value));
});
formSelect.AddChangeListener([&](ChangeEvent event) {
formOutput.SetInnerHTML(std::format("<p><strong>Select Change:</strong> Value='{}'</p>", event.value));
});
formElement.AddSubmitListener([&]() {
formOutput.SetInnerHTML("<p><strong>Submit:</strong> Form submitted successfully!</p>");
});
body.AddResizeListener([&](ResizeEvent event) {
windowOutput.SetInnerHTML(std::format("<p><strong>Resize:</strong> Width={}, Height={}</p>", event.width, event.height));
});
body.AddScrollListener([&](ScrollEvent event) {
windowOutput.SetInnerHTML(std::format("<p><strong>Scroll:</strong> X={}, Y={}</p>", event.scrollX, event.scrollY));
});
body.AddContextMenuListener([&](MouseEvent event) {
windowOutput.SetInnerHTML(std::format("<p><strong>Context Menu:</strong> X={}, Y={}</p>", event.clientX, event.clientY));
});
dragSource.AddDragStartListener([&](MouseEvent event) {
dragOutput.SetInnerHTML("<p><strong>Drag Start:</strong> Dragging started</p>");
});
dragSource.AddDragEndListener([&](MouseEvent event) {
dragOutput.SetInnerHTML("<p><strong>Drag End:</strong> Dragging ended</p>");
});
dropTarget.AddDragOverListener([&](MouseEvent event) {
dragOutput.SetInnerHTML("<p><strong>Drag Over:</strong> Dragging over drop target</p>");
});
dropTarget.AddDragEnterListener([&](MouseEvent event) {
dragOutput.SetInnerHTML("<p><strong>Drag Enter:</strong> Drag entered drop target</p>");
});
dropTarget.AddDragLeaveListener([&](MouseEvent event) {
dragOutput.SetInnerHTML("<p><strong>Drag Leave:</strong> Drag left drop target</p>");
});
dropTarget.AddDropListener([&](MouseEvent event) {
dragOutput.SetInnerHTML("<p><strong>Drop:</strong> Item dropped</p>");
});
wheelContainer.AddWheelListener([&](WheelEvent event) {
wheelOutput.SetInnerHTML(std::format("<p><strong>Wheel:</strong> DeltaX={}, DeltaY={}, DeltaZ={}</p>",
event.deltaX, event.deltaY, event.deltaZ));
});
body.AddChild(div);
// Demonstrate new bindings
std::string path = GetPathNameString();
std::cout << "Current path: " << path << std::endl;
return 0;
}

View file

@ -1,17 +1,16 @@
import Crafter.CppDOM;
import std;
using namespace Crafter;
using namespace Crafter::CppDOMBindings;
HtmlElementPtr body("body", "<h1>Fetch Example</h1><p>Testing HTTP requests...</p>");
int main(){
void* body = GetElementById("body");
SetInnerHTML(body, "<h1>Fetch Example</h1><p>Testing HTTP requests...</p>");
Fetch("https://httpbin.org/get", [body](std::string result){
Fetch("https://httpbin.org/get", [](std::string result){
if (!result.empty()) {
SetInnerHTML(body, "<h1>Fetch Example</h1><p>Response: " + result + "</p>");
body.SetInnerHTML("<h1>Fetch Example</h1><p>Response: " + result + "</p>");
} else {
SetInnerHTML(body, "<h1>Fetch Example</h1><p>Failed to fetch data</p>");
body.SetInnerHTML("<h1>Fetch Example</h1><p>Failed to fetch data</p>");
}
FreeJs(body);
});
}

View file

@ -2,7 +2,7 @@ import Crafter.CppDOM;
using namespace Crafter;
int main(){
HtmlElementView body("body");
HtmlElementPtr body("body");
body.SetInnerHTML("Hello World!");
//No need to call FreeJs, this is done in the destructor of HtmlElementView.
//No need to call FreeJs, this is done in the destructor of HtmlElementPtr.
}

View file

@ -2,16 +2,15 @@ import Crafter.CppDOM;
import std;
using namespace Crafter;
HtmlElement* body = new HtmlElement("body", R"(<h1>Input GetValue() and SetValue() Example</h1><br><input id="input" type="text" placeholder="Enter your text here..."><br><button id="button">Change Value</button><p id ="valueOutput"></p>)");
HtmlElement* button = new HtmlElement("button");
HtmlElement* output = new HtmlElement("valueOutput");
HtmlElement* input = new HtmlElement("input");
HtmlElementPtr body("body", R"(<h1>Input GetValue() and SetValue() Example</h1><br><input id="input" type="text" placeholder="Enter your text here..."><br><button id="button">Change Value</button><p id ="valueOutput"></p>)");
HtmlElementPtr button("button");
HtmlElementPtr output("valueOutput");
HtmlElementPtr input("input");
int main(){
button->AddClickListener([](Crafter::MouseEvent) {
std::string newValue = input->GetValue();
output->SetInnerHTML(newValue);
input->SetValue("");
button.AddClickListener([](Crafter::MouseEvent) {
std::string newValue = input.GetValue();
output.SetInnerHTML(newValue);
input.SetValue("");
});
}

View file

@ -2,15 +2,15 @@ import Crafter.CppDOM;
import std;
using namespace Crafter;
HtmlElementView body("body","<h1>Interactive Element Demo</h1>"
HtmlElementPtr body("body","<h1>Interactive Element Demo</h1>"
"<button id='myButton'>Click Me!</button>"
"<p id='output'>Click the button above</p>");
HtmlElementView* button = new HtmlElement("myButton"); //prevent destruction
HtmlElementPtr button("myButton");
HtmlElementPtr output("output");
int main() {
button->AddClickListener([](Crafter::MouseEvent event){
auto output = HtmlElementView("output");
button.AddClickListener([](Crafter::MouseEvent event){
output.SetInnerHTML("Button was clicked at (" + std::to_string(event.clientX) + ", " + std::to_string(event.clientY) + ")!");
});
}

View file

@ -3,7 +3,7 @@ import std;
using namespace Crafter;
using namespace Crafter::CppDOMBindings;
HtmlElementView* body = new HtmlElementView("body", R"(
HtmlElementPtr body("body", R"(
<nav>
<h2 style="margin-bottom: 20px; padding: 10px; background-color: #f0f0f0;">SPA Navigation Demo</h2>
<a id="home" style="margin-right: 15px; text-decoration: none; color: blue; cursor: pointer;">Home</a>
@ -12,36 +12,36 @@ HtmlElementView* body = new HtmlElementView("body", R"(
</nav>
<div id="content" style="min-height: 200px; padding: 15px; border: 1px solid #ccc;"></div>
)");
HtmlElementView* home = new HtmlElementView("home");
HtmlElementView* about = new HtmlElementView("about");
HtmlElementView* contact = new HtmlElementView("contact");
HtmlElementView* content = new HtmlElementView("content");
HtmlElementPtr home("home");
HtmlElementPtr about("about");
HtmlElementPtr contact("contact");
HtmlElementPtr content("content");
void UpdateContent(const std::string_view page) {
if (page == "home") {
content->SetInnerHTML("<h3>Home Page</h3><p>Welcome to the Home page of our Single Page Application!</p><p>This demo shows how to use the new history.pushState and popstate event handling features.</p>");
content.SetInnerHTML("<h3>Home Page</h3><p>Welcome to the Home page of our Single Page Application!</p><p>This demo shows how to use the new history.pushState and popstate event handling features.</p>");
} else if (page == "about") {
content->SetInnerHTML("<h3>About Page</h3><p>This is the About page.</p><p>Notice how the URL changes without reloading the page.</p>");
content.SetInnerHTML("<h3>About Page</h3><p>This is the About page.</p><p>Notice how the URL changes without reloading the page.</p>");
} else if (page == "contact") {
content->SetInnerHTML("<h3>Contact Page</h3><p>This is the Contact page.</p><p>You can navigate back and forth using the browser's back/forward buttons.</p>");
content.SetInnerHTML("<h3>Contact Page</h3><p>This is the Contact page.</p><p>You can navigate back and forth using the browser's back/forward buttons.</p>");
}
}
int main() {
body->SetStyle("font-family: Arial, sans-serif; margin: 0; padding: 20px;");
body.SetStyle("font-family: Arial, sans-serif; margin: 0; padding: 20px;");
// Add click handlers for navigation
home->AddClickListener([](Crafter::MouseEvent e) {
home.AddClickListener([](Crafter::MouseEvent e) {
PushState("{\"page\": \"home\"}", "Home", "/");
UpdateContent("home");
});
about->AddClickListener([](Crafter::MouseEvent e) {
about.AddClickListener([](Crafter::MouseEvent e) {
PushState("{\"page\": \"about\"}", "About", "/about");
UpdateContent("about");
});
contact->AddClickListener([](Crafter::MouseEvent e) {
contact.AddClickListener([](Crafter::MouseEvent e) {
PushState("{\"page\": \"contact\"}", "Contact", "/contact");
UpdateContent("contact");
});

View file

@ -2,9 +2,9 @@ import Crafter.CppDOM;
using namespace Crafter;
int main(){
HtmlElementView body("body","<div id=\"myDiv\"></div>");
HtmlElementPtr body("body","<div id=\"myDiv\"></div>");
// Create a div element
HtmlElementView div("myDiv");
HtmlElementPtr div("myDiv");
// Set some initial content
div.SetInnerHTML("<p>This is a styled paragraph</p>");

View file

@ -11,12 +11,11 @@ struct Note {
void RenderNotes();
// Global vector to store notes
std::vector<Note>* notes = new std::vector<Note>();
std::vector<HtmlElementView>* noteButtons = new std::vector<HtmlElementView>();
std::vector<Note> notes;
std::vector<HtmlElementView> noteButtons;
// Create the head section
HtmlElementView* head = new HtmlElementView("head", R"(
HtmlElementPtr head("head", R"(
<title>Note Taking App</title>
<style>
body {
@ -123,7 +122,7 @@ HtmlElementView* head = new HtmlElementView("head", R"(
)");
// Create the body section
HtmlElementView* body = new HtmlElementView("body", R"(
HtmlElementPtr body("body", R"(
<div class="app-container">
<h1>📝 Note Taking App</h1>
@ -142,16 +141,16 @@ HtmlElementView* body = new HtmlElementView("body", R"(
</div>
</div>
)");
HtmlElementView* addNoteBtn = new HtmlElementView("addNoteBtn");
HtmlElementView* noteInput = new HtmlElementView("noteInput");
HtmlElementView* loadingIndicator = new HtmlElementView("loading");
HtmlElementView* notesContainer = new HtmlElementView("notesContainer");
HtmlElementPtr addNoteBtn("addNoteBtn");
HtmlElementPtr noteInput("noteInput");
HtmlElementPtr loadingIndicator("loading");
HtmlElementPtr notesContainer ("notesContainer");
void FetchNotes() {
loadingIndicator->SetInnerHTML("<p>Loading notes...</p>");
loadingIndicator.SetInnerHTML("<p>Loading notes...</p>");
Fetch("http://localhost:3000/getNotes", [](std::string input) {
notes->clear();
notes.clear();
std::istringstream stream(input);
std::string line;
@ -169,35 +168,35 @@ void FetchNotes() {
content += line;
}
notes->push_back(Note{id, content});
notes.push_back(Note{id, content});
}
loadingIndicator->SetInnerHTML("<p>Notes loaded!</p>");
loadingIndicator.SetInnerHTML("<p>Notes loaded!</p>");
RenderNotes();
});
}
// Function to delete a note via the backend
void DeleteNote(std::uint32_t id) {
loadingIndicator->SetInnerHTML("<p>Deleting note...</p>");
loadingIndicator.SetInnerHTML("<p>Deleting note...</p>");
// Make POST request to delete note
Fetch("http://localhost:3000/deleteNote", std::to_string(id), [](std::string content) {
FetchNotes();
loadingIndicator->SetInnerHTML("<p>Note deleted!</p>");
loadingIndicator.SetInnerHTML("<p>Note deleted!</p>");
});
}
// Function to render all notes to the DOM
void RenderNotes() {
if (notes->empty()) {
notesContainer->SetInnerHTML("<p>No notes yet. Add one below!</p>");
if (notes.empty()) {
notesContainer.SetInnerHTML("<p>No notes yet. Add one below!</p>");
return;
}
std::string html = "<div class='notes-list'>";
noteButtons->clear();
for (const Note& note : *notes) {
noteButtons.clear();
for (const Note& note : notes) {
html += std::format(
R"(<div class='note-item' id='note-{}'>
<div class='note-content'>{}</div>
@ -208,10 +207,10 @@ void RenderNotes() {
}
html += "</div>";
notesContainer->SetInnerHTML(html);
notesContainer.SetInnerHTML(html);
for (const Note& note : *notes) {
HtmlElementView& view = noteButtons->emplace_back(std::format("note-{}-delete", note.id));
for (const Note& note : notes) {
HtmlElementView& view = noteButtons.emplace_back(std::format("note-{}-delete", note.id));
view.AddClickListener([id = note.id](MouseEvent event) {
DeleteNote(id);
});
@ -223,17 +222,17 @@ int main() {
FetchNotes();
// Add click listener to add note button
addNoteBtn->AddClickListener([&](MouseEvent event) {
std::string noteValue = noteInput->GetValue();
addNoteBtn.AddClickListener([&](MouseEvent event) {
std::string noteValue = noteInput.GetValue();
std::cout << noteValue << std::endl;
if (!noteValue.empty()) {
loadingIndicator->SetInnerHTML("<p>Saving note...</p>");
loadingIndicator.SetInnerHTML("<p>Saving note...</p>");
Fetch("http://localhost:3000/createNote", noteValue, [noteValue](std::string content) {
std::cout << noteValue << std::endl;
notes->push_back({static_cast<std::uint32_t>(std::stoi(content)), noteValue});
noteInput->SetValue("");
notes.push_back({static_cast<std::uint32_t>(std::stoi(content)), noteValue});
noteInput.SetValue("");
RenderNotes();
loadingIndicator->SetInnerHTML("<p>Saved note!</p>");
loadingIndicator.SetInnerHTML("<p>Saved note!</p>");
});
}
});

View file

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