ElementHandle.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  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/TestsShell.h"
  29. #include <RmlUi/Core/Context.h>
  30. #include <RmlUi/Core/Element.h>
  31. #include <RmlUi/Core/ElementDocument.h>
  32. #include <RmlUi/Core/Event.h>
  33. #include <doctest.h>
  34. using namespace Rml;
  35. static const char* document_handle_rml = R"(
  36. <rml>
  37. <head>
  38. <title>Handle Test</title>
  39. <link type="text/rcss" href="/assets/rml.rcss"/>
  40. <style>
  41. body {
  42. left: 0;
  43. top: 0;
  44. width: 500px;
  45. height: 500px;
  46. background-color: #ccc;
  47. }
  48. #handle {
  49. position: absolute;
  50. top: -10px;
  51. left: -10px;
  52. width: 20px;
  53. height: 20px;
  54. background-color: #000
  55. }
  56. #target {
  57. position: absolute;
  58. background: #a33;
  59. }
  60. </style>
  61. </head>
  62. <body>
  63. <handle id="handle"></handle>
  64. <div id="target"></div>
  65. </body>
  66. </rml>
  67. )";
  68. TEST_CASE("ElementHandle")
  69. {
  70. Context* context = TestsShell::GetContext();
  71. ElementDocument* document = context->LoadDocumentFromMemory(document_handle_rml, "assets/");
  72. document->Show();
  73. Element* target = document->GetElementById("target");
  74. Element* handle = document->GetElementById("handle");
  75. REQUIRE(target);
  76. REQUIRE(handle);
  77. auto dispatch_mouse_event = [](EventId id, Element* dispatch_target, Vector2f mouse_pos) {
  78. Dictionary drag_start_parameters;
  79. drag_start_parameters["mouse_x"] = mouse_pos.x;
  80. drag_start_parameters["mouse_y"] = mouse_pos.y;
  81. dispatch_target->DispatchEvent(id, drag_start_parameters);
  82. };
  83. auto document_set_size = [&](Vector2f new_size) {
  84. document->SetProperty(PropertyId::Width, Property{new_size.x, Unit::PX});
  85. document->SetProperty(PropertyId::Height, Property{new_size.y, Unit::PX});
  86. context->Update();
  87. };
  88. SUBCASE("MoveTargetWithTopLeft")
  89. {
  90. handle->SetAttribute("move_target", "target");
  91. target->SetProperty(PropertyId::Top, Property(0, Unit::PX));
  92. target->SetProperty(PropertyId::Left, Property(0, Unit::PX));
  93. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  94. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  95. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  96. dispatch_mouse_event(EventId::Drag, handle, {10, 10});
  97. CHECK(target->GetProperty(PropertyId::Top)->Get<float>() == 10);
  98. CHECK(target->GetProperty(PropertyId::Left)->Get<float>() == 10);
  99. context->Update();
  100. CHECK(target->GetAbsoluteOffset() == Vector2f(10, 10));
  101. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  102. document_set_size({1000, 1000});
  103. CHECK(target->GetAbsoluteOffset() == Vector2f(10, 10));
  104. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  105. }
  106. SUBCASE("MoveTargetWithTopLeftRightBottom")
  107. {
  108. handle->SetAttribute("move_target", "target");
  109. target->SetProperty(PropertyId::Top, Property(50, Unit::PX));
  110. target->SetProperty(PropertyId::Left, Property(50, Unit::PX));
  111. target->SetProperty(PropertyId::Right, Property(50, Unit::PX));
  112. target->SetProperty(PropertyId::Bottom, Property(50, Unit::PX));
  113. context->Update();
  114. REQUIRE(target->GetAbsoluteOffset() == Vector2f(50, 50));
  115. REQUIRE(target->GetBox().GetSize() == Vector2f(400, 400));
  116. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  117. dispatch_mouse_event(EventId::Drag, handle, {10, 10});
  118. context->Update();
  119. CHECK(target->GetAbsoluteOffset() == Vector2f(60, 60));
  120. CHECK(target->GetBox().GetSize() == Vector2f(400, 400));
  121. document_set_size({1000, 1000});
  122. CHECK(target->GetAbsoluteOffset() == Vector2f(60, 60));
  123. CHECK(target->GetBox().GetSize() == Vector2f(900, 900));
  124. }
  125. SUBCASE("MoveTargetWithWidthHeightBottomRight")
  126. {
  127. handle->SetAttribute("move_target", "target");
  128. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  129. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  130. target->SetProperty(PropertyId::Bottom, Property(50, Unit::PX));
  131. target->SetProperty(PropertyId::Right, Property(50, Unit::PX));
  132. context->Update();
  133. REQUIRE(target->GetBox().GetSize() == Vector2f(100, 100));
  134. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  135. dispatch_mouse_event(EventId::Drag, handle, {10, 10});
  136. context->Update();
  137. CHECK(target->GetAbsoluteOffset() == Vector2f(360, 360));
  138. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  139. document_set_size({1000, 1000});
  140. CHECK(target->GetAbsoluteOffset() == Vector2f(860, 860));
  141. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  142. }
  143. SUBCASE("MoveTargetWithTopLeftRightBottomAndWidthHeight")
  144. {
  145. handle->SetAttribute("move_target", "target");
  146. target->SetProperty(PropertyId::Top, Property(50, Unit::PX));
  147. target->SetProperty(PropertyId::Left, Property(50, Unit::PX));
  148. target->SetProperty(PropertyId::Right, Property(50, Unit::PX));
  149. target->SetProperty(PropertyId::Bottom, Property(50, Unit::PX));
  150. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  151. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  152. context->Update();
  153. REQUIRE(target->GetAbsoluteOffset() == Vector2f(50, 50));
  154. REQUIRE(target->GetBox().GetSize() == Vector2f(100, 100));
  155. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  156. dispatch_mouse_event(EventId::Drag, handle, {10, 10});
  157. context->Update();
  158. CHECK(target->GetAbsoluteOffset() == Vector2f(60, 60));
  159. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  160. document_set_size({1000, 1000});
  161. CHECK(target->GetAbsoluteOffset() == Vector2f(60, 60));
  162. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  163. }
  164. SUBCASE("EdgeMarginDefaultConstrainsMoveTarget")
  165. {
  166. handle->SetAttribute("move_target", "target");
  167. target->SetProperty(PropertyId::Top, Property(50, Unit::PX));
  168. target->SetProperty(PropertyId::Left, Property(50, Unit::PX));
  169. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  170. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  171. context->Update();
  172. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  173. dispatch_mouse_event(EventId::Drag, handle, {-1000, -1000});
  174. context->Update();
  175. CHECK(target->GetAbsoluteOffset() == Vector2f(0, 0));
  176. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  177. }
  178. SUBCASE("EdgeMarginLengthConstrainsMoveTarget")
  179. {
  180. handle->SetAttribute("move_target", "target");
  181. handle->SetAttribute("edge_margin", "10px 10px 10px 20px");
  182. target->SetProperty(PropertyId::Top, Property(50, Unit::PX));
  183. target->SetProperty(PropertyId::Left, Property(50, Unit::PX));
  184. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  185. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  186. context->Update();
  187. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  188. dispatch_mouse_event(EventId::Drag, handle, {-1000, -1000});
  189. context->Update();
  190. CHECK(target->GetAbsoluteOffset() == Vector2f(20, 10));
  191. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  192. }
  193. SUBCASE("EdgeMarginPercentageConstrainsMoveTarget")
  194. {
  195. handle->SetAttribute("move_target", "target");
  196. handle->SetAttribute("edge_margin", "-50%");
  197. target->SetProperty(PropertyId::Top, Property(50, Unit::PX));
  198. target->SetProperty(PropertyId::Left, Property(50, Unit::PX));
  199. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  200. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  201. context->Update();
  202. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  203. dispatch_mouse_event(EventId::Drag, handle, {-1000, -1000});
  204. context->Update();
  205. CHECK(target->GetAbsoluteOffset() == Vector2f(-50, -50));
  206. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  207. }
  208. SUBCASE("EdgeMarginNoneUnconstrainsMoveTarget")
  209. {
  210. handle->SetAttribute("move_target", "target");
  211. handle->SetAttribute("edge_margin", "none");
  212. target->SetProperty(PropertyId::Top, Property(50, Unit::PX));
  213. target->SetProperty(PropertyId::Left, Property(50, Unit::PX));
  214. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  215. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  216. context->Update();
  217. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  218. dispatch_mouse_event(EventId::Drag, handle, {-1000, -1000});
  219. context->Update();
  220. CHECK(target->GetAbsoluteOffset() == Vector2f(-950, -950));
  221. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  222. }
  223. SUBCASE("EdgeMarginLengthConstrainsSizeTarget")
  224. {
  225. handle->SetAttribute("size_target", "target");
  226. handle->SetAttribute("edge_margin", "10px");
  227. target->SetProperty(PropertyId::Bottom, Property(50, Unit::PX));
  228. target->SetProperty(PropertyId::Right, Property(50, Unit::PX));
  229. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  230. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  231. context->Update();
  232. CHECK(target->GetAbsoluteOffset() == Vector2f(350, 350));
  233. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  234. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  235. dispatch_mouse_event(EventId::Drag, handle, {500, 500});
  236. context->Update();
  237. CHECK(target->GetAbsoluteOffset() == Vector2f(350, 350));
  238. CHECK(target->GetBox().GetSize() == Vector2f(140, 140));
  239. }
  240. SUBCASE("EdgeMarginPercentageConstrainsSizeTarget")
  241. {
  242. handle->SetAttribute("size_target", "target");
  243. handle->SetAttribute("edge_margin", "-50%");
  244. target->SetProperty(PropertyId::Bottom, Property(50, Unit::PX));
  245. target->SetProperty(PropertyId::Right, Property(50, Unit::PX));
  246. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  247. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  248. context->Update();
  249. CHECK(target->GetAbsoluteOffset() == Vector2f(350, 350));
  250. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  251. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  252. dispatch_mouse_event(EventId::Drag, handle, {500, 500});
  253. context->Update();
  254. CHECK(target->GetAbsoluteOffset() == Vector2f(350, 350));
  255. CHECK(target->GetBox().GetSize() == Vector2f(200, 200));
  256. }
  257. SUBCASE("AutoMarginMove")
  258. {
  259. handle->SetAttribute("move_target", "target");
  260. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  261. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  262. target->SetProperty("margin", "auto");
  263. context->Update();
  264. CHECK(target->GetAbsoluteOffset() == Vector2f(200, 200));
  265. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  266. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  267. dispatch_mouse_event(EventId::Drag, handle, {10, 10});
  268. CHECK(target->GetProperty(PropertyId::MarginTop)->Get<float>() == 200);
  269. CHECK(target->GetProperty(PropertyId::MarginRight)->Get<float>() == 200);
  270. CHECK(target->GetProperty(PropertyId::MarginBottom)->Get<float>() == 200);
  271. CHECK(target->GetProperty(PropertyId::MarginLeft)->Get<float>() == 200);
  272. CHECK(target->GetProperty(PropertyId::Top)->Get<float>() == 10);
  273. CHECK(target->GetProperty(PropertyId::Left)->Get<float>() == 10);
  274. context->Update();
  275. CHECK(target->GetAbsoluteOffset() == Vector2f(210, 210));
  276. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  277. document_set_size({1000, 1000});
  278. CHECK(target->GetAbsoluteOffset() == Vector2f(210, 210));
  279. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  280. }
  281. SUBCASE("AutoMarginMoveConstraints")
  282. {
  283. handle->SetAttribute("move_target", "target");
  284. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  285. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  286. target->SetProperty("margin", "auto");
  287. context->Update();
  288. CHECK(target->GetAbsoluteOffset() == Vector2f(200, 200));
  289. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  290. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  291. dispatch_mouse_event(EventId::Drag, handle, {-1000, -1000});
  292. CHECK(target->GetProperty(PropertyId::Top)->Get<float>() == -200);
  293. CHECK(target->GetProperty(PropertyId::Left)->Get<float>() == -200);
  294. context->Update();
  295. CHECK(target->GetAbsoluteOffset() == Vector2f(0, 0));
  296. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  297. }
  298. SUBCASE("AutoMarginSize")
  299. {
  300. handle->SetAttribute("size_target", "target");
  301. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  302. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  303. target->SetProperty("margin", "auto");
  304. context->Update();
  305. CHECK(target->GetAbsoluteOffset() == Vector2f(200, 200));
  306. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  307. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  308. dispatch_mouse_event(EventId::Drag, handle, {10, 10});
  309. CHECK(target->GetProperty(PropertyId::MarginTop)->Get<float>() == 200);
  310. CHECK(target->GetProperty(PropertyId::MarginRight)->Get<float>() == 200);
  311. CHECK(target->GetProperty(PropertyId::MarginBottom)->Get<float>() == 200);
  312. CHECK(target->GetProperty(PropertyId::MarginLeft)->Get<float>() == 200);
  313. CHECK(target->GetProperty(PropertyId::Width)->Get<float>() == 110);
  314. CHECK(target->GetProperty(PropertyId::Height)->Get<float>() == 110);
  315. context->Update();
  316. CHECK(target->GetAbsoluteOffset() == Vector2f(200, 200));
  317. CHECK(target->GetBox().GetSize() == Vector2f(110, 110));
  318. }
  319. SUBCASE("AutoMarginSizeConstraints")
  320. {
  321. handle->SetAttribute("size_target", "target");
  322. target->SetProperty(PropertyId::Width, Property(100, Unit::PX));
  323. target->SetProperty(PropertyId::Height, Property(100, Unit::PX));
  324. target->SetProperty("margin", "auto");
  325. context->Update();
  326. CHECK(target->GetAbsoluteOffset() == Vector2f(200, 200));
  327. CHECK(target->GetBox().GetSize() == Vector2f(100, 100));
  328. dispatch_mouse_event(EventId::Dragstart, handle, {0, 0});
  329. dispatch_mouse_event(EventId::Drag, handle, {1000, 1000});
  330. context->Update();
  331. CHECK(target->GetAbsoluteOffset() == Vector2f(200, 200));
  332. CHECK(target->GetBox().GetSize() == Vector2f(300, 300));
  333. }
  334. TestsShell::RenderLoop();
  335. document->Close();
  336. TestsShell::ShutdownShell();
  337. }