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