Decorator.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736
  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-2023 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/Mocks.h"
  29. #include "../Common/TestsInterface.h"
  30. #include "../Common/TestsShell.h"
  31. #include "../Common/TypesToString.h"
  32. #include "RmlUi/Core/DecorationTypes.h"
  33. #include <RmlUi/Core/Context.h>
  34. #include <RmlUi/Core/Element.h>
  35. #include <RmlUi/Core/ElementDocument.h>
  36. #include <doctest.h>
  37. #include <float.h>
  38. using namespace Rml;
  39. static bool DictionaryApproximateMatch(const Rml::Dictionary& dict, const Rml::Dictionary& dict_expected)
  40. {
  41. for (auto& pair : dict)
  42. {
  43. const String& name = pair.first;
  44. const Variant& value = pair.second;
  45. auto it = dict_expected.find(name);
  46. if (it == dict_expected.end())
  47. {
  48. FAIL("Unexpected key: ", name, ". Value: ", value);
  49. return false;
  50. }
  51. const Variant& value_expected = it->second;
  52. CAPTURE(name);
  53. REQUIRE(value.GetType() == value_expected.GetType());
  54. if (value.GetType() == Rml::Variant::Type::FLOAT)
  55. {
  56. REQUIRE(value.Get<float>() == doctest::Approx(value_expected.Get<float>()));
  57. }
  58. else if (value_expected.GetType() == Rml::Variant::Type::VECTOR2)
  59. {
  60. auto a = value.Get<Vector2f>();
  61. auto b = value_expected.Get<Vector2f>();
  62. REQUIRE(a.x == doctest::Approx(b.x));
  63. REQUIRE(a.y == doctest::Approx(b.y));
  64. }
  65. else if (value_expected.GetType() == Rml::Variant::Type::COLORSTOPLIST)
  66. {
  67. const auto& a = value.GetReference<ColorStopList>();
  68. const auto& b = value_expected.GetReference<ColorStopList>();
  69. REQUIRE(a.size() == b.size());
  70. for (size_t i = 0; i < Math::Min(a.size(), b.size()); i++)
  71. {
  72. REQUIRE(a[i].color == b[i].color);
  73. REQUIRE(a[i].position.unit == b[i].position.unit);
  74. REQUIRE(a[i].position.number == doctest::Approx(b[i].position.number));
  75. }
  76. }
  77. else
  78. {
  79. REQUIRE(value == value_expected);
  80. }
  81. }
  82. for (auto& pair_expected : dict_expected)
  83. {
  84. if (dict.find(pair_expected.first) == dict.end())
  85. {
  86. FAIL("Missing key: ", pair_expected.first, ". Expected value: ", pair_expected.second);
  87. return false;
  88. }
  89. }
  90. return true;
  91. }
  92. static const String document_decorator_rml = R"(
  93. <rml>
  94. <head>
  95. <title>Test</title>
  96. <link type="text/rcss" href="/assets/rml.rcss"/>
  97. <style>
  98. body {
  99. left: 0;
  100. top: 0;
  101. right: 0;
  102. bottom: 0;
  103. }
  104. @decorator my-gradient : horizontal-gradient {
  105. start-color: #f0f;
  106. stop-color: #fff;
  107. }
  108. div {
  109. border: 20px transparent;
  110. padding: 30px;
  111. width: 50px;
  112. height: 50px;
  113. }
  114. #content_box {
  115. decorator: horizontal-gradient(#f00 #ff0) content-box;
  116. }
  117. #padding_box {
  118. decorator: horizontal-gradient(#f00 #ff0) padding-box;
  119. }
  120. #auto_box {
  121. decorator: horizontal-gradient(#f00 #ff0);
  122. }
  123. #border_box {
  124. decorator: horizontal-gradient(#f00 #ff0) border-box;
  125. }
  126. body.at_decorator #content_box {
  127. decorator: my-gradient content-box;
  128. }
  129. body.at_decorator #padding_box {
  130. decorator: my-gradient padding-box;
  131. }
  132. body.at_decorator #auto_box {
  133. decorator: my-gradient;
  134. }
  135. body.at_decorator #border_box {
  136. decorator: my-gradient border-box;
  137. }
  138. </style>
  139. </head>
  140. <body>
  141. <div id="content_box"/>
  142. <div id="padding_box"/>
  143. <div id="auto_box"/>
  144. <div id="border_box"/>
  145. </body>
  146. </rml>
  147. )";
  148. TEST_CASE("decorator.paint-area")
  149. {
  150. TestsRenderInterface* render_interface = TestsShell::GetTestsRenderInterface();
  151. // This test only works with the dummy renderer.
  152. if (!render_interface)
  153. return;
  154. Context* context = TestsShell::GetContext();
  155. ElementDocument* document = context->LoadDocumentFromMemory(document_decorator_rml, "assets/");
  156. document->Show();
  157. for (const bool set_at_decorator_class : {false, true})
  158. {
  159. document->SetClass("at_decorator", set_at_decorator_class);
  160. const byte blue = (set_at_decorator_class ? 255 : 0);
  161. render_interface->ExpectCompileGeometry({
  162. Mesh{
  163. Vector<Vertex>{
  164. {{50, 50}, {255, 0, blue, 255}, {0, 0}},
  165. {{100, 50}, {255, 255, blue, 255}, {0, 0}},
  166. {{100, 100}, {255, 255, blue, 255}, {0, 0}},
  167. {{50, 100}, {255, 0, blue, 255}, {0, 0}},
  168. },
  169. Vector<int>{0, 2, 1, 0, 3, 2},
  170. },
  171. Mesh{
  172. Vector<Vertex>{
  173. {{20, 20}, {255, 0, blue, 255}, {0, 0}},
  174. {{130, 20}, {255, 255, blue, 255}, {0, 0}},
  175. {{130, 130}, {255, 255, blue, 255}, {0, 0}},
  176. {{20, 130}, {255, 0, blue, 255}, {0, 0}},
  177. },
  178. Vector<int>{0, 2, 1, 0, 3, 2},
  179. },
  180. Mesh{
  181. Vector<Vertex>{
  182. {{20, 20}, {255, 0, blue, 255}, {0, 0}},
  183. {{130, 20}, {255, 255, blue, 255}, {0, 0}},
  184. {{130, 130}, {255, 255, blue, 255}, {0, 0}},
  185. {{20, 130}, {255, 0, blue, 255}, {0, 0}},
  186. },
  187. Vector<int>{0, 2, 1, 0, 3, 2},
  188. },
  189. Mesh{
  190. Vector<Vertex>{
  191. {{0, 0}, {255, 0, blue, 255}, {0, 0}},
  192. {{150, 0}, {255, 255, blue, 255}, {0, 0}},
  193. {{150, 150}, {255, 255, blue, 255}, {0, 0}},
  194. {{0, 150}, {255, 0, blue, 255}, {0, 0}},
  195. },
  196. Vector<int>{0, 2, 1, 0, 3, 2},
  197. },
  198. });
  199. context->Update();
  200. context->Render();
  201. }
  202. document->Close();
  203. TestsShell::ShutdownShell();
  204. }
  205. TEST_CASE("decorator.gradients_and_shader")
  206. {
  207. namespace tl = trompeloeil;
  208. MockRenderInterface mockRenderInterface;
  209. Context* context = TestsShell::GetContext(true, &mockRenderInterface);
  210. REQUIRE(context);
  211. static const String document_gradients_rml = R"(
  212. <rml>
  213. <head>
  214. <title>Test</title>
  215. <link type="text/rcss" href="/assets/rml.rcss"/>
  216. <style>
  217. body {
  218. left: 0;
  219. top: 0;
  220. right: 0;
  221. bottom: 0;
  222. }
  223. div {
  224. margin: auto;
  225. border: 10px transparent;
  226. padding: 50px;
  227. width: 100px;
  228. height: 100px;
  229. }
  230. </style>
  231. </head>
  232. <body>
  233. <div/>
  234. </body>
  235. </rml>
  236. )";
  237. struct TestCase {
  238. String value;
  239. String expected_name;
  240. Dictionary expected_dictionary;
  241. };
  242. TestCase test_cases[] = {
  243. // -- linear-gradient --
  244. TestCase{
  245. "linear-gradient(to right, #000, #fff)",
  246. "linear-gradient",
  247. Dictionary{
  248. {"length", Variant(200.f)},
  249. {"p0", Variant(Vector2f{0.f, 100.f})},
  250. {"color_stop_list",
  251. Variant(ColorStopList{
  252. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  253. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  254. })},
  255. {"p1", Variant(Vector2f{200.f, 100.f})},
  256. {"repeating", Variant(false)},
  257. },
  258. },
  259. TestCase{
  260. "repeating-linear-gradient(to right, #000, #fff)",
  261. "linear-gradient",
  262. Dictionary{
  263. {"length", Variant(200.f)},
  264. {"p0", Variant(Vector2f{0.f, 100.f})},
  265. {"p1", Variant(Vector2f{200.f, 100.f})},
  266. {"color_stop_list",
  267. Variant(ColorStopList{
  268. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  269. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  270. })},
  271. {"repeating", Variant(true)},
  272. },
  273. },
  274. TestCase{
  275. "linear-gradient(to right, #000, rgba( 150, 150, 150, 255 ) 25%, #f00)",
  276. "linear-gradient",
  277. Dictionary{
  278. {"length", Variant(200.f)},
  279. {"p0", Variant(Vector2f{0.f, 100.f})},
  280. {"p1", Variant(Vector2f{200.f, 100.f})},
  281. {"color_stop_list",
  282. Variant(ColorStopList{
  283. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  284. ColorStop{{150, 150, 150, 255}, {0.25f, Unit::NUMBER}},
  285. ColorStop{{255, 0, 0, 255}, {1, Unit::NUMBER}},
  286. })},
  287. {"repeating", Variant(false)},
  288. },
  289. },
  290. TestCase{
  291. "linear-gradient(to right, #000, #fff 50px)",
  292. "linear-gradient",
  293. Dictionary{
  294. {"length", Variant(200.f)},
  295. {"p0", Variant(Vector2f{0.f, 100.f})},
  296. {"p1", Variant(Vector2f{200.f, 100.f})},
  297. {"color_stop_list",
  298. Variant(ColorStopList{
  299. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  300. ColorStop{{255, 255, 255, 255}, {0.25f, Unit::NUMBER}},
  301. })},
  302. {"repeating", Variant(false)},
  303. },
  304. },
  305. TestCase{
  306. "linear-gradient(to right, #000, #f00 100px 75%, #fff)",
  307. "linear-gradient",
  308. Dictionary{
  309. {"length", Variant(200.f)},
  310. {"p0", Variant(Vector2f{0.f, 100.f})},
  311. {"p1", Variant(Vector2f{200.f, 100.f})},
  312. {"color_stop_list",
  313. Variant(ColorStopList{
  314. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  315. ColorStop{{255, 0, 0, 255}, {0.5f, Unit::NUMBER}},
  316. ColorStop{{255, 0, 0, 255}, {0.75f, Unit::NUMBER}},
  317. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  318. })},
  319. {"repeating", Variant(false)},
  320. },
  321. },
  322. TestCase{
  323. "linear-gradient(to right, #000, #fff) content-box",
  324. "linear-gradient",
  325. Dictionary{
  326. {"length", Variant(100.f)},
  327. {"p0", Variant(Vector2f{0.f, 50.f})},
  328. {"p1", Variant(Vector2f{100.f, 50.f})},
  329. {"color_stop_list",
  330. Variant(ColorStopList{
  331. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  332. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  333. })},
  334. {"repeating", Variant(false)},
  335. },
  336. },
  337. TestCase{
  338. "linear-gradient(0deg, #000, #fff)",
  339. "linear-gradient",
  340. Dictionary{
  341. {"length", Variant(200.f)},
  342. {"p0", Variant(Vector2f{100.f, 200.f})},
  343. {"p1", Variant(Vector2f{100.f, 0.f})},
  344. {"color_stop_list",
  345. Variant(ColorStopList{
  346. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  347. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  348. })},
  349. {"repeating", Variant(false)},
  350. },
  351. },
  352. TestCase{
  353. "linear-gradient(#000, #fff)",
  354. "linear-gradient",
  355. Dictionary{
  356. {"length", Variant(200.f)},
  357. {"p0", Variant(Vector2f{100.f, 0.f})},
  358. {"p1", Variant(Vector2f{100.f, 200.f})},
  359. {"color_stop_list",
  360. Variant(ColorStopList{
  361. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  362. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  363. })},
  364. {"repeating", Variant(false)},
  365. },
  366. },
  367. TestCase{
  368. "linear-gradient(to top right, #000, #fff)",
  369. "linear-gradient",
  370. Dictionary{
  371. {"length", Variant(282.843f)},
  372. {"p0", Variant(Vector2f{0.f, 200.f})},
  373. {"p1", Variant(Vector2f{200.f, 0.f})},
  374. {"color_stop_list",
  375. Variant(ColorStopList{
  376. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  377. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  378. })},
  379. {"repeating", Variant(false)},
  380. },
  381. },
  382. // -- radial-gradient --
  383. TestCase{
  384. "radial-gradient(#000, #fff)",
  385. "radial-gradient",
  386. Dictionary{
  387. {"center", Variant(Vector2f{100.f, 100.f})},
  388. {"radius", Variant(Vector2f{Math::SquareRoot(2.f) * 100.f})},
  389. {"color_stop_list",
  390. Variant(ColorStopList{
  391. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  392. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  393. })},
  394. {"repeating", Variant(false)},
  395. },
  396. },
  397. TestCase{
  398. "repeating-radial-gradient(#000, #fff)",
  399. "radial-gradient",
  400. Dictionary{
  401. {"center", Variant(Vector2f{100.f, 100.f})},
  402. {"radius", Variant(Vector2f{Math::SquareRoot(2.f) * 100.f})},
  403. {"color_stop_list",
  404. Variant(ColorStopList{
  405. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  406. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  407. })},
  408. {"repeating", Variant(true)},
  409. },
  410. },
  411. TestCase{
  412. "radial-gradient(closest-side, #000, #fff)",
  413. "radial-gradient",
  414. Dictionary{
  415. {"center", Variant(Vector2f{100.f, 100.f})},
  416. {"radius", Variant(Vector2f{100.f})},
  417. {"color_stop_list",
  418. Variant(ColorStopList{
  419. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  420. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  421. })},
  422. {"repeating", Variant(false)},
  423. },
  424. },
  425. TestCase{
  426. "radial-gradient(closest-side, #000 25px 50%, #fff)",
  427. "radial-gradient",
  428. Dictionary{
  429. {"center", Variant(Vector2f{100.f, 100.f})},
  430. {"radius", Variant(Vector2f{100.f})},
  431. {"color_stop_list",
  432. Variant(ColorStopList{
  433. ColorStop{{0, 0, 0, 255}, {0.25f, Unit::NUMBER}},
  434. ColorStop{{0, 0, 0, 255}, {0.5f, Unit::NUMBER}},
  435. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  436. })},
  437. {"repeating", Variant(false)},
  438. },
  439. },
  440. TestCase{
  441. "radial-gradient(circle closest-side at 75% 50%, #000, #fff)",
  442. "radial-gradient",
  443. Dictionary{
  444. {"center", Variant(Vector2f{150.f, 100.f})},
  445. {"radius", Variant(Vector2f{50.f})},
  446. {"color_stop_list",
  447. Variant(ColorStopList{
  448. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  449. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  450. })},
  451. {"repeating", Variant(false)},
  452. },
  453. },
  454. TestCase{
  455. "radial-gradient(ellipse closest-side at 75% 50%, #000, #fff)",
  456. "radial-gradient",
  457. Dictionary{
  458. {"center", Variant(Vector2f{150.f, 100.f})},
  459. {"radius", Variant(Vector2f{50.f, 100.f})},
  460. {"color_stop_list",
  461. Variant(ColorStopList{
  462. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  463. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  464. })},
  465. {"repeating", Variant(false)},
  466. },
  467. },
  468. TestCase{
  469. "radial-gradient(circle farthest-side at 150px 100px, #000, #fff)",
  470. "radial-gradient",
  471. Dictionary{
  472. {"center", Variant(Vector2f{150.f, 100.f})},
  473. {"radius", Variant(Vector2f{150.f})},
  474. {"color_stop_list",
  475. Variant(ColorStopList{
  476. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  477. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  478. })},
  479. {"repeating", Variant(false)},
  480. },
  481. },
  482. TestCase{
  483. "radial-gradient(farthest-side at 150px 100px, #000, #fff)",
  484. "radial-gradient",
  485. Dictionary{
  486. {"center", Variant(Vector2f{150.f, 100.f})},
  487. {"radius", Variant(Vector2f{150.f, 100.f})},
  488. {"color_stop_list",
  489. Variant(ColorStopList{
  490. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  491. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  492. })},
  493. {"repeating", Variant(false)},
  494. },
  495. },
  496. TestCase{
  497. "radial-gradient(farthest-corner at right, #000, #fff)",
  498. "radial-gradient",
  499. Dictionary{
  500. {"center", Variant(Vector2f{200.f, 100.f})},
  501. {"radius", Variant(Math::SquareRoot(2.f) * Vector2f{200.f, 100.f})},
  502. {"color_stop_list",
  503. Variant(ColorStopList{
  504. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  505. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  506. })},
  507. {"repeating", Variant(false)},
  508. },
  509. },
  510. TestCase{
  511. "radial-gradient(50px at top right, #000, #fff)",
  512. "radial-gradient",
  513. Dictionary{
  514. {"center", Variant(Vector2f{200.f, 0.f})},
  515. {"radius", Variant(Vector2f{50.f})},
  516. {"color_stop_list",
  517. Variant(ColorStopList{
  518. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  519. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  520. })},
  521. {"repeating", Variant(false)},
  522. },
  523. },
  524. TestCase{
  525. "radial-gradient(50% 25% at bottom left, #000, #fff)",
  526. "radial-gradient",
  527. Dictionary{
  528. {"center", Variant(Vector2f{0.f, 200.f})},
  529. {"radius", Variant(Vector2f{100.f, 50.f})},
  530. {"color_stop_list",
  531. Variant(ColorStopList{
  532. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  533. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  534. })},
  535. {"repeating", Variant(false)},
  536. },
  537. },
  538. // -- conic-gradient --
  539. TestCase{
  540. "conic-gradient(#000, #fff)",
  541. "conic-gradient",
  542. Dictionary{
  543. {"center", Variant(Vector2f{100.f, 100.f})},
  544. {"angle", Variant(0.f)},
  545. {"color_stop_list",
  546. Variant(ColorStopList{
  547. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  548. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  549. })},
  550. {"repeating", Variant(false)},
  551. },
  552. },
  553. TestCase{
  554. "repeating-conic-gradient(#000, #fff)",
  555. "conic-gradient",
  556. Dictionary{
  557. {"center", Variant(Vector2f{100.f, 100.f})},
  558. {"angle", Variant(0.f)},
  559. {"color_stop_list",
  560. Variant(ColorStopList{
  561. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  562. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  563. })},
  564. {"repeating", Variant(true)},
  565. },
  566. },
  567. TestCase{
  568. "conic-gradient(#000 50%, #fff)",
  569. "conic-gradient",
  570. Dictionary{
  571. {"center", Variant(Vector2f{100.f, 100.f})},
  572. {"angle", Variant(0.f)},
  573. {"color_stop_list",
  574. Variant(ColorStopList{
  575. ColorStop{{0, 0, 0, 255}, {0.5f, Unit::NUMBER}},
  576. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  577. })},
  578. {"repeating", Variant(false)},
  579. },
  580. },
  581. TestCase{
  582. "conic-gradient(#000 50% 270deg, #fff)",
  583. "conic-gradient",
  584. Dictionary{
  585. {"center", Variant(Vector2f{100.f, 100.f})},
  586. {"angle", Variant(0.f)},
  587. {"color_stop_list",
  588. Variant(ColorStopList{
  589. ColorStop{{0, 0, 0, 255}, {0.5f, Unit::NUMBER}},
  590. ColorStop{{0, 0, 0, 255}, {0.75f, Unit::NUMBER}},
  591. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  592. })},
  593. {"repeating", Variant(false)},
  594. },
  595. },
  596. TestCase{
  597. "conic-gradient(from 90deg, #000, #fff)",
  598. "conic-gradient",
  599. Dictionary{
  600. {"center", Variant(Vector2f{100.f, 100.f})},
  601. {"angle", Variant(1.5708f)},
  602. {"color_stop_list",
  603. Variant(ColorStopList{
  604. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  605. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  606. })},
  607. {"repeating", Variant(false)},
  608. },
  609. },
  610. TestCase{
  611. "conic-gradient(from 90deg at bottom right, #000, #fff)",
  612. "conic-gradient",
  613. Dictionary{
  614. {"center", Variant(Vector2f{200.f, 200.f})},
  615. {"angle", Variant(1.5708f)},
  616. {"color_stop_list",
  617. Variant(ColorStopList{
  618. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  619. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  620. })},
  621. {"repeating", Variant(false)},
  622. },
  623. },
  624. TestCase{
  625. "conic-gradient(at 180px 25%, #000, #fff)",
  626. "conic-gradient",
  627. Dictionary{
  628. {"center", Variant(Vector2f{180.f, 50.f})},
  629. {"angle", Variant(0.f)},
  630. {"color_stop_list",
  631. Variant(ColorStopList{
  632. ColorStop{{0, 0, 0, 255}, {0, Unit::NUMBER}},
  633. ColorStop{{255, 255, 255, 255}, {1, Unit::NUMBER}},
  634. })},
  635. {"repeating", Variant(false)},
  636. },
  637. },
  638. // -- shader --
  639. TestCase{
  640. "shader(cake)",
  641. "shader",
  642. Dictionary{
  643. {"value", Variant("cake")},
  644. {"dimensions", Variant(Vector2f{200, 200})},
  645. },
  646. },
  647. TestCase{
  648. "shader(animated_radar) content-box",
  649. "shader",
  650. Dictionary{
  651. {"value", Variant("animated_radar")},
  652. {"dimensions", Variant(Vector2f{100, 100})},
  653. },
  654. },
  655. TestCase{
  656. "shader(\"taco party\") border-box",
  657. "shader",
  658. Dictionary{
  659. {"value", Variant("taco party")},
  660. {"dimensions", Variant(Vector2f{220, 220})},
  661. },
  662. },
  663. };
  664. ElementDocument* document = context->LoadDocumentFromMemory(document_gradients_rml);
  665. REQUIRE(document);
  666. auto div = document->GetChild(0);
  667. REQUIRE(div);
  668. document->Show();
  669. CompiledShaderHandle compiled_shader_handle = {1};
  670. CompiledGeometryHandle compiled_geometry_handle = {1001};
  671. for (const TestCase& test_case : test_cases)
  672. {
  673. compiled_shader_handle += 1;
  674. compiled_geometry_handle += 1;
  675. CAPTURE(test_case.value);
  676. REQUIRE_CALL(mockRenderInterface, CompileGeometry(tl::_, tl::_)).RETURN(compiled_geometry_handle);
  677. REQUIRE_CALL(mockRenderInterface, ReleaseGeometry(compiled_geometry_handle));
  678. REQUIRE_CALL(mockRenderInterface, CompileShader(test_case.expected_name, tl::_))
  679. .WITH(DictionaryApproximateMatch(_2, test_case.expected_dictionary))
  680. .RETURN(compiled_shader_handle);
  681. REQUIRE_CALL(mockRenderInterface, RenderShader(compiled_shader_handle, compiled_geometry_handle, tl::_, TextureHandle{}));
  682. REQUIRE_CALL(mockRenderInterface, ReleaseShader(compiled_shader_handle));
  683. div->SetProperty("decorator", test_case.value);
  684. TestsShell::RenderLoop();
  685. div->SetProperty("decorator", "none");
  686. context->Update();
  687. context->Render();
  688. }
  689. document->Close();
  690. TestsShell::ShutdownShell();
  691. }