Node.cpp 7.6 KB

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