DataView.cpp 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #include "DataView.h"
  2. #include "../../Include/RmlUi/Core/Element.h"
  3. #include <algorithm>
  4. namespace Rml {
  5. DataView::~DataView() {}
  6. Element* DataView::GetElement() const
  7. {
  8. Element* result = attached_element.get();
  9. if (!result)
  10. Log::Message(Log::LT_WARNING, "Could not retrieve element in view, was it destroyed?");
  11. return result;
  12. }
  13. int DataView::GetSortOrder() const
  14. {
  15. return sort_order;
  16. }
  17. bool DataView::IsValid() const
  18. {
  19. return static_cast<bool>(attached_element);
  20. }
  21. DataView::DataView(Element* element, int bias) : attached_element(element->GetObserverPtr()), sort_order(bias + 1000)
  22. {
  23. RMLUI_ASSERT(bias >= -1000 && bias <= 999);
  24. if (element)
  25. {
  26. for (Element* parent = element->GetParentNode(); parent; parent = parent->GetParentNode())
  27. sort_order += 2000;
  28. }
  29. }
  30. DataViews::DataViews() {}
  31. DataViews::~DataViews() {}
  32. void DataViews::Add(DataViewPtr view)
  33. {
  34. views_to_add.push_back(std::move(view));
  35. }
  36. void DataViews::OnElementRemove(Element* element)
  37. {
  38. for (auto it = views.begin(); it != views.end();)
  39. {
  40. auto& view = *it;
  41. if (view && view->GetElement() == element)
  42. {
  43. views_to_remove.push_back(std::move(view));
  44. it = views.erase(it);
  45. }
  46. else
  47. ++it;
  48. }
  49. }
  50. bool DataViews::Update(DataModel& model, const DirtyVariables& dirty_variables)
  51. {
  52. bool result = false;
  53. size_t num_dirty_variables_prev = 0;
  54. // View updates may result in newly added views, or even new dirty variables. Thus, we do the
  55. // update recursively but with an upper limit. Without the loop, newly added views won't be
  56. // updated until the next Update() call.
  57. for (int i = 0; (i == 0 || !views_to_add.empty() || num_dirty_variables_prev != dirty_variables.size()) && i < 10; i++)
  58. {
  59. num_dirty_variables_prev = dirty_variables.size();
  60. Vector<DataView*> dirty_views;
  61. if (!views_to_add.empty())
  62. {
  63. views.reserve(views.size() + views_to_add.size());
  64. for (auto&& view : views_to_add)
  65. {
  66. dirty_views.push_back(view.get());
  67. for (const String& variable_name : view->GetVariableNameList())
  68. name_view_map.emplace(variable_name, view.get());
  69. views.push_back(std::move(view));
  70. }
  71. views_to_add.clear();
  72. }
  73. for (const String& variable_name : dirty_variables)
  74. {
  75. auto pair = name_view_map.equal_range(variable_name);
  76. for (auto it = pair.first; it != pair.second; ++it)
  77. dirty_views.push_back(it->second);
  78. }
  79. // Remove duplicate entries
  80. std::sort(dirty_views.begin(), dirty_views.end());
  81. auto it_remove = std::unique(dirty_views.begin(), dirty_views.end());
  82. dirty_views.erase(it_remove, dirty_views.end());
  83. // Sort by the element's depth in the document tree so that any structural changes due to a changed variable are reflected in the element's
  84. // children. Eg. the 'data-for' view will remove children if any of its data variable array size is reduced.
  85. std::sort(dirty_views.begin(), dirty_views.end(), [](auto&& left, auto&& right) { return left->GetSortOrder() < right->GetSortOrder(); });
  86. for (DataView* view : dirty_views)
  87. {
  88. RMLUI_ASSERT(view);
  89. if (!view)
  90. continue;
  91. if (view->IsValid())
  92. result |= view->Update(model);
  93. }
  94. // Destroy views marked for destruction
  95. // @performance: Horrible...
  96. if (!views_to_remove.empty())
  97. {
  98. for (const auto& view : views_to_remove)
  99. {
  100. for (auto it = name_view_map.begin(); it != name_view_map.end();)
  101. {
  102. if (it->second == view.get())
  103. it = name_view_map.erase(it);
  104. else
  105. ++it;
  106. }
  107. }
  108. views_to_remove.clear();
  109. }
  110. }
  111. return result;
  112. }
  113. } // namespace Rml