• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 "core/components_ng/pattern/list/list_position_map.h"
17 
18 namespace OHOS::Ace::NG {
UpdatePosRange(int32_t startIndex,int32_t endIndex,ListPositionInfo posInfo,float space,int32_t lanes)19 void ListPositionMap::UpdatePosRange(int32_t startIndex, int32_t endIndex,
20     ListPositionInfo posInfo, float space, int32_t lanes)
21 {
22     for (int32_t i = startIndex; i < endIndex; i++) {
23         posMap_[i] = posInfo;
24         if (lanes >= 1 && (i % lanes == lanes - 1)) {
25             posInfo.mainPos = posInfo.mainPos + posInfo.mainSize + space;
26         }
27     }
28 }
29 
UpdatePosWithCheck(int32_t index,ListPositionInfo posInfo)30 void ListPositionMap::UpdatePosWithCheck(int32_t index, ListPositionInfo posInfo)
31 {
32     auto iter = posMap_.find(index);
33     if (iter == posMap_.end()) {
34         posMap_[index] = posInfo;
35         return;
36     }
37     iter->second.isGroup = posInfo.isGroup;
38     if (LessNotEqual(iter->second.mainSize, posInfo.mainSize)) {
39         iter->second.mainSize = posInfo.mainSize;
40     }
41 }
42 
CheckPosMapUpdateRule()43 ListPosMapUpdate ListPositionMap::CheckPosMapUpdateRule()
44 {
45     ListPosMapUpdate flag;
46     if (dirty_ == LIST_NO_CHANGE) {
47         flag = ListPosMapUpdate::NO_CHANGE;
48     } else if (0 == (dirty_ & (LIST_UPDATE_CHILD_SIZE | LIST_UPDATE_LANES | LIST_GROUP_UPDATE_HEADER_FOOTER |
49                                     LIST_UPDATE_HEADER_FOOTER))) {
50         flag = ListPosMapUpdate::UPDATE_ALL_SIZE;
51     } else {
52         flag = ListPosMapUpdate::RE_CALCULATE;
53     }
54     return flag;
55 }
56 
UpdatePosMapStart(float delta,double & listCurrentPos,float space,int32_t startIndex,float startPos,bool groupAtStart)57 void ListPositionMap::UpdatePosMapStart(float delta, double& listCurrentPos, float space,
58     int32_t startIndex, float startPos, bool groupAtStart)
59 {
60     auto it = posMap_.find(startIndex);
61     if (it == posMap_.begin() || it == posMap_.end()) {
62         return;
63     }
64     startPos = startPos + listCurrentPos;
65     it--;
66     float prevPos = it->second.mainPos + it->second.mainSize + space;
67     int32_t prevIndex = it->first;
68     if (prevIndex + 1 >= startIndex && groupAtStart) {
69         if (NearEqual(prevPos, startPos)) {
70             return;
71         }
72     } else {
73         if (LessNotEqual(prevPos, startPos)) {
74             return;
75         }
76     }
77     listCurrentPos += prevPos - startPos;
78 }
79 
UpdatePosMapEnd(int32_t prevEndIndex,float space,bool groupAtEnd)80 void ListPositionMap::UpdatePosMapEnd(int32_t prevEndIndex, float space, bool groupAtEnd)
81 {
82     auto it = posMap_.find(prevEndIndex);
83     if (it == posMap_.end()) {
84         return;
85     }
86     float prevPos = it->second.mainPos + it->second.mainSize + space;
87     it++;
88     if (it == posMap_.end()) {
89         return;
90     }
91     if (prevEndIndex + 1 >= it->first && groupAtEnd) {
92         if (NearEqual(prevPos, it->second.mainPos)) {
93             return;
94         }
95     } else {
96         if (LessNotEqual(prevPos, it->second.mainPos)) {
97             return;
98         }
99     }
100     float delta = prevPos - it->second.mainPos;
101     while (it != posMap_.end()) {
102         it->second.mainPos += delta;
103         it++;
104     }
105 }
106 
CalculateUINode(RefPtr<UINode> node)107 void ListPositionMap::CalculateUINode(RefPtr<UINode> node)
108 {
109     CHECK_NULL_VOID(node);
110     auto children = node->GetChildren();
111     for (const auto& child : children) {
112         if (AceType::InstanceOf<FrameNode>(child)) {
113             auto frameNode = AceType::DynamicCast<FrameNode>(child);
114             CalculateFrameNode(frameNode);
115         } else if (AceType::InstanceOf<LazyForEachNode>(child)) {
116             // Rules: only one type node(ListItem or ListItemGroup) can exist in LazyForEach.
117             CalculateLazyForEachNode(child);
118         } else if (AceType::InstanceOf<RepeatVirtualScrollNode>(child)) {
119             CalculateLazyForEachNode(child);
120         } else if (AceType::InstanceOf<RepeatVirtualScroll2Node>(child)) {
121             CalculateLazyForEachNode(child);
122         } else {
123             CalculateUINode(child);
124         }
125     }
126 }
127 
GetLazyForEachChildIsGroup(RefPtr<UINode> node)128 std::optional<bool> ListPositionMap::GetLazyForEachChildIsGroup(RefPtr<UINode> node)
129 {
130     std::optional<bool> isGroup;
131     auto children = node->GetChildren();
132     if (children.size() > 0) {
133         auto child = children.begin();
134         while (child != children.end() && !((*child)->GetFrameChildByIndex(0, false))) {
135             child++;
136         }
137         auto frameNode = AceType::DynamicCast<FrameNode>((*child)->GetFrameChildByIndex(0, false));
138         if (frameNode) {
139             isGroup = frameNode->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
140         }
141     }
142     if (!(isGroup.has_value())) {
143         auto listNode = node->GetParentFrameNode();
144         CHECK_NULL_RETURN(listNode, isGroup);
145         auto wrapper = listNode->GetOrCreateChildByIndex(curIndex_);
146         CHECK_NULL_RETURN(wrapper, isGroup);
147         isGroup = wrapper->GetHostTag() == V2::LIST_ITEM_GROUP_ETS_TAG;
148     }
149     return isGroup;
150 }
151 
CalculateLazyForEachNode(RefPtr<UINode> node)152 void ListPositionMap::CalculateLazyForEachNode(RefPtr<UINode> node)
153 {
154     int32_t count = node->FrameCount();
155     if (count <= 0) {
156         return;
157     }
158     std::optional<bool> isGroup = GetLazyForEachChildIsGroup(node);
159     if (!(isGroup.has_value())) {
160         TAG_LOGW(AceLogTag::ACE_LIST,
161             "ListPositionMap Conflict: LazyForEach FrameCount > 0, but get child type failed.");
162         isGroup = false;
163     }
164     while (count--) {
165         isGroup.value() ? CalculateGroupNode() : CalculateListItemNode();
166     }
167     return;
168 }
169 
CalculateFrameNode(RefPtr<FrameNode> frameNode)170 void ListPositionMap::CalculateFrameNode(RefPtr<FrameNode> frameNode)
171 {
172     CHECK_NULL_VOID(frameNode);
173     auto listItemGroupPatten = frameNode->GetPattern<ListItemGroupPattern>();
174     if (listItemGroupPatten) {
175         CalculateGroupNode();
176     } else {
177         CalculateListItemNode();
178     }
179 }
180 
CalculateListItemNode()181 void ListPositionMap::CalculateListItemNode()
182 {
183     curRowHeight_ = std::max(curRowHeight_, childrenSize_->GetChildSize(curIndex_));
184     curLine_++;
185     if (curLine_ == lanes_ || curIndex_ == totalItemCount_ - 1) {
186         while (curLine_) {
187             curLine_--;
188             posMap_[curIndex_ - curLine_] = { totalHeight_, curRowHeight_ };
189         }
190         totalHeight_ += (curRowHeight_ + space_);
191         curRowHeight_ = 0.0f;
192     }
193     curIndex_++;
194 }
195 
CalculateGroupNode()196 void ListPositionMap::CalculateGroupNode()
197 {
198     if (curLine_ > 0) {
199         while (curLine_) {
200             curLine_--;
201             posMap_[curIndex_ - 1 - curLine_] = { totalHeight_, curRowHeight_ };
202         }
203         totalHeight_ += (curRowHeight_ + space_);
204         curRowHeight_ = 0.0f;
205     }
206     curRowHeight_ = childrenSize_->GetChildSize(curIndex_);
207     posMap_[curIndex_] = { totalHeight_, curRowHeight_ };
208     totalHeight_ += (curRowHeight_ + space_);
209     curLine_ = 0;
210     curRowHeight_ = 0.0f;
211     curIndex_++;
212 }
213 
PosMapRecalculate(LayoutWrapper * layoutWrapper)214 void ListPositionMap::PosMapRecalculate(LayoutWrapper* layoutWrapper)
215 {
216     curIndex_ = 0;
217     curLine_ = 0;
218     totalHeight_ = 0.0f;
219     curRowHeight_ = 0.0f;
220     if (lanes_ == 1) {
221         for (int32_t index = 0; index < totalItemCount_; index++) {
222             curRowHeight_ = childrenSize_->GetChildSize(index);
223             posMap_[index] = { totalHeight_, curRowHeight_ };
224             totalHeight_ += (curRowHeight_ + space_);
225         }
226         totalHeight_ -= space_;
227     } else {
228         auto listNode = layoutWrapper->GetHostNode();
229         CHECK_NULL_VOID(listNode);
230         CalculateUINode(listNode);
231         totalHeight_ -= space_;
232     }
233     TAG_LOGI(AceLogTag::ACE_LIST, "List PosMapRecalculate totalHeight:%{public}f prevTotalHeight:%{public}f, "
234         "lanes:%{public}d, space:%{public}f, totalItemCount:%{public}d",
235         totalHeight_, prevTotalHeight_, lanes_, space_, totalItemCount_);
236 }
237 
GroupPosMapRecalculate()238 void ListPositionMap::GroupPosMapRecalculate()
239 {
240     curIndex_ = 0;
241     totalHeight_ = headerSize_;
242     curRowHeight_ = 0.0f;
243     curLine_ = 0;
244 
245     for (int32_t index = 0; index < totalItemCount_;) {
246         while (curLine_ < lanes_) {
247             curRowHeight_ = std::max(curRowHeight_, childrenSize_->GetChildSize(index + curLine_));
248             curLine_++;
249         }
250         curLine_ = 0;
251         int32_t curRowEndIndex = std::min(index + lanes_ - 1, totalItemCount_ - 1);
252         while (index <= curRowEndIndex) {
253             posMap_[index++] = { totalHeight_, curRowHeight_ };
254         }
255         totalHeight_ += (curRowHeight_ + space_);
256         curRowHeight_ = 0.0f;
257     }
258     totalHeight_ = totalHeight_ - space_ + footerSize_;
259 }
260 
UpdatePosMap(LayoutWrapper * layoutWrapper,int32_t lanes,float space,RefPtr<ListChildrenMainSize> & childrenSize)261 void ListPositionMap::UpdatePosMap(LayoutWrapper* layoutWrapper, int32_t lanes, float space,
262     RefPtr<ListChildrenMainSize>& childrenSize)
263 {
264     childrenSize_ = childrenSize;
265     if (totalItemCount_ != layoutWrapper->GetTotalChildCount()) {
266         dirty_ |= LIST_UPDATE_ITEM_COUNT;
267         totalItemCount_ = layoutWrapper->GetTotalChildCount();
268     }
269     if (lanes != lanes_) {
270         dirty_ |= LIST_UPDATE_LANES;
271         lanes_ = lanes;
272     }
273     if (!NearEqual(space, space_)) {
274         dirty_ |= LIST_UPDATE_SPACE;
275         space_ = space;
276     }
277     switch (CheckPosMapUpdateRule()) {
278         case ListPosMapUpdate::NO_CHANGE:
279             break;
280         case ListPosMapUpdate::UPDATE_ALL_SIZE:
281             PosMapRecalculate(layoutWrapper);
282             break;
283         case ListPosMapUpdate::RE_CALCULATE:
284         default:
285             PosMapRecalculate(layoutWrapper);
286     }
287     ClearDirty();
288 }
289 
UpdateGroupPosMap(int32_t totalCount,int32_t lanes,float space,RefPtr<ListChildrenMainSize> & childrenSize,float headerSize,float footerSize)290 void ListPositionMap::UpdateGroupPosMap(int32_t totalCount, int32_t lanes, float space,
291     RefPtr<ListChildrenMainSize>& childrenSize, float headerSize, float footerSize)
292 {
293     childrenSize_ = childrenSize;
294     prevTotalHeight_ = totalHeight_;
295     if (totalCount != totalItemCount_) {
296         dirty_ |= LIST_UPDATE_ITEM_COUNT;
297         totalItemCount_ = totalCount;
298     }
299     if (lanes != lanes_) {
300         dirty_ |= LIST_UPDATE_LANES;
301         lanes_ = lanes;
302     }
303     if (!NearEqual(space, space_)) {
304         dirty_ |= LIST_UPDATE_SPACE;
305         space_ = space;
306     }
307     if (!NearEqual(headerSize, headerSize_) || !NearEqual(footerSize, footerSize_)) {
308         dirty_ |= LIST_GROUP_UPDATE_HEADER_FOOTER;
309         headerSize_ = headerSize;
310         footerSize_ = footerSize;
311     }
312     if (totalItemCount_ <= 0) {
313         totalHeight_ = 0;
314         ClearPosMap();
315         ClearDirty();
316         return;
317     }
318     switch (CheckPosMapUpdateRule()) {
319         case ListPosMapUpdate::NO_CHANGE:
320             break;
321         case ListPosMapUpdate::UPDATE_ALL_SIZE:
322             GroupPosMapRecalculate();
323             break;
324         case ListPosMapUpdate::RE_CALCULATE:
325         default:
326             GroupPosMapRecalculate();
327     }
328     ClearDirty();
329 }
330 
UpdateTotalCount(int32_t totalCount)331 void ListPositionMap::UpdateTotalCount(int32_t totalCount)
332 {
333     totalItemCount_ = totalCount;
334     auto iter = posMap_.lower_bound(totalCount);
335     posMap_.erase(iter, posMap_.end());
336 }
337 
GetPositionInfo(int32_t index) const338 ListPositionInfo ListPositionMap::GetPositionInfo(int32_t index) const
339 {
340     auto it = posMap_.find(index);
341     if (it == posMap_.end()) {
342         return { -1.0f, -1.0f };
343     }
344     return it->second;
345 }
346 
GetStartIndexAndPos() const347 std::pair<int32_t, float> ListPositionMap::GetStartIndexAndPos() const
348 {
349     if (posMap_.empty()) {
350         return { -1, 0.0f };
351     }
352     const auto& start = posMap_.begin();
353     return { start->first, start->second.mainPos };
354 }
355 
GetEndIndexAndPos() const356 std::pair<int32_t, float> ListPositionMap::GetEndIndexAndPos() const
357 {
358     if (posMap_.empty()) {
359         return { -1, 0.0f };
360     }
361     const auto& end = posMap_.rbegin();
362     return { end->first, end->second.mainPos + end->second.mainSize };
363 }
364 
OptimizeBeforeMeasure(int32_t & beginIndex,float & beginPos,const float offset,const float contentSize)365 void ListPositionMap::OptimizeBeforeMeasure(int32_t& beginIndex, float& beginPos,
366     const float offset, const float contentSize)
367 {
368     if (NearZero(offset) || GreatOrEqual(contentSize, totalHeight_)) {
369         return;
370     }
371     float chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(beginIndex) : 0.0f;
372     if (Positive(offset)) {
373         float criticalPos = offset;
374         std::pair<int32_t, float> rowInfo = GetRowEndIndexAndHeight(beginIndex);
375         while (!NearEqual(posMap_[beginIndex].mainPos + rowInfo.second, totalHeight_) &&
376             LessNotEqual(beginPos + rowInfo.second + chainOffset, criticalPos)) {
377             beginIndex = rowInfo.first + 1;
378             beginPos += (rowInfo.second + space_);
379             rowInfo = GetRowEndIndexAndHeight(beginIndex);
380             chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(beginIndex) : 0.0f;
381         }
382     } else {
383         float criticalPos = offset + contentSize;
384         std::pair<int32_t, float> rowInfo = GetRowEndIndexAndHeight(beginIndex);
385         while (Positive(posMap_[beginIndex].mainPos) &&
386             GreatNotEqual(beginPos - rowInfo.second + chainOffset, criticalPos)) {
387             beginIndex = GetRowStartIndex(beginIndex) - 1;
388             beginPos -= (rowInfo.second + space_);
389             rowInfo = GetRowEndIndexAndHeight(beginIndex);
390             chainOffset = chainOffsetFunc_ ? chainOffsetFunc_(beginIndex) : 0.0f;
391         }
392     }
393 }
394 
GetRowStartIndex(const int32_t input)395 int32_t ListPositionMap::GetRowStartIndex(const int32_t input)
396 {
397     int32_t startIndex = input;
398     while (startIndex > 0 && NearEqual(posMap_[startIndex].mainPos, posMap_[startIndex - 1].mainPos)) {
399         startIndex--;
400     }
401     return startIndex;
402 }
403 
GetRowEndIndex(const int32_t input)404 int32_t ListPositionMap::GetRowEndIndex(const int32_t input)
405 {
406     std::pair<int32_t, float> rowInfo = GetRowEndIndexAndHeight(input);
407     return rowInfo.first;
408 }
409 
GetRowHeight(int32_t input)410 float ListPositionMap::GetRowHeight(int32_t input)
411 {
412     std::pair<int32_t, float> rowInfo = GetRowEndIndexAndHeight(input);
413     return rowInfo.second;
414 }
415 
GetRowEndIndexAndHeight(const int32_t input)416 std::pair<int32_t, float> ListPositionMap::GetRowEndIndexAndHeight(const int32_t input)
417 {
418     int32_t endIndex = input;
419     float rowHeight = 0.0f;
420     while (endIndex < (totalItemCount_ - 1) &&
421         NearEqual(posMap_[endIndex].mainPos, posMap_[endIndex + 1].mainPos)) {
422         endIndex++;
423     }
424     if (endIndex == totalItemCount_ - 1) {
425         rowHeight = totalHeight_ - posMap_[endIndex].mainPos - footerSize_;
426     } else {
427         rowHeight = posMap_[endIndex + 1].mainPos  - posMap_[endIndex].mainPos - space_;
428     }
429     return { endIndex, rowHeight };
430 }
431 
ReversePosMap()432 void ListPositionMap::ReversePosMap()
433 {
434     totalHeight_ = 0.0f;
435     float curRowHeight = posMap_.begin()->second.mainSize;
436     std::map<int32_t, ListPositionInfo> posMap;
437     for (int32_t index = totalItemCount_ - 1; index >= 0; --index) {
438         auto posIndex = totalItemCount_ - index - 1;
439         posMap[posIndex] = {totalHeight_, posMap_[posIndex].mainSize};
440         if (index > 0 && !NearEqual(posMap_[index].mainPos, posMap_[index - 1].mainPos)) {
441             curRowHeight = std::max(posMap_[posIndex].mainSize, curRowHeight);
442             totalHeight_ += (curRowHeight + space_);
443             curRowHeight = 0.0f;
444         } else {
445             curRowHeight = std::max(posMap_[posIndex].mainSize, curRowHeight);
446         }
447     }
448     posMap_ = std::move(posMap);
449     totalHeight_ += curRowHeight;
450 }
451 } // namespace OHOS::Ace::NG
452