DataBinding.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. /*
  2. * This source file is part of RmlUi, the HTML/CSS Interface Middleware
  3. *
  4. * For the latest information, see http://github.com/mikke89/RmlUi
  5. *
  6. * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
  7. * Copyright (c) 2019 The RmlUi Team, and contributors
  8. *
  9. * Permission is hereby granted, free of charge, to any person obtaining a copy
  10. * of this software and associated documentation files (the "Software"), to deal
  11. * in the Software without restriction, including without limitation the rights
  12. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. * copies of the Software, and to permit persons to whom the Software is
  14. * furnished to do so, subject to the following conditions:
  15. *
  16. * The above copyright notice and this permission notice shall be included in
  17. * all copies or substantial portions of the Software.
  18. *
  19. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. * THE SOFTWARE.
  26. *
  27. */
  28. #include "../Common/TestsShell.h"
  29. #include <RmlUi/Core/Context.h>
  30. #include <RmlUi/Core/DataModelHandle.h>
  31. #include <RmlUi/Core/Element.h>
  32. #include <RmlUi/Core/ElementDocument.h>
  33. #include <doctest.h>
  34. #include <map>
  35. using namespace Rml;
  36. namespace {
  37. static const String document_rml = R"(
  38. <rml>
  39. <head>
  40. <title>Test</title>
  41. <link type="text/rcss" href="/assets/rml.rcss"/>
  42. <link type="text/template" href="/assets/window.rml"/>
  43. <style>
  44. body.window
  45. {
  46. left: 50px;
  47. right: 50px;
  48. top: 30px;
  49. bottom: 30px;
  50. max-width: -1px;
  51. max-height: -1px;
  52. }
  53. div#content
  54. {
  55. text-align: left;
  56. padding: 50px;
  57. box-sizing: border-box;
  58. }
  59. </style>
  60. </head>
  61. <body template="window">
  62. <div data-model="basics">
  63. <input type="text" data-value="arrays.x0[1]"/>
  64. <h1>Globals</h1>
  65. <p>{{ i0 }}</p>
  66. <p>{{ i1 }}</p>
  67. <p>{{ i2 }}</p>
  68. <p>{{ i3 }}</p>
  69. <p>{{ x1 }}</p>
  70. <p>{{ s0 }}</p>
  71. <p>{{ s1 }}</p>
  72. <p>{{ s2.val }}</p>
  73. <p>{{ s3.val }}</p>
  74. <p>{{ s4.val }}</p>
  75. <p>{{ s5.val }}</p>
  76. <h1>Basic</h1>
  77. <p>{{ basic.a }}</p>
  78. <p>{{ basic.b }}</p>
  79. <p>{{ basic.c }}</p>
  80. <p>{{ basic.d }}</p>
  81. <p>{{ basic.e }}</p>
  82. <p>{{ basic.f }}</p>
  83. <h1>Wrapped</h1>
  84. <p>{{ wrapped.a.val }}</p>
  85. <p>{{ wrapped.b.val }}</p>
  86. <p>{{ wrapped.c.val }}</p>
  87. <p>{{ wrapped.d.val }}</p>
  88. <p>{{ wrapped.e.val }}</p>
  89. <h1>Pointed</h1>
  90. <p>{{ pointed.a.val }}</p>
  91. <p>{{ pointed.b.val }}</p>
  92. <p>{{ pointed.c.val }}</p>
  93. <h1>Arrays</h1>
  94. <p><span data-for="arrays.a">{{ it }} </span></p>
  95. <p><span data-for="arrays.b">{{ it }} </span></p>
  96. <p><span data-for="arrays.c">{{ it.val }} </span></p>
  97. <p><span data-for="arrays.d">{{ it.val }} </span></p>
  98. <p><span data-for="arrays.e">{{ it.val }} </span></p>
  99. <p><span data-for="arrays.x0">{{ it }} </span></p>
  100. <p>The next line crashes the program:</p>
  101. <!-- <p><span data-for="arrays.x2">{{ it }} </span></p> -->
  102. </div>
  103. </body>
  104. </rml>
  105. )";
  106. static const String inside_string_rml = R"(
  107. <rml>
  108. <head>
  109. <title>Test</title>
  110. <link type="text/rcss" href="/assets/rml.rcss"/>
  111. <link type="text/template" href="/assets/window.rml"/>
  112. <style>
  113. body.window
  114. {
  115. left: 50px;
  116. right: 50px;
  117. top: 30px;
  118. bottom: 30px;
  119. max-width: -1px;
  120. max-height: -1px;
  121. }
  122. </style>
  123. </head>
  124. <body template="window">
  125. <div data-model="basics">
  126. <p>{{ i0 }}</p>
  127. <p>{{ 'i0' }}</p>
  128. <p>{{ 'i{}23' }}</p>
  129. <p>before {{ 'i{{test}}23' }} test</p>
  130. <p>a {{ 'i' }} b {{ 'j' }} c</p>
  131. <p>{{i0}}</p>
  132. </div>
  133. </body>
  134. </rml>
  135. )";
  136. struct StringWrap
  137. {
  138. StringWrap(String val = "wrap_default") : val(val) {}
  139. String val;
  140. };
  141. struct Globals
  142. {
  143. int i0 = 0;
  144. int* i1 = new int(1);
  145. UniquePtr<int> i2 = MakeUnique<int>(2);
  146. SharedPtr<int> i3 = MakeShared<int>(3);
  147. String s0 = "s0";
  148. String* s1 = new String("s1");
  149. StringWrap s2 = StringWrap("s2");
  150. StringWrap* s3 = new StringWrap("s3");
  151. UniquePtr<StringWrap> s4 = MakeUnique<StringWrap>("s4");
  152. SharedPtr<StringWrap> s5 = MakeShared<StringWrap>("s5");
  153. // Invalid
  154. const int x0 = 100; // Invalid: const variable
  155. const int* x1 = new int(101); // Invalid: const pointer
  156. UniquePtr<const int> x2 = MakeUnique<int>(102); // Invalid: const pointer
  157. const StringWrap* x3 = new StringWrap("x2"); // Invalid: const pointer
  158. UniquePtr<const StringWrap> x4 = MakeUnique<StringWrap>("x3"); // Invalid: const pointer
  159. } globals;
  160. struct Basic
  161. {
  162. int a = 1;
  163. int* b = new int(2);
  164. int GetC() {
  165. static int v = 5;
  166. return v;
  167. }
  168. int& GetD() {
  169. static int v = 5;
  170. return v;
  171. }
  172. int* GetE() {
  173. static int v = 6;
  174. return &v;
  175. }
  176. UniquePtr<int> GetF() {
  177. return MakeUnique<int>(7);
  178. }
  179. // Invalid: const member
  180. const int x0 = 2;
  181. // Invalid: const pointer
  182. const int* x1 = new int(3);
  183. // Invalid: const qualified member function
  184. int GetX2() const {
  185. return 4;
  186. }
  187. // Invalid: const reference return
  188. const int& GetX3() {
  189. static int g = 7;
  190. return g;
  191. }
  192. // Invalid: const pointer return
  193. const int* GetX4() {
  194. static int h = 8;
  195. return &h;
  196. }
  197. // Invalid: Illegal signature
  198. int GetX5(int) {
  199. return 9;
  200. }
  201. };
  202. struct Wrapped
  203. {
  204. StringWrap a = { "a" };
  205. StringWrap* b = new StringWrap("b");
  206. UniquePtr<StringWrap> c = MakeUnique<StringWrap>("c");
  207. StringWrap& GetD() {
  208. static StringWrap v = { "e" };
  209. return v;
  210. }
  211. StringWrap* GetE() {
  212. static StringWrap v = { "f" };
  213. return &v;
  214. }
  215. // Invalid: const pointer
  216. const StringWrap* x0 = new StringWrap("x0");
  217. // Invalid (run-time): Returning non-scalar variable by value.
  218. StringWrap GetX1() {
  219. return { "x1" };
  220. }
  221. // Invalid (run-time): Returning non-scalar variable by value.
  222. UniquePtr<StringWrap> GetX2() {
  223. return MakeUnique<StringWrap>("x2");
  224. }
  225. };
  226. using StringWrapPtr = UniquePtr<StringWrap>;
  227. struct Pointed
  228. {
  229. StringWrapPtr a = MakeUnique<StringWrap>("a");
  230. StringWrapPtr& GetB() {
  231. static StringWrapPtr v = MakeUnique<StringWrap>("b");
  232. return v;
  233. }
  234. StringWrapPtr* GetC() {
  235. static StringWrapPtr v = MakeUnique<StringWrap>("c");
  236. return &v;
  237. }
  238. // Invalid: We disallow recursive pointer types (pointer to pointer)
  239. StringWrapPtr* x0 = new StringWrapPtr(new StringWrap("x0"));
  240. // Invalid (run-time error): Only scalar data members can be returned by value
  241. StringWrapPtr GetX1() {
  242. return MakeUnique<StringWrap>("x1");
  243. }
  244. };
  245. struct Arrays
  246. {
  247. Vector<int> a = { 10, 11, 12 };
  248. Vector<int*> b = { new int(20), new int(21), new int(22) };
  249. Vector<StringWrap> c = { StringWrap("c1"), StringWrap("c2"), StringWrap("c3") };
  250. Vector<StringWrap*> d = { new StringWrap("d1"), new StringWrap("d2"), new StringWrap("d3") };
  251. Vector<StringWrapPtr> e;
  252. // Invalid: const pointer
  253. Vector<const int*> x0 = { new int(30), new int(31), new int(32) };
  254. // Invalid: const pointer
  255. Vector<UniquePtr<const StringWrap>> x1;
  256. // Invalid: const object
  257. const Vector<int*> x2 = { new int(20), new int(21), new int(22) };
  258. Arrays() {
  259. e.emplace_back(MakeUnique<StringWrap>("e1"));
  260. e.emplace_back(MakeUnique<StringWrap>("e2"));
  261. e.emplace_back(MakeUnique<StringWrap>("e3"));
  262. x1.emplace_back(MakeUnique<StringWrap>("x1_1"));
  263. x1.emplace_back(MakeUnique<StringWrap>("x1_2"));
  264. x1.emplace_back(MakeUnique<StringWrap>("x1_3"));
  265. }
  266. };
  267. DataModelHandle model_handle;
  268. TEST_CASE("databinding.types")
  269. {
  270. static_assert(!PointerTraits<int>::is_pointer::value, "");
  271. static_assert(!PointerTraits<int&>::is_pointer::value, "");
  272. static_assert(PointerTraits<int*>::is_pointer::value, "");
  273. static_assert(PointerTraits<UniquePtr<int>>::is_pointer::value, "");
  274. static_assert(PointerTraits<UniquePtr<const int>>::is_pointer::value, "");
  275. static_assert(PointerTraits<SharedPtr<int>>::is_pointer::value, "");
  276. static_assert(PointerTraits<SharedPtr<const int>>::is_pointer::value, "");
  277. static_assert(std::is_same<int, PointerTraits<int>::element_type>::value, "");
  278. static_assert(std::is_same<const int, PointerTraits<const int>::element_type>::value, "");
  279. static_assert(std::is_same<int&, PointerTraits<int&>::element_type>::value, "");
  280. static_assert(std::is_same<int, PointerTraits<int*>::element_type>::value, "");
  281. static_assert(std::is_same<int, PointerTraits<UniquePtr<int>>::element_type>::value, "");
  282. static_assert(std::is_same<int, PointerTraits<SharedPtr<int>>::element_type>::value, "");
  283. static_assert(std::is_same<int, PointerTraits<SharedPtr<int>>::element_type>::value, "");
  284. static_assert(std::is_same<int*, PointerTraits<int**>::element_type>::value, "");
  285. {
  286. int x = 10;
  287. DataPointer ptr(&x);
  288. *ptr.Get<int*>() += 5;
  289. CHECK(x == 15);
  290. CHECK(x == *ptr.Get<int*>());
  291. }
  292. {
  293. UniquePtr<int> u = MakeUnique<int>(20);
  294. DataPointer ptr(&u);
  295. CHECK(ptr.Get<UniquePtr<int>*>() == &u);
  296. DataPointer ptr_underlying = PointerTraits< UniquePtr<int> >::Dereference(ptr);
  297. CHECK(ptr_underlying.Get<int*>() == u.get());
  298. int u_out = *ptr_underlying.Get<int*>();
  299. CHECK(*u == u_out);
  300. *ptr_underlying.Get<int*>() += 5;
  301. CHECK(*u == 25);
  302. }
  303. {
  304. UniquePtr<const int> u = MakeUnique<const int>(20);
  305. DataPointer ptr(&u);
  306. CHECK(ptr.Get<UniquePtr<const int>*>() == &u);
  307. DataPointer ptr_underlying = PointerTraits< UniquePtr<const int> >::Dereference(ptr);
  308. CHECK(ptr_underlying.Get<const int*>() == u.get());
  309. CHECK(ptr_underlying.Get<int*>() == nullptr);
  310. int u_out = *ptr_underlying.Get<const int*>();
  311. CHECK(*u == u_out);
  312. }
  313. }
  314. bool InitializeDataBindings(Context* context)
  315. {
  316. Rml::DataModelConstructor constructor = context->CreateDataModel("basics");
  317. if (!constructor)
  318. return false;
  319. if (auto handle = constructor.RegisterStruct<StringWrap>())
  320. {
  321. handle.RegisterMember("val", &StringWrap::val);
  322. }
  323. {
  324. // Globals
  325. constructor.Bind("i0", &globals.i0);
  326. constructor.Bind("i1", &globals.i1);
  327. constructor.Bind("i2", &globals.i2);
  328. constructor.Bind("i3", &globals.i3);
  329. constructor.Bind("s0", &globals.s0);
  330. constructor.Bind("s1", &globals.s1);
  331. constructor.Bind("s2", &globals.s2);
  332. constructor.Bind("s3", &globals.s3);
  333. constructor.Bind("s4", &globals.s4);
  334. constructor.Bind("s5", &globals.s5);
  335. // Invalid: Each of the following should give a compile-time failure.
  336. constructor.Bind("x0", &globals.x0);
  337. constructor.Bind("x1", &globals.x1);
  338. //constructor.Bind("x2", &globals.x2);
  339. //constructor.Bind("x3", &globals.x3);
  340. //constructor.Bind("x4", &globals.x4);
  341. }
  342. if (auto handle = constructor.RegisterStruct<Basic>())
  343. {
  344. handle.RegisterMember("a", &Basic::a);
  345. handle.RegisterMember("b", &Basic::b);
  346. handle.RegisterMember("c", &Basic::GetC);
  347. handle.RegisterMember("d", &Basic::GetD);
  348. handle.RegisterMember("e", &Basic::GetE);
  349. handle.RegisterMember("f", &Basic::GetF);
  350. //handle.RegisterMember("x0", &Basic::x0);
  351. //handle.RegisterMember("x1", &Basic::x1);
  352. //handle.RegisterMember("x2", &Basic::GetX2);
  353. //handle.RegisterMember("x3", &Basic::GetX3);
  354. //handle.RegisterMember("x4", &Basic::GetX4);
  355. //handle.RegisterMember("x5", &Basic::GetX5);
  356. }
  357. constructor.Bind("basic", new Basic);
  358. if (auto handle = constructor.RegisterStruct<Wrapped>())
  359. {
  360. handle.RegisterMember("a", &Wrapped::a);
  361. handle.RegisterMember("b", &Wrapped::b);
  362. handle.RegisterMember("c", &Wrapped::c);
  363. handle.RegisterMember("d", &Wrapped::GetD);
  364. handle.RegisterMember("e", &Wrapped::GetE);
  365. //handle.RegisterMember("x0", &Wrapped::x0);
  366. //handle.RegisterMember("x1", &Wrapped::GetX1);
  367. //handle.RegisterMember("x2", &Wrapped::GetX2);
  368. }
  369. constructor.Bind("wrapped", new Wrapped);
  370. if (auto handle = constructor.RegisterStruct<Pointed>())
  371. {
  372. handle.RegisterMember("a", &Pointed::a);
  373. handle.RegisterMember("b", &Pointed::GetB);
  374. handle.RegisterMember("c", &Pointed::GetC);
  375. //handle.RegisterMember("x0", &Pointed::x0);
  376. //handle.RegisterMember("x1", &Pointed::GetX1);
  377. }
  378. constructor.Bind("pointed", new Pointed);
  379. constructor.RegisterArray<decltype(Arrays::a)>();
  380. constructor.RegisterArray<decltype(Arrays::b)>();
  381. constructor.RegisterArray<decltype(Arrays::c)>();
  382. constructor.RegisterArray<decltype(Arrays::d)>();
  383. constructor.RegisterArray<decltype(Arrays::e)>();
  384. constructor.RegisterArray<decltype(Arrays::x0)>();
  385. //constructor.RegisterArray<decltype(Arrays::x1)>();
  386. constructor.RegisterArray<decltype(Arrays::x2)>();
  387. if (auto handle = constructor.RegisterStruct<Arrays>())
  388. {
  389. handle.RegisterMember("a", &Arrays::a);
  390. handle.RegisterMember("b", &Arrays::b);
  391. handle.RegisterMember("c", &Arrays::c);
  392. handle.RegisterMember("d", &Arrays::d);
  393. handle.RegisterMember("e", &Arrays::e);
  394. handle.RegisterMember("x0", &Arrays::x0);
  395. //handle.RegisterMember("x1", &Arrays::x1);
  396. handle.RegisterMember("x2", &Arrays::x2);
  397. }
  398. constructor.Bind("arrays", new Arrays);
  399. model_handle = constructor.GetModelHandle();
  400. return true;
  401. }
  402. } // Anonymous namespace
  403. TEST_CASE("databinding")
  404. {
  405. Context* context = TestsShell::GetContext();
  406. REQUIRE(context);
  407. REQUIRE(InitializeDataBindings(context));
  408. ElementDocument* document = context->LoadDocumentFromMemory(document_rml);
  409. REQUIRE(document);
  410. document->Show();
  411. context->Update();
  412. context->Render();
  413. TestsShell::RenderLoop();
  414. document->Close();
  415. TestsShell::ShutdownShell();
  416. }
  417. TEST_CASE("databinding.inside_string")
  418. {
  419. Context* context = TestsShell::GetContext();
  420. REQUIRE(context);
  421. REQUIRE(InitializeDataBindings(context));
  422. ElementDocument* document = context->LoadDocumentFromMemory(inside_string_rml);
  423. REQUIRE(document);
  424. document->Show();
  425. context->Update();
  426. context->Render();
  427. TestsShell::RenderLoop();
  428. CHECK(document->QuerySelector("p:nth-child(4)")->GetInnerRML() == "before i{{test}}23 test");
  429. CHECK(document->QuerySelector("p:nth-child(5)")->GetInnerRML() == "a i b j c");
  430. document->Close();
  431. TestsShell::ShutdownShell();
  432. }