Skip to content

Commit 2425714

Browse files
authored
Merge pull request #80 from NuiCpp/devel
Fixed non-const regressions.
2 parents d751320 + e52b893 commit 2425714

File tree

7 files changed

+235
-32
lines changed

7 files changed

+235
-32
lines changed

cmake/dependencies/gtest.cmake

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
option(NUI_FETCH_GTEST "Fetch gtest" ON)
22
set(NUI_GTEST_GIT_REPOSITORY "https://github.com/google/googletest.git" CACHE STRING "gtest git repository")
3-
set(NUI_GTEST_GIT_TAG "cead3d57c93ff8c4e5c1bbae57a5c0b0b0f6e168" CACHE STRING "gtest git tag")
3+
set(NUI_GTEST_GIT_TAG "beb552fb47e9e8a6ddab20526663c2dddd601ec6" CACHE STRING "gtest git tag")
44

55
if(NUI_FETCH_GTEST)
66
include(FetchContent)
77
FetchContent_Declare(
88
gtest
99
GIT_REPOSITORY ${NUI_GTEST_GIT_REPOSITORY}
1010
GIT_TAG ${NUI_GTEST_GIT_TAG}
11-
FIND_PACKAGE_ARGS NAMES gtest
1211
)
1312

1413
FetchContent_MakeAvailable(gtest)

nui/include/nui/frontend/elements.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <nui/frontend/elements/caption.hpp>
1818
#include <nui/frontend/elements/cite.hpp>
1919
#include <nui/frontend/elements/code.hpp>
20+
#include <nui/frontend/elements/comment.hpp>
2021
#include <nui/frontend/elements/data.hpp>
2122
#include <nui/frontend/elements/datalist.hpp>
2223
#include <nui/frontend/elements/dd.hpp>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#pragma once
2+
3+
#include <nui/frontend/elements/impl/html_element_incl.hpp>
4+
#include <nui/frontend/event_system/observed_value.hpp>
5+
#include <nui/frontend/event_system/event_context.hpp>
6+
7+
#include <string_view>
8+
9+
namespace Nui::Elements
10+
{
11+
struct comment : HtmlElement
12+
{
13+
comment(comment const&) = default;
14+
comment(comment&&) = default;
15+
comment(std::string_view content)
16+
: HtmlElement{"", &CommentElementBridge, Attribute{content, {}, {}}}
17+
{}
18+
comment(Nui::Observed<std::string>& obs)
19+
: HtmlElement{
20+
"",
21+
&CommentElementBridge,
22+
Attribute{
23+
obs.value(),
24+
[&obs](std::weak_ptr<Dom::ChildlessElement>&& element) {
25+
const auto eventId = globalEventContext.registerEvent(Event{
26+
[element, &obs](auto eventId) {
27+
if (auto shared = element.lock(); shared)
28+
{
29+
shared->setNodeValue(obs.value());
30+
return true;
31+
}
32+
obs.unattachEvent(eventId);
33+
return false;
34+
},
35+
[element]() {
36+
return !element.expired();
37+
}});
38+
obs.attachEvent(eventId);
39+
return eventId;
40+
},
41+
[&obs](EventContext::EventIdType const& id) {
42+
obs.unattachEvent(id);
43+
},
44+
},
45+
}
46+
{}
47+
};
48+
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,12 @@ namespace Nui
3030
.call<Nui::val>("createTextNode", Nui::val{element.attributes()[0].stringData()});
3131
},
3232
};
33+
34+
constexpr auto CommentElementBridge = HtmlElementBridge{
35+
.createElement =
36+
+[](HtmlElement const& element) {
37+
return Nui::val::global("document")
38+
.call<Nui::val>("createComment", Nui::val{element.attributes()[0].stringData()});
39+
},
40+
};
3341
}

nui/include/nui/frontend/elements/impl/range_renderer.tpp

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace Nui::Detail
1313
using ObservedType = typename RangeT::ObservedType;
1414

1515
template <typename Generator = GeneratorT>
16-
BasicObservedRenderer(ObservedType const& observed, Generator&& elementRenderer)
16+
BasicObservedRenderer(ObservedType& observed, Generator&& elementRenderer)
1717
: valueRange_{observed}
1818
, elementRenderer_{std::forward<GeneratorT>(elementRenderer)}
1919
{}
@@ -23,7 +23,6 @@ namespace Nui::Detail
2323
BasicObservedRenderer& operator=(BasicObservedRenderer const&) = delete;
2424
BasicObservedRenderer& operator=(BasicObservedRenderer&&) = delete;
2525

26-
protected:
2726
bool fullRangeUpdate(auto& parent) const
2827
{
2928
if (valueRange_.rangeContext().isFullRangeUpdate())
@@ -39,22 +38,8 @@ namespace Nui::Detail
3938

4039
virtual bool updateChildren() const = 0;
4140

42-
void operator()(auto& materialized)
43-
{
44-
weakMaterialized_ = materialized;
45-
valueRange_.attachEvent(Nui::globalEventContext.registerEvent(Event{
46-
[self = this->shared_from_this()](int) -> bool {
47-
return self->updateChildren();
48-
},
49-
[this /* fine because other function holds this */]() {
50-
return !weakMaterialized_.expired();
51-
},
52-
}));
53-
updateChildren();
54-
}
55-
5641
protected:
57-
ObservedType const& valueRange_;
42+
ObservedType& valueRange_;
5843
GeneratorT elementRenderer_;
5944
std::weak_ptr<Nui::Dom::Element> weakMaterialized_;
6045
};
@@ -69,19 +54,7 @@ namespace Nui::Detail
6954
using BasicObservedRenderer<RangeT, GeneratorT>::weakMaterialized_;
7055
using BasicObservedRenderer<RangeT, GeneratorT>::valueRange_;
7156
using BasicObservedRenderer<RangeT, GeneratorT>::elementRenderer_;
72-
73-
bool fullRangeUpdate(auto& parent) const
74-
{
75-
if (valueRange_.rangeContext().isFullRangeUpdate())
76-
{
77-
parent->clearChildren();
78-
long long counter = 0;
79-
for (auto& element : valueRange_.value())
80-
elementRenderer_(counter++, element)(*parent, Renderer{.type = RendererType::Append});
81-
return true;
82-
}
83-
return false;
84-
}
57+
using BasicObservedRenderer<RangeT, GeneratorT>::fullRangeUpdate;
8558

8659
void insertions(auto& parent) const
8760
{
@@ -161,6 +134,7 @@ namespace Nui::Detail
161134
using BasicObservedRenderer<RangeT, GeneratorT>::weakMaterialized_;
162135
using BasicObservedRenderer<RangeT, GeneratorT>::valueRange_;
163136
using BasicObservedRenderer<RangeT, GeneratorT>::elementRenderer_;
137+
using BasicObservedRenderer<RangeT, GeneratorT>::fullRangeUpdate;
164138

165139
bool updateChildren() const override
166140
{
@@ -171,6 +145,20 @@ namespace Nui::Detail
171145
fullRangeUpdate(parent);
172146
return KeepRange;
173147
}
148+
149+
void operator()(auto& materialized)
150+
{
151+
weakMaterialized_ = materialized;
152+
valueRange_.attachEvent(Nui::globalEventContext.registerEvent(Event{
153+
[self = this->shared_from_this()](int) -> bool {
154+
return self->updateChildren();
155+
},
156+
[this /* fine because other function holds this */]() {
157+
return !weakMaterialized_.expired();
158+
},
159+
}));
160+
updateChildren();
161+
}
174162
};
175163

176164
template <typename RangeLike, typename GeneratorT, typename... ObservedT>

nui/include/nui/frontend/event_system/range.hpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ namespace Nui
4747
: observedValues_{std::move(observedValues)}
4848
, rangeLike_{std::move(rangeLike)}
4949
{}
50+
UnoptimizedRange(CopyableRangeLike&& rangeLike)
51+
requires(sizeof...(ObservedValues) == 0)
52+
: observedValues_{}
53+
, rangeLike_{std::move(rangeLike)}
54+
{}
5055

5156
auto begin() const
5257
{
@@ -61,6 +66,10 @@ namespace Nui
6166
{
6267
return observedValues_;
6368
}
69+
ObservedValueCombinator<ObservedValues...>& underlying()
70+
{
71+
return observedValues_;
72+
}
6473

6574
private:
6675
ObservedValueCombinator<ObservedValues...> observedValues_;
@@ -101,6 +110,41 @@ namespace Nui
101110
};
102111
}
103112

113+
template <typename ContainerT, typename... Observed>
114+
UnoptimizedRange<IteratorAccessor<ContainerT const>, Observed...>
115+
range(ContainerT const& container, Observed&... observed)
116+
{
117+
return UnoptimizedRange<IteratorAccessor<ContainerT const>, Observed...>{
118+
ObservedValueCombinator{observed...},
119+
IteratorAccessor<ContainerT const>{container},
120+
};
121+
}
122+
123+
template <typename ContainerT, typename... Observed>
124+
UnoptimizedRange<IteratorAccessor<ContainerT>, Observed...> range(ContainerT& container, Observed&... observed)
125+
{
126+
return UnoptimizedRange<IteratorAccessor<ContainerT>, Observed...>{
127+
ObservedValueCombinator{observed...},
128+
IteratorAccessor<ContainerT>{container},
129+
};
130+
}
131+
132+
template <typename ContainerT>
133+
UnoptimizedRange<IteratorAccessor<ContainerT>> range(ContainerT& container)
134+
{
135+
return UnoptimizedRange<IteratorAccessor<ContainerT>>{
136+
IteratorAccessor<ContainerT>{container},
137+
};
138+
}
139+
140+
template <typename ContainerT>
141+
UnoptimizedRange<IteratorAccessor<ContainerT>> range(ContainerT const& container)
142+
{
143+
return UnoptimizedRange<IteratorAccessor<ContainerT>>{
144+
IteratorAccessor<ContainerT>{container},
145+
};
146+
}
147+
104148
#ifdef NUI_HAS_STD_RANGES
105149
template <typename T, typename... Observed>
106150
UnoptimizedRange<std::ranges::subrange<std::ranges::iterator_t<T const>>, Observed...>

nui/test/nui/test_ranges.hpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,4 +623,119 @@ namespace Nui::Tests
623623
std::string{characters[i]} + ":" + std::to_string(i));
624624
}
625625
}
626+
627+
TEST_F(TestRanges, StaticRangeRendererCanTakeNonConstElement)
628+
{
629+
std::vector<char> characters{'A', 'B', 'C', 'D'};
630+
Nui::val parent;
631+
632+
using Nui::Elements::div;
633+
using Nui::Elements::body;
634+
using namespace Nui::Attributes;
635+
636+
render(body{reference = parent}(range(characters), [&characters](long long i, auto& element) {
637+
element = 'X';
638+
return div{}(std::string{element} + ":" + std::to_string(i));
639+
}));
640+
641+
EXPECT_EQ(parent["children"]["length"].as<long long>(), static_cast<long long>(characters.size()));
642+
for (int i = 0; i != characters.size(); ++i)
643+
{
644+
EXPECT_EQ(parent["children"][i]["textContent"].as<std::string>(), "X:" + std::to_string(i));
645+
}
646+
}
647+
648+
TEST_F(TestRanges, ObservedRangeRendererCanTakeNonConstElement)
649+
{
650+
Nui::Observed<std::vector<char>> characters{{'A', 'B', 'C', 'D'}};
651+
Nui::val parent;
652+
653+
using Nui::Elements::div;
654+
using Nui::Elements::body;
655+
using namespace Nui::Attributes;
656+
657+
render(body{reference = parent}(range(characters), [&characters](long long i, auto& element) {
658+
element = 'X';
659+
return div{}(std::string{element} + ":" + std::to_string(i));
660+
}));
661+
662+
EXPECT_EQ(parent["children"]["length"].as<long long>(), static_cast<long long>(characters.size()));
663+
for (int i = 0; i != characters.size(); ++i)
664+
{
665+
EXPECT_EQ(parent["children"][i]["textContent"].as<std::string>(), "X:" + std::to_string(i));
666+
}
667+
}
668+
669+
TEST_F(TestRanges, StaticRangeRendererCanTakeConstObserved)
670+
{
671+
std::vector<char> characters{'A', 'B', 'C', 'D'};
672+
Nui::val parent;
673+
Nui::Observed<bool> other{true};
674+
675+
using Nui::Elements::div;
676+
using Nui::Elements::body;
677+
using namespace Nui::Attributes;
678+
679+
auto doRender = [&](Nui::Observed<bool> const& constObserved) {
680+
render(body{reference = parent}(
681+
range(characters, constObserved), [&characters](long long i, auto const& element) {
682+
return div{}(std::string{element} + ":" + std::to_string(i));
683+
}));
684+
};
685+
doRender(other);
686+
687+
EXPECT_EQ(parent["children"]["length"].as<long long>(), static_cast<long long>(characters.size()));
688+
for (int i = 0; i != characters.size(); ++i)
689+
{
690+
EXPECT_EQ(
691+
parent["children"][i]["textContent"].as<std::string>(),
692+
std::string{characters[i]} + ":" + std::to_string(i));
693+
}
694+
}
695+
696+
TEST_F(TestRanges, CanUseObservedNonRandomAccessRange)
697+
{
698+
Nui::Observed<std::set<char>> characters{{'A', 'B', 'C', 'D'}};
699+
Nui::val parent;
700+
701+
using Nui::Elements::div;
702+
using Nui::Elements::body;
703+
using namespace Nui::Attributes;
704+
705+
render(body{reference = parent}(range(characters), [&characters](long long i, auto const& element) {
706+
return div{}(std::string{element} + ":" + std::to_string(i));
707+
}));
708+
709+
EXPECT_EQ(parent["children"]["length"].as<long long>(), static_cast<long long>(characters.size()));
710+
int i = 0;
711+
for (auto const& elem : characters.value())
712+
{
713+
EXPECT_EQ(
714+
parent["children"][i]["textContent"].as<std::string>(), std::string{elem} + ":" + std::to_string(i));
715+
++i;
716+
}
717+
}
718+
719+
TEST_F(TestRanges, CanUseStaticNonRandomAccessRange)
720+
{
721+
std::set<char> characters{'A', 'B', 'C', 'D'};
722+
Nui::val parent;
723+
724+
using Nui::Elements::div;
725+
using Nui::Elements::body;
726+
using namespace Nui::Attributes;
727+
728+
render(body{reference = parent}(range(characters), [&characters](long long i, auto const& element) {
729+
return div{}(std::string{element} + ":" + std::to_string(i));
730+
}));
731+
732+
EXPECT_EQ(parent["children"]["length"].as<long long>(), static_cast<long long>(characters.size()));
733+
int i = 0;
734+
for (auto const& elem : characters)
735+
{
736+
EXPECT_EQ(
737+
parent["children"][i]["textContent"].as<std::string>(), std::string{elem} + ":" + std::to_string(i));
738+
++i;
739+
}
740+
}
626741
}

0 commit comments

Comments
 (0)