Node.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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-2025 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 "../../Include/RmlUi/Core/Node.h"
  29. #include "../../Include/RmlUi/Core/Context.h"
  30. #include "../../Include/RmlUi/Core/Element.h"
  31. #include "../../Include/RmlUi/Core/ElementDocument.h"
  32. #include "../../Include/RmlUi/Core/NodeInstancer.h"
  33. #include <iterator>
  34. namespace Rml {
  35. static int NodeIndexInParent(const Node* node)
  36. {
  37. const Node* parent = node->GetParentNode();
  38. if (!parent)
  39. return -1;
  40. int index = 0;
  41. for (const Node* sibling : parent->IterateChildren<Node>())
  42. {
  43. if (sibling == node)
  44. return index;
  45. index++;
  46. }
  47. return -1;
  48. }
  49. NodePtrProxy::NodePtrProxy(NodePtr node) : node(std::move(node)) {}
  50. NodePtrProxy::NodePtrProxy(ElementPtr element) : node(As<NodePtr>(std::move(element))) {}
  51. Node* NodePtrProxy::Get()
  52. {
  53. return node.get();
  54. }
  55. NodePtr NodePtrProxy::Extract()
  56. {
  57. return std::move(node);
  58. }
  59. NodePtrProxy::operator bool() const
  60. {
  61. return node != nullptr;
  62. }
  63. Node::Node() {}
  64. Node::~Node()
  65. {
  66. RMLUI_ASSERT(parent == nullptr);
  67. // A simplified version of RemoveChild() for destruction.
  68. for (NodePtr& child : children)
  69. child->SetParent(nullptr);
  70. children.clear();
  71. num_non_dom_children = 0;
  72. }
  73. Node* Node::GetChildNode(int index) const
  74. {
  75. if (index < 0 || index >= (int)children.size())
  76. return nullptr;
  77. return children[index].get();
  78. }
  79. int Node::GetNumChildNodes(bool include_non_dom_elements) const
  80. {
  81. return (int)children.size() - (include_non_dom_elements ? 0 : num_non_dom_children);
  82. }
  83. bool Node::HasChildNodes() const
  84. {
  85. return (int)children.size() > num_non_dom_children;
  86. }
  87. Node* Node::AppendChild(NodePtrProxy child, bool dom_node)
  88. {
  89. RMLUI_ASSERT(child && child.Get() != this);
  90. Node* child_ptr = child.Get();
  91. if (dom_node)
  92. {
  93. auto it_end = children.end();
  94. children.insert(it_end - num_non_dom_children, child.Extract());
  95. }
  96. else
  97. {
  98. children.push_back(child.Extract());
  99. num_non_dom_children++;
  100. }
  101. // Set parent just after inserting into children. This allows us to e.g. get our previous sibling in SetParent.
  102. child_ptr->SetParent(this);
  103. OnChildNodeAdd(child_ptr, dom_node);
  104. return child_ptr;
  105. }
  106. Node* Node::InsertBefore(NodePtrProxy child, Node* adjacent_node)
  107. {
  108. RMLUI_ASSERT(child);
  109. // Find the position in the list of children of the adjacent element. If it's nullptr, or we can't find it, then we
  110. // insert it at the end of the dom children, as a dom element.
  111. size_t child_index = 0;
  112. bool found_child = false;
  113. if (adjacent_node)
  114. {
  115. for (child_index = 0; child_index < children.size(); child_index++)
  116. {
  117. if (children[child_index].get() == adjacent_node)
  118. {
  119. found_child = true;
  120. break;
  121. }
  122. }
  123. }
  124. Node* child_ptr = nullptr;
  125. if (found_child)
  126. {
  127. child_ptr = child.Get();
  128. const bool dom_node = ((int)child_index < GetNumChildNodes());
  129. if (!dom_node)
  130. num_non_dom_children++;
  131. children.insert(children.begin() + child_index, child.Extract());
  132. child_ptr->SetParent(this);
  133. OnChildNodeAdd(child_ptr, dom_node);
  134. }
  135. else
  136. {
  137. child_ptr = AppendChild(child.Extract());
  138. }
  139. return child_ptr;
  140. }
  141. NodePtr Node::ReplaceChild(NodePtrProxy insert_node, Node* replace_node)
  142. {
  143. RMLUI_ASSERT(insert_node);
  144. auto it_replace = std::find_if(children.begin(), children.end(), [replace_node](const NodePtr& node) { return node.get() == replace_node; });
  145. if (it_replace == children.end())
  146. {
  147. AppendChild(insert_node.Extract());
  148. return nullptr;
  149. }
  150. const std::ptrdiff_t replace_index = std::distance(children.begin(), it_replace);
  151. const bool dom_node = (replace_index < (std::ptrdiff_t)children.size() - num_non_dom_children);
  152. Node* inserted_node_ptr = insert_node.Get();
  153. children.insert(children.begin() + replace_index, insert_node.Extract());
  154. if (!dom_node)
  155. num_non_dom_children++;
  156. inserted_node_ptr->SetParent(this);
  157. NodePtr result = RemoveChild(replace_node);
  158. OnChildNodeAdd(inserted_node_ptr, dom_node);
  159. return result;
  160. }
  161. NodePtr Node::RemoveChild(Node* child)
  162. {
  163. auto it_child = std::find_if(children.begin(), children.end(), [child](const NodePtr& node) { return node.get() == child; });
  164. if (it_child == children.end())
  165. return nullptr;
  166. const std::ptrdiff_t child_index = std::distance(children.begin(), it_child);
  167. const bool dom_node = (child_index < (std::ptrdiff_t)children.size() - num_non_dom_children);
  168. OnChildNodeRemove(child, dom_node);
  169. if (!dom_node)
  170. num_non_dom_children--;
  171. NodePtr detached_child = std::move(*it_child);
  172. children.erase(it_child);
  173. detached_child->SetParent(nullptr);
  174. return detached_child;
  175. }
  176. Element* Node::GetParentElement() const
  177. {
  178. return AsIf<Element*>(GetParentNode());
  179. }
  180. Node* Node::GetParentNode() const
  181. {
  182. return parent;
  183. }
  184. Node* Node::GetNextSibling() const
  185. {
  186. return GetChildNode(NodeIndexInParent(this) + 1);
  187. }
  188. Node* Node::GetPreviousSibling() const
  189. {
  190. return GetChildNode(NodeIndexInParent(this) - 1);
  191. }
  192. Node* Node::GetFirstChild() const
  193. {
  194. return children.empty() ? nullptr : children.front().get();
  195. }
  196. Node* Node::GetLastChild() const
  197. {
  198. return children.empty() ? nullptr : children.back().get();
  199. }
  200. ElementDocument* Node::GetOwnerDocument() const
  201. {
  202. #ifdef RMLUI_DEBUG
  203. if (parent && !owner_document)
  204. {
  205. // Since we have a parent but no owner_document, then we must be a 'loose' element -- that is, constructed
  206. // outside of a document and not attached to a child of any element in the hierarchy of a document.
  207. // This check ensures that we didn't just forget to set the owner document.
  208. RMLUI_ASSERT(!parent->GetOwnerDocument());
  209. }
  210. #endif
  211. return owner_document;
  212. }
  213. Context* Node::GetContext() const
  214. {
  215. ElementDocument* document = GetOwnerDocument();
  216. if (document != nullptr)
  217. return document->GetContext();
  218. return nullptr;
  219. }
  220. RenderManager* Node::GetRenderManager() const
  221. {
  222. if (Context* context = GetContext())
  223. return &context->GetRenderManager();
  224. return nullptr;
  225. }
  226. void Node::SetInstancer(NodeInstancer* new_instancer)
  227. {
  228. RMLUI_ASSERT(!instancer && new_instancer);
  229. instancer = new_instancer;
  230. }
  231. NodeInstancer* Node::GetInstancer() const
  232. {
  233. return instancer;
  234. }
  235. void Node::Release()
  236. {
  237. if (instancer)
  238. instancer->ReleaseNode(this);
  239. else
  240. Log::Message(Log::LT_WARNING, "Leak detected: Node (%s) not instanced via RmlUi Factory. Unable to release.", rmlui_type_name(*this));
  241. }
  242. void Node::SetOwnerDocument(ElementDocument* document)
  243. {
  244. if (owner_document && !document)
  245. {
  246. // We are detaching from the document and thereby also the context.
  247. if (Context* context = owner_document->GetContext())
  248. {
  249. if (Element* self = AsIf<Element*>(this))
  250. context->OnElementDetach(self);
  251. }
  252. }
  253. // If this element is a document, then never change owner_document.
  254. if (owner_document != this && owner_document != document)
  255. {
  256. owner_document = document;
  257. for (NodePtr& child : children)
  258. child->SetOwnerDocument(document);
  259. }
  260. }
  261. void Node::OnChildNodeAdd(Node* /*child*/, bool /*dom_node*/) {}
  262. void Node::OnChildNodeRemove(Node* /*child*/, bool /*dom_node*/) {}
  263. void Node::OnParentChange(Node* /*parent*/) {}
  264. void Node::SetParent(Node* new_parent)
  265. {
  266. // Assumes we are either attaching to or detaching from the hierarchy.
  267. RMLUI_ASSERT((parent == nullptr) != (new_parent == nullptr));
  268. parent = new_parent;
  269. SetOwnerDocument(parent ? parent->GetOwnerDocument() : nullptr);
  270. OnParentChange(parent);
  271. }
  272. } // namespace Rml