Skip to content

Commit

Permalink
Merge pull request #80 from NuiCpp/devel
Browse files Browse the repository at this point in the history
Fixed non-const regressions.
  • Loading branch information
5cript authored Oct 4, 2023
2 parents d751320 + e52b893 commit 2425714
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 32 deletions.
3 changes: 1 addition & 2 deletions cmake/dependencies/gtest.cmake
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
option(NUI_FETCH_GTEST "Fetch gtest" ON)
set(NUI_GTEST_GIT_REPOSITORY "https://github.com/google/googletest.git" CACHE STRING "gtest git repository")
set(NUI_GTEST_GIT_TAG "cead3d57c93ff8c4e5c1bbae57a5c0b0b0f6e168" CACHE STRING "gtest git tag")
set(NUI_GTEST_GIT_TAG "beb552fb47e9e8a6ddab20526663c2dddd601ec6" CACHE STRING "gtest git tag")

if(NUI_FETCH_GTEST)
include(FetchContent)
FetchContent_Declare(
gtest
GIT_REPOSITORY ${NUI_GTEST_GIT_REPOSITORY}
GIT_TAG ${NUI_GTEST_GIT_TAG}
FIND_PACKAGE_ARGS NAMES gtest
)

FetchContent_MakeAvailable(gtest)
Expand Down
1 change: 1 addition & 0 deletions nui/include/nui/frontend/elements.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <nui/frontend/elements/caption.hpp>
#include <nui/frontend/elements/cite.hpp>
#include <nui/frontend/elements/code.hpp>
#include <nui/frontend/elements/comment.hpp>
#include <nui/frontend/elements/data.hpp>
#include <nui/frontend/elements/datalist.hpp>
#include <nui/frontend/elements/dd.hpp>
Expand Down
48 changes: 48 additions & 0 deletions nui/include/nui/frontend/elements/comment.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#pragma once

#include <nui/frontend/elements/impl/html_element_incl.hpp>
#include <nui/frontend/event_system/observed_value.hpp>
#include <nui/frontend/event_system/event_context.hpp>

#include <string_view>

namespace Nui::Elements
{
struct comment : HtmlElement
{
comment(comment const&) = default;
comment(comment&&) = default;
comment(std::string_view content)
: HtmlElement{"", &CommentElementBridge, Attribute{content, {}, {}}}
{}
comment(Nui::Observed<std::string>& obs)
: HtmlElement{
"",
&CommentElementBridge,
Attribute{
obs.value(),
[&obs](std::weak_ptr<Dom::ChildlessElement>&& element) {
const auto eventId = globalEventContext.registerEvent(Event{
[element, &obs](auto eventId) {
if (auto shared = element.lock(); shared)
{
shared->setNodeValue(obs.value());
return true;
}
obs.unattachEvent(eventId);
return false;
},
[element]() {
return !element.expired();
}});
obs.attachEvent(eventId);
return eventId;
},
[&obs](EventContext::EventIdType const& id) {
obs.unattachEvent(id);
},
},
}
{}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,12 @@ namespace Nui
.call<Nui::val>("createTextNode", Nui::val{element.attributes()[0].stringData()});
},
};

constexpr auto CommentElementBridge = HtmlElementBridge{
.createElement =
+[](HtmlElement const& element) {
return Nui::val::global("document")
.call<Nui::val>("createComment", Nui::val{element.attributes()[0].stringData()});
},
};
}
48 changes: 18 additions & 30 deletions nui/include/nui/frontend/elements/impl/range_renderer.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace Nui::Detail
using ObservedType = typename RangeT::ObservedType;

template <typename Generator = GeneratorT>
BasicObservedRenderer(ObservedType const& observed, Generator&& elementRenderer)
BasicObservedRenderer(ObservedType& observed, Generator&& elementRenderer)
: valueRange_{observed}
, elementRenderer_{std::forward<GeneratorT>(elementRenderer)}
{}
Expand All @@ -23,7 +23,6 @@ namespace Nui::Detail
BasicObservedRenderer& operator=(BasicObservedRenderer const&) = delete;
BasicObservedRenderer& operator=(BasicObservedRenderer&&) = delete;

protected:
bool fullRangeUpdate(auto& parent) const
{
if (valueRange_.rangeContext().isFullRangeUpdate())
Expand All @@ -39,22 +38,8 @@ namespace Nui::Detail

virtual bool updateChildren() const = 0;

void operator()(auto& materialized)
{
weakMaterialized_ = materialized;
valueRange_.attachEvent(Nui::globalEventContext.registerEvent(Event{
[self = this->shared_from_this()](int) -> bool {
return self->updateChildren();
},
[this /* fine because other function holds this */]() {
return !weakMaterialized_.expired();
},
}));
updateChildren();
}

protected:
ObservedType const& valueRange_;
ObservedType& valueRange_;
GeneratorT elementRenderer_;
std::weak_ptr<Nui::Dom::Element> weakMaterialized_;
};
Expand All @@ -69,19 +54,7 @@ namespace Nui::Detail
using BasicObservedRenderer<RangeT, GeneratorT>::weakMaterialized_;
using BasicObservedRenderer<RangeT, GeneratorT>::valueRange_;
using BasicObservedRenderer<RangeT, GeneratorT>::elementRenderer_;

bool fullRangeUpdate(auto& parent) const
{
if (valueRange_.rangeContext().isFullRangeUpdate())
{
parent->clearChildren();
long long counter = 0;
for (auto& element : valueRange_.value())
elementRenderer_(counter++, element)(*parent, Renderer{.type = RendererType::Append});
return true;
}
return false;
}
using BasicObservedRenderer<RangeT, GeneratorT>::fullRangeUpdate;

void insertions(auto& parent) const
{
Expand Down Expand Up @@ -161,6 +134,7 @@ namespace Nui::Detail
using BasicObservedRenderer<RangeT, GeneratorT>::weakMaterialized_;
using BasicObservedRenderer<RangeT, GeneratorT>::valueRange_;
using BasicObservedRenderer<RangeT, GeneratorT>::elementRenderer_;
using BasicObservedRenderer<RangeT, GeneratorT>::fullRangeUpdate;

bool updateChildren() const override
{
Expand All @@ -171,6 +145,20 @@ namespace Nui::Detail
fullRangeUpdate(parent);
return KeepRange;
}

void operator()(auto& materialized)
{
weakMaterialized_ = materialized;
valueRange_.attachEvent(Nui::globalEventContext.registerEvent(Event{
[self = this->shared_from_this()](int) -> bool {
return self->updateChildren();
},
[this /* fine because other function holds this */]() {
return !weakMaterialized_.expired();
},
}));
updateChildren();
}
};

template <typename RangeLike, typename GeneratorT, typename... ObservedT>
Expand Down
44 changes: 44 additions & 0 deletions nui/include/nui/frontend/event_system/range.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ namespace Nui
: observedValues_{std::move(observedValues)}
, rangeLike_{std::move(rangeLike)}
{}
UnoptimizedRange(CopyableRangeLike&& rangeLike)
requires(sizeof...(ObservedValues) == 0)
: observedValues_{}
, rangeLike_{std::move(rangeLike)}
{}

auto begin() const
{
Expand All @@ -61,6 +66,10 @@ namespace Nui
{
return observedValues_;
}
ObservedValueCombinator<ObservedValues...>& underlying()
{
return observedValues_;
}

private:
ObservedValueCombinator<ObservedValues...> observedValues_;
Expand Down Expand Up @@ -101,6 +110,41 @@ namespace Nui
};
}

template <typename ContainerT, typename... Observed>
UnoptimizedRange<IteratorAccessor<ContainerT const>, Observed...>
range(ContainerT const& container, Observed&... observed)
{
return UnoptimizedRange<IteratorAccessor<ContainerT const>, Observed...>{
ObservedValueCombinator{observed...},
IteratorAccessor<ContainerT const>{container},
};
}

template <typename ContainerT, typename... Observed>
UnoptimizedRange<IteratorAccessor<ContainerT>, Observed...> range(ContainerT& container, Observed&... observed)
{
return UnoptimizedRange<IteratorAccessor<ContainerT>, Observed...>{
ObservedValueCombinator{observed...},
IteratorAccessor<ContainerT>{container},
};
}

template <typename ContainerT>
UnoptimizedRange<IteratorAccessor<ContainerT>> range(ContainerT& container)
{
return UnoptimizedRange<IteratorAccessor<ContainerT>>{
IteratorAccessor<ContainerT>{container},
};
}

template <typename ContainerT>
UnoptimizedRange<IteratorAccessor<ContainerT>> range(ContainerT const& container)
{
return UnoptimizedRange<IteratorAccessor<ContainerT>>{
IteratorAccessor<ContainerT>{container},
};
}

#ifdef NUI_HAS_STD_RANGES
template <typename T, typename... Observed>
UnoptimizedRange<std::ranges::subrange<std::ranges::iterator_t<T const>>, Observed...>
Expand Down
115 changes: 115 additions & 0 deletions nui/test/nui/test_ranges.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -623,4 +623,119 @@ namespace Nui::Tests
std::string{characters[i]} + ":" + std::to_string(i));
}
}

TEST_F(TestRanges, StaticRangeRendererCanTakeNonConstElement)
{
std::vector<char> characters{'A', 'B', 'C', 'D'};
Nui::val parent;

using Nui::Elements::div;
using Nui::Elements::body;
using namespace Nui::Attributes;

render(body{reference = parent}(range(characters), [&characters](long long i, auto& element) {
element = 'X';
return div{}(std::string{element} + ":" + std::to_string(i));
}));

EXPECT_EQ(parent["children"]["length"].as<long long>(), static_cast<long long>(characters.size()));
for (int i = 0; i != characters.size(); ++i)
{
EXPECT_EQ(parent["children"][i]["textContent"].as<std::string>(), "X:" + std::to_string(i));
}
}

TEST_F(TestRanges, ObservedRangeRendererCanTakeNonConstElement)
{
Nui::Observed<std::vector<char>> characters{{'A', 'B', 'C', 'D'}};
Nui::val parent;

using Nui::Elements::div;
using Nui::Elements::body;
using namespace Nui::Attributes;

render(body{reference = parent}(range(characters), [&characters](long long i, auto& element) {
element = 'X';
return div{}(std::string{element} + ":" + std::to_string(i));
}));

EXPECT_EQ(parent["children"]["length"].as<long long>(), static_cast<long long>(characters.size()));
for (int i = 0; i != characters.size(); ++i)
{
EXPECT_EQ(parent["children"][i]["textContent"].as<std::string>(), "X:" + std::to_string(i));
}
}

TEST_F(TestRanges, StaticRangeRendererCanTakeConstObserved)
{
std::vector<char> characters{'A', 'B', 'C', 'D'};
Nui::val parent;
Nui::Observed<bool> other{true};

using Nui::Elements::div;
using Nui::Elements::body;
using namespace Nui::Attributes;

auto doRender = [&](Nui::Observed<bool> const& constObserved) {
render(body{reference = parent}(
range(characters, constObserved), [&characters](long long i, auto const& element) {
return div{}(std::string{element} + ":" + std::to_string(i));
}));
};
doRender(other);

EXPECT_EQ(parent["children"]["length"].as<long long>(), static_cast<long long>(characters.size()));
for (int i = 0; i != characters.size(); ++i)
{
EXPECT_EQ(
parent["children"][i]["textContent"].as<std::string>(),
std::string{characters[i]} + ":" + std::to_string(i));
}
}

TEST_F(TestRanges, CanUseObservedNonRandomAccessRange)
{
Nui::Observed<std::set<char>> characters{{'A', 'B', 'C', 'D'}};
Nui::val parent;

using Nui::Elements::div;
using Nui::Elements::body;
using namespace Nui::Attributes;

render(body{reference = parent}(range(characters), [&characters](long long i, auto const& element) {
return div{}(std::string{element} + ":" + std::to_string(i));
}));

EXPECT_EQ(parent["children"]["length"].as<long long>(), static_cast<long long>(characters.size()));
int i = 0;
for (auto const& elem : characters.value())
{
EXPECT_EQ(
parent["children"][i]["textContent"].as<std::string>(), std::string{elem} + ":" + std::to_string(i));
++i;
}
}

TEST_F(TestRanges, CanUseStaticNonRandomAccessRange)
{
std::set<char> characters{'A', 'B', 'C', 'D'};
Nui::val parent;

using Nui::Elements::div;
using Nui::Elements::body;
using namespace Nui::Attributes;

render(body{reference = parent}(range(characters), [&characters](long long i, auto const& element) {
return div{}(std::string{element} + ":" + std::to_string(i));
}));

EXPECT_EQ(parent["children"]["length"].as<long long>(), static_cast<long long>(characters.size()));
int i = 0;
for (auto const& elem : characters)
{
EXPECT_EQ(
parent["children"][i]["textContent"].as<std::string>(), std::string{elem} + ":" + std::to_string(i));
++i;
}
}
}

0 comments on commit 2425714

Please sign in to comment.