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 #include "frameworks/core/common/frontend.h"
20 #include "frameworks/core/pipeline/base/render_element.h"
21
22 namespace OHOS::Ace {
23
CanUpdate(const RefPtr<Component> & newComponent)24 bool MultiComposedElement::CanUpdate(const RefPtr<Component>& newComponent)
25 {
26 auto multiComposed = AceType::DynamicCast<MultiComposedComponent>(newComponent);
27 return multiComposed ? id_ == multiComposed->GetId() : false;
28 }
29
PerformBuild()30 void MultiComposedElement::PerformBuild()
31 {
32 auto multiComposed = AceType::DynamicCast<MultiComposedComponent>(component_);
33 if (!multiComposed) {
34 LOGW("MultiComposedElement: component MUST be instance of MultiComposedComponent");
35 return;
36 }
37
38 UpdateChildren(multiComposed->GetChildren());
39 }
40
UpdateChildren(const std::list<RefPtr<Component>> & newComponents)41 void MultiComposedElement::UpdateChildren(const std::list<RefPtr<Component>>& newComponents)
42 {
43 if (component_->GetUpdateType() == UpdateType::REBUILD) {
44 UpdateChildrenForRebuild(newComponents);
45 return;
46 }
47 int32_t slot = 0;
48 countRenderNode_ = 0;
49 bool useSlot = GetRenderSlot() < 0 ? false : true;
50 if (children_.empty()) {
51 for (const auto& component : newComponents) {
52 auto newChild = UpdateChildWithSlot(
53 nullptr, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
54 countRenderNode_ += newChild->CountRenderNode();
55 }
56 return;
57 }
58
59 // For declarative frontend, the component tree is very stable,
60 // so size of children MUST be matched between elements and components
61 if (children_.size() != newComponents.size()) {
62 LOGW("Size of old children and new components are mismatched");
63 return;
64 }
65
66 auto itChild = children_.begin();
67 for (const auto& component : newComponents) {
68 auto newChild = UpdateChildWithSlot(
69 *(itChild++), component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
70 countRenderNode_ += newChild->CountRenderNode();
71 }
72 }
73
UpdateChildrenForRebuild(const std::list<RefPtr<Component>> & newComponents)74 void MultiComposedElement::UpdateChildrenForRebuild(const std::list<RefPtr<Component>>& newComponents)
75 {
76 auto itChildStart = children_.begin();
77 auto itChildEnd = children_.end();
78 auto itComponentStart = newComponents.begin();
79 auto itComponentEnd = newComponents.end();
80 int32_t slot = 0;
81
82 countRenderNode_ = 0;
83 bool useSlot = GetRenderSlot() < 0 ? false : true;
84
85 // 1. Try to update children at start with new components by order
86 while (itChildStart != itChildEnd && itComponentStart != itComponentEnd) {
87 const auto& child = *itChildStart;
88 const auto& component = *itComponentStart;
89 if (!child->CanUpdate(component)) {
90 break;
91 }
92 auto newChild = UpdateChildWithSlot(
93 child, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
94 countRenderNode_ += newChild->CountRenderNode();
95 ++itChildStart;
96 ++itComponentStart;
97 }
98
99 // 2. Try to find children at end with new components by order
100 while (itChildStart != itChildEnd && itComponentStart != itComponentEnd) {
101 const auto& child = *(--itChildEnd);
102 const auto& component = *(--itComponentEnd);
103 if (!child->CanUpdate(component)) {
104 ++itChildEnd;
105 ++itComponentEnd;
106 break;
107 }
108 }
109
110 // 3. Collect children at middle
111 std::unordered_multimap<ComposeId, RefPtr<Element>> elements;
112 while (itChildStart != itChildEnd) {
113 const auto& child = *(itChildStart++);
114 auto composedElement = AceType::DynamicCast<ComposedElement>(child);
115 if (composedElement) {
116 elements.emplace(composedElement->GetId(), child);
117 } else {
118 UpdateChildWithSlot(child, nullptr, DEFAULT_ELEMENT_SLOT, DEFAULT_RENDER_SLOT);
119 }
120 }
121
122 // 4. Try to update children at middle with new components by order
123 while (itComponentStart != itComponentEnd) {
124 const auto& component = *(itComponentStart++);
125 auto composedComponent = AceType::DynamicCast<BaseComposedComponent>(component);
126 if (!composedComponent) {
127 auto newChild = UpdateChildWithSlot(
128 nullptr, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
129 countRenderNode_ += newChild->CountRenderNode();
130 continue;
131 }
132 auto it = elements.find(composedComponent->GetId());
133 if (it == elements.end()) {
134 auto newChild = UpdateChildWithSlot(
135 nullptr, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
136 countRenderNode_ += newChild->CountRenderNode();
137 continue;
138 }
139
140 const auto& child = it->second;
141 if (child->CanUpdate(component)) {
142 auto newChild = UpdateChildWithSlot(
143 child, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
144 countRenderNode_ += newChild->CountRenderNode();
145 } else {
146 auto newChild = UpdateChildWithSlot(
147 nullptr, component, slot++, useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
148 countRenderNode_ += newChild->CountRenderNode();
149 continue;
150 }
151
152 elements.erase(it);
153 }
154
155 // 5. Remove these useless children
156 for (const auto& node : elements) {
157 UpdateChildWithSlot(node.second, nullptr, DEFAULT_ELEMENT_SLOT, DEFAULT_RENDER_SLOT);
158 }
159
160 // 6. Try to update children at end with new components by order
161 while (itChildEnd != children_.end() && itComponentEnd != newComponents.end()) {
162 auto child = *(itChildEnd++);
163 auto newChild = UpdateChildWithSlot(child, *(itComponentEnd++), slot++,
164 useSlot ? countRenderNode_ + GetRenderSlot() : DEFAULT_RENDER_SLOT);
165 countRenderNode_ += newChild->CountRenderNode();
166 }
167 }
168
169 } // namespace OHOS::Ace
170