• 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 "frameworks/core/components_part_upd/foreach/foreach_component.h"
19 
20 namespace OHOS::Ace::PartUpd {
21 
CanUpdate(const RefPtr<Component> & newComponent)22 bool ForEachElement::CanUpdate(const RefPtr<Component>& newComponent)
23 {
24     return AceType::InstanceOf<PartUpd::ForEachComponent>(newComponent);
25 }
26 
CompareSlots(const RefPtr<Element> & first,const RefPtr<Element> & second)27 bool ForEachElement::CompareSlots(const RefPtr<Element>& first, const RefPtr<Element>& second)
28 {
29     // sort lift of child Elements by their slot
30     return first->GetSlot() < second->GetSlot();
31 }
32 
33 // 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)34 void ForEachElement::MakeElementByIdMap(const std::list<RefPtr<Element>>& elmts, const std::list<std::string>& ids,
35     std::map<std::string, Ace::RefPtr<Element>>& result)
36 {
37     ACE_SCOPED_TRACE("ForEachElement::UpdateWithComponent makeElmtByIdMap");
38 
39     ACE_DCHECK(ids.size() == elmts.size());
40 
41     // 1. step map Elements by their slot, because elmts is not sorted by slot
42     std::map<int, Ace::RefPtr<Element>> elmtsBySlotMap;
43     for (const auto& elmt : elmts) {
44         ACE_DCHECK(elmt->GetSlot() >= 0);
45         elmtsBySlotMap.emplace(elmt->GetSlot(), elmt);
46     }
47     ACE_DCHECK(elmtsBySlotMap.size() == elmts.size());
48 
49     // 2. map elmts by their id. Note ids list is in slot order
50     auto idsIter = ids.begin();
51     int slot = 0;
52     while (idsIter != ids.end()) {
53         auto elmtIter = elmtsBySlotMap.find(slot);
54         ACE_DCHECK(elmtIter != elmtsBySlotMap.end());
55         result.emplace(*idsIter, (*elmtIter).second);
56         idsIter++;
57         slot++;
58     }
59 }
60 
RemoveUnusedChildElementsFromRegistery(const std::list<std::string> & newIds) const61 void ForEachElement::RemoveUnusedChildElementsFromRegistery(const std::list<std::string>& newIds) const
62 {
63     ACE_SCOPED_TRACE("ForEachElement::RemoveUnusedChildElementsFromRegistery");
64 
65     // ID array before update
66     std::list<std::string> oldIds = GetIdArray();
67 
68     if (oldIds.empty()) {
69         return;
70     }
71 
72     // construct a set from newIds list for faster find/search
73     std::unordered_set<std::string> newIdsSet(newIds.begin(), newIds.end());
74 
75     // Element children before update
76     const auto& oldChildElementsRef = GetChildren();
77     std::list<RefPtr<Element>> oldChildElements(oldChildElementsRef); // make a copy of the list
78     oldChildElements.sort(CompareSlots); // needs sorting by their slot to match the order of oldIds array
79 
80     ACE_DCHECK((oldIds.size() == oldChildElements.size()) &&
81                "Number of IDs generated during previous render and number of ForEach child Elements must match");
82 
83     auto oldElementIter = oldChildElements.begin();
84     for (const auto& oldId : oldIds) {
85         // check if oldId still in newIds array
86         if (newIdsSet.find(oldId) == newIdsSet.end()) {
87             (*oldElementIter)->UnregisterForPartialUpdates();
88         }
89         oldElementIter++;
90     }
91 }
92 
Update()93 void ForEachElement::Update()
94 {
95     RefPtr<PartUpd::ForEachComponent> newFEComp = AceType::DynamicCast<PartUpd::ForEachComponent>(component_);
96     if (!newFEComp) {
97         LOGE("ForEachElement elmtId : %{public}d, no ForEachComponent set to update from, internal error",
98             GetElementId());
99         return;
100     }
101     MultiComposedElement::Update();
102 
103     SetIdArray(newFEComp->GetIdArray());
104 }
105 
LocalizedUpdate()106 void ForEachElement::LocalizedUpdate()
107 {
108     ACE_SCOPED_TRACE("ForEachElement::LocalizedUpdate");
109 
110     RefPtr<PartUpd::ForEachComponent> newFEComp = AceType::DynamicCast<PartUpd::ForEachComponent>(component_);
111     if (!newFEComp) {
112         LOGE("ForEachElement elmtId : %{public}d, no ForEachComponent set to update from, internal error",
113             GetElementId());
114         return;
115     }
116     // result of id gen function of most re-recent render
117     // create a map for quicker find/search
118     std::list<std::string> newIds = newFEComp->GetIdArray();
119     std::unordered_set<std::string> newIdsSet(newIds.begin(), newIds.end());
120 
121     // result of id gen function of previous render/re-render
122     // create a map for quicker find/search
123     const auto& oldIds = GetIdArray();
124     std::unordered_set<std::string> oldIdsSet(oldIds.begin(), oldIds.end());
125 
126     // ForEachComponent only includes children for _newly created_ array items
127     // it does _not_ include children of array items that were rendered on a previous
128     // render
129     const auto& additionalChildComps = newFEComp->GetChildren();
130 
131     // create map id gen result -> Element
132     // old children
133     std::map<std::string, Ace::RefPtr<Element>> oldElmtsByIdMap;
134     MakeElementByIdMap(GetChildren(), oldIds, oldElmtsByIdMap);
135 
136     ACE_DCHECK((oldIds.size() == GetChildren().size()) &&
137                "Number of IDs generated during previous render and number of ForEach child Elements must match");
138     ACE_DCHECK(oldIdsSet.size() == oldIds.size());
139     ACE_DCHECK(GetChildren().size() == oldElmtsByIdMap.size());
140 
141     auto firstChildElement = GetChildren().begin();
142     int renderSlot = GetRenderSlot();
143 
144     bool needRequestLayout = false;
145     int32_t slot = ((*firstChildElement) != nullptr) ? (*firstChildElement)->GetSlot() : 0;
146     int additionalChildIndex = 0;
147     for (const auto& newId : newIds) {
148         if (oldIdsSet.find(newId) == oldIdsSet.end()) {
149             // found a newly added ID
150             // insert component into 'slot'
151             auto newCompsIter = additionalChildComps.begin();
152             std::advance(newCompsIter, additionalChildIndex++);
153             InflateComponent(*newCompsIter, slot, renderSlot);
154             needRequestLayout = false;
155         } else {
156             // the ID was used before, only need to update the child Element's slot
157             auto iter = oldElmtsByIdMap.find(newId);
158             auto oldElmt = (*iter).second;
159             ChangeChildSlot(oldElmt, slot);
160             ChangeChildRenderSlot(oldElmt, renderSlot, true);
161             needRequestLayout = true;
162         }
163         slot++;
164         renderSlot++;
165     }
166 
167     for (const auto& oldId : oldIds) {
168         // check if oldId still in newIds array
169         if (newIdsSet.find(oldId) == newIdsSet.end()) {
170             // the ID is no longer used, delete the child Element
171             auto iter = oldElmtsByIdMap.find(oldId);
172             auto oldElmt = iter->second;
173             // no new child component
174             UpdateChild(oldElmt, nullptr);
175             needRequestLayout = false;
176         }
177     }
178     SetIdArray(newFEComp->GetIdArray());
179     Update();
180     if (needRequestLayout) {
181         auto renderNode = GetRenderNode();
182         if (renderNode != nullptr) {
183             renderNode->MarkNeedLayout();
184         }
185     }
186     SetNewComponent(nullptr);
187 }
188 } // namespace OHOS::Ace::PartUpd
189