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