1 /*
2 * Copyright (c) 2022-2025 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/grid/grid_scroll/grid_scroll_layout_algorithm.h"
17
18 #include "base/log/event_report.h"
19 #include "base/log/log_wrapper.h"
20 #include "base/utils/feature_param.h"
21 #include "core/components_ng/pattern/grid/grid_utils.h"
22 #include "core/components_ng/pattern/grid/irregular/grid_layout_utils.h"
23 #include "core/components_ng/pattern/scrollable/scrollable_utils.h"
24 #include "core/components_ng/pattern/text_field/text_field_manager.h"
25 #include "core/components_ng/property/templates_parser.h"
26
27 namespace OHOS::Ace::NG {
28 namespace {
AddCacheItemsInFront(int32_t startIdx,LayoutWrapper * host,int32_t cacheCnt,std::list<GridPreloadItem> & buildList)29 void AddCacheItemsInFront(
30 int32_t startIdx, LayoutWrapper* host, int32_t cacheCnt, std::list<GridPreloadItem>& buildList)
31 {
32 for (int32_t i = 1; i <= cacheCnt; ++i) {
33 int32_t item = startIdx - i;
34 if (item < 0) {
35 break;
36 }
37 if (!host->GetChildByIndex(item, true)) {
38 buildList.emplace_back(item, true);
39 }
40 }
41 }
42 } // namespace
43
Measure(LayoutWrapper * layoutWrapper)44 void GridScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
45 {
46 wrapper_ = layoutWrapper;
47 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
48 CHECK_NULL_VOID(gridLayoutProperty);
49
50 // Pre-recycle
51 ScrollableUtils::RecycleItemsOutOfBoundary(
52 info_.axis_, info_.currentOffset_ - info_.prevOffset_, info_.startIndex_, info_.endIndex_, layoutWrapper);
53
54 // Step1: Decide size of Grid
55 Axis axis = info_.axis_;
56 frameSize_ = CreateIdealSize(
57 gridLayoutProperty->GetLayoutConstraint().value(), axis, gridLayoutProperty->GetMeasureType(), true);
58 auto layoutPolicy = gridLayoutProperty->GetLayoutPolicyProperty();
59 auto isMainWrap = false;
60 if (layoutPolicy.has_value()) {
61 auto isVertical = axis == Axis::VERTICAL;
62 auto widthLayoutPolicy = layoutPolicy.value().widthLayoutPolicy_.value_or(LayoutCalPolicy::NO_MATCH);
63 auto heightLayoutPolicy = layoutPolicy.value().heightLayoutPolicy_.value_or(LayoutCalPolicy::NO_MATCH);
64 auto isMainFix = (isVertical ? heightLayoutPolicy : widthLayoutPolicy) == LayoutCalPolicy::FIX_AT_IDEAL_SIZE;
65 isMainWrap = (isVertical ? heightLayoutPolicy : widthLayoutPolicy) == LayoutCalPolicy::WRAP_CONTENT;
66 if (isMainFix) {
67 frameSize_.SetMainSize(Infinity<float>(), axis);
68 }
69 auto layoutPolicySize = ConstrainIdealSizeByLayoutPolicy(
70 gridLayoutProperty->GetLayoutConstraint().value(), widthLayoutPolicy, heightLayoutPolicy, axis);
71 frameSize_.UpdateIllegalSizeWithCheck(layoutPolicySize.ConvertToSizeT());
72 }
73 if (NearZero(GetMainAxisSize(frameSize_, axis))) {
74 TAG_LOGW(AceLogTag::ACE_GRID, "size of main axis value is 0, please check");
75 return;
76 }
77 bool matchChildren = GreaterOrEqualToInfinity(GetMainAxisSize(frameSize_, axis)) || isMainWrap;
78 syncLoad_ = gridLayoutProperty->GetSyncLoad().value_or(!FeatureParam::IsSyncLoadEnabled()) || matchChildren ||
79 info_.targetIndex_.has_value() || !NearEqual(info_.currentOffset_, info_.prevOffset_);
80 layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize_);
81 MinusPaddingToSize(gridLayoutProperty->CreatePaddingAndBorder(), frameSize_);
82 info_.contentEndPadding_ = ScrollableUtils::CheckHeightExpansion(gridLayoutProperty, axis);
83 frameSize_.AddHeight(info_.contentEndPadding_);
84 auto&& safeAreaOpts = gridLayoutProperty->GetSafeAreaExpandOpts();
85 expandSafeArea_ = safeAreaOpts && safeAreaOpts->Expansive();
86 auto host = layoutWrapper->GetHostNode();
87 if (host) {
88 auto pipeline = host->GetContext();
89 if (pipeline && pipeline->GetPixelRoundMode() == PixelRoundMode::PIXEL_ROUND_AFTER_MEASURE) {
90 frameSize_.SetWidth(round(frameSize_.Width()));
91 frameSize_.SetHeight(round(frameSize_.Height()));
92 }
93 }
94
95 InitialItemsCrossSize(gridLayoutProperty, frameSize_, info_.GetChildrenCount());
96
97 // Step2: Measure children that can be displayed in viewport of Grid
98 float mainSize = GetMainAxisSize(frameSize_, axis);
99 float crossSize = GetCrossAxisSize(frameSize_, axis);
100 if (!NearEqual(mainSize, info_.lastMainSize_)) {
101 UpdateOffsetOnVirtualKeyboardHeightChange(layoutWrapper, mainSize);
102 UpdateOffsetOnHeightChangeDuringAnimation(layoutWrapper, mainSize);
103 info_.ResetPositionFlags();
104 }
105 FillGridViewportAndMeasureChildren(mainSize, crossSize, layoutWrapper);
106
107 if (gridLayoutProperty->GetAlignItems().value_or(GridItemAlignment::DEFAULT) == GridItemAlignment::STRETCH) {
108 GridLayoutBaseAlgorithm::AdjustChildrenHeight(layoutWrapper);
109 }
110 // update layout info.
111 info_.prevOffset_ = info_.currentOffset_;
112 // update cache info.
113 const int32_t cacheCnt =
114 static_cast<int32_t>(gridLayoutProperty->GetCachedCountValue(info_.defCachedCount_) * crossCount_);
115 layoutWrapper->SetCacheCount(cacheCnt);
116
117 info_.lastMainSize_ = mainSize;
118 info_.lastCrossSize_ = crossSize;
119 AdaptToChildMainSize(layoutWrapper, gridLayoutProperty, mainSize, frameSize_, matchChildren);
120
121 // reset offsetEnd after scroll to moveToEndLineIndex_
122 info_.offsetEnd_ = moveToEndLineIndex_ > 0 ? (info_.endIndex_ + 1 >= info_.GetChildrenCount()) : info_.offsetEnd_;
123
124 if (SystemProperties::GetGridCacheEnabled()) {
125 if (measureInNextFrame_) {
126 return;
127 }
128 const bool sync = gridLayoutProperty->GetShowCachedItemsValue(false);
129 if (sync) {
130 SyncPreload(
131 layoutWrapper, gridLayoutProperty->GetCachedCountValue(info_.defCachedCount_), crossSize, mainSize);
132 return;
133 }
134
135 FillCacheLineAtEnd(mainSize, crossSize, layoutWrapper);
136 AddCacheItemsInFront(info_.startIndex_, layoutWrapper, cacheCnt, predictBuildList_);
137 if (!predictBuildList_.empty()) {
138 PreloadItems(layoutWrapper);
139 predictBuildList_.clear();
140 }
141 }
142 }
143
PreloadItems(LayoutWrapper * layoutWrapper)144 void GridScrollLayoutAlgorithm::PreloadItems(LayoutWrapper* layoutWrapper)
145 {
146 GridLayoutUtils::PreloadGridItems(layoutWrapper->GetHostNode()->GetPattern<GridPattern>(),
147 std::move(predictBuildList_),
148 [param = GridPredictLayoutParam { cachedChildConstraint_, itemsCrossSize_, crossGap_ }](
149 const RefPtr<FrameNode>& host, int32_t itemIdx) {
150 CHECK_NULL_RETURN(host, false);
151 return PredictBuildItem(*host, itemIdx, param);
152 });
153 }
154
UpdateOffsetOnVirtualKeyboardHeightChange(LayoutWrapper * layoutWrapper,float mainSize)155 void GridScrollLayoutAlgorithm::UpdateOffsetOnVirtualKeyboardHeightChange(LayoutWrapper* layoutWrapper, float mainSize)
156 {
157 if (GreatOrEqual(mainSize, info_.lastMainSize_)) {
158 return;
159 }
160 // only need to offset vertical grid
161 if (info_.axis_ != Axis::VERTICAL) {
162 return;
163 }
164
165 auto grid = layoutWrapper->GetHostNode();
166 CHECK_NULL_VOID(grid);
167 auto focusHub = grid->GetFocusHub();
168 CHECK_NULL_VOID(focusHub);
169 // textField not in Grid
170 if (!focusHub->IsCurrentFocus()) {
171 return;
172 }
173
174 auto context = grid->GetContext();
175 CHECK_NULL_VOID(context);
176 auto textFieldManager = AceType::DynamicCast<TextFieldManagerNG>(context->GetTextFieldManager());
177 CHECK_NULL_VOID(textFieldManager);
178 // only when textField is onFocus
179 auto focused = textFieldManager->GetOnFocusTextField().Upgrade();
180 CHECK_NULL_VOID(focused);
181 auto position = textFieldManager->GetClickPosition().GetY();
182 auto gridOffset = grid->GetTransformRelativeOffset();
183 auto offset = mainSize + gridOffset.GetY() - position;
184 if (LessOrEqual(offset, 0.0)) {
185 // negative offset to scroll down
186 auto lineHeight = info_.GetAverageLineHeight();
187 if (GreatNotEqual(lineHeight, 0)) {
188 offset = floor(offset / lineHeight) * lineHeight;
189 }
190 info_.currentOffset_ += offset;
191 TAG_LOGI(AceLogTag::ACE_GRID, "update offset on virtual keyboard height change, %{public}f", offset);
192 }
193 }
194
AdaptToChildMainSize(LayoutWrapper * layoutWrapper,RefPtr<GridLayoutProperty> & gridLayoutProperty,float mainSize,SizeF idealSize,bool matchChildren)195 void GridScrollLayoutAlgorithm::AdaptToChildMainSize(LayoutWrapper* layoutWrapper,
196 RefPtr<GridLayoutProperty>& gridLayoutProperty, float mainSize, SizeF idealSize, bool matchChildren)
197 {
198 if (!matchChildren) {
199 // grid with columnsTemplate/rowsTemplate and maxCount
200 if (!gridLayoutProperty->HasMaxCount()) {
201 return;
202 }
203 std::optional<CalcLength> mainAxisIdealSize;
204 const auto& selfLayoutConstraint = gridLayoutProperty->GetCalcLayoutConstraint();
205 if (selfLayoutConstraint && selfLayoutConstraint->selfIdealSize.has_value()) {
206 mainAxisIdealSize = axis_ == Axis::HORIZONTAL ? selfLayoutConstraint->selfIdealSize->Width()
207 : selfLayoutConstraint->selfIdealSize->Height();
208 }
209
210 if (mainAxisIdealSize.has_value()) {
211 return;
212 }
213 }
214
215 auto lengthOfItemsInViewport = info_.GetTotalHeightOfItemsInView(mainGap_);
216 auto gridMainSize = std::min(lengthOfItemsInViewport, mainSize);
217 gridMainSize = std::max(gridMainSize, GetMainAxisSize(gridLayoutProperty->GetLayoutConstraint()->minSize, axis_));
218 idealSize.SetMainSize(gridMainSize, axis_);
219 AddPaddingToSize(gridLayoutProperty->CreatePaddingAndBorder(), idealSize);
220 layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize);
221 info_.lastMainSize_ = gridMainSize;
222 TAG_LOGI(AceLogTag::ACE_GRID, "gridMainSize:%{public}f", gridMainSize);
223 }
224
UpdateOffsetOnHeightChangeDuringAnimation(LayoutWrapper * layoutWrapper,float mainSize)225 void GridScrollLayoutAlgorithm::UpdateOffsetOnHeightChangeDuringAnimation(LayoutWrapper* layoutWrapper, float mainSize)
226 {
227 // If only the height of the Grid is changed, keep the prevOffset_ and currentOffset_ equal.
228 ResetOffsetWhenHeightChanged();
229 auto host = layoutWrapper->GetHostNode();
230 CHECK_NULL_VOID(host);
231 auto pattern = host->GetPattern<GridPattern>();
232 CHECK_NULL_VOID(pattern);
233 if (pattern->IsScrollableSpringMotionRunning()) {
234 if (info_.reachStart_) {
235 return;
236 }
237 float totalHeight = info_.GetContentHeight(mainGap_);
238 if (info_.lastMainSize_ < totalHeight) {
239 info_.currentOffset_ += (mainSize - info_.lastMainSize_);
240 return;
241 }
242 if (info_.lastMainSize_ >= totalHeight && mainSize < info_.lastMainSize_ && mainSize <= totalHeight) {
243 info_.currentOffset_ += mainSize - totalHeight;
244 }
245 }
246 }
247
Layout(LayoutWrapper * layoutWrapper)248 void GridScrollLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
249 {
250 auto props = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
251 CHECK_NULL_VOID(props);
252 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
253 auto padding = layoutWrapper->GetLayoutProperty()->CreatePaddingAndBorder();
254 MinusPaddingToSize(padding, size);
255 childFrameOffset_ = OffsetF(0.0f, padding.top.value_or(0.0f));
256 childFrameOffset_ += OffsetF(0.0f, info_.currentOffset_, axis_);
257 bool isRtl = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection() == TextDirection::RTL;
258 int32_t startIndex = -1;
259 int32_t endIndex = -1;
260 if (info_.hasMultiLineItem_) {
261 layoutWrapper->RemoveAllChildInRenderTree();
262 }
263 LargeItemForwardLineHeight(info_.startMainLineIndex_, layoutWrapper);
264 const int32_t cacheCount = props->GetCachedCountValue(info_.defCachedCount_);
265 if (!props->HasCachedCount()) {
266 info_.UpdateDefaultCachedCount();
267 }
268 int32_t cacheStart = 0;
269 int32_t cacheEnd = 0; // number of cache items at tail
270 const bool showCached = props->GetShowCachedItemsValue(false);
271
272 const int32_t start = info_.startMainLineIndex_ - cacheCount;
273 const int32_t end = info_.endMainLineIndex_ + cacheCount;
274 float mainPos = -info_.GetHeightInRange(start, info_.startMainLineIndex_, mainGap_);
275 for (auto i = start; i <= end; ++i) {
276 const bool inRange = i >= info_.startMainLineIndex_ && i <= info_.endMainLineIndex_;
277 const bool isCache = !showCached && !inRange;
278 const auto& line = info_.gridMatrix_.find(i);
279 if (line == info_.gridMatrix_.end()) {
280 continue;
281 }
282
283 auto prevLineOffset = OffsetF(0.0f, mainPos, axis_);
284 if (line->second.empty()) {
285 TAG_LOGW(AceLogTag::ACE_GRID, "line %{public}d should not be empty, please check.", line->first);
286 continue;
287 }
288 int32_t itemIdex = -1;
289 float lineHeight = info_.lineHeightMap_[line->first];
290 Alignment align = axis_ == Axis::VERTICAL ? Alignment::TOP_CENTER : Alignment::CENTER_LEFT;
291 if (props->GetPositionProperty()) {
292 align = props->GetPositionProperty()->GetAlignment().value_or(align);
293 }
294 for (auto iter = line->second.begin(); iter != line->second.end(); ++iter) {
295 // If item index is the same, must be the same GridItem, need't layout again.
296 if (itemIdex == iter->second) {
297 continue;
298 }
299 itemIdex = iter->second;
300 auto crossIter = itemsCrossPosition_.find(itemIdex);
301 if (crossIter == itemsCrossPosition_.end()) {
302 crossIter = itemsCrossPosition_.emplace(itemIdex, ComputeItemCrossPosition(iter->first)).first;
303 }
304 auto crossOffset = crossIter->second;
305 auto offset = childFrameOffset_ + prevLineOffset;
306 offset = CalculateLargeItemOffset(offset, itemIdex, i, iter->first);
307 if (axis_ == Axis::VERTICAL) {
308 offset.SetX(crossOffset);
309 } else {
310 offset.SetY(crossOffset);
311 }
312 auto wrapper = isCache ? layoutWrapper->GetChildByIndex(itemIdex, true)
313 : layoutWrapper->GetOrCreateChildByIndex(itemIdex);
314 if (!wrapper) {
315 continue;
316 }
317 if (inRange) {
318 startIndex = startIndex == -1 ? itemIdex : std::min(startIndex, itemIdex);
319 endIndex = std::max(itemIdex, endIndex);
320 }
321 auto frSize = itemsCrossSize_.find(iter->first);
322 if (frSize == itemsCrossSize_.end()) {
323 continue;
324 }
325 if (i < info_.startMainLineIndex_) {
326 ++cacheStart;
327 } else if (i > info_.endMainLineIndex_) {
328 ++cacheEnd;
329 }
330 SizeF blockSize = SizeF(frSize->second, lineHeight, axis_);
331 auto translate = OffsetF(0.0f, 0.0f);
332 auto childSize = wrapper->GetGeometryNode()->GetMarginFrameSize();
333 translate = Alignment::GetAlignPosition(blockSize, childSize, align);
334
335 if (isRtl) {
336 offset.SetX(size.Width() - offset.GetX() - childSize.Width());
337 }
338 offset += OffsetF(padding.left.value_or(0.0f), 0.0f);
339 wrapper->GetGeometryNode()->SetMarginFrameOffset(offset + translate);
340 const bool forceLayout = info_.hasMultiLineItem_ || expandSafeArea_ ||
341 wrapper->CheckNeedForceMeasureAndLayout() || wrapper->IsIgnoreOptsValid();
342 if (!isCache && forceLayout) {
343 wrapper->Layout();
344 } else {
345 SyncGeometry(wrapper);
346 }
347 measuredItems_.erase(itemIdex);
348 auto frameNode = DynamicCast<FrameNode>(wrapper);
349 if (frameNode) {
350 frameNode->MarkAndCheckNewOpIncNode(axis_);
351 }
352 auto gridItemProp = DynamicCast<GridItemLayoutProperty>(wrapper->GetLayoutProperty());
353 CHECK_NULL_CONTINUE(gridItemProp);
354 gridItemProp->UpdateIndex(itemIdex);
355 gridItemProp->UpdateMainIndex(line->first);
356 gridItemProp->UpdateCrossIndex(iter->first);
357 UpdateRealGridItemPositionInfo(wrapper, line->first, iter->first);
358 }
359 mainPos += info_.lineHeightMap_[line->first] + mainGap_;
360 }
361 info_.totalHeightOfItemsInView_ = info_.GetTotalHeightOfItemsInView(mainGap_);
362
363 if (startIndex == -1 && endIndex == -1) {
364 startIndex = endIndex = info_.GetChildrenCount();
365 }
366 ClearUnlayoutedItems(layoutWrapper);
367 if (!info_.hasMultiLineItem_) {
368 if (!showCached || !info_.reachEnd_) {
369 auto cache = CalculateCachedCount(layoutWrapper, cacheCount);
370 cacheStart = cache.first;
371 cacheEnd = cache.second; // only use counting method when last line not completely filled
372 }
373 LostChildFocusToSelf(layoutWrapper, startIndex - cacheStart, endIndex + cacheEnd);
374 layoutWrapper->SetActiveChildRange(startIndex, endIndex, cacheStart, cacheEnd, showCached);
375 info_.times_ = (info_.times_ + 1) % GRID_CHECK_INTERVAL;
376 if (info_.times_ == 0) {
377 info_.CheckGridMatrix(cacheCount);
378 }
379 }
380 UpdateOverlay(layoutWrapper);
381 }
382
ClearUnlayoutedItems(LayoutWrapper * layoutWrapper)383 void GridScrollLayoutAlgorithm::ClearUnlayoutedItems(LayoutWrapper* layoutWrapper)
384 {
385 for (int32_t itemIndex : measuredItems_) {
386 auto wrapper = layoutWrapper->GetChildByIndex(itemIndex);
387 if (!wrapper) {
388 continue;
389 }
390
391 auto frameNode = AceType::DynamicCast<FrameNode>(wrapper);
392 if (frameNode) {
393 frameNode->ClearSubtreeLayoutAlgorithm();
394 }
395 }
396 }
397
SyncGeometry(RefPtr<LayoutWrapper> & wrapper)398 void GridScrollLayoutAlgorithm::SyncGeometry(RefPtr<LayoutWrapper>& wrapper)
399 {
400 CHECK_NULL_VOID(wrapper);
401 auto host = wrapper->GetHostNode();
402 CHECK_NULL_VOID(host);
403 host->ForceSyncGeometryNode();
404 }
405
InitialItemsCrossSize(const RefPtr<GridLayoutProperty> & layoutProperty,const SizeF & frameSize,int32_t childrenCount)406 void GridScrollLayoutAlgorithm::InitialItemsCrossSize(
407 const RefPtr<GridLayoutProperty>& layoutProperty, const SizeF& frameSize, int32_t childrenCount)
408 {
409 itemsCrossSize_.clear();
410 auto rowsTemplate = layoutProperty->GetRowsTemplate().value_or("");
411 auto columnsTemplate = layoutProperty->GetColumnsTemplate().value_or("");
412 axis_ = columnsTemplate.empty() ? Axis::HORIZONTAL : Axis::VERTICAL;
413 auto scale = layoutProperty->GetLayoutConstraint()->scaleProperty;
414 auto rowsGap = ConvertToPx(layoutProperty->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0);
415 auto columnsGap =
416 ConvertToPx(layoutProperty->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0);
417 mainGap_ = axis_ == Axis::HORIZONTAL ? columnsGap : rowsGap;
418 crossGap_ = axis_ == Axis::VERTICAL ? columnsGap : rowsGap;
419 auto padding = layoutProperty->CreatePaddingAndBorder();
420 crossPaddingOffset_ = axis_ == Axis::HORIZONTAL ? padding.top.value_or(0) : 0.0f;
421
422 auto crossSize = frameSize.CrossSize(axis_);
423 std::vector<double> crossLens;
424 std::pair<std::vector<double>, double> cross;
425 if (!rowsTemplate.empty()) {
426 cross = ParseTemplateArgs(GridUtils::ParseArgs(rowsTemplate), crossSize, crossGap_, childrenCount);
427 } else {
428 cross = ParseTemplateArgs(GridUtils::ParseArgs(columnsTemplate), crossSize, crossGap_, childrenCount);
429 }
430 crossLens = cross.first;
431 crossGap_ = cross.second;
432
433 if (crossLens.empty()) {
434 crossLens.push_back(crossSize);
435 }
436
437 if (crossCount_ != crossLens.size()) {
438 crossCount_ = crossLens.size();
439 info_.crossCount_ = static_cast<int32_t>(crossCount_);
440 }
441
442 int32_t index = 0;
443 for (const auto& len : crossLens) {
444 itemsCrossSize_.try_emplace(index, len);
445 ++index;
446 }
447 }
448
FillGridViewportAndMeasureChildren(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)449 void GridScrollLayoutAlgorithm::FillGridViewportAndMeasureChildren(
450 float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
451 {
452 auto host = layoutWrapper->GetHostNode();
453 CHECK_NULL_VOID(host);
454 auto gridPattern = host->GetPattern<GridPattern>();
455 CHECK_NULL_VOID(gridPattern);
456 itemsCrossPosition_.clear();
457 UpdateGridLayoutInfo(layoutWrapper, mainSize);
458 if (info_.targetIndex_.has_value()) {
459 // Complete the gridLayoutInfo to get a complete set of data from 0 to targetIndex for the GridView. Make sure
460 // that the index of the matrix_ and heightMap_ is incremented from 0 to targetIndex and sequentially
461 SupplyAllData2ZeroIndex(mainSize, crossSize, layoutWrapper);
462 }
463 if (enableSkipping_) {
464 SkipLargeOffset(mainSize, layoutWrapper);
465 }
466
467 if (!info_.lastCrossCount_) {
468 info_.lastCrossCount_ = crossCount_;
469 reason_ = GridReloadReason::INIT;
470 }
471
472 CheckReset(mainSize, crossSize, layoutWrapper);
473
474 UpdateCurrentOffsetForJumpTo(mainSize);
475 info_.jumpIndex_ = EMPTY_JUMP_INDEX;
476 info_.scrollAlign_ = ScrollAlign::AUTO;
477
478 // Step1: Measure [GridItem] that has been recorded to [gridMatrix_]
479 float mainLength = MeasureRecordedItems(mainSize, crossSize, layoutWrapper);
480
481 // Step2: When done measure items in record, request new items to fill blank at end
482 FillBlankAtEnd(mainSize, crossSize, layoutWrapper, mainLength);
483 if (info_.reachEnd_) { // If it reaches end when [FillBlankAtEnd], modify [currentOffset_]
484 ModifyCurrentOffsetWhenReachEnd(mainSize, layoutWrapper);
485 }
486
487 // Step3: Check if need to fill blank at start (in situation of grid items moving down)
488 auto haveNewLineAtStart = FillBlankAtStart(mainSize, crossSize, layoutWrapper);
489 if (info_.reachStart_) {
490 auto offset = info_.currentOffset_;
491 if ((NonNegative(offset) && !canOverScrollStart_) || (NonPositive(offset) && !canOverScrollEnd_)) {
492 info_.currentOffset_ = 0.0;
493 info_.prevOffset_ = 0.0;
494 }
495 if (!haveNewLineAtStart) {
496 if (canOverScrollStart_) {
497 info_.UpdateEndIndex(offset, mainSize, mainGap_);
498 }
499 layoutWrapper->GetHostNode()->ChildrenUpdatedFrom(-1);
500 return;
501 }
502 // we need lastline if blank at start is not fully filled when start line is shorter
503 mainLength -= offset;
504 currentMainLineIndex_ = info_.endMainLineIndex_;
505 if (UseCurrentLines(mainSize, crossSize, layoutWrapper, mainLength)) {
506 FillBlankAtEnd(mainSize, crossSize, layoutWrapper, mainLength);
507 if (info_.reachEnd_) {
508 ModifyCurrentOffsetWhenReachEnd(mainSize, layoutWrapper);
509 }
510 }
511 } else {
512 info_.UpdateEndIndex(info_.currentOffset_, mainSize, mainGap_);
513 }
514 layoutWrapper->GetHostNode()->ChildrenUpdatedFrom(-1);
515 if (info_.targetIndex_.has_value()) {
516 info_.targetIndex_.reset();
517 } else {
518 if (info_.extraOffset_.has_value()) {
519 info_.UpdateStartIndexForExtralOffset(mainGap_, mainSize);
520 ACE_SCOPED_TRACE(
521 "UpdateStartIndexForExtralOffset startIndex:%d, endIndex:%d, currentOffset:%f, mainSize:%f, mainGap:%f",
522 info_.startIndex_, info_.endIndex_, info_.currentOffset_, mainSize, mainGap_);
523 }
524 }
525 }
526
ReloadToStartIndex(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)527 void GridScrollLayoutAlgorithm::ReloadToStartIndex(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
528 {
529 const int32_t currentItemIndex = info_.startIndex_;
530 // adjust startMainLine based on the new cross count
531 UpdateMainLineOnReload(currentItemIndex);
532 auto firstItem = GetStartingItem(layoutWrapper, currentItemIndex);
533 info_.startIndex_ = firstItem;
534 currentMainLineIndex_ = (firstItem == 0 ? 0 : info_.startMainLineIndex_) - 1;
535 info_.endIndex_ = firstItem - 1;
536 TAG_LOGI(AceLogTag::ACE_GRID,
537 "data reload begin, firstItem:%{public}d, currentItemIndex:%{public}d, reason:%{public}s", firstItem,
538 currentItemIndex, GetReloadReasonStr(reason_).c_str());
539 while (info_.endIndex_ < currentItemIndex) {
540 auto lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
541 if (LessNotEqual(lineHeight, 0.0)) {
542 info_.reachEnd_ = true;
543 break;
544 }
545 if (measureInNextFrame_) {
546 break;
547 }
548 }
549 info_.startMainLineIndex_ = currentMainLineIndex_;
550 info_.UpdateStartIndexByStartLine();
551 // FillNewLineBackward sometimes make startIndex_ > currentItemIndex
552 while (info_.startIndex_ > currentItemIndex &&
553 info_.gridMatrix_.find(info_.startMainLineIndex_) != info_.gridMatrix_.end()) {
554 info_.startMainLineIndex_--;
555 info_.UpdateStartIndexByStartLine();
556 }
557 TAG_LOGI(AceLogTag::ACE_GRID, "data reload end, startIndex_:%{public}d, startMainLineIndex_:%{public}d",
558 info_.startIndex_, info_.startMainLineIndex_);
559 reason_ = GridReloadReason::DATA_RELOAD;
560 }
561
ReloadFromUpdateIdxToStartIndex(float mainSize,float crossSize,int32_t updateLineIndex,LayoutWrapper * layoutWrapper)562 void GridScrollLayoutAlgorithm::ReloadFromUpdateIdxToStartIndex(
563 float mainSize, float crossSize, int32_t updateLineIndex, LayoutWrapper* layoutWrapper)
564 {
565 const int32_t currentItemIndex = info_.startIndex_;
566 auto firstItem = layoutWrapper->GetHostNode()->GetChildrenUpdated();
567 info_.startIndex_ = firstItem;
568 // first "-1" means trying to fill from last line;second "-1" because it will fill next line in FillNewLineBackward
569 currentMainLineIndex_ = std::max(updateLineIndex - 1, 0) - 1;
570 info_.endIndex_ = firstItem - 1;
571
572 while (info_.endIndex_ < currentItemIndex) {
573 auto lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
574 if (LessNotEqual(lineHeight, 0.0)) {
575 info_.reachEnd_ = true;
576 break;
577 }
578 }
579 info_.startMainLineIndex_ = currentMainLineIndex_;
580 info_.UpdateStartIndexByStartLine();
581 // FillNewLineBackward sometimes make startIndex_ > currentItemIndex
582 while (info_.startIndex_ > currentItemIndex &&
583 info_.gridMatrix_.find(info_.startMainLineIndex_) != info_.gridMatrix_.end()) {
584 info_.startMainLineIndex_--;
585 info_.UpdateStartIndexByStartLine();
586 }
587 TAG_LOGI(AceLogTag::ACE_GRID, "data reload end, startIndex_:%{public}d, startMainLineIndex_:%{public}d",
588 info_.startIndex_, info_.startMainLineIndex_);
589 }
590
FillBlankAtStart(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)591 bool GridScrollLayoutAlgorithm::FillBlankAtStart(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
592 {
593 bool fillNewLine = false;
594 // If [currentOffset_] is non-positive, it means no blank at start
595 if (LessOrEqual(info_.currentOffset_, 0.0)) {
596 return fillNewLine;
597 }
598 auto blankAtStart = info_.currentOffset_;
599 while (GreatNotEqual(blankAtStart, 0.0) || info_.startIndex_ > info_.GetChildrenCount() - 1) {
600 float lineHeight = FillNewLineForward(crossSize, mainSize, layoutWrapper);
601 if (GreatOrEqual(lineHeight, 0.0)) {
602 info_.lineHeightMap_[info_.startMainLineIndex_] = lineHeight;
603 blankAtStart -= (lineHeight + mainGap_);
604 fillNewLine = true;
605 if (measureInNextFrame_) {
606 break;
607 }
608 continue;
609 }
610 info_.reachStart_ = true;
611 break;
612 }
613
614 FillOneLineForwardWithoutUpdatingStartIndex(crossSize, mainSize, layoutWrapper);
615
616 info_.currentOffset_ = blankAtStart;
617 info_.prevOffset_ = info_.currentOffset_;
618 return fillNewLine;
619 }
620
621 // If there is a multi-line item in the current line and its starting line is not within this line,
622 // it may result in an incomplete layout.
FillOneLineForwardWithoutUpdatingStartIndex(float crossSize,float mainSize,LayoutWrapper * layoutWrapper)623 void GridScrollLayoutAlgorithm::FillOneLineForwardWithoutUpdatingStartIndex(
624 float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
625 {
626 if (info_.gridMatrix_.empty()) {
627 return;
628 }
629 auto startLine = info_.gridMatrix_.find(info_.startMainLineIndex_);
630 if (startLine == info_.gridMatrix_.end() || startLine->second.empty()) {
631 return;
632 }
633 if (startLine->second.size() < crossCount_ && info_.startIndex_ > 0) {
634 auto tempStartIndex = info_.startIndex_;
635 auto tempStartMainLineIndex = info_.startMainLineIndex_;
636 auto tempCurrentMainLineIndex = currentMainLineIndex_;
637 auto tempReachStart = info_.reachStart_;
638
639 float lineHeight = FillNewLineForward(crossSize, mainSize, layoutWrapper);
640 if (GreatOrEqual(lineHeight, 0.0)) {
641 info_.lineHeightMap_[info_.startMainLineIndex_] = lineHeight;
642 }
643
644 info_.startIndex_ = tempStartIndex;
645 info_.startMainLineIndex_ = tempStartMainLineIndex;
646 currentMainLineIndex_ = tempCurrentMainLineIndex;
647 info_.reachStart_ = tempReachStart;
648 }
649 }
650
651 // When a moving up event comes, the [currentOffset_] may have been reduced too much than the items really need to
652 // be moved up, so we need to modify [currentOffset_] according to previous position.
ModifyCurrentOffsetWhenReachEnd(float mainSize,LayoutWrapper * layoutWrapper)653 void GridScrollLayoutAlgorithm::ModifyCurrentOffsetWhenReachEnd(float mainSize, LayoutWrapper* layoutWrapper)
654 {
655 auto host = layoutWrapper->GetHostNode();
656 CHECK_NULL_VOID(host);
657 auto gridPattern = host->GetPattern<GridPattern>();
658 CHECK_NULL_VOID(gridPattern);
659 // use original size in order to add end spacing
660 mainSize -= info_.contentEndPadding_;
661 // Step1. Calculate total length of all items with main gap in viewport.
662 // [lengthOfItemsInViewport] must be greater than or equal to viewport height
663 float lengthOfItemsInViewport = info_.GetTotalHeightOfItemsInView(mainGap_);
664 // scroll forward
665 if (LessNotEqual(info_.prevOffset_, info_.currentOffset_)) {
666 if ((NonNegative(info_.currentOffset_) && !canOverScrollStart_) ||
667 (NonPositive(info_.currentOffset_) && !canOverScrollEnd_)) {
668 info_.reachEnd_ = false;
669 return;
670 } else if (!isChildrenUpdated_) {
671 if (LessNotEqual(lengthOfItemsInViewport, mainSize) && NearEqual(mainSize, info_.lastMainSize_)) {
672 return;
673 }
674 }
675 }
676 // Step2. Calculate real offset that items can only be moved up by.
677 // Hint: [prevOffset_] is a non-positive value
678 if (LessNotEqual(lengthOfItemsInViewport, mainSize) && info_.startIndex_ == 0) {
679 if (((NonNegative(info_.currentOffset_) && !canOverScrollStart_) ||
680 (NonPositive(info_.currentOffset_) && !canOverScrollEnd_)) ||
681 isChildrenUpdated_) {
682 info_.currentOffset_ = 0;
683 info_.prevOffset_ = 0;
684 }
685 info_.reachStart_ = true;
686 info_.offsetEnd_ = LessOrEqual(info_.currentOffset_ + lengthOfItemsInViewport, mainSize);
687 return;
688 }
689
690 // last grid item is not fully showed
691 if (GreatNotEqual(info_.currentOffset_ + lengthOfItemsInViewport, mainSize)) {
692 info_.offsetEnd_ = false;
693 return;
694 }
695
696 if (info_.hasMultiLineItem_ && info_.endIndex_ == info_.GetChildrenCount() - 1) {
697 if (!CheckLastLineItemFullyShowed(layoutWrapper)) {
698 info_.offsetEnd_ = false;
699 return;
700 }
701 }
702
703 // Step3. modify [currentOffset_]
704 if (!canOverScrollEnd_) {
705 float realOffsetToMoveUp = lengthOfItemsInViewport - mainSize + info_.prevOffset_;
706 info_.currentOffset_ = info_.prevOffset_ - realOffsetToMoveUp;
707 info_.prevOffset_ = info_.currentOffset_;
708 }
709 info_.offsetEnd_ = true;
710 }
711
FillBlankAtEnd(float mainSize,float crossSize,LayoutWrapper * layoutWrapper,float & mainLength)712 void GridScrollLayoutAlgorithm::FillBlankAtEnd(
713 float mainSize, float crossSize, LayoutWrapper* layoutWrapper, float& mainLength)
714 {
715 // fill current line first
716 FillCurrentLine(mainSize, crossSize, layoutWrapper);
717
718 if (measureInNextFrame_) {
719 return;
720 }
721
722 if (GreatNotEqual(mainLength, mainSize)) {
723 if (IsScrollToEndLine()) {
724 TAG_LOGI(AceLogTag::ACE_GRID, "scroll to end line with index:%{public}d", moveToEndLineIndex_);
725 // scrollToIndex(AUTO) on first layout
726 moveToEndLineIndex_ = -1;
727 }
728 return;
729 }
730 // When [mainLength] is still less than [mainSize], do [FillNewLineBackward] repeatedly until filling up the lower
731 // part of the viewport
732 while (LessNotEqual(mainLength, mainSize)) {
733 float lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
734 if (GreatOrEqual(lineHeight, 0.0)) {
735 mainLength += (lineHeight + mainGap_);
736 if (!syncLoad_ && layoutWrapper->ReachResponseDeadline()) {
737 measureInNextFrame_ = true;
738 break;
739 }
740 continue;
741 }
742 info_.reachEnd_ = true;
743 return;
744 };
745 // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
746 info_.reachEnd_ = info_.endIndex_ == info_.GetChildrenCount() - 1;
747 }
748
FillCurrentLine(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)749 void GridScrollLayoutAlgorithm::FillCurrentLine(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
750 {
751 auto mainIter = info_.gridMatrix_.find(currentMainLineIndex_);
752 if (mainIter != info_.gridMatrix_.end() && mainIter->second.size() < crossCount_) {
753 bool doneFillCurrentLine = false;
754 auto currentIndex = info_.endIndex_ + 1;
755 cellAveLength_ = -1.0f;
756 lastCross_ = 0;
757 auto childrenCount = info_.GetChildrenCount();
758 for (uint32_t i = mainIter->second.size(); i < crossCount_; i++) {
759 // Step1. Get wrapper of [GridItem]
760 if (currentIndex >= childrenCount) {
761 break;
762 }
763 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
764 if (!itemWrapper) {
765 break;
766 }
767 // Step2. Measure child
768 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
769 auto childState = MeasureNewChild(frameSize, currentIndex, layoutWrapper, itemWrapper, false);
770 if (childState == -1) {
771 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
772 ? info_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second
773 : cellAveLength_;
774 --currentIndex;
775 break;
776 }
777 measuredItems_.emplace(currentIndex);
778 i += static_cast<uint32_t>(childState) - 1;
779 // Step3. Measure [GridItem]
780 LargeItemLineHeight(itemWrapper);
781 info_.endIndex_ = currentIndex;
782 currentIndex++;
783 doneFillCurrentLine = true;
784 if (!syncLoad_ && layoutWrapper->ReachResponseDeadline()) {
785 measureInNextFrame_ = true;
786 break;
787 }
788 }
789 if (doneFillCurrentLine) {
790 info_.lineHeightMap_[currentMainLineIndex_] = cellAveLength_;
791 }
792 }
793 }
794
CalculateLargeItemOffset(OffsetF currOffset,int32_t itemIndex,int32_t currLineIndex,int32_t currentCrossIndex)795 OffsetF GridScrollLayoutAlgorithm::CalculateLargeItemOffset(
796 OffsetF currOffset, int32_t itemIndex, int32_t currLineIndex, int32_t currentCrossIndex)
797 {
798 OffsetF offset = currOffset;
799 for (int32_t lastCrossIndex = currLineIndex - 1; lastCrossIndex >= 0; lastCrossIndex--) {
800 auto lastGridMatrixIter = info_.gridMatrix_.find(lastCrossIndex);
801 if (lastGridMatrixIter == info_.gridMatrix_.end()) {
802 continue;
803 }
804 const auto& lastGridItemRecord = lastGridMatrixIter->second;
805 auto lastLineCrossItem = lastGridItemRecord.find(currentCrossIndex);
806 if (lastLineCrossItem == lastGridItemRecord.end()) {
807 continue;
808 }
809 if (lastLineCrossItem->second == itemIndex) {
810 offset -= axis_ == Axis::VERTICAL ? OffsetF(0, info_.lineHeightMap_[lastCrossIndex] + mainGap_)
811 : OffsetF(info_.lineHeightMap_[lastCrossIndex] + mainGap_, 0.0);
812 } else {
813 break;
814 }
815 }
816
817 return offset;
818 }
819
NeedAdjust(const RefPtr<GridItemLayoutProperty> & itemLayoutProperty)820 bool GridScrollLayoutAlgorithm::NeedAdjust(const RefPtr<GridItemLayoutProperty>& itemLayoutProperty)
821 {
822 bool needAdjust = false;
823 auto main = axis_ == Axis::VERTICAL ? mainCount_ : crossCount_;
824 auto cross = axis_ == Axis::VERTICAL ? crossCount_ : mainCount_;
825 if (itemLayoutProperty->GetRowStart().has_value()) {
826 currentItemRowStart_ = itemLayoutProperty->GetRowStart().value_or(-1);
827 if ((currentItemRowStart_ < 0) || (currentItemRowStart_ >= static_cast<int32_t>(main))) {
828 needAdjust = true;
829 }
830 }
831 if (itemLayoutProperty->GetRowEnd().has_value()) {
832 currentItemRowEnd_ = itemLayoutProperty->GetRowEnd().value_or(-1);
833 if ((currentItemRowEnd_ < 0) || (currentItemRowEnd_ >= static_cast<int32_t>(main))) {
834 needAdjust = true;
835 }
836 }
837 if (itemLayoutProperty->GetColumnStart().has_value()) {
838 currentItemColStart_ = itemLayoutProperty->GetColumnStart().value_or(-1);
839 if ((currentItemColStart_ < 0) || (currentItemColStart_ >= static_cast<int32_t>(cross))) {
840 needAdjust = true;
841 }
842 }
843 if (itemLayoutProperty->GetColumnEnd().has_value()) {
844 currentItemColEnd_ = itemLayoutProperty->GetColumnEnd().value_or(-1);
845 if ((currentItemColEnd_ < 0) || (currentItemColEnd_ >= static_cast<int32_t>(cross))) {
846 needAdjust = true;
847 }
848 }
849 return needAdjust;
850 }
851
AdjustRowColSpan(const RefPtr<LayoutWrapper> & itemLayoutWrapper,LayoutWrapper *,int32_t)852 void GridScrollLayoutAlgorithm::AdjustRowColSpan(
853 const RefPtr<LayoutWrapper>& itemLayoutWrapper, LayoutWrapper* /* layoutWrapper */, int32_t /* itemIndex */)
854 {
855 auto itemLayoutProperty = DynamicCast<GridItemLayoutProperty>(itemLayoutWrapper->GetLayoutProperty());
856 CHECK_NULL_VOID(itemLayoutProperty);
857 bool needAdjust = false;
858 currentItemRowSpan_ = 1;
859 currentItemColSpan_ = 1;
860 currentItemRowStart_ = -1;
861 currentItemColStart_ = -1;
862 currentItemColEnd_ = -1;
863 currentItemRowEnd_ = -1;
864 needAdjust = NeedAdjust(itemLayoutProperty);
865 if (!needAdjust) {
866 currentItemRowSpan_ = std::max(currentItemRowEnd_ - currentItemRowStart_ + 1, 1);
867 currentItemColSpan_ = std::max(currentItemColEnd_ - currentItemColStart_ + 1, 1);
868 } else {
869 currentItemRowStart_ = -1;
870 currentItemColStart_ = -1;
871 currentItemColEnd_ = -1;
872 currentItemRowEnd_ = -1;
873 }
874 if ((currentItemRowStart_ == -1 && currentItemRowEnd_ != -1) ||
875 (currentItemRowEnd_ == -1 && currentItemRowStart_ != -1) ||
876 (currentItemColStart_ == -1 && currentItemColEnd_ != -1) ||
877 (currentItemColEnd_ == -1 && currentItemColStart_ != -1)) {
878 currentItemRowSpan_ = 1;
879 currentItemColSpan_ = 1;
880 currentItemRowStart_ = -1;
881 currentItemColStart_ = -1;
882 currentItemColEnd_ = -1;
883 currentItemRowEnd_ = -1;
884 }
885 if (currentItemRowSpan_ > 1 || currentItemColSpan_ > 1) {
886 info_.hasBigItem_ = true;
887 }
888 int32_t mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
889 if (mainSpan > 1) {
890 info_.hasMultiLineItem_ = true;
891 }
892
893 itemLayoutProperty->UpdateRealRowSpan(currentItemRowSpan_);
894 itemLayoutProperty->UpdateRealColumnSpan(currentItemColSpan_);
895 }
896
LargeItemLineHeight(const RefPtr<LayoutWrapper> & itemWrapper)897 void GridScrollLayoutAlgorithm::LargeItemLineHeight(const RefPtr<LayoutWrapper>& itemWrapper)
898 {
899 AdjustRowColSpan(itemWrapper, nullptr, 0);
900 auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
901 auto itemSize = itemWrapper->GetGeometryNode()->GetMarginFrameSize();
902 if (mainSpan == 1) {
903 cellAveLength_ = std::max(GetMainAxisSize(itemSize, info_.axis_), cellAveLength_);
904 }
905
906 if (mainSpan > 1) {
907 cellAveLength_ =
908 std::max((GetMainAxisSize(itemSize, info_.axis_) - (mainGap_ * (mainSpan - 1))) / mainSpan, cellAveLength_);
909 }
910 }
911
IsIndexInMatrix(int32_t index,int32_t & startLine)912 bool GridScrollLayoutAlgorithm::IsIndexInMatrix(int32_t index, int32_t& startLine)
913 {
914 auto iter = std::find_if(info_.gridMatrix_.begin(), info_.gridMatrix_.end(),
915 [index, &startLine](const std::pair<int32_t, std::map<int32_t, int32_t>>& item) {
916 for (auto& subitem : item.second) {
917 if (subitem.second == index) {
918 startLine = item.first;
919 return true;
920 }
921 }
922 return false;
923 });
924 return (iter != info_.gridMatrix_.end());
925 }
926
GetTargetIndexInfoWithBenchMark(LayoutWrapper * layoutWrapper,bool isTargetBackward,int32_t targetIndex)927 void GridScrollLayoutAlgorithm::GetTargetIndexInfoWithBenchMark(
928 LayoutWrapper* layoutWrapper, bool isTargetBackward, int32_t targetIndex)
929 {
930 int32_t benchmarkIndex =
931 (isTargetBackward && !info_.gridMatrix_.empty()) ? info_.gridMatrix_.rbegin()->second.rbegin()->second + 1 : 0;
932 int32_t mainStartIndex =
933 (isTargetBackward && !info_.gridMatrix_.empty()) ? info_.gridMatrix_.rbegin()->first + 1 : 0;
934 int32_t currentIndex = benchmarkIndex;
935 int32_t headOfMainStartLine = currentIndex;
936 auto childrenCount = info_.GetChildrenCount();
937
938 while (currentIndex < targetIndex) {
939 int32_t crossGridReserve = info_.crossCount_;
940 /* go through a new line */
941 while ((crossGridReserve > 0) && (currentIndex <= targetIndex)) {
942 if (currentIndex >= childrenCount) {
943 return;
944 }
945 auto currentWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex, false);
946 CHECK_NULL_VOID(currentWrapper);
947 auto layoutProperty = DynamicCast<GridItemLayoutProperty>(currentWrapper->GetLayoutProperty());
948 CHECK_NULL_VOID(layoutProperty);
949 auto gridSpan = layoutProperty->GetCrossSpan(info_.axis_);
950 if (crossGridReserve >= gridSpan) {
951 crossGridReserve -= gridSpan;
952 } else if (info_.crossCount_ >= gridSpan) {
953 ++mainStartIndex;
954 headOfMainStartLine = currentIndex;
955 crossGridReserve = info_.crossCount_ - gridSpan;
956 }
957 ++currentIndex;
958 }
959 if (currentIndex > targetIndex) {
960 break;
961 }
962 ++mainStartIndex;
963 headOfMainStartLine = currentIndex;
964 }
965 info_.startMainLineIndex_ = mainStartIndex;
966 info_.startIndex_ = headOfMainStartLine;
967 info_.endIndex_ = headOfMainStartLine - 1;
968 info_.prevOffset_ = 0;
969 info_.currentOffset_ = 0;
970 info_.ResetPositionFlags();
971 info_.gridMatrix_.clear();
972 info_.lineHeightMap_.clear();
973 info_.irregularItemsPosition_.clear();
974 }
975
UpdateGridLayoutInfo(LayoutWrapper * layoutWrapper,float mainSize)976 void GridScrollLayoutAlgorithm::UpdateGridLayoutInfo(LayoutWrapper* layoutWrapper, float mainSize)
977 {
978 /* 1. Have gotten info_.startMainLineIndex_ and directly jump to it */
979 if (info_.jumpIndex_ < 0 && info_.jumpIndex_ != LAST_ITEM) {
980 return;
981 }
982 auto childrenCount = info_.GetChildrenCount();
983 if (info_.jumpIndex_ == LAST_ITEM) {
984 info_.jumpIndex_ = childrenCount - 1;
985 }
986 /* 2. Need to find out the startMainLineIndex according to startIndex */
987 int32_t targetIndex = info_.jumpIndex_;
988 /* 2.1 invalid targetIndex */
989 childrenCount = HasLayoutOptions(layoutWrapper) ? childrenCount + info_.repeatDifference_ : childrenCount;
990 if (childrenCount <= targetIndex) {
991 return;
992 }
993
994 canOverScrollStart_ = false;
995 canOverScrollEnd_ = false;
996 switch (info_.scrollAlign_) {
997 case ScrollAlign::START:
998 case ScrollAlign::END:
999 case ScrollAlign::CENTER:
1000 ScrollToIndexStart(layoutWrapper, targetIndex);
1001 return;
1002 default:
1003 ScrollToIndexAuto(layoutWrapper, mainSize, targetIndex);
1004 }
1005 }
1006
IsScrollToEndLine() const1007 bool GridScrollLayoutAlgorithm::IsScrollToEndLine() const
1008 {
1009 return moveToEndLineIndex_ > 0 && info_.endIndex_ >= moveToEndLineIndex_;
1010 }
1011
IsEndLineInScreenWithGap(int32_t targetLine,float totalViewHeight,float mainSize) const1012 bool GridScrollLayoutAlgorithm::IsEndLineInScreenWithGap(
1013 int32_t targetLine, float totalViewHeight, float mainSize) const
1014 {
1015 return targetLine == info_.endMainLineIndex_ && LessOrEqual(totalViewHeight + info_.currentOffset_, mainSize);
1016 }
1017
ScrollToIndexAuto(LayoutWrapper * layoutWrapper,float mainSize,int32_t targetIndex)1018 void GridScrollLayoutAlgorithm::ScrollToIndexAuto(LayoutWrapper* layoutWrapper, float mainSize, int32_t targetIndex)
1019 {
1020 int32_t startLine = 0;
1021 if (IsIndexInMatrix(targetIndex, startLine)) {
1022 auto& info = info_;
1023 if (startLine == info.startMainLineIndex_ && info.startMainLineIndex_ == info.endMainLineIndex_) {
1024 // startLine occupies the whole viewport
1025 return;
1026 }
1027 if (startLine < info_.endMainLineIndex_ && startLine > info_.startMainLineIndex_) {
1028 return;
1029 }
1030
1031 if (startLine >= info_.endMainLineIndex_) {
1032 auto totalViewHeight = info_.GetTotalHeightOfItemsInView(mainGap_);
1033 if (IsEndLineInScreenWithGap(startLine, totalViewHeight, mainSize)) {
1034 return;
1035 }
1036 // When ScrollAlign::AUTO and startLine is greater than endMainLineIndex, the effect of
1037 // ScrollToIndex is the same as ScrollAlign::END.
1038 info_.scrollAlign_ = ScrollAlign::END;
1039 }
1040
1041 // startLine <= info_.startMainLineIndex_
1042 info_.startMainLineIndex_ = startLine;
1043 info_.UpdateStartIndexByStartLine();
1044 info_.prevOffset_ = 0;
1045 info_.currentOffset_ = 0;
1046 info_.ResetPositionFlags();
1047 return;
1048 }
1049
1050 /* 2.3 targetIndex is out of the matrix */
1051 bool isTargetBackward = true;
1052 if (!info_.gridMatrix_.empty()) {
1053 if (targetIndex < info_.gridMatrix_.begin()->second.begin()->second) {
1054 isTargetBackward = false;
1055 } else if (targetIndex > info_.gridMatrix_.rbegin()->second.rbegin()->second) {
1056 isTargetBackward = true;
1057 } else {
1058 return;
1059 }
1060 }
1061 auto grid = layoutWrapper->GetHostNode();
1062 CHECK_NULL_VOID(grid);
1063 grid->ChildrenUpdatedFrom(0);
1064 reason_ = GridReloadReason::SCROLL_TO_INDEX;
1065 GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
1066 moveToEndLineIndex_ = isTargetBackward ? targetIndex : moveToEndLineIndex_;
1067 }
1068
ScrollToIndexStart(LayoutWrapper * layoutWrapper,int32_t targetIndex)1069 void GridScrollLayoutAlgorithm::ScrollToIndexStart(LayoutWrapper* layoutWrapper, int32_t targetIndex)
1070 {
1071 int32_t startLine = 0;
1072 /* targetIndex is in the matrix */
1073 if (IsIndexInMatrix(targetIndex, startLine)) {
1074 if (startLine == info_.startMainLineIndex_) {
1075 info_.prevOffset_ = info_.currentOffset_;
1076 info_.currentOffset_ = 0;
1077 info_.ResetPositionFlags();
1078 return;
1079 }
1080
1081 info_.startMainLineIndex_ = startLine;
1082 info_.UpdateStartIndexByStartLine();
1083 info_.prevOffset_ = 0;
1084 info_.currentOffset_ = 0;
1085 info_.ResetPositionFlags();
1086 return;
1087 }
1088 /* targetIndex is out of the matrix */
1089 bool isTargetBackward = true;
1090 if (!info_.gridMatrix_.empty()) {
1091 if (targetIndex < info_.gridMatrix_.begin()->second.begin()->second) {
1092 isTargetBackward = false;
1093 } else if (targetIndex > info_.gridMatrix_.rbegin()->second.rbegin()->second) {
1094 isTargetBackward = true;
1095 } else {
1096 return;
1097 }
1098 }
1099 auto grid = layoutWrapper->GetHostNode();
1100 CHECK_NULL_VOID(grid);
1101 grid->ChildrenUpdatedFrom(0);
1102 reason_ = GridReloadReason::SCROLL_TO_INDEX;
1103 GetTargetIndexInfoWithBenchMark(layoutWrapper, isTargetBackward, targetIndex);
1104 }
1105
UpdateCurrentOffsetForJumpTo(float mainSize)1106 void GridScrollLayoutAlgorithm::UpdateCurrentOffsetForJumpTo(float mainSize)
1107 {
1108 if (info_.scrollAlign_ == ScrollAlign::CENTER || info_.scrollAlign_ == ScrollAlign::END) {
1109 int32_t startLine = 0;
1110 /* targetIndex is in the matrix */
1111 if (IsIndexInMatrix(info_.jumpIndex_, startLine)) {
1112 // scroll to end of the screen
1113 info_.currentOffset_ = mainSize - info_.lineHeightMap_[startLine] - info_.contentEndPadding_;
1114 // scroll to center of the screen
1115 if (info_.scrollAlign_ == ScrollAlign::CENTER) {
1116 info_.currentOffset_ /= 2;
1117 }
1118 info_.prevOffset_ = info_.currentOffset_;
1119 } else {
1120 /* targetIndex is out of the matrix */
1121 TAG_LOGW(AceLogTag::ACE_GRID, "can not find jumpIndex in Grid Matrix :%{public}d", info_.jumpIndex_);
1122 }
1123 }
1124 if (info_.extraOffset_.has_value() && !info_.targetIndex_.has_value()) {
1125 info_.currentOffset_ += info_.extraOffset_.value();
1126 info_.prevOffset_ = info_.currentOffset_;
1127 }
1128 }
1129
MeasureRecordedItems(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)1130 float GridScrollLayoutAlgorithm::MeasureRecordedItems(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
1131 {
1132 currentMainLineIndex_ = info_.startMainLineIndex_ - 1;
1133 float mainLength = info_.currentOffset_;
1134 // already at start line, do not use offset for mainLength
1135 if (info_.startMainLineIndex_ == 0 && GreatNotEqual(mainLength, 0)) {
1136 mainLength = 0;
1137 }
1138 UseCurrentLines(mainSize, crossSize, layoutWrapper, mainLength);
1139 return mainLength;
1140 }
1141
1142 namespace {
OneLineMovesOffViewportFromAbove(float mainLength,float lineHeight)1143 inline bool OneLineMovesOffViewportFromAbove(float mainLength, float lineHeight)
1144 {
1145 return LessNotEqual(mainLength, 0.0) || (NearZero(mainLength) && GreatNotEqual(lineHeight, 0.0f));
1146 }
1147 } // namespace
1148
1149 namespace {
UpdateStartIndexByStartLine(GridLayoutInfo & info_)1150 inline void UpdateStartIndexByStartLine(GridLayoutInfo& info_)
1151 {
1152 auto startLine = info_.gridMatrix_.find(info_.startMainLineIndex_);
1153 if (startLine != info_.gridMatrix_.end() && !startLine->second.empty()) {
1154 info_.startIndex_ = startLine->second.begin()->second;
1155 return;
1156 }
1157
1158 if (info_.endIndex_ < info_.childrenCount_ - 1) {
1159 info_.startIndex_ = info_.endIndex_ + 1;
1160 }
1161 }
1162 } // namespace
1163
MeasureExistingLine(int32_t line,float & mainLength,int32_t & endIdx)1164 bool GridScrollLayoutAlgorithm::MeasureExistingLine(int32_t line, float& mainLength, int32_t& endIdx)
1165 {
1166 auto it = info_.gridMatrix_.find(line);
1167 if (it == info_.gridMatrix_.end() || info_.lineHeightMap_.find(line) == info_.lineHeightMap_.end()) {
1168 return false;
1169 }
1170 int32_t idx = -1;
1171 cellAveLength_ = -1.0f;
1172 for (const auto& cell : it->second) {
1173 if (idx == cell.second) {
1174 continue;
1175 }
1176 idx = cell.second;
1177 if (idx == -1) {
1178 // move from another grid
1179 continue;
1180 }
1181 auto item = wrapper_->GetOrCreateChildByIndex(idx);
1182 if (!item) {
1183 break;
1184 }
1185 AdjustRowColSpan(item, wrapper_, idx);
1186 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1187 if (crossStart == -1) {
1188 MeasureChildPlaced(frameSize_, idx, cell.first, wrapper_, item);
1189 } else {
1190 MeasureChildPlaced(frameSize_, idx, crossStart, wrapper_, item);
1191 }
1192 measuredItems_.emplace(idx);
1193 // Record end index. When fill new line, the [endIndex_] will be the first item index to request
1194 LargeItemLineHeight(item);
1195 endIdx = std::max(idx, endIdx);
1196 info_.endIndex_ = endIdx;
1197 }
1198
1199 if (NonNegative(cellAveLength_)) { // Means at least one item has been measured
1200 info_.lineHeightMap_[line] = cellAveLength_;
1201 mainLength += cellAveLength_ + mainGap_;
1202 }
1203 // If a line moves up out of viewport, update [startIndex_], [currentOffset_] and [startMainLineIndex_]
1204 if (OneLineMovesOffViewportFromAbove(mainLength, cellAveLength_)) {
1205 info_.currentOffset_ = mainLength;
1206 info_.prevOffset_ = info_.currentOffset_;
1207 info_.startMainLineIndex_ = line + 1;
1208 UpdateStartIndexByStartLine(info_);
1209 }
1210 return true;
1211 }
1212
UseCurrentLines(float mainSize,float crossSize,LayoutWrapper * layoutWrapper,float & mainLength)1213 bool GridScrollLayoutAlgorithm::UseCurrentLines(
1214 float mainSize, float crossSize, LayoutWrapper* layoutWrapper, float& mainLength)
1215 {
1216 bool runOutOfRecord = false;
1217 // Measure grid items row by row
1218 int32_t tempEndIndex = -1;
1219 while (LessNotEqual(mainLength, mainSize)) {
1220 if (!MeasureExistingLine(++currentMainLineIndex_, mainLength, tempEndIndex)) {
1221 runOutOfRecord = true;
1222 break;
1223 }
1224 }
1225 // Case 1. if this while-loop breaks due to running out of records, the [currentMainLineIndex_] is larger by 1 than
1226 // real main line index, so reduce 1.
1227 // Case 2. if this while-loop stops due to false result of [LessNotEqual(mainLength, mainSize)], the
1228 // [currentMainLineIndex_] is exactly the real main line index. Update [endMainLineIndex_] when the recorded items
1229 // are done measured.
1230 info_.endMainLineIndex_ = runOutOfRecord ? --currentMainLineIndex_ : currentMainLineIndex_;
1231 // reset reachEnd_ if any line at bottom is out of viewport
1232 // last line make LessNotEqual(mainLength, mainSize) and continue is reach end too
1233 info_.reachEnd_ = info_.endIndex_ == info_.GetChildrenCount() - 1;
1234 if (info_.reachEnd_) {
1235 if (LessNotEqual(info_.GetTotalHeightOfItemsInView(mainGap_) + info_.currentOffset_, mainSize)) {
1236 // skip clearing cache if overScrolling
1237 return runOutOfRecord;
1238 }
1239 } else {
1240 info_.offsetEnd_ = false;
1241 }
1242 return runOutOfRecord;
1243 }
1244
SkipLargeOffset(float mainSize,LayoutWrapper * layoutWrapper)1245 void GridScrollLayoutAlgorithm::SkipLargeOffset(float mainSize, LayoutWrapper* layoutWrapper)
1246 {
1247 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1248 CHECK_NULL_VOID(gridLayoutProperty);
1249 auto cacheCount = gridLayoutProperty->GetCachedCountValue(info_.defCachedCount_);
1250 cacheCount = std::max(cacheCount, 1);
1251 SkipForwardLines(cacheCount * mainSize, layoutWrapper);
1252 SkipBackwardLines(cacheCount * mainSize, layoutWrapper);
1253 }
1254
SkipForwardLines(float mainSize,LayoutWrapper * layoutWrapper)1255 void GridScrollLayoutAlgorithm::SkipForwardLines(float mainSize, LayoutWrapper* layoutWrapper)
1256 {
1257 if (!GreatOrEqual(info_.currentOffset_ - info_.prevOffset_, mainSize)) {
1258 return;
1259 }
1260 // skip lines in matrix
1261 while (GreatOrEqual(info_.currentOffset_, mainSize)) {
1262 auto line = info_.gridMatrix_.find(info_.startMainLineIndex_ - 1);
1263 if (line == info_.gridMatrix_.end()) {
1264 break;
1265 }
1266 auto lineHeight = info_.lineHeightMap_.find(info_.startMainLineIndex_ - 1);
1267 if (lineHeight == info_.lineHeightMap_.end()) {
1268 break;
1269 }
1270 --info_.startMainLineIndex_;
1271 info_.startIndex_ = line->second.begin()->second;
1272 info_.currentOffset_ -= lineHeight->second + mainGap_;
1273 info_.currentHeight_ -= lineHeight->second + mainGap_;
1274 }
1275
1276 // skip lines not in matrix
1277 if (GreatOrEqual(info_.currentOffset_, mainSize) && info_.startIndex_ > 0) {
1278 if (!info_.hasBigItem_) {
1279 SkipRegularLines(true);
1280 } else {
1281 SkipIrregularLines(layoutWrapper, true);
1282 }
1283 info_.startIndex_ = std::clamp(info_.startIndex_, 0, info_.GetChildrenCount() - 1);
1284 TAG_LOGI(AceLogTag::ACE_GRID, "estimatedIndex:%{public}d", info_.startIndex_);
1285 auto grid = layoutWrapper->GetHostNode();
1286 CHECK_NULL_VOID(grid);
1287 grid->ChildrenUpdatedFrom(0);
1288 reason_ = GridReloadReason::SKIP_LARGE_OFFSET;
1289 }
1290 }
1291
SkipBackwardLines(float mainSize,LayoutWrapper * layoutWrapper)1292 void GridScrollLayoutAlgorithm::SkipBackwardLines(float mainSize, LayoutWrapper* layoutWrapper)
1293 {
1294 if (!GreatOrEqual(info_.prevOffset_ - info_.currentOffset_, mainSize)) {
1295 return;
1296 }
1297
1298 if (!SkipLargeLineHeightLines(mainSize)) {
1299 return;
1300 }
1301
1302 // grid size change from big to small
1303 info_.UpdateEndLine(mainSize, mainGap_);
1304
1305 // skip lines in matrix
1306 while (GreatOrEqual(-info_.currentOffset_, mainSize)) {
1307 auto line = info_.gridMatrix_.find(info_.endMainLineIndex_ + 1);
1308 if (line == info_.gridMatrix_.end()) {
1309 break;
1310 }
1311 auto lineHeight = info_.lineHeightMap_.find(info_.endMainLineIndex_ + 1);
1312 if (lineHeight == info_.lineHeightMap_.end()) {
1313 break;
1314 }
1315 ++info_.startMainLineIndex_;
1316 ++info_.endMainLineIndex_;
1317 info_.currentOffset_ += lineHeight->second + mainGap_;
1318 info_.currentHeight_ += lineHeight->second + mainGap_;
1319 }
1320 info_.UpdateStartIndexByStartLine();
1321
1322 // skip lines not in matrix
1323 if (GreatOrEqual(-info_.currentOffset_, mainSize)) {
1324 if (!info_.hasBigItem_) {
1325 SkipRegularLines(false);
1326 } else {
1327 SkipIrregularLines(layoutWrapper, false);
1328 }
1329 info_.startIndex_ = std::clamp(info_.startIndex_, 0, info_.GetChildrenCount() - 1);
1330 TAG_LOGI(AceLogTag::ACE_GRID, "estimatedIndex:%{public}d, currentOffset:%{public}f", info_.startIndex_,
1331 info_.currentOffset_);
1332 auto grid = layoutWrapper->GetHostNode();
1333 CHECK_NULL_VOID(grid);
1334 grid->ChildrenUpdatedFrom(0);
1335 reason_ = GridReloadReason::SKIP_LARGE_OFFSET;
1336 }
1337 }
1338
SkipRegularLines(bool forward)1339 void GridScrollLayoutAlgorithm::SkipRegularLines(bool forward)
1340 {
1341 auto lineHeight = info_.GetAverageLineHeight() + mainGap_;
1342 if (LessOrEqual(lineHeight, 0.0)) {
1343 return;
1344 }
1345 int32_t estimatedLines = info_.currentOffset_ / lineHeight;
1346 if (forward && info_.startIndex_ < estimatedLines * static_cast<int32_t>(crossCount_)) {
1347 info_.startIndex_ = 0;
1348 info_.currentOffset_ = 0;
1349 } else {
1350 info_.startIndex_ -= estimatedLines * static_cast<int32_t>(crossCount_);
1351 info_.currentOffset_ -= lineHeight * estimatedLines;
1352 }
1353 }
1354
SkipIrregularLines(LayoutWrapper * layoutWrapper,bool forward)1355 void GridScrollLayoutAlgorithm::SkipIrregularLines(LayoutWrapper* layoutWrapper, bool forward)
1356 {
1357 auto grid = layoutWrapper->GetHostNode();
1358 CHECK_NULL_VOID(grid);
1359 auto pattern = grid->GetPattern<GridPattern>();
1360 CHECK_NULL_VOID(pattern);
1361 auto averageHeight = pattern->GetAverageHeight();
1362 if (LessOrEqual(averageHeight, 0.0)) {
1363 return;
1364 }
1365 int32_t estimatedIndex = (info_.currentOffset_) / averageHeight;
1366 info_.startIndex_ = std::min(info_.startIndex_ - estimatedIndex, info_.GetChildrenCount());
1367 info_.currentOffset_ = info_.prevOffset_;
1368 }
1369
FillNewLineForward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper)1370 float GridScrollLayoutAlgorithm::FillNewLineForward(float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
1371 {
1372 // To make the code more convenient to read, we name a param in situation of vertical, for example:
1373 // 1. [lineHight] means height of a row when the Grid is vertical;
1374 // 2. [lineHight] means width of a column when the Grid is horizontal;
1375 // Other params are also named according to this principle.
1376 cellAveLength_ = -1.0f;
1377 auto currentIndex = info_.startIndex_;
1378 if (info_.startMainLineIndex_ - 1 < 0) {
1379 if (currentIndex == 0) {
1380 return cellAveLength_;
1381 }
1382 // add more than one line
1383 UpdateMatrixForAddedItems();
1384 }
1385 info_.startMainLineIndex_--;
1386 bool doneCreateNewLine = false;
1387 auto gridMatrixIter = info_.gridMatrix_.find(info_.startMainLineIndex_);
1388 if (gridMatrixIter == info_.gridMatrix_.end()) {
1389 AddForwardLines(currentIndex, crossSize, mainSize, layoutWrapper);
1390 }
1391 gridMatrixIter = info_.gridMatrix_.find(info_.startMainLineIndex_);
1392 if (gridMatrixIter == info_.gridMatrix_.end()) {
1393 return cellAveLength_;
1394 }
1395
1396 // need to obtain the item node in order and by step one in LazyLayoutWrapperBuilder::OnGetOrCreateWrapperByIndex
1397 for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1398 currentIndex = itemIter->second;
1399
1400 // Step1. Get wrapper of [GridItem]
1401 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1402 if (!itemWrapper) {
1403 break;
1404 }
1405 // Step2. Measure child
1406 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1407 AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
1408 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1409 if (crossStart == -1) {
1410 MeasureChildPlaced(frameSize, currentIndex, itemIter->first, layoutWrapper, itemWrapper);
1411 } else {
1412 MeasureChildPlaced(frameSize, currentIndex, crossStart, layoutWrapper, itemWrapper);
1413 }
1414 measuredItems_.emplace(currentIndex);
1415 // Step3. Measure [GridItem]
1416 LargeItemLineHeight(itemWrapper);
1417 info_.startIndex_ = currentIndex;
1418 }
1419
1420 doneCreateNewLine = GreatOrEqual(cellAveLength_, 0.0);
1421 // If it fails to create new line when [FillNewLineForward] is called, it means that it reaches start
1422 info_.reachStart_ = !doneCreateNewLine;
1423
1424 return cellAveLength_;
1425 }
1426
UpdateMatrixForAddedItems()1427 void GridScrollLayoutAlgorithm::UpdateMatrixForAddedItems()
1428 {
1429 decltype(info_.lineHeightMap_) gridLineHeightMap(std::move(info_.lineHeightMap_));
1430 decltype(info_.gridMatrix_) gridMatrix(std::move(info_.gridMatrix_));
1431 for (const auto& item : gridMatrix) {
1432 info_.gridMatrix_[item.first + 1] = item.second;
1433 }
1434 for (const auto& item : gridLineHeightMap) {
1435 info_.lineHeightMap_[item.first + 1] = item.second;
1436 }
1437 info_.startMainLineIndex_ = info_.startMainLineIndex_ + 1;
1438 info_.endMainLineIndex_ = info_.endMainLineIndex_ + 1;
1439 TAG_LOGI(AceLogTag::ACE_GRID, "add more than one line startMainLineIndex_:%{public}d", info_.startMainLineIndex_);
1440 }
1441
AddForwardLines(int32_t currentIndex,float crossSize,float mainSize,LayoutWrapper * layoutWrapper)1442 void GridScrollLayoutAlgorithm::AddForwardLines(
1443 int32_t currentIndex, float crossSize, float mainSize, LayoutWrapper* layoutWrapper)
1444 {
1445 auto endMainLineIndex = info_.endMainLineIndex_;
1446 auto endIndex = info_.endIndex_;
1447 auto firstItem = GetStartingItem(layoutWrapper, currentIndex - 1);
1448 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(firstItem);
1449 CHECK_NULL_VOID(itemWrapper);
1450 AdjustRowColSpan(itemWrapper, layoutWrapper, firstItem);
1451 auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
1452 auto measureNumber = 0;
1453 currentMainLineIndex_ = (firstItem == 0 ? 0 : info_.startMainLineIndex_) - 1;
1454 info_.endIndex_ = firstItem - 1;
1455 // firstItem may be more than one line ahead, use new matrix to save and merge to old matrix
1456 decltype(info_.lineHeightMap_) gridLineHeightMap(std::move(info_.lineHeightMap_));
1457 decltype(info_.gridMatrix_) gridMatrix(std::move(info_.gridMatrix_));
1458 bool addLine = false;
1459 float newLineHeight = -1.0f;
1460 while (info_.endIndex_ < currentIndex - 1 || mainSpan > measureNumber) {
1461 newLineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, true);
1462 measureNumber++;
1463 if (LessNotEqual(newLineHeight, 0.0)) {
1464 info_.reachEnd_ = true;
1465 break;
1466 }
1467 addLine = true;
1468 if (!syncLoad_ && layoutWrapper->ReachResponseDeadline()) {
1469 measureInNextFrame_ = true;
1470 break;
1471 }
1472 }
1473 if (!addLine) {
1474 return;
1475 }
1476 // merge matrix
1477 auto forwardLines = info_.endMainLineIndex_ - info_.startMainLineIndex_;
1478 if (forwardLines >= 0) {
1479 auto begin = info_.gridMatrix_.begin()->first;
1480 if (info_.endMainLineIndex_ - begin <= begin) {
1481 for (auto i = begin; i <= info_.endMainLineIndex_; i++) {
1482 gridMatrix.emplace(i - forwardLines, std::move(info_.gridMatrix_[i]));
1483 gridLineHeightMap.emplace(i - forwardLines, info_.lineHeightMap_[i]);
1484 }
1485 gridMatrix.swap(info_.gridMatrix_);
1486 gridLineHeightMap.swap(info_.lineHeightMap_);
1487
1488 MergeRemainingLines(gridMatrix, forwardLines);
1489 } else {
1490 for (auto i = info_.startMainLineIndex_ + 1; i <= gridMatrix.rbegin()->first; i++) {
1491 info_.gridMatrix_.emplace(forwardLines + i, std::move(gridMatrix[i]));
1492 info_.lineHeightMap_.emplace(forwardLines + i, gridLineHeightMap[i]);
1493 }
1494 }
1495 } else {
1496 // delete more than one line items
1497 for (auto i = info_.startMainLineIndex_ + 1; i <= gridMatrix.rbegin()->first; i++) {
1498 info_.gridMatrix_.emplace(forwardLines + i, std::move(gridMatrix[i]));
1499 info_.lineHeightMap_.emplace(forwardLines + i, gridLineHeightMap[i]);
1500 }
1501 }
1502
1503 info_.startMainLineIndex_ = info_.endMainLineIndex_ - (forwardLines > 0 ? forwardLines : 0);
1504 info_.endMainLineIndex_ = endMainLineIndex + (forwardLines < 0 ? forwardLines : 0);
1505 info_.endIndex_ = endIndex;
1506 TAG_LOGI(AceLogTag::ACE_GRID,
1507 "after load forward:start main line %{public}d end main line %{public}d, new line height:%{public}f, "
1508 "gridMainSize:%{public}f",
1509 info_.startMainLineIndex_, info_.endMainLineIndex_, newLineHeight, mainSize);
1510 }
1511
FillNewLineBackward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper,bool reverse)1512 float GridScrollLayoutAlgorithm::FillNewLineBackward(
1513 float crossSize, float mainSize, LayoutWrapper* layoutWrapper, bool reverse)
1514 {
1515 // To make the code more convenient to read, we name a param in situation of vertical, for example:
1516 // 1. [lineHight] means height of a row when the Grid is vertical;
1517 // 2. [lineHight] means width of a column when the Grid is horizontal;
1518 // Other params are also named according to this principle.
1519 cellAveLength_ = -1.0f;
1520 if (IsScrollToEndLine()) {
1521 TAG_LOGI(AceLogTag::ACE_GRID, "scroll to end line with index:%{public}d", moveToEndLineIndex_);
1522 // scrollToIndex(AUTO) on first layout
1523 moveToEndLineIndex_ = -1;
1524 return cellAveLength_;
1525 }
1526 auto currentIndex = info_.endIndex_ + 1;
1527 currentMainLineIndex_++; // if it fails to fill a new line backward, do [currentMainLineIndex_--]
1528 if (info_.gridMatrix_.find(currentMainLineIndex_) != info_.gridMatrix_.end()) {
1529 cellAveLength_ = info_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second;
1530 }
1531 lastCross_ = 0;
1532 bool doneFillLine = false;
1533
1534 for (uint32_t i = 0; i < crossCount_; i++) {
1535 // already finish first line forward
1536 if ((reverse && currentIndex >= info_.startIndex_)) {
1537 break;
1538 }
1539 if (currentIndex >= info_.GetChildrenCount()) {
1540 if (currentIndex == 0) {
1541 layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1542 TAG_LOGW(ACE_GRID, "total item count is 0");
1543 }
1544 break;
1545 }
1546 // Step1. Get wrapper of [GridItem]
1547 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1548 if (!itemWrapper) {
1549 if (currentIndex < info_.GetChildrenCount()) {
1550 TAG_LOGW(ACE_GRID, "can not get item at:%{public}d, total items:%{public}d", currentIndex,
1551 info_.GetChildrenCount());
1552 std::string subErrorType = "can not get item at:" + std::to_string(currentIndex) +
1553 ", total items:" + std::to_string(info_.GetChildrenCount());
1554 EventReport::ReportScrollableErrorEvent("Grid", ScrollableErrorType::GET_CHILD_FAILED, subErrorType);
1555 }
1556 LargeItemNextLineHeight(currentMainLineIndex_, layoutWrapper);
1557 break;
1558 }
1559 // Step2. Measure child
1560 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1561 auto crossSpan = MeasureNewChild(frameSize, currentIndex, layoutWrapper, itemWrapper, false);
1562 if (crossSpan < 0) {
1563 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
1564 ? info_.lineHeightMap_.find(currentMainLineIndex_ - 1)->second
1565 : cellAveLength_;
1566 --currentIndex;
1567 break;
1568 }
1569 measuredItems_.emplace(currentIndex);
1570 i = static_cast<uint32_t>(lastCross_ - 1);
1571 // Step3. Measure [GridItem]
1572 LargeItemLineHeight(itemWrapper);
1573
1574 info_.endIndex_ = currentIndex;
1575 currentIndex++;
1576 doneFillLine = true;
1577 if (!reverse && !syncLoad_ && layoutWrapper->ReachResponseDeadline()) {
1578 measureInNextFrame_ = true;
1579 break;
1580 }
1581 }
1582
1583 if (doneFillLine || info_.gridMatrix_.find(currentMainLineIndex_) != info_.gridMatrix_.end()) {
1584 info_.lineHeightMap_[currentMainLineIndex_] = cellAveLength_;
1585 info_.endMainLineIndex_ = currentMainLineIndex_;
1586 } else {
1587 currentMainLineIndex_--;
1588 }
1589 return cellAveLength_;
1590 }
1591
LargeItemNextLineHeight(int32_t currentLineIndex,LayoutWrapper * layoutWrapper)1592 void GridScrollLayoutAlgorithm::LargeItemNextLineHeight(int32_t currentLineIndex, LayoutWrapper* layoutWrapper)
1593 {
1594 auto gridMatrixIter = info_.gridMatrix_.find(currentLineIndex);
1595 auto currentIndex = 0;
1596 if (gridMatrixIter != info_.gridMatrix_.end()) {
1597 for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1598 currentIndex = itemIter->second;
1599 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1600 if (!itemWrapper) {
1601 break;
1602 }
1603 LargeItemLineHeight(itemWrapper);
1604 }
1605 }
1606 }
1607
LargeItemForwardLineHeight(int32_t currentLineIndex,LayoutWrapper * layoutWrapper)1608 void GridScrollLayoutAlgorithm::LargeItemForwardLineHeight(int32_t currentLineIndex, LayoutWrapper* layoutWrapper)
1609 {
1610 auto lineIndex = currentLineIndex;
1611 auto gridMatrixIter = info_.gridMatrix_.find(lineIndex);
1612 if (gridMatrixIter == info_.gridMatrix_.end()) {
1613 return;
1614 }
1615 auto currentIndex = -1;
1616
1617 lineIndex = CalculateLineIndexForLargeItem(gridMatrixIter, currentIndex, lineIndex, layoutWrapper);
1618 CalculateLineHeightForLargeItem(lineIndex, currentLineIndex, gridMatrixIter, layoutWrapper);
1619 }
1620
CalculateLineIndexForLargeItem(std::map<int32_t,std::map<int32_t,int32_t>>::iterator gridMatrixIter,int32_t currentIndex,int32_t lineIndex,LayoutWrapper * layoutWrapper)1621 int32_t GridScrollLayoutAlgorithm::CalculateLineIndexForLargeItem(
1622 std::map<int32_t, std::map<int32_t, int32_t>>::iterator gridMatrixIter, int32_t currentIndex, int32_t lineIndex,
1623 LayoutWrapper* layoutWrapper)
1624 {
1625 for (const auto& gridItemRecord : gridMatrixIter->second) {
1626 if (currentIndex == gridItemRecord.second || gridItemRecord.second == -1) {
1627 continue;
1628 }
1629 currentIndex = gridItemRecord.second;
1630 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1631 if (!itemWrapper) {
1632 break;
1633 }
1634 AdjustRowColSpan(itemWrapper, layoutWrapper, currentIndex);
1635 for (int32_t lastCrossIndex = lineIndex - 1; lastCrossIndex >= 0; lastCrossIndex--) {
1636 auto lastGridMatrixIter = info_.gridMatrix_.find(lastCrossIndex);
1637 if (lastGridMatrixIter == info_.gridMatrix_.end()) {
1638 continue;
1639 }
1640 auto lastGridItemRecord = lastGridMatrixIter->second;
1641 auto lastLineCrossItem = lastGridItemRecord.find(gridItemRecord.first);
1642 if (lastLineCrossItem == lastGridItemRecord.end()) {
1643 continue;
1644 }
1645 if (lastLineCrossItem->second == currentIndex) {
1646 lineIndex--;
1647 } else {
1648 break;
1649 }
1650 }
1651 }
1652 return lineIndex;
1653 }
1654
CalculateLineHeightForLargeItem(int32_t lineIndex,int32_t currentLineIndex,std::map<int32_t,std::map<int32_t,int32_t>>::iterator gridMatrixIter,LayoutWrapper * layoutWrapper)1655 void GridScrollLayoutAlgorithm::CalculateLineHeightForLargeItem(int32_t lineIndex, int32_t currentLineIndex,
1656 std::map<int32_t, std::map<int32_t, int32_t>>::iterator gridMatrixIter, LayoutWrapper* layoutWrapper)
1657 {
1658 for (int32_t i = lineIndex; i <= currentLineIndex; i++) {
1659 auto currentGridMatrixIter = info_.gridMatrix_.find(i);
1660 if (currentGridMatrixIter == info_.gridMatrix_.end()) {
1661 continue;
1662 }
1663 auto currentIndex = 0;
1664 cellAveLength_ = -1.0f;
1665 for (auto itemIter = gridMatrixIter->second.rbegin(); itemIter != gridMatrixIter->second.rend(); ++itemIter) {
1666 if (currentIndex == itemIter->second) {
1667 continue;
1668 }
1669 currentIndex = itemIter->second;
1670 auto itemWrapper = layoutWrapper->GetOrCreateChildByIndex(currentIndex);
1671 if (!itemWrapper) {
1672 break;
1673 }
1674 LargeItemLineHeight(itemWrapper);
1675 auto line = info_.lineHeightMap_.find(i);
1676 if (line == info_.lineHeightMap_.end() || line->second < cellAveLength_) {
1677 info_.lineHeightMap_[i] = cellAveLength_;
1678 }
1679 }
1680 }
1681 }
1682
CreateChildConstraint(float mainSize,float crossSize,const RefPtr<GridLayoutProperty> & gridLayoutProperty,int32_t crossStart,int32_t crossSpan) const1683 LayoutConstraintF GridScrollLayoutAlgorithm::CreateChildConstraint(float mainSize, float crossSize,
1684 const RefPtr<GridLayoutProperty>& gridLayoutProperty, int32_t crossStart, int32_t crossSpan) const
1685 {
1686 float itemMainSize =
1687 gridLayoutProperty->IsConfiguredScrollable() ? Infinity<float>() : mainSize / static_cast<float>(mainCount_);
1688
1689 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
1690 float itemCrossSize = GridUtils::GetCrossGap(gridLayoutProperty, frameSize, axis_) * (crossSpan - 1);
1691 for (int32_t index = 0; index < crossSpan; ++index) {
1692 int32_t crossIndex = (crossStart + index) % static_cast<int32_t>(crossCount_);
1693 if (crossIndex >= 0 && crossIndex < static_cast<int32_t>(itemsCrossSize_.size())) {
1694 itemCrossSize += GetOrDefault(itemsCrossSize_, crossIndex, 0.0f);
1695 }
1696 }
1697
1698 SizeF itemIdealSize =
1699 gridLayoutProperty->IsVertical() ? SizeF(itemCrossSize, itemMainSize) : SizeF(itemMainSize, itemCrossSize);
1700 auto itemConstraint = gridLayoutProperty->CreateChildConstraint();
1701
1702 // The percent size of GridItem is based on the fraction size, for e.g., if a GridItem has width of "50%" in Grid
1703 // configured with columnsTemplate = "1fr 1fr", rowsTemplate = "1fr 1fr",
1704 // then the GridItem width = [width of 1fr] * 50%,
1705 // [itemFractionCount] is now only in direction of cross axis
1706 float widthPercentBase =
1707 GreatOrEqual(crossCount_, Infinity<uint32_t>()) ? itemConstraint.percentReference.Width() : itemCrossSize;
1708 float heightPercentBase = GreatOrEqual(mainCount_, Infinity<uint32_t>())
1709 ? itemConstraint.percentReference.Height()
1710 : itemConstraint.percentReference.Height() / static_cast<float>(mainCount_);
1711 if (axis_ == Axis::VERTICAL) {
1712 itemConstraint.percentReference = SizeF(widthPercentBase, itemConstraint.percentReference.Height());
1713 } else {
1714 itemConstraint.percentReference = SizeF(itemConstraint.percentReference.Width(), heightPercentBase);
1715 }
1716 itemConstraint.maxSize = itemIdealSize;
1717 itemConstraint.parentIdealSize.SetCrossSize(itemCrossSize, axis_);
1718 itemConstraint.UpdateIllegalSelfMarginSizeWithCheck(axis_ == Axis::VERTICAL
1719 ? OptionalSizeF(itemCrossSize, std::nullopt)
1720 : OptionalSizeF(std::nullopt, itemCrossSize));
1721 return itemConstraint;
1722 }
1723
GetNextGrid(int32_t & curMain,int32_t & curCross,bool reverse) const1724 bool GridScrollLayoutAlgorithm::GetNextGrid(int32_t& curMain, int32_t& curCross, bool reverse) const
1725 {
1726 if (!reverse) {
1727 ++curCross;
1728 if (curCross >= static_cast<int32_t>(crossCount_)) {
1729 return false;
1730 }
1731 return true;
1732 }
1733
1734 --curCross;
1735 if (curCross < 0) {
1736 return false;
1737 }
1738 return true;
1739 }
1740
MeasureNewChild(const SizeF & frameSize,int32_t itemIndex,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper,bool reverse)1741 int32_t GridScrollLayoutAlgorithm::MeasureNewChild(const SizeF& frameSize, int32_t itemIndex,
1742 LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper, bool reverse)
1743 {
1744 auto mainCount = static_cast<int32_t>(mainCount_);
1745 auto crossCount = static_cast<int32_t>(crossCount_);
1746 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1747 auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
1748 auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1749 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1750 if (crossSpan > crossCount) {
1751 TAG_LOGW(AceLogTag::ACE_GRID,
1752 "item %{public}d can not be placed in grid: cross count:%{public}d, cross span:%{public}d", itemIndex,
1753 crossCount, crossSpan);
1754 return crossSpan;
1755 }
1756 int32_t mainIndex = currentMainLineIndex_;
1757
1758 if (crossStart >= 0 && crossStart < crossCount) {
1759 if (crossStart < lastCross_) {
1760 return -1;
1761 } else if (CheckGridPlaced(itemIndex, mainIndex, crossStart, mainSpan, crossSpan)) {
1762 MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1763 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
1764 } else {
1765 return -1;
1766 }
1767 } else {
1768 int32_t crossIndex = crossStart >= 0 ? crossStart : lastCross_;
1769
1770 while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, mainSpan, crossSpan)) {
1771 if (GetNextGrid(mainIndex, crossIndex, reverse) == false) {
1772 return -1;
1773 }
1774 if (mainIndex >= mainCount || crossIndex >= crossCount) {
1775 break;
1776 }
1777 }
1778
1779 MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossIndex, crossSpan);
1780 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossIndex));
1781 }
1782 if (mainSpan > 1 || crossSpan > 1) {
1783 for (int i = mainIndex; i < mainSpan; i++) {
1784 info_.irregularLines_[i] = true;
1785 }
1786 }
1787 return crossSpan;
1788 }
1789
MeasureChildPlaced(const SizeF & frameSize,int32_t itemIndex,int32_t crossStart,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper)1790 int32_t GridScrollLayoutAlgorithm::MeasureChildPlaced(const SizeF& frameSize, int32_t itemIndex, int32_t crossStart,
1791 LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper)
1792 {
1793 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
1794 auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
1795 if (static_cast<uint32_t>(crossStart + crossSpan) > crossCount_) {
1796 TAG_LOGI(AceLogTag::ACE_GRID, "item %{public}d cross not enough, start:%{public}d, span:%{public}d", itemIndex,
1797 crossStart, crossSpan);
1798 return 0;
1799 }
1800 auto mainSpan = axis_ == Axis::HORIZONTAL ? currentItemColSpan_ : currentItemRowSpan_;
1801 if (crossSpan > 1 || mainSpan > 1) {
1802 for (int i = currentMainLineIndex_; i < mainSpan; i++) {
1803 info_.irregularLines_[i] = true;
1804 }
1805 }
1806
1807 MeasureChild(layoutWrapper, frameSize, childLayoutWrapper, crossStart, crossSpan);
1808 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
1809 return crossSpan;
1810 }
1811
CheckNeedMeasure(const RefPtr<LayoutWrapper> & layoutWrapper,const LayoutConstraintF & layoutConstraint) const1812 bool GridScrollLayoutAlgorithm::CheckNeedMeasure(
1813 const RefPtr<LayoutWrapper>& layoutWrapper, const LayoutConstraintF& layoutConstraint) const
1814 {
1815 auto childLayoutProperty = AceType::DynamicCast<GridItemLayoutProperty>(layoutWrapper->GetLayoutProperty());
1816 if (childLayoutProperty && childLayoutProperty->GetNeedStretch() && info_.clearStretch_) {
1817 childLayoutProperty->SetNeedStretch(false);
1818 if (axis_ == Axis::VERTICAL) {
1819 childLayoutProperty->ClearUserDefinedIdealSize(false, true);
1820 } else {
1821 childLayoutProperty->ClearUserDefinedIdealSize(true, false);
1822 }
1823 return true;
1824 }
1825
1826 if (expandSafeArea_ || layoutWrapper->CheckNeedForceMeasureAndLayout() || layoutWrapper->IsIgnoreOptsValid()) {
1827 return true;
1828 }
1829
1830 auto geometryNode = layoutWrapper->GetGeometryNode();
1831 CHECK_NULL_RETURN(geometryNode, true);
1832 auto constraint = geometryNode->GetParentLayoutConstraint();
1833 CHECK_NULL_RETURN(constraint, true);
1834 return constraint->maxSize != layoutConstraint.maxSize ||
1835 constraint->percentReference != layoutConstraint.percentReference ||
1836 constraint->selfIdealSize.CrossSize(axis_) != layoutConstraint.selfIdealSize.CrossSize(axis_);
1837 }
1838
MeasureChild(LayoutWrapper * layoutWrapper,const SizeF & frameSize,const RefPtr<LayoutWrapper> & childLayoutWrapper,int32_t crossStart,int32_t crossSpan)1839 void GridScrollLayoutAlgorithm::MeasureChild(LayoutWrapper* layoutWrapper, const SizeF& frameSize,
1840 const RefPtr<LayoutWrapper>& childLayoutWrapper, int32_t crossStart, int32_t crossSpan)
1841 {
1842 auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
1843 auto mainSize = GetMainAxisSize(frameSize, info_.axis_);
1844 auto crossSize = GetCrossAxisSize(frameSize, info_.axis_);
1845 auto childConstraint = CreateChildConstraint(mainSize, crossSize, gridLayoutProperty, crossStart, crossSpan);
1846 if (!CheckNeedMeasure(childLayoutWrapper, childConstraint)) {
1847 return;
1848 }
1849 auto childLayoutProperty = childLayoutWrapper->GetLayoutProperty();
1850 if (!childLayoutProperty) {
1851 childLayoutWrapper->Measure(childConstraint);
1852 return;
1853 }
1854
1855 auto oldConstraint = childLayoutProperty->GetLayoutConstraint();
1856 if (oldConstraint.has_value() && !NearEqual(GetCrossAxisSize(oldConstraint.value().maxSize, axis_),
1857 GetCrossAxisSize(childConstraint.maxSize, axis_))) {
1858 auto layoutAlgorithmWrapper = childLayoutWrapper->GetLayoutAlgorithm();
1859 if (layoutAlgorithmWrapper->SkipMeasure()) {
1860 layoutAlgorithmWrapper->SetNeedMeasure();
1861 if (layoutAlgorithmWrapper->GetLayoutAlgorithm() == nullptr) {
1862 layoutAlgorithmWrapper->SetLayoutAlgorithm(
1863 childLayoutWrapper->GetHostNode()->GetPattern()->CreateLayoutAlgorithm());
1864 }
1865 }
1866 }
1867 childLayoutWrapper->Measure(childConstraint);
1868 }
1869
CheckGridPlaced(int32_t index,int32_t main,int32_t cross,int32_t mainSpan,int32_t crossSpan)1870 bool GridScrollLayoutAlgorithm::CheckGridPlaced(
1871 int32_t index, int32_t main, int32_t cross, int32_t mainSpan, int32_t crossSpan)
1872 {
1873 // If start position is already exist in gridMatrix, place grid item fail.
1874 auto mainIter = info_.gridMatrix_.find(main);
1875 if (mainIter != info_.gridMatrix_.end()) {
1876 auto crossIter = mainIter->second.find(cross);
1877 if (crossIter != mainIter->second.end()) {
1878 return false;
1879 }
1880 }
1881
1882 // If cross length of grid item if out of range, place grid item fail.
1883 if (cross + crossSpan > static_cast<int32_t>(crossCount_)) {
1884 return false;
1885 }
1886
1887 // If any grid item is already exist in gridMatrix, place grid item fail.
1888 for (int32_t i = 0; i < mainSpan; i++) {
1889 mainIter = info_.gridMatrix_.find(i + main);
1890 if (mainIter == info_.gridMatrix_.end()) {
1891 continue;
1892 }
1893 for (int32_t j = 0; j < crossSpan; j++) {
1894 auto crossIter = mainIter->second.find(j + cross);
1895 if (crossIter != mainIter->second.end()) {
1896 return false;
1897 }
1898 }
1899 }
1900
1901 // Padding grid matrix for grid item's range.
1902 for (int32_t i = main; i < main + mainSpan; ++i) {
1903 std::map<int32_t, int32_t> mainMap;
1904 auto iter = info_.gridMatrix_.find(i);
1905 if (iter != info_.gridMatrix_.end()) {
1906 mainMap = iter->second;
1907 }
1908 for (int32_t j = cross; j < cross + crossSpan; ++j) {
1909 mainMap.emplace(std::make_pair(j, index));
1910 }
1911 info_.gridMatrix_[i] = mainMap;
1912 }
1913 lastCross_ = cross + crossSpan;
1914
1915 return true;
1916 }
1917
ComputeItemCrossPosition(int32_t crossStart) const1918 float GridScrollLayoutAlgorithm::ComputeItemCrossPosition(int32_t crossStart) const
1919 {
1920 float position = 0.0f;
1921 for (int32_t index = 0; index < crossStart; ++index) {
1922 if (index >= 0 && index < static_cast<int32_t>(itemsCrossSize_.size())) {
1923 position += GetOrDefault(itemsCrossSize_, index, 0.0f);
1924 }
1925 }
1926 position += crossStart * crossGap_ + crossPaddingOffset_;
1927 return position;
1928 }
1929
GetStartingItem(LayoutWrapper * layoutWrapper,int32_t currentIndex)1930 int32_t GridScrollLayoutAlgorithm::GetStartingItem(LayoutWrapper* layoutWrapper, int32_t currentIndex)
1931 {
1932 int32_t firstIndex = 0;
1933 currentIndex = currentIndex < info_.GetChildrenCount() ? currentIndex : info_.GetChildrenCount() - 1;
1934 auto index = currentIndex;
1935 if (info_.hasBigItem_) {
1936 while (index > 0) {
1937 auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1938 if (!childLayoutWrapper) {
1939 TAG_LOGW(AceLogTag::ACE_GRID, "item [%{public}d] does not exist, reload to [0]", index);
1940 break;
1941 }
1942
1943 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1944 auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1945 if (crossIndex == 0) {
1946 firstIndex = index;
1947 break;
1948 }
1949 --index;
1950 }
1951 } else {
1952 while (index > 0) {
1953 // need to obtain the item node in order and by step one
1954 auto childLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
1955 if (!childLayoutWrapper) {
1956 TAG_LOGW(AceLogTag::ACE_GRID, "item [%{public}d] does not exist, reload to [0]", index);
1957 break;
1958 }
1959 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, index);
1960 auto crossIndex = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
1961 // Grid may change from no big item to has big item
1962 if (crossIndex >= 0) {
1963 info_.hasBigItem_ = true;
1964 return GetStartingItem(layoutWrapper, currentIndex);
1965 }
1966 if (index % info_.crossCount_ == 0) {
1967 firstIndex = index;
1968 break;
1969 }
1970 --index;
1971 }
1972 }
1973 return firstIndex;
1974 }
1975
SupplyAllData2ZeroIndex(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)1976 void GridScrollLayoutAlgorithm::SupplyAllData2ZeroIndex(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
1977 {
1978 // Save the global variable at this moment.
1979 auto tempGridLayoutInfo = info_;
1980
1981 // When the data is supplied again, there is an update of the original global variable info_. Therefore,
1982 // each time you supply the data, you must re-complete the data based on the current screen data
1983 auto startLineIndex = tempGridLayoutInfo.startMainLineIndex_;
1984 auto startIndex = tempGridLayoutInfo.startIndex_;
1985 auto endLineIndex = tempGridLayoutInfo.endMainLineIndex_;
1986 auto endIndex = tempGridLayoutInfo.endIndex_;
1987 auto targetIndex = tempGridLayoutInfo.targetIndex_;
1988 // Remove redundant data that is visible off-screen. This is the key to completing data accurately
1989 DeleteItemsOutOfScope(info_.lineHeightMap_, startLineIndex, endLineIndex);
1990 DeleteItemsOutOfScope(info_.gridMatrix_, startLineIndex, endLineIndex);
1991
1992 // The continuous grid information is saved and used in the GridPattern to calculate the scroll distance
1993 // Complete all data with indexes from startIndex to 0
1994 if (startIndex > 0) {
1995 // The start line when completing the data
1996 currentMainLineIndex_ = startLineIndex;
1997 float lineHeight = 0.0f;
1998 do {
1999 lineHeight = FillNewLineForward(crossSize, mainSize, layoutWrapper);
2000 } while (GreatOrEqual(lineHeight, 0.0));
2001 }
2002
2003 // Complete the data from endIndex+1 to targetIndex_
2004 auto lineHeight = 0.f;
2005 if (endIndex < targetIndex) {
2006 // The start line when completing the data
2007 currentMainLineIndex_ = endLineIndex;
2008 int32_t targetLineIndex = 0;
2009 do {
2010 lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
2011 } while (!(LessNotEqual(lineHeight, 0.0) || IsIndexInMatrix(targetIndex.value(), targetLineIndex)));
2012 }
2013
2014 if (info_.extraOffset_.has_value() && Negative(info_.extraOffset_.value())) {
2015 auto extraOffset = -info_.extraOffset_.value();
2016 info_.GetLineIndexByIndex(targetIndex.value(), currentMainLineIndex_);
2017 lineHeight = info_.lineHeightMap_[currentMainLineIndex_];
2018 auto heightForExtraOffset = lineHeight + mainGap_;
2019 while (GreatOrEqual(extraOffset, heightForExtraOffset) && !Negative(lineHeight)) {
2020 lineHeight = FillNewLineBackward(crossSize, mainSize, layoutWrapper, false);
2021 heightForExtraOffset += (lineHeight + mainGap_);
2022 }
2023 ACE_SCOPED_TRACE(
2024 "SupplyAllData2ZeroIndex, extraOffset_:%f, heightForExtraOffset:%f, LineIndexForExtraOffset:%d",
2025 extraOffset, heightForExtraOffset, currentMainLineIndex_);
2026 }
2027
2028 // Once the data is completed, the global variables need to be returned
2029 infoCopy_ = std::make_unique<GridLayoutInfo>(info_);
2030 info_ = tempGridLayoutInfo;
2031 }
2032
FillCacheLineAtEnd(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)2033 void GridScrollLayoutAlgorithm::FillCacheLineAtEnd(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
2034 {
2035 auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
2036 auto cacheCount = gridLayoutProperty->GetCachedCountValue(info_.defCachedCount_);
2037 if (info_.reachEnd_ || cacheCount == 0) {
2038 return;
2039 }
2040 auto tempEndIndex = info_.endIndex_;
2041 auto tempEndMainLineIndex = info_.endMainLineIndex_;
2042 auto tempCurrentMainLineIndex = currentMainLineIndex_;
2043 currentMainLineIndex_ = info_.endMainLineIndex_;
2044
2045 for (; currentMainLineIndex_ <= tempEndMainLineIndex + cacheCount; currentMainLineIndex_++) {
2046 float lineHeight = FillNewCacheLineBackward(crossSize, mainSize, layoutWrapper, currentMainLineIndex_);
2047 if (LessNotEqual(lineHeight, 0.0)) {
2048 break;
2049 }
2050 }
2051 info_.endIndex_ = tempEndIndex;
2052 info_.endMainLineIndex_ = tempEndMainLineIndex;
2053 currentMainLineIndex_ = tempCurrentMainLineIndex;
2054
2055 if (!predictBuildList_.empty()) {
2056 CreateCachedChildConstraint(layoutWrapper, mainSize, crossSize);
2057 }
2058 }
2059
FillNewCacheLineBackward(float crossSize,float mainSize,LayoutWrapper * layoutWrapper,int32_t currentLine)2060 float GridScrollLayoutAlgorithm::FillNewCacheLineBackward(
2061 float crossSize, float mainSize, LayoutWrapper* layoutWrapper, int32_t currentLine)
2062 {
2063 // To make the code more convenient to read, we name a param in situation of vertical, for example:
2064 // 1. [lineHight] means height of a row when the Grid is vertical;
2065 // 2. [lineHight] means width of a column when the Grid is horizontal;
2066 // Other params are also named according to this principle.
2067 cellAveLength_ = -1.0f;
2068 auto currentIndex = info_.endIndex_ + 1;
2069
2070 // if it fails to fill a new line backward, do [currentLine--]
2071 auto line = info_.gridMatrix_.find(currentLine);
2072 auto childrenCount = info_.GetChildrenCount();
2073 if (info_.gridMatrix_.find(currentLine) != info_.gridMatrix_.end()) {
2074 if (line->second.size() < crossCount_) {
2075 lastCross_ = 0;
2076 for (const auto& elem : line->second) {
2077 if (elem.second > info_.endIndex_) {
2078 info_.endIndex_ = elem.second;
2079 }
2080 }
2081 auto currentIndex = info_.endIndex_ + 1;
2082 for (uint32_t i = line->second.size(); i < crossCount_; i++) {
2083 // Step1. Get wrapper of [GridItem]
2084 CHECK_NULL_RETURN(currentIndex < childrenCount, -1.0f);
2085 auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
2086 if (GridUtils::CheckNeedCacheLayout(itemWrapper)) {
2087 for (uint32_t y = i; y < crossCount_ && currentIndex < childrenCount; y++) {
2088 predictBuildList_.emplace_back(currentIndex++);
2089 }
2090 if (GreatOrEqual(cellAveLength_, 0.0f) &&
2091 info_.lineHeightMap_.find(currentLine) == info_.lineHeightMap_.end()) {
2092 info_.lineHeightMap_[currentLine] = cellAveLength_;
2093 }
2094 return -1.0f;
2095 }
2096 // Step2. Measure child
2097 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
2098 auto childState = MeasureCachedChild(frameSize, currentIndex, layoutWrapper, itemWrapper);
2099 if (childState == -1) {
2100 cellAveLength_ = LessNotEqual(cellAveLength_, 0.0)
2101 ? info_.lineHeightMap_.find(currentLine - 1)->second
2102 : cellAveLength_;
2103 --currentIndex;
2104 break;
2105 }
2106 i += static_cast<uint32_t>(childState) - 1;
2107 // Step3. Measure [GridItem]
2108 LargeItemLineHeight(itemWrapper);
2109 info_.endIndex_ = currentIndex;
2110 currentIndex++;
2111 }
2112 }
2113 CompleteItemCrossPosition(layoutWrapper, line->second);
2114 for (const auto& elem : line->second) {
2115 if (elem.second > info_.endIndex_) {
2116 info_.endIndex_ = elem.second;
2117 }
2118 }
2119 if (NonNegative(cellAveLength_)) {
2120 info_.lineHeightMap_[currentLine] = cellAveLength_;
2121 return cellAveLength_;
2122 }
2123 return GetOrDefault(info_.lineHeightMap_, currentLine, -1.f);
2124 }
2125
2126 lastCross_ = 0;
2127 bool doneFillLine = false;
2128
2129 for (uint32_t i = 0; i < crossCount_; i++) {
2130 if (currentIndex >= childrenCount) {
2131 break;
2132 }
2133 // Step1. Get wrapper of [GridItem]
2134 auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
2135 if (GridUtils::CheckNeedCacheLayout(itemWrapper)) {
2136 for (uint32_t x = i; x < crossCount_ && currentIndex < childrenCount; x++) {
2137 predictBuildList_.emplace_back(currentIndex++);
2138 }
2139 if (GreatOrEqual(cellAveLength_, 0.0f) &&
2140 info_.lineHeightMap_.find(currentLine) == info_.lineHeightMap_.end()) {
2141 info_.lineHeightMap_[currentLine] = cellAveLength_;
2142 }
2143 return -1.0f;
2144 }
2145 // // Step2. Measure child
2146 auto frameSize = axis_ == Axis::VERTICAL ? SizeF(crossSize, mainSize) : SizeF(mainSize, crossSize);
2147 auto crossSpan = MeasureCachedChild(frameSize, currentIndex, layoutWrapper, itemWrapper);
2148 if (crossSpan < 0) {
2149 cellAveLength_ =
2150 LessNotEqual(cellAveLength_, 0.0) ? info_.lineHeightMap_.find(currentLine - 1)->second : cellAveLength_;
2151 --currentIndex;
2152 break;
2153 }
2154 i = static_cast<uint32_t>(lastCross_ - 1);
2155 // // Step3. Measure [GridItem]
2156 LargeItemLineHeight(itemWrapper);
2157
2158 info_.endIndex_ = currentIndex;
2159 currentIndex++;
2160 doneFillLine = true;
2161 }
2162
2163 if (doneFillLine || info_.gridMatrix_.find(currentLine) != info_.gridMatrix_.end()) {
2164 info_.lineHeightMap_[currentLine] = cellAveLength_;
2165 info_.endMainLineIndex_ = currentLine;
2166 } else {
2167 return -1.0f;
2168 }
2169 return cellAveLength_;
2170 }
2171
MeasureCachedChild(const SizeF & frameSize,int32_t itemIndex,LayoutWrapper * layoutWrapper,const RefPtr<LayoutWrapper> & childLayoutWrapper)2172 int32_t GridScrollLayoutAlgorithm::MeasureCachedChild(const SizeF& frameSize, int32_t itemIndex,
2173 LayoutWrapper* layoutWrapper, const RefPtr<LayoutWrapper>& childLayoutWrapper)
2174 {
2175 auto mainCount = static_cast<int32_t>(mainCount_);
2176 auto crossCount = static_cast<int32_t>(crossCount_);
2177 AdjustRowColSpan(childLayoutWrapper, layoutWrapper, itemIndex);
2178 auto mainSpan = axis_ == Axis::VERTICAL ? currentItemRowSpan_ : currentItemColSpan_;
2179 auto crossSpan = axis_ == Axis::VERTICAL ? currentItemColSpan_ : currentItemRowSpan_;
2180 auto crossStart = axis_ == Axis::VERTICAL ? currentItemColStart_ : currentItemRowStart_;
2181 if (crossSpan > crossCount) {
2182 return crossSpan;
2183 }
2184 int32_t mainIndex = currentMainLineIndex_;
2185
2186 if (crossStart >= 0 && crossStart < crossCount) {
2187 if (crossStart < lastCross_) {
2188 return -1;
2189 } else if (CheckGridPlaced(itemIndex, mainIndex, crossStart, mainSpan, crossSpan)) {
2190 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossStart));
2191 } else {
2192 return -1;
2193 }
2194 } else {
2195 int32_t crossIndex = crossStart >= 0 ? crossStart : lastCross_;
2196
2197 while (!CheckGridPlaced(itemIndex, mainIndex, crossIndex, mainSpan, crossSpan)) {
2198 if (GetNextGrid(mainIndex, crossIndex, false) == false) {
2199 return -1;
2200 }
2201 if (mainIndex >= mainCount || crossIndex >= crossCount) {
2202 break;
2203 }
2204 }
2205
2206 itemsCrossPosition_.try_emplace(itemIndex, ComputeItemCrossPosition(crossIndex));
2207 }
2208 if (crossSpan > 1 || mainSpan > 1) {
2209 for (int i = currentMainLineIndex_; i < mainSpan; i++) {
2210 info_.irregularLines_[i] = true;
2211 }
2212 }
2213 return crossSpan;
2214 }
2215
CompleteItemCrossPosition(LayoutWrapper * layoutWrapper,const std::map<int32_t,int32_t> & items)2216 void GridScrollLayoutAlgorithm::CompleteItemCrossPosition(
2217 LayoutWrapper* layoutWrapper, const std::map<int32_t, int32_t>& items)
2218 {
2219 for (auto&& item : items) {
2220 auto currentIndex = item.second;
2221 auto itemWrapper = layoutWrapper->GetChildByIndex(currentIndex, true);
2222 if (!itemWrapper) {
2223 if (predictBuildList_.back().idx < currentIndex) {
2224 predictBuildList_.emplace_front(currentIndex);
2225 } else if (predictBuildList_.front().idx > currentIndex) {
2226 predictBuildList_.emplace_back(currentIndex);
2227 }
2228 }
2229 itemsCrossPosition_.try_emplace(currentIndex, ComputeItemCrossPosition(item.first));
2230 }
2231 }
2232
2233 namespace {
GenerateCacheItemConstraint(const GridItemLayoutProperty & itemProp,Axis axis,const GridPredictLayoutParam & param)2234 LayoutConstraintF GenerateCacheItemConstraint(
2235 const GridItemLayoutProperty& itemProp, Axis axis, const GridPredictLayoutParam& param)
2236 {
2237 auto constraint = param.layoutConstraint;
2238 int32_t crossSpan = itemProp.GetCrossSpan(axis);
2239 int32_t crossStart = itemProp.GetCrossStart(axis);
2240 if (crossSpan > 1) {
2241 float itemCrossSize = param.crossGap * (crossSpan - 1);
2242 for (int32_t index = 0; index < crossSpan; ++index) {
2243 int32_t crossIndex = (crossStart + index) % static_cast<int32_t>(param.itemsCrossSizes.size());
2244 if (crossIndex >= 0 && crossIndex < static_cast<int32_t>(param.itemsCrossSizes.size())) {
2245 itemCrossSize += GetOrDefault(param.itemsCrossSizes, crossIndex, 0.0f);
2246 }
2247 }
2248 constraint.maxSize.SetCrossSize(itemCrossSize, axis);
2249 constraint.selfIdealSize.SetCrossSize(itemCrossSize, axis);
2250 }
2251 return constraint;
2252 }
2253
2254 /* revert layout range in GridLayoutInfo when this object destructs */
2255 class TempLayoutRange {
2256 public:
TempLayoutRange(GridLayoutInfo & info)2257 explicit TempLayoutRange(GridLayoutInfo& info)
2258 : subStart_(info.startIndex_), subStartLine_(info.startMainLineIndex_), subEnd_(info.endIndex_),
2259 subEndLine_(info.endMainLineIndex_), info_(info)
2260 {}
~TempLayoutRange()2261 ~TempLayoutRange()
2262 {
2263 const int32_t diff = CalcDiff();
2264 info_.startIndex_ = subStart_;
2265 info_.startMainLineIndex_ = subStartLine_ - diff;
2266 info_.endIndex_ = subEnd_;
2267 info_.endMainLineIndex_ = subEndLine_ - diff;
2268 }
2269
2270 const int32_t subStart_;
2271 const int32_t subStartLine_;
2272 const int32_t subEnd_;
2273 const int32_t subEndLine_;
2274
2275 private:
2276 GridLayoutInfo& info_;
2277
2278 /**
2279 * @brief after SyncPreload, need adjust startMainLineIdx and endMainLineIdx according to startIndex_.
2280 * Because in AddForwardLines, it may change the gridMatrix_ and make the original start/endMainLineIdx invalid.
2281 */
CalcDiff()2282 int32_t CalcDiff()
2283 {
2284 if (subStartLine_ > subEndLine_) {
2285 // need not adjust when there has no items in viewport.
2286 return 0;
2287 }
2288 auto newStartMainLineIdx = subStartLine_;
2289 info_.GetLineIndexByIndex(subStart_, newStartMainLineIdx);
2290 return subStartLine_ - newStartMainLineIdx;
2291 }
2292
2293 ACE_DISALLOW_COPY_AND_MOVE(TempLayoutRange);
2294 };
2295 } // namespace
2296
SyncPreload(LayoutWrapper * wrapper,int32_t cacheLineCnt,float crossSize,float mainSize)2297 void GridScrollLayoutAlgorithm::SyncPreload(
2298 LayoutWrapper* wrapper, int32_t cacheLineCnt, float crossSize, float mainSize)
2299 {
2300 TempLayoutRange scope(info_);
2301 for (int32_t i = 0; i < cacheLineCnt; ++i) {
2302 FillNewLineForward(crossSize, mainSize, wrapper);
2303
2304 float len = 0.0f;
2305 int32_t endIdx = info_.endIndex_;
2306 int32_t line = scope.subEndLine_ + i + 1;
2307 if (!MeasureExistingLine(line, len, endIdx)) {
2308 currentMainLineIndex_ = line - 1;
2309 FillCurrentLine(mainSize, crossSize, wrapper);
2310 // avoid fill another line when current line is not full
2311 if (measureInNextFrame_) {
2312 break;
2313 }
2314 FillNewLineBackward(crossSize, mainSize, wrapper, false);
2315 }
2316 if (measureInNextFrame_) {
2317 break;
2318 }
2319 }
2320 }
2321
PredictBuildItem(FrameNode & host,int32_t itemIdx,const GridPredictLayoutParam & param)2322 bool GridScrollLayoutAlgorithm::PredictBuildItem(FrameNode& host, int32_t itemIdx, const GridPredictLayoutParam& param)
2323 {
2324 // build callback
2325 auto wrapper = host.GetOrCreateChildByIndex(itemIdx, false, true);
2326 CHECK_NULL_RETURN(wrapper, false);
2327 auto itemProperty = DynamicCast<GridItemLayoutProperty>(wrapper->GetLayoutProperty());
2328 CHECK_NULL_RETURN(itemProperty, false);
2329 const Axis axis = host.GetPattern<GridPattern>()->GetAxis();
2330
2331 auto constraint = GenerateCacheItemConstraint(*itemProperty, axis, param);
2332 wrapper->SetActive(false);
2333 auto frameNode = wrapper->GetHostNode();
2334 CHECK_NULL_RETURN(frameNode, false);
2335 frameNode->GetGeometryNode()->SetParentLayoutConstraint(constraint);
2336 FrameNode::ProcessOffscreenNode(frameNode);
2337 return true;
2338 }
2339
CreateCachedChildConstraint(LayoutWrapper * layoutWrapper,float mainSize,float crossSize)2340 void GridScrollLayoutAlgorithm::CreateCachedChildConstraint(
2341 LayoutWrapper* layoutWrapper, float mainSize, float crossSize)
2342 {
2343 auto gridLayoutProperty = DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
2344 cachedChildConstraint_ = CreateChildConstraint(mainSize, crossSize, gridLayoutProperty, 0, 1);
2345 }
2346
UpdateMainLineOnReload(int32_t startIdx)2347 void GridScrollLayoutAlgorithm::UpdateMainLineOnReload(int32_t startIdx)
2348 {
2349 auto& info = info_;
2350 if (!info.hasBigItem_) {
2351 info.startMainLineIndex_ = startIdx / info.crossCount_;
2352 }
2353 }
2354
GetResetMode(LayoutWrapper * layoutWrapper,int32_t updateIdx)2355 std::pair<bool, bool> GridScrollLayoutAlgorithm::GetResetMode(LayoutWrapper* layoutWrapper, int32_t updateIdx)
2356 {
2357 std::pair<bool, bool> res = { false, false };
2358 auto host = layoutWrapper->GetHostNode();
2359 CHECK_NULL_RETURN(host, res);
2360 auto pattern = host->GetPattern<GridPattern>();
2361 CHECK_NULL_RETURN(pattern, res);
2362 // avoid reset during overScroll
2363 if ((pattern->IsScrollableSpringMotionRunning() && info_.IsOutOfEnd(mainGap_, false))
2364 || updateIdx == -1) {
2365 return res;
2366 }
2367 bool outOfMatrix = false;
2368 if (updateIdx != -1 && updateIdx < info_.startIndex_) {
2369 int32_t startLine = 0;
2370 outOfMatrix = !IsIndexInMatrix(updateIdx, startLine);
2371 }
2372 auto hasOptions = HasLayoutOptions(layoutWrapper);
2373 return { !info_.hasBigItem_ || outOfMatrix || hasOptions, info_.hasBigItem_ && !outOfMatrix && !hasOptions };
2374 }
2375
CheckReset(float mainSize,float crossSize,LayoutWrapper * layoutWrapper)2376 void GridScrollLayoutAlgorithm::CheckReset(float mainSize, float crossSize, LayoutWrapper* layoutWrapper)
2377 {
2378 int32_t updateIdx = layoutWrapper->GetHostNode()->GetChildrenUpdated();
2379 // [resetFromStart,resetFromUpdate]
2380 std::pair<bool, bool> resetMode = GetResetMode(layoutWrapper, updateIdx);
2381 if (info_.lastCrossCount_ != crossCount_ || resetMode.first || info_.IsResetted()) {
2382 if (info_.lastCrossCount_ != crossCount_) {
2383 reason_ = GridReloadReason::CROSS_COUNT_CHANGE;
2384 }
2385 info_.lastCrossCount_ = crossCount_;
2386 info_.lineHeightMap_.clear();
2387 info_.gridMatrix_.clear();
2388 info_.irregularItemsPosition_.clear();
2389 info_.endIndex_ = -1;
2390 info_.endMainLineIndex_ = 0;
2391 info_.prevOffset_ = info_.currentOffset_;
2392 info_.ResetPositionFlags();
2393 info_.clearStretch_ = true;
2394 isChildrenUpdated_ = true;
2395 info_.hasMultiLineItem_ = false;
2396 info_.times_ = 0;
2397 ResetFocusedIndex(layoutWrapper);
2398 if (info_.GetChildrenCount() > 0) {
2399 ReloadToStartIndex(mainSize, crossSize, layoutWrapper);
2400 } else {
2401 info_.startIndex_ = 0;
2402 info_.startMainLineIndex_ = 0;
2403 }
2404 if (IsScrollToEndLine()) {
2405 info_.currentOffset_ = mainSize - info_.lineHeightMap_[info_.endMainLineIndex_];
2406 info_.prevOffset_ = info_.currentOffset_;
2407 }
2408 } else if (resetMode.second) {
2409 isChildrenUpdated_ = true;
2410 info_.irregularItemsPosition_.clear();
2411 info_.ResetPositionFlags();
2412 info_.clearStretch_ = true;
2413 info_.prevOffset_ = info_.currentOffset_;
2414 ResetFocusedIndex(layoutWrapper);
2415 auto it = info_.FindInMatrix(updateIdx);
2416 it = info_.FindStartLineInMatrix(it, updateIdx);
2417 if (it != info_.gridMatrix_.end()) {
2418 const int32_t line = it->first;
2419 info_.ClearMatrixToEnd(updateIdx, line);
2420 info_.ClearHeightsFromMatrix(line);
2421 if (updateIdx <= info_.startIndex_) {
2422 ReloadFromUpdateIdxToStartIndex(mainSize, crossSize, line, layoutWrapper);
2423 }
2424 }
2425 }
2426 }
2427
CheckLastLineItemFullyShowed(LayoutWrapper * layoutWrapper)2428 bool GridScrollLayoutAlgorithm::CheckLastLineItemFullyShowed(LayoutWrapper* layoutWrapper)
2429 {
2430 auto lastLine = info_.gridMatrix_.find(info_.endMainLineIndex_);
2431 if (lastLine != info_.gridMatrix_.end()) {
2432 for (const auto [corssIndex, itemIndex] : lastLine->second) {
2433 auto itemWrapper = layoutWrapper->GetChildByIndex(itemIndex);
2434 if (!itemWrapper) {
2435 continue;
2436 }
2437 auto itemLayoutProperty = DynamicCast<GridItemLayoutProperty>(itemWrapper->GetLayoutProperty());
2438 if (!itemLayoutProperty) {
2439 continue;
2440 }
2441 if (itemLayoutProperty->GetMainSpan(axis_) == 1) {
2442 continue;
2443 }
2444 auto it = info_.FindStartLineInMatrix(lastLine, itemIndex);
2445 if (it == info_.gridMatrix_.end()) {
2446 continue;
2447 }
2448 int32_t startLine = it->first;
2449 if (startLine + itemLayoutProperty->GetMainSpan(axis_) > info_.endMainLineIndex_ + 1) {
2450 return false;
2451 }
2452 }
2453 }
2454 return true;
2455 }
2456
IsIrregularLine(int32_t lineIndex) const2457 bool GridScrollLayoutAlgorithm::IsIrregularLine(int32_t lineIndex) const
2458 {
2459 auto irregular = info_.irregularLines_.find(lineIndex);
2460 if (irregular != info_.irregularLines_.end() && irregular->second) {
2461 return true;
2462 }
2463 return false;
2464 }
2465
ResetOffsetWhenHeightChanged()2466 void GridScrollLayoutAlgorithm::ResetOffsetWhenHeightChanged()
2467 {
2468 if (scrollSource_ == SCROLL_FROM_NONE) {
2469 info_.prevOffset_ = info_.currentOffset_;
2470 }
2471 }
2472
MergeRemainingLines(std::map<int32_t,std::map<int32_t,int32_t>> matrix,int32_t forwardLines)2473 void GridScrollLayoutAlgorithm::MergeRemainingLines(
2474 std::map<int32_t, std::map<int32_t, int32_t>> matrix, int32_t forwardLines)
2475 {
2476 for (const auto& line : matrix) {
2477 if (line.second.empty()) {
2478 continue;
2479 }
2480 for (const auto& [crossIndex, index] : line.second) {
2481 info_.gridMatrix_[line.first - forwardLines][crossIndex] = index;
2482 }
2483 }
2484 }
2485
SkipLargeLineHeightLines(float mainSize)2486 bool GridScrollLayoutAlgorithm::SkipLargeLineHeightLines(float mainSize)
2487 {
2488 bool needSkip = false;
2489 for (int32_t line = info_.startMainLineIndex_; line <= info_.endMainLineIndex_; line++) {
2490 auto iter = info_.lineHeightMap_.find(line);
2491 if (iter != info_.lineHeightMap_.end() && iter->second >= mainSize) {
2492 needSkip = true;
2493 break;
2494 }
2495 }
2496 if (needSkip) {
2497 auto totalViewHeight = info_.GetTotalHeightOfItemsInView(mainGap_);
2498 auto needSkipHeight = totalViewHeight + info_.prevOffset_ + mainGap_;
2499 if (GreatOrEqual(needSkipHeight, -info_.currentOffset_)) {
2500 return false;
2501 }
2502
2503 auto endLine = info_.gridMatrix_.find(info_.endMainLineIndex_ + 1);
2504 if (endLine != info_.gridMatrix_.end() && !endLine->second.empty()) {
2505 info_.currentOffset_ += needSkipHeight;
2506 info_.endMainLineIndex_++;
2507 info_.startMainLineIndex_ = info_.endMainLineIndex_;
2508 }
2509 }
2510 return true;
2511 }
2512
HasLayoutOptions(LayoutWrapper * layoutWrapper)2513 bool GridScrollLayoutAlgorithm::HasLayoutOptions(LayoutWrapper* layoutWrapper)
2514 {
2515 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
2516 CHECK_NULL_RETURN(gridLayoutProperty, false);
2517 return gridLayoutProperty->GetLayoutOptions().has_value();
2518 }
2519 } // namespace OHOS::Ace::NG
2520