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
IsVisible() const210 bool ListItemGroupPattern::IsVisible() const
211 {
212 auto layoutProperty = GetLayoutProperty<ListItemGroupLayoutProperty>();
213 CHECK_NULL_RETURN(layoutProperty, false);
214 auto visible = layoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
215 if (visible == VisibleType::GONE) {
216 return false;
217 }
218 return true;
219 }
220
GetEstimateHeight(float & averageHeight) const221 float ListItemGroupPattern::GetEstimateHeight(float& averageHeight) const
222 {
223 auto layoutProperty = GetLayoutProperty<ListItemGroupLayoutProperty>();
224 CHECK_NULL_RETURN(layoutProperty, 0.0f);
225 auto visible = layoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
226 if (visible == VisibleType::GONE) {
227 return 0.0f;
228 }
229 float paddingAndMargin = GetPaddingAndMargin();
230 if (layoutedItemInfo_.has_value()) {
231 auto totalHeight = (layoutedItemInfo_.value().endPos - layoutedItemInfo_.value().startPos + spaceWidth_);
232 auto itemCount = layoutedItemInfo_.value().endIndex - layoutedItemInfo_.value().startIndex + 1;
233 averageHeight = totalHeight / itemCount;
234 }
235 if (layouted_) {
236 if (itemTotalCount_ > 0) {
237 return itemTotalCount_ * averageHeight + headerMainSize_ + footerMainSize_ + paddingAndMargin - spaceWidth_;
238 } else {
239 return headerMainSize_ + footerMainSize_ + paddingAndMargin;
240 }
241 }
242 auto host = GetHost();
243 auto totalItem = host->GetTotalChildCount();
244 return averageHeight * totalItem + paddingAndMargin;
245 }
246
CheckListDirectionInCardStyle()247 void ListItemGroupPattern::CheckListDirectionInCardStyle()
248 {
249 if (axis_ == Axis::HORIZONTAL && listItemGroupStyle_ == V2::ListItemGroupStyle::CARD) {
250 auto host = GetHost();
251 CHECK_NULL_VOID(host);
252 RefPtr<FrameNode> listNode = AceType::DynamicCast<FrameNode>(host->GetParent());
253 CHECK_NULL_VOID(listNode);
254 auto listPattern = listNode->GetPattern<ListPattern>();
255 CHECK_NULL_VOID(listPattern);
256 listPattern->SetNeedToUpdateListDirectionInCardStyle(true);
257 }
258 }
259
GetListFrameNode() const260 RefPtr<FrameNode> ListItemGroupPattern::GetListFrameNode() const
261 {
262 auto host = GetHost();
263 CHECK_NULL_RETURN(host, nullptr);
264 auto parent = host->GetParent();
265 RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
266 while (parent && !frameNode) {
267 parent = parent->GetParent();
268 frameNode = AceType::DynamicCast<FrameNode>(parent);
269 }
270 return frameNode;
271 }
272
ListChildrenSizeExist()273 bool ListItemGroupPattern::ListChildrenSizeExist()
274 {
275 RefPtr<FrameNode> listNode = GetListFrameNode();
276 CHECK_NULL_RETURN(listNode, false);
277 auto listPattern = listNode->GetPattern<ListPattern>();
278 CHECK_NULL_RETURN(listPattern, false);
279 return listPattern->ListChildrenSizeExist();
280 }
281
GetOrCreateListChildrenMainSize()282 RefPtr<ListChildrenMainSize> ListItemGroupPattern::GetOrCreateListChildrenMainSize()
283 {
284 if (childrenSize_) {
285 return childrenSize_;
286 }
287 childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>();
288 auto callback = [weakPattern = WeakClaim(this)](std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag) {
289 auto pattern = weakPattern.Upgrade();
290 CHECK_NULL_VOID(pattern);
291 auto context = PipelineContext::GetCurrentContext();
292 CHECK_NULL_VOID(context);
293 context->AddBuildFinishCallBack([weakPattern, change, flag]() {
294 auto pattern = weakPattern.Upgrade();
295 CHECK_NULL_VOID(pattern);
296 pattern->OnChildrenSizeChanged(change, flag);
297 });
298 context->RequestFrame();
299 };
300 childrenSize_->SetOnDataChange(callback);
301 return childrenSize_;
302 }
303
SetListChildrenMainSize(float defaultSize,const std::vector<float> & mainSize)304 void ListItemGroupPattern::SetListChildrenMainSize(
305 float defaultSize, const std::vector<float>& mainSize)
306 {
307 childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>(mainSize, defaultSize);
308 OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
309 }
310
OnChildrenSizeChanged(std::tuple<int32_t,int32_t,int32_t> change,ListChangeFlag flag)311 void ListItemGroupPattern::OnChildrenSizeChanged(std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag)
312 {
313 if (!posMap_) {
314 posMap_ = MakeRefPtr<ListPositionMap>();
315 }
316 posMap_->MarkDirty(flag);
317 auto host = GetHost();
318 CHECK_NULL_VOID(host);
319 host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
320 }
321
GetStartListItemIndex()322 VisibleContentInfo ListItemGroupPattern::GetStartListItemIndex()
323 {
324 bool isHeader = false;
325 auto startHeaderMainSize = GetHeaderMainSize();
326 auto startFooterMainSize = GetFooterMainSize();
327 if (GetDisplayStartIndexInGroup() == 0) {
328 auto startHeaderPos = startHeaderPos_;
329 isHeader = (startHeaderPos + startHeaderMainSize) > 0 ? true : false;
330 }
331 auto startPositionSize = GetItemPosition().size();
332 auto startItemIndexInGroup = GetDisplayStartIndexInGroup();
333 auto startArea = ListItemGroupArea::IN_LIST_ITEM_AREA;
334 if (startPositionSize == 0 && startFooterMainSize > 0) {
335 startArea = ListItemGroupArea::IN_FOOTER_AREA;
336 startItemIndexInGroup = -1;
337 }
338 if (GetDisplayStartIndexInGroup() == 0 && isHeader && startHeaderMainSize > 0) {
339 startArea = ListItemGroupArea::IN_HEADER_AREA;
340 startItemIndexInGroup = -1;
341 }
342 if (startHeaderMainSize == 0 && startFooterMainSize == 0 && GetTotalItemCount() == 0) {
343 startArea = ListItemGroupArea::NONE_AREA;
344 }
345 VisibleContentInfo startInfo = { startArea, startItemIndexInGroup };
346 return startInfo;
347 }
348
GetEndListItemIndex()349 VisibleContentInfo ListItemGroupPattern::GetEndListItemIndex()
350 {
351 bool isFooter = endFooterPos_ < 0 ? true : false;
352 auto endHeaderMainSize = GetHeaderMainSize();
353 auto endFooterMainSize = GetFooterMainSize();
354 auto endPositionSize = GetItemPosition().size();
355 auto endItemIndexInGroup = GetDisplayEndIndexInGroup();
356 auto endArea = ListItemGroupArea::IN_LIST_ITEM_AREA;
357 if (endPositionSize == 0 && endHeaderMainSize > 0) {
358 endArea = ListItemGroupArea::IN_HEADER_AREA;
359 endItemIndexInGroup = -1;
360 }
361 if (isFooter && endFooterMainSize > 0) {
362 endArea = ListItemGroupArea::IN_FOOTER_AREA;
363 endItemIndexInGroup = -1;
364 }
365 if (endHeaderMainSize == 0 && endFooterMainSize == 0 && GetTotalItemCount() == 0) {
366 endArea = ListItemGroupArea::NONE_AREA;
367 }
368 VisibleContentInfo endInfo = { endArea, endItemIndexInGroup };
369 return endInfo;
370 }
371
ResetChildrenSize()372 void ListItemGroupPattern::ResetChildrenSize()
373 {
374 if (childrenSize_) {
375 childrenSize_ = nullptr;
376 auto host = GetHost();
377 CHECK_NULL_VOID(host);
378 host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
379 OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
380 }
381 }
382
ClearItemPosition()383 void ListItemGroupPattern::ClearItemPosition()
384 {
385 itemPosition_.clear();
386 }
387
ClearCachedItemPosition()388 void ListItemGroupPattern::ClearCachedItemPosition()
389 {
390 cachedItemPosition_.clear();
391 forwardCachedIndex_ = -1;
392 backwardCachedIndex_ = INT_MAX;
393 }
394
CalculateItemStartIndex()395 void ListItemGroupPattern::CalculateItemStartIndex()
396 {
397 int32_t headerIndex = -1;
398 int32_t footerIndex = -1;
399 int32_t itemStartIndex = 0;
400 auto header = header_.Upgrade();
401 if (header) {
402 auto count = header->FrameCount();
403 if (count > 0) {
404 headerIndex = itemStartIndex;
405 itemStartIndex += count;
406 }
407 }
408 auto footer = footer_.Upgrade();
409 if (footer) {
410 int32_t count = footer->FrameCount();
411 if (count > 0) {
412 footerIndex = itemStartIndex;
413 itemStartIndex += count;
414 }
415 }
416 headerIndex_ = headerIndex;
417 footerIndex_ = footerIndex;
418 itemStartIndex_ = itemStartIndex;
419 }
420
UpdateActiveChildRange(bool forward,int32_t cacheCount,bool show)421 void ListItemGroupPattern::UpdateActiveChildRange(bool forward, int32_t cacheCount, bool show)
422 {
423 auto host = GetHost();
424 CHECK_NULL_VOID(host);
425 if (forward) {
426 host->SetActiveChildRange(-1, itemStartIndex_ - 1, 0, cacheCount, show);
427 } else {
428 int32_t index = itemTotalCount_ + itemStartIndex_;
429 host->SetActiveChildRange(index, index, cacheCount, 0, show);
430 }
431 if (show && headerIndex_ >= 0) {
432 host->GetOrCreateChildByIndex(headerIndex_);
433 }
434 if (show && footerIndex_ >= 0) {
435 host->GetOrCreateChildByIndex(footerIndex_);
436 }
437 if (show) {
438 host->RebuildRenderContextTree();
439 }
440 }
441
UpdateActiveChildRange(bool show)442 void ListItemGroupPattern::UpdateActiveChildRange(bool show)
443 {
444 auto host = GetHost();
445 CHECK_NULL_VOID(host);
446 if (!itemPosition_.empty()) {
447 auto start = itemStartIndex_ + itemPosition_.begin()->first;
448 auto end = itemStartIndex_ + itemPosition_.rbegin()->first;
449 host->SetActiveChildRange(start, end);
450 } else if (headerIndex_ >= 0 || footerIndex_ >= 0) {
451 host->SetActiveChildRange(-1, itemStartIndex_ - 1);
452 } else {
453 host->SetActiveChildRange(-1, -1);
454 }
455 if (headerIndex_ >= 0) {
456 host->GetOrCreateChildByIndex(headerIndex_);
457 }
458 if (footerIndex_ >= 0) {
459 host->GetOrCreateChildByIndex(footerIndex_);
460 }
461 if (show) {
462 host->RebuildRenderContextTree();
463 }
464 }
465
UpdateCachedIndexForward(bool outOfView,bool show,int32_t cacheCount)466 int32_t ListItemGroupPattern::UpdateCachedIndexForward(bool outOfView, bool show, int32_t cacheCount)
467 {
468 int32_t endIndex = (outOfView || itemPosition_.empty()) ? -1 : itemPosition_.rbegin()->first;
469 int32_t endLimit = std::min(endIndex + cacheCount * lanes_, itemTotalCount_ - 1);
470 int32_t forwardCachedIndex = std::clamp(forwardCachedIndex_, endIndex, endLimit);
471 auto iter = cachedItemPosition_.begin();
472 while (iter != cachedItemPosition_.end()) {
473 if (iter->first >= endIndex + 1 && iter->first <= endLimit) {
474 iter++;
475 continue;
476 }
477 iter = cachedItemPosition_.erase(iter);
478 }
479 if (cachedItemPosition_.find(forwardCachedIndex) == cachedItemPosition_.end() ||
480 cachedItemPosition_.find(endIndex + 1) == cachedItemPosition_.end()) {
481 forwardCachedIndex = endIndex;
482 cachedItemPosition_.clear();
483 }
484 if (outOfView && forwardCachedIndex < forwardCachedIndex_) {
485 UpdateActiveChildRange(true, forwardCachedIndex + 1, show);
486 }
487 return forwardCachedIndex;
488 }
489
UpdateCachedIndexBackward(bool outOfView,bool show,int32_t cacheCount)490 int32_t ListItemGroupPattern::UpdateCachedIndexBackward(bool outOfView, bool show, int32_t cacheCount)
491 {
492 int32_t startIndex = (outOfView || itemPosition_.empty()) ? itemTotalCount_ : itemPosition_.begin()->first;
493 int32_t startLimit = std::max(startIndex - cacheCount * lanes_, 0);
494 if (startLimit % lanes_ != 0) {
495 startLimit += (lanes_ - startLimit % lanes_);
496 }
497 int32_t backwardCachedIndex = std::clamp(backwardCachedIndex_, startLimit, startIndex);
498 auto iter = cachedItemPosition_.begin();
499 while (iter != cachedItemPosition_.end()) {
500 if (iter->first >= startLimit && iter->first <= startIndex - 1) {
501 iter++;
502 continue;
503 }
504 iter = cachedItemPosition_.erase(iter);
505 }
506 if (cachedItemPosition_.find(backwardCachedIndex) == cachedItemPosition_.end() ||
507 cachedItemPosition_.find(startIndex - 1) == cachedItemPosition_.end()) {
508 backwardCachedIndex = startIndex;
509 cachedItemPosition_.clear();
510 }
511 if (outOfView && backwardCachedIndex > backwardCachedIndex_) {
512 UpdateActiveChildRange(false, itemTotalCount_ - backwardCachedIndex, show);
513 }
514 return backwardCachedIndex;
515 }
516
UpdateCachedIndexOmni(int32_t forwardCache,int32_t backwardCache)517 std::pair<int32_t, int32_t> ListItemGroupPattern::UpdateCachedIndexOmni(int32_t forwardCache, int32_t backwardCache)
518 {
519 int32_t forwardRes = -1;
520 int32_t backwardRes = INT_MAX;
521 int32_t startIndex = itemPosition_.begin()->first;
522 int32_t startLimit = std::max(startIndex - backwardCache * lanes_, 0);
523 if (startLimit % lanes_ != 0) {
524 startLimit += (lanes_ - startLimit % lanes_);
525 }
526 int32_t backwardCachedIndex = std::clamp(backwardCachedIndex_, startLimit, startIndex);
527 int32_t endIndex = itemPosition_.rbegin()->first;
528 int32_t endLimit = std::min(endIndex + forwardCache * lanes_, itemTotalCount_ - 1);
529 int32_t forwardCachedIndex = std::clamp(forwardCachedIndex_, endIndex, endLimit);
530 auto iter = cachedItemPosition_.begin();
531 while (iter != cachedItemPosition_.end()) {
532 if ((iter->first >= startLimit && iter->first <= startIndex - 1) ||
533 (iter->first >= endIndex + 1 && iter->first <= endLimit)) {
534 iter++;
535 continue;
536 }
537 iter = cachedItemPosition_.erase(iter);
538 }
539 if (cachedItemPosition_.find(backwardCachedIndex) == cachedItemPosition_.end() ||
540 cachedItemPosition_.find(startIndex - 1) == cachedItemPosition_.end()) {
541 backwardRes = startIndex;
542 } else {
543 backwardRes = backwardCachedIndex;
544 }
545 if (cachedItemPosition_.find(forwardCachedIndex) == cachedItemPosition_.end() ||
546 cachedItemPosition_.find(endIndex + 1) == cachedItemPosition_.end()) {
547 forwardRes = endIndex;
548 } else {
549 forwardRes = forwardCachedIndex;
550 }
551 return { forwardRes, backwardRes };
552 }
553
UpdateCachedIndex(bool outOfView,bool reCache,int32_t forwardCache,int32_t backwardCache)554 CachedIndexInfo ListItemGroupPattern::UpdateCachedIndex(
555 bool outOfView, bool reCache, int32_t forwardCache, int32_t backwardCache)
556 {
557 CachedIndexInfo res;
558 auto host = GetHost();
559 if (!host) {
560 forwardCachedIndex_ = -1;
561 backwardCachedIndex_ = INT_MAX;
562 return res;
563 }
564 auto listNode = GetListFrameNode();
565 bool show = listNode && listNode->GetLayoutProperty<ListLayoutProperty>() ?
566 listNode->GetLayoutProperty<ListLayoutProperty>()->GetShowCachedItemsValue(false) : false;
567 if (itemTotalCount_ == -1 || host->CheckNeedForceMeasureAndLayout()) {
568 CalculateItemStartIndex();
569 itemTotalCount_ = host->GetTotalChildCount() - itemStartIndex_;
570 }
571 if (outOfView) {
572 ClearItemPosition();
573 }
574 if (reCache || reCache_) {
575 ClearCachedItemPosition();
576 UpdateActiveChildRange(show);
577 reCache_ = false;
578 }
579 int32_t lanes = lanes_ > 1 ? lanes_ : 1;
580 if (forwardCache > -1 && backwardCache > -1 && !itemPosition_.empty()) {
581 auto cached = UpdateCachedIndexOmni(forwardCache, backwardCache);
582 forwardCachedIndex_ = cached.first;
583 backwardCachedIndex_ = cached.second;
584 int32_t startIndex = itemPosition_.begin()->first;
585 int32_t endIndex = itemPosition_.rbegin()->first;
586 res.forwardCachedCount = (forwardCachedIndex_ - endIndex + lanes - 1) / lanes;
587 res.forwardCacheMax = (itemTotalCount_ - 1 - endIndex + lanes - 1) / lanes;
588 res.backwardCachedCount = (startIndex - backwardCachedIndex_ + lanes - 1) / lanes;
589 res.backwardCacheMax = (startIndex + lanes - 1) / lanes;
590 } else if (forwardCache > -1) {
591 forwardCachedIndex_ = UpdateCachedIndexForward(outOfView, show, forwardCache);
592 backwardCachedIndex_ = INT_MAX;
593 int32_t endIndex = (outOfView || itemPosition_.empty()) ? -1 : itemPosition_.rbegin()->first;
594 res.forwardCachedCount = (forwardCachedIndex_ - endIndex + lanes - 1) / lanes;
595 res.forwardCacheMax = (itemTotalCount_ - 1 - endIndex + lanes - 1) / lanes;
596 } else if (backwardCache > -1) {
597 forwardCachedIndex_ = -1;
598 backwardCachedIndex_ = UpdateCachedIndexBackward(outOfView, show, backwardCache);
599 int32_t startIndex = (outOfView || itemPosition_.empty()) ? itemTotalCount_ : itemPosition_.begin()->first;
600 res.backwardCachedCount = (startIndex - backwardCachedIndex_ + lanes - 1) / lanes;
601 res.backwardCacheMax = (startIndex + lanes - 1) / lanes;
602 }
603 return res;
604 }
605
NeedCacheForward(const LayoutWrapper * listWrapper) const606 bool ListItemGroupPattern::NeedCacheForward(const LayoutWrapper* listWrapper) const
607 {
608 auto host = GetHost();
609 CHECK_NULL_RETURN(host, true);
610 auto listProperty = AceType::DynamicCast<ListLayoutProperty>(listWrapper->GetLayoutProperty());
611 CHECK_NULL_RETURN(listProperty, true);
612 auto listPadding = listProperty->CreatePaddingAndBorder().Offset();
613 auto offset = host->GetGeometryNode()->GetMarginFrameOffset();
614 if (GreatNotEqual(GetMainAxisOffset(offset, axis_) + headerMainSize_, GetMainAxisOffset(listPadding, axis_))) {
615 return true;
616 } else {
617 return false;
618 }
619 }
620
LayoutCache(const LayoutConstraintF & constraint,int64_t deadline,int32_t forwardCached,int32_t backwardCached,ListMainSizeValues listSizeValues)621 void ListItemGroupPattern::LayoutCache(const LayoutConstraintF& constraint, int64_t deadline,
622 int32_t forwardCached, int32_t backwardCached, ListMainSizeValues listSizeValues)
623 {
624 auto listNode = GetListFrameNode();
625 CHECK_NULL_VOID(listNode);
626 auto listPattern = listNode->GetPattern<ListPattern>();
627 CHECK_NULL_VOID(listPattern);
628 auto listLayoutProperty = listNode->GetLayoutProperty<ListLayoutProperty>();
629 CHECK_NULL_VOID(listLayoutProperty);
630 auto cacheCountForward = listLayoutProperty->GetCachedCountWithDefault() - forwardCached;
631 auto cacheCountBackward = listLayoutProperty->GetCachedCountWithDefault() - backwardCached;
632 if (cacheCountForward < 1 && cacheCountBackward < 1) {
633 return;
634 }
635 auto host = GetHost();
636 CHECK_NULL_VOID(host);
637 auto layoutAlgorithmWrapper = host->GetLayoutAlgorithm(true);
638 CHECK_NULL_VOID(layoutAlgorithmWrapper);
639 auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
640 CHECK_NULL_VOID(itemGroup);
641 ListItemGroupCacheParam param = {
642 .forward = listSizeValues.forward,
643 .backward = listSizeValues.backward,
644 .show = listLayoutProperty->GetShowCachedItemsValue(false),
645 .cacheCountForward = cacheCountForward,
646 .cacheCountBackward = cacheCountBackward,
647 .forwardCachedIndex = forwardCachedIndex_,
648 .backwardCachedIndex = backwardCachedIndex_,
649 .deadline = deadline,
650 };
651 itemGroup->SetContentOffset(listSizeValues.contentStartOffset, listSizeValues.contentEndOffset);
652 itemGroup->SetCacheParam(param);
653 itemGroup->SetListLayoutProperty(listLayoutProperty);
654 itemGroup->SetListMainSize(listSizeValues.startPos, listSizeValues.endPos, listSizeValues.referencePos,
655 listSizeValues.prevContentMainSize, listSizeValues.forward);
656 host->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
657 host->GetGeometryNode()->SetParentLayoutConstraint(constraint);
658 FrameNode::ProcessOffscreenNode(host);
659 if ((!NearZero(adjustRefPos_) || !NearZero(adjustTotalSize_)) && !(childrenSize_ && ListChildrenSizeExist())) {
660 listPattern->UpdateChildPosInfo(indexInList_, adjustRefPos_, adjustTotalSize_);
661 adjustRefPos_ = 0.0f;
662 adjustTotalSize_ = 0.0f;
663 }
664 }
665
SetListItemGroupStyle(V2::ListItemGroupStyle style)666 void ListItemGroupPattern::SetListItemGroupStyle(V2::ListItemGroupStyle style)
667 {
668 auto host = GetHost();
669 CHECK_NULL_VOID(host);
670 if (listItemGroupStyle_ == V2::ListItemGroupStyle::NONE && style == V2::ListItemGroupStyle::CARD) {
671 listItemGroupStyle_ = style;
672 SetListItemGroupDefaultAttributes(host);
673 }
674 }
675
GetListPaddingOffset(const RefPtr<FrameNode> & listNode) const676 float ListItemGroupPattern::GetListPaddingOffset(const RefPtr<FrameNode>& listNode) const
677 {
678 float offset = 0;
679 CHECK_NULL_RETURN(listNode, offset);
680 auto layoutProperty = listNode->GetLayoutProperty<ListLayoutProperty>();
681 CHECK_NULL_RETURN(layoutProperty, offset);
682 auto padding = layoutProperty->CreatePaddingAndBorder();
683 return GetMainAxisOffset(padding.Offset(), axis_);
684 }
685
FirstItemFullVisible(const RefPtr<FrameNode> & listNode) const686 bool ListItemGroupPattern::FirstItemFullVisible(const RefPtr<FrameNode>& listNode) const
687 {
688 auto host = GetHost();
689 CHECK_NULL_RETURN(host, true);
690 auto geometryNode = host->GetGeometryNode();
691 CHECK_NULL_RETURN(geometryNode, true);
692 OffsetF selfOffset = geometryNode->GetPaddingOffset();
693 float mainPos = GetMainAxisOffset(selfOffset, axis_) + headerMainSize_;
694 float listPadding = GetListPaddingOffset(listNode);
695 return GreatNotEqual(mainPos, listPadding);
696 }
697
CheckDataChangeOutOfStart(int32_t index,int32_t count,int32_t startIndex)698 bool ListItemGroupPattern::CheckDataChangeOutOfStart(int32_t index, int32_t count, int32_t startIndex)
699 {
700 if (count == 0 || (count > 0 && index > startIndex) ||
701 (count < 0 && index >= startIndex)) {
702 return false;
703 }
704
705 RefPtr<FrameNode> listNode = GetListFrameNode();
706 CHECK_NULL_RETURN(listNode, false);
707 auto listPattern = listNode->GetPattern<ListPattern>();
708 CHECK_NULL_RETURN(listPattern, false);
709 if (!listPattern->GetMaintainVisibleContentPosition()) {
710 return false;
711 }
712
713 if (startIndex == 0 && index == 0 && count > 0 && FirstItemFullVisible(listNode)) {
714 return false;
715 }
716 listPattern->MarkNeedReEstimateOffset();
717 return true;
718 }
719
NotifyDataChange(int32_t index,int32_t count)720 void ListItemGroupPattern::NotifyDataChange(int32_t index, int32_t count)
721 {
722 if (itemPosition_.empty()) {
723 return;
724 }
725 index -= itemStartIndex_;
726 int32_t startIndex = itemPosition_.begin()->first;
727 if (!CheckDataChangeOutOfStart(index, count, startIndex)) {
728 return;
729 }
730
731 count = std::max(count, index - startIndex);
732 int32_t mod = 0;
733 if (count < 0 && lanes_ > 1) {
734 mod = -count % lanes_;
735 }
736 auto prevPosMap = std::move(itemPosition_);
737 for (auto &pos : prevPosMap) {
738 if (mod > 0) {
739 mod--;
740 } else {
741 itemPosition_[pos.first + count] = pos.second;
742 }
743 }
744 if (layoutedItemInfo_ && layoutedItemInfo_.value().startIndex >= index) {
745 auto& info = layoutedItemInfo_.value();
746 info.startIndex = std::max(info.startIndex + count, 0);
747 info.endIndex = std::max(info.endIndex + count, 0);
748 if (lanes_ > 1) {
749 if (count < 0) {
750 info.startIndex += -count % lanes_;
751 } else {
752 info.endIndex -= count % lanes_;
753 }
754 }
755 }
756 }
757 } // namespace OHOS::Ace::NG
758