Skip to content

Commit 51bf855

Browse files
committed
Added factory for elements not in dom.
1 parent d5a9f96 commit 51bf855

File tree

9 files changed

+185
-11
lines changed

9 files changed

+185
-11
lines changed

nui/include/nui/frontend/attributes/impl/attribute_factory.hpp

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,12 @@ namespace Nui::Attributes
7070
: name_{name}
7171
{}
7272

73-
// Dont use this class like a value
74-
PropertyFactory(PropertyFactory const&) = delete;
75-
PropertyFactory(PropertyFactory&&) = delete;
73+
explicit constexpr PropertyFactory(PropertyFactory const& other)
74+
: name_{other.name_}
75+
{}
76+
explicit constexpr PropertyFactory(PropertyFactory&& other)
77+
: name_{other.name_}
78+
{}
7679
PropertyFactory& operator=(PropertyFactory const&) = delete;
7780
PropertyFactory& operator=(PropertyFactory&&) = delete;
7881

@@ -181,9 +184,12 @@ namespace Nui::Attributes
181184
: name_{name}
182185
{}
183186

184-
// Dont use this class like a value
185-
AttributeFactory(AttributeFactory const&) = delete;
186-
AttributeFactory(AttributeFactory&&) = delete;
187+
explicit constexpr AttributeFactory(AttributeFactory const& other)
188+
: name_{other.name_}
189+
{}
190+
explicit constexpr AttributeFactory(AttributeFactory&& other)
191+
: name_{other.name_}
192+
{}
187193
AttributeFactory& operator=(AttributeFactory const&) = delete;
188194
AttributeFactory& operator=(AttributeFactory&&) = delete;
189195

@@ -343,9 +349,12 @@ namespace Nui::Attributes
343349
: name_{name}
344350
{}
345351

346-
// Dont use this class like a value
347-
EventFactory(EventFactory const&) = delete;
348-
EventFactory(EventFactory&&) = delete;
352+
explicit constexpr EventFactory(EventFactory const& other)
353+
: name_{other.name_}
354+
{}
355+
explicit constexpr EventFactory(EventFactory&& other)
356+
: name_{other.name_}
357+
{}
349358
EventFactory& operator=(EventFactory const&) = delete;
350359
EventFactory& operator=(EventFactory&&) = delete;
351360

@@ -397,6 +406,31 @@ namespace Nui::Attributes
397406
return EventFactory{name};
398407
}
399408
}
409+
410+
namespace Detail
411+
{
412+
template <typename T>
413+
struct DeferWrap
414+
{
415+
T factory;
416+
417+
template <typename... Args>
418+
Attribute operator=(Args&&... args) const
419+
{
420+
auto attr = factory.operator=(std::forward<Args>(args)...);
421+
attr.defer(true);
422+
return attr;
423+
};
424+
};
425+
}
426+
427+
template <typename T>
428+
requires(
429+
std::is_same_v<T, AttributeFactory> || std::is_same_v<T, PropertyFactory> || std::is_same_v<T, EventFactory>)
430+
Detail::DeferWrap<T> operator!(T const& factory)
431+
{
432+
return Detail::DeferWrap<T>{.factory = T{std::move(factory)}};
433+
}
400434
}
401435

402436
#define MAKE_HTML_VALUE_ATTRIBUTE_RENAME(NAME, HTML_NAME) \

nui/include/nui/frontend/dom/basic_element.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ namespace Nui::Dom
6565
}
6666

6767
protected:
68+
explicit BasicElement()
69+
: element_{Nui::val::undefined()}
70+
{}
71+
6872
Nui::val element_;
6973
};
7074
}

nui/include/nui/frontend/dom/childless_element.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ namespace Nui::Dom
159159
}
160160

161161
protected:
162+
explicit ChildlessElement()
163+
: BasicElement{}
164+
{}
165+
162166
static Nui::val createElement(HtmlElement const& element)
163167
{
164168
return element.bridge()->createElement(element);

nui/include/nui/frontend/dom/element.hpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,26 @@ namespace Nui::Dom
5353
, deferredSetup_{}
5454
{}
5555

56+
/**
57+
* @brief This constructor takes ownership of a val.
58+
* This val must not be managed by any other element.
59+
*
60+
* @param val
61+
*/
5662
explicit Element(Nui::val val)
5763
: ChildlessElement{std::move(val)}
5864
, children_{}
5965
, unsetup_{}
6066
, deferredSetup_{}
6167
{}
6268

69+
explicit Element()
70+
: ChildlessElement{}
71+
, children_{}
72+
, unsetup_{}
73+
, deferredSetup_{}
74+
{}
75+
6376
Element(Element const&) = delete;
6477
Element(Element&&) = delete;
6578
Element& operator=(Element const&) = delete;
@@ -128,6 +141,22 @@ namespace Nui::Dom
128141
replaceElementImpl(element);
129142
return shared_from_base<Element>();
130143
}
144+
auto emplaceElement(HtmlElement const& element)
145+
{
146+
if (!element_.isUndefined())
147+
throw std::runtime_error("Element is not empty, cannot emplace");
148+
149+
element_ = createElement(element);
150+
setup(element);
151+
if (deferredSetup_)
152+
deferredSetup_(element);
153+
return shared_from_base<Element>();
154+
}
155+
auto emplaceElement(std::invocable<Element&, Renderer const&> auto&& fn)
156+
{
157+
fn(*this, Renderer{.type = RendererType::Emplace});
158+
return shared_from_base<Element>();
159+
}
131160

132161
void setTextContent(std::string const& text)
133162
{
@@ -275,6 +304,11 @@ namespace Nui::Dom
275304
return children_.size();
276305
}
277306

307+
std::string tagName() const
308+
{
309+
return element_["tagName"].as<std::string>();
310+
}
311+
278312
private:
279313
void replaceElementImpl(HtmlElement const& element)
280314
{
@@ -283,6 +317,11 @@ namespace Nui::Dom
283317
unsetup_();
284318
unsetup_ = {};
285319

320+
#ifndef NDEBUG
321+
if (element_.isUndefined())
322+
throw std::runtime_error("Element is undefined");
323+
#endif
324+
286325
auto replacement = createElement(element);
287326
element_.call<Nui::val>("replaceWith", replacement);
288327
element_ = std::move(replacement);
@@ -298,6 +337,13 @@ namespace Nui::Dom
298337
std::function<void()> unsetup_;
299338
std::function<void(HtmlElement const& element)> deferredSetup_;
300339
};
340+
341+
inline std::shared_ptr<Element> makeStandaloneElement(std::invocable<Element&, Renderer const&> auto&& fn)
342+
{
343+
auto elem = std::make_shared<Element>();
344+
fn(*elem, Renderer{.type = RendererType::Emplace});
345+
return elem;
346+
}
301347
}
302348

303349
#include <nui/frontend/elements/impl/html_element.tpp>

nui/include/nui/frontend/elements/impl/materialize.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ namespace Nui
2727
{
2828
return element.replaceElement(htmlElement);
2929
}
30+
/// Replaces the given element with the new one.
31+
inline auto emplaceMaterialize(auto& element, auto const& htmlElement)
32+
{
33+
return element.emplaceElement(htmlElement);
34+
}
3035
/// Used for elements that dont have a direct parent.
3136
inline auto inplaceMaterialize(auto& element, auto const&)
3237
{
@@ -40,7 +45,8 @@ namespace Nui
4045
Fragment,
4146
Insert,
4247
Replace,
43-
Inplace
48+
Inplace,
49+
Emplace
4450
};
4551
struct Renderer
4652
{
@@ -61,6 +67,8 @@ namespace Nui
6167
return Materializers::replaceMaterialize(element, htmlElement);
6268
case RendererType::Inplace:
6369
return Materializers::inplaceMaterialize(element, htmlElement);
70+
case RendererType::Emplace:
71+
return Materializers::emplaceMaterialize(element, htmlElement);
6472
}
6573
};
6674
}

nui/test/nui/emscripten_mock/emscripten/val.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ namespace emscripten
291291
#ifdef NUI_TEST_DEBUG_PRINT
292292
std::cout << "val::undefined()\n";
293293
#endif
294-
return {};
294+
return Nui::Tests::Engine::createValue();
295295
}
296296

297297
static val null()

nui/test/nui/test_attributes.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,4 +539,14 @@ namespace Nui::Tests
539539

540540
EXPECT_FALSE(Nui::val::global("document")["body"]["attributes"].hasOwnProperty("id"));
541541
}
542+
543+
TEST_F(TestAttributes, CanSetDeferredAttribute)
544+
{
545+
using Nui::Elements::div;
546+
using Nui::Attributes::id;
547+
548+
render(div{!id = "hi"}());
549+
550+
EXPECT_EQ(Nui::val::global("document")["body"]["attributes"]["id"].as<std::string>(), "hi");
551+
}
542552
}

nui/test/nui/test_elements.hpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#pragma once
2+
3+
#include <gtest/gtest.h>
4+
5+
#include "common_test_fixture.hpp"
6+
#include "engine/global_object.hpp"
7+
#include "engine/document.hpp"
8+
#include "engine/object.hpp"
9+
10+
#include <nui/frontend/dom/element.hpp>
11+
#include <nui/frontend/elements.hpp>
12+
#include <nui/frontend/attributes.hpp>
13+
14+
namespace Nui::Tests
15+
{
16+
using namespace Engine;
17+
using namespace std::string_literals;
18+
19+
class TestElements : public CommonTestFixture
20+
{};
21+
22+
TEST_F(TestElements, CanMakeStandaloneElement)
23+
{
24+
using Nui::Elements::div;
25+
26+
EXPECT_NO_THROW(std::shared_ptr<Nui::Dom::Element> element = Nui::Dom::makeStandaloneElement(div{}("Test")));
27+
}
28+
29+
TEST_F(TestElements, StandaloneElementIsOfCorrectType)
30+
{
31+
using Nui::Elements::button;
32+
33+
std::shared_ptr<Nui::Dom::Element> element = Nui::Dom::makeStandaloneElement(button{}("Test"));
34+
35+
EXPECT_EQ(element->tagName(), "button"s);
36+
}
37+
38+
TEST_F(TestElements, StandaloneElementTestContentIsSet)
39+
{
40+
using Nui::Elements::div;
41+
42+
std::shared_ptr<Nui::Dom::Element> element = Nui::Dom::makeStandaloneElement(div{}("Test"));
43+
44+
EXPECT_EQ(element->val()["textContent"].as<std::string>(), "Test"s);
45+
}
46+
47+
TEST_F(TestElements, AttributesOnStandaloneElementAreSet)
48+
{
49+
using Nui::Elements::div;
50+
using Nui::Attributes::class_;
51+
52+
std::shared_ptr<Nui::Dom::Element> element = Nui::Dom::makeStandaloneElement(div{class_ = "hi"}("Test"));
53+
54+
EXPECT_EQ(element->val()["attributes"]["class"].as<std::string>(), "hi"s);
55+
}
56+
57+
TEST_F(TestElements, CanSetDeferredAttributesOnStandaloneElement)
58+
{
59+
using Nui::Elements::div;
60+
using Nui::Attributes::class_;
61+
62+
std::function<void()> deferred;
63+
std::shared_ptr<Nui::Dom::Element> element = Nui::Dom::makeStandaloneElement(div{!class_ = "hi"}("Test"));
64+
65+
EXPECT_EQ(element->val()["attributes"]["class"].as<std::string>(), "hi"s);
66+
}
67+
}

nui/test/nui/tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "test_render.hpp"
55
#include "test_switch.hpp"
66
#include "test_events.hpp"
7+
#include "test_elements.hpp"
78
#include "test_selectables_registry.hpp"
89
#include "components/test_table.hpp"
910
#include "components/test_dialog.hpp"

0 commit comments

Comments
 (0)