Node.cpp 8.0 KB

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