• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/utils/multi_thread.h"
20 #include "core/components_ng/pattern/list/list_item_group_paint_method.h"
21 #include "core/components_ng/pattern/list/list_pattern.h"
22 #include "core/pipeline_ng/pipeline_context.h"
23 #include "core/components_ng/property/measure_utils.h"
24 #include "core/components/list/list_theme.h"
25 
26 
27 namespace OHOS::Ace::NG {
28 
OnAttachToFrameNode()29 void ListItemGroupPattern::OnAttachToFrameNode()
30 {
31     auto host = GetHost();
32     // call OnAttachToFrameNodeMultiThread by multi thread
33     THREAD_SAFE_NODE_CHECK(host, OnAttachToFrameNode);
34     CHECK_NULL_VOID(host);
35     if (listItemGroupStyle_ == V2::ListItemGroupStyle::CARD) {
36         SetListItemGroupDefaultAttributes(host);
37     }
38 }
39 
OnAttachToMainTree()40 void ListItemGroupPattern::OnAttachToMainTree()
41 {
42     auto host = GetHost();
43     // call OnAttachToMainTreeMulti by multi thread
44     THREAD_SAFE_NODE_CHECK(host, OnAttachToMainTree);
45 }
46 
OnColorConfigurationUpdate()47 void ListItemGroupPattern::OnColorConfigurationUpdate()
48 {
49     if (listItemGroupStyle_ != V2::ListItemGroupStyle::CARD) {
50         return;
51     }
52     auto itemGroupNode = GetHost();
53     CHECK_NULL_VOID(itemGroupNode);
54     auto renderContext = itemGroupNode->GetRenderContext();
55     CHECK_NULL_VOID(renderContext);
56     auto pipeline = itemGroupNode->GetContext();
57     CHECK_NULL_VOID(pipeline);
58     auto listItemGroupTheme = pipeline->GetTheme<ListItemTheme>();
59     CHECK_NULL_VOID(listItemGroupTheme);
60 
61     renderContext->UpdateBackgroundColor(listItemGroupTheme->GetItemGroupDefaultColor());
62 }
63 
SetListItemGroupDefaultAttributes(const RefPtr<FrameNode> & itemGroupNode)64 void ListItemGroupPattern::SetListItemGroupDefaultAttributes(const RefPtr<FrameNode>& itemGroupNode)
65 {
66     auto renderContext = itemGroupNode->GetRenderContext();
67     CHECK_NULL_VOID(renderContext);
68     auto layoutProperty = itemGroupNode->GetLayoutProperty<ListItemGroupLayoutProperty>();
69     CHECK_NULL_VOID(layoutProperty);
70 
71     auto pipeline = GetContext();
72     CHECK_NULL_VOID(pipeline);
73     auto listItemGroupTheme = pipeline->GetTheme<ListItemTheme>();
74     CHECK_NULL_VOID(listItemGroupTheme);
75 
76     renderContext->UpdateBackgroundColor(listItemGroupTheme->GetItemGroupDefaultColor());
77 
78     MarginProperty itemGroupMargin;
79     itemGroupMargin.left = CalcLength(listItemGroupTheme->GetItemGroupDefaultLeftMargin());
80     itemGroupMargin.right = CalcLength(listItemGroupTheme->GetItemGroupDefaultRightMargin());
81     layoutProperty->UpdateMargin(itemGroupMargin);
82 
83     PaddingProperty itemGroupPadding;
84     itemGroupPadding.left = CalcLength(listItemGroupTheme->GetItemGroupDefaultPadding().Left());
85     itemGroupPadding.right = CalcLength(listItemGroupTheme->GetItemGroupDefaultPadding().Right());
86     itemGroupPadding.top = CalcLength(listItemGroupTheme->GetItemGroupDefaultPadding().Top());
87     itemGroupPadding.bottom = CalcLength(listItemGroupTheme->GetItemGroupDefaultPadding().Bottom());
88     layoutProperty->UpdatePadding(itemGroupPadding);
89 
90     renderContext->UpdateBorderRadius(listItemGroupTheme->GetItemGroupDefaultBorderRadius());
91 }
92 
DumpAdvanceInfo()93 void ListItemGroupPattern::DumpAdvanceInfo()
94 {
95     DumpLog::GetInstance().AddDesc("itemStartIndex:" + std::to_string(itemStartIndex_));
96     DumpLog::GetInstance().AddDesc("itemTotalCount:" + std::to_string(itemTotalCount_));
97     DumpLog::GetInstance().AddDesc("itemDisplayEndIndex:" + std::to_string(itemDisplayEndIndex_));
98     DumpLog::GetInstance().AddDesc("itemDisplayStartIndex:" + std::to_string(itemDisplayStartIndex_));
99     DumpLog::GetInstance().AddDesc("headerMainSize:" + std::to_string(headerMainSize_));
100     DumpLog::GetInstance().AddDesc("footerMainSize:" + std::to_string(footerMainSize_));
101     DumpLog::GetInstance().AddDesc("spaceWidth:" + std::to_string(spaceWidth_));
102     DumpLog::GetInstance().AddDesc("lanes:" + std::to_string(lanes_));
103     DumpLog::GetInstance().AddDesc("laneGutter:" + std::to_string(laneGutter_));
104     DumpLog::GetInstance().AddDesc("startHeaderPos:" + std::to_string(startHeaderPos_));
105     DumpLog::GetInstance().AddDesc("endFooterPos:" + std::to_string(endFooterPos_));
106 }
107 
CreateLayoutAlgorithm()108 RefPtr<LayoutAlgorithm> ListItemGroupPattern::CreateLayoutAlgorithm()
109 {
110     CalculateItemStartIndex();
111     auto layoutAlgorithm =
112         MakeRefPtr<ListItemGroupLayoutAlgorithm>(headerIndex_, footerIndex_, itemStartIndex_, footerCount_);
113     layoutAlgorithm->SetItemsPosition(itemPosition_);
114     layoutAlgorithm->SetCachedItemsPosition(cachedItemPosition_);
115     layoutAlgorithm->SetCachedIndex(forwardCachedIndex_, backwardCachedIndex_);
116     layoutAlgorithm->SetLayoutedItemInfo(layoutedItemInfo_);
117     layoutAlgorithm->SetPrevTotalItemCount(itemTotalCount_);
118     layoutAlgorithm->SetPrevTotalMainSize(mainSize_);
119     layoutAlgorithm->SetPrevMeasureBreak(prevMeasureBreak_);
120     if (childrenSize_ && ListChildrenSizeExist()) {
121         if (!posMap_) {
122             posMap_ = MakeRefPtr<ListPositionMap>();
123         }
124         layoutAlgorithm->SetListChildrenMainSize(childrenSize_);
125         layoutAlgorithm->SetListPositionMap(posMap_);
126     }
127     return layoutAlgorithm;
128 }
129 
CreateNodePaintMethod()130 RefPtr<NodePaintMethod> ListItemGroupPattern::CreateNodePaintMethod()
131 {
132     auto layoutProperty = GetLayoutProperty<ListItemGroupLayoutProperty>();
133     V2::ItemDivider itemDivider;
134     auto divider = layoutProperty->GetDivider().value_or(itemDivider);
135     auto drawVertical = (axis_ == Axis::HORIZONTAL);
136     ListItemGroupPaintInfo listItemGroupPaintInfo { layoutDirection_, mainSize_, drawVertical, lanes_,
137         spaceWidth_, laneGutter_, itemTotalCount_, listContentSize_ };
138     return MakeRefPtr<ListItemGroupPaintMethod>(
139         divider, listItemGroupPaintInfo, itemPosition_, cachedItemPosition_, pressedItem_);
140 }
141 
SyncItemsToCachedItemPosition()142 void ListItemGroupPattern::SyncItemsToCachedItemPosition()
143 {
144     itemPosition_.insert(cachedItemPosition_.begin(), cachedItemPosition_.end());
145     cachedItemPosition_.swap(itemPosition_);
146     itemPosition_.clear();
147 }
148 
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)149 bool ListItemGroupPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
150 {
151     if (config.skipMeasure && config.skipLayout) {
152         return false;
153     }
154     auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
155     CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
156     auto layoutAlgorithm = DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
157     CHECK_NULL_RETURN(layoutAlgorithm, false);
158     itemTotalCount_ = layoutAlgorithm->GetTotalItemCount();
159     footerIndex_ = layoutAlgorithm->GetFooterIndex();
160     isStackFromEnd_ = layoutAlgorithm->GetStackFromEnd();
161     auto cacheParam = layoutAlgorithm->GetCacheParam();
162     if (cacheParam) {
163         forwardCachedIndex_ = isStackFromEnd_ ? itemTotalCount_ - cacheParam.value().backwardCachedIndex - 1 :
164                                                 cacheParam.value().forwardCachedIndex;
165         backwardCachedIndex_ = isStackFromEnd_ ? itemTotalCount_ - cacheParam.value().forwardCachedIndex - 1 :
166                                                  cacheParam.value().backwardCachedIndex;
167         layoutAlgorithm->SetCacheParam(std::nullopt);
168     }
169     MappingPropertiesFromLayoutAlgorithm(layoutAlgorithm);
170     CheckListDirectionInCardStyle();
171     auto host = GetHost();
172     CHECK_NULL_RETURN(host, false);
173     auto accessibilityProperty = host->GetAccessibilityProperty<ListItemGroupAccessibilityProperty>();
174     if (accessibilityProperty != nullptr) {
175         accessibilityProperty->SetCollectionItemCounts(layoutAlgorithm->GetTotalItemCount());
176     }
177     auto listLayoutProperty = host->GetLayoutProperty<ListItemGroupLayoutProperty>();
178     return listLayoutProperty && listLayoutProperty->GetDivider().has_value();
179 }
180 
GetPaddingAndMargin() const181 float ListItemGroupPattern::GetPaddingAndMargin() const
182 {
183     auto layoutProperty = GetLayoutProperty<ListItemGroupLayoutProperty>();
184     CHECK_NULL_RETURN(layoutProperty, 0.0f);
185     const auto& padding = layoutProperty->CreatePaddingAndBorder();
186     const auto& margin = layoutProperty->CreateMargin();
187     auto offsetBeforeContent = axis_ == Axis::HORIZONTAL ? padding.left.value_or(0) : padding.top.value_or(0);
188     auto offsetAfterContent = axis_ == Axis::HORIZONTAL ? padding.right.value_or(0) : padding.bottom.value_or(0);
189     offsetBeforeContent += axis_ == Axis::HORIZONTAL ? margin.left.value_or(0) : margin.top.value_or(0);
190     offsetAfterContent += axis_ == Axis::HORIZONTAL ? margin.right.value_or(0) : margin.bottom.value_or(0);
191     return offsetBeforeContent + offsetAfterContent;
192 }
193 
GetEstimateOffset(float height,const std::pair<float,float> & targetPos,float headerMainSize,float footerMainSize) const194 float ListItemGroupPattern::GetEstimateOffset(float height, const std::pair<float, float>& targetPos,
195     float headerMainSize, float footerMainSize) const
196 {
197     if (layoutedItemInfo_.has_value() && layoutedItemInfo_.value().startIndex > 0) {
198         float averageHeight = 0.0f;
199         float estimateHeight = GetEstimateHeight(averageHeight, headerMainSize, footerMainSize, spaceWidth_);
200         if (layoutedItemInfo_.value().endIndex >= itemTotalCount_ - 1) {
201             return height + estimateHeight - targetPos.second;
202         } else {
203             return height - targetPos.first + layoutedItemInfo_.value().startIndex * averageHeight - spaceWidth_;
204         }
205     }
206     return height - targetPos.first;
207 }
208 
IsVisible() const209 bool ListItemGroupPattern::IsVisible() const
210 {
211     auto layoutProperty = GetLayoutProperty<ListItemGroupLayoutProperty>();
212     CHECK_NULL_RETURN(layoutProperty, false);
213     auto visible = layoutProperty->GetVisibility().value_or(VisibleType::VISIBLE);
214     if (visible == VisibleType::GONE) {
215         return false;
216     }
217     return true;
218 }
219 
GetEstimateHeight(float & averageHeight,float headerMainSize,float footerMainSize,float spaceWidth) const220 float ListItemGroupPattern::GetEstimateHeight(float& averageHeight,
221     float headerMainSize, float footerMainSize, float spaceWidth) 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     float totalHeight = 0.0f;
243     auto host = GetHost();
244     auto totalItem = host->GetTotalChildCount();
245     if (header_.Upgrade()) {
246         totalItem -= 1;
247         totalHeight += headerMainSize;
248     }
249     if (footer_.Upgrade()) {
250         totalItem -= 1;
251         totalHeight += footerMainSize;
252     }
253     return totalHeight + averageHeight * totalItem + paddingAndMargin - spaceWidth;
254 }
255 
CheckListDirectionInCardStyle()256 void ListItemGroupPattern::CheckListDirectionInCardStyle()
257 {
258     if (axis_ == Axis::HORIZONTAL && listItemGroupStyle_ == V2::ListItemGroupStyle::CARD) {
259         auto host = GetHost();
260         CHECK_NULL_VOID(host);
261         RefPtr<FrameNode> listNode = AceType::DynamicCast<FrameNode>(host->GetParent());
262         CHECK_NULL_VOID(listNode);
263         auto listPattern = listNode->GetPattern<ListPattern>();
264         CHECK_NULL_VOID(listPattern);
265         listPattern->SetNeedToUpdateListDirectionInCardStyle(true);
266     }
267 }
268 
GetListFrameNode() const269 RefPtr<FrameNode> ListItemGroupPattern::GetListFrameNode() const
270 {
271     auto host = GetHost();
272     CHECK_NULL_RETURN(host, nullptr);
273     auto parent = host->GetParent();
274     RefPtr<FrameNode> frameNode = AceType::DynamicCast<FrameNode>(parent);
275     while (parent && !frameNode) {
276         parent = parent->GetParent();
277         frameNode = AceType::DynamicCast<FrameNode>(parent);
278     }
279     return frameNode;
280 }
281 
ListChildrenSizeExist()282 bool ListItemGroupPattern::ListChildrenSizeExist()
283 {
284     RefPtr<FrameNode> listNode = GetListFrameNode();
285     CHECK_NULL_RETURN(listNode, false);
286     auto listPattern = listNode->GetPattern<ListPattern>();
287     CHECK_NULL_RETURN(listPattern, false);
288     return listPattern->ListChildrenSizeExist();
289 }
290 
GetOrCreateListChildrenMainSize()291 RefPtr<ListChildrenMainSize> ListItemGroupPattern::GetOrCreateListChildrenMainSize()
292 {
293     if (childrenSize_) {
294         return childrenSize_;
295     }
296     childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>();
297     auto callback = [weakPattern = WeakClaim(this)](std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag) {
298         auto pattern = weakPattern.Upgrade();
299         CHECK_NULL_VOID(pattern);
300         auto context = pattern->GetContext();
301         CHECK_NULL_VOID(context);
302         context->AddBuildFinishCallBack([weakPattern, change, flag]() {
303             auto pattern = weakPattern.Upgrade();
304             CHECK_NULL_VOID(pattern);
305             pattern->OnChildrenSizeChanged(change, flag);
306         });
307         context->RequestFrame();
308     };
309     childrenSize_->SetOnDataChange(callback);
310     auto pipeline = GetContext();
311     if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
312         childrenSize_->SetIsRoundingMode();
313     }
314     return childrenSize_;
315 }
316 
SetListChildrenMainSize(float defaultSize,const std::vector<float> & mainSize)317 void ListItemGroupPattern::SetListChildrenMainSize(
318     float defaultSize, const std::vector<float>& mainSize)
319 {
320     childrenSize_ = AceType::MakeRefPtr<ListChildrenMainSize>(mainSize, defaultSize);
321     OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
322     auto pipeline = GetContext();
323     if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
324         childrenSize_->SetIsRoundingMode();
325     }
326 }
327 
OnChildrenSizeChanged(std::tuple<int32_t,int32_t,int32_t> change,ListChangeFlag flag)328 void ListItemGroupPattern::OnChildrenSizeChanged(std::tuple<int32_t, int32_t, int32_t> change, ListChangeFlag flag)
329 {
330     if (!posMap_) {
331         posMap_ = MakeRefPtr<ListPositionMap>();
332     }
333     posMap_->MarkDirty(flag);
334     auto host = GetHost();
335     CHECK_NULL_VOID(host);
336     host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
337 }
338 
GetStartListItemIndex()339 VisibleContentInfo ListItemGroupPattern::GetStartListItemIndex()
340 {
341     bool isHeader = false;
342     auto startHeaderMainSize = GetHeaderMainSize();
343     auto startFooterMainSize = GetFooterMainSize();
344     if (GetDisplayStartIndexInGroup() == 0) {
345         auto startHeaderPos = startHeaderPos_;
346         isHeader = (startHeaderPos + startHeaderMainSize) > 0 ? true : false;
347     }
348     auto startPositionSize = GetItemPosition().size();
349     auto startItemIndexInGroup = GetDisplayStartIndexInGroup();
350     auto startArea = ListItemGroupArea::IN_LIST_ITEM_AREA;
351     if (startPositionSize == 0 && startFooterMainSize > 0) {
352         startArea = ListItemGroupArea::IN_FOOTER_AREA;
353         startItemIndexInGroup = -1;
354     }
355     if (GetDisplayStartIndexInGroup() == 0 && isHeader && startHeaderMainSize > 0) {
356         startArea = ListItemGroupArea::IN_HEADER_AREA;
357         startItemIndexInGroup = -1;
358     }
359     if (startHeaderMainSize == 0 && startFooterMainSize == 0 && GetTotalItemCount() == 0) {
360         startArea = ListItemGroupArea::NONE_AREA;
361     }
362     VisibleContentInfo startInfo = { startArea, startItemIndexInGroup };
363     return startInfo;
364 }
365 
GetEndListItemIndex()366 VisibleContentInfo ListItemGroupPattern::GetEndListItemIndex()
367 {
368     bool isFooter = endFooterPos_ < 0 ? true : false;
369     auto endHeaderMainSize = GetHeaderMainSize();
370     auto endFooterMainSize = GetFooterMainSize();
371     auto endPositionSize = GetItemPosition().size();
372     auto endItemIndexInGroup = GetDisplayEndIndexInGroup();
373     auto endArea = ListItemGroupArea::IN_LIST_ITEM_AREA;
374     if (endPositionSize == 0 && endHeaderMainSize > 0) {
375         endArea = ListItemGroupArea::IN_HEADER_AREA;
376         endItemIndexInGroup = -1;
377     }
378     if (isFooter && endFooterMainSize > 0) {
379         endArea = ListItemGroupArea::IN_FOOTER_AREA;
380         endItemIndexInGroup = -1;
381     }
382     if (endHeaderMainSize == 0 && endFooterMainSize == 0 && GetTotalItemCount() == 0) {
383         endArea = ListItemGroupArea::NONE_AREA;
384     }
385     VisibleContentInfo endInfo = { endArea, endItemIndexInGroup };
386     return endInfo;
387 }
388 
ResetChildrenSize()389 void ListItemGroupPattern::ResetChildrenSize()
390 {
391     if (childrenSize_) {
392         childrenSize_ = nullptr;
393         auto host = GetHost();
394         CHECK_NULL_VOID(host);
395         host->MarkDirtyNode(PROPERTY_UPDATE_BY_CHILD_REQUEST);
396         OnChildrenSizeChanged({ -1, -1, -1 }, LIST_UPDATE_CHILD_SIZE);
397     }
398 }
399 
ClearItemPosition()400 void ListItemGroupPattern::ClearItemPosition()
401 {
402     itemPosition_.clear();
403 }
404 
ClearCachedItemPosition()405 void ListItemGroupPattern::ClearCachedItemPosition()
406 {
407     cachedItemPosition_.clear();
408     forwardCachedIndex_ = -1;
409     backwardCachedIndex_ = INT_MAX;
410 }
411 
CalculateItemStartIndex()412 void ListItemGroupPattern::CalculateItemStartIndex()
413 {
414     int32_t footerCount = 0;
415     auto footer = footer_.Upgrade();
416     if (footer) {
417         int32_t count = footer->FrameCount();
418         if (count > 0) {
419             footerCount = count;
420         }
421     }
422     AdjustMountTreeSequence(footerCount);
423 
424     int32_t headerIndex = -1;
425     int32_t itemStartIndex = 0;
426     auto header = header_.Upgrade();
427     if (header) {
428         auto count = header->FrameCount();
429         if (count > 0) {
430             headerIndex = itemStartIndex;
431             itemStartIndex += count;
432         }
433     }
434 
435     headerIndex_ = headerIndex;
436     itemStartIndex_ = itemStartIndex;
437     footerCount_ = footerCount;
438 }
439 
UpdateActiveChildRange(bool forward,int32_t cacheCount,bool show)440 void ListItemGroupPattern::UpdateActiveChildRange(bool forward, int32_t cacheCount, bool show)
441 {
442     auto host = GetHost();
443     CHECK_NULL_VOID(host);
444     if (forward) {
445         host->SetActiveChildRange(-1, itemStartIndex_ - 1, 0, cacheCount, show);
446     } else {
447         int32_t index = itemTotalCount_ + itemStartIndex_;
448         host->SetActiveChildRange(index, index, cacheCount, 0, show);
449     }
450     if (show && headerIndex_ >= 0) {
451         host->GetOrCreateChildByIndex(headerIndex_);
452     }
453     if (show && footerIndex_ >= 0) {
454         host->GetOrCreateChildByIndex(footerIndex_);
455     }
456     if (show) {
457         host->RebuildRenderContextTree();
458     }
459 }
460 
UpdateActiveChildRange(bool show)461 void ListItemGroupPattern::UpdateActiveChildRange(bool show)
462 {
463     auto host = GetHost();
464     CHECK_NULL_VOID(host);
465     if (!itemPosition_.empty()) {
466         auto start = itemStartIndex_ + itemPosition_.begin()->first;
467         auto end = itemStartIndex_ + itemPosition_.rbegin()->first;
468         host->SetActiveChildRange(start, end);
469     } else if (headerIndex_ >= 0 || footerIndex_ >= 0) {
470         host->SetActiveChildRange(-1, itemStartIndex_ - 1);
471     } else {
472         host->SetActiveChildRange(-1, -1);
473     }
474     if (headerIndex_ >= 0) {
475         host->GetOrCreateChildByIndex(headerIndex_);
476     }
477     if (footerIndex_ >= 0) {
478         host->GetOrCreateChildByIndex(footerIndex_);
479     }
480     if (show) {
481         host->RebuildRenderContextTree();
482     }
483 }
484 
UpdateCachedIndexForward(bool outOfView,bool show,int32_t cacheCount)485 int32_t ListItemGroupPattern::UpdateCachedIndexForward(bool outOfView, bool show, int32_t cacheCount)
486 {
487     int32_t endIndex = (outOfView || itemPosition_.empty()) ? -1 : itemPosition_.rbegin()->first;
488     int32_t endLimit = std::min(endIndex + cacheCount * lanes_, itemTotalCount_ - 1);
489     int32_t forwardCachedIndex = std::clamp(forwardCachedIndex_, endIndex, endLimit);
490     auto iter = cachedItemPosition_.begin();
491     while (iter != cachedItemPosition_.end()) {
492         if (iter->first >= endIndex + 1 && iter->first <= endLimit) {
493             iter++;
494             continue;
495         }
496         iter = cachedItemPosition_.erase(iter);
497     }
498     if (cachedItemPosition_.find(forwardCachedIndex) == cachedItemPosition_.end() ||
499         cachedItemPosition_.find(endIndex + 1) == cachedItemPosition_.end()) {
500         forwardCachedIndex = endIndex;
501         cachedItemPosition_.clear();
502     }
503     if (outOfView && forwardCachedIndex < forwardCachedIndex_) {
504         UpdateActiveChildRange(true, forwardCachedIndex + 1, show);
505     }
506     return forwardCachedIndex;
507 }
508 
UpdateCachedIndexBackward(bool outOfView,bool show,int32_t cacheCount)509 int32_t ListItemGroupPattern::UpdateCachedIndexBackward(bool outOfView, bool show, int32_t cacheCount)
510 {
511     int32_t startIndex = (outOfView || itemPosition_.empty()) ? itemTotalCount_ : itemPosition_.begin()->first;
512     int32_t startLimit = std::max(startIndex - cacheCount * lanes_, 0);
513     if (startLimit % lanes_ != 0) {
514         startLimit += (lanes_ - startLimit % lanes_);
515     }
516     int32_t backwardCachedIndex = std::clamp(backwardCachedIndex_, startLimit, startIndex);
517     auto iter = cachedItemPosition_.begin();
518     while (iter != cachedItemPosition_.end()) {
519         if (iter->first >= startLimit && iter->first <= startIndex - 1) {
520             iter++;
521             continue;
522         }
523         iter = cachedItemPosition_.erase(iter);
524     }
525     if (cachedItemPosition_.find(backwardCachedIndex) == cachedItemPosition_.end() ||
526         cachedItemPosition_.find(startIndex - 1) == cachedItemPosition_.end()) {
527         backwardCachedIndex = startIndex;
528         cachedItemPosition_.clear();
529     }
530     if (outOfView && backwardCachedIndex > backwardCachedIndex_) {
531         UpdateActiveChildRange(false, itemTotalCount_ - backwardCachedIndex, show);
532     }
533     return backwardCachedIndex;
534 }
535 
UpdateCachedIndexOmni(int32_t forwardCache,int32_t backwardCache)536 std::pair<int32_t, int32_t> ListItemGroupPattern::UpdateCachedIndexOmni(int32_t forwardCache, int32_t backwardCache)
537 {
538     int32_t forwardRes = -1;
539     int32_t backwardRes = INT_MAX;
540     int32_t startIndex = itemPosition_.begin()->first;
541     int32_t startLimit = std::max(startIndex - backwardCache * lanes_, 0);
542     if (startLimit % lanes_ != 0) {
543         startLimit += (lanes_ - startLimit % lanes_);
544     }
545     int32_t backwardCachedIndex = std::clamp(backwardCachedIndex_, startLimit, startIndex);
546     int32_t endIndex = itemPosition_.rbegin()->first;
547     int32_t endLimit = std::min(endIndex + forwardCache * lanes_, itemTotalCount_ - 1);
548     int32_t forwardCachedIndex = std::clamp(forwardCachedIndex_, endIndex, endLimit);
549     auto iter = cachedItemPosition_.begin();
550     while (iter != cachedItemPosition_.end()) {
551         if ((iter->first >= startLimit && iter->first <= startIndex - 1) ||
552             (iter->first >= endIndex + 1 && iter->first <= endLimit)) {
553             iter++;
554             continue;
555         }
556         iter = cachedItemPosition_.erase(iter);
557     }
558     if (cachedItemPosition_.find(backwardCachedIndex) == cachedItemPosition_.end() ||
559         cachedItemPosition_.find(startIndex - 1) == cachedItemPosition_.end()) {
560         backwardRes = startIndex;
561     } else {
562         backwardRes = backwardCachedIndex;
563     }
564     if (cachedItemPosition_.find(forwardCachedIndex) == cachedItemPosition_.end() ||
565         cachedItemPosition_.find(endIndex + 1) == cachedItemPosition_.end()) {
566         forwardRes = endIndex;
567     } else {
568         forwardRes = forwardCachedIndex;
569     }
570     return { forwardRes, backwardRes };
571 }
572 
UpdateCachedIndex(bool outOfView,bool reCache,int32_t forwardCache,int32_t backwardCache)573 CachedIndexInfo ListItemGroupPattern::UpdateCachedIndex(
574     bool outOfView, bool reCache, int32_t forwardCache, int32_t backwardCache)
575 {
576     CachedIndexInfo res;
577     auto host = GetHost();
578     if (!host) {
579         forwardCachedIndex_ = -1;
580         backwardCachedIndex_ = INT_MAX;
581         return res;
582     }
583     auto listNode = GetListFrameNode();
584     bool show = listNode && listNode->GetLayoutProperty<ListLayoutProperty>() ?
585         listNode->GetLayoutProperty<ListLayoutProperty>()->GetShowCachedItemsValue(false) : false;
586     if (itemTotalCount_ == -1 || host->CheckNeedForceMeasureAndLayout()) {
587         CalculateItemStartIndex();
588         itemTotalCount_ = host->GetTotalChildCount() - itemStartIndex_ - footerCount_;
589     }
590     if (outOfView) {
591         ClearItemPosition();
592     }
593     if (reCache || reCache_) {
594         ClearCachedItemPosition();
595         UpdateActiveChildRange(show);
596         reCache_ = false;
597     }
598     int32_t lanes = lanes_ > 1 ? lanes_ : 1;
599     if (forwardCache > -1 && backwardCache > -1 && !itemPosition_.empty()) {
600         auto cached = UpdateCachedIndexOmni(forwardCache, backwardCache);
601         forwardCachedIndex_ = cached.first;
602         backwardCachedIndex_ = cached.second;
603         int32_t startIndex = itemPosition_.begin()->first;
604         int32_t endIndex = itemPosition_.rbegin()->first;
605         res.forwardCachedCount = (forwardCachedIndex_ - endIndex + lanes - 1) / lanes;
606         res.forwardCacheMax = (itemTotalCount_ - 1 - endIndex + lanes - 1) / lanes;
607         res.backwardCachedCount = (startIndex - backwardCachedIndex_ + lanes - 1) / lanes;
608         res.backwardCacheMax = (startIndex + lanes - 1) / lanes;
609     } else if (forwardCache > -1) {
610         forwardCachedIndex_ = UpdateCachedIndexForward(outOfView, show, forwardCache);
611         backwardCachedIndex_ = INT_MAX;
612         int32_t endIndex = (outOfView || itemPosition_.empty()) ? -1 : itemPosition_.rbegin()->first;
613         res.forwardCachedCount = (forwardCachedIndex_ - endIndex + lanes - 1) / lanes;
614         res.forwardCacheMax = (itemTotalCount_ - 1 - endIndex + lanes - 1) / lanes;
615     } else if (backwardCache > -1) {
616         forwardCachedIndex_ = -1;
617         backwardCachedIndex_ = UpdateCachedIndexBackward(outOfView, show, backwardCache);
618         int32_t startIndex = (outOfView || itemPosition_.empty()) ? itemTotalCount_ : itemPosition_.begin()->first;
619         res.backwardCachedCount = (startIndex - backwardCachedIndex_ + lanes - 1) / lanes;
620         res.backwardCacheMax = (startIndex + lanes - 1) / lanes;
621     }
622     if (isStackFromEnd_) {
623         std::swap(res.forwardCachedCount, res.backwardCachedCount);
624         std::swap(res.forwardCacheMax, res.backwardCacheMax);
625     }
626     return res;
627 }
628 
NeedCacheForward(const LayoutWrapper * listWrapper) const629 bool ListItemGroupPattern::NeedCacheForward(const LayoutWrapper* listWrapper) const
630 {
631     auto host = GetHost();
632     CHECK_NULL_RETURN(host, true);
633     auto listProperty = AceType::DynamicCast<ListLayoutProperty>(listWrapper->GetLayoutProperty());
634     CHECK_NULL_RETURN(listProperty, true);
635     auto listPadding = listProperty->CreatePaddingAndBorder().Offset();
636     auto offset = host->GetGeometryNode()->GetMarginFrameOffset();
637     if (GreatNotEqual(GetMainAxisOffset(offset, axis_) + headerMainSize_, GetMainAxisOffset(listPadding, axis_))) {
638         return true;
639     } else {
640         return false;
641     }
642 }
643 
LayoutCache(const LayoutConstraintF & constraint,int64_t deadline,int32_t forwardCached,int32_t backwardCached,ListMainSizeValues listSizeValues)644 void ListItemGroupPattern::LayoutCache(const LayoutConstraintF& constraint, int64_t deadline,
645     int32_t forwardCached, int32_t backwardCached, ListMainSizeValues listSizeValues)
646 {
647     auto listNode = GetListFrameNode();
648     CHECK_NULL_VOID(listNode);
649     auto listPattern = listNode->GetPattern<ListPattern>();
650     CHECK_NULL_VOID(listPattern);
651     auto listLayoutProperty = listNode->GetLayoutProperty<ListLayoutProperty>();
652     CHECK_NULL_VOID(listLayoutProperty);
653     auto cacheCountForward = listLayoutProperty->GetCachedCountWithDefault() - forwardCached;
654     auto cacheCountBackward = listLayoutProperty->GetCachedCountWithDefault() - backwardCached;
655     if (cacheCountForward < 1 && cacheCountBackward < 1) {
656         return;
657     }
658     auto host = GetHost();
659     CHECK_NULL_VOID(host);
660     auto layoutAlgorithmWrapper = host->GetLayoutAlgorithm(true);
661     CHECK_NULL_VOID(layoutAlgorithmWrapper);
662     auto itemGroup = AceType::DynamicCast<ListItemGroupLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
663     CHECK_NULL_VOID(itemGroup);
664     ListItemGroupCacheParam param = {
665         .forward = listSizeValues.forward,
666         .backward = listSizeValues.backward,
667         .show = listLayoutProperty->GetShowCachedItemsValue(false),
668         .cacheCountForward = cacheCountForward,
669         .cacheCountBackward = cacheCountBackward,
670         .forwardCachedIndex = forwardCachedIndex_,
671         .backwardCachedIndex = backwardCachedIndex_,
672         .deadline = deadline,
673     };
674     itemGroup->SetContentOffset(listSizeValues.contentStartOffset, listSizeValues.contentEndOffset);
675     itemGroup->SetCacheParam(param);
676     itemGroup->SetListLayoutProperty(listLayoutProperty);
677     itemGroup->SetListMainSize(listSizeValues.startPos, listSizeValues.endPos, listSizeValues.referencePos,
678         listSizeValues.prevContentMainSize, listSizeValues.forward);
679     host->GetLayoutProperty()->UpdatePropertyChangeFlag(PROPERTY_UPDATE_MEASURE);
680     host->GetGeometryNode()->SetParentLayoutConstraint(constraint);
681     FrameNode::ProcessOffscreenNode(host, true);
682     if ((!NearZero(adjustRefPos_) || !NearZero(adjustTotalSize_)) && !(childrenSize_ && ListChildrenSizeExist())) {
683         listPattern->UpdateChildPosInfo(indexInList_, adjustRefPos_, adjustTotalSize_);
684         adjustRefPos_ = 0.0f;
685         adjustTotalSize_ = 0.0f;
686     }
687 }
688 
SetListItemGroupStyle(V2::ListItemGroupStyle style)689 void ListItemGroupPattern::SetListItemGroupStyle(V2::ListItemGroupStyle style)
690 {
691     auto host = GetHost();
692     CHECK_NULL_VOID(host);
693     if (listItemGroupStyle_ == V2::ListItemGroupStyle::NONE && style == V2::ListItemGroupStyle::CARD) {
694         listItemGroupStyle_ = style;
695         SetListItemGroupDefaultAttributes(host);
696     }
697 }
698 
GetListPaddingOffset(const RefPtr<FrameNode> & listNode) const699 float ListItemGroupPattern::GetListPaddingOffset(const RefPtr<FrameNode>& listNode) const
700 {
701     float offset = 0;
702     CHECK_NULL_RETURN(listNode, offset);
703     auto layoutProperty = listNode->GetLayoutProperty<ListLayoutProperty>();
704     CHECK_NULL_RETURN(layoutProperty, offset);
705     auto padding = layoutProperty->CreatePaddingAndBorder();
706     return GetMainAxisOffset(padding.Offset(), axis_);
707 }
708 
FirstItemFullVisible(const RefPtr<FrameNode> & listNode) const709 bool ListItemGroupPattern::FirstItemFullVisible(const RefPtr<FrameNode>& listNode) const
710 {
711     auto host = GetHost();
712     CHECK_NULL_RETURN(host, true);
713     auto geometryNode = host->GetGeometryNode();
714     CHECK_NULL_RETURN(geometryNode, true);
715     OffsetF selfOffset = geometryNode->GetPaddingOffset();
716     float mainPos = GetMainAxisOffset(selfOffset, axis_) + headerMainSize_;
717     float listPadding = GetListPaddingOffset(listNode);
718     return GreatNotEqual(mainPos, listPadding);
719 }
720 
CheckDataChangeOutOfStart(int32_t index,int32_t count,int32_t startIndex,int32_t endIndex)721 bool ListItemGroupPattern::CheckDataChangeOutOfStart(int32_t index, int32_t count, int32_t startIndex, int32_t endIndex)
722 {
723     if (count == 0) {
724         return false;
725     }
726     if (((count > 0 && index > startIndex) || (count < 0 && index >= startIndex)) && !isStackFromEnd_) {
727         return false;
728     }
729     if (((count > 0 && index < endIndex) || (count < 0 && index <= endIndex)) && isStackFromEnd_) {
730         return false;
731     }
732 
733     RefPtr<FrameNode> listNode = GetListFrameNode();
734     CHECK_NULL_RETURN(listNode, false);
735     auto listPattern = listNode->GetPattern<ListPattern>();
736     CHECK_NULL_RETURN(listPattern, false);
737     if (!listPattern->GetMaintainVisibleContentPosition()) {
738         return false;
739     }
740 
741     if (startIndex == 0 && index == 0 && count > 0 && FirstItemFullVisible(listNode)) {
742         return false;
743     }
744     listPattern->MarkNeedReEstimateOffset();
745     return true;
746 }
747 
NotifyDataChange(int32_t index,int32_t count)748 void ListItemGroupPattern::NotifyDataChange(int32_t index, int32_t count)
749 {
750     if (auto parentList = GetListFrameNode()) {
751         if (auto listPattern = parentList->GetPattern<ListPattern>()) {
752             listPattern->UpdateGroupFocusIndexForDataChange(GetIndexInList(), index, count);
753         }
754     }
755 
756     if (itemPosition_.empty()) {
757         return;
758     }
759     index -= itemStartIndex_;
760     int32_t startIndex = itemPosition_.begin()->first;
761     int32_t endIndex = itemPosition_.rbegin()->first;
762     if (!CheckDataChangeOutOfStart(index, count, startIndex, endIndex)) {
763         return;
764     }
765 
766     count = !isStackFromEnd_ ? std::max(count, index - startIndex) : - std::max(count, endIndex - index);
767     int32_t mod = 0;
768     if (count < 0 && lanes_ > 1) {
769         mod = -count % lanes_;
770     }
771     auto prevPosMap = std::move(itemPosition_);
772     for (auto &pos : prevPosMap) {
773         if (mod > 0) {
774             mod--;
775         } else {
776             itemPosition_[pos.first + count] = pos.second;
777         }
778     }
779     if (layoutedItemInfo_ && layoutedItemInfo_.value().startIndex >= index) {
780         auto& info = layoutedItemInfo_.value();
781         info.startIndex = std::max(info.startIndex + count, 0);
782         info.endIndex = std::max(info.endIndex + count, 0);
783         if (lanes_ > 1) {
784             if (count < 0) {
785                 info.startIndex += -count % lanes_;
786             } else {
787                 info.endIndex -= count % lanes_;
788             }
789         }
790     }
791 }
792 
UpdateDefaultColor()793 void ListItemGroupPattern::UpdateDefaultColor()
794 {
795     auto host = GetHost();
796     CHECK_NULL_VOID(host);
797     auto pipeline = host->GetContextWithCheck();
798     CHECK_NULL_VOID(pipeline);
799     auto theme = pipeline->GetTheme<ListTheme>();
800     CHECK_NULL_VOID(theme);
801     auto listItemLayoutProperty = host->GetLayoutProperty<ListItemGroupLayoutProperty>();
802     CHECK_NULL_VOID(listItemLayoutProperty);
803     if (!listItemLayoutProperty->HasDividerColorSetByUser() ||
804         (listItemLayoutProperty->HasDividerColorSetByUser() &&
805             !listItemLayoutProperty->GetDividerColorSetByUserValue())) {
806         V2::ItemDivider value;
807         ACE_GET_NODE_LAYOUT_PROPERTY_WITH_DEFAULT_VALUE(ListItemGroupLayoutProperty, Divider, value, host, value);
808         value.color = theme->GetDividerColor();
809         ACE_UPDATE_NODE_LAYOUT_PROPERTY(ListItemGroupLayoutProperty, Divider, value, host);
810     }
811 }
812 
OnColorModeChange(uint32_t colorMode)813 void ListItemGroupPattern::OnColorModeChange(uint32_t colorMode)
814 {
815     Pattern::OnColorModeChange(colorMode);
816     auto host = GetHost();
817     CHECK_NULL_VOID(host);
818     CHECK_NULL_VOID(SystemProperties::ConfigChangePerform());
819     UpdateDefaultColor();
820     host->MarkDirtyNode(PROPERTY_UPDATE_NORMAL);
821 }
822 
DumpAdvanceInfo(std::unique_ptr<JsonValue> & json)823 void ListItemGroupPattern::DumpAdvanceInfo(std::unique_ptr<JsonValue>& json)
824 {
825     json->Put("itemStartIndex", itemStartIndex_);
826     json->Put("itemTotalCount", itemTotalCount_);
827     json->Put("itemDisplayEndIndex", itemDisplayEndIndex_);
828     json->Put("itemDisplayStartIndex", itemDisplayStartIndex_);
829     json->Put("headerMainSize", headerMainSize_);
830     json->Put("footerMainSize", footerMainSize_);
831     json->Put("spaceWidth", spaceWidth_);
832     json->Put("lanes", lanes_);
833     json->Put("laneGutter", laneGutter_);
834     json->Put("startHeaderPos", startHeaderPos_);
835     json->Put("endFooterPos", endFooterPos_);
836 }
837 
GetScopeFocusAlgorithm()838 ScopeFocusAlgorithm ListItemGroupPattern::GetScopeFocusAlgorithm()
839 {
840     auto property = GetLayoutProperty<ListItemGroupLayoutProperty>();
841     if (!property) {
842         return {};
843     }
844     auto listNode = GetListFrameNode();
845     CHECK_NULL_RETURN(listNode, {});
846     auto listProperty = listNode->GetLayoutProperty<ListLayoutProperty>();
847     CHECK_NULL_RETURN(listProperty, {});
848     return ScopeFocusAlgorithm(listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL, true,
849         ScopeType::OTHERS,
850         [wp = WeakClaim(this)](
851             FocusStep step, const WeakPtr<FocusHub>& currFocusNode, WeakPtr<FocusHub>& nextFocusNode) -> bool {
852             auto listItemGroup = wp.Upgrade();
853             if (listItemGroup) {
854                 nextFocusNode = listItemGroup->GetNextFocusNode(step, currFocusNode);
855             }
856             return nextFocusNode.Upgrade() != currFocusNode.Upgrade();
857         });
858 }
859 
GetChildFocusNodeByIndex(int32_t tarIndexInGroup)860 WeakPtr<FocusHub> ListItemGroupPattern::GetChildFocusNodeByIndex(int32_t tarIndexInGroup)
861 {
862     auto listItemGroupFrame = GetHost();
863     CHECK_NULL_RETURN(listItemGroupFrame, nullptr);
864     auto listItemGroupFocus = listItemGroupFrame->GetFocusHub();
865     CHECK_NULL_RETURN(listItemGroupFocus, nullptr);
866     WeakPtr<FocusHub> target;
867     listItemGroupFocus->AnyChildFocusHub([&target, tarIndexInGroup](const RefPtr<FocusHub>& childFocus) {
868         if (!childFocus->IsFocusable()) {
869             return false;
870         }
871         auto childFrame = childFocus->GetFrameNode();
872         if (!childFrame) {
873             return false;
874         }
875         auto childPattern = childFrame->GetPattern();
876         if (!childPattern) {
877             return false;
878         }
879         auto childItemPattern = AceType::DynamicCast<ListItemPattern>(childPattern);
880         if (!childItemPattern) {
881             auto parentNode = childFrame->GetParentFrameNode();
882             CHECK_NULL_RETURN(parentNode, false);
883             auto parentPattern = AceType::DynamicCast<ListItemGroupPattern>(parentNode->GetPattern());
884             CHECK_NULL_RETURN(parentPattern, false);
885             if ((parentPattern->GetHeader() == childFrame && tarIndexInGroup == -1) ||
886                 (parentPattern->GetFooter() == childFrame && tarIndexInGroup == parentPattern->GetTotalItemCount())) {
887                 target = childFocus;
888                 return true;
889             }
890 
891             return false;
892         }
893 
894         auto curIndexInGroup = childItemPattern->GetIndexInListItemGroup();
895         if (curIndexInGroup == tarIndexInGroup) {
896             target = childFocus;
897             return true;
898         }
899         return false;
900     });
901     return target;
902 }
903 
AdjustMountTreeSequence(int32_t footerCount)904 void ListItemGroupPattern::AdjustMountTreeSequence(int32_t footerCount)
905 {
906     // Adjust the mount tree sequence to header, listitem, footer
907     if (footerIndex_ < itemStartIndex_) {
908         auto footer = footer_.Upgrade();
909         CHECK_NULL_VOID(footer);
910         auto host = GetHost();
911         CHECK_NULL_VOID(host);
912         auto totalChildCount = host->GetTotalChildCount();
913         auto childNode = host->GetChildAtIndex(itemStartIndex_);
914         CHECK_NULL_VOID(childNode);
915         footer->MovePosition(-1);
916         footerIndex_ = totalChildCount - footerCount;
917         itemStartIndex_ -= footerCount;
918     }
919 }
920 
GetCurrentFocusIndices(const RefPtr<FrameNode> & curFrame,const RefPtr<Pattern> & curPattern,int32_t & curIndexInGroup)921 bool ListItemGroupPattern::GetCurrentFocusIndices(
922     const RefPtr<FrameNode>& curFrame, const RefPtr<Pattern>& curPattern, int32_t& curIndexInGroup)
923 {
924     // Only for GetNextFocusNode
925     CHECK_NULL_RETURN(curFrame, false);
926     CHECK_NULL_RETURN(curPattern, false);
927     auto curItemPattern = AceType::DynamicCast<ListItemPattern>(curPattern);
928     // In ListItemGroup, the current focus node could be the Header, Footer, or a ListItem of the ListItemGroup.
929     // If the current focus node is the Header or Footer of the ListItemGroup,
930     // it is necessary to retrieve the index of the current focus node within the ListItemGroup.
931     if (!curItemPattern) {
932         if (GetHeader() == curFrame) {
933             curIndexInGroup = -1;
934         } else if (GetFooter() == curFrame) {
935             curIndexInGroup = GetTotalItemCount();
936         } else {
937             return false;
938         }
939     } else {
940         // If the current focus node is a ListItem, it is necessary to retrieve the index of the current focus node
941         // within the ListItemGroup.
942         curIndexInGroup = curItemPattern->GetIndexInListItemGroup();
943     }
944     return true;
945 }
946 
AdjustFocusStepForRtl(FocusStep & step,bool isVertical)947 void ListItemGroupPattern::AdjustFocusStepForRtl(FocusStep& step, bool isVertical)
948 {
949     // FocusStep LEFT/RIGH reverse flag
950     bool reverseHorizontal = false;
951     // FocusStep UP/DOWN reverse flag
952     bool reverseVertical = false;
953 
954     if ((layoutDirection_ != TextDirection::RTL && isVertical && isStackFromEnd_) ||
955         (layoutDirection_ == TextDirection::RTL && !isStackFromEnd_)) {
956         reverseHorizontal = true;
957     } else if (layoutDirection_ != TextDirection::RTL && !isVertical && isStackFromEnd_) {
958         reverseVertical = true;
959     } else if (layoutDirection_ == TextDirection::RTL && !isVertical && isStackFromEnd_) {
960         reverseHorizontal = true;
961         reverseVertical = true;
962     }
963 
964     if (reverseHorizontal) {
965         if (step == FocusStep::LEFT) {
966             step = FocusStep::RIGHT;
967         } else if (step == FocusStep::RIGHT) {
968             step = FocusStep::LEFT;
969         }
970     }
971 
972     if (reverseVertical) {
973         if (step == FocusStep::UP) {
974             step = FocusStep::DOWN;
975         } else if (step == FocusStep::DOWN) {
976             step = FocusStep::UP;
977         }
978     }
979 }
980 
GetPosition(int32_t index) const981 const ListItemGroupInfo* ListItemGroupPattern::GetPosition(int32_t index) const
982 {
983     // Only for GetCrossAxisNextIndex
984     auto it = itemPosition_.find(index);
985     if (it != itemPosition_.end()) {
986         return &it->second;
987     }
988     auto cachedIt = cachedItemPosition_.find(index);
989     return (cachedIt != cachedItemPosition_.end()) ? &cachedIt->second : nullptr;
990 }
991 
NextPositionBlocksMove(const ListItemGroupInfo * curPos,const ListItemGroupInfo * nextPos,bool isVertical) const992 bool ListItemGroupPattern::NextPositionBlocksMove(
993     const ListItemGroupInfo* curPos, const ListItemGroupInfo* nextPos, bool isVertical) const
994 {
995     // Only for GetCrossAxisNextIndex, determine if the next position blocks movement.
996 
997     if (!nextPos) {
998         // No position information, allow movement (or handle externally).
999         return false;
1000     }
1001     // Check if the current and next positions are in the same column.
1002     // If the endPos and startPos of two items are the same, it indicates they are in the same row or column, allowing
1003     // focus movement; otherwise, it is considered to have reached the first column (row) or the last column (row),
1004     // disallowing focus movement.
1005     return curPos && (!NearEqual(curPos->endPos, nextPos->endPos) && !NearEqual(curPos->startPos, nextPos->startPos));
1006 }
1007 
HandleForwardStep(const RefPtr<FrameNode> & curFrame,int32_t curIndexInGroup,int32_t & moveStep,int32_t & nextIndex)1008 void ListItemGroupPattern::HandleForwardStep(
1009     const RefPtr<FrameNode>& curFrame, int32_t curIndexInGroup, int32_t& moveStep, int32_t& nextIndex)
1010 {
1011     // Only for DetermineMultiLaneStep
1012     CHECK_NULL_VOID(curFrame);
1013     moveStep = (GetHeader() == curFrame || GetFooter() == curFrame) ? 1 : lanes_;
1014     nextIndex = nextIndex + moveStep;
1015     if (curIndexInGroup < GetTotalItemCount() && curIndexInGroup >= 0) {
1016         // Neither Header nor Footer. If it is the last row and the DOWN key is pressed,
1017         // if there is a footer, move to the footer; if there is no footer, directly move
1018         // to the position where nextIndex >= itemTotalCount_, which is nullptr.
1019         auto row = (itemTotalCount_ - 1) / lanes_;
1020         auto curRow = curIndexInGroup / lanes_;
1021         if (isStackFromEnd_) {
1022             curRow = row - (itemTotalCount_ - 1 - curIndexInGroup) / lanes_;
1023         }
1024         if (curRow == row && IsHasFooter()) {
1025             nextIndex = itemTotalCount_;
1026         } else if (curRow == row - 1 && nextIndex >= itemTotalCount_) {
1027             // If it is the second-to-last row, pressing DOWN when nextIndex >= itemTotalCount_ moves to the last item.
1028             nextIndex = itemTotalCount_ - 1;
1029         }
1030     }
1031 }
1032 
HandleBackwardStep(const RefPtr<FrameNode> & curFrame,int32_t curIndexInGroup,int32_t & moveStep,int32_t & nextIndex)1033 void ListItemGroupPattern::HandleBackwardStep(
1034     const RefPtr<FrameNode>& curFrame, int32_t curIndexInGroup, int32_t& moveStep, int32_t& nextIndex)
1035 {
1036     // Only for DetermineMultiLaneStep
1037     CHECK_NULL_VOID(curFrame);
1038     // If the current focus is on the header, set moveStep = 1; if the current focus is on the footer,
1039     // set moveStep = -1;
1040     moveStep = (GetHeader() == curFrame || GetFooter() == curFrame) ? -1 : -lanes_;
1041     nextIndex = curIndexInGroup + moveStep;
1042     if (curIndexInGroup >= 0 && curIndexInGroup < GetTotalItemCount()) {
1043         auto curRow = curIndexInGroup / lanes_;
1044         if (isStackFromEnd_) {
1045             auto row = (itemTotalCount_ - 1) / lanes_;
1046             curRow = row - (itemTotalCount_ - 1 - curIndexInGroup) / lanes_;
1047         }
1048         // If the current focus is on the first row, pressing the UP key moves to the Header
1049         if (curRow == 0 && IsHasHeader()) {
1050             nextIndex = -1;
1051         } else if (curRow == 1 && nextIndex < 0) {
1052             // If it is the second row, pressing UP when nextIndex < 0 moves to the first item.
1053             nextIndex = 0;
1054         }
1055     }
1056 }
1057 
HandleCrossAxisRightOrDownStep(bool isVertical,int32_t curIndexInGroup,int32_t & moveStep,int32_t & nextIndex)1058 bool ListItemGroupPattern::HandleCrossAxisRightOrDownStep(
1059     bool isVertical, int32_t curIndexInGroup, int32_t& moveStep, int32_t& nextIndex)
1060 {
1061     // Only for DetermineMultiLaneStep
1062     moveStep = 1;
1063     nextIndex = curIndexInGroup + moveStep;
1064     auto col = curIndexInGroup % lanes_;
1065     // If the current focus is on the last column, pressing the RIGHT key will not move the focus further.
1066     if (isStackFromEnd_) {
1067         if ((itemTotalCount_ - 1 - curIndexInGroup) % lanes_ == 0) {
1068             nextIndex = curIndexInGroup;
1069         }
1070     } else if (col == lanes_ - 1 || curIndexInGroup == itemTotalCount_ - 1) {
1071         nextIndex = curIndexInGroup;
1072     }
1073     return true;
1074 }
1075 
HandleCrossAxisLeftOrUpStep(bool isVertical,int32_t curIndexInGroup,int32_t & moveStep,int32_t & nextIndex)1076 bool ListItemGroupPattern::HandleCrossAxisLeftOrUpStep(
1077     bool isVertical, int32_t curIndexInGroup, int32_t& moveStep, int32_t& nextIndex)
1078 {
1079     // Only for DetermineMultiLaneStep
1080     moveStep = -1;
1081     nextIndex = curIndexInGroup + moveStep;
1082     auto col = curIndexInGroup % lanes_;
1083     // If the current focus is in the first column, pressing the LEFT key will not move the focus further.
1084     if (isStackFromEnd_) {
1085         if ((itemTotalCount_ - lanes_ - curIndexInGroup) % lanes_ == 0) {
1086             nextIndex = curIndexInGroup;
1087         }
1088     } else if (col <= 0) {
1089         nextIndex = curIndexInGroup;
1090     }
1091     return true;
1092 }
1093 
DetermineMultiLaneStep(FocusStep step,bool isVertical,const RefPtr<FrameNode> & curFrame,int32_t curIndexInGroup,int32_t & moveStep,int32_t & nextIndex)1094 bool ListItemGroupPattern::DetermineMultiLaneStep(FocusStep step, bool isVertical, const RefPtr<FrameNode>& curFrame,
1095     int32_t curIndexInGroup, int32_t& moveStep, int32_t& nextIndex)
1096 {
1097     auto parentList = GetListFrameNode();
1098     CHECK_NULL_RETURN(parentList, false);
1099     auto listPattern = parentList->GetPattern<ListPattern>();
1100     CHECK_NULL_RETURN(listPattern, false);
1101     auto focusWrapMode = listPattern->GetFocusWrapMode();
1102     // Only for GetNextFocusNode
1103     CHECK_NULL_RETURN(curFrame, false);
1104     // ListItemGroup does not handle HOME/END, bubble it up to List for processing.
1105     if (step == FocusStep::UP_END || step == FocusStep::LEFT_END || step == FocusStep::DOWN_END ||
1106         step == FocusStep::RIGHT_END) {
1107         return false;
1108     } else if ((isVertical && (step == FocusStep::DOWN)) || (!isVertical && step == FocusStep::RIGHT)) {
1109         HandleForwardStep(curFrame, curIndexInGroup, moveStep, nextIndex);
1110     } else if ((isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::LEFT)) {
1111         HandleBackwardStep(curFrame, curIndexInGroup, moveStep, nextIndex);
1112     } else if ((isVertical && (step == FocusStep::RIGHT)) || (!isVertical && step == FocusStep::DOWN)) {
1113         if (focusWrapMode == FocusWrapMode::WRAP_WITH_ARROW) {
1114             moveStep = 1;
1115             nextIndex = curIndexInGroup + 1;
1116         } else if (!HandleCrossAxisRightOrDownStep(isVertical, curIndexInGroup, moveStep, nextIndex)) {
1117             return false;
1118         }
1119     } else if ((isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP)) {
1120         if (focusWrapMode == FocusWrapMode::WRAP_WITH_ARROW) {
1121             moveStep = -1;
1122             nextIndex = curIndexInGroup - 1;
1123         } else if (!HandleCrossAxisLeftOrUpStep(isVertical, curIndexInGroup, moveStep, nextIndex)) {
1124             return false;
1125         }
1126     } else if (step == FocusStep::TAB) {
1127         moveStep = 1;
1128         nextIndex = curIndexInGroup + 1;
1129     } else if (step == FocusStep::SHIFT_TAB) {
1130         moveStep = -1;
1131         nextIndex = curIndexInGroup - 1;
1132     }
1133     return true;
1134 }
1135 
DetermineSingleLaneStep(FocusStep step,bool isVertical,int32_t itemTotalCount_,int32_t & moveStep,int32_t & nextIndex)1136 bool ListItemGroupPattern::DetermineSingleLaneStep(
1137     FocusStep step, bool isVertical, int32_t itemTotalCount_, int32_t& moveStep, int32_t& nextIndex)
1138 {
1139     // Only for GetNextFocusNode
1140     // ListItemGroup does not handle HOME/END, bubble it up to List for processing.
1141     auto parentList = GetListFrameNode();
1142     CHECK_NULL_RETURN(parentList, false);
1143     auto listPattern = parentList->GetPattern<ListPattern>();
1144     CHECK_NULL_RETURN(listPattern, false);
1145     auto isDefault = listPattern->GetFocusWrapMode() == FocusWrapMode::DEFAULT;
1146     if (step == FocusStep::UP_END || step == FocusStep::LEFT_END || step == FocusStep::DOWN_END ||
1147         step == FocusStep::RIGHT_END) {
1148         return false;
1149     } else if (ListPattern::IsForwardStep(step, isVertical, isDefault)) {
1150         moveStep = 1;
1151         nextIndex += moveStep;
1152     } else if (ListPattern::IsBackwardStep(step, isVertical, isDefault)) {
1153         moveStep = -1;
1154         nextIndex += moveStep;
1155     } else if ((!isVertical && step == FocusStep::UP) || (!isVertical && step == FocusStep::DOWN)) {
1156         // If it is horizontal and there is only one row, pressing UP or DOWN keys returns nullptr,
1157         return false;
1158     }
1159     return true;
1160 }
1161 
GetNextFocusNode(FocusStep step,const WeakPtr<FocusHub> & currentFocusNode)1162 WeakPtr<FocusHub> ListItemGroupPattern::GetNextFocusNode(FocusStep step, const WeakPtr<FocusHub>& currentFocusNode)
1163 {
1164     // Retrieve information about the current focus node.
1165     auto curFocus = currentFocusNode.Upgrade();
1166     CHECK_NULL_RETURN(curFocus, nullptr);
1167     auto curFrame = curFocus->GetFrameNode();
1168     CHECK_NULL_RETURN(curFrame, nullptr);
1169     auto parentList = GetListFrameNode();
1170     CHECK_NULL_RETURN(parentList, nullptr);
1171     auto listPattern = parentList->GetPattern<ListPattern>();
1172     CHECK_NULL_RETURN(listPattern, nullptr);
1173     auto curPattern = curFrame->GetPattern();
1174     CHECK_NULL_RETURN(curPattern, nullptr);
1175 
1176     int32_t curIndexInGroup = -1; // The index of the current item within the group.
1177     if (!GetCurrentFocusIndices(curFrame, curPattern, curIndexInGroup)) {
1178         return nullptr;
1179     }
1180 
1181     auto listProperty = parentList->GetLayoutProperty<ListLayoutProperty>();
1182     CHECK_NULL_RETURN(listProperty, nullptr);
1183     auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
1184 
1185     AdjustFocusStepForRtl(step, isVertical);
1186 
1187     auto moveStep = 0;
1188     auto nextIndex = curIndexInGroup;
1189 
1190     if (lanes_ <= 1) {
1191         if (!DetermineSingleLaneStep(step, isVertical, itemTotalCount_, moveStep, nextIndex)) {
1192             return nullptr;
1193         }
1194     } else {
1195         if (!DetermineMultiLaneStep(step, isVertical, curFrame, curIndexInGroup, moveStep, nextIndex)) {
1196             return nullptr;
1197         }
1198     }
1199     int32_t curGroupIndexInList = GetIndexInList();
1200     return FindNextValidFocus(moveStep, curIndexInGroup, curGroupIndexInList, nextIndex, currentFocusNode, step);
1201 }
1202 
FindNextValidFocus(int32_t moveStep,int32_t curIndexInGroup,int32_t curGroupIndexInList,int32_t nextIndexInGroup,const WeakPtr<FocusHub> & currentFocusNode,FocusStep step)1203 WeakPtr<FocusHub> ListItemGroupPattern::FindNextValidFocus(int32_t moveStep, int32_t curIndexInGroup,
1204     int32_t curGroupIndexInList, int32_t nextIndexInGroup, const WeakPtr<FocusHub>& currentFocusNode, FocusStep step)
1205 {
1206     auto curFocus = currentFocusNode.Upgrade();
1207     CHECK_NULL_RETURN(curFocus, nullptr);
1208     auto parentList = GetListFrameNode();
1209     CHECK_NULL_RETURN(parentList, nullptr);
1210     auto listPattern = parentList->GetPattern<ListPattern>();
1211     CHECK_NULL_RETURN(listPattern, nullptr);
1212     auto listProperty = parentList->GetLayoutProperty<ListLayoutProperty>();
1213     CHECK_NULL_RETURN(listProperty, nullptr);
1214     auto isVertical = listProperty->GetListDirection().value_or(Axis::VERTICAL) == Axis::VERTICAL;
1215 
1216     ListItemGroupPara listItemGroupPara = { GetLanesInGroup(), GetEndIndexInGroup(), GetDisplayStartIndexInGroup(),
1217         GetDisplayEndIndexInGroup(), IsHasHeader(), IsHasFooter() };
1218     while (IsIndexInValidRange(nextIndexInGroup, itemTotalCount_)) {
1219         if (IsFocusMovementBlock(nextIndexInGroup, curIndexInGroup, itemTotalCount_)) {
1220             return currentFocusNode;
1221         }
1222         listPattern->LayoutItemInGroupForFocus(
1223             curGroupIndexInList, nextIndexInGroup, curIndexInGroup, listItemGroupPara, itemTotalCount_);
1224         auto nextFocusNode = GetChildFocusNodeByIndex(nextIndexInGroup);
1225         if (nextFocusNode.Upgrade()) {
1226             auto isDefault = listPattern->GetFocusWrapMode() == FocusWrapMode::DEFAULT;
1227             const ListItemGroupInfo* curPos = GetPosition(curIndexInGroup);
1228             const ListItemGroupInfo* nextPos = GetPosition(nextIndexInGroup);
1229             const bool isForward = (isVertical && step == FocusStep::RIGHT) || (!isVertical && step == FocusStep::DOWN);
1230             const bool isBackward = (isVertical && step == FocusStep::LEFT) || (!isVertical && step == FocusStep::UP);
1231             if ((isForward || isBackward) && NextPositionBlocksMove(curPos, nextPos, isVertical) && isDefault) {
1232                 return nullptr;
1233             }
1234             return nextFocusNode;
1235         }
1236         nextIndexInGroup += moveStep;
1237     }
1238     return nullptr;
1239 }
1240 
IsIndexInValidRange(int32_t nextIndex,int32_t itemTotalCount_)1241 bool ListItemGroupPattern::IsIndexInValidRange(int32_t nextIndex, int32_t itemTotalCount_)
1242 {
1243     const bool lowerBound = IsHasHeader() ? (nextIndex >= -1) : (nextIndex >= 0);
1244     const bool upperBound = IsHasFooter() ? (nextIndex <= itemTotalCount_) : (nextIndex < itemTotalCount_);
1245     return lowerBound && upperBound;
1246 }
1247 
IsFocusMovementBlock(int32_t nextIndex,int32_t curIndex,int32_t maxIndex) const1248 bool ListItemGroupPattern::IsFocusMovementBlock(int32_t nextIndex, int32_t curIndex, int32_t maxIndex) const
1249 {
1250     return (nextIndex == curIndex) && (curIndex != -1) && (curIndex != maxIndex);
1251 }
1252 
FindHeadOrTailChild(const RefPtr<FocusHub> & childFocus,FocusStep step,WeakPtr<FocusHub> & target)1253 bool ListItemGroupPattern::FindHeadOrTailChild(
1254     const RefPtr<FocusHub>& childFocus, FocusStep step, WeakPtr<FocusHub>& target)
1255 {
1256     CHECK_NULL_RETURN(childFocus, false);
1257     // Support moving focus to the first item of the List when pressing HOME
1258     // and to the last item of the List when pressing END.
1259     auto isHome = step == FocusStep::LEFT_END || step == FocusStep::UP_END;
1260     auto isEnd = step == FocusStep::RIGHT_END || step == FocusStep::DOWN_END;
1261     bool isFindTailOrHead = false;
1262     if (isHome) {
1263         isFindTailOrHead = childFocus->AnyChildFocusHub([&target](const RefPtr<FocusHub>& node) {
1264             auto headNode = node->GetHeadOrTailChild(true);
1265             if (headNode) {
1266                 target = headNode;
1267                 return true;
1268             }
1269             return false;
1270         });
1271     } else if (isEnd) {
1272         isFindTailOrHead = childFocus->AnyChildFocusHub(
1273             [&target](const RefPtr<FocusHub>& node) {
1274                 auto tailNode = node->GetHeadOrTailChild(false);
1275                 if (tailNode) {
1276                     target = tailNode;
1277                     return true;
1278                 }
1279                 return false;
1280             },
1281             true);
1282     }
1283     return isFindTailOrHead;
1284 }
IsInViewport(int32_t index) const1285 bool ListItemGroupPattern::IsInViewport(int32_t index) const
1286 {
1287     if (itemDisplayStartIndex_ == itemDisplayEndIndex_ && itemDisplayStartIndex_ == 0) {
1288         auto host = GetHost();
1289         CHECK_NULL_RETURN(host, false);
1290         auto geometryNode = host->GetGeometryNode();
1291         CHECK_NULL_RETURN(geometryNode, false);
1292         auto rect = geometryNode->GetPaddingRect();
1293         auto footerOffset = rect.Height() + rect.GetY() - footerMainSize_;
1294         if (LessNotEqual(footerOffset, 0.0f)) {
1295             return false;
1296         }
1297     }
1298     return index >= itemDisplayStartIndex_ && index <= itemDisplayEndIndex_;
1299 }
1300 
MappingPropertiesFromLayoutAlgorithm(const RefPtr<ListItemGroupLayoutAlgorithm> & layoutAlgorithm)1301 void ListItemGroupPattern::MappingPropertiesFromLayoutAlgorithm(
1302     const RefPtr<ListItemGroupLayoutAlgorithm>& layoutAlgorithm)
1303 {
1304     CHECK_NULL_VOID(layoutAlgorithm);
1305     if (lanes_ != layoutAlgorithm->GetLanes()) {
1306         lanes_ = layoutAlgorithm->GetLanes();
1307         ClearCachedItemPosition();
1308     }
1309     itemPosition_ = layoutAlgorithm->GetItemPosition();
1310     cachedItemPosition_ = layoutAlgorithm->GetCachedItemPosition();
1311     spaceWidth_ = layoutAlgorithm->GetSpaceWidth();
1312     axis_ = layoutAlgorithm->GetAxis();
1313     layoutDirection_ = layoutAlgorithm->GetLayoutDirection();
1314     mainSize_ = layoutAlgorithm->GetMainSize();
1315     laneGutter_ = layoutAlgorithm->GetLaneGutter();
1316     bool indexChanged = false;
1317     indexChanged = itemDisplayEndIndex_ != layoutAlgorithm->GetEndIndex() ||
1318                    itemDisplayStartIndex_ != layoutAlgorithm->GetStartIndex();
1319     itemDisplayEndIndex_ = layoutAlgorithm->GetEndIndex();
1320     itemDisplayStartIndex_ = layoutAlgorithm->GetStartIndex();
1321     headerMainSize_ = layoutAlgorithm->GetHeaderMainSize();
1322     footerMainSize_ = layoutAlgorithm->GetFooterMainSize();
1323     layoutedItemInfo_ = layoutAlgorithm->GetLayoutedItemInfo();
1324     startHeaderPos_ = layoutAlgorithm->GetStartHeaderPos();
1325     endFooterPos_ = layoutAlgorithm->GetEndFooterPos();
1326     adjustRefPos_ = layoutAlgorithm->GetAdjustReferenceDelta();
1327     adjustTotalSize_ = layoutAlgorithm->GetAdjustTotalSize();
1328     listContentSize_ = layoutAlgorithm->GetListContentSize();
1329     prevMeasureBreak_ = layoutAlgorithm->GroupMeasureInNextFrame();
1330     layouted_ = true;
1331     if (indexChanged) {
1332         auto parentList = GetListFrameNode();
1333         CHECK_NULL_VOID(parentList);
1334         auto listPattern = parentList->GetPattern<ListPattern>();
1335         CHECK_NULL_VOID(listPattern);
1336         if (!(itemDisplayStartIndex_ == itemDisplayEndIndex_ && itemDisplayStartIndex_ == 0)) {
1337             listPattern->FireFocusInListItemGroup(GetIndexInList());
1338         }
1339     }
1340 }
1341 } // namespace OHOS::Ace::NG
1342