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