1 /*
2  * Copyright (c) 2022-2023 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_item_group_pattern.h"
17 
18 #include "base/log/dump_log.h"
19 #include "core/components/list/list_item_theme.h"
20 #include "core/components_ng/pattern/list/list_item_group_layout_algorithm.h"
21 #include "core/components_ng/pattern/list/list_item_group_paint_method.h"
22 #include "core/components_ng/pattern/list/list_pattern.h"
23 #include "core/pipeline_ng/pipeline_context.h"
24 
25 namespace OHOS::Ace::NG {
26 
OnAttachToFrameNode()27 void ListItemGroupPattern::OnAttachToFrameNode()
28 {
29     auto host = GetHost();
30     CHECK_NULL_VOID(host);
31     if (listItemGroupStyle_ == V2::ListItemGroupStyle::CARD) {
32         SetListItemGroupDefaultAttributes(host);
33     }
34 }
35 
OnColorConfigurationUpdate()36 void ListItemGroupPattern::OnColorConfigurationUpdate()
37 {
38     if (listItemGroupStyle_ != V2::ListItemGroupStyle::CARD) {
39         return;
40     }
41     auto itemGroupNode = GetHost();
42     CHECK_NULL_VOID(itemGroupNode);
43     auto renderContext = itemGroupNode->GetRenderContext();
44     CHECK_NULL_VOID(renderContext);
45     auto pipeline = itemGroupNode->GetContext();
46     CHECK_NULL_VOID(pipeline);
47     auto listItemGroupTheme = pipeline->GetTheme<ListItemTheme>();
48     CHECK_NULL_VOID(listItemGroupTheme);
49 
50     renderContext->UpdateBackgroundColor(listItemGroupTheme->GetItemGroupDefaultColor());
51 }
52 
SetListItemGroupDefaultAttributes(const RefPtr<FrameNode> & itemGroupNode)53 void ListItemGroupPattern::SetListItemGroupDefaultAttributes(const RefPtr<FrameNode>& itemGroupNode)
54 {
55     auto renderContext = itemGroupNode->GetRenderContext();
56     CHECK_NULL_VOID(renderContext);
57     auto layoutProperty = itemGroupNode->GetLayoutProperty<ListItemGroupLayoutProperty>();
58     CHECK_NULL_VOID(layoutProperty);
59 
60     auto pipeline = GetContext();
61     CHECK_NULL_VOID(pipeline);
62     auto listItemGroupTheme = pipeline->GetTheme<ListItemTheme>();
63     CHECK_NULL_VOID(listItemGroupTheme);
64 
65     renderContext->UpdateBackgroundColor(listItemGroupTheme->GetItemGroupDefaultColor());
66 
67     MarginProperty itemGroupMargin;
68     itemGroupMargin.left = CalcLength(listItemGroupTheme->GetItemGroupDefaultLeftMargin());
69     itemGroupMargin.right = CalcLength(listItemGroupTheme->GetItemGroupDefaultRightMargin());
70     layoutProperty->UpdateMargin(itemGroupMargin);
71 
72     PaddingProperty itemGroupPadding;
73     itemGroupPadding.left = CalcLength(listItemGroupTheme->GetItemGroupDefaultPadding().Left());
74     itemGroupPadding.right = CalcLength(listItemGroupTheme->GetItemGroupDefaultPadding().Right());
75     itemGroupPadding.top = CalcLength(listItemGroupTheme->GetItemGroupDefaultPadding().Top());
76     itemGroupPadding.bottom = CalcLength(listItemGroupTheme->GetItemGroupDefaultPadding().Bottom());
77     layoutProperty->UpdatePadding(itemGroupPadding);
78 
79     renderContext->UpdateBorderRadius(listItemGroupTheme->GetItemGroupDefaultBorderRadius());
80 }
81 
DumpAdvanceInfo()82 void ListItemGroupPattern::DumpAdvanceInfo()
83 {
84     DumpLog::GetInstance().AddDesc("itemStartIndex:" + std::to_string(itemStartIndex_));
85     DumpLog::GetInstance().AddDesc("itemTotalCount:" + std::to_string(itemTotalCount_));
86     DumpLog::GetInstance().AddDesc("itemDisplayEndIndex:" + std::to_string(itemDisplayEndIndex_));
87     DumpLog::GetInstance().AddDesc("itemDisplayStartIndex:" + std::to_string(itemDisplayStartIndex_));
88     DumpLog::GetInstance().AddDesc("headerMainSize:" + std::to_string(headerMainSize_));
89     DumpLog::GetInstance().AddDesc("footerMainSize:" + std::to_string(footerMainSize_));
90     DumpLog::GetInstance().AddDesc("spaceWidth:" + std::to_string(spaceWidth_));
91     DumpLog::GetInstance().AddDesc("lanes:" + std::to_string(lanes_));
92     DumpLog::GetInstance().AddDesc("laneGutter:" + std::to_string(laneGutter_));
93     DumpLog::GetInstance().AddDesc("startHeaderPos:" + std::to_string(startHeaderPos_));
94     DumpLog::GetInstance().AddDesc("endFooterPos:" + std::to_string(endFooterPos_));
95 }
96 
CreateLayoutAlgorithm()97 RefPtr<LayoutAlgorithm> ListItemGroupPattern::CreateLayoutAlgorithm()
98 {
99     CalculateItemStartIndex();
100     auto layoutAlgorithm = MakeRefPtr<ListItemGroupLayoutAlgorithm>(headerIndex_, footerIndex_, itemStartIndex_);
101     layoutAlgorithm->SetItemsPosition(itemPosition_);
102     layoutAlgorithm->SetCachedItemsPosition(cachedItemPosition_);
103     layoutAlgorithm->SetCachedIndex(forwardCachedIndex_, backwardCachedIndex_);
104     layoutAlgorithm->SetLayoutedItemInfo(layoutedItemInfo_);
105     if (childrenSize_ && ListChildrenSizeExist()) {
106         if (!posMap_) {
107             posMap_ = MakeRefPtr<ListPositionMap>();
108         }
109         layoutAlgorithm->SetListChildrenMainSize(childrenSize_);
110         layoutAlgorithm->SetListPositionMap(posMap_);
111     }
112     return layoutAlgorithm;
113 }
114 
CreateNodePaintMethod()115 RefPtr<NodePaintMethod> ListItemGroupPattern::CreateNodePaintMethod()
116 {
117     auto layoutProperty = GetLayoutProperty<ListItemGroupLayoutProperty>();
118     V2::ItemDivider itemDivider;
119     auto divider = layoutProperty->GetDivider().value_or(itemDivider);
120     auto drawVertical = (axis_ == Axis::HORIZONTAL);
121     ListItemGroupPaintInfo listItemGroupPaintInfo { layoutDirection_, mainSize_, drawVertical, lanes_,
122         spaceWidth_, laneGutter_, itemTotalCount_ };
123     return MakeRefPtr<ListItemGroupPaintMethod>(
124         divider, listItemGroupPaintInfo, itemPosition_, cachedItemPosition_, pressedItem_);
125 }
126 
SyncItemsToCachedItemPosition()127 void ListItemGroupPattern::SyncItemsToCachedItemPosition()
128 {
129     itemPosition_.insert(cachedItemPosition_.begin(), cachedItemPosition_.end());
130     cachedItemPosition_.swap(itemPosition_);
131     itemPosition_.clear();
132 }
133 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)134 bool ListItemGroupPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
135 {
136     if (config.skipMeasure && config.skipLayout) {
137         return false;
138     }
139     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
140     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
141     auto layoutAlgorithm = DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
142     CHECK_NULL_RETURN(layoutAlgorithm, false);
143     itemTotalCount_ = layoutAlgorithm->GetTotalItemCount();
144     auto cacheParam = layoutAlgorithm->GetCacheParam();
145     if (cacheParam) {
146         forwardCachedIndex_ = cacheParam.value().forwardCachedIndex;
147         backwardCachedIndex_ = cacheParam.value().backwardCachedIndex;
148         adjustRefPos_ = layoutAlgorithm->GetAdjustReferenceDelta();
149         layoutAlgorithm->SetCacheParam(std::nullopt);
150     }
151     if (lanes_ != layoutAlgorithm->GetLanes()) {
152         lanes_ = layoutAlgorithm->GetLanes();
153         ClearCachedItemPosition();
154     }
155     itemPosition_ = layoutAlgorithm->GetItemPosition();
156     cachedItemPosition_ = layoutAlgorithm->GetCachedItemPosition();
157     spaceWidth_ = layoutAlgorithm->GetSpaceWidth();
158     axis_ = layoutAlgorithm->GetAxis();
159     layoutDirection_ = layoutAlgorithm->GetLayoutDirection();
160     mainSize_ = layoutAlgorithm->GetMainSize();
161     laneGutter_ = layoutAlgorithm->GetLaneGutter();
162     itemDisplayEndIndex_ = layoutAlgorithm->GetEndIndex();
163     itemDisplayStartIndex_ = layoutAlgorithm->GetStartIndex();
164     headerMainSize_ = layoutAlgorithm->GetHeaderMainSize();
165     footerMainSize_ = layoutAlgorithm->GetFooterMainSize();
166     layoutedItemInfo_ = layoutAlgorithm->GetLayoutedItemInfo();
167     startHeaderPos_ = layoutAlgorithm->GetStartHeaderPos();
168     endFooterPos_ = layoutAlgorithm->GetEndFooterPos();
169     adjustRefPos_ = layoutAlgorithm->GetAdjustReferenceDelta();
170     adjustTotalSize_ = layoutAlgorithm->GetAdjustTotalSize();
171     layouted_ = true;
172     CheckListDirectionInCardStyle();
173     auto host = GetHost();
174     CHECK_NULL_RETURN(host, false);
175     auto accessibilityProperty = host->GetAccessibilityProperty<ListItemGroupAccessibilityProperty>();
176     if (accessibilityProperty != nullptr) {
177         accessibilityProperty->SetCollectionItemCounts(layoutAlgorithm->GetTotalItemCount());
178     }
179     auto listLayoutProperty = host->GetLayoutProperty<ListItemGroupLayoutProperty>();
180     return listLayoutProperty && listLayoutProperty->GetDivider().has_value() && !itemPosition_.empty();
181 }
182 
GetPaddingAndMargin() const183 float ListItemGroupPattern::GetPaddingAndMargin() const
184 {
185     auto layoutProperty = GetLayoutProperty<ListItemGroupLayoutProperty>();
186     CHECK_NULL_RETURN(layoutProperty, 0.0f);
187     const auto& padding = layoutProperty->CreatePaddingAndBorder();
188     const auto& margin = layoutProperty->CreateMargin();
189     auto offsetBeforeContent = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
190     auto offsetAfterContent = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
191     offsetBeforeContent += axis_ == Axis::HORIZONTAL ? margin.left.value_or(0) : margin.top.value_or(0);
192     offsetAfterContent += axis_ == Axis::HORIZONTAL ? margin.right.value_or(0) : margin.bottom.value_or(0);
193     return offsetBeforeContent + offsetAfterContent;
194 }
195 
GetEstimateOffset(float height,const std::pair<float,float> & targetPos) const196 float ListItemGroupPattern::GetEstimateOffset(float height, const std::pair<float, float>& targetPos) const
197 {
198     if (layoutedItemInfo_.has_value() && layoutedItemInfo_.value().startIndex > 0) {
199         float averageHeight = 0.0f;
200         float estimateHeight = GetEstimateHeight(averageHeight);
201         if (layoutedItemInfo_.value().endIndex >= itemTotalCount_ - 1) {
202             return height + estimateHeight - targetPos.second;
203         } else {
204             return height - targetPos.first + layoutedItemInfo_.value().startIndex * averageHeight - spaceWidth_;
205         }
206     }
207     return height - targetPos.first;
208 }
209 
GetEstimateHeight(float & averageHeight) const210 float ListItemGroupPattern::GetEstimateHeight(float& averageHeight) const
211 {
212     auto layoutProperty = GetLayoutProperty<ListItemGroupLayoutProperty>();
213     CHECK_NULL_RETURN(layoutProperty, 0.0f);
214     auto visible = layoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
215     if (visible == VisibleType::GONE) {
216         return 0.0f;
217     }
218     float paddingAndMargin = GetPaddingAndMargin();
219     if (layoutedItemInfo_.has_value()) {
220         auto totalHeight = (layoutedItemInfo_.value().endPos - layoutedItemInfo_.value().startPos + spaceWidth_);
221         auto itemCount = layoutedItemInfo_.value().endIndex - layoutedItemInfo_.value().startIndex + 1;
222         averageHeight = totalHeight / itemCount;
223     }
224     if (layouted_) {
225         if (itemTotalCount_ > 0) {
226             return itemTotalCount_ * averageHeight + headerMainSize_ + footerMainSize_ + paddingAndMargin - spaceWidth_;
227         } else {
228             return headerMainSize_ + footerMainSize_ + paddingAndMargin;
229         }
230     }
231     auto host = GetHost();
232     auto totalItem = host->GetTotalChildCount();
233     return averageHeight * totalItem + paddingAndMargin;
234 }
235 
CheckListDirectionInCardStyle()236 void ListItemGroupPattern::CheckListDirectionInCardStyle()
237 {
238     if (axis_ == Axis::HORIZONTAL && listItemGroupStyle_ == V2::ListItemGroupStyle::CARD) {
239         auto host = GetHost();
240         CHECK_NULL_VOID(host);
241         RefPtr<FrameNode> listNode = AceType::DynamicCast<FrameNode>(host->GetParent());
242         CHECK_NULL_VOID(listNode);
243         auto listPattern = listNode->GetPattern<ListPattern>();
244         CHECK_NULL_VOID(listPattern);
245         listPattern->SetNeedToUpdateListDirectionInCardStyle(true);
246     }
247 }
248 
GetListFrameNode() const249 RefPtr<FrameNode> ListItemGroupPattern::GetListFrameNode() const
250 {
251     auto host = GetHost();
252     CHECK_NULL_RETURN(host, nullptr);
253     auto parent = host->GetParent();
254     RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
255     while (parent && !frameNode) {
256         parent = parent->GetParent();
257         frameNode = AceType::DynamicCast<FrameNode>(parent);
258     }
259     return frameNode;
260 }
261 
ListChildrenSizeExist()262 bool ListItemGroupPattern::ListChildrenSizeExist()
263 {
264     RefPtr<FrameNode> listNode = GetListFrameNode();
265     CHECK_NULL_RETURN(listNode, false);
266     auto listPattern = listNode->GetPattern<ListPattern>();
267     CHECK_NULL_RETURN(listPattern, false);
268     return listPattern->ListChildrenSizeExist();
269 }
270 
GetOrCreateListChildrenMainSize()271 RefPtr<ListChildrenMainSize> ListItemGroupPattern::GetOrCreateListChildrenMainSize()
272 {
273     if (childrenSize_) {
274         return childrenSize_;
275     }
276     childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>();
277     auto callback = [weakPattern = WeakClaim(this)](std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag) {
278         auto pattern = weakPattern.Upgrade();
279         CHECK_NULL_VOID(pattern);
280         auto context = PipelineContext::GetCurrentContext();
281         CHECK_NULL_VOID(context);
282         context->AddBuildFinishCallBack([weakPattern, change, flag]() {
283             auto pattern = weakPattern.Upgrade();
284             CHECK_NULL_VOID(pattern);
285             pattern->OnChildrenSizeChanged(change, flag);
286         });
287         context->RequestFrame();
288     };
289     childrenSize_->SetOnDataChange(callback);
290     return childrenSize_;
291 }
292 
SetListChildrenMainSize(float defaultSize,const std::vector<float> & mainSize)293 void ListItemGroupPattern::SetListChildrenMainSize(
294     float defaultSize, const std::vector<float>& mainSize)
295 {
296     childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>(mainSize, defaultSize);
297     OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
298 }
299 
OnChildrenSizeChanged(std::tuple<int32_t,int32_t,int32_t> change,ListChangeFlag flag)300 void ListItemGroupPattern::OnChildrenSizeChanged(std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag)
301 {
302     if (!posMap_) {
303         posMap_ = MakeRefPtr<ListPositionMap>();
304     }
305     posMap_->MarkDirty(flag);
306     auto host = GetHost();
307     CHECK_NULL_VOID(host);
308     host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
309 }
310 
GetStartListItemIndex()311 VisibleContentInfo ListItemGroupPattern::GetStartListItemIndex()
312 {
313     bool isHeader = false;
314     auto startHeaderMainSize = GetHeaderMainSize();
315     auto startFooterMainSize = GetFooterMainSize();
316     if (GetDisplayStartIndexInGroup() == 0) {
317         auto startHeaderPos = startHeaderPos_;
318         isHeader = (startHeaderPos + startHeaderMainSize) > 0 ? true : false;
319     }
320     auto startPositionSize = GetItemPosition().size();
321     auto startItemIndexInGroup = GetDisplayStartIndexInGroup();
322     auto startArea = ListItemGroupArea::IN_LIST_ITEM_AREA;
323     if (startPositionSize == 0 && startFooterMainSize > 0) {
324         startArea = ListItemGroupArea::IN_FOOTER_AREA;
325         startItemIndexInGroup = -1;
326     }
327     if (GetDisplayStartIndexInGroup() == 0 && isHeader && startHeaderMainSize > 0) {
328         startArea = ListItemGroupArea::IN_HEADER_AREA;
329         startItemIndexInGroup = -1;
330     }
331     if (startHeaderMainSize == 0 && startFooterMainSize == 0 && GetTotalItemCount() == 0) {
332         startArea = ListItemGroupArea::NONE_AREA;
333     }
334     VisibleContentInfo startInfo = { startArea, startItemIndexInGroup };
335     return startInfo;
336 }
337 
GetEndListItemIndex()338 VisibleContentInfo ListItemGroupPattern::GetEndListItemIndex()
339 {
340     bool isFooter = endFooterPos_ < 0 ? true : false;
341     auto endHeaderMainSize = GetHeaderMainSize();
342     auto endFooterMainSize = GetFooterMainSize();
343     auto endPositionSize = GetItemPosition().size();
344     auto endItemIndexInGroup = GetDisplayEndIndexInGroup();
345     auto endArea = ListItemGroupArea::IN_LIST_ITEM_AREA;
346     if (endPositionSize == 0 && endHeaderMainSize > 0) {
347         endArea = ListItemGroupArea::IN_HEADER_AREA;
348         endItemIndexInGroup = -1;
349     }
350     if (isFooter && endFooterMainSize > 0) {
351         endArea = ListItemGroupArea::IN_FOOTER_AREA;
352         endItemIndexInGroup = -1;
353     }
354     if (endHeaderMainSize == 0 && endFooterMainSize == 0 && GetTotalItemCount() == 0) {
355         endArea = ListItemGroupArea::NONE_AREA;
356     }
357     VisibleContentInfo endInfo = { endArea, endItemIndexInGroup };
358     return endInfo;
359 }
360 
ResetChildrenSize()361 void ListItemGroupPattern::ResetChildrenSize()
362 {
363     if (childrenSize_) {
364         childrenSize_ = nullptr;
365         auto host = GetHost();
366         CHECK_NULL_VOID(host);
367         host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
368         OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
369     }
370 }
371 
ClearItemPosition()372 void ListItemGroupPattern::ClearItemPosition()
373 {
374     itemPosition_.clear();
375 }
376 
ClearCachedItemPosition()377 void ListItemGroupPattern::ClearCachedItemPosition()
378 {
379     cachedItemPosition_.clear();
380     forwardCachedIndex_ = -1;
381     backwardCachedIndex_ = INT_MAX;
382 }
383 
CalculateItemStartIndex()384 void ListItemGroupPattern::CalculateItemStartIndex()
385 {
386     int32_t headerIndex = -1;
387     int32_t footerIndex = -1;
388     int32_t itemStartIndex = 0;
389     auto header = header_.Upgrade();
390     if (header) {
391         auto count = header->FrameCount();
392         if (count > 0) {
393             headerIndex = itemStartIndex;
394             itemStartIndex += count;
395         }
396     }
397     auto footer = footer_.Upgrade();
398     if (footer) {
399         int32_t count = footer->FrameCount();
400         if (count > 0) {
401             footerIndex = itemStartIndex;
402             itemStartIndex += count;
403         }
404     }
405     headerIndex_ = headerIndex;
406     footerIndex_ = footerIndex;
407     itemStartIndex_ = itemStartIndex;
408 }
409 
UpdateActiveChildRange(bool forward,int32_t cacheCount,bool show)410 void ListItemGroupPattern::UpdateActiveChildRange(bool forward, int32_t cacheCount, bool show)
411 {
412     auto host = GetHost();
413     CHECK_NULL_VOID(host);
414     if (forward) {
415         host->SetActiveChildRange(-1, itemStartIndex_ - 1, 0, cacheCount, show);
416     } else {
417         int32_t index = itemTotalCount_ + itemStartIndex_;
418         host->SetActiveChildRange(index, index, cacheCount, 0, show);
419     }
420     if (show && headerIndex_ >= 0) {
421         host->GetOrCreateChildByIndex(headerIndex_);
422     }
423     if (show && footerIndex_ >= 0) {
424         host->GetOrCreateChildByIndex(footerIndex_);
425     }
426     if (show) {
427         host->RebuildRenderContextTree();
428     }
429 }
430 
UpdateActiveChildRange(bool show)431 void ListItemGroupPattern::UpdateActiveChildRange(bool show)
432 {
433     auto host = GetHost();
434     CHECK_NULL_VOID(host);
435     if (!itemPosition_.empty()) {
436         auto start = itemStartIndex_ + itemPosition_.begin()->first;
437         auto end = itemStartIndex_ + itemPosition_.rbegin()->first;
438         host->SetActiveChildRange(start, end);
439     } else if (headerIndex_ >= 0 || footerIndex_ >= 0) {
440         host->SetActiveChildRange(-1, itemStartIndex_ - 1);
441     } else {
442         host->RemoveAllChildInRenderTree();
443     }
444     if (headerIndex_ >= 0) {
445         host->GetOrCreateChildByIndex(headerIndex_);
446     }
447     if (footerIndex_ >= 0) {
448         host->GetOrCreateChildByIndex(footerIndex_);
449     }
450     if (show) {
451         host->RebuildRenderContextTree();
452     }
453 }
454 
UpdateCachedIndexForward(bool outOfView,bool show,int32_t cacheCount)455 int32_t ListItemGroupPattern::UpdateCachedIndexForward(bool outOfView, bool show, int32_t cacheCount)
456 {
457     int32_t endIndex = (outOfView || itemPosition_.empty()) ? -1 : itemPosition_.rbegin()->first;
458     int32_t endLimit = std::min(endIndex + cacheCount * lanes_, itemTotalCount_ - 1);
459     int32_t forwardCachedIndex = std::clamp(forwardCachedIndex_, endIndex, endLimit);
460     auto iter = cachedItemPosition_.begin();
461     while (iter != cachedItemPosition_.end()) {
462         if (iter->first >= endIndex + 1 && iter->first <= endLimit) {
463             iter++;
464             continue;
465         }
466         iter = cachedItemPosition_.erase(iter);
467     }
468     if (cachedItemPosition_.find(forwardCachedIndex) == cachedItemPosition_.end() ||
469         cachedItemPosition_.find(endIndex + 1) == cachedItemPosition_.end()) {
470         forwardCachedIndex = endIndex;
471         cachedItemPosition_.clear();
472     }
473     if (outOfView && forwardCachedIndex < forwardCachedIndex_) {
474         UpdateActiveChildRange(true, forwardCachedIndex + 1, show);
475     }
476     return forwardCachedIndex;
477 }
478 
UpdateCachedIndexBackward(bool outOfView,bool show,int32_t cacheCount)479 int32_t ListItemGroupPattern::UpdateCachedIndexBackward(bool outOfView, bool show, int32_t cacheCount)
480 {
481     int32_t startIndex = (outOfView || itemPosition_.empty()) ? itemTotalCount_ : itemPosition_.begin()->first;
482     int32_t startLimit = std::max(startIndex - cacheCount * lanes_, 0);
483     if (startLimit % lanes_ != 0) {
484         startLimit += (lanes_ - startLimit % lanes_);
485     }
486     int32_t backwardCachedIndex = std::clamp(backwardCachedIndex_, startLimit, startIndex);
487     auto iter = cachedItemPosition_.begin();
488     while (iter != cachedItemPosition_.end()) {
489         if (iter->first >= startLimit && iter->first <= startIndex - 1) {
490             iter++;
491             continue;
492         }
493         iter = cachedItemPosition_.erase(iter);
494     }
495     if (cachedItemPosition_.find(backwardCachedIndex) == cachedItemPosition_.end() ||
496         cachedItemPosition_.find(startIndex - 1) == cachedItemPosition_.end()) {
497         backwardCachedIndex = startIndex;
498         cachedItemPosition_.clear();
499     }
500     if (outOfView && backwardCachedIndex > backwardCachedIndex_) {
501         UpdateActiveChildRange(false, itemTotalCount_ - backwardCachedIndex, show);
502     }
503     return backwardCachedIndex;
504 }
505 
UpdateCachedIndexOmni(int32_t forwardCache,int32_t backwardCache)506 std::pair<int32_t, int32_t> ListItemGroupPattern::UpdateCachedIndexOmni(int32_t forwardCache, int32_t backwardCache)
507 {
508     int32_t forwardRes = -1;
509     int32_t backwardRes = INT_MAX;
510     int32_t startIndex = itemPosition_.begin()->first;
511     int32_t startLimit = std::max(startIndex - backwardCache * lanes_, 0);
512     if (startLimit % lanes_ != 0) {
513         startLimit += (lanes_ - startLimit % lanes_);
514     }
515     int32_t backwardCachedIndex = std::clamp(backwardCachedIndex_, startLimit, startIndex);
516     int32_t endIndex = itemPosition_.rbegin()->first;
517     int32_t endLimit = std::min(endIndex + forwardCache * lanes_, itemTotalCount_ - 1);
518     int32_t forwardCachedIndex = std::clamp(forwardCachedIndex_, endIndex, endLimit);
519     auto iter = cachedItemPosition_.begin();
520     while (iter != cachedItemPosition_.end()) {
521         if ((iter->first >= startLimit && iter->first <= startIndex - 1) ||
522             (iter->first >= endIndex + 1 && iter->first <= endLimit)) {
523             iter++;
524             continue;
525         }
526         iter = cachedItemPosition_.erase(iter);
527     }
528     if (cachedItemPosition_.find(backwardCachedIndex) == cachedItemPosition_.end() ||
529         cachedItemPosition_.find(startIndex - 1) == cachedItemPosition_.end()) {
530         backwardRes = startIndex;
531     } else {
532         backwardRes = backwardCachedIndex;
533     }
534     if (cachedItemPosition_.find(forwardCachedIndex) == cachedItemPosition_.end() ||
535         cachedItemPosition_.find(endIndex + 1) == cachedItemPosition_.end()) {
536         forwardRes = endIndex;
537     } else {
538         forwardRes = forwardCachedIndex;
539     }
540     return { forwardRes, backwardRes };
541 }
542 
UpdateCachedIndex(bool outOfView,bool reCache,int32_t forwardCache,int32_t backwardCache)543 CachedIndexInfo ListItemGroupPattern::UpdateCachedIndex(
544     bool outOfView, bool reCache, int32_t forwardCache, int32_t backwardCache)
545 {
546     CachedIndexInfo res;
547     auto host = GetHost();
548     if (!host) {
549         forwardCachedIndex_ = -1;
550         backwardCachedIndex_ = INT_MAX;
551         return res;
552     }
553     auto listNode = GetListFrameNode();
554     bool show = listNode && listNode->GetLayoutProperty<ListLayoutProperty>() ?
555         listNode->GetLayoutProperty<ListLayoutProperty>()->GetShowCachedItemsValue(false) : false;
556     if (itemTotalCount_ == -1 || host->CheckNeedForceMeasureAndLayout()) {
557         CalculateItemStartIndex();
558         itemTotalCount_ = host->GetTotalChildCount() - itemStartIndex_;
559     }
560     if (outOfView) {
561         ClearItemPosition();
562     }
563     if (reCache || reCache_) {
564         ClearCachedItemPosition();
565         UpdateActiveChildRange(show);
566         reCache_ = false;
567     }
568     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
569     if (forwardCache > -1 && backwardCache > -1 && !itemPosition_.empty()) {
570         auto cached = UpdateCachedIndexOmni(forwardCache, backwardCache);
571         forwardCachedIndex_ = cached.first;
572         backwardCachedIndex_ = cached.second;
573         int32_t startIndex = itemPosition_.begin()->first;
574         int32_t endIndex = itemPosition_.rbegin()->first;
575         res.forwardCachedCount = (forwardCachedIndex_ - endIndex + lanes - 1) / lanes;
576         res.forwardCacheMax = (itemTotalCount_ - 1 - endIndex + lanes - 1) / lanes;
577         res.backwardCachedCount = (startIndex - backwardCachedIndex_ + lanes - 1) / lanes;
578         res.backwardCacheMax = (startIndex + lanes - 1) / lanes;
579     } else if (forwardCache > -1) {
580         forwardCachedIndex_ = UpdateCachedIndexForward(outOfView, show, forwardCache);
581         backwardCachedIndex_ = INT_MAX;
582         int32_t endIndex = (outOfView || itemPosition_.empty()) ? -1 : itemPosition_.rbegin()->first;
583         res.forwardCachedCount = (forwardCachedIndex_ - endIndex + lanes - 1) / lanes;
584         res.forwardCacheMax = (itemTotalCount_ - 1 - endIndex + lanes - 1) / lanes;
585     } else if (backwardCache > -1) {
586         forwardCachedIndex_ = -1;
587         backwardCachedIndex_ = UpdateCachedIndexBackward(outOfView, show, backwardCache);
588         int32_t startIndex = (outOfView || itemPosition_.empty()) ? itemTotalCount_ : itemPosition_.begin()->first;
589         res.backwardCachedCount = (startIndex - backwardCachedIndex_ + lanes - 1) / lanes;
590         res.backwardCacheMax = (startIndex + lanes - 1) / lanes;
591     }
592     return res;
593 }
594 
NeedCacheForward(const LayoutWrapper * listWrapper) const595 bool ListItemGroupPattern::NeedCacheForward(const LayoutWrapper* listWrapper) const
596 {
597     auto host = GetHost();
598     CHECK_NULL_RETURN(host, true);
599     auto listProperty = AceType::DynamicCast<ListLayoutProperty>(listWrapper->GetLayoutProperty());
600     CHECK_NULL_RETURN(listProperty, true);
601     auto listPadding = listProperty->CreatePaddingAndBorder().Offset();
602     auto offset = host->GetGeometryNode()->GetMarginFrameOffset();
603     if (GreatNotEqual(GetMainAxisOffset(offset, axis_) + headerMainSize_, GetMainAxisOffset(listPadding, axis_))) {
604         return true;
605     } else {
606         return false;
607     }
608 }
609 
LayoutCache(const LayoutConstraintF & constraint,int64_t deadline,int32_t forwardCached,int32_t backwardCached,ListMainSizeValues listSizeValues)610 void ListItemGroupPattern::LayoutCache(const LayoutConstraintF& constraint, int64_t deadline,
611     int32_t forwardCached, int32_t backwardCached, ListMainSizeValues listSizeValues)
612 {
613     auto listNode = GetListFrameNode();
614     CHECK_NULL_VOID(listNode);
615     auto listPattern = listNode->GetPattern<ListPattern>();
616     CHECK_NULL_VOID(listPattern);
617     auto listLayoutProperty = listNode->GetLayoutProperty<ListLayoutProperty>();
618     CHECK_NULL_VOID(listLayoutProperty);
619     auto cacheCountForward = listLayoutProperty->GetCachedCountWithDefault() - forwardCached;
620     auto cacheCountBackward = listLayoutProperty->GetCachedCountWithDefault() - backwardCached;
621     if (cacheCountForward < 1 && cacheCountBackward < 1) {
622         return;
623     }
624     auto host = GetHost();
625     CHECK_NULL_VOID(host);
626     auto layoutAlgorithmWrapper = host->GetLayoutAlgorithm(true);
627     CHECK_NULL_VOID(layoutAlgorithmWrapper);
628     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
629     CHECK_NULL_VOID(itemGroup);
630     ListItemGroupCacheParam param = {
631         .forward = listSizeValues.forward,
632         .backward = listSizeValues.backward,
633         .show = listLayoutProperty->GetShowCachedItemsValue(false),
634         .cacheCountForward = cacheCountForward,
635         .cacheCountBackward = cacheCountBackward,
636         .forwardCachedIndex = forwardCachedIndex_,
637         .backwardCachedIndex = backwardCachedIndex_,
638         .deadline = deadline,
639     };
640     itemGroup->SetContentOffset(listSizeValues.contentStartOffset, listSizeValues.contentEndOffset);
641     itemGroup->SetCacheParam(param);
642     itemGroup->SetListLayoutProperty(listLayoutProperty);
643     itemGroup->SetListMainSize(listSizeValues.startPos, listSizeValues.endPos, listSizeValues.referencePos,
644         listSizeValues.prevContentMainSize, listSizeValues.forward);
645     host->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
646     host->GetGeometryNode()->SetParentLayoutConstraint(constraint);
647     FrameNode::ProcessOffscreenNode(host);
648     if ((!NearZero(adjustRefPos_) || !NearZero(adjustTotalSize_)) && !(childrenSize_ && ListChildrenSizeExist())) {
649         listPattern->UpdateChildPosInfo(indexInList_, adjustRefPos_, adjustTotalSize_);
650         adjustRefPos_ = 0.0f;
651         adjustTotalSize_ = 0.0f;
652     }
653 }
654 
SetListItemGroupStyle(V2::ListItemGroupStyle style)655 void ListItemGroupPattern::SetListItemGroupStyle(V2::ListItemGroupStyle style)
656 {
657     auto host = GetHost();
658     CHECK_NULL_VOID(host);
659     if (listItemGroupStyle_ == V2::ListItemGroupStyle::NONE && style == V2::ListItemGroupStyle::CARD) {
660         listItemGroupStyle_ = style;
661         SetListItemGroupDefaultAttributes(host);
662     }
663 }
664 
GetListPaddingOffset(const RefPtr<FrameNode> & listNode) const665 float ListItemGroupPattern::GetListPaddingOffset(const RefPtr<FrameNode>& listNode) const
666 {
667     float offset = 0;
668     CHECK_NULL_RETURN(listNode, offset);
669     auto layoutProperty = listNode->GetLayoutProperty<ListLayoutProperty>();
670     CHECK_NULL_RETURN(layoutProperty, offset);
671     auto padding = layoutProperty->CreatePaddingAndBorder();
672     return GetMainAxisOffset(padding.Offset(), axis_);
673 }
674 
FirstItemFullVisible(const RefPtr<FrameNode> & listNode) const675 bool ListItemGroupPattern::FirstItemFullVisible(const RefPtr<FrameNode>& listNode) const
676 {
677     auto host = GetHost();
678     CHECK_NULL_RETURN(host, true);
679     auto geometryNode = host->GetGeometryNode();
680     CHECK_NULL_RETURN(geometryNode, true);
681     OffsetF selfOffset = geometryNode->GetPaddingOffset();
682     float mainPos = GetMainAxisOffset(selfOffset, axis_) + headerMainSize_;
683     float listPadding = GetListPaddingOffset(listNode);
684     return GreatNotEqual(mainPos, listPadding);
685 }
686 
CheckDataChangeOutOfStart(int32_t index,int32_t count,int32_t startIndex)687 bool ListItemGroupPattern::CheckDataChangeOutOfStart(int32_t index, int32_t count, int32_t startIndex)
688 {
689     if (count == 0 || (count > 0 && index > startIndex) ||
690         (count < 0 && index >= startIndex)) {
691         return false;
692     }
693 
694     RefPtr<FrameNode> listNode = GetListFrameNode();
695     CHECK_NULL_RETURN(listNode, false);
696     auto listPattern = listNode->GetPattern<ListPattern>();
697     CHECK_NULL_RETURN(listPattern, false);
698     if (!listPattern->GetMaintainVisibleContentPosition()) {
699         return false;
700     }
701 
702     if (startIndex == 0 && index == 0 && count > 0 && FirstItemFullVisible(listNode)) {
703         return false;
704     }
705     listPattern->MarkNeedReEstimateOffset();
706     return true;
707 }
708 
NotifyDataChange(int32_t index,int32_t count)709 void ListItemGroupPattern::NotifyDataChange(int32_t index, int32_t count)
710 {
711     if (itemPosition_.empty()) {
712         return;
713     }
714     index -= itemStartIndex_;
715     int32_t startIndex = itemPosition_.begin()->first;
716     if (!CheckDataChangeOutOfStart(index, count, startIndex)) {
717         return;
718     }
719 
720     count = std::max(count, index - startIndex);
721     int32_t mod = 0;
722     if (count < 0 && lanes_ > 1) {
723         mod = -count % lanes_;
724     }
725     auto prevPosMap = std::move(itemPosition_);
726     for (auto &pos : prevPosMap) {
727         if (mod > 0) {
728             mod--;
729         } else {
730             itemPosition_[pos.first + count] = pos.second;
731         }
732     }
733     if (layoutedItemInfo_ && layoutedItemInfo_.value().startIndex >= index) {
734         auto& info = layoutedItemInfo_.value();
735         info.startIndex = std::max(info.startIndex + count, 0);
736         info.endIndex = std::max(info.endIndex + count, 0);
737         if (lanes_ > 1) {
738             if (count < 0) {
739                 info.startIndex += -count % lanes_;
740             } else {
741                 info.endIndex -= count % lanes_;
742             }
743         }
744     }
745 }
746 } // namespace OHOS::Ace::NG
747