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