• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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/components_part_upd/foreach/foreach_element.h"
17 
18 #include <cstdint>
19 #include <list>
20 #include <set>
21 
22 #include "frameworks/core/components_part_upd/foreach/foreach_component.h"
23 
24 namespace OHOS::Ace::PartUpd {
25 
CanUpdate(const RefPtr<Component> & newComponent)26 bool ForEachElement::CanUpdate(const RefPtr<Component>& newComponent)
27 {
28     return AceType::InstanceOf<PartUpd::ForEachComponent>(newComponent);
29 }
30 
CompareSlots(const RefPtr<Element> & first,const RefPtr<Element> & second)31 bool ForEachElement::CompareSlots(const RefPtr<Element>& first, const RefPtr<Element>& second)
32 {
33     // sort lift of child Elements by their slot
34     return first->GetSlot() < second->GetSlot();
35 }
36 
37 // adds elements from given list into a Map with key Element.GetSlot
MakeElementByIdMap(const std::list<RefPtr<Element>> & elmts,const std::list<std::string> & ids,std::map<std::string,Ace::RefPtr<Element>> & result)38 void ForEachElement::MakeElementByIdMap(const std::list<RefPtr<Element>>& elmts, const std::list<std::string>& ids,
39     std::map<std::string, Ace::RefPtr<Element>>& result)
40 {
41     ACE_SCOPED_TRACE("ForEachElement::UpdateWithComponent makeElmtByIdMap");
42 
43     ACE_DCHECK(ids.size() == elmts.size());
44 
45     // 1. step map Elements by their slot, because elmts is not sorted by slot
46     std::map<int, Ace::RefPtr<Element>> elmtsBySlotMap;
47     for (const auto& elmt : elmts) {
48         ACE_DCHECK(elmt->GetSlot() >= 0);
49         elmtsBySlotMap.emplace(elmt->GetSlot(), elmt);
50     }
51     ACE_DCHECK(elmtsBySlotMap.size() == elmts.size());
52 
53     // 2. map elmts by their id. Note ids list is in slot order
54     auto idsIter = ids.begin();
55     int slot = 0;
56     while (idsIter != ids.end()) {
57         auto elmtIter = elmtsBySlotMap.find(slot);
58         ACE_DCHECK(elmtIter != elmtsBySlotMap.end());
59         result.emplace(*idsIter, (*elmtIter).second);
60         idsIter++;
61         slot++;
62     }
63 }
64 
RemoveUnusedChildElementsFromRegistery(const std::list<std::string> & newIds) const65 void ForEachElement::RemoveUnusedChildElementsFromRegistery(const std::list<std::string>& newIds) const
66 {
67     ACE_SCOPED_TRACE("ForEachElement::RemoveUnusedChildElementsFromRegistery");
68 
69     // ID array before update
70     std::list<std::string> oldIds = GetIdArray();
71 
72     if (oldIds.empty()) {
73         return;
74     }
75 
76     // construct a set from newIds list for faster find/search
77     std::unordered_set<std::string> newIdsSet(newIds.begin(), newIds.end());
78 
79     // Element children before update
80     const auto& oldChildElementsRef = GetChildren();
81     std::list<RefPtr<Element>> oldChildElements(oldChildElementsRef); // make a copy of the list
82     oldChildElements.sort(CompareSlots); // needs sorting by their slot to match the order of oldIds array
83 
84     ACE_DCHECK((oldIds.size() == oldChildElements.size()) &&
85                "Number of IDs generated during previous render and number of ForEach child Elements must match");
86 
87     auto oldElementIter = oldChildElements.begin();
88     for (const auto& oldId : oldIds) {
89         // check if oldId still in newIds array
90         if (newIdsSet.find(oldId) == newIdsSet.end()) {
91             LOGD("ID '%{public}s' no more used, removing %{public}s(%{public}d) from ElementRegister", oldId.c_str(),
92                 AceType::TypeName((*oldElementIter)), (*oldElementIter)->GetElementId());
93             (*oldElementIter)->UnregisterForPartialUpdates();
94         }
95         oldElementIter++;
96     }
97 }
98 
Update()99 void ForEachElement::Update()
100 {
101     RefPtr<PartUpd::ForEachComponent> newFEComp = AceType::DynamicCast<PartUpd::ForEachComponent>(component_);
102     if (!newFEComp) {
103         LOGE("ForEachElement elmtId : %{public}d, no ForEachComponent set to update from, internal error",
104             GetElementId());
105         return;
106     }
107     LOGD("Update for ForEachElement, elmtId: %{public}d ....", GetElementId());
108     MultiComposedElement::Update();
109 
110     SetIdArray(newFEComp->GetIdArray());
111 }
112 
LocalizedUpdate()113 void ForEachElement::LocalizedUpdate()
114 {
115     ACE_SCOPED_TRACE("ForEachElement::LocalizedUpdate");
116 
117     RefPtr<PartUpd::ForEachComponent> newFEComp = AceType::DynamicCast<PartUpd::ForEachComponent>(component_);
118     if (!newFEComp) {
119         LOGE("ForEachElement elmtId : %{public}d, no ForEachComponent set to update from, internal error",
120             GetElementId());
121         return;
122     }
123 
124     LOGD("Local update for ForEachElement, elmtId: %{public}d ....", GetElementId());
125 
126     // result of id gen function of most re-recent render
127     // create a map for quicker find/search
128     std::list<std::string> newIds = newFEComp->GetIdArray();
129     std::unordered_set<std::string> newIdsSet(newIds.begin(), newIds.end());
130 
131     // result of id gen function of previous render/re-render
132     // create a map for quicker find/search
133     const auto& oldIds = GetIdArray();
134     std::unordered_set<std::string> oldIdsSet(oldIds.begin(), oldIds.end());
135 
136     // ForEachComponent only includes children for _newly created_ array items
137     // it does _not_ include children of array items that were rendered on a previous
138     // render
139     const auto& additionalChildComps = newFEComp->GetChildren();
140 
141     // create map id gen result -> Element
142     // old children
143     std::map<std::string, Ace::RefPtr<Element>> oldElmtsByIdMap;
144     MakeElementByIdMap(GetChildren(), oldIds, oldElmtsByIdMap);
145 
146     ACE_DCHECK((oldIds.size() == GetChildren().size()) &&
147                "Number of IDs generated during previous render and number of ForEach child Elements must match");
148     ACE_DCHECK(oldIdsSet.size() == oldIds.size());
149     ACE_DCHECK(GetChildren().size() == oldElmtsByIdMap.size());
150 
151 #ifdef ACE_DEBUG
152     std::string idS = "[";
153     for (const auto& oldId : oldIds) {
154         idS += oldId + ", ";
155     }
156     idS += "]";
157     auto idIter = oldIds.begin();
158     LOGD("  ... old Ids %{public}s .", idS.c_str());
159     LOGD("ForEachElement children before change:  ");
160     for (const auto& childElmt : GetChildren()) {
161         LOGD("   ... child arr-id %{public}s / renderSlot %{public}d, %{public}d", (*idIter).c_str(),
162             childElmt->GetSlot(), childElmt->GetRenderSlot());
163     }
164     LOGD("  ... total children Elements: %{public}d .", static_cast<int32_t>(GetChildren().size()));
165     idS = "[";
166     for (const auto& newId : newIds) {
167         idS += newId + ", ";
168     }
169     idS += "]";
170     LOGD("  ... new Ids %{public}s .", idS.c_str());
171     LOGD("  ... newly added child Components: %{public}u .", (uint32_t)additionalChildComps.size());
172 #endif
173 
174     auto firstChildElement = GetChildren().begin();
175     int renderSlot = GetRenderSlot();
176 
177     bool needRequestLayout = false;
178     int32_t slot = ((*firstChildElement) != nullptr) ? (*firstChildElement)->GetSlot() : 0;
179     int additionalChildIndex = 0;
180     for (const auto& newId : newIds) {
181         if (oldIdsSet.find(newId) == oldIdsSet.end()) {
182             // found a newly added ID
183             // insert component into 'slot'
184             auto newCompsIter = additionalChildComps.begin();
185             std::advance(newCompsIter, additionalChildIndex++);
186             LOGD("Elmt with New arr-id '%{public}s', inserting to slot %{public}d / renderSlot %{public}d",
187                 newId.c_str(), slot, renderSlot);
188             InflateComponent(*newCompsIter, slot, renderSlot);
189             needRequestLayout = false;
190         } else {
191             // the ID was used before, only need to update the child Element's slot
192             auto iter = oldElmtsByIdMap.find(newId);
193             auto oldElmt = (*iter).second;
194             LOGD("Elmt with arr-id %{public}s retained, update its slot %{public}d->%{public}d /"
195                  " renderSlot %{public}d->%{public}d",
196                 newId.c_str(), oldElmt->GetSlot(), slot, oldElmt->GetRenderSlot(), renderSlot);
197             ChangeChildSlot(oldElmt, slot);
198             ChangeChildRenderSlot(oldElmt, renderSlot, true);
199             needRequestLayout = true;
200         }
201         slot++;
202         renderSlot++;
203     }
204 
205     for (const auto& oldId : oldIds) {
206         // check if oldId still in newIds array
207         if (newIdsSet.find(oldId) == newIdsSet.end()) {
208             // the ID is no longer used, delete the child Element
209             auto iter = oldElmtsByIdMap.find(oldId);
210             auto oldElmt = iter->second;
211             LOGD("Array Id %{public}s no more used, deleting %{public}s(%{public}d)", oldId.c_str(),
212                 AceType::TypeName(oldElmt), oldElmt->GetElementId());
213             // no new child component
214             UpdateChild(oldElmt, nullptr);
215             needRequestLayout = false;
216         }
217     }
218     SetIdArray(newFEComp->GetIdArray());
219     Update();
220     if (needRequestLayout) {
221         auto renderNode = GetRenderNode();
222         if (renderNode != nullptr) {
223             renderNode->MarkNeedLayout();
224         }
225     }
226     SetNewComponent(nullptr);
227 #ifdef ACE_DEBUG
228     LOGD("ForEachElement children after change:  ");
229     for (const auto& childElmt : GetChildren()) {
230         LOGD("   ... slot %{public}d / renderSlot %{public}d.", childElmt->GetSlot(), childElmt->GetRenderSlot());
231     }
232     LOGD("  ... total children Elements: %{public}d .", static_cast<int32_t>(GetChildren().size()));
233 #endif
234 }
235 } // namespace OHOS::Ace::PartUpd
236