1 /*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "frameworks/core/pipeline/base/multi_composed_element.h"
17
18 #include "core/pipeline/base/multi_composed_component.h"
19
20 namespace OHOS::Ace {
21
CanUpdate(const RefPtr<Component> & newComponent)22 bool MultiComposedElement::CanUpdate(const RefPtr<Component>& newComponent)
23 {
24 auto multiComposed = AceType::DynamicCast<MultiComposedComponent>(newComponent);
25 return multiComposed ? id_ == multiComposed->GetId() : false;
26 }
27
PerformBuild()28 void MultiComposedElement::PerformBuild()
29 {
30 auto multiComposed = AceType::DynamicCast<MultiComposedComponent>(component_);
31 if (!multiComposed) {
32 LOGW("MultiComposedElement: component MUST be instance of MultiComposedComponent");
33 return;
34 }
35
36 UpdateChildren(multiComposed->GetChildren());
37 }
38
UpdateChildren(const std::list<RefPtr<Component>> & newComponents)39 void MultiComposedElement::UpdateChildren(const std::list<RefPtr<Component>>& newComponents)
40 {
41 if (component_->GetUpdateType() == UpdateType::REBUILD) {
42 UpdateChildrenForRebuild(newComponents);
43 return;
44 }
45 int32_t slot = 0;
46 countRenderNode_ = 0;
47 bool useSlot = GetRenderSlot() < 0 ? false : true;
48 if (children_.empty()) {
49 for (const auto& component : newComponents) {
50 auto newChild = UpdateChildWithSlot(
51 nullptr, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
52 countRenderNode_ += newChild->CountRenderNode();
53 }
54 return;
55 }
56
57 // For declarative frontend, the component tree is very stable,
58 // so size of children MUST be matched between elements and components
59 if (children_.size() != newComponents.size()) {
60 LOGW("Size of old children and new components are mismatched");
61 return;
62 }
63
64 auto itChild = children_.begin();
65 for (const auto& component : newComponents) {
66 auto newChild = UpdateChildWithSlot(
67 *(itChild++), component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
68 countRenderNode_ += newChild->CountRenderNode();
69 }
70 }
71
UpdateChildrenForRebuild(const std::list<RefPtr<Component>> & newComponents)72 void MultiComposedElement::UpdateChildrenForRebuild(const std::list<RefPtr<Component>>& newComponents)
73 {
74 auto itChildStart = children_.begin();
75 auto itChildEnd = children_.end();
76 auto itComponentStart = newComponents.begin();
77 auto itComponentEnd = newComponents.end();
78 int32_t slot = 0;
79
80 countRenderNode_ = 0;
81 bool useSlot = GetRenderSlot() < 0 ? false : true;
82
83 // 1. Try to update children at start with new components by order
84 while (itChildStart != itChildEnd && itComponentStart != itComponentEnd) {
85 const auto& child = *itChildStart;
86 const auto& component = *itComponentStart;
87 if (!child->CanUpdate(component)) {
88 break;
89 }
90 auto newChild = UpdateChildWithSlot(
91 child, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
92 countRenderNode_ += newChild->CountRenderNode();
93 ++itChildStart;
94 ++itComponentStart;
95 }
96
97 // 2. Try to find children at end with new components by order
98 while (itChildStart != itChildEnd && itComponentStart != itComponentEnd) {
99 const auto& child = *(--itChildEnd);
100 const auto& component = *(--itComponentEnd);
101 if (!child->CanUpdate(component)) {
102 ++itChildEnd;
103 ++itComponentEnd;
104 break;
105 }
106 }
107
108 // 3. Collect children at middle
109 std::unordered_multimap<ComposeId, RefPtr<Element>> elements;
110 while (itChildStart != itChildEnd) {
111 const auto& child = *(itChildStart++);
112 auto composedElement = AceType::DynamicCast<ComposedElement>(child);
113 if (composedElement) {
114 elements.emplace(composedElement->GetId(), child);
115 } else {
116 UpdateChildWithSlot(child, nullptr, DEFAULT_ELEMENT_SLOT, DEFAULT_RENDER_SLOT);
117 }
118 }
119
120 // 4. Try to update children at middle with new components by order
121 while (itComponentStart != itComponentEnd) {
122 const auto& component = *(itComponentStart++);
123 auto composedComponent = AceType::DynamicCast<BaseComposedComponent>(component);
124 if (!composedComponent) {
125 auto newChild = UpdateChildWithSlot(
126 nullptr, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
127 countRenderNode_ += newChild->CountRenderNode();
128 continue;
129 }
130 auto it = elements.find(composedComponent->GetId());
131 if (it == elements.end()) {
132 auto newChild = UpdateChildWithSlot(
133 nullptr, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
134 countRenderNode_ += newChild->CountRenderNode();
135 continue;
136 }
137
138 const auto& child = it->second;
139 if (child->CanUpdate(component)) {
140 auto newChild = UpdateChildWithSlot(
141 child, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
142 countRenderNode_ += newChild->CountRenderNode();
143 } else {
144 auto newChild = UpdateChildWithSlot(
145 nullptr, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
146 countRenderNode_ += newChild->CountRenderNode();
147 continue;
148 }
149
150 elements.erase(it);
151 }
152
153 // 5. Remove these useless children
154 for (const auto& node : elements) {
155 UpdateChildWithSlot(node.second, nullptr, DEFAULT_ELEMENT_SLOT, DEFAULT_RENDER_SLOT);
156 }
157
158 // 6. Try to update children at end with new components by order
159 while (itChildEnd != children_.end() && itComponentEnd != newComponents.end()) {
160 auto child = *(itChildEnd++);
161 auto newChild = UpdateChildWithSlot(child, *(itComponentEnd++), slot++,
162 useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
163 countRenderNode_ += newChild->CountRenderNode();
164 }
165 }
166
167 } // namespace OHOS::Ace
168