Skip to content

Commit

Permalink
Added factory for elements not in dom.
Browse files Browse the repository at this point in the history
  • Loading branch information
5cript committed Jul 18, 2024
1 parent d5a9f96 commit 51bf855
Show file tree
Hide file tree
Showing 9 changed files with 185 additions and 11 deletions.
52 changes: 43 additions & 9 deletions nui/include/nui/frontend/attributes/impl/attribute_factory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,12 @@ namespace Nui::Attributes
: name_{name}
{}

// Dont use this class like a value
PropertyFactory(PropertyFactory const&) = delete;
PropertyFactory(PropertyFactory&&) = delete;
explicit constexpr PropertyFactory(PropertyFactory const& other)
: name_{other.name_}
{}
explicit constexpr PropertyFactory(PropertyFactory&& other)
: name_{other.name_}
{}
PropertyFactory& operator=(PropertyFactory const&) = delete;
PropertyFactory& operator=(PropertyFactory&&) = delete;

Expand Down Expand Up @@ -181,9 +184,12 @@ namespace Nui::Attributes
: name_{name}
{}

// Dont use this class like a value
AttributeFactory(AttributeFactory const&) = delete;
AttributeFactory(AttributeFactory&&) = delete;
explicit constexpr AttributeFactory(AttributeFactory const& other)
: name_{other.name_}
{}
explicit constexpr AttributeFactory(AttributeFactory&& other)
: name_{other.name_}
{}
AttributeFactory& operator=(AttributeFactory const&) = delete;
AttributeFactory& operator=(AttributeFactory&&) = delete;

Expand Down Expand Up @@ -343,9 +349,12 @@ namespace Nui::Attributes
: name_{name}
{}

// Dont use this class like a value
EventFactory(EventFactory const&) = delete;
EventFactory(EventFactory&&) = delete;
explicit constexpr EventFactory(EventFactory const& other)
: name_{other.name_}
{}
explicit constexpr EventFactory(EventFactory&& other)
: name_{other.name_}
{}
EventFactory& operator=(EventFactory const&) = delete;
EventFactory& operator=(EventFactory&&) = delete;

Expand Down Expand Up @@ -397,6 +406,31 @@ namespace Nui::Attributes
return EventFactory{name};
}
}

namespace Detail
{
template <typename T>
struct DeferWrap
{
T factory;

template <typename... Args>
Attribute operator=(Args&&... args) const
{
auto attr = factory.operator=(std::forward<Args>(args)...);
attr.defer(true);
return attr;
};
};
}

template <typename T>
requires(
std::is_same_v<T, AttributeFactory> || std::is_same_v<T, PropertyFactory> || std::is_same_v<T, EventFactory>)
Detail::DeferWrap<T> operator!(T const& factory)
{
return Detail::DeferWrap<T>{.factory = T{std::move(factory)}};
}
}

#define MAKE_HTML_VALUE_ATTRIBUTE_RENAME(NAME, HTML_NAME) \
Expand Down
4 changes: 4 additions & 0 deletions nui/include/nui/frontend/dom/basic_element.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ namespace Nui::Dom
}

protected:
explicit BasicElement()
: element_{Nui::val::undefined()}
{}

Nui::val element_;
};
}
4 changes: 4 additions & 0 deletions nui/include/nui/frontend/dom/childless_element.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ namespace Nui::Dom
}

protected:
explicit ChildlessElement()
: BasicElement{}
{}

static Nui::val createElement(HtmlElement const& element)
{
return element.bridge()->createElement(element);
Expand Down
46 changes: 46 additions & 0 deletions nui/include/nui/frontend/dom/element.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,26 @@ namespace Nui::Dom
, deferredSetup_{}
{}

/**
* @brief This constructor takes ownership of a val.
* This val must not be managed by any other element.
*
* @param val
*/
explicit Element(Nui::val val)
: ChildlessElement{std::move(val)}
, children_{}
, unsetup_{}
, deferredSetup_{}
{}

explicit Element()
: ChildlessElement{}
, children_{}
, unsetup_{}
, deferredSetup_{}
{}

Element(Element const&) = delete;
Element(Element&&) = delete;
Element& operator=(Element const&) = delete;
Expand Down Expand Up @@ -128,6 +141,22 @@ namespace Nui::Dom
replaceElementImpl(element);
return shared_from_base<Element>();
}
auto emplaceElement(HtmlElement const& element)
{
if (!element_.isUndefined())
throw std::runtime_error("Element is not empty, cannot emplace");

element_ = createElement(element);
setup(element);
if (deferredSetup_)
deferredSetup_(element);
return shared_from_base<Element>();
}
auto emplaceElement(std::invocable<Element&, Renderer const&> auto&& fn)
{
fn(*this, Renderer{.type = RendererType::Emplace});
return shared_from_base<Element>();
}

void setTextContent(std::string const& text)
{
Expand Down Expand Up @@ -275,6 +304,11 @@ namespace Nui::Dom
return children_.size();
}

std::string tagName() const
{
return element_["tagName"].as<std::string>();
}

private:
void replaceElementImpl(HtmlElement const& element)
{
Expand All @@ -283,6 +317,11 @@ namespace Nui::Dom
unsetup_();
unsetup_ = {};

#ifndef NDEBUG
if (element_.isUndefined())
throw std::runtime_error("Element is undefined");
#endif

auto replacement = createElement(element);
element_.call<Nui::val>("replaceWith", replacement);
element_ = std::move(replacement);
Expand All @@ -298,6 +337,13 @@ namespace Nui::Dom
std::function<void()> unsetup_;
std::function<void(HtmlElement const& element)> deferredSetup_;
};

inline std::shared_ptr<Element> makeStandaloneElement(std::invocable<Element&, Renderer const&> auto&& fn)
{
auto elem = std::make_shared<Element>();
fn(*elem, Renderer{.type = RendererType::Emplace});
return elem;
}
}

#include <nui/frontend/elements/impl/html_element.tpp>
Expand Down
10 changes: 9 additions & 1 deletion nui/include/nui/frontend/elements/impl/materialize.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ namespace Nui
{
return element.replaceElement(htmlElement);
}
/// Replaces the given element with the new one.
inline auto emplaceMaterialize(auto& element, auto const& htmlElement)
{
return element.emplaceElement(htmlElement);
}
/// Used for elements that dont have a direct parent.
inline auto inplaceMaterialize(auto& element, auto const&)
{
Expand All @@ -40,7 +45,8 @@ namespace Nui
Fragment,
Insert,
Replace,
Inplace
Inplace,
Emplace
};
struct Renderer
{
Expand All @@ -61,6 +67,8 @@ namespace Nui
return Materializers::replaceMaterialize(element, htmlElement);
case RendererType::Inplace:
return Materializers::inplaceMaterialize(element, htmlElement);
case RendererType::Emplace:
return Materializers::emplaceMaterialize(element, htmlElement);
}
};
}
2 changes: 1 addition & 1 deletion nui/test/nui/emscripten_mock/emscripten/val.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ namespace emscripten
#ifdef NUI_TEST_DEBUG_PRINT
std::cout << "val::undefined()\n";
#endif
return {};
return Nui::Tests::Engine::createValue();
}

static val null()
Expand Down
10 changes: 10 additions & 0 deletions nui/test/nui/test_attributes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,4 +539,14 @@ namespace Nui::Tests

EXPECT_FALSE(Nui::val::global("document")["body"]["attributes"].hasOwnProperty("id"));
}

TEST_F(TestAttributes, CanSetDeferredAttribute)
{
using Nui::Elements::div;
using Nui::Attributes::id;

render(div{!id = "hi"}());

EXPECT_EQ(Nui::val::global("document")["body"]["attributes"]["id"].as<std::string>(), "hi");
}
}
67 changes: 67 additions & 0 deletions nui/test/nui/test_elements.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#include <gtest/gtest.h>

#include "common_test_fixture.hpp"
#include "engine/global_object.hpp"
#include "engine/document.hpp"
#include "engine/object.hpp"

#include <nui/frontend/dom/element.hpp>
#include <nui/frontend/elements.hpp>
#include <nui/frontend/attributes.hpp>

namespace Nui::Tests
{
using namespace Engine;
using namespace std::string_literals;

class TestElements : public CommonTestFixture
{};

TEST_F(TestElements, CanMakeStandaloneElement)
{
using Nui::Elements::div;

EXPECT_NO_THROW(std::shared_ptr<Nui::Dom::Element> element = Nui::Dom::makeStandaloneElement(div{}("Test")));
}

TEST_F(TestElements, StandaloneElementIsOfCorrectType)
{
using Nui::Elements::button;

std::shared_ptr<Nui::Dom::Element> element = Nui::Dom::makeStandaloneElement(button{}("Test"));

EXPECT_EQ(element->tagName(), "button"s);
}

TEST_F(TestElements, StandaloneElementTestContentIsSet)
{
using Nui::Elements::div;

std::shared_ptr<Nui::Dom::Element> element = Nui::Dom::makeStandaloneElement(div{}("Test"));

EXPECT_EQ(element->val()["textContent"].as<std::string>(), "Test"s);
}

TEST_F(TestElements, AttributesOnStandaloneElementAreSet)
{
using Nui::Elements::div;
using Nui::Attributes::class_;

std::shared_ptr<Nui::Dom::Element> element = Nui::Dom::makeStandaloneElement(div{class_ = "hi"}("Test"));

EXPECT_EQ(element->val()["attributes"]["class"].as<std::string>(), "hi"s);
}

TEST_F(TestElements, CanSetDeferredAttributesOnStandaloneElement)
{
using Nui::Elements::div;
using Nui::Attributes::class_;

std::function<void()> deferred;
std::shared_ptr<Nui::Dom::Element> element = Nui::Dom::makeStandaloneElement(div{!class_ = "hi"}("Test"));

EXPECT_EQ(element->val()["attributes"]["class"].as<std::string>(), "hi"s);
}
}
1 change: 1 addition & 0 deletions nui/test/nui/tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "test_render.hpp"
#include "test_switch.hpp"
#include "test_events.hpp"
#include "test_elements.hpp"
#include "test_selectables_registry.hpp"
#include "components/test_table.hpp"
#include "components/test_dialog.hpp"
Expand Down

0 comments on commit 51bf855

Please sign in to comment.